@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.
- package/CHANGELOG.md +8 -0
- package/README.md +197 -0
- package/build/addLiquidity.d.ts +67 -0
- package/build/addLiquidity.js +269 -0
- package/build/addLiquidity.js.map +1 -0
- package/build/bisect.d.ts +1 -0
- package/build/bisect.js +62 -0
- package/build/bisect.js.map +1 -0
- package/build/index.d.ts +24 -0
- package/build/index.js +76 -0
- package/build/index.js.map +1 -0
- package/build/liquidityHistogram.d.ts +50 -0
- package/build/liquidityHistogram.js +162 -0
- package/build/liquidityHistogram.js.map +1 -0
- package/build/quote.d.ts +18 -0
- package/build/quote.js +106 -0
- package/build/quote.js.map +1 -0
- package/build/swap-v2.d.ts +20 -0
- package/build/swap-v2.js +261 -0
- package/build/swap-v2.js.map +1 -0
- package/build/swap.d.ts +15 -0
- package/build/swap.js +249 -0
- package/build/swap.js.map +1 -0
- package/build/swapLegacy.d.ts +16 -0
- package/build/swapLegacy.js +229 -0
- package/build/swapLegacy.js.map +1 -0
- package/build/swapV2.d.ts +11 -0
- package/build/swapV2.js +406 -0
- package/build/swapV2.js.map +1 -0
- package/build/types.d.ts +73 -0
- package/build/types.js +9 -0
- package/build/types.js.map +1 -0
- package/build/utils.d.ts +119 -0
- package/build/utils.js +219 -0
- package/build/utils.js.map +1 -0
- package/build/utilsV2.d.ts +88 -0
- package/build/utilsV2.js +180 -0
- package/build/utilsV2.js.map +1 -0
- package/build/withdrawLiquidity.d.ts +8 -0
- package/build/withdrawLiquidity.js +174 -0
- package/build/withdrawLiquidity.js.map +1 -0
- package/build/ytTrades.d.ts +106 -0
- package/build/ytTrades.js +292 -0
- package/build/ytTrades.js.map +1 -0
- package/build/ytTradesLegacy.d.ts +106 -0
- package/build/ytTradesLegacy.js +292 -0
- package/build/ytTradesLegacy.js.map +1 -0
- package/examples/.env.example +1 -0
- package/examples/test-histogram-simple.ts +172 -0
- package/examples/test-histogram.ts +112 -0
- package/package.json +26 -0
- package/src/addLiquidity.ts +384 -0
- package/src/bisect.ts +72 -0
- package/src/index.ts +74 -0
- package/src/liquidityHistogram.ts +192 -0
- package/src/quote.ts +128 -0
- package/src/swap.ts +299 -0
- package/src/swapLegacy.ts +272 -0
- package/src/types.ts +80 -0
- package/src/utils.ts +235 -0
- package/src/withdrawLiquidity.ts +240 -0
- package/src/ytTrades.ts +419 -0
- package/tsconfig.json +17 -0
package/CHANGELOG.md
ADDED
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;
|
package/build/bisect.js
ADDED
|
@@ -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"}
|
package/build/index.d.ts
ADDED
|
@@ -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";
|