@w3lib/evm-batch-call 0.2.0 → 0.4.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.
Files changed (108) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +184 -30
  3. package/contracts/DeploylessMulticall3.sol +5 -0
  4. package/dist/aggregate.d.ts +25 -3
  5. package/dist/aggregate.d.ts.map +1 -1
  6. package/dist/aggregate.js +143 -5
  7. package/dist/aggregate.js.map +1 -1
  8. package/dist/batcher.d.ts +6 -1
  9. package/dist/batcher.d.ts.map +1 -1
  10. package/dist/batcher.js +59 -9
  11. package/dist/batcher.js.map +1 -1
  12. package/dist/call.d.ts +27 -2
  13. package/dist/call.d.ts.map +1 -1
  14. package/dist/call.js +22 -0
  15. package/dist/call.js.map +1 -1
  16. package/dist/chains.d.ts +7 -0
  17. package/dist/chains.d.ts.map +1 -1
  18. package/dist/chains.js +11 -0
  19. package/dist/chains.js.map +1 -1
  20. package/dist/deployless.d.ts +1 -1
  21. package/dist/deployless.d.ts.map +1 -1
  22. package/dist/deployless.js +1 -1
  23. package/dist/deployless.js.map +1 -1
  24. package/dist/erc20.d.ts +70 -4
  25. package/dist/erc20.d.ts.map +1 -1
  26. package/dist/erc20.js +121 -5
  27. package/dist/erc20.js.map +1 -1
  28. package/dist/events.d.ts +25 -0
  29. package/dist/events.d.ts.map +1 -0
  30. package/dist/events.js +25 -0
  31. package/dist/events.js.map +1 -0
  32. package/dist/fallback.d.ts +54 -3
  33. package/dist/fallback.d.ts.map +1 -1
  34. package/dist/fallback.js +273 -21
  35. package/dist/fallback.js.map +1 -1
  36. package/dist/index.d.ts +10 -8
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +7 -5
  39. package/dist/index.js.map +1 -1
  40. package/dist/multicall.d.ts.map +1 -1
  41. package/dist/multicall.js +33 -2
  42. package/dist/multicall.js.map +1 -1
  43. package/dist/nft.d.ts +5 -0
  44. package/dist/nft.d.ts.map +1 -1
  45. package/dist/nft.js +5 -0
  46. package/dist/nft.js.map +1 -1
  47. package/dist/owner.d.ts +145 -0
  48. package/dist/owner.d.ts.map +1 -0
  49. package/dist/owner.js +510 -0
  50. package/dist/owner.js.map +1 -0
  51. package/dist/provider.d.ts +63 -18
  52. package/dist/provider.d.ts.map +1 -1
  53. package/dist/provider.js +166 -100
  54. package/dist/provider.js.map +1 -1
  55. package/dist/scan.d.ts +59 -0
  56. package/dist/scan.d.ts.map +1 -0
  57. package/dist/scan.js +245 -0
  58. package/dist/scan.js.map +1 -0
  59. package/dist/shortcuts.d.ts +34 -35
  60. package/dist/shortcuts.d.ts.map +1 -1
  61. package/dist/shortcuts.js +41 -73
  62. package/dist/shortcuts.js.map +1 -1
  63. package/dist/source.d.ts +6 -2
  64. package/dist/source.d.ts.map +1 -1
  65. package/dist/source.js +49 -6
  66. package/dist/source.js.map +1 -1
  67. package/dist/subpaths/nft.d.ts +46 -0
  68. package/dist/subpaths/nft.d.ts.map +1 -0
  69. package/dist/subpaths/nft.js +55 -0
  70. package/dist/subpaths/nft.js.map +1 -0
  71. package/dist/subpaths/owner.d.ts +33 -0
  72. package/dist/subpaths/owner.d.ts.map +1 -0
  73. package/dist/subpaths/owner.js +34 -0
  74. package/dist/subpaths/owner.js.map +1 -0
  75. package/dist/tron.d.ts +2 -0
  76. package/dist/tron.d.ts.map +1 -1
  77. package/dist/tron.js +5 -0
  78. package/dist/tron.js.map +1 -1
  79. package/dist/util.d.ts +5 -0
  80. package/dist/util.d.ts.map +1 -1
  81. package/dist/util.js +23 -0
  82. package/dist/util.js.map +1 -1
  83. package/dist/watch.d.ts +25 -0
  84. package/dist/watch.d.ts.map +1 -0
  85. package/dist/watch.js +105 -0
  86. package/dist/watch.js.map +1 -0
  87. package/package.json +19 -10
  88. package/src/aggregate.ts +177 -9
  89. package/src/batcher.ts +69 -9
  90. package/src/call.ts +42 -2
  91. package/src/chains.ts +15 -0
  92. package/src/deployless.ts +1 -1
  93. package/src/erc20.ts +171 -8
  94. package/src/events.ts +42 -0
  95. package/src/fallback.ts +306 -22
  96. package/src/index.ts +20 -27
  97. package/src/multicall.ts +34 -2
  98. package/src/nft.ts +5 -0
  99. package/src/owner.ts +660 -0
  100. package/src/provider.ts +255 -112
  101. package/src/scan.ts +293 -0
  102. package/src/shortcuts.ts +76 -88
  103. package/src/source.ts +51 -8
  104. package/src/subpaths/nft.ts +107 -0
  105. package/src/subpaths/owner.ts +69 -0
  106. package/src/tron.ts +7 -0
  107. package/src/util.ts +27 -0
  108. package/src/watch.ts +149 -0
