private void SpendGasV1(Address from) { var availableAmount = _allowanceMap.Get <Address, BigInteger>(from); var spentGas = Runtime.UsedGas; var requiredAmount = spentGas * Runtime.GasPrice; Runtime.Expect(requiredAmount > 0, "gas fee must exist"); Runtime.Expect(availableAmount >= requiredAmount, "gas allowance is not enough"); var targetAddress = _allowanceTargets.Get <Address, Address>(from); BigInteger targetGas; Runtime.Notify(EventKind.GasPayment, from, new GasEventData(targetAddress, Runtime.GasPrice, spentGas)); // return escrowed gas to transaction creator Runtime.TransferTokens(DomainSettings.FuelTokenSymbol, this.Address, from, availableAmount); Runtime.Expect(spentGas > 1, "gas spent too low"); var burnGas = spentGas / 2; if (burnGas > 0) { Runtime.BurnTokens(DomainSettings.FuelTokenSymbol, from, burnGas); spentGas -= burnGas; } targetGas = spentGas / 2; // 50% for dapps (or reward accum if dapp not specified) if (targetGas > 0) { var targetPayment = targetGas * Runtime.GasPrice; if (targetAddress == Runtime.Chain.Address) { _rewardAccum += targetPayment; Runtime.TransferTokens(DomainSettings.FuelTokenSymbol, from, this.Address, targetPayment); Runtime.Notify(EventKind.CrownRewards, from, new TokenEventData(DomainSettings.FuelTokenSymbol, targetPayment, Runtime.Chain.Name)); } else { Runtime.TransferTokens(DomainSettings.FuelTokenSymbol, from, targetAddress, targetPayment); } spentGas -= targetGas; } if (spentGas > 0) { var validatorPayment = spentGas * Runtime.GasPrice; var validatorAddress = SmartContract.GetAddressForNative(NativeContractKind.Block); Runtime.TransferTokens(DomainSettings.FuelTokenSymbol, from, validatorAddress, validatorPayment); spentGas = 0; } _allowanceMap.Remove(from); _allowanceTargets.Remove(from); CheckInflation(); }
public void SwapTokens(Address from, string fromSymbol, string toSymbol, BigInteger amount) { Runtime.Expect(Runtime.IsWitness(from), "invalid witness"); Runtime.Expect(amount > 0, "invalid amount"); var fromInfo = Runtime.GetToken(fromSymbol); Runtime.Expect(IsSupportedToken(fromSymbol), "source token is unsupported"); var fromBalance = Runtime.GetBalance(fromSymbol, from); Runtime.Expect(fromBalance > 0, $"not enough {fromSymbol} balance"); var toInfo = Runtime.GetToken(toSymbol); Runtime.Expect(IsSupportedToken(toSymbol), "destination token is unsupported"); var total = GetRate(fromSymbol, toSymbol, amount); Runtime.Expect(total > 0, "amount to swap needs to be larger than zero"); var toPotBalance = GetAvailableForSymbol(toSymbol); if (toPotBalance < total && toSymbol == DomainSettings.FuelTokenSymbol) { var gasAddress = SmartContract.GetAddressForNative(NativeContractKind.Gas); var gasBalance = Runtime.GetBalance(toSymbol, gasAddress); if (gasBalance >= total) { Runtime.TransferTokens(toSymbol, gasAddress, this.Address, total); toPotBalance = total; } } var toSymbolDecimalsInfo = Runtime.GetToken(toSymbol); var toSymbolDecimals = Math.Pow(10, toSymbolDecimalsInfo.Decimals); var fromSymbolDecimalsInfo = Runtime.GetToken(fromSymbol); var fromSymbolDecimals = Math.Pow(10, fromSymbolDecimalsInfo.Decimals); Runtime.Expect(toPotBalance >= total, $"insufficient balance in pot, have {(double)toPotBalance/toSymbolDecimals} {toSymbol} in pot, need {(double)total/toSymbolDecimals} {toSymbol}, have {(double)fromBalance/fromSymbolDecimals} {fromSymbol} to convert from"); var half = toPotBalance / 2; Runtime.Expect(total < half, $"taking too much {toSymbol} from pot at once"); Runtime.TransferTokens(fromSymbol, from, this.Address, amount); Runtime.TransferTokens(toSymbol, this.Address, from, total); }
public void ApplyInflation(Address from) { Runtime.Expect(_inflationReady, "inflation not ready"); Runtime.Expect(Runtime.IsRootChain(), "only on root chain"); var currentSupply = Runtime.GetTokenSupply(DomainSettings.StakingTokenSymbol); var minExpectedSupply = UnitConversion.ToBigInteger(100000000, DomainSettings.StakingTokenDecimals); if (currentSupply < minExpectedSupply) { currentSupply = minExpectedSupply; } // NOTE this gives an approximate inflation of 3% per year (0.75% per season) var inflationAmount = currentSupply / 133; BigInteger mintedAmount = 0; Runtime.Expect(inflationAmount > 0, "invalid inflation amount"); var masterOrg = Runtime.GetOrganization(DomainSettings.MastersOrganizationName); var masters = masterOrg.GetMembers(); var rewardList = new List <Address>(); foreach (var addr in masters) { var masterDate = Runtime.CallNativeContext(NativeContractKind.Stake, nameof(StakeContract.GetMasterDate), addr).AsTimestamp(); if (masterDate <= _lastInflationDate) { rewardList.Add(addr); } } if (rewardList.Count > 0) { var rewardAmount = inflationAmount / 10; var rewardStake = rewardAmount / rewardList.Count; rewardAmount = rewardList.Count * rewardStake; // eliminate leftovers var rewardFuel = _rewardAccum / rewardList.Count; BigInteger stakeAmount; if (Runtime.ProtocolVersion > 5) { stakeAmount = UnitConversion.ToBigInteger(2, DomainSettings.StakingTokenDecimals); } else { stakeAmount = UnitConversion.ToBigInteger(1, DomainSettings.StakingTokenDecimals); } Runtime.MintTokens(DomainSettings.StakingTokenSymbol, this.Address, this.Address, rewardAmount); var crownAddress = TokenUtils.GetContractAddress(DomainSettings.RewardTokenSymbol); Runtime.MintTokens(DomainSettings.StakingTokenSymbol, this.Address, crownAddress, stakeAmount); Runtime.CallNativeContext(NativeContractKind.Stake, nameof(StakeContract.Stake), crownAddress, stakeAmount); foreach (var addr in rewardList) { var reward = new StakeReward(addr, Runtime.Time); /*var temp = VMObject.FromObject(reward); * temp = VMObject.CastTo(temp, VMType.Struct); * * var rom = temp.Serialize();*/ var rom = Serialization.Serialize(reward); var tokenID = Runtime.MintToken(DomainSettings.RewardTokenSymbol, this.Address, this.Address, rom, new byte[0], 0); Runtime.InfuseToken(DomainSettings.RewardTokenSymbol, this.Address, tokenID, DomainSettings.FuelTokenSymbol, rewardFuel); Runtime.InfuseToken(DomainSettings.RewardTokenSymbol, this.Address, tokenID, DomainSettings.StakingTokenSymbol, rewardStake); Runtime.TransferToken(DomainSettings.RewardTokenSymbol, this.Address, addr, tokenID); } _rewardAccum -= rewardList.Count * rewardFuel; Runtime.Expect(_rewardAccum >= 0, "invalid reward leftover"); inflationAmount -= rewardAmount; inflationAmount -= stakeAmount; } var refillAmount = inflationAmount / 50; var cosmicAddress = SmartContract.GetAddressForNative(NativeContractKind.Swap); Runtime.MintTokens(DomainSettings.StakingTokenSymbol, this.Address, cosmicAddress, refillAmount); inflationAmount -= refillAmount; var phantomOrg = Runtime.GetOrganization(DomainSettings.PhantomForceOrganizationName); if (phantomOrg != null) { var phantomFunding = inflationAmount / 3; Runtime.MintTokens(DomainSettings.StakingTokenSymbol, this.Address, phantomOrg.Address, phantomFunding); inflationAmount -= phantomFunding; if (phantomOrg.Size == 1) { Runtime.CallNativeContext(NativeContractKind.Stake, nameof(StakeContract.Stake), phantomOrg.Address, phantomFunding); } } var bpOrg = Runtime.GetOrganization(DomainSettings.ValidatorsOrganizationName); if (bpOrg != null) { Runtime.MintTokens(DomainSettings.StakingTokenSymbol, this.Address, bpOrg.Address, inflationAmount); if (bpOrg.Size == 1) { Runtime.CallNativeContext(NativeContractKind.Stake, nameof(StakeContract.Stake), bpOrg.Address, inflationAmount); } } Runtime.Notify(EventKind.Inflation, from, new TokenEventData(DomainSettings.StakingTokenSymbol, mintedAmount, Runtime.Chain.Name)); _lastInflationDate = Runtime.Time; _inflationReady = false; }
public void EditAuction(Address from, string baseSymbol, string quoteSymbol, BigInteger tokenID, BigInteger price, BigInteger endPrice, Timestamp startDate, Timestamp endDate, BigInteger extensionPeriod) { Runtime.Expect(Runtime.IsWitness(from), "invalid witness"); Runtime.Expect(Runtime.TokenExists(quoteSymbol), "invalid quote token"); var quoteToken = Runtime.GetToken(quoteSymbol); Runtime.Expect(quoteToken.Flags.HasFlag(TokenFlags.Fungible), "quote token must be fungible"); var nft = Runtime.ReadToken(baseSymbol, tokenID); Runtime.Expect(nft.CurrentChain == Runtime.Chain.Name, "token not currently in this chain"); var marketAddress = SmartContract.GetAddressForNative(NativeContractKind.Market); Runtime.Expect(nft.CurrentOwner == marketAddress, "invalid owner"); var auctionID = baseSymbol + "." + tokenID; Runtime.Expect(_auctionMap.ContainsKey <string>(auctionID), "invalid auction"); var auction = _auctionMap.Get <string, MarketAuction>(auctionID); Runtime.Expect(auction.Creator == from, "invalid auction creator"); if (auction.Type != TypeAuction.Fixed) // prevent edit already started auctions { Runtime.Expect(auction.StartDate > Runtime.Time, "EditAuction can only be used before listing start"); } if (price == 0) { price = auction.Price; } if (endPrice == 0) { endPrice = auction.EndPrice; } if (startDate == 0 || startDate == null) { startDate = auction.StartDate; } if (endDate == 0 || endDate == null) { endDate = auction.EndDate; } Runtime.Expect(endDate > startDate, "invalid end date"); if (extensionPeriod == 0 || auction.Type == TypeAuction.Fixed) { extensionPeriod = auction.ExtensionPeriod; } if (auction.Type == TypeAuction.Classic || auction.Type == TypeAuction.Reserve) { Runtime.Expect(extensionPeriod <= oneHour, "extensionPeriod must be <= 1 hour"); } var auctionNew = new MarketAuction(from, startDate, endDate, baseSymbol, quoteSymbol, tokenID, price, endPrice, extensionPeriod, auction.Type, auction.ListingFee, auction.ListingFeeAddress, auction.BuyingFee, auction.BuyingFeeAddress, auction.CurrentBidWinner); _auctionMap.Set(auctionID, auctionNew); Runtime.Notify(EventKind.OrderCancelled, auctionNew.Creator, new MarketEventData() { ID = auction.TokenID, BaseSymbol = auction.BaseSymbol, QuoteSymbol = auction.QuoteSymbol, Price = auction.Price, EndPrice = auction.EndPrice, Type = auction.Type }); Runtime.Notify(EventKind.OrderCreated, auctionNew.Creator, new MarketEventData() { ID = auctionNew.TokenID, BaseSymbol = auctionNew.BaseSymbol, QuoteSymbol = auctionNew.QuoteSymbol, Price = auctionNew.Price, EndPrice = auctionNew.EndPrice, Type = auctionNew.Type }); }