@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
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { SYSTEM_PROGRAM_ID, TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from '../constants.js'
|
|
2
|
+
|
|
3
|
+
// 交易错误的可读说明(发送、/swap 的交易检查、/fees 共用)
|
|
4
|
+
|
|
5
|
+
export function errorText(err: unknown): string {
|
|
6
|
+
return err instanceof Error ? err.message : String(err)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// ---- 交易错误 ----
|
|
10
|
+
|
|
11
|
+
const TX_ERRORS: Record<string, string> = {
|
|
12
|
+
InsufficientFundsForFee: 'insufficient SOL for the transaction fee',
|
|
13
|
+
AccountNotFound: 'fee payer account not found (it has no SOL)',
|
|
14
|
+
BlockhashNotFound: 'blockhash not found',
|
|
15
|
+
AlreadyProcessed: 'transaction already processed',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 日志里最有用的一行:程序打印的 “Program log: Error: …”,其次是系统程序直接打印的说明(如 “Transfer: insufficient lamports …”,
|
|
20
|
+
* 不以 “Program ” 开头),最后才是运行时的 “Program … failed: custom program error: 0x1”
|
|
21
|
+
*/
|
|
22
|
+
function logDetail(logs: readonly string[]): string | undefined {
|
|
23
|
+
const reversed = [...logs].reverse()
|
|
24
|
+
const programError = reversed.find((line) => /^Program log: (Error|error)/.test(line))
|
|
25
|
+
if (programError) return programError.replace(/^Program log: /, '')
|
|
26
|
+
const plain = reversed.find((line) => !line.startsWith('Program '))
|
|
27
|
+
if (plain) return plain
|
|
28
|
+
return reversed.find((line) => / failed: /.test(line))?.replace(/^Program \w+ failed: /, '')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 常见程序的自定义错误码(上链失败时没有日志,只有错误码)
|
|
32
|
+
const SYSTEM_ERRORS = ['account already in use', 'insufficient lamports', 'invalid program id', 'invalid account data length']
|
|
33
|
+
const TOKEN_ERRORS: Record<number, string> = {
|
|
34
|
+
0: 'account not rent-exempt',
|
|
35
|
+
1: 'insufficient funds',
|
|
36
|
+
2: 'invalid mint',
|
|
37
|
+
3: 'account not associated with this mint',
|
|
38
|
+
4: 'owner does not match',
|
|
39
|
+
9: 'account is not initialized',
|
|
40
|
+
11: 'non-native account can only be closed if its balance is zero',
|
|
41
|
+
17: 'account is frozen',
|
|
42
|
+
18: 'mint decimals mismatch',
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function customErrorText(program: string | undefined, code: number): string | undefined {
|
|
46
|
+
if (program === SYSTEM_PROGRAM_ID) return SYSTEM_ERRORS[code]
|
|
47
|
+
if (program === TOKEN_PROGRAM_ID || program === TOKEN_2022_PROGRAM_ID) return TOKEN_ERRORS[code]
|
|
48
|
+
return undefined
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 交易错误的可读说明:优先取日志(见 logDetail),否则按错误类型;
|
|
53
|
+
* programs 是每条指令的程序(按下标),用来翻译常见程序的自定义错误码
|
|
54
|
+
*/
|
|
55
|
+
export function transactionErrorText(err: unknown, logs?: readonly string[] | null, programs?: readonly string[]): string {
|
|
56
|
+
const detail = logDetail(logs ?? [])
|
|
57
|
+
if (typeof err === 'string') {
|
|
58
|
+
return TX_ERRORS[err] ?? err
|
|
59
|
+
}
|
|
60
|
+
const instruction = (err as { InstructionError?: [number, unknown] } | null)?.InstructionError
|
|
61
|
+
if (instruction) {
|
|
62
|
+
const [index, reason] = instruction
|
|
63
|
+
const custom = (reason as { Custom?: number } | null)?.Custom
|
|
64
|
+
const known = custom === undefined ? undefined : customErrorText(programs?.[index], custom)
|
|
65
|
+
return detail ?? known ?? (typeof reason === 'string' ? reason : JSON.stringify(reason))
|
|
66
|
+
}
|
|
67
|
+
if ((err as { InsufficientFundsForRent?: unknown } | null)?.InsufficientFundsForRent) {
|
|
68
|
+
return 'account would be left with less SOL than the rent-exempt minimum'
|
|
69
|
+
}
|
|
70
|
+
return detail ?? JSON.stringify(err)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function isBudgetExceeded(err: unknown, logs?: readonly string[] | null): boolean {
|
|
74
|
+
const reason = (err as { InstructionError?: [number, unknown] } | null)?.InstructionError?.[1]
|
|
75
|
+
return reason === 'ComputationalBudgetExceeded' || (logs ?? []).some((l) => /exceeded CUs meter|compute units/i.test(l) && /exceed/i.test(l))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** 用户在钱包里拒绝签名 */
|
|
79
|
+
export function isRejection(err: unknown): boolean {
|
|
80
|
+
const e = err as { code?: unknown; name?: unknown; message?: unknown } | null
|
|
81
|
+
return e?.code === 4001 || /reject|denied|cancel|declined/i.test(`${String(e?.name ?? '')} ${String(e?.message ?? '')}`)
|
|
82
|
+
}
|
|
@@ -74,6 +74,23 @@ export function closeAccount(account: string, destination: string, owner: string
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
/** SPL Token / Token-2022 BurnChecked:销毁代币账户里的代币(校验 mint 和精度),owner 签名 */
|
|
78
|
+
export function burnChecked(account: string, mint: string, owner: string, amount: bigint, decimals: number, tokenProgram: string): Instruction {
|
|
79
|
+
return {
|
|
80
|
+
programId: tokenProgram,
|
|
81
|
+
keys: [
|
|
82
|
+
{ pubkey: account, isSigner: false, isWritable: true },
|
|
83
|
+
{ pubkey: mint, isSigner: false, isWritable: true },
|
|
84
|
+
{ pubkey: owner, isSigner: true, isWritable: false },
|
|
85
|
+
],
|
|
86
|
+
data: bytes(10, (v) => {
|
|
87
|
+
v.setUint8(0, 15)
|
|
88
|
+
v.setBigUint64(1, amount, true)
|
|
89
|
+
v.setUint8(9, decimals)
|
|
90
|
+
}),
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
77
94
|
/** 撤销代币账户的授权(Approve 设置的 delegate),owner 签名 */
|
|
78
95
|
export function revoke(account: string, owner: string, tokenProgram: string): Instruction {
|
|
79
96
|
return {
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { MAX_COMPUTE_UNITS, setComputeUnitLimit, setComputeUnitPrice } from './instructions.js'
|
|
2
|
+
import { MAX_ACCOUNT_LOCKS, PACKET_DATA_SIZE, compileMessage, transactionSize, type Instruction } from './transaction.js'
|
|
3
|
+
|
|
4
|
+
/** 打包用的占位 blockhash(大小与真实值相同) */
|
|
5
|
+
export const PLACEHOLDER_BLOCKHASH = '11111111111111111111111111111111'
|
|
6
|
+
|
|
7
|
+
/** 计算预算指令(上限 + 单价):发送时排在最前面 */
|
|
8
|
+
export function budgetInstructions(units: number, price: bigint): Instruction[] {
|
|
9
|
+
return [setComputeUnitLimit(units), setComputeUnitPrice(price)]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** 加上计算预算指令后,这些指令能否放进一笔交易(完整编译一次;不超过 1232 字节和 64 个账户) */
|
|
13
|
+
export function fitsInTransaction(payer: string, instructions: readonly Instruction[]): boolean {
|
|
14
|
+
const message = compileMessage(payer, [...budgetInstructions(MAX_COMPUTE_UNITS, 0n), ...instructions], PLACEHOLDER_BLOCKHASH)
|
|
15
|
+
return transactionSize(message) <= PACKET_DATA_SIZE && message.accountKeys.length <= MAX_ACCOUNT_LOCKS
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function compactLength(n: number): number {
|
|
19
|
+
return n < 0x80 ? 1 : n < 0x4000 ? 2 : 3
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 增量估算 legacy 交易的大小(与 compileMessage + transactionSize 的结果相同):
|
|
24
|
+
* 签名数 + 64 × 签名数 + 3 字节头 + 账户数 + 32 × 账户数 + 32 字节 blockhash + 指令数 + 各指令(程序下标 1 + 账户数 + 账户下标 + 数据长度 + 数据)。
|
|
25
|
+
* 只记累计的账户集合(是否签名者)和指令字节,加一个调用只看它自己的指令
|
|
26
|
+
*/
|
|
27
|
+
class SizeEstimate {
|
|
28
|
+
/** 账户 → 是否签名者 */
|
|
29
|
+
readonly #signer = new Map<string, boolean>()
|
|
30
|
+
#signers = 0
|
|
31
|
+
#instructions = 0
|
|
32
|
+
#instructionBytes = 0
|
|
33
|
+
|
|
34
|
+
constructor(payer: string) {
|
|
35
|
+
this.#signer.set(payer, true)
|
|
36
|
+
this.#signers = 1
|
|
37
|
+
this.add(budgetInstructions(MAX_COMPUTE_UNITS, 0n))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** 加入这些指令后的大小和账户数(不修改状态) */
|
|
41
|
+
probe(instructions: readonly Instruction[]): { size: number; accounts: number } {
|
|
42
|
+
let accounts = this.#signer.size
|
|
43
|
+
let signers = this.#signers
|
|
44
|
+
let bytes = this.#instructionBytes
|
|
45
|
+
const added = new Map<string, boolean>()
|
|
46
|
+
const touch = (pubkey: string, isSigner: boolean) => {
|
|
47
|
+
// 本次已经加过 / 升级过的以 added 为准(累计集合里是非签名者 false 时,?? 不会回落,不能先查累计集合)
|
|
48
|
+
const known = added.has(pubkey) ? added.get(pubkey) : this.#signer.get(pubkey)
|
|
49
|
+
if (known === undefined) {
|
|
50
|
+
accounts++
|
|
51
|
+
if (isSigner) signers++
|
|
52
|
+
added.set(pubkey, isSigner)
|
|
53
|
+
} else if (isSigner && !known) {
|
|
54
|
+
signers++
|
|
55
|
+
added.set(pubkey, true)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
for (const ix of instructions) {
|
|
59
|
+
ix.keys.forEach((k) => touch(k.pubkey, k.isSigner))
|
|
60
|
+
touch(ix.programId, false)
|
|
61
|
+
bytes += 1 + compactLength(ix.keys.length) + ix.keys.length + compactLength(ix.data.length) + ix.data.length
|
|
62
|
+
}
|
|
63
|
+
const count = this.#instructions + instructions.length
|
|
64
|
+
const size = compactLength(signers) + 64 * signers + 3 + compactLength(accounts) + 32 * accounts + 32 + compactLength(count) + bytes
|
|
65
|
+
return { size, accounts }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
fits(instructions: readonly Instruction[]): boolean {
|
|
69
|
+
const { size, accounts } = this.probe(instructions)
|
|
70
|
+
return size <= PACKET_DATA_SIZE && accounts <= MAX_ACCOUNT_LOCKS
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
add(instructions: readonly Instruction[]): void {
|
|
74
|
+
for (const ix of instructions) {
|
|
75
|
+
for (const k of [...ix.keys.map((key) => [key.pubkey, key.isSigner] as const), [ix.programId, false] as const]) {
|
|
76
|
+
const [pubkey, isSigner] = k
|
|
77
|
+
const known = this.#signer.get(pubkey)
|
|
78
|
+
if (known === undefined) {
|
|
79
|
+
this.#signer.set(pubkey, isSigner)
|
|
80
|
+
if (isSigner) this.#signers++
|
|
81
|
+
} else if (isSigner && !known) {
|
|
82
|
+
this.#signer.set(pubkey, true)
|
|
83
|
+
this.#signers++
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
this.#instructionBytes += 1 + compactLength(ix.keys.length) + ix.keys.length + compactLength(ix.data.length) + ix.data.length
|
|
87
|
+
this.#instructions++
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface PackOptions {
|
|
93
|
+
/** 每笔交易最多几个调用,默认不限(只受交易大小 / 账户数限制) */
|
|
94
|
+
batchSize?: number
|
|
95
|
+
/** 只能单独成一笔交易的调用(如计算单元和优先费用固定的扫空转账) */
|
|
96
|
+
alone?: (index: number) => boolean
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** 打包结果的一笔交易:包含的调用下标;tooLarge 为 true 时是单个调用自己就放不进一笔交易 */
|
|
100
|
+
export interface Pack {
|
|
101
|
+
indices: number[]
|
|
102
|
+
tooLarge: boolean
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 把调用按顺序打包成交易(batchCall / batchTransfer / batchCollect 的发送和 /reclaim、/claim 的批量计划共用):
|
|
107
|
+
* 相邻的调用放进同一笔,直到加上计算预算指令后超过 1232 字节或 64 个账户、或达到 batchSize;alone 的调用单独成一笔;
|
|
108
|
+
* 单个调用自己就放不进的单独成一笔并标记 tooLarge。
|
|
109
|
+
* 大小按累计的账户集合和指令字节增量估算(每加一个调用只看它自己的指令),每笔打完再完整编译校验一次
|
|
110
|
+
*/
|
|
111
|
+
export function packUnits(payer: string, units: ReadonlyArray<{ instructions: readonly Instruction[] }>, options: PackOptions = {}): Pack[] {
|
|
112
|
+
const batchSize = options.batchSize ?? Number.POSITIVE_INFINITY
|
|
113
|
+
const alone = options.alone ?? (() => false)
|
|
114
|
+
const packs: Pack[] = []
|
|
115
|
+
let i = 0
|
|
116
|
+
while (i < units.length) {
|
|
117
|
+
const estimate = new SizeEstimate(payer)
|
|
118
|
+
const first = (units[i] as { instructions: readonly Instruction[] }).instructions
|
|
119
|
+
if (!estimate.fits(first)) {
|
|
120
|
+
packs.push({ indices: [i], tooLarge: true })
|
|
121
|
+
i++
|
|
122
|
+
continue
|
|
123
|
+
}
|
|
124
|
+
estimate.add(first)
|
|
125
|
+
const indices = [i++]
|
|
126
|
+
if (!alone(indices[0] as number)) {
|
|
127
|
+
while (i < units.length && indices.length < batchSize && !alone(i)) {
|
|
128
|
+
const next = (units[i] as { instructions: readonly Instruction[] }).instructions
|
|
129
|
+
if (!estimate.fits(next)) break
|
|
130
|
+
estimate.add(next)
|
|
131
|
+
indices.push(i++)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// 完整编译校验一次(估算与编译结果应当一致;万一不一致,去掉末尾的调用直到放得下,去掉的留给下一笔)
|
|
135
|
+
while (indices.length > 1 && !fitsInTransaction(payer, indices.flatMap((k) => (units[k] as { instructions: readonly Instruction[] }).instructions))) {
|
|
136
|
+
indices.pop()
|
|
137
|
+
i--
|
|
138
|
+
}
|
|
139
|
+
packs.push({ indices, tooLarge: false })
|
|
140
|
+
}
|
|
141
|
+
return packs
|
|
142
|
+
}
|