@cubee_ee/sdk 0.1.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 (179) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +110 -0
  3. package/dist/clients/AdminClient.d.ts +61 -0
  4. package/dist/clients/AdminClient.d.ts.map +1 -0
  5. package/dist/clients/AdminClient.js +196 -0
  6. package/dist/clients/AdminClient.js.map +1 -0
  7. package/dist/clients/CubeBackendClient.d.ts +67 -0
  8. package/dist/clients/CubeBackendClient.d.ts.map +1 -0
  9. package/dist/clients/CubeBackendClient.js +126 -0
  10. package/dist/clients/CubeBackendClient.js.map +1 -0
  11. package/dist/clients/CubicPoolClient.d.ts +73 -0
  12. package/dist/clients/CubicPoolClient.d.ts.map +1 -0
  13. package/dist/clients/CubicPoolClient.js +425 -0
  14. package/dist/clients/CubicPoolClient.js.map +1 -0
  15. package/dist/clients/PoolFactoryClient.d.ts +45 -0
  16. package/dist/clients/PoolFactoryClient.d.ts.map +1 -0
  17. package/dist/clients/PoolFactoryClient.js +83 -0
  18. package/dist/clients/PoolFactoryClient.js.map +1 -0
  19. package/dist/clients/RpcClient.d.ts +28 -0
  20. package/dist/clients/RpcClient.d.ts.map +1 -0
  21. package/dist/clients/RpcClient.js +58 -0
  22. package/dist/clients/RpcClient.js.map +1 -0
  23. package/dist/clients/SingleTokenDepositClient.d.ts +45 -0
  24. package/dist/clients/SingleTokenDepositClient.d.ts.map +1 -0
  25. package/dist/clients/SingleTokenDepositClient.js +63 -0
  26. package/dist/clients/SingleTokenDepositClient.js.map +1 -0
  27. package/dist/clients/index.d.ts +8 -0
  28. package/dist/clients/index.d.ts.map +1 -0
  29. package/dist/clients/index.js +24 -0
  30. package/dist/clients/index.js.map +1 -0
  31. package/dist/clients/tx-builders.d.ts +32 -0
  32. package/dist/clients/tx-builders.d.ts.map +1 -0
  33. package/dist/clients/tx-builders.js +398 -0
  34. package/dist/clients/tx-builders.js.map +1 -0
  35. package/dist/config/index.d.ts +60 -0
  36. package/dist/config/index.d.ts.map +1 -0
  37. package/dist/config/index.js +59 -0
  38. package/dist/config/index.js.map +1 -0
  39. package/dist/config/networks.d.ts +10 -0
  40. package/dist/config/networks.d.ts.map +1 -0
  41. package/dist/config/networks.js +31 -0
  42. package/dist/config/networks.js.map +1 -0
  43. package/dist/config/tokens.d.ts +19 -0
  44. package/dist/config/tokens.d.ts.map +1 -0
  45. package/dist/config/tokens.js +43 -0
  46. package/dist/config/tokens.js.map +1 -0
  47. package/dist/idl/cubic_pool.json +2497 -0
  48. package/dist/idl/index.d.ts +975 -0
  49. package/dist/idl/index.d.ts.map +1 -0
  50. package/dist/idl/index.js +18 -0
  51. package/dist/idl/index.js.map +1 -0
  52. package/dist/idl/protocol_admin.json +1816 -0
  53. package/dist/idl/single_token_liquidity.json +745 -0
  54. package/dist/index.d.ts +48 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +136 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/math/cubicMath.d.ts +40 -0
  59. package/dist/math/cubicMath.d.ts.map +1 -0
  60. package/dist/math/cubicMath.js +75 -0
  61. package/dist/math/cubicMath.js.map +1 -0
  62. package/dist/math/fixedPoint.d.ts +14 -0
  63. package/dist/math/fixedPoint.d.ts.map +1 -0
  64. package/dist/math/fixedPoint.js +46 -0
  65. package/dist/math/fixedPoint.js.map +1 -0
  66. package/dist/math/index.d.ts +7 -0
  67. package/dist/math/index.d.ts.map +1 -0
  68. package/dist/math/index.js +23 -0
  69. package/dist/math/index.js.map +1 -0
  70. package/dist/math/logExp.d.ts +15 -0
  71. package/dist/math/logExp.d.ts.map +1 -0
  72. package/dist/math/logExp.js +91 -0
  73. package/dist/math/logExp.js.map +1 -0
  74. package/dist/math/singleToken.d.ts +53 -0
  75. package/dist/math/singleToken.d.ts.map +1 -0
  76. package/dist/math/singleToken.js +185 -0
  77. package/dist/math/singleToken.js.map +1 -0
  78. package/dist/math/slippage.d.ts +24 -0
  79. package/dist/math/slippage.d.ts.map +1 -0
  80. package/dist/math/slippage.js +54 -0
  81. package/dist/math/slippage.js.map +1 -0
  82. package/dist/math/weightedMath.d.ts +18 -0
  83. package/dist/math/weightedMath.d.ts.map +1 -0
  84. package/dist/math/weightedMath.js +45 -0
  85. package/dist/math/weightedMath.js.map +1 -0
  86. package/dist/parsers/borsh.d.ts +24 -0
  87. package/dist/parsers/borsh.d.ts.map +1 -0
  88. package/dist/parsers/borsh.js +80 -0
  89. package/dist/parsers/borsh.js.map +1 -0
  90. package/dist/parsers/events.d.ts +3 -0
  91. package/dist/parsers/events.d.ts.map +1 -0
  92. package/dist/parsers/events.js +172 -0
  93. package/dist/parsers/events.js.map +1 -0
  94. package/dist/parsers/index.d.ts +5 -0
  95. package/dist/parsers/index.d.ts.map +1 -0
  96. package/dist/parsers/index.js +21 -0
  97. package/dist/parsers/index.js.map +1 -0
  98. package/dist/parsers/mintAccount.d.ts +22 -0
  99. package/dist/parsers/mintAccount.d.ts.map +1 -0
  100. package/dist/parsers/mintAccount.js +22 -0
  101. package/dist/parsers/mintAccount.js.map +1 -0
  102. package/dist/parsers/poolAccount.d.ts +32 -0
  103. package/dist/parsers/poolAccount.d.ts.map +1 -0
  104. package/dist/parsers/poolAccount.js +88 -0
  105. package/dist/parsers/poolAccount.js.map +1 -0
  106. package/dist/types/events.d.ts +82 -0
  107. package/dist/types/events.d.ts.map +1 -0
  108. package/dist/types/events.js +3 -0
  109. package/dist/types/events.js.map +1 -0
  110. package/dist/types/index.d.ts +5 -0
  111. package/dist/types/index.d.ts.map +1 -0
  112. package/dist/types/index.js +21 -0
  113. package/dist/types/index.js.map +1 -0
  114. package/dist/types/pool.d.ts +66 -0
  115. package/dist/types/pool.d.ts.map +1 -0
  116. package/dist/types/pool.js +3 -0
  117. package/dist/types/pool.js.map +1 -0
  118. package/dist/types/result.d.ts +23 -0
  119. package/dist/types/result.d.ts.map +1 -0
  120. package/dist/types/result.js +11 -0
  121. package/dist/types/result.js.map +1 -0
  122. package/dist/types/tx.d.ts +80 -0
  123. package/dist/types/tx.d.ts.map +1 -0
  124. package/dist/types/tx.js +3 -0
  125. package/dist/types/tx.js.map +1 -0
  126. package/dist/utils/errors.d.ts +8 -0
  127. package/dist/utils/errors.d.ts.map +1 -0
  128. package/dist/utils/errors.js +83 -0
  129. package/dist/utils/errors.js.map +1 -0
  130. package/dist/utils/index.d.ts +4 -0
  131. package/dist/utils/index.d.ts.map +1 -0
  132. package/dist/utils/index.js +20 -0
  133. package/dist/utils/index.js.map +1 -0
  134. package/dist/utils/pda.d.ts +8 -0
  135. package/dist/utils/pda.d.ts.map +1 -0
  136. package/dist/utils/pda.js +27 -0
  137. package/dist/utils/pda.js.map +1 -0
  138. package/dist/utils/retry.d.ts +22 -0
  139. package/dist/utils/retry.d.ts.map +1 -0
  140. package/dist/utils/retry.js +62 -0
  141. package/dist/utils/retry.js.map +1 -0
  142. package/package.json +54 -0
  143. package/src/clients/AdminClient.ts +254 -0
  144. package/src/clients/CubeBackendClient.ts +193 -0
  145. package/src/clients/CubicPoolClient.ts +492 -0
  146. package/src/clients/PoolFactoryClient.ts +102 -0
  147. package/src/clients/RpcClient.ts +78 -0
  148. package/src/clients/SingleTokenDepositClient.ts +91 -0
  149. package/src/clients/index.ts +7 -0
  150. package/src/clients/tx-builders.ts +538 -0
  151. package/src/config/index.ts +82 -0
  152. package/src/config/networks.ts +49 -0
  153. package/src/config/tokens.ts +52 -0
  154. package/src/idl/cubic_pool.json +2497 -0
  155. package/src/idl/index.ts +13 -0
  156. package/src/idl/protocol_admin.json +1816 -0
  157. package/src/idl/single_token_liquidity.json +745 -0
  158. package/src/index.ts +154 -0
  159. package/src/math/cubicMath.ts +89 -0
  160. package/src/math/fixedPoint.ts +39 -0
  161. package/src/math/index.ts +6 -0
  162. package/src/math/logExp.ts +82 -0
  163. package/src/math/singleToken.ts +250 -0
  164. package/src/math/slippage.ts +49 -0
  165. package/src/math/weightedMath.ts +48 -0
  166. package/src/parsers/borsh.ts +80 -0
  167. package/src/parsers/events.ts +172 -0
  168. package/src/parsers/index.ts +4 -0
  169. package/src/parsers/mintAccount.ts +37 -0
  170. package/src/parsers/poolAccount.ts +113 -0
  171. package/src/types/events.ts +100 -0
  172. package/src/types/index.ts +4 -0
  173. package/src/types/pool.ts +64 -0
  174. package/src/types/result.ts +45 -0
  175. package/src/types/tx.ts +87 -0
  176. package/src/utils/errors.ts +78 -0
  177. package/src/utils/index.ts +3 -0
  178. package/src/utils/pda.ts +58 -0
  179. package/src/utils/retry.ts +85 -0
