diff --git a/packages/frontend/src/constants/abis/ArcherSwapRouter.json b/packages/frontend/src/constants/abis/ArcherSwapRouter.json index 096c697..35f9bbd 100644 --- a/packages/frontend/src/constants/abis/ArcherSwapRouter.json +++ b/packages/frontend/src/constants/abis/ArcherSwapRouter.json @@ -3,7 +3,12 @@ "inputs": [ { "internalType": "address", - "name": "_tipJar", + "name": "_uniV3Factory", + "type": "address" + }, + { + "internalType": "address", + "name": "_WETH", "type": "address" } ], @@ -11,121 +16,96 @@ "type": "constructor" }, { - "stateMutability": "payable", - "type": "fallback" + "inputs": [], + "name": "WETH", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" }, { "inputs": [ - { - "internalType": "contract IUniRouter", - "name": "router", - "type": "address" - }, { "components": [ { - "internalType": "address", - "name": "tokenA", - "type": "address" + "internalType": "bytes", + "name": "path", + "type": "bytes" }, { "internalType": "address", - "name": "tokenB", + "name": "recipient", "type": "address" }, { "internalType": "uint256", - "name": "amountADesired", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountBDesired", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountAMin", + "name": "deadline", "type": "uint256" }, { "internalType": "uint256", - "name": "amountBMin", + "name": "amountIn", "type": "uint256" }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, { "internalType": "uint256", - "name": "deadline", + "name": "amountOutMinimum", "type": "uint256" } ], - "internalType": "struct ArcherSwapRouter.AddLiquidity", - "name": "liquidity", + "internalType": "struct IUniV3Router.ExactInputParams", + "name": "params", "type": "tuple" } ], - "name": "addLiquidityAndTipAmount", - "outputs": [], + "name": "exactInput", + "outputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + } + ], "stateMutability": "payable", "type": "function" }, { "inputs": [ - { - "internalType": "contract IUniRouter", - "name": "router", - "type": "address" - }, { "components": [ { - "internalType": "address", - "name": "tokenA", - "type": "address" + "internalType": "bytes", + "name": "path", + "type": "bytes" }, { "internalType": "address", - "name": "tokenB", + "name": "recipient", "type": "address" }, { "internalType": "uint256", - "name": "amountADesired", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountBDesired", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountAMin", + "name": "deadline", "type": "uint256" }, { "internalType": "uint256", - "name": "amountBMin", + "name": "amountIn", "type": "uint256" }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, { "internalType": "uint256", - "name": "deadline", + "name": "amountOutMinimum", "type": "uint256" } ], - "internalType": "struct ArcherSwapRouter.AddLiquidity", - "name": "liquidity", + "internalType": "struct IUniV3Router.ExactInputParams", + "name": "params", "type": "tuple" }, { @@ -134,215 +114,157 @@ "type": "uint256" } ], - "name": "addLiquidityETHAndTipAmount", - "outputs": [], + "name": "exactInputAndTipAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + } + ], "stateMutability": "payable", "type": "function" }, { "inputs": [ - { - "internalType": "contract IUniRouter", - "name": "router", - "type": "address" - }, { "components": [ { "internalType": "address", - "name": "tokenA", + "name": "tokenIn", "type": "address" }, { "internalType": "address", - "name": "tokenB", + "name": "tokenOut", "type": "address" }, { - "internalType": "uint256", - "name": "amountADesired", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountBDesired", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountAMin", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountBMin", - "type": "uint256" + "internalType": "uint24", + "name": "fee", + "type": "uint24" }, { "internalType": "address", - "name": "to", + "name": "recipient", "type": "address" }, { "internalType": "uint256", "name": "deadline", "type": "uint256" - } - ], - "internalType": "struct ArcherSwapRouter.AddLiquidity", - "name": "liquidity", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "contract IERC20Extended", - "name": "token", - "type": "address" }, { "internalType": "uint256", - "name": "amount", + "name": "amountIn", "type": "uint256" }, { "internalType": "uint256", - "name": "deadline", + "name": "amountOutMinimum", "type": "uint256" }, { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" + "internalType": "uint160", + "name": "sqrtPriceLimitX96", + "type": "uint160" } ], - "internalType": "struct ArcherSwapRouter.Permit", - "name": "permit", + "internalType": "struct IUniV3Router.ExactInputSingleParams", + "name": "params", "type": "tuple" - }, + } + ], + "name": "exactInputSingle", + "outputs": [ { "internalType": "uint256", - "name": "tipAmount", + "name": "amountOut", "type": "uint256" } ], - "name": "addLiquidityETHWithPermitAndTipAmount", - "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [ - { - "internalType": "contract IUniRouter", - "name": "router", - "type": "address" - }, { "components": [ { "internalType": "address", - "name": "tokenA", + "name": "tokenIn", "type": "address" }, { "internalType": "address", - "name": "tokenB", + "name": "tokenOut", "type": "address" }, { - "internalType": "uint256", - "name": "amountADesired", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountBDesired", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountAMin", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountBMin", - "type": "uint256" + "internalType": "uint24", + "name": "fee", + "type": "uint24" }, { "internalType": "address", - "name": "to", + "name": "recipient", "type": "address" }, { "internalType": "uint256", "name": "deadline", "type": "uint256" - } - ], - "internalType": "struct ArcherSwapRouter.AddLiquidity", - "name": "liquidity", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "contract IERC20Extended", - "name": "token", - "type": "address" }, { "internalType": "uint256", - "name": "amount", + "name": "amountIn", "type": "uint256" }, { "internalType": "uint256", - "name": "deadline", + "name": "amountOutMinimum", "type": "uint256" }, { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" + "internalType": "uint160", + "name": "sqrtPriceLimitX96", + "type": "uint160" } ], - "internalType": "struct ArcherSwapRouter.Permit", - "name": "permitA", + "internalType": "struct IUniV3Router.ExactInputSingleParams", + "name": "params", "type": "tuple" }, + { + "internalType": "uint256", + "name": "tipAmount", + "type": "uint256" + } + ], + "name": "exactInputSingleAndTipAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ { "components": [ { - "internalType": "contract IERC20Extended", - "name": "token", - "type": "address" + "internalType": "bytes", + "name": "path", + "type": "bytes" }, { - "internalType": "uint256", - "name": "amount", - "type": "uint256" + "internalType": "address", + "name": "recipient", + "type": "address" }, { "internalType": "uint256", @@ -350,244 +272,232 @@ "type": "uint256" }, { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" }, { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" + "internalType": "uint256", + "name": "amountInMaximum", + "type": "uint256" } ], - "internalType": "struct ArcherSwapRouter.Permit", - "name": "permitB", + "internalType": "struct IUniV3Router.ExactOutputParams", + "name": "params", "type": "tuple" } ], - "name": "addLiquidityWithPermitAndTipAmount", - "outputs": [], + "name": "exactOutput", + "outputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + } + ], "stateMutability": "payable", "type": "function" }, { "inputs": [ - { - "internalType": "contract IUniRouter", - "name": "router", - "type": "address" - }, { "components": [ { - "internalType": "contract IERC20Extended", - "name": "lpToken", - "type": "address" - }, - { - "internalType": "address", - "name": "tokenA", - "type": "address" + "internalType": "bytes", + "name": "path", + "type": "bytes" }, { "internalType": "address", - "name": "tokenB", + "name": "recipient", "type": "address" }, { "internalType": "uint256", - "name": "liquidity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountAMin", + "name": "deadline", "type": "uint256" }, { "internalType": "uint256", - "name": "amountBMin", + "name": "amountOut", "type": "uint256" }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, { "internalType": "uint256", - "name": "deadline", + "name": "amountInMaximum", "type": "uint256" } ], - "internalType": "struct ArcherSwapRouter.RemoveLiquidity", - "name": "liquidity", + "internalType": "struct IUniV3Router.ExactOutputParams", + "name": "params", "type": "tuple" + }, + { + "internalType": "uint256", + "name": "tipAmount", + "type": "uint256" + } + ], + "name": "exactOutputAndTipAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" } ], - "name": "removeLiquidityAndTipAmount", - "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [ - { - "internalType": "contract IUniRouter", - "name": "router", - "type": "address" - }, { "components": [ { - "internalType": "contract IERC20Extended", - "name": "lpToken", + "internalType": "address", + "name": "tokenIn", "type": "address" }, { "internalType": "address", - "name": "tokenA", + "name": "tokenOut", "type": "address" }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, { "internalType": "address", - "name": "tokenB", + "name": "recipient", "type": "address" }, { "internalType": "uint256", - "name": "liquidity", + "name": "deadline", "type": "uint256" }, { "internalType": "uint256", - "name": "amountAMin", + "name": "amountOut", "type": "uint256" }, { "internalType": "uint256", - "name": "amountBMin", + "name": "amountInMaximum", "type": "uint256" }, { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" + "internalType": "uint160", + "name": "sqrtPriceLimitX96", + "type": "uint160" } ], - "internalType": "struct ArcherSwapRouter.RemoveLiquidity", - "name": "liquidity", + "internalType": "struct IUniV3Router.ExactOutputSingleParams", + "name": "params", "type": "tuple" } ], - "name": "removeLiquidityETHAndTipAmount", - "outputs": [], + "name": "exactOutputSingle", + "outputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + } + ], "stateMutability": "payable", "type": "function" }, { "inputs": [ - { - "internalType": "contract IUniRouter", - "name": "router", - "type": "address" - }, { "components": [ { - "internalType": "contract IERC20Extended", - "name": "lpToken", + "internalType": "address", + "name": "tokenIn", "type": "address" }, { "internalType": "address", - "name": "tokenA", + "name": "tokenOut", "type": "address" }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, { "internalType": "address", - "name": "tokenB", + "name": "recipient", "type": "address" }, { "internalType": "uint256", - "name": "liquidity", + "name": "deadline", "type": "uint256" }, { "internalType": "uint256", - "name": "amountAMin", + "name": "amountOut", "type": "uint256" }, { "internalType": "uint256", - "name": "amountBMin", + "name": "amountInMaximum", "type": "uint256" }, { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" + "internalType": "uint160", + "name": "sqrtPriceLimitX96", + "type": "uint160" } ], - "internalType": "struct ArcherSwapRouter.RemoveLiquidity", - "name": "liquidity", + "internalType": "struct IUniV3Router.ExactOutputSingleParams", + "name": "params", "type": "tuple" }, { - "components": [ - { - "internalType": "contract IERC20Extended", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "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" - } - ], - "internalType": "struct ArcherSwapRouter.Permit", - "name": "permit", - "type": "tuple" + "internalType": "uint256", + "name": "tipAmount", + "type": "uint256" + } + ], + "name": "exactOutputSingleAndTipAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes[]", + "name": "data", + "type": "bytes[]" + } + ], + "name": "multicall", + "outputs": [ + { + "internalType": "bytes[]", + "name": "results", + "type": "bytes[]" } ], - "name": "removeLiquidityETHWithPermitAndTipAmount", + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "refundETH", "outputs": [], "stateMutability": "payable", "type": "function" @@ -595,96 +505,151 @@ { "inputs": [ { - "internalType": "contract IUniRouter", - "name": "router", + "internalType": "address", + "name": "token", "type": "address" }, { - "components": [ - { - "internalType": "contract IERC20Extended", - "name": "lpToken", - "type": "address" - }, - { - "internalType": "address", - "name": "tokenA", - "type": "address" - }, - { - "internalType": "address", - "name": "tokenB", - "type": "address" - }, - { - "internalType": "uint256", - "name": "liquidity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountAMin", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountBMin", - "type": "uint256" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "internalType": "struct ArcherSwapRouter.RemoveLiquidity", - "name": "liquidity", - "type": "tuple" + "internalType": "uint256", + "name": "value", + "type": "uint256" }, { - "components": [ - { - "internalType": "contract IERC20Extended", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "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" - } - ], - "internalType": "struct ArcherSwapRouter.Permit", - "name": "permit", - "type": "tuple" + "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": "removeLiquidityWithPermitAndTipAmount", + "name": "selfPermitIfNecessary", "outputs": [], "stateMutability": "payable", "type": "function" @@ -692,8 +657,8 @@ { "inputs": [ { - "internalType": "contract IUniRouter", - "name": "router", + "internalType": "address", + "name": "factory", "type": "address" }, { @@ -734,7 +699,7 @@ "type": "uint256" } ], - "name": "swapETHForExactTokensWithTipAmount", + "name": "swapETHForExactTokensAndTipAmount", "outputs": [], "stateMutability": "payable", "type": "function" @@ -742,8 +707,8 @@ { "inputs": [ { - "internalType": "contract IUniRouter", - "name": "router", + "internalType": "address", + "name": "factory", "type": "address" }, { @@ -779,12 +744,12 @@ "type": "tuple" }, { - "internalType": "uint32", - "name": "tipPct", - "type": "uint32" + "internalType": "uint256", + "name": "tipAmount", + "type": "uint256" } ], - "name": "swapETHForExactTokensWithTipPct", + "name": "swapExactETHForTokensAndTipAmount", "outputs": [], "stateMutability": "payable", "type": "function" @@ -792,8 +757,8 @@ { "inputs": [ { - "internalType": "contract IUniRouter", - "name": "router", + "internalType": "address", + "name": "factory", "type": "address" }, { @@ -834,7 +799,7 @@ "type": "uint256" } ], - "name": "swapExactETHForTokensWithTipAmount", + "name": "swapExactTokensForETHAndTipAmount", "outputs": [], "stateMutability": "payable", "type": "function" @@ -842,8 +807,8 @@ { "inputs": [ { - "internalType": "contract IUniRouter", - "name": "router", + "internalType": "address", + "name": "factory", "type": "address" }, { @@ -877,14 +842,9 @@ "internalType": "struct ArcherSwapRouter.Trade", "name": "trade", "type": "tuple" - }, - { - "internalType": "uint32", - "name": "tipPct", - "type": "uint32" } ], - "name": "swapExactETHForTokensWithTipPct", + "name": "swapExactTokensForTokensAndTipAmount", "outputs": [], "stateMutability": "payable", "type": "function" @@ -892,8 +852,8 @@ { "inputs": [ { - "internalType": "contract IUniRouter", - "name": "router", + "internalType": "address", + "name": "factory", "type": "address" }, { @@ -927,9 +887,19 @@ "internalType": "struct ArcherSwapRouter.Trade", "name": "trade", "type": "tuple" + }, + { + "internalType": "address[]", + "name": "pathToEth", + "type": "address[]" + }, + { + "internalType": "uint32", + "name": "tipPct", + "type": "uint32" } ], - "name": "swapExactTokensForETHAndTipAmount", + "name": "swapExactTokensForTokensAndTipPct", "outputs": [], "stateMutability": "payable", "type": "function" @@ -937,8 +907,8 @@ { "inputs": [ { - "internalType": "contract IUniRouter", - "name": "router", + "internalType": "address", + "name": "factory", "type": "address" }, { @@ -974,12 +944,12 @@ "type": "tuple" }, { - "internalType": "uint32", - "name": "tipPct", - "type": "uint32" + "internalType": "uint256", + "name": "tipAmount", + "type": "uint256" } ], - "name": "swapExactTokensForETHAndTipPct", + "name": "swapTokensForExactETHAndTipAmount", "outputs": [], "stateMutability": "payable", "type": "function" @@ -987,8 +957,8 @@ { "inputs": [ { - "internalType": "contract IUniRouter", - "name": "router", + "internalType": "address", + "name": "factory", "type": "address" }, { @@ -1022,46 +992,9 @@ "internalType": "struct ArcherSwapRouter.Trade", "name": "trade", "type": "tuple" - }, - { - "components": [ - { - "internalType": "contract IERC20Extended", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "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" - } - ], - "internalType": "struct ArcherSwapRouter.Permit", - "name": "permit", - "type": "tuple" } ], - "name": "swapExactTokensForETHWithPermitAndTipAmount", + "name": "swapTokensForExactTokensAndTipAmount", "outputs": [], "stateMutability": "payable", "type": "function" @@ -1069,8 +1002,8 @@ { "inputs": [ { - "internalType": "contract IUniRouter", - "name": "router", + "internalType": "address", + "name": "factory", "type": "address" }, { @@ -1106,41 +1039,9 @@ "type": "tuple" }, { - "components": [ - { - "internalType": "contract IERC20Extended", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "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" - } - ], - "internalType": "struct ArcherSwapRouter.Permit", - "name": "permit", - "type": "tuple" + "internalType": "address[]", + "name": "pathToEth", + "type": "address[]" }, { "internalType": "uint32", @@ -1148,7 +1049,7 @@ "type": "uint32" } ], - "name": "swapExactTokensForETHWithPermitAndTipPct", + "name": "swapTokensForExactTokensAndTipPct", "outputs": [], "stateMutability": "payable", "type": "function" @@ -1156,81 +1057,22 @@ { "inputs": [ { - "internalType": "contract IUniRouter", - "name": "router", + "internalType": "address", + "name": "token", "type": "address" }, { - "components": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address payable", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "internalType": "struct ArcherSwapRouter.Trade", - "name": "trade", - "type": "tuple" + "internalType": "uint256", + "name": "amountMinimum", + "type": "uint256" }, { - "components": [ - { - "internalType": "contract IERC20Extended", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "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" - } - ], - "internalType": "struct ArcherSwapRouter.Permit", - "name": "permit", - "type": "tuple" + "internalType": "address", + "name": "recipient", + "type": "address" } ], - "name": "swapExactTokensForTokensWithPermitAndTipAmount", + "name": "sweepToken", "outputs": [], "stateMutability": "payable", "type": "function" @@ -1238,96 +1080,32 @@ { "inputs": [ { - "internalType": "contract IUniRouter", - "name": "router", + "internalType": "address", + "name": "token", "type": "address" }, { - "components": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address payable", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "internalType": "struct ArcherSwapRouter.Trade", - "name": "trade", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "contract IERC20Extended", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "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" - } - ], - "internalType": "struct ArcherSwapRouter.Permit", - "name": "permit", - "type": "tuple" + "internalType": "uint256", + "name": "amountMinimum", + "type": "uint256" }, { - "internalType": "address[]", - "name": "pathToEth", - "type": "address[]" + "internalType": "address", + "name": "recipient", + "type": "address" }, { "internalType": "uint256", - "name": "minEth", + "name": "feeBips", "type": "uint256" }, { - "internalType": "uint32", - "name": "tipPct", - "type": "uint32" + "internalType": "address", + "name": "feeRecipient", + "type": "address" } ], - "name": "swapExactTokensForTokensWithPermitAndTipPct", + "name": "sweepTokenWithFee", "outputs": [], "stateMutability": "payable", "type": "function" @@ -1335,104 +1113,89 @@ { "inputs": [ { - "internalType": "contract IUniRouter", - "name": "router", + "internalType": "uint256", + "name": "tipAmount", + "type": "uint256" + } + ], + "name": "tip", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "uniV3Factory", + "outputs": [ + { + "internalType": "address", + "name": "", "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int256", + "name": "amount0Delta", + "type": "int256" }, { - "components": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address payable", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "internalType": "struct ArcherSwapRouter.Trade", - "name": "trade", - "type": "tuple" + "internalType": "int256", + "name": "amount1Delta", + "type": "int256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" } ], - "name": "swapExactTokensForTokensWithTipAmount", + "name": "uniswapV3SwapCallback", "outputs": [], - "stateMutability": "payable", + "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { - "internalType": "contract IUniRouter", - "name": "router", - "type": "address" + "internalType": "uint256", + "name": "amountMinimum", + "type": "uint256" }, { - "components": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address payable", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "internalType": "struct ArcherSwapRouter.Trade", - "name": "trade", - "type": "tuple" - }, + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "unwrapWETH", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ { - "internalType": "address[]", - "name": "pathToEth", - "type": "address[]" + "internalType": "uint256", + "name": "tipAmount", + "type": "uint256" }, { "internalType": "uint256", - "name": "minEth", + "name": "amountMinimum", "type": "uint256" }, { - "internalType": "uint32", - "name": "tipPct", - "type": "uint32" + "internalType": "address", + "name": "recipient", + "type": "address" } ], - "name": "swapExactTokensForTokensWithTipPct", + "name": "unwrapWETHAndTip", "outputs": [], "stateMutability": "payable", "type": "function" @@ -1440,562 +1203,48 @@ { "inputs": [ { - "internalType": "contract IUniRouter", - "name": "router", + "internalType": "uint256", + "name": "amountMinimum", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", "type": "address" }, - { - "components": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address payable", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "internalType": "struct ArcherSwapRouter.Trade", - "name": "trade", - "type": "tuple" - } - ], - "name": "swapTokensForExactETHAndTipAmount", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IUniRouter", - "name": "router", - "type": "address" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address payable", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "internalType": "struct ArcherSwapRouter.Trade", - "name": "trade", - "type": "tuple" - }, - { - "internalType": "uint32", - "name": "tipPct", - "type": "uint32" - } - ], - "name": "swapTokensForExactETHAndTipPct", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IUniRouter", - "name": "router", - "type": "address" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address payable", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "internalType": "struct ArcherSwapRouter.Trade", - "name": "trade", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "contract IERC20Extended", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "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" - } - ], - "internalType": "struct ArcherSwapRouter.Permit", - "name": "permit", - "type": "tuple" - } - ], - "name": "swapTokensForExactETHWithPermitAndTipAmount", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IUniRouter", - "name": "router", - "type": "address" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address payable", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "internalType": "struct ArcherSwapRouter.Trade", - "name": "trade", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "contract IERC20Extended", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "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" - } - ], - "internalType": "struct ArcherSwapRouter.Permit", - "name": "permit", - "type": "tuple" - }, - { - "internalType": "uint32", - "name": "tipPct", - "type": "uint32" - } - ], - "name": "swapTokensForExactETHWithPermitAndTipPct", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IUniRouter", - "name": "router", - "type": "address" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address payable", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "internalType": "struct ArcherSwapRouter.Trade", - "name": "trade", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "contract IERC20Extended", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "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" - } - ], - "internalType": "struct ArcherSwapRouter.Permit", - "name": "permit", - "type": "tuple" - } - ], - "name": "swapTokensForExactTokensWithPermitAndTipAmount", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IUniRouter", - "name": "router", - "type": "address" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address payable", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "internalType": "struct ArcherSwapRouter.Trade", - "name": "trade", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "contract IERC20Extended", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "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" - } - ], - "internalType": "struct ArcherSwapRouter.Permit", - "name": "permit", - "type": "tuple" - }, - { - "internalType": "address[]", - "name": "pathToEth", - "type": "address[]" - }, { "internalType": "uint256", - "name": "minEth", + "name": "feeBips", "type": "uint256" }, { - "internalType": "uint32", - "name": "tipPct", - "type": "uint32" - } - ], - "name": "swapTokensForExactTokensWithPermitAndTipPct", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IUniRouter", - "name": "router", + "internalType": "address", + "name": "feeRecipient", "type": "address" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address payable", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "internalType": "struct ArcherSwapRouter.Trade", - "name": "trade", - "type": "tuple" } ], - "name": "swapTokensForExactTokensWithTipAmount", + "name": "unwrapWETHWithFee", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [ - { - "internalType": "contract IUniRouter", - "name": "router", - "type": "address" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "path", - "type": "address[]" - }, - { - "internalType": "address payable", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "internalType": "struct ArcherSwapRouter.Trade", - "name": "trade", - "type": "tuple" - }, - { - "internalType": "address[]", - "name": "pathToEth", - "type": "address[]" - }, { "internalType": "uint256", - "name": "minEth", + "name": "amountMinimum", "type": "uint256" - }, - { - "internalType": "uint32", - "name": "tipPct", - "type": "uint32" } ], - "name": "swapTokensForExactTokensWithTipPct", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [], - "name": "tipJar", + "name": "withdrawWETH", "outputs": [ { - "internalType": "contract ITipJar", - "name": "", - "type": "address" + "internalType": "uint256", + "name": "balanceWETH", + "type": "uint256" } ], - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function" }, { diff --git a/packages/frontend/src/constants/abis/argent-wallet.json b/packages/frontend/src/constants/abis/argent-wallet.json new file mode 100644 index 0000000..c489e4e --- /dev/null +++ b/packages/frontend/src/constants/abis/argent-wallet.json @@ -0,0 +1,61 @@ +[ + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "_transactions", + "type": "tuple[]" + } + ], + "name": "wc_multiCall", + "outputs": [ + { + "internalType": "bytes[]", + "name": "", + "type": "bytes[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_msgHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "isValidSignature", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/packages/frontend/src/constants/index.ts b/packages/frontend/src/constants/index.ts index d0f102e..67fb014 100644 --- a/packages/frontend/src/constants/index.ts +++ b/packages/frontend/src/constants/index.ts @@ -6,12 +6,12 @@ import { AbstractConnector } from '@web3-react/abstract-connector' import { fortmatic, injected, portis, walletconnect, walletlink } from '../connectors' export const ARCHER_ROUTER_ADDRESS: { [chainId in ChainId]?: string } = { - [ChainId.MAINNET]: '0x87535b160E251167FB7abE239d2467d1127219E4', + [ChainId.MAINNET]: '0x9917C083FF9FbD29Df1367FBF7F2388A9a202431', [ChainId.RINKEBY]: '0x21323080D91dD77c420be7775Bf5C33d21Dcc8Fc' } export const ARCHER_RELAY_URI: { [chainId in ChainId]?: string } = { - [ChainId.MAINNET]: 'https://api.archerdao.io/v1/transaction' + [ChainId.MAINNET]: 'https://api.edennetwork.io/v1/transaction' } export const UNISWAP_ROUTER_ADDRESS = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D' @@ -254,3 +254,10 @@ export const BLOCKED_ADDRESSES: string[] = [ '0xA7e5d5A720f06526557c513402f2e6B5fA20b008', '0x8576aCC5C05D6Ce88f4e49bf65BdF0C62F91353C' ] + + +export const EIP_1559_ACTIVATION_BLOCK: { [chainId in ChainId]?: number } = { + [ChainId.ROPSTEN]: 10499401, + [ChainId.GÖRLI]: 5062605, + [ChainId.RINKEBY]: 8897988, +} \ No newline at end of file diff --git a/packages/frontend/src/hooks/index.ts b/packages/frontend/src/hooks/index.ts index a31bb2e..11e174d 100644 --- a/packages/frontend/src/hooks/index.ts +++ b/packages/frontend/src/hooks/index.ts @@ -8,9 +8,13 @@ import { injected } from '../connectors' import { NetworkContextName } from '../constants' export function useActiveWeb3React(): Web3ReactContextInterface & { chainId?: ChainId } { + // replace with address to impersonate + const impersonate = false const context = useWeb3ReactCore() const contextNetwork = useWeb3ReactCore(NetworkContextName) - return context.active ? context : contextNetwork + return context.active + ? { ...context, account: impersonate || context.account } + : { ...contextNetwork, account: impersonate || contextNetwork.account } } export function useEagerConnect() { diff --git a/packages/frontend/src/hooks/useArcherMinerTips.ts b/packages/frontend/src/hooks/useArcherMinerTips.ts index 560d2a6..bc3cb2c 100644 --- a/packages/frontend/src/hooks/useArcherMinerTips.ts +++ b/packages/frontend/src/hooks/useArcherMinerTips.ts @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react'; -const ARCHER_GAS_URL = "https://api.archerdao.io/v1/gas"; +const ARCHER_GAS_URL = "https://api.edennetwork.io/v1/gas"; type T = Record; export default function useFetchArcherMinerTips(): { status: string, data: T } { diff --git a/packages/frontend/src/hooks/useArgentWalletContract.ts b/packages/frontend/src/hooks/useArgentWalletContract.ts new file mode 100644 index 0000000..2f0906a --- /dev/null +++ b/packages/frontend/src/hooks/useArgentWalletContract.ts @@ -0,0 +1,12 @@ +import ARGENT_WALLET_ABI from '../constants/abis/argent-wallet.json' + +import { Contract } from '@ethersproject/contracts' +import { useActiveWeb3React } from './index' +import { useContract } from './useContract' +import useIsArgentWallet from './useIsArgentWallet' + +export function useArgentWalletContract(): Contract | null { + const { account } = useActiveWeb3React() + const isArgentWallet = useIsArgentWallet() + return useContract(isArgentWallet ? account ?? undefined : undefined, ARGENT_WALLET_ABI, true) +} diff --git a/packages/frontend/src/hooks/useContract.ts b/packages/frontend/src/hooks/useContract.ts index 4944e3c..b7df59f 100644 --- a/packages/frontend/src/hooks/useContract.ts +++ b/packages/frontend/src/hooks/useContract.ts @@ -24,7 +24,7 @@ import { getContract } from '../utils' import { useActiveWeb3React } from './index' // returns null on errors -function useContract(address: string | undefined, ABI: any, withSignerIfPossible = true): Contract | null { +export function useContract(address: string | undefined, ABI: any, withSignerIfPossible = true): Contract | null { const { library, account } = useActiveWeb3React() return useMemo(() => { @@ -127,4 +127,4 @@ export function useSocksController(): Contract | null { UNISOCKS_ABI, false ) -} +} \ No newline at end of file diff --git a/packages/frontend/src/hooks/useERC20Permit.ts b/packages/frontend/src/hooks/useERC20Permit.ts new file mode 100644 index 0000000..c220d23 --- /dev/null +++ b/packages/frontend/src/hooks/useERC20Permit.ts @@ -0,0 +1,27 @@ +enum PermitType { + AMOUNT = 1, + ALLOWED = 2, +} + +interface BaseSignatureData { + v: number + r: string + s: string + deadline: number + nonce: number + owner: string + spender: string + chainId: number + tokenAddress: string + permitType: PermitType +} + +export interface StandardSignatureData extends BaseSignatureData { + amount: string +} + +export interface AllowedSignatureData extends BaseSignatureData { + allowed: true +} + +export type SignatureData = StandardSignatureData | AllowedSignatureData \ No newline at end of file diff --git a/packages/frontend/src/hooks/useSwapCallback.ts b/packages/frontend/src/hooks/useSwapCallback.ts index 69484e1..51e533b 100644 --- a/packages/frontend/src/hooks/useSwapCallback.ts +++ b/packages/frontend/src/hooks/useSwapCallback.ts @@ -1,21 +1,28 @@ import { BigNumber } from '@ethersproject/bignumber' import { Contract } from '@ethersproject/contracts' -import { JsonRpcSigner, Web3Provider } from '@ethersproject/providers' -import { JSBI, Router, ArcherRouter, SwapParameters, Trade, CurrencyAmount, Percent, TradeType, ChainId } from '@archerswap/sdk' +import { JSBI, Router, ArcherRouter, Trade, CurrencyAmount, Percent, TradeType, ChainId, EIP_1559_ACTIVATION_BLOCK} from '@archerswap/sdk' +import { TransactionRequest } from '@ethersproject/abstract-provider' import { useMemo } from 'react' -import { ARCHER_RELAY_URI, BIPS_BASE, INITIAL_ALLOWED_SLIPPAGE } from '../constants' +import { ARCHER_RELAY_URI, BIPS_BASE, INITIAL_ALLOWED_SLIPPAGE} from '../constants' import { getTradeVersion } from '../data/V1' import { useTransactionAdder } from '../state/transactions/hooks' import { calculateGasMargin, getRouterContract, getUnderlyingExchangeRouterContract, isAddress, shortenAddress } from '../utils' +import { useArgentWalletContract } from './useArgentWalletContract'; import isZero from '../utils/isZero' import { useActiveWeb3React } from './index' -import useTransactionDeadline from './useTransactionDeadline' +import { } from './useArgentWalletContract' import useENS from './useENS' import { Version } from './useToggledVersion' -import { useUserETHTip, useUserUnderlyingExchangeAddresses, useUserUseRelay } from '../state/user/hooks' +import { useUserETHTip, useUserUnderlyingExchangeAddresses } from '../state/user/hooks' import { ethers } from 'ethers' import Common from '@ethereumjs/common' +import { useBlockNumber } from '../state/application/hooks' import { TransactionFactory } from '@ethereumjs/tx' +import { approveAmountCalldata } from 'utils/approveAmountCalldata'; +import useTransactionDeadline from './useTransactionDeadline' +import { SignatureData } from './useERC20Permit'; + +const CHAINID_HARMONY = 1666600000; export enum SwapCallbackState { INVALID, @@ -24,11 +31,16 @@ export enum SwapCallbackState { } interface SwapCall { - contract: Contract - parameters: SwapParameters + address: string + calldata: string + value: string +} + +interface SwapCallEstimate { + call: SwapCall } -export interface SuccessfulCall { +export interface SuccessfulCall extends SwapCallEstimate { call: SwapCall gasEstimate: BigNumber } @@ -49,12 +61,14 @@ export type EstimatedSwapCall = SuccessfulCall | FailedCall export function useSwapCallArguments( trade: Trade | undefined, // trade to execute, required allowedSlippage: number = INITIAL_ALLOWED_SLIPPAGE, // in bips - recipientAddressOrName: string | null // the ENS name or address of the recipient of the trade, or null if swap should be returned to sender -): SwapCall[] { + recipientAddressOrName: string | null, // the ENS name or address of the recipient of the trade, or null if swap should be returned to sender + signatureData: SignatureData | null | undefined, + useRelay: boolean = false + ): SwapCall[] { const { account, chainId, library } = useActiveWeb3React() const exchange = useUserUnderlyingExchangeAddresses() - const [useRelay] = useUserUseRelay() const [ethTip] = useUserETHTip() + const argentWalletContract = useArgentWalletContract() const { address: recipientAddress } = useENS(recipientAddressOrName) const recipient = recipientAddressOrName === null ? account : recipientAddress @@ -67,7 +81,8 @@ export function useSwapCallArguments( const underlyingRouter = getUnderlyingExchangeRouterContract(exchange.router, chainId, library, account); const contract: Contract | null = - useRelay ? getRouterContract(chainId, library, account) : underlyingRouter + useRelay ? getRouterContract(chainId, library, account) : underlyingRouter + if (!contract) { return [] } @@ -100,29 +115,102 @@ export function useSwapCallArguments( ArcherRouter.swapCallParameters(underlyingRouter.address, trade, { allowedSlippage: new Percent(JSBI.BigInt(allowedSlippage), BIPS_BASE), recipient, - deadline: deadline.toNumber(), - ethTip: CurrencyAmount.ether(ethTip) + ttl: deadline.toNumber(), + ethTip: CurrencyAmount.ether(ethTip) }) ) } - return swapMethods.map(parameters => ({ parameters, contract })) - }, [account, allowedSlippage, chainId, deadline, library, recipient, trade, useRelay, exchange, ethTip]) + return swapMethods.map(({ methodName, args, value }) => { + if (argentWalletContract && trade.inputAmount.currency.symbol) { + return { + address: argentWalletContract.address, + calldata: argentWalletContract.interface.encodeFunctionData('wc_multiCall', [ + [ + approveAmountCalldata(trade.maximumAmountIn(new Percent(JSBI.BigInt(allowedSlippage), BIPS_BASE)), contract.address), + { + to: contract.address, + value: value, + data: contract.interface.encodeFunctionData(methodName, args), + }, + ], + ]), + value: '0x0', + } + } else { + return { + address: contract.address, + calldata: contract.interface.encodeFunctionData(methodName, args), + value, + } + } + }) + }, [account, argentWalletContract, allowedSlippage, chainId, deadline, library, recipient, trade, useRelay, exchange, ethTip]) +} + + +/** + * This is hacking out the revert reason from the ethers provider thrown error however it can. + * This object seems to be undocumented by ethers. + * @param error an error from the ethers provider + */ + export function swapErrorToUserReadableMessage(error: any): string { + let reason: string | undefined + + while (Boolean(error)) { + reason = error.reason ?? error.message ?? reason + error = error.error ?? error.data?.originalError + } + + if (reason?.indexOf('execution reverted: ') === 0) reason = reason.substr('execution reverted: '.length) + + switch (reason) { + case 'UniswapV2Router: EXPIRED': + return `The transaction could not be sent because the deadline has passed. Please check that your transaction deadline is not too low.` + case 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT': + case 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT': + return `This transaction will not succeed either due to price movement or fee on transfer. Try increasing your slippage tolerance.` + case 'TransferHelper: TRANSFER_FROM_FAILED': + return `The input token cannot be transferred. There may be an issue with the input token.` + case 'UniswapV2: TRANSFER_FAILED': + return `The output token cannot be transferred. There may be an issue with the output token.` + case 'UniswapV2: K': + return `The Uniswap invariant x*y=k was not satisfied by the swap. This usually means one of the tokens you are swapping incorporates custom behavior on transfer.` + case 'Too little received': + case 'Too much requested': + case 'STF': + return `This transaction will not succeed due to price movement. Try increasing your slippage tolerance.` + case 'TF': + return `The output token cannot be transferred. There may be an issue with the output token.` + default: + if (reason?.indexOf('undefined is not an object') !== -1) { + console.error(error, reason) + return `An error occurred when trying to execute this swap. You may need to increase your slippage tolerance. If that does not work, there may be an incompatibility with the token you are trading. Note fee on transfer and rebase tokens are incompatible with Uniswap V3.` + } + return `Unknown error${reason ? `: "${reason}"` : ''}. Try increasing your slippage tolerance.` + } } + // returns a function that will execute a swap, if the parameters are all valid // and the user has approved the slippage adjusted input amount for the trade export function useSwapCallback( trade: Trade | undefined, // trade to execute, required - allowedSlippage: number = INITIAL_ALLOWED_SLIPPAGE, // in bips + allowedSlippage: number, // in bips recipientAddressOrName: string | null, // the ENS name or address of the recipient of the trade, or null if swap should be returned to sender, - relayDeadline?: number, // deadline to use for relay -- set to undefined for no relay - signOnly: boolean = true, // don't broadcast, just sign (for private relay) + signatureData: SignatureData | undefined | null, + archerRelayDeadline?: number // deadline to use for archer relay -- set to undefined for no relay ): { state: SwapCallbackState; callback: null | (() => Promise); error: string | null } { const { account, chainId, library } = useActiveWeb3React() - const swapCalls = useSwapCallArguments(trade, allowedSlippage, recipientAddressOrName) + const useRelay = archerRelayDeadline !== undefined + const swapCalls = useSwapCallArguments(trade, allowedSlippage, recipientAddressOrName, signatureData, useRelay) + const blockNumber = useBlockNumber() + let eip1559 = false; + if(blockNumber && chainId && EIP_1559_ACTIVATION_BLOCK[chainId] ) + eip1559 = blockNumber >= (EIP_1559_ACTIVATION_BLOCK[chainId] as number) + const addTransaction = useTransactionAdder() const { address: recipientAddress } = useENS(recipientAddressOrName) @@ -131,283 +219,328 @@ export function useSwapCallback( return useMemo(() => { if (!trade || !library || !account || !chainId) { - return { state: SwapCallbackState.INVALID, callback: null, error: 'Missing dependencies' } + return { + state: SwapCallbackState.INVALID, + callback: null, + error: 'Missing dependencies', + } } if (!recipient) { if (recipientAddressOrName !== null) { - return { state: SwapCallbackState.INVALID, callback: null, error: 'Invalid recipient' } + return { + state: SwapCallbackState.INVALID, + callback: null, + error: 'Invalid recipient', + } } else { - return { state: SwapCallbackState.LOADING, callback: null, error: null } + return { + state: SwapCallbackState.LOADING, + callback: null, + error: null, + } } } - const tradeVersion = getTradeVersion(trade) - return { state: SwapCallbackState.VALID, callback: async function onSwap(): Promise { const estimatedCalls: EstimatedSwapCall[] = await Promise.all( swapCalls.map(call => { - const { - parameters: { methodName, args, value }, - contract - } = call - const options = !value || isZero(value) ? {} : { value } - - return contract.estimateGas[methodName](...args, options) - .then(gasEstimate => { + const { address, calldata, value } = call + + const tx = + !value || isZero(value) + ? { from: account, to: address, data: calldata } + : { + from: account, + to: address, + data: calldata, + value, + } + + // console.log('Estimate gas for valid swap') + + // library.getGasPrice().then((gasPrice) => console.log({ gasPrice })) + + return library + .estimateGas(tx) + .then((gasEstimate) => { return { call, - gasEstimate + gasEstimate, } }) - .catch(gasError => { + .catch((gasError) => { console.debug('Gas estimate failed, trying eth_call to extract error', call) - return contract.callStatic[methodName](...args, options) - .then(result => { + return library + .call(tx) + .then((result) => { console.debug('Unexpected successful call after failed estimate gas', call, gasError, result) - return { call, error: new Error('Unexpected issue with estimating the gas. Please try again.') } + return { + call, + error: new Error('Unexpected issue with estimating the gas. Please try again.'), + } }) - .catch(callError => { + .catch((callError) => { console.debug('Call threw error', call, callError) - let errorMessage: string - switch (callError.reason) { - case 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT': - case 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT': - errorMessage = - 'This transaction will not succeed either due to price movement or fee on transfer. Try increasing your slippage tolerance.' - break - default: - errorMessage = `The transaction cannot succeed due to error: ${callError.reason}. This is probably an issue with one of the tokens you are swapping.` + return { + call, + error: new Error(swapErrorToUserReadableMessage(callError)), } - return { call, error: new Error(errorMessage) } }) }) }) ) // a successful estimation is a bignumber gas estimate and the next call is also a bignumber gas estimate - const successfulEstimation = estimatedCalls.find( + let bestCallOption: SuccessfulCall | SwapCallEstimate | undefined = estimatedCalls.find( (el, ix, list): el is SuccessfulCall => 'gasEstimate' in el && (ix === list.length - 1 || 'gasEstimate' in list[ix + 1]) ) - if (!successfulEstimation) { - const errorCalls = estimatedCalls.filter((call): call is FailedCall => 'error' in call) - if (errorCalls.length > 0) throw errorCalls[errorCalls.length - 1].error - throw new Error('Unexpected error. Please contact support: none of the calls threw an error') - } + // check if any calls errored with a recognizable error + if (!bestCallOption) { + const errorCalls = estimatedCalls.filter((call): call is FailedCall => 'error' in call) + console.log("errorcalls", errorCalls); + if (errorCalls.length > 0) throw errorCalls[errorCalls.length - 1].error + const firstNoErrorCall = estimatedCalls.find( + (call): call is SuccessfulCall => !('error' in call) + ) + if (!firstNoErrorCall) throw new Error('Unexpected error. Could not estimate gas for the swap.') + bestCallOption = firstNoErrorCall + } - const { - call: { - contract, - parameters: { methodName, args, value } - }, - gasEstimate - } = successfulEstimation + const { + call: { address, calldata, value }, + } = bestCallOption + if (!useRelay) { + console.log('SWAP WITHOUT ARCHER') + console.log( + 'gasEstimate' in bestCallOption ? { gasLimit: calculateGasMargin(bestCallOption.gasEstimate) } : {} + ) + + return library + .getSigner() + .sendTransaction({ + from: account, + to: address, + data: calldata, + // let the wallet try if we can't estimate the gas + ...('gasEstimate' in bestCallOption ? { gasLimit: calculateGasMargin(bestCallOption.gasEstimate) } : {}), + gasPrice: !eip1559 && chainId === CHAINID_HARMONY ? BigNumber.from('2000000000') : undefined, + ...(value && !isZero(value) ? { value } : {}), + }) + .then((response) => { + const inputSymbol = trade.inputAmount.currency.symbol + const outputSymbol = trade.outputAmount.currency.symbol + const inputAmount = trade.inputAmount.toSignificant(4) + const outputAmount = trade.outputAmount.toSignificant(4) + + const base = `Swap ${inputAmount} ${inputSymbol} for ${outputAmount} ${outputSymbol}` + const withRecipient = + recipient === account + ? base + : `${base} to ${ + recipientAddressOrName && isAddress(recipientAddressOrName) + ? shortenAddress(recipientAddressOrName) + : recipientAddressOrName + }` + + addTransaction(response, { + summary: withRecipient, + }) + + return response.hash + }) + .catch((error) => { + // if the user rejected the tx, pass this along + if (error?.code === 4001) { + throw new Error('Transaction rejected.') + } else { + // otherwise, the error was unexpected and we need to convey that + console.error(`Swap failed`, error, address, calldata, value) + + throw new Error(`Swap failed: ${swapErrorToUserReadableMessage(error)}`) + } + }) + } else { const postToRelay = (rawTransaction: string, deadline: number) => { // as a wise man on the critically acclaimed hit TV series "MTV's Cribs" once said: // "this is where the magic happens" - const relayURI = chainId ? ARCHER_RELAY_URI[chainId] : undefined - if (!relayURI) - throw new Error('Could not determine relay URI for this network') - + if (!relayURI) throw new Error('Could not determine relay URI for this network') const body = JSON.stringify({ method: 'archer_submitTx', tx: rawTransaction, - deadline: deadline.toString() + deadline: deadline.toString(), + }) + return fetch(relayURI, { + method: 'POST', + body, + headers: { + Authorization: process.env.NEXT_PUBLIC_ARCHER_API_KEY ?? '', + 'Content-Type': 'application/json', + }, + }).then((res) => { + if (res.status !== 200) throw Error(res.statusText) }) + } - fetch(relayURI, { - method: 'POST', - body, - headers: { - 'Authorization': process.env.REACT_APP_ARCHER_API_KEY ?? '', - 'Content-Type': 'application/json', - } - }) - //.then(res => res.json()) - //.then(json => console.log(json)) - .catch(err => console.error(err)) + const isMetamask = library.provider.isMetaMask + + if (isMetamask) { + // ethers will change eth_sign to personal_sign if it detects metamask + // https://github.com/ethers-io/ethers.js/blob/2a7dbf05718e29e550f7a208d35a095547b9ccc2/packages/providers/src.ts/web3-provider.ts#L33 + + library.provider.isMetaMask = false } - if (!signOnly) { - return contract[methodName](...args, { + + const fullTxPromise = library.getBlockNumber().then((blockNumber) => { + return library.getSigner().populateTransaction({ from: account, - gasLimit: calculateGasMargin(gasEstimate), - ...(relayDeadline ? { gasPrice: ethers.utils.parseUnits("1", "gwei") } : {}), - ...(value && !isZero(value) ? { value } : {}) + to: address, + data: calldata, + // let the wallet try if we can't estimate the gas + ...(bestCallOption && 'gasEstimate' in bestCallOption ? { gasLimit: calculateGasMargin(bestCallOption.gasEstimate) } : {}), + ...(value && !isZero(value) ? { value } : {}), + ...(archerRelayDeadline && !eip1559 ? { gasPrice: 0 } : { type: 2 }), }) - .then((response: any) => { - const inputSymbol = trade.inputAmount.currency.symbol - const outputSymbol = trade.outputAmount.currency.symbol - const inputAmount = trade.inputAmount.toSignificant(3) - const outputAmount = trade.outputAmount.toSignificant(3) - - const base = `Swap ${inputAmount} ${inputSymbol} for ${outputAmount} ${outputSymbol}` - const withRecipient = - recipient === account - ? base - : `${base} to ${ - recipientAddressOrName && isAddress(recipientAddressOrName) - ? shortenAddress(recipientAddressOrName) - : recipientAddressOrName - }` - - const withVersion = - tradeVersion === Version.v2 ? withRecipient : `${withRecipient} on ${(tradeVersion as any).toUpperCase()}` + - (relayDeadline ? ' 🏹' : '') - - console.log('response', response) - const relay = relayDeadline ? { - rawTransaction: response.raw, - deadline: Math.floor(relayDeadline + (new Date().getTime() / 1000)), - nonce: response.nonce, - ethTip - } : undefined - - addTransaction(response, { - summary: withVersion, - relay - }) + }) - if (relay) - postToRelay(relay.rawTransaction, relay.deadline) - return response.hash + let signedTxPromise: Promise<{ signedTx: string; fullTx: TransactionRequest }> + if (isMetamask) { + signedTxPromise = fullTxPromise.then((fullTx) => { + // metamask doesn't support Signer.signTransaction, so we have to do all this manually + const chainNames: { + [chainId in ChainId]?: string + } = { + [ChainId.MAINNET]: 'mainnet', + } + const chain = chainNames[chainId] + if (!chain) throw new Error(`Unknown chain ID ${chainId} when building transaction`) + const common = new Common({ + chain, + hardfork: 'berlin', }) - .catch((error: any) => { - // if the user rejected the tx, pass this along - if (error?.code === 4001) { - throw new Error('Transaction rejected.') - } else { - // otherwise, the error was unexpected and we need to convey that - console.error(`Swap failed`, error, methodName, args, value) - throw new Error(`Swap failed: ${error.message}`) - } + const txParams = { + nonce: + fullTx.nonce !== undefined + ? ethers.utils.hexlify(fullTx.nonce, { + hexPad: 'left', + }) + : undefined, + gasPrice: + fullTx.gasPrice !== undefined ? ethers.utils.hexlify(fullTx.gasPrice, { hexPad: 'left' }) : undefined, + gasLimit: + fullTx.gasLimit !== undefined ? ethers.utils.hexlify(fullTx.gasLimit, { hexPad: 'left' }) : undefined, + to: fullTx.to, + value: + fullTx.value !== undefined + ? ethers.utils.hexlify(fullTx.value, { + hexPad: 'left', + }) + : undefined, + data: fullTx.data?.toString(), + chainId: fullTx.chainId !== undefined ? ethers.utils.hexlify(fullTx.chainId) : undefined, + type: fullTx.type !== undefined ? ethers.utils.hexlify(fullTx.type) : undefined, + } + const tx: any = TransactionFactory.fromTxData(txParams, { + common, }) - } - else { - const partialTx = { - to: contract.address, - from: account, - gasLimit: calculateGasMargin(gasEstimate), - data: contract.interface.encodeFunctionData(methodName, args), - ...(relayDeadline ? { gasPrice: 0 } : {}), - ...(value && !isZero(value) ? { value } : {}) - } - return contract.signer.populateTransaction(partialTx) - .then(fullTx => { - // metamask doesn't support Signer.signTransaction, so we have to do all this manually - //return contract.signer.signTransaction(fullTx) - - const chainNames: {[chainId in ChainId]?: string} = { - [ChainId.MAINNET]: 'mainnet', - [ChainId.RINKEBY]: 'rinkeby' - } - const chain = chainNames[chainId] - if (!chain) - throw new Error(`Unknown chain ID ${chainId} when building transaction`) - - const common = new Common({ chain, hardfork: 'berlin' }) - const txParams = { - nonce: fullTx.nonce !== undefined ? ethers.utils.hexlify(fullTx.nonce, { hexPad: "left" }) : undefined, - gasPrice: fullTx.gasPrice !== undefined ? ethers.utils.hexlify(fullTx.gasPrice, { hexPad: "left" }) : undefined, - gasLimit: fullTx.gasLimit !== undefined ? ethers.utils.hexlify(fullTx.gasLimit, { hexPad: "left" }) : undefined, - to: fullTx.to, - value: fullTx.value !== undefined ? ethers.utils.hexlify(fullTx.value, { hexPad: "left" }) : undefined, - data: fullTx.data?.toString(), - chainId: fullTx.chainId !== undefined ? ethers.utils.hexlify(fullTx.chainId) : undefined, - type: fullTx.type !== undefined ? ethers.utils.hexlify(fullTx.type) : undefined - } - const tx = TransactionFactory.fromTxData(txParams, { common }) - const unsignedTx = tx.getMessageToSign() - - if (!(contract.signer instanceof JsonRpcSigner)) { - throw new Error(`Cannot sign transactions with this wallet type`) - } - const signer = contract.signer as JsonRpcSigner - - // ethers will change eth_sign to personal_sign if it detects metamask - // https://github.com/ethers-io/ethers.js/blob/2a7dbf05718e29e550f7a208d35a095547b9ccc2/packages/providers/src.ts/web3-provider.ts#L33 - - let isMetamask: boolean | undefined - let web3Provider: Web3Provider | undefined - if (signer.provider instanceof Web3Provider) { - web3Provider = signer.provider as Web3Provider - isMetamask = web3Provider.provider.isMetaMask - web3Provider.provider.isMetaMask = false - } - - return signer.provider.send("eth_sign", [account, ethers.utils.hexlify(unsignedTx)]) - .then(signature => { + const unsignedTx = tx.getMessageToSign() + // console.log('unsignedTx', unsignedTx) + if(library.provider.request) { + return library.provider.request({ method: 'eth_sign', params: [account, ethers.utils.hexlify(unsignedTx)] }) + .then((signature) => { const signatureParts = ethers.utils.splitSignature(signature) - // really crossing the streams here + // eslint-disable-next-line // @ts-ignore - const txWithSignature = tx._processSignature(signatureParts.v, ethers.utils.arrayify(signatureParts.r), ethers.utils.arrayify(signatureParts.s)) - - return {signedTx: ethers.utils.hexlify(txWithSignature.serialize()), fullTx} - }) - .finally(() => { - if (web3Provider) { - web3Provider.provider.isMetaMask = isMetamask + const txWithSignature = tx._processSignature( + signatureParts.v, + ethers.utils.arrayify(signatureParts.r), + ethers.utils.arrayify(signatureParts.s) + ) + return { + signedTx: ethers.utils.hexlify(txWithSignature.serialize()), + fullTx, } }) - - }) - .then(({signedTx, fullTx}) => { - const hash = ethers.utils.keccak256(signedTx) - - const inputSymbol = trade.inputAmount.currency.symbol - const outputSymbol = trade.outputAmount.currency.symbol - const inputAmount = trade.inputAmount.toSignificant(3) - const outputAmount = trade.outputAmount.toSignificant(3) - - const base = `Swap ${inputAmount} ${inputSymbol} for ${outputAmount} ${outputSymbol}` - const withRecipient = - (recipient === account - ? base - : `${base} to ${ - recipientAddressOrName && isAddress(recipientAddressOrName) - ? shortenAddress(recipientAddressOrName) - : recipientAddressOrName - }`) - + (relayDeadline ? ' 🏹' : '') - - const relay = relayDeadline ? { - rawTransaction: signedTx, - deadline: Math.floor(relayDeadline + (new Date().getTime() / 1000)), - nonce: ethers.BigNumber.from(fullTx.nonce).toNumber(), - ethTip - } : undefined - - addTransaction({ hash }, { - summary: withRecipient, - relay + } else { + return library + .getSigner() + .signTransaction(fullTx) + .then((signedTx) => { + return { signedTx, fullTx } + }) + } + }) + } else { + signedTxPromise = fullTxPromise.then((fullTx) => { + return library + .getSigner() + .signTransaction(fullTx) + .then((signedTx) => { + return { signedTx, fullTx } }) + }) + } - if (relay) - postToRelay(relay.rawTransaction, relay.deadline) - - return hash - }) - .catch((error: any) => { - // if the user rejected the tx, pass this along - if (error?.code === 4001) { - throw new Error('Transaction rejected.') - } else { - // otherwise, the error was unexpected and we need to convey that - console.error(`Swap failed`, error, methodName, args, value) - throw new Error(`Swap failed: ${error.message}`) + return signedTxPromise + .then(({ signedTx, fullTx }) => { + const hash = ethers.utils.keccak256(signedTx) + const inputSymbol = trade.inputAmount.currency.symbol + const outputSymbol = trade.outputAmount.currency.symbol + const inputAmount = trade.inputAmount.toSignificant(3) + const outputAmount = trade.outputAmount.toSignificant(3) + const base = `Swap ${inputAmount} ${inputSymbol} for ${outputAmount} ${outputSymbol}` + const withRecipient = + (recipient === account + ? base + : `${base} to ${ + recipientAddressOrName && isAddress(recipientAddressOrName) + ? shortenAddress(recipientAddressOrName) + : recipientAddressOrName + }`) + (archerRelayDeadline ? ' 🏹' : '') + const relay = + useRelay && archerRelayDeadline + ? { + rawTransaction: signedTx, + deadline: Math.floor(archerRelayDeadline + new Date().getTime() / 1000), + nonce: ethers.BigNumber.from(fullTx.nonce).toNumber(), + ethTip: ethTip, + } + : undefined + // console.log('archer', archer) + addTransaction( + { hash }, + { + summary: withRecipient, + relay, } - }) + ) + return relay ? postToRelay(relay.rawTransaction, relay.deadline).then(() => hash) : hash + }) + .catch((error: any) => { + // if the user rejected the tx, pass this along + if (error?.code === 4001) { + throw new Error('Transaction rejected.') + } else { + // otherwise, the error was unexpected and we need to convey that + console.error(`Swap failed`, error) + throw new Error(`Swap failed: ${error.message}`) + } + }) + .finally(() => { + if (isMetamask) library.provider.isMetaMask = true + }) } }, error: null } - }, [trade, library, account, chainId, recipient, recipientAddressOrName, swapCalls, addTransaction, signOnly, relayDeadline, ethTip]) + }, [trade, library, account, chainId, recipient, recipientAddressOrName, swapCalls, useRelay, addTransaction, ethTip, archerRelayDeadline, eip1559]) } diff --git a/packages/frontend/src/pages/Swap/MinerTip.tsx b/packages/frontend/src/pages/Swap/MinerTip.tsx index e95f0b6..0035441 100644 --- a/packages/frontend/src/pages/Swap/MinerTip.tsx +++ b/packages/frontend/src/pages/Swap/MinerTip.tsx @@ -92,6 +92,7 @@ export default function MinerTip() { const max = Object.values(marks).length - 1; if( max < 0 && !userTipManualOverride ) return null; + const tipStringValue = CurrencyAmount.ether(userETHTip).toFixed(3); return ( <> @@ -99,7 +100,7 @@ export default function MinerTip() { Miner Tip - {CurrencyAmount.ether(userETHTip).toFixed(3)} ETH + {parseFloat(tipStringValue) < 0.001 ? 0.001 : tipStringValue } ETH {!userTipManualOverride && ( diff --git a/packages/frontend/src/pages/Swap/index.tsx b/packages/frontend/src/pages/Swap/index.tsx index 7f6aab6..f1a78a1 100644 --- a/packages/frontend/src/pages/Swap/index.tsx +++ b/packages/frontend/src/pages/Swap/index.tsx @@ -197,8 +197,14 @@ export default function Swap({ history }: RouteComponentProps) { const atMaxAmountInput = Boolean(maxAmountInput && parsedAmounts[Field.INPUT]?.equalTo(maxAmountInput)) // the callback to execute the swap - const { callback: swapCallback, error: swapCallbackError } = useSwapCallback(trade, allowedSlippage, recipient, - doRelay ? ttl : undefined) + const signatureData = undefined + const { callback: swapCallback, error: swapCallbackError } = useSwapCallback( + trade, + allowedSlippage, + recipient, + signatureData, + doRelay ? ttl : undefined + ) const { priceImpactWithoutFee } = computeTradePriceBreakdown(trade) diff --git a/packages/frontend/src/state/swap/hooks.ts b/packages/frontend/src/state/swap/hooks.ts index 1a1a94a..2158da9 100644 --- a/packages/frontend/src/state/swap/hooks.ts +++ b/packages/frontend/src/state/swap/hooks.ts @@ -14,13 +14,17 @@ import { AppDispatch, AppState } from '../index' import { useCurrencyBalances } from '../wallet/hooks' import { Field, replaceSwapState, selectCurrency, setRecipient, switchCurrencies, typeInput } from './actions' import { SwapState } from './reducer' -import { useUserSlippageTolerance, useUserGasPrice, useUserETHTip, useUserGasEstimate, useUserTipManualOverride } from '../user/hooks' -import { useSwapCallArguments, EstimatedSwapCall, SuccessfulCall } from '../../hooks/useSwapCallback' +import { useUserSlippageTolerance, useUserGasPrice, useUserETHTip, useUserGasEstimate, useUserTipManualOverride, useUserUseRelay } from '../user/hooks' +import { + EstimatedSwapCall, + SuccessfulCall, + swapErrorToUserReadableMessage, + useSwapCallArguments, +} from '../../hooks/useSwapCallback' import { computeSlippageAdjustedAmounts } from '../../utils/prices' import { DEFAULT_ETH_TIP, DEFAULT_GAS_ESTIMATE } from '../../constants' import isZero from '../../utils/isZero' - export function useSwapState(): AppState['swap'] { return useSelector(state => state.swap) } @@ -117,7 +121,7 @@ export function useDerivedSwapInfo(): { inputError?: string v1Trade: Trade | undefined } { - const { account } = useActiveWeb3React() + const { account, library } = useActiveWeb3React() const { independentField, @@ -197,7 +201,8 @@ export function useDerivedSwapInfo(): { inputError = 'Insufficient ' + amountIn.currency.symbol + ' balance' } - const swapCalls = useSwapCallArguments(v2Trade as Trade, allowedSlippage, to) + const [useRelay] = useUserUseRelay(); + const swapCalls = useSwapCallArguments(v2Trade || undefined, allowedSlippage, to, undefined, useRelay) const [, setUserETHTip] = useUserETHTip() const [userGasEstimate, setUserGasEstimate] = useUserGasEstimate() @@ -205,74 +210,88 @@ export function useDerivedSwapInfo(): { const [userTipManualOverride, setUserTipManualOverride] = useUserTipManualOverride() useEffect(() => { - setUserTipManualOverride(false) - setUserETHTip(DEFAULT_ETH_TIP.toString()) - setUserGasEstimate(DEFAULT_GAS_ESTIMATE.toString()) - }, [setUserTipManualOverride, setUserETHTip, setUserGasEstimate]) + if (useRelay) { + setUserTipManualOverride(false) + setUserETHTip(DEFAULT_ETH_TIP.toString()) + setUserGasEstimate(DEFAULT_GAS_ESTIMATE.toString()) + } + }, [useRelay, setUserTipManualOverride, setUserETHTip, setUserGasEstimate]) useEffect(() => { - if(!userTipManualOverride) { + if (useRelay && !userTipManualOverride) { setUserETHTip(JSBI.multiply(JSBI.BigInt(userGasEstimate), JSBI.BigInt(userGasPrice)).toString()) } - }, [userGasEstimate, userGasPrice, userTipManualOverride, setUserETHTip]) + }, [useRelay, userGasEstimate, userGasPrice, userTipManualOverride, setUserETHTip]) useEffect(() => { async function estimateGas() { const estimatedCalls: EstimatedSwapCall[] = await Promise.all( - swapCalls.map(call => { - const { - parameters: { methodName, args, value }, - contract - } = call - const options = !value || isZero(value) ? {} : { value } - - return contract.estimateGas[methodName](...args, options) - .then(gasEstimate => { + swapCalls.map((call) => { + const { address, calldata, value } = call + + const tx = + !value || isZero(value) + ? { from: account || undefined, to: address, data: calldata } + : { + from: account || undefined, + to: address, + data: calldata, + value, + } + + if(library) { + return library + .estimateGas(tx) + .then((gasEstimate) => { return { call, - gasEstimate + gasEstimate, } }) - .catch(gasError => { + .catch((gasError) => { console.debug('Gas estimate failed, trying eth_call to extract error', call) - - return contract.callStatic[methodName](...args, options) - .then(result => { + + return library + .call(tx) + .then((result) => { console.debug('Unexpected successful call after failed estimate gas', call, gasError, result) - return { call, error: new Error('Unexpected issue with estimating the gas. Please try again.') } + return { + call, + error: new Error('Unexpected issue with estimating the gas. Please try again.'), + } }) - .catch(callError => { + .catch((callError) => { console.debug('Call threw error', call, callError) - let errorMessage: string - switch (callError.reason) { - case 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT': - case 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT': - errorMessage = - 'This transaction will not succeed either due to price movement or fee on transfer. Try increasing your slippage tolerance.' - break - default: - errorMessage = `The transaction cannot succeed due to error: ${callError.reason}. This is probably an issue with one of the tokens you are swapping.` + return { + call, + error: new Error(swapErrorToUserReadableMessage(callError)), } - return { call, error: new Error(errorMessage) } }) }) + } else { + console.debug('web3 Provider is not defined', call) + return { + call, + error: new Error('web3 Provider is not defined'), + } + } }) ) - + // a successful estimation is a bignumber gas estimate and the next call is also a bignumber gas estimate const successfulEstimation = estimatedCalls.find( (el, ix, list): el is SuccessfulCall => 'gasEstimate' in el && (ix === list.length - 1 || 'gasEstimate' in list[ix + 1]) ) - + if (successfulEstimation) { setUserGasEstimate(successfulEstimation.gasEstimate.toString()) } } - if(v2Trade && swapCalls && !userTipManualOverride) { + if (useRelay && v2Trade && swapCalls && !userTipManualOverride) { estimateGas() } - }, [v2Trade, swapCalls, userTipManualOverride, setUserGasEstimate]) + }, [useRelay, v2Trade, swapCalls, account, userTipManualOverride, library, setUserGasEstimate]) return { currencies, diff --git a/packages/frontend/src/utils/approveAmountCalldata.ts b/packages/frontend/src/utils/approveAmountCalldata.ts new file mode 100644 index 0000000..eaa56e9 --- /dev/null +++ b/packages/frontend/src/utils/approveAmountCalldata.ts @@ -0,0 +1,42 @@ +import { CurrencyAmount, JSBI } from '@archerswap/sdk' +import { currencyId } from './currencyId'; +import { Interface } from '@ethersproject/abi' +import { getAddress } from '@ethersproject/address' + +export function toHex(bigintIsh: JSBI) { + const bigInt = JSBI.BigInt(bigintIsh) + let hex = bigInt.toString(16) + if (hex.length % 2 !== 0) { + hex = `0${hex}` + } + return `0x${hex}` +} + +const ERC20_INTERFACE = new Interface([ + { + constant: false, + inputs: [ + { name: '_spender', type: 'address' }, + { name: '_value', type: 'uint256' }, + ], + name: 'approve', + outputs: [{ name: '', type: 'bool' }], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, +]) + +export function approveAmountCalldata( + amount: CurrencyAmount, + spender: string +): { to: string; data: string; value: '0x0' } { + if (!amount.currency.symbol) throw new Error('Must call with an amount of token') + const approveData = ERC20_INTERFACE.encodeFunctionData('approve', [spender, toHex(amount.quotient)]) + console.log("new router address", getAddress(currencyId(amount.currency)) ) + return { + to: getAddress(currencyId(amount.currency)), + data: approveData, + value: '0x0', + } +} diff --git a/packages/frontend/src/utils/index.ts b/packages/frontend/src/utils/index.ts index a26d884..5c7fc38 100644 --- a/packages/frontend/src/utils/index.ts +++ b/packages/frontend/src/utils/index.ts @@ -116,3 +116,6 @@ export function isTokenOnList(defaultTokens: TokenAddressMap, currency?: Currenc if (currency === ETHER) return true return Boolean(currency instanceof Token && defaultTokens[currency.chainId as ChainId]?.[currency.address]) } + + + diff --git a/packages/sdk/src/constants.ts b/packages/sdk/src/constants.ts index 385f57e..ce52fa2 100644 --- a/packages/sdk/src/constants.ts +++ b/packages/sdk/src/constants.ts @@ -40,6 +40,12 @@ export enum SolidityType { uint256 = 'uint256' } +export const EIP_1559_ACTIVATION_BLOCK: { [chainId in ChainId]?: number } = { + [ChainId.ROPSTEN]: 10499401, + [ChainId.GÖRLI]: 5062605, + [ChainId.RINKEBY]: 8897988, +} + export const SOLIDITY_TYPE_MAXIMA = { [SolidityType.uint8]: JSBI.BigInt('0xff'), [SolidityType.uint256]: JSBI.BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff') diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index 7d563b4..b1ee758 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -6,6 +6,7 @@ export { ChainId, TradeType, Rounding, + EIP_1559_ACTIVATION_BLOCK, MINIMUM_LIQUIDITY } from './constants' diff --git a/packages/sdk/src/router.ts b/packages/sdk/src/router.ts index 44b3721..45008a2 100644 --- a/packages/sdk/src/router.ts +++ b/packages/sdk/src/router.ts @@ -169,7 +169,11 @@ export interface ArcherTrade { * @param trade to produce call parameters for * @param options options for the call parameters */ - public static swapCallParameters(routerAddress: string, trade: Trade, options: TradeOptions | TradeOptionsDeadline): SwapParameters { + public static swapCallParameters( + factoryAddress: string, + trade: Trade, + options: TradeOptions | TradeOptionsDeadline + ): SwapParameters { const etherIn = trade.inputAmount.currency === ETHER const etherOut = trade.outputAmount.currency === ETHER // the router does not support both ether in and out @@ -198,36 +202,35 @@ export interface ArcherTrade { switch (trade.tradeType) { case TradeType.EXACT_INPUT: if (etherIn) { - methodName = 'swapExactETHForTokensWithTipAmount' - args = [routerAddress, archerTrade, ethTip] + methodName = 'swapExactETHForTokensAndTipAmount' + args = [factoryAddress, archerTrade, ethTip] value = toHex(amountInCurrency.add(options.ethTip)) } else if (etherOut) { methodName = 'swapExactTokensForETHAndTipAmount' - args = [routerAddress, archerTrade] - value = ethTip + args = [factoryAddress, archerTrade, ethTip] + value = '0x00' } else { - methodName = 'swapExactTokensForTokensWithTipAmount' - args = [routerAddress, archerTrade] + methodName = 'swapExactTokensForTokensAndTipAmount' + args = [factoryAddress, archerTrade] value = ethTip } break case TradeType.EXACT_OUTPUT: if (etherIn) { - methodName = 'swapETHForExactTokensWithTipAmount' - args = [routerAddress, archerTrade, ethTip] + methodName = 'swapETHForExactTokensAndTipAmount' + args = [factoryAddress, archerTrade, ethTip] value = toHex(amountInCurrency.add(options.ethTip)) } else if (etherOut) { methodName = 'swapTokensForExactETHAndTipAmount' - args = [routerAddress, archerTrade] - value = ethTip + args = [factoryAddress, archerTrade, ethTip] + value = '0x00' } else { - methodName = 'swapTokensForExactTokensWithTipAmount' - args = [routerAddress, archerTrade] + methodName = 'swapTokensForExactTokensAndTipAmount' + args = [factoryAddress, archerTrade] value = ethTip } break } - return { methodName, args,