@w3lib/solana-batch-call 0.1.0 → 0.2.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 CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0
4
+
5
+ - `getBalances` / `getOwnerTokens` 新增 `tokenPrograms`:按代币类型过滤,`['spl']` 不含 Token-2022(扫描时也不再请求 Token-2022 的代币账户),`['token-2022']` 反之;也可以传程序地址。不认识的值或空数组报错
6
+ - `getBalances` 新增 `accounts: 'ata' | 'all'`:指定 mints 时用按 mint 过滤的 `getTokenAccountsByOwner` 统计全部代币账户(含非 ATA),比全量扫描轻
7
+ - 失败项带原因:`success: false` 时返回 `error`(`invalid-address` / `not-found` / `not-token` / `no-metadata` / `no-holder` / `missing-field`),`getTokens` 另带 `errorField`;新增导出类型 `FailureReason`。mint 地址非法和 mint 不存在可以区分了
8
+ - 去掉运行时依赖 `@noble/curves`:PDA 推导的“是否在曲线上”改为内置实现(与 noble / web3.js 结果一致,新增导出 `isOnCurve`);`@noble/hashes` 版本范围放宽为 `^1.8.0 || ^2.0.0`,可与 `@solana/web3.js` 带的 1.x 去重。打包体积(minify)64.2KB → 36.4KB,gzip 24.7KB → 14.0KB
9
+ - 修复:`nativeMints` 不含 `So111…112` 时,扫描模式的余额列表里 wSOL 出现两次、没有原生 SOL
10
+ - 文档:`nativeMints` 可以包含非 Solana 地址(作为调用方自己的主币标识)
11
+ - 所有节点都失败时抛 `AllNodesFailedError`(`errors: { node, error }[]`,node 只含 URL 的 host),不再只有最后一个节点的错误;单节点时仍抛原始错误
12
+ - 超时与出错分开计:超时只换节点、不冷却,同一节点连续 3 次超时才冷却;`HttpError.timeout` 标记超时
13
+ - 节点健康状态(冷却、连续超时)全局共享:URL 相同即同一节点,客户端重建后仍然有效
14
+ - `HttpRpc` 报错信息里的地址只保留 origin,不再带出 path / query 里的 API Key
15
+ - 单个节点也包一层 `FallbackRpc`(`client.transport` 现在总是 `FallbackRpc`,原节点在 `transport.nodes[0]`)
16
+ - `onRequest(listener)`:监听每次调用的节点、方法、耗时、成功与否、第几次尝试;新增 `nodeLabel`、`RequestEvent`,自定义传输可以设 `label`
17
+ - 所有查询方法和独立函数支持 `signal`(`AbortSignal`)
18
+ - `minContextSlot` / `withSlot`(`balances` / `multiBalances` / `solBalances` / `ownerTokens`,`accounts` 支持 `minContextSlot`):节点高度不够时换节点或等待重试(最多约 10 秒),结果带读取时的 `slot`
19
+ - `multiBalances` / `getMultiBalances`:多个钱包一次查,ATA 模式下合并成一次 `getMultipleAccounts`
20
+ - `watchBalances`:轮询余额,相同参数的订阅共用一份轮询,余额变化时才通知
21
+ - 代币信息缓存可持久化:`persistTokenMetaCache(localStorage)`,或 `exportTokenMetaCache()` / `importTokenMetaCache()`
22
+ - `getTokens` 新增字段 `transferFee`:Token-2022 转账手续费(按当前 epoch 选出生效的配置);新增 `parseTransferFeeConfig`
23
+ - 新增 `formatUnits`(与 ethers 同名),`formatAmount` 保留为别名并标记 deprecated
24
+ - `engines.node` 改为 `>=20.19.0`(依赖的 `@noble/hashes` 2.x 的要求)
25
+
3
26
  ## 0.1.0
4
27
 
5
28
  首个版本:
package/README.md CHANGED
@@ -16,7 +16,7 @@ pnpm add @w3lib/solana-batch-call
16
16
  ## 快速上手
17
17
 
