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 }); }
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 }); }
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 }
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 }); }
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)); }
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); }
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 }); } }
// 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); } }
// 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); } }
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 }); }
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 }); }
private BigInteger GetAvailableForSymbol(string symbol) { var balances = new BalanceSheet(symbol); return(balances.Get(this.Storage, this.Address)); }
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)); } } }
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 }); }
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 }
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); }