@w3lib/evm-batch-call 0.7.0 → 0.8.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 (159) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +206 -1
  3. package/dist/dividends.d.ts +135 -0
  4. package/dist/dividends.d.ts.map +1 -0
  5. package/dist/dividends.js +253 -0
  6. package/dist/dividends.js.map +1 -0
  7. package/dist/history.d.ts +154 -0
  8. package/dist/history.d.ts.map +1 -0
  9. package/dist/history.js +247 -0
  10. package/dist/history.js.map +1 -0
  11. package/dist/historySources.d.ts +32 -0
  12. package/dist/historySources.d.ts.map +1 -0
  13. package/dist/historySources.js +319 -0
  14. package/dist/historySources.js.map +1 -0
  15. package/dist/liquidity/v3.d.ts +2 -0
  16. package/dist/liquidity/v3.d.ts.map +1 -1
  17. package/dist/liquidity/v3.js +1 -1
  18. package/dist/liquidity/v3.js.map +1 -1
  19. package/dist/liquidity/v4.d.ts +21 -0
  20. package/dist/liquidity/v4.d.ts.map +1 -1
  21. package/dist/liquidity/v4.js +4 -3
  22. package/dist/liquidity/v4.js.map +1 -1
  23. package/dist/lp.d.ts +8 -0
  24. package/dist/lp.d.ts.map +1 -1
  25. package/dist/lp.js +17 -1
  26. package/dist/lp.js.map +1 -1
  27. package/dist/lpV4.d.ts +4 -0
  28. package/dist/lpV4.d.ts.map +1 -1
  29. package/dist/lpV4.js +27 -3
  30. package/dist/lpV4.js.map +1 -1
  31. package/dist/permit.d.ts +137 -0
  32. package/dist/permit.d.ts.map +1 -0
  33. package/dist/permit.js +355 -0
  34. package/dist/permit.js.map +1 -0
  35. package/dist/portfolio.d.ts +2 -0
  36. package/dist/portfolio.d.ts.map +1 -1
  37. package/dist/portfolio.js +9 -4
  38. package/dist/portfolio.js.map +1 -1
  39. package/dist/rewards/farms.d.ts +26 -0
  40. package/dist/rewards/farms.d.ts.map +1 -0
  41. package/dist/rewards/farms.js +210 -0
  42. package/dist/rewards/farms.js.map +1 -0
  43. package/dist/rewards/index.d.ts +42 -0
  44. package/dist/rewards/index.d.ts.map +1 -0
  45. package/dist/rewards/index.js +382 -0
  46. package/dist/rewards/index.js.map +1 -0
  47. package/dist/rewards/types.d.ts +170 -0
  48. package/dist/rewards/types.d.ts.map +1 -0
  49. package/dist/rewards/types.js +2 -0
  50. package/dist/rewards/types.js.map +1 -0
  51. package/dist/simulate.d.ts +186 -0
  52. package/dist/simulate.d.ts.map +1 -0
  53. package/dist/simulate.js +559 -0
  54. package/dist/simulate.js.map +1 -0
  55. package/dist/simulateDecode.d.ts +33 -0
  56. package/dist/simulateDecode.d.ts.map +1 -0
  57. package/dist/simulateDecode.js +321 -0
  58. package/dist/simulateDecode.js.map +1 -0
  59. package/dist/smartAccount/account.d.ts +31 -0
  60. package/dist/smartAccount/account.d.ts.map +1 -0
  61. package/dist/smartAccount/account.js +33 -0
  62. package/dist/smartAccount/account.js.map +1 -0
  63. package/dist/smartAccount/clients.d.ts +77 -0
  64. package/dist/smartAccount/clients.d.ts.map +1 -0
  65. package/dist/smartAccount/clients.js +122 -0
  66. package/dist/smartAccount/clients.js.map +1 -0
  67. package/dist/smartAccount/eip5792.d.ts +77 -0
  68. package/dist/smartAccount/eip5792.d.ts.map +1 -0
  69. package/dist/smartAccount/eip5792.js +152 -0
  70. package/dist/smartAccount/eip5792.js.map +1 -0
  71. package/dist/smartAccount/send.d.ts +40 -0
  72. package/dist/smartAccount/send.d.ts.map +1 -0
  73. package/dist/smartAccount/send.js +271 -0
  74. package/dist/smartAccount/send.js.map +1 -0
  75. package/dist/smartAccount/userOp.d.ts +91 -0
  76. package/dist/smartAccount/userOp.d.ts.map +1 -0
  77. package/dist/smartAccount/userOp.js +124 -0
  78. package/dist/smartAccount/userOp.js.map +1 -0
  79. package/dist/subpaths/dividends.d.ts +28 -0
  80. package/dist/subpaths/dividends.d.ts.map +1 -0
  81. package/dist/subpaths/dividends.js +29 -0
  82. package/dist/subpaths/dividends.js.map +1 -0
  83. package/dist/subpaths/history.d.ts +22 -0
  84. package/dist/subpaths/history.d.ts.map +1 -0
  85. package/dist/subpaths/history.js +23 -0
  86. package/dist/subpaths/history.js.map +1 -0
  87. package/dist/subpaths/permit.d.ts +14 -0
  88. package/dist/subpaths/permit.d.ts.map +1 -0
  89. package/dist/subpaths/permit.js +14 -0
  90. package/dist/subpaths/permit.js.map +1 -0
  91. package/dist/subpaths/price.js +2 -2
  92. package/dist/subpaths/price.js.map +1 -1
  93. package/dist/subpaths/rewards.d.ts +27 -0
  94. package/dist/subpaths/rewards.d.ts.map +1 -0
  95. package/dist/subpaths/rewards.js +25 -0
  96. package/dist/subpaths/rewards.js.map +1 -0
  97. package/dist/subpaths/simulate.d.ts +25 -0
  98. package/dist/subpaths/simulate.d.ts.map +1 -0
  99. package/dist/subpaths/simulate.js +28 -0
  100. package/dist/subpaths/simulate.js.map +1 -0
  101. package/dist/subpaths/smart-account.d.ts +24 -0
  102. package/dist/subpaths/smart-account.d.ts.map +1 -0
  103. package/dist/subpaths/smart-account.js +24 -0
  104. package/dist/subpaths/smart-account.js.map +1 -0
  105. package/dist/swap.d.ts +45 -5
  106. package/dist/swap.d.ts.map +1 -1
  107. package/dist/swap.js +108 -12
  108. package/dist/swap.js.map +1 -1
  109. package/dist/write/batch.d.ts +34 -2
  110. package/dist/write/batch.d.ts.map +1 -1
  111. package/dist/write/batch.js +22 -3
  112. package/dist/write/batch.js.map +1 -1
  113. package/dist/write/collect.d.ts +9 -0
  114. package/dist/write/collect.d.ts.map +1 -1
  115. package/dist/write/collect.js +41 -1
  116. package/dist/write/collect.js.map +1 -1
  117. package/dist/write/send.d.ts +4 -0
  118. package/dist/write/send.d.ts.map +1 -1
  119. package/dist/write/send.js.map +1 -1
  120. package/dist/write/signer.d.ts +5 -1
  121. package/dist/write/signer.d.ts.map +1 -1
  122. package/dist/write/signer.js.map +1 -1
  123. package/dist/write/transfer.d.ts +5 -3
  124. package/dist/write/transfer.d.ts.map +1 -1
  125. package/dist/write/transfer.js +12 -6
  126. package/dist/write/transfer.js.map +1 -1
  127. package/package.json +35 -1
  128. package/src/dividends.ts +360 -0
  129. package/src/history.ts +367 -0
  130. package/src/historySources.ts +360 -0
  131. package/src/liquidity/v3.ts +1 -1
  132. package/src/liquidity/v4.ts +4 -3
  133. package/src/lp.ts +18 -1
  134. package/src/lpV4.ts +32 -2
  135. package/src/permit.ts +457 -0
  136. package/src/portfolio.ts +11 -4
  137. package/src/rewards/farms.ts +229 -0
  138. package/src/rewards/index.ts +431 -0
  139. package/src/rewards/types.ts +179 -0
  140. package/src/simulate.ts +727 -0
  141. package/src/simulateDecode.ts +352 -0
  142. package/src/smartAccount/account.ts +63 -0
  143. package/src/smartAccount/clients.ts +180 -0
  144. package/src/smartAccount/eip5792.ts +187 -0
  145. package/src/smartAccount/send.ts +341 -0
  146. package/src/smartAccount/userOp.ts +180 -0
  147. package/src/subpaths/dividends.ts +47 -0
  148. package/src/subpaths/history.ts +41 -0
  149. package/src/subpaths/permit.ts +39 -0
  150. package/src/subpaths/price.ts +2 -2
  151. package/src/subpaths/rewards.ts +46 -0
  152. package/src/subpaths/simulate.ts +51 -0
  153. package/src/subpaths/smart-account.ts +40 -0
  154. package/src/swap.ts +139 -18
  155. package/src/write/batch.ts +44 -5
  156. package/src/write/collect.ts +60 -1
  157. package/src/write/send.ts +4 -0
  158. package/src/write/signer.ts +5 -1
  159. package/src/write/transfer.ts +17 -10
