@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.
Files changed (165) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +16 -8
  3. package/dist/accountReader.d.ts +57 -0
  4. package/dist/accountReader.d.ts.map +1 -0
  5. package/dist/accountReader.js +149 -0
  6. package/dist/accountReader.js.map +1 -0
  7. package/dist/address.d.ts +9 -0
  8. package/dist/address.d.ts.map +1 -1
  9. package/dist/address.js +36 -6
  10. package/dist/address.js.map +1 -1
  11. package/dist/approvals.d.ts +5 -3
  12. package/dist/approvals.d.ts.map +1 -1
  13. package/dist/approvals.js +25 -36
  14. package/dist/approvals.js.map +1 -1
  15. package/dist/balances.d.ts +178 -0
  16. package/dist/balances.d.ts.map +1 -0
  17. package/dist/balances.js +524 -0
  18. package/dist/balances.js.map +1 -0
  19. package/dist/batchPlan.d.ts +2 -2
  20. package/dist/batchPlan.d.ts.map +1 -1
  21. package/dist/batchPlan.js +5 -26
  22. package/dist/batchPlan.js.map +1 -1
  23. package/dist/bytes.d.ts +31 -0
  24. package/dist/bytes.d.ts.map +1 -0
  25. package/dist/bytes.js +74 -0
  26. package/dist/bytes.js.map +1 -0
  27. package/dist/claim.d.ts +5 -3
  28. package/dist/claim.d.ts.map +1 -1
  29. package/dist/claim.js +10 -10
  30. package/dist/claim.js.map +1 -1
  31. package/dist/client.d.ts +26 -41
  32. package/dist/client.d.ts.map +1 -1
  33. package/dist/client.js +41 -986
  34. package/dist/client.js.map +1 -1
  35. package/dist/clientConfig.d.ts +11 -0
  36. package/dist/clientConfig.d.ts.map +1 -0
  37. package/dist/clientConfig.js +3 -0
  38. package/dist/clientConfig.js.map +1 -0
  39. package/dist/decode.d.ts.map +1 -1
  40. package/dist/decode.js +2 -29
  41. package/dist/decode.js.map +1 -1
  42. package/dist/fees.d.ts.map +1 -1
  43. package/dist/fees.js +2 -12
  44. package/dist/fees.js.map +1 -1
  45. package/dist/history.d.ts.map +1 -1
  46. package/dist/history.js +4 -15
  47. package/dist/history.js.map +1 -1
  48. package/dist/index.d.ts +3 -3
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +1 -1
  51. package/dist/index.js.map +1 -1
  52. package/dist/layout.d.ts +21 -0
  53. package/dist/layout.d.ts.map +1 -1
  54. package/dist/layout.js +50 -69
  55. package/dist/layout.js.map +1 -1
  56. package/dist/liquidity/common.js +2 -2
  57. package/dist/liquidity/common.js.map +1 -1
  58. package/dist/lp.d.ts +9 -13
  59. package/dist/lp.d.ts.map +1 -1
  60. package/dist/lp.js +67 -55
  61. package/dist/lp.js.map +1 -1
  62. package/dist/portfolio.d.ts.map +1 -1
  63. package/dist/portfolio.js +3 -8
  64. package/dist/portfolio.js.map +1 -1
  65. package/dist/reclaim.d.ts +4 -2
  66. package/dist/reclaim.d.ts.map +1 -1
  67. package/dist/reclaim.js +16 -16
  68. package/dist/reclaim.js.map +1 -1
  69. package/dist/risk.js +2 -2
  70. package/dist/risk.js.map +1 -1
  71. package/dist/rpc.d.ts +30 -7
  72. package/dist/rpc.d.ts.map +1 -1
  73. package/dist/rpc.js +400 -109
  74. package/dist/rpc.js.map +1 -1
  75. package/dist/simulate.d.ts.map +1 -1
  76. package/dist/simulate.js +3 -26
  77. package/dist/simulate.js.map +1 -1
  78. package/dist/source.d.ts +8 -4
  79. package/dist/source.d.ts.map +1 -1
  80. package/dist/source.js +11 -4
  81. package/dist/source.js.map +1 -1
  82. package/dist/subpaths/approvals.d.ts +3 -3
  83. package/dist/subpaths/approvals.d.ts.map +1 -1
  84. package/dist/subpaths/approvals.js.map +1 -1
  85. package/dist/subpaths/claim.d.ts +3 -3
  86. package/dist/subpaths/claim.d.ts.map +1 -1
  87. package/dist/subpaths/claim.js.map +1 -1
  88. package/dist/subpaths/reclaim.d.ts +3 -3
  89. package/dist/subpaths/reclaim.d.ts.map +1 -1
  90. package/dist/subpaths/reclaim.js.map +1 -1
  91. package/dist/swap/index.js +3 -3
  92. package/dist/swap/index.js.map +1 -1
  93. package/dist/tokenDetails.d.ts +12 -0
  94. package/dist/tokenDetails.d.ts.map +1 -0
  95. package/dist/tokenDetails.js +196 -0
  96. package/dist/tokenDetails.js.map +1 -0
  97. package/dist/tokenMeta.d.ts +63 -0
  98. package/dist/tokenMeta.d.ts.map +1 -0
  99. package/dist/tokenMeta.js +226 -0
  100. package/dist/tokenMeta.js.map +1 -0
  101. package/dist/tradeCheck.js +3 -3
  102. package/dist/tradeCheck.js.map +1 -1
  103. package/dist/util.d.ts +22 -0
  104. package/dist/util.d.ts.map +1 -0
  105. package/dist/util.js +107 -0
  106. package/dist/util.js.map +1 -0
  107. package/dist/write/chain.d.ts +51 -0
  108. package/dist/write/chain.d.ts.map +1 -0
  109. package/dist/write/chain.js +184 -0
  110. package/dist/write/chain.js.map +1 -0
  111. package/dist/write/collect.js +2 -2
  112. package/dist/write/collect.js.map +1 -1
  113. package/dist/write/errors.d.ts +10 -0
  114. package/dist/write/errors.d.ts.map +1 -0
  115. package/dist/write/errors.js +77 -0
  116. package/dist/write/errors.js.map +1 -0
  117. package/dist/write/pack.d.ts +28 -0
  118. package/dist/write/pack.d.ts.map +1 -0
  119. package/dist/write/pack.js +125 -0
  120. package/dist/write/pack.js.map +1 -0
  121. package/dist/write/send.d.ts +23 -9
  122. package/dist/write/send.d.ts.map +1 -1
  123. package/dist/write/send.js +53 -351
  124. package/dist/write/send.js.map +1 -1
  125. package/dist/write/tracker.d.ts +24 -0
  126. package/dist/write/tracker.d.ts.map +1 -0
  127. package/dist/write/tracker.js +129 -0
  128. package/dist/write/tracker.js.map +1 -0
  129. package/package.json +1 -1
  130. package/src/accountReader.ts +187 -0
  131. package/src/address.ts +41 -6
  132. package/src/approvals.ts +36 -44
  133. package/src/balances.ts +678 -0
  134. package/src/batchPlan.ts +6 -26
  135. package/src/bytes.ts +89 -0
  136. package/src/claim.ts +15 -11
  137. package/src/client.ts +59 -1239
  138. package/src/clientConfig.ts +12 -0
  139. package/src/decode.ts +4 -30
  140. package/src/fees.ts +2 -9
  141. package/src/history.ts +4 -15
  142. package/src/index.ts +13 -1
  143. package/src/layout.ts +63 -82
  144. package/src/liquidity/common.ts +2 -2
  145. package/src/lp.ts +86 -60
  146. package/src/portfolio.ts +8 -11
  147. package/src/reclaim.ts +20 -17
  148. package/src/risk.ts +2 -2
  149. package/src/rpc.ts +472 -116
  150. package/src/simulate.ts +3 -21
  151. package/src/source.ts +14 -5
  152. package/src/subpaths/approvals.ts +3 -2
  153. package/src/subpaths/claim.ts +3 -2
  154. package/src/subpaths/reclaim.ts +3 -2
  155. package/src/swap/index.ts +3 -3
  156. package/src/tokenDetails.ts +240 -0
  157. package/src/tokenMeta.ts +270 -0
  158. package/src/tradeCheck.ts +3 -3
  159. package/src/util.ts +110 -0
  160. package/src/write/chain.ts +218 -0
  161. package/src/write/collect.ts +2 -2
  162. package/src/write/errors.ts +82 -0
  163. package/src/write/pack.ts +142 -0
  164. package/src/write/send.ts +74 -407
  165. 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
+ }