@@ -0,0 +1,538 @@
1
+ import {
2
+ AccountMeta,
3
+ ComputeBudgetProgram,
4
+ PublicKey,
5
+ SystemProgram,
6
+ TransactionInstruction,
7
+ } from "@solana/web3.js";
8
+ import {
9
+ ASSOCIATED_TOKEN_PROGRAM_ID,
10
+ TOKEN_PROGRAM_ID,
11
+ createAssociatedTokenAccountIdempotentInstruction,
12
+ } from "@solana/spl-token";
13
+ import BN from "bn.js";
14
+ import { CubeConfig } from "../config";
15
+ import { PoolInfo } from "../types/pool";
16
+ import {
17
+ AddLiquidityParams,
18
+ BuiltTx,
19
+ DeployPoolParams,
20
+ RemoveLiquidityParams,
21
+ SingleTokenDepositParams,
22
+ SwapParams,
23
+ } from "../types/tx";
24
+ import { deriveAta, deriveBptMint, deriveHelperPda } from "../utils/pda";
25
+
26
+ /**
27
+ * Low-level transaction builders. Emit raw `TransactionInstruction`s suitable
28
+ * for combination with others (e.g. versioned transactions with ALTs) in a
29
+ * single tx. Higher-level convenience lives on `CubicPoolClient`.
30
+ */
31
+
32
+ const CUBIC_POOL_DISC = {
33
+ swap: computeDiscriminator("swap"),
34
+ addLiquidity: computeDiscriminator("add_liquidity"),
35
+ removeLiquidity: computeDiscriminator("remove_liquidity"),
36
+ initializeCubicPool: computeDiscriminator("initialize_cubic_pool"),
37
+ };
38
+
39
+ const STLD_DISC = {
40
+ depositSingleToken: computeDiscriminator("deposit_single_token"),
41
+ };
42
+
43
+ /**
44
+ * Anchor discriminator: sha256("global:<ix_name>")[0..8]. The SDK
45
+ * pre-computes only the cases it needs.
46
+ *
47
+ * Pre-computed values (confirmed against `src/idl/*.json`):
48
+ * swap → f8c69e91e17587c8
49
+ * add_liquidity → b59d59438fb63448
50
+ * remove_liquidity → 5055d14818ceb16c
51
+ * initialize_cubic_pool → d79474cf79686f83
52
+ * deposit_single_token → a688a62fc7c056a9
53
+ *
54
+ * If you change an instruction name in Rust, regenerate by reading the
55
+ * target/idl/*.json discriminator field.
56
+ */
57
+ function computeDiscriminator(ixName: string): Buffer {
58
+ // Fallback: Anchor exposes discriminators in the IDL. We ship the known
59
+ // ones as a static map; callers passing unknown names error clearly.
60
+ const KNOWN: Record<string, string> = {
61
+ swap: "f8c69e91e17587c8",
62
+ add_liquidity: "b59d59438fb63448",
63
+ remove_liquidity: "5055d14818ceb16c",
64
+ initialize_cubic_pool: "d79474cf79686f83",
65
+ deposit_single_token: "a688a62fc7c056a9",
66
+ initialize_config: "d07f1501c2bec446",
67
+ };
68
+ const hex = KNOWN[ixName];
69
+ if (!hex) throw new Error(`tx-builders: unknown discriminator for "${ixName}"`);
70
+ return Buffer.from(hex, "hex");
71
+ }
72
+
73
+ function requirePositiveMinimumBpt(minimumBptAmount: BN | undefined, ixName: string): BN {
74
+ if (!minimumBptAmount || minimumBptAmount.lte(new BN(0))) {
75
+ throw new Error(`${ixName}: minimumBptAmount must be positive`);
76
+ }
77
+ return minimumBptAmount;
78
+ }
79
+
80
+ // ============================================================
81
+ // Swap
82
+ // ============================================================
83
+
84
+ export function buildSwapIx(
85
+ cfg: CubeConfig,
86
+ pool: PoolInfo,
87
+ params: SwapParams & { minAmountOut: BN }
88
+ ): TransactionInstruction {
89
+ const inTok = pool.tokens[params.tokenInIndex];
90
+ const outTok = pool.tokens[params.tokenOutIndex];
91
+
92
+ const userTokenIn = deriveAta(params.user, inTok.mint, inTok.tokenProgram);
93
+ const userTokenOut = deriveAta(params.user, outTok.mint, outTok.tokenProgram);
94
+
95
+ const data = Buffer.concat([
96
+ CUBIC_POOL_DISC.swap,
97
+ encodeU64(params.amountIn),
98
+ encodeU64(params.minAmountOut),
99
+ encodeU8(params.tokenInIndex),
100
+ encodeU8(params.tokenOutIndex),
101
+ ]);
102
+
103
+ const keys: AccountMeta[] = [
104
+ { pubkey: pool.address, isSigner: false, isWritable: true },
105
+ { pubkey: inTok.mint, isSigner: false, isWritable: false },
106
+ { pubkey: outTok.mint, isSigner: false, isWritable: false },
107
+ { pubkey: userTokenIn, isSigner: false, isWritable: true },
108
+ { pubkey: userTokenOut, isSigner: false, isWritable: true },
109
+ { pubkey: inTok.vault, isSigner: false, isWritable: true },
110
+ { pubkey: outTok.vault, isSigner: false, isWritable: true },
111
+ { pubkey: params.user, isSigner: true, isWritable: true },
112
+ { pubkey: inTok.tokenProgram, isSigner: false, isWritable: false },
113
+ { pubkey: outTok.tokenProgram, isSigner: false, isWritable: false },
114
+ ];
115
+
116
+ return new TransactionInstruction({
117
+ programId: cfg.programs.cubicPool,
118
+ keys,
119
+ data,
120
+ });
121
+ }
122
+
123
+ export function buildSwapTx(
124
+ cfg: CubeConfig,
125
+ pool: PoolInfo,
126
+ params: SwapParams & { minAmountOut: BN }
127
+ ): BuiltTx {
128
+ return {
129
+ instructions: [
130
+ ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }),
131
+ buildSwapIx(cfg, pool, params),
132
+ ],
133
+ suggestedCuLimit: 400_000,
134
+ };
135
+ }
136
+
137
+ // ============================================================
138
+ // Add liquidity (proportional)
139
+ // ============================================================
140
+
141
+ export function buildAddLiquidityIx(
142
+ cfg: CubeConfig,
143
+ pool: PoolInfo,
144
+ params: AddLiquidityParams
145
+ ): TransactionInstruction {
146
+ const userBpt = deriveAta(params.user, pool.bptMint, TOKEN_PROGRAM_ID);
147
+ const minBpt = requirePositiveMinimumBpt(params.minimumBptAmount, "add_liquidity");
148
+
149
+ const data = Buffer.concat([
150
+ CUBIC_POOL_DISC.addLiquidity,
151
+ encodeVecU64(params.tokenAmounts),
152
+ encodeU64(minBpt),
153
+ ]);
154
+
155
+ // remaining_accounts layout:
156
+ // [user_token_i, vault_i] × N, mint_i × N, token_program_i × N
157
+ const remaining: AccountMeta[] = [];
158
+ for (let i = 0; i < pool.tokenCount; i++) {
159
+ const t = pool.tokens[i];
160
+ const userAta = deriveAta(params.user, t.mint, t.tokenProgram);
161
+ remaining.push({ pubkey: userAta, isSigner: false, isWritable: true });
162
+ remaining.push({ pubkey: t.vault, isSigner: false, isWritable: true });
163
+ }
164
+ for (let i = 0; i < pool.tokenCount; i++) {
165
+ remaining.push({ pubkey: pool.tokens[i].mint, isSigner: false, isWritable: false });
166
+ }
167
+ for (let i = 0; i < pool.tokenCount; i++) {
168
+ remaining.push({ pubkey: pool.tokens[i].tokenProgram, isSigner: false, isWritable: false });
169
+ }
170
+
171
+ const keys: AccountMeta[] = [
172
+ { pubkey: pool.address, isSigner: false, isWritable: true },
173
+ { pubkey: pool.bptMint, isSigner: false, isWritable: true },
174
+ { pubkey: userBpt, isSigner: false, isWritable: true },
175
+ { pubkey: params.user, isSigner: true, isWritable: true },
176
+ { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
177
+ ...remaining,
178
+ ];
179
+
180
+ return new TransactionInstruction({
181
+ programId: cfg.programs.cubicPool,
182
+ keys,
183
+ data,
184
+ });
185
+ }
186
+
187
+ export function buildAddLiquidityTx(
188
+ cfg: CubeConfig,
189
+ pool: PoolInfo,
190
+ params: AddLiquidityParams
191
+ ): BuiltTx {
192
+ const userBpt = deriveAta(params.user, pool.bptMint, TOKEN_PROGRAM_ID);
193
+ return {
194
+ instructions: [
195
+ ComputeBudgetProgram.setComputeUnitLimit({ units: 600_000 }),
196
+ createAssociatedTokenAccountIdempotentInstruction(
197
+ params.user,
198
+ userBpt,
199
+ params.user,
200
+ pool.bptMint,
201
+ TOKEN_PROGRAM_ID
202
+ ),
203
+ buildAddLiquidityIx(cfg, pool, params),
204
+ ],
205
+ suggestedCuLimit: 600_000,
206
+ };
207
+ }
208
+
209
+ // ============================================================
210
+ // Remove liquidity
211
+ // ============================================================
212
+
213
+ export function buildRemoveLiquidityIx(
214
+ cfg: CubeConfig,
215
+ pool: PoolInfo,
216
+ params: RemoveLiquidityParams
217
+ ): TransactionInstruction {
218
+ const userBpt = deriveAta(params.user, pool.bptMint, TOKEN_PROGRAM_ID);
219
+ const mins = params.minimumTokenAmounts ?? pool.tokens.map(() => new BN(0));
220
+
221
+ const data = Buffer.concat([
222
+ CUBIC_POOL_DISC.removeLiquidity,
223
+ encodeU64(params.bptAmount),
224
+ encodeVecU64(mins),
225
+ ]);
226
+
227
+ // remove_liquidity remaining_accounts format:
228
+ // [vault_i, user_token_i] × N, mint_i × N, token_program_i × N
229
+ const remaining: AccountMeta[] = [];
230
+ for (let i = 0; i < pool.tokenCount; i++) {
231
+ const t = pool.tokens[i];
232
+ const userAta = deriveAta(params.user, t.mint, t.tokenProgram);
233
+ remaining.push({ pubkey: t.vault, isSigner: false, isWritable: true });
234
+ remaining.push({ pubkey: userAta, isSigner: false, isWritable: true });
235
+ }
236
+ for (let i = 0; i < pool.tokenCount; i++) {
237
+ remaining.push({ pubkey: pool.tokens[i].mint, isSigner: false, isWritable: false });
238
+ }
239
+ for (let i = 0; i < pool.tokenCount; i++) {
240
+ remaining.push({ pubkey: pool.tokens[i].tokenProgram, isSigner: false, isWritable: false });
241
+ }
242
+
243
+ const keys: AccountMeta[] = [
244
+ { pubkey: pool.address, isSigner: false, isWritable: true },
245
+ { pubkey: pool.bptMint, isSigner: false, isWritable: true },
246
+ { pubkey: userBpt, isSigner: false, isWritable: true },
247
+ { pubkey: params.user, isSigner: true, isWritable: true },
248
+ { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
249
+ ...remaining,
250
+ ];
251
+
252
+ return new TransactionInstruction({
253
+ programId: cfg.programs.cubicPool,
254
+ keys,
255
+ data,
256
+ });
257
+ }
258
+
259
+ export function buildRemoveLiquidityTx(
260
+ cfg: CubeConfig,
261
+ pool: PoolInfo,
262
+ params: RemoveLiquidityParams
263
+ ): BuiltTx {
264
+ const ixs: TransactionInstruction[] = [
265
+ ComputeBudgetProgram.setComputeUnitLimit({ units: cfg.defaults.cuLimit }),
266
+ ];
267
+
268
+ // The contract transfers every pool token to the user's ATA. Include
269
+ // idempotent creates so a proportional burn works even when the user never
270
+ // held one of the receive tokens before.
271
+ for (const t of pool.tokens) {
272
+ const userAta = deriveAta(params.user, t.mint, t.tokenProgram);
273
+ ixs.push(
274
+ createAssociatedTokenAccountIdempotentInstruction(
275
+ params.user,
276
+ userAta,
277
+ params.user,
278
+ t.mint,
279
+ t.tokenProgram
280
+ )
281
+ );
282
+ }
283
+
284
+ ixs.push(buildRemoveLiquidityIx(cfg, pool, params));
285
+
286
+ return {
287
+ instructions: ixs,
288
+ suggestedCuLimit: cfg.defaults.cuLimit,
289
+ };
290
+ }
291
+
292
+ // ============================================================
293
+ // Single-token deposit (helper program)
294
+ // ============================================================
295
+
296
+ export function buildSingleTokenDepositIx(
297
+ cfg: CubeConfig,
298
+ pool: PoolInfo,
299
+ params: SingleTokenDepositParams
300
+ ): TransactionInstruction {
301
+ const slip = params.slippageHundredthsBps ?? cfg.defaults.slippageHundredthsBps;
302
+ const minBpt = requirePositiveMinimumBpt(params.minimumBptAmount, "deposit_single_token");
303
+ const [helper] = deriveHelperPda(cfg.programs.singleTokenLiquidity, pool.address);
304
+ const helperBpt = deriveAta(helper, pool.bptMint, TOKEN_PROGRAM_ID);
305
+ const userBpt = deriveAta(params.user, pool.bptMint, TOKEN_PROGRAM_ID);
306
+
307
+ const data = Buffer.concat([
308
+ STLD_DISC.depositSingleToken,
309
+ encodeU64(params.amountIn),
310
+ encodeU8(params.tokenInIndex),
311
+ encodeU32(slip),
312
+ encodeU64(minBpt),
313
+ ]);
314
+
315
+ // stld remaining_accounts: [mint_i, user_ata_i, helper_ata_i, vault_i, tp_i] × N
316
+ const remaining: AccountMeta[] = [];
317
+ for (let i = 0; i < pool.tokenCount; i++) {
318
+ const t = pool.tokens[i];
319
+ const userAta = deriveAta(params.user, t.mint, t.tokenProgram);
320
+ const helperAta = deriveAta(helper, t.mint, t.tokenProgram);
321
+ remaining.push({ pubkey: t.mint, isSigner: false, isWritable: false });
322
+ remaining.push({ pubkey: userAta, isSigner: false, isWritable: true });
323
+ remaining.push({ pubkey: helperAta, isSigner: false, isWritable: true });
324
+ remaining.push({ pubkey: t.vault, isSigner: false, isWritable: true });
325
+ remaining.push({ pubkey: t.tokenProgram, isSigner: false, isWritable: false });
326
+ }
327
+
328
+ const keys: AccountMeta[] = [
329
+ { pubkey: pool.address, isSigner: false, isWritable: true },
330
+ { pubkey: helper, isSigner: false, isWritable: false },
331
+ { pubkey: pool.bptMint, isSigner: false, isWritable: true },
332
+ { pubkey: helperBpt, isSigner: false, isWritable: true },
333
+ { pubkey: userBpt, isSigner: false, isWritable: true },
334
+ { pubkey: params.user, isSigner: true, isWritable: true },
335
+ { pubkey: cfg.programs.cubicPool, isSigner: false, isWritable: false },
336
+ { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
337
+ ...remaining,
338
+ ];
339
+
340
+ return new TransactionInstruction({
341
+ programId: cfg.programs.singleTokenLiquidity,
342
+ keys,
343
+ data,
344
+ });
345
+ }
346
+
347
+ /**
348
+ * Full single-token deposit tx: makes sure helper ATAs and the user's
349
+ * per-token ATAs exist (idempotent create instructions), creates the user's
350
+ * BPT ATA if absent, then the deposit ix itself. The helper program validates
351
+ * all user ATAs up front because it may refund dust for any pool token.
352
+ * CU limit set to 1.4M (mainnet/devnet max).
353
+ */
354
+ export function buildSingleTokenDepositTx(
355
+ cfg: CubeConfig,
356
+ pool: PoolInfo,
357
+ params: SingleTokenDepositParams
358
+ ): BuiltTx {
359
+ const [helper] = deriveHelperPda(cfg.programs.singleTokenLiquidity, pool.address);
360
+ const helperBpt = deriveAta(helper, pool.bptMint, TOKEN_PROGRAM_ID);
361
+ const userBpt = deriveAta(params.user, pool.bptMint, TOKEN_PROGRAM_ID);
362
+
363
+ const ixs: TransactionInstruction[] = [
364
+ ComputeBudgetProgram.setComputeUnitLimit({ units: cfg.defaults.cuLimit }),
365
+ ];
366
+ // User + helper ATAs (helper has an off-curve owner). Idempotent — safe to
367
+ // include even when the accounts already exist.
368
+ for (const t of pool.tokens) {
369
+ const userAta = deriveAta(params.user, t.mint, t.tokenProgram);
370
+ const helperAta = deriveAta(helper, t.mint, t.tokenProgram);
371
+ ixs.push(
372
+ createAssociatedTokenAccountIdempotentInstruction(
373
+ params.user,
374
+ userAta,
375
+ params.user,
376
+ t.mint,
377
+ t.tokenProgram
378
+ )
379
+ );
380
+ ixs.push(
381
+ createAssociatedTokenAccountIdempotentInstruction(
382
+ params.user,
383
+ helperAta,
384
+ helper,
385
+ t.mint,
386
+ t.tokenProgram
387
+ )
388
+ );
389
+ }
390
+ ixs.push(
391
+ createAssociatedTokenAccountIdempotentInstruction(
392
+ params.user,
393
+ helperBpt,
394
+ helper,
395
+ pool.bptMint,
396
+ TOKEN_PROGRAM_ID
397
+ )
398
+ );
399
+ ixs.push(
400
+ createAssociatedTokenAccountIdempotentInstruction(
401
+ params.user,
402
+ userBpt,
403
+ params.user,
404
+ pool.bptMint,
405
+ TOKEN_PROGRAM_ID
406
+ )
407
+ );
408
+ ixs.push(buildSingleTokenDepositIx(cfg, pool, params));
409
+
410
+ return {
411
+ instructions: ixs,
412
+ suggestedCuLimit: cfg.defaults.cuLimit,
413
+ };
414
+ }
415
+
416
+ // ============================================================
417
+ // Deploy new pool (PoolFactory)
418
+ // ============================================================
419
+
420
+ export function buildInitializeConfigIx(
421
+ cfg: CubeConfig,
422
+ params: { config: PublicKey; payer: PublicKey; defaultProtocolFeeRate: number }
423
+ ): TransactionInstruction {
424
+ // cubic_pool v0.6.0 initialize_config:
425
+ // - the program now enforces `protocol_admin == TreasuryPDA(protocol-admin)`
426
+ // - the Treasury PDA is passed as an account; Anchor derives it from
427
+ // seeds [b"treasury"] on the protocol-admin program ID and rejects
428
+ // anything else, so neither the caller nor the SDK can substitute
429
+ // a wallet here. The on-chain `config.protocol_admin` field will
430
+ // always be this PDA.
431
+ const [treasuryPda] = PublicKey.findProgramAddressSync(
432
+ [Buffer.from("treasury")],
433
+ cfg.programs.protocolAdmin
434
+ );
435
+ const data = Buffer.concat([
436
+ computeDiscriminator("initialize_config"),
437
+ treasuryPda.toBuffer(),
438
+ encodeU16(params.defaultProtocolFeeRate),
439
+ ]);
440
+ const keys: AccountMeta[] = [
441
+ { pubkey: params.config, isSigner: true, isWritable: true },
442
+ { pubkey: treasuryPda, isSigner: false, isWritable: false },
443
+ { pubkey: params.payer, isSigner: true, isWritable: true },
444
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
445
+ ];
446
+ return new TransactionInstruction({
447
+ programId: cfg.programs.cubicPool,
448
+ keys,
449
+ data,
450
+ });
451
+ }
452
+
453
+ export function buildInitializeCubicPoolIx(
454
+ cfg: CubeConfig,
455
+ params: DeployPoolParams
456
+ ): TransactionInstruction {
457
+ const tokenProgram = params.bptTokenProgram ?? TOKEN_PROGRAM_ID;
458
+ const [pool] = PublicKey.findProgramAddressSync(
459
+ [Buffer.from("cubic_pool"), params.configKey.toBuffer(), params.poolId.toArrayLike(Buffer, "le", 8)],
460
+ cfg.programs.cubicPool
461
+ );
462
+ const [bptMint] = deriveBptMint(cfg.programs.cubicPool, pool);
463
+
464
+ const data = Buffer.concat([
465
+ CUBIC_POOL_DISC.initializeCubicPool,
466
+ encodeVecPubkey(params.tokens),
467
+ encodeVecU64(params.weightsBps.map((w) => new BN(w))),
468
+ encodeVecU64(params.virtualBalances),
469
+ encodeU32(params.swapFeeRate),
470
+ encodeU64(params.poolId),
471
+ ]);
472
+
473
+ const remaining: AccountMeta[] = params.tokens.map((m) => ({
474
+ pubkey: m,
475
+ isSigner: false,
476
+ isWritable: false,
477
+ }));
478
+
479
+ const keys: AccountMeta[] = [
480
+ { pubkey: params.configKey, isSigner: false, isWritable: false },
481
+ { pubkey: pool, isSigner: false, isWritable: true },
482
+ { pubkey: bptMint, isSigner: false, isWritable: true },
483
+ { pubkey: params.payer, isSigner: true, isWritable: true },
484
+ { pubkey: tokenProgram, isSigner: false, isWritable: false },
485
+ { pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
486
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
487
+ ...remaining,
488
+ ];
489
+
490
+ return new TransactionInstruction({
491
+ programId: cfg.programs.cubicPool,
492
+ keys,
493
+ data,
494
+ });
495
+ }
496
+
497
+ export function buildDeployPoolTx(cfg: CubeConfig, params: DeployPoolParams): BuiltTx {
498
+ return {
499
+ instructions: [
500
+ ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }),
501
+ buildInitializeCubicPoolIx(cfg, params),
502
+ ],
503
+ suggestedCuLimit: 400_000,
504
+ };
505
+ }
506
+
507
+ // ============================================================
508
+ // Borsh encoding helpers (subset used above)
509
+ // ============================================================
510
+
511
+ function encodeU8(v: number): Buffer {
512
+ const b = Buffer.alloc(1);
513
+ b.writeUInt8(v & 0xff, 0);
514
+ return b;
515
+ }
516
+ function encodeU16(v: number): Buffer {
517
+ const b = Buffer.alloc(2);
518
+ b.writeUInt16LE(v & 0xffff, 0);
519
+ return b;
520
+ }
521
+ function encodeU32(v: number): Buffer {
522
+ const b = Buffer.alloc(4);
523
+ b.writeUInt32LE(v >>> 0, 0);
524
+ return b;
525
+ }
526
+ function encodeU64(v: BN): Buffer {
527
+ return v.toArrayLike(Buffer, "le", 8);
528
+ }
529
+ function encodeVecU64(vs: BN[]): Buffer {
530
+ const len = Buffer.alloc(4);
531
+ len.writeUInt32LE(vs.length, 0);
532
+ return Buffer.concat([len, ...vs.map(encodeU64)]);
533
+ }
534
+ function encodeVecPubkey(pks: PublicKey[]): Buffer {
535
+ const len = Buffer.alloc(4);
536
+ len.writeUInt32LE(pks.length, 0);
537
+ return Buffer.concat([len, ...pks.map((p) => p.toBuffer())]);
538
+ }
@@ -0,0 +1,82 @@
1
+ import { Commitment, PublicKey } from "@solana/web3.js";
2
+ import { NETWORK_PROGRAMS, NetworkPrograms, Network, DEFAULT_RPC_ENDPOINT } from "./networks";
3
+ import { TokenInfo } from "./tokens";
4
+
5
+ export * from "./networks";
6
+ export * from "./tokens";
7
+
8
+ /**
9
+ * Root SDK configuration. Passed into every Client class at construction.
10
+ * Immutable after creation — rebuild if you need to switch networks.
11
+ */
12
+ export interface CubeConfig {
13
+ network: Network;
14
+ programs: NetworkPrograms;
15
+ defaults: {
16
+ rpcEndpoint: string;
17
+ rpcCommitment: Commitment;
18
+ /** Used by helpers that prepend a ComputeBudget instruction. */
19
+ cuLimit: number;
20
+ /**
21
+ * Default slippage budget when the caller doesn't pass one. Units:
22
+ * hundredths of a basis point (matches cubic-pool's `swap_fee_rate`
23
+ * scale). 50_000 = 5%.
24
+ */
25
+ slippageHundredthsBps: number;
26
+ };
27
+ /** Optional token registry keyed by mint pubkey string. */
28
+ tokens?: Record<string, TokenInfo>;
29
+ /** Optional backend endpoint; consumed by CubeBackendClient. */
30
+ backendEndpoint?: string;
31
+ }
32
+
33
+ export interface CubeConfigOverrides {
34
+ rpcEndpoint?: string;
35
+ rpcCommitment?: Commitment;
36
+ cuLimit?: number;
37
+ slippageHundredthsBps?: number;
38
+ backendEndpoint?: string;
39
+ tokens?: Record<string, TokenInfo>;
40
+ }
41
+
42
+ /** Build a CubeConfig for the named network with optional overrides. */
43
+ export function getConfig(network: Network, overrides: CubeConfigOverrides = {}): CubeConfig {
44
+ return {
45
+ network,
46
+ programs: NETWORK_PROGRAMS[network],
47
+ defaults: {
48
+ rpcEndpoint: overrides.rpcEndpoint ?? DEFAULT_RPC_ENDPOINT[network],
49
+ rpcCommitment: overrides.rpcCommitment ?? "confirmed",
50
+ cuLimit: overrides.cuLimit ?? 1_400_000,
51
+ slippageHundredthsBps: overrides.slippageHundredthsBps ?? 50_000,
52
+ },
53
+ tokens: overrides.tokens,
54
+ backendEndpoint: overrides.backendEndpoint,
55
+ };
56
+ }
57
+
58
+ export const CUBIC_POOL_SEED = Buffer.from("cubic_pool");
59
+ export const BPT_MINT_SEED = Buffer.from("bpt_mint");
60
+ export const STLD_HELPER_SEED = Buffer.from("stld_helper");
61
+ export const TREASURY_SEED = Buffer.from("treasury");
62
+
63
+ /** Contract-level constants mirrored from Rust. */
64
+ export const WEIGHT_SCALE = 10_000; // 100 %
65
+ export const MIN_WEIGHT = 100; // 1 %
66
+ export const MAX_WEIGHT = 9_900; // 99 %
67
+ export const MIN_TOKENS = 2;
68
+ export const MAX_TOKENS = 10;
69
+ export const BPT_DECIMALS = 9;
70
+ export const SWAP_FEE_PRECISION = 1_000_000; // 100 %
71
+ export const PROTOCOL_FEE_PRECISION = 10_000; // 100 %
72
+ export const MAX_SWAP_FEE_RATE = 100_000; // 10 %
73
+ export const MAX_PROTOCOL_FEE_RATE = 5_000; // 50 %
74
+ export const MINIMUM_INITIAL_BPT = 1_000n;
75
+ export const SLIPPAGE_PRECISION = 1_000_000;
76
+ export const MIN_SLIPPAGE_HBPS = 10; // 0.001 %
77
+
78
+ export type ProgramIdKind = "cubicPool" | "singleTokenLiquidity" | "protocolAdmin";
79
+
80
+ export function programId(cfg: CubeConfig, kind: ProgramIdKind): PublicKey {
81
+ return cfg.programs[kind];
82
+ }
@@ -0,0 +1,49 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+
3
+ export type Network = "mainnet" | "devnet" | "localnet";
4
+
5
+ export interface NetworkPrograms {
6
+ cubicPool: PublicKey;
7
+ singleTokenLiquidity: PublicKey;
8
+ protocolAdmin: PublicKey;
9
+ }
10
+
11
+ export const NETWORK_PROGRAMS: Record<Network, NetworkPrograms> = {
12
+ mainnet: {
13
+ cubicPool: new PublicKey("8iQtGj9mcUfFUGaiCpPy89swC3s8YTC8FhVZWfgeZhwu"),
14
+ // TODO(mainnet): single_token_liquidity is NOT deployed on mainnet — this
15
+ // is the devnet program ID as a placeholder. Single-token deposit is
16
+ // disabled in the frontend. If/when stld ships on mainnet, replace this
17
+ // with the real mainnet program ID.
18
+ singleTokenLiquidity: new PublicKey(
19
+ "7BpdUH1tzTSXLuQNo6YpjJ8Eagw8AkrS6cnkxiJdCFS2"
20
+ ),
21
+ protocolAdmin: new PublicKey(
22
+ "3jiojHZbjJQ7QLMGSTjFwxVEmx4NtuRy34nLAmsJME81"
23
+ ),
24
+ },
25
+ devnet: {
26
+ cubicPool: new PublicKey("8iQtGj9mcUfFUGaiCpPy89swC3s8YTC8FhVZWfgeZhwu"),
27
+ singleTokenLiquidity: new PublicKey(
28
+ "7BpdUH1tzTSXLuQNo6YpjJ8Eagw8AkrS6cnkxiJdCFS2"
29
+ ),
30
+ protocolAdmin: new PublicKey(
31
+ "3jiojHZbjJQ7QLMGSTjFwxVEmx4NtuRy34nLAmsJME81"
32
+ ),
33
+ },
34
+ localnet: {
35
+ cubicPool: new PublicKey("8iQtGj9mcUfFUGaiCpPy89swC3s8YTC8FhVZWfgeZhwu"),
36
+ singleTokenLiquidity: new PublicKey(
37
+ "7BpdUH1tzTSXLuQNo6YpjJ8Eagw8AkrS6cnkxiJdCFS2"
38
+ ),
39
+ protocolAdmin: new PublicKey(
40
+ "3jiojHZbjJQ7QLMGSTjFwxVEmx4NtuRy34nLAmsJME81"
41
+ ),
42
+ },
43
+ };
44
+
45
+ export const DEFAULT_RPC_ENDPOINT: Record<Network, string> = {
46
+ mainnet: "https://api.mainnet-beta.solana.com",
47
+ devnet: "https://api.devnet.solana.com",
48
+ localnet: "http://127.0.0.1:8899",
49
+ };