@coffer_so/sdk 1.0.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 (228) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +283 -0
  3. package/dist/clients/AdminClient.d.ts +194 -0
  4. package/dist/clients/AdminClient.d.ts.map +1 -0
  5. package/dist/clients/AdminClient.js +482 -0
  6. package/dist/clients/AdminClient.js.map +1 -0
  7. package/dist/clients/CofferBackendClient.d.ts +877 -0
  8. package/dist/clients/CofferBackendClient.d.ts.map +1 -0
  9. package/dist/clients/CofferBackendClient.js +536 -0
  10. package/dist/clients/CofferBackendClient.js.map +1 -0
  11. package/dist/clients/CubeBackendClient.d.ts +877 -0
  12. package/dist/clients/CubeBackendClient.d.ts.map +1 -0
  13. package/dist/clients/CubeBackendClient.js +536 -0
  14. package/dist/clients/CubeBackendClient.js.map +1 -0
  15. package/dist/clients/CubicPoolClient.d.ts +120 -0
  16. package/dist/clients/CubicPoolClient.d.ts.map +1 -0
  17. package/dist/clients/CubicPoolClient.js +687 -0
  18. package/dist/clients/CubicPoolClient.js.map +1 -0
  19. package/dist/clients/PoolFactoryClient.d.ts +53 -0
  20. package/dist/clients/PoolFactoryClient.d.ts.map +1 -0
  21. package/dist/clients/PoolFactoryClient.js +91 -0
  22. package/dist/clients/PoolFactoryClient.js.map +1 -0
  23. package/dist/clients/RpcClient.d.ts +49 -0
  24. package/dist/clients/RpcClient.d.ts.map +1 -0
  25. package/dist/clients/RpcClient.js +135 -0
  26. package/dist/clients/RpcClient.js.map +1 -0
  27. package/dist/clients/SingleTokenDepositClient.d.ts +67 -0
  28. package/dist/clients/SingleTokenDepositClient.d.ts.map +1 -0
  29. package/dist/clients/SingleTokenDepositClient.js +105 -0
  30. package/dist/clients/SingleTokenDepositClient.js.map +1 -0
  31. package/dist/clients/contract-instructions.d.ts +24 -0
  32. package/dist/clients/contract-instructions.d.ts.map +1 -0
  33. package/dist/clients/contract-instructions.js +130 -0
  34. package/dist/clients/contract-instructions.js.map +1 -0
  35. package/dist/clients/index.d.ts +10 -0
  36. package/dist/clients/index.d.ts.map +1 -0
  37. package/dist/clients/index.js +26 -0
  38. package/dist/clients/index.js.map +1 -0
  39. package/dist/clients/quote-math.d.ts +15 -0
  40. package/dist/clients/quote-math.d.ts.map +1 -0
  41. package/dist/clients/quote-math.js +98 -0
  42. package/dist/clients/quote-math.js.map +1 -0
  43. package/dist/clients/tx-builders.d.ts +231 -0
  44. package/dist/clients/tx-builders.d.ts.map +1 -0
  45. package/dist/clients/tx-builders.js +861 -0
  46. package/dist/clients/tx-builders.js.map +1 -0
  47. package/dist/clients/versioned.d.ts +34 -0
  48. package/dist/clients/versioned.d.ts.map +1 -0
  49. package/dist/clients/versioned.js +55 -0
  50. package/dist/clients/versioned.js.map +1 -0
  51. package/dist/config/index.d.ts +65 -0
  52. package/dist/config/index.d.ts.map +1 -0
  53. package/dist/config/index.js +75 -0
  54. package/dist/config/index.js.map +1 -0
  55. package/dist/config/networks.d.ts +12 -0
  56. package/dist/config/networks.d.ts.map +1 -0
  57. package/dist/config/networks.js +51 -0
  58. package/dist/config/networks.js.map +1 -0
  59. package/dist/config/tokens.d.ts +19 -0
  60. package/dist/config/tokens.d.ts.map +1 -0
  61. package/dist/config/tokens.js +43 -0
  62. package/dist/config/tokens.js.map +1 -0
  63. package/dist/idl/cubic_pool.json +4203 -0
  64. package/dist/idl/index.d.ts +1769 -0
  65. package/dist/idl/index.d.ts.map +1 -0
  66. package/dist/idl/index.js +18 -0
  67. package/dist/idl/index.js.map +1 -0
  68. package/dist/idl/protocol_admin.json +2615 -0
  69. package/dist/idl/single_token_liquidity.json +1121 -0
  70. package/dist/index.d.ts +75 -0
  71. package/dist/index.d.ts.map +1 -0
  72. package/dist/index.js +206 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/math/cubicMath.d.ts +40 -0
  75. package/dist/math/cubicMath.d.ts.map +1 -0
  76. package/dist/math/cubicMath.js +82 -0
  77. package/dist/math/cubicMath.js.map +1 -0
  78. package/dist/math/fixedPoint.d.ts +19 -0
  79. package/dist/math/fixedPoint.d.ts.map +1 -0
  80. package/dist/math/fixedPoint.js +86 -0
  81. package/dist/math/fixedPoint.js.map +1 -0
  82. package/dist/math/index.d.ts +9 -0
  83. package/dist/math/index.d.ts.map +1 -0
  84. package/dist/math/index.js +30 -0
  85. package/dist/math/index.js.map +1 -0
  86. package/dist/math/logExp.d.ts +6 -0
  87. package/dist/math/logExp.d.ts.map +1 -0
  88. package/dist/math/logExp.js +68 -0
  89. package/dist/math/logExp.js.map +1 -0
  90. package/dist/math/lpLoss.d.ts +44 -0
  91. package/dist/math/lpLoss.d.ts.map +1 -0
  92. package/dist/math/lpLoss.js +103 -0
  93. package/dist/math/lpLoss.js.map +1 -0
  94. package/dist/math/maxSelloff.d.ts +164 -0
  95. package/dist/math/maxSelloff.d.ts.map +1 -0
  96. package/dist/math/maxSelloff.js +397 -0
  97. package/dist/math/maxSelloff.js.map +1 -0
  98. package/dist/math/singleToken.d.ts +54 -0
  99. package/dist/math/singleToken.d.ts.map +1 -0
  100. package/dist/math/singleToken.js +188 -0
  101. package/dist/math/singleToken.js.map +1 -0
  102. package/dist/math/slippage.d.ts +13 -0
  103. package/dist/math/slippage.d.ts.map +1 -0
  104. package/dist/math/slippage.js +44 -0
  105. package/dist/math/slippage.js.map +1 -0
  106. package/dist/math/surgeFee.d.ts +24 -0
  107. package/dist/math/surgeFee.d.ts.map +1 -0
  108. package/dist/math/surgeFee.js +85 -0
  109. package/dist/math/surgeFee.js.map +1 -0
  110. package/dist/math/weightedMath.d.ts +20 -0
  111. package/dist/math/weightedMath.d.ts.map +1 -0
  112. package/dist/math/weightedMath.js +66 -0
  113. package/dist/math/weightedMath.js.map +1 -0
  114. package/dist/parsers/borsh.d.ts +28 -0
  115. package/dist/parsers/borsh.d.ts.map +1 -0
  116. package/dist/parsers/borsh.js +104 -0
  117. package/dist/parsers/borsh.js.map +1 -0
  118. package/dist/parsers/contracts.d.ts +26 -0
  119. package/dist/parsers/contracts.d.ts.map +1 -0
  120. package/dist/parsers/contracts.js +62 -0
  121. package/dist/parsers/contracts.js.map +1 -0
  122. package/dist/parsers/events.d.ts +3 -0
  123. package/dist/parsers/events.d.ts.map +1 -0
  124. package/dist/parsers/events.js +281 -0
  125. package/dist/parsers/events.js.map +1 -0
  126. package/dist/parsers/index.d.ts +6 -0
  127. package/dist/parsers/index.d.ts.map +1 -0
  128. package/dist/parsers/index.js +22 -0
  129. package/dist/parsers/index.js.map +1 -0
  130. package/dist/parsers/mintAccount.d.ts +24 -0
  131. package/dist/parsers/mintAccount.d.ts.map +1 -0
  132. package/dist/parsers/mintAccount.js +27 -0
  133. package/dist/parsers/mintAccount.js.map +1 -0
  134. package/dist/parsers/poolAccount.d.ts +141 -0
  135. package/dist/parsers/poolAccount.d.ts.map +1 -0
  136. package/dist/parsers/poolAccount.js +224 -0
  137. package/dist/parsers/poolAccount.js.map +1 -0
  138. package/dist/types/contracts.d.ts +1340 -0
  139. package/dist/types/contracts.d.ts.map +1 -0
  140. package/dist/types/contracts.js +3 -0
  141. package/dist/types/contracts.js.map +1 -0
  142. package/dist/types/events.d.ts +172 -0
  143. package/dist/types/events.d.ts.map +1 -0
  144. package/dist/types/events.js +3 -0
  145. package/dist/types/events.js.map +1 -0
  146. package/dist/types/index.d.ts +6 -0
  147. package/dist/types/index.d.ts.map +1 -0
  148. package/dist/types/index.js +22 -0
  149. package/dist/types/index.js.map +1 -0
  150. package/dist/types/pool.d.ts +149 -0
  151. package/dist/types/pool.d.ts.map +1 -0
  152. package/dist/types/pool.js +3 -0
  153. package/dist/types/pool.js.map +1 -0
  154. package/dist/types/result.d.ts +24 -0
  155. package/dist/types/result.d.ts.map +1 -0
  156. package/dist/types/result.js +11 -0
  157. package/dist/types/result.js.map +1 -0
  158. package/dist/types/tx.d.ts +261 -0
  159. package/dist/types/tx.d.ts.map +1 -0
  160. package/dist/types/tx.js +3 -0
  161. package/dist/types/tx.js.map +1 -0
  162. package/dist/utils/errors.d.ts +44 -0
  163. package/dist/utils/errors.d.ts.map +1 -0
  164. package/dist/utils/errors.js +192 -0
  165. package/dist/utils/errors.js.map +1 -0
  166. package/dist/utils/extensions.d.ts +98 -0
  167. package/dist/utils/extensions.d.ts.map +1 -0
  168. package/dist/utils/extensions.js +187 -0
  169. package/dist/utils/extensions.js.map +1 -0
  170. package/dist/utils/index.d.ts +5 -0
  171. package/dist/utils/index.d.ts.map +1 -0
  172. package/dist/utils/index.js +21 -0
  173. package/dist/utils/index.js.map +1 -0
  174. package/dist/utils/pda.d.ts +8 -0
  175. package/dist/utils/pda.d.ts.map +1 -0
  176. package/dist/utils/pda.js +27 -0
  177. package/dist/utils/pda.js.map +1 -0
  178. package/dist/utils/retry.d.ts +22 -0
  179. package/dist/utils/retry.d.ts.map +1 -0
  180. package/dist/utils/retry.js +62 -0
  181. package/dist/utils/retry.js.map +1 -0
  182. package/package.json +54 -0
  183. package/src/clients/AdminClient.ts +608 -0
  184. package/src/clients/CofferBackendClient.ts +1413 -0
  185. package/src/clients/CubicPoolClient.ts +719 -0
  186. package/src/clients/PoolFactoryClient.ts +110 -0
  187. package/src/clients/RpcClient.ts +195 -0
  188. package/src/clients/SingleTokenDepositClient.ts +143 -0
  189. package/src/clients/contract-instructions.ts +115 -0
  190. package/src/clients/index.ts +9 -0
  191. package/src/clients/quote-math.ts +81 -0
  192. package/src/clients/tx-builders.ts +1096 -0
  193. package/src/clients/versioned.ts +80 -0
  194. package/src/config/index.ts +111 -0
  195. package/src/config/networks.ts +71 -0
  196. package/src/config/tokens.ts +52 -0
  197. package/src/idl/cubic_pool.json +4203 -0
  198. package/src/idl/index.ts +13 -0
  199. package/src/idl/protocol_admin.json +2615 -0
  200. package/src/idl/single_token_liquidity.json +1121 -0
  201. package/src/index.ts +262 -0
  202. package/src/math/cubicMath.ts +92 -0
  203. package/src/math/fixedPoint.ts +71 -0
  204. package/src/math/index.ts +13 -0
  205. package/src/math/logExp.ts +61 -0
  206. package/src/math/lpLoss.ts +160 -0
  207. package/src/math/maxSelloff.ts +564 -0
  208. package/src/math/singleToken.ts +251 -0
  209. package/src/math/slippage.ts +38 -0
  210. package/src/math/surgeFee.ts +101 -0
  211. package/src/math/weightedMath.ts +64 -0
  212. package/src/parsers/borsh.ts +106 -0
  213. package/src/parsers/contracts.ts +64 -0
  214. package/src/parsers/events.ts +275 -0
  215. package/src/parsers/index.ts +5 -0
  216. package/src/parsers/mintAccount.ts +44 -0
  217. package/src/parsers/poolAccount.ts +372 -0
  218. package/src/types/contracts.ts +238 -0
  219. package/src/types/events.ts +198 -0
  220. package/src/types/index.ts +5 -0
  221. package/src/types/pool.ts +147 -0
  222. package/src/types/result.ts +51 -0
  223. package/src/types/tx.ts +278 -0
  224. package/src/utils/errors.ts +210 -0
  225. package/src/utils/extensions.ts +196 -0
  226. package/src/utils/index.ts +4 -0
  227. package/src/utils/pda.ts +58 -0
  228. package/src/utils/retry.ts +85 -0
