diff --git a/Frontend/BackgroundServices/PrometheusService.cs b/Frontend/BackgroundServices/PrometheusService.cs index 9b3bf54f4ca0202149b26ca95c37d802c6e3ab28..0a617016651548d32404b8ced5c72ee365fb2f7d 100644 --- a/Frontend/BackgroundServices/PrometheusService.cs +++ b/Frontend/BackgroundServices/PrometheusService.cs @@ -1,22 +1,31 @@ using System.Diagnostics.Metrics; +using Frontend.Configuration; using Frontend.Metrics; - +using Nethereum.Web3; +using Nethereum.Web3.Accounts; namespace Frontend.BackgroundServices; public class PrometheusService : BackgroundService { - private readonly ILogger<PrometheusService> _logger; + private readonly ILogger<PrometheusService> _logger ; private readonly TimeSpan _interval = TimeSpan.FromSeconds(10); private readonly Erc20TokenMetrics _erc20TokenMetrics; private readonly EthMetrics _ethMetrics; private readonly MeterListener _meterListener; + private readonly PoolMetrics _poolMetrics; + + public PrometheusService( ILogger<PrometheusService> logger, Erc20TokenMetrics tokenMetrics, EthMetrics ethMetrics, + PoolMetrics poolMetrics, + ChainSettings chainSettings, + Web3 web3, + Account account, PotatoStorageMetrics potatoStorageMetrics, PotatoMarketplaceMetrics potatoMarketplaceMetrics ) @@ -24,6 +33,7 @@ public class PrometheusService : BackgroundService _logger = logger; _erc20TokenMetrics = tokenMetrics; _ethMetrics = ethMetrics; + _poolMetrics = poolMetrics; _interval = TimeSpan.FromSeconds(10); _meterListener = new MeterListener(); _meterListener.InstrumentPublished = (instrument, listener) => @@ -35,6 +45,8 @@ public class PrometheusService : BackgroundService _ethMetrics.CreateMetrics(); potatoStorageMetrics.CreateMetrics(); potatoMarketplaceMetrics.CreateMetrics(); + PoolV3Client poolClient = new( web3,chainSettings,account); + _poolMetrics.CreateMetrics(poolClient); } protected async override Task ExecuteAsync(CancellationToken stoppingToken) diff --git a/Frontend/Configuration/Abi/0x0227628f3F023bb0B980b67D528571c95c6DaC1c.abi.json b/Frontend/Configuration/Abi/0x0227628f3F023bb0B980b67D528571c95c6DaC1c.abi.json new file mode 100644 index 0000000000000000000000000000000000000000..e2354a6ca54ba0579d35ea868e5a5bbc1bdbf4b4 --- /dev/null +++ b/Frontend/Configuration/Abi/0x0227628f3F023bb0B980b67D528571c95c6DaC1c.abi.json @@ -0,0 +1 @@ +[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"fee","type":"uint24"},{"indexed":true,"internalType":"int24","name":"tickSpacing","type":"int24"}],"name":"FeeAmountEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token0","type":"address"},{"indexed":true,"internalType":"address","name":"token1","type":"address"},{"indexed":true,"internalType":"uint24","name":"fee","type":"uint24"},{"indexed":false,"internalType":"int24","name":"tickSpacing","type":"int24"},{"indexed":false,"internalType":"address","name":"pool","type":"address"}],"name":"PoolCreated","type":"event"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"}],"name":"createPool","outputs":[{"internalType":"address","name":"pool","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"int24","name":"tickSpacing","type":"int24"}],"name":"enableFeeAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint24","name":"","type":"uint24"}],"name":"feeAmountTickSpacing","outputs":[{"internalType":"int24","name":"","type":"int24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint24","name":"","type":"uint24"}],"name":"getPool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"parameters","outputs":[{"internalType":"address","name":"factory","type":"address"},{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"int24","name":"tickSpacing","type":"int24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/Frontend/Configuration/Abi/swapRouterV2.abi.json b/Frontend/Configuration/Abi/swapRouterV2.abi.json new file mode 100644 index 0000000000000000000000000000000000000000..dbbcca0879cd5b90a77d86692ff029707a3e3477 --- /dev/null +++ b/Frontend/Configuration/Abi/swapRouterV2.abi.json @@ -0,0 +1,1063 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_factoryV2", + "type": "address" + }, + { + "internalType": "address", + "name": "factoryV3", + "type": "address" + }, + { + "internalType": "address", + "name": "_positionManager", + "type": "address" + }, + { + "internalType": "address", + "name": "_WETH9", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "WETH9", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "approveMax", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "approveMaxMinusOne", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "approveZeroThenMax", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "approveZeroThenMaxMinusOne", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "callPositionManager", + "outputs": [ + { + "internalType": "bytes", + "name": "result", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes[]", + "name": "paths", + "type": "bytes[]" + }, + { + "internalType": "uint128[]", + "name": "amounts", + "type": "uint128[]" + }, + { + "internalType": "uint24", + "name": "maximumTickDivergence", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "secondsAgo", + "type": "uint32" + } + ], + "name": "checkOracleSlippage", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "path", + "type": "bytes" + }, + { + "internalType": "uint24", + "name": "maximumTickDivergence", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "secondsAgo", + "type": "uint32" + } + ], + "name": "checkOracleSlippage", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes", + "name": "path", + "type": "bytes" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMinimum", + "type": "uint256" + } + ], + "internalType": "struct IV3SwapRouter.ExactInputParams", + "name": "params", + "type": "tuple" + } + ], + "name": "exactInput", + "outputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenOut", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMinimum", + "type": "uint256" + }, + { + "internalType": "uint160", + "name": "sqrtPriceLimitX96", + "type": "uint160" + } + ], + "internalType": "struct IV3SwapRouter.ExactInputSingleParams", + "name": "params", + "type": "tuple" + } + ], + "name": "exactInputSingle", + "outputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes", + "name": "path", + "type": "bytes" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountInMaximum", + "type": "uint256" + } + ], + "internalType": "struct IV3SwapRouter.ExactOutputParams", + "name": "params", + "type": "tuple" + } + ], + "name": "exactOutput", + "outputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenOut", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountInMaximum", + "type": "uint256" + }, + { + "internalType": "uint160", + "name": "sqrtPriceLimitX96", + "type": "uint160" + } + ], + "internalType": "struct IV3SwapRouter.ExactOutputSingleParams", + "name": "params", + "type": "tuple" + } + ], + "name": "exactOutputSingle", + "outputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "factoryV2", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "getApprovalType", + "outputs": [ + { + "internalType": "enum IApproveAndCall.ApprovalType", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount0Min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1Min", + "type": "uint256" + } + ], + "internalType": "struct IApproveAndCall.IncreaseLiquidityParams", + "name": "params", + "type": "tuple" + } + ], + "name": "increaseLiquidity", + "outputs": [ + { + "internalType": "bytes", + "name": "result", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "uint256", + "name": "amount0Min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1Min", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "internalType": "struct IApproveAndCall.MintParams", + "name": "params", + "type": "tuple" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "bytes", + "name": "result", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "previousBlockhash", + "type": "bytes32" + }, + { + "internalType": "bytes[]", + "name": "data", + "type": "bytes[]" + } + ], + "name": "multicall", + "outputs": [ + { + "internalType": "bytes[]", + "name": "", + "type": "bytes[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bytes[]", + "name": "data", + "type": "bytes[]" + } + ], + "name": "multicall", + "outputs": [ + { + "internalType": "bytes[]", + "name": "", + "type": "bytes[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes[]", + "name": "data", + "type": "bytes[]" + } + ], + "name": "multicall", + "outputs": [ + { + "internalType": "bytes[]", + "name": "results", + "type": "bytes[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "positionManager", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "pull", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "refundETH", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "selfPermit", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expiry", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "selfPermitAllowed", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expiry", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "selfPermitAllowedIfNecessary", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "selfPermitIfNecessary", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "swapExactTokensForTokens", + "outputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountInMax", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "swapTokensForExactTokens", + "outputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountMinimum", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "sweepToken", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountMinimum", + "type": "uint256" + } + ], + "name": "sweepToken", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountMinimum", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeBips", + "type": "uint256" + }, + { + "internalType": "address", + "name": "feeRecipient", + "type": "address" + } + ], + "name": "sweepTokenWithFee", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountMinimum", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "feeBips", + "type": "uint256" + }, + { + "internalType": "address", + "name": "feeRecipient", + "type": "address" + } + ], + "name": "sweepTokenWithFee", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int256", + "name": "amount0Delta", + "type": "int256" + }, + { + "internalType": "int256", + "name": "amount1Delta", + "type": "int256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "uniswapV3SwapCallback", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountMinimum", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "unwrapWETH9", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountMinimum", + "type": "uint256" + } + ], + "name": "unwrapWETH9", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountMinimum", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "feeBips", + "type": "uint256" + }, + { + "internalType": "address", + "name": "feeRecipient", + "type": "address" + } + ], + "name": "unwrapWETH9WithFee", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountMinimum", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeBips", + "type": "uint256" + }, + { + "internalType": "address", + "name": "feeRecipient", + "type": "address" + } + ], + "name": "unwrapWETH9WithFee", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "wrapETH", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] \ No newline at end of file diff --git a/Frontend/Configuration/Abi/uniswapV3Pool.abi.json b/Frontend/Configuration/Abi/uniswapV3Pool.abi.json new file mode 100644 index 0000000000000000000000000000000000000000..bd99dfb44598dde0bd52e61ab3ed557ace49a7d6 --- /dev/null +++ b/Frontend/Configuration/Abi/uniswapV3Pool.abi.json @@ -0,0 +1 @@ +[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"int24","name":"tickLower","type":"int24"},{"indexed":true,"internalType":"int24","name":"tickUpper","type":"int24"},{"indexed":false,"internalType":"uint128","name":"amount","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"int24","name":"tickLower","type":"int24"},{"indexed":true,"internalType":"int24","name":"tickUpper","type":"int24"},{"indexed":false,"internalType":"uint128","name":"amount0","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"amount1","type":"uint128"}],"name":"Collect","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint128","name":"amount0","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"amount1","type":"uint128"}],"name":"CollectProtocol","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"paid0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"paid1","type":"uint256"}],"name":"Flash","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"observationCardinalityNextOld","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"observationCardinalityNextNew","type":"uint16"}],"name":"IncreaseObservationCardinalityNext","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint160","name":"sqrtPriceX96","type":"uint160"},{"indexed":false,"internalType":"int24","name":"tick","type":"int24"}],"name":"Initialize","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"int24","name":"tickLower","type":"int24"},{"indexed":true,"internalType":"int24","name":"tickUpper","type":"int24"},{"indexed":false,"internalType":"uint128","name":"amount","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"feeProtocol0Old","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"feeProtocol1Old","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"feeProtocol0New","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"feeProtocol1New","type":"uint8"}],"name":"SetFeeProtocol","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"int256","name":"amount0","type":"int256"},{"indexed":false,"internalType":"int256","name":"amount1","type":"int256"},{"indexed":false,"internalType":"uint160","name":"sqrtPriceX96","type":"uint160"},{"indexed":false,"internalType":"uint128","name":"liquidity","type":"uint128"},{"indexed":false,"internalType":"int24","name":"tick","type":"int24"}],"name":"Swap","type":"event"},{"inputs":[{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"uint128","name":"amount","type":"uint128"}],"name":"burn","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"uint128","name":"amount0Requested","type":"uint128"},{"internalType":"uint128","name":"amount1Requested","type":"uint128"}],"name":"collect","outputs":[{"internalType":"uint128","name":"amount0","type":"uint128"},{"internalType":"uint128","name":"amount1","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint128","name":"amount0Requested","type":"uint128"},{"internalType":"uint128","name":"amount1Requested","type":"uint128"}],"name":"collectProtocol","outputs":[{"internalType":"uint128","name":"amount0","type":"uint128"},{"internalType":"uint128","name":"amount1","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fee","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeGrowthGlobal0X128","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeGrowthGlobal1X128","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"flash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"observationCardinalityNext","type":"uint16"}],"name":"increaseObservationCardinalityNext","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint160","name":"sqrtPriceX96","type":"uint160"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liquidity","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxLiquidityPerTick","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"mint","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"observations","outputs":[{"internalType":"uint32","name":"blockTimestamp","type":"uint32"},{"internalType":"int56","name":"tickCumulative","type":"int56"},{"internalType":"uint160","name":"secondsPerLiquidityCumulativeX128","type":"uint160"},{"internalType":"bool","name":"initialized","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32[]","name":"secondsAgos","type":"uint32[]"}],"name":"observe","outputs":[{"internalType":"int56[]","name":"tickCumulatives","type":"int56[]"},{"internalType":"uint160[]","name":"secondsPerLiquidityCumulativeX128s","type":"uint160[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"positions","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint256","name":"feeGrowthInside0LastX128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthInside1LastX128","type":"uint256"},{"internalType":"uint128","name":"tokensOwed0","type":"uint128"},{"internalType":"uint128","name":"tokensOwed1","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFees","outputs":[{"internalType":"uint128","name":"token0","type":"uint128"},{"internalType":"uint128","name":"token1","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"feeProtocol0","type":"uint8"},{"internalType":"uint8","name":"feeProtocol1","type":"uint8"}],"name":"setFeeProtocol","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"slot0","outputs":[{"internalType":"uint160","name":"sqrtPriceX96","type":"uint160"},{"internalType":"int24","name":"tick","type":"int24"},{"internalType":"uint16","name":"observationIndex","type":"uint16"},{"internalType":"uint16","name":"observationCardinality","type":"uint16"},{"internalType":"uint16","name":"observationCardinalityNext","type":"uint16"},{"internalType":"uint8","name":"feeProtocol","type":"uint8"},{"internalType":"bool","name":"unlocked","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"}],"name":"snapshotCumulativesInside","outputs":[{"internalType":"int56","name":"tickCumulativeInside","type":"int56"},{"internalType":"uint160","name":"secondsPerLiquidityInsideX128","type":"uint160"},{"internalType":"uint32","name":"secondsInside","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bool","name":"zeroForOne","type":"bool"},{"internalType":"int256","name":"amountSpecified","type":"int256"},{"internalType":"uint160","name":"sqrtPriceLimitX96","type":"uint160"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swap","outputs":[{"internalType":"int256","name":"amount0","type":"int256"},{"internalType":"int256","name":"amount1","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int16","name":"","type":"int16"}],"name":"tickBitmap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tickSpacing","outputs":[{"internalType":"int24","name":"","type":"int24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int24","name":"","type":"int24"}],"name":"ticks","outputs":[{"internalType":"uint128","name":"liquidityGross","type":"uint128"},{"internalType":"int128","name":"liquidityNet","type":"int128"},{"internalType":"uint256","name":"feeGrowthOutside0X128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthOutside1X128","type":"uint256"},{"internalType":"int56","name":"tickCumulativeOutside","type":"int56"},{"internalType":"uint160","name":"secondsPerLiquidityOutsideX128","type":"uint160"},{"internalType":"uint32","name":"secondsOutside","type":"uint32"},{"internalType":"bool","name":"initialized","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token0","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token1","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/Frontend/Configuration/ChainSettings.cs b/Frontend/Configuration/ChainSettings.cs new file mode 100644 index 0000000000000000000000000000000000000000..c644f88160ff728fe4a9f652d32a42ec3585789c --- /dev/null +++ b/Frontend/Configuration/ChainSettings.cs @@ -0,0 +1,11 @@ +namespace Frontend.Configuration; +public class ChainSettings + { + public int ChainId { get; set; } + public string TokenAddress { get; set; } + public string WethTokenAddress { get; set; } + public UniswapSettings Uniswap { get; set; } + + public string PrivateKey { get; set; } + + } \ No newline at end of file diff --git a/Frontend/Configuration/UniswapSettings.cs b/Frontend/Configuration/UniswapSettings.cs new file mode 100644 index 0000000000000000000000000000000000000000..0ea0580b49d805def97778cf815e87e09e1803e2 --- /dev/null +++ b/Frontend/Configuration/UniswapSettings.cs @@ -0,0 +1,11 @@ + namespace Frontend.Configuration; + public class UniswapSettings + { + public string UniswapV3Factory { get; set; } + public string QuoterV2 { get; set; } + + public string SwapRouterV2Address { get; set; } + public string RocEthPoolAddress {get;set;} + + public string PermitV2Address {get;set;} + } \ No newline at end of file diff --git a/Frontend/Events/SwapEventDTO.cs b/Frontend/Events/SwapEventDTO.cs new file mode 100644 index 0000000000000000000000000000000000000000..2acc86b76f134d4f2f3effc6d24865dd6be0f770 --- /dev/null +++ b/Frontend/Events/SwapEventDTO.cs @@ -0,0 +1,15 @@ +using Nethereum.ABI.FunctionEncoding.Attributes; +using Nethereum.Contracts; +using System.Numerics; + +namespace Frontend.Events; + +[Event("Swap")] +public class SwapEventDTO : IEventDTO +{ + [Parameter("uint256", "amountIn", 1, true)] + public BigInteger AmountIn { get; set; } + + [Parameter("uint256", "amountOut", 2, true)] + public BigInteger AmountOut { get; set; } +} \ No newline at end of file diff --git a/Frontend/Functions/ExactSingleParamBase.cs b/Frontend/Functions/ExactSingleParamBase.cs new file mode 100644 index 0000000000000000000000000000000000000000..935dc61eec598862641a317249f482cd6de8d99b --- /dev/null +++ b/Frontend/Functions/ExactSingleParamBase.cs @@ -0,0 +1,38 @@ +using System.Numerics; +using Nethereum.Hex.HexTypes; +using Nethereum.ABI.FunctionEncoding.Attributes; +using Nethereum.Web3; +using Nethereum.RPC.Eth.DTOs; +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 partial class ExactInputSingleParams : ExactInputSingleParamsBase { } + + +[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 diff --git a/Frontend/Functions/FeeFunction.cs b/Frontend/Functions/FeeFunction.cs new file mode 100644 index 0000000000000000000000000000000000000000..1f2add2b8324515212d15daba1bd533fed61c21f --- /dev/null +++ b/Frontend/Functions/FeeFunction.cs @@ -0,0 +1,9 @@ +using Nethereum.ABI.FunctionEncoding.Attributes; +using Nethereum.Contracts; + +namespace Frontend.Functions; + +[Function("fee", "uint24")] +public class FeeFunction : FunctionMessage{ + +} \ No newline at end of file diff --git a/Frontend/Functions/Slot0Function.cs b/Frontend/Functions/Slot0Function.cs new file mode 100644 index 0000000000000000000000000000000000000000..345bf78115c5f93a1379aba8b8cd7c28c1f40c1e --- /dev/null +++ b/Frontend/Functions/Slot0Function.cs @@ -0,0 +1,9 @@ +using Nethereum.ABI.FunctionEncoding.Attributes; +using Nethereum.Contracts; + +namespace Frontend.Functions; + +[Function("slot0")] +public class Slot0Funtion : FunctionMessage +{ +} diff --git a/Frontend/Functions/Slot0OutputDTO.cs b/Frontend/Functions/Slot0OutputDTO.cs new file mode 100644 index 0000000000000000000000000000000000000000..6615f5e312b02d5dad6b701dd4fe1d1c2a0a9625 --- /dev/null +++ b/Frontend/Functions/Slot0OutputDTO.cs @@ -0,0 +1,23 @@ +using System.Numerics; +using Nethereum.ABI.FunctionEncoding.Attributes; + +namespace Frontend.Functions; + +[FunctionOutput] +public class Slot0OutputDTO :IFunctionOutputDTO +{ + [Parameter("uint160", "sqrtPriceX96")] + public BigInteger SqrtPriceX96 { get; set; } + [Parameter( "int24","tick")] + public int Tick { get; set; } + [Parameter( "uint16","observationIndex")] + public ushort ObservationIndex { get; set; } + [Parameter("uint16","observationCardinality")] + public ushort ObservationCardinality { get; set; } + [Parameter("uint16","observationCardinalityNext")] + public ushort ObservationCardinalityNext { get; set; } + [Parameter( "uint8","feeProtocol")] + public byte FeeProtocol { get; set; } + [Parameter( "bool","unlocked")] + public bool Unlocked { get; set; } +} \ No newline at end of file diff --git a/Frontend/Functions/Token0Function.cs b/Frontend/Functions/Token0Function.cs new file mode 100644 index 0000000000000000000000000000000000000000..38ab0f836af8592ae8edfab15aaae79a8fa1be4a --- /dev/null +++ b/Frontend/Functions/Token0Function.cs @@ -0,0 +1,11 @@ +using System.Numerics; +using Nethereum.ABI.FunctionEncoding.Attributes; +using Nethereum.Contracts; + +namespace Frontend.Functions; + +[Function("token0","address")] +public class Token0Function : FunctionMessage +{ + +} diff --git a/Frontend/Functions/Token1Function.cs b/Frontend/Functions/Token1Function.cs new file mode 100644 index 0000000000000000000000000000000000000000..60b321acfb7137b1764a21aa3fef8314959e500c --- /dev/null +++ b/Frontend/Functions/Token1Function.cs @@ -0,0 +1,11 @@ +using System.Numerics; +using Nethereum.ABI.FunctionEncoding.Attributes; +using Nethereum.Contracts; + +namespace Frontend.Functions; + +[Function("token1","address")] +public class Token1Function : FunctionMessage +{ + +} diff --git a/Frontend/Metrics/Erc20TokenMetrics.cs b/Frontend/Metrics/Erc20TokenMetrics.cs index 39dca2842f10a26a28b5f19cf3d03eb493dcf132..a6b4ed8ce5789f7204ef31f2c7873c12ea123c6a 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/Metrics/EthMetrics.cs b/Frontend/Metrics/EthMetrics.cs index 5a60e1f270671bda5f8b38099a9ca49208a8d09f..6395b06abee2834e03313fb9df77e5d79d95f1c6 100644 --- a/Frontend/Metrics/EthMetrics.cs +++ b/Frontend/Metrics/EthMetrics.cs @@ -26,7 +26,7 @@ public class EthMetrics{ response.EnsureSuccessStatusCode(); string responseBody = response.Content.ReadAsStringAsync().Result; JObject json = JObject.Parse(responseBody); - double priceInChf = json["CHF"].Value<double>(); + double priceInChf = json["CHF"]!.Value<double>(); return priceInChf; } diff --git a/Frontend/Metrics/PoolMetrics.cs b/Frontend/Metrics/PoolMetrics.cs new file mode 100644 index 0000000000000000000000000000000000000000..2c0ea46c21f51b04c8ad3a4a5e29cecd82c0f372 --- /dev/null +++ b/Frontend/Metrics/PoolMetrics.cs @@ -0,0 +1,22 @@ +using System.Diagnostics.Metrics; +namespace Frontend.Metrics; + +public class PoolMetrics{ + private readonly IMeterFactory _meterFactory; + private ObservableGauge<double>? _rocethGauge; + + public PoolMetrics(IMeterFactory meterFactory) + { + _meterFactory = meterFactory; + } + + public void CreateMetrics(PoolV3Client poolClient) + { + var meter = _meterFactory.Create("RoestiCoin"); + _rocethGauge = meter.CreateObservableGauge("Uniswap.ROCWETH.price", () => GetCurrentPrice(poolClient), unit: "WETH/ROC"); + } + + public double GetCurrentPrice(PoolV3Client poolClient){ + return (double)poolClient.GetPairRatio().Result; + } +} \ No newline at end of file diff --git a/Frontend/Pages/Index.cshtml.cs b/Frontend/Pages/Index.cshtml.cs index 5c1304ab7cee86d31613da59207cc2b49e5d5ce6..080720e6e6a8a6accec5441c98b86ad9af090380 100644 --- a/Frontend/Pages/Index.cshtml.cs +++ b/Frontend/Pages/Index.cshtml.cs @@ -1,19 +1,46 @@ -using Microsoft.AspNetCore.Mvc; +using Frontend.Configuration; using Microsoft.AspNetCore.Mvc.RazorPages; +using Nethereum.Web3; +using Nethereum.Web3.Accounts; namespace Frontend.Pages; public class IndexModel : PageModel { private readonly ILogger<IndexModel> _logger; + private readonly PoolV3Client _poolV3Client; + private readonly ChainSettings _chainSettings; + private readonly Account _account; - public IndexModel(ILogger<IndexModel> logger) + public IndexModel(ILogger<IndexModel> logger, Web3 web3, ChainSettings chainSettings, Account account) { _logger = logger; + _poolV3Client = new PoolV3Client(web3, chainSettings, account); + _chainSettings = chainSettings; + _account = account; } public void OnGet() { + string? fromTokenAddress = null; + string? toTokenAddress = null; + decimal amount = 0; + if (Request.Query.ContainsKey("swapROCToETH")) + { + fromTokenAddress = _chainSettings.TokenAddress; + toTokenAddress = _chainSettings.WethTokenAddress; + amount = decimal.Parse(Request.Query["swapROCToETH"]); + + }else if (Request.Query.ContainsKey("swapETHToROC")) + { + fromTokenAddress = _chainSettings.WethTokenAddress; + toTokenAddress = _chainSettings.TokenAddress; + amount = decimal.Parse(Request.Query["swapETHToROC"]); + } + if (amount > 0) + { + var amountRecieved = _poolV3Client.SwapAsync(fromTokenAddress!, toTokenAddress!, amount).Result; + } } } diff --git a/Frontend/PoolV3Client.cs b/Frontend/PoolV3Client.cs new file mode 100644 index 0000000000000000000000000000000000000000..5d1cba47df8e418efab085fdd43384d442a4501e --- /dev/null +++ b/Frontend/PoolV3Client.cs @@ -0,0 +1,226 @@ +using System.Numerics; +using Frontend.Functions; +using Frontend.Events; +using Nethereum.Contracts; +using Nethereum.Hex.HexTypes; +using Nethereum.Web3; +using Nethereum.Web3.Accounts; + +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; + +public class PoolV3Client +{ + private readonly Contract _pool; + private readonly Web3 _web3; + private readonly ChainSettings _chainSettings; + private readonly Account _account; + private string? _token0Address = null; + private string? _token1Address = null; + private uint? _poolFeeTier = null; + 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, Account account) + { + _account = account; + _pool = web3.Eth.GetContract( + File.ReadAllText( + Path.Combine( + Directory.GetCurrentDirectory(), + "Configuration", + "Abi", + "uniswapV3Pool.abi.json" + ) + ), + chainSettings.Uniswap.RocEthPoolAddress + ); + _web3 = web3; + _chainSettings = chainSettings; + } + + public async Task<decimal> GetPairRatio() + { + 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); + } + + public double SqrtPriceX96ToPrice(BigInteger sqrtPriceX96) + { + var priceX96 = sqrtPriceX96 * sqrtPriceX96; + return (double)priceX96 / Math.Pow(2, 192); + } + + public BigInteger PriceToSqrtPriceX96(double price) + { + return new BigInteger(Math.Sqrt(price) * Math.Pow(2, 96)); + } + + private async Task<string> QueryToken0Address() + { + if (null == _token0Address) + { + var handler = _web3.Eth.GetContractQueryHandler<Token0Function>(); + var func = new Token0Function(); + var result = await handler.QueryAsync<string>(_pool.Address, func); + _token0Address = result; + } + + return _token0Address; + } + + private async Task<string> QueryToken1Address() + { + if (null == _token1Address) + { + var handler = _web3.Eth.GetContractQueryHandler<Token1Function>(); + var func = new Token1Function(); + var result = await handler.QueryAsync<string>(_pool.Address, func); + _token1Address = result; + } + + return _token1Address; + } + + public async Task<uint> getPoolFeeTier() + { + 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; + } + return (uint)_poolFeeTier; + } + + + /* + this function should swap the tokens in the pool + */ + public async Task<decimal> SwapAsync(string fromTokenAddress, string toTokenAddress, decimal amount, decimal amountOutMinimum = 0, uint sqrtPriceLimitX96 = 0, bool doApproval = true) + { + + 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 + { + TokenIn = fromTokenAddress, + TokenOut = toTokenAddress, + Fee = await getPoolFeeTier(), + Recipient = _account.Address, + AmountIn = Web3.Convert.ToWei(amount), + AmountOutMinimum = Web3.Convert.ToWei(amountOutMinimum), // Set your minimum amount out + 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"); + } + + /// <summary> + /// 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) + { + 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); + } + else + { + amount = 1; + throw new NotImplementedException(); + await SwapAsync(Token1Address, Token0Address, amount); + } + throw new NotImplementedException(); + } + +} \ No newline at end of file diff --git a/Frontend/Program.cs b/Frontend/Program.cs index 1b91a6c296b9247bcfa7773987f841408699f235..de4846dfcab0ff3d866bf6e498af6dabfbb4a974 100644 --- a/Frontend/Program.cs +++ b/Frontend/Program.cs @@ -1,12 +1,18 @@ +using System.Runtime.CompilerServices; using Frontend; using Frontend.AssetStores; using Frontend.BackgroundServices; +using Frontend.Configuration; using Frontend.Marketplaces; using Frontend.Marketplaces.Interfaces; using Frontend.Metrics; +using Nethereum.Web3; +using Nethereum.Web3.Accounts; using Microsoft.EntityFrameworkCore; + using OpenTelemetry.Metrics; using OpenTelemetry.Resources; +using Org.BouncyCastle.Utilities; // the location differ in dev and releas mode. var tokenAbiLocation = Environment.GetEnvironmentVariable("TOKEN_ABI_LOCATION")!; @@ -14,7 +20,6 @@ var tokenAbi = File.ReadAllText(tokenAbiLocation)!; var chainApiUrl = Environment.GetEnvironmentVariable("API_URL")!; var accountPrivateKey = Environment.GetEnvironmentVariable("ACCOUNT_PRIVATE_KEY")!; - var postgresHost = Environment.GetEnvironmentVariable("POSTGRES_HOST")!; var postgresUser = Environment.GetEnvironmentVariable("POSTGRES_USER")!; var postgresPassword = Environment.GetEnvironmentVariable("POSTGRES_PASSWORD")!; @@ -24,6 +29,8 @@ var builder = WebApplication.CreateBuilder(args); // Configurations var chainConfiguration = builder.Configuration.GetSection("Chain"); +var chainSettings = new ChainSettings(); +chainConfiguration.Bind(chainSettings); // Add services to the container. builder.Services.AddHostedService<PrometheusService>(); @@ -33,12 +40,25 @@ builder.Services.AddSingleton<PotatoStorageMetrics>(); builder.Services.AddSingleton<PotatoMarketplaceMetrics>(); builder.Services.AddSingleton<IPotatoMarketPlace>(sp => new PotatoMarketplaceMock()); -builder.Services.AddSingleton(provider => new TokenClient( - chainApiUrl, - chainConfiguration["tokenAddress"]!, - tokenAbi, - accountPrivateKey -)); +Account account = new Account( + accountPrivateKey, + chainId: chainSettings.ChainId +); + +builder.Services.AddSingleton(provider => chainSettings); + + +builder.Services.AddSingleton(provider=>account); +builder.Services.AddTransient<Web3>(sp => +{ + var chainSettings = sp.GetRequiredService<ChainSettings>(); + var account = sp.GetRequiredService<Account>(); + return new Web3(account, chainApiUrl); +}); + +builder.Services.AddSingleton<PoolMetrics>(); + + builder.Services.AddDbContextFactory<PotatoStorage>(options => options.UseNpgsql($"Host={postgresHost};Database={postgresDb};Username={postgresUser};Password={postgresPassword}")); diff --git a/Frontend/TokenClient.cs b/Frontend/TokenClient.cs index 225a0057ed6435b3e339713c56766f2d8f687cb9..61ab43e2171f1df5e98959bc7f8d0d61c1959920 100644 --- a/Frontend/TokenClient.cs +++ b/Frontend/TokenClient.cs @@ -11,61 +11,102 @@ 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 - ) { + Web3 web3, + string tokenAddress + ) + { _tokenAddress = tokenAddress; - _tokenAbi = tokenAbi; - _account = new(accountPrivateKey, chainId: 11155111); - _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) { return (decimal)(smallUnit / (BigInteger)Math.Pow(10, Decimals)); } + + + public async Task<BigInteger> GetAllowanceAsync(string ownerAddress, string spenderAddress) + { + var allowanceFunction = new AllowanceFunction + { + Owner = ownerAddress, + Spender = spenderAddress + }; + + 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; + } + } diff --git a/Frontend/appsettings.json b/Frontend/appsettings.json index 04fe56ad6736712b93359c895d547d4d078776a4..250caa99b3e702db7e74405ab7d9c4dea517ab3a 100644 --- a/Frontend/appsettings.json +++ b/Frontend/appsettings.json @@ -6,7 +6,16 @@ } }, "Chain": { - "tokenAddress": "0xaF9EeB7b30fb1e1De40b284a3a92F5cB8E93cfff" + "ChainId": 11155111, + "TokenAddress": "0xaF9EeB7b30fb1e1De40b284a3a92F5cB8E93cfff", + "WethTokenAddress":"0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14", + "Uniswap": { + "UniswapV3Factory":"0x0227628f3F023bb0B980b67D528571c95c6DaC1c", + "QuoterV2":"0xEd1f6473345F45b75F8179591dd5bA1888cf2FB3", + "RocEthPoolAddress": "0xF22CA6B27eaC677f132945C50Cd3499A9b8a6CBC", + "SwapRouterV2Address": "0x3bFA4769FB09eefC5a80d6E87c3B9C650f7Ae48E", + "PermitV2Address":"0x000000000022D473030F116dDEE9F6B43aC78BA3" + } }, "AllowedHosts": "*", "Urls": "http://0.0.0.0:8080" diff --git a/grafana/dashboards/totalSupply.json b/grafana/dashboards/totalSupply.json index 6d9200dd53fc052afe206a9457a3b8f7d6fc2f7b..9809b18d3da31e67fede240c54d44e9ac6a42723 100644 --- a/grafana/dashboards/totalSupply.json +++ b/grafana/dashboards/totalSupply.json @@ -87,6 +87,224 @@ "x": 0, "y": 0 }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.0+security-01", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "Ethereum_Price_Current_CHF * Uniswap_ROCWETH_price_WETH_per_ROC", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "price", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "avg_over_time(Ethereum_Price_Current_CHF[5m]) * avg_over_time(Uniswap_ROCWETH_price_WETH_per_ROC[5m])", + "hide": false, + "instant": false, + "legendFormat": "Price average over 5 min", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "avg_over_time(Ethereum_Price_Current_CHF[2m]) * avg_over_time(Uniswap_ROCWETH_price_WETH_per_ROC[2m])", + "hide": false, + "instant": false, + "legendFormat": "Price average over 2 min", + "range": true, + "refId": "C" + } + ], + "title": "ROC Price in CHF", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.0+security-01", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "Uniswap_ROCWETH_price_WETH_per_ROC", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "WETH ROC", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, "id": 2, "options": { "legend": { @@ -181,7 +399,7 @@ "gridPos": { "h": 8, "w": 12, - "x": 0, + "x": 12, "y": 8 }, "id": 1, @@ -234,6 +452,6 @@ "timezone": "browser", "title": "Total Supply of ROC", "uid": "ce3nybz6khudcd", - "version": 1, + "version": 2, "weekStart": "" } \ No newline at end of file