@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,51 @@
1
+ /**
2
+ * Result returned by high-level client reads, quotes and transaction builders.
3
+ * Raw builders, parsers, math functions and Anchor instruction methods may
4
+ * throw. Use `if (res.ok) { ... } else { ... }` for result-based methods.
5
+ */
6
+ export type SdkResult<T> =
7
+ | { ok: true; data: T }
8
+ | { ok: false; error: SdkError };
9
+
10
+ export interface SdkError {
11
+ /** Stable machine-readable code. */
12
+ code: SdkErrorCode;
13
+ /** Human-readable message, safe to show to end users. */
14
+ humanMessage: string;
15
+ /** Original error for debugging; never render this to end users. */
16
+ cause?: unknown;
17
+ }
18
+
19
+ export type SdkErrorCode =
20
+ | "rpc_unavailable"
21
+ | "rpc_timeout"
22
+ | "rpc_rate_limited"
23
+ | "account_not_found"
24
+ | "invalid_input"
25
+ | "math_overflow"
26
+ | "parse_failure"
27
+ | "backend_unavailable"
28
+ | "backend_invalid_response"
29
+ | "insufficient_funds"
30
+ | "pool_disabled"
31
+ | "swaps_disabled"
32
+ | "selloff_window_full"
33
+ | "token_swaps_disabled"
34
+ | "unsupported_pool_state"
35
+ | "unsupported_token_extension"
36
+ | "slippage_exceeded"
37
+ | "simulation_failed"
38
+ | "tx_build_failed"
39
+ | "alt_fetch_failed"
40
+ | "auth_failed"
41
+ | "unknown";
42
+
43
+ export const ok = <T>(data: T): SdkResult<T> => ({ ok: true, data });
44
+ export const err = (
45
+ code: SdkErrorCode,
46
+ humanMessage: string,
47
+ cause?: unknown
48
+ ): SdkResult<never> => ({
49
+ ok: false,
50
+ error: { code, humanMessage, cause },
51
+ });
@@ -0,0 +1,278 @@
1
+ import { PublicKey, TransactionInstruction } from "@solana/web3.js";
2
+ import BN from "bn.js";
3
+
4
+ export interface SwapParams {
5
+ user: PublicKey;
6
+ tokenInIndex: number;
7
+ tokenOutIndex: number;
8
+ amountIn: BN;
9
+ /** Hundredths-bps; omit to use SDK config default. */
10
+ slippageHundredthsBps?: number;
11
+ /** Optional explicit minimum; overrides slippage-derived value. */
12
+ minAmountOut?: BN;
13
+ }
14
+
15
+ export interface SwapQuote {
16
+ tokenInIndex: number;
17
+ tokenOutIndex: number;
18
+ amountIn: BN;
19
+ amountOut: BN;
20
+ /** Output before the sell-off surge fee (equals amountOut + surgeFeeAmount). */
21
+ grossAmountOut?: BN;
22
+ /** Spot-based upper bound on amountOut; useful for price-impact UI. */
23
+ spotOut: BN;
24
+ /** Absolute price impact in hundredths of basis point. */
25
+ priceImpactHbps: number;
26
+ feeAmount: BN;
27
+ protocolFeeAmount: BN;
28
+ /** Minimum amount_out to pass to the swap ix given the quoted slippage. */
29
+ minAmountOut: BN;
30
+ /** Variable sell-off surge fee taken from the output. `0` when no surge. */
31
+ surgeFeeAmount: BN;
32
+ /** Static fee (in PERCENT_SCALE units) + surge pct, for UI display. */
33
+ effectiveFeePct: number;
34
+ /** Post-trade window fill in PERCENT_SCALE units. `0` when window disabled. */
35
+ windowFillPct: number;
36
+ }
37
+
38
+ export interface AddLiquidityParams {
39
+ user: PublicKey;
40
+ /**
41
+ * Per-token spend CEILING, one entry per pool token, in raw units.
42
+ *
43
+ * ⚠ As of v5.1 (audit M-4 / I-1) this is an upper bound, **not** an exact
44
+ * amount. The program computes the largest strictly-proportional basket
45
+ * that fits inside this vector, deposits that, and leaves the rest in the
46
+ * user's wallet. Passing `[100, 100]` to a 1:9 pool deposits `[11, 100]`,
47
+ * not `[100, 100]`.
48
+ *
49
+ * Consequences for callers:
50
+ * - Do not render these as "amount you will deposit". Show the quoted
51
+ * proportional basket and treat this vector as a spend limit.
52
+ * - Balance checks should validate against the ceiling (that is the most
53
+ * that can be pulled), but post-tx accounting must read the
54
+ * `LiquidityAdded` event's `tokenAmounts` for what actually moved.
55
+ * - Length must equal `pool.tokenCount` or the program reverts with
56
+ * `InvalidArrayLength` (6064).
57
+ */
58
+ tokenAmounts: BN[];
59
+ /**
60
+ * Slippage floor on BPT minted. Required and must be > 0 — this is the
61
+ * only guard on the join, since `tokenAmounts` is now a ceiling rather
62
+ * than a commitment.
63
+ */
64
+ minimumBptAmount?: BN;
65
+ }
66
+
67
+ /**
68
+ * Result of `CubicPoolClient.quoteAddLiquidity`. Mirrors
69
+ * `cubic-pool/add_liquidity.rs`: the program crops the caller's ceiling
70
+ * vector to the single limiting ratio `min_i(amount_i / actual_i)`, pulls
71
+ * `depositAmounts` and leaves `refundAmounts` in the wallet.
72
+ */
73
+ export interface AddLiquidityQuote {
74
+ /** The ceiling vector that was quoted (echo of the input). */
75
+ tokenAmounts: BN[];
76
+ /** What the program will actually pull per token (proportional basket). */
77
+ depositAmounts: BN[];
78
+ /** `tokenAmounts - depositAmounts`; stays in the user's wallet. */
79
+ refundAmounts: BN[];
80
+ /** BPT the program will mint for this basket at the synced state. */
81
+ bptOut: BN;
82
+ /** `bptOut` after the quoted slippage; pass as `minimumBptAmount`. */
83
+ minimumBptAmount: BN;
84
+ /** Index of the token that limits the basket (smallest amount/balance). */
85
+ limitingTokenIndex: number;
86
+ }
87
+
88
+ export interface RemoveLiquidityParams {
89
+ user: PublicKey;
90
+ bptAmount: BN;
91
+ minimumTokenAmounts?: BN[];
92
+ }
93
+
94
+ /**
95
+ * Single-token deposit ("zap"): deposit one token, the helper swaps it into
96
+ * the pool's proportions and joins on your behalf.
97
+ *
98
+ * Supported on pools of up to 10 tokens as of v5.1 (was 5 — the old cap was
99
+ * a heap-exhaustion limit, audit SF-3). Beyond 10 the program rejects with
100
+ * `PoolTooLargeForZap`.
101
+ *
102
+ * This deployed build does not account for Token-2022 transfer fees. The SDK
103
+ * rejects transfer-fee mints and other runtime-incompatible extensions before
104
+ * building a deposit. Plain Token-2022 mints and compatible extensions work.
105
+ */
106
+ export interface SingleTokenDepositParams {
107
+ user: PublicKey;
108
+ tokenInIndex: number;
109
+ amountIn: BN;
110
+ /**
111
+ * Used only by the off-chain quote to derive `minimumBptAmount`. There is
112
+ * no per-call slippage argument on the instruction any more — the single
113
+ * `minimumBptAmount` floor bounds the entire zap, and per-leg swaps run
114
+ * with `min_out = 0` (which is what keeps the route correct under the
115
+ * path-dependent surge fee).
116
+ */
117
+ slippageHundredthsBps?: number;
118
+ /**
119
+ * Required, must be > 0. The only slippage guard on the whole route:
120
+ * internal swaps + surge fees + the join.
121
+ */
122
+ minimumBptAmount?: BN;
123
+ }
124
+
125
+ /** Quote includes the helperBalances supplied to the quote call, or assumes
126
+ * zero balances when omitted. Re-read helper ATAs and pool state before signing. */
127
+ export interface SingleTokenDepositQuote {
128
+ tokenInIndex: number;
129
+ amountIn: BN;
130
+ /** Per-token allocations (sum = amountIn). */
131
+ allocations: BN[];
132
+ /** Per-leg expected swap out. `0` for sidelined tokens. */
133
+ expectedOuts: BN[];
134
+ /** Per-leg min_out derived from slippage. */
135
+ minOuts: BN[];
136
+ /** Estimated actual pool credits after both helper and pool proportional cropping. */
137
+ depositedAmounts: BN[];
138
+ /** Helper-held excess returned to the user after add_liquidity. */
139
+ refundAmounts: BN[];
140
+ /** Projected new BPT from the complete swap-and-join sequence at quoted state/time. */
141
+ estimatedBpt: BN;
142
+ /** Indices of tokens excluded from the deposit (actBal == 0). */
143
+ sidelinedTokenIndices: number[];
144
+ }
145
+
146
+ export interface BuiltTx {
147
+ instructions: TransactionInstruction[];
148
+ /** Accounts that should sign (user is implicit). */
149
+ extraSigners?: PublicKey[];
150
+ /** Suggested CU limit — caller decides whether to prepend ComputeBudget ix. */
151
+ suggestedCuLimit: number;
152
+ }
153
+
154
+ export interface DeployPoolParams {
155
+ payer: PublicKey;
156
+ configKey: PublicKey;
157
+ poolId: BN;
158
+ /**
159
+ * Pool's token mints, in the same order as `weightsBps`/`virtualBalances`.
160
+ * There is no `tokens` field in the on-chain `initialize_cubic_pool` ix
161
+ * data — the program derives the token set solely from remaining_accounts
162
+ * (one mint per token). The SDK still requires this array so it can build
163
+ * that remaining_accounts list in the right order.
164
+ */
165
+ tokens: PublicKey[];
166
+ weightsBps: number[];
167
+ virtualBalances: BN[];
168
+ swapFeeRate: number;
169
+ /** SPL Token program to use for the BPT mint (classic SPL Token recommended). */
170
+ bptTokenProgram?: PublicKey;
171
+ /**
172
+ * Optional creator-chosen Token-2022 banned-extensions bitmap. Omit
173
+ * (`undefined`/`null`) to inherit the config default. Set a bitmap to vet
174
+ * THIS pool's tokens against it and store it on the pool. `0` clears the
175
+ * configurable default; the hard floor and runtime restrictions still apply.
176
+ *
177
+ * ⚠ A permissive policy (un-banning PermanentDelegate / TransferHook /
178
+ * TransferFee) makes the pool drainable/abusable by the token issuer —
179
+ * surface this to the user before deploying.
180
+ */
181
+ bannedExtensions?: BN | number | null;
182
+ }
183
+
184
+ // ============================================================
185
+ // Range manager
186
+ // ============================================================
187
+
188
+ /**
189
+ * One compare-and-swap entry in a `range_manager_update`.
190
+ *
191
+ * ⚠ `expectedCurrent` was inserted **between** `index` and `newValue` in
192
+ * v5.1 (audit M-6). It is not optional padding and it is not appended at
193
+ * the end: the on-chain struct is `{ index: u8, expected_current: u64,
194
+ * new_value: u64 }` in exactly that order.
195
+ */
196
+ export interface TokenChange {
197
+ /** Token index within the pool, `0 .. tokenCount-1`. */
198
+ index: number;
199
+ /**
200
+ * The value the caller believes is currently stored for this token —
201
+ * `virtualBalance` for a `vbChanges` entry, `weightBps` (as a BN) for a
202
+ * `weightChanges` entry.
203
+ *
204
+ * The program compares this against the live value and reverts the whole
205
+ * instruction with `RangeManagerStaleValue` (6061) on any mismatch. This
206
+ * makes every update a compare-and-swap: two managers racing, or a bot
207
+ * replaying a stale quote, can no longer clobber each other's writes.
208
+ *
209
+ * It must come from the same pool read that produced `newValue`. Do not
210
+ * synthesise it, and do not carry it across a re-read — on a stale
211
+ * failure, re-read the pool and recompute both fields together.
212
+ */
213
+ expectedCurrent: BN;
214
+ /** The value to write if `expectedCurrent` still matches. */
215
+ newValue: BN;
216
+ }
217
+
218
+ export interface RangeManagerUpdateParams {
219
+ /** Must be the pool's configured `range_manager` and it must be enabled. */
220
+ authority: PublicKey;
221
+ /** Sparse virtual-balance changes. May be empty. */
222
+ vbChanges?: TokenChange[];
223
+ /**
224
+ * Sparse normalized-weight changes (basis points). May be empty. The
225
+ * resulting weight vector must still sum to 10_000.
226
+ */
227
+ weightChanges?: TokenChange[];
228
+ }
229
+
230
+ export interface SetRangeManagerParams {
231
+ /**
232
+ * Pool's governance config account. New in v5.1 (audit L-1) — it is
233
+ * PREPENDED before `pool` in the account list.
234
+ */
235
+ config: PublicKey;
236
+ /** Pool admin, or config protocol admin for disable-only with the same manager. */
237
+ authority: PublicKey;
238
+ /** Pubkey allowed to call `range_manager_update`. */
239
+ newManager: PublicKey;
240
+ /** Gate flag; `false` blocks the manager even when the pubkey is set. */
241
+ enabled: boolean;
242
+ }
243
+
244
+ export interface SetRangeManagerConfigParams {
245
+ /** Pool-admin only. */
246
+ authority: PublicKey;
247
+ /** Max per-update virtual-balance change, `PERCENT_SCALE` (10_000 = 100%). */
248
+ maxVbChangePct: number;
249
+ /** Max per-update weight change, same units. */
250
+ maxWeightChangePct: number;
251
+ minUpdateIntervalSecs: number;
252
+ /**
253
+ * Absolute UPPER bound on `virtual_balance / actual_balance`, in basis
254
+ * points (10_000 = 1.0×). `0` disables the band. New in v5.1 (M-2/SF-20).
255
+ *
256
+ * The per-update percentage caps velocity, not distance — 5% steps
257
+ * compound to ~131× in 100 updates. This is the distance limit.
258
+ */
259
+ maxLeverageBps: number;
260
+ /**
261
+ * Absolute LOWER bound on the same ratio, same units. `0` disables the
262
+ * floor. New in v5.1.
263
+ */
264
+ minLeverageBps: number;
265
+ }
266
+
267
+ /** Per-token sell-off policy, in pool order. Percent fields use 10,000 = 100%,
268
+ * except feeKinkPct, which is a whole percent (0 disables the kink).
269
+ */
270
+ export interface SelloffParams {
271
+ maxSelloffPct: number;
272
+ periodLength: number;
273
+ feeThresholdPct: number;
274
+ feeSlopeLowPct: number;
275
+ feeSlopeHighPct: number;
276
+ feeSlopeMidPct: number;
277
+ feeKinkPct: number;
278
+ }
@@ -0,0 +1,210 @@
1
+ import cubicPoolIdl from "../idl/cubic_pool.json";
2
+ import protocolAdminIdl from "../idl/protocol_admin.json";
3
+ import singleTokenLiquidityIdl from "../idl/single_token_liquidity.json";
4
+ import { SdkError, SdkErrorCode } from "../types/result";
5
+
6
+ /**
7
+ * Which on-chain program produced an error code.
8
+ *
9
+ * Anchor numbers every program's errors from 6000, so a bare custom-error
10
+ * code is ambiguous across the three Coffer programs — `6030` is
11
+ * `InvalidTokenProgram` in cubic-pool but `TokenExtensionsUnsupported` in
12
+ * single-token-liquidity. Pass this to {@link toSdkError} when you know
13
+ * which program the failing instruction belonged to; without it the
14
+ * cubic-pool table is used, which is the right default for swap / add /
15
+ * remove but WRONG for a zap or an admin call.
16
+ */
17
+ export type CofferProgram = "cubicPool" | "singleTokenLiquidity" | "protocolAdmin";
18
+
19
+ const ANCHOR_NAME_TO_SDK: Record<string, SdkErrorCode> = {
20
+ InvalidTokenCount: "invalid_input",
21
+ InvalidTokenIndex: "invalid_input",
22
+ InvalidWeights: "invalid_input",
23
+ InvalidVirtualBalances: "invalid_input",
24
+ InsufficientLiquidity: "insufficient_funds",
25
+ SlippageExceeded: "slippage_exceeded",
26
+ InvalidAmounts: "invalid_input",
27
+ InsufficientBptOut: "slippage_exceeded",
28
+ InsufficientTokensOut: "slippage_exceeded",
29
+ FeeRateMaxExceeded: "invalid_input",
30
+ ProtocolFeeRateMaxExceeded: "invalid_input",
31
+ MathOverflow: "math_overflow",
32
+ MathUnderflow: "math_overflow",
33
+ DivisionByZero: "math_overflow",
34
+ TokenMintMismatch: "invalid_input",
35
+ InvalidTokenDecimals: "invalid_input",
36
+ AmountOutExceedsBalance: "invalid_input",
37
+ InvalidBptAmount: "invalid_input",
38
+ Unauthorized: "invalid_input",
39
+ InvalidMint: "invalid_input",
40
+ PoolDisabled: "pool_disabled",
41
+ SwapsDisabled: "swaps_disabled",
42
+ MaxSelloffExceeded: "selloff_window_full",
43
+ TokenInactive: "token_swaps_disabled",
44
+ PoolMustBeDisabled: "invalid_input",
45
+ ZeroAmount: "invalid_input",
46
+ ZeroFeeAmount: "invalid_input",
47
+ BannedExtension: "invalid_input",
48
+ TokenProgramMismatch: "invalid_input",
49
+ UserTokenAccountOwnerMismatch: "invalid_input",
50
+ InitialLiquidityTooSmall: "invalid_input",
51
+ InvalidTokenProgram: "invalid_input",
52
+ InvalidVault: "invalid_input",
53
+ InvalidSourceOwner: "invalid_input",
54
+ WouldBreakRentExempt: "invalid_input",
55
+ PoolAdminDisabled: "invalid_input",
56
+ ProtocolAdminUnset: "invalid_input",
57
+ NoPendingAdmin: "invalid_input",
58
+ NotPendingAdmin: "invalid_input",
59
+ InvalidPendingAdmin: "invalid_input",
60
+ ProtocolAdminNotTreasury: "invalid_input",
61
+
62
+ // ── cubic-pool 6055–6068, new in v5.1 ────────────────────────────────
63
+ /** Mint carries a Token-2022 extension this pool's policy forbids. */
64
+ UnsupportedExtension: "invalid_input",
65
+ /** Destination requires a memo — include the SPL Memo program account. */
66
+ MemoProgramMissing: "invalid_input",
67
+ TransferFeeCalculationMismatch: "invalid_input",
68
+ /** The whole deposit was eaten by the mint's transfer fee. */
69
+ AmountFullyConsumedByTransferFee: "invalid_input",
70
+ /** Only `pool.pool_admin` may make a pool's first (seed) deposit. */
71
+ SeedDepositNotPoolAdmin: "auth_failed",
72
+ StldTokenExtensionsUnsupported: "unsupported_pool_state",
73
+ /**
74
+ * Compare-and-swap guard on `range_manager_update` failed: the pool moved
75
+ * between the read and the write. The bot must re-read the pool and retry
76
+ * with fresh `expectedCurrent` values — it is a retryable race, not a
77
+ * permanent rejection.
78
+ */
79
+ RangeManagerStaleValue: "invalid_input",
80
+ RangeManagerLeverageBandExceeded: "invalid_input",
81
+ RangeManagerOverrideMustDisable: "auth_failed",
82
+ /** An array argument's length != the pool's `token_count`. */
83
+ InvalidArrayLength: "invalid_input",
84
+ FirstDepositRequiresNonzero: "invalid_input",
85
+ TokenLivenessMismatch: "invalid_input",
86
+ /** Deposit too small to mint a single unit of BPT — increase the amount. */
87
+ DepositTooSmall: "invalid_input",
88
+ /** Pool holds no liquidity yet; the pool admin must seed it first. */
89
+ PoolNotSeeded: "unsupported_pool_state",
90
+
91
+ // ── protocol-admin ───────────────────────────────────────────────────
92
+ /** New admin cannot be the all-zero pubkey. */
93
+ InvalidAdmin: "invalid_input",
94
+
95
+ // ── single-token-liquidity 6030–6032, new in v5.1 ────────────────────
96
+ /** Zap route can't carry an armed transfer hook's extra accounts. */
97
+ TokenExtensionsUnsupported: "unsupported_pool_state",
98
+ InsufficientHelperSol: "insufficient_funds",
99
+ /** Pool exceeds `STLD_MAX_TOKENS` (10) — use `add_liquidity` instead. */
100
+ PoolTooLargeForZap: "unsupported_pool_state",
101
+ };
102
+
103
+ type AnchorIdlError = { code: number; name: string; msg: string };
104
+ type ErrorTable = Record<number, { code: SdkErrorCode; message: string; name: string }>;
105
+
106
+ function buildErrorTable(errors: AnchorIdlError[]): ErrorTable {
107
+ return Object.fromEntries(
108
+ errors.map((anchorError) => [
109
+ anchorError.code,
110
+ {
111
+ code: ANCHOR_NAME_TO_SDK[anchorError.name] ?? "invalid_input",
112
+ message: anchorError.msg,
113
+ name: anchorError.name,
114
+ },
115
+ ])
116
+ );
117
+ }
118
+
119
+ const ERROR_TABLES: Record<CofferProgram, ErrorTable> = {
120
+ cubicPool: buildErrorTable(cubicPoolIdl.errors as AnchorIdlError[]),
121
+ protocolAdmin: buildErrorTable(protocolAdminIdl.errors as AnchorIdlError[]),
122
+ singleTokenLiquidity: buildErrorTable(singleTokenLiquidityIdl.errors as AnchorIdlError[]),
123
+ };
124
+
125
+ const CONTRACT_ERROR_MAP = ERROR_TABLES.cubicPool;
126
+
127
+ /**
128
+ * Convert a raw error (possibly from Anchor, web3.js, or fetch) into a
129
+ * cleanly-typed SdkError. Used by safeCall wrappers so callers always
130
+ * get structured errors, never exceptions.
131
+ *
132
+ * @param program Which Coffer program the failing instruction belonged to.
133
+ * Anchor numbers each program's errors from 6000 independently, so the
134
+ * same code means different things in different programs. Defaults to
135
+ * `"cubicPool"` (correct for swap / add / remove liquidity). Pass
136
+ * `"singleTokenLiquidity"` for zap failures and `"protocolAdmin"` for
137
+ * treasury-routed admin calls, or the message will be plausible and wrong.
138
+ */
139
+ export function toSdkError(cause: unknown, program: CofferProgram = "cubicPool"): SdkError {
140
+ // Anchor-encoded program errors
141
+ const msg = extractMessage(cause);
142
+ const code = extractErrorCode(msg);
143
+ const table = ERROR_TABLES[program] ?? CONTRACT_ERROR_MAP;
144
+ if (code !== null && table[code]) {
145
+ return {
146
+ code: table[code].code,
147
+ humanMessage: table[code].message,
148
+ cause,
149
+ };
150
+ }
151
+ // Common network strings
152
+ if (/timed? ?out|ETIMEDOUT/i.test(msg)) {
153
+ return { code: "rpc_timeout", humanMessage: "RPC timed out", cause };
154
+ }
155
+ if (/429|rate limit/i.test(msg)) {
156
+ return { code: "rpc_rate_limited", humanMessage: "RPC rate-limited - slow down", cause };
157
+ }
158
+ if (/fetch failed|ECONNREFUSED|ENOTFOUND|Proxy error|-32056|-32052|403/i.test(msg)) {
159
+ return { code: "rpc_unavailable", humanMessage: "RPC endpoint unreachable", cause };
160
+ }
161
+ if (/Account does not exist|account not found/i.test(msg)) {
162
+ return { code: "account_not_found", humanMessage: "Account does not exist on-chain", cause };
163
+ }
164
+ if (/insufficient funds/i.test(msg)) {
165
+ return { code: "insufficient_funds", humanMessage: "Insufficient funds for this operation", cause };
166
+ }
167
+ return { code: "unknown", humanMessage: msg || "Unknown error", cause };
168
+ }
169
+
170
+ function extractMessage(e: unknown): string {
171
+ if (!e) return "";
172
+ if (typeof e === "string") return e;
173
+ if (e instanceof Error) return e.message;
174
+ if (typeof e === "object" && e !== null && "message" in e) {
175
+ return String((e as { message?: string }).message ?? "");
176
+ }
177
+ try {
178
+ return JSON.stringify(e);
179
+ } catch {
180
+ return String(e);
181
+ }
182
+ }
183
+
184
+ function extractErrorCode(msg: string): number | null {
185
+ // Anchor error format: "custom program error: 0x1774"
186
+ const hex = msg.match(/custom program error: 0x([0-9a-fA-F]+)/);
187
+ if (hex) return parseInt(hex[1], 16);
188
+ // Anchor SDK format: "Error Number: 6007"
189
+ const dec = msg.match(/Error Number:\s*(\d+)/);
190
+ if (dec) return parseInt(dec[1], 10);
191
+ return null;
192
+ }
193
+
194
+ /** @internal Exported for tests */
195
+ export function contractErrorMapForTests(program: CofferProgram = "cubicPool"): ErrorTable {
196
+ return ERROR_TABLES[program];
197
+ }
198
+
199
+ /**
200
+ * Look up a program error by its numeric code without needing an exception
201
+ * to parse. Returns `undefined` for codes the program does not define.
202
+ */
203
+ export function describeProgramError(
204
+ code: number,
205
+ program: CofferProgram = "cubicPool"
206
+ ): { name: string; message: string; sdkCode: SdkErrorCode } | undefined {
207
+ const hit = ERROR_TABLES[program]?.[code];
208
+ if (!hit) return undefined;
209
+ return { name: hit.name, message: hit.message, sdkCode: hit.code };
210
+ }