four-flap-meme-sdk 1.4.77 → 1.4.79
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/contracts/tm-bundle-merkle/swap-buy-first.d.ts +4 -2
- package/dist/contracts/tm-bundle-merkle/swap-buy-first.js +195 -12
- package/dist/flap/portal-bundle-merkle/encryption.d.ts +16 -0
- package/dist/flap/portal-bundle-merkle/encryption.js +146 -0
- package/dist/flap/portal-bundle-merkle/swap-buy-first.d.ts +4 -2
- package/dist/flap/portal-bundle-merkle/swap-buy-first.js +222 -9
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/pancake/bundle-buy-first.d.ts +4 -2
- package/dist/pancake/bundle-buy-first.js +293 -14
- package/package.json +3 -38
- package/dist/sol/constants.d.ts +0 -126
- package/dist/sol/constants.js +0 -145
- package/dist/sol/dex/index.d.ts +0 -8
- package/dist/sol/dex/index.js +0 -12
- package/dist/sol/dex/meteora/client.d.ts +0 -76
- package/dist/sol/dex/meteora/client.js +0 -219
- package/dist/sol/dex/meteora/damm-v1-bundle.d.ts +0 -61
- package/dist/sol/dex/meteora/damm-v1-bundle.js +0 -112
- package/dist/sol/dex/meteora/damm-v1.d.ts +0 -118
- package/dist/sol/dex/meteora/damm-v1.js +0 -315
- package/dist/sol/dex/meteora/damm-v2-bundle.d.ts +0 -82
- package/dist/sol/dex/meteora/damm-v2-bundle.js +0 -242
- package/dist/sol/dex/meteora/damm-v2.d.ts +0 -172
- package/dist/sol/dex/meteora/damm-v2.js +0 -632
- package/dist/sol/dex/meteora/dbc-bundle.d.ts +0 -123
- package/dist/sol/dex/meteora/dbc-bundle.js +0 -304
- package/dist/sol/dex/meteora/dbc.d.ts +0 -192
- package/dist/sol/dex/meteora/dbc.js +0 -619
- package/dist/sol/dex/meteora/dlmm-bundle.d.ts +0 -39
- package/dist/sol/dex/meteora/dlmm-bundle.js +0 -189
- package/dist/sol/dex/meteora/dlmm.d.ts +0 -157
- package/dist/sol/dex/meteora/dlmm.js +0 -671
- package/dist/sol/dex/meteora/index.d.ts +0 -25
- package/dist/sol/dex/meteora/index.js +0 -65
- package/dist/sol/dex/meteora/types.d.ts +0 -787
- package/dist/sol/dex/meteora/types.js +0 -110
- package/dist/sol/dex/orca/index.d.ts +0 -10
- package/dist/sol/dex/orca/index.js +0 -16
- package/dist/sol/dex/orca/orca-bundle.d.ts +0 -41
- package/dist/sol/dex/orca/orca-bundle.js +0 -173
- package/dist/sol/dex/orca/orca.d.ts +0 -65
- package/dist/sol/dex/orca/orca.js +0 -474
- package/dist/sol/dex/orca/types.d.ts +0 -263
- package/dist/sol/dex/orca/types.js +0 -38
- package/dist/sol/dex/orca/wavebreak-bundle.d.ts +0 -34
- package/dist/sol/dex/orca/wavebreak-bundle.js +0 -198
- package/dist/sol/dex/orca/wavebreak-types.d.ts +0 -227
- package/dist/sol/dex/orca/wavebreak-types.js +0 -23
- package/dist/sol/dex/orca/wavebreak.d.ts +0 -78
- package/dist/sol/dex/orca/wavebreak.js +0 -497
- package/dist/sol/dex/pump/index.d.ts +0 -9
- package/dist/sol/dex/pump/index.js +0 -14
- package/dist/sol/dex/pump/pump-bundle.d.ts +0 -92
- package/dist/sol/dex/pump/pump-bundle.js +0 -383
- package/dist/sol/dex/pump/pump-swap-bundle.d.ts +0 -103
- package/dist/sol/dex/pump/pump-swap-bundle.js +0 -380
- package/dist/sol/dex/pump/pump-swap.d.ts +0 -46
- package/dist/sol/dex/pump/pump-swap.js +0 -199
- package/dist/sol/dex/pump/pump.d.ts +0 -35
- package/dist/sol/dex/pump/pump.js +0 -352
- package/dist/sol/dex/pump/types.d.ts +0 -215
- package/dist/sol/dex/pump/types.js +0 -5
- package/dist/sol/dex/raydium/index.d.ts +0 -8
- package/dist/sol/dex/raydium/index.js +0 -12
- package/dist/sol/dex/raydium/launchlab.d.ts +0 -68
- package/dist/sol/dex/raydium/launchlab.js +0 -210
- package/dist/sol/dex/raydium/raydium-bundle.d.ts +0 -64
- package/dist/sol/dex/raydium/raydium-bundle.js +0 -324
- package/dist/sol/dex/raydium/raydium.d.ts +0 -40
- package/dist/sol/dex/raydium/raydium.js +0 -366
- package/dist/sol/dex/raydium/types.d.ts +0 -240
- package/dist/sol/dex/raydium/types.js +0 -5
- package/dist/sol/index.d.ts +0 -10
- package/dist/sol/index.js +0 -16
- package/dist/sol/jito/bundle.d.ts +0 -90
- package/dist/sol/jito/bundle.js +0 -263
- package/dist/sol/jito/index.d.ts +0 -7
- package/dist/sol/jito/index.js +0 -7
- package/dist/sol/jito/tip.d.ts +0 -51
- package/dist/sol/jito/tip.js +0 -83
- package/dist/sol/jito/types.d.ts +0 -100
- package/dist/sol/jito/types.js +0 -5
- package/dist/sol/token/create-complete.d.ts +0 -115
- package/dist/sol/token/create-complete.js +0 -235
- package/dist/sol/token/create-token.d.ts +0 -57
- package/dist/sol/token/create-token.js +0 -230
- package/dist/sol/token/index.d.ts +0 -9
- package/dist/sol/token/index.js +0 -14
- package/dist/sol/token/metadata-upload.d.ts +0 -86
- package/dist/sol/token/metadata-upload.js +0 -173
- package/dist/sol/token/metadata.d.ts +0 -92
- package/dist/sol/token/metadata.js +0 -274
- package/dist/sol/token/types.d.ts +0 -153
- package/dist/sol/token/types.js +0 -5
- package/dist/sol/types.d.ts +0 -176
- package/dist/sol/types.js +0 -7
- package/dist/sol/utils/balance.d.ts +0 -160
- package/dist/sol/utils/balance.js +0 -638
- package/dist/sol/utils/connection.d.ts +0 -78
- package/dist/sol/utils/connection.js +0 -168
- package/dist/sol/utils/index.d.ts +0 -9
- package/dist/sol/utils/index.js +0 -9
- package/dist/sol/utils/lp-inspect.d.ts +0 -129
- package/dist/sol/utils/lp-inspect.js +0 -900
- package/dist/sol/utils/transfer.d.ts +0 -125
- package/dist/sol/utils/transfer.js +0 -220
- package/dist/sol/utils/wallet.d.ts +0 -107
- package/dist/sol/utils/wallet.js +0 -210
|
@@ -66,8 +66,8 @@ const BRIBE_TX_COUNT = 1;
|
|
|
66
66
|
const PROFIT_TX_COUNT = PROFIT_HOP_COUNT + 2; // 2 + 2 = 4
|
|
67
67
|
/** 最大买卖交易数 */
|
|
68
68
|
const MAX_SWAP_TX_COUNT = MAX_BUNDLE_SIGNATURES - BRIBE_TX_COUNT - PROFIT_TX_COUNT; // 50 - 1 - 4 = 45
|
|
69
|
-
/** 每笔交易利润比例(基点):
|
|
70
|
-
const PROFIT_RATE_PER_TX_BPS =
|
|
69
|
+
/** 每笔交易利润比例(基点):6 bps = 0.06% = 万分之六 */
|
|
70
|
+
const PROFIT_RATE_PER_TX_BPS = 6;
|
|
71
71
|
/**
|
|
72
72
|
* 验证买卖笔数
|
|
73
73
|
*/
|
|
@@ -100,10 +100,29 @@ function getNativeTokenName(chain) {
|
|
|
100
100
|
}
|
|
101
101
|
// 使用公共工具的 getGasLimit,移除本地重复实现
|
|
102
102
|
export async function flapBundleBuyFirstMerkle(params) {
|
|
103
|
-
const { chain, buyerPrivateKey, sellerPrivateKey, tokenAddress, buyerFunds, buyerFundsPercentage, config, quoteToken, quoteTokenDecimals, buyCount: _buyCount, sellCount: _sellCount } = params;
|
|
104
|
-
// ✅
|
|
103
|
+
const { chain, buyerPrivateKey, buyerPrivateKeys, sellerPrivateKey, sellerPrivateKeys, tokenAddress, buyerFunds, buyerFundsPercentage, config, quoteToken, quoteTokenDecimals, buyCount: _buyCount, sellCount: _sellCount } = params;
|
|
104
|
+
// ✅ 判断是否为多钱包模式
|
|
105
|
+
const isMultiWalletMode = !!(buyerPrivateKeys && buyerPrivateKeys.length > 0) ||
|
|
106
|
+
!!(sellerPrivateKeys && sellerPrivateKeys.length > 0);
|
|
105
107
|
const buyCount = _buyCount ?? 1;
|
|
106
108
|
const sellCount = _sellCount ?? 1;
|
|
109
|
+
// ✅ 多钱包模式:使用单独的处理逻辑
|
|
110
|
+
if (isMultiWalletMode) {
|
|
111
|
+
return await flapBundleBuyFirstMultiWallet({
|
|
112
|
+
chain,
|
|
113
|
+
buyerPrivateKeys: buyerPrivateKeys || (buyerPrivateKey ? [buyerPrivateKey] : []),
|
|
114
|
+
sellerPrivateKeys: sellerPrivateKeys || (sellerPrivateKey ? [sellerPrivateKey] : []),
|
|
115
|
+
tokenAddress,
|
|
116
|
+
buyerFunds,
|
|
117
|
+
config,
|
|
118
|
+
quoteToken,
|
|
119
|
+
quoteTokenDecimals
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
// ✅ 单钱包模式(向后兼容)
|
|
123
|
+
if (!buyerPrivateKey || !sellerPrivateKey) {
|
|
124
|
+
throw new Error('单钱包模式需要提供 buyerPrivateKey 和 sellerPrivateKey');
|
|
125
|
+
}
|
|
107
126
|
validateSwapCounts(buyCount, sellCount);
|
|
108
127
|
// ✅ 计算利润比例:每笔万分之3
|
|
109
128
|
const totalTxCount = buyCount + sellCount;
|
|
@@ -160,16 +179,12 @@ export async function flapBundleBuyFirstMerkle(params) {
|
|
|
160
179
|
// ✅ 拆分买入和卖出金额
|
|
161
180
|
const buyAmountsWei = splitAmount(buyerFundsWei, buyCount);
|
|
162
181
|
const sellAmountsWei = splitAmount(sellAmountWei, sellCount);
|
|
163
|
-
// ✅ 多笔买入时,minOutputAmount 设置为 0 以避免滑点导致交易失败
|
|
164
|
-
// 原因:每笔买入会改变池子价格,后续买入获得的代币会变少
|
|
165
|
-
// 单笔买入时使用正常的 minOutputAmount 作为保护
|
|
166
|
-
const minOutputPerBuy = buyCount === 1 ? sellAmountWei : 0n;
|
|
167
182
|
// ✅ 第三批并行 - 构建多笔买入和卖出交易、estimatedSellFunds
|
|
168
183
|
const buyUnsignedPromises = buyAmountsWei.map(amount => portalBuyer.swapExactInput.populateTransaction({
|
|
169
184
|
inputToken,
|
|
170
185
|
outputToken: tokenAddress,
|
|
171
186
|
inputAmount: amount,
|
|
172
|
-
minOutputAmount:
|
|
187
|
+
minOutputAmount: 0n,
|
|
173
188
|
permitData: '0x'
|
|
174
189
|
}, useNativeToken ? { value: amount } : {}));
|
|
175
190
|
const sellUnsignedPromises = sellAmountsWei.map(amount => portalSeller.swapExactInput.populateTransaction({
|
|
@@ -589,3 +604,201 @@ function createChainContext(chain, rpcUrl) {
|
|
|
589
604
|
}
|
|
590
605
|
return { chainId, nativeToken, portalAddress, provider };
|
|
591
606
|
}
|
|
607
|
+
/**
|
|
608
|
+
* ✅ Flap 多钱包捆绑换手
|
|
609
|
+
* - 多个买方钱包执行买入(每个钱包1笔)
|
|
610
|
+
* - 多个卖方钱包执行卖出(每个钱包1笔)
|
|
611
|
+
* - 买入总价值 = 卖出总价值
|
|
612
|
+
*/
|
|
613
|
+
async function flapBundleBuyFirstMultiWallet(params) {
|
|
614
|
+
const { chain, buyerPrivateKeys, sellerPrivateKeys, tokenAddress, buyerFunds, config, quoteToken, quoteTokenDecimals = 18 } = params;
|
|
615
|
+
const buyCount = buyerPrivateKeys.length;
|
|
616
|
+
const sellCount = sellerPrivateKeys.length;
|
|
617
|
+
if (buyCount === 0)
|
|
618
|
+
throw new Error('买方钱包数量不能为0');
|
|
619
|
+
if (sellCount === 0)
|
|
620
|
+
throw new Error('卖方钱包数量不能为0');
|
|
621
|
+
// 验证总交易数不超过限制
|
|
622
|
+
validateSwapCounts(buyCount, sellCount);
|
|
623
|
+
// ✅ 计算利润比例:每笔万分之6
|
|
624
|
+
const totalTxCount = buyCount + sellCount;
|
|
625
|
+
const profitRateBps = PROFIT_RATE_PER_TX_BPS * totalTxCount;
|
|
626
|
+
const chainContext = createChainContext(chain, config.rpcUrl);
|
|
627
|
+
const nonceManager = new NonceManager(chainContext.provider);
|
|
628
|
+
// 创建所有钱包实例
|
|
629
|
+
const buyers = buyerPrivateKeys.map(pk => new Wallet(pk, chainContext.provider));
|
|
630
|
+
const sellers = sellerPrivateKeys.map(pk => new Wallet(pk, chainContext.provider));
|
|
631
|
+
// 使用第一个卖方作为主卖方(支付贿赂和利润)
|
|
632
|
+
const mainSeller = sellers[0];
|
|
633
|
+
// ✅ 判断是否使用原生代币
|
|
634
|
+
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
635
|
+
const inputToken = useNativeToken ? ZERO_ADDRESS : quoteToken;
|
|
636
|
+
const outputToken = inputToken;
|
|
637
|
+
// ✅ 计算总交易金额
|
|
638
|
+
if (!buyerFunds) {
|
|
639
|
+
throw new Error('多钱包模式必须提供 buyerFunds(总交易金额)');
|
|
640
|
+
}
|
|
641
|
+
const totalFundsWei = useNativeToken
|
|
642
|
+
? ethers.parseEther(String(buyerFunds))
|
|
643
|
+
: ethers.parseUnits(String(buyerFunds), quoteTokenDecimals);
|
|
644
|
+
if (totalFundsWei <= 0n) {
|
|
645
|
+
throw new Error('交易金额必须大于0');
|
|
646
|
+
}
|
|
647
|
+
// ✅ 获取报价:买入能获得多少代币
|
|
648
|
+
const quoteResult = await quoteBuyerOutput({
|
|
649
|
+
portalAddress: chainContext.portalAddress,
|
|
650
|
+
tokenAddress,
|
|
651
|
+
buyerFundsWei: totalFundsWei,
|
|
652
|
+
provider: chainContext.provider,
|
|
653
|
+
skipQuoteOnError: config.skipQuoteOnError,
|
|
654
|
+
inputToken
|
|
655
|
+
});
|
|
656
|
+
// ✅ 将总金额平均分配给买方
|
|
657
|
+
const buyAmountsWei = splitAmount(totalFundsWei, buyCount);
|
|
658
|
+
// ✅ 将代币平均分配给卖方
|
|
659
|
+
const sellAmountsWei = splitAmount(quoteResult.sellAmountWei, sellCount);
|
|
660
|
+
const finalGasLimit = getGasLimit(config);
|
|
661
|
+
const gasPrice = await getOptimizedGasPrice(chainContext.provider, getGasPriceConfig(config));
|
|
662
|
+
const txType = getTxType(config);
|
|
663
|
+
const priorityFee = gasPrice / 10n === 0n ? 1n : gasPrice / 10n;
|
|
664
|
+
// ✅ 估算利润
|
|
665
|
+
const portal = new Contract(chainContext.portalAddress, PORTAL_ABI, chainContext.provider);
|
|
666
|
+
const estimatedSellFunds = await estimateSellFunds(portal, tokenAddress, quoteResult.sellAmountWei, outputToken);
|
|
667
|
+
const profitBase = estimatedSellFunds > 0n ? estimatedSellFunds : totalFundsWei;
|
|
668
|
+
const tokenProfitAmount = (profitBase * BigInt(profitRateBps)) / 10000n;
|
|
669
|
+
// ERC20 购买:获取代币利润等值的原生代币报价
|
|
670
|
+
let nativeProfitAmount = tokenProfitAmount;
|
|
671
|
+
if (!useNativeToken && tokenProfitAmount > 0n) {
|
|
672
|
+
nativeProfitAmount = await getTokenToNativeQuote(chainContext.provider, inputToken, tokenProfitAmount, chainContext.chainId);
|
|
673
|
+
}
|
|
674
|
+
// ✅ 获取贿赂金额
|
|
675
|
+
const bribeAmount = getBribeAmount(config);
|
|
676
|
+
const needBribeTx = bribeAmount > 0n;
|
|
677
|
+
// ✅ 获取所有钱包的 nonces
|
|
678
|
+
const noncesMap = new Map();
|
|
679
|
+
await Promise.all([...sellers, ...buyers].map(async (wallet) => {
|
|
680
|
+
const addr = wallet.address.toLowerCase();
|
|
681
|
+
if (!noncesMap.has(addr)) {
|
|
682
|
+
const nonce = await nonceManager.getNextNonce(wallet);
|
|
683
|
+
noncesMap.set(addr, nonce);
|
|
684
|
+
}
|
|
685
|
+
}));
|
|
686
|
+
// ✅ 构建交易列表
|
|
687
|
+
const allTransactions = [];
|
|
688
|
+
// 1. 贿赂交易(由主卖方支付)
|
|
689
|
+
if (needBribeTx) {
|
|
690
|
+
const mainSellerAddr = mainSeller.address.toLowerCase();
|
|
691
|
+
const bribeNonce = noncesMap.get(mainSellerAddr);
|
|
692
|
+
noncesMap.set(mainSellerAddr, bribeNonce + 1);
|
|
693
|
+
const bribeTx = await mainSeller.signTransaction({
|
|
694
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
695
|
+
value: bribeAmount,
|
|
696
|
+
nonce: bribeNonce,
|
|
697
|
+
gasPrice,
|
|
698
|
+
gasLimit: 21000n,
|
|
699
|
+
chainId: chainContext.chainId,
|
|
700
|
+
type: txType
|
|
701
|
+
});
|
|
702
|
+
allTransactions.push(bribeTx);
|
|
703
|
+
}
|
|
704
|
+
// 2. 构建所有买入交易
|
|
705
|
+
const buyTxPromises = buyers.map(async (buyer, i) => {
|
|
706
|
+
const buyAmount = buyAmountsWei[i];
|
|
707
|
+
const buyerAddr = buyer.address.toLowerCase();
|
|
708
|
+
const nonce = noncesMap.get(buyerAddr);
|
|
709
|
+
noncesMap.set(buyerAddr, nonce + 1);
|
|
710
|
+
const portalBuyer = new Contract(chainContext.portalAddress, PORTAL_ABI, buyer);
|
|
711
|
+
const unsigned = await portalBuyer.swapExactInput.populateTransaction({
|
|
712
|
+
inputToken,
|
|
713
|
+
outputToken: tokenAddress,
|
|
714
|
+
inputAmount: buyAmount,
|
|
715
|
+
minOutputAmount: 0n,
|
|
716
|
+
permitData: '0x'
|
|
717
|
+
}, useNativeToken ? { value: buyAmount } : {});
|
|
718
|
+
return buyer.signTransaction(buildTransactionRequest(unsigned, {
|
|
719
|
+
from: buyer.address,
|
|
720
|
+
nonce,
|
|
721
|
+
gasLimit: finalGasLimit,
|
|
722
|
+
gasPrice,
|
|
723
|
+
priorityFee,
|
|
724
|
+
chainId: chainContext.chainId,
|
|
725
|
+
txType,
|
|
726
|
+
value: useNativeToken ? buyAmount : 0n
|
|
727
|
+
}));
|
|
728
|
+
});
|
|
729
|
+
// 3. 构建所有卖出交易
|
|
730
|
+
const sellTxPromises = sellers.map(async (seller, i) => {
|
|
731
|
+
const sellAmount = sellAmountsWei[i];
|
|
732
|
+
const sellerAddr = seller.address.toLowerCase();
|
|
733
|
+
const nonce = noncesMap.get(sellerAddr);
|
|
734
|
+
noncesMap.set(sellerAddr, nonce + 1);
|
|
735
|
+
const portalSeller = new Contract(chainContext.portalAddress, PORTAL_ABI, seller);
|
|
736
|
+
const unsigned = await portalSeller.swapExactInput.populateTransaction({
|
|
737
|
+
inputToken: tokenAddress,
|
|
738
|
+
outputToken,
|
|
739
|
+
inputAmount: sellAmount,
|
|
740
|
+
minOutputAmount: 0n,
|
|
741
|
+
permitData: '0x'
|
|
742
|
+
});
|
|
743
|
+
return seller.signTransaction(buildTransactionRequest(unsigned, {
|
|
744
|
+
from: seller.address,
|
|
745
|
+
nonce,
|
|
746
|
+
gasLimit: finalGasLimit,
|
|
747
|
+
gasPrice,
|
|
748
|
+
priorityFee,
|
|
749
|
+
chainId: chainContext.chainId,
|
|
750
|
+
txType,
|
|
751
|
+
value: 0n
|
|
752
|
+
}));
|
|
753
|
+
});
|
|
754
|
+
// ✅ 并行签名所有买卖交易
|
|
755
|
+
const [signedBuys, signedSells] = await Promise.all([
|
|
756
|
+
Promise.all(buyTxPromises),
|
|
757
|
+
Promise.all(sellTxPromises)
|
|
758
|
+
]);
|
|
759
|
+
// 先买后卖:买入交易在前
|
|
760
|
+
allTransactions.push(...signedBuys, ...signedSells);
|
|
761
|
+
// 4. 利润多跳转账(由主卖方支付)
|
|
762
|
+
let profitHopWallets;
|
|
763
|
+
if (nativeProfitAmount > 0n) {
|
|
764
|
+
const mainSellerAddr = mainSeller.address.toLowerCase();
|
|
765
|
+
const profitNonce = noncesMap.get(mainSellerAddr);
|
|
766
|
+
const profitResult = await buildProfitTransaction({
|
|
767
|
+
provider: chainContext.provider,
|
|
768
|
+
seller: mainSeller,
|
|
769
|
+
profitAmount: nativeProfitAmount,
|
|
770
|
+
profitNonce,
|
|
771
|
+
gasPrice,
|
|
772
|
+
chainId: chainContext.chainId,
|
|
773
|
+
txType
|
|
774
|
+
});
|
|
775
|
+
if (profitResult) {
|
|
776
|
+
allTransactions.push(...profitResult.signedTransactions);
|
|
777
|
+
profitHopWallets = profitResult.hopWallets;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
nonceManager.clearTemp();
|
|
781
|
+
// 获取代币精度
|
|
782
|
+
const sellerInfo = await ensureSellerBalance({
|
|
783
|
+
tokenAddress,
|
|
784
|
+
provider: chainContext.provider,
|
|
785
|
+
seller: mainSeller,
|
|
786
|
+
sellAmountWei: 0n,
|
|
787
|
+
skipBalanceCheck: true
|
|
788
|
+
});
|
|
789
|
+
return {
|
|
790
|
+
signedTransactions: allTransactions,
|
|
791
|
+
profitHopWallets,
|
|
792
|
+
metadata: {
|
|
793
|
+
buyerAddress: buyers.map(b => b.address).join(','),
|
|
794
|
+
sellerAddress: sellers.map(s => s.address).join(','),
|
|
795
|
+
buyAmount: ethers.formatEther(totalFundsWei),
|
|
796
|
+
sellAmount: ethers.formatUnits(quoteResult.sellAmountWei, sellerInfo.decimals),
|
|
797
|
+
profitAmount: nativeProfitAmount > 0n ? ethers.formatEther(nativeProfitAmount) : undefined,
|
|
798
|
+
buyCount,
|
|
799
|
+
sellCount,
|
|
800
|
+
buyAmounts: buyAmountsWei.map(amt => ethers.formatEther(amt)),
|
|
801
|
+
sellAmounts: sellAmountsWei.map(amt => ethers.formatUnits(amt, sellerInfo.decimals))
|
|
802
|
+
}
|
|
803
|
+
};
|
|
804
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export * as Abis from './abis/index.js';
|
|
2
|
-
export * as Sol from './sol/index.js';
|
|
3
2
|
export { ERC20_ABI, ERC20_BALANCE_ABI, ERC20_ALLOWANCE_ABI, MULTICALL3_ABI, V2_ROUTER_ABI, V2_ROUTER_QUOTE_ABI, V3_ROUTER02_ABI, V3_ROUTER_LEGACY_ABI, V3_QUOTER_ABI, V2_FACTORY_ABI, V2_PAIR_ABI, V3_FACTORY_ABI, V3_POOL_ABI, FLAP_PORTAL_ABI, TM2_ABI, HELPER3_ABI, } from './abis/common.js';
|
|
4
3
|
export { ADDRESSES, CHAIN, BLOCKRAZOR_BUILDER_EOA as BUILDER_EOA, ZERO_ADDRESS as COMMON_ZERO_ADDRESS, DEFAULT_DEADLINE_MINUTES, V3_FEE_TIERS as COMMON_V3_FEE_TIERS, } from './utils/constants.js';
|
|
5
4
|
export { isExclusiveOnChain, isExclusiveOffChain } from './utils/mpcExclusive.js';
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// 公共 ABI(统一管理)
|
|
3
3
|
// ============================================================================
|
|
4
4
|
export * as Abis from './abis/index.js';
|
|
5
|
-
export * as Sol from './sol/index.js';
|
|
6
5
|
export { ERC20_ABI, ERC20_BALANCE_ABI, ERC20_ALLOWANCE_ABI, MULTICALL3_ABI, V2_ROUTER_ABI, V2_ROUTER_QUOTE_ABI, V3_ROUTER02_ABI, V3_ROUTER_LEGACY_ABI, V3_QUOTER_ABI, V2_FACTORY_ABI, V2_PAIR_ABI, V3_FACTORY_ABI, V3_POOL_ABI, FLAP_PORTAL_ABI, TM2_ABI, HELPER3_ABI, } from './abis/common.js';
|
|
7
6
|
// ============================================================================
|
|
8
7
|
// 公共常量(统一管理)
|
|
@@ -45,8 +45,10 @@ export interface PancakeBuyFirstConfig extends CommonBundleConfig {
|
|
|
45
45
|
waitTimeoutMs?: number;
|
|
46
46
|
}
|
|
47
47
|
export interface PancakeBundleBuyFirstSignParams {
|
|
48
|
-
buyerPrivateKey
|
|
49
|
-
|
|
48
|
+
buyerPrivateKey?: string;
|
|
49
|
+
buyerPrivateKeys?: string[];
|
|
50
|
+
sellerPrivateKey?: string;
|
|
51
|
+
sellerPrivateKeys?: string[];
|
|
50
52
|
tokenAddress: string;
|
|
51
53
|
routeParams: RouteParams;
|
|
52
54
|
buyerFunds?: string;
|
|
@@ -43,7 +43,6 @@ const PANCAKE_V2_ROUTER_ADDRESS = ADDRESSES.BSC.PancakeV2Router;
|
|
|
43
43
|
const PANCAKE_V3_ROUTER_ADDRESS = ADDRESSES.BSC.PancakeV3Router;
|
|
44
44
|
// 常量
|
|
45
45
|
const FLAT_FEE = 0n;
|
|
46
|
-
const WBNB_ADDRESS = ADDRESSES.BSC.WBNB;
|
|
47
46
|
// ==================== 多笔买卖常量 ====================
|
|
48
47
|
/** 最大 Bundle 签名数 */
|
|
49
48
|
const MAX_BUNDLE_SIGNATURES = 50;
|
|
@@ -53,8 +52,8 @@ const BRIBE_TX_COUNT = 1;
|
|
|
53
52
|
const PROFIT_TX_COUNT = PROFIT_HOP_COUNT + 2; // 2 + 2 = 4
|
|
54
53
|
/** 最大买卖交易数 */
|
|
55
54
|
const MAX_SWAP_TX_COUNT = MAX_BUNDLE_SIGNATURES - BRIBE_TX_COUNT - PROFIT_TX_COUNT; // 50 - 1 - 4 = 45
|
|
56
|
-
/** 每笔交易利润比例(基点):
|
|
57
|
-
const PROFIT_RATE_PER_TX_BPS =
|
|
55
|
+
/** 每笔交易利润比例(基点):6 bps = 0.06% = 万分之六 */
|
|
56
|
+
const PROFIT_RATE_PER_TX_BPS = 6;
|
|
58
57
|
/**
|
|
59
58
|
* 验证买卖笔数
|
|
60
59
|
*/
|
|
@@ -110,11 +109,32 @@ function splitAmount(totalAmount, count) {
|
|
|
110
109
|
return amounts;
|
|
111
110
|
}
|
|
112
111
|
export async function pancakeBundleBuyFirstMerkle(params) {
|
|
113
|
-
const { buyerPrivateKey, sellerPrivateKey, tokenAddress, routeParams, buyerFunds, buyerFundsPercentage, config, quoteToken, quoteTokenDecimals = 18, startNonces, // ✅ 可选:前端预获取的 nonces
|
|
112
|
+
const { buyerPrivateKey, buyerPrivateKeys, sellerPrivateKey, sellerPrivateKeys, tokenAddress, routeParams, buyerFunds, buyerFundsPercentage, config, quoteToken, quoteTokenDecimals = 18, startNonces, // ✅ 可选:前端预获取的 nonces
|
|
114
113
|
buyCount: _buyCount, sellCount: _sellCount } = params;
|
|
115
|
-
// ✅
|
|
114
|
+
// ✅ 判断是否为多钱包模式
|
|
115
|
+
const isMultiWalletMode = !!(buyerPrivateKeys && buyerPrivateKeys.length > 0) ||
|
|
116
|
+
!!(sellerPrivateKeys && sellerPrivateKeys.length > 0);
|
|
117
|
+
// ✅ 多钱包模式:buyCount/sellCount 代表钱包数量
|
|
118
|
+
// 单钱包模式(向后兼容):buyCount/sellCount 代表同一钱包执行的交易笔数
|
|
116
119
|
const buyCount = _buyCount ?? 1;
|
|
117
120
|
const sellCount = _sellCount ?? 1;
|
|
121
|
+
// ✅ 多钱包模式:使用单独的处理逻辑
|
|
122
|
+
if (isMultiWalletMode) {
|
|
123
|
+
return await pancakeBundleBuyFirstMultiWallet({
|
|
124
|
+
buyerPrivateKeys: buyerPrivateKeys || (buyerPrivateKey ? [buyerPrivateKey] : []),
|
|
125
|
+
sellerPrivateKeys: sellerPrivateKeys || (sellerPrivateKey ? [sellerPrivateKey] : []),
|
|
126
|
+
tokenAddress,
|
|
127
|
+
routeParams,
|
|
128
|
+
buyerFunds,
|
|
129
|
+
config,
|
|
130
|
+
quoteToken,
|
|
131
|
+
quoteTokenDecimals
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
// ✅ 单钱包模式(向后兼容):验证买卖笔数
|
|
135
|
+
if (!buyerPrivateKey || !sellerPrivateKey) {
|
|
136
|
+
throw new Error('单钱包模式需要提供 buyerPrivateKey 和 sellerPrivateKey');
|
|
137
|
+
}
|
|
118
138
|
validateSwapCounts(buyCount, sellCount);
|
|
119
139
|
// ✅ 计算利润比例:每笔万分之3
|
|
120
140
|
const totalTxCount = buyCount + sellCount;
|
|
@@ -140,17 +160,9 @@ export async function pancakeBundleBuyFirstMerkle(params) {
|
|
|
140
160
|
buyerFundsWei: buyerFundsInfo.buyerFundsWei,
|
|
141
161
|
provider: context.provider
|
|
142
162
|
});
|
|
143
|
-
// ✅ 多笔交易时添加滑点保护,避免因价格变动导致代币不足
|
|
144
|
-
// 原因:多笔买入会累积滑点,实际获得的代币少于一次性报价
|
|
145
|
-
// 滑点比例:buyCount * 1%(每多一笔买入增加 1% 保护)
|
|
146
|
-
let adjustedSellAmount = quoteResult.quotedTokenOut;
|
|
147
|
-
if (buyCount > 1 || sellCount > 1) {
|
|
148
|
-
const slippageBps = BigInt(buyCount * 100); // buyCount=3 → 3%
|
|
149
|
-
adjustedSellAmount = quoteResult.quotedTokenOut * (10000n - slippageBps) / 10000n;
|
|
150
|
-
}
|
|
151
163
|
// ✅ 拆分买入和卖出金额
|
|
152
164
|
const buyAmountsWei = splitAmount(buyerFundsInfo.buyerFundsWei, buyCount);
|
|
153
|
-
const sellAmountsWei = splitAmount(
|
|
165
|
+
const sellAmountsWei = splitAmount(quoteResult.quotedTokenOut, sellCount);
|
|
154
166
|
// ✅ 构建多笔买入和卖出交易
|
|
155
167
|
const swapUnsignedArray = await buildMultiRouteTransactions({
|
|
156
168
|
routeParams,
|
|
@@ -737,3 +749,270 @@ function buildNoncePlanFromStartNonces(startNonces, sameAddress, profitNeeded, n
|
|
|
737
749
|
const buyerNonce = startNonces[1];
|
|
738
750
|
return { buyerNonce, sellerNonce, bribeNonce, profitNonce };
|
|
739
751
|
}
|
|
752
|
+
/**
|
|
753
|
+
* ✅ 多钱包捆绑换手
|
|
754
|
+
* - 多个买方钱包执行买入(每个钱包1笔)
|
|
755
|
+
* - 多个卖方钱包执行卖出(每个钱包1笔)
|
|
756
|
+
* - 买入总价值 = 卖出总价值
|
|
757
|
+
*/
|
|
758
|
+
async function pancakeBundleBuyFirstMultiWallet(params) {
|
|
759
|
+
const { buyerPrivateKeys, sellerPrivateKeys, tokenAddress, routeParams, buyerFunds, config, quoteToken, quoteTokenDecimals = 18 } = params;
|
|
760
|
+
const buyCount = buyerPrivateKeys.length;
|
|
761
|
+
const sellCount = sellerPrivateKeys.length;
|
|
762
|
+
if (buyCount === 0)
|
|
763
|
+
throw new Error('买方钱包数量不能为0');
|
|
764
|
+
if (sellCount === 0)
|
|
765
|
+
throw new Error('卖方钱包数量不能为0');
|
|
766
|
+
// 验证总交易数不超过限制
|
|
767
|
+
validateSwapCounts(buyCount, sellCount);
|
|
768
|
+
// ✅ 计算利润比例:每笔万分之6
|
|
769
|
+
const totalTxCount = buyCount + sellCount;
|
|
770
|
+
const profitRateBps = PROFIT_RATE_PER_TX_BPS * totalTxCount;
|
|
771
|
+
// ✅ 判断是否使用原生代币
|
|
772
|
+
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
773
|
+
const context = createPancakeContext(config);
|
|
774
|
+
const nonceManager = new NonceManager(context.provider);
|
|
775
|
+
// 创建所有钱包实例
|
|
776
|
+
const buyers = buyerPrivateKeys.map(pk => new Wallet(pk, context.provider));
|
|
777
|
+
const sellers = sellerPrivateKeys.map(pk => new Wallet(pk, context.provider));
|
|
778
|
+
// 使用第一个卖方作为主卖方(支付贿赂和利润)
|
|
779
|
+
const mainSeller = sellers[0];
|
|
780
|
+
// ✅ 计算总交易金额
|
|
781
|
+
let totalFundsWei;
|
|
782
|
+
if (buyerFunds) {
|
|
783
|
+
totalFundsWei = useNativeToken
|
|
784
|
+
? ethers.parseEther(String(buyerFunds))
|
|
785
|
+
: ethers.parseUnits(String(buyerFunds), quoteTokenDecimals);
|
|
786
|
+
}
|
|
787
|
+
else {
|
|
788
|
+
throw new Error('多钱包模式必须提供 buyerFunds(总交易金额)');
|
|
789
|
+
}
|
|
790
|
+
if (totalFundsWei <= 0n) {
|
|
791
|
+
throw new Error('交易金额必须大于0');
|
|
792
|
+
}
|
|
793
|
+
// ✅ 获取报价:买入能获得多少代币
|
|
794
|
+
const quoteResult = await quoteTokenOutput({
|
|
795
|
+
routeParams,
|
|
796
|
+
buyerFundsWei: totalFundsWei,
|
|
797
|
+
provider: context.provider
|
|
798
|
+
});
|
|
799
|
+
// ✅ 将总金额平均分配给买方
|
|
800
|
+
const buyAmountsWei = splitAmount(totalFundsWei, buyCount);
|
|
801
|
+
// ✅ 将代币平均分配给卖方
|
|
802
|
+
const sellAmountsWei = splitAmount(quoteResult.quotedTokenOut, sellCount);
|
|
803
|
+
const finalGasLimit = getGasLimit(config);
|
|
804
|
+
const gasPrice = await getGasPrice(context.provider, config);
|
|
805
|
+
const txType = config.txType ?? 0;
|
|
806
|
+
const deadline = BigInt(getDeadline());
|
|
807
|
+
// ✅ 估算利润
|
|
808
|
+
const estimatedProfitFromSell = await estimateProfitAmount({
|
|
809
|
+
provider: context.provider,
|
|
810
|
+
tokenAddress,
|
|
811
|
+
sellAmountToken: quoteResult.quotedTokenOut,
|
|
812
|
+
routeParams
|
|
813
|
+
});
|
|
814
|
+
const profitBase = estimatedProfitFromSell > 0n ? estimatedProfitFromSell : totalFundsWei;
|
|
815
|
+
const profitAmount = (profitBase * BigInt(profitRateBps)) / 10000n;
|
|
816
|
+
// ✅ 获取贿赂金额
|
|
817
|
+
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
818
|
+
? ethers.parseEther(String(config.bribeAmount))
|
|
819
|
+
: 0n;
|
|
820
|
+
const needBribeTx = bribeAmount > 0n;
|
|
821
|
+
// ✅ 获取所有钱包的 nonces
|
|
822
|
+
const allWallets = [...sellers, ...buyers];
|
|
823
|
+
const noncesMap = new Map();
|
|
824
|
+
await Promise.all(allWallets.map(async (wallet) => {
|
|
825
|
+
const addr = wallet.address.toLowerCase();
|
|
826
|
+
if (!noncesMap.has(addr)) {
|
|
827
|
+
const nonce = await nonceManager.getNextNonce(wallet);
|
|
828
|
+
noncesMap.set(addr, nonce);
|
|
829
|
+
}
|
|
830
|
+
}));
|
|
831
|
+
// ✅ 构建交易列表
|
|
832
|
+
const allTransactions = [];
|
|
833
|
+
// 1. 贿赂交易(由主卖方支付)
|
|
834
|
+
if (needBribeTx) {
|
|
835
|
+
const mainSellerAddr = mainSeller.address.toLowerCase();
|
|
836
|
+
const bribeNonce = noncesMap.get(mainSellerAddr);
|
|
837
|
+
noncesMap.set(mainSellerAddr, bribeNonce + 1);
|
|
838
|
+
const bribeTx = await mainSeller.signTransaction({
|
|
839
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
840
|
+
value: bribeAmount,
|
|
841
|
+
nonce: bribeNonce,
|
|
842
|
+
gasPrice,
|
|
843
|
+
gasLimit: 21000n,
|
|
844
|
+
chainId: context.chainId,
|
|
845
|
+
type: txType
|
|
846
|
+
});
|
|
847
|
+
allTransactions.push(bribeTx);
|
|
848
|
+
}
|
|
849
|
+
// 2. 构建所有买入交易
|
|
850
|
+
const buyTxPromises = buyers.map(async (buyer, i) => {
|
|
851
|
+
const buyAmount = buyAmountsWei[i];
|
|
852
|
+
const buyerAddr = buyer.address.toLowerCase();
|
|
853
|
+
const nonce = noncesMap.get(buyerAddr);
|
|
854
|
+
noncesMap.set(buyerAddr, nonce + 1);
|
|
855
|
+
const unsigned = await buildSingleBuyTx({
|
|
856
|
+
routeParams,
|
|
857
|
+
buyAmount,
|
|
858
|
+
buyer,
|
|
859
|
+
tokenAddress,
|
|
860
|
+
useNativeToken,
|
|
861
|
+
deadline
|
|
862
|
+
});
|
|
863
|
+
return buyer.signTransaction({
|
|
864
|
+
...unsigned,
|
|
865
|
+
from: buyer.address,
|
|
866
|
+
nonce,
|
|
867
|
+
gasLimit: finalGasLimit,
|
|
868
|
+
gasPrice,
|
|
869
|
+
chainId: context.chainId,
|
|
870
|
+
type: txType
|
|
871
|
+
});
|
|
872
|
+
});
|
|
873
|
+
// 3. 构建所有卖出交易
|
|
874
|
+
const sellTxPromises = sellers.map(async (seller, i) => {
|
|
875
|
+
const sellAmount = sellAmountsWei[i];
|
|
876
|
+
const sellerAddr = seller.address.toLowerCase();
|
|
877
|
+
const nonce = noncesMap.get(sellerAddr);
|
|
878
|
+
noncesMap.set(sellerAddr, nonce + 1);
|
|
879
|
+
const unsigned = await buildSingleSellTx({
|
|
880
|
+
routeParams,
|
|
881
|
+
sellAmount,
|
|
882
|
+
seller,
|
|
883
|
+
tokenAddress,
|
|
884
|
+
useNativeToken,
|
|
885
|
+
deadline
|
|
886
|
+
});
|
|
887
|
+
return seller.signTransaction({
|
|
888
|
+
...unsigned,
|
|
889
|
+
from: seller.address,
|
|
890
|
+
nonce,
|
|
891
|
+
gasLimit: finalGasLimit,
|
|
892
|
+
gasPrice,
|
|
893
|
+
chainId: context.chainId,
|
|
894
|
+
type: txType
|
|
895
|
+
});
|
|
896
|
+
});
|
|
897
|
+
// ✅ 并行签名所有买卖交易
|
|
898
|
+
const [signedBuys, signedSells] = await Promise.all([
|
|
899
|
+
Promise.all(buyTxPromises),
|
|
900
|
+
Promise.all(sellTxPromises)
|
|
901
|
+
]);
|
|
902
|
+
// 先买后卖:买入交易在前
|
|
903
|
+
allTransactions.push(...signedBuys, ...signedSells);
|
|
904
|
+
// 4. 利润多跳转账(由主卖方支付)
|
|
905
|
+
let profitHopWallets;
|
|
906
|
+
if (profitAmount > 0n) {
|
|
907
|
+
const mainSellerAddr = mainSeller.address.toLowerCase();
|
|
908
|
+
const profitNonce = noncesMap.get(mainSellerAddr);
|
|
909
|
+
const profitResult = await buildProfitTransaction({
|
|
910
|
+
provider: context.provider,
|
|
911
|
+
seller: mainSeller,
|
|
912
|
+
profitAmount,
|
|
913
|
+
profitNonce,
|
|
914
|
+
gasPrice,
|
|
915
|
+
chainId: context.chainId,
|
|
916
|
+
txType
|
|
917
|
+
});
|
|
918
|
+
if (profitResult) {
|
|
919
|
+
allTransactions.push(...profitResult.signedTransactions);
|
|
920
|
+
profitHopWallets = profitResult.hopWallets;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
nonceManager.clearTemp();
|
|
924
|
+
return {
|
|
925
|
+
signedTransactions: allTransactions,
|
|
926
|
+
profitHopWallets,
|
|
927
|
+
metadata: {
|
|
928
|
+
buyerAddress: buyers.map(b => b.address).join(','),
|
|
929
|
+
sellerAddress: sellers.map(s => s.address).join(','),
|
|
930
|
+
buyAmount: useNativeToken
|
|
931
|
+
? ethers.formatEther(totalFundsWei)
|
|
932
|
+
: ethers.formatUnits(totalFundsWei, quoteTokenDecimals),
|
|
933
|
+
sellAmount: quoteResult.quotedTokenOut.toString(),
|
|
934
|
+
profitAmount: profitAmount > 0n ? ethers.formatEther(profitAmount) : undefined,
|
|
935
|
+
buyCount,
|
|
936
|
+
sellCount,
|
|
937
|
+
buyAmounts: buyAmountsWei.map(amt => useNativeToken
|
|
938
|
+
? ethers.formatEther(amt)
|
|
939
|
+
: ethers.formatUnits(amt, quoteTokenDecimals)),
|
|
940
|
+
sellAmounts: sellAmountsWei.map(amt => amt.toString())
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
// ✅ 构建单笔买入交易
|
|
945
|
+
async function buildSingleBuyTx({ routeParams, buyAmount, buyer, tokenAddress, useNativeToken, deadline }) {
|
|
946
|
+
if (routeParams.routeType === 'v2') {
|
|
947
|
+
const { v2Path } = routeParams;
|
|
948
|
+
const v2Router = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, buyer);
|
|
949
|
+
if (useNativeToken) {
|
|
950
|
+
return await v2Router.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, v2Path, buyer.address, deadline, { value: buyAmount });
|
|
951
|
+
}
|
|
952
|
+
else {
|
|
953
|
+
return await v2Router.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(buyAmount, 0n, v2Path, buyer.address, deadline);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
if (routeParams.routeType === 'v3-single') {
|
|
957
|
+
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
958
|
+
const v3RouterIface = new ethers.Interface(PANCAKE_V3_ROUTER_ABI);
|
|
959
|
+
const v3Router = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, buyer);
|
|
960
|
+
const buyValue = useNativeToken ? buyAmount : 0n;
|
|
961
|
+
const buySwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
|
|
962
|
+
tokenIn: v3TokenIn,
|
|
963
|
+
tokenOut: v3TokenOut,
|
|
964
|
+
fee: v3Fee,
|
|
965
|
+
recipient: buyer.address,
|
|
966
|
+
amountIn: buyAmount,
|
|
967
|
+
amountOutMinimum: 0n,
|
|
968
|
+
sqrtPriceLimitX96: 0n
|
|
969
|
+
}]);
|
|
970
|
+
return await v3Router.multicall.populateTransaction(deadline, [buySwapData], { value: buyValue });
|
|
971
|
+
}
|
|
972
|
+
throw new Error('V3 多跳路由暂不支持');
|
|
973
|
+
}
|
|
974
|
+
// ✅ 构建单笔卖出交易
|
|
975
|
+
async function buildSingleSellTx({ routeParams, sellAmount, seller, tokenAddress, useNativeToken, deadline }) {
|
|
976
|
+
if (routeParams.routeType === 'v2') {
|
|
977
|
+
const { v2Path } = routeParams;
|
|
978
|
+
const reversePath = [...v2Path].reverse();
|
|
979
|
+
const v2Router = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, seller);
|
|
980
|
+
if (useNativeToken) {
|
|
981
|
+
return await v2Router.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmount, 0n, reversePath, seller.address, deadline);
|
|
982
|
+
}
|
|
983
|
+
else {
|
|
984
|
+
return await v2Router.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(sellAmount, 0n, reversePath, seller.address, deadline);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
if (routeParams.routeType === 'v3-single') {
|
|
988
|
+
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
989
|
+
const v3RouterIface = new ethers.Interface(PANCAKE_V3_ROUTER_ABI);
|
|
990
|
+
const v3Router = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, seller);
|
|
991
|
+
if (useNativeToken) {
|
|
992
|
+
const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
|
|
993
|
+
tokenIn: v3TokenOut,
|
|
994
|
+
tokenOut: v3TokenIn,
|
|
995
|
+
fee: v3Fee,
|
|
996
|
+
recipient: PANCAKE_V3_ROUTER_ADDRESS,
|
|
997
|
+
amountIn: sellAmount,
|
|
998
|
+
amountOutMinimum: 0n,
|
|
999
|
+
sqrtPriceLimitX96: 0n
|
|
1000
|
+
}]);
|
|
1001
|
+
const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
|
|
1002
|
+
return await v3Router.multicall.populateTransaction(deadline, [sellSwapData, sellUnwrapData]);
|
|
1003
|
+
}
|
|
1004
|
+
else {
|
|
1005
|
+
const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
|
|
1006
|
+
tokenIn: v3TokenOut,
|
|
1007
|
+
tokenOut: v3TokenIn,
|
|
1008
|
+
fee: v3Fee,
|
|
1009
|
+
recipient: seller.address,
|
|
1010
|
+
amountIn: sellAmount,
|
|
1011
|
+
amountOutMinimum: 0n,
|
|
1012
|
+
sqrtPriceLimitX96: 0n
|
|
1013
|
+
}]);
|
|
1014
|
+
return await v3Router.multicall.populateTransaction(deadline, [sellSwapData]);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
throw new Error('V3 多跳路由暂不支持');
|
|
1018
|
+
}
|