@w3lib/evm-batch-call 0.2.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.
- package/CHANGELOG.md +11 -0
- package/README.md +91 -0
- package/dist/aggregate.d.ts +5 -2
- package/dist/aggregate.d.ts.map +1 -1
- package/dist/aggregate.js.map +1 -1
- package/dist/fallback.d.ts +23 -0
- package/dist/fallback.d.ts.map +1 -1
- package/dist/fallback.js +117 -3
- package/dist/fallback.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/owner.d.ts +136 -0
- package/dist/owner.d.ts.map +1 -0
- package/dist/owner.js +501 -0
- package/dist/owner.js.map +1 -0
- package/dist/provider.d.ts +19 -0
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +37 -0
- package/dist/provider.js.map +1 -1
- package/dist/scan.d.ts +59 -0
- package/dist/scan.d.ts.map +1 -0
- package/dist/scan.js +245 -0
- package/dist/scan.js.map +1 -0
- package/dist/shortcuts.d.ts +18 -0
- package/dist/shortcuts.d.ts.map +1 -1
- package/dist/shortcuts.js +18 -0
- package/dist/shortcuts.js.map +1 -1
- package/dist/source.d.ts +2 -0
- package/dist/source.d.ts.map +1 -1
- package/dist/source.js +14 -0
- package/dist/source.js.map +1 -1
- package/package.json +1 -1
- package/src/aggregate.ts +5 -2
- package/src/fallback.ts +120 -3
- package/src/index.ts +35 -0
- package/src/owner.ts +651 -0
- package/src/provider.ts +42 -0
- package/src/scan.ts +293 -0
- package/src/shortcuts.ts +22 -0
- package/src/source.ts +16 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### 新增
|
|
6
|
+
|
|
7
|
+
- `Provider.getBlockNumber()` / `Provider.getLogs()`;多节点时 getLogs / getBlockNumber 失败不影响 call / getBalance 的节点顺序,getLogs 全部失败抛 `GetLogsError`(带各节点的错误)
|
|
8
|
+
- 资产列表 `getOwnerTokens` / `ownerTokens`:从代币来源发现候选代币,multicall 在链上核对余额,返回持有的代币
|
|
9
|
+
- 代币来源可替换:`metamaskTokenList`、`coingeckoTokenList`、`tokenList(url)`、`staticTokens`、`alchemy({ apiKey })`、`nodereal({ apiKey })`,以及 `firstAvailable` / `combine` 组合;默认 MetaMask 列表 → CoinGecko 列表(免费免 Key)
|
|
10
|
+
- 可选价格:`prices: true` 用 DefiLlama,或传自定义价格源;`minUsd` 过滤
|
|
11
|
+
- 增量扫描开关 `scanTransfers`(默认关闭):从第一次调用开始扫描转入的 ERC20 Transfer 事件,补充列表里没有的新代币;进度可持久化(`keyValueScanStorage` 或自定义存储),自动适配节点的区块范围上限与限频;`timeBudget` 限制每次调用的扫描时间,`confirmations` 避开链重组
|
|
12
|
+
- 局限性见 README「资产列表」;结果不保证完整、准确,因此标记为 `@deprecated`(不推荐使用)
|
|
13
|
+
|
|
3
14
|
## 0.2.0
|
|
4
15
|
|
|
5
16
|
### 不兼容的改动
|
package/README.md
CHANGED
|
@@ -49,6 +49,7 @@ await getErc1155Balances(user, [{ contract: ITEMS, tokenId: 7 }], { chainId: 137
|
|
|
49
49
|
| 函数 | Provider 方法 | 说明 |
|
|
50
50
|
| --- | --- | --- |
|
|
51
51
|
| `getBalances(owner, tokens, opts)` | `balances` | 主币 + 代币余额;`symbol: true` 额外返回 symbol |
|
|
52
|
+
| `getOwnerTokens(owner, opts)` | `ownerTokens` | 资产列表:地址持有的代币(**不推荐使用**,结果不保证完整,见[资产列表](#资产列表)) |
|
|
52
53
|
| `getTokens(tokens, opts)` | `tokens` | ERC20 详情,`fields` 选择返回字段,结果类型随之收窄 |
|
|
53
54
|
| `getAllowances(owner, spender, tokens, opts)` | `allowances` | 授权额度;`unlimited` 表示额度 ≥ uint96 最大值(覆盖 MaxUint256 及 UNI / COMP 这类截断为 uint96 的代币) |
|
|
54
55
|
| `getNftCollections(contracts, opts)` | `nftCollections` | NFT 集合 standard / name / symbol / totalSupply,`fields` 可选 |
|
|
@@ -126,9 +127,99 @@ const router = multi.contract(routerAddress, RouterABI)
|
|
|
126
127
|
const amountOut = await router.swap.staticCall(params, { value, from: user })
|
|
127
128
|
```
|
|
128
129
|
|
|
130
|
+
## 资产列表
|
|
131
|
+
|
|
132
|
+
> **不推荐使用**(已标记 `@deprecated`):`getOwnerTokens` / `provider.ownerTokens` 只能尽力而为,结果不保证完整、准确,局限性见下文。已知要查哪些代币时请用 `getBalances`;传 `alchemy` / `nodereal` 来源能查全,但同样受第三方服务的额度和可用性约束,需要可靠的完整持仓请直接接入索引服务的接口或自己的索引。
|
|
133
|
+
|
|
134
|
+
EVM 链上**没有办法只靠节点列出一个地址持有的全部代币**(链上没有“某地址持有哪些代币”的索引)。`getOwnerTokens` 的做法是:先从“代币来源”拿到候选代币,再用 multicall 在链上核对这些代币的余额,返回有余额的部分。
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
import { alchemy, getOwnerTokens, nodereal, tokenList } from '@w3lib/evm-batch-call'
|
|
138
|
+
|
|
139
|
+
await getOwnerTokens(user, { chainId: 56 }) // 默认:免费公开代币列表
|
|
140
|
+
await getOwnerTokens(user, { chainId: 1, prices: true, minUsd: 1 }) // 带美元价值,按价值排序,过滤 < $1 的代币
|
|
141
|
+
await getOwnerTokens(user, { chainId: 56, source: alchemy({ apiKey: KEY }) }) // 用 Alchemy 查全部历史持仓(需要 Key)
|
|
142
|
+
// [
|
|
143
|
+
// { token: NATIVE_TOKEN, native: true, symbol: 'BNB', formatted: '1.5', price: 600, value: 900, source: 'native', … },
|
|
144
|
+
// { token: '0x…', native: false, symbol: 'CAKE', formatted: '120', price: 2, value: 240, source: 'metamask', logo: '…', … },
|
|
145
|
+
// ]
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### 代币来源
|
|
149
|
+
|
|
150
|
+
| 来源 | 需要 Key | 能发现什么 | 说明 |
|
|
151
|
+
| --- | --- | --- | --- |
|
|
152
|
+
| `metamaskTokenList()`(默认首选) | 否 | MetaMask 汇总的代币列表(Ethereum 约 9500 个、BSC 约 2 万个),默认只用至少 3 家来源收录的 | MetaMask 自用接口,非对外承诺的公开 API |
|
|
153
|
+
| `coingeckoTokenList()`(默认备用) | 否 | CoinGecko 收录的代币(Ethereum 约 6000 个、BSC 约 4500 个) | 覆盖 26 条链,见 `COINGECKO_PLATFORMS` |
|
|
154
|
+
| `tokenList(url)` | 否 | 任意 Uniswap Token List 格式的列表(自己维护的、PancakeSwap 等) | |
|
|
155
|
+
| `staticTokens(list)` | 否 | 固定的代币地址 | 如之前发现过、存下来的代币 |
|
|
156
|
+
| `alchemy({ apiKey })` | **是** | 地址的全部历史持仓 | 19 条链,见 `ALCHEMY_NETWORKS`;可用 `urls: { [chainId]: url }` 按链覆盖地址 |
|
|
157
|
+
| `nodereal({ apiKey })` | **是** | 地址的全部历史持仓 | 仅 BSC、Ethereum;同样支持 `urls` |
|
|
158
|
+
|
|
159
|
+
- 默认来源是 `firstAvailable(metamaskTokenList(), coingeckoTokenList())`:MetaMask 列表不支持这条链、为空或请求失败时,用 CoinGecko 列表
|
|
160
|
+
- 来源可以组合:`firstAvailable(a, b)` 依次尝试,`combine(a, b)` 合并去重(单个来源出错不影响其他来源),例如 `combine(defaultTokenSource(), staticTokens(savedTokens))`
|
|
161
|
+
- **Key 只在调用时传入**:库里没有内置任何 Key,`alchemy()` / `nodereal()` 不传 `apiKey` 会直接报错,不传就不能用这两个来源。出错信息里不会包含 Key。在浏览器里使用时 Key 会暴露给用户,建议在服务端调用,或使用对方控制台里的域名白名单
|
|
162
|
+
- 不管代币从哪个来源发现,**余额和精度都由本库在链上用 multicall 核对**(来源返回的余额不使用,来源给错的精度会被链上的值纠正),链上读不到精度的合约(不是正常的 ERC20)不返回
|
|
163
|
+
- 主币用 Provider 配置的 `nativeTokens` 里的第一个地址查询(默认 `0xeeee…eeee`),名称 / symbol 同样遵循 `nativeName` / `nativeSymbol` 配置;列表里出现的主币映射地址(如 Polygon 的 `0x…1010`、zkSync 的 `0x…800A`)会被排除,不会把主币算两次
|
|
164
|
+
- 公开代币列表按 URL 缓存 1 小时;`clearTokenListCache()` 可手动清空
|
|
165
|
+
|
|
166
|
+
### 增量扫描(可选)
|
|
167
|
+
|
|
168
|
+
代币列表发现不了的新代币(刚发行、未被收录),可以用 `scanTransfers` 开关补充:开启后,从**第一次调用开始**,每次调用都往后扫描新区块里转给该地址的 ERC20 `Transfer` 事件,把发现的代币合并进来(结果的 `source` 为 `'transfers'`)。默认关闭。
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
import { getOwnerTokens, keyValueScanStorage } from '@w3lib/evm-batch-call'
|
|
172
|
+
|
|
173
|
+
await getOwnerTokens(user, { chainId: 1, scanTransfers: true }) // 进度存在内存里
|
|
174
|
+
await getOwnerTokens(user, { chainId: 1, scanTransfers: { storage: keyValueScanStorage(localStorage) } }) // 刷新页面后接着扫
|
|
175
|
+
await getOwnerTokens(user, { chainId: 1, scanTransfers: { lookbackBlocks: 20_000 } }) // 第一次额外往回扫一段
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
| 选项 | 默认 | 说明 |
|
|
179
|
+
| --- | --- | --- |
|
|
180
|
+
| `storage` | 内存 | 扫描进度(起始区块、已扫到的区块、发现的代币)。内存存储在进程 / 页面结束后丢失;`keyValueScanStorage(localStorage)` 或自己实现 `{ get, set }`(如存数据库)可以持久化 |
|
|
181
|
+
| `lookbackBlocks` | 0 | 第一次调用时往回扫的区块数(0 表示只从现在开始) |
|
|
182
|
+
| `blockRange` | 1000 | 单次 `eth_getLogs` 的最大区块范围。节点有上限时按报错里的上限自动缩小(报错里没写上限就减半试),记下的上限 1 天后失效再重新试;偶发错误导致缩小后,连续成功会逐步放大回去 |
|
|
183
|
+
| `maxRequests` | 20 | 每次调用最多发多少个 `eth_getLogs`;落后很多时分多次追上 |
|
|
184
|
+
| `timeBudget` | 5000 | 每次调用的扫描时间预算(毫秒),超过后不再发新请求,留到下次调用 |
|
|
185
|
+
| `confirmations` | 5 | 只扫到最新区块往前这么多个区块,避开链重组和节点之间的高度差(否则可能漏掉刚出的区块) |
|
|
186
|
+
|
|
187
|
+
出错时的处理:所有节点都明确不开放 `eth_getLogs` 时暂停 10 分钟;能用的节点只是限频 / 超时时不缩小范围,退避后重试(连续 3 次失败留到下次调用);有节点报范围超限时缩小范围。存储里的进度格式不对(如旧版本留下的)会被丢弃,重新开始。
|
|
188
|
+
|
|
189
|
+
也可以单独当作代币来源使用:`combine(defaultTokenSource(), transferScan({ storage }))`;`getTransferScanState(chainId, owner, storage)` 读取扫描进度。
|
|
190
|
+
|
|
191
|
+
**增量扫描的局限**:
|
|
192
|
+
|
|
193
|
+
- **只能发现开始扫描之后收到的代币**(以及 `lookbackBlocks` 范围内的)。更早收到、又不在代币列表里的代币仍然查不到;需要完整历史请用 `alchemy` / `nodereal`
|
|
194
|
+
- **需要节点支持 `eth_getLogs`**,免费公共节点限制很多:BSC 上内置节点只有 blockrazor 可用,且每次最多 25 个区块(会自动适配),连续请求还会被限频;节点完全不支持时会暂停 10 分钟再试。持续使用建议传自己的节点
|
|
195
|
+
- **进度默认只在内存里**:页面刷新、服务重启后会从头开始(重新记下当前区块),需要持久化请传 `storage`
|
|
196
|
+
- **会增加 `getOwnerTokens` 的耗时**:扫描和余额查询一起等待,每次调用通常多几百毫秒到 `timeBudget`(默认 5 秒),最坏再加上一个请求的超时时间(节点超时默认 10 秒)。对耗时敏感时可以调小 `timeBudget` / `maxRequests`,或单独用 `transferScan` 在后台定期扫描、展示时只读进度
|
|
197
|
+
- **很久没调用时追赶较慢**:每次最多 `maxRequests` 个请求、`timeBudget` 时间(如 BSC 上 20 × 25 = 500 个区块),落后很多时需要多次调用才能追上
|
|
198
|
+
- **最新几个区块里收到的代币要等下一次调用**(`confirmations`,默认 5 个区块)
|
|
199
|
+
- 扫描到的代币不经过任何列表筛选,**大部分会是空投垃圾币**,建议配合 `prices` + `minUsd` 使用
|
|
200
|
+
- 只看转入(`to` 为该地址)的 ERC20 `Transfer` 事件;不发标准 `Transfer` 事件的代币发现不了;Tron 不支持
|
|
201
|
+
|
|
202
|
+
### 价格
|
|
203
|
+
|
|
204
|
+
`prices: true` 用 DefiLlama 查美元价格(免费、不需要 Key),结果带 `price` / `value`,并按价值从高到低排序;`minUsd` 只保留价值不低于它的代币(没有价格的代币也会被去掉,主币始终保留)。只传 `minUsd` 时会自动开启 DefiLlama 价格;与 `prices: false` 同时使用会报错。也可以传自己的价格源:`prices: { name, prices: async ({ chainId, tokens, native }) => Map }`。
|
|
205
|
+
|
|
206
|
+
### 局限性
|
|
207
|
+
|
|
208
|
+
使用前请了解:
|
|
209
|
+
|
|
210
|
+
1. **默认来源只能发现公开列表里的代币**。刚发行、还没被列表收录的代币(如新上线的 meme 币)、小众代币查不到;需要完整持仓时,传 `alchemy({ apiKey })` 或 `nodereal({ apiKey })`
|
|
211
|
+
2. **默认来源依赖第三方免费服务**(MetaMask、CoinGecko 的列表,DefiLlama 的价格):它们可能限流、变更格式或停止服务;MetaMask 列表接口不是对外承诺的公开 API。生产环境建议组合多个来源,或使用自己维护的列表(`tokenList(url)`)
|
|
212
|
+
3. **列表里也有垃圾币和零头**。默认只用至少 3 家来源收录的代币来减少垃圾币,但仍会有不少价值很低的代币;需要干净的列表时开启 `prices` 并设置 `minUsd`
|
|
213
|
+
4. **价格不一定可信**。流动性很差的代币价格可能严重虚高(实测有空投代币被估值数十万美元),默认已过滤 DefiLlama 置信度低于 0.9 的价格,但展示总资产时仍建议使用自己的价格源或再做校验
|
|
214
|
+
5. **需要查几千到两万个代币的余额**,耗时几秒:实测 Ethereum(约 9500 个代币)首次约 11 秒、之后约 6 秒,BSC(约 4800 个)首次约 8 秒、之后约 4 秒(首次要在链上核对持有代币的精度,之后有缓存)。大量使用时建议缓存结果;列表很大时也会占用较多节点请求
|
|
215
|
+
6. **只包含 ERC20 代币**,不含 NFT(NFT 请用 `getNftBalances` 等);Tron 链没有默认来源,需要自己传 `source`
|
|
216
|
+
7. 使用 Alchemy / NodeReal 时受它们的免费额度和限频约束,翻页次数默认最多 20 页(2000 个代币),可用 `maxPages` 调整
|
|
217
|
+
8. 开启 `scanTransfers` 时另有局限,见上面「增量扫描」
|
|
218
|
+
|
|
129
219
|
## 功能
|
|
130
220
|
|
|
131
221
|
- **常用查询**:余额、代币详情、授权额度、NFT(集合 / 持有 / 元数据地址 / ERC1155 余额),一个函数一次请求
|
|
222
|
+
- **资产列表**(不推荐使用,尽力而为):`getOwnerTokens`,免费公开代币列表或带 Key 的索引服务发现代币,multicall 核对余额,可选 DefiLlama 价格
|
|
132
223
|
- **批量读取**:`all` / `tryAll` / `tryEach`,N 条读调用合成一次 `eth_call`;支持数组或对象输入
|
|
133
224
|
- **自动合并**:绑定合约直接 `await`,或 `provider.call(x)`,同一收集窗口内的调用合并成一次请求并去重
|
|
134
225
|
- **预执行**:`staticCall` / `staticCallAll` / `method.staticCall`,带 `from` / `value` 模拟交易,返回解析好的 revert 原因
|
package/dist/aggregate.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { Interface, type Provider as EthersProvider } from 'ethers';
|
|
2
2
|
import type { CallRequest, RawResult } from './call.js';
|
|
3
3
|
import { type Multicall } from './multicall.js';
|
|
4
|
-
/**
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* 只用到 ethers Provider 的这几个方法:必需 call / getBalance;getLogs / getBlockNumber 可选(只有增量扫描 Transfer 事件时用到)。
|
|
6
|
+
* ethers 的 JsonRpcProvider / BrowserProvider 都满足
|
|
7
|
+
*/
|
|
8
|
+
export type EthersLikeProvider = Pick<EthersProvider, 'call' | 'getBalance'> & Partial<Pick<EthersProvider, 'getLogs' | 'getBlockNumber'>>;
|
|
6
9
|
export type BlockTag = number | bigint | string;
|
|
7
10
|
export interface CallOverrides {
|
|
8
11
|
blockTag?: BlockTag;
|
package/dist/aggregate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aggregate.d.ts","sourceRoot":"","sources":["../src/aggregate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,SAAS,EAAkC,KAAK,QAAQ,IAAI,cAAc,EAAE,MAAM,QAAQ,CAAA;AAE7G,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAGvD,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAEnE
|
|
1
|
+
{"version":3,"file":"aggregate.d.ts","sourceRoot":"","sources":["../src/aggregate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,SAAS,EAAkC,KAAK,QAAQ,IAAI,cAAc,EAAE,MAAM,QAAQ,CAAA;AAE7G,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAGvD,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAEnE;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,YAAY,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,gBAAgB,CAAC,CAAC,CAAA;AAE1I,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;AAE/C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,eAAO,MAAM,mBAAmB,WAG9B,CAAA;AAaF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,kBAAkB,CAAA;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,4BAA4B;IAC5B,SAAS,EAAE,SAAS,GAAG,IAAI,CAAA;IAC3B,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAC7B,GAAG,EAAE,gBAAgB,EACrB,QAAQ,EAAE,WAAW,EAAE,EACvB,SAAS,GAAE,aAAkB,GAC5B,OAAO,CAAC,SAAS,EAAE,CAAC,CActB;AA0LD,uBAAuB;AACvB,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C"}
|
package/dist/aggregate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aggregate.js","sourceRoot":"","sources":["../src/aggregate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,EAAmC,MAAM,QAAQ,CAAA;AAG7G,OAAO,EAAE,8BAA8B,EAAE,MAAM,iBAAiB,CAAA;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAE,kBAAkB,EAAkB,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"aggregate.js","sourceRoot":"","sources":["../src/aggregate.ts"],"names":[],"mappings":"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"}
|
package/dist/fallback.d.ts
CHANGED
|
@@ -21,5 +21,28 @@ export declare class FallbackRpc implements EthersLikeProvider {
|
|
|
21
21
|
getBalance(address: Parameters<EthersLikeProvider['getBalance']>[0], blockTag?: Parameters<EthersLikeProvider['getBalance']>[1]): Promise<bigint>;
|
|
22
22
|
/** 按节点顺序探测 chainId(节点需实现 getChainId,见 detect.ts) */
|
|
23
23
|
getChainId(): Promise<number>;
|
|
24
|
+
/**
|
|
25
|
+
* 当前区块号。优先问上次 getLogs 成功的节点(增量扫描时区块号和日志来自同一个节点,不会因节点间的高度差漏扫);
|
|
26
|
+
* 不支持 getBlockNumber 的节点跳过,失败也不让节点进入冷却(不影响 call / getBalance)
|
|
27
|
+
*/
|
|
28
|
+
getBlockNumber(): Promise<number>;
|
|
29
|
+
/**
|
|
30
|
+
* 依次尝试支持 getLogs 的节点。与其他请求不同:
|
|
31
|
+
* - getLogs 失败多半是“区块范围超限 / 节点不开放这个方法”,不代表节点故障,所以不影响 call / getBalance 的节点顺序;
|
|
32
|
+
* 超时 / 连不上的节点只在 getLogs 里排到后面(冷却期内),上次成功的节点排在最前
|
|
33
|
+
* - 在用的节点被限频时直接抛出(连续 3 次后才换节点),其他情况全部失败时抛出;GetLogsError 带上每个节点的错误
|
|
34
|
+
*/
|
|
35
|
+
getLogs(filter: Parameters<NonNullable<EthersLikeProvider['getLogs']>>[0]): Promise<Awaited<ReturnType<NonNullable<EthersLikeProvider['getLogs']>>>>;
|
|
24
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* getLogs 失败时抛出(所有节点都失败,或在用的节点被限频),errors 是各节点的错误;
|
|
39
|
+
* preferred 是上次成功的节点这次返回的错误(它最能说明问题:其他节点可能一直就不能用)
|
|
40
|
+
*/
|
|
41
|
+
export declare class GetLogsError extends Error {
|
|
42
|
+
readonly errors: readonly unknown[];
|
|
43
|
+
readonly preferred?: unknown | undefined;
|
|
44
|
+
constructor(errors: readonly unknown[], preferred?: unknown | undefined);
|
|
45
|
+
}
|
|
46
|
+
/** 取出错误里节点返回的原始信息(ethers 会把它放在 error.message 里,外层是 “could not coalesce error”) */
|
|
47
|
+
export declare function errorText(err: unknown): string;
|
|
25
48
|
//# sourceMappingURL=fallback.d.ts.map
|
package/dist/fallback.d.ts.map
CHANGED
|
@@ -1 +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;AAKxD,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;;
|
|
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;AAKxD,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;;gBAYxC,KAAK,EAAE,kBAAkB,EAAE,EAAE,OAAO,GAAE,eAAoB;IAWtE,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;IAIjJ,oDAAoD;IACpD,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAI7B;;;OAGG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAgBvC;;;;;OAKG;IACG,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC,WAAW,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;CAgF3J;AAED;;;GAGG;AACH,qBAAa,YAAa,SAAQ,KAAK;IAEnC,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO;gBADnB,MAAM,EAAE,SAAS,OAAO,EAAE,EAC1B,SAAS,CAAC,EAAE,OAAO,YAAA;CAK/B;AAED,kFAAkF;AAClF,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAI9C"}
|
package/dist/fallback.js
CHANGED
|
@@ -14,6 +14,12 @@ export class FallbackRpc {
|
|
|
14
14
|
#timeout;
|
|
15
15
|
#cooldown;
|
|
16
16
|
#failedAt;
|
|
17
|
+
/** 上次 getLogs 成功的节点 */
|
|
18
|
+
#logsNode = -1;
|
|
19
|
+
/** 上次成功的节点连续被限频的次数 */
|
|
20
|
+
#logsThrottled = 0;
|
|
21
|
+
/** getLogs 超时 / 连不上的时间,只影响 getLogs 的节点顺序 */
|
|
22
|
+
#logsFailedAt;
|
|
17
23
|
constructor(nodes, options = {}) {
|
|
18
24
|
if (!nodes.length) {
|
|
19
25
|
throw new Error('FallbackRpc requires at least one node');
|
|
@@ -22,6 +28,7 @@ export class FallbackRpc {
|
|
|
22
28
|
this.#timeout = options.timeout ?? 10_000;
|
|
23
29
|
this.#cooldown = options.cooldown ?? 30_000;
|
|
24
30
|
this.#failedAt = nodes.map(() => 0);
|
|
31
|
+
this.#logsFailedAt = nodes.map(() => 0);
|
|
25
32
|
}
|
|
26
33
|
get nodes() {
|
|
27
34
|
return this.#nodes;
|
|
@@ -36,6 +43,76 @@ export class FallbackRpc {
|
|
|
36
43
|
getChainId() {
|
|
37
44
|
return this.#run((node) => detectChainIdOf(node));
|
|
38
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* 当前区块号。优先问上次 getLogs 成功的节点(增量扫描时区块号和日志来自同一个节点,不会因节点间的高度差漏扫);
|
|
48
|
+
* 不支持 getBlockNumber 的节点跳过,失败也不让节点进入冷却(不影响 call / getBalance)
|
|
49
|
+
*/
|
|
50
|
+
async getBlockNumber() {
|
|
51
|
+
let lastError = new Error('No node supports getBlockNumber');
|
|
52
|
+
for (const index of this.#preferLogsNode(this.#order())) {
|
|
53
|
+
const node = this.#nodes[index];
|
|
54
|
+
if (!node.getBlockNumber) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
return await withTimeout(node.getBlockNumber(), this.#timeout);
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
lastError = err;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
throw lastError;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 依次尝试支持 getLogs 的节点。与其他请求不同:
|
|
68
|
+
* - getLogs 失败多半是“区块范围超限 / 节点不开放这个方法”,不代表节点故障,所以不影响 call / getBalance 的节点顺序;
|
|
69
|
+
* 超时 / 连不上的节点只在 getLogs 里排到后面(冷却期内),上次成功的节点排在最前
|
|
70
|
+
* - 在用的节点被限频时直接抛出(连续 3 次后才换节点),其他情况全部失败时抛出;GetLogsError 带上每个节点的错误
|
|
71
|
+
*/
|
|
72
|
+
async getLogs(filter) {
|
|
73
|
+
const errors = [];
|
|
74
|
+
let preferred;
|
|
75
|
+
const sticky = this.#logsNode;
|
|
76
|
+
for (const index of this.#preferLogsNode(this.#order(this.#logsFailedAt))) {
|
|
77
|
+
const node = this.#nodes[index];
|
|
78
|
+
if (!node.getLogs) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const logs = await withTimeout(node.getLogs(filter), this.#timeout);
|
|
83
|
+
this.#logsNode = index;
|
|
84
|
+
this.#logsFailedAt[index] = 0;
|
|
85
|
+
this.#logsThrottled = 0;
|
|
86
|
+
return logs;
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
if (isError(err, 'INVALID_ARGUMENT')) {
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
if (!isAlive(err)) {
|
|
93
|
+
// 超时 / 连不上:排到后面;如果是上次成功的节点,不再优先用它
|
|
94
|
+
this.#logsFailedAt[index] = Date.now();
|
|
95
|
+
if (index === this.#logsNode) {
|
|
96
|
+
this.#logsNode = -1;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else if (index === sticky) {
|
|
100
|
+
preferred = err;
|
|
101
|
+
if (isRateLimited(err)) {
|
|
102
|
+
// 在用的节点只是限频:直接抛出让调用方退避重试(其他节点多半本来就不能用,逐个试只会更慢);
|
|
103
|
+
// 连续 3 次就不再优先用它,试其他节点
|
|
104
|
+
if (++this.#logsThrottled < 3) {
|
|
105
|
+
throw new GetLogsError([err], err);
|
|
106
|
+
}
|
|
107
|
+
this.#logsThrottled = 0;
|
|
108
|
+
this.#logsNode = -1;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
errors.push(err);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
throw new GetLogsError(errors, preferred);
|
|
115
|
+
}
|
|
39
116
|
async #run(fn) {
|
|
40
117
|
let lastError;
|
|
41
118
|
for (const index of this.#order()) {
|
|
@@ -55,14 +132,51 @@ export class FallbackRpc {
|
|
|
55
132
|
throw lastError;
|
|
56
133
|
}
|
|
57
134
|
/** 健康的节点按原顺序在前,冷却中的按出错时间先后排在后面(全挂时也都会再试一次) */
|
|
58
|
-
#order() {
|
|
135
|
+
#order(failedAt = this.#failedAt) {
|
|
59
136
|
const now = Date.now();
|
|
60
137
|
const healthy = [];
|
|
61
138
|
const cooling = [];
|
|
62
|
-
|
|
63
|
-
cooling.sort((a, b) =>
|
|
139
|
+
failedAt.forEach((at, i) => (at && now - at < this.#cooldown ? cooling : healthy).push(i));
|
|
140
|
+
cooling.sort((a, b) => failedAt[a] - failedAt[b]);
|
|
64
141
|
return [...healthy, ...cooling];
|
|
65
142
|
}
|
|
143
|
+
/** 上次 getLogs 成功的节点(不在 getLogs 冷却中时)排到最前 */
|
|
144
|
+
#preferLogsNode(order) {
|
|
145
|
+
const index = this.#logsNode;
|
|
146
|
+
const at = this.#logsFailedAt[index];
|
|
147
|
+
if (index < 0 || (at && Date.now() - at < this.#cooldown)) {
|
|
148
|
+
return order;
|
|
149
|
+
}
|
|
150
|
+
return [index, ...order.filter((i) => i !== index)];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* getLogs 失败时抛出(所有节点都失败,或在用的节点被限频),errors 是各节点的错误;
|
|
155
|
+
* preferred 是上次成功的节点这次返回的错误(它最能说明问题:其他节点可能一直就不能用)
|
|
156
|
+
*/
|
|
157
|
+
export class GetLogsError extends Error {
|
|
158
|
+
errors;
|
|
159
|
+
preferred;
|
|
160
|
+
constructor(errors, preferred) {
|
|
161
|
+
super(errors.length ? `getLogs failed: ${errors.map(errorText).join(' | ')}` : 'No node supports getLogs');
|
|
162
|
+
this.errors = errors;
|
|
163
|
+
this.preferred = preferred;
|
|
164
|
+
this.name = 'GetLogsError';
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/** 取出错误里节点返回的原始信息(ethers 会把它放在 error.message 里,外层是 “could not coalesce error”) */
|
|
168
|
+
export function errorText(err) {
|
|
169
|
+
const e = err;
|
|
170
|
+
const parts = [e?.error?.message, e?.info?.error?.message, e?.shortMessage ?? e?.message].filter((p) => typeof p === 'string');
|
|
171
|
+
return parts.length ? parts.join('; ') : String(err);
|
|
172
|
+
}
|
|
173
|
+
/** 节点正常返回了 JSON-RPC 错误(ethers 把它放在 error / info.error 里)或只是限频,说明节点本身可用 */
|
|
174
|
+
function isAlive(err) {
|
|
175
|
+
const e = err;
|
|
176
|
+
return typeof (e?.error?.message ?? e?.info?.error?.message) === 'string' || isRateLimited(err);
|
|
177
|
+
}
|
|
178
|
+
function isRateLimited(err) {
|
|
179
|
+
return /too many requests|rate.?limit|\b429\b/i.test(errorText(err));
|
|
66
180
|
}
|
|
67
181
|
/** 换节点也不会变的错误:合约执行结果、参数错误、用户拒绝等 */
|
|
68
182
|
function isDeterministic(err) {
|
package/dist/fallback.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fallback.js","sourceRoot":"","sources":["../src/fallback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAA2B,MAAM,QAAQ,CAAA;AAGzD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AASvC;;;;;;GAMG;AACH,MAAM,OAAO,WAAW;IACb,MAAM,CAAsB;IAC5B,QAAQ,CAAQ;IAChB,SAAS,CAAQ;IACjB,SAAS,CAAU;
|
|
1
|
+
{"version":3,"file":"fallback.js","sourceRoot":"","sources":["../src/fallback.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export { Contract, bindCall, type BoundMethod, type ContractAbi, type ContractRunner } from './contract.js';
|
|
2
|
-
export {
|
|
2
|
+
export { getTransferScanState, keyValueScanStorage, memoryScanStorage, transferScan, type TransferScanOptions, type TransferScanState, type TransferScanStorage, } from './scan.js';
|
|
3
|
+
export { GetLogsError } from './fallback.js';
|
|
4
|
+
export { ALCHEMY_NETWORKS, COINGECKO_PLATFORMS, DEFILLAMA_CHAINS, alchemy, clearTokenListCache, coingeckoTokenList, combine, defaultTokenSource, defillamaPrices, firstAvailable, metamaskTokenList, nodereal, staticTokens, tokenList, type DiscoveredToken, type OwnedToken, type OwnerTokensOptions, type PriceSource, type PriceSourceContext, type TokenSource, type TokenSourceContext, } from './owner.js';
|
|
5
|
+
export { getAllowances, getBalances, getErc1155Balances, getNftBalances, getNftCollections, getNftOwners, getNftTokenUris, getOwnerTokens, getTokens, type GetBalancesOptions, type GetNftCollectionsOptions, type GetNftTokenUrisOptions, type GetOwnerTokensOptions, type GetTokensOptions, type ShortcutOptions, type ShortcutProviderOptions, } from './shortcuts.js';
|
|
3
6
|
export { DEFAULT_NFT_COLLECTION_FIELDS, ERC1155_ABI, ERC721_ABI, type DefaultNftCollectionField, type Erc1155Balance, type NftBalance, type NftCollection, type NftCollectionField, type NftCollectionsOptions, type NftItem, type NftOwner, type NftStandard, type NftTokenUri, type NftTokenUriOptions, } from './nft.js';
|
|
4
7
|
export { NATIVE_CURRENCIES, getNativeCurrency, type NativeCurrency } from './chains.js';
|
|
5
8
|
export { clearChainIdCache, detectChainId } from './detect.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAA;AAC3G,OAAO,EACL,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,SAAS,EACT,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,EAC3B,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,uBAAuB,GAC7B,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,6BAA6B,EAC7B,WAAW,EACX,UAAU,EACV,KAAK,yBAAyB,EAC9B,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,kBAAkB,GACxB,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAA;AACvF,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC9D,OAAO,EACL,qBAAqB,EACrB,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,WAAW,EACX,6BAA6B,EAC7B,oBAAoB,EACpB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,UAAU,GAChB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,QAAQ,EACR,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,cAAc,GACpB,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,aAAa,EACb,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,aAAa,EACb,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,WAAW,GACjB,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AACnF,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAA;AACjE,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,WAAW,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAA;AACtG,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AACpE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAClF,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACjF,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAChD,YAAY,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAA;AAC3G,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,YAAY,EACZ,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,GACzB,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,OAAO,EACP,mBAAmB,EACnB,kBAAkB,EAClB,OAAO,EACP,kBAAkB,EAClB,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,kBAAkB,GACxB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,cAAc,EACd,SAAS,EACT,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,uBAAuB,GAC7B,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,6BAA6B,EAC7B,WAAW,EACX,UAAU,EACV,KAAK,yBAAyB,EAC9B,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,kBAAkB,GACxB,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAA;AACvF,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC9D,OAAO,EACL,qBAAqB,EACrB,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,WAAW,EACX,6BAA6B,EAC7B,oBAAoB,EACpB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,UAAU,GAChB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,QAAQ,EACR,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,cAAc,GACpB,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,aAAa,EACb,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,aAAa,EACb,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,WAAW,GACjB,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AACnF,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAA;AACjE,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,WAAW,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAA;AACtG,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AACpE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAClF,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACjF,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAChD,YAAY,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export { Contract, bindCall } from './contract.js';
|
|
2
|
-
export {
|
|
2
|
+
export { getTransferScanState, keyValueScanStorage, memoryScanStorage, transferScan, } from './scan.js';
|
|
3
|
+
export { GetLogsError } from './fallback.js';
|
|
4
|
+
export { ALCHEMY_NETWORKS, COINGECKO_PLATFORMS, DEFILLAMA_CHAINS, alchemy, clearTokenListCache, coingeckoTokenList, combine, defaultTokenSource, defillamaPrices, firstAvailable, metamaskTokenList, nodereal, staticTokens, tokenList, } from './owner.js';
|
|
5
|
+
export { getAllowances, getBalances, getErc1155Balances, getNftBalances, getNftCollections, getNftOwners, getNftTokenUris, getOwnerTokens, getTokens, } from './shortcuts.js';
|
|
3
6
|
export { DEFAULT_NFT_COLLECTION_FIELDS, ERC1155_ABI, ERC721_ABI, } from './nft.js';
|
|
4
7
|
export { NATIVE_CURRENCIES, getNativeCurrency } from './chains.js';
|
|
5
8
|
export { clearChainIdCache, detectChainId } from './detect.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAA2D,MAAM,eAAe,CAAA;AAC3G,OAAO,EACL,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,SAAS,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAA2D,MAAM,eAAe,CAAA;AAC3G,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,YAAY,GAIb,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,OAAO,EACP,mBAAmB,EACnB,kBAAkB,EAClB,OAAO,EACP,kBAAkB,EAClB,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,QAAQ,EACR,YAAY,EACZ,SAAS,GAQV,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,cAAc,EACd,SAAS,GAQV,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,6BAA6B,EAC7B,WAAW,EACX,UAAU,GAYX,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAuB,MAAM,aAAa,CAAA;AACvF,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC9D,OAAO,EACL,qBAAqB,EACrB,SAAS,EACT,YAAY,EACZ,YAAY,EAKZ,WAAW,EACX,6BAA6B,EAC7B,oBAAoB,GAIrB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,QAAQ,GAWT,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,aAAa,EACb,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,aAAa,GAId,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AACnF,OAAO,EAAE,WAAW,EAAwB,MAAM,eAAe,CAAA;AACjE,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,WAAW,EAAsB,MAAM,aAAa,CAAA;AACtG,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AACpE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAkB,MAAM,gBAAgB,CAAA"}
|
package/dist/owner.d.ts
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { CallOverrides } from './aggregate.js';
|
|
2
|
+
import type { Provider } from './provider.js';
|
|
3
|
+
import { type TransferScanOptions } from './scan.js';
|
|
4
|
+
export interface DiscoveredToken {
|
|
5
|
+
address: string;
|
|
6
|
+
symbol?: string | null;
|
|
7
|
+
name?: string | null;
|
|
8
|
+
decimals?: number | null;
|
|
9
|
+
logo?: string | null;
|
|
10
|
+
}
|
|
11
|
+
export interface TokenSourceContext {
|
|
12
|
+
chainId: number;
|
|
13
|
+
owner: string;
|
|
14
|
+
fetch: typeof fetch;
|
|
15
|
+
/** 查询用的 Provider(扫描链上事件的来源会用到,如 transferScan) */
|
|
16
|
+
provider?: Provider;
|
|
17
|
+
}
|
|
18
|
+
export interface TokenSource {
|
|
19
|
+
/** 名称,出现在结果的 source 字段 */
|
|
20
|
+
readonly name: string;
|
|
21
|
+
/** 返回候选代币;不支持这条链时返回 null(firstAvailable 会接着试下一个来源) */
|
|
22
|
+
discover(ctx: TokenSourceContext): Promise<DiscoveredToken[] | null>;
|
|
23
|
+
}
|
|
24
|
+
export declare class HttpStatusError extends Error {
|
|
25
|
+
readonly status: number;
|
|
26
|
+
constructor(status: number, target: string);
|
|
27
|
+
}
|
|
28
|
+
/** 清空代币列表缓存 */
|
|
29
|
+
export declare function clearTokenListCache(): void;
|
|
30
|
+
/**
|
|
31
|
+
* MetaMask 钱包用来自动发现代币的列表(汇总多家来源,Ethereum 约 9500 个、BSC 约 2 万个)。
|
|
32
|
+
* minOccurrences:至少被几家来源收录(MetaMask 自己用 3),用来过滤垃圾币。
|
|
33
|
+
*
|
|
34
|
+
* 注意:这是 MetaMask 自己在用的接口,并非对外承诺的公开 API,可能随时限流或变更,建议与其他来源组合使用。
|
|
35
|
+
*/
|
|
36
|
+
export declare function metamaskTokenList(options?: {
|
|
37
|
+
minOccurrences?: number;
|
|
38
|
+
}): TokenSource;
|
|
39
|
+
/** CoinGecko 按链维护的代币列表:chainId → 平台 id(2026-10 实测可用) */
|
|
40
|
+
export declare const COINGECKO_PLATFORMS: Readonly<Record<number, string>>;
|
|
41
|
+
/** CoinGecko 代币列表(CoinGecko 收录的代币,Ethereum 约 6000 个、BSC 约 4500 个) */
|
|
42
|
+
export declare function coingeckoTokenList(): TokenSource;
|
|
43
|
+
/** 任意 Uniswap Token List 格式的列表(如 https://tokens.uniswap.org、PancakeSwap 列表、自己维护的列表) */
|
|
44
|
+
export declare function tokenList(url: string, options?: {
|
|
45
|
+
name?: string;
|
|
46
|
+
}): TokenSource;
|
|
47
|
+
/** 固定的代币列表(如之前发现过、存下来的代币) */
|
|
48
|
+
export declare function staticTokens(tokens: ReadonlyArray<string | DiscoveredToken>, options?: {
|
|
49
|
+
name?: string;
|
|
50
|
+
}): TokenSource;
|
|
51
|
+
/** Alchemy 各链的子域名(2026-10 实测存在) */
|
|
52
|
+
export declare const ALCHEMY_NETWORKS: Readonly<Record<number, string>>;
|
|
53
|
+
export interface IndexerSourceOptions {
|
|
54
|
+
/** API Key(必填;库里没有内置 Key) */
|
|
55
|
+
apiKey: string;
|
|
56
|
+
/** 按链覆盖请求地址(如代理或表里没有的链):{ [chainId]: url };其他链仍用默认地址 */
|
|
57
|
+
urls?: Readonly<Record<number, string>>;
|
|
58
|
+
/** 最多翻几页(每页 100 个代币),默认 20 */
|
|
59
|
+
maxPages?: number;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Alchemy `alchemy_getTokenBalances`:查到地址的全部历史持仓(不限于公开列表)。需要 Alchemy 的 API Key(有免费额度)。
|
|
63
|
+
*/
|
|
64
|
+
export declare function alchemy(options: IndexerSourceOptions): TokenSource;
|
|
65
|
+
/**
|
|
66
|
+
* NodeReal `nr_getTokenHoldings`:查到地址的全部历史持仓(仅 BSC、Ethereum)。需要 NodeReal 的 API Key(有免费额度)。
|
|
67
|
+
*/
|
|
68
|
+
export declare function nodereal(options: IndexerSourceOptions): TokenSource;
|
|
69
|
+
/** 按顺序尝试:前一个不支持这条链(返回 null)或出错时用下一个;全部出错时抛出最后一个错误 */
|
|
70
|
+
export declare function firstAvailable(...sources: TokenSource[]): TokenSource;
|
|
71
|
+
/**
|
|
72
|
+
* 合并多个来源(去重,先出现的元数据优先),如“公开列表 + 自己存下来的代币”。
|
|
73
|
+
* 单个来源出错不影响其他来源;全部出错时抛出第一个错误。
|
|
74
|
+
*/
|
|
75
|
+
export declare function combine(...sources: TokenSource[]): TokenSource;
|
|
76
|
+
/** 默认来源:MetaMask 列表(至少 3 家收录),不支持或不可用时用 CoinGecko 列表。都免费、不需要 Key */
|
|
77
|
+
export declare function defaultTokenSource(): TokenSource;
|
|
78
|
+
export interface PriceSourceContext {
|
|
79
|
+
chainId: number;
|
|
80
|
+
/** 代币地址(不含主币) */
|
|
81
|
+
tokens: readonly string[];
|
|
82
|
+
/** 主币 symbol(需要主币价格时);不需要时为 null */
|
|
83
|
+
nativeSymbol: string | null;
|
|
84
|
+
fetch: typeof fetch;
|
|
85
|
+
}
|
|
86
|
+
export interface PriceSource {
|
|
87
|
+
readonly name: string;
|
|
88
|
+
/** 返回 去重键(0x 地址小写)→ 美元价格;主币用键 'native'。没有价格的不返回 */
|
|
89
|
+
prices(ctx: PriceSourceContext): Promise<Map<string, number>>;
|
|
90
|
+
}
|
|
91
|
+
/** DefiLlama 的链标识:chainId → slug(2026-10 实测可用) */
|
|
92
|
+
export declare const DEFILLAMA_CHAINS: Readonly<Record<number, string>>;
|
|
93
|
+
/**
|
|
94
|
+
* DefiLlama 价格(免费、不需要 Key)。minConfidence:DefiLlama 给出的置信度下限(0~1),低于它视为没有价格,默认 0.9。
|
|
95
|
+
* 注意:流动性很差的代币价格可能虚高,展示总资产时建议配合 minUsd 或自己的价格源。
|
|
96
|
+
*/
|
|
97
|
+
export declare function defillamaPrices(options?: {
|
|
98
|
+
minConfidence?: number;
|
|
99
|
+
}): PriceSource;
|
|
100
|
+
export interface OwnerTokensOptions extends CallOverrides {
|
|
101
|
+
/** 代币来源,默认 defaultTokenSource()(MetaMask 列表 → CoinGecko 列表,免费免 Key) */
|
|
102
|
+
source?: TokenSource;
|
|
103
|
+
/** 查美元价格:true 用 DefiLlama,也可以传自己的 PriceSource。传了 minUsd 而没设置 prices 时自动用 DefiLlama */
|
|
104
|
+
prices?: boolean | PriceSource;
|
|
105
|
+
/** 只保留价值 ≥ minUsd 的代币(没有价格的代币也会被去掉;主币不受影响)。与 prices: false 同时使用会报错 */
|
|
106
|
+
minUsd?: number;
|
|
107
|
+
/** 第一项返回主币(用 Provider 的 nativeTokens 配置里的第一个地址查,默认 0xeeee…eeee;配置成 [] 时不返回)。默认 true */
|
|
108
|
+
includeNative?: boolean;
|
|
109
|
+
/** 自定义 fetch(代理等场景);默认全局 fetch */
|
|
110
|
+
fetch?: typeof fetch;
|
|
111
|
+
/**
|
|
112
|
+
* 增量扫描开关(默认关闭):开启后,额外合并“从第一次调用开始,该地址收到过的 ERC20 代币”(扫描 Transfer 事件),
|
|
113
|
+
* 用来补充代币列表里没有的新代币。传对象可以配置进度存储、回扫区块数等,见 TransferScanOptions
|
|
114
|
+
*/
|
|
115
|
+
scanTransfers?: boolean | TransferScanOptions;
|
|
116
|
+
}
|
|
117
|
+
export interface OwnedToken {
|
|
118
|
+
token: string;
|
|
119
|
+
native: boolean;
|
|
120
|
+
/** 余额(最小单位的十进制字符串),由 multicall 在链上核对 */
|
|
121
|
+
balance: string;
|
|
122
|
+
/** 精度,由 multicall 在链上核对(不使用来源给的值) */
|
|
123
|
+
decimals: number;
|
|
124
|
+
formatted: string;
|
|
125
|
+
symbol: string | null;
|
|
126
|
+
name: string | null;
|
|
127
|
+
logo: string | null;
|
|
128
|
+
/** 美元单价;未开启价格或没有价格时为 null */
|
|
129
|
+
price: number | null;
|
|
130
|
+
/** 美元价值(formatted × price) */
|
|
131
|
+
value: number | null;
|
|
132
|
+
/** 发现这个代币的来源(如 metamask / coingecko / alchemy);主币为 'native' */
|
|
133
|
+
source: string;
|
|
134
|
+
}
|
|
135
|
+
export declare function ownerTokens(provider: Provider, owner: string, options?: OwnerTokensOptions): Promise<OwnedToken[]>;
|
|
136
|
+
//# sourceMappingURL=owner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"owner.d.ts","sourceRoot":"","sources":["../src/owner.ts"],"names":[],"mappings":"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"}
|