@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,251 @@
1
+ import { ONE, assertU64, assertInteger, mulDivDown } from "./fixedPoint";
2
+ import { calcOutGivenIn } from "./cubicMath";
3
+ import { lpBalances, calculateSwapFee, calculateProtocolFee } from "./slippage";
4
+
5
+ export interface AllocationResult {
6
+ /** Per-token integer amount allocations; sum = amountIn. */
7
+ allocations: bigint[];
8
+ /** Scaled W values, for inspection/debugging. */
9
+ wScaled: bigint[];
10
+ /** Sum of wScaled. */
11
+ sumW: bigint;
12
+ }
13
+
14
+ /**
15
+ * Port of stld `compute_allocations` from
16
+ * `contracts/programs/single-token-liquidity/src/math.rs`.
17
+ *
18
+ * W_i = weight_i * min(actual_i, virtual_i) / virtual_i.
19
+ * Division order matches the two integer divisions on-chain.
20
+ * share_i = W_i / ΣW. amount_alloc[i] = amountIn * share_i.
21
+ *
22
+ * Integer truncation of per-token amounts routes the remainder to the
23
+ * input-token slot so Σ allocations == amountIn exactly.
24
+ */
25
+ export function computeAllocations(params: {
26
+ actualBalances: bigint[];
27
+ virtualBalances: bigint[];
28
+ weightsBps: number[];
29
+ amountIn: bigint;
30
+ tokenInIndex: number;
31
+ }): AllocationResult {
32
+ const { actualBalances, virtualBalances, weightsBps, amountIn, tokenInIndex } = params;
33
+ assertU64(amountIn, "amountIn");
34
+ const n = actualBalances.length;
35
+ if (n !== virtualBalances.length || n !== weightsBps.length) {
36
+ throw new Error("computeAllocations: length mismatch");
37
+ }
38
+ if (!Number.isInteger(tokenInIndex) || tokenInIndex < 0 || tokenInIndex >= n) {
39
+ throw new Error("computeAllocations: tokenInIndex out of range");
40
+ }
41
+ const wScaled: bigint[] = [];
42
+ for (let i = 0; i < n; i++) {
43
+ if (virtualBalances[i] <= 0n) {
44
+ throw new Error(`computeAllocations: virtualBalance[${i}] must be positive`);
45
+ }
46
+ assertU64(actualBalances[i]); assertU64(virtualBalances[i]);
47
+ assertInteger(weightsBps[i], 0, 10000, "weight");
48
+ const effectiveActual = actualBalances[i] < virtualBalances[i] ? actualBalances[i] : virtualBalances[i];
49
+ const scaled = mulDivDown(effectiveActual, ONE, virtualBalances[i]);
50
+ const w = mulDivDown(scaled, BigInt(weightsBps[i]), 10_000n);
51
+ wScaled.push(w);
52
+ }
53
+ const sumW = wScaled.reduce((a, b) => a + b, 0n);
54
+ if (sumW === 0n) {
55
+ throw new Error("computeAllocations: pool not seeded (ΣW = 0)");
56
+ }
57
+ const allocations: bigint[] = wScaled.map((w) => (amountIn * w) / sumW);
58
+ const sumAlloc = allocations.reduce((a, b) => a + b, 0n);
59
+ if (sumAlloc < amountIn) {
60
+ allocations[tokenInIndex] += amountIn - sumAlloc;
61
+ }
62
+ return { allocations, wScaled, sumW };
63
+ }
64
+
65
+ function swapFeeAmount(amount: bigint, swapFeeRate: number): bigint {
66
+ return calculateSwapFee(amount, swapFeeRate);
67
+ }
68
+
69
+ function amountAfterSwapFee(amount: bigint, swapFeeRate: number): bigint {
70
+ const fee = swapFeeAmount(amount, swapFeeRate);
71
+ if (swapFeeRate > 0 && fee === 0n) {
72
+ throw new Error("computeTwoTokenOptimalAllocations: swap amount too small, fee rounds to zero");
73
+ }
74
+ return amount - fee;
75
+ }
76
+
77
+ function protocolFeeAmount(swapFee: bigint, protocolFeeRate: number): bigint {
78
+ return calculateProtocolFee(swapFee, protocolFeeRate);
79
+ }
80
+
81
+ function scoreTwoTokenSwap(params: {
82
+ actualBalances: [bigint, bigint];
83
+ virtualBalances: [bigint, bigint];
84
+ protocolFeesOwed: [bigint, bigint];
85
+ weightsBps: [number, number];
86
+ amountIn: bigint;
87
+ tokenInIndex: 0 | 1;
88
+ swapToOther: bigint;
89
+ swapFeeRate: number;
90
+ protocolFeeRate: number;
91
+ }): { score: bigint; amountOut: bigint } {
92
+ const {
93
+ actualBalances,
94
+ virtualBalances,
95
+ protocolFeesOwed,
96
+ weightsBps,
97
+ amountIn,
98
+ tokenInIndex,
99
+ swapToOther,
100
+ swapFeeRate,
101
+ protocolFeeRate,
102
+ } = params;
103
+ const other = tokenInIndex === 0 ? 1 : 0;
104
+ const remainingInput = amountIn - swapToOther;
105
+ if (swapToOther <= 0n || remainingInput <= 0n) return { score: 0n, amountOut: 0n };
106
+
107
+ let afterFee: bigint;
108
+ try {
109
+ afterFee = amountAfterSwapFee(swapToOther, swapFeeRate);
110
+ } catch {
111
+ return { score: 0n, amountOut: 0n };
112
+ }
113
+ const swapFee = swapToOther - afterFee;
114
+ const protocolFee = protocolFeeAmount(swapFee, protocolFeeRate);
115
+
116
+ const inLp = lpBalances(
117
+ actualBalances[tokenInIndex],
118
+ virtualBalances[tokenInIndex],
119
+ protocolFeesOwed[tokenInIndex]
120
+ );
121
+ const outLp = lpBalances(
122
+ actualBalances[other],
123
+ virtualBalances[other],
124
+ protocolFeesOwed[other]
125
+ );
126
+
127
+ let amountOut: bigint;
128
+ try {
129
+ amountOut = calcOutGivenIn({
130
+ virtualBalanceIn: inLp.lpVirtual,
131
+ weightInBps: BigInt(weightsBps[tokenInIndex]),
132
+ virtualBalanceOut: outLp.lpVirtual,
133
+ weightOutBps: BigInt(weightsBps[other]),
134
+ amountIn: afterFee,
135
+ actualBalanceOut: outLp.lpActual,
136
+ });
137
+ } catch {
138
+ return { score: 0n, amountOut: 0n };
139
+ }
140
+ if (amountOut <= 0n || amountOut >= outLp.lpActual) return { score: 0n, amountOut };
141
+
142
+ const lpInAfter = inLp.lpActual + swapToOther - protocolFee;
143
+ const lpOutAfter = outLp.lpActual - amountOut;
144
+ if (lpInAfter <= 0n || lpOutAfter <= 0n) return { score: 0n, amountOut };
145
+
146
+ const ratioIn = (remainingInput * ONE) / lpInAfter;
147
+ const ratioOut = (amountOut * ONE) / lpOutAfter;
148
+ return { score: ratioIn < ratioOut ? ratioIn : ratioOut, amountOut };
149
+ }
150
+
151
+ /** @deprecated Analytical base-fee-only optimizer. The deployed helper uses computeAllocations; this does not model selloff/surge. */
152
+ export function computeTwoTokenOptimalAllocations(params: {
153
+ actualBalances: [bigint, bigint];
154
+ virtualBalances: [bigint, bigint];
155
+ protocolFeesOwed: [bigint, bigint];
156
+ weightsBps: [number, number];
157
+ amountIn: bigint;
158
+ tokenInIndex: 0 | 1;
159
+ swapFeeRate: number;
160
+ protocolFeeRate: number;
161
+ }): AllocationResult {
162
+ const { amountIn, tokenInIndex } = params;
163
+ if (amountIn <= 1n) {
164
+ throw new Error("computeTwoTokenOptimalAllocations: amountIn too small");
165
+ }
166
+
167
+ let low = 1n;
168
+ let high = amountIn - 1n;
169
+ for (let i = 0; i < 32 && low < high; i++) {
170
+ const mid = low + (high - low) / 2n;
171
+ const current = scoreTwoTokenSwap({ ...params, swapToOther: mid }).score;
172
+ const next = scoreTwoTokenSwap({
173
+ ...params,
174
+ swapToOther: mid + 1n < amountIn ? mid + 1n : amountIn - 1n,
175
+ }).score;
176
+ if (next >= current) low = mid + 1n;
177
+ else high = mid;
178
+ }
179
+
180
+ const center = low < 1n ? 1n : low >= amountIn ? amountIn - 1n : low;
181
+ let bestSwap = center;
182
+ let bestScore = 0n;
183
+ const start = center > 8n ? center - 8n : 1n;
184
+ const end = center + 8n < amountIn ? center + 8n : amountIn - 1n;
185
+ for (let swapToOther = start; swapToOther <= end; swapToOther++) {
186
+ const { score, amountOut } = scoreTwoTokenSwap({ ...params, swapToOther });
187
+ if (amountOut > 0n && score > bestScore) {
188
+ bestScore = score;
189
+ bestSwap = swapToOther;
190
+ }
191
+ }
192
+ if (bestScore === 0n) {
193
+ throw new Error("computeTwoTokenOptimalAllocations: amount too small");
194
+ }
195
+
196
+ const allocations: [bigint, bigint] = [0n, 0n];
197
+ const other = tokenInIndex === 0 ? 1 : 0;
198
+ allocations[tokenInIndex] = amountIn - bestSwap;
199
+ allocations[other] = bestSwap;
200
+ return { allocations, wScaled: [], sumW: bestScore };
201
+ }
202
+
203
+ /**
204
+ * Mirror of stld `cap_deposit_amounts_to_lp_ratio`.
205
+ *
206
+ * Stored actual balances already exclude protocol fees. The helper deposits
207
+ * the common raw-actual ratio and refunds the remaining basket.
208
+ */
209
+ export function capDepositAmountsToLpRatio(params: {
210
+ helperBalances: bigint[];
211
+ actualBalances: bigint[];
212
+ /** Retained for compatibility; actual balances already exclude these fees. */
213
+ protocolFeesOwed?: bigint[];
214
+ }): { depositAmounts: bigint[]; refundAmounts: bigint[]; lpBalancesForAdd: bigint[] } {
215
+ const { helperBalances, actualBalances, protocolFeesOwed } = params;
216
+ const n = helperBalances.length;
217
+ if (n !== actualBalances.length || (protocolFeesOwed && n !== protocolFeesOwed.length)) {
218
+ throw new Error("capDepositAmountsToLpRatio: length mismatch");
219
+ }
220
+
221
+ helperBalances.forEach((b) => assertU64(b));
222
+ const lpBalancesForAdd = actualBalances.map((actual) => assertU64(actual));
223
+ let ratioMin: bigint | null = null;
224
+
225
+ for (let i = 0; i < n; i++) {
226
+ if (lpBalancesForAdd[i] === 0n) continue;
227
+ if (helperBalances[i] <= 0n) {
228
+ throw new Error("capDepositAmountsToLpRatio: amount too small");
229
+ }
230
+ const ratio = (helperBalances[i] * ONE) / lpBalancesForAdd[i];
231
+ ratioMin = ratioMin === null || ratio < ratioMin ? ratio : ratioMin;
232
+ }
233
+
234
+ if (ratioMin === null || ratioMin === 0n) {
235
+ throw new Error("capDepositAmountsToLpRatio: amount too small");
236
+ }
237
+
238
+ const depositAmounts = lpBalancesForAdd.map((lp, i) => {
239
+ if (lp === 0n) return 0n;
240
+ const amount = (lp * ratioMin!) / ONE;
241
+ if (amount <= 0n) {
242
+ throw new Error("capDepositAmountsToLpRatio: amount too small");
243
+ }
244
+ return amount > helperBalances[i] ? helperBalances[i] : amount;
245
+ });
246
+ const refundAmounts = helperBalances.map((balance, i) =>
247
+ balance > depositAmounts[i] ? balance - depositAmounts[i] : 0n
248
+ );
249
+
250
+ return { depositAmounts, refundAmounts, lpBalancesForAdd };
251
+ }
@@ -0,0 +1,38 @@
1
+ import { SLIPPAGE_PRECISION, SWAP_FEE_PRECISION, PROTOCOL_FEE_PRECISION, MAX_SWAP_FEE_RATE, MAX_PROTOCOL_FEE_RATE } from "../config";
2
+ import { assertInteger, assertU64 } from "./fixedPoint";
3
+ export function applySlippage(expected: bigint, slippageHbps: number): bigint {
4
+ assertU64(expected);
5
+ assertInteger(slippageHbps, 0, SLIPPAGE_PRECISION, "slippage");
6
+ return expected * BigInt(SLIPPAGE_PRECISION - slippageHbps) / BigInt(SLIPPAGE_PRECISION);
7
+ }
8
+ function ceilRatio(amount: bigint, rate: number, scale: number): bigint {
9
+ const numerator = amount * BigInt(rate), denominator = BigInt(scale);
10
+ return numerator / denominator + (numerator % denominator === 0n ? 0n : 1n);
11
+ }
12
+ /** Base fee rounds UP, exactly as swap.rs. Rate is in hundredths of bps. */
13
+ export function calculateSwapFee(amount: bigint, swapFeeRate: number): bigint {
14
+ assertU64(amount);
15
+ assertInteger(swapFeeRate, 0, MAX_SWAP_FEE_RATE, "swap fee");
16
+ return assertU64(ceilRatio(amount, swapFeeRate, SWAP_FEE_PRECISION));
17
+ }
18
+ /** Protocol share of the input fee also rounds UP. Rate is in basis points. */
19
+ export function calculateProtocolFee(fee: bigint, protocolFeeRate: number): bigint {
20
+ assertU64(fee);
21
+ assertInteger(protocolFeeRate, 0, MAX_PROTOCOL_FEE_RATE, "protocol fee");
22
+ return assertU64(ceilRatio(fee, protocolFeeRate, PROTOCOL_FEE_PRECISION));
23
+ }
24
+ export function applySwapFee(amount: bigint, swapFeeRate: number): bigint {
25
+ return amount - calculateSwapFee(amount, swapFeeRate);
26
+ }
27
+ /** Stored actual balances already exclude protocol fees. The third argument is retained for compatibility. */
28
+ export function lpBalances(actual: bigint, virtualBal: bigint, _protocolFeesOwed?: bigint): {
29
+ lpActual: bigint;
30
+ lpVirtual: bigint;
31
+ } {
32
+ return { lpActual: assertU64(actual), lpVirtual: assertU64(virtualBal) };
33
+ }
34
+ export function priceImpactHbps(spot: bigint, actual: bigint): number {
35
+ if (spot <= 0n || actual >= spot)
36
+ return 0;
37
+ return Number((spot - actual) * BigInt(SLIPPAGE_PRECISION) / spot);
38
+ }
@@ -0,0 +1,101 @@
1
+ /** Piecewise-linear surge rate and four-segment output fee from swap.rs. */
2
+ import { ONE, assertInteger, assertU64 } from "./fixedPoint";
3
+ import { calcOutGivenIn } from "./cubicMath";
4
+ import { SelloffResult } from "./maxSelloff";
5
+ const SCALE = 10000n;
6
+ export interface SurgeCurve {
7
+ thresholdPct: number;
8
+ slopeLowPct: number;
9
+ slopeMidPct: number;
10
+ slopeHighPct: number;
11
+ kinkPct: number;
12
+ }
13
+ function validateCurve(c: SurgeCurve): void {
14
+ assertInteger(c.thresholdPct, 0, 65535, "surge threshold");
15
+ assertInteger(c.slopeLowPct, 0, 65535, "surge low");
16
+ assertInteger(c.slopeMidPct, 0, 65535, "surge mid");
17
+ assertInteger(c.slopeHighPct, 0, 65535, "surge high");
18
+ assertInteger(c.kinkPct, 0, 255, "surge kink");
19
+ }
20
+ const sub = (a: bigint, b: bigint): bigint => a > b ? a - b : 0n;
21
+ const min = (a: bigint, b: bigint): bigint => a < b ? a : b;
22
+ const ceil = (a: bigint, b: bigint): bigint => a / b + (a % b === 0n ? 0n : 1n);
23
+ function kinkT(c: SurgeCurve): bigint { const thr = BigInt(c.thresholdPct), fill = BigInt(c.kinkPct) * 100n; return c.kinkPct === 0 || thr >= SCALE || fill <= thr || fill >= SCALE ? 0n : (fill - thr) * ONE / (SCALE - thr); }
24
+ function rateAt(t: bigint, c: SurgeCurve, k: bigint): bigint {
25
+ t = min(t, ONE);
26
+ const low = BigInt(c.slopeLowPct), mid = BigInt(c.slopeMidPct), high = BigInt(c.slopeHighPct);
27
+ if (k === 0n)
28
+ return min(low + ceil(sub(high, low) * t, ONE), SCALE);
29
+ if (t <= k)
30
+ return min(low + ceil(sub(mid, low) * t, k), SCALE);
31
+ return min(mid + ceil(sub(high, mid) * (t - k), ONE - k), SCALE);
32
+ }
33
+ function integralTo(t: bigint, c: SurgeCurve, k: bigint): bigint {
34
+ t = min(t, ONE);
35
+ const low = BigInt(c.slopeLowPct), mid = BigInt(c.slopeMidPct), high = BigInt(c.slopeHighPct);
36
+ // Keep Rust's division before multiplication; algebraic reordering changes rounding.
37
+ const ramp = (x: bigint, span: bigint, den: bigint): bigint => den === 0n || span === 0n || x === 0n ? 0n : span * (x * x / (2n * den));
38
+ if (k === 0n)
39
+ return low * t + ramp(t, sub(high, low), ONE);
40
+ if (t <= k)
41
+ return low * t + ramp(t, sub(mid, low), k);
42
+ const rest = t - k;
43
+ return k * (low + mid) / 2n + mid * rest + ramp(rest, sub(high, mid), ONE - k);
44
+ }
45
+ export function calcSurgeFeePct(params: SurgeCurve & {
46
+ before: bigint;
47
+ after: bigint;
48
+ cap: bigint;
49
+ }): bigint {
50
+ validateCurve(params);
51
+ const { before, after, cap } = params;
52
+ assertU64(before);
53
+ assertU64(after);
54
+ assertU64(cap);
55
+ const thr = BigInt(params.thresholdPct);
56
+ if (cap === 0n || params.slopeHighPct === 0 || thr >= SCALE)
57
+ return 0n;
58
+ const k = kinkT(params), f0 = min(before * SCALE / cap, SCALE), f1 = min(after * SCALE / cap, SCALE);
59
+ if (f1 <= thr)
60
+ return 0n;
61
+ const den = SCALE - thr, t1 = (f1 - thr) * ONE / den;
62
+ if (f1 <= f0)
63
+ return rateAt(t1, params, k);
64
+ const f0Clamped = f0 > thr ? f0 : thr, t0 = (f0Clamped - thr) * ONE / den;
65
+ const integral = sub(integralTo(t1, params, k), integralTo(t0, params, k));
66
+ return min(ceil(den * integral, (f1 - f0Clamped) * ONE), SCALE);
67
+ }
68
+ export function calcSurgeFeeAmount(params: SurgeCurve & {
69
+ window: SelloffResult | null;
70
+ amountInAfterFee: bigint;
71
+ amountOut: bigint;
72
+ virtualBalanceIn: bigint;
73
+ weightInBps: bigint;
74
+ virtualBalanceOut: bigint;
75
+ weightOutBps: bigint;
76
+ actualBalanceOut: bigint;
77
+ }): bigint {
78
+ const { window, amountInAfterFee, amountOut } = params;
79
+ assertU64(amountInAfterFee);
80
+ assertU64(amountOut);
81
+ if (!window)
82
+ return 0n;
83
+ const before = window.effectiveSelloffBefore, after = window.effectiveSelloff, cap = window.maxSelloffCap;
84
+ if (calcSurgeFeePct({ ...params, before, after, cap }) === 0n)
85
+ return 0n;
86
+ const thrUnits = cap * BigInt(params.thresholdPct) / SCALE;
87
+ const span = sub(after, before), taxedLo = before > thrUnits ? before : thrUnits;
88
+ if (span === 0n || after <= taxedLo)
89
+ return 0n;
90
+ const cumulative = (u: bigint): bigint => u <= before ? 0n : u >= after ? amountOut : calcOutGivenIn({ ...params, amountIn: amountInAfterFee * (u - before) / span });
91
+ const width = after - taxedLo;
92
+ let fee = 0n, prevU = taxedLo, prevY = cumulative(taxedLo);
93
+ for (let k = 1n; k <= 4n; k++) {
94
+ const u = k === 4n ? after : taxedLo + width * k / 4n, y = cumulative(u), segmentOut = sub(y, prevY);
95
+ if (segmentOut > 0n && u > prevU)
96
+ fee += ceil(segmentOut * calcSurgeFeePct({ ...params, before: prevU, after: u, cap }), SCALE);
97
+ prevU = u;
98
+ prevY = y;
99
+ }
100
+ return min(fee, amountOut);
101
+ }
@@ -0,0 +1,64 @@
1
+ import {ONE, assertU64, assertInteger, assertU128, mulDivDown, weightToFp} from "./fixedPoint";
2
+ import {powFp} from "./logExp";
3
+ import { MAX_WEIGHT, MIN_WEIGHT, WEIGHT_SCALE } from "../config";
4
+
5
+ /**
6
+ * Validate a weight vector. Must sum exactly to 10_000 (WEIGHT_SCALE) and
7
+ * every entry must be in [MIN_WEIGHT, MAX_WEIGHT].
8
+ */
9
+ export function validateWeights(weights: number[]): void {
10
+ if (!Array.isArray(weights) || weights.length < 2) {
11
+ throw new Error("weightedMath: at least 2 weights required");
12
+ }
13
+ let sum = 0;
14
+ for (const w of weights) {
15
+ if (!Number.isInteger(w)) {
16
+ throw new Error("weightedMath: weights must be integers (bps)");
17
+ }
18
+ if (w < MIN_WEIGHT || w > MAX_WEIGHT) {
19
+ throw new Error(`weightedMath: weight ${w} out of [${MIN_WEIGHT}, ${MAX_WEIGHT}]`);
20
+ }
21
+ sum += w;
22
+ }
23
+ if (sum !== WEIGHT_SCALE) {
24
+ throw new Error(`weightedMath: weights sum to ${sum}, expected ${WEIGHT_SCALE}`);
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Weighted spot price of token_out priced in token_in, normalised to 18
30
+ * decimals. Used only for display; on-chain quoting uses calcOutGivenIn.
31
+ */
32
+ export function calcSpotPrice(params: {
33
+ balanceIn: bigint;
34
+ weightInBps: bigint;
35
+ balanceOut: bigint;
36
+ weightOutBps: bigint;
37
+ decimalsIn: number;
38
+ decimalsOut: number;
39
+ }): bigint {
40
+ const { balanceIn, weightInBps, balanceOut, weightOutBps, decimalsIn, decimalsOut } = params;
41
+ const scaleIn = 10n ** BigInt(18 - decimalsIn);
42
+ const scaleOut = 10n ** BigInt(18 - decimalsOut);
43
+ const bI = balanceIn * scaleIn;
44
+ const bO = balanceOut * scaleOut;
45
+ if (bO === 0n || weightOutBps === 0n) return 0n;
46
+ // price = (bI / wI) / (bO / wO)
47
+ const numer = bI * weightOutBps * 10n ** 18n;
48
+ const denom = bO * weightInBps;
49
+ return numer / denom;
50
+ }
51
+
52
+ /** Exact seed-deposit invariant from weighted_math.rs; returns raw BPT before u64/minimum-supply checks. */
53
+ export function calculateInvariant(balances:bigint[], normalizedWeights:number[], decimals:number[]):bigint {
54
+ if(balances.length!==normalizedWeights.length||balances.length!==decimals.length||balances.length<2)throw new Error("invariant: invalid vector lengths");
55
+ let invariant=ONE;
56
+ for(let i=0;i<balances.length;i++) {
57
+ const raw=assertU64(balances[i]);assertInteger(decimals[i],0,18,"decimals");assertInteger(normalizedWeights[i],0,10000,"weight");
58
+ if(raw===0n)return 0n;
59
+ const balance=decimals[i]>=6?raw/(10n**BigInt(decimals[i]-6)):assertU128(raw*10n**BigInt(6-decimals[i]));
60
+ if(balance===0n)return 0n;
61
+ invariant=mulDivDown(invariant,powFp(balance,weightToFp(normalizedWeights[i])),ONE);
62
+ }
63
+ return invariant;
64
+ }
@@ -0,0 +1,106 @@
1
+ import BN from "bn.js";
2
+ import { PublicKey } from "@solana/web3.js";
3
+
4
+ /**
5
+ * Minimal Borsh-compatible reader. Enough to decode the Coffer contract
6
+ * event structs (Pubkey × N, u8/u16/u32/u64/i64, bool, Vec<u64>,
7
+ * Vec<Pubkey>). Avoids pulling a borsh dep into the SDK's hot path.
8
+ */
9
+ export class BorshReader {
10
+ private offset = 0;
11
+ constructor(private readonly buf: Buffer) {}
12
+
13
+ remaining(): number {
14
+ return this.buf.length - this.offset;
15
+ }
16
+
17
+ /** Current read position — pair with `seek` to re-parse an ambiguous tail. */
18
+ pos(): number {
19
+ return this.offset;
20
+ }
21
+
22
+ seek(offset: number): void {
23
+ this.offset = offset;
24
+ }
25
+
26
+ skip(n: number): void {
27
+ this.requireBytes(n);
28
+ this.offset += n;
29
+ }
30
+
31
+ u8(): number {
32
+ this.requireBytes(1);
33
+ const v = this.buf.readUInt8(this.offset);
34
+ this.offset += 1;
35
+ return v;
36
+ }
37
+
38
+ u16(): number {
39
+ this.requireBytes(2);
40
+ const v = this.buf.readUInt16LE(this.offset);
41
+ this.offset += 2;
42
+ return v;
43
+ }
44
+
45
+ u32(): number {
46
+ this.requireBytes(4);
47
+ const v = this.buf.readUInt32LE(this.offset);
48
+ this.offset += 4;
49
+ return v;
50
+ }
51
+
52
+ u64(): BN {
53
+ this.requireBytes(8);
54
+ const slice = this.buf.slice(this.offset, this.offset + 8);
55
+ this.offset += 8;
56
+ return new BN(slice, "le");
57
+ }
58
+
59
+ i64(): BN {
60
+ this.requireBytes(8);
61
+ const slice = this.buf.slice(this.offset, this.offset + 8);
62
+ this.offset += 8;
63
+ // i64 is two's complement little-endian — treat as signed.
64
+ const bn = new BN(slice, "le");
65
+ if (slice[7] & 0x80) {
66
+ // top bit set → negative: subtract 2^64
67
+ return bn.sub(new BN(2).pow(new BN(64)));
68
+ }
69
+ return bn;
70
+ }
71
+
72
+ bool(): boolean {
73
+ const value = this.u8();
74
+ if (value > 1) throw new Error(`BorshReader: invalid bool ${value}`);
75
+ return value === 1;
76
+ }
77
+
78
+ pubkey(): PublicKey {
79
+ this.requireBytes(32);
80
+ const slice = this.buf.slice(this.offset, this.offset + 32);
81
+ this.offset += 32;
82
+ return new PublicKey(slice);
83
+ }
84
+
85
+ vecU64(): BN[] {
86
+ const len = this.u32();
87
+ this.requireBytes(len * 8);
88
+ const out: BN[] = [];
89
+ for (let i = 0; i < len; i++) out.push(this.u64());
90
+ return out;
91
+ }
92
+
93
+ vecPubkey(): PublicKey[] {
94
+ const len = this.u32();
95
+ this.requireBytes(len * 32);
96
+ const out: PublicKey[] = [];
97
+ for (let i = 0; i < len; i++) out.push(this.pubkey());
98
+ return out;
99
+ }
100
+
101
+ private requireBytes(length: number): void {
102
+ if (!Number.isSafeInteger(length) || length < 0 || length > this.remaining()) {
103
+ throw new Error(`BorshReader: expected ${length} bytes at offset ${this.offset}, ${this.remaining()} remain`);
104
+ }
105
+ }
106
+ }
@@ -0,0 +1,64 @@
1
+ import { BorshAccountsCoder, BorshEventCoder, Idl } from "@coral-xyz/anchor";
2
+ import { IDLS } from "../idl";
3
+ import { ContractAccountMap, ContractEventMap, ContractProgram } from "../types/contracts";
4
+
5
+ export type ContractEvent = {
6
+ [P in ContractProgram]: {
7
+ [E in keyof ContractEventMap[P]]: { program: P; kind: E; data: ContractEventMap[P][E] }
8
+ }[keyof ContractEventMap[P]]
9
+ }[ContractProgram];
10
+
11
+ const accountCoders = new Map<ContractProgram, BorshAccountsCoder>();
12
+ const eventCoders = new Map<ContractProgram, BorshEventCoder>();
13
+
14
+ /** Decode any account in the current ABI, including Config and Treasury.
15
+ * Fields retain IDL snake_case names; u64/i64 values remain BN without loss.
16
+ * The discriminator is verified. Check RPC account ownership separately.
17
+ */
18
+ export function decodeContractAccount<P extends ContractProgram, A extends keyof ContractAccountMap[P] & string>(
19
+ program: P, account: A, data: Buffer,
20
+ ): ContractAccountMap[P][A] {
21
+ let coder = accountCoders.get(program);
22
+ if (!coder) {
23
+ coder = new BorshAccountsCoder(IDLS[program] as unknown as Idl);
24
+ accountCoders.set(program, coder);
25
+ }
26
+ const expectedSize = coder.size(account);
27
+ if (data.length !== expectedSize) throw new Error(`${account} has ${data.length} bytes; expected ${expectedSize} for the current ABI`);
28
+ return coder.decode(account, data);
29
+ }
30
+
31
+ /** Decode the payload following `Program data:` using all current event fields.
32
+ * Returns null for an unknown or malformed event. `program` limits decoding to
33
+ * one ABI when the emitting program is known from the transaction invoke stack.
34
+ */
35
+ export function decodeContractEvent(base64: string, program?: ContractProgram): ContractEvent | null {
36
+ const programs = program ? [program] : Object.keys(IDLS) as ContractProgram[];
37
+ for (const candidate of programs) {
38
+ let coder = eventCoders.get(candidate);
39
+ if (!coder) {
40
+ coder = new BorshEventCoder(IDLS[candidate] as unknown as Idl);
41
+ eventCoders.set(candidate, coder);
42
+ }
43
+ try {
44
+ const result = coder.decode(base64);
45
+ if (result) return { program: candidate, kind: result.name, data: result.data } as ContractEvent;
46
+ } catch { /* Try the other ABI, or report an undecodable event. */ }
47
+ }
48
+ return null;
49
+ }
50
+
51
+ /** Parse all 60 known events; fields and names match the shipped contract IDLs.
52
+ * This is decoding, not proof of the emitting program's identity. Consumers
53
+ * must verify transaction success and program provenance before indexing.
54
+ */
55
+ export function parseContractEvents(logs: string[], program?: ContractProgram): ContractEvent[] {
56
+ const events: ContractEvent[] = [];
57
+ for (const line of logs) {
58
+ const payload = /^Program data:\s+(.+)$/.exec(line)?.[1];
59
+ if (!payload) continue;
60
+ const event = decodeContractEvent(payload, program);
61
+ if (event) events.push(event);
62
+ }
63
+ return events;
64
+ }