diff --git a/.changeset/fluffy-elephants-shake.md b/.changeset/fluffy-elephants-shake.md new file mode 100644 index 000000000..8731facd6 --- /dev/null +++ b/.changeset/fluffy-elephants-shake.md @@ -0,0 +1,5 @@ +--- +"@axelarjs/api": patch +--- + +Improve l1 fee calculation for estimateGasFee diff --git a/packages/api/src/axelar-query/client.spec.ts b/packages/api/src/axelar-query/client.spec.ts index 05af22914..0cc95c2e8 100644 --- a/packages/api/src/axelar-query/client.spec.ts +++ b/packages/api/src/axelar-query/client.spec.ts @@ -1,6 +1,6 @@ import { ENVIRONMENTS } from "@axelarjs/core"; -import { formatEther, pad } from "viem"; +import { formatEther } from "viem"; import type { AxelarQueryAPIClient, @@ -12,7 +12,7 @@ import { createAxelarQueryClient } from "./client"; describe("axelar-query (node client)", () => { describe("estimateGasFee", () => { const requestParam: EstimateGasFeeParams = { - sourceChain: "ethereum-2", + sourceChain: "ethereum", destinationChain: "avalanche", gasLimit: 700_000n, gasMultiplier: 1.1, @@ -22,14 +22,16 @@ describe("axelar-query (node client)", () => { amountInUnits: "1000000", }; - let api: AxelarQueryAPIClient; + let mainnetApi: AxelarQueryAPIClient; + // let testnetApi: AxelarQueryAPIClient; beforeEach(() => { - api = createAxelarQueryClient(ENVIRONMENTS.testnet, {}); + mainnetApi = createAxelarQueryClient(ENVIRONMENTS.mainnet, {}); + // testnetApi = createAxelarQueryClient(ENVIRONMENTS.testnet, {}); }); test("It should return estimated gas amount in terms of native tokens", async () => { - const fees = await api.estimateGasFee({ + const fees = await mainnetApi.estimateGasFee({ ...requestParam, showDetailedFees: false, }); @@ -40,57 +42,37 @@ describe("axelar-query (node client)", () => { ); }); - test.skip("It should include the L1 gas fee", async () => { + test("It should include the L1 gas fee", async () => { const l2RequestParams: EstimateGasFeeParams = { - sourceChain: "avalanche", + sourceChain: "ethereum", destinationChain: "optimism", gasLimit: 700_000n, gasMultiplier: 1.1, - executeData: pad("0x1234", { size: 5000 }), amount: 1, amountInUnits: "1000000", }; - const fees = (await api.estimateGasFee({ + const fees = (await mainnetApi.estimateGasFee({ ...l2RequestParams, showDetailedFees: true, })) as EstimateGasFeeResponse; - const l1ExecutionFeeWithMultiplier = parseInt( + const l1ExecutionFeeWithMultiplier = BigInt( fees.l1ExecutionFeeWithMultiplier ); - const executionFeeWithMultiplier = parseInt( + const l1ExecutionFee = BigInt(fees.l1ExecutionFee); + const executionFee = BigInt(fees.executionFee); + const executionFeeWithMultiplier = BigInt( fees.executionFeeWithMultiplier ); expect(fees).toBeTruthy(); - expect(l1ExecutionFeeWithMultiplier).toBeGreaterThan( - executionFeeWithMultiplier - ); - }); - - test("it should throw an error if the destination chain is L2 but not specified the executeData", async () => { - const l2RequestParams: EstimateGasFeeParams = { - sourceChain: "ethereum-2", - destinationChain: "optimism", - gasLimit: 700_000n, - gasMultiplier: 1.1, - amount: 1, - amountInUnits: "1000000", - }; - - await expect( - api.estimateGasFee({ - ...l2RequestParams, - showDetailedFees: true, - }) - ).rejects.toThrowError( - "executeData is required to calculate the L1 execution fee for optimism" - ); + expect(l1ExecutionFeeWithMultiplier).toBeGreaterThan(l1ExecutionFee); + expect(executionFeeWithMultiplier).toBeGreaterThan(executionFee); }); test("It should return a detailed object response with the components of the fee", async () => { - const res: EstimateGasFeeResponse = (await api.estimateGasFee({ + const res: EstimateGasFeeResponse = (await mainnetApi.estimateGasFee({ ...requestParam, showDetailedFees: true, })) as EstimateGasFeeResponse; diff --git a/packages/api/src/axelar-query/client.ts b/packages/api/src/axelar-query/client.ts index 9753c04bc..62acb34cc 100644 --- a/packages/api/src/axelar-query/client.ts +++ b/packages/api/src/axelar-query/client.ts @@ -4,6 +4,7 @@ import { type HttpClientOptions, } from "@axelarjs/utils/http-client"; +import { createAxelarscanClient } from "../axelarscan"; import { createGMPClient } from "../gmp/client"; import { AxelarQueryAPIClient } from "./isomorphic"; @@ -17,6 +18,7 @@ export const createAxelarQueryClient = ( }, { gmpClient: createGMPClient(env), + axelarscanClient: createAxelarscanClient(env), }, env ); diff --git a/packages/api/src/axelar-query/constant.ts b/packages/api/src/axelar-query/constant.ts new file mode 100644 index 000000000..4cb218e20 --- /dev/null +++ b/packages/api/src/axelar-query/constant.ts @@ -0,0 +1,3 @@ +// A sample approval execution data on destination chain. Copied it from https://github.com/axelarnetwork/gmp-api/blob/462ebb1176d2f3293ef19fc9122cb8435bfb57b8/config/base_fees.yml#L41C16-L41C22554 +export const DEFAULT_L1_EXECUTE_DATA = + "0x09c5eabe00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000002bc0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000003e00000000000000000000000000000000000000000000000000000000000000380000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000016869f63d12923a6b6a1e7d0845bc36761c958f95fc1253061a90b202b4de1e2500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001b617070726f7665436f6e747261637443616c6c576974684d696e7400000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666d929706526b352b9b3dfac6e02ee8423446d723c431a26a90399a4bfad35ebeb00000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000012874840944511f98378055372e111bfafd7d08f7d89bc050dafe29c866d8f91e640e949000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000007506f6c79676f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a30786365313646363933373535323061623031333737636537423838663542413843343846384436363600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000761786c555344430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027c0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000009c0000000000000000000000000000000000000000000000000000000000001ef210000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000000000004900000000000000000000000000bad53bcfa41dfd97b7bbc1aa63ed1e2b60ca6b0000000000000000000000000632ebed87ca01f3209d43dfda57c936970247960000000000000000000000000874289da1dea55c05c37362e8f6c4e2d363ab7600000000000000000000000008f08f6263e5a0af04449f6d656e7bd3d61f5d0b000000000000000000000000105a4f54dfdb4c14df0a8980656521dbedad34ba00000000000000000000000010eb643e5d2b5f43815c9fbf147c50ca017bf739000000000000000000000000115535334d51526c3dd1fd0b9c102f9ce5c41533000000000000000000000000155d7eb55353a3c341cfdabb44e5f6b800eeb388000000000000000000000000199f1099769ba5ff2a09f2e8af3fe17ebf4f75590000000000000000000000001fab6d2a6c47568e8791837b3cd7b08a6175518e0000000000000000000000002139db217c3da96f151c55aca010f75d2c498ebe000000000000000000000000249fb4a02da54ce3ccc2eeebf7cff9adb41cf65c00000000000000000000000025d01a8c55037ea0151756ed8b83c3dffc296ad200000000000000000000000045fd1d9fe52728dc6218456affd0f7e0f9d32582000000000000000000000000490a7a6beb9a6258573cc2cce3e9a86df63171bd0000000000000000000000004eb37f3950b4ff4075d91db895cf6807e8db2ed90000000000000000000000005116fd98e95a98955a2932e9c9ba292e8184f68e00000000000000000000000057670691f78fe49ab9d5a2e4decd7010f8813764000000000000000000000000577bc5713e570b31e7d33a76d0e402404b3cf5ec00000000000000000000000057d6aab745ebec3cdc51d4dac92b1e2f9d6b978c0000000000000000000000006f8def421036933383e774d4c16f23b5ff758bfe00000000000000000000000070d6d6ce17c5a7139179baff95563d726e6208f7000000000000000000000000736d209b5af966917d8b0d499be1cec43b20bb9d00000000000000000000000075ca2f93ad9d621e2fa684148c69909e28a0bfa40000000000000000000000007b6848712d7dd631688dae4c1315f29d008e7814000000000000000000000000884dd182f2f567791131408b127f21ae8a6c901d0000000000000000000000008b80247550a3cac82f767bb91c7995b4291441bb0000000000000000000000008e90188bc4a9d9578722fb63ab702e2c4653a40d0000000000000000000000008f1a0936a0e4bd648e1dd77802a16b6f182e12bd00000000000000000000000090b460ad14efe194d23c9682dafc43d1bba0466f00000000000000000000000094c34cd9ef967c3a0aae0a2a3038f96c97259b83000000000000000000000000980e78073926665a1fdab7eaf139871c43d0ddc40000000000000000000000009aaaf117d36f36d4ddd062fc8d52fe538b31490e000000000000000000000000a517cb0ba3c949d012b13596fefc2803b3b91f0a000000000000000000000000a68465e2ee9f02ef4c5d0e09b2e157a34f631e0b000000000000000000000000a72d82741f3c246efd8b137d2c927188c822b57a000000000000000000000000a87d29cf4ce22694f9df052ccac41acb362698a4000000000000000000000000acc4a07ce7f15a690712967275625721cc677d24000000000000000000000000ae1938f872c66db538739ca0b4ff68a542a7ecda000000000000000000000000ae4bb7e81762f3c256b5b8bec612a6ddcc6d5cef000000000000000000000000afecc724083d8b2ce1a17dc0a5ddac08ce4dac01000000000000000000000000bc3d8181c1edeae088e25b79bd51409e0dff7bb8000000000000000000000000be1766e184f0c4d9832facc8193718638214c61d000000000000000000000000be979a5d5ad1adcfd9f23390dc7a09c16198b8da000000000000000000000000c0145bc5f5cd19888b739660c842c6a7819b4c2b000000000000000000000000c0245e5c54ef2c227063c5502a216a8b0313d1b0000000000000000000000000c2a4c6759e30a891619f2e2e5af247dac9b7365f000000000000000000000000c2ace3790099a0c674ca37121df97e589a1695f1000000000000000000000000c3ffa6d084b4670b91135939e14453f42cdb1f3e000000000000000000000000c4540a44cbc891eefdaf4b9a86f4dd51f4f5600f000000000000000000000000c5ef9cddfcacdd1ffd20aa5717c18424516299a6000000000000000000000000c88ea9cb0d2573a4043f200a16e4534f8393ed3e000000000000000000000000c95851c3f0c6373507f19d47f38b52ca676ef197000000000000000000000000d1acc237de7518447abf3fce08e52b510008a2f5000000000000000000000000d3f175d6e6a669a7d9acf153b5e124a86d38355c000000000000000000000000d7c82bb1774d2322c2318e1c39ee17e8dd07e96c000000000000000000000000d8b290ce78582ed643c0b388dcf670728066ad4b000000000000000000000000dc6ca923d9583378f99038a790ca0fa9c6e26f8d000000000000000000000000df8094d4f507861f303396ab1205d42522bf04c6000000000000000000000000e0913fc169ba723fa358282181389bd3af3ec697000000000000000000000000e3caaa4c45a36a4bd3774cc5781938522fb1feb6000000000000000000000000e8692f729f779c5f64c4010c554e9e98d4817f32000000000000000000000000e9c432933b562471aef1d46eb05d5a3e5d3ee57c000000000000000000000000ea475fceed7ae7a89a6aba573251d935bc17928f000000000000000000000000ea98fc28c13828d6b37b17eaf85ffb655f205398000000000000000000000000ef572e9b96e4302d21450be4fc01b042568f3c7c000000000000000000000000f27700773b00f3b24d31a2e1aa8557292f349b00000000000000000000000000f773a12d7db42d4c216e6f5bae3c7b3e6cc2fb99000000000000000000000000f7e53952f76493f8951671e1d735ff0532688d70000000000000000000000000f94135a221859cee2ea4175659b6eaff335de65b000000000000000000000000fc1e02f1446f4ae5b99d9c05ee1ec4901779339f000000000000000000000000fc6a8d595f05a7025cb1c97fa45bbd7d755497dc000000000000000000000000ff8f73d5b9f3f75fde11e3c6eb23c01c89681b1500000000000000000000000000000000000000000000000000000000000000490000000000000000000000000000000000000000000000000000000000000eee000000000000000000000000000000000000000000000000000000000000076b00000000000000000000000000000000000000000000000000000000000007fe0000000000000000000000000000000000000000000000000000000000000bc90000000000000000000000000000000000000000000000000000000000000e6e00000000000000000000000000000000000000000000000000000000000005300000000000000000000000000000000000000000000000000000000000000d8a000000000000000000000000000000000000000000000000000000000000053d0000000000000000000000000000000000000000000000000000000000000d1200000000000000000000000000000000000000000000000000000000000008c50000000000000000000000000000000000000000000000000000000000000a25000000000000000000000000000000000000000000000000000000000000061f000000000000000000000000000000000000000000000000000000000000104500000000000000000000000000000000000000000000000000000000000006ec0000000000000000000000000000000000000000000000000000000000000e62000000000000000000000000000000000000000000000000000000000000075400000000000000000000000000000000000000000000000000000000000005ae0000000000000000000000000000000000000000000000000000000000000b910000000000000000000000000000000000000000000000000000000000000e2c00000000000000000000000000000000000000000000000000000000000008a000000000000000000000000000000000000000000000000000000000000007560000000000000000000000000000000000000000000000000000000000000a830000000000000000000000000000000000000000000000000000000000001ae00000000000000000000000000000000000000000000000000000000000000b470000000000000000000000000000000000000000000000000000000000000f380000000000000000000000000000000000000000000000000000000000000ddf00000000000000000000000000000000000000000000000000000000000006cb0000000000000000000000000000000000000000000000000000000000000a870000000000000000000000000000000000000000000000000000000000000ae90000000000000000000000000000000000000000000000000000000000000b3a00000000000000000000000000000000000000000000000000000000000008f90000000000000000000000000000000000000000000000000000000000000c5a00000000000000000000000000000000000000000000000000000000000017290000000000000000000000000000000000000000000000000000000000000b400000000000000000000000000000000000000000000000000000000000000fec0000000000000000000000000000000000000000000000000000000000000eb1000000000000000000000000000000000000000000000000000000000000008d0000000000000000000000000000000000000000000000000000000000000eaa00000000000000000000000000000000000000000000000000000000000009340000000000000000000000000000000000000000000000000000000000000a3c00000000000000000000000000000000000000000000000000000000000007d20000000000000000000000000000000000000000000000000000000000000d6d0000000000000000000000000000000000000000000000000000000000000d2a000000000000000000000000000000000000000000000000000000000000009e000000000000000000000000000000000000000000000000000000000000074f0000000000000000000000000000000000000000000000000000000000000eec000000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000c8b0000000000000000000000000000000000000000000000000000000000000d800000000000000000000000000000000000000000000000000000000000000e6c0000000000000000000000000000000000000000000000000000000000000cf30000000000000000000000000000000000000000000000000000000000001082000000000000000000000000000000000000000000000000000000000000070700000000000000000000000000000000000000000000000000000000000007750000000000000000000000000000000000000000000000000000000000000fc60000000000000000000000000000000000000000000000000000000000000cc800000000000000000000000000000000000000000000000000000000000007e80000000000000000000000000000000000000000000000000000000000000ccb00000000000000000000000000000000000000000000000000000000000008490000000000000000000000000000000000000000000000000000000000000cca0000000000000000000000000000000000000000000000000000000000000b490000000000000000000000000000000000000000000000000000000000000dcd00000000000000000000000000000000000000000000000000000000000006180000000000000000000000000000000000000000000000000000000000000d6500000000000000000000000000000000000000000000000000000000000012f50000000000000000000000000000000000000000000000000000000000000a830000000000000000000000000000000000000000000000000000000000000b58000000000000000000000000000000000000000000000000000000000000061c0000000000000000000000000000000000000000000000000000000000000cbf000000000000000000000000000000000000000000000000000000000000149b000000000000000000000000000000000000000000000000000000000000071a00000000000000000000000000000000000000000000000000000000000010d500000000000000000000000000000000000000000000000000000000000007590000000000000000000000000000000000000000000000000000000000000021000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000004a0000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000062000000000000000000000000000000000000000000000000000000000000006a0000000000000000000000000000000000000000000000000000000000000072000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000082000000000000000000000000000000000000000000000000000000000000008a0000000000000000000000000000000000000000000000000000000000000092000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000000000a200000000000000000000000000000000000000000000000000000000000000aa00000000000000000000000000000000000000000000000000000000000000b200000000000000000000000000000000000000000000000000000000000000ba00000000000000000000000000000000000000000000000000000000000000c200000000000000000000000000000000000000000000000000000000000000ca00000000000000000000000000000000000000000000000000000000000000d200000000000000000000000000000000000000000000000000000000000000da00000000000000000000000000000000000000000000000000000000000000e200000000000000000000000000000000000000000000000000000000000000ea00000000000000000000000000000000000000000000000000000000000000f200000000000000000000000000000000000000000000000000000000000000fa0000000000000000000000000000000000000000000000000000000000000102000000000000000000000000000000000000000000000000000000000000010a0000000000000000000000000000000000000000000000000000000000000112000000000000000000000000000000000000000000000000000000000000011a0000000000000000000000000000000000000000000000000000000000000122000000000000000000000000000000000000000000000000000000000000012a0000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000013a000000000000000000000000000000000000000000000000000000000000014200000000000000000000000000000000000000000000000000000000000000041d90ab8f6f676297769e5ee3741081e139e756b8063857f915f3c18aa55492b0377c850301f44b44ae6a51b24315eb83a3d0967ac92adca3c457337aa4bfcb2e01b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041d40296c343f1031bcc9998a86b6c5cb66297d657ec03c810186102d8c6b2f3a23ba952ff9359c4f221f159ce8df08c5131e27ced0ad065dfe9f7164e567348461c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f97f77d4478080d7f8a05c575017e135e77480321369ffaa886c8cf5151fa2a17622f2584ae9ced15f96c2cebf3df5626640d6e96c9324aecb8af619ad3b7b541c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041388107e16868f12a21d50e2636b53a69475cd30e2c33b7640c66ca0018096c2d4ef5db8134e2dc52481d655dd8e581acc2d0fb8f7be83c6df42d6c5cd1b58ba31c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004114c457f899d174dbe91b6cfe8b95dba26c2a4c936ed55b70de29f94902fa7d2936cb2bf999409a5bded8f56aa765e9aa7818f4872fd99c37a34a238bc3873f671c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041117fb0e77bb1796ec1c118c7cff16ca6ae86fe57e184be7e260c07ca9cb65f0711d8d6b06d8da2c6f214ef06e75fac49e83bd445c1f30d1add3681640fb0b8031b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041ffb29e3b044a9a8fa7c3fee4c7f68bf5bc9ee8defa8db9f97d94de40fbdbde2357eccb3abe2af80382450e7b68774d3743cd166d2a4e0fd637d582e93e19a35a1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041326be6706a7c1b5751ee46ced1889832203279021a14962cd279749e7547b77f591552dd62f251b921cb8223b24b00c003faa5aa832c64d3b9e79245661227c51b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041b2b6002f94c413aa8578af51f852516a270d248920d76ec5a6c4f837551796815c6878e787c9d350af951ff82348e6452adc5e6dc1933e592a16c154bd5acc681b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414e45aba2560943bdfd56edf9df33a45bcf00ab702785b0569dbb41a1b267d2db21baf6a1fbd88dd8486a3ba5d9e12236af44fe943115661664262250c5c017c51b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f8b76e83d766fd95c1614c8631043b66689e54cb54e3a51dfcdce28396d3fccb0909fdc3434db2fc2f243991f85fce499af313854642ca581767c976802c3a831b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004127820cd6b978540aa3344a419c7bf2e2a4f22ed632f1bf90da81d3c278677c961c9b27cb3860655d7b102c36cb1523c020094e55309076d0038351eb25f1f33e1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041c77d02fe8848c9655ba162dee11caa4db78bc7258ca3c38d625fe32de92590ae0c8cc49be7c65290e49659c6668967842895a2115ece167822c1062b496983031b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f67348761e1fb729024ecc6bb1ee1cedd94baa1be508b09617f0c4a1cd9a784a2964fbdeb7e4f49759c7f38e17dab1c3b1dc28255b309d3cb875494d393feb7b1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041af9215d79b43982e763e1513bf202265415fca5e7bc964e88873239c5e5bf99467ee716fb6b26edf1d1ef0d8ec7de6f7b48d03b0169cdbe874430d2d3db9c5ae1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414e23c63ef6953a0353bc7ff6146f8f9ba8a61808fbb483dc5b6fd2abb324eea7024e152ee45e3f9f8b89ab68846170525b1815662faeeecee0efa9cc5c8cdf631c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041236b4b802c2ec5eb387f93d648f05ef37357d7bcbd0a81fb5f0f7ec34fae7db25ef00b5cbd88fa8c899be149df0f1e3f455916125ab3fefbe3692b0ee1ebee521b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004191b5a0cee4d6c1ac37b0f3b67a01338eeb584733ab9eaff25d9356a8d23b57b70bf77f919eb57013a28a0186c581e5d508e97f6ab7d6ef62106246f61f00e5c81b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000410342c70ad29d199923730ba99c533ef282414d4e0d4bc0ddb791335aff09e8ad110d8259ed7b2331f948e249aa5dfc3a9699d90bce280d8ce6829fb296e42e001b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004120738466897dac80150f30578868a2ad6b21cb06a0a0e9dde28d91b64adbf55060184accaae153116004e737153abe8850b48006d9942378eafcc7c9d623bc411c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000415a6a6fa17851789c70b29dd58b84b0b0bf3a7b0c421387af8b0895cfc064b1307a3516f497578115a19fbb9619dd3cbb1299700cd0fbed757ab3463bc7a2e4111c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000415c6910606af5911ba68d5581b5c6c0de3bdefa24cafd7abc898366b0f512aa662509ad62396bf0a7b22980db89a2fbb31da2c1e8811d7936a3a24301cf2aa9951b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004131760d8ada5433d2111f38ead2507d0ae48d62f417540d58b779105aac8b52162311c081604773763ea11cad5995f4b7659da989524d4396dba6ff9a65d1cc131b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041a65f9d59a84b798a9a4cbb6b321573bb7614d59e07ae562fd32dcc1005d823ae7c9d6e22b524e3bdd681fd7a028b8136ed3d02113ba90ec8734dc5036a5b4f931c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000410c974a6bbc3e767e39832140a661c45791fdff841a2e12b1324011a6e7104c3574be95898b98e3d29f943078f701f0916a243142bb7fb7215933544c97cb6f121b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041eef19484116fd4d50e78b3b9ffc0bbed42fa28da05ff2c989dc2d495a7f11a7a2c8eb7244ad13f2ab1182d8d68912260a35ed0e40ee41f3b4a45489efca358141c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f562d048abdf167f080c3bfed53687ce4537b6f17cc7267eb26aadf1410381455a31f6bc516bde6b2399150615561c3d0349fd1443b710f650de6dbc71d92b151b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041829f74017e53d23a93d0d894a96dad4aed7379d7f726d69b515c182bc157152f2638e7fa46a1ecf588d4450c5e303a05ea7ed960e8fb4f28aeeb7a62e1acfbc31c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000417a08ffef9b49f3addd3a54ec293da9a0dc696536be2c56ea764d27cd1423981f0354501d54e8f5f7f8783ad7910dae4c281345c73c584f8171ad9ecd1e6eb4dc1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000411931fa1f971cde70b4d26b6abe6994d8cd60167cd3fd456f8fc8ac31809f761e3b4197dcd91a5de7233e20b799ac243d764aaea430d5597e49973bbb76784cf31c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000412afa4a6a5e29aabc4cf744fe7bc26370ec8a7554ae33af7514782509d7bed8903262769e6e031f0326e1ad869a8c32656fc6cbf71f82d4f0657ab864f89379641b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041ae2a9430bc790a7e95e4b69cfc8d3af1dd52688fb9aac974e81435728340e2a07ef5ba06b3b980b045525ea8150c673bf7fe9f007cc6fad7e061ca48bfea0ebd1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004180881a76bffea4ea0d28fa9ea9193455e2ce962a72ab4e724450938190f1420d1781e47360816dd991f8b55dc477b7db358a8f92a3389a1f2d3cc06be3addbb51c00000000000000000000000000000000000000000000000000000000000000"; diff --git a/packages/api/src/axelar-query/fee/getL1Fee.spec.ts b/packages/api/src/axelar-query/fee/getL1Fee.spec.ts index 5075b2179..ffb69007a 100644 --- a/packages/api/src/axelar-query/fee/getL1Fee.spec.ts +++ b/packages/api/src/axelar-query/fee/getL1Fee.spec.ts @@ -1,13 +1,21 @@ -import { createGMPClient, GMPClient } from "../.."; +import { + createAxelarscanClient, + createGMPClient, + GMPClient, + type EVMChainConfig, +} from "../.."; import { getL1FeeForL2 } from "./getL1Fee"; import { EstimateL1FeeParams } from "./types"; describe("getL1Fee", () => { const env = "mainnet"; let client: GMPClient; + let configs: { evm: EVMChainConfig[] }; - beforeAll(() => { + beforeAll(async () => { client = createGMPClient(env); + const axelarscanClient = createAxelarscanClient(env); + configs = await axelarscanClient.getChainConfigs(); }); it("query arbitrum l1 fee should work", async () => { @@ -17,31 +25,38 @@ describe("getL1Fee", () => { }); const params: EstimateL1FeeParams = { - destinationContractAddress: "0xce16F69375520ab01377ce7B88f5BA8C48F8D666", executeData: "0x1a98b2e0e68ba0eb84262d4bcf91955ec2680b37bcedd59a1f48e18d183dac9961bf9d1400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000d40000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000000000762696e616e636500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863653136463639333735353230616230313337376365374238386635424138433438463844363636000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000000a8000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000deac2c6000000000000000000000000000000000000000000000000000000000dc647500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f40521500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab100000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3000000000000000000000000000000000000000000000000000000000de83dbf000000000000000000000000000000000000000000000000015d8c7908dbe7130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004607cad6135d7a119185ebe062d3b369b1b536ef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000761786c5553444300000000000000000000000000000000000000000000000000", + l1GasOracleAddress: fees.destination_native_token.l1_gas_oracle_address!, l1GasPrice: fees.destination_native_token.l1_gas_price_in_units!, }; - const fee = await getL1FeeForL2("mainnet", "arbitrum", params); + const rpcUrl = configs.evm.find((chain) => chain.id === "arbitrum") + ?.endpoints.rpc?.[0]; + + const fee = await getL1FeeForL2(rpcUrl!, params); expect(fee).toBeDefined(); }); it("query optimism l1 fee should work", async () => { + const destChain = "optimism"; + const config = configs.evm.find((c) => c.id === destChain); + const rpcUrl = config?.endpoints.rpc[0]; const fees = await client.getFees({ sourceChain: "avalanche", - destinationChain: "optimism", + destinationChain: destChain, }); // reference: https://optimistic.etherscan.io/tx/0x5203e3629019b8bae494ec5e6b83ddfaf660ae6d5c88ba79093f5f5429143c00 const params: EstimateL1FeeParams = { - destinationContractAddress: "0xce16F69375520ab01377ce7B88f5BA8C48F8D666", executeData: "0x1a98b2e04ee0e180bcfb26420ebb9e7a86f1fdf8c627d79ade87880bf74b7dd73c1b749b00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000006e00000000000000000000000000000000000000000000000000000000258380180000000000000000000000000000000000000000000000000000000000000000646616e746f6d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a3078636531364636393337353532306162303133373763653742383866354241384334384638443636360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005600000000000000000000000000000000000000000000000000000000000000040000000000000000000000000c9f7113042615ce4796f8cbbfa6f42170d908e0500000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000007f5c764cbc14f9669b88837ca1490cca17c316070000000000000000000000000000000000000000000000000000000000000064000000000000000000000000c9f7113042615ce4796f8cbbfa6f42170d908e0500000000000000000000000000000000000000000000000000000002583801800000000000000000000000000000000000000000000000000000000254660d5a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f4052150000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000761786c5553444300000000000000000000000000000000000000000000000000", l1GasPrice: fees.destination_native_token.l1_gas_price_in_units!, + l1GasOracleAddress: fees.destination_native_token.l1_gas_oracle_address!, + l2Type: "op", }; - const fee = await getL1FeeForL2("mainnet", "optimism", params); + const fee = await getL1FeeForL2(rpcUrl!, params); expect(fee).toBeGreaterThan(0n); }); @@ -52,14 +67,16 @@ describe("getL1Fee", () => { }); const params: EstimateL1FeeParams = { - destinationContractAddress: "0xce16F69375520ab01377ce7B88f5BA8C48F8D666", executeData: "0x1a98b2e04ee0e180bcfb26420ebb9e7a86f1fdf8c627d79ade87880bf74b7dd73", l1GasPrice: fees.destination_native_token.l1_gas_price_in_units!, + l2Type: "mantle", + l1GasOracleAddress: fees.destination_native_token.l1_gas_oracle_address!, }; + const rpcUrl = configs.evm.find((chain) => chain.id === "mantle")?.endpoints + ?.rpc[0]; - const fee = await getL1FeeForL2("mainnet", "mantle", params); - expect(fee).toBeDefined(); - expect(fee).toBeGreaterThan(0n); + const fee = await getL1FeeForL2(rpcUrl!, params); + expect(fee).toBe(0n); }); }); diff --git a/packages/api/src/axelar-query/fee/getL1Fee.ts b/packages/api/src/axelar-query/fee/getL1Fee.ts index 2c99c4382..026bd0c17 100644 --- a/packages/api/src/axelar-query/fee/getL1Fee.ts +++ b/packages/api/src/axelar-query/fee/getL1Fee.ts @@ -1,21 +1,14 @@ /* eslint-disable @typescript-eslint/no-unsafe-return */ -import { Environment } from "@axelarjs/core"; import { createPublicClient, http, parseAbi, - type Chain, type HttpTransport, type PublicClient, } from "viem"; -import { - MAINNET_L2_CHAINS, - TESTNET_L2_CHAINS, - type EstimateL1FeeParams, - type L2Chain, -} from "./types"; +import { type EstimateL1FeeParams } from "./types"; /** * Get the estimated L1 fee for a given L2 chain. @@ -24,140 +17,40 @@ import { * @param params The parameters to use for the estimation. * @returns */ -export function getL1FeeForL2( - env: Environment, - chain: L2Chain, +export async function getL1FeeForL2( + rpcUrl: string, params: EstimateL1FeeParams ): Promise { - const chains = env === "mainnet" ? MAINNET_L2_CHAINS : TESTNET_L2_CHAINS; - const publicClient = createPublicClient({ - chain: chains[chain], - transport: http(), + transport: http(rpcUrl), }); - switch (chain) { - // Most of the ethereum clients are already included L1 fee in the gas estimation for Arbitrum. - case "arbitrum": - return Promise.resolve(0n); - case "optimism": - case "scroll": - case "base": + switch (params.l2Type) { + case "op": return getOptimismL1Fee(publicClient, params); case "mantle": - return getMantleL1Fee(publicClient, params); + case "arb": + default: + return 0n; } } -async function getOptimismL1Fee( - publicClient: PublicClient, +async function getOptimismL1Fee( + publicClient: PublicClient, estimateL1FeeParams: EstimateL1FeeParams ) { - const { l1GasPrice, executeData } = estimateL1FeeParams; + const { executeData, l1GasOracleAddress } = estimateL1FeeParams; - const contractAddress = "0x420000000000000000000000000000000000000F"; - const abi = parseAbi([ - "function getL1GasUsed(bytes) returns (uint256)", - "function scalar() returns (uint256)", - "function overhead() returns (uint256)", - ]); + const contractAddress = + l1GasOracleAddress || "0x420000000000000000000000000000000000000F"; + const abi = parseAbi(["function getL1Fee(bytes) returns (uint256)"]); - const multicallResponse = await publicClient.multicall({ - contracts: [ - { - address: contractAddress, - abi, - functionName: "getL1GasUsed" as never, - args: [executeData], - }, - { - address: contractAddress, - abi, - functionName: "scalar" as never, - args: [], - }, - { - address: contractAddress, - abi, - functionName: "overhead" as never, - args: [], - }, - ], + const fee = await publicClient.readContract({ + address: contractAddress, + abi, + functionName: "getL1Fee", + args: [executeData], }); - const [gasUsed, _dynamicOverhead, _fixedOverhead] = multicallResponse.flatMap( - (r) => r.result ?? 0n - ) as [bigint, bigint, bigint]; - - const dynamicOverhead = _dynamicOverhead || 684000n; - const fixedOverhead = _fixedOverhead || 2100n; - - const totalGasUsed = - ((gasUsed + fixedOverhead) * dynamicOverhead) / 1_000_000n; - const gasPrice = BigInt(l1GasPrice.value); - - return totalGasUsed * 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( - publicClient: PublicClient, - estimateL1FeeParams: EstimateL1FeeParams -) { - const contractAddress = "0x420000000000000000000000000000000000000F"; - const { l1GasPrice } = estimateL1FeeParams; - - const abi = parseAbi([ - "function overhead() returns (uint256)", - "function scalar() returns (uint256)", - ]); - - const multicallResponse = await publicClient.multicall({ - contracts: [ - { - address: contractAddress, - abi, - functionName: "overhead" as never, - args: [], - }, - { - address: contractAddress, - abi, - functionName: "scalar" as never, - args: [], - }, - ], - }); - - const [fixedOverhead, dynamicOverhead] = multicallResponse.flatMap( - (r) => r.result ?? 0n - ) as [bigint, bigint]; - - const totalGasUsed = (fixedOverhead * dynamicOverhead) / 1_000_000n; - const gasPrice = BigInt(l1GasPrice.value); - - return totalGasUsed * gasPrice; + return fee; } diff --git a/packages/api/src/axelar-query/fee/index.ts b/packages/api/src/axelar-query/fee/index.ts index afe63e244..f920367f9 100644 --- a/packages/api/src/axelar-query/fee/index.ts +++ b/packages/api/src/axelar-query/fee/index.ts @@ -1,3 +1,2 @@ export * from "./getL1Fee"; -export * from "./utils"; export * from "./types"; diff --git a/packages/api/src/axelar-query/fee/types.ts b/packages/api/src/axelar-query/fee/types.ts index abff52c36..a99a963c6 100644 --- a/packages/api/src/axelar-query/fee/types.ts +++ b/packages/api/src/axelar-query/fee/types.ts @@ -1,39 +1,8 @@ -import type { Chain } from "viem"; -import { - arbitrum, - arbitrumSepolia, - base, - baseSepolia, - mantle, - mantleTestnet, - optimism, - optimismSepolia, - scroll, - scrollSepolia, -} from "viem/chains"; - -import { TokenUnit } from "../../gmp"; - -export type L2Chain = "optimism" | "arbitrum" | "mantle" | "base" | "scroll"; - -export const MAINNET_L2_CHAINS = { - arbitrum, - base, - optimism, - scroll, - mantle, -} as Record; - -export const TESTNET_L2_CHAINS = { - arbitrum: arbitrumSepolia, - base: baseSepolia, - optimism: optimismSepolia, - scroll: scrollSepolia, - mantle: mantleTestnet, -} as Record; +import { TokenUnit, type L2Type } from "../../gmp"; export type EstimateL1FeeParams = { - destinationContractAddress?: `0x${string}` | undefined; executeData: `0x${string}`; l1GasPrice: TokenUnit; + l1GasOracleAddress?: `0x${string}` | undefined; + l2Type?: L2Type; }; diff --git a/packages/api/src/axelar-query/fee/utils.ts b/packages/api/src/axelar-query/fee/utils.ts deleted file mode 100644 index ff4cd97ad..000000000 --- a/packages/api/src/axelar-query/fee/utils.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { L2Chain } from "./types"; - -export function isL2Chain(value: string): value is L2Chain { - const validChains: L2Chain[] = [ - "optimism", - "arbitrum", - "mantle", - "base", - "scroll", - ]; - return validChains.includes(value as L2Chain); -} diff --git a/packages/api/src/axelar-query/isomorphic.ts b/packages/api/src/axelar-query/isomorphic.ts index f0637efac..e8a09ce25 100644 --- a/packages/api/src/axelar-query/isomorphic.ts +++ b/packages/api/src/axelar-query/isomorphic.ts @@ -2,22 +2,27 @@ import { Environment } from "@axelarjs/core"; import { parseUnits } from "viem"; +import type { AxelarscanClient } from "../axelarscan"; +import type { GetBaseFeesResult } from "../gmp"; import type { GMPClient } from "../gmp/isomorphic"; import { RestService, type ClientMeta, type RestServiceOptions, } from "../lib/rest-service"; -import { getL1FeeForL2, isL2Chain } from "./fee"; +import { DEFAULT_L1_EXECUTE_DATA } from "./constant"; +import { getL1FeeForL2 } from "./fee"; import type { EstimateGasFeeParams, EstimateGasFeeResponse } from "./types"; -import { gasToWei } from "./utils/bigint"; +import { gasToWei, multiplyFloatByBigInt } from "./utils/bigint"; type AxelarscanClientDependencies = { gmpClient: GMPClient; + axelarscanClient: AxelarscanClient; }; export class AxelarQueryAPIClient extends RestService { protected gmpClient: GMPClient; + protected axelarScanClient: AxelarscanClient; protected env: Environment; public constructor( @@ -28,6 +33,7 @@ export class AxelarQueryAPIClient extends RestService { ) { super(options, meta); this.gmpClient = dependencies.gmpClient; + this.axelarScanClient = dependencies.axelarscanClient; this.env = env; } @@ -42,20 +48,79 @@ export class AxelarQueryAPIClient extends RestService { }); } + private async getRpcUrl(chain: string) { + const configs = await this.axelarScanClient.getChainConfigs(); + return configs.evm.find((c) => c.id === chain)?.endpoints?.rpc?.[0]; + } + + private async _getL1FeeForL2( + executeData: `0x${string}` | undefined, + destinationChain: string, + actualGasMultiplier: number, + feeResponse: GetBaseFeesResult + ) { + const { destination_native_token, source_token, ethereum_token } = + feeResponse; + + if (!destination_native_token.l1_gas_price_in_units) { + return { + l1ExecutionFee: 0n, + l1ExecutionFeeWithMultiplier: 0n, + }; + } + + const actualExecuteData = executeData || DEFAULT_L1_EXECUTE_DATA; + const rpcUrl = await this.getRpcUrl(destinationChain); + + if (!rpcUrl) { + throw new Error("Failed to retrieve RPC URL for the destination chain."); + } + + // Calculate the L1 execution fee. This value is in ETH. + const ethL1ExecutionFee = await getL1FeeForL2(rpcUrl, { + executeData: actualExecuteData, + l1GasPrice: destination_native_token.l1_gas_price_in_units, + l2Type: feeResponse.l2_type, + l1GasOracleAddress: + feeResponse.destination_native_token.l1_gas_oracle_address, + }); + + // Convert the L1 execution fee to the source token + const srcTokenPrice = Number(source_token.token_price.usd); + const ethTokenPrice = Number(ethereum_token.token_price.usd); + const ethToSrcTokenPriceRatio = ethTokenPrice / srcTokenPrice; + + // Calculate the L1 execution fee in the source token + const l1ExecutionFee = multiplyFloatByBigInt( + ethToSrcTokenPriceRatio, + ethL1ExecutionFee + ); + + // Calculate the L1 execution fee with the gas multiplier + const l1ExecutionFeeWithMultiplier = multiplyFloatByBigInt( + actualGasMultiplier, + l1ExecutionFee + ); + + return { + l1ExecutionFee, + l1ExecutionFeeWithMultiplier, + }; + } + /** * Calculate estimated gas amount to pay for the gas receiver contract. * @param sourceChain Chain ID (as recognized by Axelar) of the source chain * @param destinationChain Chain ID (as recognized by Axelar) of the destination chain + * @param gasLimit An estimated gas amount required to execute `executeWithToken` function. * @param sourceTokenSymbol (Optional) the token symbol on the source chain * @param sourceContractAddress (Optional) the address of the contract invoking the GMP call from the source chain * @param sourceTokenAddress (Optional) the contract address of the token symbol on the source chain - * @param executeData (Optional) the transaction data to be executed on the destination chain. Required if the destination chain is L2. - * @param destinationContractAddress (Optional) the address of the contract invoking the GMP call from the source chain - * @param amount (Optional) the amount of assets transferred in terms of symbol, not unit denom, e.g. use 1 for 1 axlUSDC, not 1000000 - * @param amountInUnits (Optional) the amount of assets transferred in terms of unit denom, not symbol, e.g. use 1000000 for 1 axlUSDC, not 1 + * @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 destinationContractAddress (Optional) the destination contract address for checking express supported + * @param amountInUnits (Optional) An amount (in the smallest denomination) that using in callContractWithToken for checking express supported * @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 gasLimit (Optional) An estimated gas amount required to execute `executeWithToken` function. The default value is 1MM which should be sufficient for most transactions. - * @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 + * @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. Default to "auto" which will use the gas multiplier from the GMP response. * @param showDetailedFees (Optional) will return the full breakdown of fee components if specified true * @returns */ @@ -67,14 +132,13 @@ export class AxelarQueryAPIClient extends RestService { sourceContractAddress, sourceTokenAddress, destinationContractAddress, - amount, amountInUnits, executeData, minGasPrice = "0", gasMultiplier = "auto", showDetailedFees = false, }: EstimateGasFeeParams): Promise { - if (gasLimit < 21000) { + if (gasLimit < 1) { throw new Error("Gas limit is too low."); } @@ -85,7 +149,6 @@ export class AxelarQueryAPIClient extends RestService { sourceContractAddress, sourceTokenAddress, destinationContractAddress, - amount, amountInUnits, }); @@ -95,7 +158,6 @@ export class AxelarQueryAPIClient extends RestService { source_token, destination_native_token, execute_gas_multiplier, - ethereum_token, express_supported, } = response; @@ -130,52 +192,19 @@ export class AxelarQueryAPIClient extends RestService { const excludedL1ExecutionFeeWithMultiplier = actualGasMultiplier > 1 - ? Math.floor( - Number(excludedL1ExecutionFee) * Number(actualGasMultiplier) - ) + ? multiplyFloatByBigInt(actualGasMultiplier, excludedL1ExecutionFee) : excludedL1ExecutionFee; const baseFee = parseUnits(base_fee.toString(), source_token.decimals); - let l1ExecutionFee = 0n; - let l1ExecutionFeeWithMultiplier = 0; - - // If the destination chain is L2, calculate the L1 execution fee - if (isL2Chain(destinationChain)) { - if (!executeData) { - throw new Error( - `executeData is required to calculate the L1 execution fee for ${destinationChain}` - ); - } - - if (!destination_native_token.l1_gas_price_in_units) { - throw new Error( - `Could not find L1 gas price for ${destinationChain}. Please try again later.` - ); - } - - // Calculate the L1 execution fee. This value is in ETH. - l1ExecutionFee = await getL1FeeForL2(this.env, destinationChain, { - destinationContractAddress, + const { l1ExecutionFee, l1ExecutionFeeWithMultiplier } = + await this._getL1FeeForL2( executeData, - l1GasPrice: destination_native_token.l1_gas_price_in_units, - }); - - // Convert the L1 execution fee to the source token - const srcTokenPrice = Number(source_token.token_price.usd); - const ethTokenPrice = Number(ethereum_token.token_price.usd); - const ethToSrcTokenPriceRatio = ethTokenPrice / srcTokenPrice; - - l1ExecutionFee = BigInt( - Math.ceil(Number(l1ExecutionFee) * ethToSrcTokenPriceRatio) + destinationChain, + actualGasMultiplier, + response ); - // Calculate the L1 execution fee with the gas multiplier - l1ExecutionFeeWithMultiplier = Math.floor( - Number(l1ExecutionFee) * Number(actualGasMultiplier) - ); - } - // If showDetailedFees is false, return the total fee amount if (!showDetailedFees) { return ( diff --git a/packages/api/src/axelar-query/types.ts b/packages/api/src/axelar-query/types.ts index 394c11fd0..180e45ff2 100644 --- a/packages/api/src/axelar-query/types.ts +++ b/packages/api/src/axelar-query/types.ts @@ -2,7 +2,7 @@ import type { GetFeesParams } from ".."; export type EstimateGasFeeParams = GetFeesParams & { gasLimit: bigint; - gasMultiplier: number | "auto"; + gasMultiplier?: number | "auto"; minGasPrice?: string; showDetailedFees?: boolean; }; diff --git a/packages/api/src/axelar-query/utils/bigint.spec.ts b/packages/api/src/axelar-query/utils/bigint.spec.ts index cac27c2da..263d96271 100644 --- a/packages/api/src/axelar-query/utils/bigint.spec.ts +++ b/packages/api/src/axelar-query/utils/bigint.spec.ts @@ -1,8 +1,32 @@ -import { gasToWei } from "./bigint"; +import { gasToWei, multiplyFloatByBigInt } from "./bigint"; describe("bigint", () => { test("should work even gas_price has more fractional digits than specified decimals", () => { const wei = gasToWei(1000000n, "0.00000001", 6); expect(wei).toBe(10000n); }); + + it("should handle multiplication with whole numbers without loss of precision", () => { + const floatNum = 2.0; + const bigIntNum = 12345678901234567890n; + + const result = multiplyFloatByBigInt(floatNum, bigIntNum); + expect(result).toEqual(24691357802469135780n); + }); + + it("should handle multiplication with decimals without loss of precision", () => { + const floatNum = 3.14159; + const bigIntNum = 12345678901234567890n; + + const result = multiplyFloatByBigInt(floatNum, bigIntNum); + expect(result).toEqual(38785061379329506137n); + }); + + it("should handle very large floating-point numbers", () => { + const floatNum = 1.2345678901234567; // Large float + const bigIntNum = 1000n; + + const result = multiplyFloatByBigInt(floatNum, bigIntNum); + expect(result).toEqual(1234n); + }); }); diff --git a/packages/api/src/axelar-query/utils/bigint.ts b/packages/api/src/axelar-query/utils/bigint.ts index 560eecdf3..38b445399 100644 --- a/packages/api/src/axelar-query/utils/bigint.ts +++ b/packages/api/src/axelar-query/utils/bigint.ts @@ -36,3 +36,20 @@ export function gasToWei( ); } } + +export function multiplyFloatByBigInt(floatNum: number, bigIntNum: bigint) { + // Determine the number of decimal places in the float + const decimalPlaces = floatNum.toString().split(".")[1]?.length || 0; + + // Scaling factor + const scalingFactor = 10 ** decimalPlaces; + + // Convert float to scaled BigInt (as before) + const scaledBigInt = BigInt(floatNum * scalingFactor); + + // Perform the multiplication (both are now BigInts) + const result = scaledBigInt * bigIntNum; + + // Divide by scaling factor to get the final result + return result / BigInt(scalingFactor); +} diff --git a/packages/api/src/gmp/types.ts b/packages/api/src/gmp/types.ts index 79d73bb25..a52fd8de5 100644 --- a/packages/api/src/gmp/types.ts +++ b/packages/api/src/gmp/types.ts @@ -401,6 +401,8 @@ export type TokenUnit = { decimals: number; }; +export type L2Type = "op" | "mantle" | "arb" | undefined; + export type Token = { contract_address: string; symbol: string; @@ -414,7 +416,7 @@ export type Token = { gas_price_gwei: string; }; -type GetBaseFeesResult = { +export type GetBaseFeesResult = { base_fee: number; base_fee_usd: number; source_base_fee: number; @@ -433,9 +435,11 @@ type GetBaseFeesResult = { source_express_fee: ExpressFee; destination_express_fee: ExpressFee; source_token: Token; + l2_type: L2Type; destination_native_token: Token & { name: string; symbol: string; + l1_gas_oracle_address?: `0x${string}` | undefined; }; ethereum_token: { name: string; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 306bf12a0..7dccc2027 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9907,6 +9907,9 @@ packages: resolution: {integrity: sha512-GBryCiyl+taz5DPq0expxzfMVcrpKjWvEBSAqT1tPUSthnzOWnVF77XlUAYgFZqzPsEu9lQ1dHCdqZx7WWMCXA==} peerDependencies: '@web3modal/wallet': 4.1.1 + peerDependenciesMeta: + '@web3modal/siwe': + optional: true dependencies: '@web3modal/common': 4.1.1 '@web3modal/core': 4.1.1(@types/react@18.2.21)(react@18.2.0) @@ -9947,6 +9950,15 @@ packages: '@wagmi/connectors': '>=4.0.0' '@wagmi/core': '>=2.0.0' viem: '>=2.0.0' + peerDependenciesMeta: + '@web3modal/siwe': + optional: true + react: + optional: true + react-dom: + optional: true + vue: + optional: true dependencies: '@wagmi/connectors': 4.1.18(@types/react@18.2.21)(@vercel/kv@1.0.1)(@wagmi/core@2.6.9)(react-dom@18.2.0)(react-native@0.73.6)(react@18.2.0)(typescript@5.4.3)(viem@2.8.18)(zod@3.22.4) '@wagmi/core': 2.6.9(@types/react@18.2.21)(react@18.2.0)(typescript@5.4.3)(viem@2.8.18)(zod@3.22.4)