@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
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.simulateSellYt = exports.simulateBuyYtWithSyIn = exports.simulateBuyYt = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* YT (Yield Token) trade simulations
|
|
6
|
+
*
|
|
7
|
+
* YT trades are multi-step operations that involve PT trades:
|
|
8
|
+
* - Buy YT: Strip SY → PT+YT, then sell PT back to pool
|
|
9
|
+
* - Sell YT: Merge PT+YT → SY, then buy PT from pool to repay
|
|
10
|
+
*/
|
|
11
|
+
const bisect_1 = require("./bisect");
|
|
12
|
+
const swapV2_1 = require("./swapV2");
|
|
13
|
+
const types_1 = require("./types");
|
|
14
|
+
/**
|
|
15
|
+
* Helper function to convert PY to SY
|
|
16
|
+
* @param syExchangeRate - The SY exchange rate
|
|
17
|
+
* @param pyAmount - The PY (PT or YT) amount
|
|
18
|
+
* @returns The equivalent SY amount
|
|
19
|
+
*/
|
|
20
|
+
function pyToSy(syExchangeRate, pyAmount) {
|
|
21
|
+
if (syExchangeRate <= 0)
|
|
22
|
+
return 0;
|
|
23
|
+
return Math.floor(pyAmount / syExchangeRate);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Helper function to convert SY to PY
|
|
27
|
+
* @param syExchangeRate - The SY exchange rate
|
|
28
|
+
* @param syAmount - The SY amount
|
|
29
|
+
* @returns The equivalent PY amount
|
|
30
|
+
*/
|
|
31
|
+
function syToPy(syExchangeRate, syAmount) {
|
|
32
|
+
return Math.floor(syAmount * syExchangeRate);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Simulates buying YT tokens
|
|
36
|
+
*
|
|
37
|
+
* Process:
|
|
38
|
+
* 1. Calculate how much SY to strip to get desired YT
|
|
39
|
+
* 2. Strip SY → PT + YT (PT amount ≈ YT amount)
|
|
40
|
+
* 3. Sell PT to the pool (PtToSy direction)
|
|
41
|
+
* 4. Net cost = SY stripped - SY received from PT sale
|
|
42
|
+
*
|
|
43
|
+
* @param marketState - Current market state
|
|
44
|
+
* @param args - Simulation arguments
|
|
45
|
+
* @returns Simulation result with net SY cost
|
|
46
|
+
*/
|
|
47
|
+
function simulateBuyYt(marketState, args) {
|
|
48
|
+
const { ytOut, syExchangeRate, priceSpotLimit } = args;
|
|
49
|
+
// Calculate how much SY needs to be stripped to get the desired YT
|
|
50
|
+
// Add 1 to counter-act the flooring function when converting from PY to SY
|
|
51
|
+
const syToStrip = pyToSy(syExchangeRate, ytOut) + 1;
|
|
52
|
+
// Stripping gives approximately equal amounts of PT and YT
|
|
53
|
+
const ptFromStrip = ytOut;
|
|
54
|
+
// Simulate selling the PT to get SY back
|
|
55
|
+
// Note: We use ytOut as the amount because PT out = YT out from the strip
|
|
56
|
+
const swapResult = (0, swapV2_1.simulateSwap)(marketState, {
|
|
57
|
+
direction: types_1.SwapDirection.PtToSy,
|
|
58
|
+
amountIn: ytOut,
|
|
59
|
+
priceSpotLimit,
|
|
60
|
+
syExchangeRate,
|
|
61
|
+
isCurrentFlashSwap: true,
|
|
62
|
+
});
|
|
63
|
+
const syFromPtSale = swapResult.amountOut;
|
|
64
|
+
// Net cost is the difference between what was stripped and what was received
|
|
65
|
+
const netSyCost = syToStrip - syFromPtSale;
|
|
66
|
+
return {
|
|
67
|
+
ytOut,
|
|
68
|
+
netSyCost,
|
|
69
|
+
syToStrip,
|
|
70
|
+
ptFromStrip,
|
|
71
|
+
syFromPtSale,
|
|
72
|
+
lpFee: swapResult.lpFeeChargedOutToken,
|
|
73
|
+
protocolFee: swapResult.protocolFeeChargedOutToken,
|
|
74
|
+
finalSpotPrice: swapResult.finalSpotPrice,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
exports.simulateBuyYt = simulateBuyYt;
|
|
78
|
+
/**
|
|
79
|
+
* Simulates buying YT tokens given a SY input amount
|
|
80
|
+
*
|
|
81
|
+
* Process:
|
|
82
|
+
* 1. Strip syIn → PT + YT
|
|
83
|
+
* 2. Sell PT → get SY back (with price impact)
|
|
84
|
+
* 3. Strip SY again → more PT + YT
|
|
85
|
+
* 4. Repeat until convergence
|
|
86
|
+
* Total YT = YT₁ + YT₂ + YT₃ + ...
|
|
87
|
+
*
|
|
88
|
+
* Uses bisection search to find ytOut such that netSyCost = syIn
|
|
89
|
+
*
|
|
90
|
+
* @param marketState - Current market state
|
|
91
|
+
* @param args - Simulation arguments
|
|
92
|
+
* @returns Simulation result with calculated YT output
|
|
93
|
+
*/
|
|
94
|
+
function simulateBuyYtWithSyIn(marketState, args) {
|
|
95
|
+
const { syIn, syExchangeRate, priceSpotLimit } = args;
|
|
96
|
+
// Lower bound: Start very low since PT recycling allows buying much more YT than naive calculation
|
|
97
|
+
// The actual minimum depends on PT price (how much SY we get back from selling PT)
|
|
98
|
+
// If PT price is high (close to 1), we get most SY back, so we can buy much more YT
|
|
99
|
+
// Example: With 1000 SY and PT price 0.9:
|
|
100
|
+
// - To get 5000 YT, strip 5000 SY → 5000 PT + 5000 YT
|
|
101
|
+
// - Sell 5000 PT at 0.9 → 4500 SY back
|
|
102
|
+
// - Net cost: 5000 - 4500 = 500 SY (way less than 1000!)
|
|
103
|
+
const minPossibleYt = 1;
|
|
104
|
+
// Better initial upper bound estimate based on market liquidity
|
|
105
|
+
// Maximum possible YT is constrained by available PT liquidity in the market
|
|
106
|
+
const marketPtLiquidity = Number(marketState.financials.ptBalance);
|
|
107
|
+
const marketSyLiquidity = Number(marketState.financials.syBalance);
|
|
108
|
+
// Conservative estimate: we can't buy more YT than there's PT in the market
|
|
109
|
+
// High multiplier needed because PT recycling amplifies buying power dramatically
|
|
110
|
+
// Formula: ytOut ≈ syIn / (1 - ptPrice)
|
|
111
|
+
// Examples: PT@0.99 → 100x, PT@0.95 → 20x, PT@0.90 → 10x, PT@0.80 → 5x
|
|
112
|
+
const liquidityBasedMax = Math.min(marketPtLiquidity * 0.9, // 90% of PT liquidity to be safe
|
|
113
|
+
syToPy(syExchangeRate, syIn) * 100);
|
|
114
|
+
// Start with a reasonable initial guess (10x covers most realistic scenarios)
|
|
115
|
+
let maxPossibleYt = Math.min(syToPy(syExchangeRate, syIn) * 10, liquidityBasedMax);
|
|
116
|
+
let foundUpperBound = false;
|
|
117
|
+
let lastValidCost = 0;
|
|
118
|
+
// Use exponential search with better growth rate
|
|
119
|
+
for (let attempt = 0; attempt < 12; attempt++) {
|
|
120
|
+
try {
|
|
121
|
+
const syToStrip = pyToSy(syExchangeRate, maxPossibleYt) + 1;
|
|
122
|
+
const swapResult = (0, swapV2_1.simulateSwap)(marketState, {
|
|
123
|
+
direction: types_1.SwapDirection.PtToSy,
|
|
124
|
+
amountIn: maxPossibleYt,
|
|
125
|
+
priceSpotLimit,
|
|
126
|
+
syExchangeRate,
|
|
127
|
+
isCurrentFlashSwap: true,
|
|
128
|
+
});
|
|
129
|
+
const syFromPtSale = swapResult.amountOut;
|
|
130
|
+
const netSyCost = syToStrip - syFromPtSale;
|
|
131
|
+
// If this costs more than syIn, we found our upper bound
|
|
132
|
+
if (netSyCost > syIn) {
|
|
133
|
+
foundUpperBound = true;
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
lastValidCost = netSyCost;
|
|
137
|
+
// Use adaptive growth rate based on how far we are from target
|
|
138
|
+
const costRatio = syIn / Math.max(netSyCost, 1);
|
|
139
|
+
const growthFactor = attempt < 3 ? 2.0 : Math.min(1.5, 1 + costRatio * 0.3);
|
|
140
|
+
maxPossibleYt *= growthFactor;
|
|
141
|
+
// Don't exceed liquidity constraints
|
|
142
|
+
if (maxPossibleYt > liquidityBasedMax) {
|
|
143
|
+
maxPossibleYt = liquidityBasedMax;
|
|
144
|
+
foundUpperBound = true;
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
// If simulation fails, we've exceeded market capacity
|
|
150
|
+
// Use last valid value with small buffer, ensuring it's > minPossibleYt
|
|
151
|
+
if (lastValidCost > 0) {
|
|
152
|
+
// Interpolate to find better upper bound
|
|
153
|
+
const estimatedMax = maxPossibleYt * 0.7 * (syIn / lastValidCost);
|
|
154
|
+
maxPossibleYt = Math.max(estimatedMax, minPossibleYt * 1.2);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
// No valid cost yet, use a conservative upper bound
|
|
158
|
+
maxPossibleYt = Math.max(maxPossibleYt * 0.8, minPossibleYt * 1.5);
|
|
159
|
+
}
|
|
160
|
+
foundUpperBound = true;
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (!foundUpperBound) {
|
|
165
|
+
throw new Error(`Could not find upper bound for YT amount. Market may have unusual price dynamics.`);
|
|
166
|
+
}
|
|
167
|
+
// Final safety check: ensure maxPossibleYt > minPossibleYt
|
|
168
|
+
if (maxPossibleYt <= minPossibleYt) {
|
|
169
|
+
maxPossibleYt = minPossibleYt * 2;
|
|
170
|
+
}
|
|
171
|
+
// Use bisection search to find the ytOut that results in netSyCost = syIn
|
|
172
|
+
// Adaptive epsilon based on input size for better precision scaling
|
|
173
|
+
const adaptiveEpsilon = Math.max(0.01, syIn * 0.0001);
|
|
174
|
+
// Reduce max iterations since we have better bounds
|
|
175
|
+
const maxIterations = 10000;
|
|
176
|
+
// Debug info for bounds validation
|
|
177
|
+
if (maxPossibleYt <= minPossibleYt) {
|
|
178
|
+
throw new Error(`Invalid bisection bounds for buy YT. ` +
|
|
179
|
+
`Min: ${minPossibleYt}, Max: ${maxPossibleYt}, ` +
|
|
180
|
+
`SyIn: ${syIn}, SyExchangeRate: ${syExchangeRate}, ` +
|
|
181
|
+
`MarketPtBalance: ${marketPtLiquidity}, MarketSyBalance: ${marketSyLiquidity}`);
|
|
182
|
+
}
|
|
183
|
+
const ytOut = (0, bisect_1.bisectSearch2)((ytGuess) => {
|
|
184
|
+
// Calculate the cost for this ytGuess
|
|
185
|
+
const syToStrip = pyToSy(syExchangeRate, ytGuess) + 1;
|
|
186
|
+
const swapResult = (0, swapV2_1.simulateSwap)(marketState, {
|
|
187
|
+
direction: types_1.SwapDirection.PtToSy,
|
|
188
|
+
amountIn: ytGuess,
|
|
189
|
+
priceSpotLimit,
|
|
190
|
+
syExchangeRate,
|
|
191
|
+
isCurrentFlashSwap: true,
|
|
192
|
+
});
|
|
193
|
+
const syFromPtSale = swapResult.amountOut;
|
|
194
|
+
const netSyCost = syToStrip - syFromPtSale;
|
|
195
|
+
// Return the difference between actual cost and target syIn
|
|
196
|
+
return netSyCost - syIn;
|
|
197
|
+
}, minPossibleYt, maxPossibleYt, adaptiveEpsilon, maxIterations);
|
|
198
|
+
if (ytOut === null) {
|
|
199
|
+
throw new Error(`Failed to converge on correct YT amount. ` +
|
|
200
|
+
`Search range: [${minPossibleYt}, ${maxPossibleYt}], ` +
|
|
201
|
+
`Epsilon: ${adaptiveEpsilon}`);
|
|
202
|
+
}
|
|
203
|
+
// Now calculate the full result with the found ytOut
|
|
204
|
+
return simulateBuyYt(marketState, {
|
|
205
|
+
ytOut,
|
|
206
|
+
syExchangeRate,
|
|
207
|
+
priceSpotLimit,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
exports.simulateBuyYtWithSyIn = simulateBuyYtWithSyIn;
|
|
211
|
+
/**
|
|
212
|
+
* Simulates selling YT tokens
|
|
213
|
+
*
|
|
214
|
+
* Process:
|
|
215
|
+
* 1. Merge PT + YT → SY (receive SY from the merge)
|
|
216
|
+
* 2. Buy PT from the pool to repay the borrowed PT (SyToPt direction)
|
|
217
|
+
* 3. Net received = SY from merge - SY spent on PT
|
|
218
|
+
*
|
|
219
|
+
* Note: The market must have at least 2x the YT amount in PT liquidity
|
|
220
|
+
* because the trader borrows PT, which is then bought back.
|
|
221
|
+
*
|
|
222
|
+
* @param marketState - Current market state
|
|
223
|
+
* @param args - Simulation arguments
|
|
224
|
+
* @returns Simulation result with net SY received
|
|
225
|
+
*/
|
|
226
|
+
function simulateSellYt(marketState, args) {
|
|
227
|
+
const { ytIn, syExchangeRate, priceSpotLimit } = args;
|
|
228
|
+
// Check if there's sufficient PT liquidity
|
|
229
|
+
// The market needs at least 2x the YT amount in PT
|
|
230
|
+
if (marketState.financials.ptBalance < ytIn * 2) {
|
|
231
|
+
throw new Error(`Insufficient PT liquidity in the market. Required: ${ytIn * 2}, Available: ${marketState.financials.ptBalance}`);
|
|
232
|
+
}
|
|
233
|
+
// Merging PT + YT gives back the original SY
|
|
234
|
+
// The amount of PT needed equals ytIn (1:1 ratio)
|
|
235
|
+
const syFromMerge = pyToSy(syExchangeRate, ytIn);
|
|
236
|
+
const ptNeeded = ytIn;
|
|
237
|
+
const upperBoundSwap = (0, swapV2_1.simulateSwap)(marketState, {
|
|
238
|
+
direction: types_1.SwapDirection.SyToPt,
|
|
239
|
+
amountIn: syFromMerge,
|
|
240
|
+
priceSpotLimit,
|
|
241
|
+
syExchangeRate,
|
|
242
|
+
isCurrentFlashSwap: true,
|
|
243
|
+
});
|
|
244
|
+
// Check that we can buy enough PT from CLMM
|
|
245
|
+
if (upperBoundSwap.amountOut < ptNeeded) {
|
|
246
|
+
throw new Error(`Cannot buy enough PT with available SY. Need ${ytIn} PT but can only afford ${upperBoundSwap.amountOut} PT`);
|
|
247
|
+
}
|
|
248
|
+
// Better initial bounds for bisection
|
|
249
|
+
// We know the upper bound from the check above, and we can estimate a better lower bound
|
|
250
|
+
// based on the current price
|
|
251
|
+
const estimatedLowerBound = Math.min(syFromMerge * 0.5, syFromMerge * 0.9); // Start from 50% of max, but not too close to upper
|
|
252
|
+
// Adaptive epsilon based on PT needed
|
|
253
|
+
const adaptiveEpsilon = Math.max(0.01, ptNeeded * 0.0001);
|
|
254
|
+
// Safety check: ensure lower bound is less than upper bound
|
|
255
|
+
const safeLowerBound = Math.min(estimatedLowerBound, syFromMerge * 0.95);
|
|
256
|
+
const safeUpperBound = syFromMerge;
|
|
257
|
+
if (safeLowerBound >= safeUpperBound) {
|
|
258
|
+
throw new Error(`Invalid bisection bounds for sell YT. Lower: ${safeLowerBound}, Upper: ${safeUpperBound}`);
|
|
259
|
+
}
|
|
260
|
+
const syToSpend = (0, bisect_1.bisectSearch2)((syGuess) => {
|
|
261
|
+
const swapResult = (0, swapV2_1.simulateSwap)(marketState, {
|
|
262
|
+
direction: types_1.SwapDirection.SyToPt,
|
|
263
|
+
amountIn: syGuess,
|
|
264
|
+
priceSpotLimit,
|
|
265
|
+
syExchangeRate,
|
|
266
|
+
isCurrentFlashSwap: true,
|
|
267
|
+
});
|
|
268
|
+
return swapResult.amountOut - ptNeeded;
|
|
269
|
+
}, safeLowerBound, safeUpperBound, adaptiveEpsilon, 100);
|
|
270
|
+
if (syToSpend === null) {
|
|
271
|
+
throw new Error("Failed to converge on correct SY amount using bisection search");
|
|
272
|
+
}
|
|
273
|
+
const swapResult = (0, swapV2_1.simulateSwap)(marketState, {
|
|
274
|
+
direction: types_1.SwapDirection.SyToPt,
|
|
275
|
+
amountIn: syToSpend,
|
|
276
|
+
priceSpotLimit,
|
|
277
|
+
syExchangeRate,
|
|
278
|
+
isCurrentFlashSwap: true,
|
|
279
|
+
});
|
|
280
|
+
const netSyReceived = syFromMerge - swapResult.amountInConsumed;
|
|
281
|
+
return {
|
|
282
|
+
ytIn,
|
|
283
|
+
netSyReceived,
|
|
284
|
+
syFromMerge,
|
|
285
|
+
sySpentOnPt: swapResult.amountInConsumed,
|
|
286
|
+
lpFee: swapResult.lpFeeChargedOutToken,
|
|
287
|
+
protocolFee: swapResult.protocolFeeChargedOutToken,
|
|
288
|
+
finalSpotPrice: swapResult.finalSpotPrice,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
exports.simulateSellYt = simulateSellYt;
|
|
292
|
+
//# sourceMappingURL=ytTradesLegacy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ytTradesLegacy.js","sourceRoot":"","sources":["../src/ytTradesLegacy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
RPC_URL=
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple example: Testing liquidity histogram with mock data
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { buildLiquidityHistogram, buildLiquidityHistogramSimple } from "../src"
|
|
6
|
+
import { Ticks } from "@exponent-labs/exponent-fetcher"
|
|
7
|
+
import { PublicKey } from "@solana/web3.js"
|
|
8
|
+
|
|
9
|
+
// Mock ticks data simulating a market with liquidity at various price points
|
|
10
|
+
export const mockTicks: Ticks = {
|
|
11
|
+
currentTick: 300,
|
|
12
|
+
currentSpotPrice: 0.03,
|
|
13
|
+
feeGrowthIndexGlobalPt: BigInt(0),
|
|
14
|
+
feeGrowthIndexGlobalSy: BigInt(0),
|
|
15
|
+
currentPrefixSum: BigInt(0),
|
|
16
|
+
market: new PublicKey("11111111111111111111111111111111"), // Dummy public key for testing
|
|
17
|
+
ticksTree: [
|
|
18
|
+
{
|
|
19
|
+
apyBasePoints: 50, // 0.5%
|
|
20
|
+
liquidityNet: BigInt(50000),
|
|
21
|
+
liquidityGross: BigInt(50000),
|
|
22
|
+
principalPt: BigInt(25000),
|
|
23
|
+
principalSy: BigInt(25000),
|
|
24
|
+
impliedRate: 1.005,
|
|
25
|
+
feeGrowthOutsidePt: BigInt(0),
|
|
26
|
+
feeGrowthOutsideSy: BigInt(0),
|
|
27
|
+
principalShareSupply: BigInt(50000),
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
apyBasePoints: 100, // 1%
|
|
31
|
+
liquidityNet: BigInt(100000),
|
|
32
|
+
liquidityGross: BigInt(100000),
|
|
33
|
+
principalPt: BigInt(50000),
|
|
34
|
+
principalSy: BigInt(50000),
|
|
35
|
+
impliedRate: 1.01,
|
|
36
|
+
feeGrowthOutsidePt: BigInt(0),
|
|
37
|
+
feeGrowthOutsideSy: BigInt(0),
|
|
38
|
+
principalShareSupply: BigInt(100000),
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
apyBasePoints: 200, // 2%
|
|
42
|
+
liquidityNet: BigInt(75000),
|
|
43
|
+
liquidityGross: BigInt(75000),
|
|
44
|
+
principalPt: BigInt(37500),
|
|
45
|
+
principalSy: BigInt(37500),
|
|
46
|
+
impliedRate: 1.02,
|
|
47
|
+
feeGrowthOutsidePt: BigInt(0),
|
|
48
|
+
feeGrowthOutsideSy: BigInt(0),
|
|
49
|
+
principalShareSupply: BigInt(75000),
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
apyBasePoints: 300, // 3%
|
|
53
|
+
liquidityNet: BigInt(80000),
|
|
54
|
+
liquidityGross: BigInt(80000),
|
|
55
|
+
principalPt: BigInt(40000),
|
|
56
|
+
principalSy: BigInt(40000),
|
|
57
|
+
impliedRate: 1.03,
|
|
58
|
+
feeGrowthOutsidePt: BigInt(0),
|
|
59
|
+
feeGrowthOutsideSy: BigInt(0),
|
|
60
|
+
principalShareSupply: BigInt(80000),
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
apyBasePoints: 400, // 4%
|
|
64
|
+
liquidityNet: BigInt(-60000),
|
|
65
|
+
liquidityGross: BigInt(60000),
|
|
66
|
+
principalPt: BigInt(30000),
|
|
67
|
+
principalSy: BigInt(30000),
|
|
68
|
+
impliedRate: 1.04,
|
|
69
|
+
feeGrowthOutsidePt: BigInt(0),
|
|
70
|
+
feeGrowthOutsideSy: BigInt(0),
|
|
71
|
+
principalShareSupply: BigInt(60000),
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
apyBasePoints: 500, // 5%
|
|
75
|
+
liquidityNet: BigInt(-50000),
|
|
76
|
+
liquidityGross: BigInt(50000),
|
|
77
|
+
principalPt: BigInt(25000),
|
|
78
|
+
principalSy: BigInt(25000),
|
|
79
|
+
impliedRate: 1.05,
|
|
80
|
+
feeGrowthOutsidePt: BigInt(0),
|
|
81
|
+
feeGrowthOutsideSy: BigInt(0),
|
|
82
|
+
principalShareSupply: BigInt(50000),
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
apyBasePoints: 600, // 6%
|
|
86
|
+
liquidityNet: BigInt(-50000),
|
|
87
|
+
liquidityGross: BigInt(50000),
|
|
88
|
+
principalPt: BigInt(15000),
|
|
89
|
+
principalSy: BigInt(15000),
|
|
90
|
+
impliedRate: 1.06,
|
|
91
|
+
feeGrowthOutsidePt: BigInt(0),
|
|
92
|
+
feeGrowthOutsideSy: BigInt(0),
|
|
93
|
+
principalShareSupply: BigInt(50000),
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
apyBasePoints: 700, // 7%
|
|
97
|
+
liquidityNet: BigInt(-40000),
|
|
98
|
+
liquidityGross: BigInt(40000),
|
|
99
|
+
principalPt: BigInt(20000),
|
|
100
|
+
principalSy: BigInt(20000),
|
|
101
|
+
impliedRate: 1.07,
|
|
102
|
+
feeGrowthOutsidePt: BigInt(0),
|
|
103
|
+
feeGrowthOutsideSy: BigInt(0),
|
|
104
|
+
principalShareSupply: BigInt(40000),
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
apyBasePoints: 800, // 8%
|
|
108
|
+
liquidityNet: BigInt(-30000),
|
|
109
|
+
liquidityGross: BigInt(30000),
|
|
110
|
+
principalPt: BigInt(15000),
|
|
111
|
+
principalSy: BigInt(15000),
|
|
112
|
+
impliedRate: 1.08,
|
|
113
|
+
feeGrowthOutsidePt: BigInt(0),
|
|
114
|
+
feeGrowthOutsideSy: BigInt(0),
|
|
115
|
+
principalShareSupply: BigInt(30000),
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
apyBasePoints: 1000, // 10%
|
|
119
|
+
liquidityNet: BigInt(-20000),
|
|
120
|
+
liquidityGross: BigInt(20000),
|
|
121
|
+
principalPt: BigInt(10000),
|
|
122
|
+
principalSy: BigInt(10000),
|
|
123
|
+
impliedRate: 1.1,
|
|
124
|
+
feeGrowthOutsidePt: BigInt(0),
|
|
125
|
+
feeGrowthOutsideSy: BigInt(0),
|
|
126
|
+
principalShareSupply: BigInt(20000),
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
console.log("=== Mock Market Configuration ===")
|
|
132
|
+
console.log(`Total Ticks: ${mockTicks.ticksTree.length}`)
|
|
133
|
+
console.log(`Current Spot Price: ${mockTicks.currentSpotPrice}`)
|
|
134
|
+
console.log()
|
|
135
|
+
|
|
136
|
+
// Build simple histogram (no projection)
|
|
137
|
+
console.log("=== Simple Histogram (Created Ticks Only) ===")
|
|
138
|
+
const simpleHistogram = buildLiquidityHistogramSimple(mockTicks)
|
|
139
|
+
console.log(`Bins: ${simpleHistogram.length}`)
|
|
140
|
+
|
|
141
|
+
for (const bin of simpleHistogram) {
|
|
142
|
+
console.log(` APY: ${bin.apy.toFixed(2)}%, PT: ${bin.principalPt}, SY: ${bin.principalSy}`)
|
|
143
|
+
}
|
|
144
|
+
console.log()
|
|
145
|
+
|
|
146
|
+
// Build projected histogram (aligned to tickSpace)
|
|
147
|
+
console.log("=== Projected Histogram (100bp bins) ===")
|
|
148
|
+
const tickSpace = 100 // 1% = 100 basis points
|
|
149
|
+
const projectedHistogram = buildLiquidityHistogram(mockTicks, tickSpace)
|
|
150
|
+
console.log(`Bins: ${projectedHistogram.length}`)
|
|
151
|
+
|
|
152
|
+
for (const bin of projectedHistogram) {
|
|
153
|
+
console.log(` APY: ${bin.apy.toFixed(2)}%, PT: ${bin.principalPt.toFixed(0)}, SY: ${bin.principalSy.toFixed(0)}`)
|
|
154
|
+
}
|
|
155
|
+
console.log()
|
|
156
|
+
|
|
157
|
+
// Verify totals
|
|
158
|
+
const totalPtSimple = simpleHistogram.reduce((sum, bin) => sum + bin.principalPt, 0)
|
|
159
|
+
const totalSySimple = simpleHistogram.reduce((sum, bin) => sum + bin.principalSy, 0)
|
|
160
|
+
const totalPtProjected = projectedHistogram.reduce((sum, bin) => sum + bin.principalPt, 0)
|
|
161
|
+
const totalSyProjected = projectedHistogram.reduce((sum, bin) => sum + bin.principalSy, 0)
|
|
162
|
+
|
|
163
|
+
console.log("=== Totals Verification ===")
|
|
164
|
+
console.log(`Simple: PT=${totalPtSimple}, SY=${totalSySimple}`)
|
|
165
|
+
console.log(`Projected: PT=${totalPtProjected.toFixed(0)}, SY=${totalSyProjected.toFixed(0)}`)
|
|
166
|
+
|
|
167
|
+
const ptDiff = Math.abs(totalPtSimple - totalPtProjected)
|
|
168
|
+
const syDiff = Math.abs(totalSySimple - totalSyProjected)
|
|
169
|
+
console.log(`Difference (due to rounding): PT=${ptDiff.toFixed(0)}, SY=${syDiff.toFixed(0)}`)
|
|
170
|
+
|
|
171
|
+
console.log()
|
|
172
|
+
console.log("✅ Histogram test completed successfully")
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example: Testing liquidity histogram on live market data
|
|
3
|
+
*
|
|
4
|
+
* This script demonstrates how to build a liquidity distribution histogram
|
|
5
|
+
* from market ticks data.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Connection, PublicKey } from "@solana/web3.js"
|
|
9
|
+
import { ExponentFetcher } from "@exponent-labs/exponent-fetcher"
|
|
10
|
+
import { buildLiquidityHistogram, buildLiquidityHistogramSimple } from "../src/liquidityHistogram"
|
|
11
|
+
|
|
12
|
+
// Example market address (replace with actual market)
|
|
13
|
+
const MARKET_ADDRESS = "5TSUipMkXQraAik8QVGkvvDFwhLxUjgfW7WqGpsfvG1C"
|
|
14
|
+
const RPC_URL = process.env.RPC_URL || "https://api.mainnet-beta.solana.com"
|
|
15
|
+
|
|
16
|
+
async function main() {
|
|
17
|
+
const connection = new Connection(RPC_URL, "confirmed")
|
|
18
|
+
const fetcher = new ExponentFetcher({ connection })
|
|
19
|
+
|
|
20
|
+
console.log("Fetching market data...")
|
|
21
|
+
const marketPubkey = new PublicKey(MARKET_ADDRESS)
|
|
22
|
+
const marketData = await fetcher.fetchMarketThree(marketPubkey)
|
|
23
|
+
|
|
24
|
+
console.log("Fetching ticks data...")
|
|
25
|
+
const ticksData = await fetcher.fetchMarketThreeTicks(marketData.ticks)
|
|
26
|
+
|
|
27
|
+
console.log(`\nMarket Configuration:`)
|
|
28
|
+
console.log(` Tick Space: ${marketData.configurationOptions.tickSpace} basis points`)
|
|
29
|
+
console.log(` Total Ticks: ${ticksData.ticksTree.length}`)
|
|
30
|
+
console.log(` Current Spot Price: ${ticksData.currentSpotPrice.toFixed(4)}`)
|
|
31
|
+
|
|
32
|
+
// Convert to market-three-math types
|
|
33
|
+
const ticks = {
|
|
34
|
+
currentTick: ticksData.currentTick,
|
|
35
|
+
currentSpotPrice: ticksData.currentSpotPrice,
|
|
36
|
+
feeGrowthIndexGlobalPt: Number(ticksData.feeGrowthIndexGlobalPt),
|
|
37
|
+
feeGrowthIndexGlobalSy: Number(ticksData.feeGrowthIndexGlobalSy),
|
|
38
|
+
currentPrefixSum: Number(ticksData.currentPrefixSum),
|
|
39
|
+
market: ticksData.market.toBase58(),
|
|
40
|
+
ticks: ticksData.ticksTree.map((tick) => ({
|
|
41
|
+
apyBasePoints: tick.apyBasePoints,
|
|
42
|
+
liquidityNet: Number(tick.liquidityNet),
|
|
43
|
+
liquidityGross: Number(tick.liquidityGross),
|
|
44
|
+
principalPt: Number(tick.principalPt),
|
|
45
|
+
principalSy: Number(tick.principalSy),
|
|
46
|
+
impliedRate: tick.impliedRate,
|
|
47
|
+
feeGrowthOutsidePt: Number(tick.feeGrowthOutsidePt),
|
|
48
|
+
feeGrowthOutsideSy: Number(tick.feeGrowthOutsideSy),
|
|
49
|
+
principalShareSupply: Number(tick.principalShareSupply),
|
|
50
|
+
})),
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Build simple histogram (no projection)
|
|
54
|
+
console.log("\n=== Simple Histogram (Created Ticks Only) ===")
|
|
55
|
+
const simpleHistogram = buildLiquidityHistogramSimple(ticks)
|
|
56
|
+
console.log(`Bins: ${simpleHistogram.length}`)
|
|
57
|
+
|
|
58
|
+
for (const bin of simpleHistogram.slice(0, 5)) {
|
|
59
|
+
// Show first 5
|
|
60
|
+
console.log(` APY: ${bin.apy.toFixed(2)}%, PT: ${bin.principalPt.toFixed(0)}, SY: ${bin.principalSy.toFixed(0)}`)
|
|
61
|
+
}
|
|
62
|
+
if (simpleHistogram.length > 5) {
|
|
63
|
+
console.log(` ... and ${simpleHistogram.length - 5} more`)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Build projected histogram (aligned to tickSpace)
|
|
67
|
+
console.log("\n=== Projected Histogram (TickSpace Aligned) ===")
|
|
68
|
+
const tickSpace = marketData.configurationOptions.tickSpace
|
|
69
|
+
const projectedHistogram = buildLiquidityHistogram(ticks, tickSpace)
|
|
70
|
+
console.log(`Bins: ${projectedHistogram.length}`)
|
|
71
|
+
|
|
72
|
+
for (const bin of projectedHistogram.slice(0, 10)) {
|
|
73
|
+
// Show first 10
|
|
74
|
+
console.log(` APY: ${bin.apy.toFixed(2)}%, PT: ${bin.principalPt.toFixed(0)}, SY: ${bin.principalSy.toFixed(0)}`)
|
|
75
|
+
}
|
|
76
|
+
if (projectedHistogram.length > 10) {
|
|
77
|
+
console.log(` ... and ${projectedHistogram.length - 10} more`)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Verify totals
|
|
81
|
+
const totalPtSimple = simpleHistogram.reduce((sum, bin) => sum + bin.principalPt, 0)
|
|
82
|
+
const totalSySimple = simpleHistogram.reduce((sum, bin) => sum + bin.principalSy, 0)
|
|
83
|
+
const totalPtProjected = projectedHistogram.reduce((sum, bin) => sum + bin.principalPt, 0)
|
|
84
|
+
const totalSyProjected = projectedHistogram.reduce((sum, bin) => sum + bin.principalSy, 0)
|
|
85
|
+
|
|
86
|
+
console.log("\n=== Totals ===")
|
|
87
|
+
console.log(`Simple: PT=${totalPtSimple.toFixed(0)}, SY=${totalSySimple.toFixed(0)}`)
|
|
88
|
+
console.log(`Projected: PT=${totalPtProjected.toFixed(0)}, SY=${totalSyProjected.toFixed(0)}`)
|
|
89
|
+
console.log(`Difference: PT=${(totalPtSimple - totalPtProjected).toFixed(0)}, SY=${(totalSySimple - totalSyProjected).toFixed(0)}`)
|
|
90
|
+
|
|
91
|
+
// Calculate distribution stats
|
|
92
|
+
if (projectedHistogram.length > 0) {
|
|
93
|
+
const maxPt = Math.max(...projectedHistogram.map((b) => b.principalPt))
|
|
94
|
+
const maxSy = Math.max(...projectedHistogram.map((b) => b.principalSy))
|
|
95
|
+
const maxPtBin = projectedHistogram.find((b) => b.principalPt === maxPt)
|
|
96
|
+
const maxSyBin = projectedHistogram.find((b) => b.principalSy === maxSy)
|
|
97
|
+
|
|
98
|
+
console.log("\n=== Distribution Stats ===")
|
|
99
|
+
console.log(`Max PT: ${maxPt.toFixed(0)} at APY ${maxPtBin?.apy.toFixed(2)}%`)
|
|
100
|
+
console.log(`Max SY: ${maxSy.toFixed(0)} at APY ${maxSyBin?.apy.toFixed(2)}%`)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
main()
|
|
105
|
+
.then(() => {
|
|
106
|
+
console.log("\n✅ Done")
|
|
107
|
+
process.exit(0)
|
|
108
|
+
})
|
|
109
|
+
.catch((err) => {
|
|
110
|
+
console.error("\n❌ Error:", err)
|
|
111
|
+
process.exit(1)
|
|
112
|
+
})
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@exponent-labs/market-three-math",
|
|
3
|
+
"version": "0.1.8",
|
|
4
|
+
"main": "build/index.js",
|
|
5
|
+
"types": "build/index.d.ts",
|
|
6
|
+
"license": "AGPL-3.0",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc --build",
|
|
9
|
+
"test": "jest",
|
|
10
|
+
"test:live": "ts-node examples/test-live-market.ts"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@exponent-labs/exponent-fetcher": "0.1.8",
|
|
14
|
+
"@solana/web3.js": "^1.95.8"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/jest": "^29.5.12",
|
|
18
|
+
"@types/node": "^20.0.0",
|
|
19
|
+
"dotenv": "^16.4.5",
|
|
20
|
+
"jest": "^29.7.0",
|
|
21
|
+
"ts-jest": "^29.1.2",
|
|
22
|
+
"ts-node": "10.9.2",
|
|
23
|
+
"typescript": "5.4.5"
|
|
24
|
+
},
|
|
25
|
+
"gitHead": "209b8847e9a0fadb5b5ec96b9b47f0ace4a3bf9d"
|
|
26
|
+
}
|