diff --git a/Frontend/Functions/LiquidityFunction.cs b/Frontend/Functions/LiquidityFunction.cs new file mode 100644 index 0000000000000000000000000000000000000000..fdc7cb27a4043fae9f6bb44c6f93eda47796ff06 --- /dev/null +++ b/Frontend/Functions/LiquidityFunction.cs @@ -0,0 +1,9 @@ +using Nethereum.ABI.FunctionEncoding.Attributes; +using Nethereum.Contracts; + +namespace Frontend.Functions; + +[Function("liquiditry", "uint128")] +public class LiquidityFunction : FunctionMessage{ + +} \ No newline at end of file diff --git a/Frontend/PoolV3Client.cs b/Frontend/PoolV3Client.cs index 5d1cba47df8e418efab085fdd43384d442a4501e..1bb3b6f9ba2a418249bf5ec10368aa04e01d0c65 100644 --- a/Frontend/PoolV3Client.cs +++ b/Frontend/PoolV3Client.cs @@ -93,12 +93,16 @@ public class PoolV3Client } public async Task<decimal> GetPairRatio() + { + return (decimal)SqrtPriceX96ToPrice(await GetSqrtPriceX96()); + } + + public async Task<BigInteger> GetSqrtPriceX96() { var handler = _web3.Eth.GetContractQueryHandler<Slot0Funtion>(); var func = new Slot0Funtion(); var slot0Result = await handler.QueryDeserializingToObjectAsync<Slot0OutputDTO>(func, _pool.Address); - var sqrtPriceX96 = slot0Result.SqrtPriceX96; - return (decimal)SqrtPriceX96ToPrice(sqrtPriceX96); + return slot0Result.SqrtPriceX96; } public double SqrtPriceX96ToPrice(BigInteger sqrtPriceX96) @@ -138,7 +142,7 @@ public class PoolV3Client return _token1Address; } - public async Task<uint> getPoolFeeTier() + public async Task<uint> QueryPoolFee() { if (null == _poolFeeTier) { @@ -150,6 +154,13 @@ public class PoolV3Client return (uint)_poolFeeTier; } + public async Task<BigInteger> QueryLiquidity() + { + var handler = _web3.Eth.GetContractQueryHandler<LiquidityFunction>(); + var func = new LiquidityFunction(); + return await handler.QueryAsync<BigInteger>(_pool.Address, func); + } + /* this function should swap the tokens in the pool @@ -181,7 +192,7 @@ public class PoolV3Client { TokenIn = fromTokenAddress, TokenOut = toTokenAddress, - Fee = await getPoolFeeTier(), + Fee = await QueryPoolFee(), Recipient = _account.Address, AmountIn = Web3.Convert.ToWei(amount), AmountOutMinimum = Web3.Convert.ToWei(amountOutMinimum), // Set your minimum amount out @@ -201,26 +212,43 @@ public class PoolV3Client /// This function should swap the tokens in the pool to create a desired quote /// </summary> /// <param name="desiredQuote">the ration between token1/token0</param> - /// <returns></returns> - public async Task<decimal> PerformSwapToCreatDesiredQuote(decimal desiredQuote) + /// <returns>Quote after the swap</returns> + public async Task<decimal> PerformSwapTowardsDesiredQuote(decimal desiredQuote) { - var currentQuote = await GetPairRatio(); - throw new NotImplementedException(); - var amount = 0.0M; - if (currentQuote < desiredQuote) - { - // we sell some of token1 to buy token0 - amount = (desiredQuote - currentQuote) / desiredQuote; - throw new NotImplementedException(); - await SwapAsync(Token1Address, Token0Address, amount); + var sqrtPriceX96 = await GetSqrtPriceX96(); + var desiredSqrtPriceX96 = PriceToSqrtPriceX96((double)desiredQuote); + string[] tokenAddresses = [Token0Address, Token1Address]; + var fromTokenIndex = 0; + var amount = 0.0; + var liquidity = await QueryLiquidity(); + var balance = new BigInteger(0); + if (desiredSqrtPriceX96 > sqrtPriceX96) + { // we have to by token0 to increase demand and drive the price up + fromTokenIndex = 1; // we want to swap from token1 to token0 + + // Taken from getNextSqrtPriceFromAmount1RoundingDown see https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/SqrtPriceMath.sol + amount = (double)(liquidity * (desiredSqrtPriceX96 - sqrtPriceX96)) / (double)BigInteger.Pow(2, 96); + balance = await Token1Client.BalanceOf(_account.Address); } else + { // whe have to sell token0 to lower the price + + // Taken from getNextSqrtPriceFromAmount0ReoundingUp see https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/SqrtPriceMath.sol + amount = (double)(liquidity * BigInteger.Pow(2, 96) * (sqrtPriceX96 - desiredSqrtPriceX96)) / (double)(desiredSqrtPriceX96 * sqrtPriceX96); + balance = await Token0Client.BalanceOf(_account.Address); + } + amount *= 1.0 + (double)await QueryPoolFee() / 1000.0; // add the pool fees + var bigIntAmount = new BigInteger(amount); + + if (balance < bigIntAmount) { - amount = 1; - throw new NotImplementedException(); - await SwapAsync(Token1Address, Token0Address, amount); + bigIntAmount = balance; } - throw new NotImplementedException(); + + + var receipt = await SwapAsync(tokenAddresses[fromTokenIndex], tokenAddresses[(fromTokenIndex + 1) % 2], Web3.Convert.FromWei(bigIntAmount)); + + return await GetPairRatio(); } } \ No newline at end of file diff --git a/Frontend/TokenClient.cs b/Frontend/TokenClient.cs index 61ab43e2171f1df5e98959bc7f8d0d61c1959920..9f2d9b3f82b35e3117dd083fe0c43c01bc377309 100644 --- a/Frontend/TokenClient.cs +++ b/Frontend/TokenClient.cs @@ -109,4 +109,15 @@ public class TokenClient return transactionReceipt.TransactionHash; } + public async Task<BigInteger> BalanceOf(string address){ + var func = new BalanceOfFunction + { + Owner = address, + }; + + var handler = _web3.Eth.GetContractQueryHandler<BalanceOfFunction>(); + return await handler.QueryAsync<BigInteger>(_tokenAddress, func); + + } + }