18
18
  ```ts
19
- import { NATIVE_MINT, getBalances, getNftOwners, getNfts, getOwnerNfts, getOwnerTokens, getSolBalances, getTokens } from '@w3lib/solana-batch-call'
19
+ import { NATIVE_MINT, getBalances, getMultiBalances, getNftOwners, getNfts, getOwnerNfts, getOwnerTokens, getSolBalances, getTokens, watchBalances } from '@w3lib/solana-batch-call'
20
20
 
21
21
  // 资产列表:持有人拥有的全部代币(SOL 在第一位,带 name / symbol;需要支持索引方法的节点)
22
22
  await getOwnerTokens(owner, { provider: 'https://my-rpc.example' })
@@ -34,10 +34,22 @@ await getBalances(owner, [NATIVE_MINT, USDC, PYUSD], { symbol: true })
34
34
  // ...
35
35
  // ]
36
36
 
37
+ // 只要某类代币:tokenPrograms 传 ['spl'] 时结果不含 Token-2022 代币(SOL 不受影响)
38
+ await getBalances(owner, undefined, { provider: 'https://my-rpc.example', tokenPrograms: ['spl'] })
39
+
37
40
  // 多个地址的 SOL 余额
38
41
  await getSolBalances([addressA, addressB])
39
42
 
40
- // 代币详情,字段可选:name / symbol / uri / decimals / supply / tokenProgram / mintAuthority / freezeAuthority(默认 name、symbol、decimals)
43
+ // 多个钱包一次查(ATA 模式下合并成一次 getMultipleAccounts),结果与 queries 一一对应
44
+ await getMultiBalances([{ owner: walletA, mints: [NATIVE_MINT, USDC] }, { owner: walletB, mints: [USDC] }])
45
+
46
+ // 刚成交后刷新余额:要求节点高度至少到成交所在的 slot(落后的节点自动换掉),结果带 slot
47
+ await getBalances(owner, [NATIVE_MINT, USDC], { minContextSlot: txSlot })
48
+
49
+ // 多处共用一份轮询,余额变化时才通知
50
+ const stop = watchBalances(owner, [NATIVE_MINT, USDC], { interval: 10_000, onChange: (list) => render(list) })
51
+
52
+ // 代币详情,字段可选:name / symbol / uri / decimals / supply / tokenProgram / mintAuthority / freezeAuthority / transferFee(默认 name、symbol、decimals)
41
53
  await getTokens([USDC, PYUSD], { fields: ['name', 'symbol', 'decimals', 'supply'] })
42
54
  // [{ address: USDC, native: false, name: 'USD Coin', symbol: 'USDC', decimals: 6, supply: '…', supplyFormatted: '…', success: true }, ...]
43
55
 
@@ -49,10 +61,12 @@ await getOwnerNfts(owner) // 某地址持有的全部 NFT(需要支持索
49
61
 
50
62
  | 函数 | SolanaClient 方法 | 说明 |
51
63
  | --- | --- | --- |
52
- | `getBalances(owner, mints?, opts)` | `balances` | SOL + SPL Token + Token-2022 余额;`symbol: true` 额外返回 symbol;不传 mints 返回全部持仓 |
53
- | `getOwnerTokens(owner, opts)` | `ownerTokens` | 持有人的全部代币(资产列表),带 name / symbol;默认不含 NFT 和余额为 0 的账户 |
64
+ | `getBalances(owner, mints?, opts)` | `balances` | SOL + SPL Token + Token-2022 余额;`symbol: true` 额外返回 symbol;不传 mints 返回全部持仓;`tokenPrograms` 按代币类型过滤 |
65
+ | `getOwnerTokens(owner, opts)` | `ownerTokens` | 持有人的全部代币(资产列表),带 name / symbol;默认不含 NFT 和余额为 0 的账户;`tokenPrograms` 按代币类型过滤 |
66
+ | `getMultiBalances(queries, opts)` | `multiBalances` | 多个钱包一次查,参数同 `getBalances` |
67
+ | `watchBalances(owner, mints?, opts)` | — | 轮询余额,相同参数的订阅共用一份轮询,余额变化时才通知 `onChange`;返回取消函数 |
54
68
  | `getSolBalances(addresses, opts)` | `solBalances` | 多个地址的 SOL 余额 |
55
- | `getTokens(mints, opts)` | `tokens` | 代币详情,`fields` 选择字段,结果类型随之收窄 |
69
+ | `getTokens(mints, opts)` | `tokens` | 代币详情,`fields` 选择字段,结果类型随之收窄;`transferFee` 返回 Token-2022 转账手续费 |
56
70
  | `getNfts(mints, opts)` | `nfts` | NFT 元数据(Metaplex;Token-2022 NFT 取扩展) |
57
71
  | `getNftOwners(mints, opts)` | `nftOwners` | NFT 持有人 |
58
72
  | `getOwnerNfts(owner, opts)` | `ownerNfts` | 某地址持有的全部 NFT |
@@ -72,22 +86,86 @@ await getOwnerNfts(owner) // 某地址持有的全部 NFT(需要支持索
72
86
 
73
87
  需要支持 `getTokenAccountsByOwner` 的节点,内置的免费公共节点大多不支持。
74
88
 
75
- ## 余额的两种模式
89
+ ## 余额的三种模式
76
90
 
77
- | | 指定 mints(默认) | 扫描(不传 mints,或 `scan: true`) |
78
- | --- | --- | --- |
79
- | 实现 | 本地推导 ATA + 一次 `getMultipleAccounts` | `getTokenAccountsByOwner`(Token / Token-2022 各一次) |
80
- | 统计范围 | 只统计 **ATA** 里的余额 | 该地址的**全部**代币账户,同一代币多个账户合计 |
81
- | 免费公共节点 | 可用 | 大多不支持,需要自己的节点(Helius、QuickNode、Triton 等) |
91
+ | | 指定 mints(默认,`accounts: 'ata'`) | 指定 mints + `accounts: 'all'` | 扫描(不传 mints,或 `scan: true`) |
92
+ | --- | --- | --- | --- |
93
+ | 实现 | 本地推导 ATA + 一次 `getMultipleAccounts` | 每个代币一次按 mint 过滤的 `getTokenAccountsByOwner`(同一 tick 发出,合并成批量请求) | `getTokenAccountsByOwner`(Token / Token-2022 各一次) |
94
+ | 统计范围 | 只统计 **ATA** 里的余额 | 指定代币的**全部**代币账户,多个账户合计 | 该地址的**全部**代币账户,同一代币多个账户合计 |
95
+ | 返回 | 指定的代币 | 指定的代币 | 不传 mints:所有余额大于 0 的代币;传 mints:指定的代币 |
96
+ | 免费公共节点 | 可用 | 大多不支持,需要自己的节点(Helius、QuickNode、Triton 等) | 同左 |
82
97
 
83
- 普通钱包的代币都在 ATA 里,用默认模式即可。交易所、托管类地址常有非 ATA 的代币账户,默认模式会少算,需要 `scan: true`。
98
+ 普通钱包的代币都在 ATA 里,用默认模式即可。交易所、托管类地址常有非 ATA 的代币账户(实际碰到过 PYUSD 余额全在非 ATA 账户里的持有人),默认模式会少算:只关心少数几个代币时用 `accounts: 'all'`,要全部持仓时用扫描。同时传 `scan: true` 和 `accounts` 时按扫描处理。
99
+
100
+ ```ts
101
+ await getBalances(owner, [USDC, PYUSD], { provider: 'https://my-rpc.example', accounts: 'all' })
102
+ ```
84
103
 
85
104
  其他规则:
86
105
 
87
- - **主币**:`So111…112`(`NATIVE_MINT`,Wrapped SOL 的 mint)和 `11111…1`(System Program)按原生 SOL 查询(lamports,9 位精度);此时 Wrapped SOL 代币账户里的余额不单独列出。可用 `nativeMints` 配置,传 `[SYSTEM_PROGRAM_ID]` 时 `So111…112` 按普通代币(Wrapped SOL)查询
106
+ - **按代币类型过滤**:`tokenPrograms` 默认两种都返回,传 `['spl']` 只要 SPL Token、传 `['token-2022']` 只要 Token-2022(也可以传程序地址 `TOKEN_PROGRAM_ID` / `TOKEN_2022_PROGRAM_ID`;不认识的值或空数组会报错),不符合的代币直接从结果里去掉(结果条数可能少于 mints)。扫描模式下只请求对应程序的代币账户;指定 mints 时所属程序未知的代币先读 mint 账户确认(与 ATA 在同一次请求里)。mint 不存在、无法判断类型的项照常返回 `success: false`;主币 SOL 不受影响(`getOwnerTokens` 用 `includeNative` 控制)
107
+
108
+ - **主币**:`So111…112`(`NATIVE_MINT`,Wrapped SOL 的 mint)和 `11111…1`(System Program)按原生 SOL 查询(lamports,9 位精度);此时 Wrapped SOL 代币账户里的余额不单独列出。可用 `nativeMints` 配置,传 `[SYSTEM_PROGRAM_ID]` 时 `So111…112` 按普通代币(Wrapped SOL)查询。`nativeMints` 也可以包含非 Solana 地址,作为调用方自己的主币标识(如多链项目统一用 `0xEeee…EEeE` 表示主币),`balances` / `tokens` 遇到它时按 SOL 返回、`token` 原样带回,这是支持的用法
88
109
  - **缓存**:decimals、所属程序永久缓存;name、symbol 来自可修改的元数据,缓存 1 小时(确认没有元数据的代币同样缓存,不会每次重查)。按网络共享(各函数共用);自定义节点且没指定 `cluster` 时只在该客户端内缓存。最多 5 万个代币,超出淘汰最早的
89
110
  - **大数**:lamports 等 u64 超过 2⁵³ 时不会丢精度
90
- - **失败**:mint 不存在、地址非法等只让该项 `success: false`;节点不可用等整个请求失败时才抛错
111
+ - **失败**:mint 不存在、地址非法等只让该项 `success: false`,并带 `error` 说明原因;节点不可用等整个请求失败时才抛错(多个节点都失败时是 `AllNodesFailedError`,`err.errors` 里有每个节点的名称和原因)
112
+ - **取消**:所有查询都可以传 `signal`(`AbortSignal`)。已取消时直接 reject、不发请求;中途取消时立即 reject,已经发出、和其他查询合并在一起的底层请求继续完成
113
+ - **最低 slot**:`getBalances` / `getMultiBalances` / `getSolBalances` / `getOwnerTokens` / `accounts` 可以传 `minContextSlot`:节点高度不够时换下一个节点,都不够时每 500ms 重试,最多约 10 秒;`withSlot: true`(或传了 `minContextSlot`)时结果项带读取时的 `slot`(一次查询发了多个请求时取最小的)
114
+
115
+ ## 轮询余额
116
+
117
+ ```ts
118
+ const stop = watchBalances(owner, [NATIVE_MINT, USDC], {
119
+ interval: 10_000, // 默认 15 秒;多处订阅同一份时取最小的
120
+ onChange: (list, previous) => render(list), // 第一次结果、以及之后余额变化时调用
121
+ onError: (err) => console.warn(err), // 某次失败,之后照常轮询
122
+ })
123
+ stop() // 最后一个订阅者取消时停止轮询
124
+ ```
125
+
126
+ - 同样的节点配置 + owner + mints(顺序无关)+ 影响结果的选项共用一份轮询;结果顺序按第一个订阅者传的 mints
127
+ - 上一次请求没完成时不会发起下一次;新订阅者加入时如果已经有结果,立即收到一次
128
+
129
+ ## 代币信息缓存持久化
130
+
131
+ decimals / 所属程序永久缓存、name / symbol 缓存 1 小时,默认只在内存里。刷新页面后不想重查 decimals 时:
132
+
133
+ ```ts
134
+ import { persistTokenMetaCache } from '@w3lib/solana-batch-call'
135
+
136
+ const stop = persistTokenMetaCache(localStorage) // 读入上次保存的,之后有变化约 1 秒后写回(最多 5000 条,可用 maxEntries 调整)
137
+ ```
138
+
139
+ 也可以自己存:`exportTokenMetaCache()` 返回可以 `JSON.stringify` 的快照,`importTokenMetaCache(snapshot)` 导入(格式不对的条目跳过,已有的不覆盖)。只包含按网络共享的条目(自定义节点且没指定 `cluster` 的客户端不导出)。
140
+
141
+ ## 转账手续费(Token-2022)
142
+
143
+ ```ts
144
+ const [token] = await getTokens([mint], { fields: ['decimals', 'transferFee'] })
145
+ token.transferFee
146
+ // { basisPoints: 50, maximumFee: '5000', epoch: '100', older: { … }, newer: { basisPoints: 200, maximumFee: '9000', epoch: '600' } }
147
+ ```
148
+
149
+ 顶层是当前 epoch 生效的配置(请求时顺带发一次 `getEpochInfo`,与账户读取在同一个批量请求里),`older` / `newer` 是扩展里的两套原始配置。SPL Token、没有这个扩展、主币为 `null`(不算失败)。实际到账 = 数量 − min(⌈数量 × basisPoints / 10000⌉, maximumFee)。
150
+
151
+ ## 失败原因
152
+
153
+ 各函数的单项结果在 `success: false` 时带 `error`(类型 `FailureReason`),成功时不带:
154
+
155
+ | `error` | 含义 | 出现在 |
156
+ | --- | --- | --- |
157
+ | `invalid-address` | 地址格式非法(不是 base58 的 32 字节) | 全部 |
158
+ | `not-found` | 地址合法,但链上没有这个账户(如 mint 不存在) | `getBalances` / `getTokens` / `getNfts` / `getNftOwners` |
159
+ | `not-token` | 账户存在,但不是代币 mint | 同上 |
160
+ | `no-metadata` | 代币存在,但没有元数据 | `getNfts` |
161
+ | `no-holder` | 没有持有人(供应量为 0 或已销毁) | `getNftOwners` |
162
+ | `missing-field` | 代币存在,但请求的字段读不到;`errorField` 是第一个读不到的字段(如没有元数据时的 `name`) | `getTokens` |
163
+
164
+ ```ts
165
+ const [item] = await getBalances(owner, [mint])
166
+ if (item.error === 'invalid-address') throw new Error('bad mint') // 参数错误
167
+ if (item.error === 'not-found') { /* 按 0 处理 */ }
168
+ ```
91
169
 
92
170
  ## 节点
93
171
 
@@ -119,7 +197,24 @@ await getBalances(owner, mints, { provider: 'https://my-rpc.example', cluster: '
119
197
  - HTTP 429 和 JSON-RPC 错误码 429 都会退避重试(`retries`,默认 2;传入多个自定义节点时为 0,直接换节点;内置节点保留重试),批量里只重试被限频的那几条
120
198
  - 单次请求超时 `timeout`,默认 10 秒
121
199
 
122
- **故障切换**(多节点时):网络错误、超时、限频、403、需要 API Key、请求被拦截等节点问题换下一个节点,出错节点冷却 `cooldown`(默认 30 秒);参数错误等确定性错误直接抛出(`RpcError`,`isNodeFault(err)` 可判断)。
200
+ **故障切换**(多节点时):网络错误、超时、限频、403、需要 API Key、请求被拦截、高度不到 `minContextSlot` 等节点问题换下一个节点;参数错误等确定性错误直接抛出(`RpcError`,`isNodeFault(err)` 可判断)。
201
+
202
+ - 出错的节点冷却 `cooldown`(默认 30 秒),期间排到最后。**超时不冷却**(大批量查询慢一点很正常),同一节点连续 3 次超时才冷却;高度不够也不冷却
203
+ - 节点健康状态全局共享:URL 相同即同一个节点(其他节点按对象),客户端重建后刚出过错的节点仍排在后面
204
+ - 所有节点都失败时抛 `AllNodesFailedError`,`err.errors` 是 `{ node, error }[]`;单个节点时抛原始错误
205
+ - 节点名称(`node`)只取 URL 的 host,报错信息里的地址也只保留 origin,不会带出 path / query 里的 API Key;自定义传输可以设 `label`
206
+
207
+ **可观测性**:`onRequest(listener)` 监听所有客户端发出的调用,返回取消函数:
208
+
209
+ ```ts
210
+ import { onRequest } from '@w3lib/solana-batch-call'
211
+
212
+ const stop = onRequest(({ node, method, ms, ok, error, attempt }) => {
213
+ metrics.record({ node, method, ms, ok }) // attempt > 0 表示这次调用换过节点
214
+ })
215
+ ```
216
+
217
+ 每个 JSON-RPC 调用对每个节点的每次尝试一条(合并成批量 HTTP 请求时,批里每个调用各一条,`ms` 是该调用从发出到返回的时间);单节点时也会触发。监听函数出错不影响请求。
123
218
 
124
219
  **网络校验**:传了 `cluster`,或传了多个节点时,会用 `getGenesisHash` 确认每个节点所在的网络,不一致时报 `NetworkMismatchError`(多节点时换下一个节点)。校验与请求并行发出,结果按节点缓存,每个节点只多一次请求;内置节点不校验。`await client.getCluster()` 返回节点所在网络。
125
220
 
@@ -151,12 +246,18 @@ import {
151
246
  getMetadataAddress, // Metaplex 元数据账户地址
152
247
  findProgramAddress, // 通用 PDA 推导(与 web3.js findProgramAddressSync 相同)
153
248
  isAddress,
249
+ isOnCurve, // 32 字节是否是 ed25519 曲线上的点(与 web3.js PublicKey.isOnCurve 相同)
154
250
  parseMint, parseTokenAccount, parseMetaplexMetadata, // 账户数据解析
155
- formatAmount,
251
+ parseTransferFeeConfig, // Token-2022 转账手续费扩展
252
+ formatUnits, // 按精度换算:formatUnits(1_500_000_000n, 9) === '1.5'(formatAmount 是同一个函数的旧名字,已不推荐)
156
253
  TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID, NATIVE_MINT, GENESIS_HASHES,
157
254
  } from '@w3lib/solana-batch-call'
158
255
  ```