@@ -1,11 +1,10 @@
1
1
  import { type BigNumberish } from 'ethers';
2
- import { type BlockTag, type CallOverrides, type EthersLikeProvider } from './aggregate.js';
2
+ import { type BlockTag, type CallOverrides, type EthersLikeProvider, type NativeBalanceMode } from './aggregate.js';
3
3
  import { type BatchOptions } from './batcher.js';
4
4
  import { type BoundCall, type Call } from './call.js';
5
5
  import { Contract, type ContractAbi, type ContractRunner } from './contract.js';
6
- import { type BalanceToken, type Erc20Contract, type TokenBalance, type TokenAllowance, type DefaultTokenField, type TokenDetails, type TokenField } from './erc20.js';
6
+ import { type BalanceToken, type Erc20Contract, type RawTokenAllowance, type RawTokenBalance, type TokenBalance, type TokenAllowance, type DefaultTokenField, type TokenDetails, type TokenField } from './erc20.js';
7
7
  import { type Multicall } from './multicall.js';
8
- import { type DefaultNftCollectionField, type Erc1155Balance, type NftBalance, type NftCollection, type NftCollectionField, type NftCollectionsOptions, type NftItem, type NftOwner, type NftTokenUri, type NftTokenUriOptions } from './nft.js';
9
8
  import { type ProviderSource, type SourceOptions } from './source.js';
10
9
  export type { ProviderSource };
