@w3lib/solana-batch-call 0.2.2 → 0.4.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 +26 -0
- package/README.md +205 -2
- package/dist/address.d.ts +2 -0
- package/dist/address.d.ts.map +1 -1
- package/dist/address.js +8 -0
- package/dist/address.js.map +1 -1
- package/dist/client.d.ts +4 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +8 -0
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/layout.d.ts +5 -0
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +16 -0
- package/dist/layout.js.map +1 -1
- package/dist/liquidity/amm.d.ts +7 -0
- package/dist/liquidity/amm.d.ts.map +1 -0
- package/dist/liquidity/amm.js +307 -0
- package/dist/liquidity/amm.js.map +1 -0
- package/dist/liquidity/clmm.d.ts +18 -0
- package/dist/liquidity/clmm.d.ts.map +1 -0
- package/dist/liquidity/clmm.js +85 -0
- package/dist/liquidity/clmm.js.map +1 -0
- package/dist/liquidity/common.d.ts +94 -0
- package/dist/liquidity/common.d.ts.map +1 -0
- package/dist/liquidity/common.js +228 -0
- package/dist/liquidity/common.js.map +1 -0
- package/dist/liquidity/index.d.ts +34 -0
- package/dist/liquidity/index.d.ts.map +1 -0
- package/dist/liquidity/index.js +157 -0
- package/dist/liquidity/index.js.map +1 -0
- package/dist/liquidity/orca.d.ts +6 -0
- package/dist/liquidity/orca.d.ts.map +1 -0
- package/dist/liquidity/orca.js +303 -0
- package/dist/liquidity/orca.js.map +1 -0
- package/dist/liquidity/raydiumClmm.d.ts +6 -0
- package/dist/liquidity/raydiumClmm.d.ts.map +1 -0
- package/dist/liquidity/raydiumClmm.js +307 -0
- package/dist/liquidity/raydiumClmm.js.map +1 -0
- package/dist/liquidity/types.d.ts +150 -0
- package/dist/liquidity/types.d.ts.map +1 -0
- package/dist/liquidity/types.js +2 -0
- package/dist/liquidity/types.js.map +1 -0
- package/dist/lp.d.ts +168 -0
- package/dist/lp.d.ts.map +1 -0
- package/dist/lp.js +632 -0
- package/dist/lp.js.map +1 -0
- package/dist/lpMath.d.ts +82 -0
- package/dist/lpMath.d.ts.map +1 -0
- package/dist/lpMath.js +365 -0
- package/dist/lpMath.js.map +1 -0
- package/dist/shortcuts.d.ts +8 -0
- package/dist/shortcuts.d.ts.map +1 -1
- package/dist/shortcuts.js +1 -1
- package/dist/shortcuts.js.map +1 -1
- package/dist/subpaths/liquidity.d.ts +23 -0
- package/dist/subpaths/liquidity.d.ts.map +1 -0
- package/dist/subpaths/liquidity.js +19 -0
- package/dist/subpaths/liquidity.js.map +1 -0
- package/dist/subpaths/lp.d.ts +31 -0
- package/dist/subpaths/lp.d.ts.map +1 -0
- package/dist/subpaths/lp.js +33 -0
- package/dist/subpaths/lp.js.map +1 -0
- package/dist/subpaths/write.d.ts +22 -0
- package/dist/subpaths/write.d.ts.map +1 -0
- package/dist/subpaths/write.js +19 -0
- package/dist/subpaths/write.js.map +1 -0
- package/dist/write/batch.d.ts +41 -0
- package/dist/write/batch.d.ts.map +1 -0
- package/dist/write/batch.js +46 -0
- package/dist/write/batch.js.map +1 -0
- package/dist/write/collect.d.ts +50 -0
- package/dist/write/collect.d.ts.map +1 -0
- package/dist/write/collect.js +220 -0
- package/dist/write/collect.js.map +1 -0
- package/dist/write/instructions.d.ts +16 -0
- package/dist/write/instructions.d.ts.map +1 -0
- package/dist/write/instructions.js +89 -0
- package/dist/write/instructions.js.map +1 -0
- package/dist/write/send.d.ts +115 -0
- package/dist/write/send.d.ts.map +1 -0
- package/dist/write/send.js +720 -0
- package/dist/write/send.js.map +1 -0
- package/dist/write/signer.d.ts +94 -0
- package/dist/write/signer.d.ts.map +1 -0
- package/dist/write/signer.js +197 -0
- package/dist/write/signer.js.map +1 -0
- package/dist/write/transaction.d.ts +75 -0
- package/dist/write/transaction.d.ts.map +1 -0
- package/dist/write/transaction.js +133 -0
- package/dist/write/transaction.js.map +1 -0
- package/dist/write/transfer.d.ts +72 -0
- package/dist/write/transfer.d.ts.map +1 -0
- package/dist/write/transfer.js +127 -0
- package/dist/write/transfer.js.map +1 -0
- package/package.json +23 -5
- package/src/address.ts +9 -0
- package/src/client.ts +10 -0
- package/src/index.ts +2 -1
- package/src/layout.ts +17 -0
- package/src/liquidity/amm.ts +353 -0
- package/src/liquidity/clmm.ts +108 -0
- package/src/liquidity/common.ts +285 -0
- package/src/liquidity/index.ts +177 -0
- package/src/liquidity/orca.ts +344 -0
- package/src/liquidity/raydiumClmm.ts +348 -0
- package/src/liquidity/types.ts +158 -0
- package/src/lp.ts +850 -0
- package/src/lpMath.ts +420 -0
- package/src/shortcuts.ts +1 -1
- package/src/subpaths/liquidity.ts +45 -0
- package/src/subpaths/lp.ts +71 -0
- package/src/subpaths/write.ts +29 -0
- package/src/write/batch.ts +78 -0
- package/src/write/collect.ts +286 -0
- package/src/write/instructions.ts +98 -0
- package/src/write/send.ts +876 -0
- package/src/write/signer.ts +314 -0
- package/src/write/transaction.ts +181 -0
- package/src/write/transfer.ts +189 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { SolanaClient } from '../client.js';
|
|
2
|
+
import { type SendOptions, type WriteResult } from './send.js';
|
|
3
|
+
import { type ResolvedSigner, type SignerInput } from './signer.js';
|
|
4
|
+
import type { Instruction } from './transaction.js';
|
|
5
|
+
/** 一个收款项:amount(最小单位)和 formatted(按精度换算后,如 '1.5')二选一 */
|
|
6
|
+
export interface TransferItem {
|
|
7
|
+
/** 收款钱包地址(代币转到它的关联代币账户 ATA,没有时自动创建) */
|
|
8
|
+
to: string;
|
|
9
|
+
/** 数量,最小单位(lamports / 代币最小单位),bigint 或十进制字符串 */
|
|
10
|
+
amount?: bigint | number | string;
|
|
11
|
+
/** 按精度换算后的数量,如 '1.5';小数位超过精度时报错(不四舍五入) */
|
|
12
|
+
formatted?: string;
|
|
13
|
+
/** 这一项转的代币 mint,默认 options.token */
|
|
14
|
+
token?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* - 'pack'(默认):同一笔交易里打包多个收款人(Solana 原生支持一笔交易多条转账指令,不需要 Disperse 这样的合约),
|
|
18
|
+
* 按交易大小上限自动分笔;模拟失败的收款项(如收款账户被冻结)单独标记失败,其余照常发送
|
|
19
|
+
* - 'tx':每个收款人一笔交易
|
|
20
|
+
*/
|
|
21
|
+
export type TransferMode = 'pack' | 'tx';
|
|
22
|
+
export interface BatchTransferOptions extends SendOptions {
|
|
23
|
+
/** 默认转的代币 mint,默认原生 SOL(NATIVE_MINT);每项也可以单独指定 token */
|
|
24
|
+
token?: string;
|
|
25
|
+
mode?: TransferMode;
|
|
26
|
+
/** 'pack' 时每笔交易最多几个收款人,默认不限(按交易大小,SOL 约 20 个一笔,代币约 10 个) */
|
|
27
|
+
batchSize?: number;
|
|
28
|
+
/** 发送前检查余额够不够(按代币汇总),不够时抛错、什么都不发。默认 true(SOL 不含手续费和新建账户的租金) */
|
|
29
|
+
checkBalance?: boolean;
|
|
30
|
+
/** 收款人没有该代币的 ATA 时自动创建(租金约 0.00204 SOL,由手续费支付者付)。默认 true;false 时这些收款项失败 */
|
|
31
|
+
createAccounts?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/** 单个收款项的结果 */
|
|
34
|
+
export interface TransferResult extends WriteResult {
|
|
35
|
+
/** 收款钱包 */
|
|
36
|
+
to: string;
|
|
37
|
+
token: string;
|
|
38
|
+
native: boolean;
|
|
39
|
+
/** 数量(最小单位) */
|
|
40
|
+
amount: string;
|
|
41
|
+
/** 按精度换算后的数量 */
|
|
42
|
+
formatted: string;
|
|
43
|
+
/** 是否为收款人新建了代币账户(这一项额外付了租金) */
|
|
44
|
+
createdAccount: boolean;
|
|
45
|
+
}
|
|
46
|
+
/** 解析后的收款项 */
|
|
47
|
+
interface Planned {
|
|
48
|
+
index: number;
|
|
49
|
+
to: string;
|
|
50
|
+
token: string;
|
|
51
|
+
native: boolean;
|
|
52
|
+
amount: bigint;
|
|
53
|
+
decimals: number;
|
|
54
|
+
tokenProgram: string | null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 一对多批量转账(SOL / SPL Token / Token-2022,可混合)。结果与 recipients 一一对应。
|
|
58
|
+
*
|
|
59
|
+
* ```ts
|
|
60
|
+
* await batchTransfer(client, privateKey, [
|
|
61
|
+
* { to: a, formatted: '0.5' }, // 默认代币(options.token,默认 SOL)
|
|
62
|
+
* { to: b, token: USDC, formatted: '20' }, // 代币:收款人没有 ATA 时自动创建
|
|
63
|
+
* ])
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare function batchTransfer(client: SolanaClient, from: SignerInput, recipients: readonly TransferItem[], options?: BatchTransferOptions): Promise<TransferResult[]>;
|
|
67
|
+
/** 转账指令:SOL 直接转;代币按需先创建收款人的 ATA,再 TransferChecked */
|
|
68
|
+
export declare function transferInstructions(signer: ResolvedSigner, payer: ResolvedSigner, p: Pick<Planned, 'to' | 'token' | 'native' | 'amount' | 'tokenProgram'> & {
|
|
69
|
+
decimals: number | null;
|
|
70
|
+
}, ata: string | null, create: boolean): Instruction[];
|
|
71
|
+
export {};
|
|
72
|
+
//# sourceMappingURL=transfer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transfer.d.ts","sourceRoot":"","sources":["../../src/write/transfer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAKhD,OAAO,EAA0C,KAAK,WAAW,EAA6B,KAAK,WAAW,EAAE,MAAM,WAAW,CAAA;AACjI,OAAO,EAAiB,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AAClF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAEnD,uDAAuD;AACvD,MAAM,WAAW,YAAY;IAC3B,uCAAuC;IACvC,EAAE,EAAE,MAAM,CAAA;IACV,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;IACjC,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,IAAI,CAAA;AAExC,MAAM,WAAW,oBAAqB,SAAQ,WAAW;IACvD,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,YAAY,CAAA;IACnB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,2EAA2E;IAC3E,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAED,eAAe;AACf,MAAM,WAAW,cAAe,SAAQ,WAAW;IACjD,WAAW;IACX,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,OAAO,CAAA;IACf,eAAe;IACf,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,+BAA+B;IAC/B,cAAc,EAAE,OAAO,CAAA;CACxB;AAED,cAAc;AACd,UAAU,OAAO;IACf,KAAK,EAAE,MAAM,CAAA;IACb,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,OAAO,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,YAAY,EAAE,EAAE,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAwC/K;AAED,qDAAqD;AACrD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,cAAc,CAAC,GAAG;IAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,OAAO,GAAG,WAAW,EAAE,CAW7O"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { getAssociatedTokenAddress, isAddress } from '../address.js';
|
|
2
|
+
import { NATIVE_MINT, SOL_DECIMALS } from '../constants.js';
|
|
3
|
+
import { formatUnits, parseUnits } from '../layout.js';
|
|
4
|
+
import { createAssociatedTokenAccountIdempotent, systemTransfer, transferChecked } from './instructions.js';
|
|
5
|
+
import { emptyOutcome, sendUnits, toWriteResult } from './send.js';
|
|
6
|
+
import { resolveSigner } from './signer.js';
|
|
7
|
+
/**
|
|
8
|
+
* 一对多批量转账(SOL / SPL Token / Token-2022,可混合)。结果与 recipients 一一对应。
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* await batchTransfer(client, privateKey, [
|
|
12
|
+
* { to: a, formatted: '0.5' }, // 默认代币(options.token,默认 SOL)
|
|
13
|
+
* { to: b, token: USDC, formatted: '20' }, // 代币:收款人没有 ATA 时自动创建
|
|
14
|
+
* ])
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export async function batchTransfer(client, from, recipients, options = {}) {
|
|
18
|
+
const cache = new Map();
|
|
19
|
+
const signer = await resolveSigner(from, cache);
|
|
20
|
+
const payer = options.feePayer === undefined ? signer : await resolveSigner(options.feePayer, cache);
|
|
21
|
+
const planned = await plan(client, signer.address, recipients, options);
|
|
22
|
+
// 收款人的 ATA 是否存在:一次 getMultipleAccounts
|
|
23
|
+
const atas = planned.map((p) => (p.native ? null : getAssociatedTokenAddress(p.to, p.token, p.tokenProgram)));
|
|
24
|
+
const needed = [...new Set(atas.filter((a) => a !== null))];
|
|
25
|
+
const existing = new Set();
|
|
26
|
+
if (needed.length) {
|
|
27
|
+
const accounts = await client.accounts(needed, { signal: options.signal });
|
|
28
|
+
needed.forEach((a, i) => accounts[i] && existing.add(a));
|
|
29
|
+
}
|
|
30
|
+
const results = planned.map((p) => toTransferResult(p, toWriteResult(p.index, signer.address, emptyOutcome('skipped')), false));
|
|
31
|
+
const units = [];
|
|
32
|
+
/** 已经安排创建的 ATA:同一个收款人同一种代币有多项时只在第一项创建(后面的项在同一组里按顺序发送,模拟失败时会等前面的确认) */
|
|
33
|
+
const creating = new Set();
|
|
34
|
+
planned.forEach((p, i) => {
|
|
35
|
+
const ata = atas[i] ?? null;
|
|
36
|
+
const create = ata !== null && !existing.has(ata) && !creating.has(ata);
|
|
37
|
+
if (create)
|
|
38
|
+
creating.add(ata);
|
|
39
|
+
if (create && options.createAccounts === false) {
|
|
40
|
+
results[i] = toTransferResult(p, toWriteResult(p.index, signer.address, emptyOutcome('failed', 'recipient has no token account (createAccounts is false)')), false);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
units.push({
|
|
44
|
+
instructions: transferInstructions(signer, payer, p, ata, create),
|
|
45
|
+
payer,
|
|
46
|
+
signers: payer.address === signer.address ? [] : [signer],
|
|
47
|
+
continueOnError: true,
|
|
48
|
+
onUpdate: (outcome) => {
|
|
49
|
+
results[i] = toTransferResult(p, toWriteResult(p.index, signer.address, outcome), create && outcome.status === 'confirmed');
|
|
50
|
+
options.onUpdate?.(results[i]);
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
await sendUnits(client, units, options, { batchSize: (options.mode ?? 'pack') === 'tx' ? 1 : options.batchSize });
|
|
55
|
+
return results;
|
|
56
|
+
}
|
|
57
|
+
/** 转账指令:SOL 直接转;代币按需先创建收款人的 ATA,再 TransferChecked */
|
|
58
|
+
export function transferInstructions(signer, payer, p, ata, create) {
|
|
59
|
+
if (p.native) {
|
|
60
|
+
return [systemTransfer(signer.address, p.to, p.amount)];
|
|
61
|
+
}
|
|
62
|
+
const program = p.tokenProgram;
|
|
63
|
+
const source = getAssociatedTokenAddress(signer.address, p.token, program);
|
|
64
|
+
const destination = ata ?? getAssociatedTokenAddress(p.to, p.token, program);
|
|
65
|
+
return [
|
|
66
|
+
...(create ? [createAssociatedTokenAccountIdempotent(payer.address, destination, p.to, p.token, program)] : []),
|
|
67
|
+
transferChecked(source, p.token, destination, signer.address, p.amount, p.decimals, program),
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
/** 解析数量(formatted 按精度换算)、代币程序,并检查余额 */
|
|
71
|
+
async function plan(client, owner, recipients, options) {
|
|
72
|
+
const defaultToken = options.token ?? NATIVE_MINT;
|
|
73
|
+
const tokens = [...new Set(recipients.map((r) => r.token ?? defaultToken))];
|
|
74
|
+
const balances = tokens.length ? await client.balances(owner, tokens, { signal: options.signal }) : [];
|
|
75
|
+
const info = new Map(balances.map((b, i) => [tokens[i], b]));
|
|
76
|
+
const planned = recipients.map((item, index) => {
|
|
77
|
+
const token = item.token ?? defaultToken;
|
|
78
|
+
const balance = info.get(token);
|
|
79
|
+
if (!isAddress(item.to)) {
|
|
80
|
+
throw new Error(`recipients[${index}]: invalid address ${item.to}`);
|
|
81
|
+
}
|
|
82
|
+
if ((item.amount === undefined) === (item.formatted === undefined)) {
|
|
83
|
+
throw new Error(`recipients[${index}]: pass exactly one of amount / formatted`);
|
|
84
|
+
}
|
|
85
|
+
const native = client.isNativeMint(token);
|
|
86
|
+
if (!native && (!balance?.success || !balance.tokenProgram)) {
|
|
87
|
+
throw new Error(`recipients[${index}]: cannot read token ${token} (${balance?.error ?? 'unknown error'})`);
|
|
88
|
+
}
|
|
89
|
+
// 原生 SOL 的精度固定;代币读不到时上面已经报错
|
|
90
|
+
const decimals = native ? SOL_DECIMALS : balance.decimals;
|
|
91
|
+
let amount;
|
|
92
|
+
if (item.formatted !== undefined) {
|
|
93
|
+
amount = parseUnits(item.formatted, decimals);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
amount = BigInt(item.amount);
|
|
97
|
+
}
|
|
98
|
+
if (amount < 0n || amount >= 2n ** 64n) {
|
|
99
|
+
throw new Error(`recipients[${index}]: amount out of range`);
|
|
100
|
+
}
|
|
101
|
+
return { index, to: item.to, token, native, amount, decimals, tokenProgram: native ? null : (balance?.tokenProgram ?? null) };
|
|
102
|
+
});
|
|
103
|
+
if (options.checkBalance ?? true) {
|
|
104
|
+
const totals = new Map();
|
|
105
|
+
planned.forEach((p) => totals.set(p.token, (totals.get(p.token) ?? 0n) + p.amount));
|
|
106
|
+
for (const [token, total] of totals) {
|
|
107
|
+
// 读到这里时余额都读成功了(代币读不到时上面已经报错,原生 SOL 总能读到)
|
|
108
|
+
const balance = info.get(token);
|
|
109
|
+
if (BigInt(balance.balance) < total) {
|
|
110
|
+
throw new Error(`Insufficient balance of ${token}: need ${total}, have ${balance.balance}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return planned;
|
|
115
|
+
}
|
|
116
|
+
function toTransferResult(p, result, createdAccount) {
|
|
117
|
+
return {
|
|
118
|
+
...result,
|
|
119
|
+
to: p.to,
|
|
120
|
+
token: p.token,
|
|
121
|
+
native: p.native,
|
|
122
|
+
amount: p.amount.toString(),
|
|
123
|
+
formatted: formatUnits(p.amount, p.decimals),
|
|
124
|
+
createdAccount,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=transfer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transfer.js","sourceRoot":"","sources":["../../src/write/transfer.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@w3lib/solana-batch-call",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Batched Solana reads: SOL / SPL / Token-2022 balances, token metadata, NFTs. Auto-batching JSON-RPC, getMultipleAccounts chunking, RPC failover. No Solana SDK dependency.",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Batched Solana reads: SOL / SPL / Token-2022 balances, token metadata, NFTs; batch transfer, collect and program calls (packed transactions, fee payer). Auto-batching JSON-RPC, getMultipleAccounts chunking, RPC failover. No Solana SDK dependency.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"solana",
|
|
7
7
|
"spl-token",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"metaplex",
|
|
10
10
|
"batch",
|
|
11
11
|
"getMultipleAccounts",
|
|
12
|
-
"json-rpc"
|
|
12
|
+
"json-rpc",
|
|
13
|
+
"batch-transfer"
|
|
13
14
|
],
|
|
14
15
|
"license": "MIT",
|
|
15
16
|
"author": "L",
|
|
@@ -20,6 +21,18 @@
|
|
|
20
21
|
".": {
|
|
21
22
|
"types": "./dist/index.d.ts",
|
|
22
23
|
"import": "./dist/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./write": {
|
|
26
|
+
"types": "./dist/subpaths/write.d.ts",
|
|
27
|
+
"import": "./dist/subpaths/write.js"
|
|
28
|
+
},
|
|
29
|
+
"./lp": {
|
|
30
|
+
"types": "./dist/subpaths/lp.d.ts",
|
|
31
|
+
"import": "./dist/subpaths/lp.js"
|
|
32
|
+
},
|
|
33
|
+
"./liquidity": {
|
|
34
|
+
"types": "./dist/subpaths/liquidity.d.ts",
|
|
35
|
+
"import": "./dist/subpaths/liquidity.js"
|
|
23
36
|
}
|
|
24
37
|
},
|
|
25
38
|
"files": [
|
|
@@ -38,7 +51,11 @@
|
|
|
38
51
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
39
52
|
"test": "vitest run",
|
|
40
53
|
"test:live": "LIVE=1 vitest run test/live.test.ts",
|
|
41
|
-
"
|
|
54
|
+
"test:live:lp": "LIVE=1 vitest run test/lp-live.test.ts",
|
|
55
|
+
"test:live:liquidity": "LIVE=1 vitest run test/liquidity-live.test.ts",
|
|
56
|
+
"prepublishOnly": "pnpm typecheck && pnpm test && pnpm build",
|
|
57
|
+
"demo": "vite demo",
|
|
58
|
+
"demo:build": "vite build demo"
|
|
42
59
|
},
|
|
43
60
|
"repository": {
|
|
44
61
|
"type": "git",
|
|
@@ -57,6 +74,7 @@
|
|
|
57
74
|
"@scure/base": "^2.4.0"
|
|
58
75
|
},
|
|
59
76
|
"devDependencies": {
|
|
60
|
-
"@noble/curves": "^2.4.0"
|
|
77
|
+
"@noble/curves": "^2.4.0",
|
|
78
|
+
"vite": "^8.3.4"
|
|
61
79
|
}
|
|
62
80
|
}
|
package/src/address.ts
CHANGED
|
@@ -130,6 +130,15 @@ export function findProgramAddress(seeds: Uint8Array[], programId: string): stri
|
|
|
130
130
|
throw new Error('Unable to find a viable program address bump seed')
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
/** createProgramAddress:用给定的种子(含 bump / nonce)算 PDA;落在曲线上时报错(与 web3.js PublicKey.createProgramAddressSync 相同) */
|
|
134
|
+
export function createProgramAddress(seeds: Uint8Array[], programId: string): string {
|
|
135
|
+
const hash = sha256(concat([...seeds, decodeAddress(programId), PDA_MARKER]))
|
|
136
|
+
if (isOnCurve(hash)) {
|
|
137
|
+
throw new Error('Invalid seeds: address is on the ed25519 curve')
|
|
138
|
+
}
|
|
139
|
+
return encodeAddress(hash)
|
|
140
|
+
}
|
|
141
|
+
|
|
133
142
|
/** 关联代币账户(ATA)地址 */
|
|
134
143
|
export function getAssociatedTokenAddress(owner: string, mint: string, tokenProgram: string): string {
|
|
135
144
|
return findProgramAddress([decodeAddress(owner), decodeAddress(tokenProgram), decodeAddress(mint)], ASSOCIATED_TOKEN_PROGRAM_ID)
|
package/src/client.ts
CHANGED
|
@@ -505,6 +505,16 @@ export class SolanaClient {
|
|
|
505
505
|
return this.#cluster ?? clusterOfGenesis(await this.#genesis())
|
|
506
506
|
}
|
|
507
507
|
|
|
508
|
+
/** 默认的 commitment(ClientConfig.commitment,默认 'confirmed') */
|
|
509
|
+
get commitment(): Commitment {
|
|
510
|
+
return this.#commitment
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/** 是否按原生 SOL 处理这个 mint(ClientConfig.nativeMints) */
|
|
514
|
+
isNativeMint(mint: string): boolean {
|
|
515
|
+
return this.#nativeMints.has(mint)
|
|
516
|
+
}
|
|
517
|
+
|
|
508
518
|
/** 直接发 JSON-RPC 调用(同样参与自动合并与故障切换) */
|
|
509
519
|
request<T = unknown>(method: string, params?: readonly unknown[], options: RequestOptions = {}): Promise<T> {
|
|
510
520
|
return abortable(options.signal, () => this.transport.request<T>(method, params))
|
package/src/index.ts
CHANGED
|
@@ -64,7 +64,7 @@ export {
|
|
|
64
64
|
clusterOfGenesis,
|
|
65
65
|
type Cluster,
|
|
66
66
|
} from './constants.js'
|
|
67
|
-
export { decodeAddress, encodeAddress, findProgramAddress, getAssociatedTokenAddress, getMetadataAddress, isAddress, isOnCurve } from './address.js'
|
|
67
|
+
export { createProgramAddress, decodeAddress, encodeAddress, findProgramAddress, getAssociatedTokenAddress, getMetadataAddress, isAddress, isOnCurve } from './address.js'
|
|
68
68
|
export {
|
|
69
69
|
formatAmount,
|
|
70
70
|
formatUnits,
|
|
@@ -72,6 +72,7 @@ export {
|
|
|
72
72
|
parseMint,
|
|
73
73
|
parseTokenAccount,
|
|
74
74
|
parseTransferFeeConfig,
|
|
75
|
+
parseUnits,
|
|
75
76
|
type AccountInfo,
|
|
76
77
|
type MetaplexMetadata,
|
|
77
78
|
type MintInfo,
|
package/src/layout.ts
CHANGED
|
@@ -313,6 +313,23 @@ export function formatUnits(value: bigint, decimals: number): string {
|
|
|
313
313
|
return `${negative ? '-' : ''}${whole}${fraction ? `.${fraction}` : ''}`
|
|
314
314
|
}
|
|
315
315
|
|
|
316
|
+
/**
|
|
317
|
+
* formatUnits 的逆运算(与 ethers 的 parseUnits 同名同义):parseUnits('1.5', 9) === 1_500_000_000n。
|
|
318
|
+
* 小数位超过精度时报错(不四舍五入;多出的只有 0 时允许)
|
|
319
|
+
*/
|
|
320
|
+
export function parseUnits(value: string, decimals: number): bigint {
|
|
321
|
+
const match = /^(-)?(\d*)(?:\.(\d*))?$/.exec(value.trim())
|
|
322
|
+
if (!match || (!match[2] && !match[3])) {
|
|
323
|
+
throw new Error(`Invalid amount: ${JSON.stringify(value)}`)
|
|
324
|
+
}
|
|
325
|
+
const fraction = match[3] ?? ''
|
|
326
|
+
if (/[1-9]/.test(fraction.slice(decimals))) {
|
|
327
|
+
throw new Error(`Too many decimals: ${value} (max ${decimals})`)
|
|
328
|
+
}
|
|
329
|
+
const amount = BigInt((match[2] || '0') + fraction.slice(0, decimals).padEnd(decimals, '0'))
|
|
330
|
+
return match[1] ? -amount : amount
|
|
331
|
+
}
|
|
332
|
+
|
|
316
333
|
/**
|
|
317
334
|
* 同 formatUnits。
|
|
318
335
|
* @deprecated 改用 formatUnits:formatAmount 这个名字容易和项目里的数字展示函数撞名
|