159
256
 
257
+ ## 运行环境
258
+
259
+ Node 20.19+(依赖的 `@noble/hashes` 2.x 要求)、现代浏览器。
260
+
160
261
  ## 开发
161
262
 
162
263
  ```bash
package/dist/address.d.ts CHANGED
@@ -2,6 +2,12 @@
2
2
  export declare function isAddress(value: unknown): value is string;
3
3
  export declare function decodeAddress(address: string): Uint8Array;
4
4
  export declare function encodeAddress(bytes: Uint8Array): string;
5
+ /**
6
+ * 32 字节是否是合法的 ed25519 压缩点(PDA 必须不在曲线上)。
7
+ * 按 RFC 8032 5.1.3 解压缩,与 @noble/curves 的 Point.fromBytes(web3.js 的 isOnCurve 同样如此)一致:
8
+ * y ≥ p、x = 0 但符号位为 1 都算不在曲线上。只需判断 x² = (y² - 1) / (d·y² + 1) 有没有解,不必真的开方
9
+ */
10
+ export declare function isOnCurve(bytes: Uint8Array): boolean;
5
11
  /**
6
12
  * 推导 PDA(与 web3.js PublicKey.findProgramAddressSync 相同),返回地址。
7
13
  * seeds 每项不超过 32 字节。
@@ -1 +1 @@
1
- {"version":3,"file":"address.d.ts","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":"AAMA,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;AA2BD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAmBjF;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"}
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;AAiBD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAmBjF;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"}
package/dist/address.js CHANGED
@@ -1,4 +1,3 @@
1
- import { ed25519 } from '@noble/curves/ed25519.js';
2
1
  import { sha256 } from '@noble/hashes/sha2.js';
3
2
  import { base58 } from '@scure/base';
4
3
  import { ASSOCIATED_TOKEN_PROGRAM_ID, METADATA_PROGRAM_ID } from './constants.js';
@@ -24,15 +23,55 @@ export function decodeAddress(address) {
24
23
  export function encodeAddress(bytes) {
25
24
  return base58.encode(bytes);
26
25
  }
27
- /** 32 字节是否在 ed25519 曲线上(PDA 必须不在曲线上) */
28
- function isOnCurve(bytes) {
29
- try {
30
- ed25519.Point.fromBytes(bytes);
31
- return true;
26
+ // ed25519:-x² + y² = 1 + d·x²·y²(mod p)
27
+ const P = 2n ** 255n - 19n;
28
+ const D = 37095705934669439343138083508754565189542113879843219016388785533085940283555n;
29
+ /** x^(2^k) mod p */
30
+ function pow2k(x, k) {
31
+ let r = x;
32
+ for (let i = 0; i < k; i++)
33
+ r = (r * r) % P;
34
+ return r;
35
+ }
36
+ /** x^((p-1)/2) mod p(欧拉判别法),(p-1)/2 = (2^250 - 1)·2^4 + 6,用加法链:约 254 次平方 + 12 次乘法 */
37
+ function legendre(x) {
38
+ const b2 = (((x * x) % P) * x) % P; // x^(2^2-1)
39
+ const b4 = (pow2k(b2, 2) * b2) % P;
40
+ const b5 = (pow2k(b4, 1) * x) % P;
41
+ const b10 = (pow2k(b5, 5) * b5) % P;
42
+ const b20 = (pow2k(b10, 10) * b10) % P;
43
+ const b40 = (pow2k(b20, 20) * b20) % P;
44
+ const b80 = (pow2k(b40, 40) * b40) % P;
45
+ const b160 = (pow2k(b80, 80) * b80) % P;
46
+ const b240 = (pow2k(b160, 80) * b80) % P;
47
+ const b250 = (pow2k(b240, 10) * b10) % P;
48
+ return (pow2k(b250, 4) * ((b2 * b2) % P)) % P;
49
+ }
50
+ /**
51
+ * 32 字节是否是合法的 ed25519 压缩点(PDA 必须不在曲线上)。
52
+ * 按 RFC 8032 5.1.3 解压缩,与 @noble/curves 的 Point.fromBytes(web3.js 的 isOnCurve 同样如此)一致:
53
+ * y ≥ p、x = 0 但符号位为 1 都算不在曲线上。只需判断 x² = (y² - 1) / (d·y² + 1) 有没有解,不必真的开方
54
+ */
55
+ export function isOnCurve(bytes) {
56
+ if (bytes.length !== 32) {
57
+ return false;
32
58
  }
33
- catch {
59
+ let y = 0n;
60
+ for (let i = 31; i >= 0; i--) {
61
+ y = (y << 8n) | BigInt(i === 31 ? bytes[i] & 0x7f : bytes[i]);
62
+ }
63
+ if (y >= P) {
34
64
  return false;
35
65
  }
66
+ const y2 = (y * y) % P;
67
+ const u = (y2 - 1n + P) % P;
68
+ const v = (D * y2 + 1n) % P; // d 不是平方数,v 不会为 0
69
+ if (u === 0n) {
70
+ // x = 0:符号位必须为 0
71
+ return (bytes[31] & 0x80) === 0;
72
+ }
73
+ // u / v 是平方数 ⇔ u·v 是平方数(欧拉判别法)
74
+ return legendre((u * v) % P) === 1n;
36
75
  }
37
76
  const PDA_MARKER = new TextEncoder().encode('ProgramDerivedAddress');
38
77
  function concat(parts) {
@@ -1 +1 @@
1
- {"version":3,"file":"address.js","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"address.js","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":"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"}
package/dist/client.d.ts CHANGED
@@ -2,16 +2,38 @@ import { type Cluster } from './constants.js';
2
2
  import { type AccountInfo } from './layout.js';
3
3
  import { type RpcTransport } from './rpc.js';
4
4
  import { type RpcSource, type SourceOptions } from './source.js';
5
- import { type Commitment, type DefaultTokenField, type NftDetails, type NftOwner, type OwnedToken, type SolBalance, type TokenBalance, type TokenDetails, type TokenField } from './types.js';
5
+ import { type Commitment, type DefaultTokenField, type NftDetails, type NftOwner, type OwnedToken, type SolBalance, type TokenBalance, type TokenDetails, type TokenField, type TokenProgramOption } from './types.js';
6
6
  export interface ClientConfig extends SourceOptions {
7
7
  /** 网络。不传 provider 时用于选择内置节点(默认 mainnet);传了 provider 时用于校验节点所在网络 */
8
8
  cluster?: Cluster;
9
9
  /** 默认 'confirmed' */
10
10
  commitment?: Commitment;
11
- /** 视为原生 SOL 的 mint,默认 [So111…112(Wrapped SOL), 11111…1(System Program)] */
11
+ /**
12
+ * 视为原生 SOL 的 mint,默认 [So111…112(Wrapped SOL), 11111…1(System Program)]。
13
+ * 可以包含非 Solana 地址(如 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE),作为调用方自己的“主币”标识:
14
+ * balances / tokens 遇到它时按 SOL 返回,token 原样带回(这是支持的用法,不会做地址校验)
15
+ */
12
16
  nativeMints?: readonly string[];
13
17
  }
14
- export interface BalancesOptions {
18
+ /** 所有查询共用:取消 */
19
+ export interface RequestOptions {
20
+ /**
21
+ * 取消查询:已取消时直接 reject(不发请求);中途取消时立即 reject(reason 为 signal.reason)。
22
+ * 已经发出、和其他查询合并在一起的底层请求会继续完成(结果给其他调用方),不会中断
23
+ */
24
+ signal?: AbortSignal;
25
+ }
26
+ /** 读余额类查询共用:指定最低 slot、返回读取时的 slot */
27
+ export interface SlotOptions {
28
+ /**
29
+ * 最低 slot:节点高度不到这个 slot 时(如刚成交、公共节点还落后一两个 slot)换下一个节点;
30
+ * 所有节点都不够时每 500ms 重试,最多约 10 秒后抛错。传了它时结果项带 slot
31
+ */
32
+ minContextSlot?: number;
33
+ /** 结果项带 slot(读取时节点的 context.slot;一次查询发了多个请求时取最小的)。默认 false */
34
+ withSlot?: boolean;
35
+ }
36
+ export interface BalancesOptions extends RequestOptions, SlotOptions {
15
37
  /** 同时返回 symbol(代币查一次后缓存,主币为 SOL)。默认 false */
16
38
  symbol?: boolean;
17
39
  /**
@@ -19,8 +41,22 @@ export interface BalancesOptions {
19
41
  * 不传 mints 时总是扫描。注意:免费公共节点大多不支持这个方法,需要自己的节点。
20
42
  */
21
43
  scan?: boolean;
44
+ /**
45
+ * 指定 mints 时统计哪些代币账户(不传 mints 或 scan: true 时不起作用,总是统计全部账户):
46
+ * - 'ata'(默认):只看关联代币账户(ATA),本地推导地址 + getMultipleAccounts,免费节点也能用
47
+ * - 'all':每个代币发一次按 mint 过滤的 getTokenAccountsByOwner(同一 tick 发出,合并成批量请求),统计该代币的全部账户并合计。
48
+ * 只关心少数几个代币、又要算上非 ATA 账户时比 scan 轻得多;同样需要支持索引方法的节点
49
+ */
50
+ accounts?: 'ata' | 'all';
51
+ /**
52
+ * 只返回这些类型的代币:'spl'(SPL Token)、'token-2022',也可以传程序地址。默认两种都返回。
53
+ * 如传 ['spl']:结果里不含 Token-2022 代币(扫描时也不再请求 Token-2022 的代币账户)。主币 SOL 不受影响
54
+ */
55
+ tokenPrograms?: readonly TokenProgramOption[];
22
56
  }
23
- export interface OwnerTokensOptions {
57
+ export interface OwnerTokensOptions extends RequestOptions, SlotOptions {
58
+ /** 只返回这些类型的代币:'spl'、'token-2022' 或程序地址,默认两种都返回;主币 SOL 不受影响(用 includeNative 控制) */
59
+ tokenPrograms?: readonly TokenProgramOption[];
24
60
  /** 返回 name / symbol(代币查一次后缓存)。默认 true */
25
61
  metadata?: boolean;
26
62
  /** 第一项返回 SOL。默认 true */
@@ -30,26 +66,63 @@ export interface OwnerTokensOptions {
30
66
  /** 包含 NFT(精度 0 且数量 1)。默认 false */
31
67
  includeNfts?: boolean;
32
68
  }
33
- export interface TokensOptions<F extends TokenField = DefaultTokenField> {
69
+ export interface TokensOptions<F extends TokenField = DefaultTokenField> extends RequestOptions {
34
70
  /** 要返回的字段,默认 ['name', 'symbol', 'decimals'] */
35
71
  fields?: readonly F[];
36
72
  }
73
+ /** accounts() 的参数 */
74
+ export interface AccountsOptions extends RequestOptions {
75
+ /** 最低 slot,见 SlotOptions.minContextSlot */
76
+ minContextSlot?: number;
77
+ }
78
+ /** solBalances() 的参数 */
79
+ export interface SolBalancesOptions extends RequestOptions, SlotOptions {
80
+ }
81
+ /** multiBalances() 的单项:一个钱包和要查的代币 */
82
+ export interface BalanceQuery {
83
+ owner: string;
84
+ mints: readonly string[];
85
+ }
37
86
  /** 测试用:清空代币信息缓存 */
38
87
  export declare function resetTokenMetaCache(): void;
88
+ /** exportTokenMetaCache() 的结果,可以 JSON 序列化 */
89
+ export interface TokenMetaSnapshot {
90
+ version: 1;
91
+ /** [网络:mint, 信息];namesAt 是 name / symbol 的写入时间(毫秒时间戳),导入后继续按 1 小时过期 */
92
+ entries: Array<[string, {
93
+ decimals?: number;
94
+ tokenProgram?: string;
95
+ name?: string | null;
96
+ symbol?: string | null;
97
+ namesAt?: number;
98
+ }]>;
99
+ }
39
100
  /**
40
- * Solana 批量读取客户端。
101
+ * 导出代币信息缓存(decimals / 所属程序 / name / symbol),用于持久化:刷新页面后导入,不必重查 decimals。
102
+ * 只导出按网络共享的条目(自定义节点且没指定 cluster 的客户端,缓存只在该客户端内,不导出)。
103
+ * maxEntries:只导出最近写入的这么多条
104
+ */
105
+ export declare function exportTokenMetaCache(maxEntries?: number): TokenMetaSnapshot;
106
+ /** 导入 exportTokenMetaCache() 的结果;格式不对的条目跳过,内存里已有的条目不覆盖 */
107
+ export declare function importTokenMetaCache(snapshot: unknown): void;
108
+ export interface PersistTokenMetaOptions {
109
+ /** 存储的键,默认 'w3lib:solana-token-meta' */
110
+ key?: string;
111
+ /** 最多保存多少条(最近写入的优先),默认 5000 */
112
+ maxEntries?: number;
113
+ }
114
+ /**
115
+ * 把代币信息缓存持久化到同步的键值存储(如 localStorage):调用时读入,之后缓存有写入就防抖(约 1 秒)整体写回。
116
+ * 返回停止函数(会立即写回还没写的变化)。读到格式不对的数据会丢弃;写入失败(如存储已满)忽略。
41
117
  *
42
118
  * ```ts
