@exponent-labs/market-three-math 0.1.8

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 (63) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +197 -0
  3. package/build/addLiquidity.d.ts +67 -0
  4. package/build/addLiquidity.js +269 -0
  5. package/build/addLiquidity.js.map +1 -0
  6. package/build/bisect.d.ts +1 -0
  7. package/build/bisect.js +62 -0
  8. package/build/bisect.js.map +1 -0
  9. package/build/index.d.ts +24 -0
  10. package/build/index.js +76 -0
  11. package/build/index.js.map +1 -0
  12. package/build/liquidityHistogram.d.ts +50 -0
  13. package/build/liquidityHistogram.js +162 -0
  14. package/build/liquidityHistogram.js.map +1 -0
  15. package/build/quote.d.ts +18 -0
  16. package/build/quote.js +106 -0
  17. package/build/quote.js.map +1 -0
  18. package/build/swap-v2.d.ts +20 -0
  19. package/build/swap-v2.js +261 -0
  20. package/build/swap-v2.js.map +1 -0
  21. package/build/swap.d.ts +15 -0
  22. package/build/swap.js +249 -0
  23. package/build/swap.js.map +1 -0
  24. package/build/swapLegacy.d.ts +16 -0
  25. package/build/swapLegacy.js +229 -0
  26. package/build/swapLegacy.js.map +1 -0
  27. package/build/swapV2.d.ts +11 -0
  28. package/build/swapV2.js +406 -0
  29. package/build/swapV2.js.map +1 -0
  30. package/build/types.d.ts +73 -0
  31. package/build/types.js +9 -0
  32. package/build/types.js.map +1 -0
  33. package/build/utils.d.ts +119 -0
  34. package/build/utils.js +219 -0
  35. package/build/utils.js.map +1 -0
  36. package/build/utilsV2.d.ts +88 -0
  37. package/build/utilsV2.js +180 -0
  38. package/build/utilsV2.js.map +1 -0
  39. package/build/withdrawLiquidity.d.ts +8 -0
  40. package/build/withdrawLiquidity.js +174 -0
  41. package/build/withdrawLiquidity.js.map +1 -0
  42. package/build/ytTrades.d.ts +106 -0
  43. package/build/ytTrades.js +292 -0
  44. package/build/ytTrades.js.map +1 -0
  45. package/build/ytTradesLegacy.d.ts +106 -0
  46. package/build/ytTradesLegacy.js +292 -0
  47. package/build/ytTradesLegacy.js.map +1 -0
  48. package/examples/.env.example +1 -0
  49. package/examples/test-histogram-simple.ts +172 -0
  50. package/examples/test-histogram.ts +112 -0
  51. package/package.json +26 -0
  52. package/src/addLiquidity.ts +384 -0
  53. package/src/bisect.ts +72 -0
  54. package/src/index.ts +74 -0
  55. package/src/liquidityHistogram.ts +192 -0
  56. package/src/quote.ts +128 -0
  57. package/src/swap.ts +299 -0
  58. package/src/swapLegacy.ts +272 -0
  59. package/src/types.ts +80 -0
  60. package/src/utils.ts +235 -0
  61. package/src/withdrawLiquidity.ts +240 -0
  62. package/src/ytTrades.ts +419 -0
  63. package/tsconfig.json +17 -0
