@hawksightco/hawk-sdk 1.3.218 → 1.3.220
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/classes/Transactions.d.ts +32 -45
- package/dist/src/classes/Transactions.d.ts.map +1 -1
- package/dist/src/classes/Transactions.js +754 -378
- package/dist/src/classes/TxGeneratorAutomations.d.ts +11 -46
- package/dist/src/classes/TxGeneratorAutomations.d.ts.map +1 -1
- package/dist/src/classes/TxGeneratorAutomations.js +48 -127
- package/dist/src/ixGenerator/MeteoraDlmmIxGenerator.d.ts +1 -2
- package/dist/src/ixGenerator/MeteoraDlmmIxGenerator.d.ts.map +1 -1
- package/dist/src/ixGenerator/MeteoraDlmmIxGenerator.js +4 -11
- package/dist/src/meteora/liquidityStrategy.d.ts +73 -0
- package/dist/src/meteora/liquidityStrategy.d.ts.map +1 -1
- package/dist/src/meteora/liquidityStrategy.js +130 -0
- package/dist/src/types.d.ts +22 -0
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1484,13 +1484,17 @@ class Transactions {
|
|
|
1484
1484
|
}
|
|
1485
1485
|
// ≥150 bins: chunked multi-TX approach
|
|
1486
1486
|
const dlmmPool = yield meteora_1.MeteoraDLMM.create(connection, lbPair, this.ix);
|
|
1487
|
-
//
|
|
1488
|
-
//
|
|
1489
|
-
//
|
|
1490
|
-
//
|
|
1491
|
-
|
|
1492
|
-
const
|
|
1493
|
-
const
|
|
1487
|
+
// Compute expected post-claim ATA amounts for accurate strategy parameters.
|
|
1488
|
+
// TX0 claims fees to ATA, minus 20% protocol fee sent to treasury on-chain.
|
|
1489
|
+
// TX1 deposit must use strategy params that match actual post-claim ATA balance.
|
|
1490
|
+
// Fee computation uses raw account parsing (avoids expensive getPositionsByUserAndLbPair).
|
|
1491
|
+
const claimableFees = yield this._computeClaimableFees(connection, params.position, lbPair, lowerBinId, upperBinId);
|
|
1492
|
+
const PROTOCOL_FEE_BPS = 2000; // 20% protocol fee deducted during claim_fee on-chain
|
|
1493
|
+
const totalXAmount = claimableFees.feeX.sub(claimableFees.feeX.mul(new bn_js_1.default(PROTOCOL_FEE_BPS)).div(new bn_js_1.default(10000)));
|
|
1494
|
+
const totalYAmount = claimableFees.feeY.sub(claimableFees.feeY.mul(new bn_js_1.default(PROTOCOL_FEE_BPS)).div(new bn_js_1.default(10000)));
|
|
1495
|
+
if (totalXAmount.isZero() && totalYAmount.isZero()) {
|
|
1496
|
+
throw new Error('No claimable fees to compound');
|
|
1497
|
+
}
|
|
1494
1498
|
const lbPairState = dlmmPool.dlmm.lbPair;
|
|
1495
1499
|
const tokenXMint = lbPairState.tokenXMint;
|
|
1496
1500
|
const tokenYMint = lbPairState.tokenYMint;
|
|
@@ -1590,6 +1594,7 @@ class Transactions {
|
|
|
1590
1594
|
activeId,
|
|
1591
1595
|
totalXAmount,
|
|
1592
1596
|
totalYAmount,
|
|
1597
|
+
skipInitBinArrays: true, // compound: position already exists, bin arrays are already initialized
|
|
1593
1598
|
});
|
|
1594
1599
|
// =========================================================================
|
|
1595
1600
|
// Phase 3: Assemble transactions
|
|
@@ -1615,7 +1620,6 @@ class Transactions {
|
|
|
1615
1620
|
});
|
|
1616
1621
|
}
|
|
1617
1622
|
/**
|
|
1618
|
-
* @deprecated Use rebalanceLargerPositionAutomation instead
|
|
1619
1623
|
*
|
|
1620
1624
|
* @param connection The Solana web3 connection object for blockchain interactions.
|
|
1621
1625
|
* @param params Parameters required for rebalance
|
|
@@ -1680,7 +1684,6 @@ class Transactions {
|
|
|
1680
1684
|
});
|
|
1681
1685
|
}
|
|
1682
1686
|
/**
|
|
1683
|
-
* @deprecated Use rebalanceLargerPositionAutomation instaed
|
|
1684
1687
|
*
|
|
1685
1688
|
* @param connection The Solana web3 connection object for blockchain interactions.
|
|
1686
1689
|
* @param params Parameters required for rebalance
|
|
@@ -1983,35 +1986,7 @@ class Transactions {
|
|
|
1983
1986
|
}
|
|
1984
1987
|
});
|
|
1985
1988
|
}
|
|
1986
|
-
|
|
1987
|
-
* Rebalance a Meteora DLMM position with support for larger positions.
|
|
1988
|
-
*
|
|
1989
|
-
* This is a backwards-compatible implementation of the rebalance operation that handles
|
|
1990
|
-
* positions of any size. The method automatically splits operations into 2-dimensional
|
|
1991
|
-
* instruction arrays (where each dimension represents a transaction) when dealing with
|
|
1992
|
-
* large bin arrays that would exceed single transaction limits.
|
|
1993
|
-
*
|
|
1994
|
-
* This rebalance method performs the following steps:
|
|
1995
|
-
* 1. Removes liquidity from the current position (supports larger bin ranges, not limited to 70-bin chunks, max 149-bin)
|
|
1996
|
-
* 2. Claims fees from the current position
|
|
1997
|
-
* 3. Claims rewards from the current position (if any)
|
|
1998
|
-
* 4. Closes the current position
|
|
1999
|
-
* 5. Initializes a new position
|
|
2000
|
-
* 6. Extends the new position length if needed (for larger positions)
|
|
2001
|
-
* 7. Adds liquidity to the new position using deposit automation
|
|
2002
|
-
*
|
|
2003
|
-
* Key benefits:
|
|
2004
|
-
* - Backwards-compatible with existing rebalance implementations
|
|
2005
|
-
* - Supports large bin arrays through automatic transaction splitting into multiple transactions
|
|
2006
|
-
* - Does NOT depend on Meteora SDK
|
|
2007
|
-
* - Handles positions that exceed single transaction size limits
|
|
2008
|
-
* - Uses increasePositionLengthAutomation for larger new positions
|
|
2009
|
-
*
|
|
2010
|
-
* @param connection The Solana web3 connection object for blockchain interactions.
|
|
2011
|
-
* @param params Parameters required for rebalance (same as MeteoraRebalance)
|
|
2012
|
-
* @returns Array of TransactionMetadataResponse for each transaction
|
|
2013
|
-
*/
|
|
2014
|
-
rebalanceLargerPositionAutomation(_a) {
|
|
1989
|
+
rebalanceSmallPositionAutomation(_a) {
|
|
2015
1990
|
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
2016
1991
|
// Current theoretical limit (149-bin)
|
|
2017
1992
|
const MAX_POSITION_WIDTH = 149;
|
|
@@ -2267,6 +2242,7 @@ class Transactions {
|
|
|
2267
2242
|
}
|
|
2268
2243
|
redepositForLargePositionAutomationIx(_a) {
|
|
2269
2244
|
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
2245
|
+
var _b;
|
|
2270
2246
|
try {
|
|
2271
2247
|
const program = yield meteora_1.MeteoraDLMM.program(connection);
|
|
2272
2248
|
const positionData = yield program.account.positionV2.fetch(params.position);
|
|
@@ -2292,7 +2268,6 @@ class Transactions {
|
|
|
2292
2268
|
const remainingAccountsInfo = yield (0, functions_1.fetchRemainingAccountsInfo)(connection, tokenXMint, tokenYMint, tokenXProgram, tokenYProgram);
|
|
2293
2269
|
const transactions = [];
|
|
2294
2270
|
const withdrawIxs = [];
|
|
2295
|
-
const depositIxs = [];
|
|
2296
2271
|
const removeLiquidityIx = yield this.ix.meteoraDlmm.removeLiquidityByRange2Automation({
|
|
2297
2272
|
connection,
|
|
2298
2273
|
userWallet: params.userWallet,
|
|
@@ -2310,9 +2285,20 @@ class Transactions {
|
|
|
2310
2285
|
});
|
|
2311
2286
|
withdrawIxs.push(removeLiquidityIx);
|
|
2312
2287
|
const { binId: activeId } = yield dlmmPool.dlmm.getActiveBin();
|
|
2313
|
-
|
|
2314
|
-
const
|
|
2315
|
-
const
|
|
2288
|
+
// Compute total token amounts from position bin data for strategy parameter calculation
|
|
2289
|
+
const positionBinData = userPosition.positionData.positionBinData;
|
|
2290
|
+
const { totalXAmount, totalYAmount } = positionBinData.reduce((acc, bin) => ({
|
|
2291
|
+
totalXAmount: acc.totalXAmount.add(new bn_js_1.default(bin.positionXAmount)),
|
|
2292
|
+
totalYAmount: acc.totalYAmount.add(new bn_js_1.default(bin.positionYAmount)),
|
|
2293
|
+
}), { totalXAmount: new bn_js_1.default(0), totalYAmount: new bn_js_1.default(0) });
|
|
2294
|
+
const distributionToStrategyType = {
|
|
2295
|
+
'SPOT': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-IMBALANCED': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-ONE-SIDE': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-BALANCED': liquidityStrategy_1.StrategyType.SPOT,
|
|
2296
|
+
'CURVE': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-IMBALANCED': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-ONE-SIDE': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-BALANCED': liquidityStrategy_1.StrategyType.CURVE,
|
|
2297
|
+
'BID-ASK': liquidityStrategy_1.StrategyType.BID_ASK, 'BID-ASK-IMBALANCED': liquidityStrategy_1.StrategyType.BID_ASK, 'BID-ASK-ONE-SIDE': liquidityStrategy_1.StrategyType.BID_ASK, 'BID-ASK-BALANCED': liquidityStrategy_1.StrategyType.BID_ASK,
|
|
2298
|
+
};
|
|
2299
|
+
const strategyType = (_b = distributionToStrategyType[params.distribution]) !== null && _b !== void 0 ? _b : liquidityStrategy_1.StrategyType.SPOT;
|
|
2300
|
+
// Deposit using chunked rebalanceLiquidityAutomation (handles >70 bins)
|
|
2301
|
+
const depositIxArrays = yield this._meteoraDepositToLargePositionAutomation({
|
|
2316
2302
|
connection,
|
|
2317
2303
|
userWallet: params.userWallet,
|
|
2318
2304
|
lbPair,
|
|
@@ -2321,38 +2307,28 @@ class Transactions {
|
|
|
2321
2307
|
tokenYMint,
|
|
2322
2308
|
tokenXProgram,
|
|
2323
2309
|
tokenYProgram,
|
|
2310
|
+
lowerBinId: currentLowerBinId,
|
|
2311
|
+
upperBinId: currentUpperBinId,
|
|
2312
|
+
strategyType,
|
|
2324
2313
|
activeId,
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
},
|
|
2329
|
-
strategyType: types_3.StrategyTypeMap[params.distribution],
|
|
2330
|
-
pdaTokenType: types_1.TokenType.ATA,
|
|
2331
|
-
remainingAccountsInfo,
|
|
2332
|
-
positionIsSigner: false,
|
|
2314
|
+
totalXAmount,
|
|
2315
|
+
totalYAmount,
|
|
2316
|
+
skipInitBinArrays: true,
|
|
2333
2317
|
});
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
}
|
|
2344
|
-
else {
|
|
2345
|
-
transactions.push(yield (0, functions_1.createTransactionMeta)({
|
|
2346
|
-
payer: params.userWallet,
|
|
2347
|
-
description: 'Automation IX: Meteora Redeposit TX1: Remove liquidity',
|
|
2348
|
-
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
2349
|
-
mainInstructions: withdrawIxs,
|
|
2350
|
-
}));
|
|
2318
|
+
// TX1: withdraw (full range)
|
|
2319
|
+
transactions.push(yield (0, functions_1.createTransactionMeta)({
|
|
2320
|
+
payer: params.userWallet,
|
|
2321
|
+
description: 'Automation IX: Meteora Redeposit TX1: Remove liquidity',
|
|
2322
|
+
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
2323
|
+
mainInstructions: withdrawIxs,
|
|
2324
|
+
}));
|
|
2325
|
+
// TX2..N: deposit chunks
|
|
2326
|
+
for (let i = 0; i < depositIxArrays.length; i++) {
|
|
2351
2327
|
transactions.push(yield (0, functions_1.createTransactionMeta)({
|
|
2352
2328
|
payer: params.userWallet,
|
|
2353
|
-
description:
|
|
2329
|
+
description: `Automation IX: Meteora Redeposit TX${i + 2}: Add liquidity`,
|
|
2354
2330
|
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
2355
|
-
mainInstructions:
|
|
2331
|
+
mainInstructions: depositIxArrays[i],
|
|
2356
2332
|
}));
|
|
2357
2333
|
}
|
|
2358
2334
|
return transactions;
|
|
@@ -2366,10 +2342,213 @@ class Transactions {
|
|
|
2366
2342
|
}
|
|
2367
2343
|
});
|
|
2368
2344
|
}
|
|
2345
|
+
/**
|
|
2346
|
+
* Phased rebalance for Meteora DLMM positions (71-149 bins).
|
|
2347
|
+
*
|
|
2348
|
+
* Produces exactly 2 TXs:
|
|
2349
|
+
* TX1: removeLiquidity + claimFee (with fee splitting) + claimRewards + closePosition
|
|
2350
|
+
* TX2: initPosition + extendPosition + initBinArrays + deposit (rebalanceLiquidityAutomation)
|
|
2351
|
+
*
|
|
2352
|
+
* Uses rebalanceLiquidityAutomation for deposit (handles 149 bins reliably),
|
|
2353
|
+
* unlike rebalanceSmallPositionAutomation which uses meteoraDlmmDepositRelativeAutomation.
|
|
2354
|
+
* Separate withdraw/deposit TXs enable Jupiter swap injection by the caller.
|
|
2355
|
+
*/
|
|
2356
|
+
rebalancePhasedPositionAutomation(_a) {
|
|
2357
|
+
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
2358
|
+
var _b, _c, _d;
|
|
2359
|
+
const MAX_POSITION_WIDTH = 149;
|
|
2360
|
+
const MAX_INIT_WIDTH = 70;
|
|
2361
|
+
const MAX_EXTEND_LENGTH = 91;
|
|
2362
|
+
try {
|
|
2363
|
+
const program = yield meteora_1.MeteoraDLMM.program(connection);
|
|
2364
|
+
const positionData = yield program.account.positionV2.fetch(params.currentPosition);
|
|
2365
|
+
if (!positionData || !positionData.lbPair) {
|
|
2366
|
+
throw new Error(`Invalid position data: ${params.currentPosition.toString()}`);
|
|
2367
|
+
}
|
|
2368
|
+
const lbPair = positionData.lbPair;
|
|
2369
|
+
const currentLowerBinId = positionData.lowerBinId;
|
|
2370
|
+
const currentUpperBinId = positionData.upperBinId;
|
|
2371
|
+
if (params.width > MAX_POSITION_WIDTH) {
|
|
2372
|
+
throw new Error(`Position width ${params.width} exceeds maximum ${MAX_POSITION_WIDTH}`);
|
|
2373
|
+
}
|
|
2374
|
+
const dlmmPool = yield meteora_1.MeteoraDLMM.create(connection, lbPair, this.ix);
|
|
2375
|
+
const userPda = (0, functions_1.generateUserPda)(params.userWallet);
|
|
2376
|
+
const { userPositions } = yield dlmmPool.getPositionsByUserAndLbPair(userPda);
|
|
2377
|
+
const userPosition = userPositions.find(pos => pos.publicKey.equals(params.currentPosition));
|
|
2378
|
+
if (!userPosition) {
|
|
2379
|
+
throw new Error(`Position ${params.currentPosition.toString()} not found`);
|
|
2380
|
+
}
|
|
2381
|
+
const positionBinData = userPosition.positionData.positionBinData;
|
|
2382
|
+
const { totalXAmount, totalYAmount } = positionBinData.reduce((acc, bin) => ({
|
|
2383
|
+
totalXAmount: acc.totalXAmount.add(new bn_js_1.default(bin.positionXAmount)),
|
|
2384
|
+
totalYAmount: acc.totalYAmount.add(new bn_js_1.default(bin.positionYAmount)),
|
|
2385
|
+
}), { totalXAmount: new bn_js_1.default(0), totalYAmount: new bn_js_1.default(0) });
|
|
2386
|
+
const lbPairState = dlmmPool.dlmm.lbPair;
|
|
2387
|
+
const tokenXMint = lbPairState.tokenXMint;
|
|
2388
|
+
const tokenYMint = lbPairState.tokenYMint;
|
|
2389
|
+
const rewardInfos = lbPairState.rewardInfos;
|
|
2390
|
+
const tokenProgramMap = yield (0, functions_1.getTokenProgramMapForMints)(connection, [tokenXMint, tokenYMint]);
|
|
2391
|
+
const tokenXProgram = tokenProgramMap[tokenXMint.toString()];
|
|
2392
|
+
const tokenYProgram = tokenProgramMap[tokenYMint.toString()];
|
|
2393
|
+
const remainingAccountsInfo = yield (0, functions_1.fetchRemainingAccountsInfo)(connection, tokenXMint, tokenYMint, tokenXProgram, tokenYProgram);
|
|
2394
|
+
const pdaTokenTypeForClaimables = params.useAta ? types_1.TokenType.ATA : types_1.TokenType.STA;
|
|
2395
|
+
const { binId: activeId } = yield dlmmPool.dlmm.getActiveBin();
|
|
2396
|
+
const distributionToStrategyType = {
|
|
2397
|
+
'SPOT': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-IMBALANCED': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-ONE-SIDE': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-BALANCED': liquidityStrategy_1.StrategyType.SPOT,
|
|
2398
|
+
'CURVE': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-IMBALANCED': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-ONE-SIDE': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-BALANCED': liquidityStrategy_1.StrategyType.CURVE,
|
|
2399
|
+
'BID-ASK': liquidityStrategy_1.StrategyType.BID_ASK, 'BID-ASK-IMBALANCED': liquidityStrategy_1.StrategyType.BID_ASK, 'BID-ASK-ONE-SIDE': liquidityStrategy_1.StrategyType.BID_ASK, 'BID-ASK-BALANCED': liquidityStrategy_1.StrategyType.BID_ASK,
|
|
2400
|
+
};
|
|
2401
|
+
const strategyType = (_b = distributionToStrategyType[params.distribution]) !== null && _b !== void 0 ? _b : liquidityStrategy_1.StrategyType.SPOT;
|
|
2402
|
+
// =========================================================================
|
|
2403
|
+
// TX1: Withdraw + Claim + Close (separate IXs — preserves fee splitting)
|
|
2404
|
+
// =========================================================================
|
|
2405
|
+
const group1Ixs = [];
|
|
2406
|
+
const removeLiquidityIx = yield this.ix.meteoraDlmm.removeLiquidityByRange2Automation({
|
|
2407
|
+
connection,
|
|
2408
|
+
userWallet: params.userWallet,
|
|
2409
|
+
lbPair,
|
|
2410
|
+
position: params.currentPosition,
|
|
2411
|
+
tokenXMint, tokenYMint, tokenXProgram, tokenYProgram,
|
|
2412
|
+
fromBinId: currentLowerBinId,
|
|
2413
|
+
toBinId: currentUpperBinId,
|
|
2414
|
+
bpsToRemove: 10000,
|
|
2415
|
+
pdaTokenType: types_1.TokenType.ATA,
|
|
2416
|
+
remainingAccountsInfo,
|
|
2417
|
+
});
|
|
2418
|
+
group1Ixs.push(removeLiquidityIx);
|
|
2419
|
+
const claimFeeIx = yield this.ix.meteoraDlmm.claimFee2Automation(connection, {
|
|
2420
|
+
userWallet: params.userWallet,
|
|
2421
|
+
lbPair,
|
|
2422
|
+
position: params.currentPosition,
|
|
2423
|
+
tokenMintX: tokenXMint, tokenMintY: tokenYMint,
|
|
2424
|
+
tokenProgramX: tokenXProgram, tokenProgramY: tokenYProgram,
|
|
2425
|
+
lowerBinId: currentLowerBinId, upperBinId: currentUpperBinId,
|
|
2426
|
+
pdaTokenType: pdaTokenTypeForClaimables,
|
|
2427
|
+
remainingAccountsInfo,
|
|
2428
|
+
});
|
|
2429
|
+
group1Ixs.push(claimFeeIx);
|
|
2430
|
+
for (let rewardIndex = 0; rewardIndex < 2; rewardIndex++) {
|
|
2431
|
+
const rewardInfo = rewardInfos[rewardIndex];
|
|
2432
|
+
if (!rewardInfo || rewardInfo.mint.equals(web3.PublicKey.default))
|
|
2433
|
+
continue;
|
|
2434
|
+
const rewardTokenProgramMap = yield (0, functions_1.getTokenProgramMapForMints)(connection, [rewardInfo.mint]);
|
|
2435
|
+
const rewardTokenProgram = rewardTokenProgramMap[rewardInfo.mint.toString()];
|
|
2436
|
+
const rewardRemainingAccountsInfo = yield (0, functions_1.fetchRemainingAccountsInfoForReward)(connection, rewardInfo.mint, rewardTokenProgram);
|
|
2437
|
+
const claimRewardIx = yield this.ix.meteoraDlmm.claimReward2Automation(connection, {
|
|
2438
|
+
userWallet: params.userWallet,
|
|
2439
|
+
lbPair,
|
|
2440
|
+
position: params.currentPosition,
|
|
2441
|
+
rewardIndex,
|
|
2442
|
+
rewardMint: rewardInfo.mint,
|
|
2443
|
+
rewardVault: rewardInfo.vault,
|
|
2444
|
+
tokenProgram: rewardTokenProgram,
|
|
2445
|
+
lowerBinId: currentLowerBinId, upperBinId: currentUpperBinId,
|
|
2446
|
+
pdaTokenType: types_1.TokenType.STA,
|
|
2447
|
+
remainingAccountsInfo: rewardRemainingAccountsInfo,
|
|
2448
|
+
});
|
|
2449
|
+
group1Ixs.push(claimRewardIx);
|
|
2450
|
+
}
|
|
2451
|
+
const closePositionIx = yield this.ix.meteoraDlmm.closePosition2Automation({
|
|
2452
|
+
connection,
|
|
2453
|
+
userWallet: params.userWallet,
|
|
2454
|
+
position: params.currentPosition,
|
|
2455
|
+
lbPair,
|
|
2456
|
+
});
|
|
2457
|
+
group1Ixs.push(closePositionIx);
|
|
2458
|
+
// =========================================================================
|
|
2459
|
+
// TX2: Init position + Extend + InitBinArrays + Deposit (rebalanceLiquidityAutomation)
|
|
2460
|
+
// =========================================================================
|
|
2461
|
+
const group2Ixs = [];
|
|
2462
|
+
const initialWidth = Math.min(MAX_INIT_WIDTH, params.width);
|
|
2463
|
+
const initPositionIx = yield this.ix.meteoraDlmm.initializePositionRelativeAutomation(connection, {
|
|
2464
|
+
userWallet: params.userWallet,
|
|
2465
|
+
lbPair,
|
|
2466
|
+
position: params.newPosition,
|
|
2467
|
+
relativeLowerBinId: params.relativeLowerBin,
|
|
2468
|
+
width: initialWidth,
|
|
2469
|
+
});
|
|
2470
|
+
group2Ixs.push(initPositionIx);
|
|
2471
|
+
let positionBinsAllocated = initialWidth;
|
|
2472
|
+
while (positionBinsAllocated < params.width) {
|
|
2473
|
+
const toAdd = Math.min(MAX_EXTEND_LENGTH, params.width - positionBinsAllocated);
|
|
2474
|
+
const extendIx = yield this.ix.meteoraDlmm.increasePositionLengthAutomation({
|
|
2475
|
+
connection,
|
|
2476
|
+
userWallet: params.userWallet,
|
|
2477
|
+
lbPair,
|
|
2478
|
+
position: params.newPosition,
|
|
2479
|
+
lengthToAdd: toAdd,
|
|
2480
|
+
side: types_1.MeteoraPositionSide.Upper,
|
|
2481
|
+
});
|
|
2482
|
+
group2Ixs.push(extendIx);
|
|
2483
|
+
positionBinsAllocated += toAdd;
|
|
2484
|
+
}
|
|
2485
|
+
const newLowerBinId = activeId + params.relativeLowerBin;
|
|
2486
|
+
const newUpperBinId = newLowerBinId + params.width - 1;
|
|
2487
|
+
// Only initialize bin arrays that don't exist yet (avoids bloating TX with redundant inits)
|
|
2488
|
+
const binArraysRequired = yield (0, dlmm_1.getBinArraysRequiredByPositionRange)(lbPair, new bn_js_1.default(newLowerBinId), new bn_js_1.default(newUpperBinId), dlmmPool.dlmm.program.programId);
|
|
2489
|
+
if (binArraysRequired.length > 0) {
|
|
2490
|
+
const initBinArraysIx = yield dlmmPool.dlmm.initializeBinArrays(binArraysRequired.map(b => b.index), addresses_1.HS_AUTHORITY);
|
|
2491
|
+
group2Ixs.push(...initBinArraysIx);
|
|
2492
|
+
}
|
|
2493
|
+
// Use adjusted amounts for strategy computation if provided (post-swap prediction).
|
|
2494
|
+
// When a Jupiter swap occurs between withdraw and deposit, the ATA balances change.
|
|
2495
|
+
// Strategy parameters (x0, y0) must match what will actually be in the ATA at deposit time.
|
|
2496
|
+
const depositXAmount = (_c = params.adjustedTotalXAmount) !== null && _c !== void 0 ? _c : totalXAmount;
|
|
2497
|
+
const depositYAmount = (_d = params.adjustedTotalYAmount) !== null && _d !== void 0 ? _d : totalYAmount;
|
|
2498
|
+
// Skip bin array init inside deposit helper since we handled it above.
|
|
2499
|
+
// Allow active bin slippage of 10 bins to tolerate price movement from the
|
|
2500
|
+
// Jupiter swap that may occur between the withdraw TX and this deposit TX.
|
|
2501
|
+
const depositIxArrays = yield this._meteoraDepositToLargePositionAutomation({
|
|
2502
|
+
connection,
|
|
2503
|
+
userWallet: params.userWallet,
|
|
2504
|
+
lbPair,
|
|
2505
|
+
position: params.newPosition,
|
|
2506
|
+
tokenXMint, tokenYMint, tokenXProgram, tokenYProgram,
|
|
2507
|
+
lowerBinId: newLowerBinId,
|
|
2508
|
+
upperBinId: newUpperBinId,
|
|
2509
|
+
strategyType,
|
|
2510
|
+
activeId,
|
|
2511
|
+
totalXAmount: depositXAmount,
|
|
2512
|
+
totalYAmount: depositYAmount,
|
|
2513
|
+
skipInitBinArrays: true,
|
|
2514
|
+
maxActiveBinSlippage: 10,
|
|
2515
|
+
});
|
|
2516
|
+
// For ≤149 bins, depositIxArrays has exactly 1 chunk
|
|
2517
|
+
group2Ixs.push(...depositIxArrays[0]);
|
|
2518
|
+
// =========================================================================
|
|
2519
|
+
// Assemble TXs
|
|
2520
|
+
// =========================================================================
|
|
2521
|
+
const transactions = [];
|
|
2522
|
+
transactions.push(yield (0, functions_1.createTransactionMeta)({
|
|
2523
|
+
payer: params.userWallet,
|
|
2524
|
+
description: 'Automation IX: Meteora Phased Rebalance TX1: Withdraw + Claim + Close',
|
|
2525
|
+
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
2526
|
+
mainInstructions: group1Ixs,
|
|
2527
|
+
}));
|
|
2528
|
+
transactions.push(yield (0, functions_1.createTransactionMeta)({
|
|
2529
|
+
payer: params.userWallet,
|
|
2530
|
+
description: 'Automation IX: Meteora Phased Rebalance TX2: Init + Deposit',
|
|
2531
|
+
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
2532
|
+
mainInstructions: group2Ixs,
|
|
2533
|
+
}));
|
|
2534
|
+
return transactions;
|
|
2535
|
+
}
|
|
2536
|
+
catch (error) {
|
|
2537
|
+
console.error('=== REBALANCE PHASED POSITION AUTOMATION ERROR ===');
|
|
2538
|
+
console.error('Error type:', error instanceof Error ? error.constructor.name : typeof error);
|
|
2539
|
+
console.error('Error message:', error instanceof Error ? error.message : String(error));
|
|
2540
|
+
console.error('Error stack:', error instanceof Error ? error.stack : 'No stack trace');
|
|
2541
|
+
console.error('Full error object:', error);
|
|
2542
|
+
console.error('=== END ERROR LOG ===');
|
|
2543
|
+
throw error;
|
|
2544
|
+
}
|
|
2545
|
+
});
|
|
2546
|
+
}
|
|
2369
2547
|
/**
|
|
2370
2548
|
* Rebalance a large Meteora DLMM position (up to 1400 bins) using automation instructions.
|
|
2371
2549
|
*
|
|
2372
|
-
* For ≤
|
|
2550
|
+
* For ≤70 bins, delegates to rebalanceSmallerPositionAutomation.
|
|
2551
|
+
* For 71-149 bins, delegates to rebalancePhasedPositionAutomation.
|
|
2373
2552
|
* For ≥150 bins, uses a chunked multi-TX approach:
|
|
2374
2553
|
* Phase 1: Chunked withdraw from current position
|
|
2375
2554
|
* Phase 2: Initialize new large position
|
|
@@ -2393,9 +2572,13 @@ class Transactions {
|
|
|
2393
2572
|
const currentLowerBinId = positionData.lowerBinId;
|
|
2394
2573
|
const currentUpperBinId = positionData.upperBinId;
|
|
2395
2574
|
const currentBinCount = currentUpperBinId - currentLowerBinId + 1;
|
|
2396
|
-
// For ≤
|
|
2575
|
+
// For ≤70 bins, delegate to existing rebalanceSmallPositionAutomation
|
|
2576
|
+
if (currentBinCount <= 70 && params.width <= 70) {
|
|
2577
|
+
return this.rebalanceSmallPositionAutomation({ connection, params, fetch: undefined });
|
|
2578
|
+
}
|
|
2579
|
+
// For 71-149 bins, use phased approach (separate withdraw/deposit TXs)
|
|
2397
2580
|
if (currentBinCount <= 149 && params.width <= 149) {
|
|
2398
|
-
return this.
|
|
2581
|
+
return this.rebalancePhasedPositionAutomation({ connection, params, fetch: undefined });
|
|
2399
2582
|
}
|
|
2400
2583
|
// ≥150 bins: multi-TX chunked approach
|
|
2401
2584
|
const dlmmPool = yield meteora_1.MeteoraDLMM.create(connection, lbPair, this.ix);
|
|
@@ -2543,7 +2726,7 @@ class Transactions {
|
|
|
2543
2726
|
tokenXMint, tokenYMint, tokenXProgram, tokenYProgram,
|
|
2544
2727
|
activeId: params.activeId,
|
|
2545
2728
|
pdaTokenType: types_1.TokenType.ATA,
|
|
2546
|
-
maxActiveBinSlippage:
|
|
2729
|
+
maxActiveBinSlippage: 0,
|
|
2547
2730
|
shouldClaimFee: false,
|
|
2548
2731
|
shouldClaimReward: false,
|
|
2549
2732
|
minWithdrawXAmount: new bn_js_1.default(0),
|
|
@@ -2560,304 +2743,121 @@ class Transactions {
|
|
|
2560
2743
|
});
|
|
2561
2744
|
}
|
|
2562
2745
|
/**
|
|
2563
|
-
*
|
|
2564
|
-
*
|
|
2565
|
-
*
|
|
2566
|
-
*
|
|
2567
|
-
* Remove TXs: rebalanceLiquidityAutomation with removeLiquidityParams (~75 bins/TX)
|
|
2568
|
-
* Deposit TXs: rebalanceLiquidityAutomation with addLiquidityParams (~75 bins/TX)
|
|
2569
|
-
*
|
|
2570
|
-
* Meteora's rebalance_liquidity handles position extension and bin array
|
|
2571
|
-
* initialization internally — no separate IXs needed.
|
|
2572
|
-
*
|
|
2573
|
-
* Key differences from rebalanceLargePositionAutomation:
|
|
2574
|
-
* - Same position (no close/create, no new position keypair needed)
|
|
2575
|
-
* - Supports positions of any size (69 to 1400+ bins)
|
|
2746
|
+
* Compute claimable fees for a position directly from on-chain account data.
|
|
2747
|
+
* Avoids expensive getPositionsByUserAndLbPair by parsing raw buffers.
|
|
2748
|
+
* Handles extended positions (>70 bins) by reading extended data after reserved bytes.
|
|
2749
|
+
* Reference: data/tvl/src/meteoraFunctions.ts:getBatchedFees
|
|
2576
2750
|
*/
|
|
2577
|
-
|
|
2578
|
-
return __awaiter(this,
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
const
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
const
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
console.log(`Total chunk per tx (withdraw): ${WITHDRAW_CHUNK_SIZE} bins, total chunk per tx (deposit): ${DEPOSIT_CHUNK_SIZE} bins`);
|
|
2613
|
-
console.log(`old lower bin id: ${oldLowerBinId}, old upper bin id: ${oldUpperBinId}, old width: ${oldUpperBinId - oldLowerBinId + 1}`);
|
|
2614
|
-
console.log(`new lower bin id: ${newLowerBinId}, new upper bin id: ${newUpperBinId}, new width: ${newUpperBinId - newLowerBinId + 1}`);
|
|
2615
|
-
const lbPairState = dlmmPool.dlmm.lbPair;
|
|
2616
|
-
const tokenXMint = lbPairState.tokenXMint;
|
|
2617
|
-
const tokenYMint = lbPairState.tokenYMint;
|
|
2618
|
-
const tokenProgramMap = yield (0, functions_1.getTokenProgramMapForMints)(connection, [tokenXMint, tokenYMint]);
|
|
2619
|
-
const tokenXProgram = tokenProgramMap[tokenXMint.toString()];
|
|
2620
|
-
const tokenYProgram = tokenProgramMap[tokenYMint.toString()];
|
|
2621
|
-
// Fetch transfer hook accounts for token X and Y (Token2022 support)
|
|
2622
|
-
const baseRemainingAccountsInfo = yield (0, functions_1.fetchRemainingAccountsInfo)(connection, tokenXMint, tokenYMint, tokenXProgram, tokenYProgram);
|
|
2623
|
-
// Extract reward infos for claiming rewards during rebalance
|
|
2624
|
-
const rewardInfos = [];
|
|
2625
|
-
const rewardInfosFromPair = lbPairState.rewardInfos;
|
|
2626
|
-
if (rewardInfosFromPair && rewardInfosFromPair.length > 0) {
|
|
2627
|
-
// Collect reward mints that are active (non-default)
|
|
2628
|
-
const activeRewardMints = [];
|
|
2629
|
-
for (const reward of rewardInfosFromPair) {
|
|
2630
|
-
if (!reward.mint.equals(web3.PublicKey.default)) {
|
|
2631
|
-
activeRewardMints.push(reward.mint);
|
|
2632
|
-
}
|
|
2633
|
-
}
|
|
2634
|
-
// Get token programs for reward mints
|
|
2635
|
-
if (activeRewardMints.length > 0) {
|
|
2636
|
-
const rewardTokenProgramMap = yield (0, functions_1.getTokenProgramMapForMints)(connection, activeRewardMints);
|
|
2637
|
-
for (let i = 0; i < rewardInfosFromPair.length; i++) {
|
|
2638
|
-
const reward = rewardInfosFromPair[i];
|
|
2639
|
-
if (!reward.mint.equals(web3.PublicKey.default)) {
|
|
2640
|
-
rewardInfos.push({
|
|
2641
|
-
rewardIndex: i,
|
|
2642
|
-
rewardVault: reward.vault,
|
|
2643
|
-
rewardMint: reward.mint,
|
|
2644
|
-
rewardTokenProgram: rewardTokenProgramMap[reward.mint.toString()],
|
|
2645
|
-
});
|
|
2646
|
-
}
|
|
2647
|
-
}
|
|
2648
|
-
}
|
|
2649
|
-
}
|
|
2650
|
-
console.log(`[Pipeline] Found ${rewardInfos.length} active reward(s) for this pool`);
|
|
2651
|
-
// Build combined remainingAccountsInfo with transfer hooks for X, Y, and rewards
|
|
2652
|
-
// The slices describe: TransferHookX, TransferHookY, TransferHookMultiReward(index) for each reward
|
|
2653
|
-
// IMPORTANT: Always include all slices, even with length=0, so Meteora knows the account structure
|
|
2654
|
-
const combinedSlices = [];
|
|
2655
|
-
const combinedAccounts = [];
|
|
2656
|
-
// Always add TransferHookX and TransferHookY slices (even if length=0)
|
|
2657
|
-
const xSlice = baseRemainingAccountsInfo.slices.find(s => s.accountsType === types_1.RemainingAccountsType.TransferHookX);
|
|
2658
|
-
const ySlice = baseRemainingAccountsInfo.slices.find(s => s.accountsType === types_1.RemainingAccountsType.TransferHookY);
|
|
2659
|
-
combinedSlices.push({ accountsType: types_1.RemainingAccountsType.TransferHookX, length: (_b = xSlice === null || xSlice === void 0 ? void 0 : xSlice.length) !== null && _b !== void 0 ? _b : 0 });
|
|
2660
|
-
combinedSlices.push({ accountsType: types_1.RemainingAccountsType.TransferHookY, length: (_c = ySlice === null || ySlice === void 0 ? void 0 : ySlice.length) !== null && _c !== void 0 ? _c : 0 });
|
|
2661
|
-
// Add transfer hook accounts from base (for X and Y)
|
|
2662
|
-
combinedAccounts.push(...baseRemainingAccountsInfo.accounts);
|
|
2663
|
-
// Fetch transfer hook accounts for each reward and add to combined info
|
|
2664
|
-
// IMPORTANT: Always add TransferHookMultiReward slice for each reward, even if length=0
|
|
2665
|
-
// This tells Meteora about the reward position in the remaining accounts
|
|
2666
|
-
for (const reward of rewardInfos) {
|
|
2667
|
-
const rewardTransferHookInfo = yield (0, functions_1.fetchRemainingAccountsInfoForReward)(connection, reward.rewardMint, reward.rewardTokenProgram);
|
|
2668
|
-
// Always add TransferHookMultiReward slice (even if empty length for standard SPL tokens)
|
|
2669
|
-
const transferHookLength = rewardTransferHookInfo.slices.length > 0
|
|
2670
|
-
? rewardTransferHookInfo.slices[0].length
|
|
2671
|
-
: 0;
|
|
2672
|
-
combinedSlices.push({
|
|
2673
|
-
accountsType: types_1.RemainingAccountsType.TransferHookMultiReward,
|
|
2674
|
-
length: transferHookLength,
|
|
2675
|
-
multiRewardIndex: reward.rewardIndex,
|
|
2676
|
-
});
|
|
2677
|
-
combinedAccounts.push(...rewardTransferHookInfo.accounts);
|
|
2678
|
-
}
|
|
2679
|
-
const remainingAccountsInfo = {
|
|
2680
|
-
slices: combinedSlices,
|
|
2681
|
-
accounts: combinedAccounts,
|
|
2682
|
-
};
|
|
2683
|
-
const distributionToStrategyType = {
|
|
2684
|
-
'SPOT': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-IMBALANCED': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-ONE-SIDE': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-BALANCED': liquidityStrategy_1.StrategyType.SPOT,
|
|
2685
|
-
'CURVE': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-IMBALANCED': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-ONE-SIDE': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-BALANCED': liquidityStrategy_1.StrategyType.CURVE,
|
|
2686
|
-
'BID-ASK': liquidityStrategy_1.StrategyType.BID_ASK, 'BID-ASK-IMBALANCED': liquidityStrategy_1.StrategyType.BID_ASK, 'BID-ASK-ONE-SIDE': liquidityStrategy_1.StrategyType.BID_ASK, 'BID-ASK-BALANCED': liquidityStrategy_1.StrategyType.BID_ASK,
|
|
2687
|
-
};
|
|
2688
|
-
const strategyType = (_d = distributionToStrategyType[params.distribution]) !== null && _d !== void 0 ? _d : liquidityStrategy_1.StrategyType.SPOT;
|
|
2689
|
-
const binStep = new bn_js_1.default(dlmmPool.dlmm.lbPair.binStep);
|
|
2690
|
-
// Build new-position deposit chunks
|
|
2691
|
-
const minDeltaId = new bn_js_1.default(newLowerBinId - activeId);
|
|
2692
|
-
const maxDeltaId = new bn_js_1.default(newUpperBinId - activeId);
|
|
2693
|
-
const favorXInActiveId = totalYAmount.isZero() && !totalXAmount.isZero();
|
|
2694
|
-
const strategyParams = (0, liquidityStrategy_1.buildStrategyParameters)(strategyType, totalXAmount, totalYAmount, minDeltaId, maxDeltaId, binStep, new bn_js_1.default(activeId), favorXInActiveId);
|
|
2695
|
-
// =====================================================================
|
|
2696
|
-
// Pipeline algorithm: pack withdraw + deposit work into fewer TXs.
|
|
2697
|
-
//
|
|
2698
|
-
// Given W old bins to withdraw and D new bins to deposit:
|
|
2699
|
-
// Phase 1: Pure withdraw TXs — all withdraw chunks except the last
|
|
2700
|
-
// Phase 2: Pivot TX — last withdraw chunk + extend + initBinArrays
|
|
2701
|
-
// + first deposit (if small enough to fit without CU issues)
|
|
2702
|
-
// Phase 3: Pure deposit TXs — remaining deposit bins by CHUNK_SIZE
|
|
2703
|
-
//
|
|
2704
|
-
// When the total extension is large or the deposit range spans many
|
|
2705
|
-
// bin arrays, the transition IXs (extend + initBinArrays) are placed
|
|
2706
|
-
// in a separate TX to avoid exceeding compute unit limits.
|
|
2707
|
-
// =====================================================================
|
|
2708
|
-
const txInstructions = [];
|
|
2709
|
-
// Helper: build on-chain deposit param from a ChunkedDepositParameters
|
|
2710
|
-
const buildDepositParam = (chunk) => {
|
|
2711
|
-
const p = (0, liquidityStrategy_1.buildBitFlagAndNegateStrategyParameters)(chunk.params.x0, chunk.params.y0, chunk.params.deltaX, chunk.params.deltaY);
|
|
2712
|
-
return {
|
|
2713
|
-
minDeltaId: chunk.minDeltaId.toNumber(),
|
|
2714
|
-
maxDeltaId: chunk.maxDeltaId.toNumber(),
|
|
2715
|
-
x0: p.x0, y0: p.y0, deltaX: p.deltaX, deltaY: p.deltaY,
|
|
2716
|
-
bitFlag: p.bitFlag, favorXInActiveId,
|
|
2717
|
-
};
|
|
2718
|
-
};
|
|
2719
|
-
// Helper: build withdraw IX
|
|
2720
|
-
const buildWithdrawIx = (lower, upper) => __awaiter(this, void 0, void 0, function* () {
|
|
2721
|
-
const binArrayOverrides = this.ix.pda.meteora.deriveBinArrays(params.lbPair, lower, upper);
|
|
2722
|
-
return this.ix.meteoraDlmm.rebalanceLiquidityAutomation({
|
|
2723
|
-
connection,
|
|
2724
|
-
userWallet: params.userWallet,
|
|
2725
|
-
position: params.position,
|
|
2726
|
-
lbPair: params.lbPair,
|
|
2727
|
-
tokenXMint, tokenYMint, tokenXProgram, tokenYProgram,
|
|
2728
|
-
activeId,
|
|
2729
|
-
pdaTokenType: types_1.TokenType.ATA,
|
|
2730
|
-
maxActiveBinSlippage: 300,
|
|
2731
|
-
shouldClaimFee: true,
|
|
2732
|
-
shouldClaimReward: rewardInfos.length > 0,
|
|
2733
|
-
minWithdrawXAmount: new bn_js_1.default(0),
|
|
2734
|
-
maxDepositXAmount: new bn_js_1.default(0),
|
|
2735
|
-
minWithdrawYAmount: new bn_js_1.default(0),
|
|
2736
|
-
maxDepositYAmount: new bn_js_1.default(0),
|
|
2737
|
-
removeLiquidityParams: [{ minBinId: lower, maxBinId: upper, bps: 10000 }],
|
|
2738
|
-
addLiquidityParams: [],
|
|
2739
|
-
binArrayOverrides,
|
|
2740
|
-
shrinkMode: types_3.ShrinkMode.Default,
|
|
2741
|
-
rewardInfos,
|
|
2742
|
-
remainingAccountsInfo,
|
|
2743
|
-
});
|
|
2751
|
+
_computeClaimableFees(connection, position, lbPair, lowerBinId, upperBinId) {
|
|
2752
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2753
|
+
const MAX_BIN_PER_ARRAY = 70;
|
|
2754
|
+
const DLMM_PROGRAM_ID = new web3.PublicKey('LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo');
|
|
2755
|
+
const positionAccountInfo = yield connection.getAccountInfo(position);
|
|
2756
|
+
if (!positionAccountInfo || !positionAccountInfo.data) {
|
|
2757
|
+
throw new Error(`Failed to fetch position account: ${position.toString()}`);
|
|
2758
|
+
}
|
|
2759
|
+
const posBuffer = positionAccountInfo.data;
|
|
2760
|
+
const binCount = upperBinId - lowerBinId + 1;
|
|
2761
|
+
// PositionV2 struct layout offsets
|
|
2762
|
+
const LIQUIDITY_OFFSET = 72; // 8 discriminator + 32 lbPair + 32 owner
|
|
2763
|
+
const REWARD_OFFSET = LIQUIDITY_OFFSET + 70 * 16; // liquidityShares: u128[70]
|
|
2764
|
+
const FEE_OFFSET = REWARD_OFFSET + 70 * 48; // rewardInfos: UserRewardInfo[70], 48 bytes each
|
|
2765
|
+
// After feeInfos[70]: lowerBinId(4) + upperBinId(4) + lastUpdatedAt(8) + totalClaimedFeeX(8) + totalClaimedFeeY(8) + totalClaimedRewards(16) + reserved(160)
|
|
2766
|
+
const EXTENDED_OFFSET = FEE_OFFSET + 70 * 48 + 4 + 4 + 8 + 8 + 8 + 16 + 160;
|
|
2767
|
+
function readU64(buf, off) {
|
|
2768
|
+
return buf.readBigUInt64LE(off);
|
|
2769
|
+
}
|
|
2770
|
+
function readU128(buf, off) {
|
|
2771
|
+
const low = buf.readBigUInt64LE(off);
|
|
2772
|
+
const high = buf.readBigUInt64LE(off + 8);
|
|
2773
|
+
return low + (high << BigInt(64));
|
|
2774
|
+
}
|
|
2775
|
+
const liquidityShares = [];
|
|
2776
|
+
const feeInfos = [];
|
|
2777
|
+
const binsInFixed = Math.min(binCount, 70);
|
|
2778
|
+
for (let i = 0; i < binsInFixed; i++) {
|
|
2779
|
+
liquidityShares.push(readU128(posBuffer, LIQUIDITY_OFFSET + i * 16));
|
|
2780
|
+
const feeStart = FEE_OFFSET + i * 48;
|
|
2781
|
+
feeInfos.push({
|
|
2782
|
+
feeXPerTokenComplete: readU128(posBuffer, feeStart),
|
|
2783
|
+
feeYPerTokenComplete: readU128(posBuffer, feeStart + 16),
|
|
2784
|
+
feeXPending: readU64(posBuffer, feeStart + 32),
|
|
2785
|
+
feeYPending: readU64(posBuffer, feeStart + 40),
|
|
2744
2786
|
});
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
lbPair: params.lbPair,
|
|
2759
|
-
tokenXMint, tokenYMint, tokenXProgram, tokenYProgram,
|
|
2760
|
-
activeId,
|
|
2761
|
-
pdaTokenType: types_1.TokenType.ATA,
|
|
2762
|
-
maxActiveBinSlippage: 300,
|
|
2763
|
-
shouldClaimFee: removeLiquidityParams ? true : false,
|
|
2764
|
-
shouldClaimReward: !!removeLiquidityParams && rewardInfos.length > 0,
|
|
2765
|
-
minWithdrawXAmount: new bn_js_1.default(0),
|
|
2766
|
-
maxDepositXAmount: chunk.maxAmountX,
|
|
2767
|
-
minWithdrawYAmount: new bn_js_1.default(0),
|
|
2768
|
-
maxDepositYAmount: chunk.maxAmountY,
|
|
2769
|
-
removeLiquidityParams: removeLiquidityParams ? [{ minBinId: removeLiquidityParams.minBinId, maxBinId: removeLiquidityParams.maxBinId, bps: 10000 }] : [],
|
|
2770
|
-
addLiquidityParams: [buildDepositParam(chunk)],
|
|
2771
|
-
binArrayOverrides,
|
|
2772
|
-
shrinkMode: removeLiquidityParams ? types_3.ShrinkMode.Default : types_3.ShrinkMode.NoShrinkBoth,
|
|
2773
|
-
rewardInfos: removeLiquidityParams ? rewardInfos : undefined,
|
|
2774
|
-
remainingAccountsInfo,
|
|
2787
|
+
}
|
|
2788
|
+
// Extended bins (>70) — appended after reserved bytes
|
|
2789
|
+
if (binCount > 70) {
|
|
2790
|
+
let offset = EXTENDED_OFFSET;
|
|
2791
|
+
for (let i = 70; i < binCount; i++) {
|
|
2792
|
+
liquidityShares.push(readU128(posBuffer, offset));
|
|
2793
|
+
offset += 16;
|
|
2794
|
+
offset += 48; // skip rewardInfo
|
|
2795
|
+
feeInfos.push({
|
|
2796
|
+
feeXPerTokenComplete: readU128(posBuffer, offset),
|
|
2797
|
+
feeYPerTokenComplete: readU128(posBuffer, offset + 16),
|
|
2798
|
+
feeXPending: readU64(posBuffer, offset + 32),
|
|
2799
|
+
feeYPending: readU64(posBuffer, offset + 40),
|
|
2775
2800
|
});
|
|
2776
|
-
|
|
2777
|
-
// Build transition IXs: increasePositionLength + initBinArrays.
|
|
2778
|
-
// Bin arrays must be pre-initialized before rebalanceLiquidity deposits.
|
|
2779
|
-
const increasePositionLengthIxs = [];
|
|
2780
|
-
// Generate increasePositionLength IXs to extend position (assumption at the moment, I expect calculation to be wrong)
|
|
2781
|
-
let currentLower = newLowerBinId + WITHDRAW_CHUNK_SIZE;
|
|
2782
|
-
while (currentLower < newUpperBinId) {
|
|
2783
|
-
const currentWidth = newUpperBinId - currentLower + 1;
|
|
2784
|
-
const binsToAdd = Math.min(91, currentWidth);
|
|
2785
|
-
console.log('Increasing position length by', binsToAdd, 'bins at lower', currentLower);
|
|
2786
|
-
increasePositionLengthIxs.push(yield this.ix.meteoraDlmm.increasePositionLengthAutomation({
|
|
2787
|
-
connection,
|
|
2788
|
-
userWallet: params.userWallet,
|
|
2789
|
-
lbPair: params.lbPair,
|
|
2790
|
-
position: params.position,
|
|
2791
|
-
lengthToAdd: binsToAdd,
|
|
2792
|
-
side: types_1.MeteoraPositionSide.Upper,
|
|
2793
|
-
}));
|
|
2794
|
-
currentLower = currentLower + binsToAdd;
|
|
2795
|
-
}
|
|
2796
|
-
// Build all deposit chunks upfront
|
|
2797
|
-
const depositChunks = (0, liquidityStrategy_1.chunkDepositParametersWithInitialSize)(strategyParams, minDeltaId, maxDeltaId, new bn_js_1.default(activeId), binStep, DEPOSIT_CHUNK_INITIAL_SIZE, DEPOSIT_CHUNK_SIZE, favorXInActiveId);
|
|
2798
|
-
// Build old-position withdraw chunks, reserving PIVOT_RESERVE bins for pivot TX
|
|
2799
|
-
const oldWidth = oldUpperBinId - oldLowerBinId + 1;
|
|
2800
|
-
const oldChunks = [];
|
|
2801
|
-
let current = oldLowerBinId;
|
|
2802
|
-
// Build Phase 1 withdraw chunks (before pivot)
|
|
2803
|
-
while (current < oldUpperBinId) {
|
|
2804
|
-
const upper = Math.min(WITHDRAW_CHUNK_SIZE - 1, oldUpperBinId - current) + current;
|
|
2805
|
-
console.log(`Generating withdraw chunk: [${current}, ${upper}, ${upper - current + 1} bins]`);
|
|
2806
|
-
oldChunks.push({ lower: current, upper });
|
|
2807
|
-
current = upper + 1;
|
|
2808
|
-
}
|
|
2809
|
-
const remainder = oldWidth % WITHDRAW_CHUNK_SIZE;
|
|
2810
|
-
if (remainder === 0 || remainder > PIVOT_RESERVE) {
|
|
2811
|
-
// Pop last chunk to reserve for pivot
|
|
2812
|
-
const popped = oldChunks.pop();
|
|
2813
|
-
// Then insert bigger and final chunk for pivot
|
|
2814
|
-
const upper = popped.upper - PIVOT_RESERVE;
|
|
2815
|
-
oldChunks.push({ lower: popped.lower, upper });
|
|
2816
|
-
oldChunks.push({ lower: upper + 1, upper: popped.upper });
|
|
2801
|
+
offset += 48;
|
|
2817
2802
|
}
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
const
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2803
|
+
}
|
|
2804
|
+
// Derive and fetch bin array accounts
|
|
2805
|
+
const binArrayIndices = new Set();
|
|
2806
|
+
for (let i = 0; i < binCount; i++) {
|
|
2807
|
+
binArrayIndices.add(Math.floor((lowerBinId + i) / MAX_BIN_PER_ARRAY));
|
|
2808
|
+
}
|
|
2809
|
+
const binArrayIndexList = Array.from(binArrayIndices);
|
|
2810
|
+
const binArrayPDAs = binArrayIndexList.map(index => {
|
|
2811
|
+
const buf = Buffer.alloc(8);
|
|
2812
|
+
buf.writeBigInt64LE(BigInt(index));
|
|
2813
|
+
const [pda] = web3.PublicKey.findProgramAddressSync([Buffer.from('bin_array'), lbPair.toBytes(), buf], DLMM_PROGRAM_ID);
|
|
2814
|
+
return pda;
|
|
2815
|
+
});
|
|
2816
|
+
const binArrayAccounts = yield connection.getMultipleAccountsInfo(binArrayPDAs);
|
|
2817
|
+
// Parse bin array fee accumulators
|
|
2818
|
+
const BIN_ARRAY_HEADER = 56;
|
|
2819
|
+
const BIN_SIZE = 144;
|
|
2820
|
+
const binArrayFeeMap = new Map();
|
|
2821
|
+
for (let a = 0; a < binArrayIndexList.length; a++) {
|
|
2822
|
+
const account = binArrayAccounts[a];
|
|
2823
|
+
if (!account || !account.data)
|
|
2824
|
+
continue;
|
|
2825
|
+
const feeMap = new Map();
|
|
2826
|
+
for (let b = 0; b < MAX_BIN_PER_ARRAY; b++) {
|
|
2827
|
+
const binStart = BIN_ARRAY_HEADER + b * BIN_SIZE;
|
|
2828
|
+
feeMap.set(b, {
|
|
2829
|
+
feeXStored: readU128(account.data, binStart + 80),
|
|
2830
|
+
feeYStored: readU128(account.data, binStart + 96),
|
|
2831
|
+
});
|
|
2845
2832
|
}
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2833
|
+
binArrayFeeMap.set(binArrayIndexList[a], feeMap);
|
|
2834
|
+
}
|
|
2835
|
+
// Compute total claimable fees
|
|
2836
|
+
let totalFeeX = BigInt(0);
|
|
2837
|
+
let totalFeeY = BigInt(0);
|
|
2838
|
+
for (let i = 0; i < binCount; i++) {
|
|
2839
|
+
const binId = lowerBinId + i;
|
|
2840
|
+
const arrayIndex = Math.floor(binId / MAX_BIN_PER_ARRAY);
|
|
2841
|
+
const binIndexInArray = binId - arrayIndex * MAX_BIN_PER_ARRAY;
|
|
2842
|
+
let feeX = feeInfos[i].feeXPending;
|
|
2843
|
+
let feeY = feeInfos[i].feeYPending;
|
|
2844
|
+
const binArray = binArrayFeeMap.get(arrayIndex);
|
|
2845
|
+
if (binArray && liquidityShares[i] > BigInt(0)) {
|
|
2846
|
+
const binData = binArray.get(binIndexInArray);
|
|
2847
|
+
if (binData) {
|
|
2848
|
+
const deltaX = binData.feeXStored - feeInfos[i].feeXPerTokenComplete;
|
|
2849
|
+
const deltaY = binData.feeYStored - feeInfos[i].feeYPerTokenComplete;
|
|
2850
|
+
feeX += (liquidityShares[i] * deltaX) >> BigInt(128);
|
|
2851
|
+
feeY += (liquidityShares[i] * deltaY) >> BigInt(128);
|
|
2852
|
+
}
|
|
2855
2853
|
}
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
catch (error) {
|
|
2859
|
-
throw error;
|
|
2854
|
+
totalFeeX += feeX;
|
|
2855
|
+
totalFeeY += feeY;
|
|
2860
2856
|
}
|
|
2857
|
+
return {
|
|
2858
|
+
feeX: new bn_js_1.default(totalFeeX.toString()),
|
|
2859
|
+
feeY: new bn_js_1.default(totalFeeY.toString()),
|
|
2860
|
+
};
|
|
2861
2861
|
});
|
|
2862
2862
|
}
|
|
2863
2863
|
/**
|
|
@@ -2868,7 +2868,8 @@ class Transactions {
|
|
|
2868
2868
|
_meteoraDepositToLargePositionAutomation(params) {
|
|
2869
2869
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2870
2870
|
const MAX_BINS_PER_TX = 149;
|
|
2871
|
-
const { connection, userWallet, lbPair, position, tokenXMint, tokenYMint, tokenXProgram, tokenYProgram, lowerBinId, upperBinId, strategyType, activeId, totalXAmount, totalYAmount, } = params;
|
|
2871
|
+
const { connection, userWallet, lbPair, position, tokenXMint, tokenYMint, tokenXProgram, tokenYProgram, lowerBinId, upperBinId, strategyType, activeId, totalXAmount, totalYAmount, skipInitBinArrays, maxActiveBinSlippage: maxActiveBinSlippageParam, } = params;
|
|
2872
|
+
const maxActiveBinSlippage = maxActiveBinSlippageParam !== null && maxActiveBinSlippageParam !== void 0 ? maxActiveBinSlippageParam : 0;
|
|
2872
2873
|
const dlmmPool = yield meteora_1.MeteoraDLMM.create(connection, lbPair, this.ix);
|
|
2873
2874
|
const binStep = new bn_js_1.default(dlmmPool.dlmm.lbPair.binStep);
|
|
2874
2875
|
// Calculate delta IDs relative to active bin for entire position
|
|
@@ -2886,16 +2887,20 @@ class Transactions {
|
|
|
2886
2887
|
// Convert strategy parameters to on-chain format with bitFlag for negative values
|
|
2887
2888
|
const onChainParams = (0, liquidityStrategy_1.buildBitFlagAndNegateStrategyParameters)(chunk.params.x0, chunk.params.y0, chunk.params.deltaX, chunk.params.deltaY);
|
|
2888
2889
|
const instructions = [];
|
|
2889
|
-
// Add initialize bin arrays instruction
|
|
2890
|
-
//
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2890
|
+
// Add initialize bin arrays instruction only when bin arrays may not yet exist.
|
|
2891
|
+
// For compound operations the position already exists, so bin arrays are already
|
|
2892
|
+
// initialized — skipping these saves TX space.
|
|
2893
|
+
if (!skipInitBinArrays) {
|
|
2894
|
+
// Use HS_AUTHORITY as funder since this is a raw Meteora IX and the automation worker signs with this key
|
|
2895
|
+
const initBinArraysIx = yield this.ix.meteoraDlmm.meteoraDlmmInitializeBinArrayDefault({
|
|
2896
|
+
connection,
|
|
2897
|
+
userWallet: addresses_1.HS_AUTHORITY,
|
|
2898
|
+
lbPair,
|
|
2899
|
+
lowerBinId: chunk.lowerBinId,
|
|
2900
|
+
upperBinId: chunk.upperBinId,
|
|
2901
|
+
});
|
|
2902
|
+
instructions.push(...initBinArraysIx);
|
|
2903
|
+
}
|
|
2899
2904
|
// Add liquidity via rebalanceLiquidityAutomation
|
|
2900
2905
|
// Use u64::MAX for maxDeposit amounts so they don't cap the deposit.
|
|
2901
2906
|
// The on-chain program uses ATA balance for actual amounts; these are just safety ceilings.
|
|
@@ -2911,7 +2916,7 @@ class Transactions {
|
|
|
2911
2916
|
tokenYProgram,
|
|
2912
2917
|
activeId,
|
|
2913
2918
|
pdaTokenType: types_1.TokenType.ATA,
|
|
2914
|
-
maxActiveBinSlippage
|
|
2919
|
+
maxActiveBinSlippage,
|
|
2915
2920
|
shouldClaimFee: false,
|
|
2916
2921
|
shouldClaimReward: false,
|
|
2917
2922
|
minWithdrawXAmount: new bn_js_1.default(0),
|
|
@@ -3110,7 +3115,7 @@ class Transactions {
|
|
|
3110
3115
|
tokenYProgram,
|
|
3111
3116
|
activeId,
|
|
3112
3117
|
pdaTokenType: types_1.TokenType.ATA,
|
|
3113
|
-
maxActiveBinSlippage:
|
|
3118
|
+
maxActiveBinSlippage: 0,
|
|
3114
3119
|
shouldClaimFee: false,
|
|
3115
3120
|
shouldClaimReward: false,
|
|
3116
3121
|
minWithdrawXAmount: new bn_js_1.default(0),
|
|
@@ -3856,7 +3861,7 @@ class Transactions {
|
|
|
3856
3861
|
activeId,
|
|
3857
3862
|
pdaTokenType,
|
|
3858
3863
|
// RebalanceLiquidityParams
|
|
3859
|
-
maxActiveBinSlippage:
|
|
3864
|
+
maxActiveBinSlippage: 0,
|
|
3860
3865
|
shouldClaimFee: false,
|
|
3861
3866
|
shouldClaimReward: false,
|
|
3862
3867
|
minWithdrawXAmount: new bn_js_1.default(0),
|
|
@@ -5909,6 +5914,377 @@ class Transactions {
|
|
|
5909
5914
|
});
|
|
5910
5915
|
});
|
|
5911
5916
|
}
|
|
5917
|
+
/**
|
|
5918
|
+
* Hybrid liquidity rebalance for automation: teardown old position and rebuild
|
|
5919
|
+
* with independent X/Y distributions per layer using rebalanceLiquidityAutomation.
|
|
5920
|
+
*
|
|
5921
|
+
* Scope: 70-bin positions only. No increasePositionLength needed.
|
|
5922
|
+
*/
|
|
5923
|
+
meteoraRebalanceHybridLiquidityAutomation(_a) {
|
|
5924
|
+
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
5925
|
+
var _b, _c;
|
|
5926
|
+
try {
|
|
5927
|
+
// --- Validate layers ---
|
|
5928
|
+
const layers = params.layers;
|
|
5929
|
+
if (layers.length === 0 || layers.length > 3) {
|
|
5930
|
+
throw new Error('layers must have 1-3 entries');
|
|
5931
|
+
}
|
|
5932
|
+
const sumX = layers.reduce((s, l) => s + l.x.percentage, 0);
|
|
5933
|
+
const sumY = layers.reduce((s, l) => s + l.y.percentage, 0);
|
|
5934
|
+
if (sumX !== 100 || sumY !== 100) {
|
|
5935
|
+
throw new Error(`Layer percentages must sum to 100 per side (got X=${sumX}, Y=${sumY})`);
|
|
5936
|
+
}
|
|
5937
|
+
const { lbPair, lbPairState, lowerBinId: currentLowerBinId, upperBinId: currentUpperBinId, estimatedTotalX, estimatedTotalY, activeId, withdrawFn, depositFn, } = yield this.resolvePositionContext({
|
|
5938
|
+
connection,
|
|
5939
|
+
userWallet: params.userWallet,
|
|
5940
|
+
position: params.currentPosition,
|
|
5941
|
+
compound: params.compound,
|
|
5942
|
+
targetActiveBin: params.targetActiveBin,
|
|
5943
|
+
});
|
|
5944
|
+
// checkRange validation
|
|
5945
|
+
if (activeId < params.checkRange.lowerRange || activeId > params.checkRange.upperRange) {
|
|
5946
|
+
throw new Error(`Active bin ${activeId} outside checkRange [${params.checkRange.lowerRange}, ${params.checkRange.upperRange}]`);
|
|
5947
|
+
}
|
|
5948
|
+
const newLowerBinId = activeId + params.relativeBinRange.lowerRange;
|
|
5949
|
+
const newUpperBinId = activeId + params.relativeBinRange.upperRange;
|
|
5950
|
+
const binStep = new bn_js_1.default(lbPairState.binStep);
|
|
5951
|
+
// --- Build TX1 (teardown) ---
|
|
5952
|
+
// Generate rebalance_liquidity instruction meant for withdrawing liquidity, fees, and rewards, and
|
|
5953
|
+
// shrinking position prior to closing
|
|
5954
|
+
const tx1Instructions = [
|
|
5955
|
+
// Withdraw + claim fees + claim rewards using rebalanceLiquidity
|
|
5956
|
+
...yield withdrawFn({
|
|
5957
|
+
position: params.currentPosition,
|
|
5958
|
+
minBinId: currentLowerBinId,
|
|
5959
|
+
maxBinId: currentUpperBinId,
|
|
5960
|
+
compound: params.compound,
|
|
5961
|
+
origin: `meteoraRebalanceHybridLiquidityAutomation`
|
|
5962
|
+
}),
|
|
5963
|
+
// Close position
|
|
5964
|
+
{
|
|
5965
|
+
label: 'close_position',
|
|
5966
|
+
class: 'meteoraRebalanceHybridLiquidityAutomation',
|
|
5967
|
+
instruction: yield this.ix.meteoraDlmm.closePosition2Automation({
|
|
5968
|
+
connection,
|
|
5969
|
+
userWallet: params.userWallet,
|
|
5970
|
+
position: params.currentPosition,
|
|
5971
|
+
lbPair,
|
|
5972
|
+
})
|
|
5973
|
+
}
|
|
5974
|
+
];
|
|
5975
|
+
// --- Build TX2+ (rebuild) ---
|
|
5976
|
+
const CLASS = 'meteoraRebalanceHybridLiquidityAutomation';
|
|
5977
|
+
const txInstructions = [tx1Instructions];
|
|
5978
|
+
// New position bin arrays
|
|
5979
|
+
const newWidth = newUpperBinId - newLowerBinId + 1;
|
|
5980
|
+
// TX2: initializePositionRelativeAutomation + first deposit batch
|
|
5981
|
+
const initIx = {
|
|
5982
|
+
label: 'initialize_position_relative',
|
|
5983
|
+
class: CLASS,
|
|
5984
|
+
instruction: yield this.ix.meteoraDlmm.initializePositionRelativeAutomation(connection, {
|
|
5985
|
+
userWallet: params.userWallet,
|
|
5986
|
+
lbPair,
|
|
5987
|
+
position: params.newPosition,
|
|
5988
|
+
relativeLowerBinId: params.relativeBinRange.lowerRange,
|
|
5989
|
+
width: newWidth,
|
|
5990
|
+
}),
|
|
5991
|
+
};
|
|
5992
|
+
// --- Build addLiquidityParams per layer ---
|
|
5993
|
+
const minDeltaId = newLowerBinId - activeId;
|
|
5994
|
+
const maxDeltaId = newUpperBinId - activeId;
|
|
5995
|
+
const effectiveAmountX = (_b = params.amountXOverride) !== null && _b !== void 0 ? _b : estimatedTotalX;
|
|
5996
|
+
const effectiveAmountY = (_c = params.amountYOverride) !== null && _c !== void 0 ? _c : estimatedTotalY;
|
|
5997
|
+
const favorXInActiveId = effectiveAmountY.isZero() && !effectiveAmountX.isZero();
|
|
5998
|
+
const strategyResults = (0, liquidityStrategy_1.distributionToHybridStrategy)({
|
|
5999
|
+
hybridDistribution: layers,
|
|
6000
|
+
amountX: effectiveAmountX,
|
|
6001
|
+
amountY: effectiveAmountY,
|
|
6002
|
+
minDeltaId: new bn_js_1.default(minDeltaId),
|
|
6003
|
+
maxDeltaId: new bn_js_1.default(maxDeltaId),
|
|
6004
|
+
binStep,
|
|
6005
|
+
activeId: new bn_js_1.default(activeId),
|
|
6006
|
+
});
|
|
6007
|
+
const allAddLiquidityParams = strategyResults.map(mergedParams => {
|
|
6008
|
+
const p = (0, liquidityStrategy_1.buildBitFlagAndNegateStrategyParameters)(mergedParams.x0, mergedParams.y0, mergedParams.deltaX, mergedParams.deltaY);
|
|
6009
|
+
return {
|
|
6010
|
+
minDeltaId,
|
|
6011
|
+
maxDeltaId,
|
|
6012
|
+
x0: p.x0, y0: p.y0, deltaX: p.deltaX, deltaY: p.deltaY,
|
|
6013
|
+
bitFlag: p.bitFlag,
|
|
6014
|
+
favorXInActiveId,
|
|
6015
|
+
maxAmountX: mergedParams.maxAmountX,
|
|
6016
|
+
maxAmountY: mergedParams.maxAmountY,
|
|
6017
|
+
};
|
|
6018
|
+
});
|
|
6019
|
+
// One deposit IX per addLiquidityParams
|
|
6020
|
+
const depositIxs = [];
|
|
6021
|
+
for (const p of allAddLiquidityParams) {
|
|
6022
|
+
depositIxs.push(yield depositFn({
|
|
6023
|
+
position: params.newPosition,
|
|
6024
|
+
shouldClaimFee: false,
|
|
6025
|
+
shouldClaimReward: false,
|
|
6026
|
+
maxDepositXAmount: p.maxAmountX,
|
|
6027
|
+
maxDepositYAmount: p.maxAmountY,
|
|
6028
|
+
pdaTokenType: types_1.TokenType.ATA,
|
|
6029
|
+
addLiquidityParams: [{
|
|
6030
|
+
minDeltaId: p.minDeltaId,
|
|
6031
|
+
maxDeltaId: p.maxDeltaId,
|
|
6032
|
+
x0: p.x0, y0: p.y0, deltaX: p.deltaX, deltaY: p.deltaY,
|
|
6033
|
+
bitFlag: p.bitFlag,
|
|
6034
|
+
favorXInActiveId: p.favorXInActiveId,
|
|
6035
|
+
}],
|
|
6036
|
+
origin: CLASS,
|
|
6037
|
+
}));
|
|
6038
|
+
}
|
|
6039
|
+
// TX2: init + first deposit
|
|
6040
|
+
txInstructions.push([initIx, depositIxs[0]]);
|
|
6041
|
+
// TX3+: remaining deposit IXs
|
|
6042
|
+
for (let i = 1; i < depositIxs.length; i++) {
|
|
6043
|
+
txInstructions.push([depositIxs[i]]);
|
|
6044
|
+
}
|
|
6045
|
+
// --- Wrap in TransactionMetadataResponse2 ---
|
|
6046
|
+
const transactions = [];
|
|
6047
|
+
for (let i = 0; i < txInstructions.length; i++) {
|
|
6048
|
+
transactions.push(yield (0, functions_1.createTransactionMeta2)({
|
|
6049
|
+
payer: params.userWallet,
|
|
6050
|
+
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
6051
|
+
mainInstructions: txInstructions[i],
|
|
6052
|
+
}));
|
|
6053
|
+
}
|
|
6054
|
+
return transactions;
|
|
6055
|
+
}
|
|
6056
|
+
catch (error) {
|
|
6057
|
+
console.error('=== REBALANCE HYBRID LIQUIDITY AUTOMATION ERROR ===');
|
|
6058
|
+
console.error('Error:', error instanceof Error ? error.message : String(error));
|
|
6059
|
+
console.error('Stack:', error instanceof Error ? error.stack : 'No stack trace');
|
|
6060
|
+
console.error('=== END ERROR LOG ===');
|
|
6061
|
+
throw error;
|
|
6062
|
+
}
|
|
6063
|
+
});
|
|
6064
|
+
}
|
|
6065
|
+
/**
|
|
6066
|
+
* Fetches a DLMM position's on-chain state and resolves all associated context
|
|
6067
|
+
* needed for downstream instructions: pool state, token mints/programs,
|
|
6068
|
+
* transfer hook remaining accounts, and reward hook remaining accounts.
|
|
6069
|
+
*/
|
|
6070
|
+
resolvePositionContext(_a) {
|
|
6071
|
+
return __awaiter(this, arguments, void 0, function* ({ connection, userWallet, position, compound, targetActiveBin, }) {
|
|
6072
|
+
var _b, _c;
|
|
6073
|
+
// --- Fetch position + pool state ---
|
|
6074
|
+
const program = yield meteora_1.MeteoraDLMM.program(connection);
|
|
6075
|
+
const positionOnChain = yield program.account.positionV2.fetch(position);
|
|
6076
|
+
const lbPair = positionOnChain.lbPair;
|
|
6077
|
+
const dlmmPool = yield meteora_1.MeteoraDLMM.create(connection, lbPair, this.ix);
|
|
6078
|
+
const userPda = (0, functions_1.generateUserPda)(userWallet);
|
|
6079
|
+
const lowerBinId = positionOnChain.lowerBinId;
|
|
6080
|
+
const upperBinId = positionOnChain.upperBinId;
|
|
6081
|
+
const lbPairState = dlmmPool.dlmm.lbPair;
|
|
6082
|
+
const tokenXMint = lbPairState.tokenXMint;
|
|
6083
|
+
const tokenYMint = lbPairState.tokenYMint;
|
|
6084
|
+
const activeId = targetActiveBin !== undefined ? targetActiveBin : lbPairState.activeId;
|
|
6085
|
+
// Position data. (techdebt, needs not to call this in the future)
|
|
6086
|
+
// AI note, just keep it. we'll work on this later.
|
|
6087
|
+
const { userPositions } = yield dlmmPool.getPositionsByUserAndLbPair(userPda);
|
|
6088
|
+
const userPosition = userPositions.find(p => p.publicKey.equals(position));
|
|
6089
|
+
if (userPosition === undefined) {
|
|
6090
|
+
throw new Error(`Unexpected error: Position (${position}) doesn't exist on-chain`);
|
|
6091
|
+
}
|
|
6092
|
+
const tokenProgramMap = yield (0, functions_1.getTokenProgramMapForMints)(connection, [tokenXMint, tokenYMint]);
|
|
6093
|
+
const tokenXProgram = tokenProgramMap[tokenXMint.toString()];
|
|
6094
|
+
const tokenYProgram = tokenProgramMap[tokenYMint.toString()];
|
|
6095
|
+
const baseRemainingAccountsInfo = yield (0, functions_1.fetchRemainingAccountsInfo)(connection, tokenXMint, tokenYMint, tokenXProgram, tokenYProgram);
|
|
6096
|
+
// --- Build combined remainingAccountsInfo with rewards ---
|
|
6097
|
+
const rewardInfos = [];
|
|
6098
|
+
const rewardInfosFromPair = lbPairState.rewardInfos;
|
|
6099
|
+
if (rewardInfosFromPair && rewardInfosFromPair.length > 0) {
|
|
6100
|
+
const activeRewardMints = [];
|
|
6101
|
+
for (const reward of rewardInfosFromPair) {
|
|
6102
|
+
if (!reward.mint.equals(web3.PublicKey.default)) {
|
|
6103
|
+
activeRewardMints.push(reward.mint);
|
|
6104
|
+
}
|
|
6105
|
+
}
|
|
6106
|
+
if (activeRewardMints.length > 0) {
|
|
6107
|
+
const rewardTokenProgramMap = yield (0, functions_1.getTokenProgramMapForMints)(connection, activeRewardMints);
|
|
6108
|
+
for (let i = 0; i < rewardInfosFromPair.length; i++) {
|
|
6109
|
+
const reward = rewardInfosFromPair[i];
|
|
6110
|
+
if (!reward.mint.equals(web3.PublicKey.default)) {
|
|
6111
|
+
rewardInfos.push({
|
|
6112
|
+
rewardIndex: i,
|
|
6113
|
+
rewardVault: reward.vault,
|
|
6114
|
+
rewardMint: reward.mint,
|
|
6115
|
+
rewardTokenProgram: rewardTokenProgramMap[reward.mint.toString()],
|
|
6116
|
+
});
|
|
6117
|
+
}
|
|
6118
|
+
}
|
|
6119
|
+
}
|
|
6120
|
+
}
|
|
6121
|
+
const combinedSlices = [];
|
|
6122
|
+
const combinedAccounts = [];
|
|
6123
|
+
const xSlice = baseRemainingAccountsInfo.slices.find(s => s.accountsType === types_1.RemainingAccountsType.TransferHookX);
|
|
6124
|
+
const ySlice = baseRemainingAccountsInfo.slices.find(s => s.accountsType === types_1.RemainingAccountsType.TransferHookY);
|
|
6125
|
+
combinedSlices.push({ accountsType: types_1.RemainingAccountsType.TransferHookX, length: (_b = xSlice === null || xSlice === void 0 ? void 0 : xSlice.length) !== null && _b !== void 0 ? _b : 0 });
|
|
6126
|
+
combinedSlices.push({ accountsType: types_1.RemainingAccountsType.TransferHookY, length: (_c = ySlice === null || ySlice === void 0 ? void 0 : ySlice.length) !== null && _c !== void 0 ? _c : 0 });
|
|
6127
|
+
combinedAccounts.push(...baseRemainingAccountsInfo.accounts);
|
|
6128
|
+
for (const reward of rewardInfos) {
|
|
6129
|
+
const rewardTransferHookInfo = yield (0, functions_1.fetchRemainingAccountsInfoForReward)(connection, reward.rewardMint, reward.rewardTokenProgram);
|
|
6130
|
+
const transferHookLength = rewardTransferHookInfo.slices.length > 0
|
|
6131
|
+
? rewardTransferHookInfo.slices[0].length
|
|
6132
|
+
: 0;
|
|
6133
|
+
combinedSlices.push({
|
|
6134
|
+
accountsType: types_1.RemainingAccountsType.TransferHookMultiReward,
|
|
6135
|
+
length: transferHookLength,
|
|
6136
|
+
multiRewardIndex: reward.rewardIndex,
|
|
6137
|
+
});
|
|
6138
|
+
combinedAccounts.push(...rewardTransferHookInfo.accounts);
|
|
6139
|
+
}
|
|
6140
|
+
const remainingAccountsInfo = {
|
|
6141
|
+
slices: combinedSlices,
|
|
6142
|
+
accounts: combinedAccounts,
|
|
6143
|
+
};
|
|
6144
|
+
// --- Estimate total amounts ---
|
|
6145
|
+
const positionData = userPosition.positionData;
|
|
6146
|
+
const feeX = positionData.feeX;
|
|
6147
|
+
const feeY = positionData.feeY;
|
|
6148
|
+
const estimatedTotalX = compound
|
|
6149
|
+
? new bn_js_1.default(positionData.totalXAmount).add(feeX)
|
|
6150
|
+
: new bn_js_1.default(positionData.totalXAmount);
|
|
6151
|
+
const estimatedTotalY = compound
|
|
6152
|
+
? new bn_js_1.default(positionData.totalYAmount).add(feeY)
|
|
6153
|
+
: new bn_js_1.default(positionData.totalYAmount);
|
|
6154
|
+
const _rebalanceLiquidityAutomation = (_a) => __awaiter(this, [_a], void 0, function* ({ position, maxDepositXAmount, maxDepositYAmount, pdaTokenType, shrinkMode, shouldClaimFee, shouldClaimReward, removeLiquidityParams, addLiquidityParams, }) {
|
|
6155
|
+
return yield this.ix.meteoraDlmm.rebalanceLiquidityAutomation({
|
|
6156
|
+
connection,
|
|
6157
|
+
userWallet: userWallet,
|
|
6158
|
+
position,
|
|
6159
|
+
lbPair,
|
|
6160
|
+
tokenXMint, tokenYMint, tokenXProgram, tokenYProgram,
|
|
6161
|
+
activeId,
|
|
6162
|
+
pdaTokenType,
|
|
6163
|
+
maxActiveBinSlippage: 0,
|
|
6164
|
+
shouldClaimFee,
|
|
6165
|
+
shouldClaimReward,
|
|
6166
|
+
minWithdrawXAmount: new bn_js_1.default(0),
|
|
6167
|
+
maxDepositXAmount,
|
|
6168
|
+
minWithdrawYAmount: new bn_js_1.default(0),
|
|
6169
|
+
maxDepositYAmount,
|
|
6170
|
+
removeLiquidityParams,
|
|
6171
|
+
addLiquidityParams,
|
|
6172
|
+
shrinkMode,
|
|
6173
|
+
rewardInfos,
|
|
6174
|
+
remainingAccountsInfo,
|
|
6175
|
+
});
|
|
6176
|
+
});
|
|
6177
|
+
const _withdrawFn = (_a) => __awaiter(this, [_a], void 0, function* ({ position, minBinId, maxBinId, shrinkMode, shouldClaimFee, shouldClaimReward, pdaTokenType, }) {
|
|
6178
|
+
return yield _rebalanceLiquidityAutomation({
|
|
6179
|
+
position,
|
|
6180
|
+
pdaTokenType,
|
|
6181
|
+
shouldClaimFee,
|
|
6182
|
+
shouldClaimReward,
|
|
6183
|
+
maxDepositXAmount: new bn_js_1.default(0),
|
|
6184
|
+
maxDepositYAmount: new bn_js_1.default(0),
|
|
6185
|
+
removeLiquidityParams: [{ minBinId, maxBinId, bps: 10000 }],
|
|
6186
|
+
addLiquidityParams: [],
|
|
6187
|
+
shrinkMode,
|
|
6188
|
+
});
|
|
6189
|
+
});
|
|
6190
|
+
const withdrawFn = (_a) => __awaiter(this, [_a], void 0, function* ({ position, minBinId, maxBinId, compound, origin, }) {
|
|
6191
|
+
if (compound) {
|
|
6192
|
+
// Withdraw + Claim Fee + Claim Reward
|
|
6193
|
+
return [
|
|
6194
|
+
{
|
|
6195
|
+
label: 'compound_withdraw_using_rbl',
|
|
6196
|
+
class: origin,
|
|
6197
|
+
instruction: yield _withdrawFn({
|
|
6198
|
+
position,
|
|
6199
|
+
minBinId,
|
|
6200
|
+
maxBinId,
|
|
6201
|
+
shouldClaimReward: true,
|
|
6202
|
+
shrinkMode: types_3.ShrinkMode.Default,
|
|
6203
|
+
shouldClaimFee: true,
|
|
6204
|
+
pdaTokenType: types_1.TokenType.ATA,
|
|
6205
|
+
})
|
|
6206
|
+
}
|
|
6207
|
+
];
|
|
6208
|
+
}
|
|
6209
|
+
return [
|
|
6210
|
+
// Withdraw + claim reward from position
|
|
6211
|
+
{
|
|
6212
|
+
label: 'non_compound_withdraw_using_rbl',
|
|
6213
|
+
class: 'withdrawFn',
|
|
6214
|
+
instruction: yield _withdrawFn({
|
|
6215
|
+
position,
|
|
6216
|
+
minBinId,
|
|
6217
|
+
maxBinId,
|
|
6218
|
+
shouldClaimReward: true,
|
|
6219
|
+
shrinkMode: types_3.ShrinkMode.NoShrinkBoth,
|
|
6220
|
+
shouldClaimFee: false,
|
|
6221
|
+
// Note:
|
|
6222
|
+
// User Reward Token is not affected by this parameter. It's always STA regardless.
|
|
6223
|
+
// pdaTokenType only affects token deposits (which essentially are fees as well)
|
|
6224
|
+
pdaTokenType: types_1.TokenType.ATA,
|
|
6225
|
+
})
|
|
6226
|
+
},
|
|
6227
|
+
// Claim fees and shrink position
|
|
6228
|
+
{
|
|
6229
|
+
label: 'claim_fees_using_rbl',
|
|
6230
|
+
class: 'withdrawFn',
|
|
6231
|
+
instruction: yield _withdrawFn({
|
|
6232
|
+
position,
|
|
6233
|
+
minBinId,
|
|
6234
|
+
maxBinId,
|
|
6235
|
+
shouldClaimReward: false,
|
|
6236
|
+
// We shrink position at claim fees where we have zero deposits on given min ~ max binId
|
|
6237
|
+
// at this point
|
|
6238
|
+
shrinkMode: types_3.ShrinkMode.Default,
|
|
6239
|
+
shouldClaimFee: true,
|
|
6240
|
+
// Note (continuation):
|
|
6241
|
+
// That is why we split it to 2 ix, because we want fees to be transfered to STA if
|
|
6242
|
+
// compound is off
|
|
6243
|
+
pdaTokenType: types_1.TokenType.STA,
|
|
6244
|
+
})
|
|
6245
|
+
}
|
|
6246
|
+
];
|
|
6247
|
+
});
|
|
6248
|
+
const depositFn = (_a) => __awaiter(this, [_a], void 0, function* ({ position, maxDepositXAmount, maxDepositYAmount, pdaTokenType, addLiquidityParams, origin, }) {
|
|
6249
|
+
return {
|
|
6250
|
+
label: 'deposit_using_rbl',
|
|
6251
|
+
class: origin,
|
|
6252
|
+
instruction: yield _rebalanceLiquidityAutomation({
|
|
6253
|
+
position,
|
|
6254
|
+
maxDepositXAmount,
|
|
6255
|
+
maxDepositYAmount,
|
|
6256
|
+
pdaTokenType,
|
|
6257
|
+
shrinkMode: types_3.ShrinkMode.NoShrinkBoth,
|
|
6258
|
+
shouldClaimFee: false,
|
|
6259
|
+
shouldClaimReward: false,
|
|
6260
|
+
removeLiquidityParams: [],
|
|
6261
|
+
addLiquidityParams,
|
|
6262
|
+
})
|
|
6263
|
+
};
|
|
6264
|
+
});
|
|
6265
|
+
return {
|
|
6266
|
+
program,
|
|
6267
|
+
lbPair,
|
|
6268
|
+
lbPairState,
|
|
6269
|
+
activeId,
|
|
6270
|
+
userPosition,
|
|
6271
|
+
dlmmPool,
|
|
6272
|
+
userPda,
|
|
6273
|
+
lowerBinId,
|
|
6274
|
+
upperBinId,
|
|
6275
|
+
tokenXMint,
|
|
6276
|
+
tokenYMint,
|
|
6277
|
+
tokenXProgram,
|
|
6278
|
+
tokenYProgram,
|
|
6279
|
+
remainingAccountsInfo,
|
|
6280
|
+
rewardInfos,
|
|
6281
|
+
estimatedTotalX,
|
|
6282
|
+
estimatedTotalY,
|
|
6283
|
+
withdrawFn,
|
|
6284
|
+
depositFn,
|
|
6285
|
+
};
|
|
6286
|
+
});
|
|
6287
|
+
}
|
|
5912
6288
|
}
|
|
5913
6289
|
exports.Transactions = Transactions;
|
|
5914
6290
|
exports.txgen = Transactions.getInstance();
|