@raac/rpc 2.0.0-beta.3 → 2.0.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,3 +26,13 @@ export declare function decodeRevertWithAbi(abi: Interface | InterfaceAbi, data?
26
26
  * @returns The error name and its decoded arguments, or null if no known contract declares that selector.
27
27
  */
28
28
  export declare function decodeRevertWithAllAbis(data?: string): DecodedRevert | null;
29
+ /**
30
+ * Names the custom error behind a bare 4-byte selector, for when a wallet or
31
+ * client surfaced the selector but not the full revert data (so the arguments
32
+ * cannot be decoded). Looks in `abi` first, then in every known ABI.
33
+ *
34
+ * @param selector The 4-byte error selector, e.g. "0x79db4619".
35
+ * @param abi The ABI of the contract that was called, when known.
36
+ * @returns The error name, or null if no known contract declares that selector.
37
+ */
38
+ export declare function errorNameForSelector(selector: string, abi?: Interface | InterfaceAbi): string | null;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.decodeRevertWithAbi = decodeRevertWithAbi;
4
4
  exports.decodeRevertWithAllAbis = decodeRevertWithAllAbis;
5
+ exports.errorNameForSelector = errorNameForSelector;
5
6
  const ethers_1 = require("ethers");
6
7
  const artifacts_1 = require("./artifacts");
7
8
  /**
@@ -32,6 +33,22 @@ function decodeRevertWithAbi(abi, data) {
32
33
  return { name: parsed.name, args };
33
34
  }
34
35
  let ALL_ERRORS;
36
+ function allErrors() {
37
+ if (!ALL_ERRORS) {
38
+ const bySelector = new Map();
39
+ for (const abi of Object.values(artifacts_1.ABIS)) {
40
+ for (const item of abi) {
41
+ if (item?.type !== "error")
42
+ continue;
43
+ const fragment = ethers_1.ErrorFragment.from(item);
44
+ if (!bySelector.has(fragment.selector))
45
+ bySelector.set(fragment.selector, fragment);
46
+ }
47
+ }
48
+ ALL_ERRORS = new ethers_1.Interface([...bySelector.values()]);
49
+ }
50
+ return ALL_ERRORS;
51
+ }
35
52
  /**
36
53
  * Decodes revert data against every custom error in {@link ABIS}. This is the
37
54
  * fallback for a revert thrown by a contract other than the one called: e.g.
@@ -44,18 +61,29 @@ let ALL_ERRORS;
44
61
  * @returns The error name and its decoded arguments, or null if no known contract declares that selector.
45
62
  */
46
63
  function decodeRevertWithAllAbis(data) {
47
- if (!ALL_ERRORS) {
48
- const bySelector = new Map();
49
- for (const abi of Object.values(artifacts_1.ABIS)) {
50
- for (const item of abi) {
51
- if (item?.type !== "error")
52
- continue;
53
- const fragment = ethers_1.ErrorFragment.from(item);
54
- if (!bySelector.has(fragment.selector))
55
- bySelector.set(fragment.selector, fragment);
56
- }
64
+ return decodeRevertWithAbi(allErrors(), data);
65
+ }
66
+ /**
67
+ * Names the custom error behind a bare 4-byte selector, for when a wallet or
68
+ * client surfaced the selector but not the full revert data (so the arguments
69
+ * cannot be decoded). Looks in `abi` first, then in every known ABI.
70
+ *
71
+ * @param selector The 4-byte error selector, e.g. "0x79db4619".
72
+ * @param abi The ABI of the contract that was called, when known.
73
+ * @returns The error name, or null if no known contract declares that selector.
74
+ */
75
+ function errorNameForSelector(selector, abi) {
76
+ for (const iface of [abi, allErrors()]) {
77
+ if (!iface)
78
+ continue;
79
+ try {
80
+ const fragment = ethers_1.Interface.from(iface).getError(selector);
81
+ if (fragment)
82
+ return fragment.name;
83
+ }
84
+ catch {
85
+ // Not declared on this ABI.
57
86
  }
58
- ALL_ERRORS = new ethers_1.Interface([...bySelector.values()]);
59
87
  }
60
- return decodeRevertWithAbi(ALL_ERRORS, data);
88
+ return null;
61
89
  }
@@ -51,6 +51,27 @@ function isUserRejection(error) {
51
51
  return codes.some((code) => REJECTION_CODES.has(code));
52
52
  }
53
53
  const NETWORK_CODES = new Set(["NETWORK_ERROR", "TIMEOUT", "SERVER_ERROR"]);
54
+ const SELECTOR = /^0x[0-9a-fA-F]{8}$/;
55
+ const SIGNATURE_IN_MESSAGE = /Encoded error signature "(0x[0-9a-fA-F]{8})"/;
56
+ /**
57
+ * The revert selector when a client decoded the revert itself and threw only
58
+ * the selector. thirdweb does this: its send path decodes the revert data with
59
+ * viem against the contract's ABI, which it lacks behind the ethers adapter, so
60
+ * it throws viem's `AbiErrorSignatureNotFoundError` (selector on `.signature`
61
+ * and in the message) and drops the data.
62
+ */
63
+ function selectorWithoutData(error) {
64
+ let current = error;
65
+ for (let depth = 0; current && typeof current === "object" && depth < 5; depth++) {
66
+ if (typeof current.signature === "string" && SELECTOR.test(current.signature))
67
+ return current.signature.toLowerCase();
68
+ const match = typeof current.message === "string" ? current.message.match(SIGNATURE_IN_MESSAGE) : null;
69
+ if (match)
70
+ return match[1].toLowerCase();
71
+ current = current.cause;
72
+ }
73
+ return undefined;
74
+ }
54
75
  /**
55
76
  * Builds an {@link RpcError} from whatever ethers, a wallet, or the network
56
77
  * threw. Every `rpc` function's catch block calls this instead of hand-rolling
@@ -86,7 +107,7 @@ function toRpcError(action, error, resolve) {
86
107
  }
87
108
  }
88
109
  const data = err?.data ?? err?.error?.data ?? err?.info?.error?.data;
89
- const selector = typeof data === "string" && /^0x[0-9a-fA-F]{8}/.test(data) ? data.slice(0, 10).toLowerCase() : undefined;
110
+ const selector = typeof data === "string" && /^0x[0-9a-fA-F]{8}/.test(data) ? data.slice(0, 10).toLowerCase() : selectorWithoutData(err);
90
111
  let errorName;
91
112
  let args;
92
113
  // ethers already decoded this against the ABI it used to make the call —
@@ -111,6 +132,9 @@ function toRpcError(action, error, resolve) {
111
132
  args = decoded.args;
112
133
  }
113
134
  }
135
+ if (!errorName && selector) {
136
+ errorName = (0, errorDecoder_1.errorNameForSelector)(selector, abi) ?? undefined;
137
+ }
114
138
  let code;
115
139
  if (errorName || selector || err?.code === "CALL_EXCEPTION") {
116
140
  code = "CONTRACT_REVERT";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raac/rpc",
3
- "version": "2.0.0-beta.3",
3
+ "version": "2.0.0-beta.4",
4
4
  "description": "RPC Library for RAAC",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/types/index.d.ts",