@hawksightco/hawk-sdk 1.3.219 → 1.3.221
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 +739 -368
- 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;
|
|
@@ -1616,7 +1620,6 @@ class Transactions {
|
|
|
1616
1620
|
});
|
|
1617
1621
|
}
|
|
1618
1622
|
/**
|
|
1619
|
-
* @deprecated Use rebalanceLargerPositionAutomation instead
|
|
1620
1623
|
*
|
|
1621
1624
|
* @param connection The Solana web3 connection object for blockchain interactions.
|
|
1622
1625
|
* @param params Parameters required for rebalance
|
|
@@ -1681,7 +1684,6 @@ class Transactions {
|
|
|
1681
1684
|
});
|
|
1682
1685
|
}
|
|
1683
1686
|
/**
|
|
1684
|
-
* @deprecated Use rebalanceLargerPositionAutomation instaed
|
|
1685
1687
|
*
|
|
1686
1688
|
* @param connection The Solana web3 connection object for blockchain interactions.
|
|
1687
1689
|
* @param params Parameters required for rebalance
|
|
@@ -1984,35 +1986,7 @@ class Transactions {
|
|
|
1984
1986
|
}
|
|
1985
1987
|
});
|
|
1986
1988
|
}
|
|
1987
|
-
|
|
1988
|
-
* Rebalance a Meteora DLMM position with support for larger positions.
|
|
1989
|
-
*
|
|
1990
|
-
* This is a backwards-compatible implementation of the rebalance operation that handles
|
|
1991
|
-
* positions of any size. The method automatically splits operations into 2-dimensional
|
|
1992
|
-
* instruction arrays (where each dimension represents a transaction) when dealing with
|
|
1993
|
-
* large bin arrays that would exceed single transaction limits.
|
|
1994
|
-
*
|
|
1995
|
-
* This rebalance method performs the following steps:
|
|
1996
|
-
* 1. Removes liquidity from the current position (supports larger bin ranges, not limited to 70-bin chunks, max 149-bin)
|
|
1997
|
-
* 2. Claims fees from the current position
|
|
1998
|
-
* 3. Claims rewards from the current position (if any)
|
|
1999
|
-
* 4. Closes the current position
|
|
2000
|
-
* 5. Initializes a new position
|
|
2001
|
-
* 6. Extends the new position length if needed (for larger positions)
|
|
2002
|
-
* 7. Adds liquidity to the new position using deposit automation
|
|
2003
|
-
*
|
|
2004
|
-
* Key benefits:
|
|
2005
|
-
* - Backwards-compatible with existing rebalance implementations
|
|
2006
|
-
* - Supports large bin arrays through automatic transaction splitting into multiple transactions
|
|
2007
|
-
* - Does NOT depend on Meteora SDK
|
|
2008
|
-
* - Handles positions that exceed single transaction size limits
|
|
2009
|
-
* - Uses increasePositionLengthAutomation for larger new positions
|
|
2010
|
-
*
|
|
2011
|
-
* @param connection The Solana web3 connection object for blockchain interactions.
|
|
2012
|
-
* @param params Parameters required for rebalance (same as MeteoraRebalance)
|
|
2013
|
-
* @returns Array of TransactionMetadataResponse for each transaction
|
|
2014
|
-
*/
|
|
2015
|
-
rebalanceLargerPositionAutomation(_a) {
|
|
1989
|
+
rebalanceSmallPositionAutomation(_a) {
|
|
2016
1990
|
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
2017
1991
|
// Current theoretical limit (149-bin)
|
|
2018
1992
|
const MAX_POSITION_WIDTH = 149;
|
|
@@ -2268,6 +2242,7 @@ class Transactions {
|
|
|
2268
2242
|
}
|
|
2269
2243
|
redepositForLargePositionAutomationIx(_a) {
|
|
2270
2244
|
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
2245
|
+
var _b;
|
|
2271
2246
|
try {
|
|
2272
2247
|
const program = yield meteora_1.MeteoraDLMM.program(connection);
|
|
2273
2248
|
const positionData = yield program.account.positionV2.fetch(params.position);
|
|
@@ -2293,7 +2268,6 @@ class Transactions {
|
|
|
2293
2268
|
const remainingAccountsInfo = yield (0, functions_1.fetchRemainingAccountsInfo)(connection, tokenXMint, tokenYMint, tokenXProgram, tokenYProgram);
|
|
2294
2269
|
const transactions = [];
|
|
2295
2270
|
const withdrawIxs = [];
|
|
2296
|
-
const depositIxs = [];
|
|
2297
2271
|
const removeLiquidityIx = yield this.ix.meteoraDlmm.removeLiquidityByRange2Automation({
|
|
2298
2272
|
connection,
|
|
2299
2273
|
userWallet: params.userWallet,
|
|
@@ -2311,9 +2285,20 @@ class Transactions {
|
|
|
2311
2285
|
});
|
|
2312
2286
|
withdrawIxs.push(removeLiquidityIx);
|
|
2313
2287
|
const { binId: activeId } = yield dlmmPool.dlmm.getActiveBin();
|
|
2314
|
-
|
|
2315
|
-
const
|
|
2316
|
-
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({
|
|
2317
2302
|
connection,
|
|
2318
2303
|
userWallet: params.userWallet,
|
|
2319
2304
|
lbPair,
|
|
@@ -2322,38 +2307,28 @@ class Transactions {
|
|
|
2322
2307
|
tokenYMint,
|
|
2323
2308
|
tokenXProgram,
|
|
2324
2309
|
tokenYProgram,
|
|
2310
|
+
lowerBinId: currentLowerBinId,
|
|
2311
|
+
upperBinId: currentUpperBinId,
|
|
2312
|
+
strategyType,
|
|
2325
2313
|
activeId,
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
},
|
|
2330
|
-
strategyType: types_3.StrategyTypeMap[params.distribution],
|
|
2331
|
-
pdaTokenType: types_1.TokenType.ATA,
|
|
2332
|
-
remainingAccountsInfo,
|
|
2333
|
-
positionIsSigner: false,
|
|
2314
|
+
totalXAmount,
|
|
2315
|
+
totalYAmount,
|
|
2316
|
+
skipInitBinArrays: true,
|
|
2334
2317
|
});
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
}
|
|
2345
|
-
else {
|
|
2346
|
-
transactions.push(yield (0, functions_1.createTransactionMeta)({
|
|
2347
|
-
payer: params.userWallet,
|
|
2348
|
-
description: 'Automation IX: Meteora Redeposit TX1: Remove liquidity',
|
|
2349
|
-
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
2350
|
-
mainInstructions: withdrawIxs,
|
|
2351
|
-
}));
|
|
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++) {
|
|
2352
2327
|
transactions.push(yield (0, functions_1.createTransactionMeta)({
|
|
2353
2328
|
payer: params.userWallet,
|
|
2354
|
-
description:
|
|
2329
|
+
description: `Automation IX: Meteora Redeposit TX${i + 2}: Add liquidity`,
|
|
2355
2330
|
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
2356
|
-
mainInstructions:
|
|
2331
|
+
mainInstructions: depositIxArrays[i],
|
|
2357
2332
|
}));
|
|
2358
2333
|
}
|
|
2359
2334
|
return transactions;
|
|
@@ -2367,10 +2342,213 @@ class Transactions {
|
|
|
2367
2342
|
}
|
|
2368
2343
|
});
|
|
2369
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
|
+
}
|
|
2370
2547
|
/**
|
|
2371
2548
|
* Rebalance a large Meteora DLMM position (up to 1400 bins) using automation instructions.
|
|
2372
2549
|
*
|
|
2373
|
-
* For ≤
|
|
2550
|
+
* For ≤70 bins, delegates to rebalanceSmallerPositionAutomation.
|
|
2551
|
+
* For 71-149 bins, delegates to rebalancePhasedPositionAutomation.
|
|
2374
2552
|
* For ≥150 bins, uses a chunked multi-TX approach:
|
|
2375
2553
|
* Phase 1: Chunked withdraw from current position
|
|
2376
2554
|
* Phase 2: Initialize new large position
|
|
@@ -2394,9 +2572,13 @@ class Transactions {
|
|
|
2394
2572
|
const currentLowerBinId = positionData.lowerBinId;
|
|
2395
2573
|
const currentUpperBinId = positionData.upperBinId;
|
|
2396
2574
|
const currentBinCount = currentUpperBinId - currentLowerBinId + 1;
|
|
2397
|
-
// 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)
|
|
2398
2580
|
if (currentBinCount <= 149 && params.width <= 149) {
|
|
2399
|
-
return this.
|
|
2581
|
+
return this.rebalancePhasedPositionAutomation({ connection, params, fetch: undefined });
|
|
2400
2582
|
}
|
|
2401
2583
|
// ≥150 bins: multi-TX chunked approach
|
|
2402
2584
|
const dlmmPool = yield meteora_1.MeteoraDLMM.create(connection, lbPair, this.ix);
|
|
@@ -2544,7 +2726,7 @@ class Transactions {
|
|
|
2544
2726
|
tokenXMint, tokenYMint, tokenXProgram, tokenYProgram,
|
|
2545
2727
|
activeId: params.activeId,
|
|
2546
2728
|
pdaTokenType: types_1.TokenType.ATA,
|
|
2547
|
-
maxActiveBinSlippage:
|
|
2729
|
+
maxActiveBinSlippage: 0,
|
|
2548
2730
|
shouldClaimFee: false,
|
|
2549
2731
|
shouldClaimReward: false,
|
|
2550
2732
|
minWithdrawXAmount: new bn_js_1.default(0),
|
|
@@ -2561,304 +2743,121 @@ class Transactions {
|
|
|
2561
2743
|
});
|
|
2562
2744
|
}
|
|
2563
2745
|
/**
|
|
2564
|
-
*
|
|
2565
|
-
*
|
|
2566
|
-
*
|
|
2567
|
-
*
|
|
2568
|
-
* Remove TXs: rebalanceLiquidityAutomation with removeLiquidityParams (~75 bins/TX)
|
|
2569
|
-
* Deposit TXs: rebalanceLiquidityAutomation with addLiquidityParams (~75 bins/TX)
|
|
2570
|
-
*
|
|
2571
|
-
* Meteora's rebalance_liquidity handles position extension and bin array
|
|
2572
|
-
* initialization internally — no separate IXs needed.
|
|
2573
|
-
*
|
|
2574
|
-
* Key differences from rebalanceLargePositionAutomation:
|
|
2575
|
-
* - Same position (no close/create, no new position keypair needed)
|
|
2576
|
-
* - 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
|
|
2577
2750
|
*/
|
|
2578
|
-
|
|
2579
|
-
return __awaiter(this,
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
const
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
const
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
console.log(`Total chunk per tx (withdraw): ${WITHDRAW_CHUNK_SIZE} bins, total chunk per tx (deposit): ${DEPOSIT_CHUNK_SIZE} bins`);
|
|
2614
|
-
console.log(`old lower bin id: ${oldLowerBinId}, old upper bin id: ${oldUpperBinId}, old width: ${oldUpperBinId - oldLowerBinId + 1}`);
|
|
2615
|
-
console.log(`new lower bin id: ${newLowerBinId}, new upper bin id: ${newUpperBinId}, new width: ${newUpperBinId - newLowerBinId + 1}`);
|
|
2616
|
-
const lbPairState = dlmmPool.dlmm.lbPair;
|
|
2617
|
-
const tokenXMint = lbPairState.tokenXMint;
|
|
2618
|
-
const tokenYMint = lbPairState.tokenYMint;
|
|
2619
|
-
const tokenProgramMap = yield (0, functions_1.getTokenProgramMapForMints)(connection, [tokenXMint, tokenYMint]);
|
|
2620
|
-
const tokenXProgram = tokenProgramMap[tokenXMint.toString()];
|
|
2621
|
-
const tokenYProgram = tokenProgramMap[tokenYMint.toString()];
|
|
2622
|
-
// Fetch transfer hook accounts for token X and Y (Token2022 support)
|
|
2623
|
-
const baseRemainingAccountsInfo = yield (0, functions_1.fetchRemainingAccountsInfo)(connection, tokenXMint, tokenYMint, tokenXProgram, tokenYProgram);
|
|
2624
|
-
// Extract reward infos for claiming rewards during rebalance
|
|
2625
|
-
const rewardInfos = [];
|
|
2626
|
-
const rewardInfosFromPair = lbPairState.rewardInfos;
|
|
2627
|
-
if (rewardInfosFromPair && rewardInfosFromPair.length > 0) {
|
|
2628
|
-
// Collect reward mints that are active (non-default)
|
|
2629
|
-
const activeRewardMints = [];
|
|
2630
|
-
for (const reward of rewardInfosFromPair) {
|
|
2631
|
-
if (!reward.mint.equals(web3.PublicKey.default)) {
|
|
2632
|
-
activeRewardMints.push(reward.mint);
|
|
2633
|
-
}
|
|
2634
|
-
}
|
|
2635
|
-
// Get token programs for reward mints
|
|
2636
|
-
if (activeRewardMints.length > 0) {
|
|
2637
|
-
const rewardTokenProgramMap = yield (0, functions_1.getTokenProgramMapForMints)(connection, activeRewardMints);
|
|
2638
|
-
for (let i = 0; i < rewardInfosFromPair.length; i++) {
|
|
2639
|
-
const reward = rewardInfosFromPair[i];
|
|
2640
|
-
if (!reward.mint.equals(web3.PublicKey.default)) {
|
|
2641
|
-
rewardInfos.push({
|
|
2642
|
-
rewardIndex: i,
|
|
2643
|
-
rewardVault: reward.vault,
|
|
2644
|
-
rewardMint: reward.mint,
|
|
2645
|
-
rewardTokenProgram: rewardTokenProgramMap[reward.mint.toString()],
|
|
2646
|
-
});
|
|
2647
|
-
}
|
|
2648
|
-
}
|
|
2649
|
-
}
|
|
2650
|
-
}
|
|
2651
|
-
console.log(`[Pipeline] Found ${rewardInfos.length} active reward(s) for this pool`);
|
|
2652
|
-
// Build combined remainingAccountsInfo with transfer hooks for X, Y, and rewards
|
|
2653
|
-
// The slices describe: TransferHookX, TransferHookY, TransferHookMultiReward(index) for each reward
|
|
2654
|
-
// IMPORTANT: Always include all slices, even with length=0, so Meteora knows the account structure
|
|
2655
|
-
const combinedSlices = [];
|
|
2656
|
-
const combinedAccounts = [];
|
|
2657
|
-
// Always add TransferHookX and TransferHookY slices (even if length=0)
|
|
2658
|
-
const xSlice = baseRemainingAccountsInfo.slices.find(s => s.accountsType === types_1.RemainingAccountsType.TransferHookX);
|
|
2659
|
-
const ySlice = baseRemainingAccountsInfo.slices.find(s => s.accountsType === types_1.RemainingAccountsType.TransferHookY);
|
|
2660
|
-
combinedSlices.push({ accountsType: types_1.RemainingAccountsType.TransferHookX, length: (_b = xSlice === null || xSlice === void 0 ? void 0 : xSlice.length) !== null && _b !== void 0 ? _b : 0 });
|
|
2661
|
-
combinedSlices.push({ accountsType: types_1.RemainingAccountsType.TransferHookY, length: (_c = ySlice === null || ySlice === void 0 ? void 0 : ySlice.length) !== null && _c !== void 0 ? _c : 0 });
|
|
2662
|
-
// Add transfer hook accounts from base (for X and Y)
|
|
2663
|
-
combinedAccounts.push(...baseRemainingAccountsInfo.accounts);
|
|
2664
|
-
// Fetch transfer hook accounts for each reward and add to combined info
|
|
2665
|
-
// IMPORTANT: Always add TransferHookMultiReward slice for each reward, even if length=0
|
|
2666
|
-
// This tells Meteora about the reward position in the remaining accounts
|
|
2667
|
-
for (const reward of rewardInfos) {
|
|
2668
|
-
const rewardTransferHookInfo = yield (0, functions_1.fetchRemainingAccountsInfoForReward)(connection, reward.rewardMint, reward.rewardTokenProgram);
|
|
2669
|
-
// Always add TransferHookMultiReward slice (even if empty length for standard SPL tokens)
|
|
2670
|
-
const transferHookLength = rewardTransferHookInfo.slices.length > 0
|
|
2671
|
-
? rewardTransferHookInfo.slices[0].length
|
|
2672
|
-
: 0;
|
|
2673
|
-
combinedSlices.push({
|
|
2674
|
-
accountsType: types_1.RemainingAccountsType.TransferHookMultiReward,
|
|
2675
|
-
length: transferHookLength,
|
|
2676
|
-
multiRewardIndex: reward.rewardIndex,
|
|
2677
|
-
});
|
|
2678
|
-
combinedAccounts.push(...rewardTransferHookInfo.accounts);
|
|
2679
|
-
}
|
|
2680
|
-
const remainingAccountsInfo = {
|
|
2681
|
-
slices: combinedSlices,
|
|
2682
|
-
accounts: combinedAccounts,
|
|
2683
|
-
};
|
|
2684
|
-
const distributionToStrategyType = {
|
|
2685
|
-
'SPOT': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-IMBALANCED': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-ONE-SIDE': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-BALANCED': liquidityStrategy_1.StrategyType.SPOT,
|
|
2686
|
-
'CURVE': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-IMBALANCED': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-ONE-SIDE': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-BALANCED': liquidityStrategy_1.StrategyType.CURVE,
|
|
2687
|
-
'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,
|
|
2688
|
-
};
|
|
2689
|
-
const strategyType = (_d = distributionToStrategyType[params.distribution]) !== null && _d !== void 0 ? _d : liquidityStrategy_1.StrategyType.SPOT;
|
|
2690
|
-
const binStep = new bn_js_1.default(dlmmPool.dlmm.lbPair.binStep);
|
|
2691
|
-
// Build new-position deposit chunks
|
|
2692
|
-
const minDeltaId = new bn_js_1.default(newLowerBinId - activeId);
|
|
2693
|
-
const maxDeltaId = new bn_js_1.default(newUpperBinId - activeId);
|
|
2694
|
-
const favorXInActiveId = totalYAmount.isZero() && !totalXAmount.isZero();
|
|
2695
|
-
const strategyParams = (0, liquidityStrategy_1.buildStrategyParameters)(strategyType, totalXAmount, totalYAmount, minDeltaId, maxDeltaId, binStep, new bn_js_1.default(activeId), favorXInActiveId);
|
|
2696
|
-
// =====================================================================
|
|
2697
|
-
// Pipeline algorithm: pack withdraw + deposit work into fewer TXs.
|
|
2698
|
-
//
|
|
2699
|
-
// Given W old bins to withdraw and D new bins to deposit:
|
|
2700
|
-
// Phase 1: Pure withdraw TXs — all withdraw chunks except the last
|
|
2701
|
-
// Phase 2: Pivot TX — last withdraw chunk + extend + initBinArrays
|
|
2702
|
-
// + first deposit (if small enough to fit without CU issues)
|
|
2703
|
-
// Phase 3: Pure deposit TXs — remaining deposit bins by CHUNK_SIZE
|
|
2704
|
-
//
|
|
2705
|
-
// When the total extension is large or the deposit range spans many
|
|
2706
|
-
// bin arrays, the transition IXs (extend + initBinArrays) are placed
|
|
2707
|
-
// in a separate TX to avoid exceeding compute unit limits.
|
|
2708
|
-
// =====================================================================
|
|
2709
|
-
const txInstructions = [];
|
|
2710
|
-
// Helper: build on-chain deposit param from a ChunkedDepositParameters
|
|
2711
|
-
const buildDepositParam = (chunk) => {
|
|
2712
|
-
const p = (0, liquidityStrategy_1.buildBitFlagAndNegateStrategyParameters)(chunk.params.x0, chunk.params.y0, chunk.params.deltaX, chunk.params.deltaY);
|
|
2713
|
-
return {
|
|
2714
|
-
minDeltaId: chunk.minDeltaId.toNumber(),
|
|
2715
|
-
maxDeltaId: chunk.maxDeltaId.toNumber(),
|
|
2716
|
-
x0: p.x0, y0: p.y0, deltaX: p.deltaX, deltaY: p.deltaY,
|
|
2717
|
-
bitFlag: p.bitFlag, favorXInActiveId,
|
|
2718
|
-
};
|
|
2719
|
-
};
|
|
2720
|
-
// Helper: build withdraw IX
|
|
2721
|
-
const buildWithdrawIx = (lower, upper) => __awaiter(this, void 0, void 0, function* () {
|
|
2722
|
-
const binArrayOverrides = this.ix.pda.meteora.deriveBinArrays(params.lbPair, lower, upper);
|
|
2723
|
-
return this.ix.meteoraDlmm.rebalanceLiquidityAutomation({
|
|
2724
|
-
connection,
|
|
2725
|
-
userWallet: params.userWallet,
|
|
2726
|
-
position: params.position,
|
|
2727
|
-
lbPair: params.lbPair,
|
|
2728
|
-
tokenXMint, tokenYMint, tokenXProgram, tokenYProgram,
|
|
2729
|
-
activeId,
|
|
2730
|
-
pdaTokenType: types_1.TokenType.ATA,
|
|
2731
|
-
maxActiveBinSlippage: 300,
|
|
2732
|
-
shouldClaimFee: true,
|
|
2733
|
-
shouldClaimReward: rewardInfos.length > 0,
|
|
2734
|
-
minWithdrawXAmount: new bn_js_1.default(0),
|
|
2735
|
-
maxDepositXAmount: new bn_js_1.default(0),
|
|
2736
|
-
minWithdrawYAmount: new bn_js_1.default(0),
|
|
2737
|
-
maxDepositYAmount: new bn_js_1.default(0),
|
|
2738
|
-
removeLiquidityParams: [{ minBinId: lower, maxBinId: upper, bps: 10000 }],
|
|
2739
|
-
addLiquidityParams: [],
|
|
2740
|
-
binArrayOverrides,
|
|
2741
|
-
shrinkMode: types_3.ShrinkMode.Default,
|
|
2742
|
-
rewardInfos,
|
|
2743
|
-
remainingAccountsInfo,
|
|
2744
|
-
});
|
|
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),
|
|
2745
2786
|
});
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
lbPair: params.lbPair,
|
|
2760
|
-
tokenXMint, tokenYMint, tokenXProgram, tokenYProgram,
|
|
2761
|
-
activeId,
|
|
2762
|
-
pdaTokenType: types_1.TokenType.ATA,
|
|
2763
|
-
maxActiveBinSlippage: 300,
|
|
2764
|
-
shouldClaimFee: removeLiquidityParams ? true : false,
|
|
2765
|
-
shouldClaimReward: !!removeLiquidityParams && rewardInfos.length > 0,
|
|
2766
|
-
minWithdrawXAmount: new bn_js_1.default(0),
|
|
2767
|
-
maxDepositXAmount: chunk.maxAmountX,
|
|
2768
|
-
minWithdrawYAmount: new bn_js_1.default(0),
|
|
2769
|
-
maxDepositYAmount: chunk.maxAmountY,
|
|
2770
|
-
removeLiquidityParams: removeLiquidityParams ? [{ minBinId: removeLiquidityParams.minBinId, maxBinId: removeLiquidityParams.maxBinId, bps: 10000 }] : [],
|
|
2771
|
-
addLiquidityParams: [buildDepositParam(chunk)],
|
|
2772
|
-
binArrayOverrides,
|
|
2773
|
-
shrinkMode: removeLiquidityParams ? types_3.ShrinkMode.Default : types_3.ShrinkMode.NoShrinkBoth,
|
|
2774
|
-
rewardInfos: removeLiquidityParams ? rewardInfos : undefined,
|
|
2775
|
-
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),
|
|
2776
2800
|
});
|
|
2777
|
-
|
|
2778
|
-
// Build transition IXs: increasePositionLength + initBinArrays.
|
|
2779
|
-
// Bin arrays must be pre-initialized before rebalanceLiquidity deposits.
|
|
2780
|
-
const increasePositionLengthIxs = [];
|
|
2781
|
-
// Generate increasePositionLength IXs to extend position (assumption at the moment, I expect calculation to be wrong)
|
|
2782
|
-
let currentLower = newLowerBinId + WITHDRAW_CHUNK_SIZE;
|
|
2783
|
-
while (currentLower < newUpperBinId) {
|
|
2784
|
-
const currentWidth = newUpperBinId - currentLower + 1;
|
|
2785
|
-
const binsToAdd = Math.min(91, currentWidth);
|
|
2786
|
-
console.log('Increasing position length by', binsToAdd, 'bins at lower', currentLower);
|
|
2787
|
-
increasePositionLengthIxs.push(yield this.ix.meteoraDlmm.increasePositionLengthAutomation({
|
|
2788
|
-
connection,
|
|
2789
|
-
userWallet: params.userWallet,
|
|
2790
|
-
lbPair: params.lbPair,
|
|
2791
|
-
position: params.position,
|
|
2792
|
-
lengthToAdd: binsToAdd,
|
|
2793
|
-
side: types_1.MeteoraPositionSide.Upper,
|
|
2794
|
-
}));
|
|
2795
|
-
currentLower = currentLower + binsToAdd;
|
|
2796
|
-
}
|
|
2797
|
-
// Build all deposit chunks upfront
|
|
2798
|
-
const depositChunks = (0, liquidityStrategy_1.chunkDepositParametersWithInitialSize)(strategyParams, minDeltaId, maxDeltaId, new bn_js_1.default(activeId), binStep, DEPOSIT_CHUNK_INITIAL_SIZE, DEPOSIT_CHUNK_SIZE, favorXInActiveId);
|
|
2799
|
-
// Build old-position withdraw chunks, reserving PIVOT_RESERVE bins for pivot TX
|
|
2800
|
-
const oldWidth = oldUpperBinId - oldLowerBinId + 1;
|
|
2801
|
-
const oldChunks = [];
|
|
2802
|
-
let current = oldLowerBinId;
|
|
2803
|
-
// Build Phase 1 withdraw chunks (before pivot)
|
|
2804
|
-
while (current < oldUpperBinId) {
|
|
2805
|
-
const upper = Math.min(WITHDRAW_CHUNK_SIZE - 1, oldUpperBinId - current) + current;
|
|
2806
|
-
console.log(`Generating withdraw chunk: [${current}, ${upper}, ${upper - current + 1} bins]`);
|
|
2807
|
-
oldChunks.push({ lower: current, upper });
|
|
2808
|
-
current = upper + 1;
|
|
2801
|
+
offset += 48;
|
|
2809
2802
|
}
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
const
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
// Phase 3: Remaining deposit TXs
|
|
2840
|
-
// - Second deposit (i=1): includes increasePositionLengthIxs if needed
|
|
2841
|
-
// - Subsequent deposits (i=2+): pure deposit
|
|
2842
|
-
// =====================================================================
|
|
2843
|
-
for (let i = 1; i < depositChunks.length; i++) {
|
|
2844
|
-
const chunk = depositChunks[i];
|
|
2845
|
-
txInstructions.push([yield buildDepositIx(chunk)]);
|
|
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
|
+
});
|
|
2846
2832
|
}
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
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
|
+
}
|
|
2856
2853
|
}
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
catch (error) {
|
|
2860
|
-
throw error;
|
|
2854
|
+
totalFeeX += feeX;
|
|
2855
|
+
totalFeeY += feeY;
|
|
2861
2856
|
}
|
|
2857
|
+
return {
|
|
2858
|
+
feeX: new bn_js_1.default(totalFeeX.toString()),
|
|
2859
|
+
feeY: new bn_js_1.default(totalFeeY.toString()),
|
|
2860
|
+
};
|
|
2862
2861
|
});
|
|
2863
2862
|
}
|
|
2864
2863
|
/**
|
|
@@ -2869,7 +2868,8 @@ class Transactions {
|
|
|
2869
2868
|
_meteoraDepositToLargePositionAutomation(params) {
|
|
2870
2869
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2871
2870
|
const MAX_BINS_PER_TX = 149;
|
|
2872
|
-
const { connection, userWallet, lbPair, position, tokenXMint, tokenYMint, tokenXProgram, tokenYProgram, lowerBinId, upperBinId, strategyType, activeId, totalXAmount, totalYAmount, skipInitBinArrays, } = 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;
|
|
2873
2873
|
const dlmmPool = yield meteora_1.MeteoraDLMM.create(connection, lbPair, this.ix);
|
|
2874
2874
|
const binStep = new bn_js_1.default(dlmmPool.dlmm.lbPair.binStep);
|
|
2875
2875
|
// Calculate delta IDs relative to active bin for entire position
|
|
@@ -2916,7 +2916,7 @@ class Transactions {
|
|
|
2916
2916
|
tokenYProgram,
|
|
2917
2917
|
activeId,
|
|
2918
2918
|
pdaTokenType: types_1.TokenType.ATA,
|
|
2919
|
-
maxActiveBinSlippage
|
|
2919
|
+
maxActiveBinSlippage,
|
|
2920
2920
|
shouldClaimFee: false,
|
|
2921
2921
|
shouldClaimReward: false,
|
|
2922
2922
|
minWithdrawXAmount: new bn_js_1.default(0),
|
|
@@ -3115,7 +3115,7 @@ class Transactions {
|
|
|
3115
3115
|
tokenYProgram,
|
|
3116
3116
|
activeId,
|
|
3117
3117
|
pdaTokenType: types_1.TokenType.ATA,
|
|
3118
|
-
maxActiveBinSlippage:
|
|
3118
|
+
maxActiveBinSlippage: 0,
|
|
3119
3119
|
shouldClaimFee: false,
|
|
3120
3120
|
shouldClaimReward: false,
|
|
3121
3121
|
minWithdrawXAmount: new bn_js_1.default(0),
|
|
@@ -3861,7 +3861,7 @@ class Transactions {
|
|
|
3861
3861
|
activeId,
|
|
3862
3862
|
pdaTokenType,
|
|
3863
3863
|
// RebalanceLiquidityParams
|
|
3864
|
-
maxActiveBinSlippage:
|
|
3864
|
+
maxActiveBinSlippage: 0,
|
|
3865
3865
|
shouldClaimFee: false,
|
|
3866
3866
|
shouldClaimReward: false,
|
|
3867
3867
|
minWithdrawXAmount: new bn_js_1.default(0),
|
|
@@ -5914,6 +5914,377 @@ class Transactions {
|
|
|
5914
5914
|
});
|
|
5915
5915
|
});
|
|
5916
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
|
+
}
|
|
5917
6288
|
}
|
|
5918
6289
|
exports.Transactions = Transactions;
|
|
5919
6290
|
exports.txgen = Transactions.getInstance();
|