예제 #1
0
        public void TakeOrder(Address from, BigInteger uid)
        {
            Runtime.Expect(Runtime.IsWitness(from), "invalid witness");

            var count = _otcBook.Count();

            for (int i = 0; i < count; i++)
            {
                var order = _otcBook.Get <ExchangeOrder>(i);
                if (order.Uid == uid)
                {
                    var baseBalance = Runtime.GetBalance(order.BaseSymbol, this.Address);
                    Runtime.Expect(baseBalance >= order.Price, "invalid seller amount");

                    var quoteBalance = Runtime.GetBalance(order.QuoteSymbol, from);
                    Runtime.Expect(quoteBalance >= order.Amount, "invalid buyer amount");

                    Runtime.TransferTokens(order.BaseSymbol, this.Address, from, order.Price);
                    Runtime.TransferTokens(order.QuoteSymbol, from, order.Creator, order.Amount);
                    _otcBook.RemoveAt(i);
                    return;
                }
            }

            Runtime.Expect(false, "order not found");
        }
예제 #2
0
        // migrates the full stake from one address to other
        public void Migrate(Address from, Address to)
        {
            Runtime.Expect(Runtime.IsWitness(from), "invalid witness");
            Runtime.Expect(to.IsUser, "destination must be user address");

            var targetStake = _stakes.Get <Address, EnergyAction>(to);

            Runtime.Expect(targetStake.totalAmount == 0, "Tried to migrate to an account that's already staking");

            //migrate stake
            var sourceStake = _stakes.Get <Address, EnergyAction>(from);

            _stakes.Set(to, sourceStake);
            _stakes.Remove(from);

            //migrate master claim
            var masterAccountThreshold = GetMasterThreshold();

            if (sourceStake.totalAmount >= masterAccountThreshold)
            {
                var       count     = _mastersList.Count();
                var       index     = -1;
                Timestamp claimDate = Runtime.Time;
                for (int i = 0; i < count; i++)
                {
                    var master = _mastersList.Get <EnergyMaster>(i);
                    if (master.address == from)
                    {
                        index     = i;
                        claimDate = master.claimDate;
                        break;
                    }
                }

                Runtime.Expect(index >= 0, "Expected this address to be a master");

                _mastersList.RemoveAt <EnergyMaster>(index);
                _mastersList.Add(new EnergyMaster()
                {
                    address = to, claimDate = claimDate
                });
            }

            //migrate voting power
            var votingLogbook = _voteHistory.Get <Address, StorageList>(from);

            votingLogbook.Add(to);
            votingLogbook.Remove(from);
        }
예제 #3
0
        public void StopLend(Address from)
        {
            Runtime.Expect(IsWitness(from), "invalid witness");
            Runtime.Expect(IsLender(from), "not a lender");

            int index = -1;
            var count = _lenderList.Count();

            for (int i = 0; i < count; i++)
            {
                var entry = _lenderList.Get <Address>(i);
                if (entry == from)
                {
                    index = i;
                    break;
                }
            }

            Runtime.Expect(index >= 0, "not lending");

            _lenderList.RemoveAt <Address>(index);
            _lenderMap.Remove <Address>(from);

            Runtime.Notify(EventKind.AddressUnlink, from, Runtime.Chain.Address);
        }
예제 #4
0
        private IEnumerable <Transaction> ProcessPendingTasks(Block block, OracleReader oracle, BigInteger minimumFee, StorageChangeSetContext changeSet, bool allowModify)
        {
            var taskList  = new StorageList(TaskListTag, changeSet);
            var taskCount = taskList.Count();

            List <Transaction> transactions = null;

            int i = 0;

            while (i < taskCount)
            {
                var taskID = taskList.Get <BigInteger>(i);
                var task   = GetTask(changeSet, taskID);

                Transaction tx;

                var taskResult = ProcessPendingTask(block, oracle, minimumFee, changeSet, allowModify, task, out tx);
                if (taskResult == TaskResult.Running)
                {
                    i++;
                }
                else
                {
                    taskList.RemoveAt <BigInteger>(i);
                }

                if (tx != null)
                {
                    if (transactions == null)
                    {
                        transactions = new List <Transaction>();
                    }

                    transactions.Add(tx);
                }
            }

            if (transactions != null)
            {
                return(transactions);
            }

            return(Enumerable.Empty <Transaction>());
        }
