From 875900b493fe95ebf4998971aabc1bf6dcedcca9 Mon Sep 17 00:00:00 2001 From: Mark Spanbroek Date: Mon, 18 Mar 2024 16:02:39 +0100 Subject: [PATCH] jsonrpc: extract error data from JSON RPC error Inspired by 'spelunk' from ethers.js: https://github.com/ethers-io/ethers.js/blob/f97b92bbb1bde22fcc44100af78d7f31602863ab/packages/providers/src.ts/json-rpc-provider.ts#L25 --- ethers/provider.nim | 1 + ethers/providers/jsonrpc/errors.nim | 19 ++++++++++++++- testmodule/providers/jsonrpc/testErrors.nim | 27 +++++++++++++++++++++ testmodule/providers/testJsonRpc.nim | 1 + 4 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 testmodule/providers/jsonrpc/testErrors.nim diff --git a/ethers/provider.nim b/ethers/provider.nim index 068439c..a8cd542 100644 --- a/ethers/provider.nim +++ b/ethers/provider.nim @@ -14,6 +14,7 @@ export blocktag type Provider* = ref object of RootObj ProviderError* = object of EthersError + data*: ?seq[byte] Subscription* = ref object of RootObj EventFilter* {.serialize.} = ref object of RootObj address*: Address diff --git a/ethers/providers/jsonrpc/errors.nim b/ethers/providers/jsonrpc/errors.nim index e6da61e..c84d4c5 100644 --- a/ethers/providers/jsonrpc/errors.nim +++ b/ethers/providers/jsonrpc/errors.nim @@ -1,13 +1,30 @@ +import std/strutils import pkg/stew/byteutils +import ../../basics import ../../provider import ./conversions +{.push raises:[].} + type JsonRpcProviderError* = object of ProviderError -func new(_: type JsonRpcProviderError, json: JsonNode): ref JsonRpcProviderError = +func extractErrorData(json: JsonNode): ?seq[byte] = + if json.kind == JObject: + if "message" in json and "data" in json: + let message = json{"message"}.getStr() + let hex = json{"data"}.getStr() + if "reverted" in message and hex.startsWith("0x"): + if data =? hexToSeqByte(hex).catch: + return some data + for key in json.keys: + if data =? extractErrorData(json{key}): + return some data + +func new*(_: type JsonRpcProviderError, json: JsonNode): ref JsonRpcProviderError = let error = (ref JsonRpcProviderError)() if "message" in json: error.msg = json{"message"}.getStr + error.data = extractErrorData(json) error proc raiseJsonRpcProviderError*( diff --git a/testmodule/providers/jsonrpc/testErrors.nim b/testmodule/providers/jsonrpc/testErrors.nim new file mode 100644 index 0000000..afa9eda --- /dev/null +++ b/testmodule/providers/jsonrpc/testErrors.nim @@ -0,0 +1,27 @@ +import std/unittest +import std/json +import pkg/questionable +import pkg/ethers/providers/jsonrpc/errors + +suite "JSON RPC errors": + + test "converts JSON RPC error to Nim error": + let error = %*{ "message": "some error" } + check JsonRpcProviderError.new(error).msg == "some error" + + test "converts error data to bytes": + let error = %*{ + "message": "VM Exception: reverted with 'some error'", + "data": "0xabcd" + } + check JsonRpcProviderError.new(error).data == some @[0xab'u8, 0xcd'u8] + + test "converts nested error data to bytes": + let error = %*{ + "message": "VM Exception: reverted with 'some error'", + "data": { + "message": "VM Exception: reverted with 'some error'", + "data": "0xabcd" + } + } + check JsonRpcProviderError.new(error).data == some @[0xab'u8, 0xcd'u8] diff --git a/testmodule/providers/testJsonRpc.nim b/testmodule/providers/testJsonRpc.nim index 8dbf77e..8e7de1d 100644 --- a/testmodule/providers/testJsonRpc.nim +++ b/testmodule/providers/testJsonRpc.nim @@ -2,5 +2,6 @@ import ./jsonrpc/testJsonRpcProvider import ./jsonrpc/testJsonRpcSigner import ./jsonrpc/testJsonRpcSubscriptions import ./jsonrpc/testConversions +import ./jsonrpc/testErrors {.warning[UnusedImport]:off.}