@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,687 @@
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.CubicPoolClient = void 0;
7
+ const quote_math_1 = require("./quote-math");
8
+ const spl_token_1 = require("@solana/spl-token");
9
+ const web3_js_1 = require("@solana/web3.js");
10
+ const bn_js_1 = __importDefault(require("bn.js"));
11
+ const result_1 = require("../types/result");
12
+ const RpcClient_1 = require("./RpcClient");
13
+ const poolAccount_1 = require("../parsers/poolAccount");
14
+ const mintAccount_1 = require("../parsers/mintAccount");
15
+ const events_1 = require("../parsers/events");
16
+ const pda_1 = require("../utils/pda");
17
+ const tokens_1 = require("../config/tokens");
18
+ const extensions_1 = require("../utils/extensions");
19
+ const cubicMath_1 = require("../math/cubicMath");
20
+ const fixedPoint_1 = require("../math/fixedPoint");
21
+ const weightedMath_1 = require("../math/weightedMath");
22
+ const maxSelloff_1 = require("../math/maxSelloff");
23
+ const slippage_1 = require("../math/slippage");
24
+ const singleToken_1 = require("../math/singleToken");
25
+ const maxSelloff_2 = require("../math/maxSelloff");
26
+ const config_1 = require("../config");
27
+ const tx_builders_1 = require("./tx-builders");
28
+ const SingleTokenDepositClient_1 = require("./SingleTokenDepositClient");
29
+ /**
30
+ * Per-pool client. Fetch the state with `sync()`, then call any of the
31
+ * quote / buildTx methods off the cached snapshot.
32
+ */
33
+ class CubicPoolClient {
34
+ constructor(params) {
35
+ this.config = params.config;
36
+ this.poolAddress = params.poolAddress;
37
+ this.rpc =
38
+ params.rpc instanceof RpcClient_1.RpcClient
39
+ ? params.rpc
40
+ : new RpcClient_1.RpcClient({
41
+ endpoint: params.rpc?.endpoint,
42
+ endpoints: params.rpc?.endpoints ?? (params.rpc?.endpoint ? undefined : params.config.defaults.rpcEndpoints),
43
+ fallbackEndpoints: params.rpc?.fallbackEndpoints,
44
+ apiKey: params.rpc?.apiKey,
45
+ commitment: params.rpc?.commitment ?? params.config.defaults.rpcCommitment,
46
+ timeoutMs: params.rpc?.timeoutMs ?? params.config.defaults.rpcTimeoutMs,
47
+ });
48
+ }
49
+ /** Last-fetched pool state. `undefined` before first `sync()`. */
50
+ getCached() {
51
+ return this.cache;
52
+ }
53
+ /**
54
+ * Fetch and decode the pool account + BPT mint + per-token mint decimals.
55
+ * Safe to call repeatedly; subsequent calls replace the cache.
56
+ */
57
+ async sync() {
58
+ const poolInfo = await this.rpc.getAccountInfo(this.poolAddress);
59
+ if (!poolInfo.ok)
60
+ return poolInfo;
61
+ if (poolInfo.data === null) {
62
+ return (0, result_1.err)("account_not_found", `Pool ${this.poolAddress.toBase58()} does not exist on-chain`);
63
+ }
64
+ if (!poolInfo.data.owner.equals(this.config.programs.cubicPool))
65
+ return (0, result_1.err)("parse_failure", "Pool account has an unexpected owner");
66
+ let raw;
67
+ try {
68
+ raw = (0, poolAccount_1.decodePoolAccount)(poolInfo.data.data);
69
+ }
70
+ catch (e) {
71
+ return (0, result_1.err)("parse_failure", "Could not decode pool account", e);
72
+ }
73
+ const n = raw.tokenCount;
74
+ const mintAddrs = raw.tokenMints.slice(0, n);
75
+ const [bptMint, _bptBump] = (0, pda_1.deriveBptMint)(this.config.programs.cubicPool, this.poolAddress);
76
+ const mintInfos = await this.rpc.getMultipleAccountsWithInfo([bptMint, ...mintAddrs, web3_js_1.SYSVAR_CLOCK_PUBKEY]);
77
+ if (!mintInfos.ok)
78
+ return mintInfos;
79
+ const [bptInfo, ...rest] = mintInfos.data;
80
+ const tokenMintInfos = rest.slice(0, n);
81
+ const clockInfo = rest[n];
82
+ const bptMintData = bptInfo?.data;
83
+ if (bptInfo && ![spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.TOKEN_2022_PROGRAM_ID].some(p => p.equals(bptInfo.owner)))
84
+ return (0, result_1.err)("parse_failure", "Invalid BPT token program");
85
+ if (!clockInfo || clockInfo.data.length < 40)
86
+ return (0, result_1.err)("parse_failure", "Solana Clock account missing");
87
+ const chainTimestamp = Number(clockInfo.data.readBigInt64LE(32));
88
+ const createdAt = Number(raw.createdAt.toString());
89
+ if (!Number.isSafeInteger(chainTimestamp) || !Number.isSafeInteger(createdAt)) {
90
+ return (0, result_1.err)("parse_failure", "Pool/Clock timestamp cannot be represented exactly by PoolInfo; use the raw account decoder for full i64 values");
91
+ }
92
+ if (!bptMintData) {
93
+ return (0, result_1.err)("account_not_found", "BPT mint account missing");
94
+ }
95
+ let bptMintAcc;
96
+ try {
97
+ bptMintAcc = (0, mintAccount_1.decodeMintAccount)(bptMintData);
98
+ }
99
+ catch (e) {
100
+ return (0, result_1.err)("parse_failure", "Could not decode BPT mint", e);
101
+ }
102
+ const tokens = [];
103
+ for (let i = 0; i < n; i++) {
104
+ const mintInfo = tokenMintInfos[i];
105
+ const mintRaw = mintInfo?.data;
106
+ if (mintInfo && !mintInfo.owner.equals(raw.tokenPrograms[i]))
107
+ return (0, result_1.err)("parse_failure", `Mint token program mismatch at index ${i}`);
108
+ if (!mintRaw) {
109
+ return (0, result_1.err)("account_not_found", `Mint account missing for token index ${i}`);
110
+ }
111
+ let mintAcc;
112
+ try {
113
+ mintAcc = (0, mintAccount_1.decodeMintAccount)(mintRaw);
114
+ }
115
+ catch (e) {
116
+ return (0, result_1.err)("parse_failure", `Could not decode mint ${mintAddrs[i].toBase58()}`, e);
117
+ }
118
+ const actualBalance = raw.actualBalances[i];
119
+ const virtualBalance = raw.virtualBalances[i];
120
+ if (raw.normalizedWeights[i].gt(new bn_js_1.default(Number.MAX_SAFE_INTEGER))) {
121
+ return (0, result_1.err)("parse_failure", `Weight cannot be represented exactly at token index ${i}`);
122
+ }
123
+ const concentration = virtualBalance.isZero() ? 0 : Number(actualBalance.toString()) / Number(virtualBalance.toString());
124
+ tokens.push({
125
+ index: i,
126
+ mint: raw.tokenMints[i],
127
+ tokenProgram: raw.tokenPrograms[i],
128
+ decimals: mintAcc.decimals,
129
+ weightBps: raw.normalizedWeights[i].toNumber(),
130
+ virtualBalance,
131
+ actualBalance,
132
+ protocolFeesOwed: raw.protocolFeesOwed[i],
133
+ vault: (0, pda_1.deriveAta)(this.poolAddress, raw.tokenMints[i], raw.tokenPrograms[i]),
134
+ metadata: this.config.tokens?.[raw.tokenMints[i].toBase58()] ??
135
+ (0, tokens_1.resolveKnownToken)(raw.tokenMints[i].toBase58()),
136
+ concentration,
137
+ isActive: raw.isActive[i],
138
+ maxSelloffPct: raw.maxSelloffPct[i],
139
+ maxSelloffPeriodLength: raw.maxSelloffPeriodLength[i],
140
+ variableFeeThresholdPct: raw.variableFeeThresholdPct[i],
141
+ variableFeeSlopeLowPct: raw.variableFeeSlopeLowPct[i],
142
+ variableFeeSlopeHighPct: raw.variableFeeSlopeHighPct[i],
143
+ previousSelloff: raw.previousSelloff[i],
144
+ currentSelloff: raw.currentSelloff[i],
145
+ windowStartTimestamp: raw.windowStartTimestamp[i],
146
+ selloffVbSnapshot: raw.selloffVbSnapshot[i],
147
+ variableFeeSlopeMidPct: raw.variableFeeSlopeMidPct[i],
148
+ variableFeeKinkPct: raw.variableFeeKinkPct[i],
149
+ extensions: mintAcc.extensions,
150
+ unsupportedExtensions: (0, extensions_1.unsupportedMintExtensions)(mintAcc.extensions, raw.bannedExtensions),
151
+ });
152
+ }
153
+ const info = {
154
+ address: this.poolAddress,
155
+ config: raw.config,
156
+ bump: raw.bump,
157
+ poolId: raw.poolId,
158
+ tokenCount: n,
159
+ tokens,
160
+ unsupportedTokenIndices: tokens.filter((t) => (t.unsupportedExtensions?.length ?? 0) > 0).map((t) => t.index),
161
+ bptMint,
162
+ bptTokenProgram: bptInfo.owner,
163
+ poolAdmin: raw.poolAdmin,
164
+ pendingPoolAdmin: raw.pendingPoolAdmin,
165
+ rangeManager: raw.rangeManager,
166
+ rangeManagerEnabled: raw.rangeManagerEnabled,
167
+ rangeManagerMaxVbChangePct: raw.rangeManagerMaxVbChangePct,
168
+ rangeManagerMaxWeightChangePct: raw.rangeManagerMaxWeightChangePct,
169
+ rangeManagerMinUpdateIntervalSecs: raw.rangeManagerMinUpdateIntervalSecs,
170
+ rangeManagerLastUpdated: raw.rangeManagerLastUpdated,
171
+ chainTimestamp,
172
+ bptTotalSupply: bptMintAcc.supply,
173
+ swapFeeRate: raw.swapFeeRate,
174
+ protocolFeeRate: raw.protocolFeeRate,
175
+ poolEnabled: raw.poolEnabled,
176
+ swapsEnabled: raw.swapsEnabled,
177
+ createdAt,
178
+ lookupTable: raw.lookupTable,
179
+ bannedExtensions: raw.bannedExtensions,
180
+ rangeManagerMaxLeverageBps: raw.rangeManagerMaxLeverageBps,
181
+ rangeManagerMinLeverageBps: raw.rangeManagerMinLeverageBps,
182
+ syncedAt: Date.now(),
183
+ };
184
+ this.cache = info;
185
+ this.rawAccount = raw;
186
+ return (0, result_1.ok)(info);
187
+ }
188
+ /** Derived helper PDA for this pool (used by single-token-deposit). */
189
+ helperPda() {
190
+ return (0, pda_1.deriveHelperPda)(this.config.programs.singleTokenLiquidity, this.poolAddress)[0];
191
+ }
192
+ // ---------- Quote helpers (pure, require sync()) ----------
193
+ /**
194
+ * Quote a swap. Requires the cache to be populated (call `sync()` first).
195
+ * Returns exact on-chain math result + spot bound + price impact +
196
+ * slippage-derived minAmountOut.
197
+ */
198
+ quoteSwap(tokenInIndex, tokenOutIndex, amountIn, slippageHundredthsBps, nowSeconds) {
199
+ const pool = this.requireCache();
200
+ if (!pool.ok)
201
+ return pool;
202
+ const unsupported = this.requireSupported([tokenInIndex, tokenOutIndex], "swap");
203
+ if (!unsupported.ok)
204
+ return unsupported;
205
+ const slip = slippageHundredthsBps ?? this.config.defaults.slippageHundredthsBps;
206
+ try {
207
+ const q = (0, quote_math_1.simulatePoolSwap)(pool.data, tokenInIndex, tokenOutIndex, BigInt(amountIn.toString()), (0, quote_math_1.quoteTimestamp)(pool.data, nowSeconds));
208
+ // v5 display contract: post-trade window fill and static+surge fee
209
+ // pct (span-average) — the same fields the pre-merge v5 SDK returned.
210
+ const inTok = pool.data.tokens[tokenInIndex];
211
+ let windowFillPct = 0;
212
+ let surgePct = 0;
213
+ if (q.window && q.window.maxSelloffCap > 0n) {
214
+ const scale = BigInt(config_1.PERCENT_SCALE);
215
+ const fillRaw = (q.window.effectiveSelloff * scale) / q.window.maxSelloffCap;
216
+ windowFillPct = Number(fillRaw > scale ? scale : fillRaw);
217
+ surgePct = (0, maxSelloff_2.calcSurgeFeePct)(q.window.effectiveSelloffBefore, q.window.effectiveSelloff, q.window.maxSelloffCap, inTok.variableFeeThresholdPct ?? 0, inTok.variableFeeSlopeLowPct ?? 0, inTok.variableFeeSlopeMidPct ?? 0, inTok.variableFeeSlopeHighPct ?? 0, inTok.variableFeeKinkPct ?? 0);
218
+ }
219
+ const effectiveFeePct = (pool.data.swapFeeRate * config_1.PERCENT_SCALE) / config_1.SWAP_FEE_PRECISION + surgePct;
220
+ return (0, result_1.ok)({
221
+ tokenInIndex,
222
+ tokenOutIndex,
223
+ amountIn,
224
+ amountOut: new bn_js_1.default(q.amountOut.toString()),
225
+ grossAmountOut: new bn_js_1.default(q.grossAmountOut.toString()),
226
+ surgeFeeAmount: new bn_js_1.default(q.surgeFeeAmount.toString()),
227
+ feeAmount: new bn_js_1.default(q.feeAmount.toString()),
228
+ protocolFeeAmount: new bn_js_1.default(q.protocolFeeAmount.toString()),
229
+ spotOut: new bn_js_1.default(q.spotOut.toString()),
230
+ // Impact excludes fees and the surge charge (industry standard):
231
+ // measured against the GROSS curve output.
232
+ priceImpactHbps: (0, slippage_1.priceImpactHbps)(q.spotOut, q.grossAmountOut),
233
+ minAmountOut: new bn_js_1.default((0, slippage_1.applySlippage)(q.amountOut, slip).toString()),
234
+ effectiveFeePct,
235
+ windowFillPct,
236
+ });
237
+ }
238
+ catch (e) {
239
+ const msg = e instanceof Error ? e.message : String(e);
240
+ // Keep the specific v5 error codes existing consumers rely on.
241
+ const code = msg === "MaxSelloffExceeded" ? "selloff_window_full"
242
+ : msg === "TokenInactive" ? "token_swaps_disabled"
243
+ : msg === "PoolDisabled" ? "pool_disabled"
244
+ : msg === "SwapsDisabled" ? "swaps_disabled"
245
+ : "invalid_input";
246
+ const human = code === "selloff_window_full"
247
+ ? "Token max-selloff threshold exceeded for current window"
248
+ : `Swap quote failed: ${msg}`;
249
+ return (0, result_1.err)(code, human, e);
250
+ }
251
+ }
252
+ /**
253
+ * Read-only status of a token's max-selloff window at `now`
254
+ * (default: current unix seconds). Requires a prior `sync()`.
255
+ */
256
+ getSelloffWindowStatus(tokenIndex, now) {
257
+ const poolRes = this.requireCache();
258
+ if (!poolRes.ok)
259
+ return poolRes;
260
+ const raw = this.rawAccount;
261
+ if (!raw) {
262
+ return (0, result_1.err)("invalid_input", "Call `sync()` first to populate window state");
263
+ }
264
+ const pool = poolRes.data;
265
+ if (tokenIndex < 0 || tokenIndex >= pool.tokens.length) {
266
+ return (0, result_1.err)("invalid_input", "Invalid tokenIndex");
267
+ }
268
+ const i = tokenIndex;
269
+ return (0, result_1.ok)((0, maxSelloff_2.computeSelloffWindow)({
270
+ maxSelloffPct: raw.maxSelloffPct[i],
271
+ periodLength: raw.maxSelloffPeriodLength[i],
272
+ previousSelloff: raw.previousSelloff[i],
273
+ currentSelloff: raw.currentSelloff[i],
274
+ windowStartTimestamp: raw.windowStartTimestamp[i],
275
+ selloffVbSnapshot: raw.selloffVbSnapshot[i],
276
+ virtualBalance: pool.tokens[i].virtualBalance,
277
+ now: now ?? Math.floor(Date.now() / 1000),
278
+ }));
279
+ }
280
+ /**
281
+ * Quote a single-token deposit: split amountIn by W-based shares, quote
282
+ * each swap leg, then execute the helper cap and proportional-join math.
283
+ * `helperBalances` supplies existing helper ATA holdings (defaults to zero).
284
+ * Pass them when the helper contains donations or dust; all leftovers are refunded.
285
+ */
286
+ quoteSingleTokenDeposit(tokenInIndex, amountIn, slippageHundredthsBps, nowSeconds, helperBalances) {
287
+ const poolRes = this.requireCache();
288
+ if (!poolRes.ok)
289
+ return poolRes;
290
+ const pool = poolRes.data;
291
+ if (!Number.isInteger(tokenInIndex) || tokenInIndex < 0 || tokenInIndex >= pool.tokens.length) {
292
+ return (0, result_1.err)("invalid_input", "Invalid tokenInIndex");
293
+ }
294
+ if (!pool.poolEnabled)
295
+ return (0, result_1.err)("pool_disabled", "Pool is disabled");
296
+ if (!pool.swapsEnabled)
297
+ return (0, result_1.err)("invalid_input", "SwapsDisabled");
298
+ if (pool.bptTotalSupply.isZero())
299
+ return (0, result_1.err)("invalid_input", "PoolNotSeeded");
300
+ if (pool.tokens[tokenInIndex].actualBalance.isZero())
301
+ return (0, result_1.err)("invalid_input", "Input token is sidelined");
302
+ const unsupported = this.requireSupported(pool.tokens.filter(t => !t.actualBalance.isZero()).map(t => t.index), "deposit_single_token");
303
+ if (!unsupported.ok)
304
+ return unsupported;
305
+ const slip = slippageHundredthsBps ?? this.config.defaults.slippageHundredthsBps;
306
+ try {
307
+ const input = (0, quote_math_1.u64)(BigInt(amountIn.toString()), "amountIn");
308
+ if (input === 0n)
309
+ return (0, result_1.err)("invalid_input", "amountIn must be positive");
310
+ const now = (0, quote_math_1.quoteTimestamp)(pool, nowSeconds);
311
+ const actualBalances = pool.tokens.map(t => BigInt(t.actualBalance.toString()));
312
+ const virtualBalances = pool.tokens.map(t => BigInt(t.virtualBalance.toString()));
313
+ const alloc = (0, singleToken_1.computeAllocations)({ actualBalances, virtualBalances, weightsBps: pool.tokens.map(t => t.weightBps), amountIn: input, tokenInIndex });
314
+ if (alloc.allocations.some((a, i) => actualBalances[i] > 0n && a === 0n))
315
+ throw new Error("AmountTooSmall");
316
+ const simulated = { ...pool, tokens: pool.tokens.map(t => ({ ...t })) };
317
+ if (helperBalances && helperBalances.length !== pool.tokenCount)
318
+ throw new Error("Invalid helper balance vector length");
319
+ const helper = pool.tokens.map((_, i) => (0, quote_math_1.u64)(BigInt(helperBalances?.[i].toString() ?? "0"), "helper balance"));
320
+ const helperSupport = this.requireSupported(helper.flatMap((a, i) => a > 0n ? [i] : []), "helper refund");
321
+ if (!helperSupport.ok)
322
+ return helperSupport;
323
+ (0, quote_math_1.u64)(helper[tokenInIndex] + input, "helper input balance");
324
+ const expectedOuts = pool.tokens.map(() => new bn_js_1.default(0));
325
+ const minOuts = pool.tokens.map(() => new bn_js_1.default(0));
326
+ let remainingInput = input;
327
+ for (let i = 0; i < pool.tokenCount; i++) {
328
+ if (i === tokenInIndex || alloc.allocations[i] === 0n)
329
+ continue;
330
+ const q = (0, quote_math_1.simulatePoolSwap)(simulated, tokenInIndex, i, alloc.allocations[i], now, true);
331
+ remainingInput -= alloc.allocations[i];
332
+ helper[i] = (0, quote_math_1.u64)(helper[i] + q.amountOut, "helper output balance");
333
+ expectedOuts[i] = new bn_js_1.default(q.amountOut.toString());
334
+ minOuts[i] = new bn_js_1.default((0, slippage_1.applySlippage)(q.amountOut, slip).toString());
335
+ }
336
+ helper[tokenInIndex] = (0, quote_math_1.u64)(helper[tokenInIndex] + remainingInput, "helper input balance");
337
+ const postActual = simulated.tokens.map(t => BigInt(t.actualBalance.toString()));
338
+ const capped = (0, singleToken_1.capDepositAmountsToLpRatio)({ helperBalances: helper, actualBalances: postActual });
339
+ // The helper's integer ratio cap is followed by cubic-pool's fixed-point proportional join.
340
+ if (capped.depositAmounts.some((v, i) => (v === 0n) !== (postActual[i] === 0n)))
341
+ throw new Error("TokenLivenessMismatch");
342
+ let ratio = null;
343
+ for (let i = 0; i < pool.tokenCount; i++)
344
+ if (postActual[i] > 0n) {
345
+ const r = (0, fixedPoint_1.divDown)(capped.depositAmounts[i], postActual[i]);
346
+ ratio = ratio === null || r < ratio ? r : ratio;
347
+ }
348
+ if (ratio === null)
349
+ throw new Error("Pool has no live tokens");
350
+ const deposited = postActual.map(a => (0, fixedPoint_1.mulDown)(a, ratio));
351
+ const estBpt = (0, cubicMath_1.calcBptOutGivenExactTokensIn)(postActual, capped.depositAmounts, BigInt(pool.bptTotalSupply.toString()));
352
+ if (estBpt === 0n || deposited.some((d, i) => postActual[i] > 0n && d === 0n))
353
+ throw new Error("DepositTooSmall");
354
+ (0, quote_math_1.u64)(estBpt + BigInt(pool.bptTotalSupply.toString()), "BPT supply");
355
+ for (let i = 0; i < pool.tokenCount; i++) {
356
+ (0, quote_math_1.u64)(postActual[i] + deposited[i], "reserve balance");
357
+ const vb = BigInt(simulated.tokens[i].virtualBalance.toString());
358
+ (0, quote_math_1.u64)(vb + (0, fixedPoint_1.mulDown)(vb, ratio), "virtual balance");
359
+ }
360
+ this.validateDepositWindows(simulated, ratio);
361
+ return (0, result_1.ok)({ tokenInIndex, amountIn, allocations: alloc.allocations.map(a => new bn_js_1.default(a.toString())),
362
+ expectedOuts, minOuts, depositedAmounts: deposited.map(a => new bn_js_1.default(a.toString())),
363
+ refundAmounts: helper.map((a, i) => new bn_js_1.default((a - deposited[i]).toString())),
364
+ estimatedBpt: new bn_js_1.default(estBpt.toString()),
365
+ sidelinedTokenIndices: pool.tokens.filter(t => t.actualBalance.isZero()).map(t => t.index),
366
+ });
367
+ }
368
+ catch (e) {
369
+ return (0, result_1.err)("invalid_input", `Single-token deposit quote failed: ${e instanceof Error ? e.message : String(e)}`, e);
370
+ }
371
+ }
372
+ /** Seed deposit: creator-only, exact amounts, BPT derived from virtual reserves. */
373
+ quoteSeedDeposit(user, tokenAmounts, slippageHundredthsBps) {
374
+ const state = this.requireCache();
375
+ if (!state.ok)
376
+ return state;
377
+ const pool = state.data;
378
+ if (!pool.poolEnabled)
379
+ return (0, result_1.err)("pool_disabled", "Pool is disabled");
380
+ if (!pool.bptTotalSupply.isZero())
381
+ return (0, result_1.err)("invalid_input", "Pool already seeded; use quoteAddLiquidity");
382
+ if (!pool.poolAdmin)
383
+ return (0, result_1.err)("invalid_input", "Pool admin missing: sync the pool before quoting");
384
+ if (pool.poolAdmin.equals(web3_js_1.PublicKey.default))
385
+ return (0, result_1.err)("invalid_input", "PoolAdminDisabled");
386
+ if (!pool.poolAdmin.equals(user))
387
+ return (0, result_1.err)("invalid_input", "SeedDepositNotPoolAdmin");
388
+ if (tokenAmounts.length !== pool.tokenCount || tokenAmounts.some(a => a.isNeg() || a.bitLength() > 64))
389
+ return (0, result_1.err)("invalid_input", "Expected one u64 amount per token");
390
+ if (tokenAmounts.every(a => a.isZero()))
391
+ return (0, result_1.err)("invalid_input", "FirstDepositRequiresNonzero");
392
+ const supported = this.requireSupported(tokenAmounts.flatMap((a, i) => a.isZero() ? [] : [i]), "seed deposit");
393
+ if (!supported.ok)
394
+ return supported;
395
+ try {
396
+ const virtual = pool.tokens.map(t => BigInt(t.virtualBalance.toString()));
397
+ const balances = virtual.every(v => v === 0n) ? tokenAmounts.map(a => BigInt(a.toString())) : virtual;
398
+ const bptOut = (0, quote_math_1.u64)((0, weightedMath_1.calculateInvariant)(balances, pool.tokens.map(t => t.weightBps), pool.tokens.map(t => t.decimals)), "BPT amount");
399
+ if (bptOut < 1000n)
400
+ return (0, result_1.err)("invalid_input", "InitialLiquidityTooSmall");
401
+ return (0, result_1.ok)({ tokenAmounts, depositAmounts: tokenAmounts.map(a => a.clone()), refundAmounts: tokenAmounts.map(() => new bn_js_1.default(0)),
402
+ bptOut: new bn_js_1.default(bptOut.toString()), minimumBptAmount: new bn_js_1.default((0, slippage_1.applySlippage)(bptOut, slippageHundredthsBps ?? this.config.defaults.slippageHundredthsBps).toString()),
403
+ limitingTokenIndex: -1 });
404
+ }
405
+ catch (e) {
406
+ return (0, result_1.err)("math_overflow", "Seed-deposit quote failed", e);
407
+ }
408
+ }
409
+ /**
410
+ * Proportional-join quote. `tokenAmounts` is a CEILING (v5.1, audit
411
+ * M-4 / I-1): the program takes the largest strictly-proportional basket
412
+ * that fits inside it and leaves the rest in the wallet. This returns the
413
+ * basket it will actually pull, the BPT it will mint, and a
414
+ * slippage-derived `minimumBptAmount` to pass to `buildAddLiquidityTx`.
415
+ *
416
+ * Not applicable to the seed deposit (BPT supply == 0): the program
417
+ * mints invariant-based BPT there and takes the full amounts.
418
+ */
419
+ quoteAddLiquidity(tokenAmounts, slippageHundredthsBps) {
420
+ const poolRes = this.requireCache();
421
+ if (!poolRes.ok)
422
+ return poolRes;
423
+ const pool = poolRes.data;
424
+ if (tokenAmounts.length !== pool.tokenCount) {
425
+ return (0, result_1.err)("invalid_input", "tokenAmounts length must equal pool.tokenCount");
426
+ }
427
+ if (pool.bptTotalSupply.isZero()) {
428
+ return (0, result_1.err)("invalid_input", "Pool has zero BPT supply; use quoteSeedDeposit");
429
+ }
430
+ if (!pool.poolEnabled)
431
+ return (0, result_1.err)("pool_disabled", "Pool is disabled");
432
+ if (tokenAmounts.some((a) => a.isNeg() || a.bitLength() > 64)) {
433
+ return (0, result_1.err)("invalid_input", "tokenAmounts must be u64 values");
434
+ }
435
+ if (pool.tokens.some((t, i) => t.actualBalance.isZero() !== tokenAmounts[i].isZero())) {
436
+ return (0, result_1.err)("invalid_input", "TokenLivenessMismatch: live tokens need positive amounts; zero-balance tokens need zero amounts");
437
+ }
438
+ const unsupported = this.requireSupported(pool.tokens.filter(t => !t.actualBalance.isZero()).map(t => t.index), "add_liquidity");
439
+ if (!unsupported.ok)
440
+ return unsupported;
441
+ const slip = slippageHundredthsBps ?? this.config.defaults.slippageHundredthsBps;
442
+ try {
443
+ // Same inputs as cubic-pool/add_liquidity.rs: raw stored actual
444
+ // balances, ratio = min over tokens with actual > 0.
445
+ const bals = pool.tokens.map((t) => BigInt(t.actualBalance.toString()));
446
+ const amounts = tokenAmounts.map((a) => BigInt(a.toString()));
447
+ const supply = BigInt(pool.bptTotalSupply.toString());
448
+ let ratioMin = null;
449
+ let limiting = -1;
450
+ for (let i = 0; i < bals.length; i++) {
451
+ if (bals[i] === 0n)
452
+ continue;
453
+ const r = (0, fixedPoint_1.divDown)(amounts[i], bals[i]);
454
+ if (ratioMin === null || r < ratioMin) {
455
+ ratioMin = r;
456
+ limiting = i;
457
+ }
458
+ }
459
+ if (ratioMin === null)
460
+ return (0, result_1.err)("invalid_input", "Pool has no token with a non-zero balance");
461
+ const bptOut = (0, cubicMath_1.calcBptOutGivenExactTokensIn)(bals, amounts, supply);
462
+ if (bptOut === 0n || bptOut > ((1n << 64n) - 1n) || supply + bptOut > ((1n << 64n) - 1n))
463
+ return (0, result_1.err)("invalid_input", "DepositTooSmall or BPT supply overflow");
464
+ const deposit = bals.map((b) => (b === 0n ? 0n : (0, fixedPoint_1.mulDown)(b, ratioMin)));
465
+ if (deposit.some((d, i) => bals[i] > 0n && d === 0n || bals[i] + d > ((1n << 64n) - 1n)))
466
+ return (0, result_1.err)("invalid_input", "DepositTooSmall or reserve overflow");
467
+ if (pool.tokens.some(t => BigInt(t.virtualBalance.toString()) + (0, fixedPoint_1.mulDown)(BigInt(t.virtualBalance.toString()), ratioMin) > ((1n << 64n) - 1n)))
468
+ return (0, result_1.err)("math_overflow", "Virtual balance overflow");
469
+ this.validateDepositWindows(pool, ratioMin);
470
+ const refund = amounts.map((a, i) => a - deposit[i]);
471
+ return (0, result_1.ok)({
472
+ tokenAmounts,
473
+ depositAmounts: deposit.map((d) => new bn_js_1.default(d.toString())),
474
+ refundAmounts: refund.map((r) => new bn_js_1.default(r.toString())),
475
+ bptOut: new bn_js_1.default(bptOut.toString()),
476
+ minimumBptAmount: new bn_js_1.default((0, slippage_1.applySlippage)(bptOut, slip).toString()),
477
+ limitingTokenIndex: limiting,
478
+ });
479
+ }
480
+ catch (e) {
481
+ return (0, result_1.err)("math_overflow", "Add-liquidity quote failed", e);
482
+ }
483
+ }
484
+ /** Proportional-withdraw quote for a given BPT amount. */
485
+ quoteRemove(bptIn) {
486
+ const poolRes = this.requireCache();
487
+ if (!poolRes.ok)
488
+ return poolRes;
489
+ const pool = poolRes.data;
490
+ if (pool.bptTotalSupply.isZero()) {
491
+ return (0, result_1.err)("invalid_input", "Pool has zero BPT supply");
492
+ }
493
+ if (!pool.poolEnabled)
494
+ return (0, result_1.err)("pool_disabled", "Pool is disabled");
495
+ try {
496
+ // Match cubic-pool/remove_liquidity.rs exactly: it computes
497
+ // token_amounts via divDown(bpt_amount, bpt_supply), then mulDown(actual, ratio)
498
+ // against the raw stored actual (no protocol-fee subtraction). Use
499
+ // the same input here so the SDK quote equals what the contract
500
+ // actually transfers.
501
+ const bals = pool.tokens.map((t) => BigInt(t.actualBalance.toString()));
502
+ const supply = BigInt(pool.bptTotalSupply.toString());
503
+ const requested = BigInt(bptIn.toString());
504
+ if (requested <= 0n || requested > supply || supply <= 1000n)
505
+ return (0, result_1.err)("invalid_input", "InvalidBptAmount");
506
+ const effective = requested < supply - 1000n ? requested : supply - 1000n;
507
+ const outs = (0, cubicMath_1.calcTokensOutGivenBptIn)(bals, effective, supply);
508
+ const supported = this.requireSupported(outs.flatMap((amount, i) => amount > 0n ? [i] : []), "remove_liquidity");
509
+ if (!supported.ok)
510
+ return supported;
511
+ return (0, result_1.ok)({ tokenOuts: outs.map((o) => new bn_js_1.default(o.toString())), effectiveBptIn: new bn_js_1.default(effective.toString()) });
512
+ }
513
+ catch (e) {
514
+ return (0, result_1.err)("math_overflow", "Remove quote failed", e);
515
+ }
516
+ }
517
+ // ---------- Transaction builders ----------
518
+ //
519
+ // For external SDK users, deposit/swap/remove are one-call operations via
520
+ // the CubicPoolClient. The `singleTokenDeposit` field is a proxy into the
521
+ // dedicated SingleTokenDepositClient as the user expects: importing this
522
+ // class is enough, no need to know the helper program exists.
523
+ /** Build a swap transaction. Requires `sync()` first. */
524
+ buildSwapTx(params) {
525
+ const poolRes = this.requireCache();
526
+ if (!poolRes.ok)
527
+ return poolRes;
528
+ const unsupported = this.requireSupported([params.tokenInIndex, params.tokenOutIndex], "swap");
529
+ if (!unsupported.ok)
530
+ return unsupported;
531
+ const slip = params.slippageHundredthsBps ?? this.config.defaults.slippageHundredthsBps;
532
+ let minOut;
533
+ if (params.minAmountOut) {
534
+ minOut = params.minAmountOut;
535
+ }
536
+ else {
537
+ const q = this.quoteSwap(params.tokenInIndex, params.tokenOutIndex, params.amountIn, slip);
538
+ if (!q.ok) {
539
+ return (0, result_1.err)(q.error.code, `Cannot build swap without minAmountOut: ${q.error.humanMessage}`, q.error.cause);
540
+ }
541
+ minOut = q.data.minAmountOut;
542
+ }
543
+ try {
544
+ const tx = (0, tx_builders_1.buildSwapTx)(this.config, poolRes.data, { ...params, minAmountOut: minOut });
545
+ return (0, result_1.ok)(tx);
546
+ }
547
+ catch (e) {
548
+ return (0, result_1.err)("tx_build_failed", "Failed to build swap tx", e);
549
+ }
550
+ }
551
+ /** Build a proportional add-liquidity transaction. Requires `sync()` first. */
552
+ buildAddLiquidityTx(params) {
553
+ const poolRes = this.requireCache();
554
+ if (!poolRes.ok)
555
+ return poolRes;
556
+ if (params.tokenAmounts.length !== poolRes.data.tokenCount) {
557
+ return (0, result_1.err)("invalid_input", "tokenAmounts length must equal pool.tokenCount");
558
+ }
559
+ const unsupported = this.requireSupported(params.tokenAmounts.flatMap((a, i) => a.isZero() ? [] : [i]), "add_liquidity");
560
+ if (!unsupported.ok)
561
+ return unsupported;
562
+ try {
563
+ return (0, result_1.ok)((0, tx_builders_1.buildAddLiquidityTx)(this.config, poolRes.data, params));
564
+ }
565
+ catch (e) {
566
+ return (0, result_1.err)("tx_build_failed", "Failed to build add_liquidity tx", e);
567
+ }
568
+ }
569
+ /** Build a proportional remove-liquidity transaction. Requires `sync()` first. */
570
+ buildRemoveLiquidityTx(params) {
571
+ const poolRes = this.requireCache();
572
+ if (!poolRes.ok)
573
+ return poolRes;
574
+ if (!params.minimumTokenAmounts) {
575
+ return (0, result_1.err)("invalid_input", "minimumTokenAmounts is required (one per token). Derive per-token floors from quoteRemove(); pass explicit zeros only to intentionally disable slippage protection.");
576
+ }
577
+ if (params.minimumTokenAmounts.length !== poolRes.data.tokenCount) {
578
+ return (0, result_1.err)("invalid_input", "minimumTokenAmounts length must equal pool.tokenCount");
579
+ }
580
+ const quote = this.quoteRemove(params.bptAmount);
581
+ if (!quote.ok)
582
+ return quote;
583
+ try {
584
+ return (0, result_1.ok)((0, tx_builders_1.buildRemoveLiquidityTx)(this.config, poolRes.data, params));
585
+ }
586
+ catch (e) {
587
+ return (0, result_1.err)("tx_build_failed", "Failed to build remove_liquidity tx", e);
588
+ }
589
+ }
590
+ /**
591
+ * Build a single-token deposit transaction (swaps + add_liquidity via the
592
+ * helper program + BPT forward to user). Requires `sync()` first.
593
+ */
594
+ buildSingleTokenDepositTx(params) {
595
+ const poolRes = this.requireCache();
596
+ if (!poolRes.ok)
597
+ return poolRes;
598
+ if (params.amountIn.lten(0))
599
+ return (0, result_1.err)("invalid_input", "amountIn must be > 0");
600
+ const unsupported = this.requireSupported(poolRes.data.tokens.filter(t => !t.actualBalance.isZero() || t.index === params.tokenInIndex).map(t => t.index), "deposit_single_token");
601
+ if (!unsupported.ok)
602
+ return unsupported;
603
+ try {
604
+ return (0, result_1.ok)((0, tx_builders_1.buildSingleTokenDepositTx)(this.config, poolRes.data, params));
605
+ }
606
+ catch (e) {
607
+ return (0, result_1.err)("tx_build_failed", "Failed to build single-token deposit tx", e);
608
+ }
609
+ }
610
+ /**
611
+ * Single-token deposit split into `{ setup, deposit }` transactions.
612
+ *
613
+ * Required for pools beyond a handful of tokens: the ATA creates and the
614
+ * zap compete for the same 64-frame instruction-trace budget, and at
615
+ * N=10 the zap alone uses 52 of them. Send `setup` (idempotent, safe to
616
+ * repeat), wait for confirmation, then send `deposit` compiled against
617
+ * the pool's ALT via `compileBuiltTx`.
618
+ */
619
+ buildSingleTokenDepositTxs(params) {
620
+ const poolRes = this.requireCache();
621
+ if (!poolRes.ok)
622
+ return poolRes;
623
+ if (params.amountIn.lten(0))
624
+ return (0, result_1.err)("invalid_input", "amountIn must be > 0");
625
+ const unsupported = this.requireSupported(poolRes.data.tokens.filter(t => !t.actualBalance.isZero() || t.index === params.tokenInIndex).map(t => t.index), "deposit_single_token");
626
+ if (!unsupported.ok)
627
+ return unsupported;
628
+ try {
629
+ return (0, result_1.ok)((0, tx_builders_1.buildSingleTokenDepositTxs)(this.config, poolRes.data, params));
630
+ }
631
+ catch (e) {
632
+ return (0, result_1.err)("tx_build_failed", "Failed to build single-token deposit txs", e);
633
+ }
634
+ }
635
+ /** Proxy into the dedicated SingleTokenDepositClient for consumers who
636
+ * want the quote/build methods exposed on a focused object. */
637
+ get singleTokenDeposit() {
638
+ if (!this._std) {
639
+ this._std = new SingleTokenDepositClient_1.SingleTokenDepositClient({
640
+ config: this.config,
641
+ poolAddress: this.poolAddress,
642
+ poolClient: this,
643
+ });
644
+ }
645
+ return this._std;
646
+ }
647
+ // ---------- Event helpers ----------
648
+ /** Parse logs from a confirmed transaction into typed events. */
649
+ parseEventsFromLogs(logs) {
650
+ return (0, events_1.parseCubicPoolEvents)(logs);
651
+ }
652
+ // ---------- Internals ----------
653
+ /**
654
+ * Refuse when any of `indices` (or every token, for `"all"`) has an
655
+ * unsupported Token-2022 extension. The program would revert inside the
656
+ * token program on transfer, so failing here saves the user a fee.
657
+ */
658
+ requireSupported(indices, op) {
659
+ const pool = this.cache;
660
+ if (!pool)
661
+ return (0, result_1.ok)(undefined);
662
+ const idx = indices === "all" ? pool.tokens.map((t) => t.index) : indices;
663
+ const bad = idx.map((i) => pool.tokens[i]).filter((t) => t && (t.unsupportedExtensions?.length ?? 0) > 0);
664
+ if (bad.length === 0)
665
+ return (0, result_1.ok)(undefined);
666
+ return (0, result_1.err)("unsupported_token_extension", `${op} refused: ${bad.map(extensions_1.describeUnsupportedToken).join("; ")}. ` +
667
+ "The cubic-pool program cannot transfer such tokens; the transaction would revert on-chain.");
668
+ }
669
+ validateDepositWindows(pool, ratio) {
670
+ for (const token of pool.tokens) {
671
+ if (token.maxSelloffPct === 0)
672
+ continue;
673
+ if ([token.previousSelloff, token.currentSelloff, token.selloffVbSnapshot, token.windowStartTimestamp].some(v => v === undefined))
674
+ throw new Error("Selloff state missing: sync the pool before quoting");
675
+ (0, maxSelloff_1.rescaleSelloffWindow)({ previousSelloff: BigInt(token.previousSelloff.toString()), currentSelloff: BigInt(token.currentSelloff.toString()),
676
+ selloffVbSnapshot: BigInt(token.selloffVbSnapshot.toString()), windowStartTimestamp: BigInt(token.windowStartTimestamp.toString()) }, ratio, true, token.maxSelloffPct);
677
+ }
678
+ }
679
+ requireCache() {
680
+ if (!this.cache) {
681
+ return (0, result_1.err)("invalid_input", "Call `sync()` first to populate pool state");
682
+ }
683
+ return (0, result_1.ok)(this.cache);
684
+ }
685
+ }
686
+ exports.CubicPoolClient = CubicPoolClient;
687
+ //# sourceMappingURL=CubicPoolClient.js.map