From 56e592619e73a79b7579da4a0a791be315a4099c Mon Sep 17 00:00:00 2001 From: npty Date: Mon, 19 Feb 2024 19:07:24 +0700 Subject: [PATCH 01/33] chore: add L2Chain --- src/constants/L2Chain.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/constants/L2Chain.ts diff --git a/src/constants/L2Chain.ts b/src/constants/L2Chain.ts new file mode 100644 index 00000000..f4514440 --- /dev/null +++ b/src/constants/L2Chain.ts @@ -0,0 +1,10 @@ +export enum L2Chain { + ARBITRUM = "arbitrum", + ARBITRUM_SEPOLIA = "arbitrum-sepolia", + BASE = "base", + OPTIMISM = "optimism", + MANTLE = "mantle", + SCROLL = "scroll", +} + +export const L2_CHAINS = Object.values(L2Chain) as string[]; From 9d55e1369d33cd55b0d61d090e389562d79540fc Mon Sep 17 00:00:00 2001 From: npty Date: Mon, 19 Feb 2024 19:07:37 +0700 Subject: [PATCH 02/33] feat: add ethereum-multicall lib --- package.json | 1 + pnpm-lock.yaml | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/package.json b/package.json index 2eb7d46f..73538bfc 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "bech32": "^2.0.0", "clone-deep": "^4.0.1", "cross-fetch": "^3.1.5", + "ethereum-multicall": "^2.21.0", "ethers": "^5.7.2", "socket.io-client": "^4.6.1", "standard-http-error": "^2.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 99829e85..0b4a2736 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,6 +38,9 @@ dependencies: cross-fetch: specifier: ^3.1.5 version: 3.1.5 + ethereum-multicall: + specifier: ^2.21.0 + version: 2.21.0 ethers: specifier: ^5.7.2 version: 5.7.2 @@ -3563,6 +3566,16 @@ packages: engines: {node: '>=0.10.0'} dev: true + /ethereum-multicall@2.21.0: + resolution: {integrity: sha512-J234OuvUheTKvZVhMk41SwyB66m+MU+Xe2FFWOln8xu6TXKzOzsjSFQn/f5OTDGEiRStKMnJpCvQDim+Uk+qBQ==} + dependencies: + '@ethersproject/providers': 5.7.2 + ethers: 5.7.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: false + /ethers@5.7.2: resolution: {integrity: sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==} dependencies: @@ -4533,6 +4546,7 @@ packages: /node-gyp-build@4.6.0: resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==} hasBin: true + requiresBuild: true dev: true /node-releases@2.0.12: From fb8fecb3c4d8269f3f437c8ec0755a0952f4502b Mon Sep 17 00:00:00 2001 From: npty Date: Mon, 19 Feb 2024 19:08:26 +0700 Subject: [PATCH 03/33] feat: implement get L1 fee --- src/libs/fee/getL1Fee.ts | 136 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 src/libs/fee/getL1Fee.ts diff --git a/src/libs/fee/getL1Fee.ts b/src/libs/fee/getL1Fee.ts new file mode 100644 index 00000000..eef718e3 --- /dev/null +++ b/src/libs/fee/getL1Fee.ts @@ -0,0 +1,136 @@ +/* eslint-disable @typescript-eslint/no-unsafe-return */ + +import { BigNumber, ethers } from "ethers"; +import { EstimateL1FeeParams } from "../types"; +import { Multicall, ContractCallContext } from "ethereum-multicall"; + +/** + * Get the estimated L1 fee for a given L2 chain. + * @param env The environment to use. Either "mainnet" or "testnet". + * @param chain The destination L2 chain. + * @param params The parameters to use for the estimation. + * @returns + */ +export function getL1FeeForL2( + provider: ethers.providers.JsonRpcProvider, + chain: string, + params: EstimateL1FeeParams +): Promise { + const multicall = new Multicall({ ethersProvider: provider, tryAggregate: true }); + + switch (chain) { + // Most of the ethereum clients are already included L1 fee in the gas estimation for Arbitrum. + case "arbitrum": + case "arbitrum-sepolia": + return Promise.resolve(BigNumber.from(0)); + case "optimism": + case "scroll": + case "base": + return getOptimismL1Fee(multicall, params); + case "mantle": + return getMantleL1Fee(multicall, params); + default: + return Promise.resolve(BigNumber.from(0)); + } +} + +async function getOptimismL1Fee(multicall: Multicall, estimateL1FeeParams: EstimateL1FeeParams) { + const { l1GasPrice, executeData } = estimateL1FeeParams; + + const contractAddress = "0x420000000000000000000000000000000000000F"; + + const contractCallContext: ContractCallContext[] = [ + { + reference: "gasOracle", + contractAddress, + abi: [ + "function getL1GasUsed(bytes executeData) returns (uint256)", + "function scalar() returns (uint256)", + "function overhead() returns (uint256)", + ], + calls: [ + { + reference: "l1GasUsed", + methodName: "getL1GasUsed(bytes)", + methodParameters: [executeData], + }, + { reference: "scalar", methodName: "scalar()", methodParameters: [] }, + { reference: "overhead", methodName: "overhead()", methodParameters: [] }, + ], + }, + ]; + + const { results } = await multicall.call(contractCallContext); + + const [gasUsed, _dynamicOverhead, _fixedOverhead] = results["gasOracle"].callsReturnContext.map( + (call) => BigNumber.from(call.returnValues[0].hex) + ); + + const dynamicOverhead = BigNumber.from(_dynamicOverhead || 684000); + const fixedOverhead = BigNumber.from(_fixedOverhead || 2100); + + const totalGasUsed = gasUsed.add(fixedOverhead).mul(dynamicOverhead).div(1_000_000); + const gasPrice = BigNumber.from(l1GasPrice.value); + + return totalGasUsed.mul(gasPrice); +} + +// TODO: Not used for now because the gas estimation is already included the L1 fee by default. +// async function getArbitrumL1Fee( +// publicClient: PublicClient, +// destinationContractAddress: string, +// executeData: string +// ) { +// // Arbitrum NodeInterface contract address +// const contractAddress = "0x00000000000000000000000000000000000000C8"; + +// // https://github.com/OffchainLabs/nitro-contracts/blob/0a149d2af9aee566c4abf493479ec15e5fc32d98/src/node-interface/NodeInterface.sol#L112 +// const abi = parseAbi([ +// "function gasEstimateL1Component(address to, bool contractCreation, bytes calldata data) external payable returns (uint64,uint256,uint256)", +// ]); + +// const fee = (await publicClient.readContract({ +// address: contractAddress, +// abi, +// functionName: "gasEstimateL1Component" as never, +// args: [destinationContractAddress, false, executeData], +// })) as [bigint, bigint, bigint]; + +// return fee[0]; +// } + +async function getMantleL1Fee(multicall: Multicall, estimateL1FeeParams: EstimateL1FeeParams) { + const contractAddress = "0x420000000000000000000000000000000000000F"; + const { l1GasPrice } = estimateL1FeeParams; + + const abi = ["function overhead() returns (uint256)", "function scalar() returns (uint256)"]; + + const contractCallContext: ContractCallContext[] = [ + { + reference: "gasOracle", // Common reference + contractAddress, + abi, + calls: [ + { reference: "overhead", methodName: "overhead", methodParameters: [] }, + { reference: "scalar", methodName: "scalar", methodParameters: [] }, + ], + }, + ]; + + // Execute Multicall + const { results } = await multicall.call(contractCallContext); + + // Extract results + const [fixedOverhead, dynamicOverhead] = results.gasOracle.callsReturnContext.map( + (call) => call.returnValues[0] + ); + + // (fixedOverhead * dynamicOverhead) / 1_000_000n; + const totalGasUsed = BigNumber.from(fixedOverhead) + .mul(BigNumber.from(dynamicOverhead)) + .div(1_000_000); + + const gasPrice = BigNumber.from(l1GasPrice.value); + + return totalGasUsed.mul(gasPrice); +} From b85cf216381b1c307037247e7b50e74877c55f6a Mon Sep 17 00:00:00 2001 From: npty Date: Mon, 19 Feb 2024 19:08:38 +0700 Subject: [PATCH 04/33] chore: add utility function isL2Chain --- src/utils/isL2Chain.ts | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/utils/isL2Chain.ts diff --git a/src/utils/isL2Chain.ts b/src/utils/isL2Chain.ts new file mode 100644 index 00000000..3df63e6b --- /dev/null +++ b/src/utils/isL2Chain.ts @@ -0,0 +1,5 @@ +import { L2_CHAINS } from "../constants/L2Chain"; + +export function isL2Chain(chain: string): boolean { + return L2_CHAINS.includes(chain); +} From fa197454ddc29e8f373d3728f1291660bb8a2fcb Mon Sep 17 00:00:00 2001 From: npty Date: Mon, 19 Feb 2024 19:08:59 +0700 Subject: [PATCH 05/33] chore: add EstimateL1FeeParams type --- src/libs/types/index.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/libs/types/index.ts b/src/libs/types/index.ts index 9941d4b8..52b0a548 100644 --- a/src/libs/types/index.ts +++ b/src/libs/types/index.ts @@ -81,6 +81,7 @@ export interface BaseFeeResponse { destToken: { gas_price: string; gas_price_gwei: string; + gas_price_in_units: TokenUnit; decimals: number; }; expressSupported: boolean; @@ -225,3 +226,13 @@ export const isNativeToken = ( ): boolean => { return nativeGasTokenSymbol[environment][chain]?.toLowerCase() === selectedToken?.toLowerCase(); }; + +export type TokenUnit = { + value: string; + decimals: number; +}; + +export type EstimateL1FeeParams = { + executeData: `0x${string}`; + l1GasPrice: TokenUnit; +}; From f3b45f2bcafe4a4fe06bedb04001bd9da9a4515f Mon Sep 17 00:00:00 2001 From: npty Date: Mon, 19 Feb 2024 19:09:10 +0700 Subject: [PATCH 06/33] chore: update rpc node for ethereum --- src/libs/TransactionRecoveryApi/constants/chain/mainnet.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/TransactionRecoveryApi/constants/chain/mainnet.ts b/src/libs/TransactionRecoveryApi/constants/chain/mainnet.ts index cf987ce7..6adf8033 100644 --- a/src/libs/TransactionRecoveryApi/constants/chain/mainnet.ts +++ b/src/libs/TransactionRecoveryApi/constants/chain/mainnet.ts @@ -1,12 +1,12 @@ import { EvmChain } from "../../../../constants/EvmChain"; import { Network } from "@ethersproject/networks"; -export const rpcMap: Partial> = { +export const rpcMap: Partial> = { [EvmChain.FANTOM]: "https://rpc.ftm.tools", [EvmChain.POLYGON]: "https://polygon-rpc.com", [EvmChain.MOONBEAM]: "https://rpc.api.moonbeam.network", [EvmChain.AVALANCHE]: "https://api.avax.network/ext/bc/C/rpc", - [EvmChain.ETHEREUM]: "https://mainnet.infura.io/v3/510b6d5b3c56497b8070626a54f565a9", + [EvmChain.ETHEREUM]: "https://ethereum.publicnode.com", [EvmChain.AURORA]: "https://mainnet.aurora.dev", [EvmChain.BINANCE]: "https://bsc-dataseed.binance.org", [EvmChain.BNBCHAIN]: "https://bsc-dataseed.binance.org", From 75fd9a370829804cf6ea4e60c82107f4d96efa85 Mon Sep 17 00:00:00 2001 From: npty Date: Mon, 19 Feb 2024 19:37:23 +0700 Subject: [PATCH 07/33] feat: implement getL1Fee --- src/libs/fee/getL1Fee.spec.ts | 52 +++++++++++++++++++++++++++++++++++ src/libs/fee/getL1Fee.ts | 30 ++++---------------- 2 files changed, 57 insertions(+), 25 deletions(-) create mode 100644 src/libs/fee/getL1Fee.spec.ts diff --git a/src/libs/fee/getL1Fee.spec.ts b/src/libs/fee/getL1Fee.spec.ts new file mode 100644 index 00000000..f4cbadea --- /dev/null +++ b/src/libs/fee/getL1Fee.spec.ts @@ -0,0 +1,52 @@ +import { ethers } from "ethers"; +import { rpcMap } from "../TransactionRecoveryApi/constants/chain/mainnet"; +import { Environment, EstimateL1FeeParams } from "../types"; +import { getL1FeeForL2 } from "./getL1Fee"; +import { AxelarQueryAPI } from "../AxelarQueryAPI"; +import { formatEther } from "ethers/lib/utils"; + +describe("getL1Fee", () => { + const env = Environment.MAINNET; + + it("query optimism l1 fee should work", async () => { + const srcChain = "optimism"; + const destChain = "ethereum"; + + const queryAPI = new AxelarQueryAPI({ environment: env }); + + const { destToken } = await queryAPI.getNativeGasBaseFee(srcChain, destChain); + + const provider = new ethers.providers.JsonRpcProvider(rpcMap[srcChain]); + + const params: EstimateL1FeeParams = { + executeData: + "0x1a98b2e0e68ba0eb84262d4bcf91955ec2680b37bcedd59a1f48e18d183dac9961bf9d1400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000d40000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000000000762696e616e636500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863653136463639333735353230616230313337376365374238386635424138433438463844363636000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000000a8000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000dc647500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f40521500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab100000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000de83dbf000000000000000000000000000000000000000000000000015d8c7908dbe7130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000761786c5553444300000000000000000000000000000000000000000000000000", + l1GasPrice: destToken.gas_price_in_units, + }; + + const fee = await getL1FeeForL2(provider, srcChain, params); + + expect(fee).toBeDefined(); + }); + + it("query mantle l1 fee should work", async () => { + const srcChain = "mantle"; + const destChain = "ethereum"; + + const queryAPI = new AxelarQueryAPI({ environment: env }); + + const { destToken } = await queryAPI.getNativeGasBaseFee(srcChain, destChain); + + const provider = new ethers.providers.JsonRpcProvider(rpcMap[srcChain]); + + const params: EstimateL1FeeParams = { + executeData: + "0x1a98b2e0e68ba0eb84262d4bcf91955ec2680b37bcedd59a1f48e18d183dac9961bf9d1400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000d40000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000000000762696e616e636500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863653136463639333735353230616230313337376365374238386635424138433438463844363636000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000000a8000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000dc647500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f40521500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab100000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000de83dbf000000000000000000000000000000000000000000000000015d8c7908dbe7130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000761786c5553444300000000000000000000000000000000000000000000000000", + l1GasPrice: destToken.gas_price_in_units, + }; + + const fee = await getL1FeeForL2(provider, srcChain, params); + + expect(fee).toBeDefined(); + }); +}); diff --git a/src/libs/fee/getL1Fee.ts b/src/libs/fee/getL1Fee.ts index eef718e3..c9d4636a 100644 --- a/src/libs/fee/getL1Fee.ts +++ b/src/libs/fee/getL1Fee.ts @@ -69,36 +69,16 @@ async function getOptimismL1Fee(multicall: Multicall, estimateL1FeeParams: Estim const dynamicOverhead = BigNumber.from(_dynamicOverhead || 684000); const fixedOverhead = BigNumber.from(_fixedOverhead || 2100); + console.log("gasUsed", gasUsed.toString()); + console.log("dynamicOverhead", dynamicOverhead.toString()); + console.log("fixedOverhead", fixedOverhead.toString()); + const totalGasUsed = gasUsed.add(fixedOverhead).mul(dynamicOverhead).div(1_000_000); const gasPrice = BigNumber.from(l1GasPrice.value); return totalGasUsed.mul(gasPrice); } -// TODO: Not used for now because the gas estimation is already included the L1 fee by default. -// async function getArbitrumL1Fee( -// publicClient: PublicClient, -// destinationContractAddress: string, -// executeData: string -// ) { -// // Arbitrum NodeInterface contract address -// const contractAddress = "0x00000000000000000000000000000000000000C8"; - -// // https://github.com/OffchainLabs/nitro-contracts/blob/0a149d2af9aee566c4abf493479ec15e5fc32d98/src/node-interface/NodeInterface.sol#L112 -// const abi = parseAbi([ -// "function gasEstimateL1Component(address to, bool contractCreation, bytes calldata data) external payable returns (uint64,uint256,uint256)", -// ]); - -// const fee = (await publicClient.readContract({ -// address: contractAddress, -// abi, -// functionName: "gasEstimateL1Component" as never, -// args: [destinationContractAddress, false, executeData], -// })) as [bigint, bigint, bigint]; - -// return fee[0]; -// } - async function getMantleL1Fee(multicall: Multicall, estimateL1FeeParams: EstimateL1FeeParams) { const contractAddress = "0x420000000000000000000000000000000000000F"; const { l1GasPrice } = estimateL1FeeParams; @@ -122,7 +102,7 @@ async function getMantleL1Fee(multicall: Multicall, estimateL1FeeParams: Estimat // Extract results const [fixedOverhead, dynamicOverhead] = results.gasOracle.callsReturnContext.map( - (call) => call.returnValues[0] + (call) => call.returnValues ); // (fixedOverhead * dynamicOverhead) / 1_000_000n; From cfb282f9a9883fcaf7558764a83824ebe4d5ec08 Mon Sep 17 00:00:00 2001 From: npty Date: Tue, 20 Feb 2024 16:01:46 +0700 Subject: [PATCH 08/33] chore: add types --- src/libs/fee/types.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/libs/fee/types.ts diff --git a/src/libs/fee/types.ts b/src/libs/fee/types.ts new file mode 100644 index 00000000..8707c0f4 --- /dev/null +++ b/src/libs/fee/types.ts @@ -0,0 +1,16 @@ +import { + arbitrum, + arbitrumGoerli, + base, + baseGoerli, + mantle, + mantleTestnet, + optimism, + optimismGoerli, + scroll, + scrollSepolia, +} from "viem/chains"; + +import { TokenUnit } from "../../gmp"; + +export type L2Chain = "optimism" | "arbitrum" | "mantle" | "base" | "scroll"; From 24dbdfd2ff26b61bd7dee72efd9beea5bd728d94 Mon Sep 17 00:00:00 2001 From: npty Date: Tue, 20 Feb 2024 16:02:10 +0700 Subject: [PATCH 09/33] chore: make sourceChainTokenSymbol optional --- src/libs/AxelarQueryAPI.ts | 122 +++++++++++++++++++++------ src/libs/fee/getL1Fee.spec.ts | 20 ++--- src/libs/fee/getL1Fee.ts | 15 ++-- src/libs/test/AxelarQueryAPI.spec.ts | 18 +++- src/libs/types/index.ts | 16 +++- 5 files changed, 140 insertions(+), 51 deletions(-) diff --git a/src/libs/AxelarQueryAPI.ts b/src/libs/AxelarQueryAPI.ts index 2a5edae7..4e786623 100644 --- a/src/libs/AxelarQueryAPI.ts +++ b/src/libs/AxelarQueryAPI.ts @@ -3,7 +3,7 @@ import { parseUnits } from "ethers/lib/utils"; import { loadAssets } from "../assets"; import { EnvironmentConfigs, getConfigs } from "../constants"; import { RestService } from "../services"; -import { AxelarQueryAPIConfig, BaseFeeResponse, Environment } from "./types"; +import { AxelarQueryAPIConfig, BaseFeeResponse, Environment, EstimateL1FeeParams } from "./types"; import { EvmChain } from "../constants/EvmChain"; import { GasToken } from "../constants/GasToken"; import { AxelarQueryClient, AxelarQueryClientType } from "./AxelarQueryClient"; @@ -16,9 +16,13 @@ import { import { throwIfInvalidChainIds } from "../utils"; import { loadChains } from "../chains"; import s3 from "./TransactionRecoveryApi/constants/s3"; -import { BigNumber, BigNumberish } from "ethers"; +import { BigNumber, BigNumberish, ethers } from "ethers"; import { ChainInfo } from "src/chains/types"; import { BigNumberUtils } from "./BigNumberUtils"; +import { rpcMap as testnetRpcMap } from "./TransactionRecoveryApi/constants/chain/testnet"; +import { rpcMap as mainnetRpcMap } from "./TransactionRecoveryApi/constants/chain/mainnet"; +import { isL2Chain } from "../utils/isL2Chain"; +import { getL1FeeForL2 } from "./fee/getL1Fee"; interface TranslatedTransferRateLimitResponse { incoming: string; @@ -215,6 +219,7 @@ export class AxelarQueryAPI { const { source_base_fee_string, source_token, + ethereum_token, destination_native_token, express_fee_string, express_supported, @@ -229,13 +234,16 @@ export class AxelarQueryAPI { return { baseFee, expressFee, - sourceToken: source_token, + sourceToken: source_token as BaseFeeResponse["sourceToken"], executeGasMultiplier: parseFloat(execute_gas_multiplier.toFixed(2)), destToken: { gas_price: destination_native_token.gas_price, gas_price_gwei: parseInt(destination_native_token.gas_price_gwei).toString(), decimals: destination_native_token.decimals, + token_price: destination_native_token.token_price, + l1_gas_price_in_units: destination_native_token.l1_gas_price_in_units, }, + ethereumToken: ethereum_token as BaseFeeResponse["ethereumToken"], apiResponse: response, success: true, expressSupported: express_supported, @@ -243,6 +251,29 @@ export class AxelarQueryAPI { }); } + public async estimateL1GasFee( + destChainId: EvmChain | string, + l1FeeParams: EstimateL1FeeParams + ): Promise { + if (!isL2Chain(destChainId)) { + return BigNumber.from(0); + } + + // Retrieve the RPC URL for the source chain to calculate L1 fee + const rpcMap = this.environment === "mainnet" ? mainnetRpcMap : testnetRpcMap; + + // Throw an error if the RPC URL is not found + if (!rpcMap[destChainId]) { + throw new Error(`RPC URL not found for chain ${destChainId}`); + } + + const provider = new ethers.providers.JsonRpcProvider(rpcMap[destChainId]); + + const l1FeeForL2 = await getL1FeeForL2(provider, destChainId, l1FeeParams); + + return l1FeeForL2; + } + /** * Calculate estimated gas amount to pay for the gas receiver contract. * @param sourceChainId Can be of the EvmChain enum or string. If string, should try to generalize to use the CHAINS constants (e.g. CHAINS.MAINNET.ETHEREUM) @@ -258,44 +289,44 @@ export class AxelarQueryAPI { public async estimateGasFee( sourceChainId: EvmChain | string, destinationChainId: EvmChain | string, - sourceChainTokenSymbol: GasToken | string, gasLimit: BigNumberish, gasMultiplier: number | "auto" = "auto", minGasPrice = "0", + executeData?: `0x${string}`, + sourceChainTokenSymbol?: GasToken | string, gmpParams?: GMPParams ): Promise { await throwIfInvalidChainIds([sourceChainId, destinationChainId], this.environment); - const response = await this.getNativeGasBaseFee( - sourceChainId, - destinationChainId, - sourceChainTokenSymbol as GasToken, - gmpParams?.tokenSymbol, - gmpParams?.destinationContractAddress, - gmpParams?.sourceContractAddress, - gmpParams?.transferAmount, - gmpParams?.transferAmountInUnits - ); + if (!BigNumber.from(gasLimit).gt(0)) { + throw new Error("Gas limit must be provided"); + } const { baseFee, expressFee, sourceToken, + ethereumToken, executeGasMultiplier, destToken, apiResponse, success, expressSupported, - } = response; + } = await this.getNativeGasBaseFee( + sourceChainId, + destinationChainId, + sourceChainTokenSymbol as GasToken, + gmpParams?.tokenSymbol, + gmpParams?.destinationContractAddress, + gmpParams?.sourceContractAddress, + gmpParams?.transferAmount, + gmpParams?.transferAmountInUnits + ); if (!success || !baseFee || !sourceToken) { throw new Error("Failed to estimate gas fee"); } - if (!BigNumber.from(gasLimit).gt(0)) { - throw new Error("Gas limit must be provided"); - } - const destGasFeeWei = BigNumberUtils.multiplyToGetWei( BigNumber.from(gasLimit), destToken.gas_price, @@ -309,30 +340,69 @@ export class AxelarQueryAPI { sourceToken.decimals ); - const executionFee = destGasFeeWei.gt(minDestGasFeeWei) + const excludedL1ExecutionFee = destGasFeeWei.gt(minDestGasFeeWei) ? srcGasFeeWei : srcGasFeeWei.mul(minDestGasFeeWei).div(destGasFeeWei); const actualGasMultiplier = gasMultiplier === "auto" ? executeGasMultiplier : gasMultiplier; - const executionFeeWithMultiplier = + const excludedL1ExecutionFeeWithMultiplier = actualGasMultiplier > 1 - ? executionFee.mul(actualGasMultiplier * 10000).div(10000) - : executionFee; + ? excludedL1ExecutionFee.mul(actualGasMultiplier * 10000).div(10000) + : excludedL1ExecutionFee; + + let l1ExecutionFee = BigNumber.from(0); + let l1ExecutionFeeWithMultiplier = BigNumber.from(0); + + // If the destination chain is L2, calculate the L1 execution fee + if (isL2Chain(destinationChainId)) { + if (!executeData) { + throw new Error( + `executeData is required to calculate the L1 execution fee for ${destinationChainId}` + ); + } + + if (!destToken.l1_gas_price_in_units) { + throw new Error( + `Could not find L1 gas price for ${destinationChainId}. Please try again later.` + ); + } + + // Calculate the L1 execution fee. This value is in ETH. + l1ExecutionFee = await this.estimateL1GasFee(destinationChainId, { + executeData: executeData || "0x", + l1GasPrice: destToken.l1_gas_price_in_units, + }); + + // Convert the L1 execution fee to the source token + const srcTokenPrice = Number(sourceToken.token_price.usd); + const ethTokenPrice = Number(ethereumToken.token_price.usd); + const ethToSrcTokenPriceRatio = ethTokenPrice / srcTokenPrice; + + const actualL1ExecutionFee = Math.ceil(l1ExecutionFee.toNumber() * ethToSrcTokenPriceRatio); + console.log(actualL1ExecutionFee); + + l1ExecutionFee = BigNumber.from(actualL1ExecutionFee.toString()); + + // Calculate the L1 execution fee with the gas multiplier + l1ExecutionFeeWithMultiplier = BigNumber.from( + Math.floor(actualGasMultiplier * actualGasMultiplier) + ); + } return gmpParams?.showDetailedFees ? { baseFee, expressFee, - executionFee: executionFee.toString(), - executionFeeWithMultiplier: executionFeeWithMultiplier.toString(), + executionFee: excludedL1ExecutionFeeWithMultiplier.toString(), + executionFeeWithMultiplier: excludedL1ExecutionFeeWithMultiplier.toString(), gasLimit, gasMultiplier: actualGasMultiplier, minGasPrice: minGasPrice === "0" ? "NA" : minGasPrice, apiResponse, isExpressSupported: expressSupported, } - : executionFeeWithMultiplier.add(baseFee).toString(); + : l1ExecutionFeeWithMultiplier.add(excludedL1ExecutionFee).add(baseFee).toString(); } /** diff --git a/src/libs/fee/getL1Fee.spec.ts b/src/libs/fee/getL1Fee.spec.ts index f4cbadea..0de964dd 100644 --- a/src/libs/fee/getL1Fee.spec.ts +++ b/src/libs/fee/getL1Fee.spec.ts @@ -9,43 +9,43 @@ describe("getL1Fee", () => { const env = Environment.MAINNET; it("query optimism l1 fee should work", async () => { - const srcChain = "optimism"; - const destChain = "ethereum"; + const srcChain = "avalanche"; + const destChain = "optimism"; const queryAPI = new AxelarQueryAPI({ environment: env }); const { destToken } = await queryAPI.getNativeGasBaseFee(srcChain, destChain); - const provider = new ethers.providers.JsonRpcProvider(rpcMap[srcChain]); + const provider = new ethers.providers.JsonRpcProvider(rpcMap[destChain]); const params: EstimateL1FeeParams = { executeData: "0x1a98b2e0e68ba0eb84262d4bcf91955ec2680b37bcedd59a1f48e18d183dac9961bf9d1400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000d40000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000000000762696e616e636500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863653136463639333735353230616230313337376365374238386635424138433438463844363636000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000000a8000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000dc647500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f40521500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab100000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000de83dbf000000000000000000000000000000000000000000000000015d8c7908dbe7130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000761786c5553444300000000000000000000000000000000000000000000000000", - l1GasPrice: destToken.gas_price_in_units, + l1GasPrice: destToken.l1_gas_price_in_units, }; - const fee = await getL1FeeForL2(provider, srcChain, params); + const fee = await getL1FeeForL2(provider, destChain, params); expect(fee).toBeDefined(); }); it("query mantle l1 fee should work", async () => { - const srcChain = "mantle"; - const destChain = "ethereum"; + const srcChain = "avalanche"; + const destChain = "mantle"; const queryAPI = new AxelarQueryAPI({ environment: env }); const { destToken } = await queryAPI.getNativeGasBaseFee(srcChain, destChain); - const provider = new ethers.providers.JsonRpcProvider(rpcMap[srcChain]); + const provider = new ethers.providers.JsonRpcProvider(rpcMap[destChain]); const params: EstimateL1FeeParams = { executeData: "0x1a98b2e0e68ba0eb84262d4bcf91955ec2680b37bcedd59a1f48e18d183dac9961bf9d1400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000d40000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000000000762696e616e636500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863653136463639333735353230616230313337376365374238386635424138433438463844363636000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000000a8000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000dc647500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f40521500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab100000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000de83dbf000000000000000000000000000000000000000000000000015d8c7908dbe7130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000761786c5553444300000000000000000000000000000000000000000000000000", - l1GasPrice: destToken.gas_price_in_units, + l1GasPrice: destToken.l1_gas_price_in_units, }; - const fee = await getL1FeeForL2(provider, srcChain, params); + const fee = await getL1FeeForL2(provider, destChain, params); expect(fee).toBeDefined(); }); diff --git a/src/libs/fee/getL1Fee.ts b/src/libs/fee/getL1Fee.ts index c9d4636a..099f81e5 100644 --- a/src/libs/fee/getL1Fee.ts +++ b/src/libs/fee/getL1Fee.ts @@ -9,7 +9,7 @@ import { Multicall, ContractCallContext } from "ethereum-multicall"; * @param env The environment to use. Either "mainnet" or "testnet". * @param chain The destination L2 chain. * @param params The parameters to use for the estimation. - * @returns + * @returns The estimated L1 fee. */ export function getL1FeeForL2( provider: ethers.providers.JsonRpcProvider, @@ -62,16 +62,11 @@ async function getOptimismL1Fee(multicall: Multicall, estimateL1FeeParams: Estim const { results } = await multicall.call(contractCallContext); - const [gasUsed, _dynamicOverhead, _fixedOverhead] = results["gasOracle"].callsReturnContext.map( - (call) => BigNumber.from(call.returnValues[0].hex) - ); - - const dynamicOverhead = BigNumber.from(_dynamicOverhead || 684000); - const fixedOverhead = BigNumber.from(_fixedOverhead || 2100); + const gasUsed = BigNumber.from(results["gasOracle"].callsReturnContext[0].returnValues[0].hex); - console.log("gasUsed", gasUsed.toString()); - console.log("dynamicOverhead", dynamicOverhead.toString()); - console.log("fixedOverhead", fixedOverhead.toString()); + const overheads = results["gasOracle"].callsReturnContext.slice(1); + const dynamicOverhead = BigNumber.from(overheads[0].returnValues[0] || 684000); + const fixedOverhead = BigNumber.from(overheads[1].returnValues[0] || 2100); const totalGasUsed = gasUsed.add(fixedOverhead).mul(dynamicOverhead).div(1_000_000); const gasPrice = BigNumber.from(l1GasPrice.value); diff --git a/src/libs/test/AxelarQueryAPI.spec.ts b/src/libs/test/AxelarQueryAPI.spec.ts index 333af864..6f453a35 100644 --- a/src/libs/test/AxelarQueryAPI.spec.ts +++ b/src/libs/test/AxelarQueryAPI.spec.ts @@ -88,7 +88,6 @@ describe("AxelarQueryAPI", () => { const gasAmount = await api.estimateGasFee( EvmChain.AVALANCHE, EvmChain.ETHEREUM, - GasToken.USDC, 700000, 1.1, "500000" @@ -98,11 +97,24 @@ describe("AxelarQueryAPI", () => { expect(ethers.utils.parseUnits("10000", 6).gt(gasAmount as BigNumberish)).toBeTruthy(); }); + test("It should include L1 fee for L2 destination chain", async () => { + const gasAmount = await api.estimateGasFee( + EvmChain.AVALANCHE, + EvmChain.OPTIMISM, + 700000, + 1.1, + "500000", + "0x1a98b2e0e68ba0eb84262d4bcf91955ec2680b37bcedd59a1f48e18d183dac9961bf9d1400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000d40000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000000000762696e616e636500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863653136463639333735353230616230313337376365374238386635424138433438463844363636000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000000a8000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000dc647500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f40521500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab100000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000de83dbf000000000000000000000000000000000000000000000000015d8c7908dbe7130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000761786c5553444300000000000000000000000000000000000000000000000000", + undefined + ); + + expect(gasAmount).toBeDefined(); + }); + test("It should return estimated gas amount that makes sense for native token", async () => { const gasAmount = await api.estimateGasFee( CHAINS.TESTNET.AVALANCHE as EvmChain, CHAINS.TESTNET.ETHEREUM as EvmChain, - GasToken.AVAX, 700000, 1.1, "5000000000" @@ -122,7 +134,6 @@ describe("AxelarQueryAPI", () => { const gasAmount = await api.estimateGasFee( CHAINS.TESTNET.AVALANCHE as EvmChain, CHAINS.TESTNET.ETHEREUM as EvmChain, - GasToken.AVAX, gasLimit.toNumber(), 1.1, minGasPrice.toString() @@ -161,7 +172,6 @@ describe("AxelarQueryAPI", () => { const gasAmount = await api.estimateGasFee( CHAINS.TESTNET.AVALANCHE as EvmChain, CHAINS.TESTNET.ETHEREUM as EvmChain, - GasToken.AVAX, gasLimit.toNumber(), 1.1, minGasPrice.toString() diff --git a/src/libs/types/index.ts b/src/libs/types/index.ts index 52b0a548..a2502f2f 100644 --- a/src/libs/types/index.ts +++ b/src/libs/types/index.ts @@ -77,12 +77,26 @@ export interface BaseFeeResponse { decimals: number; name: string; symbol: string; + token_price: { + usd: number; + }; }; destToken: { gas_price: string; gas_price_gwei: string; - gas_price_in_units: TokenUnit; + l1_gas_price_in_units: TokenUnit; decimals: number; + token_price: { + usd: number; + }; + }; + ethereumToken: { + name: string; + symbol: string; + decimals: number; + token_price: { + usd: number; + }; }; expressSupported: boolean; } From 27069bd73dffdbaa895aee297fa3302b8059b363 Mon Sep 17 00:00:00 2001 From: npty Date: Tue, 20 Feb 2024 16:23:28 +0700 Subject: [PATCH 10/33] chore: refactor getL1Fee --- src/libs/fee/getL1Fee.ts | 165 ++++++++++++++++++++++----------------- 1 file changed, 95 insertions(+), 70 deletions(-) diff --git a/src/libs/fee/getL1Fee.ts b/src/libs/fee/getL1Fee.ts index 099f81e5..2e1ede25 100644 --- a/src/libs/fee/getL1Fee.ts +++ b/src/libs/fee/getL1Fee.ts @@ -2,7 +2,16 @@ import { BigNumber, ethers } from "ethers"; import { EstimateL1FeeParams } from "../types"; -import { Multicall, ContractCallContext } from "ethereum-multicall"; +import { Multicall, ContractCallContext, ContractCallResults } from "ethereum-multicall"; + +const ABI = { + Optimism: [ + "function getL1GasUsed(bytes executeData) returns (uint256)", + "function scalar() returns (uint256)", + "function overhead() returns (uint256)", + ], + Mantle: ["function overhead() returns (uint256)", "function scalar() returns (uint256)"], +}; /** * Get the estimated L1 fee for a given L2 chain. @@ -19,16 +28,15 @@ export function getL1FeeForL2( const multicall = new Multicall({ ethersProvider: provider, tryAggregate: true }); switch (chain) { - // Most of the ethereum clients are already included L1 fee in the gas estimation for Arbitrum. - case "arbitrum": - case "arbitrum-sepolia": - return Promise.resolve(BigNumber.from(0)); + case "mantle": + return getMantleL1Fee(multicall, params); case "optimism": case "scroll": case "base": return getOptimismL1Fee(multicall, params); - case "mantle": - return getMantleL1Fee(multicall, params); + // Most of the ethereum clients are already included L1 fee in the gas estimation for Arbitrum. + case "arbitrum": + case "arbitrum-sepolia": default: return Promise.resolve(BigNumber.from(0)); } @@ -36,76 +44,93 @@ export function getL1FeeForL2( async function getOptimismL1Fee(multicall: Multicall, estimateL1FeeParams: EstimateL1FeeParams) { const { l1GasPrice, executeData } = estimateL1FeeParams; - - const contractAddress = "0x420000000000000000000000000000000000000F"; - - const contractCallContext: ContractCallContext[] = [ - { - reference: "gasOracle", - contractAddress, - abi: [ - "function getL1GasUsed(bytes executeData) returns (uint256)", - "function scalar() returns (uint256)", - "function overhead() returns (uint256)", - ], - calls: [ - { - reference: "l1GasUsed", - methodName: "getL1GasUsed(bytes)", - methodParameters: [executeData], - }, - { reference: "scalar", methodName: "scalar()", methodParameters: [] }, - { reference: "overhead", methodName: "overhead()", methodParameters: [] }, - ], - }, - ]; - - const { results } = await multicall.call(contractCallContext); - - const gasUsed = BigNumber.from(results["gasOracle"].callsReturnContext[0].returnValues[0].hex); - - const overheads = results["gasOracle"].callsReturnContext.slice(1); - const dynamicOverhead = BigNumber.from(overheads[0].returnValues[0] || 684000); - const fixedOverhead = BigNumber.from(overheads[1].returnValues[0] || 2100); - - const totalGasUsed = gasUsed.add(fixedOverhead).mul(dynamicOverhead).div(1_000_000); - const gasPrice = BigNumber.from(l1GasPrice.value); - - return totalGasUsed.mul(gasPrice); + const results = await multicall.call(buildContractCallContext("optimism", executeData)); + const { gasUsed, fixedOverhead, dynamicOverhead } = extractMulticallResults("optimism", results); + return calculateL1Fee(gasUsed, fixedOverhead, dynamicOverhead, BigNumber.from(l1GasPrice.value)); } async function getMantleL1Fee(multicall: Multicall, estimateL1FeeParams: EstimateL1FeeParams) { - const contractAddress = "0x420000000000000000000000000000000000000F"; - const { l1GasPrice } = estimateL1FeeParams; - - const abi = ["function overhead() returns (uint256)", "function scalar() returns (uint256)"]; + const { l1GasPrice, executeData } = estimateL1FeeParams; + const results = await multicall.call(buildContractCallContext("mantle", executeData)); + const { gasUsed, fixedOverhead, dynamicOverhead } = extractMulticallResults("mantle", results); + return calculateL1Fee(gasUsed, fixedOverhead, dynamicOverhead, BigNumber.from(l1GasPrice.value)); +} - const contractCallContext: ContractCallContext[] = [ - { - reference: "gasOracle", // Common reference - contractAddress, - abi, - calls: [ - { reference: "overhead", methodName: "overhead", methodParameters: [] }, - { reference: "scalar", methodName: "scalar", methodParameters: [] }, - ], - }, - ]; +function extractMulticallResults( + type: L1FeeCalculationType, + contractCallResults: ContractCallResults +) { + const { results } = contractCallResults; + + if (type === "optimism") { + const returnContexts = results["gasOracle"].callsReturnContext; + const gasUsed = BigNumber.from(returnContexts[0].returnValues[0].hex); + const dynamicOverhead = BigNumber.from(returnContexts[1].returnValues[0] || 684000); + const fixedOverhead = BigNumber.from(returnContexts[2].returnValues[0] || 2100); + + return { gasUsed, fixedOverhead, dynamicOverhead }; + } else if (type === "mantle") { + const [fixedOverhead, dynamicOverhead] = results.gasOracle.callsReturnContext.map( + (call) => call.returnValues + ); + + return { + gasUsed: BigNumber.from(0), + fixedOverhead: BigNumber.from(fixedOverhead), + dynamicOverhead: BigNumber.from(dynamicOverhead), + }; + } - // Execute Multicall - const { results } = await multicall.call(contractCallContext); + throw new Error("Invalid type"); +} - // Extract results - const [fixedOverhead, dynamicOverhead] = results.gasOracle.callsReturnContext.map( - (call) => call.returnValues - ); +type L1FeeCalculationType = "optimism" | "mantle"; - // (fixedOverhead * dynamicOverhead) / 1_000_000n; - const totalGasUsed = BigNumber.from(fixedOverhead) - .mul(BigNumber.from(dynamicOverhead)) - .div(1_000_000); +function calculateL1Fee( + gasUsed: BigNumber, + fixedOverhead: BigNumber, + dynamicOverhead: BigNumber, + gasPrice: BigNumber +) { + const totalGas = gasUsed.add(fixedOverhead).mul(dynamicOverhead).div(1_000_000); + return totalGas.mul(gasPrice); +} - const gasPrice = BigNumber.from(l1GasPrice.value); +function buildContractCallContext( + type: L1FeeCalculationType, + executeData: string +): ContractCallContext[] { + const contractAddress = "0x420000000000000000000000000000000000000F"; + if (type === "optimism") { + return [ + { + reference: "gasOracle", + contractAddress, + abi: ABI.Optimism, + calls: [ + { + reference: "l1GasUsed", + methodName: "getL1GasUsed(bytes)", + methodParameters: [executeData], + }, + { reference: "scalar", methodName: "scalar()", methodParameters: [] }, + { reference: "overhead", methodName: "overhead()", methodParameters: [] }, + ], + }, + ]; + } else if (type === "mantle") { + return [ + { + reference: "gasOracle", + contractAddress, + abi: ABI.Mantle, + calls: [ + { reference: "overhead", methodName: "overhead", methodParameters: [] }, + { reference: "scalar", methodName: "scalar", methodParameters: [] }, + ], + }, + ]; + } - return totalGasUsed.mul(gasPrice); + throw new Error("Invalid contract call type"); } From 128753989b22148b12f7cda107653ec8aea81094 Mon Sep 17 00:00:00 2001 From: npty Date: Tue, 20 Feb 2024 16:47:06 +0700 Subject: [PATCH 11/33] chore: add tests for checking undefined executeData --- src/libs/AxelarQueryAPI.ts | 3 +-- src/libs/test/AxelarQueryAPI.spec.ts | 37 ++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/libs/AxelarQueryAPI.ts b/src/libs/AxelarQueryAPI.ts index 4e786623..2476cc1a 100644 --- a/src/libs/AxelarQueryAPI.ts +++ b/src/libs/AxelarQueryAPI.ts @@ -1,5 +1,5 @@ import { AssetConfig } from "../assets/types"; -import { parseUnits } from "ethers/lib/utils"; +import { formatEther, parseUnits } from "ethers/lib/utils"; import { loadAssets } from "../assets"; import { EnvironmentConfigs, getConfigs } from "../constants"; import { RestService } from "../services"; @@ -380,7 +380,6 @@ export class AxelarQueryAPI { const ethToSrcTokenPriceRatio = ethTokenPrice / srcTokenPrice; const actualL1ExecutionFee = Math.ceil(l1ExecutionFee.toNumber() * ethToSrcTokenPriceRatio); - console.log(actualL1ExecutionFee); l1ExecutionFee = BigNumber.from(actualL1ExecutionFee.toString()); diff --git a/src/libs/test/AxelarQueryAPI.spec.ts b/src/libs/test/AxelarQueryAPI.spec.ts index 6f453a35..59f65147 100644 --- a/src/libs/test/AxelarQueryAPI.spec.ts +++ b/src/libs/test/AxelarQueryAPI.spec.ts @@ -3,7 +3,7 @@ import { TransferFeeResponse, } from "@axelar-network/axelarjs-types/axelar/nexus/v1beta1/query"; import { BigNumber, BigNumberish, ethers } from "ethers"; -import { parseUnits } from "ethers/lib/utils"; +import { formatEther, parseUnits } from "ethers/lib/utils"; import { CHAINS } from "../../chains"; import { AxelarQueryAPI } from "../AxelarQueryAPI"; import { Environment } from "../types"; @@ -84,26 +84,29 @@ describe("AxelarQueryAPI", () => { }); describe("estimateGasFee", () => { - test.skip("It should return estimated gas amount that makes sense for USDC", async () => { + test("It should return estimated gas amount that makes sense for USDC", async () => { const gasAmount = await api.estimateGasFee( EvmChain.AVALANCHE, - EvmChain.ETHEREUM, + "ethereum-2", 700000, 1.1, - "500000" + "500000", + undefined, + GasToken.USDC ); // gasAmount should be less than 10k usd, otherwise we handle decimal conversion incorrectly. - expect(ethers.utils.parseUnits("10000", 6).gt(gasAmount as BigNumberish)).toBeTruthy(); + expect(ethers.utils.parseEther("10000").gt(gasAmount as BigNumberish)).toBeTruthy(); }); test("It should include L1 fee for L2 destination chain", async () => { - const gasAmount = await api.estimateGasFee( - EvmChain.AVALANCHE, + const mainnetApi = new AxelarQueryAPI({ environment: Environment.MAINNET }); + const gasAmount = await mainnetApi.estimateGasFee( + EvmChain.ETHEREUM, EvmChain.OPTIMISM, - 700000, - 1.1, - "500000", + 529994, + 1, + undefined, "0x1a98b2e0e68ba0eb84262d4bcf91955ec2680b37bcedd59a1f48e18d183dac9961bf9d1400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000d40000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000000000762696e616e636500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863653136463639333735353230616230313337376365374238386635424138433438463844363636000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000000a8000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000dc647500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f40521500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab100000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000de83dbf000000000000000000000000000000000000000000000000015d8c7908dbe7130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000761786c5553444300000000000000000000000000000000000000000000000000", undefined ); @@ -111,6 +114,20 @@ describe("AxelarQueryAPI", () => { expect(gasAmount).toBeDefined(); }); + it("should return error when the source chain is L2, but the executeData is undefuned ", async () => { + expect( + api.estimateGasFee( + "ethereum-2", + EvmChain.OPTIMISM, + 700000, + 1.1, + "500000", + undefined, + GasToken.USDC + ) + ).rejects.toThrow("executeData is required to calculate the L1 execution fee for optimism"); + }); + test("It should return estimated gas amount that makes sense for native token", async () => { const gasAmount = await api.estimateGasFee( CHAINS.TESTNET.AVALANCHE as EvmChain, From db64d2b7c112e782c5827dc67d891655a00ca6bd Mon Sep 17 00:00:00 2001 From: npty Date: Tue, 20 Feb 2024 16:55:10 +0700 Subject: [PATCH 12/33] chore: remove unused imports --- src/libs/fee/types.ts | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/libs/fee/types.ts b/src/libs/fee/types.ts index 8707c0f4..cd698df4 100644 --- a/src/libs/fee/types.ts +++ b/src/libs/fee/types.ts @@ -1,16 +1 @@ -import { - arbitrum, - arbitrumGoerli, - base, - baseGoerli, - mantle, - mantleTestnet, - optimism, - optimismGoerli, - scroll, - scrollSepolia, -} from "viem/chains"; - -import { TokenUnit } from "../../gmp"; - export type L2Chain = "optimism" | "arbitrum" | "mantle" | "base" | "scroll"; From f77f744362c38c6dcc24c3c1155a67b88a18bdfc Mon Sep 17 00:00:00 2001 From: npty Date: Tue, 20 Feb 2024 16:56:29 +0700 Subject: [PATCH 13/33] chore: refactor l1FeeForL2 --- src/libs/AxelarQueryAPI.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libs/AxelarQueryAPI.ts b/src/libs/AxelarQueryAPI.ts index 2476cc1a..bd2fde1d 100644 --- a/src/libs/AxelarQueryAPI.ts +++ b/src/libs/AxelarQueryAPI.ts @@ -269,9 +269,7 @@ export class AxelarQueryAPI { const provider = new ethers.providers.JsonRpcProvider(rpcMap[destChainId]); - const l1FeeForL2 = await getL1FeeForL2(provider, destChainId, l1FeeParams); - - return l1FeeForL2; + return getL1FeeForL2(provider, destChainId, l1FeeParams); } /** From f0ca0fb7e1f0fa6eb3b1bfacaad74f6b3bcc6d83 Mon Sep 17 00:00:00 2001 From: npty Date: Tue, 20 Feb 2024 17:07:27 +0700 Subject: [PATCH 14/33] chore: move param --- src/libs/AxelarQueryAPI.ts | 2 +- .../AxelarGMPRecoveryAPI.ts | 9 +++------ src/libs/test/AxelarQueryAPI.spec.ts | 15 +++++++++------ src/libs/types/index.ts | 1 + 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/libs/AxelarQueryAPI.ts b/src/libs/AxelarQueryAPI.ts index bd2fde1d..77027b06 100644 --- a/src/libs/AxelarQueryAPI.ts +++ b/src/libs/AxelarQueryAPI.ts @@ -289,9 +289,9 @@ export class AxelarQueryAPI { destinationChainId: EvmChain | string, gasLimit: BigNumberish, gasMultiplier: number | "auto" = "auto", + sourceChainTokenSymbol?: GasToken | string, minGasPrice = "0", executeData?: `0x${string}`, - sourceChainTokenSymbol?: GasToken | string, gmpParams?: GMPParams ): Promise { await throwIfInvalidChainIds([sourceChainId, destinationChainId], this.environment); diff --git a/src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts b/src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts index b024da47..ed49aa95 100644 --- a/src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts +++ b/src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts @@ -777,7 +777,6 @@ export class AxelarGMPRecoveryAPI extends AxelarRecoveryApi { txHash: string, sourceChain: EvmChain, destinationChain: EvmChain, - gasTokenSymbol: GasToken | string, estimatedGasUsed: number, options: QueryGasFeeOptions ): Promise { @@ -790,7 +789,6 @@ export class AxelarGMPRecoveryAPI extends AxelarRecoveryApi { return this.subtractGasFee( sourceChain, destinationChain, - gasTokenSymbol, paidGasFee, estimatedGasUsed, options @@ -864,9 +862,9 @@ export class AxelarGMPRecoveryAPI extends AxelarRecoveryApi { amount: await this.axelarQueryApi.estimateGasFee( tx.call.chain, tx.call.returnValues.destinationChain, - tx.gas_paid?.returnValues?.denom ?? "uaxl", gasLimit, - autocalculateGasOptions?.gasMultipler + autocalculateGasOptions?.gasMultipler, + tx.gas_paid?.returnValues?.denom ?? "uaxl" ), }; @@ -1161,7 +1159,6 @@ export class AxelarGMPRecoveryAPI extends AxelarRecoveryApi { private async subtractGasFee( sourceChain: string, destinationChain: string, - gasTokenSymbol: string, paidGasFee: string, estimatedGas: number, options: QueryGasFeeOptions @@ -1169,9 +1166,9 @@ export class AxelarGMPRecoveryAPI extends AxelarRecoveryApi { const totalGasFee = await this.axelarQueryApi.estimateGasFee( sourceChain, destinationChain, - gasTokenSymbol, estimatedGas, options.gasMultipler, + options.gasTokenSymbol, undefined, undefined ); diff --git a/src/libs/test/AxelarQueryAPI.spec.ts b/src/libs/test/AxelarQueryAPI.spec.ts index 59f65147..b62ff5c8 100644 --- a/src/libs/test/AxelarQueryAPI.spec.ts +++ b/src/libs/test/AxelarQueryAPI.spec.ts @@ -90,9 +90,9 @@ describe("AxelarQueryAPI", () => { "ethereum-2", 700000, 1.1, + GasToken.USDC, "500000", - undefined, - GasToken.USDC + undefined ); // gasAmount should be less than 10k usd, otherwise we handle decimal conversion incorrectly. @@ -107,8 +107,8 @@ describe("AxelarQueryAPI", () => { 529994, 1, undefined, - "0x1a98b2e0e68ba0eb84262d4bcf91955ec2680b37bcedd59a1f48e18d183dac9961bf9d1400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000d40000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000000000762696e616e636500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863653136463639333735353230616230313337376365374238386635424138433438463844363636000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000000a8000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000dc647500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f40521500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab100000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000de83dbf000000000000000000000000000000000000000000000000015d8c7908dbe7130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000761786c5553444300000000000000000000000000000000000000000000000000", - undefined + undefined, + "0x1a98b2e0e68ba0eb84262d4bcf91955ec2680b37bcedd59a1f48e18d183dac9961bf9d1400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000d40000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000000000762696e616e636500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863653136463639333735353230616230313337376365374238386635424138433438463844363636000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000000a8000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000dc647500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f40521500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab100000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000de83dbf000000000000000000000000000000000000000000000000015d8c7908dbe7130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000761786c5553444300000000000000000000000000000000000000000000000000" ); expect(gasAmount).toBeDefined(); @@ -121,9 +121,9 @@ describe("AxelarQueryAPI", () => { EvmChain.OPTIMISM, 700000, 1.1, + GasToken.USDC, "500000", - undefined, - GasToken.USDC + undefined ) ).rejects.toThrow("executeData is required to calculate the L1 execution fee for optimism"); }); @@ -134,6 +134,7 @@ describe("AxelarQueryAPI", () => { CHAINS.TESTNET.ETHEREUM as EvmChain, 700000, 1.1, + undefined, "5000000000" ); @@ -153,6 +154,7 @@ describe("AxelarQueryAPI", () => { CHAINS.TESTNET.ETHEREUM as EvmChain, gasLimit.toNumber(), 1.1, + undefined, minGasPrice.toString() ); const destGasFeeWei = parseUnits( @@ -191,6 +193,7 @@ describe("AxelarQueryAPI", () => { CHAINS.TESTNET.ETHEREUM as EvmChain, gasLimit.toNumber(), 1.1, + undefined, minGasPrice.toString() ); diff --git a/src/libs/types/index.ts b/src/libs/types/index.ts index a2502f2f..cddfcd2b 100644 --- a/src/libs/types/index.ts +++ b/src/libs/types/index.ts @@ -170,6 +170,7 @@ export interface TxResult { export interface QueryGasFeeOptions { provider?: ethers.providers.JsonRpcProvider; + gasTokenSymbol?: GasToken | string; gasMultipler?: number; shouldSubtractBaseFee?: boolean; } From 038e212c57af1b7ae37fdcf9bcc9564d1383a605 Mon Sep 17 00:00:00 2001 From: npty Date: Tue, 20 Feb 2024 17:13:01 +0700 Subject: [PATCH 15/33] chore: remove gasTokenSymbol from calculateNativeGasFee --- src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts | 6 +----- .../TransactionRecoveryAPI/AxelarGMPRecoveryAPI.spec.ts | 2 -- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts b/src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts index ed49aa95..e534b5c2 100644 --- a/src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts +++ b/src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts @@ -742,7 +742,6 @@ export class AxelarGMPRecoveryAPI extends AxelarRecoveryApi { txHash: string, sourceChain: string, destinationChain: string, - gasTokenSymbol: GasToken | string, estimatedGasUsed: number, options: QueryGasFeeOptions ): Promise { @@ -757,7 +756,6 @@ export class AxelarGMPRecoveryAPI extends AxelarRecoveryApi { return this.subtractGasFee( sourceChain, destinationChain, - gasTokenSymbol, paidGasFee, estimatedGasUsed, options @@ -958,7 +956,6 @@ export class AxelarGMPRecoveryAPI extends AxelarRecoveryApi { txHash, chain, destinationChain, - gasToken, estimatedGasUsed, { gasMultipler: options?.gasMultipler, @@ -1052,7 +1049,6 @@ export class AxelarGMPRecoveryAPI extends AxelarRecoveryApi { txHash, chain, destinationChain as EvmChain, - gasTokenSymbol, estimatedGasUsed, { provider: evmWalletDetails.provider, @@ -1176,7 +1172,7 @@ export class AxelarGMPRecoveryAPI extends AxelarRecoveryApi { let topupGasAmount = ethers.BigNumber.from(totalGasFee); if (options.shouldSubtractBaseFee) { const response = await this.axelarQueryApi - .getNativeGasBaseFee(sourceChain, destinationChain, gasTokenSymbol as GasToken) + .getNativeGasBaseFee(sourceChain, destinationChain) .catch(() => undefined); if (response && response.baseFee) { topupGasAmount = topupGasAmount.sub(response.baseFee); diff --git a/src/libs/test/TransactionRecoveryAPI/AxelarGMPRecoveryAPI.spec.ts b/src/libs/test/TransactionRecoveryAPI/AxelarGMPRecoveryAPI.spec.ts index 52ee5c04..3852a332 100644 --- a/src/libs/test/TransactionRecoveryAPI/AxelarGMPRecoveryAPI.spec.ts +++ b/src/libs/test/TransactionRecoveryAPI/AxelarGMPRecoveryAPI.spec.ts @@ -749,7 +749,6 @@ describe("AxelarGMPRecoveryAPI", () => { tx.transactionHash, EvmChain.AVALANCHE, EvmChain.MOONBEAM, - GasToken.AVAX, 700000, { provider } ); @@ -785,7 +784,6 @@ describe("AxelarGMPRecoveryAPI", () => { tx.transactionHash, EvmChain.AVALANCHE, EvmChain.MOONBEAM, - GasToken.AVAX, 700000, { provider } ); From 20721e2d2493f88f2ae7ac225f7a0f863fa77ee8 Mon Sep 17 00:00:00 2001 From: npty Date: Tue, 20 Feb 2024 17:21:03 +0700 Subject: [PATCH 16/33] chore: update changelog --- CHANGELOG.md | 11 +++++++++++ .../TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts | 1 - 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9835858..68b134b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +# [0.15.0] - 2024-FEBRUARY-21 + +- Improved the accuracy of `estimateGasFee` function by incorporating L1 rollup fee for destination L2 chain. + +Breaking Changes: + +The `sourceChainTokenSymbol` will not be required anymore. If not specified, the native token of source chain will be used to calculate estimated fee. The following functions are affected: + +- `calculateNativeGasFee` +- `estimateGasFee` + ## [0.14.1] - 2024-FEBRUARY-16 - Fix floating points issue for `executeGasMultiplier` in `estimateGasFee` function. diff --git a/src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts b/src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts index e534b5c2..985aeda6 100644 --- a/src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts +++ b/src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts @@ -934,7 +934,6 @@ export class AxelarGMPRecoveryAPI extends AxelarRecoveryApi { chain, "gas_service" ); - const gasToken = nativeGasTokenSymbol[this.environment][chain]; const receipt = await signer.provider.getTransactionReceipt(txHash); if (!receipt) return InvalidTransactionError(chain); From c16f23305f9f0ee4f4c25e503d83ee2ae5cbfa95 Mon Sep 17 00:00:00 2001 From: npty Date: Tue, 20 Feb 2024 17:22:04 +0700 Subject: [PATCH 17/33] chore: add function doc --- src/libs/AxelarQueryAPI.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/AxelarQueryAPI.ts b/src/libs/AxelarQueryAPI.ts index 77027b06..482fef9d 100644 --- a/src/libs/AxelarQueryAPI.ts +++ b/src/libs/AxelarQueryAPI.ts @@ -276,11 +276,12 @@ export class AxelarQueryAPI { * Calculate estimated gas amount to pay for the gas receiver contract. * @param sourceChainId Can be of the EvmChain enum or string. If string, should try to generalize to use the CHAINS constants (e.g. CHAINS.MAINNET.ETHEREUM) * @param destinationChainId Can be of the EvmChain enum or string. If string, should try to generalize to use the CHAINS constants (e.g. CHAINS.MAINNET.ETHEREUM) - * @param sourceChainTokenSymbol * @param gasLimit An estimated gas amount required to execute `executeWithToken` function. * @param gasMultiplier (Optional) A multiplier used to create a buffer above the calculated gas fee, to account for potential slippage throughout tx execution, e.g. 1.1 = 10% buffer. supports up to 3 decimal places * The default value is "auto", which uses the gas multiplier from the fee response + * @param sourceChainTokenSymbol (Optional) the gas token symbol on the source chain. * @param minGasPrice (Optional) A minimum value, in wei, for the gas price on the destination chain that is used to override the estimated gas price if it falls below this specified value. + * @param executeData (Optional) The data to be executed on the destination chain. Required if the destination chain is an L2 chain. * @param gmpParams (Optional) Additional parameters for GMP transactions, including the ability to see a detailed view of the fee response * @returns */ From bd2def519948cd53d915b8b9520bf27e3cc03ff9 Mon Sep 17 00:00:00 2001 From: npty Date: Tue, 20 Feb 2024 17:25:24 +0700 Subject: [PATCH 18/33] chore: add l1ExecutionFee to response --- src/libs/AxelarQueryAPI.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libs/AxelarQueryAPI.ts b/src/libs/AxelarQueryAPI.ts index 482fef9d..8e8a0c79 100644 --- a/src/libs/AxelarQueryAPI.ts +++ b/src/libs/AxelarQueryAPI.ts @@ -42,6 +42,8 @@ export interface AxelarQueryAPIFeeResponse { baseFee: string; executionFee: string; executionFeeWithMultiplier: string; + l1ExecutionFeeWithMultiplier: string; + l1ExecutionFee: string; gasMultiplier: number; gasLimit: BigNumberish; minGasPrice: string; @@ -394,6 +396,8 @@ export class AxelarQueryAPI { expressFee, executionFee: excludedL1ExecutionFeeWithMultiplier.toString(), executionFeeWithMultiplier: excludedL1ExecutionFeeWithMultiplier.toString(), + l1ExecutionFeeWithMultiplier: l1ExecutionFeeWithMultiplier.toString(), + l1ExecutionFee: l1ExecutionFee.toString(), gasLimit, gasMultiplier: actualGasMultiplier, minGasPrice: minGasPrice === "0" ? "NA" : minGasPrice, From 08bb03786960deaccb68ec9d66ec7169ee71a883 Mon Sep 17 00:00:00 2001 From: npty Date: Tue, 20 Feb 2024 17:32:41 +0700 Subject: [PATCH 19/33] chore: add more tests --- src/libs/test/AxelarQueryAPI.spec.ts | 33 ++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/libs/test/AxelarQueryAPI.spec.ts b/src/libs/test/AxelarQueryAPI.spec.ts index b62ff5c8..0e5f5992 100644 --- a/src/libs/test/AxelarQueryAPI.spec.ts +++ b/src/libs/test/AxelarQueryAPI.spec.ts @@ -3,7 +3,7 @@ import { TransferFeeResponse, } from "@axelar-network/axelarjs-types/axelar/nexus/v1beta1/query"; import { BigNumber, BigNumberish, ethers } from "ethers"; -import { formatEther, parseUnits } from "ethers/lib/utils"; +import { formatEther, parseEther, parseUnits } from "ethers/lib/utils"; import { CHAINS } from "../../chains"; import { AxelarQueryAPI } from "../AxelarQueryAPI"; import { Environment } from "../types"; @@ -11,6 +11,9 @@ import { EvmChain } from "../../constants/EvmChain"; import { GasToken } from "../../constants/GasToken"; import { activeChainsStub, getFeeStub } from "./stubs"; +const MOCK_EXECUTE_DATA = + "0x1a98b2e0e68ba0eb84262d4bcf91955ec2680b37bcedd59a1f48e18d183dac9961bf9d1400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000d40000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000000000762696e616e636500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863653136463639333735353230616230313337376365374238386635424138433438463844363636000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000000a8000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000dc647500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f40521500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab100000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000de83dbf000000000000000000000000000000000000000000000000015d8c7908dbe7130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000761786c5553444300000000000000000000000000000000000000000000000000"; + describe("AxelarQueryAPI", () => { const api = new AxelarQueryAPI({ environment: Environment.TESTNET }); @@ -83,6 +86,32 @@ describe("AxelarQueryAPI", () => { }); }); + describe("estimateL1GasFee", () => { + const mainnetApi = new AxelarQueryAPI({ environment: Environment.MAINNET }); + + test("it should return 0 if the destination chain is not a L2 chain", async () => { + const gasAmount = await mainnetApi.estimateL1GasFee(EvmChain.AVALANCHE, { + executeData: "0x", + l1GasPrice: { + decimals: 18, + value: "32534506865", + }, + }); + expect(gasAmount.toString()).toEqual("0"); + }); + + test("it should return the logical L1 gas fee for a destination L2 chain", async () => { + const gasAmount = await mainnetApi.estimateL1GasFee(EvmChain.OPTIMISM, { + executeData: MOCK_EXECUTE_DATA, + l1GasPrice: { + decimals: 18, + value: "32534506865", + }, + }); + expect(gasAmount.gt(parseEther("0.00001"))).toBeTruthy(); + }); + }); + describe("estimateGasFee", () => { test("It should return estimated gas amount that makes sense for USDC", async () => { const gasAmount = await api.estimateGasFee( @@ -108,7 +137,7 @@ describe("AxelarQueryAPI", () => { 1, undefined, undefined, - "0x1a98b2e0e68ba0eb84262d4bcf91955ec2680b37bcedd59a1f48e18d183dac9961bf9d1400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000d40000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000000000762696e616e636500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863653136463639333735353230616230313337376365374238386635424138433438463844363636000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000000a8000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000dc647500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f40521500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab100000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000de83dbf000000000000000000000000000000000000000000000000015d8c7908dbe7130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000761786c5553444300000000000000000000000000000000000000000000000000" + MOCK_EXECUTE_DATA ); expect(gasAmount).toBeDefined(); From a943d7d3f2353bc1c367c33f60fa1706ece9e7dd Mon Sep 17 00:00:00 2001 From: npty Date: Tue, 20 Feb 2024 17:34:27 +0700 Subject: [PATCH 20/33] chore: cleanup tests --- src/libs/AxelarQueryAPI.ts | 2 +- src/libs/fee/getL1Fee.spec.ts | 10 +++++----- src/libs/test/AxelarQueryAPI.spec.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libs/AxelarQueryAPI.ts b/src/libs/AxelarQueryAPI.ts index 8e8a0c79..59224c59 100644 --- a/src/libs/AxelarQueryAPI.ts +++ b/src/libs/AxelarQueryAPI.ts @@ -1,5 +1,5 @@ import { AssetConfig } from "../assets/types"; -import { formatEther, parseUnits } from "ethers/lib/utils"; +import { parseUnits } from "ethers/lib/utils"; import { loadAssets } from "../assets"; import { EnvironmentConfigs, getConfigs } from "../constants"; import { RestService } from "../services"; diff --git a/src/libs/fee/getL1Fee.spec.ts b/src/libs/fee/getL1Fee.spec.ts index 0de964dd..ff330493 100644 --- a/src/libs/fee/getL1Fee.spec.ts +++ b/src/libs/fee/getL1Fee.spec.ts @@ -3,7 +3,9 @@ import { rpcMap } from "../TransactionRecoveryApi/constants/chain/mainnet"; import { Environment, EstimateL1FeeParams } from "../types"; import { getL1FeeForL2 } from "./getL1Fee"; import { AxelarQueryAPI } from "../AxelarQueryAPI"; -import { formatEther } from "ethers/lib/utils"; + +const MOCK_EXECUTE_DATA = + "0x1a98b2e0e68ba0eb84262d4bcf91955ec2680b37bcedd59a1f48e18d183dac9961bf9d1400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000d40000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000000000762696e616e636500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863653136463639333735353230616230313337376365374238386635424138433438463844363636000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000000a8000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000dc647500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f40521500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab100000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000de83dbf000000000000000000000000000000000000000000000000015d8c7908dbe7130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000761786c5553444300000000000000000000000000000000000000000000000000"; describe("getL1Fee", () => { const env = Environment.MAINNET; @@ -19,8 +21,7 @@ describe("getL1Fee", () => { const provider = new ethers.providers.JsonRpcProvider(rpcMap[destChain]); const params: EstimateL1FeeParams = { - executeData: - "0x1a98b2e0e68ba0eb84262d4bcf91955ec2680b37bcedd59a1f48e18d183dac9961bf9d1400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000d40000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000000000762696e616e636500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863653136463639333735353230616230313337376365374238386635424138433438463844363636000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000000a8000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000dc647500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f40521500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab100000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000de83dbf000000000000000000000000000000000000000000000000015d8c7908dbe7130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000761786c5553444300000000000000000000000000000000000000000000000000", + executeData: MOCK_EXECUTE_DATA, l1GasPrice: destToken.l1_gas_price_in_units, }; @@ -40,8 +41,7 @@ describe("getL1Fee", () => { const provider = new ethers.providers.JsonRpcProvider(rpcMap[destChain]); const params: EstimateL1FeeParams = { - executeData: - "0x1a98b2e0e68ba0eb84262d4bcf91955ec2680b37bcedd59a1f48e18d183dac9961bf9d1400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000d40000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000000000762696e616e636500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863653136463639333735353230616230313337376365374238386635424138433438463844363636000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000000a8000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000dc647500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f40521500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab100000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000de83dbf000000000000000000000000000000000000000000000000015d8c7908dbe7130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000761786c5553444300000000000000000000000000000000000000000000000000", + executeData: MOCK_EXECUTE_DATA, l1GasPrice: destToken.l1_gas_price_in_units, }; diff --git a/src/libs/test/AxelarQueryAPI.spec.ts b/src/libs/test/AxelarQueryAPI.spec.ts index 0e5f5992..74f5062c 100644 --- a/src/libs/test/AxelarQueryAPI.spec.ts +++ b/src/libs/test/AxelarQueryAPI.spec.ts @@ -3,7 +3,7 @@ import { TransferFeeResponse, } from "@axelar-network/axelarjs-types/axelar/nexus/v1beta1/query"; import { BigNumber, BigNumberish, ethers } from "ethers"; -import { formatEther, parseEther, parseUnits } from "ethers/lib/utils"; +import { parseEther, parseUnits } from "ethers/lib/utils"; import { CHAINS } from "../../chains"; import { AxelarQueryAPI } from "../AxelarQueryAPI"; import { Environment } from "../types"; From 73d6d7ee67b5da7bfbf7372703369d673c91442b Mon Sep 17 00:00:00 2001 From: npty Date: Fri, 23 Feb 2024 23:09:02 +0700 Subject: [PATCH 21/33] chore: use chains from the api instead of hardcoded chains --- src/utils/isL2Chain.ts | 13 ++++++++++--- src/utils/test/isL2Chain.spec.ts | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 src/utils/test/isL2Chain.spec.ts diff --git a/src/utils/isL2Chain.ts b/src/utils/isL2Chain.ts index 3df63e6b..2c4f4cf9 100644 --- a/src/utils/isL2Chain.ts +++ b/src/utils/isL2Chain.ts @@ -1,5 +1,12 @@ -import { L2_CHAINS } from "../constants/L2Chain"; +import { loadChains } from "../chains"; +import { Environment, FeeToken } from "../libs"; -export function isL2Chain(chain: string): boolean { - return L2_CHAINS.includes(chain); +export async function isL2Chain( + environment: Environment, + chain: string, + destinationNativeToken: FeeToken +) { + const chains = await loadChains({ environment }); + const evmChains = chains.filter((chain) => chain.module === "evm").map((chain) => chain.id); + return destinationNativeToken.l1_gas_price_in_units && evmChains.includes(chain); } diff --git a/src/utils/test/isL2Chain.spec.ts b/src/utils/test/isL2Chain.spec.ts new file mode 100644 index 00000000..2ab0290f --- /dev/null +++ b/src/utils/test/isL2Chain.spec.ts @@ -0,0 +1,33 @@ +import { Environment, FeeToken } from "../../libs"; +import { isL2Chain } from "../isL2Chain"; + +const mockFeeToken: FeeToken = { + gas_price: "1893353954513", + decimals: 18, + name: "Avalanche", + symbol: "AVAX", + token_price: { + usd: 20.56, + }, + l1_gas_price_in_units: { + decimals: 18, + value: "1", + }, +}; + +describe("isL2Chain", () => { + it("should return true if the chain is an L2 chain", async () => { + const environment = Environment.MAINNET; + const l2Chain = await isL2Chain(environment, "arbitrum", mockFeeToken); + expect(l2Chain).toBeTruthy(); + }); + + it("should return false if dest token.l1_gas_price_in_units is undefined", async () => { + const environment = Environment.MAINNET; + const l2Chain = await isL2Chain(environment, "arbitrum", { + ...mockFeeToken, + l1_gas_price_in_units: undefined, + }); + expect(l2Chain).toBeFalsy(); + }); +}); From 3d482ea56dd3ef93b65ddd0366f93c36042c63e9 Mon Sep 17 00:00:00 2001 From: npty Date: Fri, 23 Feb 2024 23:09:27 +0700 Subject: [PATCH 22/33] chore: group l1 fee calculation --- src/libs/AxelarQueryAPI.ts | 114 ++++++++++++++++++++--------------- src/libs/test/stubs/index.ts | 1 - src/libs/types/index.ts | 31 ++++------ 3 files changed, 80 insertions(+), 66 deletions(-) diff --git a/src/libs/AxelarQueryAPI.ts b/src/libs/AxelarQueryAPI.ts index 59224c59..a8fd7f03 100644 --- a/src/libs/AxelarQueryAPI.ts +++ b/src/libs/AxelarQueryAPI.ts @@ -3,7 +3,13 @@ import { parseUnits } from "ethers/lib/utils"; import { loadAssets } from "../assets"; import { EnvironmentConfigs, getConfigs } from "../constants"; import { RestService } from "../services"; -import { AxelarQueryAPIConfig, BaseFeeResponse, Environment, EstimateL1FeeParams } from "./types"; +import { + AxelarQueryAPIConfig, + BaseFeeResponse, + Environment, + EstimateL1FeeParams, + FeeToken, +} from "./types"; import { EvmChain } from "../constants/EvmChain"; import { GasToken } from "../constants/GasToken"; import { AxelarQueryClient, AxelarQueryClientType } from "./AxelarQueryClient"; @@ -240,9 +246,10 @@ export class AxelarQueryAPI { executeGasMultiplier: parseFloat(execute_gas_multiplier.toFixed(2)), destToken: { gas_price: destination_native_token.gas_price, - gas_price_gwei: parseInt(destination_native_token.gas_price_gwei).toString(), decimals: destination_native_token.decimals, token_price: destination_native_token.token_price, + name: destination_native_token.name, + symbol: destination_native_token.symbol, l1_gas_price_in_units: destination_native_token.l1_gas_price_in_units, }, ethereumToken: ethereum_token as BaseFeeResponse["ethereumToken"], @@ -253,14 +260,7 @@ export class AxelarQueryAPI { }); } - public async estimateL1GasFee( - destChainId: EvmChain | string, - l1FeeParams: EstimateL1FeeParams - ): Promise { - if (!isL2Chain(destChainId)) { - return BigNumber.from(0); - } - + public async estimateL1GasFee(destChainId: EvmChain | string, l1FeeParams: EstimateL1FeeParams) { // Retrieve the RPC URL for the source chain to calculate L1 fee const rpcMap = this.environment === "mainnet" ? mainnetRpcMap : testnetRpcMap; @@ -274,6 +274,55 @@ export class AxelarQueryAPI { return getL1FeeForL2(provider, destChainId, l1FeeParams); } + public async calculateL1FeeForDestL2( + destChainId: EvmChain | string, + destToken: FeeToken, + executeData: `0x${string}` | undefined, + sourceToken: FeeToken, + ethereumToken: BaseFeeResponse["ethereumToken"], + actualGasMultiplier: number + ): Promise<[BigNumber, BigNumber]> { + let l1ExecutionFee = BigNumber.from(0); + let l1ExecutionFeeWithMultiplier = BigNumber.from(0); + + // If the destination chain is L2, calculate the L1 execution fee + const isDestinationChainL2 = await isL2Chain(this.environment, destChainId, destToken); + + if (isDestinationChainL2) { + if (!executeData) { + throw new Error( + `executeData is required to calculate the L1 execution fee for ${destChainId}` + ); + } + + if (!destToken.l1_gas_price_in_units) { + throw new Error(`Could not find L1 gas price for ${destChainId}. Please try again later.`); + } + + // Calculate the L1 execution fee. This value is in ETH. + l1ExecutionFee = await this.estimateL1GasFee(destChainId, { + executeData: executeData, + l1GasPrice: destToken.l1_gas_price_in_units, + }); + + // Convert the L1 execution fee to the source token + const srcTokenPrice = Number(sourceToken.token_price.usd); + const ethTokenPrice = Number(ethereumToken.token_price.usd); + const ethToSrcTokenPriceRatio = ethTokenPrice / srcTokenPrice; + + const actualL1ExecutionFee = Math.ceil(l1ExecutionFee.toNumber() * ethToSrcTokenPriceRatio); + + l1ExecutionFee = BigNumber.from(actualL1ExecutionFee.toString()); + + // Calculate the L1 execution fee with the gas multiplier + l1ExecutionFeeWithMultiplier = BigNumber.from( + Math.floor(actualGasMultiplier * actualGasMultiplier) + ); + } + + return [l1ExecutionFee, l1ExecutionFeeWithMultiplier]; + } + /** * Calculate estimated gas amount to pay for the gas receiver contract. * @param sourceChainId Can be of the EvmChain enum or string. If string, should try to generalize to use the CHAINS constants (e.g. CHAINS.MAINNET.ETHEREUM) @@ -352,43 +401,14 @@ export class AxelarQueryAPI { ? excludedL1ExecutionFee.mul(actualGasMultiplier * 10000).div(10000) : excludedL1ExecutionFee; - let l1ExecutionFee = BigNumber.from(0); - let l1ExecutionFeeWithMultiplier = BigNumber.from(0); - - // If the destination chain is L2, calculate the L1 execution fee - if (isL2Chain(destinationChainId)) { - if (!executeData) { - throw new Error( - `executeData is required to calculate the L1 execution fee for ${destinationChainId}` - ); - } - - if (!destToken.l1_gas_price_in_units) { - throw new Error( - `Could not find L1 gas price for ${destinationChainId}. Please try again later.` - ); - } - - // Calculate the L1 execution fee. This value is in ETH. - l1ExecutionFee = await this.estimateL1GasFee(destinationChainId, { - executeData: executeData || "0x", - l1GasPrice: destToken.l1_gas_price_in_units, - }); - - // Convert the L1 execution fee to the source token - const srcTokenPrice = Number(sourceToken.token_price.usd); - const ethTokenPrice = Number(ethereumToken.token_price.usd); - const ethToSrcTokenPriceRatio = ethTokenPrice / srcTokenPrice; - - const actualL1ExecutionFee = Math.ceil(l1ExecutionFee.toNumber() * ethToSrcTokenPriceRatio); - - l1ExecutionFee = BigNumber.from(actualL1ExecutionFee.toString()); - - // Calculate the L1 execution fee with the gas multiplier - l1ExecutionFeeWithMultiplier = BigNumber.from( - Math.floor(actualGasMultiplier * actualGasMultiplier) - ); - } + const [l1ExecutionFee, l1ExecutionFeeWithMultiplier] = await this.calculateL1FeeForDestL2( + destinationChainId, + destToken, + executeData, + sourceToken, + ethereumToken, + actualGasMultiplier + ); return gmpParams?.showDetailedFees ? { diff --git a/src/libs/test/stubs/index.ts b/src/libs/test/stubs/index.ts index aaa1ba77..b45897ed 100644 --- a/src/libs/test/stubs/index.ts +++ b/src/libs/test/stubs/index.ts @@ -328,7 +328,6 @@ export const getFeeStub = () => ({ usd: 1674.72, }, gas_price: "0.000000023244098897", - gas_price_gwei: "23.244098897", }, destination_base_fee: 0.00514232878886753, }, diff --git a/src/libs/types/index.ts b/src/libs/types/index.ts index cddfcd2b..b4d07ec0 100644 --- a/src/libs/types/index.ts +++ b/src/libs/types/index.ts @@ -65,6 +65,17 @@ export interface AxelarQueryAPIConfig { debug?: boolean; } +export type FeeToken = { + gas_price: string; + decimals: number; + name: string; + l1_gas_price_in_units?: TokenUnit; + symbol: string; + token_price: { + usd: number; + }; +}; + export interface BaseFeeResponse { success: boolean; apiResponse?: any; @@ -72,24 +83,8 @@ export interface BaseFeeResponse { baseFee: string; expressFee: string; executeGasMultiplier: number; - sourceToken: { - gas_price: string; - decimals: number; - name: string; - symbol: string; - token_price: { - usd: number; - }; - }; - destToken: { - gas_price: string; - gas_price_gwei: string; - l1_gas_price_in_units: TokenUnit; - decimals: number; - token_price: { - usd: number; - }; - }; + sourceToken: FeeToken; + destToken: FeeToken; ethereumToken: { name: string; symbol: string; From 7f8917d92286d8e64c05a84cee53f856c479e196 Mon Sep 17 00:00:00 2001 From: npty Date: Fri, 23 Feb 2024 23:11:03 +0700 Subject: [PATCH 23/33] chore: fix build error --- src/libs/fee/getL1Fee.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/fee/getL1Fee.spec.ts b/src/libs/fee/getL1Fee.spec.ts index ff330493..a6605403 100644 --- a/src/libs/fee/getL1Fee.spec.ts +++ b/src/libs/fee/getL1Fee.spec.ts @@ -22,7 +22,7 @@ describe("getL1Fee", () => { const params: EstimateL1FeeParams = { executeData: MOCK_EXECUTE_DATA, - l1GasPrice: destToken.l1_gas_price_in_units, + l1GasPrice: destToken.l1_gas_price_in_units!, }; const fee = await getL1FeeForL2(provider, destChain, params); @@ -42,7 +42,7 @@ describe("getL1Fee", () => { const params: EstimateL1FeeParams = { executeData: MOCK_EXECUTE_DATA, - l1GasPrice: destToken.l1_gas_price_in_units, + l1GasPrice: destToken.l1_gas_price_in_units!, }; const fee = await getL1FeeForL2(provider, destChain, params); From 784458753d3af8971562eb19ea4689a63109d0ab Mon Sep 17 00:00:00 2001 From: Canh Trinh Date: Wed, 28 Feb 2024 20:31:09 -0500 Subject: [PATCH 24/33] chore: optimize l2 gas calculations for estimateGasFee (#300) --- src/constants/L2Chain.ts | 10 ---------- src/libs/AxelarQueryAPI.ts | 20 +++++++++++--------- src/utils/isL2Chain.ts | 5 +---- 3 files changed, 12 insertions(+), 23 deletions(-) delete mode 100644 src/constants/L2Chain.ts diff --git a/src/constants/L2Chain.ts b/src/constants/L2Chain.ts deleted file mode 100644 index f4514440..00000000 --- a/src/constants/L2Chain.ts +++ /dev/null @@ -1,10 +0,0 @@ -export enum L2Chain { - ARBITRUM = "arbitrum", - ARBITRUM_SEPOLIA = "arbitrum-sepolia", - BASE = "base", - OPTIMISM = "optimism", - MANTLE = "mantle", - SCROLL = "scroll", -} - -export const L2_CHAINS = Object.values(L2Chain) as string[]; diff --git a/src/libs/AxelarQueryAPI.ts b/src/libs/AxelarQueryAPI.ts index a8fd7f03..3d6744b7 100644 --- a/src/libs/AxelarQueryAPI.ts +++ b/src/libs/AxelarQueryAPI.ts @@ -290,9 +290,11 @@ export class AxelarQueryAPI { if (isDestinationChainL2) { if (!executeData) { - throw new Error( - `executeData is required to calculate the L1 execution fee for ${destChainId}` + console.warn( + `Since you did not provide executeData, this API will not accurately calculate the + total required fee as we will not be able to capture the L1 inclusion fee for this L2 chain.` ); + return [l1ExecutionFee, l1ExecutionFeeWithMultiplier]; } if (!destToken.l1_gas_price_in_units) { @@ -390,16 +392,16 @@ export class AxelarQueryAPI { sourceToken.decimals ); - const excludedL1ExecutionFee = destGasFeeWei.gt(minDestGasFeeWei) + const executionFee = destGasFeeWei.gt(minDestGasFeeWei) ? srcGasFeeWei : srcGasFeeWei.mul(minDestGasFeeWei).div(destGasFeeWei); const actualGasMultiplier = gasMultiplier === "auto" ? executeGasMultiplier : gasMultiplier; - const excludedL1ExecutionFeeWithMultiplier = + const executionFeeWithMultiplier = actualGasMultiplier > 1 - ? excludedL1ExecutionFee.mul(actualGasMultiplier * 10000).div(10000) - : excludedL1ExecutionFee; + ? executionFee.mul(actualGasMultiplier * 10000).div(10000) + : executionFee; const [l1ExecutionFee, l1ExecutionFeeWithMultiplier] = await this.calculateL1FeeForDestL2( destinationChainId, @@ -414,8 +416,8 @@ export class AxelarQueryAPI { ? { baseFee, expressFee, - executionFee: excludedL1ExecutionFeeWithMultiplier.toString(), - executionFeeWithMultiplier: excludedL1ExecutionFeeWithMultiplier.toString(), + executionFee: executionFeeWithMultiplier.toString(), + executionFeeWithMultiplier: executionFeeWithMultiplier.toString(), l1ExecutionFeeWithMultiplier: l1ExecutionFeeWithMultiplier.toString(), l1ExecutionFee: l1ExecutionFee.toString(), gasLimit, @@ -424,7 +426,7 @@ export class AxelarQueryAPI { apiResponse, isExpressSupported: expressSupported, } - : l1ExecutionFeeWithMultiplier.add(excludedL1ExecutionFee).add(baseFee).toString(); + : l1ExecutionFeeWithMultiplier.add(executionFeeWithMultiplier).add(baseFee).toString(); } /** diff --git a/src/utils/isL2Chain.ts b/src/utils/isL2Chain.ts index 2c4f4cf9..6d90ec44 100644 --- a/src/utils/isL2Chain.ts +++ b/src/utils/isL2Chain.ts @@ -1,4 +1,3 @@ -import { loadChains } from "../chains"; import { Environment, FeeToken } from "../libs"; export async function isL2Chain( @@ -6,7 +5,5 @@ export async function isL2Chain( chain: string, destinationNativeToken: FeeToken ) { - const chains = await loadChains({ environment }); - const evmChains = chains.filter((chain) => chain.module === "evm").map((chain) => chain.id); - return destinationNativeToken.l1_gas_price_in_units && evmChains.includes(chain); + return destinationNativeToken.l1_gas_price_in_units; } From f0a05e720ed2576e330d44c1c84fe59d3018f745 Mon Sep 17 00:00:00 2001 From: npty Date: Thu, 29 Feb 2024 08:35:42 +0700 Subject: [PATCH 25/33] chore: executeData is 0x if not specified --- src/libs/AxelarQueryAPI.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libs/AxelarQueryAPI.ts b/src/libs/AxelarQueryAPI.ts index 3d6744b7..2d67389a 100644 --- a/src/libs/AxelarQueryAPI.ts +++ b/src/libs/AxelarQueryAPI.ts @@ -291,10 +291,9 @@ export class AxelarQueryAPI { if (isDestinationChainL2) { if (!executeData) { console.warn( - `Since you did not provide executeData, this API will not accurately calculate the + `Since you did not provide executeData, this API will not accurately calculate the total required fee as we will not be able to capture the L1 inclusion fee for this L2 chain.` ); - return [l1ExecutionFee, l1ExecutionFeeWithMultiplier]; } if (!destToken.l1_gas_price_in_units) { @@ -303,7 +302,7 @@ export class AxelarQueryAPI { // Calculate the L1 execution fee. This value is in ETH. l1ExecutionFee = await this.estimateL1GasFee(destChainId, { - executeData: executeData, + executeData: executeData || "0x", l1GasPrice: destToken.l1_gas_price_in_units, }); @@ -334,7 +333,7 @@ export class AxelarQueryAPI { * The default value is "auto", which uses the gas multiplier from the fee response * @param sourceChainTokenSymbol (Optional) the gas token symbol on the source chain. * @param minGasPrice (Optional) A minimum value, in wei, for the gas price on the destination chain that is used to override the estimated gas price if it falls below this specified value. - * @param executeData (Optional) The data to be executed on the destination chain. Required if the destination chain is an L2 chain. + * @param executeData (Optional) The data to be executed on the destination chain. It's recommended to specify it if the destination chain is an L2 chain to calculate more accurate gas fee. * @param gmpParams (Optional) Additional parameters for GMP transactions, including the ability to see a detailed view of the fee response * @returns */ From 1c923967e38756ff22dc05c665880de9b46f83eb Mon Sep 17 00:00:00 2001 From: npty Date: Thu, 29 Feb 2024 08:38:13 +0700 Subject: [PATCH 26/33] chore: fix test --- src/libs/test/AxelarQueryAPI.spec.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/libs/test/AxelarQueryAPI.spec.ts b/src/libs/test/AxelarQueryAPI.spec.ts index 74f5062c..573ff44b 100644 --- a/src/libs/test/AxelarQueryAPI.spec.ts +++ b/src/libs/test/AxelarQueryAPI.spec.ts @@ -143,18 +143,18 @@ describe("AxelarQueryAPI", () => { expect(gasAmount).toBeDefined(); }); - it("should return error when the source chain is L2, but the executeData is undefuned ", async () => { - expect( - api.estimateGasFee( - "ethereum-2", - EvmChain.OPTIMISM, - 700000, - 1.1, - GasToken.USDC, - "500000", - undefined - ) - ).rejects.toThrow("executeData is required to calculate the L1 execution fee for optimism"); + it("should be able to return the gas fee when the source chain is L2, but the executeData is undefined ", async () => { + const response = await api.estimateGasFee( + "ethereum-2", + EvmChain.OPTIMISM, + 700000, + 1.1, + GasToken.USDC, + "500000", + undefined + ); + + expect(response).toBeDefined(); }); test("It should return estimated gas amount that makes sense for native token", async () => { From 78f045cc7ee8552257b3ff22cd8bd27c164579e6 Mon Sep 17 00:00:00 2001 From: npty Date: Thu, 29 Feb 2024 11:07:14 +0700 Subject: [PATCH 27/33] chore: remove error throw for l1 gas price --- src/libs/AxelarQueryAPI.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libs/AxelarQueryAPI.ts b/src/libs/AxelarQueryAPI.ts index 2d67389a..3f9d9c85 100644 --- a/src/libs/AxelarQueryAPI.ts +++ b/src/libs/AxelarQueryAPI.ts @@ -296,10 +296,6 @@ export class AxelarQueryAPI { ); } - if (!destToken.l1_gas_price_in_units) { - throw new Error(`Could not find L1 gas price for ${destChainId}. Please try again later.`); - } - // Calculate the L1 execution fee. This value is in ETH. l1ExecutionFee = await this.estimateL1GasFee(destChainId, { executeData: executeData || "0x", From 72da47ea949c907a72d5dab165c7485a872365bb Mon Sep 17 00:00:00 2001 From: npty Date: Thu, 29 Feb 2024 11:11:22 +0700 Subject: [PATCH 28/33] chore: please linter --- src/libs/AxelarQueryAPI.ts | 6 +----- src/utils/isL2Chain.ts | 9 --------- src/utils/test/isL2Chain.spec.ts | 33 -------------------------------- 3 files changed, 1 insertion(+), 47 deletions(-) delete mode 100644 src/utils/isL2Chain.ts delete mode 100644 src/utils/test/isL2Chain.spec.ts diff --git a/src/libs/AxelarQueryAPI.ts b/src/libs/AxelarQueryAPI.ts index 3f9d9c85..46ad7cf2 100644 --- a/src/libs/AxelarQueryAPI.ts +++ b/src/libs/AxelarQueryAPI.ts @@ -27,7 +27,6 @@ import { ChainInfo } from "src/chains/types"; import { BigNumberUtils } from "./BigNumberUtils"; import { rpcMap as testnetRpcMap } from "./TransactionRecoveryApi/constants/chain/testnet"; import { rpcMap as mainnetRpcMap } from "./TransactionRecoveryApi/constants/chain/mainnet"; -import { isL2Chain } from "../utils/isL2Chain"; import { getL1FeeForL2 } from "./fee/getL1Fee"; interface TranslatedTransferRateLimitResponse { @@ -285,10 +284,7 @@ export class AxelarQueryAPI { let l1ExecutionFee = BigNumber.from(0); let l1ExecutionFeeWithMultiplier = BigNumber.from(0); - // If the destination chain is L2, calculate the L1 execution fee - const isDestinationChainL2 = await isL2Chain(this.environment, destChainId, destToken); - - if (isDestinationChainL2) { + if (destToken.l1_gas_price_in_units) { if (!executeData) { console.warn( `Since you did not provide executeData, this API will not accurately calculate the diff --git a/src/utils/isL2Chain.ts b/src/utils/isL2Chain.ts deleted file mode 100644 index 6d90ec44..00000000 --- a/src/utils/isL2Chain.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Environment, FeeToken } from "../libs"; - -export async function isL2Chain( - environment: Environment, - chain: string, - destinationNativeToken: FeeToken -) { - return destinationNativeToken.l1_gas_price_in_units; -} diff --git a/src/utils/test/isL2Chain.spec.ts b/src/utils/test/isL2Chain.spec.ts deleted file mode 100644 index 2ab0290f..00000000 --- a/src/utils/test/isL2Chain.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Environment, FeeToken } from "../../libs"; -import { isL2Chain } from "../isL2Chain"; - -const mockFeeToken: FeeToken = { - gas_price: "1893353954513", - decimals: 18, - name: "Avalanche", - symbol: "AVAX", - token_price: { - usd: 20.56, - }, - l1_gas_price_in_units: { - decimals: 18, - value: "1", - }, -}; - -describe("isL2Chain", () => { - it("should return true if the chain is an L2 chain", async () => { - const environment = Environment.MAINNET; - const l2Chain = await isL2Chain(environment, "arbitrum", mockFeeToken); - expect(l2Chain).toBeTruthy(); - }); - - it("should return false if dest token.l1_gas_price_in_units is undefined", async () => { - const environment = Environment.MAINNET; - const l2Chain = await isL2Chain(environment, "arbitrum", { - ...mockFeeToken, - l1_gas_price_in_units: undefined, - }); - expect(l2Chain).toBeFalsy(); - }); -}); From 8247d7911baf61e8e84a42f43bf535721aaf2489 Mon Sep 17 00:00:00 2001 From: npty Date: Thu, 29 Feb 2024 18:58:16 +0700 Subject: [PATCH 29/33] chore: remove log --- src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts b/src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts index 985aeda6..ca14a0ca 100644 --- a/src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts +++ b/src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts @@ -189,7 +189,6 @@ export class AxelarGMPRecoveryAPI extends AxelarRecoveryApi { return currentBlock - gmpTx.call.blockNumber; } - console.log(receipt); return receipt.confirmations; }); From 956540f408311b6a83ddc635468f7348ab763a68 Mon Sep 17 00:00:00 2001 From: npty Date: Thu, 29 Feb 2024 19:09:03 +0700 Subject: [PATCH 30/33] feat: use l2_type from getFees api instead of hardcoded chain --- src/libs/AxelarQueryAPI.ts | 12 +++++++++--- src/libs/fee/getL1Fee.spec.ts | 10 ++++++---- src/libs/fee/getL1Fee.ts | 10 +++------- src/libs/test/AxelarQueryAPI.spec.ts | 3 ++- src/libs/types/index.ts | 2 ++ 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/libs/AxelarQueryAPI.ts b/src/libs/AxelarQueryAPI.ts index 46ad7cf2..8717fa81 100644 --- a/src/libs/AxelarQueryAPI.ts +++ b/src/libs/AxelarQueryAPI.ts @@ -230,6 +230,7 @@ export class AxelarQueryAPI { destination_native_token, express_fee_string, express_supported, + l2_type, } = response.result; const execute_gas_multiplier = response.result.execute_gas_multiplier as number; const { decimals: sourceTokenDecimals } = source_token; @@ -251,6 +252,7 @@ export class AxelarQueryAPI { symbol: destination_native_token.symbol, l1_gas_price_in_units: destination_native_token.l1_gas_price_in_units, }, + l2_type, ethereumToken: ethereum_token as BaseFeeResponse["ethereumToken"], apiResponse: response, success: true, @@ -270,7 +272,7 @@ export class AxelarQueryAPI { const provider = new ethers.providers.JsonRpcProvider(rpcMap[destChainId]); - return getL1FeeForL2(provider, destChainId, l1FeeParams); + return getL1FeeForL2(provider, l1FeeParams); } public async calculateL1FeeForDestL2( @@ -279,7 +281,8 @@ export class AxelarQueryAPI { executeData: `0x${string}` | undefined, sourceToken: FeeToken, ethereumToken: BaseFeeResponse["ethereumToken"], - actualGasMultiplier: number + actualGasMultiplier: number, + l2Type: BaseFeeResponse["l2_type"] ): Promise<[BigNumber, BigNumber]> { let l1ExecutionFee = BigNumber.from(0); let l1ExecutionFeeWithMultiplier = BigNumber.from(0); @@ -296,6 +299,7 @@ export class AxelarQueryAPI { l1ExecutionFee = await this.estimateL1GasFee(destChainId, { executeData: executeData || "0x", l1GasPrice: destToken.l1_gas_price_in_units, + l2Type, }); // Convert the L1 execution fee to the source token @@ -353,6 +357,7 @@ export class AxelarQueryAPI { executeGasMultiplier, destToken, apiResponse, + l2_type, success, expressSupported, } = await this.getNativeGasBaseFee( @@ -400,7 +405,8 @@ export class AxelarQueryAPI { executeData, sourceToken, ethereumToken, - actualGasMultiplier + actualGasMultiplier, + l2_type ); return gmpParams?.showDetailedFees diff --git a/src/libs/fee/getL1Fee.spec.ts b/src/libs/fee/getL1Fee.spec.ts index a6605403..42c146ba 100644 --- a/src/libs/fee/getL1Fee.spec.ts +++ b/src/libs/fee/getL1Fee.spec.ts @@ -16,16 +16,17 @@ describe("getL1Fee", () => { const queryAPI = new AxelarQueryAPI({ environment: env }); - const { destToken } = await queryAPI.getNativeGasBaseFee(srcChain, destChain); + const { destToken, l2_type } = await queryAPI.getNativeGasBaseFee(srcChain, destChain); const provider = new ethers.providers.JsonRpcProvider(rpcMap[destChain]); const params: EstimateL1FeeParams = { executeData: MOCK_EXECUTE_DATA, l1GasPrice: destToken.l1_gas_price_in_units!, + l2Type: l2_type, }; - const fee = await getL1FeeForL2(provider, destChain, params); + const fee = await getL1FeeForL2(provider, params); expect(fee).toBeDefined(); }); @@ -36,16 +37,17 @@ describe("getL1Fee", () => { const queryAPI = new AxelarQueryAPI({ environment: env }); - const { destToken } = await queryAPI.getNativeGasBaseFee(srcChain, destChain); + const { destToken, l2_type } = await queryAPI.getNativeGasBaseFee(srcChain, destChain); const provider = new ethers.providers.JsonRpcProvider(rpcMap[destChain]); const params: EstimateL1FeeParams = { executeData: MOCK_EXECUTE_DATA, l1GasPrice: destToken.l1_gas_price_in_units!, + l2Type: l2_type, }; - const fee = await getL1FeeForL2(provider, destChain, params); + const fee = await getL1FeeForL2(provider, params); expect(fee).toBeDefined(); }); diff --git a/src/libs/fee/getL1Fee.ts b/src/libs/fee/getL1Fee.ts index 2e1ede25..f65a05f8 100644 --- a/src/libs/fee/getL1Fee.ts +++ b/src/libs/fee/getL1Fee.ts @@ -22,21 +22,17 @@ const ABI = { */ export function getL1FeeForL2( provider: ethers.providers.JsonRpcProvider, - chain: string, params: EstimateL1FeeParams ): Promise { const multicall = new Multicall({ ethersProvider: provider, tryAggregate: true }); - switch (chain) { + switch (params.l2Type) { case "mantle": return getMantleL1Fee(multicall, params); - case "optimism": - case "scroll": - case "base": + case "op": return getOptimismL1Fee(multicall, params); // Most of the ethereum clients are already included L1 fee in the gas estimation for Arbitrum. - case "arbitrum": - case "arbitrum-sepolia": + case "arb": default: return Promise.resolve(BigNumber.from(0)); } diff --git a/src/libs/test/AxelarQueryAPI.spec.ts b/src/libs/test/AxelarQueryAPI.spec.ts index 573ff44b..43802880 100644 --- a/src/libs/test/AxelarQueryAPI.spec.ts +++ b/src/libs/test/AxelarQueryAPI.spec.ts @@ -107,6 +107,7 @@ describe("AxelarQueryAPI", () => { decimals: 18, value: "32534506865", }, + l2Type: "op", }); expect(gasAmount.gt(parseEther("0.00001"))).toBeTruthy(); }); @@ -128,7 +129,7 @@ describe("AxelarQueryAPI", () => { expect(ethers.utils.parseEther("10000").gt(gasAmount as BigNumberish)).toBeTruthy(); }); - test("It should include L1 fee for L2 destination chain", async () => { + test.only("It should include L1 fee for L2 destination chain", async () => { const mainnetApi = new AxelarQueryAPI({ environment: Environment.MAINNET }); const gasAmount = await mainnetApi.estimateGasFee( EvmChain.ETHEREUM, diff --git a/src/libs/types/index.ts b/src/libs/types/index.ts index b4d07ec0..c0962cae 100644 --- a/src/libs/types/index.ts +++ b/src/libs/types/index.ts @@ -85,6 +85,7 @@ export interface BaseFeeResponse { executeGasMultiplier: number; sourceToken: FeeToken; destToken: FeeToken; + l2_type: "op" | "arb" | "mantle" | undefined; ethereumToken: { name: string; symbol: string; @@ -245,4 +246,5 @@ export type TokenUnit = { export type EstimateL1FeeParams = { executeData: `0x${string}`; l1GasPrice: TokenUnit; + l2Type?: "op" | "arb" | "mantle" | undefined; }; From 184f26a50cfa9055b2d53d216599a28afdbb6bf3 Mon Sep 17 00:00:00 2001 From: npty Date: Thu, 29 Feb 2024 19:10:39 +0700 Subject: [PATCH 31/33] chore: remove only --- src/libs/test/AxelarQueryAPI.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/test/AxelarQueryAPI.spec.ts b/src/libs/test/AxelarQueryAPI.spec.ts index 43802880..21acb3c0 100644 --- a/src/libs/test/AxelarQueryAPI.spec.ts +++ b/src/libs/test/AxelarQueryAPI.spec.ts @@ -129,7 +129,7 @@ describe("AxelarQueryAPI", () => { expect(ethers.utils.parseEther("10000").gt(gasAmount as BigNumberish)).toBeTruthy(); }); - test.only("It should include L1 fee for L2 destination chain", async () => { + test("It should include L1 fee for L2 destination chain", async () => { const mainnetApi = new AxelarQueryAPI({ environment: Environment.MAINNET }); const gasAmount = await mainnetApi.estimateGasFee( EvmChain.ETHEREUM, From 9d320b3693c924d6c988ded9df420116ffb5db77 Mon Sep 17 00:00:00 2001 From: npty Date: Thu, 29 Feb 2024 19:12:53 +0700 Subject: [PATCH 32/33] chore: remove unused imports --- src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts | 2 -- .../test/TransactionRecoveryAPI/AxelarGMPRecoveryAPI.spec.ts | 1 - 2 files changed, 3 deletions(-) diff --git a/src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts b/src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts index ca14a0ca..d32e12d4 100644 --- a/src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts +++ b/src/libs/TransactionRecoveryApi/AxelarGMPRecoveryAPI.ts @@ -11,7 +11,6 @@ import { Environment, } from "../types"; import { EvmChain } from "../../constants/EvmChain"; -import { GasToken } from "../../constants/GasToken"; import { AxelarRecoveryApi, ExecuteParams, @@ -21,7 +20,6 @@ import { import EVMClient from "./client/EVMClient"; import IAxelarExecutable from "../abi/IAxelarExecutable"; import { ContractReceipt, ContractTransaction, ethers } from "ethers"; -import { nativeGasTokenSymbol } from "../../constants"; import { AxelarQueryAPI } from "../AxelarQueryAPI"; import rpcInfo from "./constants/chain"; import { diff --git a/src/libs/test/TransactionRecoveryAPI/AxelarGMPRecoveryAPI.spec.ts b/src/libs/test/TransactionRecoveryAPI/AxelarGMPRecoveryAPI.spec.ts index 3852a332..881f71e1 100644 --- a/src/libs/test/TransactionRecoveryAPI/AxelarGMPRecoveryAPI.spec.ts +++ b/src/libs/test/TransactionRecoveryAPI/AxelarGMPRecoveryAPI.spec.ts @@ -7,7 +7,6 @@ import { EvmWalletDetails, } from "../../types"; import { EvmChain } from "../../../constants/EvmChain"; -import { GasToken } from "../../../constants/GasToken"; import { createNetwork, utils } from "@axelar-network/axelar-local-dev"; import { Contract, ContractReceipt, ContractTransaction, ethers, Wallet } from "ethers"; import DistributionExecutable from "../abi/DistributionExecutable.json"; From f93ff5590bec90d38cdd776a4060caf059873b1e Mon Sep 17 00:00:00 2001 From: npty Date: Wed, 6 Mar 2024 19:54:59 +0700 Subject: [PATCH 33/33] chore: fix md header --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90a69b73..f1c7c690 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -# [0.15.0] - 2024-FEBRUARY-21 +## [0.15.0] - 2024-FEBRUARY-21 - Improved the accuracy of `estimateGasFee` function by incorporating L1 rollup fee for destination L2 chain.