예제 #5
0
        public void CancelOrder(BigInteger uid)
        {
            Runtime.Expect(_orderMap.ContainsKey <BigInteger>(uid), "order not found");
            var         key       = _orderMap.Get <BigInteger, string>(uid);
            StorageList orderList = _orders.Get <string, StorageList>(key);

            var count = orderList.Count();

            for (int i = 0; i < count; i++)
            {
                var order = orderList.Get <ExchangeOrder>(i);
                if (order.Uid == uid)
                {
                    Runtime.Expect(IsWitness(order.Creator), "invalid witness");

                    orderList.RemoveAt <ExchangeOrder>(i);
                    _orderMap.Remove <BigInteger>(uid);
                    _fills.Remove <BigInteger>(uid);

                    if (_escrows.ContainsKey <BigInteger>(uid))
                    {
                        var leftoverEscrow = _escrows.Get <BigInteger, BigInteger>(uid);
                        if (leftoverEscrow > 0)
                        {
                            var escrowSymbol = order.Side == ExchangeOrderSide.Sell ? order.QuoteSymbol : order.BaseSymbol;
                            Runtime.Nexus.TransferTokens(Runtime, escrowSymbol, this.Address, order.Creator, leftoverEscrow);
                            Runtime.Notify(EventKind.TokenReceive, order.Creator, new TokenEventData()
                            {
                                chainAddress = this.Address, symbol = escrowSymbol, value = leftoverEscrow
                            });
                        }
                    }

                    return;
                }
            }

            // if it reaches here, it means it not found nothing in previous part
            throw new Exception("order not found");
        }
예제 #6
0
        public void TestStorageList()
        {
            var context = new MemoryStorageContext();
            var list    = new StorageList("test".AsByteArray(), context);

            Assert.IsTrue(list.Count() == 0);

            list.Add("hello");
            list.Add("world");
            Assert.IsTrue(list.Count() == 2);

            list.RemoveAt(0);
            Assert.IsTrue(list.Count() == 1);

            var temp = list.Get <string>(0);

            Assert.IsTrue(temp == "world");

            list.Replace <string>(0, "hello");

            temp = list.Get <string>(0);
            Assert.IsTrue(temp == "hello");
        }
예제 #7
0
        public void Update()
        {
            if (this.platforms == null)
            {
                if (!Nexus.HasGenesis)
                {
                    return;
                }

                var platforms = Nexus.GetPlatforms(Nexus.RootStorage);
                this.platforms = platforms.Select(x => Nexus.GetPlatformInfo(Nexus.RootStorage, x)).ToArray();

                if (this.platforms.Length == 0)
                {
                    logger.Warning("No interop platforms found. Make sure that the Nexus was created correctly.");
                    return;
                }

                _finders["neo"] = new NeoInterop(this, wifs["neo"], interopBlocks["neo"], neoscanAPI, logger);
            }

            if (this.platforms.Length == 0)
            {
                return;
            }

            var pendingList = new StorageList(PendingTag, this.Storage);
            int i           = 0;
            var count       = pendingList.Count();

            while (i < count)
            {
                var settlement = pendingList.Get <PendingSettle>(i);
                if (UpdatePendingSettle(pendingList, i))
                {
                    pendingList.RemoveAt <PendingSettle>(i);
                    count--;
                }
                else
                {
                    i++;
                }
            }

            foreach (var finder in _finders.Values)
            {
                var swaps = finder.Update();

                foreach (var swap in swaps)
                {
                    if (_pendingSwaps.ContainsKey(swap.hash))
                    {
                        logger.Message($"Already known swap, ignore {finder.PlatformName} swap: {swap.source} => {swap.destination}");
                        continue;
                    }

                    logger.Message($"Detected {finder.PlatformName} swap: {swap.source} => {swap.destination} hash: {swap.hash}");
                    _pendingSwaps[swap.hash] = swap;
                    MapSwap(swap.source, swap.hash);
                    MapSwap(swap.destination, swap.hash);
                }
            }
        }
