Exemplo n.º 1
0
        public void SwapToken(Address buyer, Address seller, string baseSymbol, string quoteSymbol, BigInteger tokenID, BigInteger price, byte[] signature)
        {
            Runtime.Expect(IsWitness(buyer), "invalid witness");
            Runtime.Expect(seller != buyer, "invalid seller");

            Runtime.Expect(Runtime.Nexus.TokenExists(baseSymbol), "invalid base token");
            var baseToken = Runtime.Nexus.GetTokenInfo(baseSymbol);

            Runtime.Expect(!baseToken.Flags.HasFlag(TokenFlags.Fungible), "token must be non-fungible");

            var ownerships = new OwnershipSheet(baseSymbol);
            var owner      = ownerships.GetOwner(this.Storage, tokenID);

            Runtime.Expect(owner == seller, "invalid owner");

            var swap = new TokenSwap()
            {
                baseSymbol  = baseSymbol,
                quoteSymbol = quoteSymbol,
                buyer       = buyer,
                seller      = seller,
                price       = price,
                value       = tokenID,
            };

            var msg = Serialization.Serialize(swap);

            Runtime.Expect(Ed25519.Verify(signature, msg, seller.PublicKey), "invalid signature");

            Runtime.Expect(Runtime.Nexus.TokenExists(quoteSymbol), "invalid quote token");
            var quoteToken = Runtime.Nexus.GetTokenInfo(quoteSymbol);

            Runtime.Expect(quoteToken.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible");

            var balances = new BalanceSheet(quoteSymbol);
            var balance  = balances.Get(this.Storage, buyer);

            Runtime.Expect(balance >= price, "invalid balance");

            Runtime.Expect(Runtime.Nexus.TransferTokens(quoteSymbol, this.Storage, Runtime.Chain, buyer, owner, price), "payment failed");
            Runtime.Expect(Runtime.Nexus.TransferToken(baseSymbol, this.Storage, Runtime.Chain, owner, buyer, tokenID), "transfer failed");

            Runtime.Notify(EventKind.TokenSend, seller, new TokenEventData()
            {
                chainAddress = Runtime.Chain.Address, symbol = baseSymbol, value = tokenID
            });
            Runtime.Notify(EventKind.TokenSend, buyer, new TokenEventData()
            {
                chainAddress = Runtime.Chain.Address, symbol = quoteSymbol, value = price
            });

            Runtime.Notify(EventKind.TokenReceive, seller, new TokenEventData()
            {
                chainAddress = Runtime.Chain.Address, symbol = quoteSymbol, value = price
            });
            Runtime.Notify(EventKind.TokenReceive, buyer, new TokenEventData()
            {
                chainAddress = Runtime.Chain.Address, symbol = baseSymbol, value = tokenID
            });
        }
Exemplo n.º 2
0
        public void Unstake(Address address)
        {
            Runtime.Expect(IsValidator(address), "validator failed");
            Runtime.Expect(IsWitness(address), "witness failed");

            var entry = _entryMap.Get <Address, ValidatorInfo>(address);

            var diff = Timestamp.Now - entry.timestamp;
            var days = diff / 86400; // convert seconds to days

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

            var stakeAmount = entry.stake;
            var token       = Runtime.Nexus.GetTokenInfo(Nexus.StakingTokenSymbol);
            var balances    = new BalanceSheet(token.Symbol);
            var balance     = balances.Get(this.Storage, Runtime.Chain.Address);

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

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

            _entryMap.Remove(address);
            _entryList.Remove(address);

            Runtime.Notify(EventKind.TokenUnstake, address, new TokenEventData()
            {
                chainAddress = Runtime.Chain.Address, symbol = token.Symbol, value = stakeAmount
            });
        }
Exemplo n.º 3
0
        public uint PutPrivate(Address from, string symbol)
        {
            Runtime.Expect(IsWitness(from), "invalid witness");

            Runtime.Expect(this.Runtime.Nexus.TokenExists(symbol), "invalid token");

            var tokenInfo = this.Runtime.Nexus.GetTokenInfo(symbol);

            Runtime.Expect(tokenInfo.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible");

            var balances = new BalanceSheet(symbol);
            var balance  = balances.Get(this.Storage, from);

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

            var queue = FetchQueue(symbol);

            Runtime.Expect(queue.addresses.Count() < queue.size, "queue full");

            var addresses = queue.addresses.All <Address>();

            foreach (var address in addresses)
            {
                Runtime.Expect(address != from, "address already in queue");
            }

            // TODO it is wrong to do this in current codebase...
            balances.Subtract(this.Storage, from, TransferAmount);
            queue.addresses.Add(from);

            return(queue.ID); // TODO should be quueue ID
        }
Exemplo n.º 4
0
        public void Stake(Address from, BigInteger stakeAmount)
        {
            Runtime.Expect(stakeAmount >= MinimumValidStake, "invalid amount");
            Runtime.Expect(IsWitness(from), "witness failed");

            var stakeBalances = new BalanceSheet(Nexus.StakingTokenSymbol);
            var balance       = stakeBalances.Get(this.Storage, from);

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

            var newStake = stakeAmount + currentStake.totalAmount;

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

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

            var entry = new EnergyAction()
            {
                unclaimedPartials = stakeAmount + GetLastAction(from).unclaimedPartials,
                totalAmount       = newStake,
                timestamp         = this.Runtime.Time,
            };

            _stakes.Set(from, entry);

            var logEntry = new VotingLogEntry()
            {
                timestamp = this.Runtime.Time,
                amount    = stakeAmount
            };

            var votingLogbook = _voteHistory.Get <Address, StorageList>(from);

            votingLogbook.Add(logEntry);

            var masterAccountThreshold = GetMasterThreshold();

            if (Runtime.Nexus.GenesisAddress != from && newStake >= masterAccountThreshold && !IsMaster(from))
            {
                var nextClaim = GetMasterClaimDate(2);

                _mastersList.Add(new EnergyMaster()
                {
                    address = from, claimDate = nextClaim
                });
                Runtime.Notify(EventKind.RolePromote, from, new RoleEventData()
                {
                    role = "master", date = nextClaim
                });
            }

            Runtime.Notify(EventKind.TokenStake, from, new TokenEventData()
            {
                chainAddress = Runtime.Chain.Address, symbol = Nexus.StakingTokenSymbol, value = stakeAmount
            });
        }
Exemplo n.º 5
0
        public BigInteger GetBalance(Address address, string symbol)
        {
            Runtime.Expect(this.Runtime.Nexus.TokenExists(symbol), "invalid token");
            var token = this.Runtime.Nexus.GetTokenInfo(symbol);

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

            var balances = new BalanceSheet(symbol);

            return(balances.Get(this.Storage, address));
        }
Exemplo n.º 6
0
        public void OpenOrder(Address from, string baseSymbol, string quoteSymbol, BigInteger quantity, BigInteger rate, ExchangeOrderSide side)
        {
            Runtime.Expect(IsWitness(from), "invalid witness");

            Runtime.Expect(Runtime.Nexus.TokenExists(baseSymbol), "invalid base token");
            var baseToken = Runtime.Nexus.GetTokenInfo(baseSymbol);

            Runtime.Expect(baseToken.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible");

            Runtime.Expect(Runtime.Nexus.TokenExists(quoteSymbol), "invalid quote token");
            var quoteToken = Runtime.Nexus.GetTokenInfo(quoteSymbol);

            Runtime.Expect(quoteToken.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible");

            //var tokenABI = Chain.FindABI(NativeABI.Token);
            //Runtime.Expect(baseTokenContract.ABI.Implements(tokenABI));

            var pair = baseSymbol + "_" + quoteSymbol;

            switch (side)
            {
            case ExchangeOrderSide.Sell:
            {
                var balances = new BalanceSheet(baseSymbol);
                var balance  = balances.Get(this.Storage, from);
                Runtime.Expect(balance >= quantity, "not enought balance");

                Runtime.Expect(Runtime.Nexus.TransferTokens(baseSymbol, this.Storage, Runtime.Chain, from, Runtime.Chain.Address, quantity), "transfer failed");

                break;
            }

            case ExchangeOrderSide.Buy:
            {
                var balances = new BalanceSheet(quoteSymbol);
                var balance  = balances.Get(this.Storage, from);

                var expectedAmount = quantity / rate;
                Runtime.Expect(balance >= expectedAmount, "not enought balance");

                // TODO check this
                Runtime.Expect(Runtime.Nexus.TransferTokens(quoteSymbol, this.Storage, Runtime.Chain, from, Runtime.Chain.Address, expectedAmount), "transfer failed");
                break;
            }

            default: throw new ContractException("invalid order side");
            }

            var order = new ExchangeOrder(Runtime.Time, from, quantity, rate, side);
            var list  = _orders.Get <string, StorageList>(pair);

            list.Add(order);
        }
Exemplo n.º 7
0
        public void BuyToken(Address from, string symbol, BigInteger tokenID)
        {
            Runtime.Expect(IsWitness(from), "invalid witness");

            var auctionID = symbol + "." + tokenID;

            Runtime.Expect(_auctionMap.ContainsKey <string>(auctionID), "invalid auction");
            var auction = _auctionMap.Get <string, MarketAuction>(auctionID);

            Runtime.Expect(Runtime.Nexus.TokenExists(auction.BaseSymbol), "invalid base token");
            var baseToken = Runtime.Nexus.GetTokenInfo(auction.BaseSymbol);

            Runtime.Expect(!baseToken.Flags.HasFlag(TokenFlags.Fungible), "token must be non-fungible");

            var ownerships = new OwnershipSheet(baseToken.Symbol);
            var owner      = ownerships.GetOwner(this.Storage, auction.TokenID);

            Runtime.Expect(owner == Runtime.Chain.Address, "invalid owner");

            if (auction.Creator != from)
            {
                Runtime.Expect(Runtime.Nexus.TokenExists(auction.QuoteSymbol), "invalid quote token");
                var quoteToken = Runtime.Nexus.GetTokenInfo(auction.QuoteSymbol);
                Runtime.Expect(quoteToken.Flags.HasFlag(TokenFlags.Fungible), "quote token must be fungible");

                var balances = new BalanceSheet(quoteToken.Symbol);
                var balance  = balances.Get(this.Storage, from);
                Runtime.Expect(balance >= auction.Price, "not enough balance");

                Runtime.Expect(Runtime.Nexus.TransferTokens(quoteToken.Symbol, this.Storage, Runtime.Chain, from, auction.Creator, auction.Price), "payment failed");
            }

            Runtime.Expect(Runtime.Nexus.TransferToken(baseToken.Symbol, this.Storage, Runtime.Chain, Runtime.Chain.Address, from, auction.TokenID), "transfer failed");

            _auctionMap.Remove <string>(auctionID);
            _auctionIDs.Remove(auctionID);

            if (auction.Creator == from)
            {
                Runtime.Notify(EventKind.AuctionCancelled, from, new MarketEventData()
                {
                    ID = auction.TokenID, BaseSymbol = auction.BaseSymbol, QuoteSymbol = auction.QuoteSymbol, Price = 0
                });
            }
            else
            {
                Runtime.Notify(EventKind.AuctionFilled, from, new MarketEventData()
                {
                    ID = auction.TokenID, BaseSymbol = auction.BaseSymbol, QuoteSymbol = auction.QuoteSymbol, Price = auction.Price
                });
            }
        }
Exemplo n.º 8
0
 // NOTE should never be used directly from a contract, instead use Runtime.GetBalance!
 public BigInteger GetTokenBalance(StorageContext storage, IToken token, Address address)
 {
     if (token.Flags.HasFlag(TokenFlags.Fungible))
     {
         var balances = new BalanceSheet(token.Symbol);
         return(balances.Get(storage, address));
     }
     else
     {
         var ownerships = new OwnershipSheet(token.Symbol);
         var items      = ownerships.Get(storage, address);
         return(items.Length);
     }
 }
Exemplo n.º 9
0
        // NOTE should never be used directly from a contract, instead use Runtime.GetBalance!
        public BigInteger GetTokenBalance(string tokenSymbol, Address address)
        {
            var tokenInfo = Nexus.GetTokenInfo(tokenSymbol);

            if (tokenInfo.Flags.HasFlag(TokenFlags.Fungible))
            {
                var balances = new BalanceSheet(tokenSymbol);
                return(balances.Get(Storage, address));
            }
            else
            {
                var ownerships = new OwnershipSheet(tokenSymbol);
                var items      = ownerships.Get(this.Storage, address);
                return(items.Length);
            }
        }
Exemplo n.º 10
0
        internal StorageMap _allowanceTargets; //<Address, Address>

        public void AllowGas(Address from, Address to, BigInteger price, BigInteger limit)
        {
            if (Runtime.readOnlyMode)
            {
                return;
            }

            Runtime.Expect(IsWitness(from), "invalid witness");
            Runtime.Expect(from != to, "invalid gas target");
            Runtime.Expect(Runtime.Chain.Address != to, "invalid gas target");

            Runtime.Expect(price > 0, "price must be positive amount");
            Runtime.Expect(limit > 0, "limit must be positive amount");

            //var token = this.Runtime.Nexus.FuelToken;
            //Runtime.Expect(token != null, "invalid token");
            //Runtime.Expect(token.Flags.HasFlag(TokenFlags.Fungible), "must be fungible token");

            var balances  = new BalanceSheet(Nexus.FuelTokenSymbol);
            var maxAmount = price * limit;

            var balance = balances.Get(this.Storage, from);

            Runtime.Expect(balance >= maxAmount, "not enough gas in address");

            Runtime.Expect(balances.Subtract(this.Storage, from, maxAmount), "gas escrow withdraw failed");
            Runtime.Expect(balances.Add(this.Storage, Runtime.Chain.Address, maxAmount), "gas escrow deposit failed");

            var allowance = _allowanceMap.ContainsKey(from) ? _allowanceMap.Get <Address, BigInteger>(from) : 0;

            Runtime.Expect(allowance == 0, "unexpected pending allowance");

            allowance += maxAmount;
            _allowanceMap.Set(from, allowance);
            _allowanceTargets.Set(from, to);

            Runtime.Notify(EventKind.GasEscrow, from, new GasEventData()
            {
                address = Runtime.Chain.Address, price = price, amount = limit
            });
        }
Exemplo n.º 11
0
        public void Stake(Address address)
        {
            Runtime.Expect(IsWitness(address), "witness failed");

            var count = _entryList.Count();
            var max   = GetMaxValidators();

            Runtime.Expect(count < max, "no open validators spots");

            var stakeAmount = GetRequiredStake();

            var token    = Runtime.Nexus.GetTokenInfo(Nexus.StakingTokenSymbol);
            var balances = new BalanceSheet(token.Symbol);
            var balance  = balances.Get(this.Storage, address);

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

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

            _entryList.Add(address);

            var entry = new ValidatorInfo()
            {
                address   = address,
                stake     = stakeAmount,
                timestamp = Runtime.Time,
                slashes   = 0
            };

            _entryMap.Set(address, entry);

            Runtime.Notify(EventKind.TokenStake, address, new TokenEventData()
            {
                chainAddress = Runtime.Chain.Address, symbol = token.Symbol, value = stakeAmount
            });
        }
Exemplo n.º 12
0
        private BigInteger GetAvailableForSymbol(string symbol)
        {
            var balances = new BalanceSheet(symbol);

            return(balances.Get(this.Storage, this.Address));
        }
Exemplo n.º 13
0
        public void CloseBlock(Block block, StorageChangeSetContext storage)
        {
            var rootStorage = this.IsRoot ? storage : Nexus.RootStorage;

            if (block.Height > 1)
            {
                var prevBlock = GetBlockByHash(block.PreviousHash);

                if (prevBlock.Validator != block.Validator)
                {
                    block.Notify(new Event(EventKind.ValidatorSwitch, block.Validator, "block", Serialization.Serialize(prevBlock)));
                }
            }

            var balance        = new BalanceSheet(DomainSettings.FuelTokenSymbol);
            var blockAddress   = Address.FromHash("block");
            var totalAvailable = balance.Get(storage, blockAddress);

            var targets = new List <Address>();

            if (Nexus.HasGenesis)
            {
                var validators = Nexus.GetValidators();

                var totalValidators = Nexus.GetPrimaryValidatorCount();

                for (int i = 0; i < totalValidators; i++)
                {
                    var validator = validators[i];
                    if (validator.type != ValidatorType.Primary)
                    {
                        continue;
                    }

                    targets.Add(validator.address);
                }
            }
            else
            if (totalAvailable > 0)
            {
                targets.Add(Nexus.GetGenesisAddress(rootStorage));
            }

            if (targets.Count > 0)
            {
                if (!balance.Subtract(storage, blockAddress, totalAvailable))
                {
                    throw new BlockGenerationException("could not subtract balance from block address");
                }

                var amountPerValidator = totalAvailable / targets.Count;
                var leftOvers          = totalAvailable - (amountPerValidator * targets.Count);

                foreach (var address in targets)
                {
                    BigInteger amount = amountPerValidator;

                    if (address == block.Validator)
                    {
                        amount += leftOvers;
                    }

                    // TODO this should use triggers when available...
                    if (!balance.Add(storage, address, amount))
                    {
                        throw new BlockGenerationException($"could not add balance to {address}");
                    }

                    var eventData = Serialization.Serialize(new TokenEventData(DomainSettings.FuelTokenSymbol, amount, this.Name));
                    block.Notify(new Event(EventKind.TokenClaim, address, "block", eventData));
                }
            }
        }
Exemplo n.º 14
0
        public void SwapTokens(Address buyer, Address seller, string baseSymbol, string quoteSymbol, BigInteger amount, BigInteger price, byte[] signature)
        {
            Runtime.Expect(IsWitness(buyer), "invalid witness");
            Runtime.Expect(seller != buyer, "invalid seller");

            Runtime.Expect(seller.IsUser, "seller must be user address");

            Runtime.Expect(Runtime.Nexus.TokenExists(baseSymbol), "invalid base token");
            var baseToken = Runtime.Nexus.GetTokenInfo(baseSymbol);

            Runtime.Expect(baseToken.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible");

            var baseBalances = new BalanceSheet(baseSymbol);
            var baseBalance  = baseBalances.Get(this.Storage, seller);

            Runtime.Expect(baseBalance >= amount, "invalid amount");

            var swap = new TokenSwap()
            {
                baseSymbol  = baseSymbol,
                quoteSymbol = quoteSymbol,
                buyer       = buyer,
                seller      = seller,
                price       = price,
                value       = amount,
            };

            var msg = Serialization.Serialize(swap);

            Runtime.Expect(Ed25519.Verify(signature, msg, seller.PublicKey), "invalid signature");

            Runtime.Expect(Runtime.Nexus.TokenExists(quoteSymbol), "invalid quote token");
            var quoteToken = Runtime.Nexus.GetTokenInfo(quoteSymbol);

            Runtime.Expect(quoteToken.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible");

            var quoteBalances = new BalanceSheet(quoteSymbol);
            var quoteBalance  = quoteBalances.Get(this.Storage, buyer);

            Runtime.Expect(quoteBalance >= price, "invalid balance");

            Runtime.Expect(Runtime.Nexus.TransferTokens(Runtime, quoteSymbol, buyer, seller, price), "payment failed");
            Runtime.Expect(Runtime.Nexus.TransferTokens(Runtime, baseSymbol, seller, buyer, amount), "transfer failed");

            Runtime.Notify(EventKind.TokenSend, seller, new TokenEventData()
            {
                chainAddress = this.Address, symbol = baseSymbol, value = amount
            });
            Runtime.Notify(EventKind.TokenSend, buyer, new TokenEventData()
            {
                chainAddress = this.Address, symbol = quoteSymbol, value = price
            });

            Runtime.Notify(EventKind.TokenReceive, seller, new TokenEventData()
            {
                chainAddress = this.Address, symbol = quoteSymbol, value = price
            });
            Runtime.Notify(EventKind.TokenReceive, buyer, new TokenEventData()
            {
                chainAddress = this.Address, symbol = baseSymbol, value = amount
            });
        }
Exemplo n.º 15
0
        private void OpenOrder(Address from, Address provider, string baseSymbol, string quoteSymbol, ExchangeOrderSide side, ExchangeOrderType orderType, BigInteger orderSize, BigInteger price)
        {
            Runtime.Expect(IsWitness(from), "invalid witness");

            Runtime.Expect(Runtime.GasTarget == provider, "invalid gas target");

            Runtime.Expect(baseSymbol != quoteSymbol, "invalid base/quote pair");

            Runtime.Expect(Runtime.Nexus.TokenExists(baseSymbol), "invalid base token");
            var baseToken = Runtime.Nexus.GetTokenInfo(baseSymbol);

            Runtime.Expect(baseToken.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible");

            Runtime.Expect(Runtime.Nexus.TokenExists(quoteSymbol), "invalid quote token");
            var quoteToken = Runtime.Nexus.GetTokenInfo(quoteSymbol);

            Runtime.Expect(quoteToken.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible");

            if (orderType != Market)
            {
                Runtime.Expect(orderSize >= GetMinimumTokenQuantity(baseToken), "order size is not sufficient");
                Runtime.Expect(price >= GetMinimumTokenQuantity(quoteToken), "order price is not sufficient");
            }

            var uid = Runtime.Chain.GenerateUID(this.Storage);

            //--------------
            //perform escrow for non-market orders
            string     orderEscrowSymbol = CalculateEscrowSymbol(baseToken, quoteToken, side);
            TokenInfo  orderEscrowToken  = orderEscrowSymbol == baseSymbol ? baseToken : quoteToken;
            BigInteger orderEscrowAmount;
            BigInteger orderEscrowUsage = 0;

            if (orderType == Market)
            {
                orderEscrowAmount = orderSize;
                Runtime.Expect(orderEscrowAmount >= GetMinimumTokenQuantity(orderEscrowToken), "market order size is not sufficient");
            }
            else
            {
                orderEscrowAmount = CalculateEscrowAmount(orderSize, price, baseToken, quoteToken, side);
            }

            //BigInteger baseTokensUnfilled = orderSize;

            var balances = new BalanceSheet(orderEscrowSymbol);
            var balance  = balances.Get(this.Storage, from);

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

            Runtime.Expect(Runtime.Nexus.TransferTokens(Runtime, orderEscrowSymbol, from, this.Address, orderEscrowAmount), "transfer failed");
            //------------

            var         thisOrder = new ExchangeOrder();
            StorageList orderList;
            BigInteger  orderIndex = 0;

            thisOrder = new ExchangeOrder(uid, Runtime.Time, from, provider, orderSize, baseSymbol, price, quoteSymbol, side, orderType);
            Runtime.Notify(EventKind.OrderCreated, from, uid);

            var key = BuildOrderKey(side, quoteSymbol, baseSymbol);

            orderList  = _orders.Get <string, StorageList>(key);
            orderIndex = orderList.Add <ExchangeOrder>(thisOrder);
            _orderMap.Set <BigInteger, string>(uid, key);

            var makerSide   = side == Buy ? Sell : Buy;
            var makerKey    = BuildOrderKey(makerSide, quoteSymbol, baseSymbol);
            var makerOrders = _orders.Get <string, StorageList>(makerKey);

            do
            {
                int        bestIndex          = -1;
                BigInteger bestPrice          = 0;
                Timestamp  bestPriceTimestamp = 0;

                ExchangeOrder takerOrder = thisOrder;

                var makerOrdersCount = makerOrders.Count();
                for (int i = 0; i < makerOrdersCount; i++)
                {
                    var makerOrder = makerOrders.Get <ExchangeOrder>(i);

                    if (side == Buy)
                    {
                        if (makerOrder.Price > takerOrder.Price && orderType != Market) // too expensive, we wont buy at this price
                        {
                            continue;
                        }

                        if (bestIndex == -1 || makerOrder.Price < bestPrice || (makerOrder.Price == bestPrice && makerOrder.Timestamp < bestPriceTimestamp))
                        {
                            bestIndex          = i;
                            bestPrice          = makerOrder.Price;
                            bestPriceTimestamp = makerOrder.Timestamp;
                        }
                    }
                    else
                    {
                        if (makerOrder.Price < takerOrder.Price && orderType != Market) // too cheap, we wont sell at this price
                        {
                            continue;
                        }

                        if (bestIndex == -1 || makerOrder.Price > bestPrice || (makerOrder.Price == bestPrice && makerOrder.Timestamp < bestPriceTimestamp))
                        {
                            bestIndex          = i;
                            bestPrice          = makerOrder.Price;
                            bestPriceTimestamp = makerOrder.Timestamp;
                        }
                    }
                }

                if (bestIndex >= 0)
                {
                    //since order "uid" has found a match, the creator of this order will be a taker as he will remove liquidity from the market
                    //and the creator of the "bestIndex" order is the maker as he is providing liquidity to the taker
                    var takerAvailableEscrow = orderEscrowAmount - orderEscrowUsage;
                    var takerEscrowUsage     = BigInteger.Zero;
                    var takerEscrowSymbol    = orderEscrowSymbol;

                    var makerOrder        = makerOrders.Get <ExchangeOrder>(bestIndex);
                    var makerEscrow       = _escrows.Get <BigInteger, BigInteger>(makerOrder.Uid);
                    var makerEscrowUsage  = BigInteger.Zero;;
                    var makerEscrowSymbol = orderEscrowSymbol == baseSymbol ? quoteSymbol : baseSymbol;

                    //Get fulfilled order size in base tokens
                    //and then calculate the corresponding fulfilled order size in quote tokens
                    if (takerEscrowSymbol == baseSymbol)
                    {
                        var makerEscrowBaseEquivalent = ConvertQuoteToBase(makerEscrow, makerOrder.Price, baseToken, quoteToken);
                        takerEscrowUsage = takerAvailableEscrow < makerEscrowBaseEquivalent ? takerAvailableEscrow : makerEscrowBaseEquivalent;

                        makerEscrowUsage = CalculateEscrowAmount(takerEscrowUsage, makerOrder.Price, baseToken, quoteToken, Buy);
                    }
                    else
                    {
                        var takerEscrowBaseEquivalent = ConvertQuoteToBase(takerAvailableEscrow, makerOrder.Price, baseToken, quoteToken);
                        makerEscrowUsage = makerEscrow < takerEscrowBaseEquivalent ? makerEscrow : takerEscrowBaseEquivalent;

                        takerEscrowUsage = CalculateEscrowAmount(makerEscrowUsage, makerOrder.Price, baseToken, quoteToken, Buy);
                    }

                    Runtime.Expect(takerEscrowUsage <= takerAvailableEscrow, "Taker tried to use more escrow than available");
                    Runtime.Expect(makerEscrowUsage <= makerEscrow, "Maker tried to use more escrow than available");

                    if (takerEscrowUsage < GetMinimumSymbolQuantity(takerEscrowSymbol) ||
                        makerEscrowUsage < GetMinimumSymbolQuantity(makerEscrowSymbol))
                    {
                        break;
                    }

                    Runtime.Nexus.TransferTokens(Runtime, takerEscrowSymbol, this.Address, makerOrder.Creator, takerEscrowUsage);
                    Runtime.Nexus.TransferTokens(Runtime, makerEscrowSymbol, this.Address, takerOrder.Creator, makerEscrowUsage);

                    Runtime.Notify(EventKind.TokenReceive, makerOrder.Creator, new TokenEventData()
                    {
                        chainAddress = this.Address, symbol = takerEscrowSymbol, value = takerEscrowUsage
                    });
                    Runtime.Notify(EventKind.TokenReceive, takerOrder.Creator, new TokenEventData()
                    {
                        chainAddress = this.Address, symbol = makerEscrowSymbol, value = makerEscrowUsage
                    });

                    orderEscrowUsage += takerEscrowUsage;

                    Runtime.Notify(EventKind.OrderFilled, takerOrder.Creator, takerOrder.Uid);
                    Runtime.Notify(EventKind.OrderFilled, makerOrder.Creator, makerOrder.Uid);

                    if (makerEscrowUsage == makerEscrow)
                    {
                        makerOrders.RemoveAt <ExchangeOrder>(bestIndex);
                        _orderMap.Remove(makerOrder.Uid);

                        Runtime.Expect(_escrows.ContainsKey(makerOrder.Uid), "An orderbook entry must have registered escrow");
                        _escrows.Remove(makerOrder.Uid);

                        Runtime.Notify(EventKind.OrderClosed, makerOrder.Creator, makerOrder.Uid);
                    }
                    else
                    {
                        _escrows.Set(makerOrder.Uid, makerEscrow - makerEscrowUsage);
                    }
                }
                else
                {
                    break;
                }
            } while (orderEscrowUsage < orderEscrowAmount);

            var leftoverEscrow = orderEscrowAmount - orderEscrowUsage;

            if (leftoverEscrow == 0 || orderType != Limit)
            {
                orderList.RemoveAt <ExchangeOrder>(orderIndex);
                _orderMap.Remove(thisOrder.Uid);
                _escrows.Remove(thisOrder.Uid);

                if (leftoverEscrow > 0)
                {
                    Runtime.Nexus.TransferTokens(Runtime, orderEscrowSymbol, this.Address, thisOrder.Creator, leftoverEscrow);
                    Runtime.Notify(EventKind.TokenReceive, thisOrder.Creator, new TokenEventData()
                    {
                        chainAddress = this.Address, symbol = orderEscrowSymbol, value = leftoverEscrow
                    });
                    Runtime.Notify(EventKind.OrderCancelled, thisOrder.Creator, thisOrder.Uid);
                }
                else
                {
                    Runtime.Notify(EventKind.OrderClosed, thisOrder.Creator, thisOrder.Uid);
                }
            }
            else
            {
                _escrows.Set(uid, leftoverEscrow);
            }

            //TODO: ADD FEES, SEND THEM TO this.Address FOR NOW
        }
Exemplo n.º 16
0
        public BigInteger Unstake(Address from, BigInteger unstakeAmount)
        {
            Runtime.Expect(IsWitness(from), "witness failed");
            Runtime.Expect(unstakeAmount >= MinimumValidStake, "invalid amount");

            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);
            }

            Runtime.Expect(Runtime.Time >= stake.timestamp, "Negative time diff");

            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 = new BalanceSheet(token.Symbol);
            var balance  = balances.Get(this.Storage, Runtime.Chain.Address);

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

            var availableStake = stake.totalAmount;

            availableStake -= GetStorageStake(from);
            Runtime.Expect(availableStake >= unstakeAmount, "tried to unstake more than what was staked");

            //if this is a partial unstake
            if (availableStake - unstakeAmount > 0)
            {
                Runtime.Expect(availableStake - unstakeAmount >= MinimumValidStake, "leftover stake would be below minimum staking amount");
            }

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

            stake.totalAmount -= unstakeAmount;

            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, unstakeAmount);
            }

            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.RoleDemote, from, new RoleEventData()
                    {
                        role = "master", date = penalizationDate
                    });
                }
            }

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

            return(unstakeAmount);
        }