@@ -0,0 +1,275 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ import BN from "bn.js";
3
+ import { BorshReader } from "./borsh";
4
+ import { decodeContractEvent, ContractEvent } from "./contracts";
5
+ import { CubicPoolEvent, AdditionalContractEvent } from "../types/events";
6
+
7
+ /**
8
+ * Anchor event log format (base64-encoded after `Program data:`):
9
+ * [ 8-byte discriminator | borsh-encoded event struct ]
10
+ *
11
+ * Discriminators for cubic-pool and single-token-liquidity are hard-coded
12
+ * here from the generated IDL. Never guess from event name — the Anchor
13
+ * macro actually uses `sha256("event:<Name>")[0..8]`, but for robustness
14
+ * we pin the bytes.
15
+ */
16
+
17
+ // Cubic-pool event discriminators (from target/idl/cubic_pool.json):
18
+ const DISC = {
19
+ PoolInitialized: Buffer.from([100, 118, 173, 87, 12, 198, 254, 229]),
20
+ Swap: Buffer.from([81, 108, 227, 190, 205, 208, 10, 196]),
21
+ LiquidityAdded: Buffer.from([154, 26, 221, 108, 238, 64, 217, 161]),
22
+ LiquidityRemoved: Buffer.from([225, 105, 216, 39, 124, 116, 169, 189]),
23
+ ProtocolFeesCollected: Buffer.from([165, 34, 125, 155, 15, 86, 99, 191]),
24
+ SwapFeeRateUpdated: Buffer.from([101, 132, 24, 255, 91, 253, 227, 101]),
25
+ ProtocolFeeRateUpdated: Buffer.from([189, 56, 7, 65, 0, 95, 192, 6]),
26
+ PoolEnabledUpdated: Buffer.from([101, 47, 3, 240, 197, 181, 236, 142]),
27
+ SwapsEnabledUpdated: Buffer.from([55, 116, 118, 138, 102, 26, 227, 223]),
28
+ DebugLiquidityWithdrawn: Buffer.from([174, 59, 149, 22, 135, 129, 129, 83]),
29
+ PoolStateLog: Buffer.from([59, 254, 237, 111, 163, 10, 140, 224]),
30
+ PoolInfo: Buffer.from([207, 20, 87, 97, 251, 212, 234, 45]),
31
+ BannedExtensionsUpdated: Buffer.from([107, 126, 13, 149, 182, 108, 139, 202]),
32
+ MaxSelloffWindowAdvanced: Buffer.from([229, 227, 163, 30, 22, 183, 78, 57]),
33
+ // Stld:
34
+ SingleTokenDeposit: Buffer.from([215, 54, 137, 104, 219, 39, 164, 235]),
35
+ };
36
+
37
+ type DiscName = keyof typeof DISC;
38
+
39
+ export function parseCubicPoolEvents(logs: string[]): CubicPoolEvent[] {
40
+ const out: CubicPoolEvent[] = [];
41
+ for (const line of logs) {
42
+ const m = line.match(/^Program data:\s+(.+)$/);
43
+ if (!m) continue;
44
+ const buf = Buffer.from(m[1], "base64");
45
+ if (buf.length < 8) continue;
46
+ const disc = buf.slice(0, 8);
47
+ const payload = buf.slice(8);
48
+ const name = matchDiscriminator(disc);
49
+ if (!name) {
50
+ const current = decodeContractEvent(m[1]);
51
+ if (current) {
52
+ try { out.push(camelEvent(current)); }
53
+ catch (error) { out.push({ kind: "Unknown", name: current.kind, data: { error: String(error), payload: payload.toString("base64") } }); }
54
+ continue;
55
+ }
56
+ out.push({ kind: "Unknown", name: "unknown", data: { disc: disc.toString("base64"), payload: payload.toString("base64") } });
57
+ continue;
58
+ }
59
+ try {
60
+ const ev = decodeEvent(name, payload);
61
+ if (ev) out.push(ev);
62
+ } catch (e) {
63
+ out.push({
64
+ kind: "Unknown",
65
+ name,
66
+ data: { error: String(e), payload: payload.toString("base64") },
67
+ });
68
+ }
69
+ }
70
+ return out;
71
+ }
72
+
73
+ function matchDiscriminator(d: Buffer): DiscName | null {
74
+ for (const [name, bytes] of Object.entries(DISC)) {
75
+ if (bytes.equals(d)) return name as DiscName;
76
+ }
77
+ return null;
78
+ }
79
+
80
+ function decodeEvent(name: DiscName, buf: Buffer): CubicPoolEvent | null {
81
+ const r = new BorshReader(buf);
82
+ switch (name) {
83
+ case "Swap": {
84
+ // The deployed without-SF event ends with timestamp, surge_fee_amount.
85
+ // Optional trailing transfer-fee fields are retained only for decoding
86
+ // logs from other historical builds; they do not imply mint support.
87
+ const pool = r.pubkey();
88
+ const user = r.pubkey();
89
+ const tokenIn = r.pubkey();
90
+ const tokenOut = r.pubkey();
91
+ const amountIn = r.u64();
92
+ const amountOut = r.u64();
93
+ const feeAmount = r.u64();
94
+ const protocolFeeAmount = r.u64();
95
+ const timestamp = r.i64().toNumber();
96
+ const surgeFeeAmount = r.u64();
97
+ // Absent in the deployed without-SF ABI: expose zero for compatibility.
98
+ const transferFeeIn = r.remaining() >= 8 ? r.u64() : new BN(0);
99
+ const transferFeeOut = r.remaining() >= 8 ? r.u64() : new BN(0);
100
+ return {
101
+ kind: "Swap",
102
+ pool,
103
+ user,
104
+ tokenIn,
105
+ tokenOut,
106
+ amountIn,
107
+ amountOut,
108
+ feeAmount,
109
+ protocolFeeAmount,
110
+ surgeFeeAmount,
111
+ timestamp,
112
+ transferFeeIn,
113
+ transferFeeOut,
114
+ };
115
+ }
116
+ case "LiquidityAdded": {
117
+ const pool = r.pubkey();
118
+ const user = r.pubkey();
119
+ const tokenAmounts = r.vecU64();
120
+ const bptAmount = r.u64();
121
+ const timestamp = r.i64().toNumber();
122
+ return { kind: "LiquidityAdded", pool, user, tokenAmounts, bptAmount, timestamp };
123
+ }
124
+ case "LiquidityRemoved": {
125
+ const pool = r.pubkey();
126
+ const user = r.pubkey();
127
+ const bptAmount = r.u64();
128
+ const tokenAmounts = r.vecU64();
129
+ const timestamp = r.i64().toNumber();
130
+ return { kind: "LiquidityRemoved", pool, user, bptAmount, tokenAmounts, timestamp };
131
+ }
132
+ case "ProtocolFeesCollected": {
133
+ const pool = r.pubkey();
134
+ const authority = r.pubkey();
135
+ const tokenAmounts = r.vecU64();
136
+ const timestamp = r.i64().toNumber();
137
+ return { kind: "ProtocolFeesCollected", pool, authority, tokenAmounts, timestamp };
138
+ }
139
+ case "PoolInitialized": {
140
+ const pool = r.pubkey();
141
+ const config = r.pubkey();
142
+ const tokenCount = r.u8();
143
+ const bptMint = r.pubkey();
144
+ const timestamp = r.i64().toNumber();
145
+ // Appended: the effective banned-extensions bitmap the pool's tokens
146
+ // were vetted against at creation. Guarded for older logs.
147
+ const bannedExtensions = r.remaining() >= 8 ? r.u64() : new BN(0);
148
+ return { kind: "PoolInitialized", pool, config, tokenCount, bptMint, timestamp, bannedExtensions };
149
+ }
150
+ case "PoolEnabledUpdated": {
151
+ const pool = r.pubkey();
152
+ const authority = r.pubkey();
153
+ const oldValue = r.bool();
154
+ const newValue = r.bool();
155
+ const timestamp = r.i64().toNumber();
156
+ return { kind: "PoolEnabledUpdated", pool, authority, oldValue, newValue, timestamp };
157
+ }
158
+ case "SwapsEnabledUpdated": {
159
+ const pool = r.pubkey();
160
+ const authority = r.pubkey();
161
+ const oldValue = r.bool();
162
+ const newValue = r.bool();
163
+ const timestamp = r.i64().toNumber();
164
+ return { kind: "SwapsEnabledUpdated", pool, authority, oldValue, newValue, timestamp };
165
+ }
166
+ case "SingleTokenDeposit": {
167
+ // Verified against `SingleTokenDeposit` in
168
+ // src/idl/single_token_liquidity.json:
169
+ // helper, pool, user, token_in_index, amount_in, allocations,
170
+ // deposited_amounts, bpt_received, dust_refunded, timestamp
171
+ //
172
+ // Two fixes vs. the previous decoder:
173
+ // 1. There is NO `slippage_hundredths_bps: u32` field. The per-call
174
+ // slippage argument was removed from `deposit_single_token` (the
175
+ // single `minimum_bpt_amount` guard replaced it) and the event
176
+ // field went with it. Reading a phantom u32 here shifted every
177
+ // later field by 4 bytes.
178
+ // 2. `dust_refunded` is `Vec<u64>` (index-aligned with the pool's
179
+ // tokens), not a scalar `u64`.
180
+ const helper = r.pubkey();
181
+ const pool = r.pubkey();
182
+ const user = r.pubkey();
183
+ const tokenInIndex = r.u8();
184
+ const amountIn = r.u64();
185
+ const allocations = r.vecU64();
186
+ const depositedAmounts = r.vecU64();
187
+ const bptReceived = r.u64();
188
+ const dustRefunded = r.vecU64();
189
+ const timestamp = r.i64().toNumber();
190
+ return {
191
+ kind: "SingleTokenDeposit",
192
+ helper,
193
+ pool,
194
+ user,
195
+ tokenInIndex,
196
+ amountIn,
197
+ allocations,
198
+ depositedAmounts,
199
+ bptReceived,
200
+ dustRefunded,
201
+ timestamp,
202
+ };
203
+ }
204
+ case "PoolStateLog": {
205
+ const pool = r.pubkey();
206
+ const virtualBalances = r.vecU64();
207
+ const actualBalances = r.vecU64();
208
+ const protocolFeesOwed = r.vecU64();
209
+ const timestamp = r.i64().toNumber();
210
+ return { kind: "PoolStateLog", pool, virtualBalances, actualBalances, protocolFeesOwed, timestamp };
211
+ }
212
+ case "MaxSelloffWindowAdvanced": {
213
+ const pool = r.pubkey();
214
+ const tokenIndex = r.u8();
215
+ const effectiveSelloff = r.u64();
216
+ const maxSelloffCap = r.u64();
217
+ const vbSnapshot = r.u64();
218
+ const previousSelloff = r.u64();
219
+ const currentSelloff = r.u64();
220
+ const windowStartTimestamp = r.i64().toNumber();
221
+ const timestamp = r.i64().toNumber();
222
+ return {
223
+ kind: "MaxSelloffWindowAdvanced",
224
+ pool,
225
+ tokenIndex,
226
+ effectiveSelloff,
227
+ maxSelloffCap,
228
+ vbSnapshot,
229
+ previousSelloff,
230
+ currentSelloff,
231
+ windowStartTimestamp,
232
+ timestamp,
233
+ };
234
+ }
235
+ case "BannedExtensionsUpdated": {
236
+ const config = r.pubkey();
237
+ const authority = r.pubkey();
238
+ const oldValue = r.u64();
239
+ const newValue = r.u64();
240
+ const timestamp = r.i64().toNumber();
241
+ // Appended in v5.1 alongside `set_banned_extensions`' new
242
+ // `hard_banned_extensions` argument. Guarded for older logs.
243
+ const oldHardValue = r.remaining() >= 8 ? r.u64() : new BN(0);
244
+ const newHardValue = r.remaining() >= 8 ? r.u64() : new BN(0);
245
+ return {
246
+ kind: "BannedExtensionsUpdated",
247
+ config,
248
+ authority,
249
+ oldValue,
250
+ newValue,
251
+ timestamp,
252
+ oldHardValue,
253
+ newHardValue,
254
+ };
255
+ }
256
+ // Decode the remaining current events through the complete shipped ABI.
257
+ case "SwapFeeRateUpdated":
258
+ case "ProtocolFeeRateUpdated":
259
+ case "DebugLiquidityWithdrawn":
260
+ case "PoolInfo":
261
+ const current = decodeContractEvent(Buffer.concat([DISC[name], buf]).toString("base64"));
262
+ return current ? camelEvent(current) : { kind: "Unknown", name, data: { raw: buf.toString("base64") } };
263
+ }
264
+ }
265
+
266
+ void PublicKey; // keep import used by types above via type inference
267
+
268
+ function camelEvent(event: ContractEvent): AdditionalContractEvent {
269
+ const fields: Record<string, unknown> = { kind: event.kind };
270
+ for (const [key, value] of Object.entries(event.data)) {
271
+ const name = key.replace(/_([a-z])/g, (_, letter: string) => letter.toUpperCase());
272
+ fields[name] = key === "timestamp" && BN.isBN(value) ? value.toNumber() : value;
273
+ }
274
+ return fields as AdditionalContractEvent;
275
+ }
@@ -0,0 +1,5 @@
1
+ export * from "./poolAccount";
2
+ export * from "./mintAccount";
3
+ export * from "./events";
4
+ export * from "./borsh";
5
+ export * from "./contracts";
@@ -0,0 +1,44 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ import BN from "bn.js";
3
+ import { parseMintExtensions } from "../utils/extensions";
4
+
5
+ /**
6
+ * SPL Token (or Token-2022) Mint parser. Reads the 82-byte base layout and,
7
+ * for Token-2022 mints, the TLV extension list (discriminants only).
8
+ *
9
+ * Layout:
10
+ * 0..36 COption<Pubkey> mintAuthority (4 bytes tag + 32 bytes pubkey)
11
+ * 36..44 u64 supply
12
+ * 44..45 u8 decimals
13
+ * 45..46 u8 isInitialized
14
+ * 46..82 COption<Pubkey> freezeAuthority
15
+ */
16
+ export interface RawMintAccount {
17
+ mintAuthority: PublicKey | null;
18
+ supply: BN;
19
+ decimals: number;
20
+ isInitialized: boolean;
21
+ freezeAuthority: PublicKey | null;
22
+ /** Token-2022 extension discriminants; `[]` for classic SPL mints. */
23
+ extensions: number[];
24
+ }
25
+
26
+ export function decodeMintAccount(data: Buffer): RawMintAccount {
27
+ const extensions = parseMintExtensions(data);
28
+ const mintAuthTag = data.readUInt32LE(0);
29
+ if (mintAuthTag !== 0 && mintAuthTag !== 1) {
30
+ throw new Error(`decodeMintAccount: invalid mint-authority option ${mintAuthTag}`);
31
+ }
32
+ const mintAuthority =
33
+ mintAuthTag === 0 ? null : new PublicKey(data.slice(4, 36));
34
+ const supply = new BN(data.slice(36, 44), "le");
35
+ const decimals = data.readUInt8(44);
36
+ const isInitialized = data.readUInt8(45) === 1;
37
+ const freezeTag = data.readUInt32LE(46);
38
+ if (freezeTag !== 0 && freezeTag !== 1) {
39
+ throw new Error(`decodeMintAccount: invalid freeze-authority option ${freezeTag}`);
40
+ }
41
+ const freezeAuthority =
42
+ freezeTag === 0 ? null : new PublicKey(data.slice(50, 82));
43
+ return { mintAuthority, supply, decimals, isInitialized, freezeAuthority, extensions };
44
+ }
@@ -0,0 +1,372 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ import BN from "bn.js";
3
+
4
+ /**
5
+ * Raw binary layout of CubicPool **v5** (see
6
+ * `contracts/programs/cubic-pool/src/state/cubic_pool.rs`).
7
+ *
8
+ * v4 reorganised per-token data from six parallel arrays into a single
9
+ * `tokens: [TokenSlot; 10]` array (each slot = `AssetConfig` +
10
+ * `AssetDynamics`). For backwards compatibility with downstream
11
+ * consumers (`CubicPoolClient.sync()` and similar), this decoder still
12
+ * exposes the parallel-array shape, plus the new max-selloff and admin
13
+ * fields.
14
+ *
15
+ * v5.1 layout deltas (all carved out of `reserved`, total size unchanged
16
+ * at 1683 — every pre-existing field keeps its byte offset):
17
+ * - `AssetConfig.reserved[3]` → `variable_fee_slope_mid_pct: u16` +
18
+ * `variable_fee_kink_pct: u8`. Slot stays 88 bytes.
19
+ * - `CubicPool`: `range_manager_max_leverage_bps: u32` and
20
+ * `range_manager_min_leverage_bps: u32` appended after
21
+ * `banned_extensions`; trailing `reserved` shrank [u8;24] → [u8;16].
22
+ *
23
+ * Pools written by the pre-v5.1 program decode these new fields as `0`,
24
+ * which is the back-compatible reading (curve has no kink, leverage band
25
+ * disabled).
26
+ *
27
+ * We decode manually to avoid bundling an Anchor `Program` instance into
28
+ * the SDK — both frontend and backend consume the SDK, and pulling the
29
+ * full Anchor runtime is heavy.
30
+ */
31
+ export interface RawPoolAccount {
32
+ config: PublicKey;
33
+ bump: number;
34
+ tokenCount: number;
35
+ poolId: BN;
36
+ swapFeeRate: number;
37
+ protocolFeeRate: number;
38
+ createdAt: BN;
39
+ poolEnabled: boolean;
40
+ swapsEnabled: boolean;
41
+ poolAdmin: PublicKey;
42
+ pendingPoolAdmin: PublicKey;
43
+
44
+ rangeManager: PublicKey;
45
+ rangeManagerEnabled: boolean;
46
+ /** Percent-of-current-value cap, `PERCENT_SCALE` units (10_000 = 100%). */
47
+ rangeManagerMaxVbChangePct: number;
48
+ rangeManagerMaxWeightChangePct: number;
49
+ rangeManagerMinUpdateIntervalSecs: number;
50
+ rangeManagerLastUpdated: BN;
51
+ /**
52
+ * Absolute UPPER bound on `virtual_balance / actual_balance` the range
53
+ * manager may leave a token at, in basis points (10_000 = 1.0×).
54
+ * `0` ⇒ band disabled. Pools written before v5.1 read `0`.
55
+ *
56
+ * The per-update percentage caps *velocity*; this caps *distance*.
57
+ * Breaching it reverts with `RangeManagerLeverageBandExceeded` (6062).
58
+ */
59
+ rangeManagerMaxLeverageBps: number;
60
+ /**
61
+ * Absolute LOWER bound on `virtual_balance / actual_balance`, same units.
62
+ * `0` ⇒ floor disabled. Pools written before v5.1 read `0`.
63
+ */
64
+ rangeManagerMinLeverageBps: number;
65
+
66
+ // Per-token (length 10 each; values past `tokenCount` are zeroed).
67
+ tokenMints: PublicKey[];
68
+ tokenPrograms: PublicKey[];
69
+ normalizedWeights: BN[];
70
+ /**
71
+ * Max cumulative sell volume per window, as a percent of the token's
72
+ * virtual balance (`PERCENT_SCALE` units; 10_000 = 100%). `0` disables.
73
+ */
74
+ maxSelloffPct: number[];
75
+ maxSelloffPeriodLength: number[];
76
+ /** Variable surge-fee curve, per token (`PERCENT_SCALE` units). */
77
+ variableFeeThresholdPct: number[];
78
+ variableFeeSlopeLowPct: number[];
79
+ variableFeeSlopeHighPct: number[];
80
+ /**
81
+ * Surge fee at the KINK — the middle control point of the three-point
82
+ * curve (`PERCENT_SCALE` units). Between `variableFeeThresholdPct` and
83
+ * the kink the fee runs linearly from `variableFeeSlopeLowPct` to this
84
+ * value; beyond it, from this value to `variableFeeSlopeHighPct` at full
85
+ * window fill. Contract enforces `low <= mid <= high`.
86
+ *
87
+ * New in v5.1 (carved out of `AssetConfig.reserved`). Pools written by
88
+ * the older program read `0`.
89
+ */
90
+ variableFeeSlopeMidPct: number[];
91
+ /**
92
+ * Window fill at which the surge curve kinks, in WHOLE PERCENT (0..=100)
93
+ * — NOT `PERCENT_SCALE` units. `0` ⇒ no kink: the curve degenerates to a
94
+ * single straight line from `low` at the threshold to `high` at full fill,
95
+ * which is exactly how every pre-v5.1 pool decodes.
96
+ *
97
+ * New in v5.1 (carved out of `AssetConfig.reserved`).
98
+ */
99
+ variableFeeKinkPct: number[];
100
+ /**
101
+ * Per-token input kill switch. `false` ⇒ swaps with this token as INPUT
102
+ * revert (`TokenInactive`). Defaults to `true`.
103
+ */
104
+ isActive: boolean[];
105
+
106
+ virtualBalances: BN[];
107
+ actualBalances: BN[];
108
+ protocolFeesOwed: BN[];
109
+ previousSelloff: BN[];
110
+ currentSelloff: BN[];
111
+ windowStartTimestamp: BN[];
112
+ /** Virtual-balance snapshot taken when the max-selloff window opened. */
113
+ selloffVbSnapshot: BN[];
114
+
115
+ /**
116
+ * Per-pool Address Lookup Table. `PublicKey.default` means the pool's
117
+ * ALT has not been provisioned yet (`initialize_pool_alt` not called).
118
+ * SDK uses this to choose between v0 (with ALT) and legacy tx-building
119
+ * paths.
120
+ */
121
+ lookupTable: PublicKey;
122
+
123
+ /**
124
+ * Effective Token-2022 banned-extensions bitmap this pool's tokens were
125
+ * vetted against at creation (creator override, or the config default).
126
+ * `0` ⇒ no extensions banned. Pre-upgrade pools read `0`. A permissive
127
+ * value is a rug-risk — surface it before depositing.
128
+ */
129
+ bannedExtensions: BN;
130
+ /** The exact trailing ABI padding, preserved for complete state inspection. */
131
+ reserved: Buffer;
132
+ }
133
+
134
+ /** 8-byte anchor discriminator for CubicPool. */
135
+ export const POOL_DISCRIMINATOR_LEN = 8;
136
+ const POOL_DISCRIMINATOR = Buffer.from([137, 210, 42, 22, 209, 156, 43, 78]);
137
+ const MAX_TOKENS = 10;
138
+ /**
139
+ * Total on-chain size of a v4/v5 CubicPool (includes the 8-byte
140
+ * discriminator). v5.1 carved its new fields out of `reserved`, so the
141
+ * size is deliberately identical to v4 — size alone cannot tell the two
142
+ * apart. Decoding interprets the current layout; it does not prove an older
143
+ * account has been migrated.
144
+ */
145
+ export const POOL_V4_LEN = 1683;
146
+ /** Alias for {@link POOL_V4_LEN}; the layout is shared by v4 and v5. */
147
+ export const POOL_LEN = POOL_V4_LEN;
148
+ /** Per-token `TokenSlot` = `AssetConfig` (88) + `AssetDynamics` (56). */
149
+ const ASSET_CONFIG_LEN = 88;
150
+ const ASSET_DYNAMICS_LEN = 56;
151
+ const TOKEN_SLOT_LEN = ASSET_CONFIG_LEN + ASSET_DYNAMICS_LEN;
152
+ /** Pre-v4 size — legacy accounts are unsupported by the current v4-to-v5 migration. */
153
+ export const POOL_V3_LEN = 1154;
154
+
155
+ export function decodePoolAccount(data: Buffer): RawPoolAccount {
156
+ if (data.length === POOL_V3_LEN) {
157
+ throw new Error(
158
+ `decodePoolAccount: account is at v3 size (${POOL_V3_LEN}). ` +
159
+ `Legacy v3 accounts require a separate migration; migrate_to_v5 supports only 1683-byte v4 accounts.`,
160
+ );
161
+ }
162
+ if (data.length !== POOL_V4_LEN) {
163
+ throw new Error(
164
+ `decodePoolAccount: unexpected data length ${data.length} ` +
165
+ `(expected ${POOL_V4_LEN} for v4).`,
166
+ );
167
+ }
168
+ if (!data.subarray(0, POOL_DISCRIMINATOR_LEN).equals(POOL_DISCRIMINATOR)) {
169
+ throw new Error("decodePoolAccount: invalid CubicPool discriminator");
170
+ }
171
+
172
+ let off = POOL_DISCRIMINATOR_LEN;
173
+
174
+ const config = readPubkey(data, off);
175
+ off += 32;
176
+ const bump = data.readUInt8(off);
177
+ off += 1;
178
+ const tokenCount = data.readUInt8(off);
179
+ off += 1;
180
+ if (tokenCount > MAX_TOKENS) {
181
+ throw new Error(`decodePoolAccount: token_count ${tokenCount} exceeds ${MAX_TOKENS}`);
182
+ }
183
+ const poolId = readU64LE(data, off);
184
+ off += 8;
185
+ const swapFeeRate = data.readUInt32LE(off);
186
+ off += 4;
187
+ const protocolFeeRate = data.readUInt16LE(off);
188
+ off += 2;
189
+ const createdAt = readI64LE(data, off);
190
+ off += 8;
191
+ const poolEnabled = readBool(data, off);
192
+ off += 1;
193
+ const swapsEnabled = readBool(data, off);
194
+ off += 1;
195
+ const poolAdmin = readPubkey(data, off);
196
+ off += 32;
197
+ const pendingPoolAdmin = readPubkey(data, off);
198
+ off += 32;
199
+
200
+ const rangeManager = readPubkey(data, off);
201
+ off += 32;
202
+ const rangeManagerEnabled = readBool(data, off);
203
+ off += 1;
204
+ const rangeManagerMaxVbChangePct = data.readUInt16LE(off);
205
+ off += 2;
206
+ const rangeManagerMaxWeightChangePct = data.readUInt16LE(off);
207
+ off += 2;
208
+ const rangeManagerMinUpdateIntervalSecs = data.readUInt32LE(off);
209
+ off += 4;
210
+ const rangeManagerLastUpdated = readI64LE(data, off);
211
+ off += 8;
212
+
213
+ // Per-token AoS — 10 slots, each 144 bytes.
214
+ const tokenMints: PublicKey[] = [];
215
+ const tokenPrograms: PublicKey[] = [];
216
+ const normalizedWeights: BN[] = [];
217
+ const maxSelloffPct: number[] = [];
218
+ const maxSelloffPeriodLength: number[] = [];
219
+ const variableFeeThresholdPct: number[] = [];
220
+ const variableFeeSlopeLowPct: number[] = [];
221
+ const variableFeeSlopeHighPct: number[] = [];
222
+ const variableFeeSlopeMidPct: number[] = [];
223
+ const variableFeeKinkPct: number[] = [];
224
+ const isActive: boolean[] = [];
225
+ const virtualBalances: BN[] = [];
226
+ const actualBalances: BN[] = [];
227
+ const protocolFeesOwed: BN[] = [];
228
+ const previousSelloff: BN[] = [];
229
+ const currentSelloff: BN[] = [];
230
+ const windowStartTimestamp: BN[] = [];
231
+ const selloffVbSnapshot: BN[] = [];
232
+
233
+ for (let i = 0; i < MAX_TOKENS; i++) {
234
+ const slotStart = off;
235
+
236
+ // AssetConfig — 88 bytes
237
+ tokenMints.push(readPubkey(data, off));
238
+ off += 32;
239
+ tokenPrograms.push(readPubkey(data, off));
240
+ off += 32;
241
+ normalizedWeights.push(readU64LE(data, off));
242
+ off += 8;
243
+ maxSelloffPct.push(data.readUInt16LE(off));
244
+ off += 2;
245
+ maxSelloffPeriodLength.push(data.readUInt32LE(off));
246
+ off += 4;
247
+ variableFeeThresholdPct.push(data.readUInt16LE(off));
248
+ off += 2;
249
+ variableFeeSlopeLowPct.push(data.readUInt16LE(off));
250
+ off += 2;
251
+ variableFeeSlopeHighPct.push(data.readUInt16LE(off));
252
+ off += 2;
253
+ isActive.push(readBool(data, off));
254
+ off += 1;
255
+ // v5.1: the trailing `AssetConfig.reserved[3]` became these two fields.
256
+ // Byte-for-byte replacement — the slot is still 88 bytes and every
257
+ // preceding field keeps its offset, so a v4 account decodes as 0/0.
258
+ variableFeeSlopeMidPct.push(data.readUInt16LE(off));
259
+ off += 2;
260
+ variableFeeKinkPct.push(data.readUInt8(off));
261
+ off += 1;
262
+
263
+ // AssetDynamics — 56 bytes
264
+ virtualBalances.push(readU64LE(data, off));
265
+ off += 8;
266
+ actualBalances.push(readU64LE(data, off));
267
+ off += 8;
268
+ protocolFeesOwed.push(readU64LE(data, off));
269
+ off += 8;
270
+ previousSelloff.push(readU64LE(data, off));
271
+ off += 8;
272
+ currentSelloff.push(readU64LE(data, off));
273
+ off += 8;
274
+ windowStartTimestamp.push(readI64LE(data, off));
275
+ off += 8;
276
+ selloffVbSnapshot.push(readU64LE(data, off));
277
+ off += 8; // AssetDynamics.selloff_vb_snapshot
278
+
279
+ // Fail loudly rather than silently sliding every later field if the
280
+ // slot layout ever drifts again.
281
+ if (off - slotStart !== TOKEN_SLOT_LEN) {
282
+ throw new Error(
283
+ `decodePoolAccount: TokenSlot ${i} consumed ${off - slotStart} bytes, expected ${TOKEN_SLOT_LEN}`,
284
+ );
285
+ }
286
+ }
287
+
288
+ const lookupTable = readPubkey(data, off);
289
+ off += 32;
290
+
291
+ // Effective per-pool Token-2022 banned-extensions bitmap (carved out of
292
+ // the old reserved[32]; pre-upgrade pools read 0).
293
+ const bannedExtensions = readU64LE(data, off);
294
+ off += 8;
295
+
296
+ // v5.1: two u32s carved out of the trailing reserved blob, which shrank
297
+ // [u8;24] → [u8;16]. Pre-v5.1 pools read 0 (band disabled).
298
+ const rangeManagerMaxLeverageBps = data.readUInt32LE(off);
299
+ off += 4;
300
+ const rangeManagerMinLeverageBps = data.readUInt32LE(off);
301
+ off += 4;
302
+
303
+ // Preserve the trailing ABI padding after checking its exact offset.
304
+ if (off + 16 !== POOL_V4_LEN) {
305
+ throw new Error(
306
+ `decodePoolAccount: consumed ${off} bytes before reserved[16], ` +
307
+ `expected ${POOL_V4_LEN - 16}. Layout drift — regenerate this decoder.`,
308
+ );
309
+ }
310
+ const reserved = Buffer.from(data.subarray(off, off + 16));
311
+
312
+ return {
313
+ config,
314
+ bump,
315
+ tokenCount,
316
+ poolId,
317
+ swapFeeRate,
318
+ protocolFeeRate,
319
+ createdAt,
320
+ poolEnabled,
321
+ swapsEnabled,
322
+ poolAdmin,
323
+ pendingPoolAdmin,
324
+ rangeManager,
325
+ rangeManagerEnabled,
326
+ rangeManagerMaxVbChangePct,
327
+ rangeManagerMaxWeightChangePct,
328
+ rangeManagerMinUpdateIntervalSecs,
329
+ rangeManagerLastUpdated,
330
+ rangeManagerMaxLeverageBps,
331
+ rangeManagerMinLeverageBps,
332
+ tokenMints,
333
+ tokenPrograms,
334
+ normalizedWeights,
335
+ maxSelloffPct,
336
+ maxSelloffPeriodLength,
337
+ variableFeeThresholdPct,
338
+ variableFeeSlopeLowPct,
339
+ variableFeeSlopeHighPct,
340
+ variableFeeSlopeMidPct,
341
+ variableFeeKinkPct,
342
+ isActive,
343
+ virtualBalances,
344
+ actualBalances,
345
+ protocolFeesOwed,
346
+ previousSelloff,
347
+ currentSelloff,
348
+ windowStartTimestamp,
349
+ selloffVbSnapshot,
350
+ lookupTable,
351
+ bannedExtensions,
352
+ reserved,
353
+ };
354
+ }
355
+
356
+ function readPubkey(data: Buffer, off: number): PublicKey {
357
+ return new PublicKey(data.slice(off, off + 32));
358
+ }
359
+
360
+ function readU64LE(data: Buffer, off: number): BN {
361
+ return new BN(data.slice(off, off + 8), "le");
362
+ }
363
+
364
+ function readI64LE(data: Buffer, off: number): BN {
365
+ return new BN(data.subarray(off, off + 8), "le").fromTwos(64);
366
+ }
367
+
368
+ function readBool(data: Buffer, off: number): boolean {
369
+ const value = data.readUInt8(off);
370
+ if (value > 1) throw new Error(`decodePoolAccount: invalid Borsh bool ${value} at offset ${off}`);
371
+ return value === 1;
372
+ }