From f62dd06e5022dfdf36a7d8c48d6560c338bcdbd9 Mon Sep 17 00:00:00 2001 From: Goums Date: Mon, 25 Mar 2024 15:11:05 +0100 Subject: [PATCH] fix: keep provider original error (#100) * fix: keep proxy provider original error * test: proxy provider originalError.data * chore: example decoding custom ABI error * chore: add changeset --- .changeset/short-ads-nail.md | 5 ++ .../Web3FunctionProxyProvider.spec.ts | 37 ++++++++++++++ src/lib/provider/Web3FunctionProxyProvider.ts | 4 +- src/web3-functions/custom-error/index.ts | 48 +++++++++++++++++++ src/web3-functions/custom-error/schema.json | 8 ++++ 5 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 .changeset/short-ads-nail.md create mode 100644 src/web3-functions/custom-error/index.ts create mode 100644 src/web3-functions/custom-error/schema.json diff --git a/.changeset/short-ads-nail.md b/.changeset/short-ads-nail.md new file mode 100644 index 00000000..7dcecc35 --- /dev/null +++ b/.changeset/short-ads-nail.md @@ -0,0 +1,5 @@ +--- +"@gelatonetwork/web3-functions-sdk": patch +--- + +fix: keep proxy provider original error diff --git a/src/lib/provider/Web3FunctionProxyProvider.spec.ts b/src/lib/provider/Web3FunctionProxyProvider.spec.ts index 07e4ec80..4db4a597 100644 --- a/src/lib/provider/Web3FunctionProxyProvider.spec.ts +++ b/src/lib/provider/Web3FunctionProxyProvider.spec.ts @@ -8,10 +8,12 @@ describe("Web3FunctionProxyProvider", () => { enum TestChainIds { Sepolia = 11155111, Amoy = 80002, + ArbSepolia = 421614, } enum TestChainProviders { Sepolia = "https://rpc.ankr.com/eth_sepolia", Amoy = "https://rpc.ankr.com/polygon_amoy", + ArbSepolia = "https://sepolia-rollup.arbitrum.io/rpc", } let proxyProvider: Web3FunctionProxyProvider; @@ -30,6 +32,9 @@ describe("Web3FunctionProxyProvider", () => { TestChainProviders.Sepolia ), [TestChainIds.Amoy]: new StaticJsonRpcProvider(TestChainProviders.Amoy), + [TestChainIds.ArbSepolia]: new StaticJsonRpcProvider( + TestChainProviders.ArbSepolia + ), }; }); @@ -177,6 +182,38 @@ describe("Web3FunctionProxyProvider", () => { expect(response.error.message.includes("does not exist")).toBeTruthy(); }); + test("should return original error data", async () => { + const { body } = await request( + `${proxyProvider.getProxyUrl()}/${TestChainIds.ArbSepolia}`, + { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + id: 0, + jsonrpc: "2.0", + method: "eth_call", + params: [ + { + to: "0xac9f91277ccbb5d270e27246b203b221023a0e06", + data: "0x7894e0b0000000000000000000000000000000000000000000000000000000000000000a", + }, + "latest", + ], + }), + dispatcher: new Agent({ pipelining: 0 }), + } + ); + const response = (await body.json()) as any; + + expect(response.error).toBeDefined(); + expect(response.error.message).toBeDefined(); + expect(response.error.data).toBeDefined(); + + expect(response.error.data.originalError.data).toEqual( + "0x110b3655000000000000000000000000000000000000000000000000000000000000000a" + ); + }); + test("should respond with main chain when chainId is not provided", async () => { const { body } = await request(proxyProvider.getProxyUrl(), { method: "POST", diff --git a/src/lib/provider/Web3FunctionProxyProvider.ts b/src/lib/provider/Web3FunctionProxyProvider.ts index 3cb62235..cf5861bf 100644 --- a/src/lib/provider/Web3FunctionProxyProvider.ts +++ b/src/lib/provider/Web3FunctionProxyProvider.ts @@ -90,9 +90,9 @@ export class Web3FunctionProxyProvider { } catch (_error) { // Standardizing RPC error before returning to the user // If the serializer cannot extract a valid error, it will fallback to: { code: -32603, message: 'Internal JSON-RPC error.'} - const { code, message } = serializeError(_error); + const { code, message, data } = serializeError(_error); // Send result as valid JsonRPC error - res.send({ id, jsonrpc, error: { code, message } }); + res.send({ id, jsonrpc, error: { code, message, data } }); } } diff --git a/src/web3-functions/custom-error/index.ts b/src/web3-functions/custom-error/index.ts new file mode 100644 index 00000000..3d7051c0 --- /dev/null +++ b/src/web3-functions/custom-error/index.ts @@ -0,0 +1,48 @@ +import { + Web3Function, + Web3FunctionContext, +} from "@gelatonetwork/web3-functions-sdk"; + +import { Contract } from "@ethersproject/contracts"; + +export const abi = [ + { + inputs: [{ internalType: "uint256", name: "random", type: "uint256" }], + name: "CustomError", + type: "error", + }, + { + inputs: [{ internalType: "uint256", name: "_param", type: "uint256" }], + name: "throwCustom", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +]; + +// To run against Arbitrum Sepolia: +// yarn test src/web3-functions/custom-error/index.ts --logs --chain-id=421614 + +Web3Function.onRun(async (context: Web3FunctionContext) => { + const ERRORS_CONTRACT = "0xac9f91277cCbb5d270e27246b203B221023A0e06"; + + const { multiChainProvider } = context; + let provider = multiChainProvider.default(); + + const errorContract = new Contract(ERRORS_CONTRACT, abi, provider); + + try { + const res = await errorContract.callStatic.throwCustom(10); + } catch (error: any) { + console.log("error message ", error.message); + console.log("error data: ", error.data); + const ei = errorContract.interface.parseError(error.data); + console.log("error name: ", ei.name); + console.log("error args: ", ei.args); + } + + return { + canExec: false, + message: "executed", + }; +}); diff --git a/src/web3-functions/custom-error/schema.json b/src/web3-functions/custom-error/schema.json new file mode 100644 index 00000000..bf47f6ad --- /dev/null +++ b/src/web3-functions/custom-error/schema.json @@ -0,0 +1,8 @@ +{ + "web3FunctionVersion": "2.0.0", + "runtime": "js-1.0", + "memory": 128, + "timeout": 30, + "userArgs": {}, + "executionMode": "sequential" +} \ No newline at end of file