@@ -0,0 +1,247 @@
1
+ /**
2
+ * 交易历史(子路径 `@w3lib/evm-batch-call/history`):一个地址的转账 / 兑换 / 授权 / 流动性 / 铸造记录,统一成 Activity。
3
+ * 数据来源是可替换的适配器(见 historySources.ts):Blockscout、Etherscan 系(需要 Key)、纯 RPC 日志、Tron 的 TronGrid;
4
+ * `source: [a, b]` 按顺序故障切换。适配器只返回原始记录(交易、转账),这里负责合并、解码和分类。
5
+ */
6
+ import { getAddress } from 'ethers';
7
+ import { getNativeCurrency } from './chains.js';
8
+ import { formatUnits } from './erc20.js';
9
+ import { decodeCalldata } from './simulateDecode.js';
10
+ import { historySourcesFor } from './historySources.js';
11
+ import { isTronChain, toEvmAddress, toTronAddress } from './tron.js';
12
+ export class HistorySourceError extends Error {
13
+ errors;
14
+ constructor(errors) {
15
+ super(`All history sources failed: ${errors.map((e) => `${e.source}: ${e.error instanceof Error ? e.error.message : String(e.error)}`).join(' | ')}`);
16
+ this.errors = errors;
17
+ this.name = 'HistorySourceError';
18
+ }
19
+ }
20
+ const ZERO = '0x0000000000000000000000000000000000000000';
21
+ const CURSOR_RE = /^p:(\d+)$/;
22
+ const short = (a) => `${a.slice(0, 6)}…${a.slice(-4)}`;
23
+ /** 地址的比较键:EVM 小写 0x;Tron 的 T 地址 / 41 开头的 hex 地址转成 0x 小写 */
24
+ function key(ctx, address) {
25
+ if (!ctx.tron)
26
+ return address.toLowerCase();
27
+ try {
28
+ return toEvmAddress(address).toLowerCase();
29
+ }
30
+ catch {
31
+ return address.toLowerCase();
32
+ }
33
+ }
34
+ const show = (ctx, address) => {
35
+ try {
36
+ return ctx.tron ? toTronAddress(address) : getAddress(address);
37
+ }
38
+ catch {
39
+ return address;
40
+ }
41
+ };
42
+ function changesOf(ctx, moves) {
43
+ const net = new Map();
44
+ const partners = new Map();
45
+ let mints = false;
46
+ for (const m of moves) {
47
+ const from = key(ctx, m.from);
48
+ const to = key(ctx, m.to);
49
+ if (from === to)
50
+ continue;
51
+ const sign = to === ctx.me ? 1n : from === ctx.me ? -1n : 0n;
52
+ if (!sign)
53
+ continue;
54
+ const nft = m.tokenId !== undefined;
55
+ const k = `${m.asset}|${m.tokenId ?? ''}`;
56
+ const decimals = nft ? 0 : (m.decimals ?? (m.asset === 'native' ? ctx.native.decimals : 18));
57
+ const entry = net.get(k) ?? { asset: m.asset === 'native' ? 'native' : show(ctx, m.asset), symbol: m.asset === 'native' ? ctx.native.symbol : (m.symbol ?? null), decimals, delta: '0', formatted: '0', ...(nft ? { tokenId: m.tokenId } : {}), raw: 0n };
58
+ entry.raw += sign * m.value;
59
+ net.set(k, entry);
60
+ const other = sign > 0n ? from : to;
61
+ partners.set(other, (partners.get(other) ?? 0n) + m.value);
62
+ if (sign > 0n && from === ZERO && (nft || m.asset !== 'native'))
63
+ mints = true;
64
+ }
65
+ const changes = [...net.values()].filter((e) => e.raw !== 0n).sort((a, b) => Number(a.raw > 0n) - Number(b.raw > 0n)).map(({ raw, ...e }) => ({ ...e, delta: raw.toString(), formatted: formatUnits(raw, e.decimals) }));
66
+ return { changes, mints, partners };
67
+ }
68
+ const amount = (c) => `${c.formatted.replace('-', '')} ${c.symbol ?? (c.tokenId === undefined ? short(c.asset) : `#${c.tokenId}`)}`;
69
+ function classify(ctx, tx, decoded, changes, mints) {
70
+ const outs = changes.filter((c) => c.delta.startsWith('-'));
71
+ const ins = changes.filter((c) => !c.delta.startsWith('-'));
72
+ const sender = tx !== undefined && key(ctx, tx.from) === ctx.me;
73
+ if (sender && decoded?.type === 'approve')
74
+ return 'approve';
75
+ if (sender && decoded?.type === 'liquidity')
76
+ return 'liquidity';
77
+ if (mints && ins.length)
78
+ return 'mint';
79
+ if (outs.length && ins.length && new Set([...outs, ...ins].map((c) => `${c.asset}|${c.tokenId ?? ''}`)).size > 1)
80
+ return 'swap';
81
+ if (decoded?.type === 'swap')
82
+ return 'swap';
83
+ if (changes.length || (tx && tx.input === '0x') || decoded?.type === 'transfer')
84
+ return 'transfer';
85
+ if (decoded)
86
+ return 'contract';
87
+ return 'other';
88
+ }
89
+ export function buildActivities(page, ctx, minPos = 0) {
90
+ const groups = new Map();
91
+ const group = (hash, pos, time, block) => {
92
+ let g = groups.get(hash);
93
+ if (!g)
94
+ groups.set(hash, (g = { moves: [], pos, time, block }));
95
+ return g;
96
+ };
97
+ for (const tx of page.txs)
98
+ if (tx.pos >= minPos)
99
+ group(tx.hash, tx.pos, tx.time, tx.block).tx = tx;
100
+ for (const m of page.moves)
101
+ if (m.pos >= minPos)
102
+ group(m.hash, m.pos, m.time, m.block).moves.push(m);
103
+ const items = [];
104
+ for (const [hash, g] of groups) {
105
+ const { tx } = g;
106
+ const moves = [...g.moves];
107
+ // 顶层的主币转账(explorer 的转账列表里没有);失败的交易没有任何资产变化
108
+ if (tx?.ok && tx.value > 0n && tx.to)
109
+ moves.push({ hash, pos: g.pos, time: g.time, asset: 'native', from: tx.from, to: tx.to, value: tx.value });
110
+ const { changes, mints, partners } = tx && !tx.ok ? { changes: [], mints: false, partners: new Map() } : changesOf(ctx, moves);
111
+ const decoded = tx && tx.input !== '0x' && tx.to ? decodeCalldata(tx.input, { to: show(ctx, tx.to), chainId: ctx.chainId, value: tx.value }) : null;
112
+ const type = classify(ctx, tx, decoded, changes, mints);
113
+ const sender = tx !== undefined && key(ctx, tx.from) === ctx.me;
114
+ const outs = changes.filter((c) => c.delta.startsWith('-'));
115
+ const ins = changes.filter((c) => !c.delta.startsWith('-'));
116
+ let direction;
117
+ if (type === 'transfer' || type === 'mint') {
118
+ if (tx?.to && key(ctx, tx.to) === ctx.me && sender)
119
+ direction = 'self';
120
+ else if (outs.length && !ins.length)
121
+ direction = 'out';
122
+ else if (ins.length && !outs.length)
123
+ direction = 'in';
124
+ else if (!changes.length)
125
+ direction = sender ? 'out' : 'in';
126
+ }
127
+ // 对方:转账是出入金额最大的那一方;其他是合约(自己发起时)或发起者
128
+ let counterparty;
129
+ if (type === 'transfer' || type === 'mint') {
130
+ const top = [...partners.entries()].sort((a, b) => (a[1] > b[1] ? -1 : 1))[0]?.[0];
131
+ counterparty = top && top !== ZERO ? top : tx?.to && !sender ? tx.from : (tx?.to ?? undefined);
132
+ }
133
+ else if (tx) {
134
+ counterparty = sender ? (tx.to ?? undefined) : tx.from;
135
+ }
136
+ const protocol = decoded?.protocol;
137
+ const failed = tx ? !tx.ok : false;
138
+ let label;
139
+ if (failed)
140
+ label = `Failed ${decoded?.method ?? 'transaction'}${protocol ? ` on ${protocol}` : ''}`;
141
+ else if (type === 'swap')
142
+ label = `Swapped ${outs.map(amount).join(' + ')} for ${ins.map(amount).join(' + ')}${protocol ? ` on ${protocol}` : ''}`;
143
+ else if (type === 'approve')
144
+ label = `Approved ${decoded?.args?.spender ? short(String(decoded.args.spender)) : (decoded?.method ?? 'spending')}${decoded?.args?.amount === '0' || decoded?.args?.approved === false ? ' (revoked)' : ''}`;
145
+ else if (type === 'liquidity')
146
+ label = `${decoded?.method ?? 'Liquidity'}${protocol ? ` on ${protocol}` : ''}: ${changes.map((c) => `${c.delta.startsWith('-') ? '-' : '+'}${amount(c)}`).join(', ')}`;
147
+ else if (type === 'mint')
148
+ label = `Minted ${ins.map(amount).join(' + ')}`;
149
+ else if (type === 'transfer') {
150
+ const cp = counterparty ? short(show(ctx, counterparty)) : '';
151
+ label = direction === 'in' ? `Received ${ins.map(amount).join(' + ')}${cp ? ` from ${cp}` : ''}` : direction === 'self' ? 'Sent to self' : `Sent ${outs.map(amount).join(' + ') || 'a transaction'}${cp ? ` to ${cp}` : ''}`;
152
+ }
153
+ else
154
+ label = decoded?.summary ?? 'Transaction';
155
+ items.push({
156
+ hash,
157
+ time: g.time,
158
+ ...(g.block === undefined ? {} : { block: g.block }),
159
+ status: failed ? 'failed' : 'success',
160
+ type,
161
+ ...(direction ? { direction } : {}),
162
+ ...(counterparty ? { counterparty: show(ctx, counterparty) } : {}),
163
+ label,
164
+ changes,
165
+ ...(sender && tx?.fee !== undefined ? { fee: tx.fee.toString() } : {}),
166
+ ...(protocol ? { protocol } : {}),
167
+ ...(decoded?.method ? { method: decoded.method } : {}),
168
+ });
169
+ }
170
+ // 位置新的在前(同一个位置保持原来的顺序)
171
+ const posOf = new Map([...groups].map(([h, g]) => [h, g.pos]));
172
+ return items.sort((a, b) => posOf.get(b.hash) - posOf.get(a.hash));
173
+ }
174
+ // ---------------------------------------------------------------------------
175
+ // 入口
176
+ // ---------------------------------------------------------------------------
177
+ /**
178
+ * 查一个地址的历史活动(最新的在前),分页用 nextCursor。
179
+ *
180
+ * ```ts
181
+ * const page = await history(provider, user, { limit: 20 }) // Blockscout,失败换 RPC 日志
182
+ * const next = await history(provider, user, { cursor: page.nextCursor, types: ['swap'] })
183
+ * await history(provider, user, { source: ['etherscan', 'rpc'], apiKey }) // Etherscan v2 的 Key 覆盖多条链
184
+ * ```
185
+ */
186
+ export async function history(provider, address, options = {}) {
187
+ const chainId = await provider.getChainId();
188
+ const tron = isTronChain(chainId);
189
+ const me = tron ? toTronAddress(address) : address.toLowerCase();
190
+ if (!tron && !/^0x[0-9a-f]{40}$/.test(me))
191
+ throw new Error(`Invalid address: ${address}`);
192
+ const limit = Math.max(1, Math.min(200, Math.floor(options.limit ?? 25)));
193
+ const cursor = options.cursor === undefined ? undefined : Number(CURSOR_RE.exec(options.cursor)?.[1] ?? Number.NaN);
194
+ if (cursor !== undefined && !Number.isFinite(cursor))
195
+ throw new Error(`Invalid cursor: ${options.cursor}`);
196
+ const adapters = historySourcesFor(chainId, options.source, options.apiKey);
197
+ if (!adapters.length)
198
+ throw new Error(`No history source for chain ${chainId}: pass options.source`);
199
+ const nativeCurrency = getNativeCurrency(chainId);
200
+ const ctx = { chainId, tron, me: tron ? toEvmAddress(me).toLowerCase() : me, native: { symbol: nativeCurrency?.symbol ?? null, decimals: nativeCurrency?.decimals ?? 18 } };
201
+ const query = (endPos) => ({ address: me, chainId, limit: Math.max(limit, 10), ...(endPos === undefined ? {} : { endPos }), nft: options.nft ?? true, provider, fetch: options.fetch ?? fetch, ...(options.signal ? { signal: options.signal } : {}) });
202
+ const items = [];
203
+ const seen = new Set();
204
+ let pos = cursor;
205
+ let used = adapters[0]?.name ?? '';
206
+ let more = true;
207
+ for (let pages = 0; pages < (options.maxPages ?? 5) && more; pages++) {
208
+ let page;
209
+ const errors = [];
210
+ for (const adapter of adapters) {
211
+ try {
212
+ page = await adapter.fetch(query(pos));
213
+ used = adapter.name;
214
+ break;
215
+ }
216
+ catch (err) {
217
+ if (options.signal?.aborted)
218
+ throw err;
219
+ errors.push({ source: adapter.name, error: err });
220
+ }
221
+ }
222
+ if (!page)
223
+ throw new HistorySourceError(errors);
224
+ // 整页都落在同一个位置、被条数截断时放不下完整记录:宁可收下这一页,也不能原地打转
225
+ const hasComplete = [...page.txs, ...page.moves].some((r) => r.pos >= page.completeFrom);
226
+ const batch = buildActivities(page, ctx, hasComplete ? page.completeFrom : 0);
227
+ for (const a of batch) {
228
+ if (seen.has(a.hash))
229
+ continue;
230
+ seen.add(a.hash);
231
+ if (options.before !== undefined && a.time >= options.before)
232
+ continue;
233
+ if (options.after !== undefined && a.time < options.after)
234
+ continue;
235
+ if (options.types && !options.types.includes(a.type))
236
+ continue;
237
+ items.push(a);
238
+ }
239
+ const oldest = batch.length ? Math.min(...batch.map((a) => a.time)) : Number.POSITIVE_INFINITY;
240
+ pos = hasComplete ? page.next : page.next === undefined ? undefined : page.next - 1;
241
+ more = pos !== undefined && pos >= 0 && !(options.after !== undefined && oldest < options.after);
242
+ if (items.length >= limit)
243
+ break;
244
+ }
245
+ return { items, ...(more && pos !== undefined ? { nextCursor: `p:${pos}` } : {}), source: used };
246
+ }
247
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import type { HistoryAdapter } from './history.js';
2
+ export type HistorySource = 'blockscout' | 'etherscan' | 'rpc' | 'trongrid' | HistoryAdapter;
3
+ /**
4
+ * 各链的 Blockscout 实例。2026-10 验证过可用:1、10、100、534352、324、130、42220、34443、57073;
5
+ * 8453 / 42161 / 137 的官方实例前面有 Cloudflare 人机验证,脚本请求可能被 403(此时自动换下一个来源)
6
+ */
7
+ export declare const BLOCKSCOUT_APIS: Readonly<Record<number, string>>;
8
+ /** Blockscout / Etherscan 系适配器:`url` 是接口地址(可以带 chainid 等固定参数) */
9
+ export declare function explorerSource(name: string, url: (chainId: number) => string | undefined, secret?: string): HistoryAdapter;
10
+ /** Blockscout:`urls` 覆盖 / 补充内置的实例表(chainId → 接口地址) */
11
+ export declare function blockscoutSource(urls?: Readonly<Record<number, string>>): HistoryAdapter;
12
+ /** Etherscan v2:一个 Key 覆盖多条链;`baseUrl` 可换成其他 Etherscan 兼容的地址 */
13
+ export declare function etherscanSource(options: {
14
+ apiKey: string;
15
+ baseUrl?: string;
16
+ }): HistoryAdapter;
17
+ export interface RpcSourceOptions {
18
+ /** 每次 getLogs 的区块窗口,默认 2000(节点嫌大时自动对半缩小) */
19
+ window?: number;
20
+ /** 一次最多扫多少个窗口,默认 10(凑够条数就提前停;扫不到记录时用 nextCursor 继续) */
21
+ maxWindows?: number;
22
+ }
23
+ export declare function rpcSource(options?: RpcSourceOptions): HistoryAdapter;
24
+ export interface TrongridSourceOptions {
25
+ /** 全节点地址,默认 https://api.trongrid.io */
26
+ host?: string;
27
+ apiKey?: string;
28
+ }
29
+ export declare function trongridSource(options?: TrongridSourceOptions): HistoryAdapter;
30
+ /** 来源列表 → 适配器:名字按默认配置创建;没传时按链选默认(传了 apiKey 时 etherscan 排最前) */
31
+ export declare function historySourcesFor(chainId: number, source: HistorySource | readonly HistorySource[] | undefined, apiKey?: string): HistoryAdapter[];
32
+ //# sourceMappingURL=historySources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"historySources.d.ts","sourceRoot":"","sources":["../src/historySources.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,cAAc,CAAA;AAIzF,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,WAAW,GAAG,KAAK,GAAG,UAAU,GAAG,cAAc,CAAA;AAE5F;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAa5D,CAAA;AA6DD,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,cAAc,CAoC1H;AAED,sDAAsD;AACtD,wBAAgB,gBAAgB,CAAC,IAAI,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,GAAG,cAAc,CAE5F;AAED,gEAAgE;AAChE,wBAAgB,eAAe,CAAC,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,cAAc,CAG7F;AAYD,MAAM,WAAW,gBAAgB;IAC/B,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAmBD,wBAAgB,SAAS,CAAC,OAAO,GAAE,gBAAqB,GAAG,cAAc,CAsFxE;AAMD,MAAM,WAAW,qBAAqB;IACpC,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAsBD,wBAAgB,cAAc,CAAC,OAAO,GAAE,qBAA0B,GAAG,cAAc,CAyClF;AAID,+DAA+D;AAC/D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,SAAS,aAAa,EAAE,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,cAAc,EAAE,CAalJ"}
@@ -0,0 +1,319 @@
1
+ /**
2
+ * 历史记录的数据来源(适配器)。每个适配器按 HistoryQuery 返回原始的交易和转账(RawPage),合并和分类由 history.ts 完成。
3
+ *
4
+ * - blockscout:各链的 Blockscout 实例(免 Key)的 Etherscan 兼容接口(`/api?module=account&action=txlist …`)。
5
+ * 没用 v2 REST(`/api/v2/addresses/…`):同样的数据,v2 每页带大量标签 / 元数据(50 条约 500KB),这个接口只有必要字段,
6
+ * 并且与 Etherscan 系完全相同,一套解析两处用
7
+ * - etherscan:Etherscan v2(一个 Key 覆盖 60+ 条链)及其他 Etherscan 兼容的接口
8
+ * - rpc:只用 `eth_getLogs`(ERC20 / 721 / 1155 的 Transfer 日志按地址所在的 topic 查)+ 交易 / 回执 / 区块。没有主币转账(顶层的 value 只有
9
+ * 带代币转账的交易才有)、没有内部交易;要按区块窗口往回扫,稀疏的地址一次调用可能扫不到记录(返回 nextCursor 继续)
10
+ * - trongrid:TronGrid 的 `/v1/accounts/{address}/transactions` 和 `/transactions/trc20`(内部的 TRX 转账查不到)
11
+ */
12
+ import { AbiCoder, id, zeroPadValue } from 'ethers';
13
+ import { HttpStatusError, fetchJson } from './http.js';
14
+ import { errorText } from './fallback.js';
15
+ import { isTronChain, toTronAddress } from './tron.js';
16
+ /**
17
+ * 各链的 Blockscout 实例。2026-10 验证过可用:1、10、100、534352、324、130、42220、34443、57073;
18
+ * 8453 / 42161 / 137 的官方实例前面有 Cloudflare 人机验证,脚本请求可能被 403(此时自动换下一个来源)
19
+ */
20
+ export const BLOCKSCOUT_APIS = {
21
+ 1: 'https://eth.blockscout.com/api',
22
+ 10: 'https://explorer.optimism.io/api',
23
+ 100: 'https://gnosisscan.io/api',
24
+ 130: 'https://unichain.blockscout.com/api',
25
+ 137: 'https://polygon.blockscout.com/api',
26
+ 324: 'https://zksync.blockscout.com/api',
27
+ 8453: 'https://base.blockscout.com/api',
28
+ 34443: 'https://explorer.mode.network/api',
29
+ 42161: 'https://arbitrum.blockscout.com/api',
30
+ 42220: 'https://celo.blockscout.com/api',
31
+ 57073: 'https://explorer.inkonchain.com/api',
32
+ 534352: 'https://scrollscan.com/api',
33
+ };
34
+ const num = (v) => Number(v ?? 0);
35
+ const big = (v) => {
36
+ try {
37
+ return BigInt(String(v ?? '0') || '0');
38
+ }
39
+ catch {
40
+ return 0n;
41
+ }
42
+ };
43
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
44
+ /** 各个列表(每个都按位置从新到旧、最多 limit 条)合成一页:列表满了说明后面还有,被截断的位置要留到下一页 */
45
+ function paged(txs, moves, lists, limit) {
46
+ const full = lists.filter((l) => l.count >= limit && l.count > 0);
47
+ if (!full.length)
48
+ return { txs, moves, completeFrom: 0 };
49
+ const last = Math.max(...full.map((l) => l.lastPos));
50
+ return { txs, moves, completeFrom: last + 1, next: last };
51
+ }
52
+ // ---------------------------------------------------------------------------
53
+ // Etherscan 兼容接口(Blockscout / Etherscan v2)
54
+ // ---------------------------------------------------------------------------
55
+ const ACTIONS = { txs: 'txlist', internal: 'txlistinternal', tokens: 'tokentx', nfts: 'tokennfttx', multi: 'token1155tx' };
56
+ async function explorerCall(q, base, action, secret) {
57
+ const url = new URL(base);
58
+ const params = { module: 'account', action, address: q.address, startblock: '0', page: '1', offset: String(q.limit), sort: 'desc' };
59
+ if (q.endPos !== undefined)
60
+ params.endblock = String(q.endPos);
61
+ for (const [k, v] of Object.entries(params))
62
+ url.searchParams.set(k, v);
63
+ for (let attempt = 0;; attempt++) {
64
+ let json;
65
+ try {
66
+ json = await fetchJson(q.fetch, url.toString(), undefined, secret);
67
+ }
68
+ catch (err) {
69
+ // 公共实例限流(HTTP 429):等一下重试
70
+ if (err instanceof HttpStatusError && err.status === 429 && attempt < 3) {
71
+ await sleep(1200 * (attempt + 1));
72
+ continue;
73
+ }
74
+ throw err;
75
+ }
76
+ if (Array.isArray(json.result)) {
77
+ const rows = json.result;
78
+ // 个别实例的某些列表不认 endblock,返回的还是最新的记录:翻页会重复 / 漏记录,换下一个来源
79
+ if (q.endPos !== undefined && rows.some((r) => num(r.blockNumber) > q.endPos))
80
+ throw new Error(`${new URL(base).host}: ${action} ignores endblock`);
81
+ return rows;
82
+ }
83
+ const text = typeof json.result === 'string' ? json.result : (json.message ?? 'unexpected response');
84
+ if (/no (transactions|records|token)/i.test(`${json.message} ${text}`))
85
+ return [];
86
+ if (/rate limit|too many/i.test(text) && attempt < 3) {
87
+ await sleep(1200 * (attempt + 1));
88
+ continue;
89
+ }
90
+ throw new Error(`${new URL(base).host}: ${text}`);
91
+ }
92
+ }
93
+ /** Blockscout / Etherscan 系适配器:`url` 是接口地址(可以带 chainid 等固定参数) */
94
+ export function explorerSource(name, url, secret) {
95
+ return {
96
+ name,
97
+ supports: (chainId) => url(chainId) !== undefined,
98
+ async fetch(q) {
99
+ const base = url(q.chainId);
100
+ if (!base)
101
+ throw new Error(`${name}: chain ${q.chainId} is not supported`);
102
+ const call = (action) => explorerCall(q, base, action, secret);
103
+ // 依次请求(公共实例按请求频率限流,并发会触发 429)
104
+ const txRows = await call(ACTIONS.txs);
105
+ const internalRows = await call(ACTIONS.internal);
106
+ const tokenRows = await call(ACTIONS.tokens);
107
+ const nftRows = q.nft ? await call(ACTIONS.nfts) : [];
108
+ const multiRows = q.nft ? await call(ACTIONS.multi) : [];
109
+ const txs = txRows.map((r) => ({
110
+ hash: r.hash,
111
+ pos: num(r.blockNumber),
112
+ block: num(r.blockNumber),
113
+ time: num(r.timeStamp),
114
+ from: r.from,
115
+ to: r.to || r.contractAddress || null,
116
+ value: big(r.value),
117
+ input: r.input || '0x',
118
+ ok: r.isError !== '1' && r.txreceipt_status !== '0',
119
+ fee: big(r.gasUsed) * big(r.gasPrice),
120
+ }));
121
+ const moves = [];
122
+ const base0 = (r) => ({ hash: (r.hash ?? r.transactionHash), pos: num(r.blockNumber), block: num(r.blockNumber), time: num(r.timeStamp), from: r.from, to: r.to });
123
+ for (const r of internalRows)
124
+ if (r.isError !== '1' && big(r.value) > 0n)
125
+ moves.push({ ...base0(r), asset: 'native', value: big(r.value) });
126
+ for (const r of tokenRows)
127
+ moves.push({ ...base0(r), asset: r.contractAddress, value: big(r.value), symbol: r.tokenSymbol || null, decimals: r.tokenDecimal ? num(r.tokenDecimal) : null });
128
+ for (const r of nftRows)
129
+ moves.push({ ...base0(r), asset: r.contractAddress, value: 1n, tokenId: r.tokenID, symbol: r.tokenSymbol || null });
130
+ for (const r of multiRows)
131
+ moves.push({ ...base0(r), asset: r.contractAddress, value: big(r.tokenValue || '1'), tokenId: r.tokenID, symbol: r.tokenSymbol || null });
132
+ const lists = [txRows, internalRows, tokenRows, nftRows, multiRows].map((rows) => ({ count: rows.length, lastPos: rows.length ? Math.min(...rows.map((r) => num(r.blockNumber))) : 0 }));
133
+ return paged(txs, moves, lists, q.limit);
134
+ },
135
+ };
136
+ }
137
+ /** Blockscout:`urls` 覆盖 / 补充内置的实例表(chainId → 接口地址) */
138
+ export function blockscoutSource(urls = {}) {
139
+ return explorerSource('blockscout', (chainId) => urls[chainId] ?? BLOCKSCOUT_APIS[chainId]);
140
+ }
141
+ /** Etherscan v2:一个 Key 覆盖多条链;`baseUrl` 可换成其他 Etherscan 兼容的地址 */
142
+ export function etherscanSource(options) {
143
+ if (!options.apiKey)
144
+ throw new Error('etherscan source requires an apiKey');
145
+ return explorerSource('etherscan', (chainId) => `${options.baseUrl ?? 'https://api.etherscan.io/v2/api'}?chainid=${chainId}&apikey=${encodeURIComponent(options.apiKey)}`, options.apiKey);
146
+ }
147
+ // ---------------------------------------------------------------------------
148
+ // 纯 RPC 日志
149
+ // ---------------------------------------------------------------------------
150
+ const TRANSFER = id('Transfer(address,address,uint256)');
151
+ const SINGLE = id('TransferSingle(address,address,address,uint256,uint256)');
152
+ const BATCH = id('TransferBatch(address,address,address,uint256[],uint256[])');
153
+ const coder = AbiCoder.defaultAbiCoder();
154
+ const topicAddr = (hex) => `0x${hex.slice(-40)}`.toLowerCase();
155
+ async function pool(items, size, work) {
156
+ const out = new Array(items.length);
157
+ let next = 0;
158
+ await Promise.all(Array.from({ length: Math.min(size, items.length) }, async () => {
159
+ while (next < items.length) {
160
+ const i = next++;
161
+ out[i] = await work(items[i]);
162
+ }
163
+ }));
164
+ return out;
165
+ }
166
+ export function rpcSource(options = {}) {
167
+ const window = Math.max(10, Math.floor(options.window ?? 2000));
168
+ const maxWindows = Math.max(1, Math.floor(options.maxWindows ?? 10));
169
+ return {
170
+ name: 'rpc',
171
+ supports: (chainId) => !isTronChain(chainId),
172
+ async fetch(q) {
173
+ const addr = zeroPadValue(q.address, 32);
174
+ const filters = [[TRANSFER, null, addr], [TRANSFER, addr], ...(q.nft ? [[[SINGLE, BATCH], null, addr], [[SINGLE, BATCH], null, null, addr]] : [])];
175
+ const logsIn = async (from, to, depth = 0) => {
176
+ try {
177
+ const lists = await Promise.all(filters.map((topics) => q.provider.getLogs({ fromBlock: from, toBlock: to, topics })));
178
+ // ERC-721 的 Transfer 和 ERC-20 同一个 topic(4 个 topic):不要 NFT 时丢掉
179
+ return lists.flat().filter((l) => q.nft || l.topics.length === 3);
180
+ }
181
+ catch (err) {
182
+ // 范围太大 / 结果太多:对半拆开重试(最多拆 4 层)
183
+ if (depth < 4 && to > from && /range|too (large|wide|big|many)|exceed|limit|more than|response size/i.test(errorText(err))) {
184
+ const mid = from + Math.floor((to - from) / 2);
185
+ return [...(await logsIn(from, mid, depth + 1)), ...(await logsIn(mid + 1, to, depth + 1))];
186
+ }
187
+ throw err;
188
+ }
189
+ };
190
+ let to = q.endPos ?? (await q.provider.getBlockNumber());
191
+ let from = to;
192
+ const logs = [];
193
+ for (let w = 0; w < maxWindows; w++) {
194
+ from = Math.max(0, to - window + 1);
195
+ logs.push(...(await logsIn(from, to)));
196
+ if (new Set(logs.map((l) => l.transactionHash)).size >= q.limit || from === 0)
197
+ break;
198
+ to = from - 1;
199
+ }
200
+ const next = from > 0 ? from - 1 : undefined;
201
+ // 只保留最新的 limit 笔交易;被丢掉的更老的交易所在的区块留给下一页
202
+ const byBlock = new Map();
203
+ for (const l of logs)
204
+ byBlock.set(l.transactionHash, Math.max(byBlock.get(l.transactionHash) ?? 0, l.blockNumber));
205
+ const hashes = [...byBlock.entries()].sort((a, b) => b[1] - a[1]);
206
+ const kept = hashes.slice(0, q.limit);
207
+ const dropped = hashes.slice(q.limit);
208
+ const completeFrom = dropped.length ? dropped[0][1] + 1 : from;
209
+ const keepSet = new Set(kept.map(([h]) => h));
210
+ const { runOnNodes } = await import('./write/rpc.js');
211
+ const node = (method, fn) => runOnNodes(q.provider.rpc, method, (n) => fn(n));
212
+ const blockTimes = new Map();
213
+ const blocks = [...new Set(kept.map(([, b]) => b))];
214
+ await pool(blocks, 6, async (b) => {
215
+ blockTimes.set(b, (await node('getBlock', (n) => n.getBlock(b)))?.timestamp ?? 0);
216
+ });
217
+ const txs = (await pool(kept, 6, async ([hash, block]) => {
218
+ const [tx, receipt] = await Promise.all([node('getTransaction', (n) => n.getTransaction(hash)), node('getTransactionReceipt', (n) => n.getTransactionReceipt(hash))]);
219
+ if (!tx)
220
+ return null;
221
+ return { hash, pos: block, block, time: blockTimes.get(block) ?? 0, from: tx.from, to: tx.to, value: tx.value, input: tx.data, ok: receipt?.status !== 0, ...(receipt ? { fee: receipt.gasUsed * receipt.gasPrice } : {}) };
222
+ })).filter((t) => t !== null);
223
+ const moves = [];
224
+ for (const l of logs) {
225
+ if (!keepSet.has(l.transactionHash))
226
+ continue;
227
+ const base = { hash: l.transactionHash, pos: l.blockNumber, block: l.blockNumber, time: blockTimes.get(l.blockNumber) ?? 0, asset: l.address.toLowerCase() };
228
+ if (l.topics[0] === TRANSFER && l.topics.length === 3)
229
+ moves.push({ ...base, from: topicAddr(l.topics[1]), to: topicAddr(l.topics[2]), value: big(l.data) });
230
+ else if (l.topics[0] === TRANSFER && l.topics.length === 4)
231
+ moves.push({ ...base, from: topicAddr(l.topics[1]), to: topicAddr(l.topics[2]), value: 1n, tokenId: BigInt(l.topics[3]).toString() });
232
+ else if (l.topics[0] === SINGLE) {
233
+ const [tokenId, value] = coder.decode(['uint256', 'uint256'], l.data);
234
+ moves.push({ ...base, from: topicAddr(l.topics[2]), to: topicAddr(l.topics[3]), value, tokenId: tokenId.toString() });
235
+ }
236
+ else if (l.topics[0] === BATCH) {
237
+ const [ids, values] = coder.decode(['uint256[]', 'uint256[]'], l.data);
238
+ ids.forEach((tokenId, i) => moves.push({ ...base, from: topicAddr(l.topics[2]), to: topicAddr(l.topics[3]), value: values[i], tokenId: tokenId.toString() }));
239
+ }
240
+ }
241
+ // 代币的 symbol / decimals(一次 multicall);NFT 合约没有 decimals,symbol 能读到就用
242
+ const assets = [...new Set(moves.map((m) => m.asset))];
243
+ if (assets.length) {
244
+ const details = await q.provider.tokens(assets, { fields: ['symbol', 'decimals'] }).catch(() => []);
245
+ const meta = new Map(assets.map((a, i) => [a, details[i]]));
246
+ for (const m of moves) {
247
+ const d = meta.get(m.asset);
248
+ m.symbol = d?.symbol ?? null;
249
+ m.decimals = typeof d?.decimals === 'number' ? d.decimals : null;
250
+ }
251
+ }
252
+ return { txs, moves, completeFrom: dropped.length ? completeFrom : from, ...(dropped.length ? { next: completeFrom - 1 } : next === undefined ? {} : { next }) };
253
+ },
254
+ };
255
+ }
256
+ export function trongridSource(options = {}) {
257
+ const host = (options.host ?? 'https://api.trongrid.io').replace(/\/+$/, '');
258
+ return {
259
+ name: 'trongrid',
260
+ supports: isTronChain,
261
+ async fetch(q) {
262
+ const headers = options.apiKey ? { 'TRON-PRO-API-KEY': options.apiKey } : {};
263
+ const qs = `limit=${Math.min(200, q.limit)}&order_by=block_timestamp,desc&only_confirmed=true${q.endPos === undefined ? '' : `&max_timestamp=${q.endPos}`}`;
264
+ const get = async (path) => {
265
+ const json = await fetchJson(q.fetch, `${host}/v1/accounts/${q.address}/${path}?${qs}`, { headers }, options.apiKey);
266
+ if (json.success === false || !Array.isArray(json.data))
267
+ throw new Error(`trongrid: ${json.error ?? 'unexpected response'}`);
268
+ return json.data;
269
+ };
270
+ const [txRows, trc20Rows] = await Promise.all([get('transactions'), get('transactions/trc20')]);
271
+ const txs = txRows.map((r) => {
272
+ const c = r.raw_data?.contract?.[0];
273
+ const v = (c?.parameter?.value ?? {});
274
+ const hexAddr = (a) => (typeof a === 'string' && a ? toTronAddress(a) : undefined);
275
+ const trigger = c?.type === 'TriggerSmartContract';
276
+ const ms = r.block_timestamp ?? 0;
277
+ return {
278
+ hash: r.txID,
279
+ pos: ms,
280
+ ...(r.blockNumber === undefined ? {} : { block: r.blockNumber }),
281
+ time: Math.floor(ms / 1000),
282
+ from: hexAddr(v.owner_address) ?? q.address,
283
+ to: hexAddr(trigger ? v.contract_address : v.to_address) ?? null,
284
+ value: big(trigger ? v.call_value : v.amount),
285
+ input: trigger && typeof v.data === 'string' && v.data ? `0x${v.data}` : '0x',
286
+ ok: r.ret?.[0]?.contractRet === 'SUCCESS',
287
+ fee: big(r.ret?.[0]?.fee ?? (r.net_fee ?? 0) + (r.energy_fee ?? 0)),
288
+ };
289
+ });
290
+ const moves = trc20Rows
291
+ .filter((r) => (r.type ?? 'Transfer') === 'Transfer')
292
+ .map((r) => ({ hash: r.transaction_id, pos: r.block_timestamp, time: Math.floor(r.block_timestamp / 1000), asset: r.token_info?.address ?? '', from: r.from, to: r.to, value: big(r.value), symbol: r.token_info?.symbol ?? null, decimals: r.token_info?.decimals ?? null }))
293
+ .filter((m) => m.asset);
294
+ const lists = [txRows.map((r) => r.block_timestamp ?? 0), trc20Rows.map((r) => r.block_timestamp)].map((ts) => ({ count: ts.length, lastPos: ts.length ? Math.min(...ts) : 0 }));
295
+ return paged(txs, moves, lists, Math.min(200, q.limit));
296
+ },
297
+ };
298
+ }
299
+ // ---------------------------------------------------------------------------
300
+ /** 来源列表 → 适配器:名字按默认配置创建;没传时按链选默认(传了 apiKey 时 etherscan 排最前) */
301
+ export function historySourcesFor(chainId, source, apiKey) {
302
+ const tron = isTronChain(chainId);
303
+ const defaults = tron ? ['trongrid'] : [...(apiKey ? ['etherscan'] : []), ...(BLOCKSCOUT_APIS[chainId] ? ['blockscout'] : []), 'rpc'];
304
+ const list = source === undefined ? defaults : Array.isArray(source) ? source : [source];
305
+ return list
306
+ .map((s) => {
307
+ if (typeof s !== 'string')
308
+ return s;
309
+ if (s === 'blockscout')
310
+ return blockscoutSource();
311
+ if (s === 'etherscan')
312
+ return etherscanSource({ apiKey: apiKey ?? '' });
313
+ if (s === 'rpc')
314
+ return rpcSource();
315
+ return trongridSource({ ...(apiKey ? { apiKey } : {}) });
316
+ })
317
+ .filter((a) => a.supports?.(chainId) !== false);
318
+ }
319
+ //# sourceMappingURL=historySources.js.map