@w3lib/evm-batch-call 0.1.0 → 0.3.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 (77) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +194 -12
  3. package/dist/aggregate.d.ts +5 -2
  4. package/dist/aggregate.d.ts.map +1 -1
  5. package/dist/aggregate.js.map +1 -1
  6. package/dist/call.d.ts +8 -2
  7. package/dist/call.d.ts.map +1 -1
  8. package/dist/call.js +25 -1
  9. package/dist/call.js.map +1 -1
  10. package/dist/chains.d.ts +13 -0
  11. package/dist/chains.d.ts.map +1 -0
  12. package/dist/chains.js +35 -0
  13. package/dist/chains.js.map +1 -0
  14. package/dist/detect.d.ts +23 -0
  15. package/dist/detect.d.ts.map +1 -0
  16. package/dist/detect.js +178 -0
  17. package/dist/detect.js.map +1 -0
  18. package/dist/erc20.d.ts +83 -7
  19. package/dist/erc20.d.ts.map +1 -1
  20. package/dist/erc20.js +36 -5
  21. package/dist/erc20.js.map +1 -1
  22. package/dist/fallback.d.ts +25 -0
  23. package/dist/fallback.d.ts.map +1 -1
  24. package/dist/fallback.js +123 -21
  25. package/dist/fallback.js.map +1 -1
  26. package/dist/index.d.ts +10 -3
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +9 -2
  29. package/dist/index.js.map +1 -1
  30. package/dist/nft.d.ts +71 -0
  31. package/dist/nft.d.ts.map +1 -0
  32. package/dist/nft.js +194 -0
  33. package/dist/nft.js.map +1 -0
  34. package/dist/owner.d.ts +136 -0
  35. package/dist/owner.d.ts.map +1 -0
  36. package/dist/owner.js +501 -0
  37. package/dist/owner.js.map +1 -0
  38. package/dist/provider.d.ts +112 -12
  39. package/dist/provider.d.ts.map +1 -1
  40. package/dist/provider.js +343 -48
  41. package/dist/provider.js.map +1 -1
  42. package/dist/scan.d.ts +59 -0
  43. package/dist/scan.d.ts.map +1 -0
  44. package/dist/scan.js +245 -0
  45. package/dist/scan.js.map +1 -0
  46. package/dist/shortcuts.d.ts +113 -0
  47. package/dist/shortcuts.d.ts.map +1 -0
  48. package/dist/shortcuts.js +193 -0
  49. package/dist/shortcuts.js.map +1 -0
  50. package/dist/source.d.ts +23 -1
  51. package/dist/source.d.ts.map +1 -1
  52. package/dist/source.js +110 -3
  53. package/dist/source.js.map +1 -1
  54. package/dist/tron.d.ts +6 -0
  55. package/dist/tron.d.ts.map +1 -1
  56. package/dist/tron.js +16 -1
  57. package/dist/tron.js.map +1 -1
  58. package/dist/util.d.ts +6 -0
  59. package/dist/util.d.ts.map +1 -0
  60. package/dist/util.js +20 -0
  61. package/dist/util.js.map +1 -0
  62. package/package.json +3 -2
  63. package/src/aggregate.ts +5 -2
  64. package/src/call.ts +32 -3
  65. package/src/chains.ts +46 -0
  66. package/src/detect.ts +193 -0
  67. package/src/erc20.ts +111 -9
  68. package/src/fallback.ts +127 -23
  69. package/src/index.ts +90 -4
  70. package/src/nft.ts +298 -0
  71. package/src/owner.ts +651 -0
  72. package/src/provider.ts +458 -54
  73. package/src/scan.ts +293 -0
  74. package/src/shortcuts.ts +278 -0
  75. package/src/source.ts +118 -3
  76. package/src/tron.ts +21 -2
  77. package/src/util.ts +20 -0
package/src/erc20.ts CHANGED
@@ -28,16 +28,118 @@ export interface Erc20Contract extends Contract {
28
28
  transferFrom: Method<[from: string, to: string, amount: bigint | string | number], boolean>
29
29
  }
30
30
 
