@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,861 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.STLD_DEPOSIT_CU_LIMIT = exports.STLD_MAX_TOKENS = void 0;
7
+ exports.buildSwapIx = buildSwapIx;
8
+ exports.buildSwapTx = buildSwapTx;
9
+ exports.buildAddLiquidityIx = buildAddLiquidityIx;
10
+ exports.buildAddLiquidityTx = buildAddLiquidityTx;
11
+ exports.buildRemoveLiquidityIx = buildRemoveLiquidityIx;
12
+ exports.buildRemoveLiquidityTx = buildRemoveLiquidityTx;
13
+ exports.buildSingleTokenDepositIx = buildSingleTokenDepositIx;
14
+ exports.buildSingleTokenDepositAtaIxs = buildSingleTokenDepositAtaIxs;
15
+ exports.buildSingleTokenDepositTx = buildSingleTokenDepositTx;
16
+ exports.buildSingleTokenDepositTxs = buildSingleTokenDepositTxs;
17
+ exports.buildInitializeConfigIx = buildInitializeConfigIx;
18
+ exports.buildPoolInitializeConfigIx = buildPoolInitializeConfigIx;
19
+ exports.buildInitializeCubicPoolIx = buildInitializeCubicPoolIx;
20
+ exports.buildDeployPoolTx = buildDeployPoolTx;
21
+ exports.deriveAltAddress = deriveAltAddress;
22
+ exports.buildInitializePoolAltIx = buildInitializePoolAltIx;
23
+ exports.buildInitializePoolAltTx = buildInitializePoolAltTx;
24
+ exports.buildSetRangeManagerIx = buildSetRangeManagerIx;
25
+ exports.buildSetRangeManagerConfigIx = buildSetRangeManagerConfigIx;
26
+ exports.buildRangeManagerUpdateIx = buildRangeManagerUpdateIx;
27
+ exports.buildSetSwapFeeRateIx = buildSetSwapFeeRateIx;
28
+ exports.buildSetMaxSelloffIx = buildSetMaxSelloffIx;
29
+ exports.buildInitiatePoolAdminTransferIx = buildInitiatePoolAdminTransferIx;
30
+ exports.buildAcceptPoolAdminTransferIx = buildAcceptPoolAdminTransferIx;
31
+ exports.buildCancelPoolAdminTransferIx = buildCancelPoolAdminTransferIx;
32
+ exports.buildDisablePoolAdminIx = buildDisablePoolAdminIx;
33
+ exports.buildGetPoolInfoIx = buildGetPoolInfoIx;
34
+ const web3_js_1 = require("@solana/web3.js");
35
+ const spl_token_1 = require("@solana/spl-token");
36
+ const bn_js_1 = __importDefault(require("bn.js"));
37
+ const config_1 = require("../config");
38
+ const idl_1 = require("../idl");
39
+ const pda_1 = require("../utils/pda");
40
+ const contract_instructions_1 = require("./contract-instructions");
41
+ const cubicMath_1 = require("../math/cubicMath");
42
+ const fixedPoint_1 = require("../math/fixedPoint");
43
+ const extensions_1 = require("../utils/extensions");
44
+ /**
45
+ * Low-level transaction builders. Emit raw `TransactionInstruction`s suitable
46
+ * for combination with others (e.g. versioned transactions with ALTs) in a
47
+ * single tx. Higher-level convenience lives on `CubicPoolClient`.
48
+ */
49
+ /**
50
+ * Anchor discriminator: sha256("global:<ix_name>")[0..8].
51
+ *
52
+ * Read straight out of the shipped IDL rather than hand-maintained, so a
53
+ * regenerated IDL can never silently disagree with the encoder. This table
54
+ * is a tripwire, not the source of truth: if a refreshed IDL changes bytes
55
+ * for an instruction we already ship, that is either a renamed instruction
56
+ * or a bad IDL copy, and we want a loud failure at import time instead of a
57
+ * transaction the program rejects with `InstructionFallbackNotFound`.
58
+ *
59
+ * NOTE: declared before `CUBIC_POOL_DISC` on purpose. Those maps call
60
+ * `computeDiscriminator` at module-evaluation time, and a `const` declared
61
+ * after them would still be in its temporal dead zone when they run.
62
+ */
63
+ const PINNED = {
64
+ swap: "f8c69e91e17587c8",
65
+ add_liquidity: "b59d59438fb63448",
66
+ remove_liquidity: "5055d14818ceb16c",
67
+ initialize_cubic_pool: "d79474cf79686f83",
68
+ initialize_pool_alt: "fb874202f4490c90",
69
+ deposit_single_token: "a688a62fc7c056a9",
70
+ initialize_config: "d07f1501c2bec446",
71
+ set_range_manager: "6766ad7008022584",
72
+ set_range_manager_config: "67d300c8a4bdf5cd",
73
+ range_manager_update: "f5b72345c80aac2c",
74
+ pool_initialize_config: "d06eadbc279990b9",
75
+ };
76
+ function computeDiscriminator(ixName, idl = "cubicPool") {
77
+ const source = idl === "stld"
78
+ ? idl_1.SINGLE_TOKEN_LIQUIDITY_IDL
79
+ : idl === "protocolAdmin"
80
+ ? idl_1.PROTOCOL_ADMIN_IDL
81
+ : idl_1.CUBIC_POOL_IDL;
82
+ const ix = source.instructions.find((candidate) => candidate.name === ixName);
83
+ if (!ix) {
84
+ throw new Error(`tx-builders: no "${ixName}" instruction in the ${idl} IDL`);
85
+ }
86
+ const bytes = Buffer.from(ix.discriminator);
87
+ const pinned = PINNED[ixName];
88
+ if (pinned && bytes.toString("hex") !== pinned) {
89
+ throw new Error(`tx-builders: IDL discriminator for "${ixName}" is ${bytes.toString("hex")}, ` +
90
+ `pinned value is ${pinned}. The IDL and this encoder disagree — ` +
91
+ `do not ship until you know which one is right.`);
92
+ }
93
+ return bytes;
94
+ }
95
+ const CUBIC_POOL_DISC = {
96
+ swap: computeDiscriminator("swap"),
97
+ addLiquidity: computeDiscriminator("add_liquidity"),
98
+ removeLiquidity: computeDiscriminator("remove_liquidity"),
99
+ initializeCubicPool: computeDiscriminator("initialize_cubic_pool"),
100
+ initializePoolAlt: computeDiscriminator("initialize_pool_alt"),
101
+ setRangeManager: computeDiscriminator("set_range_manager"),
102
+ setRangeManagerConfig: computeDiscriminator("set_range_manager_config"),
103
+ rangeManagerUpdate: computeDiscriminator("range_manager_update"),
104
+ };
105
+ const PROTOCOL_ADMIN_DISC = {
106
+ poolInitializeConfig: computeDiscriminator("pool_initialize_config", "protocolAdmin"),
107
+ };
108
+ const STLD_DISC = {
109
+ depositSingleToken: computeDiscriminator("deposit_single_token", "stld"),
110
+ };
111
+ function bptProgram(pool) {
112
+ const program = pool.bptTokenProgram ?? spl_token_1.TOKEN_PROGRAM_ID;
113
+ if (!program.equals(spl_token_1.TOKEN_PROGRAM_ID) && !program.equals(spl_token_1.TOKEN_2022_PROGRAM_ID)) {
114
+ throw new Error("BPT mint must be owned by SPL Token or Token-2022");
115
+ }
116
+ return program;
117
+ }
118
+ function requirePositiveMinimumBpt(minimumBptAmount, ixName) {
119
+ if (!minimumBptAmount || minimumBptAmount.lte(new bn_js_1.default(0))) {
120
+ throw new Error(`${ixName}: minimumBptAmount must be positive`);
121
+ }
122
+ return minimumBptAmount;
123
+ }
124
+ /**
125
+ * Require an explicit per-token slippage floor for remove_liquidity.
126
+ *
127
+ * Previously this defaulted to all-zero when omitted, which silently shipped
128
+ * a remove tx with NO slippage protection (sandwich/MEV exposure). Callers
129
+ * must now pass `minimumTokenAmounts` (derive them from a fresh `quoteRemove()`
130
+ * minus tolerance). To intentionally disable protection, pass explicit zeros.
131
+ */
132
+ function requireExplicitMinimums(minimumTokenAmounts, tokenCount, ixName) {
133
+ if (!minimumTokenAmounts) {
134
+ throw new Error(`${ixName}: minimumTokenAmounts must be provided (one per token). ` +
135
+ `Derive per-token floors from a fresh quoteRemove(); pass explicit zeros ` +
136
+ `only to intentionally disable slippage protection.`);
137
+ }
138
+ if (minimumTokenAmounts.length !== tokenCount) {
139
+ throw new Error(`${ixName}: minimumTokenAmounts length (${minimumTokenAmounts.length}) must equal token count (${tokenCount})`);
140
+ }
141
+ return minimumTokenAmounts;
142
+ }
143
+ // ============================================================
144
+ // Swap
145
+ // ============================================================
146
+ function buildSwapIx(cfg, pool, params) {
147
+ (0, extensions_1.assertTokensSupported)(pool, [params.tokenInIndex, params.tokenOutIndex], "swap");
148
+ const inTok = pool.tokens[params.tokenInIndex];
149
+ const outTok = pool.tokens[params.tokenOutIndex];
150
+ const userTokenIn = (0, pda_1.deriveAta)(params.user, inTok.mint, inTok.tokenProgram);
151
+ const userTokenOut = (0, pda_1.deriveAta)(params.user, outTok.mint, outTok.tokenProgram);
152
+ const data = Buffer.concat([
153
+ CUBIC_POOL_DISC.swap,
154
+ encodeU64(params.amountIn),
155
+ encodeU64(params.minAmountOut),
156
+ encodeU8(params.tokenInIndex),
157
+ encodeU8(params.tokenOutIndex),
158
+ ]);
159
+ const keys = [
160
+ { pubkey: pool.address, isSigner: false, isWritable: true },
161
+ { pubkey: inTok.mint, isSigner: false, isWritable: false },
162
+ { pubkey: outTok.mint, isSigner: false, isWritable: false },
163
+ { pubkey: userTokenIn, isSigner: false, isWritable: true },
164
+ { pubkey: userTokenOut, isSigner: false, isWritable: true },
165
+ { pubkey: inTok.vault, isSigner: false, isWritable: true },
166
+ { pubkey: outTok.vault, isSigner: false, isWritable: true },
167
+ { pubkey: params.user, isSigner: true, isWritable: true },
168
+ { pubkey: inTok.tokenProgram, isSigner: false, isWritable: false },
169
+ { pubkey: outTok.tokenProgram, isSigner: false, isWritable: false },
170
+ ];
171
+ return new web3_js_1.TransactionInstruction({
172
+ programId: cfg.programs.cubicPool,
173
+ keys,
174
+ data,
175
+ });
176
+ }
177
+ function buildSwapTx(cfg, pool, params) {
178
+ return {
179
+ instructions: [
180
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 400000 }),
181
+ buildSwapIx(cfg, pool, params),
182
+ ],
183
+ suggestedCuLimit: 400000,
184
+ };
185
+ }
186
+ // ============================================================
187
+ // Add liquidity (proportional)
188
+ // ============================================================
189
+ /**
190
+ * `add_liquidity` — proportional join.
191
+ *
192
+ * ⚠ **`params.tokenAmounts` is a CEILING, not an exact amount** (v5.1,
193
+ * audit M-4 / I-1). The program takes the largest strictly-proportional
194
+ * basket that fits inside the vector you pass and leaves the remainder in
195
+ * the wallet; it does NOT pull the full amounts. Any UI copy of the form
196
+ * "you will deposit exactly X" is wrong — quote the proportional basket
197
+ * instead (see `CubicPoolClient.quoteAddLiquidity`).
198
+ *
199
+ * The wire format is unchanged, so this is a behaviour note, not an
200
+ * encoding change: the same bytes now mean "at most this much".
201
+ */
202
+ function buildAddLiquidityIx(cfg, pool, params) {
203
+ (0, extensions_1.assertTokensSupported)(pool, params.tokenAmounts.flatMap((amount, i) => amount.gt(new bn_js_1.default(0)) ? [i] : []), "add_liquidity");
204
+ const userBpt = (0, pda_1.deriveAta)(params.user, pool.bptMint, bptProgram(pool));
205
+ const minBpt = requirePositiveMinimumBpt(params.minimumBptAmount, "add_liquidity");
206
+ if (params.tokenAmounts.length !== pool.tokenCount) {
207
+ // The program rejects this with `InvalidArrayLength` (6064); catching it
208
+ // here costs a round trip less.
209
+ throw new Error(`add_liquidity: tokenAmounts length (${params.tokenAmounts.length}) ` +
210
+ `must equal the pool's token count (${pool.tokenCount})`);
211
+ }
212
+ // Refuse joins whose integer-rounded basket has a zero live leg. Such a
213
+ // basket can mint positive BPT in this deployment despite moving no tokens.
214
+ if (pool.bptTotalSupply.gt(new bn_js_1.default(0))) {
215
+ const balances = pool.tokens.map(token => BigInt(token.actualBalance.toString()));
216
+ const amounts = params.tokenAmounts.map(amount => BigInt(amount.toString()));
217
+ let ratio = null;
218
+ balances.forEach((balance, i) => {
219
+ if ((balance > 0n) !== (amounts[i] > 0n))
220
+ throw new Error("add_liquidity: token liveness mismatch");
221
+ if (balance === 0n)
222
+ return;
223
+ const candidate = (0, fixedPoint_1.divDown)(amounts[i], balance);
224
+ ratio = ratio === null || candidate < ratio ? candidate : ratio;
225
+ });
226
+ if (ratio === null || (0, cubicMath_1.calcBptOutGivenExactTokensIn)(balances, amounts, BigInt(pool.bptTotalSupply.toString())) === 0n ||
227
+ balances.some(balance => balance > 0n && (0, fixedPoint_1.mulDown)(balance, ratio) === 0n)) {
228
+ throw new Error("add_liquidity: deposit too small after proportional rounding");
229
+ }
230
+ }
231
+ const data = Buffer.concat([
232
+ CUBIC_POOL_DISC.addLiquidity,
233
+ encodeVecU64(params.tokenAmounts),
234
+ encodeU64(minBpt),
235
+ ]);
236
+ // remaining_accounts layout:
237
+ // [user_token_i, vault_i] × N, mint_i × N, token_program_i × N
238
+ const remaining = [];
239
+ for (let i = 0; i < pool.tokenCount; i++) {
240
+ const t = pool.tokens[i];
241
+ const userAta = (0, pda_1.deriveAta)(params.user, t.mint, t.tokenProgram);
242
+ remaining.push({ pubkey: userAta, isSigner: false, isWritable: true });
243
+ remaining.push({ pubkey: t.vault, isSigner: false, isWritable: true });
244
+ }
245
+ for (let i = 0; i < pool.tokenCount; i++) {
246
+ remaining.push({ pubkey: pool.tokens[i].mint, isSigner: false, isWritable: false });
247
+ }
248
+ for (let i = 0; i < pool.tokenCount; i++) {
249
+ remaining.push({ pubkey: pool.tokens[i].tokenProgram, isSigner: false, isWritable: false });
250
+ }
251
+ const keys = [
252
+ { pubkey: pool.address, isSigner: false, isWritable: true },
253
+ { pubkey: pool.bptMint, isSigner: false, isWritable: true },
254
+ { pubkey: userBpt, isSigner: false, isWritable: true },
255
+ { pubkey: params.user, isSigner: true, isWritable: true },
256
+ { pubkey: bptProgram(pool), isSigner: false, isWritable: false },
257
+ ...remaining,
258
+ ];
259
+ return new web3_js_1.TransactionInstruction({
260
+ programId: cfg.programs.cubicPool,
261
+ keys,
262
+ data,
263
+ });
264
+ }
265
+ function buildAddLiquidityTx(cfg, pool, params) {
266
+ const userBpt = (0, pda_1.deriveAta)(params.user, pool.bptMint, bptProgram(pool));
267
+ return {
268
+ instructions: [
269
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 600000 }),
270
+ (0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(params.user, userBpt, params.user, pool.bptMint, bptProgram(pool)),
271
+ buildAddLiquidityIx(cfg, pool, params),
272
+ ],
273
+ suggestedCuLimit: 600000,
274
+ };
275
+ }
276
+ // ============================================================
277
+ // Remove liquidity
278
+ // ============================================================
279
+ function buildRemoveLiquidityIx(cfg, pool, params) {
280
+ const supply = pool.bptTotalSupply;
281
+ const maxBurn = bn_js_1.default.max(supply.sub(new bn_js_1.default(config_1.MINIMUM_INITIAL_BPT.toString())), new bn_js_1.default(0));
282
+ const effectiveBurn = bn_js_1.default.min(params.bptAmount, maxBurn);
283
+ const transferred = supply.gt(new bn_js_1.default(0))
284
+ ? (0, cubicMath_1.calcTokensOutGivenBptIn)(pool.tokens.map(token => BigInt(token.actualBalance.toString())), BigInt(effectiveBurn.toString()), BigInt(supply.toString()))
285
+ .flatMap((amount, i) => amount > 0n ? [i] : [])
286
+ : [];
287
+ (0, extensions_1.assertTokensSupported)(pool, transferred, "remove_liquidity");
288
+ const userBpt = (0, pda_1.deriveAta)(params.user, pool.bptMint, bptProgram(pool));
289
+ const mins = requireExplicitMinimums(params.minimumTokenAmounts, pool.tokenCount, "remove_liquidity");
290
+ const data = Buffer.concat([
291
+ CUBIC_POOL_DISC.removeLiquidity,
292
+ encodeU64(params.bptAmount),
293
+ encodeVecU64(mins),
294
+ ]);
295
+ // remove_liquidity remaining_accounts format:
296
+ // [vault_i, user_token_i] × N, mint_i × N, token_program_i × N
297
+ const remaining = [];
298
+ for (let i = 0; i < pool.tokenCount; i++) {
299
+ const t = pool.tokens[i];
300
+ const userAta = (0, pda_1.deriveAta)(params.user, t.mint, t.tokenProgram);
301
+ remaining.push({ pubkey: t.vault, isSigner: false, isWritable: true });
302
+ remaining.push({ pubkey: userAta, isSigner: false, isWritable: true });
303
+ }
304
+ for (let i = 0; i < pool.tokenCount; i++) {
305
+ remaining.push({ pubkey: pool.tokens[i].mint, isSigner: false, isWritable: false });
306
+ }
307
+ for (let i = 0; i < pool.tokenCount; i++) {
308
+ remaining.push({ pubkey: pool.tokens[i].tokenProgram, isSigner: false, isWritable: false });
309
+ }
310
+ const keys = [
311
+ { pubkey: pool.address, isSigner: false, isWritable: true },
312
+ { pubkey: pool.bptMint, isSigner: false, isWritable: true },
313
+ { pubkey: userBpt, isSigner: false, isWritable: true },
314
+ { pubkey: params.user, isSigner: true, isWritable: true },
315
+ { pubkey: bptProgram(pool), isSigner: false, isWritable: false },
316
+ ...remaining,
317
+ ];
318
+ return new web3_js_1.TransactionInstruction({
319
+ programId: cfg.programs.cubicPool,
320
+ keys,
321
+ data,
322
+ });
323
+ }
324
+ function buildRemoveLiquidityTx(cfg, pool, params) {
325
+ const ixs = [
326
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: cfg.defaults.cuLimit }),
327
+ ];
328
+ // The contract transfers every pool token to the user's ATA. Include
329
+ // idempotent creates so a proportional burn works even when the user never
330
+ // held one of the receive tokens before.
331
+ for (const t of pool.tokens) {
332
+ const userAta = (0, pda_1.deriveAta)(params.user, t.mint, t.tokenProgram);
333
+ ixs.push((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(params.user, userAta, params.user, t.mint, t.tokenProgram));
334
+ }
335
+ ixs.push(buildRemoveLiquidityIx(cfg, pool, params));
336
+ return {
337
+ instructions: ixs,
338
+ suggestedCuLimit: cfg.defaults.cuLimit,
339
+ };
340
+ }
341
+ // ============================================================
342
+ // Single-token deposit (helper program)
343
+ // ============================================================
344
+ /**
345
+ * Largest pool the zap will accept, mirroring `STLD_MAX_TOKENS` in
346
+ * `single-token-liquidity/src/constants.rs` (which is defined as
347
+ * cubic-pool's `MAX_TOKENS`).
348
+ *
349
+ * Raised from 5 to 10 in v5.1 (audit SF-3): the old ceiling existed because
350
+ * the per-leg allocations exhausted the 32 KiB bump heap. Above this the
351
+ * program rejects with `PoolTooLargeForZap` (stld 6032).
352
+ */
353
+ exports.STLD_MAX_TOKENS = 10;
354
+ /**
355
+ * Compute units to request for the zap.
356
+ *
357
+ * Measured on the v5.1 build at the worst case N=10: 1,073,206 CU for
358
+ * classic SPL and 913,986 CU for Token-2022. 1.4M is the per-transaction
359
+ * maximum and leaves headroom for the surge-fee path.
360
+ */
361
+ exports.STLD_DEPOSIT_CU_LIMIT = 1400000;
362
+ function buildSingleTokenDepositIx(cfg, pool, params) {
363
+ // v5 / new contract: deposit_single_token args are
364
+ // (amount_in: u64, token_in_index: u8, minimum_bpt_amount: u64).
365
+ // The old per-call `slippage_hundredths_bps: u32` was removed — the single
366
+ // `minimum_bpt_amount` floor now bounds the whole zap (internal swaps +
367
+ // surge fees + join); per-leg swaps use min_out = 0.
368
+ if (pool.tokenCount > exports.STLD_MAX_TOKENS) {
369
+ throw new Error(`deposit_single_token: pool has ${pool.tokenCount} tokens, the zap supports at most ` +
370
+ `${exports.STLD_MAX_TOKENS} (PoolTooLargeForZap). Use add_liquidity instead.`);
371
+ }
372
+ (0, extensions_1.assertTokensSupported)(pool, "all", "deposit_single_token");
373
+ const minBpt = requirePositiveMinimumBpt(params.minimumBptAmount, "deposit_single_token");
374
+ const [helper] = (0, pda_1.deriveHelperPda)(cfg.programs.singleTokenLiquidity, pool.address);
375
+ const helperBpt = (0, pda_1.deriveAta)(helper, pool.bptMint, bptProgram(pool));
376
+ const userBpt = (0, pda_1.deriveAta)(params.user, pool.bptMint, bptProgram(pool));
377
+ const data = Buffer.concat([
378
+ STLD_DISC.depositSingleToken,
379
+ encodeU64(params.amountIn),
380
+ encodeU8(params.tokenInIndex),
381
+ encodeU64(minBpt),
382
+ ]);
383
+ // stld remaining_accounts: [mint_i, user_ata_i, helper_ata_i, vault_i, tp_i] × N
384
+ const remaining = [];
385
+ for (let i = 0; i < pool.tokenCount; i++) {
386
+ const t = pool.tokens[i];
387
+ const userAta = (0, pda_1.deriveAta)(params.user, t.mint, t.tokenProgram);
388
+ const helperAta = (0, pda_1.deriveAta)(helper, t.mint, t.tokenProgram);
389
+ remaining.push({ pubkey: t.mint, isSigner: false, isWritable: false });
390
+ remaining.push({ pubkey: userAta, isSigner: false, isWritable: true });
391
+ remaining.push({ pubkey: helperAta, isSigner: false, isWritable: true });
392
+ remaining.push({ pubkey: t.vault, isSigner: false, isWritable: true });
393
+ remaining.push({ pubkey: t.tokenProgram, isSigner: false, isWritable: false });
394
+ }
395
+ const keys = [
396
+ { pubkey: pool.address, isSigner: false, isWritable: true },
397
+ { pubkey: helper, isSigner: false, isWritable: false },
398
+ { pubkey: pool.bptMint, isSigner: false, isWritable: true },
399
+ { pubkey: helperBpt, isSigner: false, isWritable: true },
400
+ { pubkey: userBpt, isSigner: false, isWritable: true },
401
+ { pubkey: params.user, isSigner: true, isWritable: true },
402
+ { pubkey: cfg.programs.cubicPool, isSigner: false, isWritable: false },
403
+ { pubkey: bptProgram(pool), isSigner: false, isWritable: false },
404
+ ...remaining,
405
+ ];
406
+ return new web3_js_1.TransactionInstruction({
407
+ programId: cfg.programs.singleTokenLiquidity,
408
+ keys,
409
+ data,
410
+ });
411
+ }
412
+ /**
413
+ * The idempotent ATA creates a zap depends on: the user's and the helper's
414
+ * ATA for every pool token, plus both BPT ATAs. `2N + 2` instructions.
415
+ *
416
+ * The helper validates every user ATA up front because it may refund dust
417
+ * in any pool token, so these cannot be trimmed to just the input token.
418
+ */
419
+ function buildSingleTokenDepositAtaIxs(cfg, pool, user) {
420
+ const [helper] = (0, pda_1.deriveHelperPda)(cfg.programs.singleTokenLiquidity, pool.address);
421
+ const helperBpt = (0, pda_1.deriveAta)(helper, pool.bptMint, bptProgram(pool));
422
+ const userBpt = (0, pda_1.deriveAta)(user, pool.bptMint, bptProgram(pool));
423
+ const ixs = [];
424
+ // User + helper ATAs (helper has an off-curve owner). Idempotent — safe to
425
+ // include even when the accounts already exist.
426
+ for (const t of pool.tokens) {
427
+ ixs.push((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(user, (0, pda_1.deriveAta)(user, t.mint, t.tokenProgram), user, t.mint, t.tokenProgram));
428
+ ixs.push((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(user, (0, pda_1.deriveAta)(helper, t.mint, t.tokenProgram), helper, t.mint, t.tokenProgram));
429
+ }
430
+ ixs.push((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(user, helperBpt, helper, pool.bptMint, bptProgram(pool)));
431
+ ixs.push((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(user, userBpt, user, pool.bptMint, bptProgram(pool)));
432
+ return ixs;
433
+ }
434
+ /**
435
+ * Full single-token deposit in ONE transaction: idempotent ATA setup
436
+ * followed by the deposit itself.
437
+ *
438
+ * ⚠ **This only fits for small pools.** Two independent runtime ceilings
439
+ * bite as `N` grows, and neither is a byte-size problem an ALT can fix:
440
+ *
441
+ * - `MAX_INSTRUCTION_TRACE_LENGTH = 64` counts CPI frames across the whole
442
+ * transaction. One idempotent ATA create costs 5, and the v5.1 zap alone
443
+ * uses 52/64 at N=10. The `2N + 2` setup instructions cost `10N + 10`
444
+ * frames, so the combined transaction blows the limit well before N=10.
445
+ * - Raw size at N=10 is 1796 bytes (437 with the pool's ALT), over the
446
+ * 1232-byte legacy ceiling.
447
+ *
448
+ * For anything but a small pool use {@link buildSingleTokenDepositTxs},
449
+ * which splits setup and deposit into two transactions, and compile the
450
+ * deposit leg through the pool's ALT via `compileBuiltTx`.
451
+ */
452
+ function buildSingleTokenDepositTx(cfg, pool, params) {
453
+ const cuLimit = cfg.defaults.cuLimit ?? exports.STLD_DEPOSIT_CU_LIMIT;
454
+ return {
455
+ instructions: [
456
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: cuLimit }),
457
+ ...buildSingleTokenDepositAtaIxs(cfg, pool, params.user),
458
+ buildSingleTokenDepositIx(cfg, pool, params),
459
+ ],
460
+ suggestedCuLimit: cuLimit,
461
+ };
462
+ }
463
+ /**
464
+ * Single-token deposit split into the two transactions large pools need.
465
+ *
466
+ * Send `setup` first and let it confirm, then send `deposit`. They are
467
+ * separated because the ATA creates and the zap compete for the same
468
+ * 64-frame instruction-trace budget (see {@link buildSingleTokenDepositTx}).
469
+ *
470
+ * `setup` is `null` when there is nothing to create — the caller can check
471
+ * ATA existence itself and skip the leg. Otherwise it is always safe to
472
+ * send: every instruction in it is idempotent.
473
+ *
474
+ * The `deposit` leg should be compiled as a v0 transaction against the
475
+ * pool's Address Lookup Table (`compileBuiltTx(conn, user, deposit, pool)`).
476
+ * At N=10 it is 1796 bytes raw and 437 with the ALT — the ALT is mandatory,
477
+ * not an optimisation. Note that an ALT cannot be referenced in the same
478
+ * slot it was extended, so a freshly-created table needs a slot to settle.
479
+ */
480
+ function buildSingleTokenDepositTxs(cfg, pool, params) {
481
+ const ataIxs = buildSingleTokenDepositAtaIxs(cfg, pool, params.user);
482
+ const cuLimit = cfg.defaults.cuLimit ?? exports.STLD_DEPOSIT_CU_LIMIT;
483
+ // ~30k CU per idempotent create, with headroom.
484
+ const setupCu = Math.min(1400000, 50000 * ataIxs.length);
485
+ return {
486
+ setup: ataIxs.length === 0
487
+ ? null
488
+ : {
489
+ instructions: [
490
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: setupCu }),
491
+ ...ataIxs,
492
+ ],
493
+ suggestedCuLimit: setupCu,
494
+ },
495
+ deposit: {
496
+ instructions: [
497
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: cuLimit }),
498
+ buildSingleTokenDepositIx(cfg, pool, params),
499
+ ],
500
+ suggestedCuLimit: cuLimit,
501
+ },
502
+ };
503
+ }
504
+ // ============================================================
505
+ // Deploy new pool (PoolFactory)
506
+ // ============================================================
507
+ /**
508
+ * Raw `cubic_pool::initialize_config`.
509
+ *
510
+ * ⚠ **This instruction is no longer directly callable from a wallet.** In
511
+ * v5.1 (audit H-2) the `protocol_admin: Pubkey` ARGUMENT was removed and
512
+ * the Treasury PDA account became a `Signer`. The config's admin is now
513
+ * whoever signs, which by construction can only be protocol-admin's
514
+ * Treasury PDA via `invoke_signed` — no keypair exists for it.
515
+ *
516
+ * Sending this instruction from a wallet fails signature verification. The
517
+ * only working path is `AdminClient.poolInitializeConfigIx()`, which calls
518
+ * `protocol_admin::pool_initialize_config` and CPIs in here. This builder
519
+ * is kept so the wire format stays documented and testable, and so the
520
+ * treasury meta carries `isSigner: true` — Anchor derives CPI account
521
+ * metas from the field type, and emitting it as a non-signer would strand
522
+ * the outer `invoke_signed` signature.
523
+ */
524
+ function buildInitializeConfigIx(cfg, params) {
525
+ const [treasuryPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("treasury")], cfg.programs.protocolAdmin);
526
+ // Args are `(default_protocol_fee_rate: u16)` and nothing else. The
527
+ // leading 32-byte `protocol_admin` pubkey this used to encode is gone —
528
+ // leaving it in makes the program read the fee rate out of the middle of
529
+ // a pubkey.
530
+ const data = Buffer.concat([
531
+ computeDiscriminator("initialize_config"),
532
+ encodeU16(params.defaultProtocolFeeRate),
533
+ ]);
534
+ const keys = [
535
+ { pubkey: params.config, isSigner: true, isWritable: true },
536
+ { pubkey: treasuryPda, isSigner: true, isWritable: false },
537
+ { pubkey: params.payer, isSigner: true, isWritable: true },
538
+ { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
539
+ ];
540
+ return new web3_js_1.TransactionInstruction({
541
+ programId: cfg.programs.cubicPool,
542
+ keys,
543
+ data,
544
+ });
545
+ }
546
+ /**
547
+ * `protocol_admin::pool_initialize_config` — the ONLY way to create a
548
+ * `CubicPoolConfig` that a wallet can actually sign.
549
+ *
550
+ * `cubic_pool::initialize_config` takes the Treasury PDA as a `Signer`
551
+ * (audit H-01: an unauthenticated config let anyone open a pool that charged
552
+ * a swap fee and paid the protocol nothing). A PDA cannot sign a transaction
553
+ * — only the program that owns its seeds can, via `invoke_signed`. So the
554
+ * direct instruction is unreachable from a wallet by design, and every caller
555
+ * has to come through this wrapper, where the Treasury `admin` is the human
556
+ * signer and protocol-admin performs the CPI.
557
+ *
558
+ * Signers: `admin` (must equal `Treasury.admin`) and `config` (a freshly
559
+ * generated keypair — the account is `init`, not a PDA).
560
+ */
561
+ function buildPoolInitializeConfigIx(cfg, params) {
562
+ const [treasuryPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("treasury")], cfg.programs.protocolAdmin);
563
+ const data = Buffer.concat([
564
+ PROTOCOL_ADMIN_DISC.poolInitializeConfig,
565
+ encodeU16(params.defaultProtocolFeeRate),
566
+ ]);
567
+ const keys = [
568
+ { pubkey: treasuryPda, isSigner: false, isWritable: false },
569
+ { pubkey: params.admin, isSigner: true, isWritable: true },
570
+ { pubkey: params.config, isSigner: true, isWritable: true },
571
+ { pubkey: cfg.programs.cubicPool, isSigner: false, isWritable: false },
572
+ { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
573
+ ];
574
+ return new web3_js_1.TransactionInstruction({
575
+ programId: cfg.programs.protocolAdmin,
576
+ keys,
577
+ data,
578
+ });
579
+ }
580
+ function buildInitializeCubicPoolIx(cfg, params) {
581
+ const tokenProgram = params.bptTokenProgram ?? spl_token_1.TOKEN_PROGRAM_ID;
582
+ const [pool] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("cubic_pool"), params.configKey.toBuffer(), params.poolId.toArrayLike(Buffer, "le", 8)], cfg.programs.cubicPool);
583
+ const [bptMint] = (0, pda_1.deriveBptMint)(cfg.programs.cubicPool, pool);
584
+ const data = Buffer.concat([
585
+ CUBIC_POOL_DISC.initializeCubicPool,
586
+ encodeVecU64(params.weightsBps.map((w) => new bn_js_1.default(w))),
587
+ encodeVecU64(params.virtualBalances),
588
+ encodeU32(params.swapFeeRate),
589
+ encodeU64(params.poolId),
590
+ // Option<u64> banned_extensions_override: 0x00 = None (inherit config
591
+ // default), 0x01 + u64 LE = Some(bitmap) chosen by the creator.
592
+ encodeOptionU64(params.bannedExtensions),
593
+ ]);
594
+ // There is no `tokens` ix arg — the pool's token set is defined SOLELY by
595
+ // `params.tokens` via remaining_accounts (one mint per token, in the same
596
+ // order as `weightsBps`/`virtualBalances`).
597
+ const remaining = params.tokens.map((m) => ({
598
+ pubkey: m,
599
+ isSigner: false,
600
+ isWritable: false,
601
+ }));
602
+ const keys = [
603
+ { pubkey: params.configKey, isSigner: false, isWritable: false },
604
+ { pubkey: pool, isSigner: false, isWritable: true },
605
+ { pubkey: bptMint, isSigner: false, isWritable: true },
606
+ { pubkey: params.payer, isSigner: true, isWritable: true },
607
+ { pubkey: tokenProgram, isSigner: false, isWritable: false },
608
+ { pubkey: spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
609
+ { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
610
+ ...remaining,
611
+ ];
612
+ return new web3_js_1.TransactionInstruction({
613
+ programId: cfg.programs.cubicPool,
614
+ keys,
615
+ data,
616
+ });
617
+ }
618
+ function buildDeployPoolTx(cfg, params) {
619
+ return {
620
+ instructions: [
621
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 400000 }),
622
+ buildInitializeCubicPoolIx(cfg, params),
623
+ ],
624
+ suggestedCuLimit: 400000,
625
+ };
626
+ }
627
+ /**
628
+ * Re-derives the Address Lookup Table address from `(authority, recent_slot)`
629
+ * the same way the upstream ALT program does.
630
+ *
631
+ * `@solana/web3.js` v1 doesn't expose `deriveLookupTableAddress` as a static,
632
+ * so we inline the seed derivation.
633
+ */
634
+ function deriveAltAddress(authority, recentSlot) {
635
+ const slotBuf = recentSlot.toArrayLike(Buffer, "le", 8);
636
+ const [addr] = web3_js_1.PublicKey.findProgramAddressSync([authority.toBuffer(), slotBuf], web3_js_1.AddressLookupTableProgram.programId);
637
+ return addr;
638
+ }
639
+ /**
640
+ * Build the on-chain `initialize_pool_alt` instruction. Performs three
641
+ * upstream CPIs inside the program: create + extend + freeze. After
642
+ * this ix lands, the ALT is immutable and the pool's `lookup_table`
643
+ * field points at it.
644
+ *
645
+ * Caller must NOT use the ALT in the same slot (warmup) — wait at
646
+ * least one slot before sending any v0 tx that references it.
647
+ */
648
+ function buildInitializePoolAltIx(cfg, params) {
649
+ const altAddr = deriveAltAddress(params.authority, params.recentSlot);
650
+ const data = Buffer.concat([
651
+ CUBIC_POOL_DISC.initializePoolAlt,
652
+ encodeU64(params.recentSlot),
653
+ ]);
654
+ // Account order MUST match cubic-pool's InitializePoolAlt context:
655
+ // pool, config, authority, payer, lookup_table, system_program, alt_program.
656
+ const keys = [
657
+ { pubkey: params.pool, isSigner: false, isWritable: true },
658
+ { pubkey: params.config, isSigner: false, isWritable: false },
659
+ { pubkey: params.authority, isSigner: true, isWritable: false },
660
+ { pubkey: params.payer, isSigner: true, isWritable: true },
661
+ { pubkey: altAddr, isSigner: false, isWritable: true },
662
+ { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
663
+ { pubkey: web3_js_1.AddressLookupTableProgram.programId, isSigner: false, isWritable: false },
664
+ ];
665
+ return new web3_js_1.TransactionInstruction({
666
+ programId: cfg.programs.cubicPool,
667
+ keys,
668
+ data,
669
+ });
670
+ }
671
+ function buildInitializePoolAltTx(cfg, params) {
672
+ return {
673
+ instructions: [
674
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 200000 }),
675
+ buildInitializePoolAltIx(cfg, params),
676
+ ],
677
+ suggestedCuLimit: 200000,
678
+ lookupTable: deriveAltAddress(params.authority, params.recentSlot),
679
+ };
680
+ }
681
+ // ============================================================
682
+ // Range manager
683
+ // ============================================================
684
+ /**
685
+ * `set_range_manager` — appoint or disable a pool's range manager.
686
+ *
687
+ * ⚠ v5.1 (audit L-1) PREPENDED a `config` account before `pool`. The
688
+ * account list is `[config, pool, authority]`. A client still sending
689
+ * `[pool, authority]` has the program read the pool account as the config
690
+ * and fail on the discriminator, or worse pass a lookalike.
691
+ *
692
+ * Pool admin can appoint or change the manager. The contract also accepts a
693
+ * config protocol-admin signer for disable-only updates that retain the current
694
+ * manager key; the deployed protocol-admin program has no CPI wrapper for it. Combine
695
+ * `newManager = PublicKey.default` with `enabled = false` for the fully
696
+ * disabled state.
697
+ */
698
+ function buildSetRangeManagerIx(cfg, pool, params) {
699
+ const data = Buffer.concat([
700
+ CUBIC_POOL_DISC.setRangeManager,
701
+ params.newManager.toBuffer(),
702
+ encodeBool(params.enabled),
703
+ ]);
704
+ const keys = [
705
+ { pubkey: params.config, isSigner: false, isWritable: false },
706
+ { pubkey: pool, isSigner: false, isWritable: true },
707
+ { pubkey: params.authority, isSigner: true, isWritable: false },
708
+ ];
709
+ return new web3_js_1.TransactionInstruction({ programId: cfg.programs.cubicPool, keys, data });
710
+ }
711
+ /**
712
+ * `set_range_manager_config` — the range manager's movement envelope.
713
+ *
714
+ * v5.1 appended `max_leverage_bps` and `min_leverage_bps` to the argument
715
+ * list. Both are required; pass `0` to leave a bound disabled.
716
+ */
717
+ function buildSetRangeManagerConfigIx(cfg, pool, params) {
718
+ const data = Buffer.concat([
719
+ CUBIC_POOL_DISC.setRangeManagerConfig,
720
+ encodeU16(params.maxVbChangePct),
721
+ encodeU16(params.maxWeightChangePct),
722
+ encodeU32(params.minUpdateIntervalSecs),
723
+ encodeU32(params.maxLeverageBps),
724
+ encodeU32(params.minLeverageBps),
725
+ ]);
726
+ const keys = [
727
+ { pubkey: pool, isSigner: false, isWritable: true },
728
+ { pubkey: params.authority, isSigner: true, isWritable: false },
729
+ ];
730
+ return new web3_js_1.TransactionInstruction({ programId: cfg.programs.cubicPool, keys, data });
731
+ }
732
+ /**
733
+ * `range_manager_update` — sparse compare-and-swap over virtual balances
734
+ * and/or weights.
735
+ *
736
+ * Every {@link TokenChange} carries an `expectedCurrent` guard (v5.1, audit
737
+ * M-6). The program reverts the whole instruction with
738
+ * `RangeManagerStaleValue` (6061) if any entry no longer matches the stored
739
+ * value, so a bot MUST derive `expectedCurrent` and `newValue` from the
740
+ * same pool read and retry from a fresh read on failure — never re-send the
741
+ * same payload.
742
+ *
743
+ * The instruction also honours the pool kill switch now: it reverts while
744
+ * `pool_enabled` is false.
745
+ */
746
+ function buildRangeManagerUpdateIx(cfg, pool, params) {
747
+ const vbChanges = params.vbChanges ?? [];
748
+ const weightChanges = params.weightChanges ?? [];
749
+ if (vbChanges.length === 0 && weightChanges.length === 0) {
750
+ // `RangeManagerEmptyUpdate` (6044) — cheaper to catch here.
751
+ throw new Error("range_manager_update: must change at least one virtual balance or weight");
752
+ }
753
+ const data = Buffer.concat([
754
+ CUBIC_POOL_DISC.rangeManagerUpdate,
755
+ encodeVecTokenChange(vbChanges),
756
+ encodeVecTokenChange(weightChanges),
757
+ ]);
758
+ const keys = [
759
+ { pubkey: pool, isSigner: false, isWritable: true },
760
+ { pubkey: params.authority, isSigner: true, isWritable: false },
761
+ ];
762
+ return new web3_js_1.TransactionInstruction({ programId: cfg.programs.cubicPool, keys, data });
763
+ }
764
+ /** Set the pool-local swap fee (hundredths of a basis point). */
765
+ function buildSetSwapFeeRateIx(cfg, pool, authority, swapFeeRate) {
766
+ return (0, contract_instructions_1.buildContractInstruction)(cfg, "cubicPool", "set_swap_fee_rate", { swap_fee_rate: swapFeeRate }, { pool, authority });
767
+ }
768
+ /** Replace every token's sell-off policy, including both kink parameters. */
769
+ function buildSetMaxSelloffIx(cfg, pool, authority, params) {
770
+ return (0, contract_instructions_1.buildContractInstruction)(cfg, "cubicPool", "set_max_selloff", { params: params.map(p => ({
771
+ max_selloff_pct: p.maxSelloffPct, period_length: p.periodLength,
772
+ fee_threshold_pct: p.feeThresholdPct, fee_slope_low_pct: p.feeSlopeLowPct,
773
+ fee_slope_high_pct: p.feeSlopeHighPct, fee_slope_mid_pct: p.feeSlopeMidPct,
774
+ fee_kink_pct: p.feeKinkPct,
775
+ })) }, { pool, authority });
776
+ }
777
+ function buildInitiatePoolAdminTransferIx(cfg, pool, authority, newAdmin) {
778
+ return (0, contract_instructions_1.buildContractInstruction)(cfg, "cubicPool", "initiate_pool_admin_transfer", { new_admin: newAdmin }, { pool, authority });
779
+ }
780
+ function buildAcceptPoolAdminTransferIx(cfg, pool, newAdmin) {
781
+ return (0, contract_instructions_1.buildContractInstruction)(cfg, "cubicPool", "accept_pool_admin_transfer", {}, { pool, new_admin: newAdmin });
782
+ }
783
+ function buildCancelPoolAdminTransferIx(cfg, pool, authority) {
784
+ return (0, contract_instructions_1.buildContractInstruction)(cfg, "cubicPool", "cancel_pool_admin_transfer", {}, { pool, authority });
785
+ }
786
+ /** Permanently renounce the pool-local admin role. */
787
+ function buildDisablePoolAdminIx(cfg, pool, authority) {
788
+ return (0, contract_instructions_1.buildContractInstruction)(cfg, "cubicPool", "disable_pool_admin", {}, { pool, authority });
789
+ }
790
+ function buildGetPoolInfoIx(cfg, pool) {
791
+ return (0, contract_instructions_1.buildContractInstruction)(cfg, "cubicPool", "get_pool_info", {}, { pool });
792
+ }
793
+ // ============================================================
794
+ // Borsh encoding helpers (subset used above)
795
+ // ============================================================
796
+ function encodeU8(v) {
797
+ const b = Buffer.alloc(1);
798
+ requireUnsignedNumber(v, 8);
799
+ b.writeUInt8(v, 0);
800
+ return b;
801
+ }
802
+ function encodeU16(v) {
803
+ const b = Buffer.alloc(2);
804
+ requireUnsignedNumber(v, 16);
805
+ b.writeUInt16LE(v, 0);
806
+ return b;
807
+ }
808
+ function encodeU32(v) {
809
+ const b = Buffer.alloc(4);
810
+ requireUnsignedNumber(v, 32);
811
+ b.writeUInt32LE(v, 0);
812
+ return b;
813
+ }
814
+ function requireUnsignedNumber(value, bits) {
815
+ if (!Number.isSafeInteger(value) || value < 0 || value >= 2 ** bits) {
816
+ throw new Error(`Expected an unsigned ${bits}-bit integer`);
817
+ }
818
+ }
819
+ function encodeU64(v) {
820
+ if (!bn_js_1.default.isBN(v) || v.isNeg() || v.bitLength() > 64)
821
+ throw new Error("Expected an unsigned 64-bit BN");
822
+ return v.toArrayLike(Buffer, "le", 8);
823
+ }
824
+ /** Borsh `Option<u64>`: 1 tag byte (0=None, 1=Some) + u64 LE when Some. */
825
+ function encodeOptionU64(v) {
826
+ if (v === undefined || v === null)
827
+ return Buffer.from([0]);
828
+ if (typeof v === "number" && (!Number.isSafeInteger(v) || v < 0))
829
+ throw new Error("Bitmap must be a non-negative safe integer or BN");
830
+ const bn = bn_js_1.default.isBN(v) ? v : new bn_js_1.default(v);
831
+ return Buffer.concat([Buffer.from([1]), encodeU64(bn)]);
832
+ }
833
+ function encodeVecU64(vs) {
834
+ const len = Buffer.alloc(4);
835
+ len.writeUInt32LE(vs.length, 0);
836
+ return Buffer.concat([len, ...vs.map(encodeU64)]);
837
+ }
838
+ function encodeBool(v) {
839
+ if (typeof v !== "boolean")
840
+ throw new Error("Expected boolean");
841
+ return Buffer.from([v ? 1 : 0]);
842
+ }
843
+ /**
844
+ * Borsh `TokenChange { index: u8, expected_current: u64, new_value: u64 }`.
845
+ *
846
+ * Field order verified against the `TokenChange` type in
847
+ * `src/idl/cubic_pool.json`. `expected_current` sits IN THE MIDDLE — it was
848
+ * inserted between `index` and `new_value`, not appended. Encoding it last
849
+ * produces a 17-byte record the program happily parses as
850
+ * `(index, new_value=expected, expected=new)`, which either reverts as
851
+ * stale or writes the wrong value.
852
+ */
853
+ function encodeTokenChange(c) {
854
+ return Buffer.concat([encodeU8(c.index), encodeU64(c.expectedCurrent), encodeU64(c.newValue)]);
855
+ }
856
+ function encodeVecTokenChange(cs) {
857
+ const len = Buffer.alloc(4);
858
+ len.writeUInt32LE(cs.length, 0);
859
+ return Buffer.concat([len, ...cs.map(encodeTokenChange)]);
860
+ }
861
+ //# sourceMappingURL=tx-builders.js.map