public async Task SwapTokenAToLYRAsync() { try { await semaphore.WaitAsync(); var pool = await client.GetPoolAsync(testTokenA, LyraGlobal.OFFICIALTICKERCODE); Assert.IsNotNull(pool.PoolAccountId); var poolLatestBlock = pool.GetBlock() as TransactionBlock; Assert.IsTrue(poolLatestBlock.Balances[pool.Token0] > 0 && poolLatestBlock.Balances[pool.Token1] > 0, "No liquidate in pool."); var w1 = Restore(testPrivateKey); await w1.SyncAsync(client); var testTokenBalance = w1.GetLastSyncBlock().Balances[testTokenA].ToBalanceDecimal(); var lyrBalance = w1.GetLastSyncBlock().Balances[LyraGlobal.OFFICIALTICKERCODE].ToBalanceDecimal(); var amount = Math.Round((decimal)((new Random().NextDouble() + 0.07) * 1000), 8); var cal1 = new SwapCalculator(LyraGlobal.OFFICIALTICKERCODE, testTokenA, poolLatestBlock, testTokenA, amount, 0); var result = await w1.SwapTokenAsync(LyraGlobal.OFFICIALTICKERCODE, testTokenA, testTokenA, amount, cal1.MinimumReceived); Assert.IsTrue(result.ResultCode == APIResultCodes.Success, $"Failed to swap {testTokenA}: {result.ResultCode}"); await Task.Delay(5000); var poolGenesisResult = await client.GetBlockByIndexAsync(poolLatestBlock.AccountID, 1); var block = poolGenesisResult.GetBlock(); var poolGenesis = block as PoolGenesisBlock; Assert.IsTrue(poolGenesisResult.ResultCode == APIResultCodes.Success, $"get gensis returns {poolGenesisResult.ResultCode}"); Assert.IsNotNull(poolGenesis, "Can't get pool genesis block."); var sc = new SwapCalculator(poolGenesis.Token0, poolGenesis.Token1, poolLatestBlock, testTokenA, amount, 0); var amountToGet = sc.SwapOutAmount; await w1.SyncAsync(client); var testTokenBalance2 = w1.GetLastSyncBlock().Balances[testTokenA].ToBalanceDecimal(); var lyrBalance2 = w1.GetLastSyncBlock().Balances[LyraGlobal.OFFICIALTICKERCODE].ToBalanceDecimal(); Assert.AreEqual(testTokenBalance - amount, testTokenBalance2); Assert.AreEqual(lyrBalance - 1 + amountToGet, lyrBalance2); } finally { semaphore.Release(); } }
public async Task <SwapCalculator> PoolCalculateAsync(string poolId, string swapFrom, decimal amount, decimal slippage) { var poolLatest = await NodeService.Dag.Storage.FindLatestBlockAsync(poolId) as TransactionBlock; var poolGenesis = await NodeService.Dag.Storage.FindFirstBlockAsync(poolId) as PoolGenesisBlock; if (poolLatest != null && poolGenesis != null && poolLatest.Hash != poolGenesis.Hash) { var cal = new SwapCalculator(poolGenesis.Token0, poolGenesis.Token1, poolLatest, swapFrom, amount, slippage); return(cal); } throw new Exception("Failed to get pool"); }
public void Calculate() { var X = 10000m; // LYR var Y = 20000m; // dai var fakeBlock = new SendTransferBlock(); fakeBlock.Balances = new Dictionary <string, long>(); fakeBlock.Balances.Add("LYR", X.ToBalanceLong()); fakeBlock.Balances.Add("DAI", Y.ToBalanceLong()); var u = 1000m; // dai var pureTo = X - (X * Y / (Y + u * 0.997m)); var to = Math.Round(pureTo * 0.999m, 8); var chg = Math.Round(to / X, 6); var price = Math.Round(to / u, 10); Console.WriteLine($"Price {price} Got {to} X, Price Impact: {chg * 100:n} %"); var cal1 = new SwapCalculator("LYR", "DAI", fakeBlock, "DAI", u, 0); Assert.AreEqual(to, cal1.SwapOutAmount); Assert.AreEqual("LYR", cal1.SwapOutToken); var u2 = 100000; // eth var pureTo2 = Y - (X * Y / (X + u2 * 0.996m)); var to2 = Math.Round(pureTo2, 8); var chg2 = Math.Round(to2 / Y, 6); var price2 = Math.Round(to2 / u2, 10); var cal2 = new SwapCalculator("LYR", "DAI", fakeBlock, "LYR", u2, 0); Assert.AreEqual(to2, cal2.SwapOutAmount); Assert.AreEqual("DAI", cal2.SwapOutToken); Console.WriteLine($"Price {price2} Got {to2} X, Price Impact: {chg2 * 100:n} %"); }
public async Task SwapCoinWrongAsync() { try { await semaphore.WaitAsync(); var pool = await client.GetPoolAsync(testTokenA, LyraGlobal.OFFICIALTICKERCODE); Assert.IsNotNull(pool.PoolAccountId); var poolLatestBlock = pool.GetBlock() as TransactionBlock; Assert.IsTrue(poolLatestBlock.Balances[pool.Token0] > 0 && poolLatestBlock.Balances[pool.Token1] > 0, "No liquidate in pool."); var w1 = Restore(testPrivateKey); await w1.SyncAsync(client); var testTokenBalance = w1.GetLastSyncBlock().Balances[testTokenA].ToBalanceDecimal(); var lyrBalance = w1.GetLastSyncBlock().Balances[LyraGlobal.OFFICIALTICKERCODE].ToBalanceDecimal(); // send wrong token var amount = Math.Round((decimal)((new Random().NextDouble() + 0.07) * 1000), 8); var cal = new SwapCalculator(LyraGlobal.OFFICIALTICKERCODE, testTokenA, poolLatestBlock, LyraGlobal.OFFICIALTICKERCODE, amount, 0); var result = await w1.SwapTokenAsync(LyraGlobal.OFFICIALTICKERCODE, testTokenA, testTokenB, amount, cal.SwapOutAmount); Assert.IsTrue(result.ResultCode != APIResultCodes.Success, $"Should failed but: {result.ResultCode}"); amount = Math.Round((decimal)((new Random().NextDouble() + 0.07) * 1000), 8); cal = new SwapCalculator(LyraGlobal.OFFICIALTICKERCODE, testTokenA, poolLatestBlock, LyraGlobal.OFFICIALTICKERCODE, amount, 0); result = await w1.SwapTokenAsync(LyraGlobal.OFFICIALTICKERCODE, testTokenB, LyraGlobal.OFFICIALTICKERCODE, amount, cal.SwapOutAmount); Assert.IsTrue(result.ResultCode != APIResultCodes.Success, $"Should failed but: {result.ResultCode}"); amount = Math.Round((decimal)((new Random().NextDouble() + 0.07) * 1000), 8); result = await w1.SwapTokenAsync(testTokenB, testTokenA, LyraGlobal.OFFICIALTICKERCODE, amount, cal.SwapOutAmount); Assert.IsTrue(result.ResultCode != APIResultCodes.Success, $"Should failed but: {result.ResultCode}"); amount = lyrBalance + 1; result = await w1.SwapTokenAsync(LyraGlobal.OFFICIALTICKERCODE, testTokenA, LyraGlobal.OFFICIALTICKERCODE, amount, cal.SwapOutAmount); Assert.IsTrue(result.ResultCode != APIResultCodes.Success, $"Should failed but: {result.ResultCode}"); //amount = poolLatestBlock.Balances[pool.Token0].ToBalanceDecimal() / 2 + 1000m; //result = await w1.SwapToken(LyraGlobal.OFFICIALTICKERCODE, testTokenA, LyraGlobal.OFFICIALTICKERCODE, amount, cal.SwapOutAmount); //Assert.IsTrue(result.ResultCode != APIResultCodes.Success, $"Should failed but: {result.ResultCode}"); //amount = poolLatestBlock.Balances[pool.Token0].ToBalanceDecimal(); //result = await w1.SwapToken(LyraGlobal.OFFICIALTICKERCODE, testTokenA, LyraGlobal.OFFICIALTICKERCODE, amount, cal.SwapOutAmount); //Assert.IsTrue(result.ResultCode != APIResultCodes.Success, $"Should failed but: {result.ResultCode}"); amount = 1000000000000m; try { result = await w1.SwapTokenAsync(LyraGlobal.OFFICIALTICKERCODE, testTokenA, LyraGlobal.OFFICIALTICKERCODE, amount, cal.SwapOutAmount); Assert.Fail("Should has over flow exception."); } catch { } await Task.Delay(3000); await w1.SyncAsync(client); // make sure the balance is not changed. var testTokenBalancex = w1.GetLastSyncBlock().Balances[testTokenA].ToBalanceDecimal(); var lyrBalancex = w1.GetLastSyncBlock().Balances[LyraGlobal.OFFICIALTICKERCODE].ToBalanceDecimal(); Assert.AreEqual(testTokenBalance, testTokenBalancex); Assert.AreEqual(lyrBalance, lyrBalancex); // then ok token amount = Math.Round((decimal)((new Random().NextDouble() + 0.07) * 1000), 8); cal = new SwapCalculator(LyraGlobal.OFFICIALTICKERCODE, testTokenA, poolLatestBlock, LyraGlobal.OFFICIALTICKERCODE, amount, 0); result = await w1.SwapTokenAsync(LyraGlobal.OFFICIALTICKERCODE, testTokenA, LyraGlobal.OFFICIALTICKERCODE, amount, cal.MinimumReceived); Assert.IsTrue(result.ResultCode == APIResultCodes.Success, $"Failed to swap {LyraGlobal.OFFICIALTICKERCODE}: {result.ResultCode}"); await Task.Delay(15000); var poolGenesisResult = await client.GetBlockByIndexAsync(poolLatestBlock.AccountID, 1); var block = poolGenesisResult.GetBlock(); var poolGenesis = block as PoolGenesisBlock; Assert.IsTrue(poolGenesisResult.ResultCode == APIResultCodes.Success, $"get gensis returns {poolGenesisResult.ResultCode}"); Assert.IsNotNull(poolGenesis, "Can't get pool genesis block."); var sc = new SwapCalculator(poolGenesis.Token0, poolGenesis.Token1, poolLatestBlock, LyraGlobal.OFFICIALTICKERCODE, amount, 0); var amountToGet = sc.SwapOutAmount; await w1.SyncAsync(client); var testTokenBalance2 = w1.GetLastSyncBlock().Balances[testTokenA].ToBalanceDecimal(); var lyrBalance2 = w1.GetLastSyncBlock().Balances[LyraGlobal.OFFICIALTICKERCODE].ToBalanceDecimal(); Assert.AreEqual(testTokenBalance + amountToGet, testTokenBalance2); Assert.AreEqual(lyrBalance - 1 - amount, lyrBalance2); } finally { semaphore.Release(); } }
public async Task SwapWithSlippageAsync() { try { await semaphore.WaitAsync(); var pool = await client.GetPoolAsync(testTokenA, LyraGlobal.OFFICIALTICKERCODE); Assert.IsNotNull(pool.PoolAccountId); var poolLatestBlock = pool.GetBlock() as TransactionBlock; Assert.IsTrue(poolLatestBlock.Balances[pool.Token0] > 0 && poolLatestBlock.Balances[pool.Token1] > 0, "No liquidate in pool."); var w1 = Restore(testPrivateKey); await w1.SyncAsync(client); var testTokenBalance = w1.GetLastSyncBlock().Balances[testTokenA].ToBalanceDecimal(); var lyrBalance = w1.GetLastSyncBlock().Balances[LyraGlobal.OFFICIALTICKERCODE].ToBalanceDecimal(); // ops, someone swapped var w2 = Restore(otherAccountPrivateKey); var w2result = await w2.SyncAsync(client); Assert.IsTrue(w2result == APIResultCodes.Success, $"W2 sync failed: {w2result}"); var otherAmount = 1m;// Math.Round((decimal)((new Random().NextDouble() + 0.07) * 1000), 8); var cal1 = new SwapCalculator(LyraGlobal.OFFICIALTICKERCODE, testTokenA, poolLatestBlock, LyraGlobal.OFFICIALTICKERCODE, otherAmount, 0); var otherResult = await w2.SwapTokenAsync(LyraGlobal.OFFICIALTICKERCODE, testTokenA, LyraGlobal.OFFICIALTICKERCODE, otherAmount, cal1.MinimumReceived); Assert.IsTrue(otherResult.ResultCode == APIResultCodes.Success, $"Failed to swap other account {LyraGlobal.OFFICIALTICKERCODE}: {otherResult.ResultCode}"); await Task.Delay(6000); // then the slippage is triggered var amount = Math.Round((decimal)((new Random().NextDouble() + 0.07) * 1000), 8); var cal2 = new SwapCalculator(LyraGlobal.OFFICIALTICKERCODE, testTokenA, poolLatestBlock, LyraGlobal.OFFICIALTICKERCODE, amount, 0); var result = await w1.SwapTokenAsync(LyraGlobal.OFFICIALTICKERCODE, testTokenA, LyraGlobal.OFFICIALTICKERCODE, amount, cal2.SwapOutAmount); Assert.IsTrue(result.ResultCode == APIResultCodes.SwapSlippageExcceeded, $"Should Failed to swap {LyraGlobal.OFFICIALTICKERCODE}: {result.ResultCode}"); amount = Math.Round((decimal)((new Random().NextDouble() + 0.07) * 1000), 8); var cal3 = new SwapCalculator(LyraGlobal.OFFICIALTICKERCODE, testTokenA, poolLatestBlock, LyraGlobal.OFFICIALTICKERCODE, amount, 0.001m); result = await w1.SwapTokenAsync(LyraGlobal.OFFICIALTICKERCODE, testTokenA, LyraGlobal.OFFICIALTICKERCODE, amount, cal3.SwapOutAmount + 1000); Assert.IsTrue(result.ResultCode == APIResultCodes.SwapSlippageExcceeded, $"Should Failed to swap {LyraGlobal.OFFICIALTICKERCODE}: {result.ResultCode}"); amount = Math.Round((decimal)((new Random().NextDouble() + 0.07) * 1000), 8); var cal4 = new SwapCalculator(LyraGlobal.OFFICIALTICKERCODE, testTokenA, poolLatestBlock, LyraGlobal.OFFICIALTICKERCODE, amount, 0.1m); result = await w1.SwapTokenAsync(LyraGlobal.OFFICIALTICKERCODE, testTokenA, LyraGlobal.OFFICIALTICKERCODE, amount, cal4.MinimumReceived); Assert.IsTrue(result.ResultCode == APIResultCodes.Success, $"Failed to swap {LyraGlobal.OFFICIALTICKERCODE}: {result.ResultCode}"); await Task.Delay(6000); var amountToGet = cal4.MinimumReceived; await w1.SyncAsync(client); var testTokenBalance2 = w1.GetLastSyncBlock().Balances[testTokenA].ToBalanceDecimal(); var lyrBalance2 = w1.GetLastSyncBlock().Balances[LyraGlobal.OFFICIALTICKERCODE].ToBalanceDecimal(); Assert.IsTrue(testTokenBalance + amountToGet <= testTokenBalance2, $"testTokenBalance + amountToGet is {testTokenBalance + amountToGet}, testTokenBalance2 is {testTokenBalance2}"); Assert.AreEqual(lyrBalance - 1 - amount, lyrBalance2); } finally { semaphore.Release(); } }
public async Task SwapLYRToTokenAAsync() { try { await semaphore.WaitAsync(); var pool = await client.GetPoolAsync(testTokenA, LyraGlobal.OFFICIALTICKERCODE); Assert.IsNotNull(pool.PoolAccountId); var poolLatestBlock = pool.GetBlock() as TransactionBlock; Assert.IsTrue(poolLatestBlock.Balances[pool.Token0] > 0 && poolLatestBlock.Balances[pool.Token1] > 0, "No liquidate in pool."); var w1 = Restore(testPrivateKey); await w1.SyncAsync(client); var testTokenBalance = w1.GetLastSyncBlock().Balances[testTokenA].ToBalanceDecimal(); var lyrBalance = w1.GetLastSyncBlock().Balances[LyraGlobal.OFFICIALTICKERCODE].ToBalanceDecimal(); var amount = Math.Round((decimal)((new Random().NextDouble() + 0.07) * 1000), 8); var cal1 = new SwapCalculator(LyraGlobal.OFFICIALTICKERCODE, testTokenA, poolLatestBlock, LyraGlobal.OFFICIALTICKERCODE, amount, 0); var result = await w1.SwapTokenAsync(LyraGlobal.OFFICIALTICKERCODE, testTokenA, LyraGlobal.OFFICIALTICKERCODE, amount, cal1.MinimumReceived); Assert.IsTrue(result.ResultCode == APIResultCodes.Success, $"Failed to swap {LyraGlobal.OFFICIALTICKERCODE}: {result.ResultCode}"); await Task.Delay(9000); // then the pool should receive the funds var poolInResult = await client.GetBlockByIndexAsync(pool.PoolAccountId, poolLatestBlock.Height + 1); Assert.IsTrue(poolInResult.ResultCode == APIResultCodes.Success, "failed to get pool in block. " + poolInResult.ResultCode); var poolIn = poolInResult.GetBlock() as PoolSwapInBlock; Assert.IsNotNull(poolIn); var chgs = poolIn.GetBalanceChanges(poolLatestBlock); Assert.AreEqual(1, chgs.Changes.Count); Assert.AreEqual(LyraGlobal.OFFICIALTICKERCODE, chgs.Changes.First().Key); Assert.AreEqual(amount, chgs.Changes.First().Value); var poolGenesisResult = await client.GetBlockByIndexAsync(poolLatestBlock.AccountID, 1); var block = poolGenesisResult.GetBlock(); var poolGenesis = block as PoolGenesisBlock; Assert.IsTrue(poolGenesisResult.ResultCode == APIResultCodes.Success, $"get gensis returns {poolGenesisResult.ResultCode}"); Assert.IsNotNull(poolGenesis, "Can't get pool genesis block."); var amountToGet = cal1.SwapOutAmount; await w1.SyncAsync(client); var testTokenBalance2 = w1.GetLastSyncBlock().Balances[testTokenA].ToBalanceDecimal(); var lyrBalance2 = w1.GetLastSyncBlock().Balances[LyraGlobal.OFFICIALTICKERCODE].ToBalanceDecimal(); Assert.AreEqual(testTokenBalance + amountToGet, testTokenBalance2); Assert.AreEqual(lyrBalance - 1 - amount, lyrBalance2); } finally { semaphore.Release(); } }
public override async Task <TransactionBlock> ExtraOpsAsync(DagSystem sys, string reqHash) { var blocks = await sys.Storage.FindBlocksByRelatedTxAsync(reqHash); var swout = blocks.FirstOrDefault(a => a is PoolSwapOutBlock); if (swout != null) { return(null); } var recv = blocks.FirstOrDefault(a => a is PoolSwapInBlock) as PoolSwapInBlock; var swapInBlock = recv as PoolSwapInBlock; var recvBlockPrev = await sys.Storage.FindBlockByHashAsync(recv.PreviousHash) as TransactionBlock; var recvChgs = swapInBlock.GetBalanceChanges(recvBlockPrev); var kvp = recvChgs.Changes.First(); var poolGenesis = await sys.Storage.FindFirstBlockAsync(swapInBlock.AccountID) as PoolGenesisBlock; var cfg = new SwapCalculator(poolGenesis.Token0, poolGenesis.Token1, recvBlockPrev, kvp.Key, kvp.Value, 0); var lsb = await sys.Storage.GetLastServiceBlockAsync(); var send = await sys.Storage.FindBlockByHashAsync(swapInBlock.SourceHash) as SendTransferBlock; var swapOutBlock = new PoolSwapOutBlock { AccountID = send.DestinationAccountId, ServiceHash = lsb.Hash, DestinationAccountId = send.AccountID, Balances = new Dictionary <string, long>(), Tags = null, Fee = cfg.PayToAuthorizer, FeeCode = LyraGlobal.OFFICIALTICKERCODE, FeeType = AuthorizationFeeTypes.Dynamic, RelatedTx = send.Hash }; swapOutBlock.AddTag(Block.MANAGEDTAG, ""); // value is always ignored var poolGenesisBlock = await sys.Storage.FindFirstBlockAsync(recv.AccountID) as PoolGenesisBlock; var poolLatestBlock = await sys.Storage.FindLatestBlockAsync(recv.AccountID) as TransactionBlock; var curBalance = poolLatestBlock.Balances.ToDecimalDict(); var nextBalance = poolLatestBlock.Balances.ToDecimalDict(); var tokenOut = cfg.SwapOutToken; var tokenIn = cfg.SwapInToken; if (tokenIn == LyraGlobal.OFFICIALTICKERCODE) { // tokenIn == LYR nextBalance[tokenIn] = curBalance[tokenIn] - cfg.PayToAuthorizer; // pool fee leave in the pool nextBalance[tokenOut] = curBalance[tokenOut] - cfg.SwapOutAmount; } else { // tokenIn == other token nextBalance[tokenIn] = curBalance[tokenIn]; // pool fee leave in the pool nextBalance[tokenOut] = curBalance[tokenOut] - cfg.SwapOutAmount - cfg.PayToAuthorizer; } Console.WriteLine($"user should receive {cfg.SwapOutAmount} {cfg.SwapOutToken}"); swapOutBlock.Balances = nextBalance.ToLongDict(); swapOutBlock.Shares = (poolLatestBlock as IPool).Shares; await swapOutBlock.InitializeBlockAsync(poolLatestBlock, (hash) => Task.FromResult(Signatures.GetSignature(sys.PosWallet.PrivateKey, hash, sys.PosWallet.AccountId))); // verify var chgs = swapOutBlock.GetBalanceChanges(poolLatestBlock); return(swapOutBlock); }
public override async Task <APIResultCodes> PreSendAuthAsync(DagSystem sys, SendTransferBlock block, TransactionBlock lastBlock) { if (block.Tags.Count > 3 || !block.Tags.ContainsKey("poolid")) { return(APIResultCodes.InvalidBlockTags); } if (block.Tags.Count == 3 && !block.Tags.ContainsKey("minrecv")) { return(APIResultCodes.InvalidBlockTags); } var vp = await WFPoolAddLiquidate.VerifyPoolAsync(sys, block, lastBlock); if (vp != APIResultCodes.Success) { return(vp); } var chgs = block.GetBalanceChanges(lastBlock); var poolGenesis = sys.Storage.GetPoolByID(block.Tags["poolid"]); if (chgs.Changes.Count != 1) { return(APIResultCodes.InvalidTokenToSwap); } string tokenToSwap = null; var kvp = chgs.Changes.First(); if (kvp.Key == poolGenesis.Token0) { tokenToSwap = poolGenesis.Token0; } else if (kvp.Key == poolGenesis.Token1) { tokenToSwap = poolGenesis.Token1; } if (tokenToSwap == null) { return(APIResultCodes.InvalidTokenToSwap); } // check amount var poolLatest = await sys.Storage.FindLatestBlockAsync(block.DestinationAccountId) as TransactionBlock; //if (kvp.Value > poolLatest.Balances[tokenToSwap].ToBalanceDecimal() / 2) // return APIResultCodes.TooManyTokensToSwap; // uniswap AMM don't mind how many token want to swap if (block.Tags.ContainsKey("minrecv")) { if (!long.TryParse(block.Tags["minrecv"], out long toGetLong)) { return(APIResultCodes.InvalidSwapSlippage); } decimal toGet = toGetLong.ToBalanceDecimal(); if (toGet <= 0) { return(APIResultCodes.InvalidSwapSlippage); } if (poolLatest.Balances.Any(a => a.Value == 0)) { // can't calculate rito return(APIResultCodes.PoolOutOfLiquidaty); } var cal = new SwapCalculator(poolGenesis.Token0, poolGenesis.Token1, poolLatest, chgs.Changes.First().Key, chgs.Changes.First().Value, 0); if (cal.SwapOutAmount < toGet) { return(APIResultCodes.SwapSlippageExcceeded); } } return(APIResultCodes.Success); }
protected override async Task <APIResultCodes> VerifyWithPrevAsync(DagSystem sys, Block block, Block previousBlock) { var swapOutBlock = block as PoolSwapOutBlock; if (swapOutBlock == null) { return(APIResultCodes.InvalidBlockType); } var relatedTransactions = await sys.Storage.FindBlocksByRelatedTxAsync(swapOutBlock.RelatedTx); if (relatedTransactions.Count >= 2) { return(APIResultCodes.PoolOperationAlreadyCompleted); } var poolId = (block as PoolSwapOutBlock).AccountID; var poolGenesis = await sys.Storage.FindFirstBlockAsync(poolId) as PoolGenesisBlock; var poolLatestBlock = await sys.Storage.FindLatestBlockAsync(poolId) as TransactionBlock; // get target accountId var originalSendBlock = await sys.Storage.FindBlockByHashAsync(swapOutBlock.RelatedTx) as SendTransferBlock; if (originalSendBlock == null) { return(APIResultCodes.InvalidOperation); } var targetAccountId = originalSendBlock.AccountID; if (targetAccountId != swapOutBlock.DestinationAccountId) { return(APIResultCodes.InvalidPoolSwapOutAccountId); } if (originalSendBlock.Tags == null || originalSendBlock.Tags[Block.REQSERVICETAG] != BrokerActions.BRK_POOL_SWAP) { return(APIResultCodes.InvalidPoolOperation); } var prevPrevBlock = await sys.Storage.FindBlockByHashAsync(previousBlock.PreviousHash) as TransactionBlock; var chgs = (previousBlock as TransactionBlock).GetBalanceChanges(prevPrevBlock); // calculate rito by prevBlock.prevBlock var prevprevBlock = await sys.Storage.FindBlockByHashAsync(previousBlock.PreviousHash) as TransactionBlock; if (prevprevBlock == null) { return(APIResultCodes.InvalidPoolOperation); } var swapRito = Math.Round(prevprevBlock.Balances[poolGenesis.Token0].ToBalanceDecimal() / prevprevBlock.Balances[poolGenesis.Token1].ToBalanceDecimal(), LyraGlobal.RITOPRECISION); if (chgs.Changes.Count != 1) { return(APIResultCodes.InvalidPoolOperation); } string tokenIn = chgs.Changes.First().Key; if (tokenIn != poolGenesis.Token0 && tokenIn != poolGenesis.Token1) { return(APIResultCodes.InvalidPoolOperation); } var kvp = chgs.Changes.First(); var cfg = new SwapCalculator(poolGenesis.Token0, poolGenesis.Token1, prevprevBlock, tokenIn, chgs.Changes.First().Value, 0); if (cfg.SwapOutToken == null || cfg.SwapOutAmount == 0m) { return(APIResultCodes.InvalidPoolOperation); } var currentOutChgs = swapOutBlock.GetBalanceChanges(previousBlock as TransactionBlock); if (currentOutChgs.Changes.Count != 1) //plus the fee: no, fee pay by standard and is not included here { return(APIResultCodes.InvalidPoolSwapOutToken); } if (currentOutChgs.Changes.First().Key != cfg.SwapOutToken || currentOutChgs.Changes.First().Value != cfg.SwapOutAmount) { Console.WriteLine($"Swap out: {currentOutChgs.Changes.First().Value} should be: {cfg.SwapOutAmount}"); return(APIResultCodes.InvalidPoolSwapOutAmount); } // balance & share var curBalance = poolLatestBlock.Balances.ToDecimalDict(); var nextBalance = poolLatestBlock.Balances.ToDecimalDict(); var tokenOut = cfg.SwapOutToken; if (tokenIn == LyraGlobal.OFFICIALTICKERCODE) { // tokenIn == LYR nextBalance[tokenIn] = curBalance[tokenIn] - cfg.PayToAuthorizer; // pool fee leave in the pool nextBalance[tokenOut] = curBalance[tokenOut] - cfg.SwapOutAmount; } else { // tokenOut == LYR nextBalance[tokenIn] = curBalance[tokenIn]; // pool fee leave in the pool nextBalance[tokenOut] = curBalance[tokenOut] - cfg.SwapOutAmount - cfg.PayToAuthorizer; } var outBalances = nextBalance.ToLongDict(); var outShares = (poolLatestBlock as IPool).Shares; if (!swapOutBlock.Balances.OrderBy(a => a.Key) .SequenceEqual(outBalances.OrderBy(a => a.Key))) { return(APIResultCodes.InvalidPoolSwapOutAmount); } if (!swapOutBlock.Shares.OrderBy(a => a.Key) .SequenceEqual(outShares.OrderBy(a => a.Key))) { return(APIResultCodes.InvalidPoolSwapOutShare); } _calculator = cfg; return(await base.VerifyWithPrevAsync(sys, block, previousBlock)); }
private async Task UpdateSwapToBalanceForLyraSwapAsync() { swapCalculator = null; if (walletState.Value.wallet?.GetLatestBlock()?.Balances?.ContainsKey(swapFromToken) == true) { fromTokenBalance = walletState.Value.wallet?.GetLatestBlock()?.Balances?[swapFromToken].ToBalanceDecimal() ?? 0m; } else { fromTokenBalance = 0m; } // check if pool exists. var pool = await lyraClient.GetPool(swapFromToken, swapToToken); if (pool.Successful() && pool.PoolAccountId != null) { IsDisabled = false; var token0 = pool.Token0; var token1 = pool.Token1; var swapRito = 0m; var poolLatestBlock = pool.GetBlock() as TransactionBlock; if (poolLatestBlock.Balances.ContainsKey(token0) && !poolLatestBlock.Balances.Any(a => a.Value == 0)) { swapRito = poolLatestBlock.Balances[token0].ToBalanceDecimal() / poolLatestBlock.Balances[token1].ToBalanceDecimal(); } var sb = new StringBuilder(); sb.AppendLine($"Liquidate pool for {token0} and {token1}: \n Pool account ID is {pool.PoolAccountId}\n"); if (swapRito > 0) { expectedRito = swapRito; sb.AppendLine($" Pool liquidate of {token0}: {poolLatestBlock.Balances[token0].ToBalanceDecimal()}"); sb.AppendLine($" Pool liquidate of {token1}: {poolLatestBlock.Balances[token1].ToBalanceDecimal()}"); // calculate it if (swapFromCount > 0) { if (swapFromToken == token0) { swapCalculator = new SwapCalculator(token0, token1, poolLatestBlock, token0, swapFromCount, slippage / 100); // default % } else { swapCalculator = new SwapCalculator(token0, token1, poolLatestBlock, token1, swapFromCount, slippage / 100); } swapToCount = swapCalculator.MinimumReceived; } else { swapToCount = 0; } } else { expectedRito = 0; sb.AppendLine($" Pool doesn't have liquidate yet."); } PoolInfo = sb.ToString(); } else { IsDisabled = true; PoolInfo = $"No liquidate pool for {swapFromToken} and {swapToToken}."; } if (swapCalculator == null || swapCalculator.MinimumReceived <= 0) { IsDisabled = true; } await InvokeAsync(() => { StateHasChanged(); }); }