four-flap-meme-sdk 1.4.79 → 1.4.80
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.
|
@@ -313,6 +313,7 @@ async function planMultiNonces({ buyer, seller, buyCount, sellCount, extractProf
|
|
|
313
313
|
* - 多个买方钱包执行买入(每个钱包1笔)
|
|
314
314
|
* - 多个卖方钱包执行卖出(每个钱包1笔)
|
|
315
315
|
* - 买入总价值 = 卖出总价值
|
|
316
|
+
* - ✅ 优化:最大化并行操作
|
|
316
317
|
*/
|
|
317
318
|
async function fourBundleBuyFirstMultiWallet(params) {
|
|
318
319
|
const { buyerPrivateKeys, sellerPrivateKeys, tokenAddress, buyerFunds, config } = params;
|
|
@@ -346,9 +347,20 @@ async function fourBundleBuyFirstMultiWallet(params) {
|
|
|
346
347
|
if (totalFundsWei <= 0n) {
|
|
347
348
|
throw new Error('交易金额必须大于0');
|
|
348
349
|
}
|
|
349
|
-
|
|
350
|
+
const finalGasLimit = getGasLimit(config);
|
|
351
|
+
const txType = getTxType(config);
|
|
352
|
+
const bribeAmount = getBribeAmount(config);
|
|
353
|
+
const needBribeTx = bribeAmount > 0n;
|
|
354
|
+
// ✅ 第一批并行:报价 + Gas价格 + 代币精度
|
|
350
355
|
const helper3 = new Contract(ADDRESSES.BSC.TokenManagerHelper3, HELPER3_ABI, provider);
|
|
351
|
-
const
|
|
356
|
+
const erc20 = new Contract(tokenAddress, [
|
|
357
|
+
'function decimals() view returns (uint8)'
|
|
358
|
+
], provider);
|
|
359
|
+
const [buyQuote, gasPrice, decimals] = await Promise.all([
|
|
360
|
+
helper3.tryBuy(tokenAddress, 0n, totalFundsWei),
|
|
361
|
+
getOptimizedGasPrice(provider, getGasPriceConfig(config)),
|
|
362
|
+
erc20.decimals()
|
|
363
|
+
]);
|
|
352
364
|
const estimatedTokenAmount = buyQuote.estimatedAmount ?? buyQuote[2];
|
|
353
365
|
if (!estimatedTokenAmount || estimatedTokenAmount <= 0n) {
|
|
354
366
|
throw new Error('报价失败:无法估算可买入的代币数量');
|
|
@@ -357,33 +369,36 @@ async function fourBundleBuyFirstMultiWallet(params) {
|
|
|
357
369
|
const buyAmountsWei = splitAmount(totalFundsWei, buyCount);
|
|
358
370
|
// ✅ 将代币平均分配给卖方
|
|
359
371
|
const sellAmountsWei = splitAmount(estimatedTokenAmount, sellCount);
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
const
|
|
372
|
+
// ✅ 第二批并行:估算利润 + 获取所有钱包 nonces
|
|
373
|
+
// 预先计算所有唯一钱包地址(去重)
|
|
374
|
+
const allWallets = [...sellers, ...buyers];
|
|
375
|
+
const uniqueAddresses = new Set(allWallets.map(w => w.address.toLowerCase()));
|
|
376
|
+
const uniqueWallets = allWallets.filter((w, i) => {
|
|
377
|
+
const addr = w.address.toLowerCase();
|
|
378
|
+
const firstIdx = allWallets.findIndex(x => x.address.toLowerCase() === addr);
|
|
379
|
+
return firstIdx === i;
|
|
380
|
+
});
|
|
381
|
+
const [sellResult, noncesArray] = await Promise.all([
|
|
382
|
+
trySell('BSC', config.rpcUrl, tokenAddress, estimatedTokenAmount),
|
|
383
|
+
nonceManager.getNextNoncesForWallets(uniqueWallets)
|
|
384
|
+
]);
|
|
385
|
+
// 构建 nonces Map
|
|
386
|
+
const noncesMap = new Map();
|
|
387
|
+
uniqueWallets.forEach((wallet, i) => {
|
|
388
|
+
noncesMap.set(wallet.address.toLowerCase(), noncesArray[i]);
|
|
389
|
+
});
|
|
365
390
|
const estimatedSellFunds = sellResult.funds;
|
|
366
391
|
const profitAmount = (estimatedSellFunds * BigInt(profitRateBps)) / 10000n;
|
|
367
|
-
// ✅
|
|
368
|
-
const bribeAmount = getBribeAmount(config);
|
|
369
|
-
const needBribeTx = bribeAmount > 0n;
|
|
370
|
-
// ✅ 获取所有钱包的 nonces
|
|
371
|
-
const noncesMap = new Map();
|
|
372
|
-
await Promise.all([...sellers, ...buyers].map(async (wallet) => {
|
|
373
|
-
const addr = wallet.address.toLowerCase();
|
|
374
|
-
if (!noncesMap.has(addr)) {
|
|
375
|
-
const nonce = await nonceManager.getNextNonce(wallet);
|
|
376
|
-
noncesMap.set(addr, nonce);
|
|
377
|
-
}
|
|
378
|
-
}));
|
|
379
|
-
// ✅ 构建交易列表
|
|
392
|
+
// ✅ 第三批并行:构建并签名所有交易
|
|
380
393
|
const allTransactions = [];
|
|
381
|
-
|
|
394
|
+
const tm = new Contract(TM_ADDRESS, TM_ABI, provider);
|
|
395
|
+
// 1. 贿赂交易(由主卖方支付)- 先处理以确定 nonce 偏移
|
|
396
|
+
let bribeTxPromise = null;
|
|
382
397
|
if (needBribeTx) {
|
|
383
398
|
const mainSellerAddr = mainSeller.address.toLowerCase();
|
|
384
399
|
const bribeNonce = noncesMap.get(mainSellerAddr);
|
|
385
400
|
noncesMap.set(mainSellerAddr, bribeNonce + 1);
|
|
386
|
-
|
|
401
|
+
bribeTxPromise = mainSeller.signTransaction({
|
|
387
402
|
to: BLOCKRAZOR_BUILDER_EOA,
|
|
388
403
|
value: bribeAmount,
|
|
389
404
|
nonce: bribeNonce,
|
|
@@ -392,40 +407,45 @@ async function fourBundleBuyFirstMultiWallet(params) {
|
|
|
392
407
|
chainId: chainIdNum,
|
|
393
408
|
type: txType
|
|
394
409
|
});
|
|
395
|
-
allTransactions.push(bribeTx);
|
|
396
410
|
}
|
|
397
|
-
// 2.
|
|
398
|
-
const
|
|
411
|
+
// 2. 预分配所有 nonces(同步操作,避免竞态)
|
|
412
|
+
const buyerNonces = [];
|
|
413
|
+
const sellerNonces = [];
|
|
414
|
+
buyers.forEach(buyer => {
|
|
415
|
+
const addr = buyer.address.toLowerCase();
|
|
416
|
+
const nonce = noncesMap.get(addr);
|
|
417
|
+
buyerNonces.push(nonce);
|
|
418
|
+
noncesMap.set(addr, nonce + 1);
|
|
419
|
+
});
|
|
420
|
+
sellers.forEach(seller => {
|
|
421
|
+
const addr = seller.address.toLowerCase();
|
|
422
|
+
const nonce = noncesMap.get(addr);
|
|
423
|
+
sellerNonces.push(nonce);
|
|
424
|
+
noncesMap.set(addr, nonce + 1);
|
|
425
|
+
});
|
|
426
|
+
// 3. 并行构建所有买入交易
|
|
399
427
|
const buyTxPromises = buyers.map(async (buyer, i) => {
|
|
400
|
-
const buyAmount = buyAmountsWei[i];
|
|
401
|
-
const buyerAddr = buyer.address.toLowerCase();
|
|
402
|
-
const nonce = noncesMap.get(buyerAddr);
|
|
403
|
-
noncesMap.set(buyerAddr, nonce + 1);
|
|
404
428
|
const tmBuyer = tm.connect(buyer);
|
|
405
|
-
const unsigned = await tmBuyer.buyTokenAMAP.populateTransaction(0n, tokenAddress, buyer.address,
|
|
429
|
+
const unsigned = await tmBuyer.buyTokenAMAP.populateTransaction(0n, tokenAddress, buyer.address, buyAmountsWei[i], 0n, { value: buyAmountsWei[i] });
|
|
406
430
|
return buyer.signTransaction({
|
|
407
431
|
...unsigned,
|
|
408
432
|
from: buyer.address,
|
|
409
|
-
nonce,
|
|
433
|
+
nonce: buyerNonces[i],
|
|
410
434
|
gasLimit: finalGasLimit,
|
|
411
435
|
gasPrice,
|
|
412
436
|
chainId: chainIdNum,
|
|
413
437
|
type: txType,
|
|
414
|
-
value:
|
|
438
|
+
value: buyAmountsWei[i]
|
|
415
439
|
});
|
|
416
440
|
});
|
|
417
|
-
//
|
|
441
|
+
// 4. 并行构建所有卖出交易
|
|
418
442
|
const sellTxPromises = sellers.map(async (seller, i) => {
|
|
419
|
-
const sellAmount = sellAmountsWei[i];
|
|
420
|
-
const sellerAddr = seller.address.toLowerCase();
|
|
421
|
-
const nonce = noncesMap.get(sellerAddr);
|
|
422
|
-
noncesMap.set(sellerAddr, nonce + 1);
|
|
423
443
|
const tmSeller = tm.connect(seller);
|
|
424
|
-
const unsigned = await tmSeller.sellToken.populateTransaction(0n, tokenAddress,
|
|
444
|
+
const unsigned = await tmSeller.sellToken.populateTransaction(0n, tokenAddress, sellAmountsWei[i], 0n);
|
|
425
445
|
return seller.signTransaction({
|
|
426
446
|
...unsigned,
|
|
427
447
|
from: seller.address,
|
|
428
|
-
nonce,
|
|
448
|
+
nonce: sellerNonces[i],
|
|
429
449
|
gasLimit: finalGasLimit,
|
|
430
450
|
gasPrice,
|
|
431
451
|
chainId: chainIdNum,
|
|
@@ -433,14 +453,17 @@ async function fourBundleBuyFirstMultiWallet(params) {
|
|
|
433
453
|
value: 0n
|
|
434
454
|
});
|
|
435
455
|
});
|
|
436
|
-
// ✅
|
|
437
|
-
const [signedBuys, signedSells] = await Promise.all([
|
|
456
|
+
// ✅ 并行签名所有交易(贿赂 + 买入 + 卖出)
|
|
457
|
+
const [bribeTx, signedBuys, signedSells] = await Promise.all([
|
|
458
|
+
bribeTxPromise,
|
|
438
459
|
Promise.all(buyTxPromises),
|
|
439
460
|
Promise.all(sellTxPromises)
|
|
440
461
|
]);
|
|
441
|
-
//
|
|
462
|
+
// 组装交易顺序:贿赂 → 买入 → 卖出
|
|
463
|
+
if (bribeTx)
|
|
464
|
+
allTransactions.push(bribeTx);
|
|
442
465
|
allTransactions.push(...signedBuys, ...signedSells);
|
|
443
|
-
//
|
|
466
|
+
// 5. 利润多跳转账(由主卖方支付)
|
|
444
467
|
let profitHopWallets;
|
|
445
468
|
if (profitAmount > 0n) {
|
|
446
469
|
const mainSellerAddr = mainSeller.address.toLowerCase();
|
|
@@ -460,11 +483,6 @@ async function fourBundleBuyFirstMultiWallet(params) {
|
|
|
460
483
|
profitHopWallets = profitHopResult.hopWallets;
|
|
461
484
|
}
|
|
462
485
|
nonceManager.clearTemp();
|
|
463
|
-
// 获取代币精度用于显示
|
|
464
|
-
const erc20 = new Contract(tokenAddress, [
|
|
465
|
-
'function decimals() view returns (uint8)'
|
|
466
|
-
], provider);
|
|
467
|
-
const decimals = await erc20.decimals();
|
|
468
486
|
return {
|
|
469
487
|
signedTransactions: allTransactions,
|
|
470
488
|
profitHopWallets,
|
|
@@ -609,6 +609,7 @@ function createChainContext(chain, rpcUrl) {
|
|
|
609
609
|
* - 多个买方钱包执行买入(每个钱包1笔)
|
|
610
610
|
* - 多个卖方钱包执行卖出(每个钱包1笔)
|
|
611
611
|
* - 买入总价值 = 卖出总价值
|
|
612
|
+
* - ✅ 优化:最大化并行操作
|
|
612
613
|
*/
|
|
613
614
|
async function flapBundleBuyFirstMultiWallet(params) {
|
|
614
615
|
const { chain, buyerPrivateKeys, sellerPrivateKeys, tokenAddress, buyerFunds, config, quoteToken, quoteTokenDecimals = 18 } = params;
|
|
@@ -644,24 +645,43 @@ async function flapBundleBuyFirstMultiWallet(params) {
|
|
|
644
645
|
if (totalFundsWei <= 0n) {
|
|
645
646
|
throw new Error('交易金额必须大于0');
|
|
646
647
|
}
|
|
647
|
-
|
|
648
|
-
const
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
648
|
+
const finalGasLimit = getGasLimit(config);
|
|
649
|
+
const txType = getTxType(config);
|
|
650
|
+
const bribeAmount = getBribeAmount(config);
|
|
651
|
+
const needBribeTx = bribeAmount > 0n;
|
|
652
|
+
// ✅ 第一批并行:报价 + Gas价格 + 代币精度 + 所有钱包 nonces
|
|
653
|
+
// 预先计算所有唯一钱包地址(去重)
|
|
654
|
+
const allWallets = [...sellers, ...buyers];
|
|
655
|
+
const uniqueWallets = allWallets.filter((w, i) => {
|
|
656
|
+
const addr = w.address.toLowerCase();
|
|
657
|
+
const firstIdx = allWallets.findIndex(x => x.address.toLowerCase() === addr);
|
|
658
|
+
return firstIdx === i;
|
|
655
659
|
});
|
|
660
|
+
const erc20 = new Contract(tokenAddress, ERC20_BALANCE_ABI, chainContext.provider);
|
|
661
|
+
const [quoteResult, gasPrice, decimals, noncesArray] = await Promise.all([
|
|
662
|
+
quoteBuyerOutput({
|
|
663
|
+
portalAddress: chainContext.portalAddress,
|
|
664
|
+
tokenAddress,
|
|
665
|
+
buyerFundsWei: totalFundsWei,
|
|
666
|
+
provider: chainContext.provider,
|
|
667
|
+
skipQuoteOnError: config.skipQuoteOnError,
|
|
668
|
+
inputToken
|
|
669
|
+
}),
|
|
670
|
+
getOptimizedGasPrice(chainContext.provider, getGasPriceConfig(config)),
|
|
671
|
+
erc20.decimals(),
|
|
672
|
+
nonceManager.getNextNoncesForWallets(uniqueWallets)
|
|
673
|
+
]);
|
|
674
|
+
// 构建 nonces Map
|
|
675
|
+
const noncesMap = new Map();
|
|
676
|
+
uniqueWallets.forEach((wallet, i) => {
|
|
677
|
+
noncesMap.set(wallet.address.toLowerCase(), noncesArray[i]);
|
|
678
|
+
});
|
|
679
|
+
const priorityFee = gasPrice / 10n === 0n ? 1n : gasPrice / 10n;
|
|
656
680
|
// ✅ 将总金额平均分配给买方
|
|
657
681
|
const buyAmountsWei = splitAmount(totalFundsWei, buyCount);
|
|
658
682
|
// ✅ 将代币平均分配给卖方
|
|
659
683
|
const sellAmountsWei = splitAmount(quoteResult.sellAmountWei, sellCount);
|
|
660
|
-
|
|
661
|
-
const gasPrice = await getOptimizedGasPrice(chainContext.provider, getGasPriceConfig(config));
|
|
662
|
-
const txType = getTxType(config);
|
|
663
|
-
const priorityFee = gasPrice / 10n === 0n ? 1n : gasPrice / 10n;
|
|
664
|
-
// ✅ 估算利润
|
|
684
|
+
// ✅ 第二批并行:估算利润 + ERC20 转原生代币报价(如需要)
|
|
665
685
|
const portal = new Contract(chainContext.portalAddress, PORTAL_ABI, chainContext.provider);
|
|
666
686
|
const estimatedSellFunds = await estimateSellFunds(portal, tokenAddress, quoteResult.sellAmountWei, outputToken);
|
|
667
687
|
const profitBase = estimatedSellFunds > 0n ? estimatedSellFunds : totalFundsWei;
|
|
@@ -671,26 +691,15 @@ async function flapBundleBuyFirstMultiWallet(params) {
|
|
|
671
691
|
if (!useNativeToken && tokenProfitAmount > 0n) {
|
|
672
692
|
nativeProfitAmount = await getTokenToNativeQuote(chainContext.provider, inputToken, tokenProfitAmount, chainContext.chainId);
|
|
673
693
|
}
|
|
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
|
-
// ✅ 构建交易列表
|
|
694
|
+
// ✅ 第三批并行:构建并签名所有交易
|
|
687
695
|
const allTransactions = [];
|
|
688
|
-
// 1.
|
|
696
|
+
// 1. 贿赂交易(由主卖方支付)- 先处理以确定 nonce 偏移
|
|
697
|
+
let bribeTxPromise = null;
|
|
689
698
|
if (needBribeTx) {
|
|
690
699
|
const mainSellerAddr = mainSeller.address.toLowerCase();
|
|
691
700
|
const bribeNonce = noncesMap.get(mainSellerAddr);
|
|
692
701
|
noncesMap.set(mainSellerAddr, bribeNonce + 1);
|
|
693
|
-
|
|
702
|
+
bribeTxPromise = mainSeller.signTransaction({
|
|
694
703
|
to: BLOCKRAZOR_BUILDER_EOA,
|
|
695
704
|
value: bribeAmount,
|
|
696
705
|
nonce: bribeNonce,
|
|
@@ -699,50 +708,56 @@ async function flapBundleBuyFirstMultiWallet(params) {
|
|
|
699
708
|
chainId: chainContext.chainId,
|
|
700
709
|
type: txType
|
|
701
710
|
});
|
|
702
|
-
allTransactions.push(bribeTx);
|
|
703
711
|
}
|
|
704
|
-
// 2.
|
|
712
|
+
// 2. 预分配所有 nonces(同步操作,避免竞态)
|
|
713
|
+
const buyerNonces = [];
|
|
714
|
+
const sellerNonces = [];
|
|
715
|
+
buyers.forEach(buyer => {
|
|
716
|
+
const addr = buyer.address.toLowerCase();
|
|
717
|
+
const nonce = noncesMap.get(addr);
|
|
718
|
+
buyerNonces.push(nonce);
|
|
719
|
+
noncesMap.set(addr, nonce + 1);
|
|
720
|
+
});
|
|
721
|
+
sellers.forEach(seller => {
|
|
722
|
+
const addr = seller.address.toLowerCase();
|
|
723
|
+
const nonce = noncesMap.get(addr);
|
|
724
|
+
sellerNonces.push(nonce);
|
|
725
|
+
noncesMap.set(addr, nonce + 1);
|
|
726
|
+
});
|
|
727
|
+
// 3. 并行构建所有买入交易
|
|
705
728
|
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
729
|
const portalBuyer = new Contract(chainContext.portalAddress, PORTAL_ABI, buyer);
|
|
711
730
|
const unsigned = await portalBuyer.swapExactInput.populateTransaction({
|
|
712
731
|
inputToken,
|
|
713
732
|
outputToken: tokenAddress,
|
|
714
|
-
inputAmount:
|
|
733
|
+
inputAmount: buyAmountsWei[i],
|
|
715
734
|
minOutputAmount: 0n,
|
|
716
735
|
permitData: '0x'
|
|
717
|
-
}, useNativeToken ? { value:
|
|
736
|
+
}, useNativeToken ? { value: buyAmountsWei[i] } : {});
|
|
718
737
|
return buyer.signTransaction(buildTransactionRequest(unsigned, {
|
|
719
738
|
from: buyer.address,
|
|
720
|
-
nonce,
|
|
739
|
+
nonce: buyerNonces[i],
|
|
721
740
|
gasLimit: finalGasLimit,
|
|
722
741
|
gasPrice,
|
|
723
742
|
priorityFee,
|
|
724
743
|
chainId: chainContext.chainId,
|
|
725
744
|
txType,
|
|
726
|
-
value: useNativeToken ?
|
|
745
|
+
value: useNativeToken ? buyAmountsWei[i] : 0n
|
|
727
746
|
}));
|
|
728
747
|
});
|
|
729
|
-
//
|
|
748
|
+
// 4. 并行构建所有卖出交易
|
|
730
749
|
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
750
|
const portalSeller = new Contract(chainContext.portalAddress, PORTAL_ABI, seller);
|
|
736
751
|
const unsigned = await portalSeller.swapExactInput.populateTransaction({
|
|
737
752
|
inputToken: tokenAddress,
|
|
738
753
|
outputToken,
|
|
739
|
-
inputAmount:
|
|
754
|
+
inputAmount: sellAmountsWei[i],
|
|
740
755
|
minOutputAmount: 0n,
|
|
741
756
|
permitData: '0x'
|
|
742
757
|
});
|
|
743
758
|
return seller.signTransaction(buildTransactionRequest(unsigned, {
|
|
744
759
|
from: seller.address,
|
|
745
|
-
nonce,
|
|
760
|
+
nonce: sellerNonces[i],
|
|
746
761
|
gasLimit: finalGasLimit,
|
|
747
762
|
gasPrice,
|
|
748
763
|
priorityFee,
|
|
@@ -751,14 +766,17 @@ async function flapBundleBuyFirstMultiWallet(params) {
|
|
|
751
766
|
value: 0n
|
|
752
767
|
}));
|
|
753
768
|
});
|
|
754
|
-
// ✅
|
|
755
|
-
const [signedBuys, signedSells] = await Promise.all([
|
|
769
|
+
// ✅ 并行签名所有交易(贿赂 + 买入 + 卖出)
|
|
770
|
+
const [bribeTx, signedBuys, signedSells] = await Promise.all([
|
|
771
|
+
bribeTxPromise,
|
|
756
772
|
Promise.all(buyTxPromises),
|
|
757
773
|
Promise.all(sellTxPromises)
|
|
758
774
|
]);
|
|
759
|
-
//
|
|
775
|
+
// 组装交易顺序:贿赂 → 买入 → 卖出
|
|
776
|
+
if (bribeTx)
|
|
777
|
+
allTransactions.push(bribeTx);
|
|
760
778
|
allTransactions.push(...signedBuys, ...signedSells);
|
|
761
|
-
//
|
|
779
|
+
// 5. 利润多跳转账(由主卖方支付)
|
|
762
780
|
let profitHopWallets;
|
|
763
781
|
if (nativeProfitAmount > 0n) {
|
|
764
782
|
const mainSellerAddr = mainSeller.address.toLowerCase();
|
|
@@ -778,14 +796,6 @@ async function flapBundleBuyFirstMultiWallet(params) {
|
|
|
778
796
|
}
|
|
779
797
|
}
|
|
780
798
|
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
799
|
return {
|
|
790
800
|
signedTransactions: allTransactions,
|
|
791
801
|
profitHopWallets,
|
|
@@ -793,12 +803,12 @@ async function flapBundleBuyFirstMultiWallet(params) {
|
|
|
793
803
|
buyerAddress: buyers.map(b => b.address).join(','),
|
|
794
804
|
sellerAddress: sellers.map(s => s.address).join(','),
|
|
795
805
|
buyAmount: ethers.formatEther(totalFundsWei),
|
|
796
|
-
sellAmount: ethers.formatUnits(quoteResult.sellAmountWei,
|
|
806
|
+
sellAmount: ethers.formatUnits(quoteResult.sellAmountWei, decimals),
|
|
797
807
|
profitAmount: nativeProfitAmount > 0n ? ethers.formatEther(nativeProfitAmount) : undefined,
|
|
798
808
|
buyCount,
|
|
799
809
|
sellCount,
|
|
800
810
|
buyAmounts: buyAmountsWei.map(amt => ethers.formatEther(amt)),
|
|
801
|
-
sellAmounts: sellAmountsWei.map(amt => ethers.formatUnits(amt,
|
|
811
|
+
sellAmounts: sellAmountsWei.map(amt => ethers.formatUnits(amt, decimals))
|
|
802
812
|
}
|
|
803
813
|
};
|
|
804
814
|
}
|
|
@@ -754,6 +754,7 @@ function buildNoncePlanFromStartNonces(startNonces, sameAddress, profitNeeded, n
|
|
|
754
754
|
* - 多个买方钱包执行买入(每个钱包1笔)
|
|
755
755
|
* - 多个卖方钱包执行卖出(每个钱包1笔)
|
|
756
756
|
* - 买入总价值 = 卖出总价值
|
|
757
|
+
* - ✅ 优化:最大化并行操作
|
|
757
758
|
*/
|
|
758
759
|
async function pancakeBundleBuyFirstMultiWallet(params) {
|
|
759
760
|
const { buyerPrivateKeys, sellerPrivateKeys, tokenAddress, routeParams, buyerFunds, config, quoteToken, quoteTokenDecimals = 18 } = params;
|
|
@@ -790,21 +791,40 @@ async function pancakeBundleBuyFirstMultiWallet(params) {
|
|
|
790
791
|
if (totalFundsWei <= 0n) {
|
|
791
792
|
throw new Error('交易金额必须大于0');
|
|
792
793
|
}
|
|
793
|
-
|
|
794
|
-
const
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
794
|
+
const finalGasLimit = getGasLimit(config);
|
|
795
|
+
const txType = config.txType ?? 0;
|
|
796
|
+
const deadline = BigInt(getDeadline());
|
|
797
|
+
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
798
|
+
? ethers.parseEther(String(config.bribeAmount))
|
|
799
|
+
: 0n;
|
|
800
|
+
const needBribeTx = bribeAmount > 0n;
|
|
801
|
+
// ✅ 第一批并行:报价 + Gas价格 + 所有钱包 nonces
|
|
802
|
+
// 预先计算所有唯一钱包地址(去重)
|
|
803
|
+
const allWallets = [...sellers, ...buyers];
|
|
804
|
+
const uniqueWallets = allWallets.filter((w, i) => {
|
|
805
|
+
const addr = w.address.toLowerCase();
|
|
806
|
+
const firstIdx = allWallets.findIndex(x => x.address.toLowerCase() === addr);
|
|
807
|
+
return firstIdx === i;
|
|
808
|
+
});
|
|
809
|
+
const [quoteResult, gasPrice, noncesArray] = await Promise.all([
|
|
810
|
+
quoteTokenOutput({
|
|
811
|
+
routeParams,
|
|
812
|
+
buyerFundsWei: totalFundsWei,
|
|
813
|
+
provider: context.provider
|
|
814
|
+
}),
|
|
815
|
+
getGasPrice(context.provider, config),
|
|
816
|
+
nonceManager.getNextNoncesForWallets(uniqueWallets)
|
|
817
|
+
]);
|
|
818
|
+
// 构建 nonces Map
|
|
819
|
+
const noncesMap = new Map();
|
|
820
|
+
uniqueWallets.forEach((wallet, i) => {
|
|
821
|
+
noncesMap.set(wallet.address.toLowerCase(), noncesArray[i]);
|
|
798
822
|
});
|
|
799
823
|
// ✅ 将总金额平均分配给买方
|
|
800
824
|
const buyAmountsWei = splitAmount(totalFundsWei, buyCount);
|
|
801
825
|
// ✅ 将代币平均分配给卖方
|
|
802
826
|
const sellAmountsWei = splitAmount(quoteResult.quotedTokenOut, sellCount);
|
|
803
|
-
|
|
804
|
-
const gasPrice = await getGasPrice(context.provider, config);
|
|
805
|
-
const txType = config.txType ?? 0;
|
|
806
|
-
const deadline = BigInt(getDeadline());
|
|
807
|
-
// ✅ 估算利润
|
|
827
|
+
// ✅ 第二批并行:估算利润(可以和交易构建并行,但需要先获取报价)
|
|
808
828
|
const estimatedProfitFromSell = await estimateProfitAmount({
|
|
809
829
|
provider: context.provider,
|
|
810
830
|
tokenAddress,
|
|
@@ -813,29 +833,15 @@ async function pancakeBundleBuyFirstMultiWallet(params) {
|
|
|
813
833
|
});
|
|
814
834
|
const profitBase = estimatedProfitFromSell > 0n ? estimatedProfitFromSell : totalFundsWei;
|
|
815
835
|
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
|
-
// ✅ 构建交易列表
|
|
836
|
+
// ✅ 第三批并行:构建并签名所有交易
|
|
832
837
|
const allTransactions = [];
|
|
833
|
-
// 1.
|
|
838
|
+
// 1. 贿赂交易(由主卖方支付)- 先处理以确定 nonce 偏移
|
|
839
|
+
let bribeTxPromise = null;
|
|
834
840
|
if (needBribeTx) {
|
|
835
841
|
const mainSellerAddr = mainSeller.address.toLowerCase();
|
|
836
842
|
const bribeNonce = noncesMap.get(mainSellerAddr);
|
|
837
843
|
noncesMap.set(mainSellerAddr, bribeNonce + 1);
|
|
838
|
-
|
|
844
|
+
bribeTxPromise = mainSeller.signTransaction({
|
|
839
845
|
to: BLOCKRAZOR_BUILDER_EOA,
|
|
840
846
|
value: bribeAmount,
|
|
841
847
|
nonce: bribeNonce,
|
|
@@ -844,17 +850,27 @@ async function pancakeBundleBuyFirstMultiWallet(params) {
|
|
|
844
850
|
chainId: context.chainId,
|
|
845
851
|
type: txType
|
|
846
852
|
});
|
|
847
|
-
allTransactions.push(bribeTx);
|
|
848
853
|
}
|
|
849
|
-
// 2.
|
|
854
|
+
// 2. 预分配所有 nonces(同步操作,避免竞态)
|
|
855
|
+
const buyerNonces = [];
|
|
856
|
+
const sellerNonces = [];
|
|
857
|
+
buyers.forEach(buyer => {
|
|
858
|
+
const addr = buyer.address.toLowerCase();
|
|
859
|
+
const nonce = noncesMap.get(addr);
|
|
860
|
+
buyerNonces.push(nonce);
|
|
861
|
+
noncesMap.set(addr, nonce + 1);
|
|
862
|
+
});
|
|
863
|
+
sellers.forEach(seller => {
|
|
864
|
+
const addr = seller.address.toLowerCase();
|
|
865
|
+
const nonce = noncesMap.get(addr);
|
|
866
|
+
sellerNonces.push(nonce);
|
|
867
|
+
noncesMap.set(addr, nonce + 1);
|
|
868
|
+
});
|
|
869
|
+
// 3. 并行构建所有买入交易
|
|
850
870
|
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
871
|
const unsigned = await buildSingleBuyTx({
|
|
856
872
|
routeParams,
|
|
857
|
-
buyAmount,
|
|
873
|
+
buyAmount: buyAmountsWei[i],
|
|
858
874
|
buyer,
|
|
859
875
|
tokenAddress,
|
|
860
876
|
useNativeToken,
|
|
@@ -863,22 +879,18 @@ async function pancakeBundleBuyFirstMultiWallet(params) {
|
|
|
863
879
|
return buyer.signTransaction({
|
|
864
880
|
...unsigned,
|
|
865
881
|
from: buyer.address,
|
|
866
|
-
nonce,
|
|
882
|
+
nonce: buyerNonces[i],
|
|
867
883
|
gasLimit: finalGasLimit,
|
|
868
884
|
gasPrice,
|
|
869
885
|
chainId: context.chainId,
|
|
870
886
|
type: txType
|
|
871
887
|
});
|
|
872
888
|
});
|
|
873
|
-
//
|
|
889
|
+
// 4. 并行构建所有卖出交易
|
|
874
890
|
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
891
|
const unsigned = await buildSingleSellTx({
|
|
880
892
|
routeParams,
|
|
881
|
-
sellAmount,
|
|
893
|
+
sellAmount: sellAmountsWei[i],
|
|
882
894
|
seller,
|
|
883
895
|
tokenAddress,
|
|
884
896
|
useNativeToken,
|
|
@@ -887,21 +899,24 @@ async function pancakeBundleBuyFirstMultiWallet(params) {
|
|
|
887
899
|
return seller.signTransaction({
|
|
888
900
|
...unsigned,
|
|
889
901
|
from: seller.address,
|
|
890
|
-
nonce,
|
|
902
|
+
nonce: sellerNonces[i],
|
|
891
903
|
gasLimit: finalGasLimit,
|
|
892
904
|
gasPrice,
|
|
893
905
|
chainId: context.chainId,
|
|
894
906
|
type: txType
|
|
895
907
|
});
|
|
896
908
|
});
|
|
897
|
-
// ✅
|
|
898
|
-
const [signedBuys, signedSells] = await Promise.all([
|
|
909
|
+
// ✅ 并行签名所有交易(贿赂 + 买入 + 卖出)
|
|
910
|
+
const [bribeTx, signedBuys, signedSells] = await Promise.all([
|
|
911
|
+
bribeTxPromise,
|
|
899
912
|
Promise.all(buyTxPromises),
|
|
900
913
|
Promise.all(sellTxPromises)
|
|
901
914
|
]);
|
|
902
|
-
//
|
|
915
|
+
// 组装交易顺序:贿赂 → 买入 → 卖出
|
|
916
|
+
if (bribeTx)
|
|
917
|
+
allTransactions.push(bribeTx);
|
|
903
918
|
allTransactions.push(...signedBuys, ...signedSells);
|
|
904
|
-
//
|
|
919
|
+
// 5. 利润多跳转账(由主卖方支付)
|
|
905
920
|
let profitHopWallets;
|
|
906
921
|
if (profitAmount > 0n) {
|
|
907
922
|
const mainSellerAddr = mainSeller.address.toLowerCase();
|