From cbf690d5d949e15a3e4479045c11953dd9ef6c06 Mon Sep 17 00:00:00 2001 From: David Hintermann <David.Hintermann@ost.ch> Date: Thu, 28 Nov 2024 15:02:55 +0000 Subject: [PATCH] wip: commit before cleanup --- Frontend/Functions/ApproveMaxFunctrion.cs | 13 -- Frontend/Functions/ExactInputFunction.cs | 21 --- Frontend/Functions/ExactSingleParamBase.cs | 50 +++---- Frontend/Metrics/Erc20TokenMetrics.cs | 16 +- Frontend/PoolV3Client.cs | 164 ++++++++++++--------- Frontend/Program.cs | 15 +- Frontend/TokenClient.cs | 67 ++++++--- 7 files changed, 178 insertions(+), 168 deletions(-) delete mode 100644 Frontend/Functions/ApproveMaxFunctrion.cs delete mode 100644 Frontend/Functions/ExactInputFunction.cs diff --git a/Frontend/Functions/ApproveMaxFunctrion.cs b/Frontend/Functions/ApproveMaxFunctrion.cs deleted file mode 100644 index c27a0d6..0000000 --- a/Frontend/Functions/ApproveMaxFunctrion.cs +++ /dev/null @@ -1,13 +0,0 @@ - -using System.Numerics; -using Nethereum.ABI.FunctionEncoding.Attributes; -using Nethereum.Contracts; - -namespace Frontend.Functions; public partial class ApproveMaxFunction : ApproveMaxFunctionBase { } - -[Function("approveMax")] -public class ApproveMaxFunctionBase : FunctionMessage -{ - [Parameter("address", "token", 1)] - public virtual string Token { get; set; } -} \ No newline at end of file diff --git a/Frontend/Functions/ExactInputFunction.cs b/Frontend/Functions/ExactInputFunction.cs deleted file mode 100644 index 4bcff67..0000000 --- a/Frontend/Functions/ExactInputFunction.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Nethereum.ABI.FunctionEncoding.Attributes; -using Nethereum.Contracts; -using System.Numerics; - -namespace Frontend.Functions; - -[Function("exactInput")] -class ExactInputFunction : FunctionMessage -{ - [Parameter("uint256", "amountIn", 1)] - public BigInteger AmountIn { get; set; } - - [Parameter("uint256", "amountOutMinimum", 2)] - public BigInteger AmountOutMinimum { get; set; } - - [Parameter("address[]", "path", 3)] - public string[] Path { get; set; } - - [Parameter("address", "to", 4)] - public string To { get; set; } -} \ No newline at end of file diff --git a/Frontend/Functions/ExactSingleParamBase.cs b/Frontend/Functions/ExactSingleParamBase.cs index ae54ab1..935dc61 100644 --- a/Frontend/Functions/ExactSingleParamBase.cs +++ b/Frontend/Functions/ExactSingleParamBase.cs @@ -7,32 +7,32 @@ using Nethereum.Contracts.CQS; using Nethereum.Contracts; namespace Frontend.Functions; - public class ExactInputSingleParamsBase - { - [Parameter("address", "tokenIn", 1)] - public virtual string TokenIn { get; set; } - [Parameter("address", "tokenOut", 2)] - public virtual string TokenOut { get; set; } - [Parameter("uint24", "fee", 3)] - public virtual uint Fee { get; set; } - [Parameter("address", "recipient", 4)] - public virtual string Recipient { get; set; } - [Parameter("uint256", "amountIn", 5)] - public virtual BigInteger AmountIn { get; set; } - [Parameter("uint256", "amountOutMinimum", 6)] - public virtual BigInteger AmountOutMinimum { get; set; } - [Parameter("uint160", "sqrtPriceLimitX96", 7)] - public virtual BigInteger SqrtPriceLimitX96 { get; set; } - } +public class ExactInputSingleParamsBase +{ + [Parameter("address", "tokenIn", 1)] + public virtual string TokenIn { get; set; } + [Parameter("address", "tokenOut", 2)] + public virtual string TokenOut { get; set; } + [Parameter("uint24", "fee", 3)] + public virtual uint Fee { get; set; } + [Parameter("address", "recipient", 4)] + public virtual string Recipient { get; set; } + [Parameter("uint256", "amountIn", 5)] + public virtual BigInteger AmountIn { get; set; } + [Parameter("uint256", "amountOutMinimum", 6)] + public virtual BigInteger AmountOutMinimum { get; set; } + [Parameter("uint160", "sqrtPriceLimitX96", 7)] + public virtual BigInteger SqrtPriceLimitX96 { get; set; } +} - public partial class ExactInputSingleParams : ExactInputSingleParamsBase { } +public partial class ExactInputSingleParams : ExactInputSingleParamsBase { } - [Function("exactInputSingle", "uint256")] - public class ExactInputSingleFunctionBase : FunctionMessage - { - [Parameter("tuple", "params", 1)] - public virtual ExactInputSingleParams Params { get; set; } - } +[Function("exactInputSingle", "uint256")] +public class ExactInputSingleFunctionBase : FunctionMessage +{ + [Parameter("tuple", "params", 1)] + public virtual ExactInputSingleParams Params { get; set; } +} - public partial class ExactInputSingleFunction : ExactInputSingleFunctionBase { } \ No newline at end of file +public partial class ExactInputSingleFunction : ExactInputSingleFunctionBase { } \ No newline at end of file diff --git a/Frontend/Metrics/Erc20TokenMetrics.cs b/Frontend/Metrics/Erc20TokenMetrics.cs index 39dca28..a6b4ed8 100644 --- a/Frontend/Metrics/Erc20TokenMetrics.cs +++ b/Frontend/Metrics/Erc20TokenMetrics.cs @@ -1,29 +1,33 @@ using System.Diagnostics.Metrics; +using Frontend.Configuration; +using Nethereum.Web3; namespace Frontend.Metrics; public class Erc20TokenMetrics { private readonly IMeterFactory _meterFactory; - private readonly TokenClient _tokenClient; + private readonly TokenClient _rocTokenClient; private ObservableGauge<double>? _totalSupplyGauge; - public Erc20TokenMetrics(IMeterFactory meterFactory, TokenClient tokenClient) + public Erc20TokenMetrics(IMeterFactory meterFactory,Web3 web3, ChainSettings chainSettings) { _meterFactory = meterFactory; - _tokenClient = tokenClient; + _rocTokenClient = new(web3,chainSettings.TokenAddress); } public void CreateMetrics() { var meter = _meterFactory.Create("RoestiCoin"); - _totalSupplyGauge = meter.CreateObservableGauge("RoestiCoin.Supply.Total", () => GetTotalSupply(), unit: "ROC"); + _totalSupplyGauge = meter.CreateObservableGauge("RoestiCoin.Supply.Total", () => GetTotalSupply(_rocTokenClient), unit: "ROC"); + } - private double GetTotalSupply() + private double GetTotalSupply(TokenClient client) { - var totalSupply = _tokenClient.TotalSupply(); + var totalSupply = client.TotalSupply(); totalSupply.Wait(); return (double)totalSupply.Result; } + } \ No newline at end of file diff --git a/Frontend/PoolV3Client.cs b/Frontend/PoolV3Client.cs index 2aba84b..0c54b10 100644 --- a/Frontend/PoolV3Client.cs +++ b/Frontend/PoolV3Client.cs @@ -10,6 +10,7 @@ using Frontend.Configuration; using Microsoft.AspNetCore.Components.Forms; using System.Reflection.Metadata.Ecma335; using Nethereum.Contracts.Extensions; +using Nethereum.Contracts.Standards.ERC20.TokenList; namespace Frontend; @@ -19,9 +20,60 @@ public class PoolV3Client private readonly Web3 _web3; private readonly ChainSettings _chainSettings; - private string? token0Address = null; - private string? token1Address = null; - private uint? poolFeeTier = null; + private string? _token0Address = null; + private string? _token1Address = null; + private uint? _poolFeeTier = null; + private bool _hasApproved = false; + private TokenClient? _token0Client; + private TokenClient? _token1Client; + + + public string Token0Address + { + get + { + if (_token0Address == null) + { + _token0Address = QueryToken0Address().Result; + } + return _token0Address; + } + } + + public string Token1Address + { + get + { + if (_token1Address == null) + { + _token1Address = QueryToken1Address().Result; + } + return _token1Address; + } + } + + private TokenClient Token0Client + { + get + { + if (_token0Client == null) + { + _token0Client = new TokenClient(_web3, Token0Address); + } + return _token0Client; + } + } + private TokenClient Token1Client + { + get + { + if (_token1Client == null) + { + _token1Client = new TokenClient(_web3, Token1Address); + } + return _token1Client; + } + } public PoolV3Client(Web3 web3, ChainSettings chainSettings) { @@ -60,76 +112,70 @@ public class PoolV3Client { return new BigInteger(Math.Sqrt(price) * Math.Pow(2, 96)); } - public async Task<string> Token0Address() + + private async Task<string> QueryToken0Address() { - if (null == token0Address) + if (null == _token0Address) { var handler = _web3.Eth.GetContractQueryHandler<Token0Function>(); var func = new Token0Function(); var result = await handler.QueryAsync<string>(_pool.Address, func); - token0Address = result; + _token0Address = result; } - return token0Address; + return _token0Address; } - public async Task<string> Token1Address() + private async Task<string> QueryToken1Address() { - if (null == token1Address) + if (null == _token1Address) { var handler = _web3.Eth.GetContractQueryHandler<Token1Function>(); var func = new Token1Function(); var result = await handler.QueryAsync<string>(_pool.Address, func); - token1Address = result; + _token1Address = result; } - return token1Address; + return _token1Address; } public async Task<uint> getPoolFeeTier() { - if (null == poolFeeTier) + if (null == _poolFeeTier) { var handler = _web3.Eth.GetContractQueryHandler<FeeFunction>(); var func = new FeeFunction(); var result = await handler.QueryAsync<int>(_pool.Address, func); - poolFeeTier = (uint)result; + _poolFeeTier = (uint)result; } - return (uint)poolFeeTier; + return (uint)_poolFeeTier; } /* this function should swap the tokens in the pool */ - public async Task<decimal> SwapAsync(string fromTokenAddress, string toTokenAddress, decimal amount, Account account, decimal amountOutMinimum = 0, uint sqrtPriceLimitX96 = 0) + public async Task<decimal> SwapAsync(string fromTokenAddress, string toTokenAddress, decimal amount, Account account, decimal amountOutMinimum = 0, uint sqrtPriceLimitX96 = 0, bool doApproval = true) { - /* var swapFunction = new SwapExactInputSingleFunction - { - TokenIn = fromTokenAddress, - TokenOut = toTokenAddress, - Fee = new BigInteger(await getPoolFeeTier()), - Recipient = account.Address, - AmountIn = Web3.Convert.ToWei(amount), - AmountOutMinimum = Web3.Convert.ToWei(amountOutMinimum), // Set your minimum amount out - SqrtPriceLimitX96 = sqrtPriceLimitX96, - Deadline = new BigInteger(DateTimeOffset.UtcNow.ToUnixTimeSeconds() + 60 ) // 20 minutes from now - }; - - swapFunction.Gas= new BigInteger(300000); - - var handler = _web3.Eth.GetContractTransactionHandler<SwapExactInputSingleFunction>(); - - var transactionReceipt = await handler.SendRequestAndWaitForReceiptAsync(_chainSettings.Uniswap.SwapRouterV2Address, swapFunction); - var swapEvent = transactionReceipt.DecodeAllEvents<SwapEventDTO>(); - if (swapEvent.Count > 0) - { - var receivedAmount = swapEvent[0].Event.AmountOut; - return Web3.Convert.FromWei(receivedAmount); - } - - - throw new Exception("Swap event not found in transaction receipt");*/ + + if (doApproval) + { + if (fromTokenAddress == Token0Address) + { + if(amount>Web3.Convert.FromWei(await Token0Client.GetAllowanceAsync(account.Address, _chainSettings.Uniswap.SwapRouterV2Address))) + { + await Token0Client.ApproveAsync(_chainSettings.Uniswap.SwapRouterV2Address, BigInteger.Pow(2, 256) - 1); + } + } + else + { + if(amount>Web3.Convert.FromWei(await Token0Client.GetAllowanceAsync(account.Address, _chainSettings.Uniswap.SwapRouterV2Address))) + { + await Token0Client.ApproveAsync(_chainSettings.Uniswap.SwapRouterV2Address, BigInteger.Pow(2, 256) - 1); + } + } + } + var contractHandler = _web3.Eth.GetContractHandler(_chainSettings.Uniswap.SwapRouterV2Address); var exactInputSingleFunction = new ExactInputSingleFunction(); exactInputSingleFunction.Params = new ExactInputSingleParams @@ -143,32 +189,7 @@ public class PoolV3Client SqrtPriceLimitX96 = sqrtPriceLimitX96, }; var exactInputSingleFunctionTxnReceipt = await contractHandler.SendRequestAndWaitForReceiptAsync(exactInputSingleFunction); - var swapEvent = exactInputSingleFunctionTxnReceipt.DecodeAllEvents<SwapEventDTO>(); - if (swapEvent.Count > 0) - { - var receivedAmount = swapEvent[0].Event.AmountOut; - return Web3.Convert.FromWei(receivedAmount); - } - throw new Exception("Swap event not found in transaction receipt"); - } - public async Task<decimal> SwapAsync2(string fromTokenAddress, string toTokenAddress, decimal amount, Account account, decimal amountOutMinimum = 0, uint sqrtPriceLimitX96 = 0) - { - var contract = _web3.Eth.GetContract( - File.ReadAllText( - Path.Combine( - Directory.GetCurrentDirectory(), - "Configuration", - "Abi", - "swapRouterV2.abi.json" - ) - ), - _chainSettings.Uniswap.SwapRouterV2Address - ); - - var exactInputSingle = contract.GetFunction("exactInputSingle"); - - var reciept = await exactInputSingle.SendTransactionAndWaitForReceiptAsync(account.Address, null, fromTokenAddress, toTokenAddress, await getPoolFeeTier(), account.Address, Web3.Convert.ToWei(amount), Web3.Convert.ToWei(amountOutMinimum), sqrtPriceLimitX96); - var swapEvent = reciept.DecodeAllEvents<SwapEventDTO>(); + var swapEvent = exactInputSingleFunctionTxnReceipt.DecodeAllEvents<SwapEventDTO>(); if (swapEvent.Count > 0) { var receivedAmount = swapEvent[0].Event.AmountOut; @@ -177,11 +198,10 @@ public class PoolV3Client throw new Exception("Swap event not found in transaction receipt"); } - public async Task approveMax(string tokenAddress){ - var contractHandler = _web3.Eth.GetContractHandler(_chainSettings.Uniswap.SwapRouterV2Address); - var approveMaxFunction = new ApproveMaxFunction(); - approveMaxFunction.Token = tokenAddress; - var approveMaxFunctionTxnReceipt = await contractHandler.SendRequestAndWaitForReceiptAsync(approveMaxFunction); + + public async Task<decimal> PerformSwapToStablePrice(double desiredPrice) + { + return 0.0M; } } \ No newline at end of file diff --git a/Frontend/Program.cs b/Frontend/Program.cs index e4dc332..c6ea82a 100644 --- a/Frontend/Program.cs +++ b/Frontend/Program.cs @@ -38,17 +38,14 @@ Account account = new Account( builder.Services.AddSingleton(provider => chainSettings); -builder.Services.AddSingleton(provider => new TokenClient( - chainApiUrl, - chainConfiguration["TokenAddress"]!, - tokenAbi, - accountPrivateKey, - chainId -)); builder.Services.AddSingleton(provider=>account); - -builder.Services.AddSingleton(provider => new Web3(account, chainApiUrl)); +builder.Services.AddTransient<Web3>(sp => +{ + var chainSettings = sp.GetRequiredService<ChainSettings>(); + var account = sp.GetRequiredService<Account>(); + return new Web3(account, chainApiUrl); +}); builder.Services.AddSingleton<PoolMetrics>(); diff --git a/Frontend/TokenClient.cs b/Frontend/TokenClient.cs index 1a79d7b..61ab43e 100644 --- a/Frontend/TokenClient.cs +++ b/Frontend/TokenClient.cs @@ -11,59 +11,49 @@ public class TokenClient { const int Decimals = 18; - private readonly Account _account; - private readonly Web3 _web3Client; + private readonly Web3 _web3; private readonly string _tokenAddress; - private readonly string _tokenAbi; - private Contract Contract - { - get => _web3Client.Eth.GetContract(_tokenAbi, _tokenAddress); - } + public string TokenAddress => _tokenAddress; public TokenClient( - string apiUrl, - string tokenAddress, - string tokenAbi, - string accountPrivateKey, - int chainId + Web3 web3, + string tokenAddress ) { _tokenAddress = tokenAddress; - _tokenAbi = tokenAbi; - _account = new(accountPrivateKey, chainId: chainId); - _web3Client = new(_account, apiUrl); + _web3 = web3; } public async Task<decimal> TotalSupply() { - var totalSupplyHandler = _web3Client.Eth.GetContractQueryHandler<TotalSupplyFunction>(); + var totalSupplyHandler = _web3.Eth.GetContractQueryHandler<TotalSupplyFunction>(); var totalSupplyFunction = new TotalSupplyFunction(); - var totalSupply = await totalSupplyHandler.QueryAsync<BigInteger>(Contract.Address, totalSupplyFunction); + var totalSupply = await totalSupplyHandler.QueryAsync<BigInteger>(_tokenAddress, totalSupplyFunction); return ConvertSmallUnitToBigUnit(totalSupply); } public async Task Mint(string recipientAddress, BigInteger amount, CancellationToken cancellationToken) { - var mintHandler = _web3Client.Eth.GetContractTransactionHandler<MintFunction>(); + var mintHandler = _web3.Eth.GetContractTransactionHandler<MintFunction>(); var mintFunction = new MintFunction() { To = recipientAddress, Amount = amount, }; - await mintHandler.SendRequestAndWaitForReceiptAsync(Contract.Address, mintFunction, cancellationToken); + await mintHandler.SendRequestAndWaitForReceiptAsync(_tokenAddress, mintFunction, cancellationToken); } public async Task Burn(BigInteger amount, CancellationToken cancellationToken) { - var burnHandler = _web3Client.Eth.GetContractTransactionHandler<BurnFunction>(); + var burnHandler = _web3.Eth.GetContractTransactionHandler<BurnFunction>(); var burnFunction = new BurnFunction() { Amount = amount, }; - await burnHandler.SendRequestAndWaitForReceiptAsync(Contract.Address, burnFunction, cancellationToken); + await burnHandler.SendRequestAndWaitForReceiptAsync(_tokenAddress, burnFunction, cancellationToken); } public decimal ConvertSmallUnitToBigUnit(BigInteger smallUnit) @@ -80,10 +70,43 @@ public class TokenClient Spender = spenderAddress }; - var handler = _web3Client.Eth.GetContractQueryHandler<AllowanceFunction>(); + var handler = _web3.Eth.GetContractQueryHandler<AllowanceFunction>(); var allowance = await handler.QueryAsync<BigInteger>(_tokenAddress, allowanceFunction); return allowance; } + public async Task<string> ApproveMaxAsync(string spenderAddress) + { + return await ApproveAsync(spenderAddress, BigInteger.Pow(2, 256) - 1); + } + + public async Task<string> ApproveAsync(string spenderAddress, BigInteger amount) + { + var approveFunction = new Nethereum.Contracts.Standards.ERC20.ContractDefinition.ApproveFunction + { + Spender = spenderAddress, + Value = amount + }; + + var handler = _web3.Eth.GetContractTransactionHandler<Nethereum.Contracts.Standards.ERC20.ContractDefinition.ApproveFunction>(); + var transactionReceipt = await handler.SendRequestAndWaitForReceiptAsync(_tokenAddress, approveFunction); + + return transactionReceipt.TransactionHash; + } + + public async Task<string> TransferAsync(string to, BigInteger amount) + { + var transferFunction = new TransferFunction + { + To = to, + Value = amount + }; + + var handler = _web3.Eth.GetContractTransactionHandler<TransferFunction>(); + var transactionReceipt = await handler.SendRequestAndWaitForReceiptAsync(_tokenAddress, transferFunction); + + return transactionReceipt.TransactionHash; + } + } -- GitLab