@w3lib/solana-batch-call 0.5.0 → 0.6.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 +14 -0
- package/README.md +113 -1
- package/dist/batchPlan.d.ts +52 -0
- package/dist/batchPlan.d.ts.map +1 -0
- package/dist/batchPlan.js +85 -0
- package/dist/batchPlan.js.map +1 -0
- package/dist/claim.d.ts +161 -0
- package/dist/claim.d.ts.map +1 -0
- package/dist/claim.js +197 -0
- package/dist/claim.js.map +1 -0
- package/dist/decode.d.ts +22 -0
- package/dist/decode.d.ts.map +1 -0
- package/dist/decode.js +187 -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/history.d.ts +125 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +231 -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/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 +47 -0
- package/dist/lp.d.ts.map +1 -1
- package/dist/lp.js +91 -15
- 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/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 +190 -0
- package/dist/reclaim.d.ts.map +1 -0
- package/dist/reclaim.js +399 -0
- package/dist/reclaim.js.map +1 -0
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +4 -2
- 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 +418 -0
- package/dist/simulate.js.map +1 -0
- 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/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/package.json +21 -1
- package/src/batchPlan.ts +137 -0
- package/src/claim.ts +323 -0
- package/src/decode.ts +202 -0
- package/src/describe.ts +77 -0
- package/src/history.ts +336 -0
- package/src/historyHelius.ts +136 -0
- package/src/liquidity/orca.ts +15 -5
- package/src/liquidity/raydiumClmm.ts +13 -5
- package/src/lp.ts +140 -15
- package/src/lpMath.ts +38 -0
- package/src/programs.ts +216 -0
- package/src/reclaim.ts +547 -0
- package/src/rpc.ts +4 -2
- package/src/simulate.ts +535 -0
- package/src/subpaths/claim.ts +53 -0
- package/src/subpaths/history.ts +38 -0
- package/src/subpaths/lp.ts +5 -1
- package/src/subpaths/reclaim.ts +57 -0
- package/src/subpaths/simulate.ts +37 -0
- package/src/write/instructions.ts +17 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### 新功能
|
|
6
|
+
|
|
7
|
+
- 交易模拟,子路径 `@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 个账户,自动只看和钱包有关的账户并分批
|
|
8
|
+
- 交易历史,子路径 `@w3lib/solana-batch-call/history`:`getHistory` 把地址的交易整理成统一的 `Activity`(兑换、转账、授权、流动性、铸造、合约调用、失败,带方向、对手方、资产变化、手续费、协议),`cursor` 是最后一条签名,支持 `limit`、`before` / `after`(区块时间)、`types` 过滤。数据源可换:默认 `rpcHistory()`(`getSignaturesForAddress` + `getTransaction`,限并发),可选 `heliusHistory({ apiKey })`;传数组时按顺序尝试
|
|
9
|
+
- 账户回收,子路径 `@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`
|
|
10
|
+
- 批量领取,子路径 `@w3lib/solana-batch-call/claim`:`getClaimable` / `claimable` 只读预览 Orca Whirlpool、Raydium CLMM 仓位的未领取手续费和挖矿奖励(Orca 奖励 slot、Raydium 农场奖励,数量按程序的公式结算),带美元价值和按代币汇总;`buildClaimAll` 复用 `/liquidity` 的 `collectFees` 为每个仓位构建领取(手续费 + 全部奖励),`simulateClaim` 用 `/simulate` 模拟并汇总收到的代币,`claimAll` 构建 → 模拟 → 签名发送(`minUsd`、`dryRun`)
|
|
11
|
+
- `/lp`:`getLpPositions` 新增 `rewards: true`(仓位的 `rewards`、`rewardsUsd`,`now` 指定结算时刻),`/lp` 导出 `rewardGrowthGlobal`、`getRewardGrowthInside`;`/liquidity` 内部抽出 `orcaClosePosition` / `raydiumClosePosition`(关闭已清空的仓位)
|
|
12
|
+
|
|
13
|
+
### 修复
|
|
14
|
+
|
|
15
|
+
- publicnode 对 `getMultipleAccounts` 超过 10 个账户返回 “A maximum of 10 accounts is allowed”(之前只认 “too many accounts” 和 403 “Request blocked”),会被当成节点故障。现在识别这条报错,直接按它给出的上限拆分
|
|
16
|
+
|
|
3
17
|
## 0.5.0
|
|
4
18
|
|
|
5
19
|
### 新功能
|
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
|
|
@@ -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,6 +488,60 @@ 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
|
|
|
437
547
|
| | 指定 mints(默认,`accounts: 'ata'`) | 指定 mints + `accounts: 'all'` | 扫描(不传 mints,或 `scan: true`) |
|
|
@@ -694,6 +804,8 @@ pnpm test:live:lp # LP 仓位连主网核对
|
|
|
694
804
|
pnpm test:live:liquidity # 加减流动性在主网上模拟(simulateTransaction,不发送)
|
|
695
805
|
pnpm test:live:swap # 兑换:本地报价和主网模拟逐位核对(RECORD=1 时更新 test/fixtures/swap-mainnet.json.gz)
|
|
696
806
|
pnpm test:live:risk # 代币风险和买入 / 卖出模拟连主网
|
|
807
|
+
pnpm test:live:simulate # 交易模拟连主网(自己构建的交易、Jupiter 的 v0 交易)
|
|
808
|
+
pnpm test:live:history # 交易历史连主网(翻页、类型过滤、时间范围)
|
|
697
809
|
pnpm demo # 本地运行 demo(demo/,直接用 src 的源码)
|
|
698
810
|
pnpm build
|
|
699
811
|
```
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 批量计划的公共部分(/reclaim、/claim 用):一组“调用”(每个调用的指令原子执行)按交易大小打包、先模拟再发送。
|
|
3
|
+
* 打包规则同 batchCall(相邻的调用放进同一笔交易,不超过 1232 字节和 64 个账户)
|
|
4
|
+
*/
|
|
5
|
+
import type { SolanaClient } from './client.js';
|
|
6
|
+
import type { SimulationResult } from './simulate.js';
|
|
7
|
+
import type { BatchCallOptions } from './write/batch.js';
|
|
8
|
+
import type { WriteResult } from './write/send.js';
|
|
9
|
+
import type { SignerInput } from './write/signer.js';
|
|
10
|
+
import { type Instruction } from './write/transaction.js';
|
|
11
|
+
/** 一个调用:要签名的地址(代币账户 / 仓位的 owner)和原子执行的指令 */
|
|
12
|
+
export interface PlannedCall {
|
|
13
|
+
owner: string;
|
|
14
|
+
instructions: Instruction[];
|
|
15
|
+
}
|
|
16
|
+
/** 把调用按顺序打包成交易,返回每笔交易包含的调用下标。单个调用放不进一笔交易时自己成一笔(发送时会报“transaction too large”) */
|
|
17
|
+
export declare function packCalls(payer: string, calls: readonly PlannedCall[], batchSize?: number): number[][];
|
|
18
|
+
export interface PlanSimulationOptions {
|
|
19
|
+
/** 手续费支付者,默认第一个调用的 owner */
|
|
20
|
+
feePayer?: string;
|
|
21
|
+
/** 每笔交易最多几个调用,默认不限(只受交易大小限制) */
|
|
22
|
+
batchSize?: number;
|
|
23
|
+
/** 额外要看的地址(如收款地址) */
|
|
24
|
+
watch?: readonly string[];
|
|
25
|
+
signal?: AbortSignal;
|
|
26
|
+
/** 算美元价值(/simulate 的 prices) */
|
|
27
|
+
prices?: boolean;
|
|
28
|
+
fetch?: typeof fetch;
|
|
29
|
+
}
|
|
30
|
+
export interface PlanSimulation {
|
|
31
|
+
/** 所有交易都模拟通过 */
|
|
32
|
+
success: boolean;
|
|
33
|
+
/** 打包后的每笔交易:包含的调用下标和 /simulate 的结果 */
|
|
34
|
+
transactions: Array<{
|
|
35
|
+
calls: number[];
|
|
36
|
+
simulation: SimulationResult;
|
|
37
|
+
}>;
|
|
38
|
+
/** 模拟失败的调用(整笔失败时逐个重新模拟找出来):下标和原因 */
|
|
39
|
+
failed: Array<{
|
|
40
|
+
index: number;
|
|
41
|
+
error: string;
|
|
42
|
+
}>;
|
|
43
|
+
/** 各笔交易手续费之和(lamports,十进制字符串) */
|
|
44
|
+
fee: string;
|
|
45
|
+
/** 手续费支付者的 SOL 变化之和(lamports,带符号,不含手续费:/simulate 的 SOL 余额变化已经把手续费加回) */
|
|
46
|
+
payerNativeDelta: string;
|
|
47
|
+
}
|
|
48
|
+
/** 打包后逐笔用 /simulate 模拟(不签名、不发送);失败的交易逐个调用重新模拟,找出是哪些调用失败 */
|
|
49
|
+
export declare function simulatePlan(client: SolanaClient, calls: readonly PlannedCall[], options?: PlanSimulationOptions): Promise<PlanSimulation>;
|
|
50
|
+
/** 用 from 签名发送:调用的 owner 都必须是签名者本人;一个调用失败不影响后面的(continueOnError),feePayer 可以替 owner 付手续费 */
|
|
51
|
+
export declare function sendCalls(client: SolanaClient, from: SignerInput, calls: readonly PlannedCall[], options?: BatchCallOptions): Promise<WriteResult[]>;
|
|
52
|
+
//# sourceMappingURL=batchPlan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batchPlan.d.ts","sourceRoot":"","sources":["../src/batchPlan.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAExD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,EAAwE,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAE/H,4CAA4C;AAC5C,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,WAAW,EAAE,CAAA;CAC5B;AAED,gFAAgF;AAChF,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,WAAW,EAAE,EAAE,SAAS,SAA2B,GAAG,MAAM,EAAE,EAAE,CAsBxH;AAED,MAAM,WAAW,qBAAqB;IACpC,4BAA4B;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gCAAgC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,qBAAqB;IACrB,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACzB,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,gCAAgC;IAChC,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,gBAAgB;IAChB,OAAO,EAAE,OAAO,CAAA;IAChB,sCAAsC;IACtC,YAAY,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,UAAU,EAAE,gBAAgB,CAAA;KAAE,CAAC,CAAA;IACtE,oCAAoC;IACpC,MAAM,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC/C,iCAAiC;IACjC,GAAG,EAAE,MAAM,CAAA;IACX,wEAAwE;IACxE,gBAAgB,EAAE,MAAM,CAAA;CACzB;AAED,2DAA2D;AAC3D,wBAAsB,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,WAAW,EAAE,EAAE,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAAC,cAAc,CAAC,CA8CpJ;AAED,4FAA4F;AAC5F,wBAAsB,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,WAAW,EAAE,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAgB9J"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { isAddress } from './address.js';
|
|
2
|
+
import { MAX_COMPUTE_UNITS, setComputeUnitLimit, setComputeUnitPrice } from './write/instructions.js';
|
|
3
|
+
import { MAX_ACCOUNT_LOCKS, PACKET_DATA_SIZE, compileMessage, transactionSize } from './write/transaction.js';
|
|
4
|
+
/** 把调用按顺序打包成交易,返回每笔交易包含的调用下标。单个调用放不进一笔交易时自己成一笔(发送时会报“transaction too large”) */
|
|
5
|
+
export function packCalls(payer, calls, batchSize = Number.POSITIVE_INFINITY) {
|
|
6
|
+
const packs = [];
|
|
7
|
+
let current = [];
|
|
8
|
+
let instructions = [];
|
|
9
|
+
const fits = (list) => {
|
|
10
|
+
const probe = compileMessage(payer, [setComputeUnitLimit(MAX_COMPUTE_UNITS), setComputeUnitPrice(1n), ...list], '11111111111111111111111111111111');
|
|
11
|
+
return transactionSize(probe) <= PACKET_DATA_SIZE && probe.accountKeys.length <= MAX_ACCOUNT_LOCKS;
|
|
12
|
+
};
|
|
13
|
+
calls.forEach((call, i) => {
|
|
14
|
+
const next = [...instructions, ...call.instructions];
|
|
15
|
+
if (current.length && (current.length >= batchSize || !fits(next))) {
|
|
16
|
+
packs.push(current);
|
|
17
|
+
current = [];
|
|
18
|
+
instructions = [];
|
|
19
|
+
}
|
|
20
|
+
current.push(i);
|
|
21
|
+
instructions.push(...call.instructions);
|
|
22
|
+
});
|
|
23
|
+
if (current.length) {
|
|
24
|
+
packs.push(current);
|
|
25
|
+
}
|
|
26
|
+
return packs;
|
|
27
|
+
}
|
|
28
|
+
/** 打包后逐笔用 /simulate 模拟(不签名、不发送);失败的交易逐个调用重新模拟,找出是哪些调用失败 */
|
|
29
|
+
export async function simulatePlan(client, calls, options = {}) {
|
|
30
|
+
const first = calls[0];
|
|
31
|
+
if (!first) {
|
|
32
|
+
return { success: true, transactions: [], failed: [], fee: '0', payerNativeDelta: '0' };
|
|
33
|
+
}
|
|
34
|
+
const payer = options.feePayer ?? first.owner;
|
|
35
|
+
if (!isAddress(payer)) {
|
|
36
|
+
throw new Error(`Invalid fee payer: ${payer}`);
|
|
37
|
+
}
|
|
38
|
+
const { simulate } = await import('./simulate.js');
|
|
39
|
+
const run = (list) => simulate(client, { feePayer: payer, instructions: list.flatMap((c) => c.instructions) }, { signers: [...new Set(list.map((c) => c.owner))], watch: options.watch, signal: options.signal, prices: options.prices, fetch: options.fetch });
|
|
40
|
+
const packs = packCalls(payer, calls, options.batchSize);
|
|
41
|
+
const transactions = [];
|
|
42
|
+
const failed = [];
|
|
43
|
+
for (const pack of packs) {
|
|
44
|
+
const simulation = await run(pack.map((i) => calls[i]));
|
|
45
|
+
transactions.push({ calls: pack, simulation });
|
|
46
|
+
if (!simulation.success) {
|
|
47
|
+
if (pack.length === 1) {
|
|
48
|
+
failed.push({ index: pack[0], error: simulation.error ?? 'simulation failed' });
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
for (const i of pack) {
|
|
52
|
+
const single = await run([calls[i]]);
|
|
53
|
+
if (!single.success) {
|
|
54
|
+
failed.push({ index: i, error: single.error ?? 'simulation failed' });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
let fee = 0n;
|
|
60
|
+
let delta = 0n;
|
|
61
|
+
for (const { simulation } of transactions) {
|
|
62
|
+
fee += BigInt(simulation.fee ?? '0');
|
|
63
|
+
for (const change of simulation.balanceChanges) {
|
|
64
|
+
if (change.address === payer && change.asset === 'native') {
|
|
65
|
+
delta += BigInt(change.delta);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { success: transactions.every((t) => t.simulation.success), transactions, failed, fee: fee.toString(), payerNativeDelta: delta.toString() };
|
|
70
|
+
}
|
|
71
|
+
/** 用 from 签名发送:调用的 owner 都必须是签名者本人;一个调用失败不影响后面的(continueOnError),feePayer 可以替 owner 付手续费 */
|
|
72
|
+
export async function sendCalls(client, from, calls, options = {}) {
|
|
73
|
+
if (!calls.length) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
// 发送的代码按需加载:只查询、模拟时打包不会带上
|
|
77
|
+
const [{ batchCall }, { resolveSigner }] = await Promise.all([import('./write/batch.js'), import('./write/signer.js')]);
|
|
78
|
+
const { address } = await resolveSigner(from);
|
|
79
|
+
const other = calls.find((c) => c.owner !== address);
|
|
80
|
+
if (other) {
|
|
81
|
+
throw new Error(`The account belongs to ${other.owner}, not the signer ${address}`);
|
|
82
|
+
}
|
|
83
|
+
return batchCall(client, calls.map((c) => ({ from, instructions: c.instructions })), { continueOnError: true, ...options });
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=batchPlan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batchPlan.js","sourceRoot":"","sources":["../src/batchPlan.ts"],"names":[],"mappings":"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"}
|
package/dist/claim.d.ts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 批量领取(子路径 `@w3lib/solana-batch-call/claim`):Orca Whirlpool、Raydium CLMM 仓位的未领取手续费和挖矿奖励(Orca 的奖励 slot、Raydium 的农场奖励)。
|
|
3
|
+
* 先只读预览(每个仓位能领多少、美元价值),再把全部(或选中的)仓位的领取指令打包成交易,发送前用 /simulate 模拟。
|
|
4
|
+
* 领取的指令就是 /liquidity 的 collectFees(手续费 + 全部奖励,SOL 自动解包、缺少的代币账户自动创建),这里只做批量和打包
|
|
5
|
+
*/
|
|
6
|
+
import type { RequestOptions, SolanaClient } from './client.js';
|
|
7
|
+
import type { SolanaLiquidityPlan } from './liquidity/types.js';
|
|
8
|
+
import { type SolanaLpProtocol, type SolanaLpToken } from './lp.js';
|
|
9
|
+
import { type SolanaPriceSource } from './price.js';
|
|
10
|
+
import { type PlanSimulation, type PlanSimulationOptions, type PlannedCall } from './batchPlan.js';
|
|
11
|
+
import type { BatchCallOptions } from './write/batch.js';
|
|
12
|
+
import { type WriteResult } from './write/send.js';
|
|
13
|
+
import type { SignerInput } from './write/signer.js';
|
|
14
|
+
export type ClaimProtocol = Extract<SolanaLpProtocol, 'orca-whirlpool' | 'raydium-clmm'>;
|
|
15
|
+
/** 一项能领取的东西:手续费或挖矿奖励 */
|
|
16
|
+
export interface ClaimableAmount {
|
|
17
|
+
mint: string;
|
|
18
|
+
symbol: string | null;
|
|
19
|
+
decimals: number | null;
|
|
20
|
+
/** 数量(最小单位);读不到两端的 tick 数据时为 null */
|
|
21
|
+
amount: string | null;
|
|
22
|
+
formatted: string | null;
|
|
23
|
+
/** 美元价值(查到价格时;没有价格为 null;数量为 0 时为 0) */
|
|
24
|
+
usd: number | null;
|
|
25
|
+
/** 只在按代币汇总(`Claimable.tokens`)里:true 表示有仓位的数量读不到,`amount` 只是已读到部分的合计(偏低),`usd` 为 null */
|
|
26
|
+
incomplete?: true;
|
|
27
|
+
}
|
|
28
|
+
export interface ClaimablePosition {
|
|
29
|
+
protocol: ClaimProtocol;
|
|
30
|
+
protocolName: string;
|
|
31
|
+
/** 仓位 NFT 的 mint 和仓位账户 */
|
|
32
|
+
positionMint: string;
|
|
33
|
+
position: string;
|
|
34
|
+
pool: string;
|
|
35
|
+
token0: SolanaLpToken;
|
|
36
|
+
token1: SolanaLpToken;
|
|
37
|
+
liquidity: string;
|
|
38
|
+
inRange: boolean;
|
|
39
|
+
/** token0 / token1 的未领取手续费 */
|
|
40
|
+
fees: [ClaimableAmount, ClaimableAmount];
|
|
41
|
+
/** 池子里已初始化的挖矿奖励(Orca 的奖励 slot / Raydium 的 reward_infos),数量按程序的方式结算到当前时间 */
|
|
42
|
+
rewards: Array<ClaimableAmount & {
|
|
43
|
+
index: number;
|
|
44
|
+
}>;
|
|
45
|
+
feesUsd: number | null;
|
|
46
|
+
rewardsUsd: number | null;
|
|
47
|
+
/** 手续费 + 奖励的美元价值:有数量的项缺价格或读不到时为 null */
|
|
48
|
+
totalUsd: number | null;
|
|
49
|
+
}
|
|
50
|
+
export interface Claimable {
|
|
51
|
+
owner: string;
|
|
52
|
+
/** 有手续费或奖励可领的仓位(没有流动性也没有可领的不在内),按美元价值从大到小 */
|
|
53
|
+
positions: ClaimablePosition[];
|
|
54
|
+
/** 按代币汇总:手续费 + 奖励,所有仓位合计 */
|
|
55
|
+
tokens: ClaimableAmount[];
|
|
56
|
+
/** true:有仓位的数量读不到(tick 数据读不到),`tokens` 的数量和各项合计都可能偏低 */
|
|
57
|
+
incomplete: boolean;
|
|
58
|
+
feesUsd: number | null;
|
|
59
|
+
rewardsUsd: number | null;
|
|
60
|
+
totalUsd: number | null;
|
|
61
|
+
warnings: string[];
|
|
62
|
+
}
|
|
63
|
+
export interface ClaimQuery extends RequestOptions {
|
|
64
|
+
/** 只看这些仓位 NFT 的 mint(读 ATA,免费公共节点也能用);不传时列出全部代币账户(getTokenAccountsByOwner,需要支持它的节点) */
|
|
65
|
+
mints?: readonly string[];
|
|
66
|
+
/** 协议,默认两个都查 */
|
|
67
|
+
protocols?: readonly ClaimProtocol[];
|
|
68
|
+
/** 价格:true(默认)用 DefiLlama;false 不查;也可以传自己的价格源(如 combinePrices([defillamaPrices(), onchainPrices()])) */
|
|
69
|
+
prices?: boolean | SolanaPriceSource;
|
|
70
|
+
/** 奖励结算到哪个时刻(unix 秒),默认当前时间 */
|
|
71
|
+
now?: number;
|
|
72
|
+
fetch?: typeof fetch;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 预览能领取的手续费和挖矿奖励(只读取,不构建交易):每个仓位的两种手续费、每个奖励的数量和美元价值,以及按代币汇总。
|
|
76
|
+
* 奖励照搬程序的结算:池子的奖励增长按时间摊给当前流动性,再按仓位区间内的增长计算
|
|
77
|
+
*
|
|
78
|
+
* ```ts
|
|
79
|
+
* const c = await claimable(client, owner)
|
|
80
|
+
* c.totalUsd // 全部能领的美元价值
|
|
81
|
+
* c.positions[0].rewards // [{ index: 0, symbol: 'RAY', formatted: '0.042698', usd: 0.083 }]
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export declare function claimable(client: SolanaClient, owner: string, query?: ClaimQuery): Promise<Claimable>;
|
|
85
|
+
/** 要领取的仓位:claimable 的结果,或只给协议 + 仓位 NFT 的 mint(getLpPositions 的结果也可以) */
|
|
86
|
+
export type ClaimTarget = Pick<ClaimablePosition, 'protocol' | 'positionMint'> & {
|
|
87
|
+
pool?: string;
|
|
88
|
+
};
|
|
89
|
+
export interface BuildClaimOptions extends RequestOptions {
|
|
90
|
+
/** wSOL(池子的一边或奖励)原来没有账户时用完关闭、换回 SOL,默认 true(同 /liquidity) */
|
|
91
|
+
native?: boolean;
|
|
92
|
+
/** 同时构建几个仓位(每个要读几次账户),默认 4 */
|
|
93
|
+
concurrency?: number;
|
|
94
|
+
}
|
|
95
|
+
export interface ClaimCall extends PlannedCall {
|
|
96
|
+
protocol: ClaimProtocol;
|
|
97
|
+
positionMint: string;
|
|
98
|
+
/** /liquidity 的领取计划(预计的手续费、奖励的代币、步骤) */
|
|
99
|
+
plan: SolanaLiquidityPlan;
|
|
100
|
+
}
|
|
101
|
+
export interface ClaimPlan {
|
|
102
|
+
owner: string;
|
|
103
|
+
/** 每个仓位一个调用(创建缺少的代币账户 + 领取手续费 + 领取每个奖励,原子执行) */
|
|
104
|
+
calls: ClaimCall[];
|
|
105
|
+
/** 构建失败的仓位:仓位不在 owner 名下、不支持的代币(带转账手续费的 Token-2022 池子)等 */
|
|
106
|
+
failed: Array<{
|
|
107
|
+
position: ClaimTarget;
|
|
108
|
+
error: string;
|
|
109
|
+
}>;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* 构建批量领取(只读取、不签名):每个仓位一个调用,指令来自 /liquidity 的 buildCollectFees,构建时重新读取仓位。
|
|
113
|
+
* 打包成交易由 simulateClaim / claimAll(batchCall)完成
|
|
114
|
+
*
|
|
115
|
+
* ```ts
|
|
116
|
+
* const c = await claimable(client, owner)
|
|
117
|
+
* const plan = await buildClaimAll(client, owner, c.positions)
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
export declare function buildClaimAll(client: SolanaClient, owner: string, positions: readonly ClaimTarget[], options?: BuildClaimOptions): Promise<ClaimPlan>;
|
|
121
|
+
export interface ClaimSimulation extends PlanSimulation {
|
|
122
|
+
/** 模拟里收到的代币合计(手续费支付者 / owner 的余额变化,按资产汇总;SOL 为 'native') */
|
|
123
|
+
received: Array<{
|
|
124
|
+
asset: string;
|
|
125
|
+
symbol: string;
|
|
126
|
+
decimals: number;
|
|
127
|
+
delta: string;
|
|
128
|
+
formatted: string;
|
|
129
|
+
usd?: number | null;
|
|
130
|
+
}>;
|
|
131
|
+
}
|
|
132
|
+
/** 用 /simulate 模拟构建好的批量领取(不签名、不发送):逐笔交易的结果、哪些仓位会失败、收到的代币合计 */
|
|
133
|
+
export declare function simulateClaim(client: SolanaClient, plan: ClaimPlan, options?: Omit<PlanSimulationOptions, 'watch'>): Promise<ClaimSimulation>;
|
|
134
|
+
export interface ClaimOptions extends BatchCallOptions, BuildClaimOptions {
|
|
135
|
+
/** 发送前先用 /simulate 模拟,模拟失败的仓位不发送,默认 true */
|
|
136
|
+
simulate?: boolean;
|
|
137
|
+
/** 只领取预计价值不低于它的仓位(美元);没有价格的仓位照常领取。默认 0 */
|
|
138
|
+
minUsd?: number;
|
|
139
|
+
}
|
|
140
|
+
export interface ClaimCallResult extends WriteResult {
|
|
141
|
+
protocol: ClaimProtocol;
|
|
142
|
+
positionMint: string;
|
|
143
|
+
}
|
|
144
|
+
export interface ClaimResult {
|
|
145
|
+
plan: ClaimPlan;
|
|
146
|
+
/** 发送前的模拟(simulate: false 时为 null) */
|
|
147
|
+
simulation: ClaimSimulation | null;
|
|
148
|
+
/** 与 plan.calls 一一对应;dryRun 时 status 为 'simulated' / 'failed',没有签名 */
|
|
149
|
+
results: ClaimCallResult[];
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* 批量领取:构建 → 模拟 → 用 from 签名发送(钱包同 /write 的 batchCall,多个仓位打包进同一笔交易,feePayer 可以替 owner 付手续费)。
|
|
153
|
+
* positions 不传时先预览、领取全部有东西可领的仓位;一个仓位失败不影响其他的。dryRun: true 只构建和模拟(不签名,from 只用来取地址)
|
|
154
|
+
*
|
|
155
|
+
* ```ts
|
|
156
|
+
* await claimAll(client, key) // 全部
|
|
157
|
+
* await claimAll(client, key, c.positions.slice(0, 5), { dryRun: true })
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
export declare function claimAll(client: SolanaClient, from: SignerInput, positions?: readonly (ClaimTarget & Partial<Pick<ClaimablePosition, 'totalUsd'>>)[], options?: ClaimOptions): Promise<ClaimResult>;
|
|
161
|
+
//# sourceMappingURL=claim.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim.d.ts","sourceRoot":"","sources":["../src/claim.ts"],"names":[],"mappings":"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"}
|