예제 #8
0
        public void Update()
        {
            try
            {
                if (this.platforms == null)
                {
                    if (!Nexus.HasGenesis)
                    {
                        return;
                    }

                    var platforms = Nexus.GetPlatforms(Nexus.RootStorage);
                    this.platforms = platforms.Select(x => Nexus.GetPlatformInfo(Nexus.RootStorage, x)).ToArray();

                    if (this.platforms.Length == 0)
                    {
                        Logger.Warning("No interop platforms found. Make sure that the Nexus was created correctly.");
                        return;
                    }

                    _swappers["neo"]     = new NeoInterop(this, neoAPI, interopBlocks["neo"], Settings.Oracle.NeoQuickSync);
                    SwapAddresses["neo"] = _swappers["neo"].LocalAddress;

                    _swappers["ethereum"]     = new EthereumInterop(this, ethAPI, interopBlocks["ethereum"], Nexus.GetPlatformTokenHashes("ethereum", Nexus.RootStorage).Select(x => x.ToString().Substring(0, 40)).ToArray(), Settings.Oracle.EthConfirmations);
                    SwapAddresses["ethereum"] = _swappers["ethereum"].LocalAddress;

                    Logger.Message("Available swap addresses:");
                    foreach (var x in SwapAddresses)
                    {
                        Logger.Message("platform: " + x.Key + " address: " + x.Value);
                    }
                }

                if (this.platforms.Length == 0)
                {
                    return;
                }
                else
                {
                    if (taskDict.Count == 0)
                    {
                        foreach (var platform in this.platforms)
                        {
                            taskDict.Add(platform.Name, null);
                        }
                    }
                }

                lock (StateModificationLock)
                {
                    var pendingList = new StorageList(PendingTag, this.Storage);

                    int i     = 0;
                    var count = pendingList.Count();

                    while (i < count)
                    {
                        var settlement = pendingList.Get <PendingFee>(i);
                        if (UpdatePendingSettle(pendingList, i))
                        {
                            pendingList.RemoveAt(i);
                            count--;
                        }
                        else
                        {
                            i++;
                        }
                    }
                }

                ProcessCompletedTasks();

                for (var j = 0; j < taskDict.Count; j++)
                {
                    var platform = taskDict.Keys.ElementAt(j);
                    var task     = taskDict[platform];
                    if (task == null)
                    {
                        if (_swappers.TryGetValue(platform, out ChainSwapper finder))
                        {
                            taskDict[platform] = new Task <IEnumerable <PendingSwap> >(() =>
                            {
                                return(finder.Update());
                            });
                        }
                    }
                }

                // start new tasks
                foreach (var entry in taskDict)
                {
                    var task = entry.Value;
                    if (task != null && task.Status.Equals(TaskStatus.Created))
                    {
                        task.ContinueWith(t => { Console.WriteLine($"===> task {task.ToString()} failed"); }, TaskContinuationOptions.OnlyOnFaulted);
                        task.Start();
                    }
                }
            }
            catch (Exception e)
            {
                var logMessage = "TokenSwapper.Update() exception caught:\n" + e.Message;
                var inner      = e.InnerException;
                while (inner != null)
                {
                    logMessage += "\n---> " + inner.Message + "\n\n" + inner.StackTrace;
                    inner       = inner.InnerException;
                }
                logMessage += "\n\n" + e.StackTrace;

                Logger.Error(logMessage);
            }
        }
