@hyperlane-xyz/sdk 44.0.2 → 45.0.1
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.
- package/dist/consts/multisigIsm.d.ts.map +1 -1
- package/dist/consts/multisigIsm.js +104 -772
- package/dist/consts/multisigIsm.js.map +1 -1
- package/dist/consts/testChains.d.ts.map +1 -1
- package/dist/consts/testChains.js +11 -6
- package/dist/consts/testChains.js.map +1 -1
- package/dist/core/adapters/SealevelCoreAdapter.d.ts +6 -0
- package/dist/core/adapters/SealevelCoreAdapter.d.ts.map +1 -1
- package/dist/core/adapters/SealevelCoreAdapter.js +20 -0
- package/dist/core/adapters/SealevelCoreAdapter.js.map +1 -1
- package/dist/core/adapters/SealevelCoreAdapter.test.js +17 -0
- package/dist/core/adapters/SealevelCoreAdapter.test.js.map +1 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +14 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/types.test.js +98 -1
- package/dist/core/types.test.js.map +1 -1
- package/dist/deploy/proxy.d.ts +1 -0
- package/dist/deploy/proxy.d.ts.map +1 -1
- package/dist/deploy/proxy.js +6 -0
- package/dist/deploy/proxy.js.map +1 -1
- package/dist/deploy/warp.d.ts.map +1 -1
- package/dist/deploy/warp.js +32 -43
- package/dist/deploy/warp.js.map +1 -1
- package/dist/deploy/warp.test.js +62 -1
- package/dist/deploy/warp.test.js.map +1 -1
- package/dist/gas/adapters/SealevelIgpAdapter.d.ts +3 -2
- package/dist/gas/adapters/SealevelIgpAdapter.d.ts.map +1 -1
- package/dist/gas/adapters/SealevelIgpAdapter.js +3 -2
- package/dist/gas/adapters/SealevelIgpAdapter.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/ism/EvmIsmModule.d.ts.map +1 -1
- package/dist/ism/EvmIsmModule.js +5 -1
- package/dist/ism/EvmIsmModule.js.map +1 -1
- package/dist/ism/EvmIsmModule.test.js +109 -0
- package/dist/ism/EvmIsmModule.test.js.map +1 -1
- package/dist/ism/HyperlaneIsmFactory.d.ts +26 -6
- package/dist/ism/HyperlaneIsmFactory.d.ts.map +1 -1
- package/dist/ism/HyperlaneIsmFactory.hardhat-test.js +60 -0
- package/dist/ism/HyperlaneIsmFactory.hardhat-test.js.map +1 -1
- package/dist/ism/HyperlaneIsmFactory.js +165 -34
- package/dist/ism/HyperlaneIsmFactory.js.map +1 -1
- package/dist/ism/HyperlaneIsmFactory.test.js +33 -6
- package/dist/ism/HyperlaneIsmFactory.test.js.map +1 -1
- package/dist/ism/types.d.ts +18 -2
- package/dist/ism/types.d.ts.map +1 -1
- package/dist/ism/types.js +43 -3
- package/dist/ism/types.js.map +1 -1
- package/dist/ism/types.test.js +232 -56
- package/dist/ism/types.test.js.map +1 -1
- package/dist/ism/utils.d.ts.map +1 -1
- package/dist/ism/utils.js +13 -0
- package/dist/ism/utils.js.map +1 -1
- package/dist/ism/utils.test.js +43 -1
- package/dist/ism/utils.test.js.map +1 -1
- package/dist/metadata/ChainMetadataManager.d.ts +1 -0
- package/dist/metadata/ChainMetadataManager.d.ts.map +1 -1
- package/dist/metadata/ChainMetadataManager.js +1 -0
- package/dist/metadata/ChainMetadataManager.js.map +1 -1
- package/dist/metadata/ChainMetadataManager.test.js +14 -1
- package/dist/metadata/ChainMetadataManager.test.js.map +1 -1
- package/dist/metadata/ChainMetadataResolver.d.ts +1 -0
- package/dist/metadata/ChainMetadataResolver.d.ts.map +1 -1
- package/dist/metadata/ChainMetadataResolver.js +1 -0
- package/dist/metadata/ChainMetadataResolver.js.map +1 -1
- package/dist/metadata/ChainMetadataResolver.test.js +4 -0
- package/dist/metadata/ChainMetadataResolver.test.js.map +1 -1
- package/dist/metadata/agentConfig.d.ts +209 -93
- package/dist/metadata/agentConfig.d.ts.map +1 -1
- package/dist/metadata/agentConfig.js +106 -13
- package/dist/metadata/agentConfig.js.map +1 -1
- package/dist/metadata/agentConfig.test.js +277 -25
- package/dist/metadata/agentConfig.test.js.map +1 -1
- package/dist/metadata/chainMetadata.test.js +25 -0
- package/dist/metadata/chainMetadata.test.js.map +1 -1
- package/dist/metadata/chainMetadataTypes.d.ts +36 -0
- package/dist/metadata/chainMetadataTypes.d.ts.map +1 -1
- package/dist/metadata/chainMetadataTypes.js +26 -0
- package/dist/metadata/chainMetadataTypes.js.map +1 -1
- package/dist/providers/SeismicSigner.d.ts +4 -12
- package/dist/providers/SeismicSigner.d.ts.map +1 -1
- package/dist/providers/SeismicSigner.js +84 -35
- package/dist/providers/SeismicSigner.js.map +1 -1
- package/dist/providers/SeismicSigner.test.d.ts +2 -0
- package/dist/providers/SeismicSigner.test.d.ts.map +1 -0
- package/dist/providers/SeismicSigner.test.js +160 -0
- package/dist/providers/SeismicSigner.test.js.map +1 -0
- package/dist/providers/SmartProvider/SmartProvider.d.ts +1 -5
- package/dist/providers/SmartProvider/SmartProvider.d.ts.map +1 -1
- package/dist/providers/SmartProvider/SmartProvider.emptyResponse.test.d.ts +2 -0
- package/dist/providers/SmartProvider/SmartProvider.emptyResponse.test.d.ts.map +1 -0
- package/dist/providers/SmartProvider/SmartProvider.emptyResponse.test.js +164 -0
- package/dist/providers/SmartProvider/SmartProvider.emptyResponse.test.js.map +1 -0
- package/dist/providers/SmartProvider/SmartProvider.js +35 -72
- package/dist/providers/SmartProvider/SmartProvider.js.map +1 -1
- package/dist/providers/SmartProvider/SmartProvider.test.js +181 -2
- package/dist/providers/SmartProvider/SmartProvider.test.js.map +1 -1
- package/dist/providers/SmartProvider/jsonRpcError.d.ts +7 -0
- package/dist/providers/SmartProvider/jsonRpcError.d.ts.map +1 -0
- package/dist/providers/SmartProvider/jsonRpcError.js +67 -0
- package/dist/providers/SmartProvider/jsonRpcError.js.map +1 -0
- package/dist/providers/SmartProvider/types.d.ts +1 -0
- package/dist/providers/SmartProvider/types.d.ts.map +1 -1
- package/dist/providers/builders/ethersV5.d.ts +0 -1
- package/dist/providers/builders/ethersV5.d.ts.map +1 -1
- package/dist/providers/builders/ethersV5.js +19 -5
- package/dist/providers/builders/ethersV5.js.map +1 -1
- package/dist/providers/builders/ethersV5.test.d.ts +2 -0
- package/dist/providers/builders/ethersV5.test.d.ts.map +1 -0
- package/dist/providers/builders/ethersV5.test.js +77 -0
- package/dist/providers/builders/ethersV5.test.js.map +1 -0
- package/dist/providers/builders/radix.browser.d.ts +1 -0
- package/dist/providers/builders/radix.browser.d.ts.map +1 -1
- package/dist/providers/builders/radix.browser.js +1 -0
- package/dist/providers/builders/radix.browser.js.map +1 -1
- package/dist/providers/builders/radix.d.ts.map +1 -1
- package/dist/providers/builders/radix.gateway.test.d.ts +2 -0
- package/dist/providers/builders/radix.gateway.test.d.ts.map +1 -0
- package/dist/providers/builders/radix.gateway.test.js +39 -0
- package/dist/providers/builders/radix.gateway.test.js.map +1 -0
- package/dist/providers/builders/radix.js +1 -0
- package/dist/providers/builders/radix.js.map +1 -1
- package/dist/providers/builders/radix.test.js +1 -0
- package/dist/providers/builders/radix.test.js.map +1 -1
- package/dist/providers/providerBuilders.d.ts +1 -1
- package/dist/providers/providerBuilders.d.ts.map +1 -1
- package/dist/providers/providerBuilders.js +1 -1
- package/dist/providers/providerBuilders.js.map +1 -1
- package/dist/rpc/evm/EvmEventLogsReader.hardhat-test.js +60 -8
- package/dist/rpc/evm/EvmEventLogsReader.hardhat-test.js.map +1 -1
- package/dist/test/errors.d.ts +25 -0
- package/dist/test/errors.d.ts.map +1 -1
- package/dist/test/errors.js +31 -0
- package/dist/test/errors.js.map +1 -1
- package/dist/test/fixtures/warp-route-replay/record.d.ts +2 -0
- package/dist/test/fixtures/warp-route-replay/record.d.ts.map +1 -0
- package/dist/test/fixtures/warp-route-replay/record.js +32 -0
- package/dist/test/fixtures/warp-route-replay/record.js.map +1 -0
- package/dist/test/replayRpc.d.ts +82 -0
- package/dist/test/replayRpc.d.ts.map +1 -0
- package/dist/test/replayRpc.js +331 -0
- package/dist/test/replayRpc.js.map +1 -0
- package/dist/test/replayScenarios.d.ts +19 -0
- package/dist/test/replayScenarios.d.ts.map +1 -0
- package/dist/test/replayScenarios.js +89 -0
- package/dist/test/replayScenarios.js.map +1 -0
- package/dist/timelock/evm/EvmTimelockReader.hardhat-test.js +92 -7
- package/dist/timelock/evm/EvmTimelockReader.hardhat-test.js.map +1 -1
- package/dist/timelock/evm/errors.js +1 -1
- package/dist/timelock/evm/errors.js.map +1 -1
- package/dist/token/EvmWarpRouteReader.d.ts +27 -0
- package/dist/token/EvmWarpRouteReader.d.ts.map +1 -1
- package/dist/token/EvmWarpRouteReader.hardhat-test.js +28 -3
- package/dist/token/EvmWarpRouteReader.hardhat-test.js.map +1 -1
- package/dist/token/EvmWarpRouteReader.js +172 -42
- package/dist/token/EvmWarpRouteReader.js.map +1 -1
- package/dist/token/EvmWarpRouteReader.replay.test.d.ts +2 -0
- package/dist/token/EvmWarpRouteReader.replay.test.d.ts.map +1 -0
- package/dist/token/EvmWarpRouteReader.replay.test.js +392 -0
- package/dist/token/EvmWarpRouteReader.replay.test.js.map +1 -0
- package/dist/token/EvmWarpRouteReader.test.js +529 -4
- package/dist/token/EvmWarpRouteReader.test.js.map +1 -1
- package/dist/token/types.d.ts +1 -1
- package/dist/token/warpCheck.d.ts +1 -0
- package/dist/token/warpCheck.d.ts.map +1 -1
- package/dist/token/warpCheck.js +25 -2
- package/dist/token/warpCheck.js.map +1 -1
- package/dist/token/warpCheck.test.js +119 -0
- package/dist/token/warpCheck.test.js.map +1 -1
- package/dist/utils/contract.d.ts +33 -0
- package/dist/utils/contract.d.ts.map +1 -1
- package/dist/utils/contract.js +116 -3
- package/dist/utils/contract.js.map +1 -1
- package/dist/utils/contract.test.js +465 -2
- package/dist/utils/contract.test.js.map +1 -1
- package/dist/utils/contract.transport.test.d.ts +2 -0
- package/dist/utils/contract.transport.test.d.ts.map +1 -0
- package/dist/utils/contract.transport.test.js +145 -0
- package/dist/utils/contract.transport.test.js.map +1 -0
- package/dist/utils/ism.d.ts +12 -6
- package/dist/utils/ism.d.ts.map +1 -1
- package/dist/utils/ism.js +92 -41
- package/dist/utils/ism.js.map +1 -1
- package/dist/utils/ism.test.js +75 -2
- package/dist/utils/ism.test.js.map +1 -1
- package/package.json +29 -15
package/dist/utils/contract.js
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { compareVersions } from 'compare-versions';
|
|
2
2
|
import { PackageVersioned__factory } from '@hyperlane-xyz/core';
|
|
3
3
|
import { chunk, isNullish, rootLogger, strip0x, } from '@hyperlane-xyz/utils';
|
|
4
|
+
import { getNestedJsonRpcError } from '../providers/SmartProvider/jsonRpcError.js';
|
|
5
|
+
// Diamond fallbacks can use this custom error instead of empty revert data
|
|
6
|
+
// when no facet implements the requested selector. The value is the selector of
|
|
7
|
+
// FacetNotFound() (first 4 bytes of keccak256("FacetNotFound()")), which takes no
|
|
8
|
+
// arguments, so the revert data is exactly the selector.
|
|
9
|
+
const FACET_NOT_FOUND_REVERT_DATA = '0x800ab12c';
|
|
10
|
+
// LSP17 extendable contracts revert with
|
|
11
|
+
// NoExtensionFoundForFunctionSelector(bytes4) when no extension handles the
|
|
12
|
+
// requested selector. The revert data is the error selector (first 4 bytes of
|
|
13
|
+
// keccak256 of the signature) followed by the requested bytes4 selector
|
|
14
|
+
// left-aligned in one 32-byte word.
|
|
15
|
+
const LSP17_NO_EXTENSION_REVERT_DATA_PATTERN = /^0xbb370b2b([0-9a-f]{8})0{56}$/i;
|
|
4
16
|
/**
|
|
5
17
|
* Returns true when the deployed contract version is already at or above the
|
|
6
18
|
* target version.
|
|
@@ -40,6 +52,24 @@ function findCallException(error) {
|
|
|
40
52
|
}
|
|
41
53
|
return undefined;
|
|
42
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* True for LSP17 NoExtensionFoundForFunctionSelector(bytes4) revert data. When
|
|
57
|
+
* the CALL_EXCEPTION carries the call (`transaction.data`), the embedded
|
|
58
|
+
* selector must be the called one, otherwise a getter that exists but reaches
|
|
59
|
+
* another contract's missing extension would read as absent. Without the call
|
|
60
|
+
* data the embedded selector cannot be verified and the revert is accepted.
|
|
61
|
+
*/
|
|
62
|
+
function isLsp17NoExtensionRevert(data, callException) {
|
|
63
|
+
const match = LSP17_NO_EXTENSION_REVERT_DATA_PATTERN.exec(data);
|
|
64
|
+
if (!match)
|
|
65
|
+
return false;
|
|
66
|
+
const callData = isRecord(callException.transaction)
|
|
67
|
+
? callException.transaction.data
|
|
68
|
+
: undefined;
|
|
69
|
+
if (typeof callData !== 'string')
|
|
70
|
+
return true;
|
|
71
|
+
return callData.slice(0, 10).toLowerCase() === `0x${match[1].toLowerCase()}`;
|
|
72
|
+
}
|
|
43
73
|
export function isMissingSelectorCallException(error) {
|
|
44
74
|
if (!isRecord(error))
|
|
45
75
|
return false;
|
|
@@ -47,6 +77,51 @@ export function isMissingSelectorCallException(error) {
|
|
|
47
77
|
return true;
|
|
48
78
|
return isMissingSelectorRevert(error);
|
|
49
79
|
}
|
|
80
|
+
// EIP-1474 execution error
|
|
81
|
+
const EXECUTION_REVERTED_JSON_RPC_CODE = 3;
|
|
82
|
+
// Nethermind (before it adopted code 3) reports every VM failure as -32015
|
|
83
|
+
// "VM execution error." and names the failure in the string `data`
|
|
84
|
+
// ("revert", "err: revert (supplied gas N)", "err: OutOfGas ...").
|
|
85
|
+
const NETHERMIND_VM_EXECUTION_ERROR_JSON_RPC_CODE = -32015;
|
|
86
|
+
const NODE_REVERT_MESSAGE_PATTERN = /\brevert(ed)?\b/i;
|
|
87
|
+
/**
|
|
88
|
+
* True for EVM call return/revert data with no payload ('0x' or ''). Not
|
|
89
|
+
* isStorageEmpty, which also accepts '0x0' as an empty storage slot.
|
|
90
|
+
*/
|
|
91
|
+
export function isReturnDataEmpty(data) {
|
|
92
|
+
return data === '0x' || data === '';
|
|
93
|
+
}
|
|
94
|
+
function nestedErrorReportsRevert(callException) {
|
|
95
|
+
const { code, message, data } = getNestedJsonRpcError(callException);
|
|
96
|
+
return (code === EXECUTION_REVERTED_JSON_RPC_CODE ||
|
|
97
|
+
(typeof message === 'string' &&
|
|
98
|
+
NODE_REVERT_MESSAGE_PATTERN.test(message)) ||
|
|
99
|
+
(code === NETHERMIND_VM_EXECUTION_ERROR_JSON_RPC_CODE &&
|
|
100
|
+
typeof data === 'string' &&
|
|
101
|
+
NODE_REVERT_MESSAGE_PATTERN.test(data)));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* True for an ethers CALL_EXCEPTION produced by a call that executed and
|
|
105
|
+
* returned nothing: either ethers failed to decode an empty return (no nested
|
|
106
|
+
* error) or the node reported an execution revert without data.
|
|
107
|
+
*
|
|
108
|
+
* ethers wraps every eth_call failure without revert data as a CALL_EXCEPTION
|
|
109
|
+
* with data "0x", including transport failures (HTTP status errors, dropped
|
|
110
|
+
* connections, JSON-RPC errors such as "header not found"). Those carry the
|
|
111
|
+
* original error nested and must not be read as a missing selector. The
|
|
112
|
+
* formatted message also contains data="0x" for those wrapped failures, so
|
|
113
|
+
* the message fallback (for ethers/provider combinations that only expose
|
|
114
|
+
* empty return data there) applies only when there is no nested error. With a
|
|
115
|
+
* nested error, the call counts as a missing selector when the nested error
|
|
116
|
+
* reports a revert (JSON-RPC code 3, a revert message, or Nethermind's -32015
|
|
117
|
+
* with revert data), or when it reports empty data ("0x" or "") and carries
|
|
118
|
+
* neither a code nor a message. A nested error that pairs empty data with any
|
|
119
|
+
* other code (SERVER_ERROR, 429, -32000 "header not found") or with a
|
|
120
|
+
* non-revert message ("socket hang up", "timeout") is a transport failure.
|
|
121
|
+
* Failures that are not reverts (out of gas, invalid opcode) are not missing
|
|
122
|
+
* selectors. A diamond FacetNotFound() or an LSP17
|
|
123
|
+
* NoExtensionFoundForFunctionSelector(bytes4) revert is a missing selector.
|
|
124
|
+
*/
|
|
50
125
|
export function isMissingSelectorRevert(error) {
|
|
51
126
|
const callException = findCallException(error);
|
|
52
127
|
if (!callException)
|
|
@@ -57,12 +132,50 @@ export function isMissingSelectorRevert(error) {
|
|
|
57
132
|
const data = typeof callException.data === 'string'
|
|
58
133
|
? callException.data
|
|
59
134
|
: nestedError?.data;
|
|
60
|
-
|
|
135
|
+
// A diamond or LSP17 miss is a known "no such function" revert, so unlike empty data
|
|
136
|
+
// it needs no transport gating.
|
|
137
|
+
if (typeof data === 'string' &&
|
|
138
|
+
(data.toLowerCase() === FACET_NOT_FOUND_REVERT_DATA ||
|
|
139
|
+
isLsp17NoExtensionRevert(data, callException))) {
|
|
61
140
|
return true;
|
|
141
|
+
}
|
|
142
|
+
const { code: nestedCode, message: nestedMessage, data: nestedData, } = getNestedJsonRpcError(callException);
|
|
143
|
+
if (isReturnDataEmpty(nestedData)) {
|
|
144
|
+
return (nestedErrorReportsRevert(callException) ||
|
|
145
|
+
(isNullish(nestedCode) && isNullish(nestedMessage)));
|
|
146
|
+
}
|
|
62
147
|
// Some ethers/provider combinations only expose empty return data in the
|
|
63
148
|
// formatted message.
|
|
64
|
-
|
|
65
|
-
callException.message
|
|
149
|
+
const hasEmptyData = isReturnDataEmpty(data) ||
|
|
150
|
+
(typeof callException.message === 'string' &&
|
|
151
|
+
callException.message.includes('data="0x"'));
|
|
152
|
+
if (!hasEmptyData)
|
|
153
|
+
return false;
|
|
154
|
+
if (isNullish(callException.error))
|
|
155
|
+
return true;
|
|
156
|
+
return nestedErrorReportsRevert(callException);
|
|
157
|
+
}
|
|
158
|
+
const REVERT_DATA_PATTERN = /^0x[0-9a-fA-F]+$/;
|
|
159
|
+
// Panic(uint256)
|
|
160
|
+
const PANIC_SELECTOR = '0x4e487b71';
|
|
161
|
+
/**
|
|
162
|
+
* True for a CALL_EXCEPTION whose revert data is a Panic(uint256). Unlike
|
|
163
|
+
* isMissingSelectorRevert, it treats the reverting probe as an answer rather
|
|
164
|
+
* than a failure to reach the contract.
|
|
165
|
+
*/
|
|
166
|
+
export function isPanicRevert(error) {
|
|
167
|
+
const callException = findCallException(error);
|
|
168
|
+
if (!callException)
|
|
169
|
+
return false;
|
|
170
|
+
const nestedError = isRecord(callException.error)
|
|
171
|
+
? callException.error
|
|
172
|
+
: undefined;
|
|
173
|
+
const data = typeof callException.data === 'string'
|
|
174
|
+
? callException.data
|
|
175
|
+
: nestedError?.data;
|
|
176
|
+
return (typeof data === 'string' &&
|
|
177
|
+
REVERT_DATA_PATTERN.test(data) &&
|
|
178
|
+
data.toLowerCase().startsWith(PANIC_SELECTOR));
|
|
66
179
|
}
|
|
67
180
|
export function throwIfNotMissingSelector(error) {
|
|
68
181
|
if (!isMissingSelectorCallException(error))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../../src/utils/contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGnD,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAGL,KAAK,EACL,SAAS,EACT,UAAU,EACV,OAAO,GACR,MAAM,sBAAsB,CAAC;AAE9B;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CACpC,cAAsB,EACtB,aAAqB;IAErB,OAAO,eAAe,CAAC,cAAc,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,eAAe,CAAC,KAAc;IACrC,OAAO,KAAK,YAAY,KAAK;QAC3B,CAAC,CAAC,KAAK,CAAC,OAAO;QACf,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;YACpD,CAAC,CAAC,KAAK,CAAC,OAAO;YACf,CAAC,CAAC,SAAS,CAAC;AAClB,CAAC;AAED,SAAS,uBAAuB,CAAC,KAAc;IAC7C,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,OAAO,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACzB,mEAAmE;QACnE,yEAAyE;QACzE,0CAA0C;QAC1C,IAAI,eAAe,CAAC,OAAO,CAAC,KAAK,gCAAgC,EAAE,CAAC;YAClE,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC;IAC1B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,iBAAiB,CACxB,KAAc;IAEd,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,OAAO,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACzB,IAAI,OAAO,CAAC,IAAI,KAAK,gBAAgB;YAAE,OAAO,OAAO,CAAC;QACtD,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC;IAC1B,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,8BAA8B,CAAC,KAAc;IAC3D,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACnC,IAAI,uBAAuB,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEhD,OAAO,uBAAuB,CAAC,KAAK,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,KAAc;IACpD,MAAM,aAAa,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IAC/C,IAAI,CAAC,aAAa;QAAE,OAAO,KAAK,CAAC;IAEjC,MAAM,WAAW,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC;QAC/C,CAAC,CAAC,aAAa,CAAC,KAAK;QACrB,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,IAAI,GACR,OAAO,aAAa,CAAC,IAAI,KAAK,QAAQ;QACpC,CAAC,CAAC,aAAa,CAAC,IAAI;QACpB,CAAC,CAAC,WAAW,EAAE,IAAI,CAAC;IACxB,IAAI,IAAI,KAAK,IAAI;
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../../src/utils/contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,9 +3,15 @@ import sinon from 'sinon';
|
|
|
3
3
|
import { TestChainName } from '../consts/testChains.js';
|
|
4
4
|
import { MultiProvider } from '../providers/MultiProvider.js';
|
|
5
5
|
import { stubPackageVersion } from '../test/contractStubs.js';
|
|
6
|
-
import { missingSelectorError, networkError, wrappedError, } from '../test/errors.js';
|
|
6
|
+
import { errorStringRevertError, ethersCallExceptionWithNestedError, lsp17NoExtensionError, missingSelectorError, networkError, panicRevertError, wrappedError, } from '../test/errors.js';
|
|
7
7
|
import { randomAddress } from '../test/testUtils.js';
|
|
8
|
-
import { LEGACY_PACKAGE_VERSION, fetchPackageVersion, isMissingSelectorCallException, isMissingSelectorRevert, } from './contract.js';
|
|
8
|
+
import { LEGACY_PACKAGE_VERSION, fetchPackageVersion, isMissingSelectorCallException, isMissingSelectorRevert, isPanicRevert, isReturnDataEmpty, } from './contract.js';
|
|
9
|
+
function facetNotFoundError() {
|
|
10
|
+
return Object.assign(new Error('call reverted with FacetNotFound()'), {
|
|
11
|
+
code: 'CALL_EXCEPTION',
|
|
12
|
+
data: '0x800ab12c',
|
|
13
|
+
});
|
|
14
|
+
}
|
|
9
15
|
describe('contract utils', () => {
|
|
10
16
|
describe('isMissingSelectorCallException', () => {
|
|
11
17
|
it('matches empty call exceptions', () => {
|
|
@@ -17,6 +23,20 @@ describe('contract utils', () => {
|
|
|
17
23
|
it('matches deeply wrapped empty call exceptions', () => {
|
|
18
24
|
expect(isMissingSelectorCallException(wrappedError(wrappedError(missingSelectorError())))).to.equal(true);
|
|
19
25
|
});
|
|
26
|
+
it('matches direct and wrapped diamond FacetNotFound reverts', () => {
|
|
27
|
+
expect(isMissingSelectorCallException(facetNotFoundError())).to.equal(true);
|
|
28
|
+
expect(isMissingSelectorCallException(wrappedError(facetNotFoundError()))).to.equal(true);
|
|
29
|
+
});
|
|
30
|
+
it('matches FacetNotFound revert data nested in the underlying error but not with trailing bytes', () => {
|
|
31
|
+
expect(isMissingSelectorCallException(Object.assign(new Error('call revert exception'), {
|
|
32
|
+
code: 'CALL_EXCEPTION',
|
|
33
|
+
error: { data: '0x800ab12c' },
|
|
34
|
+
}))).to.equal(true);
|
|
35
|
+
expect(isMissingSelectorCallException(Object.assign(new Error('call revert exception'), {
|
|
36
|
+
code: 'CALL_EXCEPTION',
|
|
37
|
+
data: `0x800ab12c${'00'.repeat(32)}`,
|
|
38
|
+
}))).to.equal(false);
|
|
39
|
+
});
|
|
20
40
|
it('matches HyperlaneJsonRpcProvider empty responses', () => {
|
|
21
41
|
expect(isMissingSelectorCallException(new Error('Invalid response from provider'))).to.equal(true);
|
|
22
42
|
expect(isMissingSelectorRevert(new Error('Invalid response from provider'))).to.equal(false);
|
|
@@ -28,6 +48,449 @@ describe('contract utils', () => {
|
|
|
28
48
|
expect(isMissingSelectorCallException(new Error('request failed with data="0x"'))).to.equal(false);
|
|
29
49
|
});
|
|
30
50
|
});
|
|
51
|
+
describe('isMissingSelectorRevert LSP17 NoExtensionFoundForFunctionSelector', () => {
|
|
52
|
+
const NO_EXTENSION_SELECTOR = '0xbb370b2b';
|
|
53
|
+
const FEE_HOOK_SELECTOR = 'f11f4461';
|
|
54
|
+
const lsp17Data = `${NO_EXTENSION_SELECTOR}${FEE_HOOK_SELECTOR}${'00'.repeat(28)}`;
|
|
55
|
+
const cases = [
|
|
56
|
+
{
|
|
57
|
+
name: 'an embedded selector equal to the called selector',
|
|
58
|
+
data: lsp17Data,
|
|
59
|
+
callData: `0x${FEE_HOOK_SELECTOR}`,
|
|
60
|
+
expected: true,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'an embedded selector equal to the called selector, case-insensitively',
|
|
64
|
+
data: lsp17Data,
|
|
65
|
+
callData: `0x${FEE_HOOK_SELECTOR.toUpperCase()}${'00'.repeat(32)}`,
|
|
66
|
+
expected: true,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'an embedded selector of another call than the one made',
|
|
70
|
+
data: lsp17Data,
|
|
71
|
+
callData: '0x46904840',
|
|
72
|
+
expected: false,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'a left-aligned bytes4 argument',
|
|
76
|
+
data: `${NO_EXTENSION_SELECTOR}${FEE_HOOK_SELECTOR}${'00'.repeat(28)}`,
|
|
77
|
+
expected: true,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'a different 4+32 byte custom error',
|
|
81
|
+
data: `0xdeadbeef${FEE_HOOK_SELECTOR}${'00'.repeat(28)}`,
|
|
82
|
+
expected: false,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: 'the error selector without the argument',
|
|
86
|
+
data: NO_EXTENSION_SELECTOR,
|
|
87
|
+
expected: false,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: 'the error selector with a truncated argument',
|
|
91
|
+
data: `${NO_EXTENSION_SELECTOR}${FEE_HOOK_SELECTOR}${'00'.repeat(27)}`,
|
|
92
|
+
expected: false,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'the error selector with trailing bytes',
|
|
96
|
+
data: `${NO_EXTENSION_SELECTOR}${FEE_HOOK_SELECTOR}${'00'.repeat(29)}`,
|
|
97
|
+
expected: false,
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
for (const c of cases) {
|
|
101
|
+
it(`returns ${c.expected} for ${c.name}`, () => {
|
|
102
|
+
const error = Object.assign(new Error('call revert exception'), {
|
|
103
|
+
code: 'CALL_EXCEPTION',
|
|
104
|
+
data: c.data,
|
|
105
|
+
});
|
|
106
|
+
if (c.callData !== undefined) {
|
|
107
|
+
Object.assign(error, { transaction: { data: c.callData } });
|
|
108
|
+
}
|
|
109
|
+
expect(isMissingSelectorRevert(error)).to.equal(c.expected);
|
|
110
|
+
expect(isMissingSelectorRevert(wrappedError(error))).to.equal(c.expected);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
describe('isMissingSelectorCallException with nested ethers errors', () => {
|
|
115
|
+
const cases = [
|
|
116
|
+
{
|
|
117
|
+
name: 'JSON-RPC code 3 revert without data',
|
|
118
|
+
nested: Object.assign(new Error('execution reverted'), {
|
|
119
|
+
code: 3,
|
|
120
|
+
data: '0x',
|
|
121
|
+
}),
|
|
122
|
+
expected: true,
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: 'JSON-RPC -32000 execution reverted',
|
|
126
|
+
nested: Object.assign(new Error('execution reverted'), {
|
|
127
|
+
code: -32000,
|
|
128
|
+
}),
|
|
129
|
+
expected: true,
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: 'hardhat VM exception revert',
|
|
133
|
+
nested: Object.assign(new Error("VM Exception while processing transaction: reverted with reason string ''"), { code: -32603 }),
|
|
134
|
+
expected: true,
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: 'revert JSON body inside an HTTP error response (transient, like SmartProvider)',
|
|
138
|
+
nested: {
|
|
139
|
+
code: 'SERVER_ERROR',
|
|
140
|
+
message: 'processing response error',
|
|
141
|
+
body: '{"jsonrpc":"2.0","id":1,"error":{"code":3,"message":"x"}}',
|
|
142
|
+
},
|
|
143
|
+
expected: false,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
name: 'HTTP 500 server error',
|
|
147
|
+
nested: {
|
|
148
|
+
code: 'SERVER_ERROR',
|
|
149
|
+
message: 'processing response error (body="Internal Server Error", responseText="Internal Server Error", requestBody="{}", requestMethod="POST", url="http://x", code=SERVER_ERROR, version=web/5.8.0)',
|
|
150
|
+
body: 'Internal Server Error',
|
|
151
|
+
status: 500,
|
|
152
|
+
},
|
|
153
|
+
expected: false,
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: 'JSON-RPC -32000 header not found',
|
|
157
|
+
nested: Object.assign(new Error('header not found'), { code: -32000 }),
|
|
158
|
+
expected: false,
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: 'connection reset (missing response)',
|
|
162
|
+
nested: {
|
|
163
|
+
code: 'SERVER_ERROR',
|
|
164
|
+
message: 'missing response (requestBody="{}", requestMethod="POST", serverError={"code":"ECONNRESET"}, url="http://x", code=SERVER_ERROR, version=web/5.8.0)',
|
|
165
|
+
},
|
|
166
|
+
expected: false,
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
name: 'request timeout',
|
|
170
|
+
nested: {
|
|
171
|
+
code: 'TIMEOUT',
|
|
172
|
+
message: 'timeout (requestBody="{}", timeout=120000)',
|
|
173
|
+
},
|
|
174
|
+
expected: false,
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
name: 'HTTP 429 rate limit',
|
|
178
|
+
nested: {
|
|
179
|
+
code: 'SERVER_ERROR',
|
|
180
|
+
message: 'bad response (status=429, headers={}, body="Too Many Requests")',
|
|
181
|
+
status: 429,
|
|
182
|
+
},
|
|
183
|
+
expected: false,
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: 'JSON-RPC rate limit error',
|
|
187
|
+
nested: Object.assign(new Error('rate limit exceeded'), {
|
|
188
|
+
code: -32005,
|
|
189
|
+
}),
|
|
190
|
+
expected: false,
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
name: 'nested data 0x with header not found',
|
|
194
|
+
nested: Object.assign(new Error('header not found'), {
|
|
195
|
+
code: -32000,
|
|
196
|
+
data: '0x',
|
|
197
|
+
}),
|
|
198
|
+
expected: false,
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
name: 'nested empty-string data with HTTP 429',
|
|
202
|
+
nested: {
|
|
203
|
+
code: 'SERVER_ERROR',
|
|
204
|
+
message: 'bad response (status=429, headers={}, body="Too Many Requests")',
|
|
205
|
+
status: 429,
|
|
206
|
+
data: '',
|
|
207
|
+
},
|
|
208
|
+
expected: false,
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
name: 'nested data 0x with an HTTP 500 body',
|
|
212
|
+
nested: {
|
|
213
|
+
code: 'SERVER_ERROR',
|
|
214
|
+
message: 'processing response error',
|
|
215
|
+
body: 'Internal Server Error',
|
|
216
|
+
status: 500,
|
|
217
|
+
data: '0x',
|
|
218
|
+
},
|
|
219
|
+
expected: false,
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
name: 'nested data 0x with JSON-RPC code 3',
|
|
223
|
+
nested: Object.assign(new Error('execution reverted'), {
|
|
224
|
+
code: 3,
|
|
225
|
+
data: '0x',
|
|
226
|
+
}),
|
|
227
|
+
expected: true,
|
|
228
|
+
},
|
|
229
|
+
// Captured from https://rpc.fuse.io (Nethermind 1.29.0) for an unknown
|
|
230
|
+
// selector sent to a deployed Mailbox.
|
|
231
|
+
{
|
|
232
|
+
name: 'Nethermind -32015 revert with a supplied-gas data string',
|
|
233
|
+
nested: {
|
|
234
|
+
error: {
|
|
235
|
+
code: -32015,
|
|
236
|
+
message: 'VM execution error.',
|
|
237
|
+
data: 'err: revert (supplied gas 20000000)',
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
expected: true,
|
|
241
|
+
},
|
|
242
|
+
// Captured from https://fuse.liquify.com (Nethermind 1.29.0).
|
|
243
|
+
{
|
|
244
|
+
name: 'Nethermind -32015 revert with a bare revert data string',
|
|
245
|
+
nested: {
|
|
246
|
+
error: {
|
|
247
|
+
code: -32015,
|
|
248
|
+
message: 'VM execution error.',
|
|
249
|
+
data: 'revert',
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
expected: true,
|
|
253
|
+
},
|
|
254
|
+
// Captured from https://rpc.fuse.io and https://fuse.liquify.com.
|
|
255
|
+
{
|
|
256
|
+
name: 'Nethermind -32015 out of gas',
|
|
257
|
+
nested: {
|
|
258
|
+
error: {
|
|
259
|
+
code: -32015,
|
|
260
|
+
message: 'VM execution error.',
|
|
261
|
+
data: 'err: OutOfGas (supplied gas 30000)',
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
expected: false,
|
|
265
|
+
},
|
|
266
|
+
// Captured from https://fuse.liquify.com for eth_call against code 0xfe.
|
|
267
|
+
{
|
|
268
|
+
name: 'Nethermind -32015 invalid instruction',
|
|
269
|
+
nested: {
|
|
270
|
+
error: {
|
|
271
|
+
code: -32015,
|
|
272
|
+
message: 'VM execution error.',
|
|
273
|
+
data: 'err: BadInstruction (supplied gas 20000000)',
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
expected: false,
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
name: 'revert data string under a non-Nethermind code',
|
|
280
|
+
nested: {
|
|
281
|
+
error: { code: -32005, message: 'rate limit', data: 'revert' },
|
|
282
|
+
},
|
|
283
|
+
expected: false,
|
|
284
|
+
},
|
|
285
|
+
// Captured from https://ethereum.publicnode.com (Geth 1.17.1) for eth_call
|
|
286
|
+
// against code 0xfe and against an infinite loop with a 30000 gas cap.
|
|
287
|
+
{
|
|
288
|
+
name: 'Geth invalid opcode',
|
|
289
|
+
nested: Object.assign(new Error('invalid opcode: INVALID'), {
|
|
290
|
+
code: -32000,
|
|
291
|
+
}),
|
|
292
|
+
expected: false,
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
name: 'Geth out of gas',
|
|
296
|
+
nested: Object.assign(new Error('out of gas'), { code: -32000 }),
|
|
297
|
+
expected: false,
|
|
298
|
+
},
|
|
299
|
+
];
|
|
300
|
+
for (const c of cases) {
|
|
301
|
+
it(`returns ${c.expected} for ${c.name}`, () => {
|
|
302
|
+
const error = ethersCallExceptionWithNestedError(c.nested);
|
|
303
|
+
expect(isMissingSelectorCallException(error)).to.equal(c.expected);
|
|
304
|
+
expect(isMissingSelectorRevert(error)).to.equal(c.expected);
|
|
305
|
+
expect(isMissingSelectorCallException(wrappedError(error))).to.equal(c.expected);
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
describe('isMissingSelectorRevert empty-data signals', () => {
|
|
310
|
+
const cases = [
|
|
311
|
+
{
|
|
312
|
+
name: 'nested error reporting data 0x without top-level data',
|
|
313
|
+
error: () => ({ code: 'CALL_EXCEPTION', error: { data: '0x' } }),
|
|
314
|
+
expected: true,
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
name: 'nested error data 0x alongside a non-revert message and no code',
|
|
318
|
+
error: () => ethersCallExceptionWithNestedError(Object.assign(new Error('boom'), { data: '0x' })),
|
|
319
|
+
expected: false,
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
name: 'nested transport message ECONNRESET with data 0x',
|
|
323
|
+
error: () => ({
|
|
324
|
+
code: 'CALL_EXCEPTION',
|
|
325
|
+
error: { message: 'ECONNRESET', data: '0x' },
|
|
326
|
+
}),
|
|
327
|
+
expected: false,
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
name: 'nested transport message socket hang up with empty-string data',
|
|
331
|
+
error: () => ({
|
|
332
|
+
code: 'CALL_EXCEPTION',
|
|
333
|
+
error: { message: 'socket hang up', data: '' },
|
|
334
|
+
}),
|
|
335
|
+
expected: false,
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
name: 'nested error reporting empty-string data',
|
|
339
|
+
error: () => ({ code: 'CALL_EXCEPTION', error: { data: '' } }),
|
|
340
|
+
expected: true,
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
name: 'nested error data 0x with SERVER_ERROR and no message',
|
|
344
|
+
error: () => ({
|
|
345
|
+
code: 'CALL_EXCEPTION',
|
|
346
|
+
error: { code: 'SERVER_ERROR', data: '0x' },
|
|
347
|
+
}),
|
|
348
|
+
expected: false,
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
name: 'nested error data "" with HTTP 429 status',
|
|
352
|
+
error: () => ({
|
|
353
|
+
code: 'CALL_EXCEPTION',
|
|
354
|
+
error: { code: 'SERVER_ERROR', status: 429, data: '' },
|
|
355
|
+
}),
|
|
356
|
+
expected: false,
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
name: 'top-level empty-string data without nested error',
|
|
360
|
+
error: () => ({ code: 'CALL_EXCEPTION', data: '' }),
|
|
361
|
+
expected: true,
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
name: 'top-level empty-string data with nested transport error',
|
|
365
|
+
error: () => ({
|
|
366
|
+
code: 'CALL_EXCEPTION',
|
|
367
|
+
data: '',
|
|
368
|
+
error: { code: 'SERVER_ERROR', status: 500 },
|
|
369
|
+
}),
|
|
370
|
+
expected: false,
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
name: 'message-only data="0x" without nested error',
|
|
374
|
+
error: () => ({
|
|
375
|
+
code: 'CALL_EXCEPTION',
|
|
376
|
+
message: 'call reverted with data="0x"',
|
|
377
|
+
}),
|
|
378
|
+
expected: true,
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
name: 'message data="0x" with nested transport error',
|
|
382
|
+
error: () => ethersCallExceptionWithNestedError(Object.assign(new Error('header not found'), { code: -32000 })),
|
|
383
|
+
expected: false,
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
name: 'message data="0x" with nested HTTP 500 and no top-level data',
|
|
387
|
+
error: () => ({
|
|
388
|
+
code: 'CALL_EXCEPTION',
|
|
389
|
+
message: 'call reverted with data="0x"',
|
|
390
|
+
error: { code: 'SERVER_ERROR', status: 500 },
|
|
391
|
+
}),
|
|
392
|
+
expected: false,
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
name: 'non-empty revert data',
|
|
396
|
+
error: () => ({ code: 'CALL_EXCEPTION', data: '0x08c379a0abcd' }),
|
|
397
|
+
expected: false,
|
|
398
|
+
},
|
|
399
|
+
];
|
|
400
|
+
for (const c of cases) {
|
|
401
|
+
it(`returns ${c.expected} for ${c.name}`, () => {
|
|
402
|
+
expect(isMissingSelectorRevert(c.error())).to.equal(c.expected);
|
|
403
|
+
expect(isMissingSelectorCallException(c.error())).to.equal(c.expected);
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
describe('isReturnDataEmpty', () => {
|
|
408
|
+
const cases = [
|
|
409
|
+
{ name: '0x', data: '0x', expected: true },
|
|
410
|
+
{ name: 'empty string', data: '', expected: true },
|
|
411
|
+
{ name: '0x0', data: '0x0', expected: false },
|
|
412
|
+
{ name: 'undefined', data: undefined, expected: false },
|
|
413
|
+
{ name: 'null', data: null, expected: false },
|
|
414
|
+
{ name: 'a number', data: 0, expected: false },
|
|
415
|
+
{ name: 'non-empty hex', data: '0x08c379a0', expected: false },
|
|
416
|
+
];
|
|
417
|
+
for (const c of cases) {
|
|
418
|
+
it(`returns ${c.expected} for ${c.name}`, () => {
|
|
419
|
+
expect(isReturnDataEmpty(c.data)).to.equal(c.expected);
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
describe('isPanicRevert', () => {
|
|
424
|
+
const cases = [
|
|
425
|
+
{ name: 'panic revert', error: panicRevertError, expected: true },
|
|
426
|
+
{
|
|
427
|
+
name: 'wrapped panic revert',
|
|
428
|
+
error: () => wrappedError(panicRevertError()),
|
|
429
|
+
expected: true,
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
name: 'panic data on nested error.data',
|
|
433
|
+
error: () => Object.assign(new Error('call revert exception'), {
|
|
434
|
+
code: 'CALL_EXCEPTION',
|
|
435
|
+
error: { data: `0x4e487b71${'0'.repeat(62)}11` },
|
|
436
|
+
}),
|
|
437
|
+
expected: true,
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
name: 'Error(string) revert',
|
|
441
|
+
error: errorStringRevertError,
|
|
442
|
+
expected: false,
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
name: 'LSP17 no-extension revert',
|
|
446
|
+
error: lsp17NoExtensionError,
|
|
447
|
+
expected: false,
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
name: 'empty data (missing selector)',
|
|
451
|
+
error: missingSelectorError,
|
|
452
|
+
expected: false,
|
|
453
|
+
},
|
|
454
|
+
{ name: 'network error', error: networkError, expected: false },
|
|
455
|
+
{
|
|
456
|
+
name: 'empty provider response',
|
|
457
|
+
error: () => new Error('Invalid response from provider'),
|
|
458
|
+
expected: false,
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
name: 'data shorter than the Panic selector',
|
|
462
|
+
error: () => Object.assign(new Error('call revert exception'), {
|
|
463
|
+
code: 'CALL_EXCEPTION',
|
|
464
|
+
data: '0x4e487b',
|
|
465
|
+
}),
|
|
466
|
+
expected: false,
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
name: 'non-hex data',
|
|
470
|
+
error: () => Object.assign(new Error('call revert exception'), {
|
|
471
|
+
code: 'CALL_EXCEPTION',
|
|
472
|
+
data: '0x4e487b71zz',
|
|
473
|
+
}),
|
|
474
|
+
expected: false,
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
name: 'call exception without data',
|
|
478
|
+
error: () => Object.assign(new Error('call revert exception'), {
|
|
479
|
+
code: 'CALL_EXCEPTION',
|
|
480
|
+
}),
|
|
481
|
+
expected: false,
|
|
482
|
+
},
|
|
483
|
+
];
|
|
484
|
+
for (const c of cases) {
|
|
485
|
+
it(`returns ${c.expected} for ${c.name}`, () => {
|
|
486
|
+
expect(isPanicRevert(c.error())).to.equal(c.expected);
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
it('is not a missing selector for data-carrying reverts', () => {
|
|
490
|
+
expect(isMissingSelectorRevert(panicRevertError())).to.equal(false);
|
|
491
|
+
expect(isMissingSelectorCallException(panicRevertError())).to.equal(false);
|
|
492
|
+
});
|
|
493
|
+
});
|
|
31
494
|
describe('fetchPackageVersion', () => {
|
|
32
495
|
let sandbox;
|
|
33
496
|
beforeEach(() => {
|