11
10
  export interface StaticCallOverrides extends CallOverrides {
@@ -26,11 +25,13 @@ export type StaticCallResult<T> = {
26
25
  /** all / tryAll 的输入:Call 数组,或 { 名字: Call } 对象 */
27
26
  export type CallInput = readonly Call[] | Readonly<Record<string, Call>>;
28
27
  type ResultOf<C> = C extends Call<infer R> ? R : any;
28
+ /** C 是 any(如 `all<any>(calls)` 显式写了 any,会落到第一个重载上)时按 ethcall 的写法返回 any[] */
29
+ type IsAny<T> = 0 extends 1 & T ? true : false;
29
30
  /** 与输入同构的结果:数组 → 数组(元组保留各项类型),对象 → 同名字段 */
30
- export type CallResults<C> = {
31
+ export type CallResults<C> = IsAny<C> extends true ? any[] : {
31
32
  -readonly [K in keyof C]: ResultOf<C[K]>;
32
33
  };
33
- export type TryCallResults<C> = {
34
+ export type TryCallResults<C> = IsAny<C> extends true ? any[] : {
34
35
  -readonly [K in keyof C]: ResultOf<C[K]> | null;
35
36
  };
36
37
  export interface ProviderConfig extends SourceOptions {
@@ -50,14 +51,43 @@ export interface ProviderConfig extends SourceOptions {
50
51
  nativeSymbol?: string;
51
52
  /** 主币名称(tokens 用),默认按内置链信息表,表里没有时为 null */
52
53
  nativeName?: string;
54
+ /**
55
+ * 主币余额的读取方式,默认按内置表 NATIVE_BALANCE_MODES,表里没有时为 'contract':
56
+ * - 'contract':和其他调用放在同一次 eth_call 里(Multicall3.getEthBalance / deployless 里的 BALANCE)
57
+ * - 'rpc':单独用 eth_getBalance。有的链在合约里读主币余额恒为 0、不报错,需要用这个
58
+ * - `{ erc20: '0x…' }`:主币其实是一个 ERC20 合约,改调它的 balanceOf
59
+ */
60
+ nativeBalance?: NativeBalanceMode;
53
61
  }
54
- export interface TokensOptions<F extends TokenField = DefaultTokenField> extends CallOverrides {
62
+ /** balances / allowances / tokens 共用:结果带读取时的区块号 */
63
+ export interface BlockOptions {
64
+ /**
65
+ * 每项结果带 blockNumber(读取时的区块号,与数据在同一次 eth_call 里读出)。传了 minBlock 时自动带上。
66
+ * 大批量拆成多次 eth_call 时为其中包含区块号查询的那一次
67
+ */
68
+ withBlock?: boolean;
69
+ }
70
+ export interface TokensOptions<F extends TokenField = DefaultTokenField> extends CallOverrides, BlockOptions {
55
71
  /** 要返回的字段,默认 ['name', 'symbol', 'decimals'] */
56
72
  fields?: readonly F[];
57
73
  }
58
- export interface BalancesOptions extends CallOverrides {
74
+ export interface BalancesOptions extends CallOverrides, BlockOptions {
59
75
  /** 同时返回 symbol(代币查一次后缓存;主币取 nativeSymbol / 内置链信息表)。默认 false */
60
76
  symbol?: boolean;
77
+ /**
78
+ * 是否查 decimals 并返回 decimals / formatted。默认 true。
79
+ * false:只查余额(没有 decimals 的合约如 ERC721 也能拿到余额,冷启动时子调用数减半),结果里不带 decimals / formatted
80
+ */
81
+ decimals?: boolean;
82
+ }
83
+ /** multiBalances / getMultiBalances 的单项查询 */
84
+ export interface BalancesQuery {
85
+ owner: string;
86
+ tokens: readonly BalanceToken[];
87
+ }
88
+ export interface AllowancesOptions extends CallOverrides, BlockOptions {
89
+ /** 是否查 decimals 并返回 decimals / formatted。默认 true;false 时只查额度,结果里不带 decimals / formatted */
90
+ decimals?: boolean;
61
91
  }
62
92
  /**
63
93
  * Multicall Provider,API 与 ethcall 的 Provider 保持一致:
@@ -126,7 +156,21 @@ export declare class Provider implements ContractRunner {
126
156
  * - decimals、symbol 查到一次后缓存,之后只查 balanceOf;也可以直接传 `{ address, decimals }`
127
157
  * - 单个代币失败(非合约地址、非法地址等)不影响其他代币,该项 success 为 false
128
158
  */
159
+ balances(owner: string, tokens: readonly BalanceToken[], options: BalancesOptions & {
160
+ decimals: false;
161
+ }): Promise<RawTokenBalance[]>;
129
162
  balances(owner: string, tokens: readonly BalanceToken[], options?: BalancesOptions): Promise<TokenBalance[]>;
163
+ /**
164
+ * 多个钱包一次查:同一条链上所有钱包的余额合成一次 multicall(与 balances 选项相同),结果与 queries 一一对应。
165
+ *
166
+ * ```ts
167
+ * const [a, b] = await multi.multiBalances([{ owner: walletA, tokens: [NATIVE_TOKEN, USDT] }, { owner: walletB, tokens: [USDT] }])
168
+ * ```
169
+ */
170
+ multiBalances(queries: readonly BalancesQuery[], options: BalancesOptions & {
171
+ decimals: false;
172
+ }): Promise<RawTokenBalance[][]>;
173
+ multiBalances(queries: readonly BalancesQuery[], options?: BalancesOptions): Promise<TokenBalance[][]>;
130
174
  /**
131
175
  * 批量查 ERC20 授权额度(发交易前判断是否需要 approve),一次请求。
132
176
  *
@@ -138,17 +182,10 @@ export declare class Provider implements ContractRunner {
138
182
  * - 主币不需要授权:native 为 true,额度视为 MaxUint256、unlimited 为 true,不发请求
139
183
  * - decimals 与 balances / tokens 共用缓存;也可以直接传 `{ address, decimals }`
140
184
  */
141
- allowances(owner: string, spender: string, tokens: readonly BalanceToken[], overrides?: CallOverrides): Promise<TokenAllowance[]>;
142
- /** 批量查 NFT 集合信息(标准 / name / symbol / totalSupply),字段可选 */
143
- nftCollections<const F extends NftCollectionField = DefaultNftCollectionField>(collections: readonly string[], options?: NftCollectionsOptions<F>): Promise<NftCollection<F>[]>;
144
- /** 批量查 ERC721 持有数量(balanceOf) */
145
- nftBalances(owner: string, collections: readonly string[], overrides?: CallOverrides): Promise<NftBalance[]>;
146
- /** 批量查 ERC721 持有人(ownerOf),可混合多个集合 */
147
- nftOwners(items: readonly NftItem[], overrides?: CallOverrides): Promise<NftOwner[]>;
148
- /** 批量查 NFT 元数据地址:兼容 ERC721 tokenURI 与 ERC1155 uri({id} 按规范替换),可转换 ipfs:// */
149
- nftTokenUris(items: readonly NftItem[], options?: NftTokenUriOptions): Promise<NftTokenUri[]>;
150
- /** 批量查 ERC1155 余额 */
151
- erc1155Balances(owner: string, items: readonly NftItem[], overrides?: CallOverrides): Promise<Erc1155Balance[]>;
185
+ allowances(owner: string, spender: string, tokens: readonly BalanceToken[], options: AllowancesOptions & {
186
+ decimals: false;
187
+ }): Promise<RawTokenAllowance[]>;
188
+ allowances(owner: string, spender: string, tokens: readonly BalanceToken[], options?: AllowancesOptions): Promise<TokenAllowance[]>;
152
189
  /**
153
190
  * 批量查 ERC20 代币详情,一次请求;可以选择返回哪些字段。
154
191
  *
@@ -208,6 +245,14 @@ export declare class Provider implements ContractRunner {
208
245
  * @param overrides 所有条目共享的 overrides,优先级最低
209
246
  */
210
247
  staticCallAll<T = any>(items: ReadonlyArray<Call | StaticCallItem>, overrides?: StaticCallOverrides): Promise<StaticCallResult<T>[]>;
248
+ /** 当前区块号(底层节点需支持 getBlockNumber) */
249
+ getBlockNumber(): Promise<number>;
250
+ /** eth_getLogs(底层节点需支持 getLogs)。多节点时依次尝试,全部失败抛 GetLogsError */
251
+ getLogs(filter: Parameters<NonNullable<EthersLikeProvider['getLogs']>>[0]): Promise<Awaited<ReturnType<NonNullable<EthersLikeProvider['getLogs']>>>>;
252
+ /** 是否是按主币处理的地址(config.nativeTokens,默认 0xeeee…eeee 和零地址) */
253
+ isNativeToken(address: string): boolean;
254
+ /** 按主币处理的地址(小写),顺序同 config.nativeTokens */
255
+ get nativeTokens(): readonly string[];
211
256
  /** 底层连接(多节点时是 FallbackRpc)。只传节点构造时,需在 ready() 之后读取 */
212
257
  get rpc(): EthersLikeProvider;
213
258
  /** 当前使用的 multicall 合约(null 表示 deployless;只传节点构造时在 ready() 之前也为 null) */
@@ -1 +1 @@
1
- {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"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"}
package/dist/provider.js CHANGED
@@ -1,15 +1,15 @@
1
1
  import {} from 'ethers';
2
2
  import { aggregate } from './aggregate.js';
3
3
  import { Batcher } from './batcher.js';
4
- import { asStringOrBytes32, decodeCall, encodeCall } from './call.js';
4
+ import { asStringOrBytes32, decodeCall, encodeCall, settleResult, } from './call.js';
5
5
  import { Contract, bindCall } from './contract.js';
6
- import { DEFAULT_NATIVE_TOKENS, ERC20_ABI, formatAmount, getCachedTokenMeta, setCachedTokenMeta, MAX_UINT256, UNLIMITED_ALLOWANCE_THRESHOLD, DEFAULT_TOKEN_FIELDS, } from './erc20.js';
6
+ import { DEFAULT_NATIVE_TOKENS, ERC20_ABI, formatUnits, getCachedTokenMeta, setCachedTokenMeta, MAX_UINT256, UNLIMITED_ALLOWANCE_THRESHOLD, DEFAULT_TOKEN_FIELDS, } from './erc20.js';
7
7
  import { CallFailedError, isExecutionError } from './errors.js';
8
8
  import { MULTICALL3_ADDRESS, getMulticall3 } from './multicall.js';
9
- import { getNativeCurrency } from './chains.js';
10
- import { erc1155Balances, nftBalances, nftCollections, nftOwners, nftTokenUris, } from './nft.js';
9
+ import { getNativeBalanceMode, getNativeCurrency } from './chains.js';
11
10
  import { detectChainId } from './detect.js';
12
11
  import { isTronChain, resolveSource } from './source.js';
12
+ import { withSignal } from './util.js';
13
13
  /** 构造参数第一个是 chainId 还是节点:数字、数字字符串、0x 十六进制视为 chainId,其余(URL、对象、数组)视为节点 */
14
14
  function isChainIdArg(value) {
15
15
  return (typeof value === 'number' ||
@@ -18,6 +18,7 @@ function isChainIdArg(value) {
18
18
  }
19
19
  const ETH_BALANCE_INPUTS = [{ name: 'addr', type: 'address' }];
20
20
  const ETH_BALANCE_OUTPUTS = [{ name: 'balance', type: 'uint256' }];
21
+ const BLOCK_NUMBER_OUTPUTS = [{ name: 'blockNumber', type: 'uint256' }];
21
22
  /**
22
23
  * Multicall Provider,API 与 ethcall 的 Provider 保持一致:
23
24
  *
@@ -92,6 +93,7 @@ export class Provider {
92
93
  chainId,
93
94
  multicall: config.deployless ? null : resolveMulticall(chainId, config.multicall),
94
95
  chunkSize: config.chunkSize ?? 500,
96
+ nativeBalance: config.nativeBalance ?? getNativeBalanceMode(chainId),
95
97
  };
96
98
  }
97
99
  /** 链 ID(不传 chainId 构造时,会等待从节点识别完成) */
@@ -127,20 +129,11 @@ export class Provider {
127
129
  erc20(address) {
128
130
  return new Contract(address, ERC20_ABI, this);
129
131
  }
130
- /**
131
- * 批量查余额(含按 decimals 换算后的数值),主币和代币混在一起、一次请求。
132
- *
133
- * ```ts
134
- * const list = await multi.balances(user, [NATIVE_TOKEN, USDT, { address: USDC, decimals: 6 }], { symbol: true })
135
- * list[1] // { token: USDT, native: false, balance: '1234500000000000000000', decimals: 18, formatted: '1234.5', symbol: 'USDT', success: true }
136
- * ```
137
- *
138
- * - 主币地址见 config.nativeTokens(默认 0xeeee…eeee 和零地址);精度 / symbol 按内置链信息表,可用 nativeDecimals / nativeSymbol 覆盖
139
- * - decimals、symbol 查到一次后缓存,之后只查 balanceOf;也可以直接传 `{ address, decimals }`
140
- * - 单个代币失败(非合约地址、非法地址等)不影响其他代币,该项 success 为 false
141
- */
142
- async balances(owner, tokens, options = {}) {
143
- const { symbol: withSymbol = false, ...overrides } = options;
132
+ balances(owner, tokens, options = {}) {
133
+ return withSignal(options.signal, () => this.#balances(owner, tokens, options));
134
+ }
135
+ async #balances(owner, tokens, options) {
136
+ const { symbol: withSymbol = false, decimals: withDecimals = true, ...overrides } = options;
144
137
  const { chainId } = await this.#ensureReady();
145
138
  const nativeCurrency = this.#nativeCurrency(chainId);
146
139
  const items = tokens.map((token) => {
@@ -151,102 +144,122 @@ export class Provider {
151
144
  const calls = [];
152
145
  const plan = items.map(({ address, native, decimals, symbol }) => {
153
146
  const balanceIndex = calls.push(native ? this.getEthBalance(owner) : this.erc20(address).balanceOf(owner)) - 1;
154
- const decimalsIndex = decimals === undefined ? calls.push(this.erc20(address).decimals()) - 1 : -1;
147
+ const decimalsIndex = withDecimals && decimals === undefined ? calls.push(this.erc20(address).decimals()) - 1 : -1;
155
148
  const symbolIndex = withSymbol && !native && symbol === undefined ? calls.push(this.#symbolCall(address)) - 1 : -1;
156
149
  return { balanceIndex, decimalsIndex, symbolIndex };
157
150
  });
158
- const results = await this.tryAll(calls, overrides);
151
+ const blockIndex = this.#pushBlockNumber(calls, options);
152
+ const results = await this.#settleAll(calls, overrides);
153
+ const block = blockField(results, blockIndex);
159
154
  return items.map((item, i) => {
160
155
  const { balanceIndex, decimalsIndex, symbolIndex } = plan[i];
161
156
  const balance = results[balanceIndex];
162
- const fetchedDecimals = decimalsIndex === -1 ? null : results[decimalsIndex];
163
- const fetchedSymbol = symbolIndex === -1 ? null : results[symbolIndex];
157
+ const fetchedDecimals = valueOf(results[decimalsIndex]);
158
+ const fetchedSymbol = valueOf(results[symbolIndex]);
164
159
  if (!item.native && (fetchedDecimals !== null || fetchedSymbol !== null)) {
165
160
  setCachedTokenMeta(chainId, item.address, {
166
161
  ...(fetchedDecimals !== null ? { decimals: Number(fetchedDecimals) } : {}),
167
162
  ...(fetchedSymbol !== null ? { symbol: fetchedSymbol } : {}),
168
163
  });
169
164
  }
165
+ const extra = { ...(withSymbol ? { symbol: item.symbol ?? fetchedSymbol ?? null } : {}), ...block };
166
+ if (!withDecimals) {
167
+ return balance.ok
168
+ ? { token: item.address, native: item.native, balance: balance.value.toString(), ...extra, success: true }
169
+ : { token: item.address, native: item.native, balance: '0', ...extra, success: false, error: balance.reason, errorField: 'balance' };
170
+ }
170
171
  const decimals = item.decimals ?? (fetchedDecimals === null ? null : Number(fetchedDecimals));
171
- const extra = withSymbol ? { symbol: item.symbol ?? fetchedSymbol ?? null } : {};
172
- if (balance === null || decimals === null) {
173
- return { token: item.address, native: item.native, balance: '0', decimals: decimals ?? 0, formatted: '0', ...extra, success: false };
172
+ if (!balance.ok || decimals === null) {
173
+ const failure = !balance.ok
174
+ ? { error: balance.reason, errorField: 'balance' }
175
+ : { error: reasonOf(results[decimalsIndex]), errorField: 'decimals' };
176
+ return { token: item.address, native: item.native, balance: '0', decimals: decimals ?? 0, formatted: '0', ...extra, success: false, ...failure };
174
177
  }
175
178
  // 结果全是字符串 / 数字 / 布尔 / null,可以直接 JSON.stringify
176
179
  return {
177
180
  token: item.address,
178
181
  native: item.native,
179
- balance: balance.toString(),
182
+ balance: balance.value.toString(),
180
183
  decimals,
181
- formatted: formatAmount(balance, decimals),
184
+ formatted: formatUnits(balance.value, decimals),
182
185
  ...extra,
183
186
  success: true,
184
187
  };
185
188
  });
186
189
  }
187
- /**
188
- * 批量查 ERC20 授权额度(发交易前判断是否需要 approve),一次请求。
189
- *
190
- * ```ts
191
- * const [usdt] = await multi.allowances(user, router, [USDT])
192
- * usdt // { token: USDT, spender: router, native: false, allowance: '…', decimals: 18, formatted: '100', unlimited: false, success: true }
193
- * ```
194
- *
195
- * - 主币不需要授权:native 为 true,额度视为 MaxUint256、unlimited 为 true,不发请求
196
- * - decimals 与 balances / tokens 共用缓存;也可以直接传 `{ address, decimals }`
197
- */
198
- async allowances(owner, spender, tokens, overrides) {
190
+ multiBalances(queries, options = {}) {
191
+ // 各钱包的子调用在同一 tick 进入合并队列,合成一次 eth_call(相同的 decimals / 区块号查询只发一次)
192
+ return withSignal(options.signal, () => Promise.all(queries.map((query) => this.#balances(query.owner, query.tokens, options))));
193
+ }
194
+ allowances(owner, spender, tokens, options = {}) {
195
+ return withSignal(options.signal, () => this.#allowances(owner, spender, tokens, options));
196
+ }
197
+ async #allowances(owner, spender, tokens, options) {
198
+ const { decimals: withDecimals = true, ...overrides } = options;
199
199
  const { chainId } = await this.#ensureReady();
200
200
  const nativeDecimals = this.#nativeCurrency(chainId).decimals;
201
201
  const calls = [];
202
202
  const plan = tokens.map((token) => {
203
203
  const { address, native, decimals } = this.#resolveToken(chainId, token, nativeDecimals);
204
204
  const allowanceIndex = native ? -1 : calls.push(this.erc20(address).allowance(owner, spender)) - 1;
205
- const decimalsIndex = native || decimals !== undefined ? -1 : calls.push(this.erc20(address).decimals()) - 1;
205
+ const decimalsIndex = native || !withDecimals || decimals !== undefined ? -1 : calls.push(this.erc20(address).decimals()) - 1;
206
206
  return { address, native, decimals, allowanceIndex, decimalsIndex };
207
207
  });
208
- const results = calls.length ? await this.tryAll(calls, overrides) : [];
208
+ const blockIndex = this.#pushBlockNumber(calls, options);
209
+ const results = await this.#settleAll(calls, overrides);
210
+ const block = blockField(results, blockIndex);
209
211
  return plan.map(({ address, native, decimals: known, allowanceIndex, decimalsIndex }) => {
210
- const allowance = native ? MAX_UINT256 : (results[allowanceIndex] ?? null);
211
- const fetched = decimalsIndex === -1 ? null : (results[decimalsIndex] ?? null);
212
+ const settled = native ? { ok: true, value: MAX_UINT256 } : results[allowanceIndex];
213
+ const fetched = valueOf(results[decimalsIndex]);
212
214
  if (fetched !== null) {
213
215
  setCachedTokenMeta(chainId, address, { decimals: Number(fetched) });
214
216
  }
217
+ if (!withDecimals) {
218
+ return settled.ok
219
+ ? { token: address, spender, native, allowance: settled.value.toString(), unlimited: settled.value >= UNLIMITED_ALLOWANCE_THRESHOLD, ...block, success: true }
220
+ : { token: address, spender, native, allowance: '0', unlimited: false, ...block, success: false, error: settled.reason, errorField: 'allowance' };
221
+ }
215
222
  const decimals = known ?? (fetched === null ? null : Number(fetched));
216
- if (allowance === null || decimals === null) {
217
- return { token: address, spender, native, allowance: '0', decimals: decimals ?? 0, formatted: '0', unlimited: false, success: false };
223
+ if (!settled.ok || decimals === null) {
224
+ const failure = !settled.ok
225
+ ? { error: settled.reason, errorField: 'allowance' }
226
+ : { error: reasonOf(results[decimalsIndex]), errorField: 'decimals' };
227
+ return { token: address, spender, native, allowance: '0', decimals: decimals ?? 0, formatted: '0', unlimited: false, ...block, success: false, ...failure };
218
228
  }
219
229
  return {
220
230
  token: address,
221
231
  spender,
222
232
  native,
223
- allowance: allowance.toString(),
233
+ allowance: settled.value.toString(),
224
234
  decimals,
225
- formatted: formatAmount(allowance, decimals),
226
- unlimited: allowance >= UNLIMITED_ALLOWANCE_THRESHOLD,
235
+ formatted: formatUnits(settled.value, decimals),
236
+ unlimited: settled.value >= UNLIMITED_ALLOWANCE_THRESHOLD,
237
+ ...block,
227
238
  success: true,
228
239
  };
229
240
  });
230
241
  }
231
- /** 批量查 NFT 集合信息(标准 / name / symbol / totalSupply),字段可选 */
232
- nftCollections(collections, options) {
233
- return nftCollections(this, collections, options);
234
- }
235
- /** 批量查 ERC721 持有数量(balanceOf) */
236
- nftBalances(owner, collections, overrides) {
237
- return nftBalances(this, owner, collections, overrides);
238
- }
239
- /** 批量查 ERC721 持有人(ownerOf),可混合多个集合 */
240
- nftOwners(items, overrides) {
241
- return nftOwners(this, items, overrides);
242
- }
243
- /** 批量查 NFT 元数据地址:兼容 ERC721 tokenURI 与 ERC1155 uri({id} 按规范替换),可转换 ipfs:// */
244
- nftTokenUris(items, options) {
245
- return nftTokenUris(this, items, options);
242
+ /**
243
+ * balances / tokens / allowances 的子调用进自动合并队列:与同一时刻其他组件发起的查询合并成一次 multicall。
244
+ * 逐条返回值或失败原因;节点错误照常抛出
245
+ */
246
+ #settleAll(calls, overrides = {}) {
247
+ return this.#batcher.loadSettled(calls, { blockTag: overrides.blockTag, from: overrides.from, minBlock: overrides.minBlock, signal: overrides.signal });
246
248
  }
247
- /** 批量查 ERC1155 余额 */
248
- erc1155Balances(owner, items, overrides) {
249
- return erc1155Balances(this, owner, items, overrides);
249
+ /** withBlock / minBlock 时在同一批里加一条区块号查询,返回它的下标(不需要时为 -1) */
250
+ #pushBlockNumber(calls, options) {
251
+ if (!options.withBlock && options.minBlock === undefined) {
252
+ return -1;
253
+ }
254
+ return (calls.push({
255
+ // 地址只用于展示:执行时换成实际的 multicall 地址,deployless 时由合约内部处理
256
+ contract: { address: MULTICALL3_ADDRESS },
257
+ name: 'getBlockNumber',
258
+ inputs: [],
259
+ outputs: BLOCK_NUMBER_OUTPUTS,
260
+ params: [],
261
+ kind: 'blockNumber',
262
+ }) - 1);
250
263
  }
251
264
  /** symbol() 调用:string 解码失败时按 bytes32 解析(MKR 等老代币) */
252
265
  #symbolCall(address) {
@@ -274,7 +287,10 @@ export class Provider {
274
287
  * - symbol / name 兼容返回 bytes32 的老代币(MKR 等)
275
288
  * - 读取失败的字段为 null;请求的字段都读到时 success 为 true
276
289
  */
277
- async tokens(tokens, options = {}) {
290
+ tokens(tokens, options = {}) {
291
+ return withSignal(options.signal, () => this.#tokens(tokens, options));
292
+ }
293
+ async #tokens(tokens, options) {
278
294
  const { fields = DEFAULT_TOKEN_FIELDS, ...overrides } = options;
279
295
  const wanted = new Set(fields);
280
296
  const needDecimals = wanted.has('decimals') || wanted.has('totalSupply');
@@ -304,8 +320,10 @@ export class Provider {
304
320
  }
305
321
  return { address, native, meta, index };
306
322
  });
307
- const results = calls.length ? await this.tryAll(calls, overrides) : [];
308
- const read = (i) => (i === undefined ? null : (results[i] ?? null));
323
+ const blockIndex = this.#pushBlockNumber(calls, options);
324
+ const results = await this.#settleAll(calls, overrides);
325
+ const block = blockField(results, blockIndex);
326
+ const read = (i) => (i === undefined ? null : valueOf(results[i]));
309
327
  return plan.map(({ address, native, meta, index }) => {
310
328
  const name = meta.name ?? read(index.name);
311
329
  const symbol = meta.symbol ?? read(index.symbol);
@@ -326,18 +344,25 @@ export class Provider {
326
344
  totalSupply: totalSupply === null ? null : totalSupply.toString(),
327
345
  };
328
346
  const out = { address, native };
329
- let success = true;
347
+ let failedField = null;
330
348
  for (const field of fields) {
331
349
  out[field] = values[field];
332
350
  // 主币没有 totalSupply,不算失败
333
351
  if (values[field] === null && !(native && field === 'totalSupply')) {
334
- success = false;
352
+ failedField ??= field;
335
353
  }
336
354
  }
337
355
  if (wanted.has('totalSupply')) {
338
- out.totalSupplyFormatted = totalSupply === null || decimals === null ? null : formatAmount(totalSupply, decimals);
356
+ out.totalSupplyFormatted = totalSupply === null || decimals === null ? null : formatUnits(totalSupply, decimals);
357
+ }
358
+ Object.assign(out, block);
359
+ out.success = failedField === null;
360
+ if (failedField !== null) {
361
+ // 没发请求的字段(主币信息不在内置表、也没有配置)不是调用失败
362
+ const callIndex = index[failedField];
363
+ out.error = callIndex === undefined ? 'not-configured' : reasonOf(results[callIndex]);
364
+ out.errorField = failedField;
339
365
  }
340
- out.success = success;
341
366
  return out;
342
367
  });
343
368
  }
@@ -357,10 +382,12 @@ export class Provider {
357
382
  };
358
383
  return bindCall(call, this);
359
384
  }
360
- async all(calls, overrides) {
361
- const [list, pack] = unpack(calls);
362
- const results = await this.#aggregate(list.map((call) => encodeCall(call, false)), overrides);
363
- return pack(list.map((call, i) => decodeCall(call, results[i].returnData)));
385
+ all(calls, overrides) {
386
+ return withSignal(overrides?.signal, async () => {
387
+ const [list, pack] = unpack(calls);
388
+ const results = await this.#aggregate(list.map((call) => encodeCall(call, false)), overrides);
389
+ return pack(list.map((call, i) => decodeCall(call, results[i].returnData)));
390
+ });
364
391
  }
365
392
  async tryAll(calls, overrides) {
366
393
  const [list, pack] = unpack(calls);
@@ -369,7 +396,10 @@ export class Provider {
369
396
  /**
370
397
  * 逐条指定是否允许失败:允许失败的位置返回 null,不允许失败的一旦失败整体抛错。
371
398
  */
372
- async tryEach(calls, canFail, overrides) {
399
+ tryEach(calls, canFail, overrides) {
400
+ return withSignal(overrides?.signal, () => this.#tryEach(calls, canFail, overrides));
401
+ }
402
+ async #tryEach(calls, canFail, overrides) {
373
403
  // 允许失败的条目编码出错(如后端给的脏地址)只让该条为 null,不拖垮整批
374
404
  const requests = calls.map((call, i) => {
375
405
  const callCanFail = canFail[i];
@@ -398,7 +428,7 @@ export class Provider {
398
428
  */
399
429
  call(call, overrides) {
400
430
  const own = call.overrides;
401
- return this.#batcher.load(call, merge({ blockTag: own?.blockTag, from: own?.from }, overrides));
431
+ return withSignal(overrides?.signal, () => this.#batcher.load(call, merge({ blockTag: own?.blockTag, from: own?.from }, { blockTag: overrides?.blockTag, from: overrides?.from, minBlock: overrides?.minBlock, signal: overrides?.signal })));
402
432
  }
403
433
  /**
404
434
  * 预执行(模拟交易):直接对目标合约发 eth_call,可带 from / value / gasLimit,
@@ -409,12 +439,14 @@ export class Provider {
409
439
  *
410
440
  * overrides 优先级:参数 overrides > Call 上的 overrides(`contract.swap(params, { value })`)。
411
441
  */
412
- async staticCall(call, overrides = {}) {
413
- const result = await this.#staticCall(call, overrides);
414
- if (!result.success) {
415
- throw result.error;
416
- }
417
- return result.data;
442
+ staticCall(call, overrides = {}) {
443
+ return withSignal(overrides.signal, async () => {
444
+ const result = await this.#staticCall(call, overrides);
445
+ if (!result.success) {
446
+ throw result.error;
447
+ }
448
+ return result.data;
449
+ });
418
450
  }
419
451
  /**
420
452
  * 批量预执行:每条都是独立的 eth_call,同时发出(JsonRpcProvider 会合进一个 JSON-RPC batch 请求;
@@ -423,7 +455,10 @@ export class Provider {
423
455
  * @param items Call,或 `{ call, overrides }` 以便每条带不同的 value / from
424
456
  * @param overrides 所有条目共享的 overrides,优先级最低
425
457
  */
426
- async staticCallAll(items, overrides = {}) {
458
+ staticCallAll(items, overrides = {}) {
459
+ return withSignal(overrides.signal, () => this.#staticCallAll(items, overrides));
460
+ }
461
+ #staticCallAll(items, overrides) {
427
462
  return Promise.all(items.map((item) => 'call' in item && 'contract' in item.call
428
463
  ? this.#staticCall(item.call, item.overrides, overrides)
429
464
  : this.#staticCall(item, undefined, overrides)));
@@ -433,7 +468,10 @@ export class Provider {
433
468
  const merged = merge(merge({ ...shared }, call.overrides), explicit);
434
469
  if (call.kind === 'ethBalance') {
435
470
  try {
436
- const { provider } = await this.#ensureReady();
471
+ const { provider, nativeBalance } = await this.#ensureReady();
472
+ if (typeof nativeBalance === 'object') {
473
+ return await this.#staticCall(this.erc20(nativeBalance.erc20).balanceOf(call.params[0]), explicit, shared);
474
+ }
437
475
  return { success: true, data: (await provider.getBalance(call.params[0], merged.blockTag)) };
438
476
  }
439
477
  catch (err) {
@@ -468,6 +506,30 @@ export class Provider {
468
506
  return { success: false, error: new CallFailedError(call, returnData, err) };
469
507
  }
470
508
  }
509
+ /** 当前区块号(底层节点需支持 getBlockNumber) */
510
+ async getBlockNumber() {
511
+ const { provider } = await this.#ensureReady();
512
+ if (!provider.getBlockNumber) {
513
+ throw new Error('getBlockNumber is not supported by this provider');
514
+ }
515
+ return provider.getBlockNumber();
516
+ }
517
+ /** eth_getLogs(底层节点需支持 getLogs)。多节点时依次尝试,全部失败抛 GetLogsError */
518
+ async getLogs(filter) {
519
+ const { provider } = await this.#ensureReady();
520
+ if (!provider.getLogs) {
521
+ throw new Error('getLogs is not supported by this provider');
522
+ }
523
+ return provider.getLogs(filter);
524
+ }
525
+ /** 是否是按主币处理的地址(config.nativeTokens,默认 0xeeee…eeee 和零地址) */
526
+ isNativeToken(address) {
527
+ return this.#nativeTokens.has(address.toLowerCase());
528
+ }
529
+ /** 按主币处理的地址(小写),顺序同 config.nativeTokens */
530
+ get nativeTokens() {
531
+ return [...this.#nativeTokens];
532
+ }
471
533
  /** 底层连接(多节点时是 FallbackRpc)。只传节点构造时,需在 ready() 之后读取 */
472
534
  get rpc() {
473
535
  if (!this.#ctx) {
@@ -509,19 +571,23 @@ function unpack(calls) {
509
571
  return [keys.map((key) => record[key]), (results) => Object.fromEntries(keys.map((key, i) => [key, results[i]]))];
510
572
  }
511
573
  function decodeTry(calls, results) {
512
- return calls.map((call, i) => {
513
- const result = results[i];
514
- if (!result.success) {
515
- return null;
516
- }
517
- try {
518
- return decodeCall(call, result.returnData);
519
- }
520
- catch {
521
- // 解码失败:多半是目标地址没有合约
522
- return null;
523
- }
524
- });
574
+ return calls.map((call, i) => valueOf(settleResult(call, results[i])));
575
+ }
576
+ /** withBlock 时每项结果附带的 { blockNumber }(区块号查询失败时为 null);不需要时为空对象 */
577
+ function blockField(results, index) {
578
+ if (index === -1) {
579
+ return {};
580
+ }
581
+ const value = valueOf(results[index]);
582
+ return { blockNumber: value === null ? null : Number(value) };
583
+ }
584
+ /** 成功的值;失败或没有这一项(下标为 -1)时为 null */
585
+ function valueOf(settled) {
586
+ return settled?.ok ? settled.value : null;
587
+ }
588
+ /** 失败原因;没有对应的调用时(不应发生)按 decode-failed */
589
+ function reasonOf(settled) {
590
+ return settled && !settled.ok ? settled.reason : 'decode-failed';
525
591
  }
526
592
  export default Provider;
527
593
  //# sourceMappingURL=provider.js.map