예제 #9
0
        public void SettleTransaction(Address from, string platform, string chain, Hash hash)
        {
            PlatformSwapAddress[] swapAddresses;

            if (platform != DomainSettings.PlatformName)
            {
                Runtime.Expect(Runtime.PlatformExists(platform), "unsupported platform");
                var platformInfo = Runtime.GetPlatformByName(platform);
                swapAddresses = platformInfo.InteropAddresses;
            }
            else
            {
                swapAddresses = null;
            }

            Runtime.Expect(Runtime.IsWitness(from), "invalid witness");
            Runtime.Expect(from.IsUser, "must be user address");

            var chainHashes = _hashes.Get <string, StorageMap>(platform);

            Runtime.Expect(!chainHashes.ContainsKey <Hash>(hash), "hash already seen");

            var interopTx = Runtime.ReadTransactionFromOracle(platform, chain, hash);

            Runtime.Expect(interopTx.Hash == hash, "unxpected hash");

            int swapCount = 0;

            foreach (var transfer in interopTx.Transfers)
            {
                var count = _withdraws.Count();
                var index = -1;
                for (int i = 0; i < count; i++)
                {
                    var entry = _withdraws.Get <InteropWithdraw>(i);
                    if (entry.destination == transfer.destinationAddress && entry.transferAmount == transfer.Value && entry.transferSymbol == transfer.Symbol)
                    {
                        index = i;
                        break;
                    }
                }

                if (index >= 0)
                {
                    Runtime.Expect(Runtime.TokenExists(transfer.Symbol), "invalid token");
                    var token = this.Runtime.GetToken(transfer.Symbol);

                    if (Runtime.ProtocolVersion >= 4)
                    {
                        if (token.Flags.HasFlag(TokenFlags.Fungible))
                        {
                            Runtime.Expect(transfer.Value > 0, "amount must be positive and greater than zero");
                        }
                        else
                        {
                            Runtime.Expect(Runtime.NFTExists(transfer.Symbol, transfer.Value), $"nft {transfer.Value} must exist");
                        }
                    }
                    else
                    {
                        Runtime.Expect(transfer.Value > 0, "amount must be positive and greater than zero");
                        Runtime.Expect(token.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible");
                    }

                    Runtime.Expect(token.Flags.HasFlag(TokenFlags.Transferable), "token must be transferable");

                    var withdraw = _withdraws.Get <InteropWithdraw>(index);
                    _withdraws.RemoveAt <InteropWithdraw>(index);

                    if (Runtime.ProtocolVersion >= 3)
                    {
                        var org = Runtime.GetOrganization(DomainSettings.ValidatorsOrganizationName);
                        Runtime.Expect(org.IsMember(from), $"{from.Text} is not a validator node");
                        Runtime.TransferTokens(withdraw.feeSymbol, this.Address, from, withdraw.feeAmount);
                    }
                    else
                    {
                        Runtime.TransferTokens(withdraw.feeSymbol, this.Address, transfer.sourceAddress, withdraw.feeAmount);
                    }

                    swapCount++;
                }
                else
                if (swapAddresses != null)
                {
                    foreach (var entry in swapAddresses)
                    {
                        if (transfer.destinationAddress == entry.LocalAddress)
                        {
                            Runtime.Expect(!transfer.sourceAddress.IsNull, "invalid source address");

                            // Here we detect if this transfer occurs between two swap addresses
                            var isInternalTransfer = Runtime.IsPlatformAddress(transfer.sourceAddress);

                            if (!isInternalTransfer)
                            {
                                Runtime.Expect(Runtime.TokenExists(transfer.Symbol), "invalid token");
                                var token = this.Runtime.GetToken(transfer.Symbol);

                                if (Runtime.ProtocolVersion >= 4)
                                {
                                    if (token.Flags.HasFlag(TokenFlags.Fungible))
                                    {
                                        Runtime.Expect(transfer.Value > 0, "amount must be positive and greater than zero");
                                    }
                                    else
                                    {
                                        Runtime.Expect(Runtime.NFTExists(transfer.Symbol, transfer.Value), $"nft {transfer.Value} must exist");
                                    }
                                }
                                else
                                {
                                    Runtime.Expect(transfer.Value > 0, "amount must be positive and greater than zero");
                                    Runtime.Expect(token.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible");
                                }


                                Runtime.Expect(token.Flags.HasFlag(TokenFlags.Transferable), "token must be transferable");

                                Runtime.Expect(transfer.interopAddress.IsUser, "invalid destination address");

                                Runtime.SwapTokens(platform, transfer.sourceAddress, Runtime.Chain.Name, transfer.interopAddress, transfer.Symbol, transfer.Value);


                                if (Runtime.ProtocolVersion >= 4 && !token.Flags.HasFlag(TokenFlags.Fungible))
                                {
                                    var externalNft = Runtime.ReadNFTFromOracle(platform, transfer.Symbol, transfer.Value);
                                    var ram         = Serialization.Serialize(externalNft);

                                    var localNft = Runtime.ReadToken(transfer.Symbol, transfer.Value);
                                    Runtime.WriteToken(transfer.Symbol, transfer.Value, ram);
                                }

                                swapCount++;
                            }

                            break;
                        }
                    }
                }
            }

            Runtime.Expect(swapCount > 0, "nothing to settle");
            chainHashes.Set <Hash, Hash>(hash, Runtime.Transaction.Hash);
            Runtime.Notify(EventKind.ChainSwap, from, new TransactionSettleEventData(hash, platform, chain));
        }
예제 #10
0
        public BigInteger Unstake(Address from, BigInteger amount)
        {
            Runtime.Expect(IsWitness(from), "witness failed");

            if (!_stakes.ContainsKey <Address>(from))
            {
                return(0);
            }

            var stake = _stakes.Get <Address, EnergyAction>(from);

            if (stake.timestamp.Value == 0) // failsafe, should never happen
            {
                return(0);
            }

            var diff = Runtime.Time - stake.timestamp;
            var days = diff / 86400; // convert seconds to days

            Runtime.Expect(days >= 1, "waiting period required");

            var token    = Runtime.Nexus.GetTokenInfo(Nexus.StakingTokenSymbol);
            var balances = Runtime.Chain.GetTokenBalances(token.Symbol);
            var balance  = balances.Get(this.Storage, Runtime.Chain.Address);

            Runtime.Expect(balance >= amount, "not enough balance");

            Runtime.Expect(stake.totalAmount >= amount, "tried to unstake more than what was staked");

            Runtime.Expect(balances.Subtract(this.Storage, Runtime.Chain.Address, amount), "balance subtract failed");
            Runtime.Expect(balances.Add(this.Storage, from, amount), "balance add failed");

            stake.totalAmount -= amount;

            var unclaimedPartials = GetLastAction(from).unclaimedPartials;


            if (stake.totalAmount == 0 && unclaimedPartials == 0)
            {
                _stakes.Remove(from);
                _voteHistory.Remove(from);
            }
            else
            {
                var entry = new EnergyAction()
                {
                    unclaimedPartials = unclaimedPartials,
                    totalAmount       = stake.totalAmount,
                    timestamp         = this.Runtime.Time,
                };

                _stakes.Set(from, entry);

                RemoveVotingPower(from, amount);
            }

            if (stake.totalAmount < MasterAccountThreshold)
            {
                var count = _mastersList.Count();
                var index = -1;
                for (int i = 0; i < count; i++)
                {
                    var master = _mastersList.Get <EnergyMaster>(i);
                    if (master.address == from)
                    {
                        index = i;
                        break;
                    }
                }

                if (index >= 0)
                {
                    var penalizationDate = GetMasterClaimDateFromReference(1, _mastersList.Get <EnergyMaster>(index).claimDate);
                    _mastersList.RemoveAt <EnergyMaster>(index);

                    Runtime.Notify(EventKind.MasterDemote, from, penalizationDate);
                }
            }

            Runtime.Notify(EventKind.TokenUnstake, from, new TokenEventData()
            {
                chainAddress = Runtime.Chain.Address, symbol = token.Symbol, value = amount
            });

            return(amount);
        }
예제 #11
0
        public void Update()
        {
            try
            {
                if (this.platforms == null)
                {
                    if (!Nexus.HasGenesis)
                    {
                        return;
                    }

                    var platforms = Nexus.GetPlatforms(Nexus.RootStorage);
                    this.platforms = platforms.Select(x => Nexus.GetPlatformInfo(Nexus.RootStorage, x)).ToArray();

                    if (this.platforms.Length == 0)
                    {
                        Logger.Warning("No interop platforms found. Make sure that the Nexus was created correctly.");
                        return;
                    }

                    if (IsPlatformSupported(SwapPlatformChain.Neo))
                    {
                        InitSwapper(SwapPlatformChain.Neo, new NeoInterop(this, neoAPI,
                                                                          _interopBlocks[SwapPlatformChain.Neo], Settings.Oracle.NeoQuickSync));
                    }

                    if (IsPlatformSupported(SwapPlatformChain.Ethereum))
                    {
                        InitSwapper(SwapPlatformChain.Ethereum, new EthereumInterop(this, ethAPI,
                                                                                    _interopBlocks[SwapPlatformChain.Ethereum], Nexus.GetPlatformTokenHashes(EthereumWallet.EthereumPlatform,
                                                                                                                                                             Nexus.RootStorage).Select(x => x.ToString().Substring(0, 40)).ToArray(), Settings.Oracle.EthConfirmations));
                    }

                    if (IsPlatformSupported(SwapPlatformChain.BSC) && Nexus.PlatformExists(Nexus.RootStorage, BSCWallet.BSCPlatform))
                    {
                        InitSwapper(SwapPlatformChain.BSC, new BSCInterop(this, bscAPI, _interopBlocks[SwapPlatformChain.BSC],
                                                                          Nexus.GetPlatformTokenHashes(BSCWallet.BSCPlatform, Nexus.RootStorage).Select(x =>
                                                                                                                                                        x.ToString().Substring(0, 40)).ToArray(), Settings.Oracle.EthConfirmations));
                    }

                    Logger.Message("Available swap addresses:");
                    foreach (var x in SwapAddresses)
                    {
                        Logger.Message("platform: " + x.Key + " address: " + string.Join(", ", x.Value));
                    }
                }

                if (this.platforms.Length == 0)
                {
                    return;
                }
                else
                {
                    if (_taskMap.Count == 0)
                    {
                        foreach (var platform in Settings.Oracle.SwapPlatforms)
                        {
                            if (platform.Chain != SwapPlatformChain.Phantasma) // TODO is this IF necessary?
                            {
                                _taskMap.Add(platform.Chain, null);
                            }
                        }
                    }
                }

                lock (StateModificationLock)
                {
                    var pendingList = new StorageList(PendingTag, this.Storage);

                    int i     = 0;
                    var count = pendingList.Count();

                    while (i < count)
                    {
                        var settlement = pendingList.Get <PendingFee>(i);
                        if (UpdatePendingSettle(pendingList, i))
                        {
                            pendingList.RemoveAt(i);
                            count--;
                        }
                        else
                        {
                            i++;
                        }
                    }
                }

                ProcessCompletedTasks();

                for (var j = 0; j < _taskMap.Count; j++)
                {
                    var platform = _taskMap.Keys.ElementAt(j);

                    var task = _taskMap[platform];
                    if (task == null)
                    {
                        if (_swappers.TryGetValue(platform, out ChainSwapper finder))
                        {
                            _taskMap[platform] = new Task <IEnumerable <PendingSwap> >(() =>
                            {
                                return(finder.Update());
                            });
                        }
                    }
                }

                // start new tasks
                foreach (var entry in _taskMap)
                {
                    var task = entry.Value;
                    if (task != null && task.Status.Equals(TaskStatus.Created))
                    {
                        task.ContinueWith(t => { Console.WriteLine($"===> task {task.ToString()} failed"); }, TaskContinuationOptions.OnlyOnFaulted);
                        task.Start();
                    }
                }
            }
            catch (Exception e)
            {
                var logMessage = "TokenSwapper.Update() exception caught:\n" + e.Message;
                var inner      = e.InnerException;
                while (inner != null)
                {
                    logMessage += "\n---> " + inner.Message + "\n\n" + inner.StackTrace;
                    inner       = inner.InnerException;
                }
                logMessage += "\n\n" + e.StackTrace;

                Logger.Error(logMessage);
            }
        }
예제 #12
0
        public void SettleTransaction(Address from, string platform, Hash hash)
        {
            Runtime.Expect(platform != DomainSettings.PlatformName, "must be external platform");
            Runtime.Expect(Runtime.PlatformExists(platform), "unsupported platform");
            var platformInfo = Runtime.GetPlatform(platform);

            Runtime.Expect(Runtime.IsWitness(from), "invalid witness");
            Runtime.Expect(from.IsUser, "must be user address");

            var chainHashes = _hashes.Get <string, StorageSet>(platform);

            Runtime.Expect(!chainHashes.Contains <Hash>(hash), "hash already seen");

            var interopTx = Runtime.ReadTransactionFromOracle(platform, DomainSettings.RootChainName, hash);

            Runtime.Expect(interopTx.Platform == platform, "unxpected platform name");
            Runtime.Expect(interopTx.Hash == hash, "unxpected hash");

            int swapCount = 0;

            foreach (var evt in interopTx.Events)
            {
                if (evt.Kind == EventKind.TokenReceive && evt.Address == platformInfo.Address)
                {
                    Runtime.Expect(!evt.Address.IsNull, "invalid source address");

                    var transfer = evt.GetContent <TokenEventData>();
                    Runtime.Expect(transfer.value > 0, "amount must be positive and greater than zero");

                    Runtime.Expect(Runtime.TokenExists(transfer.symbol), "invalid token");
                    var token = this.Runtime.GetToken(transfer.symbol);

                    Runtime.Expect(token.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible");
                    Runtime.Expect(token.Flags.HasFlag(TokenFlags.Transferable), "token must be transferable");
                    Runtime.Expect(token.Flags.HasFlag(TokenFlags.External), "token must be external");

                    Address destination = Address.Null;
                    foreach (var otherEvt in interopTx.Events)
                    {
                        if (otherEvt.Kind == EventKind.TokenSend)
                        {
                            var otherTransfer = otherEvt.GetContent <TokenEventData>();
                            if (otherTransfer.chainAddress == transfer.chainAddress && otherTransfer.symbol == transfer.symbol)
                            {
                                destination = otherEvt.Address;
                                break;
                            }
                        }
                    }

                    if (destination.IsInterop)
                    {
                        destination = GetLink(destination, DomainSettings.PlatformName);
                    }
                    else
                    {
                        Runtime.Expect(destination.IsUser, "invalid destination address");
                    }

                    Runtime.Expect(Runtime.TransferTokens(transfer.symbol, platformInfo.Address, destination, transfer.value), "mint failed");
                    Runtime.Notify(EventKind.TokenReceive, destination, new TokenEventData()
                    {
                        chainAddress = platformInfo.Address, value = transfer.value, symbol = transfer.symbol
                    });

                    swapCount++;
                    break;
                }

                if (evt.Kind == EventKind.TokenClaim)
                {
                    var destination = evt.Address;
                    Runtime.Expect(!destination.IsNull, "invalid destination");

                    var transfer = evt.GetContent <TokenEventData>();
                    Runtime.Expect(transfer.value > 0, "amount must be positive and greater than zero");

                    Runtime.Expect(Runtime.TokenExists(transfer.symbol), "invalid token");
                    var token = this.Runtime.GetToken(transfer.symbol);
                    Runtime.Expect(token.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible");
                    Runtime.Expect(token.Flags.HasFlag(TokenFlags.Transferable), "token must be transferable");
                    Runtime.Expect(token.Flags.HasFlag(TokenFlags.External), "token must be external");

                    var count = _withdraws.Count();
                    var index = -1;
                    for (int i = 0; i < count; i++)
                    {
                        var entry = _withdraws.Get <InteropWithdraw>(i);
                        if (entry.destination == destination && entry.transferAmount == transfer.value && entry.transferSymbol == transfer.symbol)
                        {
                            index = i;
                            break;
                        }
                    }

                    Runtime.Expect(index >= 0, "invalid withdraw, possible leak found");

                    var withdraw = _withdraws.Get <InteropWithdraw>(index);
                    Runtime.Expect(withdraw.broker == from, "invalid broker");

                    _withdraws.RemoveAt <InteropWithdraw>(index);

                    Runtime.Expect(Runtime.TransferTokens(withdraw.feeSymbol, this.Address, from, withdraw.feeAmount), "fee payment failed");

                    Runtime.Notify(EventKind.TokenReceive, from, new TokenEventData()
                    {
                        chainAddress = this.Runtime.Chain.Address, value = withdraw.feeAmount, symbol = withdraw.feeSymbol
                    });
                    Runtime.Notify(EventKind.TokenReceive, destination, new TokenEventData()
                    {
                        chainAddress = platformInfo.Address, value = withdraw.transferAmount, symbol = withdraw.transferSymbol
                    });

                    swapCount++;
                    break;
                }
            }

            Runtime.Expect(swapCount > 0, "nothing to settle");
            chainHashes.Add <Hash>(hash);
        }
예제 #13
0
        public void SettleTransaction(Address from, string platform, string chain, Hash hash)
        {
            PlatformSwapAddress[] swapAddresses;

            if (platform != DomainSettings.PlatformName)
            {
                Runtime.Expect(Runtime.PlatformExists(platform), "unsupported platform");
                var platformInfo = Runtime.GetPlatformByName(platform);
                swapAddresses = platformInfo.InteropAddresses;
            }
            else
            {
                swapAddresses = null;
            }

            Runtime.Expect(Runtime.IsWitness(from), "invalid witness");
            Runtime.Expect(from.IsUser, "must be user address");

            var chainHashes = _hashes.Get <string, StorageMap>(platform);

            Runtime.Expect(!chainHashes.ContainsKey <Hash>(hash), "hash already seen");

            var interopTx = Runtime.ReadTransactionFromOracle(platform, chain, hash);

            Runtime.Expect(interopTx.Hash == hash, "unxpected hash");

            int swapCount = 0;

            foreach (var transfer in interopTx.Transfers)
            {
                var count = _withdraws.Count();
                var index = -1;
                for (int i = 0; i < count; i++)
                {
                    var entry = _withdraws.Get <InteropWithdraw>(i);
                    if (entry.destination == transfer.destinationAddress && entry.transferAmount == transfer.Value && entry.transferSymbol == transfer.Symbol)
                    {
                        index = i;
                        break;
                    }
                }

                if (index >= 0)
                {
                    Runtime.Expect(transfer.Value > 0, "amount must be positive and greater than zero");

                    Runtime.Expect(Runtime.TokenExists(transfer.Symbol), "invalid token");
                    var token = this.Runtime.GetToken(transfer.Symbol);
                    Runtime.Expect(token.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible");
                    Runtime.Expect(token.Flags.HasFlag(TokenFlags.Transferable), "token must be transferable");
                    Runtime.Expect(token.Flags.HasFlag(TokenFlags.External), "token must be external");

                    var withdraw = _withdraws.Get <InteropWithdraw>(index);
                    _withdraws.RemoveAt <InteropWithdraw>(index);

                    Runtime.TransferTokens(withdraw.feeSymbol, this.Address, transfer.sourceAddress, withdraw.feeAmount);

                    swapCount++;
                }
                else
                if (swapAddresses != null)
                {
                    foreach (var entry in swapAddresses)
                    {
                        if (transfer.destinationAddress == entry.LocalAddress)
                        {
                            Runtime.Expect(!transfer.sourceAddress.IsNull, "invalid source address");

                            Runtime.Expect(transfer.Value > 0, "amount must be positive and greater than zero");

                            Runtime.Expect(Runtime.TokenExists(transfer.Symbol), "invalid token");
                            var token = this.Runtime.GetToken(transfer.Symbol);

                            Runtime.Expect(token.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible");
                            Runtime.Expect(token.Flags.HasFlag(TokenFlags.Transferable), "token must be transferable");
                            Runtime.Expect(token.Flags.HasFlag(TokenFlags.External), "token must be external");

                            Runtime.Expect(transfer.interopAddress.IsUser, "invalid destination address");

                            // TODO support NFT
                            Runtime.SwapTokens(platform, transfer.sourceAddress, Runtime.Chain.Name, transfer.interopAddress, transfer.Symbol, transfer.Value, null, null);
                            //Runtime.Notify(EventKind.TokenSwap, destination, new TokenEventData(token.Symbol, amount, Runtime.Chain.Name));

                            swapCount++;
                            break;
                        }
                    }
                }
            }

            Runtime.Expect(swapCount > 0, "nothing to settle");
            chainHashes.Set <Hash, Hash>(hash, Runtime.Transaction.Hash);
            Runtime.Notify(EventKind.ChainSwap, from, new TransactionSettleEventData(hash, platform, chain));
        }
        public override void Fill(float contentPos)
        {
            float     psDiff        = contentPos + headOffset;
            float     peDiff        = psDiff + currentContentSize - paddedSize;
            float     psEntryOffset = entry[0].info.size;
            float     peEntryOffset = entry[entry.Count - 1].info.size;
            int       totalCount    = indexOffset + currentCount;
            bool      sizeChanged   = false;
            Entry <T> e             = null;

            //Debug.Log($"posOffset {contentPosOffset} psDiff {psDiff} ps {psEntryOffset}");
            if (totalCount == targetCount && contentPos < 0)
            {
                //nothing
            }
            else if (psDiff < 0 && -psDiff >= psEntryOffset + recyclePadding)
            {
                //Debug.Log($"recycle head psDiff {psDiff} ps {psEntryOffset}");
                //recycle head
                indexOffset  += elementPerLine;
                currentCount -= elementPerLine;
                for (int r = 0; r < elementPerLine; ++r)
                {
                    SwitchT(entry[r].item, false);
                }
                entry.RemoveAt(0, elementPerLine);
                headOffset         += psEntryOffset;
                currentContentSize -= psEntryOffset;
            }
            else if (psDiff > 0 && indexOffset > 0)
            {
                //Debug.Log($"add head psDiff {psDiff} ps {psEntryOffset}");
                //add head
                indexOffset  -= elementPerLine;
                currentCount += elementPerLine;
                entry.AddTo(0, elementPerLine);
                for (int r = 0; r < elementPerLine; ++r)
                {
                    e      = ActivateEntry(entry[r]);
                    e.info = FillT(indexOffset + r, e.item);
                    e.transform.anchoredPosition = Repos(r, -e.info.size - currentContentSize);
                    e.transform.name             = (indexOffset + r).ToString();
                }
                headOffset         -= e.info.size;
                currentContentSize += e.info.size;
            }
            if (indexOffset == 0 && contentPos > 0)
            {
                //nothing
            }
            else if (peDiff > 0 && peDiff >= peEntryOffset + recyclePadding)
            {
                //Debug.Log($"recycle tail peDiff {peDiff} pe {peEntryOffset}");
                sizeChanged = true;
                //recycle tail
                for (int r = 0; r < tailCount; ++r)
                {
                    SwitchT((e = entry.RemoveAt(entry.Count - 1)).item, false);
                }
                //Debug.Log($"recycled {tailCount}");
                currentCount       -= tailCount;
                tailCount           = elementPerLine;
                currentContentSize -= peEntryOffset;
            }
            else if (peDiff < 0 && totalCount < targetCount)
            {
                //Debug.Log($"add tail peDiff {peDiff} pe {peEntryOffset}");
                sizeChanged = true;
                //add tail
                tailCount = Mathf.Min(targetCount - totalCount, elementPerLine);
                for (int r = 0; r < tailCount; ++r)
                {
                    e      = ActivateEntry(entry.Add());
                    e.info = FillT(totalCount + r, e.item);
                    e.transform.anchoredPosition = Repos(r, 0);
                    e.transform.name             = (totalCount + r).ToString();
                }
                //Debug.Log($"added {tailCount} {totalCount} {targetCount} {e.offset}");
                currentCount       += tailCount;
                currentContentSize += e.info.size;
            }
            if (sizeChanged)
            {
                RefreshContentSize();
            }
        }