@@ -0,0 +1,384 @@
1
+ /**
2
+ * CLMM Add Liquidity simulation
3
+ * Ported from exponent_clmm/src/state/market_three/helpers/add_liquidity.rs
4
+ */
5
+ import { AddLiquidityArgs, AddLiquidityOutcome, LiquidityNeeds, MarketThreeState } from "./types"
6
+ import { EffSnap, normalizedTimeRemaining } from "./utils"
7
+
8
+ const TICK_KEY_BASE_POINTS = 1_000_000
9
+
10
+ /**
11
+ * Compute liquidity target and token needs based on position range and budgets
12
+ * Ported from compute_liquidity_target_and_token_needs in math.rs
13
+ */
14
+ export function computeLiquidityTargetAndTokenNeeds(
15
+ snap: EffSnap,
16
+ spotPriceCurrent: number,
17
+ priceEffLower: number,
18
+ priceEffUpper: number,
19
+ lowerPrice: number,
20
+ upperPrice: number,
21
+ lowerTickIdx: number,
22
+ upperTickIdx: number,
23
+ currentIndex: number,
24
+ maxSy: number,
25
+ maxPt: number,
26
+ epsilonClamp: number,
27
+ ): LiquidityNeeds {
28
+ // Decide region and compute target ΔL and token needs
29
+ // IMPORTANT: Price below range = SY only; Price above range = PT only
30
+
31
+ if (spotPriceCurrent <= lowerPrice) {
32
+ // Below range: SY only (NOT PT!)
33
+ const deltaCTotal = Math.max(priceEffLower - priceEffUpper, epsilonClamp)
34
+ const liquidityFromSy = maxSy / deltaCTotal
35
+ const liquidityTarget = liquidityFromSy
36
+ const syNeed = liquidityTarget * (priceEffLower - priceEffUpper)
37
+
38
+ return {
39
+ liquidityTarget: Math.floor(liquidityTarget),
40
+ syNeeded: Math.floor(syNeed),
41
+ ptNeeded: 0,
42
+ priceSplitForNeed: lowerPrice,
43
+ priceSplitTickIdx: lowerTickIdx,
44
+ }
45
+ } else if (spotPriceCurrent >= upperPrice) {
46
+ // Above range: PT only (NOT SY!)
47
+ const duTotal = Math.max(upperPrice - lowerPrice, epsilonClamp)
48
+ const liquidityFromPt = maxPt / duTotal
49
+ const liquidityTarget = liquidityFromPt
50
+ const ptNeed = liquidityTarget * duTotal
51
+
52
+ return {
53
+ liquidityTarget: Math.floor(liquidityTarget),
54
+ syNeeded: 0,
55
+ ptNeeded: Math.floor(ptNeed),
56
+ priceSplitForNeed: upperPrice,
57
+ priceSplitTickIdx: upperTickIdx,
58
+ }
59
+ } else {
60
+ // Inside range: both sides
61
+ const duLeft = Math.max(spotPriceCurrent - lowerPrice, epsilonClamp)
62
+ const liquidityFromPt = maxPt / duLeft
63
+
64
+ const priceEffCurrent = snap.getEffectivePrice(spotPriceCurrent)
65
+ const deltaCRight = Math.max(priceEffCurrent - priceEffUpper, epsilonClamp)
66
+ const liquidityFromSy = maxSy / deltaCRight
67
+
68
+ const liquidityTarget = Math.min(liquidityFromPt, liquidityFromSy)
69
+ const ptNeed = liquidityTarget * duLeft
70
+ const syNeed = liquidityTarget * deltaCRight
71
+
72
+ return {
73
+ liquidityTarget: Math.floor(liquidityTarget),
74
+ syNeeded: Math.floor(syNeed),
75
+ ptNeeded: Math.floor(ptNeed),
76
+ priceSplitForNeed: spotPriceCurrent,
77
+ priceSplitTickIdx: currentIndex,
78
+ }
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Simulate adding liquidity to the CLMM market
84
+ * This is a pure function that does not mutate the market state
85
+ */
86
+ export function simulateAddLiquidity(marketState: MarketThreeState, args: AddLiquidityArgs): AddLiquidityOutcome {
87
+ const { financials, configurationOptions, ticks } = marketState
88
+ const secondsRemaining = Math.max(0, Number(financials.expirationTs) - Date.now() / 1000)
89
+
90
+ // Create effective price snapshot
91
+ const snap = new EffSnap(normalizedTimeRemaining(secondsRemaining), args.syExchangeRate)
92
+
93
+ // Current spot price
94
+ const currentSpot = ticks.currentSpotPrice
95
+
96
+ // Resolve tick prices - convert tick keys (parts per million) to spot prices
97
+ const lowerPrice = 1 + args.lowerTick / TICK_KEY_BASE_POINTS
98
+ const upperPrice = 1 + args.upperTick / TICK_KEY_BASE_POINTS
99
+
100
+ // Precompute effective prices
101
+ const priceEffLower = snap.getEffectivePrice(lowerPrice)
102
+ const priceEffUpper = snap.getEffectivePrice(upperPrice)
103
+
104
+ // Calculate liquidity needs
105
+ const liquidityNeeds = computeLiquidityTargetAndTokenNeeds(
106
+ snap,
107
+ currentSpot,
108
+ priceEffLower,
109
+ priceEffUpper,
110
+ lowerPrice,
111
+ upperPrice,
112
+ args.lowerTick,
113
+ args.upperTick,
114
+ ticks.currentTick,
115
+ args.maxSy,
116
+ args.maxPt,
117
+ configurationOptions.epsilonClamp,
118
+ )
119
+
120
+ // Enforce budgets
121
+ const sySpent = liquidityNeeds.syNeeded
122
+ const ptSpent = liquidityNeeds.ptNeeded
123
+
124
+ if (sySpent > args.maxSy) {
125
+ throw new Error(`Insufficient SY budget: need ${sySpent}, have ${args.maxSy}`)
126
+ }
127
+ if (ptSpent > args.maxPt) {
128
+ throw new Error(`Insufficient PT budget: need ${ptSpent}, have ${args.maxPt}`)
129
+ }
130
+
131
+ return {
132
+ deltaL: liquidityNeeds.liquidityTarget,
133
+ sySpent,
134
+ ptSpent,
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Calculate the LP tokens that will be received for a given deposit
140
+ * This is useful for UI display and slippage calculations
141
+ */
142
+ export function calculateLpOut(liquidityAdded: number, totalLiquidityBefore: number): number {
143
+ if (totalLiquidityBefore === 0) {
144
+ // First deposit
145
+ return liquidityAdded
146
+ }
147
+
148
+ // LP tokens are proportional to liquidity added
149
+ return liquidityAdded
150
+ }
151
+
152
+ /**
153
+ * Estimate the balanced amounts of PT and SY needed for a liquidity deposit
154
+ * Given a target liquidity amount, calculate how much PT and SY are needed
155
+ */
156
+ export function estimateBalancedDeposit(
157
+ marketState: MarketThreeState,
158
+ targetLiquidity: number,
159
+ lowerTickApy: number,
160
+ upperTickApy: number,
161
+ ): { ptNeeded: number; syNeeded: number } {
162
+ const { financials, ticks } = marketState
163
+ const secondsRemaining = Math.max(0, Number(financials.expirationTs) - Date.now() / 1000)
164
+
165
+ const snap = new EffSnap(normalizedTimeRemaining(secondsRemaining), marketState.currentSyExchangeRate)
166
+
167
+ const currentSpot = ticks.currentSpotPrice
168
+ // Convert APY (in %) to spot price: 1 + apy/100
169
+ const lowerPrice = 1.0 + lowerTickApy / 100
170
+ const upperPrice = 1.0 + upperTickApy / 100
171
+
172
+ const priceEffLower = snap.getEffectivePrice(lowerPrice)
173
+ const priceEffUpper = snap.getEffectivePrice(upperPrice)
174
+
175
+ if (currentSpot <= lowerPrice) {
176
+ // Below range: SY only
177
+ const syNeeded = (targetLiquidity / snap.timeFactor) * (priceEffLower - priceEffUpper)
178
+ return { ptNeeded: 0, syNeeded }
179
+ } else if (currentSpot >= upperPrice) {
180
+ // Above range: PT only
181
+ const ptNeeded = targetLiquidity * (upperPrice - lowerPrice)
182
+ return { ptNeeded, syNeeded: 0 }
183
+ } else {
184
+ // Inside range: both
185
+ const ptNeeded = targetLiquidity * (upperPrice - currentSpot)
186
+ const priceEffCurrent = snap.getEffectivePrice(currentSpot)
187
+ const syNeeded = (targetLiquidity / snap.timeFactor) * (priceEffLower - priceEffCurrent)
188
+ return { ptNeeded, syNeeded }
189
+ }
190
+ }
191
+
192
+ /** Calculate SY and PT needed to deposit into liquidity pool from base token amount */
193
+ /** Off-chain analogue of on-chain wrapper_provide_liquidity function */
194
+ export function calcDepositSyAndPtFromBaseAmount(params: {
195
+ lowerPrice: number
196
+ upperPrice: number
197
+ baseTokenAmount: number
198
+ expirationTs: number
199
+ currentSpotPrice: number
200
+ syExchangeRate: number
201
+ }) {
202
+ const { expirationTs, currentSpotPrice, syExchangeRate, lowerPrice, upperPrice, baseTokenAmount } = params
203
+
204
+ if (baseTokenAmount <= 0 || syExchangeRate <= 0) {
205
+ return {
206
+ syNeeded: 0,
207
+ ptNeeded: 0,
208
+ }
209
+ }
210
+
211
+ const secondsRemaining = Math.max(0, expirationTs)
212
+ const effSnap = new EffSnap(normalizedTimeRemaining(secondsRemaining), syExchangeRate)
213
+
214
+ const priceEffLower = effSnap.getEffectivePrice(lowerPrice)
215
+ const priceEffUpper = effSnap.getEffectivePrice(upperPrice)
216
+
217
+ // We mirror the on-chain logic in `wrapper_provide_liquidity`:
218
+ // 1. Use a large mock amount for both SY and PT to infer the *ratio* of SY/PT
219
+ // the market "wants" for this price range (compute_token_needs on-chain).
220
+ // 2. Use that ratio plus the current SY exchange rate to decide how much of the
221
+ // minted SY should be stripped into PT to keep the user-close to pool proportions.
222
+ const { syNeeded: syMock, ptNeeded: ptMock } = computeLiquidityTargetAndTokenNeeds(
223
+ effSnap,
224
+ currentSpotPrice,
225
+ priceEffLower,
226
+ priceEffUpper,
227
+ lowerPrice,
228
+ upperPrice,
229
+ 0,
230
+ 0,
231
+ 0,
232
+ 1e9,
233
+ 1e9,
234
+ 1e-18,
235
+ )
236
+
237
+ // Total SY the user would get by minting SY from base off-chain
238
+ // (we approximate on-chain `mint_sy_return_data.sy_out_amount`).
239
+ const syAmount = Math.floor(baseTokenAmount * syExchangeRate)
240
+
241
+ // Off-chain analogue of on-chain `calc_strip_amount`:
242
+ // B = (amt_sy * market_pt_liq) / (market_pt_liq + market_sy_liq * cur_sy_rate)
243
+ // here: amt_sy = syAmount, market_pt_liq = ptMock, market_sy_liq = syMock
244
+ const denominator = syExchangeRate * syMock + ptMock
245
+ const toStrip = denominator > 0 ? Math.ceil((syAmount * ptMock) / denominator) : 0
246
+
247
+ const syForLiquidity = Math.max(0, syAmount - toStrip)
248
+
249
+ // We approximate PT created from the stripped SY as:
250
+ // ptAmount ≈ toStrip * syExchangeRate
251
+ // which is consistent with PT being a claim on base and `syExchangeRate`
252
+ // giving the base value of 1 SY.
253
+ const ptFromStrip = Math.floor(toStrip * syExchangeRate)
254
+
255
+ return {
256
+ syNeeded: syForLiquidity,
257
+ ptNeeded: ptFromStrip,
258
+ }
259
+ }
260
+ /**
261
+ * Calculate the amount of SY to strip into PT based on market liquidity ratio
262
+ * This matches the Rust implementation: calc_strip_amount in wrapper_provide_liquidity.rs
263
+ *
264
+ * The formula splits totalAmountSy into two lots: A (remains as SY) and B (stripped to PT)
265
+ * such that: A / (B * curSyRate) = marketSyLiq / marketPtLiq
266
+ *
267
+ * Solution: B = (totalAmountSy * marketPtLiq) / (marketPtLiq + marketSyLiq * curSyRate)
268
+ */
269
+ function calcStripAmount(totalAmountSy: number, curSyRate: number, marketPtLiq: number, marketSyLiq: number): number {
270
+ const toStrip = (totalAmountSy * marketPtLiq) / (curSyRate * marketSyLiq + marketPtLiq)
271
+ return Math.ceil(toStrip)
272
+ }
273
+
274
+ /**
275
+ * Simulate wrapper_provide_liquidity instruction
276
+ * This matches the flow in wrapper_provide_liquidity.rs:
277
+ * 1. Mints SY from base asset
278
+ * 2. Calculates mock token needs for the tick range
279
+ * 3. Strips some SY into PT/YT based on the ratio
280
+ * 4. Deposits remaining SY + PT into concentrated liquidity position
281
+ * 5. Returns YT to user
282
+ *
283
+ * @param marketState - Current market state
284
+ * @param amountBase - Amount of base tokens (in asset terms, will be converted to SY)
285
+ * @param lowerTick - Lower tick (APY in basis points)
286
+ * @param upperTick - Upper tick (APY in basis points)
287
+ * @param syExchangeRate - SY exchange rate
288
+ * @returns Simulation result with LP out, YT out, amounts spent, etc.
289
+ */
290
+ export function simulateWrapperProvideLiquidity(
291
+ marketState: MarketThreeState,
292
+ amountBase: number,
293
+ lowerTick: number,
294
+ upperTick: number,
295
+ syExchangeRate: number,
296
+ ): {
297
+ lpOut: number
298
+ ytOut: number
299
+ syToStrip: number
300
+ ptFromStrip: number
301
+ syRemainder: number
302
+ ptDeposited: number
303
+ syDeposited: number
304
+ } | null {
305
+ try {
306
+ const { financials, configurationOptions, ticks } = marketState
307
+ const secondsRemaining = Math.max(0, Number(financials.expirationTs) - Date.now() / 1000)
308
+
309
+ // Step 1: Convert base to SY (simulating mint_sy)
310
+ const syAmount = amountBase / syExchangeRate
311
+
312
+ // Step 2: Create effective price snapshot
313
+ const snap = new EffSnap(normalizedTimeRemaining(secondsRemaining), syExchangeRate)
314
+
315
+ // Current spot price
316
+ const currentSpot = ticks.currentSpotPrice
317
+
318
+ // Convert tick keys to prices using compute_spot_price formula
319
+ // Rust: (1.0 + (key as f64) / TICK_KEY_BASE_POINTS).ln().exp() which simplifies to 1 + key/1_000_000
320
+ const lowerPrice = 1.0 + lowerTick / TICK_KEY_BASE_POINTS
321
+ const upperPrice = 1.0 + upperTick / TICK_KEY_BASE_POINTS
322
+
323
+ // Precompute effective prices
324
+ const priceEffLower = snap.getEffectivePrice(lowerPrice)
325
+ const priceEffUpper = snap.getEffectivePrice(upperPrice)
326
+
327
+ // Step 3: Calculate "mock" token needs using MOCK_AMOUNT (1e9)
328
+ // This gives us the ratio of SY:PT needed for this tick range
329
+ const MOCK_AMOUNT = 1e9
330
+ const mockNeeds = computeLiquidityTargetAndTokenNeeds(
331
+ snap,
332
+ currentSpot,
333
+ priceEffLower,
334
+ priceEffUpper,
335
+ lowerPrice,
336
+ upperPrice,
337
+ lowerTick,
338
+ upperTick,
339
+ ticks.currentTick,
340
+ MOCK_AMOUNT,
341
+ MOCK_AMOUNT,
342
+ configurationOptions.epsilonClamp,
343
+ )
344
+
345
+ // Step 4: Calculate how much SY to strip
346
+ // Uses the mock amounts to determine the ratio
347
+ const syToStrip = calcStripAmount(syAmount, syExchangeRate, mockNeeds.ptNeeded, mockNeeds.syNeeded)
348
+
349
+ console.log("[simulateWrapperProvideLiquidity] Strip calculation:", {
350
+ syToStrip,
351
+ formula: `(${syAmount} * ${mockNeeds.ptNeeded}) / (${syExchangeRate} * ${mockNeeds.syNeeded} + ${mockNeeds.ptNeeded})`,
352
+ })
353
+
354
+ // Step 5: Calculate PT and YT from stripping
355
+ // When you strip SY, you get PT = SY * syExchangeRate and YT = SY * syExchangeRate
356
+ const ptFromStrip = syToStrip * syExchangeRate
357
+ const ytOut = ptFromStrip // YT amount equals PT amount from strip
358
+
359
+ // Step 6: Calculate remaining SY after strip
360
+ const syRemainder = syAmount - syToStrip
361
+
362
+ // Step 7: Simulate deposit liquidity with remaining SY and PT
363
+ const depositResult = simulateAddLiquidity(marketState, {
364
+ lowerTick,
365
+ upperTick,
366
+ maxSy: Math.floor(syRemainder),
367
+ maxPt: Math.floor(ptFromStrip),
368
+ syExchangeRate,
369
+ })
370
+
371
+ return {
372
+ lpOut: depositResult.deltaL,
373
+ ytOut: Math.floor(ytOut),
374
+ syToStrip: Math.floor(syToStrip),
375
+ ptFromStrip: Math.floor(ptFromStrip),
376
+ syRemainder: Math.floor(syRemainder),
377
+ ptDeposited: depositResult.ptSpent,
378
+ syDeposited: depositResult.sySpent,
379
+ }
380
+ } catch (error) {
381
+ console.error("[simulateWrapperProvideLiquidity] Error:", error)
382
+ return null
383
+ }
384
+ }
package/src/bisect.ts ADDED
@@ -0,0 +1,72 @@
1
+ export function bisectSearch2(
2
+ func: (x: number) => number,
3
+ lo: number,
4
+ hi: number,
5
+ epsilon: number = 0.0001,
6
+ maxIterations: number = 2000,
7
+ ): number | null {
8
+ if (lo > hi) {
9
+ throw new Error("lo must be less than hi")
10
+ }
11
+
12
+ let iterations = 0
13
+
14
+ // Evaluate boundaries once
15
+ const fLo = func(lo)
16
+ const fHi = func(hi)
17
+
18
+ // Check if the target is bracketed between lo and hi
19
+ if (fLo * fHi > 0) {
20
+ return null
21
+ }
22
+
23
+ // Check if we're already at a root
24
+ if (Math.abs(fLo) < epsilon) return lo
25
+ if (Math.abs(fHi) < epsilon) return hi
26
+
27
+ // Track best approximation in case we don't converge exactly
28
+ let bestMid = (lo + hi) / 2
29
+ let bestError = Infinity
30
+
31
+ // Binary search within the range
32
+ while (iterations < maxIterations) {
33
+ // Use midpoint for standard bisection
34
+ const mid = (lo + hi) / 2
35
+ const fMid = func(mid)
36
+ const absFMid = Math.abs(fMid)
37
+
38
+ iterations++
39
+
40
+ // Track best approximation
41
+ if (absFMid < bestError) {
42
+ bestError = absFMid
43
+ bestMid = mid
44
+ }
45
+
46
+ // Check convergence
47
+ if (absFMid < epsilon) {
48
+ return mid
49
+ }
50
+
51
+ // Early termination if range is too small relative to epsilon
52
+ const rangeSize = hi - lo
53
+ if (rangeSize < epsilon * 0.01) {
54
+ return bestMid
55
+ }
56
+
57
+ // Adjust the search range based on the target
58
+ // Use fLo instead of recalculating func(lo)
59
+ if (fLo * fMid < 0) {
60
+ // eslint-disable-next-line no-param-reassign
61
+ hi = mid
62
+ // Note: fHi would be fMid, but we don't need to track it
63
+ } else {
64
+ // eslint-disable-next-line no-param-reassign
65
+ lo = mid
66
+ // Note: fLo would be fMid, but we recalculate in the condition anyway
67
+ }
68
+ }
69
+
70
+ // Return best approximation if we didn't converge
71
+ return bestMid
72
+ }
package/src/index.ts ADDED
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Market Three Math - CLMM Simulation Package
3
+ *
4
+ * This package provides TypeScript implementations of the CLMM (Concentrated Liquidity Market Maker)
5
+ * mathematical functions from the Exponent CLMM program. It allows for client-side simulation of:
6
+ * - Swap operations (PT <-> SY trades)
7
+ * - Liquidity deposits (add_liquidity)
8
+ * - Price and liquidity calculations
9
+ *
10
+ * The implementations are ported from Rust code in solana/programs/exponent_clmm
11
+ */
12
+
13
+ // Export types
14
+ export * from "./types"
15
+
16
+ // Export utility functions
17
+ export {
18
+ EffSnap,
19
+ normalizedTimeRemaining,
20
+ calculateFeeRate,
21
+ getFeeFromAmount,
22
+ getActiveLiquidity,
23
+ getSuccessorTickKey,
24
+ getPredecessorTickKey,
25
+ getImpliedRate,
26
+ findTickByKey,
27
+ convertApyToApyBp,
28
+ convertApyBpToApy,
29
+ calcPtPriceInAsset,
30
+ getSuccessorTickByIdx,
31
+ } from "./utils"
32
+
33
+ // Export swap functions
34
+ export { simulateSwap } from "./swap"
35
+ export { getSwapQuote, QuoteDirection } from "./quote"
36
+
37
+ // Export YT trade functions
38
+ export { simulateBuyYt, simulateSellYt, simulateBuyYtWithSyIn } from "./ytTrades"
39
+ export type {
40
+ BuyYtSimulationArgs,
41
+ BuyYtSimulationResult,
42
+ SellYtSimulationArgs,
43
+ SellYtSimulationResult,
44
+ } from "./ytTrades"
45
+
46
+ // Export add liquidity functions
47
+ export {
48
+ simulateAddLiquidity,
49
+ computeLiquidityTargetAndTokenNeeds,
50
+ calculateLpOut,
51
+ estimateBalancedDeposit,
52
+ calcDepositSyAndPtFromBaseAmount,
53
+ simulateWrapperProvideLiquidity,
54
+ } from "./addLiquidity"
55
+
56
+ export { getPtAndSyOnWithdrawLiquidity } from "./withdrawLiquidity"
57
+
58
+ // Export bisect search utility
59
+ export { bisectSearch2 } from "./bisect"
60
+
61
+ // Export liquidity histogram functions
62
+ export { buildLiquidityHistogram, buildLiquidityHistogramSimple } from "./liquidityHistogram"
63
+ export type { LiquidityHistogramBin } from "./liquidityHistogram"
64
+
65
+ // Re-export common types for convenience
66
+ export type {
67
+ SwapArgs,
68
+ SwapOutcome,
69
+ AddLiquidityArgs,
70
+ AddLiquidityOutcome,
71
+ LiquidityNeeds,
72
+ MarketThreeState,
73
+ } from "./types"
74
+ export { SwapDirection } from "./types"