@hawksightco/hawk-sdk 1.3.200 → 1.3.202
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 +218 -18
- package/dist/src/classes/Transactions.d.ts.map +1 -1
- package/dist/src/classes/Transactions.js +1695 -263
- package/dist/src/classes/TxGenerator.d.ts +39 -7
- package/dist/src/classes/TxGenerator.d.ts.map +1 -1
- package/dist/src/classes/TxGenerator.js +189 -6
- package/dist/src/classes/TxGeneratorAutomations.d.ts +79 -1
- package/dist/src/classes/TxGeneratorAutomations.d.ts.map +1 -1
- package/dist/src/classes/TxGeneratorAutomations.js +288 -0
- package/dist/src/functions.js +1 -1
- package/dist/src/hsToMeteora.js +6 -6
- package/dist/src/ixGenerator/MeteoraDlmmIxGenerator.d.ts +21 -2
- package/dist/src/ixGenerator/MeteoraDlmmIxGenerator.d.ts.map +1 -1
- package/dist/src/ixGenerator/MeteoraDlmmIxGenerator.js +147 -10
- package/dist/src/meteora/liquidityStrategy.d.ts +31 -0
- package/dist/src/meteora/liquidityStrategy.d.ts.map +1 -1
- package/dist/src/meteora/liquidityStrategy.js +76 -0
- package/dist/src/types.d.ts +100 -40
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +20 -1
- package/package.json +2 -2
|
@@ -200,6 +200,8 @@ class Transactions {
|
|
|
200
200
|
/**
|
|
201
201
|
* Creates meteora instruction that creates new position and deposit.
|
|
202
202
|
*
|
|
203
|
+
* @deprecated Use meteoraCreatePositionAndDepositToLargePosition instead.
|
|
204
|
+
*
|
|
203
205
|
* @param connection The Solana web3 connection object for blockchain interactions.
|
|
204
206
|
* @param payer The public key of the payer for transaction fees.
|
|
205
207
|
* @param params Parameters required
|
|
@@ -237,6 +239,8 @@ class Transactions {
|
|
|
237
239
|
* Creates meteora instruction that creates new position and deposit
|
|
238
240
|
* - Supports larger positions
|
|
239
241
|
*
|
|
242
|
+
* @deprecated Use meteoraCreatePositionAndDepositToLargePosition instead.
|
|
243
|
+
*
|
|
240
244
|
* @param connection The Solana web3 connection object for blockchain interactions.
|
|
241
245
|
* @param payer The public key of the payer for transaction fees.
|
|
242
246
|
* @param params Parameters required
|
|
@@ -420,6 +424,8 @@ class Transactions {
|
|
|
420
424
|
/**
|
|
421
425
|
* Creates meteora instruction that deposits to position.
|
|
422
426
|
*
|
|
427
|
+
* @deprecated Use meteoraDepositToLargePosition instead
|
|
428
|
+
*
|
|
423
429
|
* @param connection The Solana web3 connection object for blockchain interactions.
|
|
424
430
|
* @param payer The public key of the payer for transaction fees.
|
|
425
431
|
* @param params Parameters required
|
|
@@ -467,6 +473,8 @@ class Transactions {
|
|
|
467
473
|
/**
|
|
468
474
|
* Creates meteora instruction that deposits to position.
|
|
469
475
|
*
|
|
476
|
+
* @deprecated Use meteoraDepositToLargePosition instead
|
|
477
|
+
*
|
|
470
478
|
* @param connection The Solana web3 connection object for blockchain interactions.
|
|
471
479
|
* @param payer The public key of the payer for transaction fees.
|
|
472
480
|
* @param params Parameters required
|
|
@@ -510,6 +518,8 @@ class Transactions {
|
|
|
510
518
|
/**
|
|
511
519
|
* Creates meteora instruction withdraws from a position.
|
|
512
520
|
*
|
|
521
|
+
* @deprecated Use meteoraWithdrawLargePosition instead
|
|
522
|
+
*
|
|
513
523
|
* @param connection The Solana web3 connection object for blockchain interactions.
|
|
514
524
|
* @param payer The public key of the payer for transaction fees.
|
|
515
525
|
* @param params Parameters required
|
|
@@ -670,6 +680,8 @@ class Transactions {
|
|
|
670
680
|
/**
|
|
671
681
|
* Withdraws liquidity from a Meteora DLMM position using simple instructions.
|
|
672
682
|
*
|
|
683
|
+
* @deprecated Use meteoraWithdrawLargePosition instead
|
|
684
|
+
*
|
|
673
685
|
* Unlike meteoraWithdraw which uses Meteora SDK (that generates multiple claimFee ixs
|
|
674
686
|
* potentially bloating tx size), this method uses direct instructions:
|
|
675
687
|
* - removeLiquidityByRange2: Withdraw liquidity from the specified bin range
|
|
@@ -682,118 +694,219 @@ class Transactions {
|
|
|
682
694
|
* @returns A ResponseWithStatus containing TransactionMetadataResponse.
|
|
683
695
|
*/
|
|
684
696
|
meteoraWithdrawLargerPosition(_a) {
|
|
697
|
+
return __awaiter(this, arguments, void 0, function* ({ connection, params, fetch, }) {
|
|
698
|
+
const mainInstructions = yield this._meteoraWithdrawLargerPosition({ connection, params, fetch });
|
|
699
|
+
// Build description
|
|
700
|
+
let description;
|
|
701
|
+
if (params.shouldClaimAndClose) {
|
|
702
|
+
description = 'Full position withdrawal with claim and close from Meteora DLMM (larger position)';
|
|
703
|
+
}
|
|
704
|
+
else if (params.amountBps.eq(new bn_js_1.default(10000))) {
|
|
705
|
+
description = 'Full position withdrawal without close from Meteora DLMM (larger position)';
|
|
706
|
+
}
|
|
707
|
+
else {
|
|
708
|
+
description = 'Partial position withdrawal from Meteora DLMM (larger position)';
|
|
709
|
+
}
|
|
710
|
+
return (0, functions_1.createTransactionMeta)({
|
|
711
|
+
payer: params.userWallet,
|
|
712
|
+
description,
|
|
713
|
+
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
714
|
+
mainInstructions,
|
|
715
|
+
});
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Withdraw liquidity from a large Meteora DLMM position (up to 1400 bins).
|
|
720
|
+
*
|
|
721
|
+
* - For ≤149 bins: delegates to meteoraWithdrawLargerPosition (single tx)
|
|
722
|
+
* - For ≥150 bins: resolves pool data once, chunks into ≤149-bin ranges,
|
|
723
|
+
* calls _buildWithdrawInstructions per chunk. Only the last chunk sets
|
|
724
|
+
* shouldClaimAndClose (if requested).
|
|
725
|
+
*
|
|
726
|
+
* @param connection The Solana web3 connection object for blockchain interactions.
|
|
727
|
+
* @param params MeteoraWithdrawLargerPosition parameters
|
|
728
|
+
* @returns Array of TransactionMetadataResponse, one per chunk
|
|
729
|
+
*/
|
|
730
|
+
meteoraWithdrawLargePosition(_a) {
|
|
731
|
+
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
732
|
+
// Determine lbPair and bin range
|
|
733
|
+
let lbPair;
|
|
734
|
+
let lowerBinId;
|
|
735
|
+
let upperBinId;
|
|
736
|
+
if (params.fastGeneration !== undefined) {
|
|
737
|
+
lbPair = params.fastGeneration.pool;
|
|
738
|
+
lowerBinId = params.fastGeneration.lowerBinId;
|
|
739
|
+
upperBinId = params.fastGeneration.upperBinId;
|
|
740
|
+
}
|
|
741
|
+
else {
|
|
742
|
+
const program = yield meteora_1.MeteoraDLMM.program(connection);
|
|
743
|
+
const position = yield program.account.positionV2.fetch(params.position);
|
|
744
|
+
lbPair = position.lbPair;
|
|
745
|
+
lowerBinId = position.lowerBinId;
|
|
746
|
+
upperBinId = position.upperBinId;
|
|
747
|
+
}
|
|
748
|
+
const totalBins = upperBinId - lowerBinId + 1;
|
|
749
|
+
// ≤149 bins: single-tx path
|
|
750
|
+
if (totalBins <= 149) {
|
|
751
|
+
const result = yield this.meteoraWithdrawLargerPosition({
|
|
752
|
+
connection,
|
|
753
|
+
params,
|
|
754
|
+
fetch: undefined,
|
|
755
|
+
});
|
|
756
|
+
return [result];
|
|
757
|
+
}
|
|
758
|
+
// ≥150 bins: multi-tx chunked withdraw
|
|
759
|
+
const resolved = yield this._resolveWithdrawPoolData({ connection, lbPair });
|
|
760
|
+
const amountBps = params.shouldClaimAndClose ? new bn_js_1.default(10000) : params.amountBps;
|
|
761
|
+
// Chunk into ≤149-bin ranges
|
|
762
|
+
const CHUNK_SIZE = 149;
|
|
763
|
+
const chunks = [];
|
|
764
|
+
for (let binStart = lowerBinId; binStart <= upperBinId; binStart += CHUNK_SIZE) {
|
|
765
|
+
const chunkUpper = Math.min(binStart + CHUNK_SIZE - 1, upperBinId);
|
|
766
|
+
chunks.push({ chunkLower: binStart, chunkUpper });
|
|
767
|
+
}
|
|
768
|
+
const transactions = [];
|
|
769
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
770
|
+
const { chunkLower, chunkUpper } = chunks[i];
|
|
771
|
+
const isLastChunk = i === chunks.length - 1;
|
|
772
|
+
const instructions = yield this._buildWithdrawInstructions(Object.assign({ connection, userWallet: params.userWallet, position: params.position, lbPair, lowerBinId: chunkLower, upperBinId: chunkUpper, amountBps, shouldClaimAndClose: isLastChunk && params.shouldClaimAndClose, pdaTokenType: params.pdaTokenType }, resolved));
|
|
773
|
+
transactions.push(yield (0, functions_1.createTransactionMeta)({
|
|
774
|
+
payer: params.userWallet,
|
|
775
|
+
description: `Withdraw from large Meteora DLMM Position (chunk ${i + 1}/${chunks.length})`,
|
|
776
|
+
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
777
|
+
mainInstructions: instructions,
|
|
778
|
+
}));
|
|
779
|
+
}
|
|
780
|
+
return transactions;
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Withdraws liquidity from a Meteora DLMM position using simple instructions.
|
|
785
|
+
*
|
|
786
|
+
* Unlike meteoraWithdraw which uses Meteora SDK (that generates multiple claimFee ixs
|
|
787
|
+
* potentially bloating tx size), this method uses direct instructions:
|
|
788
|
+
* - removeLiquidityByRange2: Withdraw liquidity from the specified bin range
|
|
789
|
+
* - claimFee2: Single instruction to claim fees
|
|
790
|
+
* - claimReward2: N instructions (one per active reward)
|
|
791
|
+
* - closePositionIfEmpty: Closes position if shouldClaimAndClose is true
|
|
792
|
+
*
|
|
793
|
+
* @param connection The Solana web3 connection object for blockchain interactions.
|
|
794
|
+
* @param params Parameters required for withdrawal
|
|
795
|
+
* @returns A ResponseWithStatus containing TransactionMetadataResponse.
|
|
796
|
+
*/
|
|
797
|
+
_meteoraWithdrawLargerPosition(_a) {
|
|
685
798
|
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
686
|
-
const program = yield meteora_1.MeteoraDLMM.program(connection);
|
|
687
799
|
// Determine lbPair and bin range
|
|
688
800
|
let lbPair;
|
|
689
801
|
let lowerBinId;
|
|
690
802
|
let upperBinId;
|
|
691
803
|
if (params.fastGeneration !== undefined) {
|
|
692
|
-
// Use fastGeneration data
|
|
693
804
|
lbPair = params.fastGeneration.pool;
|
|
694
805
|
lowerBinId = params.fastGeneration.lowerBinId;
|
|
695
806
|
upperBinId = params.fastGeneration.upperBinId;
|
|
696
807
|
}
|
|
697
808
|
else {
|
|
698
|
-
|
|
809
|
+
const program = yield meteora_1.MeteoraDLMM.program(connection);
|
|
699
810
|
const position = yield program.account.positionV2.fetch(params.position);
|
|
700
811
|
lbPair = position.lbPair;
|
|
701
812
|
lowerBinId = position.lowerBinId;
|
|
702
813
|
upperBinId = position.upperBinId;
|
|
703
814
|
}
|
|
704
|
-
//
|
|
815
|
+
// Resolve pool data
|
|
816
|
+
const resolved = yield this._resolveWithdrawPoolData({ connection, lbPair });
|
|
817
|
+
return this._buildWithdrawInstructions(Object.assign({ connection, userWallet: params.userWallet, position: params.position, lbPair,
|
|
818
|
+
lowerBinId,
|
|
819
|
+
upperBinId, amountBps: params.shouldClaimAndClose ? new bn_js_1.default(10000) : params.amountBps, shouldClaimAndClose: params.shouldClaimAndClose, pdaTokenType: params.pdaTokenType }, resolved));
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Resolves pool data needed for withdraw instructions.
|
|
824
|
+
* Call once, then pass the result to _buildWithdrawInstructions for each chunk.
|
|
825
|
+
*/
|
|
826
|
+
_resolveWithdrawPoolData(_a) {
|
|
827
|
+
return __awaiter(this, arguments, void 0, function* ({ connection, lbPair }) {
|
|
828
|
+
// Create DLMM pool first — this also initializes MeteoraDLMM._program
|
|
829
|
+
// so we don't need a separate MeteoraDLMM.program() call (which fetches a dummy pool).
|
|
705
830
|
const dlmmPool = yield meteora_1.MeteoraDLMM.create(connection, lbPair, this.ix);
|
|
831
|
+
const program = dlmmPool.dlmm.program;
|
|
706
832
|
const lbPairState = dlmmPool.dlmm.lbPair;
|
|
707
833
|
const tokenXMint = lbPairState.tokenXMint;
|
|
708
834
|
const tokenYMint = lbPairState.tokenYMint;
|
|
709
|
-
// Get token programs for each mint
|
|
710
835
|
const tokenProgramMap = yield (0, functions_1.getTokenProgramMapForMints)(connection, [tokenXMint, tokenYMint]);
|
|
711
836
|
const tokenXProgram = tokenProgramMap[tokenXMint.toString()];
|
|
712
837
|
const tokenYProgram = tokenProgramMap[tokenYMint.toString()];
|
|
713
|
-
// Fetch remainingAccountsInfo for Token2022 transfer hooks (if any)
|
|
714
838
|
const remainingAccountsInfo = yield (0, functions_1.fetchRemainingAccountsInfo)(connection, tokenXMint, tokenYMint, tokenXProgram, tokenYProgram);
|
|
715
|
-
const
|
|
716
|
-
|
|
717
|
-
|
|
839
|
+
const lbPairInfo = yield program.account.lbPair.fetch(lbPair);
|
|
840
|
+
return { tokenXMint, tokenYMint, tokenXProgram, tokenYProgram, remainingAccountsInfo, lbPairInfo };
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* Builds withdraw instructions for a single bin range chunk.
|
|
845
|
+
* Requires pre-resolved pool data (from _resolveWithdrawPoolData).
|
|
846
|
+
*/
|
|
847
|
+
_buildWithdrawInstructions(params) {
|
|
848
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
718
849
|
const mainInstructions = [];
|
|
719
850
|
// 1. removeLiquidityByRange2 - withdraw liquidity
|
|
720
851
|
const removeLiquidityIx = yield this.ix.meteoraDlmm.removeLiquidityByRange2({
|
|
721
|
-
connection,
|
|
852
|
+
connection: params.connection,
|
|
722
853
|
userWallet: params.userWallet,
|
|
723
|
-
lbPair,
|
|
854
|
+
lbPair: params.lbPair,
|
|
724
855
|
position: params.position,
|
|
725
|
-
tokenXMint,
|
|
726
|
-
tokenYMint,
|
|
727
|
-
tokenXProgram,
|
|
728
|
-
tokenYProgram,
|
|
729
|
-
fromBinId: lowerBinId,
|
|
730
|
-
toBinId: upperBinId,
|
|
731
|
-
bpsToRemove: amountBps.toNumber(),
|
|
856
|
+
tokenXMint: params.tokenXMint,
|
|
857
|
+
tokenYMint: params.tokenYMint,
|
|
858
|
+
tokenXProgram: params.tokenXProgram,
|
|
859
|
+
tokenYProgram: params.tokenYProgram,
|
|
860
|
+
fromBinId: params.lowerBinId,
|
|
861
|
+
toBinId: params.upperBinId,
|
|
862
|
+
bpsToRemove: params.amountBps.toNumber(),
|
|
732
863
|
pdaTokenType: params.pdaTokenType,
|
|
733
|
-
remainingAccountsInfo,
|
|
864
|
+
remainingAccountsInfo: params.remainingAccountsInfo,
|
|
734
865
|
});
|
|
735
866
|
mainInstructions.push(removeLiquidityIx);
|
|
736
867
|
// 2. claimFee2 - single instruction to claim fees
|
|
737
|
-
const claimFeeIx = yield this.ix.meteoraDlmm.claimFee2(connection, {
|
|
868
|
+
const claimFeeIx = yield this.ix.meteoraDlmm.claimFee2(params.connection, {
|
|
738
869
|
userWallet: params.userWallet,
|
|
739
|
-
lbPair,
|
|
870
|
+
lbPair: params.lbPair,
|
|
740
871
|
position: params.position,
|
|
741
|
-
tokenMintX: tokenXMint,
|
|
742
|
-
tokenMintY: tokenYMint,
|
|
743
|
-
tokenProgramX: tokenXProgram,
|
|
744
|
-
tokenProgramY: tokenYProgram,
|
|
745
|
-
lowerBinId,
|
|
746
|
-
upperBinId,
|
|
872
|
+
tokenMintX: params.tokenXMint,
|
|
873
|
+
tokenMintY: params.tokenYMint,
|
|
874
|
+
tokenProgramX: params.tokenXProgram,
|
|
875
|
+
tokenProgramY: params.tokenYProgram,
|
|
876
|
+
lowerBinId: params.lowerBinId,
|
|
877
|
+
upperBinId: params.upperBinId,
|
|
747
878
|
});
|
|
748
879
|
mainInstructions.push(claimFeeIx);
|
|
749
880
|
// 3. claimReward2 - N instructions (one per active reward)
|
|
750
|
-
// Fetch lbPair account to get reward infos
|
|
751
|
-
const lbPairInfo = yield program.account.lbPair.fetch(lbPair);
|
|
752
881
|
for (let rewardIndex = 0; rewardIndex < 2; rewardIndex++) {
|
|
753
|
-
const rewardInfo = lbPairInfo.rewardInfos[rewardIndex];
|
|
882
|
+
const rewardInfo = params.lbPairInfo.rewardInfos[rewardIndex];
|
|
754
883
|
// Skip if reward is not initialized (mint is default/zero pubkey)
|
|
755
884
|
if (!rewardInfo || rewardInfo.mint.equals(web3.PublicKey.default)) {
|
|
756
885
|
continue;
|
|
757
886
|
}
|
|
758
|
-
const claimRewardIx = yield this.ix.meteoraDlmm.claimReward2(connection, {
|
|
887
|
+
const claimRewardIx = yield this.ix.meteoraDlmm.claimReward2(params.connection, {
|
|
759
888
|
userWallet: params.userWallet,
|
|
760
|
-
lbPair,
|
|
889
|
+
lbPair: params.lbPair,
|
|
761
890
|
position: params.position,
|
|
762
891
|
rewardIndex,
|
|
763
892
|
rewardMint: rewardInfo.mint,
|
|
764
893
|
rewardVault: rewardInfo.vault,
|
|
765
894
|
tokenProgram: addresses_1.TOKEN_PROGRAM_ID,
|
|
766
|
-
lowerBinId,
|
|
767
|
-
upperBinId,
|
|
895
|
+
lowerBinId: params.lowerBinId,
|
|
896
|
+
upperBinId: params.upperBinId,
|
|
768
897
|
});
|
|
769
898
|
mainInstructions.push(claimRewardIx);
|
|
770
899
|
}
|
|
771
900
|
// 4. closePositionIfEmpty - only when shouldClaimAndClose is true
|
|
772
901
|
if (params.shouldClaimAndClose) {
|
|
773
902
|
const closePositionIx = yield this.ix.meteoraDlmm.closePositionIfEmpty({
|
|
774
|
-
connection,
|
|
903
|
+
connection: params.connection,
|
|
775
904
|
userWallet: params.userWallet,
|
|
776
905
|
position: params.position,
|
|
777
906
|
});
|
|
778
907
|
mainInstructions.push(closePositionIx);
|
|
779
908
|
}
|
|
780
|
-
|
|
781
|
-
let description;
|
|
782
|
-
if (params.shouldClaimAndClose) {
|
|
783
|
-
description = 'Full position withdrawal with claim and close from Meteora DLMM (larger position)';
|
|
784
|
-
}
|
|
785
|
-
else if (amountBps.eq(new bn_js_1.default(10000))) {
|
|
786
|
-
description = 'Full position withdrawal without close from Meteora DLMM (larger position)';
|
|
787
|
-
}
|
|
788
|
-
else {
|
|
789
|
-
description = 'Partial position withdrawal from Meteora DLMM (larger position)';
|
|
790
|
-
}
|
|
791
|
-
return (0, functions_1.createTransactionMeta)({
|
|
792
|
-
payer: params.userWallet,
|
|
793
|
-
description,
|
|
794
|
-
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
795
|
-
mainInstructions,
|
|
796
|
-
});
|
|
909
|
+
return mainInstructions;
|
|
797
910
|
});
|
|
798
911
|
}
|
|
799
912
|
/**
|
|
@@ -827,6 +940,99 @@ class Transactions {
|
|
|
827
940
|
return txMeta;
|
|
828
941
|
});
|
|
829
942
|
}
|
|
943
|
+
/**
|
|
944
|
+
* Claim fees and rewards from a large Meteora DLMM position (up to 1400 bins).
|
|
945
|
+
*
|
|
946
|
+
* For ≤149 bins, delegates to the single-TX meteoraClaim.
|
|
947
|
+
* For ≥150 bins, chunks the bin range into ≤149-bin pieces and builds
|
|
948
|
+
* claimFee2 + claimReward2 per chunk, returning multiple TXs.
|
|
949
|
+
*
|
|
950
|
+
* @param connection The Solana web3 connection object for blockchain interactions.
|
|
951
|
+
* @param params MeteoraClaim parameters (userWallet, position, fastGeneration)
|
|
952
|
+
* @returns Array of TransactionMetadataResponse, one per chunk
|
|
953
|
+
*/
|
|
954
|
+
meteoraClaimLargePosition(_a) {
|
|
955
|
+
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
956
|
+
// Determine lbPair and bin range
|
|
957
|
+
let lbPair;
|
|
958
|
+
let lowerBinId;
|
|
959
|
+
let upperBinId;
|
|
960
|
+
if (params.fastGeneration !== undefined) {
|
|
961
|
+
lbPair = params.fastGeneration.pool;
|
|
962
|
+
// fastGeneration for MeteoraClaim doesn't include bin IDs, so we must fetch
|
|
963
|
+
const program = yield meteora_1.MeteoraDLMM.program(connection);
|
|
964
|
+
const position = yield program.account.positionV2.fetch(params.position);
|
|
965
|
+
lowerBinId = position.lowerBinId;
|
|
966
|
+
upperBinId = position.upperBinId;
|
|
967
|
+
}
|
|
968
|
+
else {
|
|
969
|
+
const program = yield meteora_1.MeteoraDLMM.program(connection);
|
|
970
|
+
const position = yield program.account.positionV2.fetch(params.position);
|
|
971
|
+
lbPair = position.lbPair;
|
|
972
|
+
lowerBinId = position.lowerBinId;
|
|
973
|
+
upperBinId = position.upperBinId;
|
|
974
|
+
}
|
|
975
|
+
const totalBins = upperBinId - lowerBinId + 1;
|
|
976
|
+
// ≤149 bins: single-tx path
|
|
977
|
+
if (totalBins <= 149) {
|
|
978
|
+
const result = yield this.meteoraClaim({ connection, params, fetch: undefined });
|
|
979
|
+
return [result];
|
|
980
|
+
}
|
|
981
|
+
// ≥150 bins: multi-tx chunked claim
|
|
982
|
+
const resolved = yield this._resolveWithdrawPoolData({ connection, lbPair });
|
|
983
|
+
// Chunk into ≤149-bin ranges
|
|
984
|
+
const CHUNK_SIZE = 149;
|
|
985
|
+
const chunks = [];
|
|
986
|
+
for (let binStart = lowerBinId; binStart <= upperBinId; binStart += CHUNK_SIZE) {
|
|
987
|
+
const chunkUpper = Math.min(binStart + CHUNK_SIZE - 1, upperBinId);
|
|
988
|
+
chunks.push({ chunkLower: binStart, chunkUpper });
|
|
989
|
+
}
|
|
990
|
+
const transactions = [];
|
|
991
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
992
|
+
const { chunkLower, chunkUpper } = chunks[i];
|
|
993
|
+
const instructions = [];
|
|
994
|
+
// claimFee2 for this chunk
|
|
995
|
+
const claimFeeIx = yield this.ix.meteoraDlmm.claimFee2(connection, {
|
|
996
|
+
userWallet: params.userWallet,
|
|
997
|
+
lbPair,
|
|
998
|
+
position: params.position,
|
|
999
|
+
tokenMintX: resolved.tokenXMint,
|
|
1000
|
+
tokenMintY: resolved.tokenYMint,
|
|
1001
|
+
tokenProgramX: resolved.tokenXProgram,
|
|
1002
|
+
tokenProgramY: resolved.tokenYProgram,
|
|
1003
|
+
lowerBinId: chunkLower,
|
|
1004
|
+
upperBinId: chunkUpper,
|
|
1005
|
+
});
|
|
1006
|
+
instructions.push(claimFeeIx);
|
|
1007
|
+
// claimReward2 per chunk for each active reward
|
|
1008
|
+
for (let rewardIndex = 0; rewardIndex < 2; rewardIndex++) {
|
|
1009
|
+
const rewardInfo = resolved.lbPairInfo.rewardInfos[rewardIndex];
|
|
1010
|
+
if (!rewardInfo || rewardInfo.mint.equals(web3.PublicKey.default)) {
|
|
1011
|
+
continue;
|
|
1012
|
+
}
|
|
1013
|
+
const claimRewardIx = yield this.ix.meteoraDlmm.claimReward2(connection, {
|
|
1014
|
+
userWallet: params.userWallet,
|
|
1015
|
+
lbPair,
|
|
1016
|
+
position: params.position,
|
|
1017
|
+
rewardIndex,
|
|
1018
|
+
rewardMint: rewardInfo.mint,
|
|
1019
|
+
rewardVault: rewardInfo.vault,
|
|
1020
|
+
tokenProgram: addresses_1.TOKEN_PROGRAM_ID,
|
|
1021
|
+
lowerBinId: chunkLower,
|
|
1022
|
+
upperBinId: chunkUpper,
|
|
1023
|
+
});
|
|
1024
|
+
instructions.push(claimRewardIx);
|
|
1025
|
+
}
|
|
1026
|
+
transactions.push(yield (0, functions_1.createTransactionMeta)({
|
|
1027
|
+
payer: params.userWallet,
|
|
1028
|
+
description: `Claim from large Meteora DLMM Position (chunk ${i + 1}/${chunks.length})`,
|
|
1029
|
+
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
1030
|
+
mainInstructions: instructions,
|
|
1031
|
+
}));
|
|
1032
|
+
}
|
|
1033
|
+
return transactions;
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
830
1036
|
/**
|
|
831
1037
|
* Claim all meteora fees and rewards by list of pools owned by given user
|
|
832
1038
|
*
|
|
@@ -982,6 +1188,9 @@ class Transactions {
|
|
|
982
1188
|
});
|
|
983
1189
|
});
|
|
984
1190
|
}
|
|
1191
|
+
/**
|
|
1192
|
+
* @deprecated Use compoundFromLargePositionAutomationIx instead
|
|
1193
|
+
*/
|
|
985
1194
|
compoundAutomationIx(_a) {
|
|
986
1195
|
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
987
1196
|
var _b;
|
|
@@ -1138,119 +1347,363 @@ class Transactions {
|
|
|
1138
1347
|
}
|
|
1139
1348
|
});
|
|
1140
1349
|
}
|
|
1350
|
+
/**
|
|
1351
|
+
* @deprecated Use claimFromLargePositionAutomationIx instead
|
|
1352
|
+
*
|
|
1353
|
+
* @param param0
|
|
1354
|
+
* @returns
|
|
1355
|
+
*/
|
|
1141
1356
|
claimAutomationIx(_a) {
|
|
1142
|
-
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
1143
|
-
try {
|
|
1144
|
-
console.log('=== CLAIM AUTOMATION IX START ===');
|
|
1145
|
-
console.log('Params:', {
|
|
1146
|
-
userWallet: params.userWallet.toString(),
|
|
1147
|
-
position: params.position.toString(),
|
|
1148
|
-
});
|
|
1149
|
-
const program = yield meteora_1.MeteoraDLMM.program(connection);
|
|
1150
|
-
console.log('Program fetched successfully');
|
|
1151
|
-
const position = yield program.account.positionV2.fetch(params.position);
|
|
1152
|
-
console.log('Position fetched:', {
|
|
1153
|
-
lbPair: position.lbPair.toString(),
|
|
1154
|
-
owner: position.owner.toString(),
|
|
1155
|
-
lowerBinId: position.lowerBinId.toString(),
|
|
1156
|
-
upperBinId: position.upperBinId.toString(),
|
|
1157
|
-
});
|
|
1158
|
-
const dlmmPool = yield meteora_1.MeteoraDLMM.create(connection, position.lbPair, this.ix);
|
|
1159
|
-
console.log('DLMM pool created successfully');
|
|
1160
|
-
const userPda = (0, functions_1.generateUserPda)(params.userWallet);
|
|
1161
|
-
console.log('User PDA generated:', userPda.toString());
|
|
1162
|
-
const { userPositions } = yield dlmmPool.getPositionsByUserAndLbPair(userPda);
|
|
1163
|
-
console.log('User positions fetched, count:', userPositions.length);
|
|
1164
|
-
const userPosition = userPositions.find(userPosition => userPosition.publicKey.equals(params.position));
|
|
1165
|
-
if (userPosition === undefined) {
|
|
1166
|
-
throw new Error(`Position: ${params.position} does not exist.`);
|
|
1167
|
-
}
|
|
1168
|
-
console.log('User position found');
|
|
1169
|
-
// Claim fee and claim reward ixs
|
|
1170
|
-
console.log('Creating MeteoraFunctions...');
|
|
1171
|
-
const fn = new meteora_1.MeteoraFunctions(this.ix);
|
|
1172
|
-
console.log('Calling claimAllRewardsByPosition...');
|
|
1173
|
-
const claimBuilder = yield fn.claimAllRewardsByPosition(connection, params.userWallet, addresses_1.HS_AUTHORITY, {
|
|
1174
|
-
owner: userPda,
|
|
1175
|
-
position: params.position,
|
|
1176
|
-
lbPair: position.lbPair,
|
|
1177
|
-
pdaTokenType: types_1.TokenType.STA,
|
|
1178
|
-
}, hsToMeteora_1.meteoraToHawksightAutomationIxs);
|
|
1179
|
-
console.log('Claim builder created successfully');
|
|
1180
|
-
const mainInstructions = [
|
|
1181
|
-
...claimBuilder.mainIxs,
|
|
1182
|
-
];
|
|
1183
|
-
console.log('=== CLAIM AUTOMATION IX SUCCESS ===');
|
|
1184
|
-
try {
|
|
1185
|
-
console.log('Creating transaction metadata...');
|
|
1186
|
-
const result = (0, functions_1.createTransactionMeta)({
|
|
1187
|
-
payer: params.userWallet,
|
|
1188
|
-
description: 'Automation IX: Meteora Auto-claim instructions (claim fee, reward)',
|
|
1189
|
-
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
1190
|
-
mainInstructions,
|
|
1191
|
-
});
|
|
1192
|
-
console.log('Transaction metadata created successfully');
|
|
1193
|
-
return result;
|
|
1194
|
-
}
|
|
1195
|
-
catch (metadataError) {
|
|
1196
|
-
console.error('=== TRANSACTION METADATA ERROR ===');
|
|
1197
|
-
console.error('Error creating transaction metadata:', metadataError);
|
|
1198
|
-
console.error('Main instructions count:', mainInstructions.length);
|
|
1199
|
-
console.error('Main instructions:', mainInstructions.map(ix => ({
|
|
1200
|
-
programId: ix.programId.toString(),
|
|
1201
|
-
keysCount: ix.keys.length,
|
|
1202
|
-
dataLength: ix.data.length,
|
|
1203
|
-
})));
|
|
1204
|
-
console.error('=== END METADATA ERROR LOG ===');
|
|
1205
|
-
throw metadataError;
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
catch (error) {
|
|
1209
|
-
console.error('=== CLAIM AUTOMATION IX ERROR ===');
|
|
1210
|
-
console.error('Error type:', error instanceof Error ? error.constructor.name : typeof error);
|
|
1211
|
-
console.error('Error message:', error instanceof Error ? error.message : String(error));
|
|
1212
|
-
console.error('Error stack:', error instanceof Error ? error.stack : 'No stack trace');
|
|
1213
|
-
console.error('Full error object:', error);
|
|
1214
|
-
console.error('=== END CLAIM AUTOMATION ERROR LOG ===');
|
|
1215
|
-
throw error;
|
|
1216
|
-
}
|
|
1217
|
-
});
|
|
1218
|
-
}
|
|
1219
|
-
rebalanceAutomationIx(_a) {
|
|
1220
1357
|
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
1221
1358
|
const program = yield meteora_1.MeteoraDLMM.program(connection);
|
|
1222
|
-
const position = yield program.account.positionV2.fetch(params.
|
|
1359
|
+
const position = yield program.account.positionV2.fetch(params.position);
|
|
1360
|
+
if (!position || !position.lbPair) {
|
|
1361
|
+
throw new Error(`Invalid position data: ${params.position}`);
|
|
1362
|
+
}
|
|
1223
1363
|
const dlmmPool = yield meteora_1.MeteoraDLMM.create(connection, position.lbPair, this.ix);
|
|
1224
1364
|
const userPda = (0, functions_1.generateUserPda)(params.userWallet);
|
|
1225
|
-
// Step 1: Claim all fees/rewards, remove all liquidity and close current position
|
|
1226
1365
|
const { userPositions } = yield dlmmPool.getPositionsByUserAndLbPair(userPda);
|
|
1227
|
-
const userPosition = userPositions.find(userPosition => userPosition.publicKey.equals(params.
|
|
1366
|
+
const userPosition = userPositions.find(userPosition => userPosition.publicKey.equals(params.position));
|
|
1228
1367
|
if (userPosition === undefined) {
|
|
1229
|
-
throw new Error(`Position: ${params.
|
|
1368
|
+
throw new Error(`Position: ${params.position} does not exist.`);
|
|
1230
1369
|
}
|
|
1231
|
-
|
|
1232
|
-
const
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
shouldClaimAndClose: true,
|
|
1370
|
+
// Claim fee and claim reward ixs
|
|
1371
|
+
const fn = new meteora_1.MeteoraFunctions(this.ix);
|
|
1372
|
+
const claimBuilder = yield fn.claimAllRewardsByPosition(connection, params.userWallet, addresses_1.HS_AUTHORITY, {
|
|
1373
|
+
owner: userPda,
|
|
1374
|
+
position: params.position,
|
|
1375
|
+
lbPair: position.lbPair,
|
|
1376
|
+
pdaTokenType: types_1.TokenType.STA,
|
|
1239
1377
|
}, hsToMeteora_1.meteoraToHawksightAutomationIxs);
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1378
|
+
const mainInstructions = [
|
|
1379
|
+
...claimBuilder.mainIxs,
|
|
1380
|
+
];
|
|
1381
|
+
return (0, functions_1.createTransactionMeta)({
|
|
1382
|
+
payer: params.userWallet,
|
|
1383
|
+
description: 'Automation IX: Meteora Auto-claim instructions (claim fee, reward)',
|
|
1384
|
+
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
1385
|
+
mainInstructions,
|
|
1386
|
+
});
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* Claim fees and rewards from a large Meteora DLMM position (up to 1400 bins).
|
|
1391
|
+
*
|
|
1392
|
+
* For ≤149 bins, delegates to the single-TX claimAutomationIx.
|
|
1393
|
+
* For ≥150 bins, chunks the bin range into ≤149-bin pieces and builds
|
|
1394
|
+
* claimFee2Automation + claimReward2Automation per chunk, returning multiple TXs.
|
|
1395
|
+
*
|
|
1396
|
+
* @param connection The Solana web3 connection object for blockchain interactions.
|
|
1397
|
+
* @param params Parameters required for claim (MeteoraCompound: userWallet, position)
|
|
1398
|
+
* @returns Array of TransactionMetadataResponse for each transaction
|
|
1399
|
+
*/
|
|
1400
|
+
claimFromLargePositionAutomationIx(_a) {
|
|
1401
|
+
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
1402
|
+
try {
|
|
1403
|
+
// Fetch position data
|
|
1404
|
+
const program = yield meteora_1.MeteoraDLMM.program(connection);
|
|
1405
|
+
const positionData = yield program.account.positionV2.fetch(params.position);
|
|
1406
|
+
if (!positionData || !positionData.lbPair) {
|
|
1407
|
+
throw new Error(`Invalid position data: ${params.position.toString()}`);
|
|
1408
|
+
}
|
|
1409
|
+
const lbPair = positionData.lbPair;
|
|
1410
|
+
const lowerBinId = positionData.lowerBinId;
|
|
1411
|
+
const upperBinId = positionData.upperBinId;
|
|
1412
|
+
const binCount = upperBinId - lowerBinId + 1;
|
|
1413
|
+
// For ≤149 bins, delegate to existing single-TX claimAutomationIx
|
|
1414
|
+
if (binCount <= 149) {
|
|
1415
|
+
const singleResult = yield this.claimAutomationIx({ connection, params, fetch: undefined });
|
|
1416
|
+
return [singleResult];
|
|
1417
|
+
}
|
|
1418
|
+
// ≥150 bins: chunked multi-TX approach
|
|
1419
|
+
// Resolve pool data (tokens, programs, remaining accounts)
|
|
1420
|
+
const resolved = yield this._resolveWithdrawPoolData({ connection, lbPair });
|
|
1421
|
+
const { tokenXMint, tokenYMint, tokenXProgram, tokenYProgram, remainingAccountsInfo, lbPairInfo } = resolved;
|
|
1422
|
+
// Get reward infos from lbPair state
|
|
1423
|
+
const rewardInfos = lbPairInfo.rewardInfos;
|
|
1424
|
+
// Chunk [lowerBinId, upperBinId] into ≤149-bin pieces
|
|
1425
|
+
const MAX_BINS_PER_CHUNK = 149;
|
|
1426
|
+
const chunks = [];
|
|
1427
|
+
let current = lowerBinId;
|
|
1428
|
+
while (current <= upperBinId) {
|
|
1429
|
+
const upper = Math.min(current + MAX_BINS_PER_CHUNK - 1, upperBinId);
|
|
1430
|
+
chunks.push({ lower: current, upper });
|
|
1431
|
+
current = upper + 1;
|
|
1432
|
+
}
|
|
1433
|
+
// Pre-resolve reward info (avoid repeated RPC calls)
|
|
1434
|
+
const rewardIxBuilders = [];
|
|
1435
|
+
for (let rewardIndex = 0; rewardIndex < 2; rewardIndex++) {
|
|
1436
|
+
const rewardInfo = rewardInfos[rewardIndex];
|
|
1437
|
+
if (!rewardInfo || rewardInfo.mint.equals(web3.PublicKey.default)) {
|
|
1438
|
+
continue;
|
|
1439
|
+
}
|
|
1440
|
+
const rewardTokenProgramMap = yield (0, functions_1.getTokenProgramMapForMints)(connection, [rewardInfo.mint]);
|
|
1441
|
+
const rewardTokenProgram = rewardTokenProgramMap[rewardInfo.mint.toString()];
|
|
1442
|
+
const rewardRemainingAccountsInfo = yield (0, functions_1.fetchRemainingAccountsInfoForReward)(connection, rewardInfo.mint, rewardTokenProgram);
|
|
1443
|
+
rewardIxBuilders.push({
|
|
1444
|
+
rewardIndex,
|
|
1445
|
+
rewardMint: rewardInfo.mint,
|
|
1446
|
+
rewardVault: rewardInfo.vault,
|
|
1447
|
+
rewardTokenProgram,
|
|
1448
|
+
rewardRemainingAccountsInfo,
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1451
|
+
// Build instructions per chunk
|
|
1452
|
+
const transactions = [];
|
|
1453
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
1454
|
+
const chunk = chunks[i];
|
|
1455
|
+
const instructions = [];
|
|
1456
|
+
// claimFee2Automation for this chunk
|
|
1457
|
+
const claimFeeIx = yield this.ix.meteoraDlmm.claimFee2Automation(connection, {
|
|
1458
|
+
userWallet: params.userWallet,
|
|
1459
|
+
lbPair,
|
|
1460
|
+
position: params.position,
|
|
1461
|
+
tokenMintX: tokenXMint,
|
|
1462
|
+
tokenMintY: tokenYMint,
|
|
1463
|
+
tokenProgramX: tokenXProgram,
|
|
1464
|
+
tokenProgramY: tokenYProgram,
|
|
1465
|
+
lowerBinId: chunk.lower,
|
|
1466
|
+
upperBinId: chunk.upper,
|
|
1467
|
+
pdaTokenType: types_1.TokenType.STA,
|
|
1468
|
+
remainingAccountsInfo,
|
|
1469
|
+
});
|
|
1470
|
+
instructions.push(claimFeeIx);
|
|
1471
|
+
// claimReward2Automation per chunk for each active reward
|
|
1472
|
+
for (const reward of rewardIxBuilders) {
|
|
1473
|
+
const claimRewardIx = yield this.ix.meteoraDlmm.claimReward2Automation(connection, {
|
|
1474
|
+
userWallet: params.userWallet,
|
|
1475
|
+
lbPair,
|
|
1476
|
+
position: params.position,
|
|
1477
|
+
rewardIndex: reward.rewardIndex,
|
|
1478
|
+
rewardMint: reward.rewardMint,
|
|
1479
|
+
rewardVault: reward.rewardVault,
|
|
1480
|
+
tokenProgram: reward.rewardTokenProgram,
|
|
1481
|
+
lowerBinId: chunk.lower,
|
|
1482
|
+
upperBinId: chunk.upper,
|
|
1483
|
+
pdaTokenType: types_1.TokenType.STA,
|
|
1484
|
+
remainingAccountsInfo: reward.rewardRemainingAccountsInfo,
|
|
1485
|
+
});
|
|
1486
|
+
instructions.push(claimRewardIx);
|
|
1487
|
+
}
|
|
1488
|
+
transactions.push(yield (0, functions_1.createTransactionMeta)({
|
|
1489
|
+
payer: params.userWallet,
|
|
1490
|
+
description: `Automation IX: Claim from large position (tx ${i + 1}/${chunks.length})`,
|
|
1491
|
+
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
1492
|
+
mainInstructions: instructions,
|
|
1493
|
+
}));
|
|
1494
|
+
}
|
|
1495
|
+
return transactions;
|
|
1496
|
+
}
|
|
1497
|
+
catch (error) {
|
|
1498
|
+
throw error;
|
|
1499
|
+
}
|
|
1500
|
+
});
|
|
1501
|
+
}
|
|
1502
|
+
/**
|
|
1503
|
+
* Compound (claim + re-deposit) fees and rewards from a large Meteora DLMM position (up to 1400 bins).
|
|
1504
|
+
*
|
|
1505
|
+
* For ≤149 bins, delegates to the single-TX compoundAutomationIx.
|
|
1506
|
+
* For ≥150 bins:
|
|
1507
|
+
* Phase 1: Chunked claim (claimFee2Automation + claimReward2Automation per chunk, to ATA)
|
|
1508
|
+
* Phase 2: Chunked re-deposit (rebalanceLiquidityAutomation per chunk, back into same position)
|
|
1509
|
+
*
|
|
1510
|
+
* @param connection The Solana web3 connection object for blockchain interactions.
|
|
1511
|
+
* @param params MeteoraCompound parameters (userWallet, position, distribution)
|
|
1512
|
+
* @returns Array of TransactionMetadataResponse for each transaction
|
|
1513
|
+
*/
|
|
1514
|
+
compoundFromLargePositionAutomationIx(_a) {
|
|
1515
|
+
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
1516
|
+
var _b, _c;
|
|
1517
|
+
try {
|
|
1518
|
+
// Fetch position data
|
|
1519
|
+
const program = yield meteora_1.MeteoraDLMM.program(connection);
|
|
1520
|
+
const positionData = yield program.account.positionV2.fetch(params.position);
|
|
1521
|
+
if (!positionData || !positionData.lbPair) {
|
|
1522
|
+
throw new Error(`Invalid position data: ${params.position.toString()}`);
|
|
1523
|
+
}
|
|
1524
|
+
const lbPair = positionData.lbPair;
|
|
1525
|
+
const lowerBinId = positionData.lowerBinId;
|
|
1526
|
+
const upperBinId = positionData.upperBinId;
|
|
1527
|
+
const binCount = upperBinId - lowerBinId + 1;
|
|
1528
|
+
// For ≤149 bins, delegate to existing single-TX compoundAutomationIx
|
|
1529
|
+
if (binCount <= 149) {
|
|
1530
|
+
const singleResult = yield this.compoundAutomationIx({ connection, params, fetch: undefined });
|
|
1531
|
+
return [singleResult];
|
|
1532
|
+
}
|
|
1533
|
+
// ≥150 bins: chunked multi-TX approach
|
|
1534
|
+
const dlmmPool = yield meteora_1.MeteoraDLMM.create(connection, lbPair, this.ix);
|
|
1535
|
+
// For compound, we use dummy equal amounts for deposit strategy calculation.
|
|
1536
|
+
// The on-chain RebalanceLiquidity overrides totalXAmount/totalYAmount with ATA balance,
|
|
1537
|
+
// so these values only affect the strategy ratio. Using equal amounts (like the single-TX
|
|
1538
|
+
// compoundAutomationIx does with 100_000) ensures the strategy distributes evenly and
|
|
1539
|
+
// doesn't try to transfer more of one token than is available from claimed fees.
|
|
1540
|
+
const totalXAmount = new bn_js_1.default(100000);
|
|
1541
|
+
const totalYAmount = new bn_js_1.default(100000);
|
|
1542
|
+
const lbPairState = dlmmPool.dlmm.lbPair;
|
|
1543
|
+
const tokenXMint = lbPairState.tokenXMint;
|
|
1544
|
+
const tokenYMint = lbPairState.tokenYMint;
|
|
1545
|
+
const rewardInfos = lbPairState.rewardInfos;
|
|
1546
|
+
const tokenProgramMap = yield (0, functions_1.getTokenProgramMapForMints)(connection, [tokenXMint, tokenYMint]);
|
|
1547
|
+
const tokenXProgram = tokenProgramMap[tokenXMint.toString()];
|
|
1548
|
+
const tokenYProgram = tokenProgramMap[tokenYMint.toString()];
|
|
1549
|
+
const remainingAccountsInfo = yield (0, functions_1.fetchRemainingAccountsInfo)(connection, tokenXMint, tokenYMint, tokenXProgram, tokenYProgram);
|
|
1550
|
+
const lbPairInfo = yield (program.account.lbPair.fetch(lbPair));
|
|
1551
|
+
// =========================================================================
|
|
1552
|
+
// Phase 1: Chunked claim (to ATA for re-deposit)
|
|
1553
|
+
// =========================================================================
|
|
1554
|
+
const MAX_BINS_PER_CHUNK = 149;
|
|
1555
|
+
const chunks = [];
|
|
1556
|
+
let current = lowerBinId;
|
|
1557
|
+
while (current <= upperBinId) {
|
|
1558
|
+
const upper = Math.min(current + MAX_BINS_PER_CHUNK - 1, upperBinId);
|
|
1559
|
+
chunks.push({ lower: current, upper });
|
|
1560
|
+
current = upper + 1;
|
|
1561
|
+
}
|
|
1562
|
+
// Pre-resolve reward info
|
|
1563
|
+
const rewardIxBuilders = [];
|
|
1564
|
+
for (let rewardIndex = 0; rewardIndex < 2; rewardIndex++) {
|
|
1565
|
+
const rewardInfo = rewardInfos[rewardIndex];
|
|
1566
|
+
if (!rewardInfo || rewardInfo.mint.equals(web3.PublicKey.default)) {
|
|
1567
|
+
continue;
|
|
1568
|
+
}
|
|
1569
|
+
const rewardTokenProgramMap = yield (0, functions_1.getTokenProgramMapForMints)(connection, [rewardInfo.mint]);
|
|
1570
|
+
const rewardTokenProgram = rewardTokenProgramMap[rewardInfo.mint.toString()];
|
|
1571
|
+
const rewardRemainingAccountsInfo = yield (0, functions_1.fetchRemainingAccountsInfoForReward)(connection, rewardInfo.mint, rewardTokenProgram);
|
|
1572
|
+
rewardIxBuilders.push({
|
|
1573
|
+
rewardIndex,
|
|
1574
|
+
rewardMint: rewardInfo.mint,
|
|
1575
|
+
rewardVault: rewardInfo.vault,
|
|
1576
|
+
rewardTokenProgram,
|
|
1577
|
+
rewardRemainingAccountsInfo,
|
|
1578
|
+
});
|
|
1579
|
+
}
|
|
1580
|
+
// Build claim instructions per chunk (to ATA for compound)
|
|
1581
|
+
const claimTxs = [];
|
|
1582
|
+
for (const chunk of chunks) {
|
|
1583
|
+
const instructions = [];
|
|
1584
|
+
const claimFeeIx = yield this.ix.meteoraDlmm.claimFee2Automation(connection, {
|
|
1585
|
+
userWallet: params.userWallet,
|
|
1586
|
+
lbPair,
|
|
1587
|
+
position: params.position,
|
|
1588
|
+
tokenMintX: tokenXMint,
|
|
1589
|
+
tokenMintY: tokenYMint,
|
|
1590
|
+
tokenProgramX: tokenXProgram,
|
|
1591
|
+
tokenProgramY: tokenYProgram,
|
|
1592
|
+
lowerBinId: chunk.lower,
|
|
1593
|
+
upperBinId: chunk.upper,
|
|
1594
|
+
pdaTokenType: types_1.TokenType.ATA,
|
|
1595
|
+
remainingAccountsInfo,
|
|
1596
|
+
});
|
|
1597
|
+
instructions.push(claimFeeIx);
|
|
1598
|
+
for (const reward of rewardIxBuilders) {
|
|
1599
|
+
const claimRewardIx = yield this.ix.meteoraDlmm.claimReward2Automation(connection, {
|
|
1600
|
+
userWallet: params.userWallet,
|
|
1601
|
+
lbPair,
|
|
1602
|
+
position: params.position,
|
|
1603
|
+
rewardIndex: reward.rewardIndex,
|
|
1604
|
+
rewardMint: reward.rewardMint,
|
|
1605
|
+
rewardVault: reward.rewardVault,
|
|
1606
|
+
tokenProgram: reward.rewardTokenProgram,
|
|
1607
|
+
lowerBinId: chunk.lower,
|
|
1608
|
+
upperBinId: chunk.upper,
|
|
1609
|
+
pdaTokenType: types_1.TokenType.ATA,
|
|
1610
|
+
remainingAccountsInfo: reward.rewardRemainingAccountsInfo,
|
|
1611
|
+
});
|
|
1612
|
+
instructions.push(claimRewardIx);
|
|
1613
|
+
}
|
|
1614
|
+
claimTxs.push(instructions);
|
|
1615
|
+
}
|
|
1616
|
+
// =========================================================================
|
|
1617
|
+
// Phase 2: Chunked re-deposit back into the same position
|
|
1618
|
+
// =========================================================================
|
|
1619
|
+
const distributionToStrategyType = {
|
|
1620
|
+
'SPOT': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-IMBALANCED': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-ONE-SIDE': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-BALANCED': liquidityStrategy_1.StrategyType.SPOT,
|
|
1621
|
+
'CURVE': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-IMBALANCED': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-ONE-SIDE': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-BALANCED': liquidityStrategy_1.StrategyType.CURVE,
|
|
1622
|
+
'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,
|
|
1623
|
+
};
|
|
1624
|
+
const strategyType = (_c = distributionToStrategyType[(_b = params.distribution) !== null && _b !== void 0 ? _b : 'SPOT-IMBALANCED']) !== null && _c !== void 0 ? _c : liquidityStrategy_1.StrategyType.SPOT;
|
|
1625
|
+
const { binId: activeId } = yield dlmmPool.dlmm.getActiveBin();
|
|
1626
|
+
const depositTxs = yield this._meteoraDepositToLargePositionAutomation({
|
|
1627
|
+
connection,
|
|
1628
|
+
userWallet: params.userWallet,
|
|
1629
|
+
lbPair,
|
|
1630
|
+
position: params.position,
|
|
1631
|
+
tokenXMint,
|
|
1632
|
+
tokenYMint,
|
|
1633
|
+
tokenXProgram,
|
|
1634
|
+
tokenYProgram,
|
|
1635
|
+
lowerBinId,
|
|
1636
|
+
upperBinId,
|
|
1637
|
+
strategyType,
|
|
1638
|
+
activeId,
|
|
1639
|
+
totalXAmount,
|
|
1640
|
+
totalYAmount,
|
|
1641
|
+
});
|
|
1642
|
+
// =========================================================================
|
|
1643
|
+
// Phase 3: Assemble transactions
|
|
1644
|
+
// =========================================================================
|
|
1645
|
+
const assembled = [
|
|
1646
|
+
...claimTxs,
|
|
1647
|
+
...depositTxs,
|
|
1648
|
+
];
|
|
1649
|
+
const transactions = [];
|
|
1650
|
+
for (let i = 0; i < assembled.length; i++) {
|
|
1651
|
+
transactions.push(yield (0, functions_1.createTransactionMeta)({
|
|
1652
|
+
payer: params.userWallet,
|
|
1653
|
+
description: `Automation IX: Compound large position (tx ${i + 1}/${assembled.length})`,
|
|
1654
|
+
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
1655
|
+
mainInstructions: assembled[i],
|
|
1656
|
+
}));
|
|
1657
|
+
}
|
|
1658
|
+
return transactions;
|
|
1659
|
+
}
|
|
1660
|
+
catch (error) {
|
|
1661
|
+
throw error;
|
|
1662
|
+
}
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1665
|
+
/**
|
|
1666
|
+
* @deprecated Use rebalanceLargerPositionAutomation instead
|
|
1667
|
+
*
|
|
1668
|
+
* @param connection The Solana web3 connection object for blockchain interactions.
|
|
1669
|
+
* @param params Parameters required for rebalance
|
|
1670
|
+
* @returns A ResponseWithStatus containing TransactionMetadataResponse.
|
|
1671
|
+
*/
|
|
1672
|
+
rebalanceAutomationIx(_a) {
|
|
1673
|
+
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
1674
|
+
const program = yield meteora_1.MeteoraDLMM.program(connection);
|
|
1675
|
+
const position = yield program.account.positionV2.fetch(params.currentPosition);
|
|
1676
|
+
const dlmmPool = yield meteora_1.MeteoraDLMM.create(connection, position.lbPair, this.ix);
|
|
1677
|
+
const userPda = (0, functions_1.generateUserPda)(params.userWallet);
|
|
1678
|
+
// Step 1: Claim all fees/rewards, remove all liquidity and close current position
|
|
1679
|
+
const { userPositions } = yield dlmmPool.getPositionsByUserAndLbPair(userPda);
|
|
1680
|
+
const userPosition = userPositions.find(userPosition => userPosition.publicKey.equals(params.currentPosition));
|
|
1681
|
+
if (userPosition === undefined) {
|
|
1682
|
+
throw new Error(`Position: ${params.currentPosition} does not exist.`);
|
|
1683
|
+
}
|
|
1684
|
+
const binIdsToRemove = userPosition.positionData.positionBinData.map(bin => bin.binId);
|
|
1685
|
+
const removeLiquidityBuilder = yield dlmmPool.removeLiquidity(connection, params.userWallet, addresses_1.HS_AUTHORITY, {
|
|
1686
|
+
user: userPda,
|
|
1687
|
+
position: params.currentPosition,
|
|
1688
|
+
fromBinId: position.lowerBinId,
|
|
1689
|
+
toBinId: position.upperBinId,
|
|
1690
|
+
bps: new bn_js_1.default(10000),
|
|
1691
|
+
shouldClaimAndClose: true,
|
|
1692
|
+
}, hsToMeteora_1.meteoraToHawksightAutomationIxs);
|
|
1693
|
+
if (!!params.useAta) {
|
|
1694
|
+
removeLiquidityBuilder.replaceClaimFeeTokenToATA();
|
|
1695
|
+
removeLiquidityBuilder.replaceClaimRewardToATA();
|
|
1696
|
+
}
|
|
1697
|
+
else {
|
|
1698
|
+
removeLiquidityBuilder.replaceClaimFeeTokenToSTA();
|
|
1699
|
+
removeLiquidityBuilder.replaceClaimRewardToSTA();
|
|
1700
|
+
}
|
|
1701
|
+
// Re-deposit fees (TODO: How to re-deposit reward tokens that is not X or Y token?)
|
|
1702
|
+
const initPositionAndAddLiquidityBuilder = yield dlmmPool.initializePositionAndAddLiquidityByStrategy(connection, params.userWallet, addresses_1.HS_AUTHORITY, {
|
|
1703
|
+
positionPubKey: params.newPosition,
|
|
1704
|
+
user: userPda,
|
|
1705
|
+
totalXAmount: new bn_js_1.default(100000), // This is overriden on-chain, so value here do not matter
|
|
1706
|
+
totalYAmount: new bn_js_1.default(100000), // This is overriden on-chain, so value here do not matter
|
|
1254
1707
|
strategy: {
|
|
1255
1708
|
maxBinId: params.binRange.upperRange,
|
|
1256
1709
|
minBinId: params.binRange.lowerRange,
|
|
@@ -1274,6 +1727,13 @@ class Transactions {
|
|
|
1274
1727
|
});
|
|
1275
1728
|
});
|
|
1276
1729
|
}
|
|
1730
|
+
/**
|
|
1731
|
+
* @deprecated Use rebalanceLargerPositionAutomation instaed
|
|
1732
|
+
*
|
|
1733
|
+
* @param connection The Solana web3 connection object for blockchain interactions.
|
|
1734
|
+
* @param params Parameters required for rebalance
|
|
1735
|
+
* @returns A ResponseWithStatus containing TransactionMetadataResponse.
|
|
1736
|
+
*/
|
|
1277
1737
|
rebalanceAutomationIx2(_a) {
|
|
1278
1738
|
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
1279
1739
|
var _b;
|
|
@@ -1597,20 +2057,6 @@ class Transactions {
|
|
|
1597
2057
|
*
|
|
1598
2058
|
* @param connection The Solana web3 connection object for blockchain interactions.
|
|
1599
2059
|
* @param params Parameters required for rebalance (same as MeteoraRebalance)
|
|
1600
|
-
* @returns A TransactionMetadataResponse containing the transaction metadata
|
|
1601
|
-
*/
|
|
1602
|
-
/**
|
|
1603
|
-
* Rebalance a Meteora DLMM position to a new bin range (supports up to 149 bins).
|
|
1604
|
-
*
|
|
1605
|
-
* This method chunks the rebalance into multiple transactions:
|
|
1606
|
-
* - TX 1: Remove liquidity + claim fees + claim rewards + close position
|
|
1607
|
-
* - TX 2: Initialize new position + extend position (if > 70 bins)
|
|
1608
|
-
* - TX 3: Initialize bin array + add liquidity (if > 70 bins)
|
|
1609
|
-
*
|
|
1610
|
-
* For positions <= 70 bins, TX 2 and TX 3 are combined into a single transaction.
|
|
1611
|
-
*
|
|
1612
|
-
* @param connection - Solana connection
|
|
1613
|
-
* @param params - MeteoraRebalance parameters
|
|
1614
2060
|
* @returns Array of TransactionMetadataResponse for each transaction
|
|
1615
2061
|
*/
|
|
1616
2062
|
rebalanceLargerPositionAutomation(_a) {
|
|
@@ -1870,65 +2316,829 @@ class Transactions {
|
|
|
1870
2316
|
}
|
|
1871
2317
|
});
|
|
1872
2318
|
}
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
strategyType: types_3.StrategyTypeMap[params.distribution],
|
|
1888
|
-
},
|
|
1889
|
-
skipInputTokenCheck: true, // Rebalance should be independent of user wallet TA
|
|
1890
|
-
opt: {
|
|
1891
|
-
pdaTokenType: params.opt.pdaTokenType,
|
|
1892
|
-
withAmount: {
|
|
1893
|
-
userTokenXAmount: params.opt.withAmount.userTokenXAmount,
|
|
1894
|
-
userTokenYAmount: params.opt.withAmount.userTokenYAmount,
|
|
1895
|
-
},
|
|
1896
|
-
fuelAccount: params.opt.fuelAccount,
|
|
1897
|
-
},
|
|
1898
|
-
}, hsToMeteora_1.meteoraToHawksightAutomationIxs);
|
|
1899
|
-
const mainInstructions = [...initPositionAndAddLiquidityBuilder.mainIxs];
|
|
1900
|
-
return (0, functions_1.createTransactionMeta)({
|
|
1901
|
-
payer: params.userWallet,
|
|
1902
|
-
description: 'Automation IX: Meteora Open instruction (Open position)',
|
|
1903
|
-
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
1904
|
-
mainInstructions,
|
|
1905
|
-
});
|
|
1906
|
-
});
|
|
1907
|
-
}
|
|
1908
|
-
relativeOpenAutomationIx(_a) {
|
|
2319
|
+
/**
|
|
2320
|
+
* Rebalance a large Meteora DLMM position (up to 1400 bins) using automation instructions.
|
|
2321
|
+
*
|
|
2322
|
+
* For ≤149 bins, delegates to rebalanceLargerPositionAutomation.
|
|
2323
|
+
* For ≥150 bins, uses a chunked multi-TX approach:
|
|
2324
|
+
* Phase 1: Chunked withdraw from current position
|
|
2325
|
+
* Phase 2: Initialize new large position
|
|
2326
|
+
* Phase 3: Chunked deposit to new position
|
|
2327
|
+
*
|
|
2328
|
+
* @param connection The Solana web3 connection object for blockchain interactions.
|
|
2329
|
+
* @param params Parameters required for rebalance
|
|
2330
|
+
* @returns Array of TransactionMetadataResponse for each transaction
|
|
2331
|
+
*/
|
|
2332
|
+
rebalanceLargePositionAutomation(_a) {
|
|
1909
2333
|
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
2334
|
+
var _b;
|
|
2335
|
+
try {
|
|
2336
|
+
// Fetch position to get lbPair and bin range
|
|
2337
|
+
const program = yield meteora_1.MeteoraDLMM.program(connection);
|
|
2338
|
+
const positionData = yield program.account.positionV2.fetch(params.currentPosition);
|
|
2339
|
+
if (!positionData || !positionData.lbPair) {
|
|
2340
|
+
throw new Error(`Invalid position data: ${params.currentPosition.toString()}`);
|
|
2341
|
+
}
|
|
2342
|
+
const lbPair = positionData.lbPair;
|
|
2343
|
+
const currentLowerBinId = positionData.lowerBinId;
|
|
2344
|
+
const currentUpperBinId = positionData.upperBinId;
|
|
2345
|
+
const currentBinCount = currentUpperBinId - currentLowerBinId + 1;
|
|
2346
|
+
// For ≤149 bins, delegate to existing rebalanceLargerPositionAutomation
|
|
2347
|
+
if (currentBinCount <= 149 && params.width <= 149) {
|
|
2348
|
+
return this.rebalanceLargerPositionAutomation({ connection, params, fetch: undefined });
|
|
2349
|
+
}
|
|
2350
|
+
// ≥150 bins: multi-TX chunked approach
|
|
2351
|
+
const dlmmPool = yield meteora_1.MeteoraDLMM.create(connection, lbPair, this.ix);
|
|
2352
|
+
const userPda = (0, functions_1.generateUserPda)(params.userWallet);
|
|
2353
|
+
// Get position's token amounts for deposit strategy calculation
|
|
2354
|
+
const { userPositions } = yield dlmmPool.getPositionsByUserAndLbPair(userPda);
|
|
2355
|
+
const userPosition = userPositions.find(pos => pos.publicKey.equals(params.currentPosition));
|
|
2356
|
+
if (!userPosition) {
|
|
2357
|
+
throw new Error(`Position ${params.currentPosition.toString()} not found`);
|
|
2358
|
+
}
|
|
2359
|
+
const positionBinData = userPosition.positionData.positionBinData;
|
|
2360
|
+
const { totalXAmount, totalYAmount } = positionBinData.reduce((acc, bin) => ({
|
|
2361
|
+
totalXAmount: acc.totalXAmount.add(new bn_js_1.default(bin.positionXAmount)),
|
|
2362
|
+
totalYAmount: acc.totalYAmount.add(new bn_js_1.default(bin.positionYAmount)),
|
|
2363
|
+
}), { totalXAmount: new bn_js_1.default(0), totalYAmount: new bn_js_1.default(0) });
|
|
2364
|
+
const lbPairState = dlmmPool.dlmm.lbPair;
|
|
2365
|
+
const tokenXMint = lbPairState.tokenXMint;
|
|
2366
|
+
const tokenYMint = lbPairState.tokenYMint;
|
|
2367
|
+
const rewardInfos = lbPairState.rewardInfos;
|
|
2368
|
+
const tokenProgramMap = yield (0, functions_1.getTokenProgramMapForMints)(connection, [tokenXMint, tokenYMint]);
|
|
2369
|
+
const tokenXProgram = tokenProgramMap[tokenXMint.toString()];
|
|
2370
|
+
const tokenYProgram = tokenProgramMap[tokenYMint.toString()];
|
|
2371
|
+
const remainingAccountsInfo = yield (0, functions_1.fetchRemainingAccountsInfo)(connection, tokenXMint, tokenYMint, tokenXProgram, tokenYProgram);
|
|
2372
|
+
const lbPairInfo = yield (program.account.lbPair.fetch(lbPair));
|
|
2373
|
+
// Whether to compound during rebalance, or nope.
|
|
2374
|
+
const pdaTokenTypeForClaimables = params.useAta ? types_1.TokenType.ATA : types_1.TokenType.STA;
|
|
2375
|
+
// =========================================================================
|
|
2376
|
+
// Merged approach: withdraw + init/extend + deposit per TX (~75 bins each)
|
|
2377
|
+
// =========================================================================
|
|
2378
|
+
const { binId: activeId } = yield dlmmPool.dlmm.getActiveBin();
|
|
2379
|
+
const distributionToStrategyType = {
|
|
2380
|
+
'SPOT': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-IMBALANCED': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-ONE-SIDE': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-BALANCED': liquidityStrategy_1.StrategyType.SPOT,
|
|
2381
|
+
'CURVE': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-IMBALANCED': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-ONE-SIDE': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-BALANCED': liquidityStrategy_1.StrategyType.CURVE,
|
|
2382
|
+
'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,
|
|
2383
|
+
};
|
|
2384
|
+
const strategyType = (_b = distributionToStrategyType[params.distribution]) !== null && _b !== void 0 ? _b : liquidityStrategy_1.StrategyType.SPOT;
|
|
2385
|
+
const binStep = new bn_js_1.default(dlmmPool.dlmm.lbPair.binStep);
|
|
2386
|
+
const assembled = yield this._buildMergedRebalanceChunks({
|
|
2387
|
+
connection,
|
|
2388
|
+
userWallet: params.userWallet,
|
|
2389
|
+
lbPair,
|
|
2390
|
+
oldPosition: params.currentPosition,
|
|
2391
|
+
oldLowerBinId: currentLowerBinId,
|
|
2392
|
+
oldUpperBinId: currentUpperBinId,
|
|
2393
|
+
newPosition: params.newPosition,
|
|
2394
|
+
relativeLowerBinId: params.relativeLowerBin,
|
|
2395
|
+
width: params.width,
|
|
2396
|
+
tokenXMint,
|
|
2397
|
+
tokenYMint,
|
|
2398
|
+
tokenXProgram,
|
|
2399
|
+
tokenYProgram,
|
|
2400
|
+
remainingAccountsInfo,
|
|
2401
|
+
lbPairInfo,
|
|
2402
|
+
rewardInfos,
|
|
2403
|
+
pdaTokenTypeForClaimables,
|
|
2404
|
+
binStep,
|
|
2405
|
+
strategyType,
|
|
2406
|
+
activeId,
|
|
2407
|
+
totalXAmount,
|
|
2408
|
+
totalYAmount,
|
|
2409
|
+
});
|
|
2410
|
+
// Wrap each instruction array with createTransactionMeta
|
|
2411
|
+
const transactions = [];
|
|
2412
|
+
for (let i = 0; i < assembled.length; i++) {
|
|
2413
|
+
transactions.push(yield (0, functions_1.createTransactionMeta)({
|
|
2414
|
+
payer: params.userWallet,
|
|
2415
|
+
description: `Automation IX: Rebalance large position (tx ${i + 1}/${assembled.length})`,
|
|
2416
|
+
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
2417
|
+
mainInstructions: assembled[i],
|
|
2418
|
+
}));
|
|
2419
|
+
}
|
|
2420
|
+
return transactions;
|
|
2421
|
+
}
|
|
2422
|
+
catch (error) {
|
|
2423
|
+
console.error('=== REBALANCE LARGE POSITION AUTOMATION ERROR ===');
|
|
2424
|
+
console.error('Error type:', error instanceof Error ? error.constructor.name : typeof error);
|
|
2425
|
+
console.error('Error message:', error instanceof Error ? error.message : String(error));
|
|
2426
|
+
console.error('Error stack:', error instanceof Error ? error.stack : 'No stack trace');
|
|
2427
|
+
console.error('Full error object:', error);
|
|
2428
|
+
console.error('=== END ERROR LOG ===');
|
|
2429
|
+
throw error;
|
|
2430
|
+
}
|
|
2431
|
+
});
|
|
2432
|
+
}
|
|
2433
|
+
/**
|
|
2434
|
+
* Generates a single rebalanceLiquidityAutomation instruction for reshaping a Meteora DLMM position.
|
|
2435
|
+
*
|
|
2436
|
+
* This is a low-level building block that creates ONE instruction to:
|
|
2437
|
+
* - Remove liquidity from specified bins (removeLiquidityParams)
|
|
2438
|
+
* - Add liquidity to new bins (addLiquidityParams)
|
|
2439
|
+
*
|
|
2440
|
+
* Unlike meteoraRebalanceLargePositionAutomation2 which handles full position reshaping
|
|
2441
|
+
* with chunking and multiple TXs, this function returns a single instruction that can
|
|
2442
|
+
* be composed into custom transaction flows.
|
|
2443
|
+
*
|
|
2444
|
+
* Features:
|
|
2445
|
+
* - Token2022 transfer hook support
|
|
2446
|
+
* - No fee/reward claiming (shouldClaimFee: false, shouldClaimReward: false)
|
|
2447
|
+
* - Uses ATA token accounts
|
|
2448
|
+
* - NoShrinkBoth shrink mode
|
|
2449
|
+
*
|
|
2450
|
+
* Note: Not part of test suite yet. For remove + add liquidity params, please fork
|
|
2451
|
+
* hawk-api/src/meteora/liquidityStrategy.ts to generate the parameters.
|
|
2452
|
+
*
|
|
2453
|
+
* @param connection Solana web3 connection
|
|
2454
|
+
* @param params Reshape parameters including position, bin ranges, and liquidity params
|
|
2455
|
+
* @returns LabeledInstructions with single 'rebalance_liquidity' instruction
|
|
2456
|
+
*/
|
|
2457
|
+
meteoraReshapePositionAutomation(_a) {
|
|
2458
|
+
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
2459
|
+
var _b, _c;
|
|
2460
|
+
// Fetch pool data
|
|
2461
|
+
const dlmmPool = yield meteora_1.MeteoraDLMM.create(connection, params.lbPair, this.ix);
|
|
2462
|
+
const lbPairState = dlmmPool.dlmm.lbPair;
|
|
2463
|
+
const tokenXMint = lbPairState.tokenXMint;
|
|
2464
|
+
const tokenYMint = lbPairState.tokenYMint;
|
|
2465
|
+
const tokenProgramMap = yield (0, functions_1.getTokenProgramMapForMints)(connection, [tokenXMint, tokenYMint]);
|
|
2466
|
+
const tokenXProgram = tokenProgramMap[tokenXMint.toString()];
|
|
2467
|
+
const tokenYProgram = tokenProgramMap[tokenYMint.toString()];
|
|
2468
|
+
// Fetch transfer hook accounts for token X and Y (Token2022 support)
|
|
2469
|
+
const baseRemainingAccountsInfo = yield (0, functions_1.fetchRemainingAccountsInfo)(connection, tokenXMint, tokenYMint, tokenXProgram, tokenYProgram);
|
|
2470
|
+
// Build combined remainingAccountsInfo with transfer hooks for X, Y, and rewards
|
|
2471
|
+
// The slices describe: TransferHookX, TransferHookY, TransferHookMultiReward(index) for each reward
|
|
2472
|
+
// IMPORTANT: Always include all slices, even with length=0, so Meteora knows the account structure
|
|
2473
|
+
const combinedSlices = [];
|
|
2474
|
+
const combinedAccounts = [];
|
|
2475
|
+
// Always add TransferHookX and TransferHookY slices (even if length=0)
|
|
2476
|
+
const xSlice = baseRemainingAccountsInfo.slices.find(s => s.accountsType === types_1.RemainingAccountsType.TransferHookX);
|
|
2477
|
+
const ySlice = baseRemainingAccountsInfo.slices.find(s => s.accountsType === types_1.RemainingAccountsType.TransferHookY);
|
|
2478
|
+
combinedSlices.push({ accountsType: types_1.RemainingAccountsType.TransferHookX, length: (_b = xSlice === null || xSlice === void 0 ? void 0 : xSlice.length) !== null && _b !== void 0 ? _b : 0 });
|
|
2479
|
+
combinedSlices.push({ accountsType: types_1.RemainingAccountsType.TransferHookY, length: (_c = ySlice === null || ySlice === void 0 ? void 0 : ySlice.length) !== null && _c !== void 0 ? _c : 0 });
|
|
2480
|
+
// Add transfer hook accounts from base (for X and Y)
|
|
2481
|
+
combinedAccounts.push(...baseRemainingAccountsInfo.accounts);
|
|
2482
|
+
const remainingAccountsInfo = {
|
|
2483
|
+
slices: combinedSlices,
|
|
2484
|
+
accounts: combinedAccounts,
|
|
2485
|
+
};
|
|
2486
|
+
return {
|
|
2487
|
+
instructions: [yield this.ix.meteoraDlmm.rebalanceLiquidityAutomation({
|
|
2488
|
+
connection,
|
|
2489
|
+
userWallet: params.userWallet,
|
|
2490
|
+
position: params.position,
|
|
2491
|
+
lbPair: params.lbPair,
|
|
2492
|
+
tokenXMint, tokenYMint, tokenXProgram, tokenYProgram,
|
|
2493
|
+
activeId: params.activeId,
|
|
2494
|
+
pdaTokenType: types_1.TokenType.ATA,
|
|
2495
|
+
maxActiveBinSlippage: 300,
|
|
2496
|
+
shouldClaimFee: false,
|
|
2497
|
+
shouldClaimReward: false,
|
|
2498
|
+
minWithdrawXAmount: new bn_js_1.default(0),
|
|
2499
|
+
maxDepositXAmount: new bn_js_1.default(0),
|
|
2500
|
+
minWithdrawYAmount: new bn_js_1.default(0),
|
|
2501
|
+
maxDepositYAmount: new bn_js_1.default(0),
|
|
2502
|
+
removeLiquidityParams: params.removeLiquidityParams,
|
|
2503
|
+
addLiquidityParams: params.addLiquidityParams,
|
|
2504
|
+
shrinkMode: types_3.ShrinkMode.NoShrinkBoth,
|
|
2505
|
+
remainingAccountsInfo,
|
|
2506
|
+
})],
|
|
2507
|
+
labels: ['rebalance_liquidity'],
|
|
2508
|
+
};
|
|
2509
|
+
});
|
|
2510
|
+
}
|
|
2511
|
+
/**
|
|
2512
|
+
* @note DO NOT USE. STILL UNDER DEVELOPMENT. Tests still failing
|
|
2513
|
+
* Reshape position: withdraw from old bins and deposit to new bins on the SAME position.
|
|
2514
|
+
*
|
|
2515
|
+
* Uses only rebalanceLiquidityAutomation (one IX per TX):
|
|
2516
|
+
* Remove TXs: rebalanceLiquidityAutomation with removeLiquidityParams (~75 bins/TX)
|
|
2517
|
+
* Deposit TXs: rebalanceLiquidityAutomation with addLiquidityParams (~75 bins/TX)
|
|
2518
|
+
*
|
|
2519
|
+
* Meteora's rebalance_liquidity handles position extension and bin array
|
|
2520
|
+
* initialization internally — no separate IXs needed.
|
|
2521
|
+
*
|
|
2522
|
+
* Key differences from rebalanceLargePositionAutomation:
|
|
2523
|
+
* - Same position (no close/create, no new position keypair needed)
|
|
2524
|
+
* - Supports positions of any size (69 to 1400+ bins)
|
|
2525
|
+
*/
|
|
2526
|
+
meteoraRebalanceLargePositionAutomation2(_a) {
|
|
2527
|
+
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
2528
|
+
var _b, _c, _d;
|
|
2529
|
+
try {
|
|
2530
|
+
const WITHDRAW_CHUNK_SIZE = 260;
|
|
2531
|
+
const DEPOSIT_CHUNK_SIZE = 200;
|
|
2532
|
+
const DEPOSIT_CHUNK_INITIAL_SIZE = 150;
|
|
2533
|
+
// Always reserve a small portion of bins for the pivot TX to ensure
|
|
2534
|
+
// withdraw + deposit happen together (prevents ReallocExceedMaxLengthPerInstruction)
|
|
2535
|
+
const PIVOT_RESERVE = 160;
|
|
2536
|
+
// Fetch pool data
|
|
2537
|
+
const dlmmPool = yield meteora_1.MeteoraDLMM.create(connection, params.lbPair, this.ix);
|
|
2538
|
+
const userPda = (0, functions_1.generateUserPda)(params.userWallet);
|
|
2539
|
+
const { binId: activeId } = yield dlmmPool.dlmm.getActiveBin();
|
|
2540
|
+
// Get position's token amounts for deposit strategy calculation
|
|
2541
|
+
const { userPositions } = yield dlmmPool.getPositionsByUserAndLbPair(userPda);
|
|
2542
|
+
const userPosition = userPositions.find(pos => pos.publicKey.equals(params.position));
|
|
2543
|
+
if (!userPosition) {
|
|
2544
|
+
throw new Error(`Position ${params.position.toString()} not found`);
|
|
2545
|
+
}
|
|
2546
|
+
const positionBinData = userPosition.positionData.positionBinData;
|
|
2547
|
+
// Use actual liquidity range (bins with non-zero amounts) instead of padded position struct range
|
|
2548
|
+
const binsWithLiquidity = positionBinData.filter(bin => !new bn_js_1.default(bin.positionXAmount).isZero() || !new bn_js_1.default(bin.positionYAmount).isZero());
|
|
2549
|
+
const oldLowerBinId = binsWithLiquidity.length > 0
|
|
2550
|
+
? binsWithLiquidity[0].binId
|
|
2551
|
+
: userPosition.positionData.lowerBinId;
|
|
2552
|
+
const oldUpperBinId = binsWithLiquidity.length > 0
|
|
2553
|
+
? binsWithLiquidity[binsWithLiquidity.length - 1].binId
|
|
2554
|
+
: userPosition.positionData.upperBinId;
|
|
2555
|
+
const { totalXAmount, totalYAmount } = positionBinData.reduce((acc, bin) => ({
|
|
2556
|
+
totalXAmount: acc.totalXAmount.add(new bn_js_1.default(bin.positionXAmount)),
|
|
2557
|
+
totalYAmount: acc.totalYAmount.add(new bn_js_1.default(bin.positionYAmount)),
|
|
2558
|
+
}), { totalXAmount: new bn_js_1.default(0), totalYAmount: new bn_js_1.default(0) });
|
|
2559
|
+
const newLowerBinId = activeId + params.relativeLowerBin;
|
|
2560
|
+
const newUpperBinId = newLowerBinId + params.width - 1;
|
|
2561
|
+
console.log(`Total chunk per tx (withdraw): ${WITHDRAW_CHUNK_SIZE} bins, total chunk per tx (deposit): ${DEPOSIT_CHUNK_SIZE} bins`);
|
|
2562
|
+
console.log(`old lower bin id: ${oldLowerBinId}, old upper bin id: ${oldUpperBinId}, old width: ${oldUpperBinId - oldLowerBinId + 1}`);
|
|
2563
|
+
console.log(`new lower bin id: ${newLowerBinId}, new upper bin id: ${newUpperBinId}, new width: ${newUpperBinId - newLowerBinId + 1}`);
|
|
2564
|
+
const lbPairState = dlmmPool.dlmm.lbPair;
|
|
2565
|
+
const tokenXMint = lbPairState.tokenXMint;
|
|
2566
|
+
const tokenYMint = lbPairState.tokenYMint;
|
|
2567
|
+
const tokenProgramMap = yield (0, functions_1.getTokenProgramMapForMints)(connection, [tokenXMint, tokenYMint]);
|
|
2568
|
+
const tokenXProgram = tokenProgramMap[tokenXMint.toString()];
|
|
2569
|
+
const tokenYProgram = tokenProgramMap[tokenYMint.toString()];
|
|
2570
|
+
// Fetch transfer hook accounts for token X and Y (Token2022 support)
|
|
2571
|
+
const baseRemainingAccountsInfo = yield (0, functions_1.fetchRemainingAccountsInfo)(connection, tokenXMint, tokenYMint, tokenXProgram, tokenYProgram);
|
|
2572
|
+
// Extract reward infos for claiming rewards during rebalance
|
|
2573
|
+
const rewardInfos = [];
|
|
2574
|
+
const rewardInfosFromPair = lbPairState.rewardInfos;
|
|
2575
|
+
if (rewardInfosFromPair && rewardInfosFromPair.length > 0) {
|
|
2576
|
+
// Collect reward mints that are active (non-default)
|
|
2577
|
+
const activeRewardMints = [];
|
|
2578
|
+
for (const reward of rewardInfosFromPair) {
|
|
2579
|
+
if (!reward.mint.equals(web3.PublicKey.default)) {
|
|
2580
|
+
activeRewardMints.push(reward.mint);
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
// Get token programs for reward mints
|
|
2584
|
+
if (activeRewardMints.length > 0) {
|
|
2585
|
+
const rewardTokenProgramMap = yield (0, functions_1.getTokenProgramMapForMints)(connection, activeRewardMints);
|
|
2586
|
+
for (let i = 0; i < rewardInfosFromPair.length; i++) {
|
|
2587
|
+
const reward = rewardInfosFromPair[i];
|
|
2588
|
+
if (!reward.mint.equals(web3.PublicKey.default)) {
|
|
2589
|
+
rewardInfos.push({
|
|
2590
|
+
rewardIndex: i,
|
|
2591
|
+
rewardVault: reward.vault,
|
|
2592
|
+
rewardMint: reward.mint,
|
|
2593
|
+
rewardTokenProgram: rewardTokenProgramMap[reward.mint.toString()],
|
|
2594
|
+
});
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
console.log(`[Pipeline] Found ${rewardInfos.length} active reward(s) for this pool`);
|
|
2600
|
+
// Build combined remainingAccountsInfo with transfer hooks for X, Y, and rewards
|
|
2601
|
+
// The slices describe: TransferHookX, TransferHookY, TransferHookMultiReward(index) for each reward
|
|
2602
|
+
// IMPORTANT: Always include all slices, even with length=0, so Meteora knows the account structure
|
|
2603
|
+
const combinedSlices = [];
|
|
2604
|
+
const combinedAccounts = [];
|
|
2605
|
+
// Always add TransferHookX and TransferHookY slices (even if length=0)
|
|
2606
|
+
const xSlice = baseRemainingAccountsInfo.slices.find(s => s.accountsType === types_1.RemainingAccountsType.TransferHookX);
|
|
2607
|
+
const ySlice = baseRemainingAccountsInfo.slices.find(s => s.accountsType === types_1.RemainingAccountsType.TransferHookY);
|
|
2608
|
+
combinedSlices.push({ accountsType: types_1.RemainingAccountsType.TransferHookX, length: (_b = xSlice === null || xSlice === void 0 ? void 0 : xSlice.length) !== null && _b !== void 0 ? _b : 0 });
|
|
2609
|
+
combinedSlices.push({ accountsType: types_1.RemainingAccountsType.TransferHookY, length: (_c = ySlice === null || ySlice === void 0 ? void 0 : ySlice.length) !== null && _c !== void 0 ? _c : 0 });
|
|
2610
|
+
// Add transfer hook accounts from base (for X and Y)
|
|
2611
|
+
combinedAccounts.push(...baseRemainingAccountsInfo.accounts);
|
|
2612
|
+
// Fetch transfer hook accounts for each reward and add to combined info
|
|
2613
|
+
// IMPORTANT: Always add TransferHookMultiReward slice for each reward, even if length=0
|
|
2614
|
+
// This tells Meteora about the reward position in the remaining accounts
|
|
2615
|
+
for (const reward of rewardInfos) {
|
|
2616
|
+
const rewardTransferHookInfo = yield (0, functions_1.fetchRemainingAccountsInfoForReward)(connection, reward.rewardMint, reward.rewardTokenProgram);
|
|
2617
|
+
// Always add TransferHookMultiReward slice (even if empty length for standard SPL tokens)
|
|
2618
|
+
const transferHookLength = rewardTransferHookInfo.slices.length > 0
|
|
2619
|
+
? rewardTransferHookInfo.slices[0].length
|
|
2620
|
+
: 0;
|
|
2621
|
+
combinedSlices.push({
|
|
2622
|
+
accountsType: types_1.RemainingAccountsType.TransferHookMultiReward,
|
|
2623
|
+
length: transferHookLength,
|
|
2624
|
+
multiRewardIndex: reward.rewardIndex,
|
|
2625
|
+
});
|
|
2626
|
+
combinedAccounts.push(...rewardTransferHookInfo.accounts);
|
|
2627
|
+
}
|
|
2628
|
+
const remainingAccountsInfo = {
|
|
2629
|
+
slices: combinedSlices,
|
|
2630
|
+
accounts: combinedAccounts,
|
|
2631
|
+
};
|
|
2632
|
+
const distributionToStrategyType = {
|
|
2633
|
+
'SPOT': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-IMBALANCED': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-ONE-SIDE': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-BALANCED': liquidityStrategy_1.StrategyType.SPOT,
|
|
2634
|
+
'CURVE': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-IMBALANCED': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-ONE-SIDE': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-BALANCED': liquidityStrategy_1.StrategyType.CURVE,
|
|
2635
|
+
'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,
|
|
2636
|
+
};
|
|
2637
|
+
const strategyType = (_d = distributionToStrategyType[params.distribution]) !== null && _d !== void 0 ? _d : liquidityStrategy_1.StrategyType.SPOT;
|
|
2638
|
+
const binStep = new bn_js_1.default(dlmmPool.dlmm.lbPair.binStep);
|
|
2639
|
+
// Build new-position deposit chunks
|
|
2640
|
+
const minDeltaId = new bn_js_1.default(newLowerBinId - activeId);
|
|
2641
|
+
const maxDeltaId = new bn_js_1.default(newUpperBinId - activeId);
|
|
2642
|
+
const favorXInActiveId = totalYAmount.isZero() && !totalXAmount.isZero();
|
|
2643
|
+
const strategyParams = (0, liquidityStrategy_1.buildStrategyParameters)(strategyType, totalXAmount, totalYAmount, minDeltaId, maxDeltaId, binStep, new bn_js_1.default(activeId), favorXInActiveId);
|
|
2644
|
+
// =====================================================================
|
|
2645
|
+
// Pipeline algorithm: pack withdraw + deposit work into fewer TXs.
|
|
2646
|
+
//
|
|
2647
|
+
// Given W old bins to withdraw and D new bins to deposit:
|
|
2648
|
+
// Phase 1: Pure withdraw TXs — all withdraw chunks except the last
|
|
2649
|
+
// Phase 2: Pivot TX — last withdraw chunk + extend + initBinArrays
|
|
2650
|
+
// + first deposit (if small enough to fit without CU issues)
|
|
2651
|
+
// Phase 3: Pure deposit TXs — remaining deposit bins by CHUNK_SIZE
|
|
2652
|
+
//
|
|
2653
|
+
// When the total extension is large or the deposit range spans many
|
|
2654
|
+
// bin arrays, the transition IXs (extend + initBinArrays) are placed
|
|
2655
|
+
// in a separate TX to avoid exceeding compute unit limits.
|
|
2656
|
+
// =====================================================================
|
|
2657
|
+
const txInstructions = [];
|
|
2658
|
+
// Helper: build on-chain deposit param from a ChunkedDepositParameters
|
|
2659
|
+
const buildDepositParam = (chunk) => {
|
|
2660
|
+
const p = (0, liquidityStrategy_1.buildBitFlagAndNegateStrategyParameters)(chunk.params.x0, chunk.params.y0, chunk.params.deltaX, chunk.params.deltaY);
|
|
2661
|
+
return {
|
|
2662
|
+
minDeltaId: chunk.minDeltaId.toNumber(),
|
|
2663
|
+
maxDeltaId: chunk.maxDeltaId.toNumber(),
|
|
2664
|
+
x0: p.x0, y0: p.y0, deltaX: p.deltaX, deltaY: p.deltaY,
|
|
2665
|
+
bitFlag: p.bitFlag, favorXInActiveId,
|
|
2666
|
+
};
|
|
2667
|
+
};
|
|
2668
|
+
// Helper: build withdraw IX
|
|
2669
|
+
const buildWithdrawIx = (lower, upper) => __awaiter(this, void 0, void 0, function* () {
|
|
2670
|
+
const binArrayOverrides = this.ix.pda.meteora.deriveBinArrays(params.lbPair, lower, upper);
|
|
2671
|
+
return this.ix.meteoraDlmm.rebalanceLiquidityAutomation({
|
|
2672
|
+
connection,
|
|
2673
|
+
userWallet: params.userWallet,
|
|
2674
|
+
position: params.position,
|
|
2675
|
+
lbPair: params.lbPair,
|
|
2676
|
+
tokenXMint, tokenYMint, tokenXProgram, tokenYProgram,
|
|
2677
|
+
activeId,
|
|
2678
|
+
pdaTokenType: types_1.TokenType.ATA,
|
|
2679
|
+
maxActiveBinSlippage: 300,
|
|
2680
|
+
shouldClaimFee: true,
|
|
2681
|
+
shouldClaimReward: rewardInfos.length > 0,
|
|
2682
|
+
minWithdrawXAmount: new bn_js_1.default(0),
|
|
2683
|
+
maxDepositXAmount: new bn_js_1.default(0),
|
|
2684
|
+
minWithdrawYAmount: new bn_js_1.default(0),
|
|
2685
|
+
maxDepositYAmount: new bn_js_1.default(0),
|
|
2686
|
+
removeLiquidityParams: [{ minBinId: lower, maxBinId: upper, bps: 10000 }],
|
|
2687
|
+
addLiquidityParams: [],
|
|
2688
|
+
binArrayOverrides,
|
|
2689
|
+
shrinkMode: types_3.ShrinkMode.Default,
|
|
2690
|
+
rewardInfos,
|
|
2691
|
+
remainingAccountsInfo,
|
|
2692
|
+
});
|
|
2693
|
+
});
|
|
2694
|
+
// Helper: build deposit IX from a deposit chunk
|
|
2695
|
+
const buildDepositIx = (chunk, removeLiquidityParams) => __awaiter(this, void 0, void 0, function* () {
|
|
2696
|
+
let trueMin = chunk.lowerBinId;
|
|
2697
|
+
let trueMax = chunk.upperBinId;
|
|
2698
|
+
if (removeLiquidityParams) {
|
|
2699
|
+
trueMin = Math.min(chunk.lowerBinId, removeLiquidityParams.minBinId);
|
|
2700
|
+
trueMax = Math.max(chunk.upperBinId, removeLiquidityParams.maxBinId);
|
|
2701
|
+
}
|
|
2702
|
+
const binArrayOverrides = this.ix.pda.meteora.deriveBinArrays(params.lbPair, trueMin, trueMax);
|
|
2703
|
+
return yield this.ix.meteoraDlmm.rebalanceLiquidityAutomation({
|
|
2704
|
+
connection,
|
|
2705
|
+
userWallet: params.userWallet,
|
|
2706
|
+
position: params.position,
|
|
2707
|
+
lbPair: params.lbPair,
|
|
2708
|
+
tokenXMint, tokenYMint, tokenXProgram, tokenYProgram,
|
|
2709
|
+
activeId,
|
|
2710
|
+
pdaTokenType: types_1.TokenType.ATA,
|
|
2711
|
+
maxActiveBinSlippage: 300,
|
|
2712
|
+
shouldClaimFee: removeLiquidityParams ? true : false,
|
|
2713
|
+
shouldClaimReward: !!removeLiquidityParams && rewardInfos.length > 0,
|
|
2714
|
+
minWithdrawXAmount: new bn_js_1.default(0),
|
|
2715
|
+
maxDepositXAmount: chunk.maxAmountX,
|
|
2716
|
+
minWithdrawYAmount: new bn_js_1.default(0),
|
|
2717
|
+
maxDepositYAmount: chunk.maxAmountY,
|
|
2718
|
+
removeLiquidityParams: removeLiquidityParams ? [{ minBinId: removeLiquidityParams.minBinId, maxBinId: removeLiquidityParams.maxBinId, bps: 10000 }] : [],
|
|
2719
|
+
addLiquidityParams: [buildDepositParam(chunk)],
|
|
2720
|
+
binArrayOverrides,
|
|
2721
|
+
shrinkMode: removeLiquidityParams ? types_3.ShrinkMode.Default : types_3.ShrinkMode.NoShrinkBoth,
|
|
2722
|
+
rewardInfos: removeLiquidityParams ? rewardInfos : undefined,
|
|
2723
|
+
remainingAccountsInfo,
|
|
2724
|
+
});
|
|
2725
|
+
});
|
|
2726
|
+
// Build transition IXs: increasePositionLength + initBinArrays.
|
|
2727
|
+
// Bin arrays must be pre-initialized before rebalanceLiquidity deposits.
|
|
2728
|
+
const increasePositionLengthIxs = [];
|
|
2729
|
+
// Generate increasePositionLength IXs to extend position (assumption at the moment, I expect calculation to be wrong)
|
|
2730
|
+
let currentLower = newLowerBinId + WITHDRAW_CHUNK_SIZE;
|
|
2731
|
+
while (currentLower < newUpperBinId) {
|
|
2732
|
+
const currentWidth = newUpperBinId - currentLower + 1;
|
|
2733
|
+
const binsToAdd = Math.min(91, currentWidth);
|
|
2734
|
+
console.log('Increasing position length by', binsToAdd, 'bins at lower', currentLower);
|
|
2735
|
+
increasePositionLengthIxs.push(yield this.ix.meteoraDlmm.increasePositionLengthAutomation({
|
|
2736
|
+
connection,
|
|
2737
|
+
userWallet: params.userWallet,
|
|
2738
|
+
lbPair: params.lbPair,
|
|
2739
|
+
position: params.position,
|
|
2740
|
+
lengthToAdd: binsToAdd,
|
|
2741
|
+
side: types_1.MeteoraPositionSide.Upper,
|
|
2742
|
+
}));
|
|
2743
|
+
currentLower = currentLower + binsToAdd;
|
|
2744
|
+
}
|
|
2745
|
+
// Build all deposit chunks upfront
|
|
2746
|
+
const depositChunks = (0, liquidityStrategy_1.chunkDepositParametersWithInitialSize)(strategyParams, minDeltaId, maxDeltaId, new bn_js_1.default(activeId), binStep, DEPOSIT_CHUNK_INITIAL_SIZE, DEPOSIT_CHUNK_SIZE, favorXInActiveId);
|
|
2747
|
+
// Build old-position withdraw chunks, reserving PIVOT_RESERVE bins for pivot TX
|
|
2748
|
+
const oldWidth = oldUpperBinId - oldLowerBinId + 1;
|
|
2749
|
+
const oldChunks = [];
|
|
2750
|
+
let current = oldLowerBinId;
|
|
2751
|
+
// Build Phase 1 withdraw chunks (before pivot)
|
|
2752
|
+
while (current < oldUpperBinId) {
|
|
2753
|
+
const upper = Math.min(WITHDRAW_CHUNK_SIZE - 1, oldUpperBinId - current) + current;
|
|
2754
|
+
console.log(`Generating withdraw chunk: [${current}, ${upper}, ${upper - current + 1} bins]`);
|
|
2755
|
+
oldChunks.push({ lower: current, upper });
|
|
2756
|
+
current = upper + 1;
|
|
2757
|
+
}
|
|
2758
|
+
const remainder = oldWidth % WITHDRAW_CHUNK_SIZE;
|
|
2759
|
+
if (remainder === 0 || remainder > PIVOT_RESERVE) {
|
|
2760
|
+
// Pop last chunk to reserve for pivot
|
|
2761
|
+
const popped = oldChunks.pop();
|
|
2762
|
+
// Then insert bigger and final chunk for pivot
|
|
2763
|
+
const upper = popped.upper - PIVOT_RESERVE;
|
|
2764
|
+
oldChunks.push({ lower: popped.lower, upper });
|
|
2765
|
+
oldChunks.push({ lower: upper + 1, upper: popped.upper });
|
|
2766
|
+
}
|
|
2767
|
+
// =====================================================================
|
|
2768
|
+
// Phase 1: Pure withdraw TXs — all chunks in oldChunks (before pivot)
|
|
2769
|
+
// =====================================================================
|
|
2770
|
+
for (const chunk of oldChunks) {
|
|
2771
|
+
console.log(`Withdrawing from chunk: [${chunk.lower}, ${chunk.upper}]`);
|
|
2772
|
+
txInstructions.push([yield buildWithdrawIx(chunk.lower, chunk.upper)]);
|
|
2773
|
+
}
|
|
2774
|
+
// Pop the last one
|
|
2775
|
+
txInstructions.pop();
|
|
2776
|
+
// Get the last withdraw chunk for pivot
|
|
2777
|
+
const lastWithdrawChunk = oldChunks[oldChunks.length - 1];
|
|
2778
|
+
// =====================================================================
|
|
2779
|
+
// Phase 2 (Pivot TX): Withdraw pivot reserve + first deposit
|
|
2780
|
+
// This ensures bins "jump" to new position, preventing ReallocExceedMaxLengthPerInstruction
|
|
2781
|
+
// =====================================================================
|
|
2782
|
+
txInstructions.push([
|
|
2783
|
+
yield buildDepositIx(depositChunks[0], { minBinId: lastWithdrawChunk.lower, maxBinId: lastWithdrawChunk.upper }),
|
|
2784
|
+
...increasePositionLengthIxs,
|
|
2785
|
+
]);
|
|
2786
|
+
// =====================================================================
|
|
2787
|
+
// Phase 3: Remaining deposit TXs
|
|
2788
|
+
// - Second deposit (i=1): includes increasePositionLengthIxs if needed
|
|
2789
|
+
// - Subsequent deposits (i=2+): pure deposit
|
|
2790
|
+
// =====================================================================
|
|
2791
|
+
for (let i = 1; i < depositChunks.length; i++) {
|
|
2792
|
+
const chunk = depositChunks[i];
|
|
2793
|
+
txInstructions.push([yield buildDepositIx(chunk)]);
|
|
2794
|
+
}
|
|
2795
|
+
// Wrap each instruction array with createTransactionMeta
|
|
2796
|
+
const transactions = [];
|
|
2797
|
+
for (let i = 0; i < txInstructions.length; i++) {
|
|
2798
|
+
transactions.push(yield (0, functions_1.createTransactionMeta)({
|
|
2799
|
+
payer: params.userWallet,
|
|
2800
|
+
description: `Automation IX: Reshape position (tx ${i + 1}/${txInstructions.length})`,
|
|
2801
|
+
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
2802
|
+
mainInstructions: txInstructions[i],
|
|
2803
|
+
}));
|
|
2804
|
+
}
|
|
2805
|
+
return transactions;
|
|
2806
|
+
}
|
|
2807
|
+
catch (error) {
|
|
2808
|
+
throw error;
|
|
2809
|
+
}
|
|
2810
|
+
});
|
|
2811
|
+
}
|
|
2812
|
+
/**
|
|
2813
|
+
* Build raw instruction arrays for depositing liquidity to a large Meteora DLMM position
|
|
2814
|
+
* using rebalanceLiquidityAutomation (automation variant).
|
|
2815
|
+
* Skips depositMultipleTokenV2 since tokens are already in ATA from the withdraw phase.
|
|
2816
|
+
*/
|
|
2817
|
+
_meteoraDepositToLargePositionAutomation(params) {
|
|
2818
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2819
|
+
const MAX_BINS_PER_TX = 149;
|
|
2820
|
+
const { connection, userWallet, lbPair, position, tokenXMint, tokenYMint, tokenXProgram, tokenYProgram, lowerBinId, upperBinId, strategyType, activeId, totalXAmount, totalYAmount, } = params;
|
|
2821
|
+
const dlmmPool = yield meteora_1.MeteoraDLMM.create(connection, lbPair, this.ix);
|
|
2822
|
+
const binStep = new bn_js_1.default(dlmmPool.dlmm.lbPair.binStep);
|
|
2823
|
+
// Calculate delta IDs relative to active bin for entire position
|
|
2824
|
+
const minDeltaId = new bn_js_1.default(lowerBinId - activeId);
|
|
2825
|
+
const maxDeltaId = new bn_js_1.default(upperBinId - activeId);
|
|
2826
|
+
// Auto-determine favorXInActiveId from deposit amounts (matches Meteora SDK behavior)
|
|
2827
|
+
const favorXInActiveId = totalYAmount.isZero() && !totalXAmount.isZero();
|
|
2828
|
+
// Build strategy parameters ONCE for entire position using actual withdrawn amounts
|
|
2829
|
+
const strategyParams = (0, liquidityStrategy_1.buildStrategyParameters)(strategyType, totalXAmount, totalYAmount, minDeltaId, maxDeltaId, binStep, new bn_js_1.default(activeId), favorXInActiveId);
|
|
2830
|
+
// Chunk the deposit using the same strategy parameters
|
|
2831
|
+
const chunks = (0, liquidityStrategy_1.chunkDepositParameters)(strategyParams, minDeltaId, maxDeltaId, new bn_js_1.default(activeId), binStep, MAX_BINS_PER_TX, favorXInActiveId);
|
|
2832
|
+
const txInstructions = [];
|
|
2833
|
+
for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {
|
|
2834
|
+
const chunk = chunks[chunkIndex];
|
|
2835
|
+
// Convert strategy parameters to on-chain format with bitFlag for negative values
|
|
2836
|
+
const onChainParams = (0, liquidityStrategy_1.buildBitFlagAndNegateStrategyParameters)(chunk.params.x0, chunk.params.y0, chunk.params.deltaX, chunk.params.deltaY);
|
|
2837
|
+
const instructions = [];
|
|
2838
|
+
// Add initialize bin arrays instruction
|
|
2839
|
+
const initBinArraysIx = yield this.ix.meteoraDlmm.meteoraDlmmInitializeBinArray({
|
|
2840
|
+
connection,
|
|
2841
|
+
userWallet,
|
|
2842
|
+
lbPair,
|
|
2843
|
+
lowerBinId: chunk.lowerBinId,
|
|
2844
|
+
upperBinId: chunk.upperBinId,
|
|
2845
|
+
});
|
|
2846
|
+
instructions.push(initBinArraysIx);
|
|
2847
|
+
// Add liquidity via rebalanceLiquidityAutomation
|
|
2848
|
+
const depositIx = yield this.ix.meteoraDlmm.rebalanceLiquidityAutomation({
|
|
2849
|
+
connection,
|
|
2850
|
+
userWallet,
|
|
2851
|
+
position,
|
|
2852
|
+
lbPair,
|
|
2853
|
+
tokenXMint,
|
|
2854
|
+
tokenYMint,
|
|
2855
|
+
tokenXProgram,
|
|
2856
|
+
tokenYProgram,
|
|
2857
|
+
activeId,
|
|
2858
|
+
pdaTokenType: types_1.TokenType.ATA,
|
|
2859
|
+
maxActiveBinSlippage: 300,
|
|
2860
|
+
shouldClaimFee: false,
|
|
2861
|
+
shouldClaimReward: false,
|
|
2862
|
+
minWithdrawXAmount: new bn_js_1.default(0),
|
|
2863
|
+
maxDepositXAmount: chunk.maxAmountX,
|
|
2864
|
+
minWithdrawYAmount: new bn_js_1.default(0),
|
|
2865
|
+
maxDepositYAmount: chunk.maxAmountY,
|
|
2866
|
+
removeLiquidityParams: [],
|
|
2867
|
+
addLiquidityParams: [{
|
|
2868
|
+
minDeltaId: chunk.minDeltaId.toNumber(),
|
|
2869
|
+
maxDeltaId: chunk.maxDeltaId.toNumber(),
|
|
2870
|
+
x0: onChainParams.x0,
|
|
2871
|
+
y0: onChainParams.y0,
|
|
2872
|
+
deltaX: onChainParams.deltaX,
|
|
2873
|
+
deltaY: onChainParams.deltaY,
|
|
2874
|
+
bitFlag: onChainParams.bitFlag,
|
|
2875
|
+
favorXInActiveId,
|
|
2876
|
+
}],
|
|
2877
|
+
});
|
|
2878
|
+
instructions.push(depositIx);
|
|
2879
|
+
txInstructions.push(instructions);
|
|
2880
|
+
}
|
|
2881
|
+
return txInstructions;
|
|
2882
|
+
});
|
|
2883
|
+
}
|
|
2884
|
+
/**
|
|
2885
|
+
* Build merged rebalance chunks that combine withdraw + init/extend + deposit
|
|
2886
|
+
* into fewer transactions. Each TX handles ~75 bins of withdraw and deposit,
|
|
2887
|
+
* significantly reducing the total number of transactions needed.
|
|
2888
|
+
*
|
|
2889
|
+
* Per-chunk instructions (in order):
|
|
2890
|
+
* [First TX only] initializePositionRelativeAutomation (≤70 bins)
|
|
2891
|
+
* [If position not yet covers this chunk] increasePositionLengthAutomation (≤91 bins each)
|
|
2892
|
+
* removeLiquidityByRange2Automation (≤75 bins from old position)
|
|
2893
|
+
* claimFee2Automation (≤75 bins from old position)
|
|
2894
|
+
* claimReward2Automation × N (≤75 bins from old position, per active reward)
|
|
2895
|
+
* [Last old chunk only] closePosition2Automation
|
|
2896
|
+
* meteoraDlmmInitializeBinArray (≤75 bins for new position)
|
|
2897
|
+
* rebalanceLiquidityAutomation (≤75 bins deposit to new position)
|
|
2898
|
+
*/
|
|
2899
|
+
_buildMergedRebalanceChunks(params) {
|
|
2900
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2901
|
+
const CHUNK_SIZE = 75;
|
|
2902
|
+
const MAX_INIT_WIDTH = 70;
|
|
2903
|
+
const MAX_EXTEND_LENGTH = 91;
|
|
2904
|
+
const { connection, userWallet, lbPair, oldPosition, oldLowerBinId, oldUpperBinId, newPosition, relativeLowerBinId, width, tokenXMint, tokenYMint, tokenXProgram, tokenYProgram, remainingAccountsInfo, rewardInfos, pdaTokenTypeForClaimables, binStep, strategyType, activeId, totalXAmount, totalYAmount, } = params;
|
|
2905
|
+
// Pre-resolve reward info (avoid repeated RPC calls)
|
|
2906
|
+
const rewardIxBuilders = [];
|
|
2907
|
+
for (let rewardIndex = 0; rewardIndex < 2; rewardIndex++) {
|
|
2908
|
+
const rewardInfo = rewardInfos[rewardIndex];
|
|
2909
|
+
if (!rewardInfo || rewardInfo.mint.equals(web3.PublicKey.default)) {
|
|
2910
|
+
continue;
|
|
2911
|
+
}
|
|
2912
|
+
const rewardTokenProgramMap = yield (0, functions_1.getTokenProgramMapForMints)(connection, [rewardInfo.mint]);
|
|
2913
|
+
const rewardTokenProgram = rewardTokenProgramMap[rewardInfo.mint.toString()];
|
|
2914
|
+
const rewardRemainingAccountsInfo = yield (0, functions_1.fetchRemainingAccountsInfoForReward)(connection, rewardInfo.mint, rewardTokenProgram);
|
|
2915
|
+
rewardIxBuilders.push({
|
|
2916
|
+
rewardIndex,
|
|
2917
|
+
rewardMint: rewardInfo.mint,
|
|
2918
|
+
rewardVault: rewardInfo.vault,
|
|
2919
|
+
rewardTokenProgram,
|
|
2920
|
+
rewardRemainingAccountsInfo,
|
|
2921
|
+
});
|
|
2922
|
+
}
|
|
2923
|
+
// Build old-position withdraw chunks (75 bins each)
|
|
2924
|
+
const oldChunks = [];
|
|
2925
|
+
let current = oldLowerBinId;
|
|
2926
|
+
while (current <= oldUpperBinId) {
|
|
2927
|
+
const upper = Math.min(current + CHUNK_SIZE - 1, oldUpperBinId);
|
|
2928
|
+
oldChunks.push({ lower: current, upper });
|
|
2929
|
+
current = upper + 1;
|
|
2930
|
+
}
|
|
2931
|
+
// Build new-position deposit chunks
|
|
2932
|
+
const newLowerBinId = activeId + relativeLowerBinId;
|
|
2933
|
+
const newUpperBinId = newLowerBinId + width - 1;
|
|
2934
|
+
const minDeltaId = new bn_js_1.default(newLowerBinId - activeId);
|
|
2935
|
+
const maxDeltaId = new bn_js_1.default(newUpperBinId - activeId);
|
|
2936
|
+
const favorXInActiveId = totalYAmount.isZero() && !totalXAmount.isZero();
|
|
2937
|
+
const strategyParams = (0, liquidityStrategy_1.buildStrategyParameters)(strategyType, totalXAmount, totalYAmount, minDeltaId, maxDeltaId, binStep, new bn_js_1.default(activeId), favorXInActiveId);
|
|
2938
|
+
const depositChunks = (0, liquidityStrategy_1.chunkDepositParameters)(strategyParams, minDeltaId, maxDeltaId, new bn_js_1.default(activeId), binStep, CHUNK_SIZE, favorXInActiveId);
|
|
2939
|
+
const totalChunks = Math.max(oldChunks.length, depositChunks.length);
|
|
2940
|
+
const txInstructions = [];
|
|
2941
|
+
let positionBinsAllocated = 0;
|
|
2942
|
+
for (let i = 0; i < totalChunks; i++) {
|
|
2943
|
+
const instructions = [];
|
|
2944
|
+
// === Init/extend new position for this chunk's deposit range ===
|
|
2945
|
+
if (i === 0) {
|
|
2946
|
+
const initWidth = Math.min(MAX_INIT_WIDTH, width);
|
|
2947
|
+
const initPositionIx = yield this.ix.meteoraDlmm.initializePositionRelativeAutomation(connection, {
|
|
2948
|
+
userWallet,
|
|
2949
|
+
lbPair,
|
|
2950
|
+
position: newPosition,
|
|
2951
|
+
relativeLowerBinId,
|
|
2952
|
+
width: initWidth,
|
|
2953
|
+
});
|
|
2954
|
+
instructions.push(initPositionIx);
|
|
2955
|
+
positionBinsAllocated = initWidth;
|
|
2956
|
+
}
|
|
2957
|
+
// Extend position to cover this chunk's deposit range
|
|
2958
|
+
if (i < depositChunks.length) {
|
|
2959
|
+
const binsNeeded = Math.min((i + 1) * CHUNK_SIZE, width);
|
|
2960
|
+
while (positionBinsAllocated < binsNeeded) {
|
|
2961
|
+
const toAdd = Math.min(MAX_EXTEND_LENGTH, binsNeeded - positionBinsAllocated);
|
|
2962
|
+
const extendIx = yield this.ix.meteoraDlmm.increasePositionLengthAutomation({
|
|
2963
|
+
connection,
|
|
2964
|
+
userWallet,
|
|
2965
|
+
lbPair,
|
|
2966
|
+
position: newPosition,
|
|
2967
|
+
lengthToAdd: toAdd,
|
|
2968
|
+
side: types_1.MeteoraPositionSide.Upper,
|
|
2969
|
+
});
|
|
2970
|
+
instructions.push(extendIx);
|
|
2971
|
+
positionBinsAllocated += toAdd;
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2974
|
+
// === Withdraw from old position ===
|
|
2975
|
+
if (i < oldChunks.length) {
|
|
2976
|
+
const chunk = oldChunks[i];
|
|
2977
|
+
const isLastOldChunk = (i === oldChunks.length - 1);
|
|
2978
|
+
const removeLiquidityIx = yield this.ix.meteoraDlmm.removeLiquidityByRange2Automation({
|
|
2979
|
+
connection,
|
|
2980
|
+
userWallet,
|
|
2981
|
+
lbPair,
|
|
2982
|
+
position: oldPosition,
|
|
2983
|
+
tokenXMint,
|
|
2984
|
+
tokenYMint,
|
|
2985
|
+
tokenXProgram,
|
|
2986
|
+
tokenYProgram,
|
|
2987
|
+
fromBinId: chunk.lower,
|
|
2988
|
+
toBinId: chunk.upper,
|
|
2989
|
+
bpsToRemove: 10000, // 100%
|
|
2990
|
+
pdaTokenType: types_1.TokenType.ATA,
|
|
2991
|
+
remainingAccountsInfo,
|
|
2992
|
+
});
|
|
2993
|
+
instructions.push(removeLiquidityIx);
|
|
2994
|
+
const claimFeeIx = yield this.ix.meteoraDlmm.claimFee2Automation(connection, {
|
|
2995
|
+
userWallet,
|
|
2996
|
+
lbPair,
|
|
2997
|
+
position: oldPosition,
|
|
2998
|
+
tokenMintX: tokenXMint,
|
|
2999
|
+
tokenMintY: tokenYMint,
|
|
3000
|
+
tokenProgramX: tokenXProgram,
|
|
3001
|
+
tokenProgramY: tokenYProgram,
|
|
3002
|
+
lowerBinId: chunk.lower,
|
|
3003
|
+
upperBinId: chunk.upper,
|
|
3004
|
+
pdaTokenType: pdaTokenTypeForClaimables,
|
|
3005
|
+
remainingAccountsInfo,
|
|
3006
|
+
});
|
|
3007
|
+
instructions.push(claimFeeIx);
|
|
3008
|
+
for (const reward of rewardIxBuilders) {
|
|
3009
|
+
const claimRewardIx = yield this.ix.meteoraDlmm.claimReward2Automation(connection, {
|
|
3010
|
+
userWallet,
|
|
3011
|
+
lbPair,
|
|
3012
|
+
position: oldPosition,
|
|
3013
|
+
rewardIndex: reward.rewardIndex,
|
|
3014
|
+
rewardMint: reward.rewardMint,
|
|
3015
|
+
rewardVault: reward.rewardVault,
|
|
3016
|
+
tokenProgram: reward.rewardTokenProgram,
|
|
3017
|
+
lowerBinId: chunk.lower,
|
|
3018
|
+
upperBinId: chunk.upper,
|
|
3019
|
+
pdaTokenType: types_1.TokenType.STA,
|
|
3020
|
+
remainingAccountsInfo: reward.rewardRemainingAccountsInfo,
|
|
3021
|
+
});
|
|
3022
|
+
instructions.push(claimRewardIx);
|
|
3023
|
+
}
|
|
3024
|
+
if (isLastOldChunk) {
|
|
3025
|
+
const closePositionIx = yield this.ix.meteoraDlmm.closePosition2Automation({
|
|
3026
|
+
connection,
|
|
3027
|
+
userWallet,
|
|
3028
|
+
position: oldPosition,
|
|
3029
|
+
lbPair,
|
|
3030
|
+
});
|
|
3031
|
+
instructions.push(closePositionIx);
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
3034
|
+
// === Deposit to new position ===
|
|
3035
|
+
if (i < depositChunks.length) {
|
|
3036
|
+
const chunk = depositChunks[i];
|
|
3037
|
+
const onChainParams = (0, liquidityStrategy_1.buildBitFlagAndNegateStrategyParameters)(chunk.params.x0, chunk.params.y0, chunk.params.deltaX, chunk.params.deltaY);
|
|
3038
|
+
const initBinArraysIx = yield this.ix.meteoraDlmm.meteoraDlmmInitializeBinArray({
|
|
3039
|
+
connection,
|
|
3040
|
+
userWallet,
|
|
3041
|
+
lbPair,
|
|
3042
|
+
lowerBinId: chunk.lowerBinId,
|
|
3043
|
+
upperBinId: chunk.upperBinId,
|
|
3044
|
+
});
|
|
3045
|
+
instructions.push(initBinArraysIx);
|
|
3046
|
+
const depositIx = yield this.ix.meteoraDlmm.rebalanceLiquidityAutomation({
|
|
3047
|
+
connection,
|
|
3048
|
+
userWallet,
|
|
3049
|
+
position: newPosition,
|
|
3050
|
+
lbPair,
|
|
3051
|
+
tokenXMint,
|
|
3052
|
+
tokenYMint,
|
|
3053
|
+
tokenXProgram,
|
|
3054
|
+
tokenYProgram,
|
|
3055
|
+
activeId,
|
|
3056
|
+
pdaTokenType: types_1.TokenType.ATA,
|
|
3057
|
+
maxActiveBinSlippage: 300,
|
|
3058
|
+
shouldClaimFee: false,
|
|
3059
|
+
shouldClaimReward: false,
|
|
3060
|
+
minWithdrawXAmount: new bn_js_1.default(0),
|
|
3061
|
+
maxDepositXAmount: chunk.maxAmountX,
|
|
3062
|
+
minWithdrawYAmount: new bn_js_1.default(0),
|
|
3063
|
+
maxDepositYAmount: chunk.maxAmountY,
|
|
3064
|
+
removeLiquidityParams: [],
|
|
3065
|
+
addLiquidityParams: [{
|
|
3066
|
+
minDeltaId: chunk.minDeltaId.toNumber(),
|
|
3067
|
+
maxDeltaId: chunk.maxDeltaId.toNumber(),
|
|
3068
|
+
x0: onChainParams.x0,
|
|
3069
|
+
y0: onChainParams.y0,
|
|
3070
|
+
deltaX: onChainParams.deltaX,
|
|
3071
|
+
deltaY: onChainParams.deltaY,
|
|
3072
|
+
bitFlag: onChainParams.bitFlag,
|
|
3073
|
+
favorXInActiveId,
|
|
3074
|
+
}],
|
|
3075
|
+
});
|
|
3076
|
+
instructions.push(depositIx);
|
|
3077
|
+
}
|
|
3078
|
+
txInstructions.push(instructions);
|
|
3079
|
+
}
|
|
3080
|
+
return txInstructions;
|
|
3081
|
+
});
|
|
3082
|
+
}
|
|
3083
|
+
openAutomationIx(_a) {
|
|
3084
|
+
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
3085
|
+
const dlmmPool = yield meteora_1.MeteoraDLMM.create(connection, params.pool, this.ix);
|
|
3086
|
+
const userPda = (0, functions_1.generateUserPda)(params.userWallet);
|
|
3087
|
+
// Open new position
|
|
3088
|
+
const initPositionAndAddLiquidityBuilder = yield dlmmPool.initializePositionAndAddLiquidityByStrategy(connection, params.userWallet, addresses_1.HS_AUTHORITY, {
|
|
3089
|
+
positionPubKey: params.position,
|
|
3090
|
+
user: userPda,
|
|
3091
|
+
slippage: params.slippage,
|
|
3092
|
+
totalXAmount: new bn_js_1.default(100000), // This is overriden on-chain, so value here do not matter
|
|
3093
|
+
totalYAmount: new bn_js_1.default(100000), // This is overriden on-chain, so value here do not matter
|
|
3094
|
+
strategy: {
|
|
3095
|
+
maxBinId: params.binRange.upperRange,
|
|
3096
|
+
minBinId: params.binRange.lowerRange,
|
|
3097
|
+
strategyType: types_3.StrategyTypeMap[params.distribution],
|
|
3098
|
+
},
|
|
3099
|
+
skipInputTokenCheck: true, // Rebalance should be independent of user wallet TA
|
|
3100
|
+
opt: {
|
|
3101
|
+
pdaTokenType: params.opt.pdaTokenType,
|
|
3102
|
+
withAmount: {
|
|
3103
|
+
userTokenXAmount: params.opt.withAmount.userTokenXAmount,
|
|
3104
|
+
userTokenYAmount: params.opt.withAmount.userTokenYAmount,
|
|
3105
|
+
},
|
|
3106
|
+
fuelAccount: params.opt.fuelAccount,
|
|
3107
|
+
},
|
|
3108
|
+
}, hsToMeteora_1.meteoraToHawksightAutomationIxs);
|
|
3109
|
+
const mainInstructions = [...initPositionAndAddLiquidityBuilder.mainIxs];
|
|
3110
|
+
return (0, functions_1.createTransactionMeta)({
|
|
3111
|
+
payer: params.userWallet,
|
|
3112
|
+
description: 'Automation IX: Meteora Open instruction (Open position)',
|
|
3113
|
+
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
3114
|
+
mainInstructions,
|
|
3115
|
+
});
|
|
3116
|
+
});
|
|
3117
|
+
}
|
|
3118
|
+
relativeOpenAutomationIx(_a) {
|
|
3119
|
+
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
3120
|
+
const { userWallet, pool, position, relativeBinRange, checkRange, distribution, targetActiveBin, fuelAccount, pdaTokenType, userTokenXAmount, userTokenYAmount } = params;
|
|
3121
|
+
const openParams = {
|
|
3122
|
+
userWallet,
|
|
3123
|
+
lbPair: pool,
|
|
3124
|
+
position,
|
|
3125
|
+
relativeLowerBinId: relativeBinRange.lowerRange,
|
|
3126
|
+
relativeUpperBinId: relativeBinRange.upperRange,
|
|
3127
|
+
strategyType: types_3.StrategyTypeMap[distribution],
|
|
3128
|
+
checkRange: {
|
|
3129
|
+
minBinId: checkRange.lowerRange,
|
|
3130
|
+
maxBinId: checkRange.upperRange,
|
|
3131
|
+
},
|
|
3132
|
+
targetActiveBin: targetActiveBin !== null && targetActiveBin !== void 0 ? targetActiveBin : undefined,
|
|
3133
|
+
useFuelAccount: fuelAccount !== null && fuelAccount !== void 0 ? fuelAccount : false,
|
|
3134
|
+
pdaTokenType,
|
|
3135
|
+
userTokenXAmount,
|
|
3136
|
+
userTokenYAmount,
|
|
3137
|
+
};
|
|
3138
|
+
const openIx = yield this.ix.meteoraDlmm.relativeOpenAutomation(connection, openParams);
|
|
3139
|
+
return (0, functions_1.createTransactionMeta)({
|
|
3140
|
+
payer: params.userWallet,
|
|
3141
|
+
description: 'Automation IX: Meteora Open with relative bins',
|
|
1932
3142
|
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
1933
3143
|
mainInstructions: [openIx],
|
|
1934
3144
|
});
|
|
@@ -2330,25 +3540,32 @@ class Transactions {
|
|
|
2330
3540
|
throw new Error(`Unexpected error: Cannot find "RemoveLiquidityByRange" or "RemoveLiquidityByRange2" instruction from instructions`);
|
|
2331
3541
|
}
|
|
2332
3542
|
/**
|
|
2333
|
-
* Creates a large position with more than 69 bins
|
|
2334
|
-
*
|
|
3543
|
+
* Creates a large position with more than 69 bins.
|
|
3544
|
+
* Splits into multiple transactions if needed (max 1344 bins per tx, max 1400 bins total).
|
|
2335
3545
|
*
|
|
2336
3546
|
* @param connection - Solana connection
|
|
2337
3547
|
* @param params - Parameters containing userWallet, lbPair, position, lowerBinId, upperBinId
|
|
2338
|
-
* @returns Array of
|
|
3548
|
+
* @returns Array of TransactionMetadataResponse (one per transaction)
|
|
2339
3549
|
*/
|
|
2340
|
-
|
|
3550
|
+
/**
|
|
3551
|
+
* Build raw instruction arrays for initializing a large Meteora DLMM position.
|
|
3552
|
+
* Returns a 2D array where each inner array is one transaction's worth of instructions.
|
|
3553
|
+
*/
|
|
3554
|
+
_meteoraInitializeLargePosition(_a) {
|
|
2341
3555
|
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
2342
3556
|
const MAX_INIT_WIDTH = 70; // Maximum bins for initialize position
|
|
2343
3557
|
const MAX_EXTEND_LENGTH = 91; // Maximum bins per increasePositionLength call
|
|
3558
|
+
const MAX_BINS_PER_TX = 1344; // Maximum bins that fit in a single transaction
|
|
2344
3559
|
const { userWallet, lbPair, position, lowerBinId, upperBinId } = params;
|
|
2345
3560
|
// Calculate total bins needed
|
|
2346
3561
|
const totalBins = upperBinId - lowerBinId + 1;
|
|
2347
|
-
// Validate bin count (max
|
|
2348
|
-
if (totalBins >
|
|
2349
|
-
throw new Error(`Position size ${totalBins} bins exceeds maximum of
|
|
3562
|
+
// Validate bin count (max 1400 supported by Meteora)
|
|
3563
|
+
if (totalBins > 1400) {
|
|
3564
|
+
throw new Error(`Position size ${totalBins} bins exceeds maximum of 1400 bins`);
|
|
2350
3565
|
}
|
|
2351
|
-
const
|
|
3566
|
+
const txInstructions = [];
|
|
3567
|
+
let instructions = [];
|
|
3568
|
+
let binsInCurrentTx = 0;
|
|
2352
3569
|
// Step 1: Create initialize position (from lowerBinId with max 70 bins)
|
|
2353
3570
|
const initWidth = Math.min(totalBins, MAX_INIT_WIDTH);
|
|
2354
3571
|
const initializePositionIx = yield this.ix.meteoraDlmm.initializePosition({
|
|
@@ -2360,46 +3577,63 @@ class Transactions {
|
|
|
2360
3577
|
width: initWidth,
|
|
2361
3578
|
});
|
|
2362
3579
|
instructions.push(initializePositionIx);
|
|
3580
|
+
binsInCurrentTx += initWidth;
|
|
2363
3581
|
// Step 2: Extend to the right in chunks of 91 bins if needed
|
|
2364
|
-
// After init, we have bins from lowerBinId to lowerBinId + initWidth - 1
|
|
2365
|
-
// We need to extend up to upperBinId
|
|
2366
3582
|
let currentUpperBinId = lowerBinId + initWidth - 1;
|
|
2367
3583
|
while (currentUpperBinId < upperBinId) {
|
|
2368
|
-
// Calculate how many bins to add (max 91)
|
|
2369
3584
|
const binsRemaining = upperBinId - currentUpperBinId;
|
|
2370
3585
|
const binsToAdd = Math.min(binsRemaining, MAX_EXTEND_LENGTH);
|
|
3586
|
+
// Check if adding this extension would exceed max bins per tx
|
|
3587
|
+
if (binsInCurrentTx + binsToAdd > MAX_BINS_PER_TX) {
|
|
3588
|
+
txInstructions.push(instructions);
|
|
3589
|
+
instructions = [];
|
|
3590
|
+
binsInCurrentTx = 0;
|
|
3591
|
+
}
|
|
2371
3592
|
const extendIx = yield this.ix.meteoraDlmm.increasePositionLength({
|
|
2372
3593
|
connection,
|
|
2373
3594
|
userWallet,
|
|
2374
3595
|
lbPair,
|
|
2375
3596
|
position,
|
|
2376
3597
|
lengthToAdd: binsToAdd,
|
|
2377
|
-
side: types_1.MeteoraPositionSide.Upper,
|
|
3598
|
+
side: types_1.MeteoraPositionSide.Upper,
|
|
2378
3599
|
});
|
|
2379
3600
|
instructions.push(extendIx);
|
|
3601
|
+
binsInCurrentTx += binsToAdd;
|
|
2380
3602
|
currentUpperBinId += binsToAdd;
|
|
2381
3603
|
}
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
3604
|
+
// Finalize remaining instructions
|
|
3605
|
+
if (instructions.length > 0) {
|
|
3606
|
+
txInstructions.push(instructions);
|
|
3607
|
+
}
|
|
3608
|
+
return txInstructions;
|
|
3609
|
+
});
|
|
3610
|
+
}
|
|
3611
|
+
meteoraInitializeLargePosition(_a) {
|
|
3612
|
+
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
3613
|
+
const txInstructions = yield this._meteoraInitializeLargePosition({ connection, params });
|
|
3614
|
+
const transactions = [];
|
|
3615
|
+
for (const instructions of txInstructions) {
|
|
3616
|
+
transactions.push(yield (0, functions_1.createTransactionMeta)({
|
|
3617
|
+
payer: params.userWallet,
|
|
3618
|
+
description: 'Initialize large Meteora DLMM Position',
|
|
3619
|
+
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
3620
|
+
mainInstructions: instructions,
|
|
3621
|
+
}));
|
|
3622
|
+
}
|
|
3623
|
+
return transactions;
|
|
2388
3624
|
});
|
|
2389
3625
|
}
|
|
2390
3626
|
/**
|
|
2391
|
-
*
|
|
2392
|
-
*
|
|
2393
|
-
* to stay within Solana's account limit per transaction.
|
|
2394
|
-
*
|
|
2395
|
-
* @param connection - Solana connection
|
|
2396
|
-
* @param params - MeteoraDepositToLargePosition parameters
|
|
2397
|
-
* @returns Array of TransactionMetadataResponse, one per chunk
|
|
3627
|
+
* Build raw instruction arrays for depositing liquidity to a large Meteora DLMM position.
|
|
3628
|
+
* Returns a 2D array where each inner array is one transaction's worth of instructions.
|
|
2398
3629
|
*/
|
|
2399
|
-
|
|
3630
|
+
_meteoraDepositToLargePosition(_a) {
|
|
2400
3631
|
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
2401
3632
|
const MAX_BINS_PER_TX = 149;
|
|
2402
|
-
const { userWallet, lbPair, position, tokenXMint, tokenYMint, tokenXProgram, tokenYProgram, lowerBinId, upperBinId, totalXAmount, totalYAmount, strategyType = liquidityStrategy_1.StrategyType.SPOT,
|
|
3633
|
+
const { userWallet, lbPair, position, tokenXMint, tokenYMint, tokenXProgram, tokenYProgram, lowerBinId, upperBinId, totalXAmount, totalYAmount, strategyType = liquidityStrategy_1.StrategyType.SPOT, pdaTokenType, } = params;
|
|
3634
|
+
// Auto-determine favorXInActiveId from deposit amounts (matches Meteora SDK behavior):
|
|
3635
|
+
// Single-sided X deposit (Y is zero) → favor X in active bin
|
|
3636
|
+
const favorXInActiveId = totalYAmount.isZero() && !totalXAmount.isZero();
|
|
2403
3637
|
const dlmmPool = yield meteora_1.MeteoraDLMM.create(connection, lbPair, this.ix);
|
|
2404
3638
|
const { binId: activeId } = yield dlmmPool.dlmm.getActiveBin();
|
|
2405
3639
|
const binStep = new bn_js_1.default(dlmmPool.dlmm.lbPair.binStep);
|
|
@@ -2413,7 +3647,7 @@ class Transactions {
|
|
|
2413
3647
|
// Chunk the deposit using the same strategy parameters
|
|
2414
3648
|
const chunks = (0, liquidityStrategy_1.chunkDepositParameters)(strategyParams, minDeltaId, maxDeltaId, new bn_js_1.default(activeId), binStep, MAX_BINS_PER_TX, favorXInActiveId);
|
|
2415
3649
|
console.log(`Total chunks: ${chunks.length}`);
|
|
2416
|
-
const
|
|
3650
|
+
const txInstructions = [];
|
|
2417
3651
|
// === TRANSACTION 1: Create LTA accounts and deposit tokens ===
|
|
2418
3652
|
const depositInstructions = [];
|
|
2419
3653
|
// Build deposit array for tokens with non-zero amounts
|
|
@@ -2456,7 +3690,6 @@ class Transactions {
|
|
|
2456
3690
|
});
|
|
2457
3691
|
instructions.push(initBinArraysIx);
|
|
2458
3692
|
// Add liquidity via rebalanceLiquidity
|
|
2459
|
-
// Note: Always use TokenType.LTA since we deposit to LTA in the first transaction
|
|
2460
3693
|
const depositIx = yield this.ix.meteoraDlmm.rebalanceLiquidity({
|
|
2461
3694
|
connection,
|
|
2462
3695
|
userWallet,
|
|
@@ -2489,14 +3722,213 @@ class Transactions {
|
|
|
2489
3722
|
}],
|
|
2490
3723
|
});
|
|
2491
3724
|
instructions.push(depositIx);
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
3725
|
+
txInstructions.push(instructions);
|
|
3726
|
+
}
|
|
3727
|
+
return txInstructions;
|
|
3728
|
+
});
|
|
3729
|
+
}
|
|
3730
|
+
/**
|
|
3731
|
+
* Deposit liquidity to a large Meteora DLMM position using rebalanceLiquidity.
|
|
3732
|
+
* Splits the deposit into multiple transactions (one per chunk of bins)
|
|
3733
|
+
* to stay within Solana's account limit per transaction.
|
|
3734
|
+
*
|
|
3735
|
+
* @param connection - Solana connection
|
|
3736
|
+
* @param params - MeteoraDepositToLargePosition parameters
|
|
3737
|
+
* @returns Array of TransactionMetadataResponse, one per chunk
|
|
3738
|
+
*/
|
|
3739
|
+
meteoraDepositToLargePosition(_a) {
|
|
3740
|
+
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
3741
|
+
var _b;
|
|
3742
|
+
let lbPair, upperBinId, lowerBinId;
|
|
3743
|
+
if (params.fastGeneration !== undefined) {
|
|
3744
|
+
lbPair = params.fastGeneration.pool;
|
|
3745
|
+
upperBinId = params.fastGeneration.upperBinId;
|
|
3746
|
+
lowerBinId = params.fastGeneration.lowerBinId;
|
|
3747
|
+
}
|
|
3748
|
+
else {
|
|
3749
|
+
const program = yield meteora_1.MeteoraDLMM.program(connection);
|
|
3750
|
+
const position = yield program.account.positionV2.fetch(params.position);
|
|
3751
|
+
lbPair = position.lbPair;
|
|
3752
|
+
upperBinId = position.upperBinId;
|
|
3753
|
+
lowerBinId = position.lowerBinId;
|
|
3754
|
+
}
|
|
3755
|
+
const totalBins = upperBinId - lowerBinId + 1;
|
|
3756
|
+
// ≤149 bins: single-tx path via meteoraDepositLargerPosition
|
|
3757
|
+
if (totalBins <= 149) {
|
|
3758
|
+
const result = yield this.meteoraDepositLargerPosition({
|
|
3759
|
+
connection,
|
|
3760
|
+
params: {
|
|
3761
|
+
userWallet: params.userWallet,
|
|
3762
|
+
lbPair,
|
|
3763
|
+
position: params.position,
|
|
3764
|
+
lowerBinId,
|
|
3765
|
+
upperBinId,
|
|
3766
|
+
totalXAmount: params.totalXAmount,
|
|
3767
|
+
totalYAmount: params.totalYAmount,
|
|
3768
|
+
distribution: params.distribution,
|
|
3769
|
+
slippage: params.slippage,
|
|
3770
|
+
skipInputTokenCheck: params.skipInputTokenCheck,
|
|
3771
|
+
pdaTokenType: params.pdaTokenType,
|
|
3772
|
+
initializePosition: false,
|
|
3773
|
+
},
|
|
3774
|
+
fetch: undefined,
|
|
3775
|
+
});
|
|
3776
|
+
return [result];
|
|
3777
|
+
}
|
|
3778
|
+
// ≥150 bins: multi-tx chunked deposit path
|
|
3779
|
+
// Resolve token mints and programs from lbPair
|
|
3780
|
+
const dlmmPool = yield meteora_1.MeteoraDLMM.create(connection, lbPair, this.ix);
|
|
3781
|
+
const lbPairState = dlmmPool.dlmm.lbPair;
|
|
3782
|
+
const tokenXMint = lbPairState.tokenXMint;
|
|
3783
|
+
const tokenYMint = lbPairState.tokenYMint;
|
|
3784
|
+
const tokenProgramMap = yield (0, functions_1.getTokenProgramMapForMints)(connection, [tokenXMint, tokenYMint]);
|
|
3785
|
+
const tokenXProgram = tokenProgramMap[tokenXMint.toString()];
|
|
3786
|
+
const tokenYProgram = tokenProgramMap[tokenYMint.toString()];
|
|
3787
|
+
// Convert Distribution to liquidityStrategy.StrategyType
|
|
3788
|
+
const distributionToStrategyType = {
|
|
3789
|
+
'SPOT': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-IMBALANCED': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-ONE-SIDE': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-BALANCED': liquidityStrategy_1.StrategyType.SPOT,
|
|
3790
|
+
'CURVE': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-IMBALANCED': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-ONE-SIDE': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-BALANCED': liquidityStrategy_1.StrategyType.CURVE,
|
|
3791
|
+
'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,
|
|
3792
|
+
};
|
|
3793
|
+
const strategyType = (_b = distributionToStrategyType[params.distribution]) !== null && _b !== void 0 ? _b : liquidityStrategy_1.StrategyType.SPOT;
|
|
3794
|
+
const txInstructions = yield this._meteoraDepositToLargePosition({ connection, params: {
|
|
3795
|
+
userWallet: params.userWallet,
|
|
3796
|
+
lbPair,
|
|
3797
|
+
position: params.position,
|
|
3798
|
+
tokenXMint,
|
|
3799
|
+
tokenYMint,
|
|
3800
|
+
tokenXProgram,
|
|
3801
|
+
tokenYProgram,
|
|
3802
|
+
lowerBinId,
|
|
3803
|
+
upperBinId,
|
|
3804
|
+
totalXAmount: params.totalXAmount,
|
|
3805
|
+
totalYAmount: params.totalYAmount,
|
|
3806
|
+
strategyType,
|
|
3807
|
+
pdaTokenType: params.pdaTokenType,
|
|
3808
|
+
} });
|
|
3809
|
+
const transactions = [];
|
|
3810
|
+
for (let i = 0; i < txInstructions.length; i++) {
|
|
3811
|
+
transactions.push(yield (0, functions_1.createTransactionMeta)({
|
|
3812
|
+
payer: params.userWallet,
|
|
3813
|
+
description: `Deposit to large Meteora DLMM Position (chunk ${i + 1}/${txInstructions.length})`,
|
|
2496
3814
|
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
2497
|
-
mainInstructions:
|
|
3815
|
+
mainInstructions: txInstructions[i],
|
|
3816
|
+
}));
|
|
3817
|
+
}
|
|
3818
|
+
return transactions;
|
|
3819
|
+
});
|
|
3820
|
+
}
|
|
3821
|
+
/**
|
|
3822
|
+
* Create a position and deposit liquidity for any bin count.
|
|
3823
|
+
*
|
|
3824
|
+
* - For ≤149 bins: delegates to meteoraDepositLargerPosition (single tx with position init)
|
|
3825
|
+
* - For ≥150 bins: uses _meteoraInitializeLargePosition + _meteoraDepositToLargePosition (multi-tx)
|
|
3826
|
+
* When init produces 2 TXs (>1344 bins), the 2nd init TX is merged into the 1st deposit TX.
|
|
3827
|
+
*/
|
|
3828
|
+
meteoraCreatePositionAndDepositToLargePosition(_a) {
|
|
3829
|
+
return __awaiter(this, arguments, void 0, function* ({ connection, params, }) {
|
|
3830
|
+
var _b;
|
|
3831
|
+
const totalBins = params.binRange.upperRange - params.binRange.lowerRange + 1;
|
|
3832
|
+
// ≤149 bins: single-tx path via meteoraDepositLargerPosition
|
|
3833
|
+
if (totalBins <= 149) {
|
|
3834
|
+
const result = yield this.meteoraDepositLargerPosition({
|
|
3835
|
+
connection,
|
|
3836
|
+
params: {
|
|
3837
|
+
userWallet: params.userWallet,
|
|
3838
|
+
lbPair: params.pool,
|
|
3839
|
+
position: params.position,
|
|
3840
|
+
lowerBinId: params.binRange.lowerRange,
|
|
3841
|
+
upperBinId: params.binRange.upperRange,
|
|
3842
|
+
totalXAmount: params.totalXAmount,
|
|
3843
|
+
totalYAmount: params.totalYAmount,
|
|
3844
|
+
distribution: params.distribution,
|
|
3845
|
+
slippage: params.slippage,
|
|
3846
|
+
skipInputTokenCheck: params.skipInputTokenCheck,
|
|
3847
|
+
pdaTokenType: params.pdaTokenType,
|
|
3848
|
+
initializePosition: true,
|
|
3849
|
+
},
|
|
3850
|
+
fetch: undefined,
|
|
2498
3851
|
});
|
|
2499
|
-
|
|
3852
|
+
return [result];
|
|
3853
|
+
}
|
|
3854
|
+
// ≥150 bins: multi-tx path
|
|
3855
|
+
// Convert Distribution string to liquidityStrategy.StrategyType number for chunked deposit
|
|
3856
|
+
const distributionToStrategyType = {
|
|
3857
|
+
'SPOT': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-IMBALANCED': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-ONE-SIDE': liquidityStrategy_1.StrategyType.SPOT, 'SPOT-BALANCED': liquidityStrategy_1.StrategyType.SPOT,
|
|
3858
|
+
'CURVE': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-IMBALANCED': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-ONE-SIDE': liquidityStrategy_1.StrategyType.CURVE, 'CURVE-BALANCED': liquidityStrategy_1.StrategyType.CURVE,
|
|
3859
|
+
'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,
|
|
3860
|
+
};
|
|
3861
|
+
const strategyType = (_b = distributionToStrategyType[params.distribution]) !== null && _b !== void 0 ? _b : liquidityStrategy_1.StrategyType.SPOT;
|
|
3862
|
+
// Step 1: Initialize position
|
|
3863
|
+
const initTxs = yield this._meteoraInitializeLargePosition({
|
|
3864
|
+
connection,
|
|
3865
|
+
params: {
|
|
3866
|
+
userWallet: params.userWallet,
|
|
3867
|
+
lbPair: params.pool,
|
|
3868
|
+
position: params.position,
|
|
3869
|
+
lowerBinId: params.binRange.lowerRange,
|
|
3870
|
+
upperBinId: params.binRange.upperRange,
|
|
3871
|
+
},
|
|
3872
|
+
});
|
|
3873
|
+
// Step 2: Resolve token mints and programs from lbPair
|
|
3874
|
+
const dlmmPool = yield meteora_1.MeteoraDLMM.create(connection, params.pool, this.ix);
|
|
3875
|
+
const lbPairState = dlmmPool.dlmm.lbPair;
|
|
3876
|
+
const tokenXMint = lbPairState.tokenXMint;
|
|
3877
|
+
const tokenYMint = lbPairState.tokenYMint;
|
|
3878
|
+
const tokenProgramMap = yield (0, functions_1.getTokenProgramMapForMints)(connection, [tokenXMint, tokenYMint]);
|
|
3879
|
+
const tokenXProgram = tokenProgramMap[tokenXMint.toString()];
|
|
3880
|
+
const tokenYProgram = tokenProgramMap[tokenYMint.toString()];
|
|
3881
|
+
// Step 3: Build deposit instructions
|
|
3882
|
+
const depositTxs = yield this._meteoraDepositToLargePosition({
|
|
3883
|
+
connection,
|
|
3884
|
+
params: {
|
|
3885
|
+
userWallet: params.userWallet,
|
|
3886
|
+
lbPair: params.pool,
|
|
3887
|
+
position: params.position,
|
|
3888
|
+
tokenXMint,
|
|
3889
|
+
tokenYMint,
|
|
3890
|
+
tokenXProgram,
|
|
3891
|
+
tokenYProgram,
|
|
3892
|
+
lowerBinId: params.binRange.lowerRange,
|
|
3893
|
+
upperBinId: params.binRange.upperRange,
|
|
3894
|
+
totalXAmount: params.totalXAmount,
|
|
3895
|
+
totalYAmount: params.totalYAmount,
|
|
3896
|
+
strategyType,
|
|
3897
|
+
pdaTokenType: params.pdaTokenType,
|
|
3898
|
+
},
|
|
3899
|
+
});
|
|
3900
|
+
// Step 4: Assemble — merge init's last TX into deposit's first TX if init has >1 TX
|
|
3901
|
+
const assembled = [];
|
|
3902
|
+
if (initTxs.length > 1) {
|
|
3903
|
+
// Add all init TXs except the last
|
|
3904
|
+
for (let i = 0; i < initTxs.length - 1; i++) {
|
|
3905
|
+
assembled.push(initTxs[i]);
|
|
3906
|
+
}
|
|
3907
|
+
// Merge last init TX into first deposit TX
|
|
3908
|
+
const lastInitTx = initTxs[initTxs.length - 1];
|
|
3909
|
+
const firstDepositTx = depositTxs[0];
|
|
3910
|
+
assembled.push([...lastInitTx, ...firstDepositTx]);
|
|
3911
|
+
// Add remaining deposit TXs
|
|
3912
|
+
for (let i = 1; i < depositTxs.length; i++) {
|
|
3913
|
+
assembled.push(depositTxs[i]);
|
|
3914
|
+
}
|
|
3915
|
+
}
|
|
3916
|
+
else {
|
|
3917
|
+
// Single init TX — no merge needed
|
|
3918
|
+
assembled.push(initTxs[0]);
|
|
3919
|
+
for (const tx of depositTxs) {
|
|
3920
|
+
assembled.push(tx);
|
|
3921
|
+
}
|
|
3922
|
+
}
|
|
3923
|
+
// Step 5: Wrap each instruction array with createTransactionMeta
|
|
3924
|
+
const transactions = [];
|
|
3925
|
+
for (let i = 0; i < assembled.length; i++) {
|
|
3926
|
+
transactions.push(yield (0, functions_1.createTransactionMeta)({
|
|
3927
|
+
payer: params.userWallet,
|
|
3928
|
+
description: `Create position and deposit to large Meteora DLMM Position (tx ${i + 1}/${assembled.length})`,
|
|
3929
|
+
addressLookupTableAddresses: addresses_1.GLOBAL_ALT,
|
|
3930
|
+
mainInstructions: assembled[i],
|
|
3931
|
+
}));
|
|
2500
3932
|
}
|
|
2501
3933
|
return transactions;
|
|
2502
3934
|
});
|