@exponent-labs/market-three-math 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +197 -0
  3. package/build/addLiquidity.d.ts +67 -0
  4. package/build/addLiquidity.js +269 -0
  5. package/build/addLiquidity.js.map +1 -0
  6. package/build/bisect.d.ts +1 -0
  7. package/build/bisect.js +62 -0
  8. package/build/bisect.js.map +1 -0
  9. package/build/index.d.ts +24 -0
  10. package/build/index.js +76 -0
  11. package/build/index.js.map +1 -0
  12. package/build/liquidityHistogram.d.ts +50 -0
  13. package/build/liquidityHistogram.js +162 -0
  14. package/build/liquidityHistogram.js.map +1 -0
  15. package/build/quote.d.ts +18 -0
  16. package/build/quote.js +106 -0
  17. package/build/quote.js.map +1 -0
  18. package/build/swap-v2.d.ts +20 -0
  19. package/build/swap-v2.js +261 -0
  20. package/build/swap-v2.js.map +1 -0
  21. package/build/swap.d.ts +15 -0
  22. package/build/swap.js +249 -0
  23. package/build/swap.js.map +1 -0
  24. package/build/swapLegacy.d.ts +16 -0
  25. package/build/swapLegacy.js +229 -0
  26. package/build/swapLegacy.js.map +1 -0
  27. package/build/swapV2.d.ts +11 -0
  28. package/build/swapV2.js +406 -0
  29. package/build/swapV2.js.map +1 -0
  30. package/build/types.d.ts +73 -0
  31. package/build/types.js +9 -0
  32. package/build/types.js.map +1 -0
  33. package/build/utils.d.ts +119 -0
  34. package/build/utils.js +219 -0
  35. package/build/utils.js.map +1 -0
  36. package/build/utilsV2.d.ts +88 -0
  37. package/build/utilsV2.js +180 -0
  38. package/build/utilsV2.js.map +1 -0
  39. package/build/withdrawLiquidity.d.ts +8 -0
  40. package/build/withdrawLiquidity.js +174 -0
  41. package/build/withdrawLiquidity.js.map +1 -0
  42. package/build/ytTrades.d.ts +106 -0
  43. package/build/ytTrades.js +292 -0
  44. package/build/ytTrades.js.map +1 -0
  45. package/build/ytTradesLegacy.d.ts +106 -0
  46. package/build/ytTradesLegacy.js +292 -0
  47. package/build/ytTradesLegacy.js.map +1 -0
  48. package/examples/.env.example +1 -0
  49. package/examples/test-histogram-simple.ts +172 -0
  50. package/examples/test-histogram.ts +112 -0
  51. package/package.json +26 -0
  52. package/src/addLiquidity.ts +384 -0
  53. package/src/bisect.ts +72 -0
  54. package/src/index.ts +74 -0
  55. package/src/liquidityHistogram.ts +192 -0
  56. package/src/quote.ts +128 -0
  57. package/src/swap.ts +299 -0
  58. package/src/swapLegacy.ts +272 -0
  59. package/src/types.ts +80 -0
  60. package/src/utils.ts +235 -0
  61. package/src/withdrawLiquidity.ts +240 -0
  62. package/src/ytTrades.ts +419 -0
  63. package/tsconfig.json +17 -0
@@ -0,0 +1,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
+ }