@w3lib/solana-batch-call 0.5.0 → 0.7.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 +47 -0
- package/README.md +129 -9
- package/dist/accountReader.d.ts +57 -0
- package/dist/accountReader.d.ts.map +1 -0
- package/dist/accountReader.js +149 -0
- package/dist/accountReader.js.map +1 -0
- package/dist/address.d.ts +9 -0
- package/dist/address.d.ts.map +1 -1
- package/dist/address.js +36 -6
- package/dist/address.js.map +1 -1
- package/dist/approvals.d.ts +5 -3
- package/dist/approvals.d.ts.map +1 -1
- package/dist/approvals.js +25 -36
- package/dist/approvals.js.map +1 -1
- package/dist/balances.d.ts +178 -0
- package/dist/balances.d.ts.map +1 -0
- package/dist/balances.js +524 -0
- package/dist/balances.js.map +1 -0
- package/dist/batchPlan.d.ts +52 -0
- package/dist/batchPlan.d.ts.map +1 -0
- package/dist/batchPlan.js +64 -0
- package/dist/batchPlan.js.map +1 -0
- package/dist/bytes.d.ts +31 -0
- package/dist/bytes.d.ts.map +1 -0
- package/dist/bytes.js +74 -0
- package/dist/bytes.js.map +1 -0
- package/dist/claim.d.ts +163 -0
- package/dist/claim.d.ts.map +1 -0
- package/dist/claim.js +197 -0
- package/dist/claim.js.map +1 -0
- package/dist/client.d.ts +26 -41
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +41 -986
- package/dist/client.js.map +1 -1
- package/dist/clientConfig.d.ts +11 -0
- package/dist/clientConfig.d.ts.map +1 -0
- package/dist/clientConfig.js +3 -0
- package/dist/clientConfig.js.map +1 -0
- package/dist/decode.d.ts +22 -0
- package/dist/decode.d.ts.map +1 -0
- package/dist/decode.js +160 -0
- package/dist/decode.js.map +1 -0
- package/dist/describe.d.ts +26 -0
- package/dist/describe.d.ts.map +1 -0
- package/dist/describe.js +58 -0
- package/dist/describe.js.map +1 -0
- package/dist/fees.d.ts.map +1 -1
- package/dist/fees.js +2 -12
- package/dist/fees.js.map +1 -1
- package/dist/history.d.ts +125 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +220 -0
- package/dist/history.js.map +1 -0
- package/dist/historyHelius.d.ts +52 -0
- package/dist/historyHelius.d.ts.map +1 -0
- package/dist/historyHelius.js +125 -0
- package/dist/historyHelius.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/layout.d.ts +21 -0
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +50 -69
- package/dist/layout.js.map +1 -1
- package/dist/liquidity/common.js +2 -2
- package/dist/liquidity/common.js.map +1 -1
- package/dist/liquidity/orca.d.ts +6 -0
- package/dist/liquidity/orca.d.ts.map +1 -1
- package/dist/liquidity/orca.js +14 -3
- package/dist/liquidity/orca.js.map +1 -1
- package/dist/liquidity/raydiumClmm.d.ts +6 -0
- package/dist/liquidity/raydiumClmm.d.ts.map +1 -1
- package/dist/liquidity/raydiumClmm.js +12 -5
- package/dist/liquidity/raydiumClmm.js.map +1 -1
- package/dist/lp.d.ts +55 -12
- package/dist/lp.d.ts.map +1 -1
- package/dist/lp.js +158 -70
- package/dist/lp.js.map +1 -1
- package/dist/lpMath.d.ts +22 -0
- package/dist/lpMath.d.ts.map +1 -1
- package/dist/lpMath.js +21 -0
- package/dist/lpMath.js.map +1 -1
- package/dist/portfolio.d.ts.map +1 -1
- package/dist/portfolio.js +3 -8
- package/dist/portfolio.js.map +1 -1
- package/dist/programs.d.ts +19 -0
- package/dist/programs.d.ts.map +1 -0
- package/dist/programs.js +175 -0
- package/dist/programs.js.map +1 -0
- package/dist/reclaim.d.ts +192 -0
- package/dist/reclaim.d.ts.map +1 -0
- package/dist/reclaim.js +399 -0
- package/dist/reclaim.js.map +1 -0
- package/dist/risk.js +2 -2
- package/dist/risk.js.map +1 -1
- package/dist/rpc.d.ts +30 -7
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +404 -111
- package/dist/rpc.js.map +1 -1
- package/dist/simulate.d.ts +106 -0
- package/dist/simulate.d.ts.map +1 -0
- package/dist/simulate.js +395 -0
- package/dist/simulate.js.map +1 -0
- package/dist/source.d.ts +8 -4
- package/dist/source.d.ts.map +1 -1
- package/dist/source.js +11 -4
- package/dist/source.js.map +1 -1
- package/dist/subpaths/approvals.d.ts +3 -3
- package/dist/subpaths/approvals.d.ts.map +1 -1
- package/dist/subpaths/approvals.js.map +1 -1
- package/dist/subpaths/claim.d.ts +31 -0
- package/dist/subpaths/claim.d.ts.map +1 -0
- package/dist/subpaths/claim.js +31 -0
- package/dist/subpaths/claim.js.map +1 -0
- package/dist/subpaths/history.d.ts +21 -0
- package/dist/subpaths/history.d.ts.map +1 -0
- package/dist/subpaths/history.js +22 -0
- package/dist/subpaths/history.js.map +1 -0
- package/dist/subpaths/lp.d.ts +2 -2
- package/dist/subpaths/lp.d.ts.map +1 -1
- package/dist/subpaths/lp.js +2 -2
- package/dist/subpaths/lp.js.map +1 -1
- package/dist/subpaths/reclaim.d.ts +34 -0
- package/dist/subpaths/reclaim.d.ts.map +1 -0
- package/dist/subpaths/reclaim.js +34 -0
- package/dist/subpaths/reclaim.js.map +1 -0
- package/dist/subpaths/simulate.d.ts +22 -0
- package/dist/subpaths/simulate.d.ts.map +1 -0
- package/dist/subpaths/simulate.js +23 -0
- package/dist/subpaths/simulate.js.map +1 -0
- package/dist/swap/index.js +3 -3
- package/dist/swap/index.js.map +1 -1
- package/dist/tokenDetails.d.ts +12 -0
- package/dist/tokenDetails.d.ts.map +1 -0
- package/dist/tokenDetails.js +196 -0
- package/dist/tokenDetails.js.map +1 -0
- package/dist/tokenMeta.d.ts +63 -0
- package/dist/tokenMeta.d.ts.map +1 -0
- package/dist/tokenMeta.js +226 -0
- package/dist/tokenMeta.js.map +1 -0
- package/dist/tradeCheck.js +3 -3
- package/dist/tradeCheck.js.map +1 -1
- package/dist/util.d.ts +22 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +107 -0
- package/dist/util.js.map +1 -0
- package/dist/write/chain.d.ts +51 -0
- package/dist/write/chain.d.ts.map +1 -0
- package/dist/write/chain.js +184 -0
- package/dist/write/chain.js.map +1 -0
- package/dist/write/collect.js +2 -2
- package/dist/write/collect.js.map +1 -1
- package/dist/write/errors.d.ts +10 -0
- package/dist/write/errors.d.ts.map +1 -0
- package/dist/write/errors.js +77 -0
- package/dist/write/errors.js.map +1 -0
- package/dist/write/instructions.d.ts +2 -0
- package/dist/write/instructions.d.ts.map +1 -1
- package/dist/write/instructions.js +16 -0
- package/dist/write/instructions.js.map +1 -1
- package/dist/write/pack.d.ts +28 -0
- package/dist/write/pack.d.ts.map +1 -0
- package/dist/write/pack.js +125 -0
- package/dist/write/pack.js.map +1 -0
- package/dist/write/send.d.ts +23 -9
- package/dist/write/send.d.ts.map +1 -1
- package/dist/write/send.js +53 -351
- package/dist/write/send.js.map +1 -1
- package/dist/write/tracker.d.ts +24 -0
- package/dist/write/tracker.d.ts.map +1 -0
- package/dist/write/tracker.js +129 -0
- package/dist/write/tracker.js.map +1 -0
- package/package.json +21 -1
- package/src/accountReader.ts +187 -0
- package/src/address.ts +41 -6
- package/src/approvals.ts +36 -44
- package/src/balances.ts +678 -0
- package/src/batchPlan.ts +117 -0
- package/src/bytes.ts +89 -0
- package/src/claim.ts +327 -0
- package/src/client.ts +59 -1239
- package/src/clientConfig.ts +12 -0
- package/src/decode.ts +176 -0
- package/src/describe.ts +77 -0
- package/src/fees.ts +2 -9
- package/src/history.ts +325 -0
- package/src/historyHelius.ts +136 -0
- package/src/index.ts +13 -1
- package/src/layout.ts +63 -82
- package/src/liquidity/common.ts +2 -2
- package/src/liquidity/orca.ts +15 -5
- package/src/liquidity/raydiumClmm.ts +13 -5
- package/src/lp.ts +226 -75
- package/src/lpMath.ts +38 -0
- package/src/portfolio.ts +8 -11
- package/src/programs.ts +216 -0
- package/src/reclaim.ts +550 -0
- package/src/risk.ts +2 -2
- package/src/rpc.ts +476 -118
- package/src/simulate.ts +517 -0
- package/src/source.ts +14 -5
- package/src/subpaths/approvals.ts +3 -2
- package/src/subpaths/claim.ts +54 -0
- package/src/subpaths/history.ts +38 -0
- package/src/subpaths/lp.ts +5 -1
- package/src/subpaths/reclaim.ts +58 -0
- package/src/subpaths/simulate.ts +37 -0
- package/src/swap/index.ts +3 -3
- package/src/tokenDetails.ts +240 -0
- package/src/tokenMeta.ts +270 -0
- package/src/tradeCheck.ts +3 -3
- package/src/util.ts +110 -0
- package/src/write/chain.ts +218 -0
- package/src/write/collect.ts +2 -2
- package/src/write/errors.ts +82 -0
- package/src/write/instructions.ts +17 -0
- package/src/write/pack.ts +142 -0
- package/src/write/send.ts +74 -407
- package/src/write/tracker.ts +152 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.0
|
|
4
|
+
|
|
5
|
+
### 改进(读取链路,无破坏性变更)
|
|
6
|
+
|
|
7
|
+
- 取消传到底层:`RpcTransport.request` 新增可选的第三个参数 `{ signal }`(新增类型 `RpcRequestOptions`,自定义传输不处理也照常工作)。各查询的 `signal` 传给传输层:`HttpRpc` 合并超时与 signal 交给 `fetch`,还在排队的调用取消时直接移出队列,已发出的批量请求在所有成员都取消后才中止;`FallbackRpc` 取消后不再换节点,也不把节点记为故障;取消时 reject 的仍是 `signal.reason`
|
|
8
|
+
- publicnode 的已知限制预置:单次 `getMultipleAccounts` 最多 10 个账户、一个批量里最多 1 个,首次请求就按它拆分,不再先等 3 秒多的 403
|
|
9
|
+
- `HttpRpc` 新增 `maxConcurrency`(默认 4):同一节点地址同时在途的 HTTP 请求数上限(按 URL 在所有连接间共享),超出的排队:排队最多等 `timeout`(等不到按超时处理、多节点时换节点),发出后重新计时。之前大批量读取在 publicnode 上同时有二十多个请求
|
|
10
|
+
- 拆分后的 `getMultipleAccounts` 子请求、客户端分组读取改用并发池(不再整波等最慢的;出错后不再发后面的请求)
|
|
11
|
+
- 429 退避加 ±25% 随机抖动,有 `Retry-After` 头时按它等(超过 10 秒不等,直接报错交给上层换节点);413 / “Batch of more than N” 缩小的批量 10 分钟后恢复(之前永久缩小);学到的节点限制表有条目上限
|
|
12
|
+
- 去重:`HttpRpc` 同一批里 method 和 params 完全相同的只读调用只发一次(新增选项 `dedupe`,默认 true);补读代币 decimals / 元数据时,同一 mint 正在被别的查询读取就共用那次读取
|
|
13
|
+
- `/lp`:LP mint → 池子地址找到后缓存(最多 1 万条),之后不再 `getProgramAccounts`;按 LP mint 找池子同时最多 4 个
|
|
14
|
+
- 内部:并发池 `mapPool`、`sleep` 挪到公共的 `util.ts`
|
|
15
|
+
|
|
16
|
+
### 新功能 / 改进(第二阶段,公开 API 只新增)
|
|
17
|
+
|
|
18
|
+
- 写操作 `SendOptions` 新增 `sendRpc`(发送交易用的节点,按 client 的网络和节点选项解析,网络不一致时报 `NetworkMismatchError`;模拟和查确认状态仍用 client)和 `broadcast: 'first' | 'all'`(默认 `'first'`;`'all'` 同时发给所有节点,第一个接受就返回,其余在后台完成)
|
|
19
|
+
- 有优先费用时少一次模拟:第一次模拟顺带取付款方模拟后的余额(和当前余额同一个批量请求),付完优先费用后仍不低于免租下限就不再按真实费用模拟第二次。取舍:第二次模拟和第一次只隔几百毫秒、基于几乎同一个状态,按收紧后的计算单元上限复核几乎不会失败,挡不住发送时的状态变化;新增 `SendOptions.computeUnitMargin`(默认 `{ ratio: 1.1, extra: 1000 }`),消耗会波动的调用可以调大余量
|
|
20
|
+
- 内部的并发池(`mapPool`)一项出错后不再执行后面的项(/lp 找池子的 `getProgramAccounts`、/history 的 `getTransaction` 等)
|
|
21
|
+
- 等待确认时不再每轮都查 `getEpochInfo`:记下上次的(区块高度, slot),用状态查询返回的 slot 估算高度上限,可能过期时才查
|
|
22
|
+
- `priorityFee: 'auto'` 去掉 0 再取 75 分位:不活跃账户的最近费用大多是 0(2026-10 主网实测 USDC mint 150 个 slot 里 135 个为 0),之前几乎总是算出 0
|
|
23
|
+
- 打包合一:`batchCall` 等的发送和 `/reclaim`、`/claim` 的批量计划共用 `packUnits`,按累计的账户集合和指令字节增量估算交易大小(之前每加一个调用就完整编译整条消息),每笔最后完整编译校验一次;分组结果与之前相同
|
|
24
|
+
- `client.balances(owner, { mints, ...options })` 新写法(新类型 `BalancesQueryOptions`),旧写法 `balances(owner, mints, options)` 照常可用
|
|
25
|
+
- 地址非法时抛 `InvalidAddressError`(继承 `Error`,message 不变,带 `code: 'INVALID_ADDRESS'` 和 `address`),主入口导出
|
|
26
|
+
- `/approvals`、`/reclaim`、`/claim` 的查询参数类型改名为 `ApprovalsOptions`、`ReclaimableOptions`、`ClaimableOptions`(与其他查询的 options 统一),旧名 `ApprovalsQuery`、`ReclaimQuery`、`ClaimQuery` 保留为别名(@deprecated)
|
|
27
|
+
- 去重:同一批里相同的只读调用,第二个起的调用方拿到结果的副本(调用方修改结果不会影响别人)
|
|
28
|
+
- PDA 缓存的键改用种子的十六进制(不再每次 base58 编码),满了按 LRU 淘汰最久没用的(之前整表清空)
|
|
29
|
+
|
|
30
|
+
### 内部重构(导出路径和名字不变)
|
|
31
|
+
|
|
32
|
+
- `client.ts` 拆成 `tokenMeta.ts`(代币信息缓存)、`accountReader.ts`(账户读取管线)、`balances.ts`(三种余额查法、持仓列表)、`tokenDetails.ts`(代币详情 / NFT),`SolanaClient` 只保留薄的公开方法
|
|
33
|
+
- `write/send.ts` 拆出 `errors.ts`(交易错误文案)、`chain.ts`(blockhash / 优先费用 / 模拟 / 广播)、`tracker.ts`(确认轮询)、`pack.ts`(打包)
|
|
34
|
+
- 二进制读取器合一(`bytes.ts`,原 layout 的 Reader、lp 的 Bytes、decode 的 Data);代币账户完整布局的解析统一到 `layout.parseTokenAccountState`(/approvals、/simulate 共用);计算预算指令的上限 / 单价解码合一(`computeBudgetOf`,/simulate、/fees 共用);`history.ts` 的并发池改用公共的 `mapPool`;“方法不可用时说明怎么绕过”的报错包装抽成 `withMethodHint`(/lp、/approvals、资产总值共用)
|
|
35
|
+
|
|
36
|
+
## 0.6.0
|
|
37
|
+
|
|
38
|
+
### 新功能
|
|
39
|
+
|
|
40
|
+
- 交易模拟,子路径 `@w3lib/solana-batch-call/simulate`:`getSimulation` / `simulate` 在主网模拟一笔交易(`simulateTransaction`,不验签名、不上链),输入是 base64 / 字节的交易(legacy、v0 含地址查找表)或本库构建器的输出(`buildSwap`、流动性计划、`{ owner, instructions }`、`buildRevokes` / `buildCloses`)。给出余额变化(SOL 不含手续费、Wrapped SOL 并入 SOL、可选美元价值)、授权变化(delegate 设置 / 变更 / 撤销、账户 owner / 关闭权限 / mint 权限被改、关闭账户,按模拟前后的账户状态比较)、指令解码(System、Token / Token-2022、ATA、计算预算、Memo,Jupiter / Raydium / Orca / Meteora / Pump.fun 的协议和指令名)、程序错误解码、计算单元、手续费和警告(无限额度授权、SetAuthority、改 owner、关闭账户退给别人、不认识的程序、新收款地址、多余的签名者、手续费过高、花掉大部分余额;可选 `/risk` 检查变化的代币)。publicnode 的 `simulateTransaction` 最多返回 10 个账户,自动只看和钱包有关的账户并分批
|
|
41
|
+
- 交易历史,子路径 `@w3lib/solana-batch-call/history`:`getHistory` 把地址的交易整理成统一的 `Activity`(兑换、转账、授权、流动性、铸造、合约调用、失败,带方向、对手方、资产变化、手续费、协议),`cursor` 是最后一条签名,支持 `limit`、`before` / `after`(区块时间)、`types` 过滤。数据源可换:默认 `rpcHistory()`(`getSignaturesForAddress` + `getTransaction`,限并发),可选 `heliusHistory({ apiKey })`;传数组时按顺序尝试
|
|
42
|
+
- 账户回收,子路径 `@w3lib/solana-batch-call/reclaim`:`getReclaimable` / `reclaimable` 扫描钱包里关闭后能退回租金的账户并给出每项和合计的 lamports:空代币账户(SPL Token / Token-2022,有暂存转账手续费的先收到 mint 上)、垃圾币(销毁后关闭;保守的默认判断:有价格且价值高的、NFT、仓位 NFT 一律不当垃圾币,只勾选有价格的尘埃 / `/risk` 标记的,可疑名字只作提示不单独触发勾选,可传 `isJunk`、`prices`、`maxJunkUsd`)、Wrapped SOL(解包)、已清空的 Orca Whirlpool / Raydium CLMM 仓位(关闭仓位、销毁 NFT,Token-2022 的 NFT 连 mint 一起退回);冻结 / 关闭权限是别人 / 仓位还有没领的手续费的列在 `blocked`。`buildReclaim` 发送前重新读取账户并核对(余额变了、冻结、仓位不再为空的跳过并给出原因),`simulateReclaim` 用 `/simulate` 逐笔模拟并给出预计退回的 SOL,`reclaim` 构建 → 模拟 → 签名发送(打包同 `batchCall`,`destination`、`feePayer`、`dryRun`)。`/write` 新增指令工具 `burnChecked`
|
|
43
|
+
- 批量领取,子路径 `@w3lib/solana-batch-call/claim`:`getClaimable` / `claimable` 只读预览 Orca Whirlpool、Raydium CLMM 仓位的未领取手续费和挖矿奖励(Orca 奖励 slot、Raydium 农场奖励,数量按程序的公式结算),带美元价值和按代币汇总;`buildClaimAll` 复用 `/liquidity` 的 `collectFees` 为每个仓位构建领取(手续费 + 全部奖励),`simulateClaim` 用 `/simulate` 模拟并汇总收到的代币,`claimAll` 构建 → 模拟 → 签名发送(`minUsd`、`dryRun`)
|
|
44
|
+
- `/lp`:`getLpPositions` 新增 `rewards: true`(仓位的 `rewards`、`rewardsUsd`,`now` 指定结算时刻),`/lp` 导出 `rewardGrowthGlobal`、`getRewardGrowthInside`;`/liquidity` 内部抽出 `orcaClosePosition` / `raydiumClosePosition`(关闭已清空的仓位)
|
|
45
|
+
|
|
46
|
+
### 修复
|
|
47
|
+
|
|
48
|
+
- publicnode 对 `getMultipleAccounts` 超过 10 个账户返回 “A maximum of 10 accounts is allowed”(之前只认 “too many accounts” 和 403 “Request blocked”),会被当成节点故障。现在识别这条报错,直接按它给出的上限拆分
|
|
49
|
+
|
|
3
50
|
## 0.5.0
|
|
4
51
|
|
|
5
52
|
### 新功能
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @w3lib/solana-batch-call
|
|
2
2
|
|
|
3
|
-
Solana 批量读取库:余额(SOL / SPL Token / Token-2022)、代币详情、NFT、[LP 仓位](#lp-仓位)(子路径 `/lp`)、[价格和资产总值](#价格)(子路径 `/price`)、[手续费估算](#手续费估算)(子路径 `/fees`)、[SNS 域名解析](#域名解析)(子路径 `/names`)、[授权管理](#授权管理)(子路径 `/approvals`)、[代币风险检查](#代币风险)(子路径 `/risk`);[添加 / 移除流动性](#添加--移除流动性)(子路径 `/liquidity`)、[兑换](#兑换)(子路径 `/swap`);以及[批量写操作](#批量写操作)(一对多转账、多钱包归集、批量调用程序,子路径 `/write`)。不依赖 Solana SDK,直接发 JSON-RPC。
|
|
3
|
+
Solana 批量读取库:余额(SOL / SPL Token / Token-2022)、代币详情、NFT、[LP 仓位](#lp-仓位)(子路径 `/lp`)、[价格和资产总值](#价格)(子路径 `/price`)、[手续费估算](#手续费估算)(子路径 `/fees`)、[SNS 域名解析](#域名解析)(子路径 `/names`)、[授权管理](#授权管理)(子路径 `/approvals`)、[代币风险检查](#代币风险)(子路径 `/risk`);[交易模拟](#交易模拟)(子路径 `/simulate`)、[交易历史](#交易历史)(子路径 `/history`);[添加 / 移除流动性](#添加--移除流动性)(子路径 `/liquidity`)、[兑换](#兑换)(子路径 `/swap`);以及[批量写操作](#批量写操作)(一对多转账、多钱包归集、批量调用程序,子路径 `/write`)。不依赖 Solana SDK,直接发 JSON-RPC。
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
pnpm add @w3lib/solana-batch-call
|
|
@@ -123,7 +123,7 @@ await getLpPositions(owner, { provider, prices: true })
|
|
|
123
123
|
| 选项 | 说明 |
|
|
124
124
|
| --- | --- |
|
|
125
125
|
| `mints` | 只看这些代币(仓位 NFT 的 mint、LP 代币的 mint),按 ATA 读余额,免费节点也能用。不传时扫描全部代币账户(需要支持 `getTokenAccountsByOwner` 的节点) |
|
|
126
|
-
| `pools` | Raydium AMM v4 / CPMM 的池子地址。LP 代币要找到对应的池子:不在这里的按 LP mint 用 `getProgramAccounts`
|
|
126
|
+
| `pools` | Raydium AMM v4 / CPMM 的池子地址。LP 代币要找到对应的池子:不在这里的按 LP mint 用 `getProgramAccounts` 查找(同时最多 4 个;找到的池子地址会缓存,之后只读池子账户核对;免费公共节点大多不支持,这时报错并提示传 `pools`,或用 `protocols` 只查 CLMM / Whirlpool);不是 AMM v4 / CPMM 池子的地址会报错;不持有 Raydium 的 LP 代币时不会用到 |
|
|
127
127
|
| `protocols` | 只查某些协议:`'raydium-clmm'`、`'orca-whirlpool'`、`'raydium-amm'`、`'raydium-cpmm'`,默认全部 |
|
|
128
128
|
| `includeClosed` | 返回已清空的仓位(流动性为 0、手续费也领完,NFT 还在),默认 false |
|
|
129
129
|
| `prices` | `true` 用 DefiLlama,也可以传自己的 `SolanaPriceSource`;默认不查。价格源出错时照样返回仓位,只是 `valueUsd` / `feesUsd` 为 null |
|
|
@@ -330,7 +330,7 @@ await getCostEstimate({ feePayer: owner, instructions: [ix1, ix2] })
|
|
|
330
330
|
await getCostEstimate({ transaction: base64Tx }) // 构建好的交易(如聚合器 API 返回的),按交易自带的计算预算算
|
|
331
331
|
```
|
|
332
332
|
|
|
333
|
-
- **费用建议**:`getRecentPrioritizationFees` 返回最近 150 个 slot 里(会写 `accounts` 的)落块交易的**最低**单价,三档取第 50 / 75 / 90 百分位(`percentiles`),`normal`
|
|
333
|
+
- **费用建议**:`getRecentPrioritizationFees` 返回最近 150 个 slot 里(会写 `accounts` 的)落块交易的**最低**单价,三档取第 50 / 75 / 90 百分位(`percentiles`),`normal` 是包含 0 的 75 分位(`/write` 的 `priorityFee: 'auto'` 去掉 0 再取 75 分位,并受 `maxPriorityFee` 限制)。不传 `accounts` 时看全网,大多是 0(每个区块总有不加优先费的交易);估某笔交易时传它的可写账户(`estimateCost` 自动这样做)
|
|
334
334
|
- **一笔交易**:模拟一次(`sigVerify: false`、`replaceRecentBlockhash`,不需要签名、不会发送),模拟失败时抛出(带程序日志里的原因)。传指令时按 `/write` 的发送方式算:去掉自带的计算预算指令,计算单元上限 = 模拟消耗 × 1.1 + 1000,单价按这一档(`level`,或 `priorityFee` 直接指定,或传查好的 `fees` 共用);传构建好的交易时按它自带的 SetComputeUnitLimit / SetComputeUnitPrice(没有单价时优先费为 0,没有上限时按每条指令 20 万),v0 交易会读地址查找表找可写账户
|
|
335
335
|
- **手续费** = 每个签名 5000 lamports + 单价 × 上限(向上取整)。**押金**:交易前不存在、模拟后存在、有数据或不归 System Program 的可写账户(新建的代币账户、仓位等),按 `getMinimumBalanceForRentExemption` 算(不写死:2026-10 主网 165 字节是 1,488,440 lamports),关闭账户时退回;普通转账给新地址不算押金
|
|
336
336
|
- 只支持 legacy 和 v0 交易:主网已经有 v1 格式的交易(2026-10 约三成),它们的布局不同,传入时报错(改传指令)
|
|
@@ -387,6 +387,62 @@ await closeTokenAccounts(client, key, list.filter((a) => a.amount === '0')) //
|
|
|
387
387
|
|
|
388
388
|
打包:在已经用到 `SolanaClient` 的基础上,`getApprovals` 约增加 6KB(minify,gzip 约 2KB),加上撤销 / 关闭约 7KB(gzip 约 3KB);发送的代码(`/write`,约 21KB,gzip 约 8KB)按需加载,只查授权时不会加载。
|
|
389
389
|
|
|
390
|
+
## 账户回收
|
|
391
|
+
|
|
392
|
+
子路径 `@w3lib/solana-batch-call/reclaim`:扫描钱包里关闭后能退回租金(SOL)的账户,给出每项和合计能拿回多少 lamports,批量关闭。
|
|
393
|
+
|
|
394
|
+
```ts
|
|
395
|
+
import { getReclaimable, reclaim } from '@w3lib/solana-batch-call/reclaim'
|
|
396
|
+
|
|
397
|
+
const r = await getReclaimable(owner, { provider: 'https://my-rpc…' }) // 全部代币账户:需要支持 getTokenAccountsByOwner 的节点;免费节点传 mints 只看指定代币的 ATA
|
|
398
|
+
r.totalLamports // 全部能拿回的 lamports;r.selectedLamports 是建议勾选的
|
|
399
|
+
// r.items: [{ kind: 'empty-token', account, mint, lamports: '2039280', selected: true, reasons: ['empty'] },
|
|
400
|
+
// { kind: 'junk-token', symbol: 'claim-rewards.xyz', amount: '777', lamports: '2039280', selected: false, reasons: ['no-price', 'spam-name'] },
|
|
401
|
+
// { kind: 'wrapped-sol', amount: '5000000', lamports: '7039280', rentLamports: '2039280' },
|
|
402
|
+
// { kind: 'orca-position', position: { protocol: 'orca-whirlpool', positionMint, pool }, lamports: '4433520' }, …]
|
|
403
|
+
// r.blocked: [{ kind: 'empty-token', blocker: 'frozen', … }, { kind: 'raydium-position', blocker: 'owed', … }]
|
|
404
|
+
|
|
405
|
+
const { simulation, results } = await reclaim(client, key, r.items.filter((i) => i.selected), { destination: coldWallet, dryRun: true })
|
|
406
|
+
simulation.lamports // 预计退回的 lamports(模拟里的 SOL 变化,不含手续费)
|
|
407
|
+
await reclaim(client, key, r.items.filter((i) => i.selected)) // 构建 → 模拟 → 签名发送
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
- **种类**(`kind`):`empty-token`(空的 SPL Token / Token-2022 账户,有暂存转账手续费的先收到 mint 上)、`junk-token`(垃圾币:销毁后关闭)、`wrapped-sol`(关闭即解包,`lamports` 含包装的 SOL,`rentLamports` 只是租金)、`orca-position` / `raydium-position`(流动性、手续费、奖励都为 0 的集中流动性仓位:关闭仓位账户、销毁 NFT;Token-2022 的 NFT 连 mint 一起退回)。`blocked` 里是暂时关不掉的:`frozen`(冻结的账户,保守起见不尝试)、`close-authority`、`confidential-balance`、`paused` / `permissioned-burn`(销毁不了)、`owed`(仓位还有没领的手续费 / 奖励,先用 `/claim` 领取)
|
|
411
|
+
- **垃圾币的判断很保守**:有价格且价值超过 `maxJunkUsd`(默认 0.01 美元)的、NFT(精度 0 余额 1)、仓位 NFT 一律不列。列出的垃圾币只有“有把握”的 `selected: true`:有价格但价值不超过 `maxJunkUsd` 的尘埃(`dust`)、`/risk` 标记为不可转让 / 转账手续费很高 / 已暂停(`risk:*`)。名字可疑(链接 / 域名 / claim / airdrop,`spam-name`)只作提示、不会单独触发勾选(名字谁都能起);没有价格也没有其他依据的只列出(`reasons: ['no-price']` / `['no-price', 'spam-name']`),由应用决定(可用 `isJunk` 勾选)。价格服务出错或 `prices: false` 时不勾选任何垃圾币(`warnings` 里有说明)。`isJunk({ account, price, usd, risk })` 返回 `true` 一定算垃圾币并勾选,`false` 一定排除,`undefined` 用默认判断(NFT 和仓位 NFT 不受它影响);`prices` 可以传自己的价格源(如 `combinePrices([defillamaPrices(), onchainPrices()])`,默认只用 DefiLlama:链上价格每个代币要读约 190 个池子地址)
|
|
412
|
+
- **安全**:`buildReclaim` 发送前重新读取每个账户并核对 owner、mint、余额、冻结、关闭权限、仓位是否仍是空的,变了的不构建、列在 `plan.skipped`(带原因)。不会关闭有余额的账户:空账户余额不为 0 时跳过;垃圾币只销毁和扫描时一致的余额(有人又转入了就跳过);只有 Wrapped SOL 带余额关闭。账户的 owner 必须是签名者,每个账户一个调用(垃圾币:销毁 → 关闭,原子执行),多个账户打包进同一笔交易,`feePayer` 可以替 owner 付手续费
|
|
413
|
+
- **模拟**:`simulateReclaim(client, plan)` 用 `/simulate` 逐笔模拟,`failed` 列出会失败的账户(整笔失败时逐个重新模拟找出来);`lamports` 是预计退回的 SOL:收款地址就是付款人时取模拟里的真实变化(`lamportsSource: 'simulation'`),否则按账户余额估算。`reclaim` 默认先模拟、失败的账户不发送;`dryRun: true` 只构建和模拟
|
|
414
|
+
- **destination**:退回的 SOL 给谁,默认 owner。Raydium CLMM 的仓位账户租金程序固定退给 owner,不能指定(`plan.warnings` 有提示)
|
|
415
|
+
- 限制:只处理上面这些;质押 / 锁仓的仓位、Orca 的仓位组合(Position Bundle)不在 owner 名下;冻结的账户不关闭(`/approvals` 的 `closable` 不检查冻结);价格只用于判断,不保证没有误判,发送前请展示列表给用户确认
|
|
416
|
+
|
|
417
|
+
主网核对(只模拟,不发送):找到有大量空仓位的真实钱包(Orca SPL NFT、Raydium SPL NFT、Raydium Token-2022 NFT 各有),其中一个 249 个账户(空代币账户、尘埃币销毁 + 关闭、136 个 Raydium 仓位、69 个 Orca 仓位)分 32 笔交易模拟全部通过,模拟的 SOL 变化与预计的 1068283440 lamports 逐位一致;Token-2022 NFT 的仓位关闭时 mint 一起退回(每个约 0.0042 SOL),预计值据此修正。
|
|
418
|
+
|
|
419
|
+
## 批量领取
|
|
420
|
+
|
|
421
|
+
子路径 `@w3lib/solana-batch-call/claim`:Orca Whirlpool、Raydium CLMM 仓位的未领取手续费和挖矿奖励(Orca 的奖励 slot、Raydium 的农场奖励)。先只读预览,再把全部(或选中的)仓位的领取打包成交易。指令来自 `/liquidity` 的 `collectFees`(手续费 + 全部奖励,SOL 自动解包,缺少的代币账户自动创建),这里只做批量和打包。
|
|
422
|
+
|
|
423
|
+
```ts
|
|
424
|
+
import { claimAll, getClaimable } from '@w3lib/solana-batch-call/claim'
|
|
425
|
+
|
|
426
|
+
const c = await getClaimable(owner, { provider: 'https://my-rpc…' }) // 只读预览,不发交易;免费节点传 mints(仓位 NFT)只看指定仓位
|
|
427
|
+
c.totalUsd // 手续费 + 奖励的美元价值;c.tokens 按代币汇总;c.incomplete 为 true 时有仓位的数量读不到(tick 数据),该代币行带 incomplete: true,数量偏低、usd 为 null
|
|
428
|
+
// c.positions: [{ protocol: 'raydium-clmm', positionMint, pool, token0, token1, inRange, totalUsd: 11.54,
|
|
429
|
+
// fees: [{ symbol: 'SOL', formatted: '0.0606', usd: 6.45 }, { symbol: 'USDC', formatted: '5.011', usd: 5.01 }],
|
|
430
|
+
// rewards: [{ index: 0, mint: RAY, symbol: 'RAY', formatted: '0.042698', usd: 0.083 }] }]
|
|
431
|
+
|
|
432
|
+
const plan = await buildClaimAll(client, owner, c.positions) // 只构建:每个仓位一个调用
|
|
433
|
+
const sim = await simulateClaim(client, plan) // 模拟:sim.received 是收到的各代币合计(usd 是合计数量的美元价值),sim.failed 是会失败的仓位
|
|
434
|
+
await claimAll(client, key, c.positions, { minUsd: 0.5 }) // 构建 → 模拟 → 签名发送(positions 不传时领取全部);dryRun: true 只模拟
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
- **奖励的数量照搬程序结算**:池子的奖励增长按时间摊给当前流动性(Δt × 每秒排放 / 流动性,Raydium 到 `end_time` 为止),再按仓位区间内的增长(全局 − 下方 − 上方,u128 回绕)计算,加上已记账的。`getLpPositions(owner, { rewards: true })` 也可以直接拿到每个仓位的 `rewards`(`/lp` 的新选项,`now` 指定结算时刻);读不到两端的 tick 数据时数量为 `null`
|
|
438
|
+
- 没有可领的东西(手续费和奖励都是 0)的仓位不列;流动性为 0 但还有没领的手续费 / 奖励的仓位照常列出。领完的空仓位可以用 `/reclaim` 关闭回收租金
|
|
439
|
+
- `claimAll` 默认先模拟,失败的仓位不发送(`results[i].status: 'failed'`),一个仓位失败不影响其他的;钱包和选项同 `batchCall`(`feePayer`、`priorityFee` 等)。新建代币账户(如还没有的奖励代币的账户)的租金由 owner 付,owner 没有 SOL 时模拟会报 createIdempotent 失败
|
|
440
|
+
- 限制:手续费和奖励都领到 owner 自己的代币账户(不能指定收款地址);带转账手续费的 Token-2022 池子不支持(构建失败,列在 `plan.failed`);预览是读取时刻的数量,发送时会继续累积
|
|
441
|
+
|
|
442
|
+
主网核对(只模拟,不发送):Raydium CLMM SOL-USDC 池(奖励 RAY)里真实仓位的预览与模拟时程序实际转出的数量一致(手续费相差几秒的累积,RAY 奖励 0.042698 逐位一致)。Orca 的奖励结算只有单元测试(主网上没有找到有奖励排放的 Orca 仓位),公式与 Raydium 相同;Orca 的手续费沿用 `/lp` 已核对的结果。
|
|
443
|
+
|
|
444
|
+
打包(minify,含 `SolanaClient`,发送和模拟的代码按需加载):`/reclaim` 约 124KB,`/claim` 约 143KB(含 `/liquidity` 的指令构建);主入口不变。
|
|
445
|
+
|
|
390
446
|
## 代币风险
|
|
391
447
|
|
|
392
448
|
子路径 `@w3lib/solana-batch-call/risk`:买入前检查代币的权限和 Token-2022 扩展里会影响持有人的设置。
|
|
@@ -432,8 +488,64 @@ const [trade] = await getTradeChecks([mint], { owner: myAddress, amountFormatted
|
|
|
432
488
|
|
|
433
489
|
打包:在已经用到 `SolanaClient` 的基础上,`getTokenRisks` 约增加 5KB(minify,gzip 约 1.5KB);`tradeChecks` 用到 `/swap`,一起约 43KB(gzip 约 16KB)。
|
|
434
490
|
|
|
491
|
+
## 交易模拟
|
|
492
|
+
|
|
493
|
+
子路径 `@w3lib/solana-batch-call/simulate`:发送前在主网模拟一笔交易(`simulateTransaction`,不验签名、换最新 blockhash,不需要私钥、不会上链),给出余额变化、授权变化、指令说明、程序错误、手续费和警告。
|
|
494
|
+
|
|
495
|
+
```ts
|
|
496
|
+
import { getSwapQuote } from '@w3lib/solana-batch-call/swap'
|
|
497
|
+
import { getSimulation } from '@w3lib/solana-batch-call/simulate'
|
|
498
|
+
|
|
499
|
+
const plan = await getSwapQuote(owner, { tokenIn: NATIVE_MINT, tokenOut: USDC, amountInFormatted: '0.1' })
|
|
500
|
+
const sim = await getSimulation(plan, { prices: true }) // 也可以是 base64 的交易:getSimulation(base64Tx, { owner })
|
|
501
|
+
// { success: true, gasUsed: '112394', fee: '5000',
|
|
502
|
+
// balanceChanges: [{ address: owner, asset: 'native', symbol: 'SOL', delta: '-100000000', formatted: '-0.1', usd: -10.68 }, { asset: USDC, symbol: 'USDC', delta: '10677005', … }],
|
|
503
|
+
// approvalChanges: [], decoded: { type: 'swap', protocol: 'Orca', summary: 'Swap 0.1 SOL for 10.677005 USDC on Orca', instructions: [...] }, warnings: [], logs, signers, version: 'legacy' }
|
|
504
|
+
if (!sim.success || sim.warnings.some((w) => w.severity === 'danger')) throw new Error(sim.error ?? sim.warnings[0].message)
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
- **输入**:base64 / 字节的交易(legacy 和 v0,v0 的地址查找表自动读取;v1 格式报错);本库构建器的输出:`buildSwap` / `buildAddLiquidity` 等的计划(多步合并成一笔,放不下时用 `step` 选一步)、`{ owner, instructions }`(如 `/write` 的指令工具)、`buildRevokes` / `buildCloses` 的结果
|
|
508
|
+
- **余额变化**:站在 `owner`(默认第一个签名者)的角度;SOL 不含手续费(手续费在 `fee`),Wrapped SOL 账户的变化并入 SOL(`asset: 'native'`),代币按模拟前后的代币账户算;`watch` 可以加看别的地址;`prices: true` 加美元价值
|
|
509
|
+
- **授权变化**(`approvalChanges`):按模拟前后的账户状态比较,CPI 里发生的也看得到:`delegate-set` / `delegate-changed` / `delegate-revoked`(带额度,接近 u64 上限为 `unlimited`)、`authority-changed`(账户 owner、关闭权限、mint 的增发 / 冻结权限被改)、`account-closed`。模拟失败时只能按指令判断
|
|
510
|
+
- **指令解码**:System、SPL Token / Token-2022、关联代币账户、计算预算、Memo、升级程序加载器,Jupiter、Raydium(AMM v4 / CLMM / CPMM)、Orca、Meteora、Pump.fun 等程序的协议和指令名(Anchor 程序按 discriminator),`decoded.type` 为 `transfer | swap | approve | liquidity | mint | contract | unknown`;程序不认识时,按钱包“一个资产减少、另一个增加”推断为 `swap`(`decoded.inferred: true`)
|
|
511
|
+
- **错误**:`error` 带失败的指令、程序名和错误名(Anchor 日志里的错误名优先;System、Token、Jupiter v6 等按错误码查表),如 `instruction 2 (SPL Token transferChecked) failed: InsufficientFunds (0x1)`
|
|
512
|
+
- **警告**(`{ code, severity: 'info' | 'warn' | 'danger', message, address? }`):`unlimited-delegate`(danger)/ `delegate-approved`、`authority-changed`(账户 owner / 关闭权限为 danger)、`assign-owner`、`program-upgrade`、`close-to-other`、`unknown-program`、`new-recipient`(收款地址从没有过 SOL / 没有这个代币的账户)、`unexpected-signer`(交易还需要别人签名,`signers` 可以列出预期的)、`high-fee`、`drains-balance`、`simulation-failed`;`risk: true` 时按 `/risk` 检查变化的代币(懒加载),永久授权、转账 hook、高转账手续费等加进 `warnings`,`risks` 里是全部标记
|
|
513
|
+
- 公共节点:publicnode 的 `simulateTransaction` 一次最多返回 10 个账户,先读前状态只留和钱包有关的账户(钱包、新建的账户、钱包的代币账户、mint),多于上限时自动分批模拟
|
|
514
|
+
|
|
515
|
+
主网核对(publicnode):热钱包转 SOL / USDC(新地址警告、自动建 ATA)、余额不够的错误说明、`buildSwap` 的兑换(SOL → USDC,Orca,美元价值)、无限额度 Approve、Jupiter 最近的一笔真实 v0 交易(地址查找表,余额变化和协议都读对)。
|
|
516
|
+
|
|
517
|
+
打包:在已经用到 `SolanaClient` 的基础上,`/simulate` 约增加 45KB(minify,gzip 约 16KB;价格、风险按需加载)。
|
|
518
|
+
|
|
519
|
+
## 交易历史
|
|
520
|
+
|
|
521
|
+
子路径 `@w3lib/solana-batch-call/history`:地址的交易整理成统一的 `Activity`,数据源可换,游标是最后一条签名。
|
|
522
|
+
|
|
523
|
+
```ts
|
|
524
|
+
import { getHistory, heliusHistory, rpcHistory } from '@w3lib/solana-batch-call/history'
|
|
525
|
+
|
|
526
|
+
const page = await getHistory(address, { limit: 20 })
|
|
527
|
+
// { items: [{ hash, time, block, status: 'success', type: 'swap', label: 'Swap 2.57 SOL for 274.47 USDC on Jupiter', protocol: 'Jupiter',
|
|
528
|
+
// changes: [{ asset: 'native', symbol: 'SOL', delta: '-2569401878', formatted: '-2.569401878' }, { asset: USDC, … }], fee: '5000' }, …],
|
|
529
|
+
// nextCursor: '4jNtY56m…' }
|
|
530
|
+
const next = await getHistory(address, { cursor: page.nextCursor, types: ['swap', 'transfer'] })
|
|
531
|
+
await getHistory(address, { source: [heliusHistory({ apiKey }), rpcHistory()] }) // Helius 失败时用节点
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
- `Activity`:`type` 为 `transfer | swap | approve | liquidity | mint | contract | other`,`direction`(`in | out | self`,转账等只有一个方向时)、`counterparty`、`label`、`changes`(这个地址的资产变化,SOL 不含手续费、Wrapped SOL 并入 SOL,symbol 自动补全)、`fee`(这个地址付的手续费)、`protocol`;失败的交易 `status: 'failed'`、没有资产变化、只有手续费
|
|
535
|
+
- 选项:`limit`(默认 20,最多 100)、`cursor`、`before` / `after`(区块时间,unix 秒,按签名里的 blockTime 先过滤、不多取详情)、`types`(不够 `limit` 时往后多找,最多看 `limit × 5` 笔,`nextCursor` 接着往后)、`source`、`symbols: false`(不补全代币符号)
|
|
536
|
+
- **默认数据源 `rpcHistory()`**:`getSignaturesForAddress`(`before` 翻页)+ `getTransaction`(逐笔,`concurrency` 默认 6,限频由节点层退避重试和换节点)。类型按指令(认得的协议 / 转账 / 授权 / 铸造)加余额变化判断:流动性 > 兑换 > 授权 > 铸造;只有转账指令是转账;有不认得的程序时一减一增推断为兑换,否则是 `contract`
|
|
537
|
+
- **`heliusHistory({ apiKey })`**:Helius 增强交易(`/v0/addresses/{address}/transactions`),按 `accountData` 的余额变化整理成同样的 `Activity`;apiKey 由应用传入。传多个数据源时按顺序尝试,前一个失败换下一个,游标通用
|
|
538
|
+
- 主网已经有 v1 格式的交易(2026-10 约三成),`getTransaction` 要传 `maxSupportedTransactionVersion: 1`(只传 0 时遇到它们整个请求报 -32015),本库已处理
|
|
539
|
+
- 限制:免费节点不支持按时间 / 区块直接定位,`before` / `after` 只能从最新往后翻到目标;每页的请求数 ≈ `limit + 1`;Helius 数据源按文档的返回格式写,单元测试用样例数据,没有用真实 key 核对
|
|
540
|
+
|
|
541
|
+
主网核对:热钱包翻两页(游标衔接、没有重复)、USDC / SOL 转账的方向和对手方、从 Raydium CLMM 池子最近的一笔交易里找到交易者并按 `types: ['swap']` 过滤、`after` 时间范围。
|
|
542
|
+
|
|
543
|
+
打包:在已经用到 `SolanaClient` 的基础上,`/history` 约增加 21KB(minify,gzip 约 8KB),`heliusHistory` 约 3KB。
|
|
544
|
+
|
|
435
545
|
## 余额的三种模式
|
|
436
546
|
|
|
547
|
+
`client.balances(owner, mints, options)` 也可以写成 `client.balances(owner, { mints, ...options })`(两种写法等价)。
|
|
548
|
+
|
|
437
549
|
| | 指定 mints(默认,`accounts: 'ata'`) | 指定 mints + `accounts: 'all'` | 扫描(不传 mints,或 `scan: true`) |
|
|
438
550
|
| --- | --- | --- | --- |
|
|
439
551
|
| 实现 | 本地推导 ATA + 一次 `getMultipleAccounts` | 每个代币一次按 mint 过滤的 `getTokenAccountsByOwner`(同一 tick 发出,合并成批量请求) | `getTokenAccountsByOwner`(Token / Token-2022 各一次) |
|
|
@@ -455,7 +567,7 @@ await getBalances(owner, [USDC, PYUSD], { provider: 'https://my-rpc.example', ac
|
|
|
455
567
|
- **缓存**:decimals、所属程序永久缓存;name、symbol 来自可修改的元数据,缓存 1 小时(确认没有元数据的代币同样缓存,不会每次重查)。按网络共享(各函数共用);自定义节点且没指定 `cluster` 时只在该客户端内缓存。最多 5 万个代币,超出淘汰最早的
|
|
456
568
|
- **大数**:lamports 等 u64 超过 2⁵³ 时不会丢精度
|
|
457
569
|
- **失败**:mint 不存在、地址非法等只让该项 `success: false`,并带 `error` 说明原因;节点不可用等整个请求失败时才抛错(多个节点都失败时是 `AllNodesFailedError`,`err.errors` 里有每个节点的名称和原因)
|
|
458
|
-
- **取消**:所有查询都可以传 `signal`(`AbortSignal`)。已取消时直接 reject、不发请求;中途取消时立即 reject
|
|
570
|
+
- **取消**:所有查询都可以传 `signal`(`AbortSignal`)。已取消时直接 reject、不发请求;中途取消时立即 reject(reason 为 `signal.reason`)。signal 会传到底层:还在排队的请求不再发出,已经发出的 HTTP 请求在合并在一起的所有调用方都取消后中止,取消后也不再换节点。自定义传输的 `request(method, params, options?)` 可以从第三个参数拿到 `{ signal }`,不处理也照常工作
|
|
459
571
|
- **最低 slot**:`getBalances` / `getMultiBalances` / `getSolBalances` / `getOwnerTokens` / `accounts` 可以传 `minContextSlot`:节点高度不够时换下一个节点,都不够时每 500ms 重试,最多约 10 秒;`withSlot: true`(或传了 `minContextSlot`)时结果项带读取时的 `slot`(一次查询发了多个请求时取最小的)
|
|
460
572
|
|
|
461
573
|
## 轮询余额
|
|
@@ -507,6 +619,8 @@ token.transferFee
|
|
|
507
619
|
| `no-holder` | 没有持有人(供应量为 0 或已销毁) | `getNftOwners` |
|
|
508
620
|
| `missing-field` | 代币存在,但请求的字段读不到;`errorField` 是第一个读不到的字段(如没有元数据时的 `name`) | `getTokens` |
|
|
509
621
|
|
|
622
|
+
owner 等参数本身非法时(不是单项失败)抛 `InvalidAddressError`(继承 `Error`,message 不变,带 `code: 'INVALID_ADDRESS'` 和 `address`)。
|
|
623
|
+
|
|
510
624
|
```ts
|
|
511
625
|
const [item] = await getBalances(owner, [mint])
|
|
512
626
|
if (item.error === 'invalid-address') throw new Error('bad mint') // 参数错误
|
|
@@ -538,9 +652,11 @@ await getBalances(owner, mints, { provider: 'https://my-rpc.example', cluster: '
|
|
|
538
652
|
|
|
539
653
|
**传输层行为**(`HttpRpc`):
|
|
540
654
|
|
|
541
|
-
- 收集窗口(`batchWait`,默认 0 即同一 tick)内的调用合并成一个批量请求,最多 `maxBatchSize`(默认 20
|
|
542
|
-
-
|
|
543
|
-
-
|
|
655
|
+
- 收集窗口(`batchWait`,默认 0 即同一 tick)内的调用合并成一个批量请求,最多 `maxBatchSize`(默认 20)条;同一批里 method 和 params 完全相同的只读调用只发一次,结果分给各调用方(`dedupe: false` 关闭)
|
|
656
|
+
- 同一节点地址同时在途的 HTTP 请求最多 `maxConcurrency`(默认 4,按 URL 在所有连接间共享;`0` 不限制),超出的排队;排队最多等 `timeout`(等不到按超时处理、换节点),发出后重新计时
|
|
657
|
+
- publicnode 的已知限制(单次 `getMultipleAccounts` 最多 10 个账户、一个批量里最多 1 个 `getMultipleAccounts`)是预置的,第一次请求就按它拆分,不用先等 3 秒多的 403;其他节点的限制从报错里学到后按节点地址记住
|
|
658
|
+
- 节点不支持批量请求时自动改为逐条并发;返回 “Batch of more than N” 时按 N 缩小批量;请求体过大(413)时批量减半(这两种缩小 10 分钟后恢复原来的大小)。批量请求被限频或拒绝访问时不会降级(降级只会让限频更严重)
|
|
659
|
+
- HTTP 429 和 JSON-RPC 错误码 429 都会退避重试(`retries`,默认 2;传入多个自定义节点时为 0,直接换节点;内置节点保留重试),退避带随机抖动,有 `Retry-After` 头时按它等(超过 10 秒不等,直接报错换节点);批量里只重试被限频的那几条
|
|
544
660
|
- 单次请求超时 `timeout`,默认 10 秒
|
|
545
661
|
|
|
546
662
|
**故障切换**(多节点时):网络错误、超时、限频、403、需要 API Key、请求被拦截、高度不到 `minContextSlot` 等节点问题换下一个节点;参数错误等确定性错误直接抛出(`RpcError`,`isNodeFault(err)` 可判断)。
|
|
@@ -576,6 +692,8 @@ new SolanaClient(provider?, {
|
|
|
576
692
|
batchWait: 0,
|
|
577
693
|
batch: true, // false:不合并成批量请求
|
|
578
694
|
retries: 2,
|
|
695
|
+
maxConcurrency: 4, // 同一节点地址同时在途的 HTTP 请求数
|
|
696
|
+
dedupe: true, // 同一批里相同的只读调用只发一次
|
|
579
697
|
cooldown: 30_000,
|
|
580
698
|
headers: { 'x-api-key': '…' },
|
|
581
699
|
fetch: customFetch, // 需要代理等场景
|
|
@@ -641,7 +759,7 @@ await batchCall(client, [{ from: signer, instructions }])
|
|
|
641
759
|
**打包与发送**:
|
|
642
760
|
- 同一手续费支付者的相邻调用打包进同一笔交易(`mode: 'pack'`,默认;`'tx'` 每个调用一笔)。Solana 一笔交易本来就能放多条指令,不需要 EVM 上 Disperse 那样的合约
|
|
643
761
|
- 每笔先模拟:失败的调用(余额不足、账户冻结、SOL 转给新地址但低于免租下限等)单独标记 `failed`,其余重新模拟后照常发送;模拟失败且同一支付者还有未确认的交易时,先等它们确认再模拟一次(后一笔依赖前一笔的结果时自动正确处理)。节点暂时的问题(限频、高度落后)会重试,不当成调用失败
|
|
644
|
-
- 计算单元上限按模拟结果设置(× 1.1 + 1000),优先费用默认 `'auto'
|
|
762
|
+
- 计算单元上限按模拟结果设置(× 1.1 + 1000),优先费用默认 `'auto'`:按这笔交易可写账户的最近费用去掉 0 后取 75 分位(不活跃的账户大多数 slot 是 0,带上 0 几乎总是 0),不超过 `maxPriorityFee`(默认 1,000,000 micro-lamports / CU)。有优先费用时,第一次模拟顺带取付款方模拟后的余额(和当前余额一起读,同一个批量请求),付完优先费用后仍不低于免租下限就不再模拟第二次,否则照旧按真实费用再模拟一次。调用里自带的 SetComputeUnitLimit / SetComputeUnitPrice 指令会被去掉(和库加的重复会让交易失败),优先费用用 `priorityFee` 指定
|
|
645
763
|
- 交易发出后不等确认就发下一笔;等待期间每两次轮询重发一次原交易,一次 `getSignatureStatuses` 查所有交易。blockhash 过期仍没上链时换新 blockhash 重新模拟、重发,最多 `retries` 次(默认 2)。判断过期时,再查一次历史的那个节点的数据必须不早于看到过期的那个 slot(多节点 / 负载均衡时落后的节点可能还没看到已上链的交易),否则这一轮先不判断,避免重复执行
|
|
646
764
|
- Token-2022:有转账手续费的代币,`amount` 是转出的数量,收款方实际收到的更少;带 transfer hook 的代币,TransferChecked 需要额外的账户,本库不支持(模拟会失败,这一项 `failed`)
|
|
647
765
|
- 同一笔交易上链后才失败时一起回滚,里面的调用都是 `reverted`
|
|
@@ -658,7 +776,7 @@ await batchCall(client, [{ from: signer, instructions }])
|
|
|
658
776
|
|
|
659
777
|
**归集流程**(`batchCollect`):一次读出所有钱包的余额 → 归集地址没有该代币的 ATA 时创建(传了 `feePayer` 时随第一批转账创建;不传时先由第一个有这种代币的钱包单独创建,其他钱包只需付得起手续费)→ 打包发送代币转账(`closeAccounts` 时随后关闭代币账户)→ 包含 SOL 时等代币归集确认,按最新余额扫空 SOL:传了 `feePayer` 时整个余额转走,否则扣掉这笔的手续费后转走(余额归零)。代币没有全部归集成功的钱包不扫 SOL;余额为 0、低于 `minAmount`、钱包就是归集地址或 `feePayer` 的项为 `skipped`。Solana 上不需要先给子钱包补 gas,传 `feePayer` 即可;不传时没有 SOL 的钱包对应的项失败。`dryRun` 时代币转账照常模拟,扫 SOL 的金额要等归集之后才知道,只标记 `simulated`、不做模拟。
|
|
660
778
|
|
|
661
|
-
**共同选项**(`SendOptions`):`feePayer`、`priorityFee` / `maxPriorityFee`、`wait`(默认 true)、`timeout`(默认 120 秒)、`pollInterval`(默认 2 秒)、`retries`(默认 2)、`commitment`(`'confirmed'` / `'finalized'`)、`concurrency`(不同手续费支付者并发数,默认 8)、`dryRun`、`signal`、`onUpdate
|
|
779
|
+
**共同选项**(`SendOptions`):`feePayer`、`priorityFee` / `maxPriorityFee`、`wait`(默认 true)、`timeout`(默认 120 秒)、`pollInterval`(默认 2 秒)、`retries`(默认 2)、`commitment`(`'confirmed'` / `'finalized'`)、`concurrency`(不同手续费支付者并发数,默认 8)、`dryRun`、`signal`、`onUpdate`、`sendRpc`(发送交易用的节点,如专门的发送服务;模拟和查状态仍用 client)、`broadcast`(`'first'` 默认只发给第一个可用节点;`'all'` 同时发给所有节点,第一个接受就返回,更容易上链但请求数成倍增加)、`computeUnitMargin`(计算单元上限 = 模拟消耗 × `ratio` + `extra`,默认 1.1 和 1000;消耗会随状态波动的调用可以调大)。`sendRpc` 按 client 的网络和节点选项(headers、fetch、timeout 等)解析,和 client 不在同一个网络时报 `NetworkMismatchError`。等待确认时只在交易可能快过期时才查区块高度(按状态查询返回的 slot 估算),不再每轮都查。
|
|
662
780
|
|
|
663
781
|
**指令工具**:`systemTransfer`、`transferChecked`、`createAssociatedTokenAccountIdempotent`、`closeAccount`、`revoke`、`harvestWithheldTokensToMint`、`setComputeUnitLimit`、`setComputeUnitPrice`,生成本库格式的指令,可直接放进 `batchCall`。
|
|
664
782
|
|
|
@@ -694,6 +812,8 @@ pnpm test:live:lp # LP 仓位连主网核对
|
|
|
694
812
|
pnpm test:live:liquidity # 加减流动性在主网上模拟(simulateTransaction,不发送)
|
|
695
813
|
pnpm test:live:swap # 兑换:本地报价和主网模拟逐位核对(RECORD=1 时更新 test/fixtures/swap-mainnet.json.gz)
|
|
696
814
|
pnpm test:live:risk # 代币风险和买入 / 卖出模拟连主网
|
|
815
|
+
pnpm test:live:simulate # 交易模拟连主网(自己构建的交易、Jupiter 的 v0 交易)
|
|
816
|
+
pnpm test:live:history # 交易历史连主网(翻页、类型过滤、时间范围)
|
|
697
817
|
pnpm demo # 本地运行 demo(demo/,直接用 src 的源码)
|
|
698
818
|
pnpm build
|
|
699
819
|
```
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { RequestOptions, SlotOptions } from './client.js';
|
|
2
|
+
import type { AccountInfo } from './layout.js';
|
|
3
|
+
import { type RpcTransport } from './rpc.js';
|
|
4
|
+
import type { Commitment } from './types.js';
|
|
5
|
+
/** 一次查询的上下文:取消、最低 slot,以及读到的 slot(各请求 context.slot 的最小值) */
|
|
6
|
+
export interface QueryContext {
|
|
7
|
+
signal?: AbortSignal;
|
|
8
|
+
minContextSlot?: number;
|
|
9
|
+
slot?: number;
|
|
10
|
+
}
|
|
11
|
+
export declare const MAX_ACCOUNTS_PER_REQUEST = 100;
|
|
12
|
+
/** fetchAccounts 里没能读到(节点问题)的账户 */
|
|
13
|
+
export declare const FAILED: unique symbol;
|
|
14
|
+
/** 可以降级处理的错误:网络 / HTTP 错误、节点限制(限频、需要 Key 等)、节点不在期望的网络上、所有节点都失败;其他(参数错误、程序 bug)照常抛出 */
|
|
15
|
+
export declare function isRecoverable(err: unknown): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* 账户读取管线:getMultipleAccounts 按 100 个一组、去重、并发池(最多同时 3 个),节点高度不够时等待重试,
|
|
18
|
+
* 记录读到的 slot;tolerant 时节点问题只标记失败。SolanaClient 和余额 / 代币详情模块共用
|
|
19
|
+
*/
|
|
20
|
+
export declare class AccountReader {
|
|
21
|
+
readonly transport: RpcTransport;
|
|
22
|
+
readonly commitment: Commitment;
|
|
23
|
+
constructor(transport: RpcTransport, commitment: Commitment);
|
|
24
|
+
/** 发给传输层:有 signal 时作为第三个参数传下去(没有时只传两个参数,自定义传输不受影响) */
|
|
25
|
+
send<T>(method: string, params: readonly unknown[] | undefined, signal: AbortSignal | undefined): Promise<T>;
|
|
26
|
+
/**
|
|
27
|
+
* 发一个读账户类的请求:params 最后一项是配置对象,传了 minContextSlot 时带上;
|
|
28
|
+
* 节点高度不够(所有节点都落后)时每 500ms 重试,最多约 10 秒;记录返回的 context.slot(取最小值)
|
|
29
|
+
*/
|
|
30
|
+
call<T>(ctx: QueryContext, method: string, params: unknown[]): Promise<T>;
|
|
31
|
+
accounts(addresses: readonly string[], ctx: QueryContext): Promise<(AccountInfo | null)[]>;
|
|
32
|
+
/**
|
|
33
|
+
* 只读 lamports(主币余额):getMultipleAccounts 带 dataSlice { offset: 0, length: 0 },不读回账户数据——
|
|
34
|
+
* 钱包可能是数据很大的程序账户(上百 KB),整份读回会拖慢甚至超时。结果与 addresses 一一对应,账户不存在或地址非法时为 null
|
|
35
|
+
*/
|
|
36
|
+
lamports(addresses: readonly string[], ctx: QueryContext): Promise<(bigint | null)[]>;
|
|
37
|
+
/** getMultipleAccounts(读账户数据):见 fetchGroups */
|
|
38
|
+
fetchAccounts(addresses: readonly string[], tolerant: boolean, ctx: QueryContext, failure?: {
|
|
39
|
+
error?: unknown;
|
|
40
|
+
}): Promise<Map<string, AccountInfo | null | typeof FAILED>>;
|
|
41
|
+
/**
|
|
42
|
+
* getMultipleAccounts:每组去重、按 100 个一组拆分,所有组的请求放进并发池,最多同时 3 个(一个结束就发下一个,不必等同一波里最慢的)。
|
|
43
|
+
* lamportsOnly 的组带 dataSlice(长度 0,对同一请求里的所有账户生效,所以只要 lamports 的账户要单独成组)。
|
|
44
|
+
* tolerant 为 true 时,某个请求因节点问题(网络、限频等)失败不抛错:该请求的账户标记为 FAILED,并且不再发后面的请求(节点已经在限频了),
|
|
45
|
+
* 第一个这样的错误记在 failure.error;否则第一个错误抛出,后面的请求也不再发。结果按组返回
|
|
46
|
+
*/
|
|
47
|
+
fetchGroups(groups: ReadonlyArray<{
|
|
48
|
+
addresses: readonly string[];
|
|
49
|
+
lamportsOnly: boolean;
|
|
50
|
+
}>, tolerant: boolean, ctx: QueryContext, failure?: {
|
|
51
|
+
error?: unknown;
|
|
52
|
+
}): Promise<Array<Map<string, AccountInfo | null | typeof FAILED>>>;
|
|
53
|
+
}
|
|
54
|
+
/** 结果项带上 slot(传了 withSlot / minContextSlot,且这次查询发了请求时) */
|
|
55
|
+
export declare function attachSlot<T extends object>(items: T[], ctx: QueryContext, options: SlotOptions): T[];
|
|
56
|
+
export declare function queryContext(options: RequestOptions & SlotOptions): QueryContext;
|
|
57
|
+
//# sourceMappingURL=accountReader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accountReader.d.ts","sourceRoot":"","sources":["../src/accountReader.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAsD,KAAK,YAAY,EAAE,MAAM,UAAU,CAAA;AAEhG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAG5C,4DAA4D;AAC5D,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAMD,eAAO,MAAM,wBAAwB,MAAM,CAAA;AAG3C,mCAAmC;AACnC,eAAO,MAAM,MAAM,eAAmB,CAAA;AAEtC,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAEnD;AAuBD;;;GAGG;AACH,qBAAa,aAAa;IAEtB,QAAQ,CAAC,SAAS,EAAE,YAAY;IAChC,QAAQ,CAAC,UAAU,EAAE,UAAU;gBADtB,SAAS,EAAE,YAAY,EACvB,UAAU,EAAE,UAAU;IAGjC,sDAAsD;IACtD,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC;IAI5G;;;OAGG;IACG,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC;IAsBzE,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,CAAC,WAAW,GAAG,IAAI,CAAC,EAAE,CAAC;IAKhG;;;OAGG;IACG,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IAK3F,+CAA+C;IACzC,aAAa,CACjB,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,QAAQ,EAAE,OAAO,EACjB,GAAG,EAAE,YAAY,EACjB,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAC5B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,MAAM,CAAC,CAAC;IAK3D;;;;;OAKG;IACG,WAAW,CACf,MAAM,EAAE,aAAa,CAAC;QAAE,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;QAAC,YAAY,EAAE,OAAO,CAAA;KAAE,CAAC,EAC9E,QAAQ,EAAE,OAAO,EACjB,GAAG,EAAE,YAAY,EACjB,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAC5B,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,MAAM,CAAC,CAAC,CAAC;CAyCnE;AAED,0DAA0D;AAC1D,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,GAAG,CAAC,EAAE,CAMrG;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,cAAc,GAAG,WAAW,GAAG,YAAY,CAEhF"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// 账户读取管线(getMultipleAccounts 分组 / 并发 / 重试)和查询上下文
|
|
2
|
+
import { base64 } from '@scure/base';
|
|
3
|
+
import { isAddress } from './address.js';
|
|
4
|
+
import { AllNodesFailedError, HttpError, RpcError, isBehind } from './rpc.js';
|
|
5
|
+
import { NetworkMismatchError } from './source.js';
|
|
6
|
+
import { mapPool, sleep } from './util.js';
|
|
7
|
+
/** 节点高度不够时的重试间隔与总时长 */
|
|
8
|
+
const BEHIND_RETRY_INTERVAL = 500;
|
|
9
|
+
const BEHIND_RETRY_TIMEOUT = 10_000;
|
|
10
|
+
export const MAX_ACCOUNTS_PER_REQUEST = 100;
|
|
11
|
+
/** 同时进行的 getMultipleAccounts 请求数上限 */
|
|
12
|
+
const MAX_PARALLEL_ACCOUNT_REQUESTS = 3;
|
|
13
|
+
/** fetchAccounts 里没能读到(节点问题)的账户 */
|
|
14
|
+
export const FAILED = Symbol('failed');
|
|
15
|
+
/** 可以降级处理的错误:网络 / HTTP 错误、节点限制(限频、需要 Key 等)、节点不在期望的网络上、所有节点都失败;其他(参数错误、程序 bug)照常抛出 */
|
|
16
|
+
export function isRecoverable(err) {
|
|
17
|
+
return err instanceof HttpError || (err instanceof RpcError && err.nodeFault) || err instanceof NetworkMismatchError || err instanceof AllNodesFailedError;
|
|
18
|
+
}
|
|
19
|
+
function toAccountInfo(address, raw) {
|
|
20
|
+
if (!raw) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const encoded = Array.isArray(raw.data) ? raw.data[0] : raw.data;
|
|
24
|
+
return {
|
|
25
|
+
address,
|
|
26
|
+
lamports: BigInt(raw.lamports),
|
|
27
|
+
owner: raw.owner,
|
|
28
|
+
data: base64.decode(encoded),
|
|
29
|
+
executable: raw.executable,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 账户读取管线:getMultipleAccounts 按 100 个一组、去重、并发池(最多同时 3 个),节点高度不够时等待重试,
|
|
34
|
+
* 记录读到的 slot;tolerant 时节点问题只标记失败。SolanaClient 和余额 / 代币详情模块共用
|
|
35
|
+
*/
|
|
36
|
+
export class AccountReader {
|
|
37
|
+
transport;
|
|
38
|
+
commitment;
|
|
39
|
+
constructor(transport, commitment) {
|
|
40
|
+
this.transport = transport;
|
|
41
|
+
this.commitment = commitment;
|
|
42
|
+
}
|
|
43
|
+
/** 发给传输层:有 signal 时作为第三个参数传下去(没有时只传两个参数,自定义传输不受影响) */
|
|
44
|
+
send(method, params, signal) {
|
|
45
|
+
return signal ? this.transport.request(method, params, { signal }) : this.transport.request(method, params);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 发一个读账户类的请求:params 最后一项是配置对象,传了 minContextSlot 时带上;
|
|
49
|
+
* 节点高度不够(所有节点都落后)时每 500ms 重试,最多约 10 秒;记录返回的 context.slot(取最小值)
|
|
50
|
+
*/
|
|
51
|
+
async call(ctx, method, params) {
|
|
52
|
+
const last = params[params.length - 1];
|
|
53
|
+
const sent = ctx.minContextSlot === undefined ? params : [...params.slice(0, -1), { ...last, minContextSlot: ctx.minContextSlot }];
|
|
54
|
+
const started = Date.now();
|
|
55
|
+
for (;;) {
|
|
56
|
+
try {
|
|
57
|
+
const res = await this.send(method, sent, ctx.signal);
|
|
58
|
+
const slot = res?.context?.slot;
|
|
59
|
+
if (typeof slot === 'number') {
|
|
60
|
+
ctx.slot = ctx.slot === undefined ? slot : Math.min(ctx.slot, slot);
|
|
61
|
+
}
|
|
62
|
+
return res;
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
if (ctx.minContextSlot === undefined || !isBehind(err) || Date.now() - started + BEHIND_RETRY_INTERVAL > BEHIND_RETRY_TIMEOUT) {
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
68
|
+
await sleep(BEHIND_RETRY_INTERVAL, ctx.signal);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async accounts(addresses, ctx) {
|
|
73
|
+
const fetched = await this.fetchAccounts(addresses, false, ctx);
|
|
74
|
+
return addresses.map((address) => fetched.get(address) ?? null);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 只读 lamports(主币余额):getMultipleAccounts 带 dataSlice { offset: 0, length: 0 },不读回账户数据——
|
|
78
|
+
* 钱包可能是数据很大的程序账户(上百 KB),整份读回会拖慢甚至超时。结果与 addresses 一一对应,账户不存在或地址非法时为 null
|
|
79
|
+
*/
|
|
80
|
+
async lamports(addresses, ctx) {
|
|
81
|
+
const [fetched] = await this.fetchGroups([{ addresses, lamportsOnly: true }], false, ctx);
|
|
82
|
+
return addresses.map((address) => fetched?.get(address)?.lamports ?? null);
|
|
83
|
+
}
|
|
84
|
+
/** getMultipleAccounts(读账户数据):见 fetchGroups */
|
|
85
|
+
async fetchAccounts(addresses, tolerant, ctx, failure) {
|
|
86
|
+
const [fetched] = await this.fetchGroups([{ addresses, lamportsOnly: false }], tolerant, ctx, failure);
|
|
87
|
+
return fetched;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* getMultipleAccounts:每组去重、按 100 个一组拆分,所有组的请求放进并发池,最多同时 3 个(一个结束就发下一个,不必等同一波里最慢的)。
|
|
91
|
+
* lamportsOnly 的组带 dataSlice(长度 0,对同一请求里的所有账户生效,所以只要 lamports 的账户要单独成组)。
|
|
92
|
+
* tolerant 为 true 时,某个请求因节点问题(网络、限频等)失败不抛错:该请求的账户标记为 FAILED,并且不再发后面的请求(节点已经在限频了),
|
|
93
|
+
* 第一个这样的错误记在 failure.error;否则第一个错误抛出,后面的请求也不再发。结果按组返回
|
|
94
|
+
*/
|
|
95
|
+
async fetchGroups(groups, tolerant, ctx, failure) {
|
|
96
|
+
const chunks = [];
|
|
97
|
+
groups.forEach(({ addresses, lamportsOnly }, group) => {
|
|
98
|
+
const unique = [...new Set(addresses.filter((a) => isAddress(a)))];
|
|
99
|
+
for (let i = 0; i < unique.length; i += MAX_ACCOUNTS_PER_REQUEST) {
|
|
100
|
+
chunks.push({ group, addresses: unique.slice(i, i + MAX_ACCOUNTS_PER_REQUEST), lamportsOnly });
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
const results = groups.map(() => new Map());
|
|
104
|
+
// 出错后不再发后面的请求
|
|
105
|
+
let stopped = false;
|
|
106
|
+
// 并发池最先发出的几个请求在同一 tick(合并成一个 JSON-RPC 批量请求)
|
|
107
|
+
await mapPool(chunks, MAX_PARALLEL_ACCOUNT_REQUESTS, async (chunk) => {
|
|
108
|
+
const result = results[chunk.group];
|
|
109
|
+
if (stopped) {
|
|
110
|
+
chunk.addresses.forEach((address) => result.set(address, FAILED));
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// 已取消的查询不再发后面的请求
|
|
114
|
+
if (ctx.signal?.aborted) {
|
|
115
|
+
throw ctx.signal.reason;
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
const res = await this.call(ctx, 'getMultipleAccounts', [
|
|
119
|
+
chunk.addresses,
|
|
120
|
+
{ encoding: 'base64', commitment: this.commitment, ...(chunk.lamportsOnly ? { dataSlice: { offset: 0, length: 0 } } : {}) },
|
|
121
|
+
]);
|
|
122
|
+
chunk.addresses.forEach((address, j) => result.set(address, toAccountInfo(address, res?.value?.[j] ?? null)));
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
stopped = true;
|
|
126
|
+
if (!tolerant || !isRecoverable(err)) {
|
|
127
|
+
throw err;
|
|
128
|
+
}
|
|
129
|
+
if (failure && !('error' in failure)) {
|
|
130
|
+
failure.error = err;
|
|
131
|
+
}
|
|
132
|
+
chunk.addresses.forEach((address) => result.set(address, FAILED));
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
return results;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/** 结果项带上 slot(传了 withSlot / minContextSlot,且这次查询发了请求时) */
|
|
139
|
+
export function attachSlot(items, ctx, options) {
|
|
140
|
+
if ((!options.withSlot && options.minContextSlot === undefined) || ctx.slot === undefined) {
|
|
141
|
+
return items;
|
|
142
|
+
}
|
|
143
|
+
const slot = ctx.slot;
|
|
144
|
+
return items.map((item) => ({ ...item, slot }));
|
|
145
|
+
}
|
|
146
|
+
export function queryContext(options) {
|
|
147
|
+
return { signal: options.signal, minContextSlot: options.minContextSlot };
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=accountReader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accountReader.js","sourceRoot":"","sources":["../src/accountReader.ts"],"names":[],"mappings":"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"}
|
package/dist/address.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export declare function encodeAddress(bytes: Uint8Array): string;
|
|
|
8
8
|
* y ≥ p、x = 0 但符号位为 1 都算不在曲线上。只需判断 x² = (y² - 1) / (d·y² + 1) 有没有解,不必真的开方
|
|
9
9
|
*/
|
|
10
10
|
export declare function isOnCurve(bytes: Uint8Array): boolean;
|
|
11
|
+
/** 测试用:这组种子的 PDA 是否在缓存里 */
|
|
12
|
+
export declare function isPdaCached(seeds: readonly Uint8Array[], programId: string): boolean;
|
|
11
13
|
/**
|
|
12
14
|
* 推导 PDA(与 web3.js PublicKey.findProgramAddressSync 相同),返回地址。
|
|
13
15
|
* seeds 每项不超过 32 字节、最多 15 个(加上 bump 共 16 个,链上上限),超出时抛错。
|
|
@@ -19,4 +21,11 @@ export declare function createProgramAddress(seeds: Uint8Array[], programId: str
|
|
|
19
21
|
export declare function getAssociatedTokenAddress(owner: string, mint: string, tokenProgram: string): string;
|
|
20
22
|
/** Metaplex Token Metadata 账户地址 */
|
|
21
23
|
export declare function getMetadataAddress(mint: string): string;
|
|
24
|
+
/** 地址不合法时抛出(继承 Error,message 与之前的普通 Error 相同;用 code / address 判断) */
|
|
25
|
+
export declare class InvalidAddressError extends Error {
|
|
26
|
+
readonly code = "INVALID_ADDRESS";
|
|
27
|
+
/** 不合法的那个值 */
|
|
28
|
+
readonly address: string;
|
|
29
|
+
constructor(message: string, address: string);
|
|
30
|
+
}
|
|
22
31
|
//# sourceMappingURL=address.d.ts.map
|
package/dist/address.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"address.d.ts","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":"AAKA,qCAAqC;AACrC,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CASzD;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAMzD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAEvD;AA4BD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAoBpD;
|
|
1
|
+
{"version":3,"file":"address.d.ts","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":"AAKA,qCAAqC;AACrC,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CASzD;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAMzD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAEvD;AA4BD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAoBpD;AAmCD,2BAA2B;AAC3B,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAEpF;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CA4BjF;AAED,iHAAiH;AACjH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAMnF;AAED,oBAAoB;AACpB,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEnG;AAED,mCAAmC;AACnC,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKvD;AAED,qEAAqE;AACrE,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,IAAI,qBAAoB;IACjC,cAAc;IACd,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;gBAEZ,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK7C"}
|