@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
package/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Initial release
6
+ - Port of CLMM swap simulation from Rust
7
+ - Port of CLMM add_liquidity simulation from Rust
8
+ - Tick-based math and effective price calculations
package/README.md ADDED
@@ -0,0 +1,197 @@
1
+ # @exponent-labs/market-three-math
2
+
3
+ Mathematical simulation package for Exponent CLMM (Concentrated Liquidity Market Maker) markets.
4
+
5
+ ## Overview
6
+
7
+ This package provides TypeScript implementations of the CLMM mathematical functions from the Exponent protocol. It enables client-side simulation of:
8
+
9
+ - **Swap operations**: Simulate PT ↔ SY trades with accurate pricing and fee calculations
10
+ - **Liquidity deposits**: Calculate required token amounts for concentrated liquidity positions
11
+ - **Price calculations**: Convert between effective prices, implied rates, and tick indices
12
+
13
+ The implementations are direct ports from the Rust on-chain program code in `solana/programs/exponent_clmm`.
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ yarn add @exponent-labs/market-three-math
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ### Simulating a Swap
24
+
25
+ ```typescript
26
+ import { MarketThreeState, SwapDirection, simulateSwap } from "@exponent-labs/market-three-math"
27
+
28
+ // Prepare market state
29
+ const marketState: MarketThreeState = {
30
+ financials: {
31
+ expirationTs: Date.now() / 1000 + 86400, // 1 day from now
32
+ ptBalance: 1000000,
33
+ syBalance: 1000000,
34
+ liquidityBalance: 1000000,
35
+ },
36
+ configurationOptions: {
37
+ lnFeeRateRoot: 0.001,
38
+ rateScalarRoot: 5.0,
39
+ treasuryFeeBps: 2000, // 20%
40
+ lastLnImpliedRate: 0.05,
41
+ epsilonClamp: 0.000001,
42
+ minLpTickAmount: 1000,
43
+ maxLpSupply: 10000000000,
44
+ tickSpace: 100,
45
+ priceDecimals: 4,
46
+ },
47
+ ticks: {
48
+ currentTick: 500,
49
+ currentSpotPrice: 0.05,
50
+ feeGrowthIndexGlobalPt: 0,
51
+ feeGrowthIndexGlobalSy: 0,
52
+ ticks: [
53
+ // ... tick data
54
+ ],
55
+ market: "marketAddress",
56
+ },
57
+ currentSyExchangeRate: 1.0,
58
+ }
59
+
60
+ // Simulate buying PT with SY
61
+ const swapResult = simulateSwap(marketState, {
62
+ direction: SwapDirection.SyToPt,
63
+ amountIn: 100000, // 100k SY
64
+ syExchangeRate: 1.0,
65
+ isCurrentFlashSwap: false,
66
+ priceSpotLimit: 0.06, // Maximum price willing to pay
67
+ })
68
+
69
+ console.log("PT received:", swapResult.amountOut)
70
+ console.log("Fees paid:", swapResult.lpFeeChargedOutToken + swapResult.protocolFeeChargedOutToken)
71
+ console.log("Final price:", swapResult.finalSpotPrice)
72
+ ```
73
+
74
+ ### Simulating Liquidity Deposit
75
+
76
+ ```typescript
77
+ import { estimateBalancedDeposit, simulateAddLiquidity } from "@exponent-labs/market-three-math"
78
+
79
+ // Estimate balanced amounts for a target liquidity
80
+ const { ptNeeded, syNeeded } = estimateBalancedDeposit(
81
+ marketState,
82
+ 100000, // Target liquidity
83
+ 4.5, // Lower tick APY (%)
84
+ 5.5, // Upper tick APY (%)
85
+ )
86
+
87
+ // Simulate the actual deposit
88
+ const depositResult = simulateAddLiquidity(marketState, {
89
+ lowerTick: 45000, // 4.5% in basis points
90
+ upperTick: 55000, // 5.5% in basis points
91
+ maxSy: syNeeded,
92
+ maxPt: ptNeeded,
93
+ syExchangeRate: 1.0,
94
+ })
95
+
96
+ console.log("Liquidity added:", depositResult.deltaL)
97
+ console.log("PT spent:", depositResult.ptSpent)
98
+ console.log("SY spent:", depositResult.sySpent)
99
+ ```
100
+
101
+ ### Working with Effective Prices
102
+
103
+ ```typescript
104
+ import { EffSnap, normalizedTimeRemaining } from "@exponent-labs/market-three-math"
105
+
106
+ const secondsRemaining = 86400 // 1 day
107
+ const syExchangeRate = 1.0
108
+
109
+ const snap = new EffSnap(normalizedTimeRemaining(secondsRemaining), syExchangeRate)
110
+
111
+ // Convert ln implied rate to effective price
112
+ const lnImpliedRate = 0.05 // 5%
113
+ const effectivePrice = snap.getEffectivePrice(lnImpliedRate)
114
+
115
+ // Convert back from effective price to implied rate
116
+ const recoveredRate = snap.impliedRateFromEffectivePrice(effectivePrice)
117
+ ```
118
+
119
+ ## Key Concepts
120
+
121
+ ### Effective Price Model
122
+
123
+ The CLMM uses an effective price model where:
124
+
125
+ ```
126
+ C = e^(-τ * u) / r
127
+ ```
128
+
129
+ Where:
130
+
131
+ - `C` = effective price (PT per SY)
132
+ - `τ` = time factor (normalized time to expiry)
133
+ - `u` = ln implied rate (spot price)
134
+ - `r` = SY exchange rate
135
+
136
+ ### Tick-Based Liquidity
137
+
138
+ Liquidity is concentrated in tick ranges, similar to Uniswap v3:
139
+
140
+ - Each tick represents a specific implied rate
141
+ - Liquidity is active only when the current price is within the tick range
142
+ - Ticks store principal PT and SY for accurate accounting
143
+
144
+ ### Fee Structure
145
+
146
+ Fees are calculated as:
147
+
148
+ - LP fee: Retained by liquidity providers
149
+ - Protocol fee: Sent to treasury (typically 20% of total fee)
150
+ - Total fee rate increases as expiration approaches
151
+
152
+ ## API Reference
153
+
154
+ ### Types
155
+
156
+ - `MarketThreeState`: Complete market state required for simulations
157
+ - `SwapArgs`: Arguments for swap simulation
158
+ - `SwapOutcome`: Results of a swap simulation
159
+ - `AddLiquidityArgs`: Arguments for liquidity deposit simulation
160
+ - `AddLiquidityOutcome`: Results of liquidity deposit simulation
161
+ - `Tick`: Individual tick data structure
162
+ - `Ticks`: Complete ticks array with metadata
163
+
164
+ ### Functions
165
+
166
+ #### Swap Functions
167
+
168
+ - `simulateSwap(marketState, args)`: Simulate a swap operation
169
+ - `getSwapQuote(marketState, amountIn, direction)`: Quick quote for a swap
170
+
171
+ #### Liquidity Functions
172
+
173
+ - `simulateAddLiquidity(marketState, args)`: Simulate liquidity deposit
174
+ - `estimateBalancedDeposit(marketState, targetLiquidity, lowerTickApy, upperTickApy)`: Estimate balanced PT/SY amounts
175
+ - `computeLiquidityTargetAndTokenNeeds(...)`: Low-level liquidity calculation
176
+
177
+ #### Utility Functions
178
+
179
+ - `EffSnap`: Effective price snapshot class
180
+ - `calculateFeeRate(lnFeeRateRoot, secondsRemaining)`: Calculate current fee rate
181
+ - `normalizedTimeRemaining(secondsRemaining)`: Normalize time to [0,1]
182
+ - `getLnImpliedRate(tickKey)`: Convert tick key to implied rate
183
+ - `convertApyToApyBp(apyPercent)` / `convertApyBpToApy(apyBp)`: APY conversion utilities
184
+
185
+ ## Relationship to On-Chain Code
186
+
187
+ This package is a direct port of the Rust implementations from:
188
+
189
+ - `solana/programs/exponent_clmm/src/state/market_three/helpers/swap.rs`
190
+ - `solana/programs/exponent_clmm/src/state/market_three/helpers/add_liquidity.rs`
191
+ - `solana/programs/exponent_clmm/src/utils/math.rs`
192
+
193
+ The TypeScript implementations maintain mathematical equivalence with the on-chain code to ensure accurate simulations.
194
+
195
+ ## License
196
+
197
+ AGPL-3.0
@@ -0,0 +1,67 @@
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 } from "./utils";
7
+ /**
8
+ * Compute liquidity target and token needs based on position range and budgets
9
+ * Ported from compute_liquidity_target_and_token_needs in math.rs
10
+ */
11
+ export declare function computeLiquidityTargetAndTokenNeeds(snap: EffSnap, spotPriceCurrent: number, priceEffLower: number, priceEffUpper: number, lowerPrice: number, upperPrice: number, lowerTickIdx: number, upperTickIdx: number, currentIndex: number, maxSy: number, maxPt: number, epsilonClamp: number): LiquidityNeeds;
12
+ /**
13
+ * Simulate adding liquidity to the CLMM market
14
+ * This is a pure function that does not mutate the market state
15
+ */
16
+ export declare function simulateAddLiquidity(marketState: MarketThreeState, args: AddLiquidityArgs): AddLiquidityOutcome;
17
+ /**
18
+ * Calculate the LP tokens that will be received for a given deposit
19
+ * This is useful for UI display and slippage calculations
20
+ */
21
+ export declare function calculateLpOut(liquidityAdded: number, totalLiquidityBefore: number): number;
22
+ /**
23
+ * Estimate the balanced amounts of PT and SY needed for a liquidity deposit
24
+ * Given a target liquidity amount, calculate how much PT and SY are needed
25
+ */
26
+ export declare function estimateBalancedDeposit(marketState: MarketThreeState, targetLiquidity: number, lowerTickApy: number, upperTickApy: number): {
27
+ ptNeeded: number;
28
+ syNeeded: number;
29
+ };
30
+ /** Calculate SY and PT needed to deposit into liquidity pool from base token amount */
31
+ /** Off-chain analogue of on-chain wrapper_provide_liquidity function */
32
+ export declare function calcDepositSyAndPtFromBaseAmount(params: {
33
+ lowerPrice: number;
34
+ upperPrice: number;
35
+ baseTokenAmount: number;
36
+ expirationTs: number;
37
+ currentSpotPrice: number;
38
+ syExchangeRate: number;
39
+ }): {
40
+ syNeeded: number;
41
+ ptNeeded: number;
42
+ };
43
+ /**
44
+ * Simulate wrapper_provide_liquidity instruction
45
+ * This matches the flow in wrapper_provide_liquidity.rs:
46
+ * 1. Mints SY from base asset
47
+ * 2. Calculates mock token needs for the tick range
48
+ * 3. Strips some SY into PT/YT based on the ratio
49
+ * 4. Deposits remaining SY + PT into concentrated liquidity position
50
+ * 5. Returns YT to user
51
+ *
52
+ * @param marketState - Current market state
53
+ * @param amountBase - Amount of base tokens (in asset terms, will be converted to SY)
54
+ * @param lowerTick - Lower tick (APY in basis points)
55
+ * @param upperTick - Upper tick (APY in basis points)
56
+ * @param syExchangeRate - SY exchange rate
57
+ * @returns Simulation result with LP out, YT out, amounts spent, etc.
58
+ */
59
+ export declare function simulateWrapperProvideLiquidity(marketState: MarketThreeState, amountBase: number, lowerTick: number, upperTick: number, syExchangeRate: number): {
60
+ lpOut: number;
61
+ ytOut: number;
62
+ syToStrip: number;
63
+ ptFromStrip: number;
64
+ syRemainder: number;
65
+ ptDeposited: number;
66
+ syDeposited: number;
67
+ } | null;
@@ -0,0 +1,269 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.simulateWrapperProvideLiquidity = exports.calcDepositSyAndPtFromBaseAmount = exports.estimateBalancedDeposit = exports.calculateLpOut = exports.simulateAddLiquidity = exports.computeLiquidityTargetAndTokenNeeds = void 0;
4
+ const utils_1 = require("./utils");
5
+ const TICK_KEY_BASE_POINTS = 1_000_000;
6
+ /**
7
+ * Compute liquidity target and token needs based on position range and budgets
8
+ * Ported from compute_liquidity_target_and_token_needs in math.rs
9
+ */
10
+ function computeLiquidityTargetAndTokenNeeds(snap, spotPriceCurrent, priceEffLower, priceEffUpper, lowerPrice, upperPrice, lowerTickIdx, upperTickIdx, currentIndex, maxSy, maxPt, epsilonClamp) {
11
+ // Decide region and compute target ΔL and token needs
12
+ // IMPORTANT: Price below range = SY only; Price above range = PT only
13
+ if (spotPriceCurrent <= lowerPrice) {
14
+ // Below range: SY only (NOT PT!)
15
+ const deltaCTotal = Math.max(priceEffLower - priceEffUpper, epsilonClamp);
16
+ const liquidityFromSy = maxSy / deltaCTotal;
17
+ const liquidityTarget = liquidityFromSy;
18
+ const syNeed = liquidityTarget * (priceEffLower - priceEffUpper);
19
+ return {
20
+ liquidityTarget: Math.floor(liquidityTarget),
21
+ syNeeded: Math.floor(syNeed),
22
+ ptNeeded: 0,
23
+ priceSplitForNeed: lowerPrice,
24
+ priceSplitTickIdx: lowerTickIdx,
25
+ };
26
+ }
27
+ else if (spotPriceCurrent >= upperPrice) {
28
+ // Above range: PT only (NOT SY!)
29
+ const duTotal = Math.max(upperPrice - lowerPrice, epsilonClamp);
30
+ const liquidityFromPt = maxPt / duTotal;
31
+ const liquidityTarget = liquidityFromPt;
32
+ const ptNeed = liquidityTarget * duTotal;
33
+ return {
34
+ liquidityTarget: Math.floor(liquidityTarget),
35
+ syNeeded: 0,
36
+ ptNeeded: Math.floor(ptNeed),
37
+ priceSplitForNeed: upperPrice,
38
+ priceSplitTickIdx: upperTickIdx,
39
+ };
40
+ }
41
+ else {
42
+ // Inside range: both sides
43
+ const duLeft = Math.max(spotPriceCurrent - lowerPrice, epsilonClamp);
44
+ const liquidityFromPt = maxPt / duLeft;
45
+ const priceEffCurrent = snap.getEffectivePrice(spotPriceCurrent);
46
+ const deltaCRight = Math.max(priceEffCurrent - priceEffUpper, epsilonClamp);
47
+ const liquidityFromSy = maxSy / deltaCRight;
48
+ const liquidityTarget = Math.min(liquidityFromPt, liquidityFromSy);
49
+ const ptNeed = liquidityTarget * duLeft;
50
+ const syNeed = liquidityTarget * deltaCRight;
51
+ return {
52
+ liquidityTarget: Math.floor(liquidityTarget),
53
+ syNeeded: Math.floor(syNeed),
54
+ ptNeeded: Math.floor(ptNeed),
55
+ priceSplitForNeed: spotPriceCurrent,
56
+ priceSplitTickIdx: currentIndex,
57
+ };
58
+ }
59
+ }
60
+ exports.computeLiquidityTargetAndTokenNeeds = computeLiquidityTargetAndTokenNeeds;
61
+ /**
62
+ * Simulate adding liquidity to the CLMM market
63
+ * This is a pure function that does not mutate the market state
64
+ */
65
+ function simulateAddLiquidity(marketState, args) {
66
+ const { financials, configurationOptions, ticks } = marketState;
67
+ const secondsRemaining = Math.max(0, Number(financials.expirationTs) - Date.now() / 1000);
68
+ // Create effective price snapshot
69
+ const snap = new utils_1.EffSnap((0, utils_1.normalizedTimeRemaining)(secondsRemaining), args.syExchangeRate);
70
+ // Current spot price
71
+ const currentSpot = ticks.currentSpotPrice;
72
+ // Resolve tick prices - convert tick keys (parts per million) to spot prices
73
+ const lowerPrice = 1 + args.lowerTick / TICK_KEY_BASE_POINTS;
74
+ const upperPrice = 1 + args.upperTick / TICK_KEY_BASE_POINTS;
75
+ // Precompute effective prices
76
+ const priceEffLower = snap.getEffectivePrice(lowerPrice);
77
+ const priceEffUpper = snap.getEffectivePrice(upperPrice);
78
+ // Calculate liquidity needs
79
+ const liquidityNeeds = computeLiquidityTargetAndTokenNeeds(snap, currentSpot, priceEffLower, priceEffUpper, lowerPrice, upperPrice, args.lowerTick, args.upperTick, ticks.currentTick, args.maxSy, args.maxPt, configurationOptions.epsilonClamp);
80
+ // Enforce budgets
81
+ const sySpent = liquidityNeeds.syNeeded;
82
+ const ptSpent = liquidityNeeds.ptNeeded;
83
+ if (sySpent > args.maxSy) {
84
+ throw new Error(`Insufficient SY budget: need ${sySpent}, have ${args.maxSy}`);
85
+ }
86
+ if (ptSpent > args.maxPt) {
87
+ throw new Error(`Insufficient PT budget: need ${ptSpent}, have ${args.maxPt}`);
88
+ }
89
+ return {
90
+ deltaL: liquidityNeeds.liquidityTarget,
91
+ sySpent,
92
+ ptSpent,
93
+ };
94
+ }
95
+ exports.simulateAddLiquidity = simulateAddLiquidity;
96
+ /**
97
+ * Calculate the LP tokens that will be received for a given deposit
98
+ * This is useful for UI display and slippage calculations
99
+ */
100
+ function calculateLpOut(liquidityAdded, totalLiquidityBefore) {
101
+ if (totalLiquidityBefore === 0) {
102
+ // First deposit
103
+ return liquidityAdded;
104
+ }
105
+ // LP tokens are proportional to liquidity added
106
+ return liquidityAdded;
107
+ }
108
+ exports.calculateLpOut = calculateLpOut;
109
+ /**
110
+ * Estimate the balanced amounts of PT and SY needed for a liquidity deposit
111
+ * Given a target liquidity amount, calculate how much PT and SY are needed
112
+ */
113
+ function estimateBalancedDeposit(marketState, targetLiquidity, lowerTickApy, upperTickApy) {
114
+ const { financials, ticks } = marketState;
115
+ const secondsRemaining = Math.max(0, Number(financials.expirationTs) - Date.now() / 1000);
116
+ const snap = new utils_1.EffSnap((0, utils_1.normalizedTimeRemaining)(secondsRemaining), marketState.currentSyExchangeRate);
117
+ const currentSpot = ticks.currentSpotPrice;
118
+ // Convert APY (in %) to spot price: 1 + apy/100
119
+ const lowerPrice = 1.0 + lowerTickApy / 100;
120
+ const upperPrice = 1.0 + upperTickApy / 100;
121
+ const priceEffLower = snap.getEffectivePrice(lowerPrice);
122
+ const priceEffUpper = snap.getEffectivePrice(upperPrice);
123
+ if (currentSpot <= lowerPrice) {
124
+ // Below range: SY only
125
+ const syNeeded = (targetLiquidity / snap.timeFactor) * (priceEffLower - priceEffUpper);
126
+ return { ptNeeded: 0, syNeeded };
127
+ }
128
+ else if (currentSpot >= upperPrice) {
129
+ // Above range: PT only
130
+ const ptNeeded = targetLiquidity * (upperPrice - lowerPrice);
131
+ return { ptNeeded, syNeeded: 0 };
132
+ }
133
+ else {
134
+ // Inside range: both
135
+ const ptNeeded = targetLiquidity * (upperPrice - currentSpot);
136
+ const priceEffCurrent = snap.getEffectivePrice(currentSpot);
137
+ const syNeeded = (targetLiquidity / snap.timeFactor) * (priceEffLower - priceEffCurrent);
138
+ return { ptNeeded, syNeeded };
139
+ }
140
+ }
141
+ exports.estimateBalancedDeposit = estimateBalancedDeposit;
142
+ /** Calculate SY and PT needed to deposit into liquidity pool from base token amount */
143
+ /** Off-chain analogue of on-chain wrapper_provide_liquidity function */
144
+ function calcDepositSyAndPtFromBaseAmount(params) {
145
+ const { expirationTs, currentSpotPrice, syExchangeRate, lowerPrice, upperPrice, baseTokenAmount } = params;
146
+ if (baseTokenAmount <= 0 || syExchangeRate <= 0) {
147
+ return {
148
+ syNeeded: 0,
149
+ ptNeeded: 0,
150
+ };
151
+ }
152
+ const secondsRemaining = Math.max(0, expirationTs);
153
+ const effSnap = new utils_1.EffSnap((0, utils_1.normalizedTimeRemaining)(secondsRemaining), syExchangeRate);
154
+ const priceEffLower = effSnap.getEffectivePrice(lowerPrice);
155
+ const priceEffUpper = effSnap.getEffectivePrice(upperPrice);
156
+ // We mirror the on-chain logic in `wrapper_provide_liquidity`:
157
+ // 1. Use a large mock amount for both SY and PT to infer the *ratio* of SY/PT
158
+ // the market "wants" for this price range (compute_token_needs on-chain).
159
+ // 2. Use that ratio plus the current SY exchange rate to decide how much of the
160
+ // minted SY should be stripped into PT to keep the user-close to pool proportions.
161
+ const { syNeeded: syMock, ptNeeded: ptMock } = computeLiquidityTargetAndTokenNeeds(effSnap, currentSpotPrice, priceEffLower, priceEffUpper, lowerPrice, upperPrice, 0, 0, 0, 1e9, 1e9, 1e-18);
162
+ // Total SY the user would get by minting SY from base off-chain
163
+ // (we approximate on-chain `mint_sy_return_data.sy_out_amount`).
164
+ const syAmount = Math.floor(baseTokenAmount * syExchangeRate);
165
+ // Off-chain analogue of on-chain `calc_strip_amount`:
166
+ // B = (amt_sy * market_pt_liq) / (market_pt_liq + market_sy_liq * cur_sy_rate)
167
+ // here: amt_sy = syAmount, market_pt_liq = ptMock, market_sy_liq = syMock
168
+ const denominator = syExchangeRate * syMock + ptMock;
169
+ const toStrip = denominator > 0 ? Math.ceil((syAmount * ptMock) / denominator) : 0;
170
+ const syForLiquidity = Math.max(0, syAmount - toStrip);
171
+ // We approximate PT created from the stripped SY as:
172
+ // ptAmount ≈ toStrip * syExchangeRate
173
+ // which is consistent with PT being a claim on base and `syExchangeRate`
174
+ // giving the base value of 1 SY.
175
+ const ptFromStrip = Math.floor(toStrip * syExchangeRate);
176
+ return {
177
+ syNeeded: syForLiquidity,
178
+ ptNeeded: ptFromStrip,
179
+ };
180
+ }
181
+ exports.calcDepositSyAndPtFromBaseAmount = calcDepositSyAndPtFromBaseAmount;
182
+ /**
183
+ * Calculate the amount of SY to strip into PT based on market liquidity ratio
184
+ * This matches the Rust implementation: calc_strip_amount in wrapper_provide_liquidity.rs
185
+ *
186
+ * The formula splits totalAmountSy into two lots: A (remains as SY) and B (stripped to PT)
187
+ * such that: A / (B * curSyRate) = marketSyLiq / marketPtLiq
188
+ *
189
+ * Solution: B = (totalAmountSy * marketPtLiq) / (marketPtLiq + marketSyLiq * curSyRate)
190
+ */
191
+ function calcStripAmount(totalAmountSy, curSyRate, marketPtLiq, marketSyLiq) {
192
+ const toStrip = (totalAmountSy * marketPtLiq) / (curSyRate * marketSyLiq + marketPtLiq);
193
+ return Math.ceil(toStrip);
194
+ }
195
+ /**
196
+ * Simulate wrapper_provide_liquidity instruction
197
+ * This matches the flow in wrapper_provide_liquidity.rs:
198
+ * 1. Mints SY from base asset
199
+ * 2. Calculates mock token needs for the tick range
200
+ * 3. Strips some SY into PT/YT based on the ratio
201
+ * 4. Deposits remaining SY + PT into concentrated liquidity position
202
+ * 5. Returns YT to user
203
+ *
204
+ * @param marketState - Current market state
205
+ * @param amountBase - Amount of base tokens (in asset terms, will be converted to SY)
206
+ * @param lowerTick - Lower tick (APY in basis points)
207
+ * @param upperTick - Upper tick (APY in basis points)
208
+ * @param syExchangeRate - SY exchange rate
209
+ * @returns Simulation result with LP out, YT out, amounts spent, etc.
210
+ */
211
+ function simulateWrapperProvideLiquidity(marketState, amountBase, lowerTick, upperTick, syExchangeRate) {
212
+ try {
213
+ const { financials, configurationOptions, ticks } = marketState;
214
+ const secondsRemaining = Math.max(0, Number(financials.expirationTs) - Date.now() / 1000);
215
+ // Step 1: Convert base to SY (simulating mint_sy)
216
+ const syAmount = amountBase / syExchangeRate;
217
+ // Step 2: Create effective price snapshot
218
+ const snap = new utils_1.EffSnap((0, utils_1.normalizedTimeRemaining)(secondsRemaining), syExchangeRate);
219
+ // Current spot price
220
+ const currentSpot = ticks.currentSpotPrice;
221
+ // Convert tick keys to prices using compute_spot_price formula
222
+ // Rust: (1.0 + (key as f64) / TICK_KEY_BASE_POINTS).ln().exp() which simplifies to 1 + key/1_000_000
223
+ const lowerPrice = 1.0 + lowerTick / TICK_KEY_BASE_POINTS;
224
+ const upperPrice = 1.0 + upperTick / TICK_KEY_BASE_POINTS;
225
+ // Precompute effective prices
226
+ const priceEffLower = snap.getEffectivePrice(lowerPrice);
227
+ const priceEffUpper = snap.getEffectivePrice(upperPrice);
228
+ // Step 3: Calculate "mock" token needs using MOCK_AMOUNT (1e9)
229
+ // This gives us the ratio of SY:PT needed for this tick range
230
+ const MOCK_AMOUNT = 1e9;
231
+ const mockNeeds = computeLiquidityTargetAndTokenNeeds(snap, currentSpot, priceEffLower, priceEffUpper, lowerPrice, upperPrice, lowerTick, upperTick, ticks.currentTick, MOCK_AMOUNT, MOCK_AMOUNT, configurationOptions.epsilonClamp);
232
+ // Step 4: Calculate how much SY to strip
233
+ // Uses the mock amounts to determine the ratio
234
+ const syToStrip = calcStripAmount(syAmount, syExchangeRate, mockNeeds.ptNeeded, mockNeeds.syNeeded);
235
+ console.log("[simulateWrapperProvideLiquidity] Strip calculation:", {
236
+ syToStrip,
237
+ formula: `(${syAmount} * ${mockNeeds.ptNeeded}) / (${syExchangeRate} * ${mockNeeds.syNeeded} + ${mockNeeds.ptNeeded})`,
238
+ });
239
+ // Step 5: Calculate PT and YT from stripping
240
+ // When you strip SY, you get PT = SY * syExchangeRate and YT = SY * syExchangeRate
241
+ const ptFromStrip = syToStrip * syExchangeRate;
242
+ const ytOut = ptFromStrip; // YT amount equals PT amount from strip
243
+ // Step 6: Calculate remaining SY after strip
244
+ const syRemainder = syAmount - syToStrip;
245
+ // Step 7: Simulate deposit liquidity with remaining SY and PT
246
+ const depositResult = simulateAddLiquidity(marketState, {
247
+ lowerTick,
248
+ upperTick,
249
+ maxSy: Math.floor(syRemainder),
250
+ maxPt: Math.floor(ptFromStrip),
251
+ syExchangeRate,
252
+ });
253
+ return {
254
+ lpOut: depositResult.deltaL,
255
+ ytOut: Math.floor(ytOut),
256
+ syToStrip: Math.floor(syToStrip),
257
+ ptFromStrip: Math.floor(ptFromStrip),
258
+ syRemainder: Math.floor(syRemainder),
259
+ ptDeposited: depositResult.ptSpent,
260
+ syDeposited: depositResult.sySpent,
261
+ };
262
+ }
263
+ catch (error) {
264
+ console.error("[simulateWrapperProvideLiquidity] Error:", error);
265
+ return null;
266
+ }
267
+ }
268
+ exports.simulateWrapperProvideLiquidity = simulateWrapperProvideLiquidity;
269
+ //# sourceMappingURL=addLiquidity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"addLiquidity.js","sourceRoot":"","sources":["../src/addLiquidity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function bisectSearch2(func: (x: number) => number, lo: number, hi: number, epsilon?: number, maxIterations?: number): number | null;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.bisectSearch2 = void 0;
4
+ function bisectSearch2(func, lo, hi, epsilon = 0.0001, maxIterations = 2000) {
5
+ if (lo > hi) {
6
+ throw new Error("lo must be less than hi");
7
+ }
8
+ let iterations = 0;
9
+ // Evaluate boundaries once
10
+ const fLo = func(lo);
11
+ const fHi = func(hi);
12
+ // Check if the target is bracketed between lo and hi
13
+ if (fLo * fHi > 0) {
14
+ return null;
15
+ }
16
+ // Check if we're already at a root
17
+ if (Math.abs(fLo) < epsilon)
18
+ return lo;
19
+ if (Math.abs(fHi) < epsilon)
20
+ return hi;
21
+ // Track best approximation in case we don't converge exactly
22
+ let bestMid = (lo + hi) / 2;
23
+ let bestError = Infinity;
24
+ // Binary search within the range
25
+ while (iterations < maxIterations) {
26
+ // Use midpoint for standard bisection
27
+ const mid = (lo + hi) / 2;
28
+ const fMid = func(mid);
29
+ const absFMid = Math.abs(fMid);
30
+ iterations++;
31
+ // Track best approximation
32
+ if (absFMid < bestError) {
33
+ bestError = absFMid;
34
+ bestMid = mid;
35
+ }
36
+ // Check convergence
37
+ if (absFMid < epsilon) {
38
+ return mid;
39
+ }
40
+ // Early termination if range is too small relative to epsilon
41
+ const rangeSize = hi - lo;
42
+ if (rangeSize < epsilon * 0.01) {
43
+ return bestMid;
44
+ }
45
+ // Adjust the search range based on the target
46
+ // Use fLo instead of recalculating func(lo)
47
+ if (fLo * fMid < 0) {
48
+ // eslint-disable-next-line no-param-reassign
49
+ hi = mid;
50
+ // Note: fHi would be fMid, but we don't need to track it
51
+ }
52
+ else {
53
+ // eslint-disable-next-line no-param-reassign
54
+ lo = mid;
55
+ // Note: fLo would be fMid, but we recalculate in the condition anyway
56
+ }
57
+ }
58
+ // Return best approximation if we didn't converge
59
+ return bestMid;
60
+ }
61
+ exports.bisectSearch2 = bisectSearch2;
62
+ //# sourceMappingURL=bisect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bisect.js","sourceRoot":"","sources":["../src/bisect.ts"],"names":[],"mappings":";;;AAAA,SAAgB,aAAa,CAC3B,IAA2B,EAC3B,EAAU,EACV,EAAU,EACV,UAAkB,MAAM,EACxB,gBAAwB,IAAI;IAE5B,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAA;IAC5C,CAAC;IAED,IAAI,UAAU,GAAG,CAAC,CAAA;IAElB,2BAA2B;IAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,CAAA;IACpB,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,CAAA;IAEpB,qDAAqD;IACrD,IAAI,GAAG,GAAG,GAAG,GAAG,CAAC,EAAE,CAAC;QAClB,OAAO,IAAI,CAAA;IACb,CAAC;IAED,mCAAmC;IACnC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,OAAO;QAAE,OAAO,EAAE,CAAA;IACtC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,OAAO;QAAE,OAAO,EAAE,CAAA;IAEtC,6DAA6D;IAC7D,IAAI,OAAO,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAA;IAC3B,IAAI,SAAS,GAAG,QAAQ,CAAA;IAExB,iCAAiC;IACjC,OAAO,UAAU,GAAG,aAAa,EAAE,CAAC;QAClC,sCAAsC;QACtC,MAAM,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAA;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAA;QACtB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAE9B,UAAU,EAAE,CAAA;QAEZ,2BAA2B;QAC3B,IAAI,OAAO,GAAG,SAAS,EAAE,CAAC;YACxB,SAAS,GAAG,OAAO,CAAA;YACnB,OAAO,GAAG,GAAG,CAAA;QACf,CAAC;QAED,oBAAoB;QACpB,IAAI,OAAO,GAAG,OAAO,EAAE,CAAC;YACtB,OAAO,GAAG,CAAA;QACZ,CAAC;QAED,8DAA8D;QAC9D,MAAM,SAAS,GAAG,EAAE,GAAG,EAAE,CAAA;QACzB,IAAI,SAAS,GAAG,OAAO,GAAG,IAAI,EAAE,CAAC;YAC/B,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,8CAA8C;QAC9C,4CAA4C;QAC5C,IAAI,GAAG,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC;YACnB,6CAA6C;YAC7C,EAAE,GAAG,GAAG,CAAA;YACR,yDAAyD;QAC3D,CAAC;aAAM,CAAC;YACN,6CAA6C;YAC7C,EAAE,GAAG,GAAG,CAAA;YACR,sEAAsE;QACxE,CAAC;IACH,CAAC;IAED,kDAAkD;IAClD,OAAO,OAAO,CAAA;AAChB,CAAC;AAvED,sCAuEC"}
@@ -0,0 +1,24 @@
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
+ export * from "./types";
13
+ export { EffSnap, normalizedTimeRemaining, calculateFeeRate, getFeeFromAmount, getActiveLiquidity, getSuccessorTickKey, getPredecessorTickKey, getImpliedRate, findTickByKey, convertApyToApyBp, convertApyBpToApy, calcPtPriceInAsset, getSuccessorTickByIdx, } from "./utils";
14
+ export { simulateSwap } from "./swap";
15
+ export { getSwapQuote, QuoteDirection } from "./quote";
16
+ export { simulateBuyYt, simulateSellYt, simulateBuyYtWithSyIn } from "./ytTrades";
17
+ export type { BuyYtSimulationArgs, BuyYtSimulationResult, SellYtSimulationArgs, SellYtSimulationResult, } from "./ytTrades";
18
+ export { simulateAddLiquidity, computeLiquidityTargetAndTokenNeeds, calculateLpOut, estimateBalancedDeposit, calcDepositSyAndPtFromBaseAmount, simulateWrapperProvideLiquidity, } from "./addLiquidity";
19
+ export { getPtAndSyOnWithdrawLiquidity } from "./withdrawLiquidity";
20
+ export { bisectSearch2 } from "./bisect";
21
+ export { buildLiquidityHistogram, buildLiquidityHistogramSimple } from "./liquidityHistogram";
22
+ export type { LiquidityHistogramBin } from "./liquidityHistogram";
23
+ export type { SwapArgs, SwapOutcome, AddLiquidityArgs, AddLiquidityOutcome, LiquidityNeeds, MarketThreeState, } from "./types";
24
+ export { SwapDirection } from "./types";