43
- * const sol = new SolanaClient() // 内置公共节点(mainnet)
44
- * const sol = new SolanaClient('https://my-rpc.example') // 自己的节点
45
- * const sol = new SolanaClient([connection, 'https://backup…']) // web3.js Connection + 备用节点
46
- *
47
- * await sol.balances(owner, [NATIVE_MINT, USDC])
48
- * await sol.tokens([USDC, PYUSD], { fields: ['name', 'symbol', 'decimals', 'supply'] })
119
+ * persistTokenMetaCache(localStorage) // 刷新页面后 decimals 不必重查
49
120
  * ```
50
- *
51
- * 同一 tick 内发起的 RPC 调用会合并成一个 JSON-RPC 批量请求;账户读取按 100 个一组用 getMultipleAccounts。
52
121
  */
122
+ export declare function persistTokenMetaCache(store: {
123
+ getItem(key: string): string | null;
124
+ setItem(key: string, value: string): void;
125
+ }, options?: PersistTokenMetaOptions): () => void;
53
126
  export declare class SolanaClient {
54
127
  #private;
55
128
  readonly transport: RpcTransport;
@@ -57,21 +130,36 @@ export declare class SolanaClient {
57
130
  /** 节点所在网络(按创世区块哈希识别);不是 mainnet / devnet / testnet 时为 null */
58
131
  getCluster(): Promise<Cluster | null>;
59
132
  /** 直接发 JSON-RPC 调用(同样参与自动合并与故障切换) */
60
- request<T = unknown>(method: string, params?: readonly unknown[]): Promise<T>;
133
+ request<T = unknown>(method: string, params?: readonly unknown[], options?: RequestOptions): Promise<T>;
61
134
  /**
62
135
  * 批量读取账户(getMultipleAccounts,按 100 个一组,去重),结果与 addresses 一一对应。
63
136
  * 账户不存在或地址非法时为 null。
64
137
  */
65
- accounts(addresses: readonly string[]): Promise<(AccountInfo | null)[]>;
138
+ accounts(addresses: readonly string[], options?: AccountsOptions): Promise<(AccountInfo | null)[]>;
66
139
  /** 批量查多个地址的 SOL 余额(一次 getMultipleAccounts;账户不存在视为 0) */
67
- solBalances(addresses: readonly string[]): Promise<SolBalance[]>;
140
+ solBalances(addresses: readonly string[], options?: SolBalancesOptions): Promise<SolBalance[]>;
68
141
  /**
69
142
  * 批量查余额(主币 SOL + SPL Token + Token-2022),返回原始余额、decimals 和换算后的数值。
70
143
  *
71
144
  * - 传 mints:本地推导 ATA,与主币账户、mint 账户一起用 getMultipleAccounts 一次读完,免费节点也能用
145
+ * - 传 mints 且 accounts: 'all':每个代币按 mint 过滤查 getTokenAccountsByOwner,统计全部代币账户(含非 ATA)
72
146
  * - 不传 mints(或 scan: true):用 getTokenAccountsByOwner 扫描全部代币账户,返回所有余额大于 0 的代币
147
+ * - 失败项(success: false)带 error:invalid-address(mint 地址非法)/ not-found(mint 不存在)/ not-token(不是代币 mint)
148
+ * - tokenPrograms:只返回这些类型的代币,如 ['spl'] 不含 Token-2022(传了 mints 时,结果里去掉不符合的代币)
149
+ * - minContextSlot / withSlot:要求节点高度、返回读取时的 slot,见 SlotOptions
73
150
  */
74
151
  balances(owner: string, mints?: readonly string[], options?: BalancesOptions): Promise<TokenBalance[]>;
152
+ /**
153
+ * 多个钱包一次查(如多钱包的持仓列表),结果与 queries 一一对应。
154
+ * ATA 模式(默认)下所有钱包的账户合并成一次(按 100 个一组的)getMultipleAccounts;
155
+ * scan / accounts: 'all' 时每个钱包各自查询(同一 tick 发出,合并成批量请求)。其他选项与 balances() 相同;
156
+ * withSlot 时所有钱包的结果带同一个 slot(整次查询的最小值)。任一 owner 非法时报错
157
+ *
158
+ * ```ts
159
+ * await sol.multiBalances([{ owner: a, mints: [NATIVE_MINT, USDC] }, { owner: b, mints: [USDC] }])
160
+ * ```
161
+ */
162
+ multiBalances(queries: readonly BalanceQuery[], options?: BalancesOptions): Promise<TokenBalance[][]>;
75
163
  /**
76
164
  * 列出持有人拥有的全部代币(SOL 在第一位),带 name / symbol,适合做资产列表。
77
165
  * 需要支持 getTokenAccountsByOwner 的节点(免费公共节点大多不支持)。
@@ -79,18 +167,24 @@ export declare class SolanaClient {
79
167
  * - 默认不含 NFT(精度 0 且数量 1)和余额为 0 的代币账户(如已清空的 ATA),可用 includeNfts / includeZero 打开
80
168
  * - accounts 是该代币的代币账户数量;大于 1 时余额为所有账户的合计
81
169
  * - 顺序:SOL,然后 SPL Token 的代币,再然后 Token-2022 的代币(各自按节点返回的顺序;没有价格信息,不按价值排序)
170
+ * - tokenPrograms: ['spl'] 时不含 Token-2022 代币
171
+ * - withSlot 的 slot 只算余额扫描(元数据读取不计入)
82
172
  */
83
173
  ownerTokens(owner: string, options?: OwnerTokensOptions): Promise<OwnedToken[]>;
84
174
  /**
85
175
  * 批量查代币详情(一次 getMultipleAccounts),可以选择返回哪些字段。
86
176
  * name / symbol / uri 优先取 Token-2022 TokenMetadata 扩展,其次 Metaplex 元数据。
177
+ * transferFee:Token-2022 转账手续费,请求时会顺带发一次 getEpochInfo(同一个批量请求里)选出当前生效的配置
87
178
  */
88
179
  tokens<const F extends TokenField = DefaultTokenField>(mints: readonly string[], options?: TokensOptions<F>): Promise<TokenDetails<F>[]>;
89
180
  /** 批量查 NFT 元数据(Metaplex;Token-2022 NFT 取 TokenMetadata 扩展),一次 getMultipleAccounts */
90
- nfts(mints: readonly string[]): Promise<NftDetails[]>;
91
- /** 批量查 NFT 持有人:getTokenLargestAccounts(合并成一个批量请求)+ 一次 getMultipleAccounts */
92
- nftOwners(mints: readonly string[]): Promise<NftOwner[]>;
181
+ nfts(mints: readonly string[], options?: RequestOptions): Promise<NftDetails[]>;
182
+ /**
183
+ * 批量查 NFT 持有人:getTokenLargestAccounts(合并成一个批量请求)+ 一次 getMultipleAccounts。
184
+ * 节点拒绝的 mint(不存在 / 不是代币)在同一次 getMultipleAccounts 里读 mint 账户区分原因
185
+ */
186
+ nftOwners(mints: readonly string[], options?: RequestOptions): Promise<NftOwner[]>;
93
187
  /** 查某地址持有的全部 NFT(扫描代币账户:数量 1、精度 0),再批量读元数据。需要支持 getTokenAccountsByOwner 的节点 */
94
- ownerNfts(owner: string): Promise<NftDetails[]>;
188
+ ownerNfts(owner: string, options?: RequestOptions): Promise<NftDetails[]>;
95
189
  }
96
190
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}