@w3lib/solana-batch-call 0.6.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 +33 -0
- package/README.md +16 -8
- 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 +2 -2
- package/dist/batchPlan.d.ts.map +1 -1
- package/dist/batchPlan.js +5 -26
- package/dist/batchPlan.js.map +1 -1
- 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 +5 -3
- package/dist/claim.d.ts.map +1 -1
- package/dist/claim.js +10 -10
- package/dist/claim.js.map +1 -1
- 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.map +1 -1
- package/dist/decode.js +2 -29
- package/dist/decode.js.map +1 -1
- 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.map +1 -1
- package/dist/history.js +4 -15
- package/dist/history.js.map +1 -1
- 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/lp.d.ts +9 -13
- package/dist/lp.d.ts.map +1 -1
- package/dist/lp.js +67 -55
- package/dist/lp.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/reclaim.d.ts +4 -2
- package/dist/reclaim.d.ts.map +1 -1
- package/dist/reclaim.js +16 -16
- package/dist/reclaim.js.map +1 -1
- 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 +400 -109
- package/dist/rpc.js.map +1 -1
- package/dist/simulate.d.ts.map +1 -1
- package/dist/simulate.js +3 -26
- package/dist/simulate.js.map +1 -1
- 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 +3 -3
- package/dist/subpaths/claim.d.ts.map +1 -1
- package/dist/subpaths/claim.js.map +1 -1
- package/dist/subpaths/reclaim.d.ts +3 -3
- package/dist/subpaths/reclaim.d.ts.map +1 -1
- package/dist/subpaths/reclaim.js.map +1 -1
- 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/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 +1 -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 +6 -26
- package/src/bytes.ts +89 -0
- package/src/claim.ts +15 -11
- package/src/client.ts +59 -1239
- package/src/clientConfig.ts +12 -0
- package/src/decode.ts +4 -30
- package/src/fees.ts +2 -9
- package/src/history.ts +4 -15
- package/src/index.ts +13 -1
- package/src/layout.ts +63 -82
- package/src/liquidity/common.ts +2 -2
- package/src/lp.ts +86 -60
- package/src/portfolio.ts +8 -11
- package/src/reclaim.ts +20 -17
- package/src/risk.ts +2 -2
- package/src/rpc.ts +472 -116
- package/src/simulate.ts +3 -21
- package/src/source.ts +14 -5
- package/src/subpaths/approvals.ts +3 -2
- package/src/subpaths/claim.ts +3 -2
- package/src/subpaths/reclaim.ts +3 -2
- 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/pack.ts +142 -0
- package/src/write/send.ts +74 -407
- package/src/write/tracker.ts +152 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { sleep } from '../util.js'
|
|
2
|
+
import type { Chain } from './chain.js'
|
|
3
|
+
|
|
4
|
+
interface SignatureStatus {
|
|
5
|
+
slot: number
|
|
6
|
+
err: unknown
|
|
7
|
+
confirmationStatus?: 'processed' | 'confirmed' | 'finalized' | null
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type WatchResult = { kind: 'landed'; slot: number; err: unknown } | { kind: 'expired' } | { kind: 'timeout' } | { kind: 'aborted' }
|
|
11
|
+
|
|
12
|
+
interface Watched {
|
|
13
|
+
signature: string
|
|
14
|
+
raw: string
|
|
15
|
+
/** blockhash 的有效期;Infinity 表示不知道(钱包换了 blockhash),这时不判断过期、不自动重发 */
|
|
16
|
+
lastValidBlockHeight: number
|
|
17
|
+
deadline: number
|
|
18
|
+
polls: number
|
|
19
|
+
resolve(result: WatchResult): void
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 统一轮询所有待确认的交易:一次 getSignatureStatuses 查全部(每 256 个一组),未确认的每两次轮询重发一次,blockhash 过期后再查一次历史确认没上链。
|
|
24
|
+
* 区块高度(getEpochInfo)不是每轮都查:记下上次查到的(高度, slot),之后用状态查询返回的 context.slot 估计高度的上限
|
|
25
|
+
* (高度每个 slot 最多加 1),估计值超过某笔交易的 lastValidBlockHeight 时才查,确认过期
|
|
26
|
+
*/
|
|
27
|
+
export class Tracker {
|
|
28
|
+
readonly #watched = new Set<Watched>()
|
|
29
|
+
#running = false
|
|
30
|
+
/** 上次查到的区块高度和对应的 slot */
|
|
31
|
+
#anchor: { height: number; slot: number } | null = null
|
|
32
|
+
|
|
33
|
+
constructor(readonly chain: Chain) {}
|
|
34
|
+
|
|
35
|
+
watch(signature: string, raw: string, lastValidBlockHeight: number): Promise<WatchResult> {
|
|
36
|
+
const { timeout = 120_000 } = this.chain.options
|
|
37
|
+
return new Promise((resolve) => {
|
|
38
|
+
this.#watched.add({ signature, raw, lastValidBlockHeight, deadline: Date.now() + timeout, polls: 0, resolve })
|
|
39
|
+
if (!this.#running) {
|
|
40
|
+
this.#running = true
|
|
41
|
+
void this.#loop()
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** 查交易状态;contextSlot 是各次响应里最小的 context.slot(节点的数据新到哪个 slot),响应没带时为 0 */
|
|
47
|
+
async #statuses(signatures: string[], history: boolean): Promise<{ found: Map<string, SignatureStatus>; contextSlot: number }> {
|
|
48
|
+
const found = new Map<string, SignatureStatus>()
|
|
49
|
+
let contextSlot = Number.POSITIVE_INFINITY
|
|
50
|
+
for (let i = 0; i < signatures.length; i += 256) {
|
|
51
|
+
const chunk = signatures.slice(i, i + 256)
|
|
52
|
+
const res = await this.chain.client.request<{ context?: { slot?: number }; value: Array<SignatureStatus | null> }>('getSignatureStatuses', [
|
|
53
|
+
chunk,
|
|
54
|
+
{ searchTransactionHistory: history },
|
|
55
|
+
])
|
|
56
|
+
contextSlot = Math.min(contextSlot, res.context?.slot ?? 0)
|
|
57
|
+
res.value.forEach((s, k) => s && found.set(chunk[k] as string, s))
|
|
58
|
+
}
|
|
59
|
+
return { found, contextSlot }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
#done(status: SignatureStatus): boolean {
|
|
63
|
+
const level = status.confirmationStatus
|
|
64
|
+
return this.chain.commitment === 'finalized' ? level === 'finalized' : level === 'confirmed' || level === 'finalized'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
#settle(item: Watched, result: WatchResult): void {
|
|
68
|
+
this.#watched.delete(item)
|
|
69
|
+
item.resolve(result)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** 是否可能有交易过期了(需要查区块高度确认):不知道当前高度、或状态查询没带 slot 时总是查 */
|
|
73
|
+
#mayHaveExpired(waiting: readonly Watched[], contextSlot: number): boolean {
|
|
74
|
+
const known = waiting.filter((w) => Number.isFinite(w.lastValidBlockHeight))
|
|
75
|
+
if (!known.length) {
|
|
76
|
+
return false
|
|
77
|
+
}
|
|
78
|
+
if (!this.#anchor || !(contextSlot > 0) || !Number.isFinite(contextSlot)) {
|
|
79
|
+
return true
|
|
80
|
+
}
|
|
81
|
+
const highest = this.#anchor.height + Math.max(0, contextSlot - this.#anchor.slot)
|
|
82
|
+
return known.some((w) => highest > w.lastValidBlockHeight)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async #loop(): Promise<void> {
|
|
86
|
+
const { pollInterval = 2_000, signal } = this.chain.options
|
|
87
|
+
try {
|
|
88
|
+
while (this.#watched.size) {
|
|
89
|
+
await sleep(pollInterval, signal, false)
|
|
90
|
+
if (signal?.aborted) {
|
|
91
|
+
;[...this.#watched].forEach((w) => this.#settle(w, { kind: 'aborted' }))
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
const items = [...this.#watched]
|
|
95
|
+
try {
|
|
96
|
+
const { found: statuses, contextSlot } = await this.#statuses(
|
|
97
|
+
items.map((w) => w.signature),
|
|
98
|
+
false,
|
|
99
|
+
)
|
|
100
|
+
for (const item of items) {
|
|
101
|
+
const status = statuses.get(item.signature)
|
|
102
|
+
if (status && this.#done(status)) {
|
|
103
|
+
this.#settle(item, { kind: 'landed', slot: status.slot, err: status.err })
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const waiting = items.filter((w) => this.#watched.has(w))
|
|
107
|
+
if (waiting.length && this.#mayHaveExpired(waiting, contextSlot)) {
|
|
108
|
+
// 区块高度和对应的 slot 一起取(同一个节点、同一个时刻)
|
|
109
|
+
const epoch = await this.chain.client.request<{ blockHeight: number; absoluteSlot: number }>('getEpochInfo', [{ commitment: 'confirmed' }])
|
|
110
|
+
this.#anchor = { height: epoch.blockHeight, slot: epoch.absoluteSlot }
|
|
111
|
+
const expired = waiting.filter((w) => epoch.blockHeight > w.lastValidBlockHeight)
|
|
112
|
+
if (expired.length) {
|
|
113
|
+
// 过期了:再查一次历史确认确实没上链。请求可能落到另一个节点(多节点 / 负载均衡),
|
|
114
|
+
// 它的数据必须不早于看到过期的那个 slot,否则可能只是它还没看到这笔交易,这一轮先不判断
|
|
115
|
+
const history = await this.#statuses(
|
|
116
|
+
expired.map((w) => w.signature),
|
|
117
|
+
true,
|
|
118
|
+
)
|
|
119
|
+
const caughtUp = history.contextSlot >= epoch.absoluteSlot
|
|
120
|
+
for (const item of expired) {
|
|
121
|
+
const status = history.found.get(item.signature)
|
|
122
|
+
if (!status) {
|
|
123
|
+
if (!caughtUp) continue
|
|
124
|
+
this.#settle(item, { kind: 'expired' })
|
|
125
|
+
} else if (this.#done(status)) {
|
|
126
|
+
this.#settle(item, { kind: 'landed', slot: status.slot, err: status.err })
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
} catch {
|
|
132
|
+
// 节点暂时出错:下次再查
|
|
133
|
+
}
|
|
134
|
+
const now = Date.now()
|
|
135
|
+
for (const item of [...this.#watched]) {
|
|
136
|
+
if (now >= item.deadline) {
|
|
137
|
+
this.#settle(item, { kind: 'timeout' })
|
|
138
|
+
} else if (++item.polls % 2 === 0) {
|
|
139
|
+
// 每两次轮询重发一次(每笔都重发会让待确认交易多时很快触发节点限频)
|
|
140
|
+
void this.chain.broadcast(item.raw).catch(() => {})
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
} finally {
|
|
145
|
+
this.#running = false
|
|
146
|
+
if (this.#watched.size) {
|
|
147
|
+
this.#running = true
|
|
148
|
+
void this.#loop()
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|