31
- export interface TokenInfo {
32
- address: string
33
- /** 读取失败(如 symbol 是 bytes32 的老代币)时为 null */
34
- symbol: string | null
31
+ /** allowances() / getAllowances() 的单项结果 */
32
+ export interface TokenAllowance {
33
+ token: string
34
+ spender: string
35
+ /** 主币不需要授权,视为无限额度 */
36
+ native: boolean
37
+ /** 授权额度(最小单位的十进制字符串) */
38
+ allowance: string
39
+ decimals: number
40
+ /** 按 decimals 换算后的额度 */
41
+ formatted: string
42
+ /** 额度 ≥ uint96 最大值时为 true(覆盖 MaxUint256,以及 UNI / COMP 等把额度截断为 uint96 的代币) */
43
+ unlimited: boolean
44
+ success: boolean
45
+ }
46
+
47
+ /** unlimited 的判定阈值:uint96 最大值 */
48
+ export const UNLIMITED_ALLOWANCE_THRESHOLD = 2n ** 96n - 1n
49
+
50
+ const MAX_UINT256 = 2n ** 256n - 1n
51
+
52
+ export { MAX_UINT256 }
53
+
54
+ /** tokens() / getTokens() 可选的字段 */
55
+ export type TokenField = 'name' | 'symbol' | 'decimals' | 'totalSupply'
56
+
57
+ /** 不指定 fields 时返回的字段 */
58
+ export const DEFAULT_TOKEN_FIELDS = ['name', 'symbol', 'decimals'] as const satisfies readonly TokenField[]
59
+
60
+ export type DefaultTokenField = (typeof DEFAULT_TOKEN_FIELDS)[number]
61
+
62
+ interface TokenFieldTypes {
35
63
  name: string | null
64
+ symbol: string | null
65
+ decimals: number | null
66
+ /** 最小单位的十进制字符串 */
67
+ totalSupply: string | null
68
+ }
69
+
70
+ /**
71
+ * tokens() / getTokens() 的单项结果,只包含请求的字段(读取失败的字段为 null)。
72
+ * 请求 totalSupply 时额外带 totalSupplyFormatted(按 decimals 换算)。
73
+ */
74
+ export type TokenDetails<F extends TokenField = DefaultTokenField> = {
75
+ address: string
76
+ /** 是否是主币占位地址(信息来自内置链信息表 / nativeSymbol 等配置) */
77
+ native: boolean
78
+ /** 请求的字段都读到时为 true(主币没有 totalSupply,不计入) */
79
+ success: boolean
80
+ } & { [K in F]: TokenFieldTypes[K] } & ('totalSupply' extends F ? { totalSupplyFormatted: string | null } : unknown)
81
+
82
+ /** 主币占位地址(多数聚合器、钱包的约定),传给 balances() 时按主币处理 */
83
+ export const NATIVE_TOKEN = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'
84
+
85
+ /** balances() 默认视为主币的地址:0xeeee…eeee 和零地址 */
86
+ export const DEFAULT_NATIVE_TOKENS: readonly string[] = [NATIVE_TOKEN, '0x0000000000000000000000000000000000000000']
87
+
88
+ /** balances() 的代币参数:地址,或带已知 decimals 的对象(跳过 decimals 查询) */
89
+ export type BalanceToken = string | { address: string; decimals?: number }
90
+
91
+ export interface TokenBalance {
92
+ /** 传入的代币地址(原样) */
93
+ token: string
94
+ /** 是否按主币查询 */
95
+ native: boolean
96
+ /** 原始余额(最小单位)的十进制字符串,如 "1234500000";需要计算时用 BigInt(balance) */
97
+ balance: string
36
98
  decimals: number
99
+ /** 按 decimals 换算后的十进制字符串,如 "1234.5";整数不带小数点 */
100
+ formatted: string
101
+ /** 仅在 symbol: true 时返回;读取失败或主币 symbol 未知时为 null */
102
+ symbol?: string | null
103
+ /** 余额和 decimals 都查到时为 true;失败时 balance、formatted 都为 "0" */
104
+ success: boolean
105
+ }
106
+
107
+ /**
108
+ * 按精度格式化:formatAmount(1234500000n, 6) === '1234.5',formatAmount(10n ** 18n, 18) === '1'。
109
+ * 不做四舍五入,保留全部有效小数位。
110
+ */
111
+ export function formatAmount(value: bigint, decimals: number): string {
112
+ const negative = value < 0n
113
+ const digits = (negative ? -value : value).toString().padStart(decimals + 1, '0')
114
+ const whole = digits.slice(0, digits.length - decimals)
115
+ const fraction = digits.slice(digits.length - decimals).replace(/0+$/, '')
116
+ return `${negative ? '-' : ''}${whole}${fraction ? `.${fraction}` : ''}`
117
+ }
118
+
119
+ // decimals / symbol / name 不会变,按 chainId + 地址缓存,轮询余额时只需要查 balanceOf
120
+ interface TokenMeta {
121
+ decimals?: number
122
+ symbol?: string
123
+ name?: string
124
+ /** NFT 集合标准(nftCollections 识别后缓存);null 表示确认不是 ERC721 / ERC1155 */
125
+ standard?: 'ERC721' | 'ERC1155' | null
126
+ }
127
+
128
+ const tokenMetaCache = new Map<string, TokenMeta>()
129
+
130
+ export function getCachedTokenMeta(chainId: number, token: string): TokenMeta {
131
+ return tokenMetaCache.get(`${chainId}:${token.toLowerCase()}`) ?? {}
37
132
  }
38
133
 
39
- /** 常见的主币占位地址:多数聚合器用 0xeeee…eeee,部分协议用零地址 */
40
- export const DEFAULT_NATIVE_TOKENS: readonly string[] = [
41
- '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
42
- '0x0000000000000000000000000000000000000000',
43
- ]
134
+ export function setCachedTokenMeta(chainId: number, token: string, meta: TokenMeta): void {
135
+ if (Object.keys(meta).length === 0) {
136
+ return // 没有新数据(如查询失败)时不写入空条目
137
+ }
138
+ const key = `${chainId}:${token.toLowerCase()}`
139
+ tokenMetaCache.set(key, { ...tokenMetaCache.get(key), ...meta })
140
+ }
141
+
142
+ /** 测试用:清空 decimals / symbol 缓存 */
143
+ export function resetDecimalsCache(): void {
144
+ tokenMetaCache.clear()
145
+ }
package/src/fallback.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import { isError, type TransactionRequest } from 'ethers'
2
2
 
3
3
  import type { EthersLikeProvider } from './aggregate.js'
4
+ import { detectChainIdOf } from './detect.js'
4
5
  import { isExecutionError } from './errors.js'
6
+ import { withTimeout } from './util.js'
5
7
 
6
8
  export interface FallbackOptions {
7
9
  /** 单个节点单次请求超时(毫秒),超时视为该节点故障并换下一个。默认 10000;<= 0 表示不限制 */
@@ -22,6 +24,12 @@ export class FallbackRpc implements EthersLikeProvider {
22
24
  readonly #timeout: number
23
25
  readonly #cooldown: number
24
26
  readonly #failedAt: number[]
27
+ /** 上次 getLogs 成功的节点 */
28
+ #logsNode = -1
29
+ /** 上次成功的节点连续被限频的次数 */
30
+ #logsThrottled = 0
31
+ /** getLogs 超时 / 连不上的时间,只影响 getLogs 的节点顺序 */
32
+ readonly #logsFailedAt: number[]
25
33
 
26
34
  constructor(nodes: EthersLikeProvider[], options: FallbackOptions = {}) {
27
35
  if (!nodes.length) {
@@ -31,6 +39,7 @@ export class FallbackRpc implements EthersLikeProvider {
31
39
  this.#timeout = options.timeout ?? 10_000
32
40
  this.#cooldown = options.cooldown ?? 30_000
33
41
  this.#failedAt = nodes.map(() => 0)
42
+ this.#logsFailedAt = nodes.map(() => 0)
34
43
  }
35
44
 
36
45
  get nodes(): readonly EthersLikeProvider[] {
@@ -45,6 +54,80 @@ export class FallbackRpc implements EthersLikeProvider {
45
54
  return this.#run((node) => node.getBalance(address, blockTag))
46
55
  }
47
56
 
57
+ /** 按节点顺序探测 chainId(节点需实现 getChainId,见 detect.ts) */
58
+ getChainId(): Promise<number> {
59
+ return this.#run((node) => detectChainIdOf(node))
60
+ }
61
+
62
+ /**
63
+ * 当前区块号。优先问上次 getLogs 成功的节点(增量扫描时区块号和日志来自同一个节点,不会因节点间的高度差漏扫);
64
+ * 不支持 getBlockNumber 的节点跳过,失败也不让节点进入冷却(不影响 call / getBalance)
65
+ */
66
+ async getBlockNumber(): Promise<number> {
67
+ let lastError: unknown = new Error('No node supports getBlockNumber')
68
+ for (const index of this.#preferLogsNode(this.#order())) {
69
+ const node = this.#nodes[index] as EthersLikeProvider
70
+ if (!node.getBlockNumber) {
71
+ continue
72
+ }
73
+ try {
74
+ return await withTimeout(node.getBlockNumber(), this.#timeout)
75
+ } catch (err) {
76
+ lastError = err
77
+ }
78
+ }
79
+ throw lastError
80
+ }
81
+
82
+ /**
83
+ * 依次尝试支持 getLogs 的节点。与其他请求不同:
84
+ * - getLogs 失败多半是“区块范围超限 / 节点不开放这个方法”,不代表节点故障,所以不影响 call / getBalance 的节点顺序;
85
+ * 超时 / 连不上的节点只在 getLogs 里排到后面(冷却期内),上次成功的节点排在最前
86
+ * - 在用的节点被限频时直接抛出(连续 3 次后才换节点),其他情况全部失败时抛出;GetLogsError 带上每个节点的错误
87
+ */
88
+ async getLogs(filter: Parameters<NonNullable<EthersLikeProvider['getLogs']>>[0]): Promise<Awaited<ReturnType<NonNullable<EthersLikeProvider['getLogs']>>>> {
89
+ const errors: unknown[] = []
90
+ let preferred: unknown
91
+ const sticky = this.#logsNode
92
+ for (const index of this.#preferLogsNode(this.#order(this.#logsFailedAt))) {
93
+ const node = this.#nodes[index] as EthersLikeProvider
94
+ if (!node.getLogs) {
95
+ continue
96
+ }
97
+ try {
98
+ const logs = await withTimeout(node.getLogs(filter), this.#timeout)
99
+ this.#logsNode = index
100
+ this.#logsFailedAt[index] = 0
101
+ this.#logsThrottled = 0
102
+ return logs
103
+ } catch (err) {
104
+ if (isError(err, 'INVALID_ARGUMENT')) {
105
+ throw err
106
+ }
107
+ if (!isAlive(err)) {
108
+ // 超时 / 连不上:排到后面;如果是上次成功的节点,不再优先用它
109
+ this.#logsFailedAt[index] = Date.now()
110
+ if (index === this.#logsNode) {
111
+ this.#logsNode = -1
112
+ }
113
+ } else if (index === sticky) {
114
+ preferred = err
115
+ if (isRateLimited(err)) {
116
+ // 在用的节点只是限频:直接抛出让调用方退避重试(其他节点多半本来就不能用,逐个试只会更慢);
117
+ // 连续 3 次就不再优先用它,试其他节点
118
+ if (++this.#logsThrottled < 3) {
119
+ throw new GetLogsError([err], err)
120
+ }
121
+ this.#logsThrottled = 0
122
+ this.#logsNode = -1
123
+ }
124
+ }
125
+ errors.push(err)
126
+ }
127
+ }
128
+ throw new GetLogsError(errors, preferred)
129
+ }
130
+
48
131
  async #run<T>(fn: (node: EthersLikeProvider) => Promise<T>): Promise<T> {
49
132
  let lastError: unknown
50
133
  for (const index of this.#order()) {
@@ -64,14 +147,55 @@ export class FallbackRpc implements EthersLikeProvider {
64
147
  }
65
148
 
66
149
  /** 健康的节点按原顺序在前,冷却中的按出错时间先后排在后面(全挂时也都会再试一次) */
67
- #order(): number[] {
150
+ #order(failedAt: readonly number[] = this.#failedAt): number[] {
68
151
  const now = Date.now()
69
152
  const healthy: number[] = []
70
153
  const cooling: number[] = []
71
- this.#failedAt.forEach((at, i) => (at && now - at < this.#cooldown ? cooling : healthy).push(i))
72
- cooling.sort((a, b) => (this.#failedAt[a] as number) - (this.#failedAt[b] as number))
154
+ failedAt.forEach((at, i) => (at && now - at < this.#cooldown ? cooling : healthy).push(i))
155
+ cooling.sort((a, b) => (failedAt[a] as number) - (failedAt[b] as number))
73
156
  return [...healthy, ...cooling]
74
157
  }
158
+
159
+ /** 上次 getLogs 成功的节点(不在 getLogs 冷却中时)排到最前 */
160
+ #preferLogsNode(order: number[]): number[] {
161
+ const index = this.#logsNode
162
+ const at = this.#logsFailedAt[index]
163
+ if (index < 0 || (at && Date.now() - at < this.#cooldown)) {
164
+ return order
165
+ }
166
+ return [index, ...order.filter((i) => i !== index)]
167
+ }
168
+ }
169
+
170
+ /**
171
+ * getLogs 失败时抛出(所有节点都失败,或在用的节点被限频),errors 是各节点的错误;
172
+ * preferred 是上次成功的节点这次返回的错误(它最能说明问题:其他节点可能一直就不能用)
173
+ */
174
+ export class GetLogsError extends Error {
175
+ constructor(
176
+ readonly errors: readonly unknown[],
177
+ readonly preferred?: unknown,
178
+ ) {
179
+ super(errors.length ? `getLogs failed: ${errors.map(errorText).join(' | ')}` : 'No node supports getLogs')
180
+ this.name = 'GetLogsError'
181
+ }
182
+ }
183
+
184
+ /** 取出错误里节点返回的原始信息(ethers 会把它放在 error.message 里,外层是 “could not coalesce error”) */
185
+ export function errorText(err: unknown): string {
186
+ const e = err as { error?: { message?: unknown }; info?: { error?: { message?: unknown } }; shortMessage?: unknown; message?: unknown } | null
187
+ const parts = [e?.error?.message, e?.info?.error?.message, e?.shortMessage ?? e?.message].filter((p) => typeof p === 'string')
188
+ return parts.length ? parts.join('; ') : String(err)
189
+ }
190
+
191
+ /** 节点正常返回了 JSON-RPC 错误(ethers 把它放在 error / info.error 里)或只是限频,说明节点本身可用 */
192
+ function isAlive(err: unknown): boolean {
193
+ const e = err as { error?: { message?: unknown }; info?: { error?: { message?: unknown } } } | null
194
+ return typeof (e?.error?.message ?? e?.info?.error?.message) === 'string' || isRateLimited(err)
195
+ }
196
+
197
+ function isRateLimited(err: unknown): boolean {
198
+ return /too many requests|rate.?limit|\b429\b/i.test(errorText(err))
75
199
  }
76
200
 
77
201
  /** 换节点也不会变的错误:合约执行结果、参数错误、用户拒绝等 */
@@ -83,23 +207,3 @@ function isDeterministic(err: unknown): boolean {
83
207
  isError(err, 'NUMERIC_FAULT')
84
208
  )
85
209
  }
86
-
87
- class TimeoutError extends Error {
88
- constructor(ms: number) {
89
- super(`RPC request timed out after ${ms}ms`)
90
- this.name = 'TimeoutError'
91
- }
92
- }
93
-
94
- function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
95
- if (!Number.isFinite(ms) || ms <= 0) {
96
- return promise
97
- }
98
- let timer: ReturnType<typeof setTimeout>
99
- return Promise.race([
100
- promise,
101
- new Promise<never>((_, reject) => {
102
- timer = setTimeout(() => reject(new TimeoutError(ms)), ms)
103
- }),
104
- ]).finally(() => clearTimeout(timer))
105
- }
package/src/index.ts CHANGED
@@ -1,15 +1,101 @@
1
1
  export { Contract, bindCall, type BoundMethod, type ContractAbi, type ContractRunner } from './contract.js'
2
- export { DEFAULT_NATIVE_TOKENS, ERC20_ABI, type Erc20Contract, type TokenInfo } from './erc20.js'
2
+ export {
3
+ getTransferScanState,
4
+ keyValueScanStorage,
5
+ memoryScanStorage,
6
+ transferScan,
7
+ type TransferScanOptions,
8
+ type TransferScanState,
9
+ type TransferScanStorage,
10
+ } from './scan.js'
11
+ export { GetLogsError } from './fallback.js'
12
+ export {
13
+ ALCHEMY_NETWORKS,
14
+ COINGECKO_PLATFORMS,
15
+ DEFILLAMA_CHAINS,
16
+ alchemy,
17
+ clearTokenListCache,
18
+ coingeckoTokenList,
19
+ combine,
20
+ defaultTokenSource,
21
+ defillamaPrices,
22
+ firstAvailable,
23
+ metamaskTokenList,
24
+ nodereal,
25
+ staticTokens,
26
+ tokenList,
27
+ type DiscoveredToken,
28
+ type OwnedToken,
29
+ type OwnerTokensOptions,
30
+ type PriceSource,
31
+ type PriceSourceContext,
32
+ type TokenSource,
33
+ type TokenSourceContext,
34
+ } from './owner.js'
35
+ export {
36
+ getAllowances,
37
+ getBalances,
38
+ getErc1155Balances,
39
+ getNftBalances,
40
+ getNftCollections,
41
+ getNftOwners,
42
+ getNftTokenUris,
43
+ getOwnerTokens,
44
+ getTokens,
45
+ type GetBalancesOptions,
46
+ type GetNftCollectionsOptions,
47
+ type GetNftTokenUrisOptions,
48
+ type GetOwnerTokensOptions,
49
+ type GetTokensOptions,
50
+ type ShortcutOptions,
51
+ type ShortcutProviderOptions,
52
+ } from './shortcuts.js'
53
+ export {
54
+ DEFAULT_NFT_COLLECTION_FIELDS,
55
+ ERC1155_ABI,
56
+ ERC721_ABI,
57
+ type DefaultNftCollectionField,
58
+ type Erc1155Balance,
59
+ type NftBalance,
60
+ type NftCollection,
61
+ type NftCollectionField,
62
+ type NftCollectionsOptions,
63
+ type NftItem,
64
+ type NftOwner,
65
+ type NftStandard,
66
+ type NftTokenUri,
67
+ type NftTokenUriOptions,
68
+ } from './nft.js'
69
+ export { NATIVE_CURRENCIES, getNativeCurrency, type NativeCurrency } from './chains.js'
70
+ export { clearChainIdCache, detectChainId } from './detect.js'
71
+ export {
72
+ DEFAULT_NATIVE_TOKENS,
73
+ ERC20_ABI,
74
+ NATIVE_TOKEN,
75
+ formatAmount,
76
+ type BalanceToken,
77
+ type Erc20Contract,
78
+ type TokenBalance,
79
+ type TokenAllowance,
80
+ MAX_UINT256,
81
+ UNLIMITED_ALLOWANCE_THRESHOLD,
82
+ DEFAULT_TOKEN_FIELDS,
83
+ type DefaultTokenField,
84
+ type TokenDetails,
85
+ type TokenField,
86
+ } from './erc20.js'
3
87
  export {
4
88
  Provider,
5
- type ProviderConfig,
89
+ type BalancesOptions,
6
90
  type CallInput,
7
91
  type CallResults,
92
+ type ProviderConfig,
8
93
  type ProviderSource,
9
- type TryCallResults,
10
94
  type StaticCallItem,
11
95
  type StaticCallOverrides,
12
96
  type StaticCallResult,
97
+ type TokensOptions,
98
+ type TryCallResults,
13
99
  } from './provider.js'
14
100
  export {
15
101
  TRON_CHAIN_ID,
@@ -23,7 +109,7 @@ export {
23
109
  } from './tron.js'
24
110
  export { CallFailedError, decodeRevertReason, isExecutionError } from './errors.js'
25
111
  export { FallbackRpc, type FallbackOptions } from './fallback.js'
26
- export { getDefaultRpcUrls, isTronChain, type SourceOptions } from './source.js'
112
+ export { ChainCheckedProvider, getDefaultRpcUrls, isTronChain, type SourceOptions } from './source.js'
27
113
  export { DEFAULT_RPC_URLS, DEFAULT_TRON_HOSTS } from './rpcNodes.js'
28
114
  export { MULTICALL3_ADDRESS, getMulticall3, type Multicall } from './multicall.js'
29
115
  export type { BlockTag, CallOverrides, EthersLikeProvider } from './aggregate.js'