@w3lib/evm-batch-call 0.1.0
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/LICENSE +24 -0
- package/README.md +257 -0
- package/contracts/DeploylessMulticall3.sol +66 -0
- package/dist/aggregate.d.ts +27 -0
- package/dist/aggregate.d.ts.map +1 -0
- package/dist/aggregate.js +198 -0
- package/dist/aggregate.js.map +1 -0
- package/dist/batcher.d.ts +21 -0
- package/dist/batcher.d.ts.map +1 -0
- package/dist/batcher.js +100 -0
- package/dist/batcher.js.map +1 -0
- package/dist/call.d.ts +53 -0
- package/dist/call.d.ts.map +1 -0
- package/dist/call.js +82 -0
- package/dist/call.js.map +1 -0
- package/dist/contract.d.ts +45 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +107 -0
- package/dist/contract.js.map +1 -0
- package/dist/deployless.d.ts +3 -0
- package/dist/deployless.d.ts.map +1 -0
- package/dist/deployless.js +4 -0
- package/dist/deployless.js.map +1 -0
- package/dist/erc20.d.ts +27 -0
- package/dist/erc20.d.ts.map +1 -0
- package/dist/erc20.js +17 -0
- package/dist/erc20.js.map +1 -0
- package/dist/errors.d.ts +20 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +56 -0
- package/dist/errors.js.map +1 -0
- package/dist/fallback.d.ts +23 -0
- package/dist/fallback.d.ts.map +1 -0
- package/dist/fallback.js +86 -0
- package/dist/fallback.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/multicall.d.ts +13 -0
- package/dist/multicall.d.ts.map +1 -0
- package/dist/multicall.js +51 -0
- package/dist/multicall.js.map +1 -0
- package/dist/provider.d.ts +137 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +269 -0
- package/dist/provider.js.map +1 -0
- package/dist/rpcNodes.d.ts +5 -0
- package/dist/rpcNodes.d.ts.map +1 -0
- package/dist/rpcNodes.js +193 -0
- package/dist/rpcNodes.js.map +1 -0
- package/dist/source.d.ts +23 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +56 -0
- package/dist/source.js.map +1 -0
- package/dist/tron.d.ts +62 -0
- package/dist/tron.d.ts.map +1 -0
- package/dist/tron.js +223 -0
- package/dist/tron.js.map +1 -0
- package/package.json +63 -0
- package/src/aggregate.ts +254 -0
- package/src/batcher.ts +126 -0
- package/src/call.ts +144 -0
- package/src/contract.ts +145 -0
- package/src/deployless.ts +5 -0
- package/src/erc20.ts +43 -0
- package/src/errors.ts +66 -0
- package/src/fallback.ts +105 -0
- package/src/index.ts +31 -0
- package/src/multicall.ts +60 -0
- package/src/provider.ts +352 -0
- package/src/rpcNodes.ts +194 -0
- package/src/source.ts +84 -0
- package/src/tron.ts +281 -0
package/dist/batcher.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { decodeCall, encodeCall } from './call.js';
|
|
2
|
+
import { CallFailedError } from './errors.js';
|
|
3
|
+
/**
|
|
4
|
+
* DataLoader 式的自动合并:各处独立调用 `provider.call(x)`,
|
|
5
|
+
* 落在同一个收集窗口、且 blockTag/from 相同的调用会合成一次 aggregate3。
|
|
6
|
+
* 相同的 (target, callData) 只请求一次。
|
|
7
|
+
*/
|
|
8
|
+
export class Batcher {
|
|
9
|
+
#run;
|
|
10
|
+
#wait;
|
|
11
|
+
#maxSize;
|
|
12
|
+
#queues = new Map();
|
|
13
|
+
constructor(run, options = {}) {
|
|
14
|
+
this.#run = run;
|
|
15
|
+
this.#wait = options.wait ?? 0;
|
|
16
|
+
this.#maxSize = Math.max(1, options.maxSize ?? 500);
|
|
17
|
+
}
|
|
18
|
+
load(call, overrides = {}) {
|
|
19
|
+
const key = `${String(overrides.blockTag ?? 'latest')}|${overrides.from ?? ''}`;
|
|
20
|
+
let queue = this.#queues.get(key);
|
|
21
|
+
if (!queue) {
|
|
22
|
+
queue = { overrides, entries: [], timer: null };
|
|
23
|
+
this.#queues.set(key, queue);
|
|
24
|
+
}
|
|
25
|
+
const promise = new Promise((resolve, reject) => {
|
|
26
|
+
queue.entries.push({ call, resolve: resolve, reject });
|
|
27
|
+
});
|
|
28
|
+
if (queue.entries.length >= this.#maxSize) {
|
|
29
|
+
this.#flush(key);
|
|
30
|
+
}
|
|
31
|
+
else if (!queue.timer) {
|
|
32
|
+
queue.timer = setTimeout(() => this.#flush(key), this.#wait);
|
|
33
|
+
}
|
|
34
|
+
return promise;
|
|
35
|
+
}
|
|
36
|
+
#flush(key) {
|
|
37
|
+
const queue = this.#queues.get(key);
|
|
38
|
+
if (!queue) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
this.#queues.delete(key);
|
|
42
|
+
if (queue.timer) {
|
|
43
|
+
clearTimeout(queue.timer);
|
|
44
|
+
}
|
|
45
|
+
void this.#dispatch(queue);
|
|
46
|
+
}
|
|
47
|
+
async #dispatch({ entries, overrides }) {
|
|
48
|
+
const requests = [];
|
|
49
|
+
const indexByKey = new Map();
|
|
50
|
+
const requestIndex = [];
|
|
51
|
+
for (const entry of entries) {
|
|
52
|
+
let request;
|
|
53
|
+
try {
|
|
54
|
+
request = encodeCall(entry.call, true);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
entry.reject(err);
|
|
58
|
+
requestIndex.push(-1);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const dedupeKey = `${request.ethBalanceOf ? 'eth' : request.target.toLowerCase()}|${request.callData}`;
|
|
62
|
+
let index = indexByKey.get(dedupeKey);
|
|
63
|
+
if (index === undefined) {
|
|
64
|
+
index = requests.push(request) - 1;
|
|
65
|
+
indexByKey.set(dedupeKey, index);
|
|
66
|
+
}
|
|
67
|
+
requestIndex.push(index);
|
|
68
|
+
}
|
|
69
|
+
if (requests.length === 0) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
let results;
|
|
73
|
+
try {
|
|
74
|
+
results = await this.#run(requests, overrides);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
entries.forEach((entry, i) => requestIndex[i] !== -1 && entry.reject(err));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
entries.forEach((entry, i) => {
|
|
81
|
+
const index = requestIndex[i];
|
|
82
|
+
if (index === -1) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const result = results[index];
|
|
86
|
+
if (!result.success) {
|
|
87
|
+
entry.reject(new CallFailedError(entry.call, result.returnData));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
entry.resolve(decodeCall(entry.call, result.returnData));
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
// 解码失败:多半是目标地址没有合约(返回空数据)
|
|
95
|
+
entry.reject(new CallFailedError(entry.call, result.returnData, err));
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=batcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batcher.js","sourceRoot":"","sources":["../src/batcher.ts"],"names":[],"mappings":"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"}
|
package/dist/call.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type JsonFragmentType } from 'ethers';
|
|
2
|
+
export type Params = any[];
|
|
3
|
+
/**
|
|
4
|
+
* 一次只读调用的描述。结构与 ethcall 的 Call 保持一致,手工构造的 Call 也能直接用。
|
|
5
|
+
* 类型参数 T 是解码后的结果类型(仅用于类型推断,运行时不存在)。
|
|
6
|
+
*/
|
|
7
|
+
export interface Call<T = any> {
|
|
8
|
+
contract: {
|
|
9
|
+
address: string;
|
|
10
|
+
};
|
|
11
|
+
name: string;
|
|
12
|
+
inputs: readonly JsonFragmentType[];
|
|
13
|
+
outputs: readonly JsonFragmentType[];
|
|
14
|
+
params: Params;
|
|
15
|
+
/**
|
|
16
|
+
* 调用自带的 overrides(`contract.method(...args, overrides)`)。
|
|
17
|
+
* blockTag / from 对 await 绑定 Call、provider.call、staticCall 生效;value / gasLimit 只对预执行生效。
|
|
18
|
+
* 传给 all / tryAll 时以批次的 overrides 为准(一批只能在同一个区块、同一个 from 下执行)。
|
|
19
|
+
*/
|
|
20
|
+
overrides?: {
|
|
21
|
+
blockTag?: number | bigint | string;
|
|
22
|
+
from?: string;
|
|
23
|
+
value?: bigint | string | number;
|
|
24
|
+
gasLimit?: bigint | string | number;
|
|
25
|
+
};
|
|
26
|
+
/** 内部标记:原生币余额查询(见 Provider.getEthBalance) */
|
|
27
|
+
kind?: 'ethBalance';
|
|
28
|
+
/** 仅用于类型推断 */
|
|
29
|
+
readonly __result?: T;
|
|
30
|
+
}
|
|
31
|
+
/** 绑定了 Provider 的 Call:可以直接 await(自动合并成 multicall),也可以照常传给 all / tryAll */
|
|
32
|
+
export type BoundCall<T = any> = Call<T> & PromiseLike<T>;
|
|
33
|
+
export interface FailableCall extends Call {
|
|
34
|
+
canFail: boolean;
|
|
35
|
+
}
|
|
36
|
+
/** multicall 返回的单条原始结果 */
|
|
37
|
+
export interface RawResult {
|
|
38
|
+
success: boolean;
|
|
39
|
+
returnData: string;
|
|
40
|
+
}
|
|
41
|
+
/** 发给 aggregate3 前的单条请求 */
|
|
42
|
+
export interface CallRequest {
|
|
43
|
+
target: string;
|
|
44
|
+
allowFailure: boolean;
|
|
45
|
+
callData: string;
|
|
46
|
+
/** 原生币余额查询:有 multicall 合约时走 getEthBalance,deployless 时改走 eth_getBalance */
|
|
47
|
+
ethBalanceOf?: string;
|
|
48
|
+
}
|
|
49
|
+
export declare function encodeCall(call: Call, allowFailure: boolean): CallRequest;
|
|
50
|
+
/** 单返回值直接返回该值,多返回值返回 ethers Result(可按下标或名字取) */
|
|
51
|
+
export declare function decodeCall<T>(call: Call, returnData: string): T;
|
|
52
|
+
export declare function encodeUint256(value: bigint): string;
|
|
53
|
+
//# sourceMappingURL=call.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call.d.ts","sourceRoot":"","sources":["../src/call.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkE,KAAK,gBAAgB,EAA+B,MAAM,QAAQ,CAAA;AAK3I,MAAM,MAAM,MAAM,GAAG,GAAG,EAAE,CAAA;AAE1B;;;GAGG;AAEH,MAAM,WAAW,IAAI,CAAC,CAAC,GAAG,GAAG;IAC3B,QAAQ,EAAE;QACR,OAAO,EAAE,MAAM,CAAA;KAChB,CAAA;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,SAAS,gBAAgB,EAAE,CAAA;IACnC,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAA;IACpC,MAAM,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,SAAS,CAAC,EAAE;QACV,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;QACnC,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;QAChC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;KACpC,CAAA;IACD,6CAA6C;IAC7C,IAAI,CAAC,EAAE,YAAY,CAAA;IACnB,cAAc;IACd,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;CACtB;AAED,2EAA2E;AAC3E,MAAM,MAAM,SAAS,CAAC,CAAC,GAAG,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;AAEzD,MAAM,WAAW,YAAa,SAAQ,IAAI;IACxC,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,0BAA0B;AAC1B,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,2BAA2B;AAC3B,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,OAAO,CAAA;IACrB,QAAQ,EAAE,MAAM,CAAA;IAChB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAgCD,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,GAAG,WAAW,CAUzE;AAkCD,gDAAgD;AAChD,wBAAgB,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,CAAC,CAK/D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEnD"}
|
package/dist/call.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { FunctionFragment, Interface, getAddress, toBeHex, zeroPadValue } from 'ethers';
|
|
2
|
+
import { isTronAddress, toEvmAddress } from './tron.js';
|
|
3
|
+
// inputs → name → outputs → Interface。同一份 ABI 生成的 Call 共享 inputs/outputs 数组(见 contract.ts 的 ABI 缓存),
|
|
4
|
+
// 按引用缓存,避免每条 Call 都重新解析;三者都参与 key,手工构造的 Call 共用数组时也不会串
|
|
5
|
+
const interfaceCache = new WeakMap();
|
|
6
|
+
function getInterface(call) {
|
|
7
|
+
let byName = interfaceCache.get(call.inputs);
|
|
8
|
+
if (!byName) {
|
|
9
|
+
byName = new Map();
|
|
10
|
+
interfaceCache.set(call.inputs, byName);
|
|
11
|
+
}
|
|
12
|
+
let byOutputs = byName.get(call.name);
|
|
13
|
+
if (!byOutputs) {
|
|
14
|
+
byOutputs = new WeakMap();
|
|
15
|
+
byName.set(call.name, byOutputs);
|
|
16
|
+
}
|
|
17
|
+
let iface = byOutputs.get(call.outputs);
|
|
18
|
+
if (!iface) {
|
|
19
|
+
const fragment = FunctionFragment.from({
|
|
20
|
+
type: 'function',
|
|
21
|
+
name: call.name,
|
|
22
|
+
inputs: call.inputs,
|
|
23
|
+
outputs: call.outputs,
|
|
24
|
+
stateMutability: 'view',
|
|
25
|
+
});
|
|
26
|
+
iface = new Interface([fragment]);
|
|
27
|
+
byOutputs.set(call.outputs, iface);
|
|
28
|
+
}
|
|
29
|
+
return iface;
|
|
30
|
+
}
|
|
31
|
+
export function encodeCall(call, allowFailure) {
|
|
32
|
+
const iface = getInterface(call);
|
|
33
|
+
const fragment = iface.fragments[0];
|
|
34
|
+
const params = normalizeAddresses(fragment, call.params);
|
|
35
|
+
return {
|
|
36
|
+
target: getAddress(toEvmAddress(call.contract.address)),
|
|
37
|
+
allowFailure,
|
|
38
|
+
callData: iface.encodeFunctionData(fragment, params),
|
|
39
|
+
ethBalanceOf: call.kind === 'ethBalance' ? toEvmAddress(params[0]) : undefined,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// Tron 的 T 开头地址在 ABI 里就是去掉 0x41 前缀的 20 字节,这里统一转成 0x 形式再编码
|
|
43
|
+
function normalizeAddresses(fragment, params) {
|
|
44
|
+
if (params.length !== fragment.inputs.length) {
|
|
45
|
+
// 参数个数不对交给 ethers 报错(overrides 不在 params 里,见 Provider.staticCall)
|
|
46
|
+
return params;
|
|
47
|
+
}
|
|
48
|
+
return fragment.inputs.map((input, i) => normalizeValue(input, params[i]));
|
|
49
|
+
}
|
|
50
|
+
// ethers 的 ParamType.walk 不接受对象形式的 struct,这里自己递归
|
|
51
|
+
function normalizeValue(type, value) {
|
|
52
|
+
if (type.baseType === 'address') {
|
|
53
|
+
return isTronAddress(value) ? toEvmAddress(value) : value;
|
|
54
|
+
}
|
|
55
|
+
if (type.isArray() && Array.isArray(value)) {
|
|
56
|
+
return value.map((v) => normalizeValue(type.arrayChildren, v));
|
|
57
|
+
}
|
|
58
|
+
if (type.isTuple() && value !== null && typeof value === 'object') {
|
|
59
|
+
if (Array.isArray(value)) {
|
|
60
|
+
return value.map((v, i) => (type.components[i] ? normalizeValue(type.components[i], v) : v));
|
|
61
|
+
}
|
|
62
|
+
const out = { ...value };
|
|
63
|
+
for (const component of type.components) {
|
|
64
|
+
if (component.name in out) {
|
|
65
|
+
out[component.name] = normalizeValue(component, out[component.name]);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
/** 单返回值直接返回该值,多返回值返回 ethers Result(可按下标或名字取) */
|
|
73
|
+
export function decodeCall(call, returnData) {
|
|
74
|
+
const iface = getInterface(call);
|
|
75
|
+
const fragment = iface.fragments[0];
|
|
76
|
+
const result = iface.decodeFunctionResult(fragment, returnData);
|
|
77
|
+
return (call.outputs.length === 1 ? result[0] : result);
|
|
78
|
+
}
|
|
79
|
+
export function encodeUint256(value) {
|
|
80
|
+
return zeroPadValue(toBeHex(value), 32);
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=call.js.map
|
package/dist/call.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call.js","sourceRoot":"","sources":["../src/call.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Interface, type InterfaceAbi } from 'ethers';
|
|
2
|
+
import type { BoundCall, Call, Params } from './call.js';
|
|
3
|
+
export type ContractAbi = InterfaceAbi | Interface;
|
|
4
|
+
/** 绑定后执行 Call 的对象(即 Provider),避免 contract ⇄ provider 循环依赖 */
|
|
5
|
+
export interface ContractRunner {
|
|
6
|
+
call(call: Call): Promise<any>;
|
|
7
|
+
staticCall(call: Call): Promise<any>;
|
|
8
|
+
}
|
|
9
|
+
/** 绑定合约上的方法:调用得到可 await 的 Call,`.staticCall(...)` 预执行 */
|
|
10
|
+
export interface BoundMethod<T = any> {
|
|
11
|
+
(...params: Params): BoundCall<T>;
|
|
12
|
+
/** 预执行(直接 eth_call,可在最后一个参数传 { from, value, gasLimit, blockTag }) */
|
|
13
|
+
staticCall(...params: Params): Promise<T>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 生成 Call 的合约包装,用法与 ethcall 的 Contract 一致:
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* const erc20 = new Contract(token, ['function balanceOf(address) view returns (uint256)'])
|
|
20
|
+
* erc20.balanceOf(user) // => Call
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* 传入 runner(或用 `provider.contract(address, abi)`)得到绑定合约:
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* const usdt = multi.contract(token, ERC20_ABI)
|
|
27
|
+
* const balance = await usdt.balanceOf(user) // 同一 tick 内的调用自动合并成一次 multicall
|
|
28
|
+
* const out = await router.swap.staticCall(params, { value, from }) // 预执行,写法同 ethers
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* 与 ethcall 的差异:
|
|
32
|
+
* - ABI 直接支持人类可读字符串 / JSON / ethers Interface,不需要再 abiToJson 一遍
|
|
33
|
+
* - 暴露 ABI 里的所有函数(不只 view/pure),可以用来模拟 nonpayable 的报价函数(如 Uniswap V3 Quoter)
|
|
34
|
+
* - 重载函数可以用完整签名取:`contract['getAmount(uint256,address)'](...)`
|
|
35
|
+
*/
|
|
36
|
+
export declare class Contract {
|
|
37
|
+
#private;
|
|
38
|
+
[method: string]: any;
|
|
39
|
+
constructor(address: string, abi: ContractAbi, runner?: ContractRunner);
|
|
40
|
+
get address(): string;
|
|
41
|
+
}
|
|
42
|
+
/** 给 Call 加上不可枚举的 then:第一次 await 时才发请求(交给 runner.call 自动合并),结果复用 */
|
|
43
|
+
export declare function bindCall<T>(call: Call<T>, runner: ContractRunner): BoundCall<T>;
|
|
44
|
+
export default Contract;
|
|
45
|
+
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAyB,KAAK,YAAY,EAA4C,MAAM,QAAQ,CAAA;AAEtH,OAAO,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAExD,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,SAAS,CAAA;AAElD,6DAA6D;AAC7D,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;IAC9B,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;CACrC;AAED,yDAAyD;AACzD,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,GAAG;IAClC,CAAC,GAAG,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,CAAA;IACjC,qEAAqE;IACrE,UAAU,CAAC,GAAG,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;CAC1C;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,QAAQ;;IAInB,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAAA;gBAET,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,cAAc;IActE,IAAI,OAAO,IAAI,MAAM,CAEpB;CAyBF;AAwCD,oEAAoE;AACpE,wBAAgB,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,cAAc,GAAG,SAAS,CAAC,CAAC,CAAC,CAU/E;AAMD,eAAe,QAAQ,CAAA"}
|
package/dist/contract.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Interface } from 'ethers';
|
|
2
|
+
/**
|
|
3
|
+
* 生成 Call 的合约包装,用法与 ethcall 的 Contract 一致:
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* const erc20 = new Contract(token, ['function balanceOf(address) view returns (uint256)'])
|
|
7
|
+
* erc20.balanceOf(user) // => Call
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* 传入 runner(或用 `provider.contract(address, abi)`)得到绑定合约:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* const usdt = multi.contract(token, ERC20_ABI)
|
|
14
|
+
* const balance = await usdt.balanceOf(user) // 同一 tick 内的调用自动合并成一次 multicall
|
|
15
|
+
* const out = await router.swap.staticCall(params, { value, from }) // 预执行,写法同 ethers
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* 与 ethcall 的差异:
|
|
19
|
+
* - ABI 直接支持人类可读字符串 / JSON / ethers Interface,不需要再 abiToJson 一遍
|
|
20
|
+
* - 暴露 ABI 里的所有函数(不只 view/pure),可以用来模拟 nonpayable 的报价函数(如 Uniswap V3 Quoter)
|
|
21
|
+
* - 重载函数可以用完整签名取:`contract['getAmount(uint256,address)'](...)`
|
|
22
|
+
*/
|
|
23
|
+
export class Contract {
|
|
24
|
+
#address;
|
|
25
|
+
#runner;
|
|
26
|
+
constructor(address, abi, runner) {
|
|
27
|
+
this.#address = address;
|
|
28
|
+
this.#runner = runner;
|
|
29
|
+
for (const fn of parseAbi(abi)) {
|
|
30
|
+
const method = this.#makeMethod(fn);
|
|
31
|
+
for (const key of fn.keys) {
|
|
32
|
+
if (key in this) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
Object.defineProperty(this, key, { enumerable: true, value: method, writable: false });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
get address() {
|
|
40
|
+
return this.#address;
|
|
41
|
+
}
|
|
42
|
+
#makeMethod({ name, inputs, outputs }) {
|
|
43
|
+
const address = this.#address;
|
|
44
|
+
const runner = this.#runner;
|
|
45
|
+
const build = (params) => {
|
|
46
|
+
// 与 ethers 一致:多出来的最后一个参数是 overrides,如 router.swap(params, { value })
|
|
47
|
+
if (params.length === inputs.length + 1 && isPlainObject(params[params.length - 1])) {
|
|
48
|
+
const overrides = params[params.length - 1];
|
|
49
|
+
return { contract: { address }, name, inputs, outputs, params: params.slice(0, -1), overrides };
|
|
50
|
+
}
|
|
51
|
+
return { contract: { address }, name, inputs, outputs, params };
|
|
52
|
+
};
|
|
53
|
+
if (!runner) {
|
|
54
|
+
return (...params) => build(params);
|
|
55
|
+
}
|
|
56
|
+
const method = (...params) => bindCall(build(params), runner);
|
|
57
|
+
Object.defineProperty(method, 'staticCall', {
|
|
58
|
+
value: (...params) => runner.staticCall(build(params)),
|
|
59
|
+
});
|
|
60
|
+
return method;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// 按 ABI 对象(数组 / Interface)缓存解析结果:循环里 new Contract(token, ERC20ABI) 只解析一次,
|
|
64
|
+
// 生成的 Call 共享 inputs/outputs 数组,call.ts 的 Interface 缓存才能命中
|
|
65
|
+
const abiCache = new WeakMap();
|
|
66
|
+
function parseAbi(abi) {
|
|
67
|
+
const cacheable = typeof abi === 'object' && abi !== null;
|
|
68
|
+
const cached = cacheable ? abiCache.get(abi) : undefined;
|
|
69
|
+
if (cached) {
|
|
70
|
+
return cached;
|
|
71
|
+
}
|
|
72
|
+
const iface = abi instanceof Interface ? abi : new Interface(abi);
|
|
73
|
+
const fragments = [];
|
|
74
|
+
iface.forEachFunction((fn) => fragments.push(fn));
|
|
75
|
+
const counts = new Map();
|
|
76
|
+
fragments.forEach((fn) => counts.set(fn.name, (counts.get(fn.name) ?? 0) + 1));
|
|
77
|
+
const parsed = fragments.map((fn) => {
|
|
78
|
+
const json = JSON.parse(fn.format('json'));
|
|
79
|
+
return {
|
|
80
|
+
keys: counts.get(fn.name) === 1 ? [fn.format('sighash'), fn.name] : [fn.format('sighash')],
|
|
81
|
+
name: fn.name,
|
|
82
|
+
inputs: json.inputs ?? [],
|
|
83
|
+
outputs: json.outputs ?? [],
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
if (cacheable) {
|
|
87
|
+
abiCache.set(abi, parsed);
|
|
88
|
+
}
|
|
89
|
+
return parsed;
|
|
90
|
+
}
|
|
91
|
+
/** 给 Call 加上不可枚举的 then:第一次 await 时才发请求(交给 runner.call 自动合并),结果复用 */
|
|
92
|
+
export function bindCall(call, runner) {
|
|
93
|
+
let promise;
|
|
94
|
+
Object.defineProperty(call, 'then', {
|
|
95
|
+
enumerable: false,
|
|
96
|
+
value: (onFulfilled, onRejected) => {
|
|
97
|
+
promise ??= runner.call(call);
|
|
98
|
+
return promise.then(onFulfilled, onRejected);
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
return call;
|
|
102
|
+
}
|
|
103
|
+
function isPlainObject(value) {
|
|
104
|
+
return value !== null && typeof value === 'object' && Object.getPrototypeOf(value) === Object.prototype;
|
|
105
|
+
}
|
|
106
|
+
export default Contract;
|
|
107
|
+
//# sourceMappingURL=contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** constructor((address target, bool allowFailure, bytes callData)[] calls) — 以 aggregate3 的返回格式带回结果 */
|
|
2
|
+
export declare const DEPLOYLESS_MULTICALL3_BYTECODE = "0x608060405260405161060f38038061060f8339810160408190526100229161036c565b80516000816001600160401b0381111561003e5761003e6102da565b60405190808252806020026020018201604052801561008457816020015b60408051808201909152600081526060602082015281526020019060019003908161005c5790505b50905060005b828110156102875760008482815181106100a6576100a6610507565b602090810291909101015160408101518151919250906001600160a01b031673ca11bde05977b3631167028862be2a173976ca111480156100e8575080516024145b801561010c5750631348c07360e21b6101008261051d565b6001600160e01b031916145b1561017e5760248101516040805180820182526001815290516001600160a01b039290921691602080830191610149918531910190815260200190565b60405160208183030381529060405281525085858151811061016d5761016d610507565b60200260200101819052505061027d565b60008083600001516001600160a01b03168360405161019d919061055b565b6000604051808303816000865af19150503d80600081146101da576040519150601f19603f3d011682016040523d82523d6000602084013e6101df565b606091505b5091509150811580156101f457508360200151155b156102465760405162461bcd60e51b815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064015b60405180910390fd5b604051806040016040528083151581526020018281525086868151811061026f5761026f610507565b602002602001018190525050505b505060010161008a565b5060008160405160200161029b9190610577565b6040516020818303038152906040529050616000815111156102d25781604051633649f05160e01b815260040161023d9190610577565b805160208201f35b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b0381118282101715610312576103126102da565b60405290565b604051601f8201601f191681016001600160401b0381118282101715610340576103406102da565b604052919050565b60005b8381101561036357818101518382015260200161034b565b50506000910152565b60006020828403121561037e57600080fd5b81516001600160401b0381111561039457600080fd5b8201601f810184136103a557600080fd5b80516001600160401b038111156103be576103be6102da565b8060051b6103ce60208201610318565b918252602081840181019290810190878411156103ea57600080fd5b6020850192505b838310156104fc5782516001600160401b0381111561040f57600080fd5b85016060818a03601f1901121561042557600080fd5b61042d6102f0565b60208201516001600160a01b038116811461044757600080fd5b81526040820151801515811461045c57600080fd5b602082015260608201516001600160401b0381111561047a57600080fd5b60208184010192505089601f83011261049257600080fd5b81516001600160401b038111156104ab576104ab6102da565b6104be601f8201601f1916602001610318565b8181528b60208386010111156104d357600080fd5b6104e4826020830160208701610348565b604083015250835250602092830192909101906103f1565b979650505050505050565b634e487b7160e01b600052603260045260246000fd5b805160208201516001600160e01b0319811691906004821015610554576001600160e01b0319600483900360031b81901b82161692505b5050919050565b6000825161056d818460208701610348565b9190910192915050565b6000602082016020835280845180835260408501915060408160051b86010192506020860160005b8281101561060257603f198786030184528151805115158652602081015190506040602087015280518060408801526105df816060890160208501610348565b601f01601f1916959095016060019450602093840193919091019060010161059f565b5092969550505050505056fe";
|
|
3
|
+
//# sourceMappingURL=deployless.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deployless.d.ts","sourceRoot":"","sources":["../src/deployless.ts"],"names":[],"mappings":"AAEA,wGAAwG;AACxG,eAAO,MAAM,8BAA8B,qiGACy/F,CAAA"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// 由 scripts/build-contracts.mjs 生成,不要手改。源码:contracts/DeploylessMulticall3.sol(solc 0.8.30+commit.73712a01.Emscripten.clang)
|
|
2
|
+
/** constructor((address target, bool allowFailure, bytes callData)[] calls) — 以 aggregate3 的返回格式带回结果 */
|
|
3
|
+
export const DEPLOYLESS_MULTICALL3_BYTECODE = '0x608060405260405161060f38038061060f8339810160408190526100229161036c565b80516000816001600160401b0381111561003e5761003e6102da565b60405190808252806020026020018201604052801561008457816020015b60408051808201909152600081526060602082015281526020019060019003908161005c5790505b50905060005b828110156102875760008482815181106100a6576100a6610507565b602090810291909101015160408101518151919250906001600160a01b031673ca11bde05977b3631167028862be2a173976ca111480156100e8575080516024145b801561010c5750631348c07360e21b6101008261051d565b6001600160e01b031916145b1561017e5760248101516040805180820182526001815290516001600160a01b039290921691602080830191610149918531910190815260200190565b60405160208183030381529060405281525085858151811061016d5761016d610507565b60200260200101819052505061027d565b60008083600001516001600160a01b03168360405161019d919061055b565b6000604051808303816000865af19150503d80600081146101da576040519150601f19603f3d011682016040523d82523d6000602084013e6101df565b606091505b5091509150811580156101f457508360200151155b156102465760405162461bcd60e51b815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064015b60405180910390fd5b604051806040016040528083151581526020018281525086868151811061026f5761026f610507565b602002602001018190525050505b505060010161008a565b5060008160405160200161029b9190610577565b6040516020818303038152906040529050616000815111156102d25781604051633649f05160e01b815260040161023d9190610577565b805160208201f35b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b0381118282101715610312576103126102da565b60405290565b604051601f8201601f191681016001600160401b0381118282101715610340576103406102da565b604052919050565b60005b8381101561036357818101518382015260200161034b565b50506000910152565b60006020828403121561037e57600080fd5b81516001600160401b0381111561039457600080fd5b8201601f810184136103a557600080fd5b80516001600160401b038111156103be576103be6102da565b8060051b6103ce60208201610318565b918252602081840181019290810190878411156103ea57600080fd5b6020850192505b838310156104fc5782516001600160401b0381111561040f57600080fd5b85016060818a03601f1901121561042557600080fd5b61042d6102f0565b60208201516001600160a01b038116811461044757600080fd5b81526040820151801515811461045c57600080fd5b602082015260608201516001600160401b0381111561047a57600080fd5b60208184010192505089601f83011261049257600080fd5b81516001600160401b038111156104ab576104ab6102da565b6104be601f8201601f1916602001610318565b8181528b60208386010111156104d357600080fd5b6104e4826020830160208701610348565b604083015250835250602092830192909101906103f1565b979650505050505050565b634e487b7160e01b600052603260045260246000fd5b805160208201516001600160e01b0319811691906004821015610554576001600160e01b0319600483900360031b81901b82161692505b5050919050565b6000825161056d818460208701610348565b9190910192915050565b6000602082016020835280845180835260408501915060408160051b86010192506020860160005b8281101561060257603f198786030184528151805115158652602081015190506040602087015280518060408801526105df816060890160208501610348565b601f01601f1916959095016060019450602093840193919091019060010161059f565b5092969550505050505056fe';
|
|
4
|
+
//# sourceMappingURL=deployless.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deployless.js","sourceRoot":"","sources":["../src/deployless.ts"],"names":[],"mappings":"AAAA,4HAA4H;AAE5H,wGAAwG;AACxG,MAAM,CAAC,MAAM,8BAA8B,GACzC,kiGAAkiG,CAAA"}
|
package/dist/erc20.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { BoundCall } from './call.js';
|
|
2
|
+
import type { BoundMethod, Contract } from './contract.js';
|
|
3
|
+
export declare const ERC20_ABI: readonly ["function name() view returns (string)", "function symbol() view returns (string)", "function decimals() view returns (uint8)", "function totalSupply() view returns (uint256)", "function balanceOf(address owner) view returns (uint256)", "function allowance(address owner, address spender) view returns (uint256)", "function approve(address spender, uint256 amount) returns (bool)", "function transfer(address to, uint256 amount) returns (bool)", "function transferFrom(address from, address to, uint256 amount) returns (bool)"];
|
|
4
|
+
type Method<Args extends unknown[], T> = ((...params: Args) => BoundCall<T>) & Pick<BoundMethod<T>, 'staticCall'>;
|
|
5
|
+
/** `provider.erc20(address)` 返回的绑定合约(带结果类型) */
|
|
6
|
+
export interface Erc20Contract extends Contract {
|
|
7
|
+
name: Method<[], string>;
|
|
8
|
+
symbol: Method<[], string>;
|
|
9
|
+
decimals: Method<[], bigint>;
|
|
10
|
+
totalSupply: Method<[], bigint>;
|
|
11
|
+
balanceOf: Method<[owner: string], bigint>;
|
|
12
|
+
allowance: Method<[owner: string, spender: string], bigint>;
|
|
13
|
+
approve: Method<[spender: string, amount: bigint | string | number], boolean>;
|
|
14
|
+
transfer: Method<[to: string, amount: bigint | string | number], boolean>;
|
|
15
|
+
transferFrom: Method<[from: string, to: string, amount: bigint | string | number], boolean>;
|
|
16
|
+
}
|
|
17
|
+
export interface TokenInfo {
|
|
18
|
+
address: string;
|
|
19
|
+
/** 读取失败(如 symbol 是 bytes32 的老代币)时为 null */
|
|
20
|
+
symbol: string | null;
|
|
21
|
+
name: string | null;
|
|
22
|
+
decimals: number;
|
|
23
|
+
}
|
|
24
|
+
/** 常见的主币占位地址:多数聚合器用 0xeeee…eeee,部分协议用零地址 */
|
|
25
|
+
export declare const DEFAULT_NATIVE_TOKENS: readonly string[];
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=erc20.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"erc20.d.ts","sourceRoot":"","sources":["../src/erc20.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAE1D,eAAO,MAAM,SAAS,2hBAUZ,CAAA;AAEV,KAAK,MAAM,CAAC,IAAI,SAAS,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,CAAA;AAEjH,+CAA+C;AAC/C,MAAM,WAAW,aAAc,SAAQ,QAAQ;IAC7C,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;IACxB,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;IAC1B,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;IAC5B,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;IAC/B,SAAS,EAAE,MAAM,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAA;IAC1C,SAAS,EAAE,MAAM,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAA;IAC3D,OAAO,EAAE,MAAM,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,EAAE,OAAO,CAAC,CAAA;IAC7E,QAAQ,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,EAAE,OAAO,CAAC,CAAA;IACzE,YAAY,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,EAAE,OAAO,CAAC,CAAA;CAC5F;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,2CAA2C;IAC3C,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,4CAA4C;AAC5C,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAGlD,CAAA"}
|
package/dist/erc20.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const ERC20_ABI = [
|
|
2
|
+
'function name() view returns (string)',
|
|
3
|
+
'function symbol() view returns (string)',
|
|
4
|
+
'function decimals() view returns (uint8)',
|
|
5
|
+
'function totalSupply() view returns (uint256)',
|
|
6
|
+
'function balanceOf(address owner) view returns (uint256)',
|
|
7
|
+
'function allowance(address owner, address spender) view returns (uint256)',
|
|
8
|
+
'function approve(address spender, uint256 amount) returns (bool)',
|
|
9
|
+
'function transfer(address to, uint256 amount) returns (bool)',
|
|
10
|
+
'function transferFrom(address from, address to, uint256 amount) returns (bool)',
|
|
11
|
+
];
|
|
12
|
+
/** 常见的主币占位地址:多数聚合器用 0xeeee…eeee,部分协议用零地址 */
|
|
13
|
+
export const DEFAULT_NATIVE_TOKENS = [
|
|
14
|
+
'0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
|
|
15
|
+
'0x0000000000000000000000000000000000000000',
|
|
16
|
+
];
|
|
17
|
+
//# sourceMappingURL=erc20.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"erc20.js","sourceRoot":"","sources":["../src/erc20.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,uCAAuC;IACvC,yCAAyC;IACzC,0CAA0C;IAC1C,+CAA+C;IAC/C,0DAA0D;IAC1D,2EAA2E;IAC3E,kEAAkE;IAClE,8DAA8D;IAC9D,gFAAgF;CACxE,CAAA;AAyBV,4CAA4C;AAC5C,MAAM,CAAC,MAAM,qBAAqB,GAAsB;IACtD,4CAA4C;IAC5C,4CAA4C;CAC7C,CAAA"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Call } from './call.js';
|
|
2
|
+
/** 自动合并模式下,单条调用失败时 reject 的错误 */
|
|
3
|
+
export declare class CallFailedError extends Error {
|
|
4
|
+
readonly call: Call;
|
|
5
|
+
/** revert 原始数据 */
|
|
6
|
+
readonly returnData: string;
|
|
7
|
+
/** 能解析出来的 revert 原因(Error(string) / Panic(uint256)) */
|
|
8
|
+
readonly reason: string | null;
|
|
9
|
+
constructor(call: Call, returnData: string, cause?: unknown);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* eth_call 的失败是否是 EVM 执行层面的确定性结果(revert、代码大小超限等),而不是节点/网络问题。
|
|
13
|
+
*
|
|
14
|
+
* ethers 会把 eth_call 返回的**任何** JSON-RPC 错误都包装成 CALL_EXCEPTION("missing revert data"),
|
|
15
|
+
* 包括限流(-32005)、header not found、非归档节点缺状态等,所以不能只看错误码:
|
|
16
|
+
* 有 revert 数据,或节点原始错误信息是执行错误,才算确定性失败;其余换节点重试可能会成功。
|
|
17
|
+
*/
|
|
18
|
+
export declare function isExecutionError(err: unknown): boolean;
|
|
19
|
+
export declare function decodeRevertReason(data: string): string | null;
|
|
20
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAIrC,iCAAiC;AACjC,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAA;IACnB,kBAAkB;IAClB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,uDAAuD;IACvD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;gBAElB,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAW5D;AAMD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAYtD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAa9D"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Interface, isError } from 'ethers';
|
|
2
|
+
const errorInterface = new Interface(['error Error(string)', 'error Panic(uint256)']);
|
|
3
|
+
/** 自动合并模式下,单条调用失败时 reject 的错误 */
|
|
4
|
+
export class CallFailedError extends Error {
|
|
5
|
+
call;
|
|
6
|
+
/** revert 原始数据 */
|
|
7
|
+
returnData;
|
|
8
|
+
/** 能解析出来的 revert 原因(Error(string) / Panic(uint256)) */
|
|
9
|
+
reason;
|
|
10
|
+
constructor(call, returnData, cause) {
|
|
11
|
+
const reason = decodeRevertReason(returnData);
|
|
12
|
+
super(`${call.name} call to ${call.contract.address} failed${reason ? `: ${reason}` : ''}`, cause === undefined ? undefined : { cause });
|
|
13
|
+
this.name = 'CallFailedError';
|
|
14
|
+
this.call = call;
|
|
15
|
+
this.returnData = returnData;
|
|
16
|
+
this.reason = reason;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
// 最后一项是 TronProvider 对交易校验失败(CONTRACT_VALIDATE_ERROR 等)抛出的错误
|
|
20
|
+
const EXECUTION_ERROR_RE = /revert|max (init)?code size|out of gas|invalid opcode|stack (underflow|overflow)|invalid jump|^CONTRACT_[A-Z_]+_ERROR:/i;
|
|
21
|
+
/**
|
|
22
|
+
* eth_call 的失败是否是 EVM 执行层面的确定性结果(revert、代码大小超限等),而不是节点/网络问题。
|
|
23
|
+
*
|
|
24
|
+
* ethers 会把 eth_call 返回的**任何** JSON-RPC 错误都包装成 CALL_EXCEPTION("missing revert data"),
|
|
25
|
+
* 包括限流(-32005)、header not found、非归档节点缺状态等,所以不能只看错误码:
|
|
26
|
+
* 有 revert 数据,或节点原始错误信息是执行错误,才算确定性失败;其余换节点重试可能会成功。
|
|
27
|
+
*/
|
|
28
|
+
export function isExecutionError(err) {
|
|
29
|
+
if (!isError(err, 'CALL_EXCEPTION')) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
if (err.data && err.data !== '0x') {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
const info = err.info;
|
|
36
|
+
const raw = info?.error?.message;
|
|
37
|
+
// 没有节点原始信息时(如 TronProvider 自己抛的)看 shortMessage;ethers 的 "missing revert data" 不算
|
|
38
|
+
const message = typeof raw === 'string' ? raw : err.shortMessage === 'missing revert data' ? '' : err.shortMessage;
|
|
39
|
+
return EXECUTION_ERROR_RE.test(message ?? '');
|
|
40
|
+
}
|
|
41
|
+
export function decodeRevertReason(data) {
|
|
42
|
+
if (!data || data === '0x') {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const parsed = errorInterface.parseError(data);
|
|
47
|
+
if (!parsed) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return parsed.name === 'Panic' ? `Panic(${parsed.args[0]})` : String(parsed.args[0]);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AAI3C,MAAM,cAAc,GAAG,IAAI,SAAS,CAAC,CAAC,qBAAqB,EAAE,sBAAsB,CAAC,CAAC,CAAA;AAErF,iCAAiC;AACjC,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAC/B,IAAI,CAAM;IACnB,kBAAkB;IACT,UAAU,CAAQ;IAC3B,uDAAuD;IAC9C,MAAM,CAAe;IAE9B,YAAY,IAAU,EAAE,UAAkB,EAAE,KAAe;QACzD,MAAM,MAAM,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAA;QAC7C,KAAK,CACH,GAAG,IAAI,CAAC,IAAI,YAAY,IAAI,CAAC,QAAQ,CAAC,OAAO,UAAU,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EACpF,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAC5C,CAAA;QACD,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAA;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAC5B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;CACF;AAED,6DAA6D;AAC7D,MAAM,kBAAkB,GACtB,yHAAyH,CAAA;AAE3H;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,gBAAgB,CAAC,EAAE,CAAC;QACpC,OAAO,KAAK,CAAA;IACd,CAAC;IACD,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QAClC,OAAO,IAAI,CAAA;IACb,CAAC;IACD,MAAM,IAAI,GAAG,GAAG,CAAC,IAAqD,CAAA;IACtE,MAAM,GAAG,GAAG,IAAI,EAAE,KAAK,EAAE,OAAO,CAAA;IAChC,iFAAiF;IACjF,MAAM,OAAO,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,KAAK,qBAAqB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAA;IAClH,OAAO,kBAAkB,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAA;AAC/C,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,cAAc,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;QAC9C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,IAAI,CAAA;QACb,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;IACtF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type TransactionRequest } from 'ethers';
|
|
2
|
+
import type { EthersLikeProvider } from './aggregate.js';
|
|
3
|
+
export interface FallbackOptions {
|
|
4
|
+
/** 单个节点单次请求超时(毫秒),超时视为该节点故障并换下一个。默认 10000;<= 0 表示不限制 */
|
|
5
|
+
timeout?: number;
|
|
6
|
+
/** 节点出错后跳过它的时长(毫秒),期间优先用其他节点。默认 30000 */
|
|
7
|
+
cooldown?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 多节点故障切换:按顺序使用节点,出错或超时换下一个;出错的节点在 cooldown 内排到最后。
|
|
11
|
+
*
|
|
12
|
+
* 与 ethers FallbackProvider 相比:
|
|
13
|
+
* - 没有首次请求前对所有节点的 getBlockNumber 同步,一个卡住的节点不会拖住第一次查询
|
|
14
|
+
* - 合约 revert 等确定性结果直接返回,不会拿去别的节点重试
|
|
15
|
+
*/
|
|
16
|
+
export declare class FallbackRpc implements EthersLikeProvider {
|
|
17
|
+
#private;
|
|
18
|
+
constructor(nodes: EthersLikeProvider[], options?: FallbackOptions);
|
|
19
|
+
get nodes(): readonly EthersLikeProvider[];
|
|
20
|
+
call(tx: TransactionRequest): Promise<string>;
|
|
21
|
+
getBalance(address: Parameters<EthersLikeProvider['getBalance']>[0], blockTag?: Parameters<EthersLikeProvider['getBalance']>[1]): Promise<bigint>;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=fallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../src/fallback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,kBAAkB,EAAE,MAAM,QAAQ,CAAA;AAEzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAGxD,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;;;;GAMG;AACH,qBAAa,WAAY,YAAW,kBAAkB;;gBAMxC,KAAK,EAAE,kBAAkB,EAAE,EAAE,OAAO,GAAE,eAAoB;IAUtE,IAAI,KAAK,IAAI,SAAS,kBAAkB,EAAE,CAEzC;IAED,IAAI,CAAC,EAAE,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC;IAI7C,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,UAAU,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;CA+BlJ"}
|