@hawksightco/hawk-sdk 1.3.169 → 1.3.171
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/dist/src/addresses.d.ts +1 -0
- package/dist/src/addresses.d.ts.map +1 -1
- package/dist/src/addresses.js +2 -1
- package/dist/src/classes/Transactions.d.ts +72 -1
- package/dist/src/classes/Transactions.d.ts.map +1 -1
- package/dist/src/classes/Transactions.js +560 -0
- package/dist/src/classes/TxGenerator.d.ts +72 -1
- package/dist/src/classes/TxGenerator.d.ts.map +1 -1
- package/dist/src/classes/TxGenerator.js +332 -0
- package/dist/src/functions.d.ts +18 -1
- package/dist/src/functions.d.ts.map +1 -1
- package/dist/src/functions.js +148 -0
- package/dist/src/idl/jupiter-idl.d.ts +36 -0
- package/dist/src/idl/jupiter-idl.d.ts.map +1 -1
- package/dist/src/idl/jupiter-idl.js +36 -0
- package/dist/src/ixGenerator/IyfMainIxGenerator.d.ts +6 -0
- package/dist/src/ixGenerator/IyfMainIxGenerator.d.ts.map +1 -1
- package/dist/src/ixGenerator/IyfMainIxGenerator.js +22 -0
- package/dist/src/ixGenerator/MeteoraDlmmIxGenerator.d.ts +167 -1
- package/dist/src/ixGenerator/MeteoraDlmmIxGenerator.d.ts.map +1 -1
- package/dist/src/ixGenerator/MeteoraDlmmIxGenerator.js +517 -0
- package/dist/src/meteora/index.d.ts +2 -0
- package/dist/src/meteora/index.d.ts.map +1 -0
- package/dist/src/meteora/index.js +17 -0
- package/dist/src/meteora/liquidityStrategy.d.ts +268 -0
- package/dist/src/meteora/liquidityStrategy.d.ts.map +1 -0
- package/dist/src/meteora/liquidityStrategy.js +1069 -0
- package/dist/src/meteora.d.ts +1 -0
- package/dist/src/meteora.d.ts.map +1 -1
- package/dist/src/meteora.js +6 -2
- package/dist/src/types.d.ts +139 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +16 -1
- package/package.json +7 -3
- package/test/artifacts/temp/.gitignore +2 -0
- package/test/artifacts/temp/accounts/.gitignore +2 -0
- package/test/visualization/output/.gitignore +2 -0
|
@@ -0,0 +1,1069 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.StrategyType = void 0;
|
|
7
|
+
exports.getQPriceFromId = getQPriceFromId;
|
|
8
|
+
exports.buildSpotStrategyParameters = buildSpotStrategyParameters;
|
|
9
|
+
exports.resetUninvolvedLiquidityParams = resetUninvolvedLiquidityParams;
|
|
10
|
+
exports.getAmountXForBin = getAmountXForBin;
|
|
11
|
+
exports.getAmountYForBin = getAmountYForBin;
|
|
12
|
+
exports.calculateChunkAmounts = calculateChunkAmounts;
|
|
13
|
+
exports.buildCurveStrategyParameters = buildCurveStrategyParameters;
|
|
14
|
+
exports.buildBidAskStrategyParameters = buildBidAskStrategyParameters;
|
|
15
|
+
exports.buildStrategyParameters = buildStrategyParameters;
|
|
16
|
+
exports.chunkBinRange = chunkBinRange;
|
|
17
|
+
exports.buildBitFlagAndNegateStrategyParameters = buildBitFlagAndNegateStrategyParameters;
|
|
18
|
+
exports.toAmountIntoBins = toAmountIntoBins;
|
|
19
|
+
exports.chunkDepositParameters = chunkDepositParameters;
|
|
20
|
+
const bn_js_1 = __importDefault(require("bn.js"));
|
|
21
|
+
/**
|
|
22
|
+
* Liquidity strategy types matching Meteora DLMM SDK.
|
|
23
|
+
*/
|
|
24
|
+
var StrategyType;
|
|
25
|
+
(function (StrategyType) {
|
|
26
|
+
/** Uniform distribution - equal value per bin (adjusted for price) */
|
|
27
|
+
StrategyType[StrategyType["SPOT"] = 0] = "SPOT";
|
|
28
|
+
/** Curve distribution - decreasing amounts away from active bin */
|
|
29
|
+
StrategyType[StrategyType["CURVE"] = 1] = "CURVE";
|
|
30
|
+
/** Bid-Ask distribution - increasing amounts away from active bin */
|
|
31
|
+
StrategyType[StrategyType["BID_ASK"] = 2] = "BID_ASK";
|
|
32
|
+
})(StrategyType || (exports.StrategyType = StrategyType = {}));
|
|
33
|
+
/**
|
|
34
|
+
* Scale offset used for fixed-point arithmetic in price calculations.
|
|
35
|
+
* Matches DLMM SDK's SCALE_OFFSET = 64
|
|
36
|
+
*/
|
|
37
|
+
const SCALE_OFFSET = 64;
|
|
38
|
+
/**
|
|
39
|
+
* Constants for Q64.64 fixed-point math (matching Meteora SDK)
|
|
40
|
+
*/
|
|
41
|
+
const ONE = new bn_js_1.default(1).shln(SCALE_OFFSET);
|
|
42
|
+
const MAX = new bn_js_1.default(2).pow(new bn_js_1.default(128)).sub(new bn_js_1.default(1));
|
|
43
|
+
const MAX_EXPONENTIAL = new bn_js_1.default(0x80000);
|
|
44
|
+
const BASIS_POINT_MAX = 10000;
|
|
45
|
+
/**
|
|
46
|
+
* Binary exponentiation for Q64.64 fixed-point numbers.
|
|
47
|
+
* Matches Meteora SDK's pow function from u64xu64_math.ts
|
|
48
|
+
*
|
|
49
|
+
* @param base - Base value in Q64.64 format
|
|
50
|
+
* @param exp - Exponent (can be negative)
|
|
51
|
+
* @returns base^exp in Q64.64 format
|
|
52
|
+
*/
|
|
53
|
+
function pow(base, exp) {
|
|
54
|
+
let invert = exp.isNeg();
|
|
55
|
+
if (exp.isZero()) {
|
|
56
|
+
return ONE;
|
|
57
|
+
}
|
|
58
|
+
exp = invert ? exp.abs() : exp;
|
|
59
|
+
if (exp.gt(MAX_EXPONENTIAL)) {
|
|
60
|
+
return new bn_js_1.default(0);
|
|
61
|
+
}
|
|
62
|
+
let squaredBase = base;
|
|
63
|
+
let result = ONE;
|
|
64
|
+
// For base >= 1, invert first for better precision
|
|
65
|
+
if (squaredBase.gte(result)) {
|
|
66
|
+
squaredBase = MAX.div(squaredBase);
|
|
67
|
+
invert = !invert;
|
|
68
|
+
}
|
|
69
|
+
if (!exp.and(new bn_js_1.default(0x1)).isZero()) {
|
|
70
|
+
result = result.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
71
|
+
}
|
|
72
|
+
squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
73
|
+
if (!exp.and(new bn_js_1.default(0x2)).isZero()) {
|
|
74
|
+
result = result.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
75
|
+
}
|
|
76
|
+
squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
77
|
+
if (!exp.and(new bn_js_1.default(0x4)).isZero()) {
|
|
78
|
+
result = result.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
79
|
+
}
|
|
80
|
+
squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
81
|
+
if (!exp.and(new bn_js_1.default(0x8)).isZero()) {
|
|
82
|
+
result = result.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
83
|
+
}
|
|
84
|
+
squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
85
|
+
if (!exp.and(new bn_js_1.default(0x10)).isZero()) {
|
|
86
|
+
result = result.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
87
|
+
}
|
|
88
|
+
squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
89
|
+
if (!exp.and(new bn_js_1.default(0x20)).isZero()) {
|
|
90
|
+
result = result.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
91
|
+
}
|
|
92
|
+
squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
93
|
+
if (!exp.and(new bn_js_1.default(0x40)).isZero()) {
|
|
94
|
+
result = result.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
95
|
+
}
|
|
96
|
+
squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
97
|
+
if (!exp.and(new bn_js_1.default(0x80)).isZero()) {
|
|
98
|
+
result = result.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
99
|
+
}
|
|
100
|
+
squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
101
|
+
if (!exp.and(new bn_js_1.default(0x100)).isZero()) {
|
|
102
|
+
result = result.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
103
|
+
}
|
|
104
|
+
squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
105
|
+
if (!exp.and(new bn_js_1.default(0x200)).isZero()) {
|
|
106
|
+
result = result.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
107
|
+
}
|
|
108
|
+
squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
109
|
+
if (!exp.and(new bn_js_1.default(0x400)).isZero()) {
|
|
110
|
+
result = result.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
111
|
+
}
|
|
112
|
+
squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
113
|
+
if (!exp.and(new bn_js_1.default(0x800)).isZero()) {
|
|
114
|
+
result = result.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
115
|
+
}
|
|
116
|
+
squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
117
|
+
if (!exp.and(new bn_js_1.default(0x1000)).isZero()) {
|
|
118
|
+
result = result.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
119
|
+
}
|
|
120
|
+
squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
121
|
+
if (!exp.and(new bn_js_1.default(0x2000)).isZero()) {
|
|
122
|
+
result = result.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
123
|
+
}
|
|
124
|
+
squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
125
|
+
if (!exp.and(new bn_js_1.default(0x4000)).isZero()) {
|
|
126
|
+
result = result.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
127
|
+
}
|
|
128
|
+
squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
129
|
+
if (!exp.and(new bn_js_1.default(0x8000)).isZero()) {
|
|
130
|
+
result = result.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
131
|
+
}
|
|
132
|
+
squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
133
|
+
if (!exp.and(new bn_js_1.default(0x10000)).isZero()) {
|
|
134
|
+
result = result.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
135
|
+
}
|
|
136
|
+
squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
137
|
+
if (!exp.and(new bn_js_1.default(0x20000)).isZero()) {
|
|
138
|
+
result = result.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
139
|
+
}
|
|
140
|
+
squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
141
|
+
if (!exp.and(new bn_js_1.default(0x40000)).isZero()) {
|
|
142
|
+
result = result.mul(squaredBase).shrn(SCALE_OFFSET);
|
|
143
|
+
}
|
|
144
|
+
if (result.isZero()) {
|
|
145
|
+
return new bn_js_1.default(0);
|
|
146
|
+
}
|
|
147
|
+
if (invert) {
|
|
148
|
+
result = MAX.div(result);
|
|
149
|
+
}
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Calculates the Q-price base factor: (1 + binStep/10000) in Q64.64 format.
|
|
154
|
+
* This represents the price multiplier between adjacent bins.
|
|
155
|
+
*
|
|
156
|
+
* @param binStep - The bin step in basis points (e.g., 100 = 1%)
|
|
157
|
+
* @returns Base factor in Q64.64 fixed-point format
|
|
158
|
+
*/
|
|
159
|
+
function getQPriceBaseFactor(binStep) {
|
|
160
|
+
const bps = binStep.shln(SCALE_OFFSET).div(new bn_js_1.default(BASIS_POINT_MAX));
|
|
161
|
+
return ONE.add(bps);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Calculates the Q-price from a bin ID.
|
|
165
|
+
* Price = (1 + binStep/10000)^binId in Q64.64 format.
|
|
166
|
+
*
|
|
167
|
+
* Uses binary exponentiation matching Meteora SDK for precision.
|
|
168
|
+
*
|
|
169
|
+
* @param binId - The bin ID (can be negative)
|
|
170
|
+
* @param binStep - The bin step in basis points
|
|
171
|
+
* @returns Price in Q64.64 fixed-point format
|
|
172
|
+
*/
|
|
173
|
+
function getQPriceFromId(binId, binStep) {
|
|
174
|
+
return pow(getQPriceBaseFactor(binStep), binId);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Calculates the sum of price weights for bins on the ask side (above active bin).
|
|
178
|
+
*
|
|
179
|
+
* For SPOT strategy, amount in each bin = x0 * price_weight
|
|
180
|
+
* where price_weight = (1 + binStep/10000)^(-(activeId + deltaId))
|
|
181
|
+
*
|
|
182
|
+
* Total amount = x0 * sum(price_weights)
|
|
183
|
+
* Therefore: x0 = totalAmount / sum(price_weights)
|
|
184
|
+
*
|
|
185
|
+
* @param activeId - The active bin ID
|
|
186
|
+
* @param minDeltaId - Minimum delta ID (relative to active bin)
|
|
187
|
+
* @param maxDeltaId - Maximum delta ID (relative to active bin)
|
|
188
|
+
* @param binStep - The bin step in basis points
|
|
189
|
+
* @returns Sum of price weights in Q64.64 format
|
|
190
|
+
*/
|
|
191
|
+
function getSumOfPriceWeights(activeId, minDeltaId, maxDeltaId, binStep) {
|
|
192
|
+
let totalWeight = new bn_js_1.default(0);
|
|
193
|
+
const baseFactor = getQPriceBaseFactor(binStep);
|
|
194
|
+
const minBinId = activeId.add(minDeltaId);
|
|
195
|
+
const maxBinId = activeId.add(maxDeltaId);
|
|
196
|
+
// Start from maxBinId and iterate down
|
|
197
|
+
// price = (1 + binStep/10000)^(-binId)
|
|
198
|
+
// We start with the price at -maxBinId and multiply by baseFactor as binId decreases
|
|
199
|
+
let currentPrice = getQPriceFromId(maxBinId.neg(), binStep);
|
|
200
|
+
for (let binId = maxBinId.toNumber(); binId >= minBinId.toNumber(); binId--) {
|
|
201
|
+
totalWeight = totalWeight.add(currentPrice);
|
|
202
|
+
// Moving to lower binId means multiplying price by baseFactor
|
|
203
|
+
currentPrice = currentPrice.mul(baseFactor).shrn(SCALE_OFFSET);
|
|
204
|
+
}
|
|
205
|
+
return totalWeight;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Finds x0 for SPOT strategy on the ask side (bins above active bin).
|
|
209
|
+
*
|
|
210
|
+
* For SPOT strategy with deltaX = 0:
|
|
211
|
+
* - Each bin gets: amountX = x0 * price_weight
|
|
212
|
+
* - Total amount = x0 * sum(price_weights)
|
|
213
|
+
* - Therefore: x0 = totalAmount / sum(price_weights)
|
|
214
|
+
*
|
|
215
|
+
* @param amountX - Total X amount to distribute
|
|
216
|
+
* @param minDeltaId - Minimum delta ID (relative to active bin), must be >= 0 for ask side
|
|
217
|
+
* @param maxDeltaId - Maximum delta ID (relative to active bin)
|
|
218
|
+
* @param binStep - The bin step in basis points
|
|
219
|
+
* @param activeId - The active bin ID
|
|
220
|
+
* @returns x0 value for the strategy
|
|
221
|
+
*/
|
|
222
|
+
function findX0ForSpot(amountX, minDeltaId, maxDeltaId, binStep, activeId) {
|
|
223
|
+
if (minDeltaId.gt(maxDeltaId) || amountX.isZero() || amountX.isNeg()) {
|
|
224
|
+
return new bn_js_1.default(0);
|
|
225
|
+
}
|
|
226
|
+
const totalWeight = getSumOfPriceWeights(activeId, minDeltaId, maxDeltaId, binStep);
|
|
227
|
+
if (totalWeight.isZero()) {
|
|
228
|
+
return new bn_js_1.default(0);
|
|
229
|
+
}
|
|
230
|
+
// x0 = amountX / totalWeight (in Q64.64)
|
|
231
|
+
// x0 = (amountX << 64) / totalWeight
|
|
232
|
+
return amountX.shln(SCALE_OFFSET).div(totalWeight);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Finds y0 for SPOT strategy on the bid side (bins below active bin).
|
|
236
|
+
*
|
|
237
|
+
* For SPOT strategy with deltaY = 0:
|
|
238
|
+
* - Each bin gets: amountY = y0 (constant per bin)
|
|
239
|
+
* - Total amount = y0 * num_bins
|
|
240
|
+
* - Therefore: y0 = totalAmount / num_bins
|
|
241
|
+
*
|
|
242
|
+
* @param amountY - Total Y amount to distribute
|
|
243
|
+
* @param minDeltaId - Minimum delta ID (relative to active bin), must be < 0 for bid side
|
|
244
|
+
* @param maxDeltaId - Maximum delta ID (relative to active bin)
|
|
245
|
+
* @returns y0 value for the strategy
|
|
246
|
+
*/
|
|
247
|
+
function findY0ForSpot(amountY, minDeltaId, maxDeltaId) {
|
|
248
|
+
if (minDeltaId.gt(maxDeltaId) || amountY.isZero() || amountY.isNeg()) {
|
|
249
|
+
return new bn_js_1.default(0);
|
|
250
|
+
}
|
|
251
|
+
// Number of bins = maxDeltaId - minDeltaId + 1
|
|
252
|
+
const numBins = maxDeltaId.sub(minDeltaId).addn(1);
|
|
253
|
+
if (numBins.isZero()) {
|
|
254
|
+
return new bn_js_1.default(0);
|
|
255
|
+
}
|
|
256
|
+
// y0 = amountY / numBins
|
|
257
|
+
return amountY.div(numBins);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Builds liquidity strategy parameters for SPOT (uniform) distribution.
|
|
261
|
+
*
|
|
262
|
+
* This function calculates x0 and y0 values that will distribute the given amounts
|
|
263
|
+
* uniformly across the specified bin range when used with the rebalanceLiquidity instruction.
|
|
264
|
+
*
|
|
265
|
+
* The function handles three cases:
|
|
266
|
+
* 1. Position entirely below active bin (bid side only) → only Y deposited
|
|
267
|
+
* 2. Position entirely above active bin (ask side only) → only X deposited
|
|
268
|
+
* 3. Position spans active bin → both X and Y deposited
|
|
269
|
+
*
|
|
270
|
+
* @param amountX - Total X amount to deposit
|
|
271
|
+
* @param amountY - Total Y amount to deposit
|
|
272
|
+
* @param minDeltaId - Minimum delta ID (lowerBinId - activeId)
|
|
273
|
+
* @param maxDeltaId - Maximum delta ID (upperBinId - activeId)
|
|
274
|
+
* @param binStep - The bin step in basis points
|
|
275
|
+
* @param activeId - The active bin ID
|
|
276
|
+
* @param favorXInActiveId - Whether X is favored in the active bin (affects bid/ask boundary)
|
|
277
|
+
* @returns Strategy parameters { x0, y0, deltaX, deltaY }
|
|
278
|
+
*/
|
|
279
|
+
function buildSpotStrategyParameters(amountX, amountY, minDeltaId, maxDeltaId, binStep, activeId, favorXInActiveId = false) {
|
|
280
|
+
// Invalid range
|
|
281
|
+
if (minDeltaId.gt(maxDeltaId)) {
|
|
282
|
+
return {
|
|
283
|
+
x0: new bn_js_1.default(0),
|
|
284
|
+
y0: new bn_js_1.default(0),
|
|
285
|
+
deltaX: new bn_js_1.default(0),
|
|
286
|
+
deltaY: new bn_js_1.default(0),
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
// Determine bid/ask boundary based on favorXInActiveId
|
|
290
|
+
// - If favorXInActiveId = true: active bin is ask side (X), bid side ends at deltaId = -1
|
|
291
|
+
// - If favorXInActiveId = false: active bin is bid side (Y), ask side starts at deltaId = 1
|
|
292
|
+
const bidSideEndDeltaId = favorXInActiveId ? new bn_js_1.default(-1) : new bn_js_1.default(0);
|
|
293
|
+
const askSideStartDeltaId = favorXInActiveId ? new bn_js_1.default(0) : new bn_js_1.default(1);
|
|
294
|
+
// Case 1: Position entirely on bid side (below active bin) - Y only
|
|
295
|
+
const depositOnlyY = maxDeltaId.lte(bidSideEndDeltaId);
|
|
296
|
+
// Case 2: Position entirely on ask side (above active bin) - X only
|
|
297
|
+
const depositOnlyX = minDeltaId.gte(askSideStartDeltaId);
|
|
298
|
+
if (depositOnlyY) {
|
|
299
|
+
// Only Y token - all bins are on bid side
|
|
300
|
+
const y0 = findY0ForSpot(amountY, minDeltaId, maxDeltaId);
|
|
301
|
+
return {
|
|
302
|
+
x0: new bn_js_1.default(0),
|
|
303
|
+
y0,
|
|
304
|
+
deltaX: new bn_js_1.default(0),
|
|
305
|
+
deltaY: new bn_js_1.default(0),
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
if (depositOnlyX) {
|
|
309
|
+
// Only X token - all bins are on ask side
|
|
310
|
+
const x0 = findX0ForSpot(amountX, minDeltaId, maxDeltaId, binStep, activeId);
|
|
311
|
+
return {
|
|
312
|
+
x0,
|
|
313
|
+
y0: new bn_js_1.default(0),
|
|
314
|
+
deltaX: new bn_js_1.default(0),
|
|
315
|
+
deltaY: new bn_js_1.default(0),
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
// Case 3: Position spans active bin - both X and Y
|
|
319
|
+
// Y goes to bid side bins (minDeltaId to bidSideEndDeltaId)
|
|
320
|
+
// X goes to ask side bins (askSideStartDeltaId to maxDeltaId)
|
|
321
|
+
const y0 = findY0ForSpot(amountY, minDeltaId, bidSideEndDeltaId);
|
|
322
|
+
const x0 = findX0ForSpot(amountX, askSideStartDeltaId, maxDeltaId, binStep, activeId);
|
|
323
|
+
return {
|
|
324
|
+
x0,
|
|
325
|
+
y0,
|
|
326
|
+
deltaX: new bn_js_1.default(0),
|
|
327
|
+
deltaY: new bn_js_1.default(0),
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Resets strategy parameters for a chunk that may not span the full position range.
|
|
332
|
+
*
|
|
333
|
+
* When splitting a position into chunks, each chunk may only cover part of the range:
|
|
334
|
+
* - If chunk is entirely on bid side → zero out X parameters
|
|
335
|
+
* - If chunk is entirely on ask side → zero out Y parameters
|
|
336
|
+
* - If chunk spans active bin → keep both X and Y parameters
|
|
337
|
+
*
|
|
338
|
+
* @param minDeltaId - Chunk's minimum delta ID
|
|
339
|
+
* @param maxDeltaId - Chunk's maximum delta ID
|
|
340
|
+
* @param favorXInActiveId - Whether X is favored in the active bin
|
|
341
|
+
* @param params - Original strategy parameters from buildSpotStrategyParameters
|
|
342
|
+
* @returns Adjusted parameters for the chunk
|
|
343
|
+
*/
|
|
344
|
+
function resetUninvolvedLiquidityParams(minDeltaId, maxDeltaId, favorXInActiveId, params) {
|
|
345
|
+
const bidSideEndDeltaId = favorXInActiveId ? new bn_js_1.default(-1) : new bn_js_1.default(0);
|
|
346
|
+
const askSideStartDeltaId = bidSideEndDeltaId.addn(1);
|
|
347
|
+
let { x0, y0, deltaX, deltaY } = params;
|
|
348
|
+
// If chunk is entirely on bid side (maxDeltaId <= bidSideEndDeltaId) → zero out X
|
|
349
|
+
if (maxDeltaId.lte(bidSideEndDeltaId)) {
|
|
350
|
+
x0 = new bn_js_1.default(0);
|
|
351
|
+
deltaX = new bn_js_1.default(0);
|
|
352
|
+
}
|
|
353
|
+
// If chunk is entirely on ask side (minDeltaId >= askSideStartDeltaId) → zero out Y
|
|
354
|
+
if (minDeltaId.gte(askSideStartDeltaId)) {
|
|
355
|
+
y0 = new bn_js_1.default(0);
|
|
356
|
+
deltaY = new bn_js_1.default(0);
|
|
357
|
+
}
|
|
358
|
+
return { x0, y0, deltaX, deltaY };
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Calculates the actual X amount that will be deposited into a bin on the ask side.
|
|
362
|
+
*
|
|
363
|
+
* For SPOT strategy: amountX = x0 * price_weight
|
|
364
|
+
* where price_weight = (1 + binStep/10000)^(-(activeId + deltaId))
|
|
365
|
+
*
|
|
366
|
+
* @param x0 - Base X amount from strategy parameters
|
|
367
|
+
* @param deltaId - Delta ID relative to active bin
|
|
368
|
+
* @param binStep - The bin step in basis points
|
|
369
|
+
* @param activeId - The active bin ID
|
|
370
|
+
* @returns Amount of X for this bin
|
|
371
|
+
*/
|
|
372
|
+
function getAmountXForBin(x0, deltaId, binStep, activeId) {
|
|
373
|
+
if (x0.isZero()) {
|
|
374
|
+
return new bn_js_1.default(0);
|
|
375
|
+
}
|
|
376
|
+
const binId = activeId.add(deltaId);
|
|
377
|
+
const priceWeight = getQPriceFromId(binId.neg(), binStep);
|
|
378
|
+
// amountX = x0 * priceWeight >> 64
|
|
379
|
+
return x0.mul(priceWeight).shrn(SCALE_OFFSET);
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Calculates the actual Y amount that will be deposited into a bin on the bid side.
|
|
383
|
+
*
|
|
384
|
+
* For SPOT strategy: amountY = y0 (constant per bin)
|
|
385
|
+
*
|
|
386
|
+
* @param y0 - Base Y amount from strategy parameters
|
|
387
|
+
* @returns Amount of Y for this bin
|
|
388
|
+
*/
|
|
389
|
+
function getAmountYForBin(y0) {
|
|
390
|
+
return y0;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Calculates total X and Y amounts for a chunk based on strategy parameters.
|
|
394
|
+
*
|
|
395
|
+
* This iterates through each bin in the chunk and sums up the amounts.
|
|
396
|
+
*
|
|
397
|
+
* @param activeId - The active bin ID
|
|
398
|
+
* @param minDeltaId - Chunk's minimum delta ID
|
|
399
|
+
* @param maxDeltaId - Chunk's maximum delta ID
|
|
400
|
+
* @param params - Strategy parameters (after resetUninvolvedLiquidityParams)
|
|
401
|
+
* @param binStep - The bin step in basis points
|
|
402
|
+
* @param favorXInActiveId - Whether X is favored in the active bin
|
|
403
|
+
* @returns Total X and Y amounts for the chunk
|
|
404
|
+
*/
|
|
405
|
+
function calculateChunkAmounts(activeId, minDeltaId, maxDeltaId, params, binStep, favorXInActiveId) {
|
|
406
|
+
const { x0, y0 } = params;
|
|
407
|
+
const bidSideEndDeltaId = favorXInActiveId ? -1 : 0;
|
|
408
|
+
const askSideStartDeltaId = bidSideEndDeltaId + 1;
|
|
409
|
+
let totalXAmount = new bn_js_1.default(0);
|
|
410
|
+
let totalYAmount = new bn_js_1.default(0);
|
|
411
|
+
for (let deltaId = minDeltaId.toNumber(); deltaId <= maxDeltaId.toNumber(); deltaId++) {
|
|
412
|
+
if (deltaId <= bidSideEndDeltaId) {
|
|
413
|
+
// Bid side - Y token
|
|
414
|
+
totalYAmount = totalYAmount.add(y0);
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
// Ask side - X token
|
|
418
|
+
const amountX = getAmountXForBin(x0, new bn_js_1.default(deltaId), binStep, activeId);
|
|
419
|
+
totalXAmount = totalXAmount.add(amountX);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return { totalXAmount, totalYAmount };
|
|
423
|
+
}
|
|
424
|
+
// =============================================================================
|
|
425
|
+
// CURVE STRATEGY
|
|
426
|
+
// =============================================================================
|
|
427
|
+
/**
|
|
428
|
+
* Finds base y0 for CURVE strategy.
|
|
429
|
+
*
|
|
430
|
+
* CURVE strategy has liquidity concentrated near active bin, decreasing away from it.
|
|
431
|
+
* Formula: amountY = y0 + deltaY * m where m = distance from active bin
|
|
432
|
+
* Setting deltaY = -y0 / (m1 + 1) gives decreasing amounts.
|
|
433
|
+
*
|
|
434
|
+
* @param amountY - Total Y amount to distribute
|
|
435
|
+
* @param minDeltaId - Minimum delta ID (negative for bid side)
|
|
436
|
+
* @param maxDeltaId - Maximum delta ID (negative for bid side)
|
|
437
|
+
* @returns Base y0 value
|
|
438
|
+
*/
|
|
439
|
+
function findBaseY0ForCurve(amountY, minDeltaId, maxDeltaId) {
|
|
440
|
+
if (minDeltaId.gt(maxDeltaId) || amountY.lte(new bn_js_1.default(0))) {
|
|
441
|
+
return new bn_js_1.default(0);
|
|
442
|
+
}
|
|
443
|
+
if (minDeltaId.eq(maxDeltaId)) {
|
|
444
|
+
return amountY;
|
|
445
|
+
}
|
|
446
|
+
// m1 = -minDeltaId, m2 = -maxDeltaId (distances from active bin)
|
|
447
|
+
const m1 = minDeltaId.neg();
|
|
448
|
+
const m2 = maxDeltaId.neg();
|
|
449
|
+
// sum(amounts) = y0 * (m1-m2+1) + deltaY * (m1*(m1+1)/2 - m2*(m2-1)/2)
|
|
450
|
+
// set deltaY = -y0 / (m1 + 1)
|
|
451
|
+
// A = (m1-m2+1) - (m1*(m1+1)/2 - m2*(m2-1)/2) / (m1+1)
|
|
452
|
+
// y0 = amountY / A
|
|
453
|
+
const b = m1.sub(m2).addn(1);
|
|
454
|
+
const c = m1.mul(m1.addn(1)).divn(2);
|
|
455
|
+
const d = m2.mul(m2.subn(1)).divn(2);
|
|
456
|
+
const a = b.sub(c.sub(d).div(m1.addn(1)));
|
|
457
|
+
if (a.isZero()) {
|
|
458
|
+
return amountY;
|
|
459
|
+
}
|
|
460
|
+
return amountY.div(a);
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Finds y0 and deltaY for CURVE strategy with iterative refinement.
|
|
464
|
+
*
|
|
465
|
+
* @param amountY - Total Y amount to distribute
|
|
466
|
+
* @param minDeltaId - Minimum delta ID
|
|
467
|
+
* @param maxDeltaId - Maximum delta ID
|
|
468
|
+
* @param activeId - Active bin ID
|
|
469
|
+
* @returns { base: y0, delta: deltaY }
|
|
470
|
+
*/
|
|
471
|
+
function findY0AndDeltaYForCurve(amountY, minDeltaId, maxDeltaId, activeId) {
|
|
472
|
+
if (minDeltaId.gt(maxDeltaId) || amountY.isZero()) {
|
|
473
|
+
return { base: new bn_js_1.default(0), delta: new bn_js_1.default(0) };
|
|
474
|
+
}
|
|
475
|
+
let baseY0 = findBaseY0ForCurve(amountY, minDeltaId, maxDeltaId);
|
|
476
|
+
const m1 = minDeltaId.neg();
|
|
477
|
+
// deltaY = -y0 / (m1 + 1)
|
|
478
|
+
const deltaY = baseY0.neg().div(m1.addn(1));
|
|
479
|
+
// Iterative refinement to ensure we don't exceed amountY
|
|
480
|
+
while (true) {
|
|
481
|
+
const totalAmountY = calculateBidSideAmount(activeId, minDeltaId, maxDeltaId, deltaY, baseY0);
|
|
482
|
+
if (totalAmountY.gt(amountY)) {
|
|
483
|
+
baseY0 = baseY0.subn(1);
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
return { base: baseY0, delta: deltaY };
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Finds x0 and deltaX for CURVE strategy that produces linear decrease from active bin.
|
|
492
|
+
*
|
|
493
|
+
* For CURVE, we want the ask side to linearly decrease away from the active bin,
|
|
494
|
+
* mirroring the bid side's behavior.
|
|
495
|
+
*
|
|
496
|
+
* The on-chain formula for ask side is:
|
|
497
|
+
* amountX = (x0 + deltaX * delta) * priceWeight
|
|
498
|
+
*
|
|
499
|
+
* For linear decrease in baseAmount (x0 + deltaX * delta):
|
|
500
|
+
* - At minDelta: baseAmount = x0 + deltaX * minDelta (highest)
|
|
501
|
+
* - At maxDelta: baseAmount approaches 0
|
|
502
|
+
*
|
|
503
|
+
* Using deltaX = -x0 / (maxDelta + 1) gives:
|
|
504
|
+
* - At delta=0: baseAmount = x0 (would be highest, but minDelta >= 1)
|
|
505
|
+
* - At delta=maxDelta+1: baseAmount = 0
|
|
506
|
+
*
|
|
507
|
+
* The final amountX will be approximately linear because the decreasing baseAmount
|
|
508
|
+
* partially compensates for the exponentially decaying priceWeight.
|
|
509
|
+
*
|
|
510
|
+
* @param amountX - Total X amount to distribute
|
|
511
|
+
* @param minDeltaId - Minimum delta ID (should be >= 1 for ask side)
|
|
512
|
+
* @param maxDeltaId - Maximum delta ID
|
|
513
|
+
* @param binStep - Bin step in basis points
|
|
514
|
+
* @param activeId - Active bin ID
|
|
515
|
+
* @returns { x0, deltaX } parameters
|
|
516
|
+
*/
|
|
517
|
+
function findX0AndDeltaXForCurveMirrored(amountX, minDeltaId, maxDeltaId, binStep, activeId) {
|
|
518
|
+
if (minDeltaId.gt(maxDeltaId) || amountX.lte(new bn_js_1.default(0))) {
|
|
519
|
+
return { base: new bn_js_1.default(0), delta: new bn_js_1.default(0) };
|
|
520
|
+
}
|
|
521
|
+
const numBins = maxDeltaId.sub(minDeltaId).addn(1).toNumber();
|
|
522
|
+
if (numBins === 1) {
|
|
523
|
+
// Single bin: just use x0 directly
|
|
524
|
+
const priceWeight = getQPriceFromId(activeId.add(minDeltaId).neg(), binStep);
|
|
525
|
+
const x0 = amountX.shln(SCALE_OFFSET).div(priceWeight);
|
|
526
|
+
return { base: x0, delta: new bn_js_1.default(0) };
|
|
527
|
+
}
|
|
528
|
+
// Meteora's approach:
|
|
529
|
+
// deltaX = -x0 / maxDelta
|
|
530
|
+
// x0 = amountX * 2^64 / (B - C)
|
|
531
|
+
// where:
|
|
532
|
+
// B = sum of priceWeights: p(m1) + p(m1+1) + ... + p(m2)
|
|
533
|
+
// C = weighted sum / m2: (m1*p(m1) + ... + m2*p(m2)) / m2
|
|
534
|
+
const m1 = minDeltaId.toNumber();
|
|
535
|
+
const m2 = maxDeltaId.toNumber();
|
|
536
|
+
let b = new bn_js_1.default(0);
|
|
537
|
+
let c = new bn_js_1.default(0);
|
|
538
|
+
for (let m = m1; m <= m2; m++) {
|
|
539
|
+
const binId = activeId.addn(m);
|
|
540
|
+
const pm = getQPriceFromId(binId.neg(), binStep);
|
|
541
|
+
b = b.add(pm);
|
|
542
|
+
const cDelta = new bn_js_1.default(m).mul(pm).div(maxDeltaId);
|
|
543
|
+
c = c.add(cDelta);
|
|
544
|
+
}
|
|
545
|
+
const denominator = b.sub(c);
|
|
546
|
+
if (denominator.isZero() || denominator.isNeg()) {
|
|
547
|
+
return { base: new bn_js_1.default(0), delta: new bn_js_1.default(0) };
|
|
548
|
+
}
|
|
549
|
+
let baseX0 = amountX.shln(SCALE_OFFSET).div(denominator);
|
|
550
|
+
const deltaX = baseX0.neg().div(maxDeltaId);
|
|
551
|
+
// Iterative refinement to ensure we don't exceed amountX
|
|
552
|
+
while (true) {
|
|
553
|
+
const totalAmountX = calculateAskSideAmount(activeId, binStep, minDeltaId, maxDeltaId, deltaX, baseX0);
|
|
554
|
+
if (totalAmountX.gt(amountX)) {
|
|
555
|
+
baseX0 = baseX0.subn(1);
|
|
556
|
+
}
|
|
557
|
+
else {
|
|
558
|
+
return { base: baseX0, delta: deltaX };
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Finds x0 and deltaX for CURVE strategy.
|
|
564
|
+
*
|
|
565
|
+
* This is a wrapper that delegates to findX0AndDeltaXForCurveMirrored which
|
|
566
|
+
* produces a shape that mirrors the bid side's linear decrease.
|
|
567
|
+
*
|
|
568
|
+
* @param amountX - Total X amount to distribute
|
|
569
|
+
* @param minDeltaId - Minimum delta ID
|
|
570
|
+
* @param maxDeltaId - Maximum delta ID
|
|
571
|
+
* @param binStep - Bin step in basis points
|
|
572
|
+
* @param activeId - Active bin ID
|
|
573
|
+
* @returns { base: x0, delta: deltaX }
|
|
574
|
+
*/
|
|
575
|
+
function findX0AndDeltaXForCurve(amountX, minDeltaId, maxDeltaId, binStep, activeId) {
|
|
576
|
+
return findX0AndDeltaXForCurveMirrored(amountX, minDeltaId, maxDeltaId, binStep, activeId);
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Builds liquidity strategy parameters for CURVE distribution.
|
|
580
|
+
*
|
|
581
|
+
* CURVE strategy concentrates liquidity near the active bin with amounts
|
|
582
|
+
* decreasing as you move away. This is achieved with negative deltaX/deltaY.
|
|
583
|
+
*
|
|
584
|
+
* @param amountX - Total X amount to deposit
|
|
585
|
+
* @param amountY - Total Y amount to deposit
|
|
586
|
+
* @param minDeltaId - Minimum delta ID (lowerBinId - activeId)
|
|
587
|
+
* @param maxDeltaId - Maximum delta ID (upperBinId - activeId)
|
|
588
|
+
* @param binStep - The bin step in basis points
|
|
589
|
+
* @param activeId - The active bin ID
|
|
590
|
+
* @param favorXInActiveId - Whether X is favored in the active bin
|
|
591
|
+
* @returns Strategy parameters { x0, y0, deltaX, deltaY }
|
|
592
|
+
*/
|
|
593
|
+
function buildCurveStrategyParameters(amountX, amountY, minDeltaId, maxDeltaId, binStep, activeId, favorXInActiveId = false) {
|
|
594
|
+
if (minDeltaId.gt(maxDeltaId)) {
|
|
595
|
+
return { x0: new bn_js_1.default(0), y0: new bn_js_1.default(0), deltaX: new bn_js_1.default(0), deltaY: new bn_js_1.default(0) };
|
|
596
|
+
}
|
|
597
|
+
const bidSideEndDeltaId = favorXInActiveId ? new bn_js_1.default(-1) : new bn_js_1.default(0);
|
|
598
|
+
const askSideStartDeltaId = favorXInActiveId ? new bn_js_1.default(0) : new bn_js_1.default(1);
|
|
599
|
+
const depositOnlyY = maxDeltaId.lte(bidSideEndDeltaId);
|
|
600
|
+
const depositOnlyX = minDeltaId.gte(askSideStartDeltaId);
|
|
601
|
+
if (depositOnlyY) {
|
|
602
|
+
const { base: y0, delta: deltaY } = findY0AndDeltaYForCurve(amountY, minDeltaId, maxDeltaId, activeId);
|
|
603
|
+
return { x0: new bn_js_1.default(0), y0, deltaX: new bn_js_1.default(0), deltaY };
|
|
604
|
+
}
|
|
605
|
+
if (depositOnlyX) {
|
|
606
|
+
const { base: x0, delta: deltaX } = findX0AndDeltaXForCurve(amountX, minDeltaId, maxDeltaId, binStep, activeId);
|
|
607
|
+
return { x0, y0: new bn_js_1.default(0), deltaX, deltaY: new bn_js_1.default(0) };
|
|
608
|
+
}
|
|
609
|
+
// Both sides - calculate independently
|
|
610
|
+
const { base: y0, delta: deltaY } = findY0AndDeltaYForCurve(amountY, minDeltaId, bidSideEndDeltaId, activeId);
|
|
611
|
+
const { base: x0, delta: deltaX } = findX0AndDeltaXForCurve(amountX, askSideStartDeltaId, maxDeltaId, binStep, activeId);
|
|
612
|
+
return { x0, y0, deltaX, deltaY };
|
|
613
|
+
}
|
|
614
|
+
// =============================================================================
|
|
615
|
+
// BID_ASK STRATEGY
|
|
616
|
+
// =============================================================================
|
|
617
|
+
/**
|
|
618
|
+
* Finds base deltaY for BID_ASK strategy.
|
|
619
|
+
*
|
|
620
|
+
* BID_ASK strategy has liquidity increasing away from active bin.
|
|
621
|
+
* Formula: amountY = y0 + deltaY * m
|
|
622
|
+
* Setting y0 = -deltaY * (m2 - 1) gives increasing amounts.
|
|
623
|
+
*
|
|
624
|
+
* @param amountY - Total Y amount to distribute
|
|
625
|
+
* @param minDeltaId - Minimum delta ID
|
|
626
|
+
* @param maxDeltaId - Maximum delta ID
|
|
627
|
+
* @returns Base deltaY value
|
|
628
|
+
*/
|
|
629
|
+
function findBaseDeltaYForBidAsk(amountY, minDeltaId, maxDeltaId) {
|
|
630
|
+
if (minDeltaId.gt(maxDeltaId) || amountY.lte(new bn_js_1.default(0))) {
|
|
631
|
+
return new bn_js_1.default(0);
|
|
632
|
+
}
|
|
633
|
+
if (minDeltaId.eq(maxDeltaId)) {
|
|
634
|
+
return amountY;
|
|
635
|
+
}
|
|
636
|
+
const m1 = minDeltaId.neg();
|
|
637
|
+
const m2 = maxDeltaId.neg();
|
|
638
|
+
// sum(amounts) = y0 * (m1-m2+1) + deltaY * (m1*(m1+1)/2 - m2*(m2-1)/2)
|
|
639
|
+
// set y0 = -deltaY * (m2 - 1)
|
|
640
|
+
// A = (-m2+1) * (m1-m2+1) + (m1*(m1+1)/2 - m2*(m2-1)/2)
|
|
641
|
+
// deltaY = amountY / A
|
|
642
|
+
const b = m2.neg().addn(1).mul(m1.sub(m2).addn(1));
|
|
643
|
+
const c = m1.mul(m1.addn(1)).divn(2);
|
|
644
|
+
const d = m2.mul(m2.subn(1)).divn(2);
|
|
645
|
+
const a = b.add(c.sub(d));
|
|
646
|
+
if (a.isZero()) {
|
|
647
|
+
return amountY;
|
|
648
|
+
}
|
|
649
|
+
return amountY.div(a);
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Finds y0 and deltaY for BID_ASK strategy with iterative refinement.
|
|
653
|
+
*
|
|
654
|
+
* @param amountY - Total Y amount to distribute
|
|
655
|
+
* @param minDeltaId - Minimum delta ID
|
|
656
|
+
* @param maxDeltaId - Maximum delta ID
|
|
657
|
+
* @param activeId - Active bin ID
|
|
658
|
+
* @returns { base: y0, delta: deltaY }
|
|
659
|
+
*/
|
|
660
|
+
function findY0AndDeltaYForBidAsk(amountY, minDeltaId, maxDeltaId, activeId) {
|
|
661
|
+
if (minDeltaId.gt(maxDeltaId) || amountY.isZero()) {
|
|
662
|
+
return { base: new bn_js_1.default(0), delta: new bn_js_1.default(0) };
|
|
663
|
+
}
|
|
664
|
+
const baseDeltaY = findBaseDeltaYForBidAsk(amountY, minDeltaId, maxDeltaId);
|
|
665
|
+
const m2 = maxDeltaId.neg();
|
|
666
|
+
// y0 is calculated once from initial baseDeltaY and kept constant (matches Meteora)
|
|
667
|
+
const y0 = baseDeltaY.neg().mul(m2.subn(1));
|
|
668
|
+
// Binary search for the correct deltaY that doesn't exceed amountY
|
|
669
|
+
let low = new bn_js_1.default(0);
|
|
670
|
+
let high = baseDeltaY.clone();
|
|
671
|
+
let result = new bn_js_1.default(0);
|
|
672
|
+
while (low.lte(high)) {
|
|
673
|
+
const mid = low.add(high).shrn(1);
|
|
674
|
+
const totalAmountY = calculateBidSideAmount(activeId, minDeltaId, maxDeltaId, mid, y0);
|
|
675
|
+
if (totalAmountY.lte(amountY)) {
|
|
676
|
+
result = mid;
|
|
677
|
+
low = mid.addn(1);
|
|
678
|
+
}
|
|
679
|
+
else {
|
|
680
|
+
high = mid.subn(1);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
return { base: y0, delta: result };
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Calculates the critical delta beyond which BID_ASK amounts will start decreasing.
|
|
687
|
+
*
|
|
688
|
+
* For BID_ASK, amountX = (x0 + deltaX * delta) * priceWeight
|
|
689
|
+
* where priceWeight decays exponentially as delta increases.
|
|
690
|
+
*
|
|
691
|
+
* The critical point is approximately 1 / (binStep / 10000) = 10000 / binStep.
|
|
692
|
+
* Beyond this point, exponential decay overwhelms linear growth.
|
|
693
|
+
*
|
|
694
|
+
* @param binStep - Bin step in basis points
|
|
695
|
+
* @returns Critical delta value
|
|
696
|
+
*/
|
|
697
|
+
function getCriticalDeltaForBidAsk(binStep) {
|
|
698
|
+
// Critical delta ≈ 1 / (binStep / 10000) = 10000 / binStep
|
|
699
|
+
// Using floor to be conservative
|
|
700
|
+
return Math.floor(10000 / binStep.toNumber());
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Finds base deltaX for BID_ASK strategy.
|
|
704
|
+
*
|
|
705
|
+
* Formula: amountX = (x0 + deltaX * m) * price_weight
|
|
706
|
+
* Setting x0 = -m1 * deltaX + deltaX gives increasing amounts.
|
|
707
|
+
*
|
|
708
|
+
* IMPORTANT: This formula only produces monotonically increasing amounts
|
|
709
|
+
* when the range is within the critical delta (approximately 10000/binStep bins).
|
|
710
|
+
* For wider ranges, consider using multiple smaller positions.
|
|
711
|
+
*
|
|
712
|
+
* @param amountX - Total X amount to distribute
|
|
713
|
+
* @param minDeltaId - Minimum delta ID
|
|
714
|
+
* @param maxDeltaId - Maximum delta ID
|
|
715
|
+
* @param binStep - Bin step in basis points
|
|
716
|
+
* @param activeId - Active bin ID
|
|
717
|
+
* @returns Base deltaX value
|
|
718
|
+
*/
|
|
719
|
+
function findBaseDeltaXForBidAsk(amountX, minDeltaId, maxDeltaId, binStep, activeId) {
|
|
720
|
+
if (minDeltaId.gt(maxDeltaId) || amountX.lte(new bn_js_1.default(0))) {
|
|
721
|
+
return new bn_js_1.default(0);
|
|
722
|
+
}
|
|
723
|
+
// sum(amounts) = x0 * B + deltaX * C
|
|
724
|
+
// where B = sum(price_weights), C = sum(m * price_weight)
|
|
725
|
+
// setting x0 = -m1 * deltaX + deltaX = deltaX * (1 - m1)
|
|
726
|
+
// sum = deltaX * (1 - m1) * B + deltaX * C = deltaX * ((1-m1)*B + C)
|
|
727
|
+
// deltaX = amountX / ((1-m1)*B + C)
|
|
728
|
+
let b = new bn_js_1.default(0);
|
|
729
|
+
let c = new bn_js_1.default(0);
|
|
730
|
+
const m1 = minDeltaId;
|
|
731
|
+
const m2 = maxDeltaId.addn(1); // +1 to ensure no zero amount at active bin
|
|
732
|
+
for (let m = m1.toNumber(); m <= m2.toNumber(); m++) {
|
|
733
|
+
const binId = activeId.addn(m);
|
|
734
|
+
const pm = getQPriceFromId(binId.neg(), binStep);
|
|
735
|
+
const bDelta = m1.mul(pm);
|
|
736
|
+
b = b.add(bDelta);
|
|
737
|
+
const cDelta = new bn_js_1.default(m).mul(pm);
|
|
738
|
+
c = c.add(cDelta);
|
|
739
|
+
}
|
|
740
|
+
const denominator = c.sub(b);
|
|
741
|
+
if (denominator.isZero()) {
|
|
742
|
+
return new bn_js_1.default(0);
|
|
743
|
+
}
|
|
744
|
+
return amountX.shln(SCALE_OFFSET).div(denominator);
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Finds x0 and deltaX for BID_ASK strategy with iterative refinement.
|
|
748
|
+
*
|
|
749
|
+
* @param amountX - Total X amount to distribute
|
|
750
|
+
* @param minDeltaId - Minimum delta ID
|
|
751
|
+
* @param maxDeltaId - Maximum delta ID
|
|
752
|
+
* @param binStep - Bin step in basis points
|
|
753
|
+
* @param activeId - Active bin ID
|
|
754
|
+
* @returns { base: x0, delta: deltaX }
|
|
755
|
+
*/
|
|
756
|
+
function findX0AndDeltaXForBidAsk(amountX, minDeltaId, maxDeltaId, binStep, activeId) {
|
|
757
|
+
if (minDeltaId.gt(maxDeltaId) || amountX.lte(new bn_js_1.default(0)) || amountX.isZero()) {
|
|
758
|
+
return { base: new bn_js_1.default(0), delta: new bn_js_1.default(0) };
|
|
759
|
+
}
|
|
760
|
+
const baseDeltaX = findBaseDeltaXForBidAsk(amountX, minDeltaId, maxDeltaId, binStep, activeId);
|
|
761
|
+
// x0 is calculated once from initial baseDeltaX and kept constant (matches Meteora)
|
|
762
|
+
const x0 = minDeltaId.neg().mul(baseDeltaX).add(baseDeltaX);
|
|
763
|
+
// Binary search for the correct deltaX that doesn't exceed amountX
|
|
764
|
+
let low = new bn_js_1.default(0);
|
|
765
|
+
let high = baseDeltaX.clone();
|
|
766
|
+
let result = new bn_js_1.default(0);
|
|
767
|
+
while (low.lte(high)) {
|
|
768
|
+
const mid = low.add(high).shrn(1);
|
|
769
|
+
const totalAmountX = calculateAskSideAmount(activeId, binStep, minDeltaId, maxDeltaId, mid, x0);
|
|
770
|
+
if (totalAmountX.lte(amountX)) {
|
|
771
|
+
result = mid;
|
|
772
|
+
low = mid.addn(1);
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
high = mid.subn(1);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
return { base: x0, delta: result };
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Builds liquidity strategy parameters for BID_ASK distribution.
|
|
782
|
+
*
|
|
783
|
+
* BID_ASK strategy has liquidity increasing away from the active bin.
|
|
784
|
+
* This is achieved with positive deltaX/deltaY values.
|
|
785
|
+
*
|
|
786
|
+
* @param amountX - Total X amount to deposit
|
|
787
|
+
* @param amountY - Total Y amount to deposit
|
|
788
|
+
* @param minDeltaId - Minimum delta ID (lowerBinId - activeId)
|
|
789
|
+
* @param maxDeltaId - Maximum delta ID (upperBinId - activeId)
|
|
790
|
+
* @param binStep - The bin step in basis points
|
|
791
|
+
* @param activeId - The active bin ID
|
|
792
|
+
* @param favorXInActiveId - Whether X is favored in the active bin
|
|
793
|
+
* @returns Strategy parameters { x0, y0, deltaX, deltaY }
|
|
794
|
+
*/
|
|
795
|
+
function buildBidAskStrategyParameters(amountX, amountY, minDeltaId, maxDeltaId, binStep, activeId, favorXInActiveId = false) {
|
|
796
|
+
if (minDeltaId.gt(maxDeltaId)) {
|
|
797
|
+
return { x0: new bn_js_1.default(0), y0: new bn_js_1.default(0), deltaX: new bn_js_1.default(0), deltaY: new bn_js_1.default(0) };
|
|
798
|
+
}
|
|
799
|
+
const bidSideEndDeltaId = favorXInActiveId ? new bn_js_1.default(-1) : new bn_js_1.default(0);
|
|
800
|
+
const askSideStartDeltaId = favorXInActiveId ? new bn_js_1.default(0) : new bn_js_1.default(1);
|
|
801
|
+
const depositOnlyY = maxDeltaId.lte(bidSideEndDeltaId);
|
|
802
|
+
const depositOnlyX = minDeltaId.gte(askSideStartDeltaId);
|
|
803
|
+
if (depositOnlyY) {
|
|
804
|
+
const { base: y0, delta: deltaY } = findY0AndDeltaYForBidAsk(amountY, minDeltaId, maxDeltaId, activeId);
|
|
805
|
+
return { x0: new bn_js_1.default(0), y0, deltaX: new bn_js_1.default(0), deltaY };
|
|
806
|
+
}
|
|
807
|
+
if (depositOnlyX) {
|
|
808
|
+
const { base: x0, delta: deltaX } = findX0AndDeltaXForBidAsk(amountX, minDeltaId, maxDeltaId, binStep, activeId);
|
|
809
|
+
return { x0, y0: new bn_js_1.default(0), deltaX, deltaY: new bn_js_1.default(0) };
|
|
810
|
+
}
|
|
811
|
+
// Both sides
|
|
812
|
+
const { base: y0, delta: deltaY } = findY0AndDeltaYForBidAsk(amountY, minDeltaId, bidSideEndDeltaId, activeId);
|
|
813
|
+
const { base: x0, delta: deltaX } = findX0AndDeltaXForBidAsk(amountX, askSideStartDeltaId, maxDeltaId, binStep, activeId);
|
|
814
|
+
return { x0, y0, deltaX, deltaY };
|
|
815
|
+
}
|
|
816
|
+
// =============================================================================
|
|
817
|
+
// HELPER FUNCTIONS FOR AMOUNT CALCULATION
|
|
818
|
+
// =============================================================================
|
|
819
|
+
/**
|
|
820
|
+
* Calculates total Y amount for bid side bins using given parameters.
|
|
821
|
+
*
|
|
822
|
+
* @param activeId - Active bin ID
|
|
823
|
+
* @param minDeltaId - Minimum delta ID
|
|
824
|
+
* @param maxDeltaId - Maximum delta ID
|
|
825
|
+
* @param deltaY - Delta Y per bin
|
|
826
|
+
* @param y0 - Base Y amount
|
|
827
|
+
* @returns Total Y amount
|
|
828
|
+
*/
|
|
829
|
+
function calculateBidSideAmount(activeId, minDeltaId, maxDeltaId, deltaY, y0) {
|
|
830
|
+
let totalAmount = new bn_js_1.default(0);
|
|
831
|
+
const minBinId = activeId.add(minDeltaId);
|
|
832
|
+
const maxBinId = activeId.add(maxDeltaId);
|
|
833
|
+
for (let binId = minBinId.toNumber(); binId <= maxBinId.toNumber(); binId++) {
|
|
834
|
+
const deltaBin = activeId.toNumber() - binId;
|
|
835
|
+
const totalDeltaY = deltaY.muln(deltaBin);
|
|
836
|
+
const amountY = y0.add(totalDeltaY);
|
|
837
|
+
if (amountY.gtn(0)) {
|
|
838
|
+
totalAmount = totalAmount.add(amountY);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
return totalAmount;
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* Calculates total X amount for ask side bins using given parameters.
|
|
845
|
+
*
|
|
846
|
+
* @param activeId - Active bin ID
|
|
847
|
+
* @param binStep - Bin step in basis points
|
|
848
|
+
* @param minDeltaId - Minimum delta ID
|
|
849
|
+
* @param maxDeltaId - Maximum delta ID
|
|
850
|
+
* @param deltaX - Delta X per bin
|
|
851
|
+
* @param x0 - Base X amount
|
|
852
|
+
* @returns Total X amount
|
|
853
|
+
*/
|
|
854
|
+
function calculateAskSideAmount(activeId, binStep, minDeltaId, maxDeltaId, deltaX, x0) {
|
|
855
|
+
let totalAmount = new bn_js_1.default(0);
|
|
856
|
+
const baseFactor = getQPriceBaseFactor(binStep);
|
|
857
|
+
const minBinId = activeId.add(minDeltaId);
|
|
858
|
+
const maxBinId = activeId.add(maxDeltaId);
|
|
859
|
+
// Start from maxBinId and iterate down
|
|
860
|
+
let inverseBasePrice = getQPriceFromId(maxBinId.neg(), binStep);
|
|
861
|
+
for (let binId = maxBinId.toNumber(); binId >= minBinId.toNumber(); binId--) {
|
|
862
|
+
const delta = binId - activeId.toNumber();
|
|
863
|
+
const totalDeltaX = deltaX.muln(delta);
|
|
864
|
+
const baseAmount = x0.add(totalDeltaX);
|
|
865
|
+
// Only calculate if baseAmount is positive (can't shift right on negative BN)
|
|
866
|
+
if (baseAmount.gtn(0)) {
|
|
867
|
+
const amountX = baseAmount.mul(inverseBasePrice).shrn(SCALE_OFFSET);
|
|
868
|
+
if (amountX.gtn(0)) {
|
|
869
|
+
totalAmount = totalAmount.add(amountX);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
inverseBasePrice = inverseBasePrice.mul(baseFactor).shrn(SCALE_OFFSET);
|
|
873
|
+
}
|
|
874
|
+
return totalAmount;
|
|
875
|
+
}
|
|
876
|
+
// =============================================================================
|
|
877
|
+
// UNIFIED STRATEGY BUILDER
|
|
878
|
+
// =============================================================================
|
|
879
|
+
/**
|
|
880
|
+
* Builds liquidity strategy parameters for the specified strategy type.
|
|
881
|
+
*
|
|
882
|
+
* This is the main entry point that dispatches to the appropriate strategy builder.
|
|
883
|
+
*
|
|
884
|
+
* @param strategyType - The type of strategy (SPOT, CURVE, or BID_ASK)
|
|
885
|
+
* @param amountX - Total X amount to deposit
|
|
886
|
+
* @param amountY - Total Y amount to deposit
|
|
887
|
+
* @param minDeltaId - Minimum delta ID (lowerBinId - activeId)
|
|
888
|
+
* @param maxDeltaId - Maximum delta ID (upperBinId - activeId)
|
|
889
|
+
* @param binStep - The bin step in basis points
|
|
890
|
+
* @param activeId - The active bin ID
|
|
891
|
+
* @param favorXInActiveId - Whether X is favored in the active bin
|
|
892
|
+
* @returns Strategy parameters { x0, y0, deltaX, deltaY }
|
|
893
|
+
*/
|
|
894
|
+
function buildStrategyParameters(strategyType, amountX, amountY, minDeltaId, maxDeltaId, binStep, activeId, favorXInActiveId = false) {
|
|
895
|
+
switch (strategyType) {
|
|
896
|
+
case StrategyType.SPOT:
|
|
897
|
+
return buildSpotStrategyParameters(amountX, amountY, minDeltaId, maxDeltaId, binStep, activeId, favorXInActiveId);
|
|
898
|
+
case StrategyType.CURVE:
|
|
899
|
+
return buildCurveStrategyParameters(amountX, amountY, minDeltaId, maxDeltaId, binStep, activeId, favorXInActiveId);
|
|
900
|
+
case StrategyType.BID_ASK:
|
|
901
|
+
return buildBidAskStrategyParameters(amountX, amountY, minDeltaId, maxDeltaId, binStep, activeId, favorXInActiveId);
|
|
902
|
+
default:
|
|
903
|
+
// Default to SPOT if unknown strategy type
|
|
904
|
+
return buildSpotStrategyParameters(amountX, amountY, minDeltaId, maxDeltaId, binStep, activeId, favorXInActiveId);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* Divides a bin range into chunks of specified size.
|
|
909
|
+
*
|
|
910
|
+
* This is similar to Meteora's `chunkBinRange` but with a configurable chunk size
|
|
911
|
+
* instead of a fixed 70-bin limit.
|
|
912
|
+
*
|
|
913
|
+
* @param minBinId - The starting bin ID of the range (absolute bin ID)
|
|
914
|
+
* @param maxBinId - The ending bin ID of the range (absolute bin ID)
|
|
915
|
+
* @param chunkSize - Maximum number of bins per chunk
|
|
916
|
+
* @returns Array of bin range chunks
|
|
917
|
+
*/
|
|
918
|
+
function chunkBinRange(minBinId, maxBinId, chunkSize) {
|
|
919
|
+
const chunkedBinRange = [];
|
|
920
|
+
let startBinId = minBinId;
|
|
921
|
+
while (startBinId <= maxBinId) {
|
|
922
|
+
const endBinId = Math.min(startBinId + chunkSize - 1, maxBinId);
|
|
923
|
+
chunkedBinRange.push({
|
|
924
|
+
lowerBinId: startBinId,
|
|
925
|
+
upperBinId: endBinId,
|
|
926
|
+
});
|
|
927
|
+
startBinId += chunkSize;
|
|
928
|
+
}
|
|
929
|
+
return chunkedBinRange;
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Converts strategy parameters to on-chain format with bit flag for negative values.
|
|
933
|
+
*
|
|
934
|
+
* The Solana program uses unsigned integers, so negative values are stored as
|
|
935
|
+
* positive with a bit flag indicating the sign.
|
|
936
|
+
*
|
|
937
|
+
* Bit flags:
|
|
938
|
+
* - Bit 0 (0b0001): x0 is negative
|
|
939
|
+
* - Bit 1 (0b0010): y0 is negative
|
|
940
|
+
* - Bit 2 (0b0100): deltaX is negative
|
|
941
|
+
* - Bit 3 (0b1000): deltaY is negative
|
|
942
|
+
*
|
|
943
|
+
* @param x0 - Base X amount (may be negative)
|
|
944
|
+
* @param y0 - Base Y amount (may be negative)
|
|
945
|
+
* @param deltaX - Delta X per bin (may be negative)
|
|
946
|
+
* @param deltaY - Delta Y per bin (may be negative)
|
|
947
|
+
* @returns Parameters with absolute values and bit flag
|
|
948
|
+
*/
|
|
949
|
+
function buildBitFlagAndNegateStrategyParameters(x0, y0, deltaX, deltaY) {
|
|
950
|
+
let bitFlag = 0;
|
|
951
|
+
if (x0.isNeg()) {
|
|
952
|
+
bitFlag |= 0b0001;
|
|
953
|
+
x0 = x0.neg();
|
|
954
|
+
}
|
|
955
|
+
if (y0.isNeg()) {
|
|
956
|
+
bitFlag |= 0b0010;
|
|
957
|
+
y0 = y0.neg();
|
|
958
|
+
}
|
|
959
|
+
if (deltaX.isNeg()) {
|
|
960
|
+
bitFlag |= 0b0100;
|
|
961
|
+
deltaX = deltaX.neg();
|
|
962
|
+
}
|
|
963
|
+
if (deltaY.isNeg()) {
|
|
964
|
+
bitFlag |= 0b1000;
|
|
965
|
+
deltaY = deltaY.neg();
|
|
966
|
+
}
|
|
967
|
+
return {
|
|
968
|
+
bitFlag,
|
|
969
|
+
x0,
|
|
970
|
+
y0,
|
|
971
|
+
deltaX,
|
|
972
|
+
deltaY,
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Calculates amounts for each bin in a range.
|
|
977
|
+
*
|
|
978
|
+
* This function iterates through all bins and calculates the X/Y amount
|
|
979
|
+
* for each bin based on the strategy parameters.
|
|
980
|
+
*
|
|
981
|
+
* @param activeId - The active bin ID
|
|
982
|
+
* @param minDeltaId - Minimum delta ID relative to active bin
|
|
983
|
+
* @param maxDeltaId - Maximum delta ID relative to active bin
|
|
984
|
+
* @param deltaX - Delta X per bin
|
|
985
|
+
* @param deltaY - Delta Y per bin
|
|
986
|
+
* @param x0 - Base X amount
|
|
987
|
+
* @param y0 - Base Y amount
|
|
988
|
+
* @param binStep - Bin step in basis points
|
|
989
|
+
* @param favorXInActiveBin - Whether X is favored in active bin
|
|
990
|
+
* @returns Array of { binId, amountX, amountY } for each bin
|
|
991
|
+
*/
|
|
992
|
+
function toAmountIntoBins(activeId, minDeltaId, maxDeltaId, deltaX, deltaY, x0, y0, binStep, favorXInActiveBin) {
|
|
993
|
+
const results = [];
|
|
994
|
+
const bidSideEndDeltaId = favorXInActiveBin ? -1 : 0;
|
|
995
|
+
for (let delta = minDeltaId.toNumber(); delta <= maxDeltaId.toNumber(); delta++) {
|
|
996
|
+
const binId = activeId.toNumber() + delta;
|
|
997
|
+
if (delta <= bidSideEndDeltaId) {
|
|
998
|
+
// Bid side - Y token
|
|
999
|
+
const distance = -delta; // Distance from active bin (positive)
|
|
1000
|
+
const amountY = y0.add(deltaY.muln(distance));
|
|
1001
|
+
results.push({
|
|
1002
|
+
binId,
|
|
1003
|
+
amountX: new bn_js_1.default(0),
|
|
1004
|
+
amountY: amountY.gtn(0) ? amountY : new bn_js_1.default(0),
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
else {
|
|
1008
|
+
// Ask side - X token
|
|
1009
|
+
const priceWeight = getQPriceFromId(new bn_js_1.default(binId).neg(), binStep);
|
|
1010
|
+
const baseAmount = x0.add(deltaX.muln(delta));
|
|
1011
|
+
const amountX = baseAmount.mul(priceWeight).shrn(SCALE_OFFSET);
|
|
1012
|
+
results.push({
|
|
1013
|
+
binId,
|
|
1014
|
+
amountX: amountX.gtn(0) ? amountX : new bn_js_1.default(0),
|
|
1015
|
+
amountY: new bn_js_1.default(0),
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
return results;
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Chunks a deposit into multiple transactions based on bin range.
|
|
1023
|
+
*
|
|
1024
|
+
* This function:
|
|
1025
|
+
* 1. Takes pre-built strategy parameters for the entire position
|
|
1026
|
+
* 2. Chunks the bin range into smaller ranges
|
|
1027
|
+
* 3. For each chunk, adjusts params and calculates amounts
|
|
1028
|
+
*
|
|
1029
|
+
* @param params - Strategy parameters from buildStrategyParameters (for entire position)
|
|
1030
|
+
* @param minDeltaId - Minimum delta ID (lowerBinId - activeId)
|
|
1031
|
+
* @param maxDeltaId - Maximum delta ID (upperBinId - activeId)
|
|
1032
|
+
* @param activeId - Current active bin ID
|
|
1033
|
+
* @param binStep - Bin step in basis points
|
|
1034
|
+
* @param chunkSize - Maximum bins per chunk
|
|
1035
|
+
* @param favorXInActiveBin - Whether X is favored in active bin
|
|
1036
|
+
* @returns Array of chunked deposit parameters
|
|
1037
|
+
*/
|
|
1038
|
+
function chunkDepositParameters(params, minDeltaId, maxDeltaId, activeId, binStep, chunkSize, favorXInActiveBin = false) {
|
|
1039
|
+
// Convert to absolute bin IDs for chunking
|
|
1040
|
+
const lowerBinId = activeId.add(minDeltaId).toNumber();
|
|
1041
|
+
const upperBinId = activeId.add(maxDeltaId).toNumber();
|
|
1042
|
+
// Chunk the bin range
|
|
1043
|
+
const chunks = chunkBinRange(lowerBinId, upperBinId, chunkSize);
|
|
1044
|
+
// For each chunk, calculate the deposit parameters
|
|
1045
|
+
const result = [];
|
|
1046
|
+
for (const chunk of chunks) {
|
|
1047
|
+
const chunkMinDeltaId = new bn_js_1.default(chunk.lowerBinId).sub(activeId);
|
|
1048
|
+
const chunkMaxDeltaId = new bn_js_1.default(chunk.upperBinId).sub(activeId);
|
|
1049
|
+
// Reset uninvolved params for this chunk
|
|
1050
|
+
const chunkParams = resetUninvolvedLiquidityParams(chunkMinDeltaId, chunkMaxDeltaId, favorXInActiveBin, params);
|
|
1051
|
+
// Calculate amounts for this chunk using toAmountIntoBins
|
|
1052
|
+
const binsAmounts = toAmountIntoBins(activeId, chunkMinDeltaId, chunkMaxDeltaId, chunkParams.deltaX, chunkParams.deltaY, chunkParams.x0, chunkParams.y0, binStep, favorXInActiveBin);
|
|
1053
|
+
// Sum up amounts
|
|
1054
|
+
const { totalXAmount, totalYAmount } = binsAmounts.reduce((acc, bin) => ({
|
|
1055
|
+
totalXAmount: acc.totalXAmount.add(bin.amountX),
|
|
1056
|
+
totalYAmount: acc.totalYAmount.add(bin.amountY),
|
|
1057
|
+
}), { totalXAmount: new bn_js_1.default(0), totalYAmount: new bn_js_1.default(0) });
|
|
1058
|
+
result.push({
|
|
1059
|
+
lowerBinId: chunk.lowerBinId,
|
|
1060
|
+
upperBinId: chunk.upperBinId,
|
|
1061
|
+
minDeltaId: chunkMinDeltaId,
|
|
1062
|
+
maxDeltaId: chunkMaxDeltaId,
|
|
1063
|
+
params: chunkParams,
|
|
1064
|
+
maxAmountX: totalXAmount,
|
|
1065
|
+
maxAmountY: totalYAmount,
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
return result;
|
|
1069
|
+
}
|