four-flap-meme-sdk 1.4.15 → 1.4.17
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.js +9 -16
- package/dist/contracts/tm-bundle-merkle/swap.d.ts +40 -0
- package/dist/contracts/tm-bundle-merkle/swap.js +209 -2
- package/dist/contracts/tm-bundle-merkle/utils.js +19 -8
- package/dist/flap/portal-bundle-merkle/swap-buy-first.js +2 -20
- package/dist/flap/portal-bundle-merkle/swap.d.ts +45 -0
- package/dist/flap/portal-bundle-merkle/swap.js +277 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/pancake/bundle-buy-first.js +53 -54
- package/dist/pancake/bundle-swap.js +20 -19
- package/dist/utils/constants.d.ts +3 -1
- package/dist/utils/constants.js +3 -1
- package/package.json +1 -1
|
@@ -7,7 +7,8 @@ import { ethers, Contract, Wallet } from 'ethers';
|
|
|
7
7
|
import { NonceManager, getOptimizedGasPrice } from '../../utils/bundle-helpers.js';
|
|
8
8
|
import { ADDRESSES } from '../../utils/constants.js';
|
|
9
9
|
import { TM_ABI, HELPER3_ABI, TM_ADDRESS } from './swap-internal.js';
|
|
10
|
-
import { getTxType, getGasPriceConfig, getProfitRecipient,
|
|
10
|
+
import { getTxType, getGasPriceConfig, getProfitRecipient, getBribeAmount } from './config.js';
|
|
11
|
+
import { PROFIT_CONFIG } from '../../utils/constants.js';
|
|
11
12
|
import { trySell } from '../tm.js';
|
|
12
13
|
// ✅ BlockRazor Builder EOA 地址(用于贿赂)
|
|
13
14
|
const BLOCKRAZOR_BUILDER_EOA = '0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20';
|
|
@@ -88,7 +89,7 @@ export async function fourBundleBuyFirstMerkle(params) {
|
|
|
88
89
|
const minBuyAmount = 0n;
|
|
89
90
|
// 预先规划 nonces
|
|
90
91
|
const extractProfit = true;
|
|
91
|
-
const profitRateBps =
|
|
92
|
+
const profitRateBps = PROFIT_CONFIG.RATE_BPS_CAPITAL; // 万分之三
|
|
92
93
|
// ✅ 获取贿赂金额
|
|
93
94
|
const bribeAmount = getBribeAmount(config);
|
|
94
95
|
const needBribeTx = bribeAmount > 0n;
|
|
@@ -101,25 +102,17 @@ export async function fourBundleBuyFirstMerkle(params) {
|
|
|
101
102
|
sellerNonceCount++; // 授权交易
|
|
102
103
|
if (extractProfit)
|
|
103
104
|
sellerNonceCount++; // 利润交易
|
|
104
|
-
// ✅ 优化:使用批量 nonce
|
|
105
|
-
// 同一地址时使用 getNextNonceBatch 获取连续 nonce
|
|
106
|
-
// 不同地址时使用 getNextNoncesForWallets 一次性获取两个地址的初始 nonce
|
|
105
|
+
// ✅ 优化:使用批量 nonce 获取(单次网络往返)
|
|
107
106
|
const getNoncesPromise = sameAddress
|
|
108
107
|
? nonceManager.getNextNonceBatch(seller, buyerNonceCount + sellerNonceCount)
|
|
109
108
|
.then(nonces => ({ buyerNonces: [], sellerNonces: nonces }))
|
|
110
|
-
: (
|
|
111
|
-
|
|
112
|
-
const initialNonces = await nonceManager.getNextNoncesForWallets([buyer, seller]);
|
|
109
|
+
: nonceManager.getNextNoncesForWallets([buyer, seller])
|
|
110
|
+
.then(initialNonces => ({
|
|
113
111
|
// buyer 只需要 1 个 nonce
|
|
114
|
-
|
|
112
|
+
buyerNonces: [initialNonces[0]],
|
|
115
113
|
// seller 需要 sellerNonceCount 个连续 nonce,从初始 nonce 开始
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
for (let i = 1; i < sellerNonceCount; i++) {
|
|
119
|
-
await nonceManager.getNextNonce(seller); // 递增缓存
|
|
120
|
-
}
|
|
121
|
-
return { buyerNonces, sellerNonces };
|
|
122
|
-
})();
|
|
114
|
+
sellerNonces: Array.from({ length: sellerNonceCount }, (_, i) => initialNonces[1] + i)
|
|
115
|
+
}));
|
|
123
116
|
const [sellResult, buyUnsigned, sellUnsigned, noncesResult] = await Promise.all([
|
|
124
117
|
trySell('BSC', config.rpcUrl, tokenAddress, sellAmountWei),
|
|
125
118
|
tmBuyer.buyTokenAMAP.populateTransaction(0n, tokenAddress, buyer.address, buyerFundsWei, minBuyAmount, { value: buyerFundsWei }),
|
|
@@ -91,3 +91,43 @@ export interface FourBatchSwapResult {
|
|
|
91
91
|
* 限制:最多 24 个买方(服务器限制 25 笔交易,包含 1 笔利润交易)
|
|
92
92
|
*/
|
|
93
93
|
export declare function fourBatchSwapMerkle(params: FourBatchSwapSignParams): Promise<FourBatchSwapResult>;
|
|
94
|
+
/**
|
|
95
|
+
* Four 快捷批量换手参数
|
|
96
|
+
*/
|
|
97
|
+
export interface FourQuickBatchSwapSignParams {
|
|
98
|
+
sellerPrivateKey: string;
|
|
99
|
+
sellAmount?: string;
|
|
100
|
+
sellPercentage?: number;
|
|
101
|
+
buyerPrivateKeys: string[];
|
|
102
|
+
buyerRatios?: number[];
|
|
103
|
+
buyerAmounts?: string[];
|
|
104
|
+
tokenAddress: string;
|
|
105
|
+
config: FourSwapSignConfig;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Four 快捷批量换手结果
|
|
109
|
+
*/
|
|
110
|
+
export interface FourQuickBatchSwapResult {
|
|
111
|
+
signedTransactions: string[];
|
|
112
|
+
metadata?: {
|
|
113
|
+
sellerAddress: string;
|
|
114
|
+
buyerAddresses: string[];
|
|
115
|
+
sellAmount: string;
|
|
116
|
+
estimatedBNBOut: string;
|
|
117
|
+
transferAmounts: string[];
|
|
118
|
+
profitAmount?: string;
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Four 内盘快捷批量换手(资金利用率模式)
|
|
123
|
+
*
|
|
124
|
+
* 流程:[贿赂] → [卖出] → [转账1, 转账2, ...] → [买入1, 买入2, ...] → [利润]
|
|
125
|
+
*
|
|
126
|
+
* 特点:
|
|
127
|
+
* - 子钱包不需要预先有 BNB
|
|
128
|
+
* - 资金来自主钱包卖出代币所得
|
|
129
|
+
* - 提升资金利用率
|
|
130
|
+
*
|
|
131
|
+
* 限制:最多 23 个买方(2N + 3 ≤ 50)
|
|
132
|
+
*/
|
|
133
|
+
export declare function fourQuickBatchSwapMerkle(params: FourQuickBatchSwapSignParams): Promise<FourQuickBatchSwapResult>;
|
|
@@ -64,7 +64,7 @@ export async function fourBundleSwapMerkle(params) {
|
|
|
64
64
|
const needApproval = currentAllowance < APPROVAL_THRESHOLD;
|
|
65
65
|
// 利润配置
|
|
66
66
|
const extractProfit = true;
|
|
67
|
-
const profitRateBps = PROFIT_CONFIG.
|
|
67
|
+
const profitRateBps = PROFIT_CONFIG.RATE_BPS_CAPITAL;
|
|
68
68
|
const profitAmount = extractProfit && sellerWillGetBNB > 0n
|
|
69
69
|
? (sellerWillGetBNB * BigInt(profitRateBps)) / 10000n
|
|
70
70
|
: 0n;
|
|
@@ -282,7 +282,7 @@ export async function fourBatchSwapMerkle(params) {
|
|
|
282
282
|
const APPROVAL_THRESHOLD = ethers.parseUnits('1000000000', decimals);
|
|
283
283
|
const needApproval = currentAllowance < APPROVAL_THRESHOLD;
|
|
284
284
|
// 利润配置
|
|
285
|
-
const profitRateBps = PROFIT_CONFIG.
|
|
285
|
+
const profitRateBps = PROFIT_CONFIG.RATE_BPS_CAPITAL;
|
|
286
286
|
const profitAmount = totalBuyerFunds > 0n
|
|
287
287
|
? (totalBuyerFunds * BigInt(profitRateBps)) / 10000n
|
|
288
288
|
: 0n;
|
|
@@ -425,3 +425,210 @@ export async function fourBatchSwapMerkle(params) {
|
|
|
425
425
|
}
|
|
426
426
|
};
|
|
427
427
|
}
|
|
428
|
+
/**
|
|
429
|
+
* Four 内盘快捷批量换手(资金利用率模式)
|
|
430
|
+
*
|
|
431
|
+
* 流程:[贿赂] → [卖出] → [转账1, 转账2, ...] → [买入1, 买入2, ...] → [利润]
|
|
432
|
+
*
|
|
433
|
+
* 特点:
|
|
434
|
+
* - 子钱包不需要预先有 BNB
|
|
435
|
+
* - 资金来自主钱包卖出代币所得
|
|
436
|
+
* - 提升资金利用率
|
|
437
|
+
*
|
|
438
|
+
* 限制:最多 23 个买方(2N + 3 ≤ 50)
|
|
439
|
+
*/
|
|
440
|
+
export async function fourQuickBatchSwapMerkle(params) {
|
|
441
|
+
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKeys, buyerRatios, buyerAmounts, tokenAddress, config } = params;
|
|
442
|
+
// ✅ 校验买方数量
|
|
443
|
+
// 贿赂(1) + 卖出(1) + 转账(N) + 买入(N) + 利润(1) ≤ 50 → 2N + 3 ≤ 50 → N ≤ 23
|
|
444
|
+
const MAX_BUYERS = 23;
|
|
445
|
+
if (buyerPrivateKeys.length === 0) {
|
|
446
|
+
throw new Error('至少需要一个买方钱包');
|
|
447
|
+
}
|
|
448
|
+
if (buyerPrivateKeys.length > MAX_BUYERS) {
|
|
449
|
+
throw new Error(`资金利用率模式买方钱包数量超过限制: ${buyerPrivateKeys.length} > ${MAX_BUYERS}`);
|
|
450
|
+
}
|
|
451
|
+
// ✅ 校验分配模式
|
|
452
|
+
if (!buyerRatios && !buyerAmounts) {
|
|
453
|
+
throw new Error('必须提供 buyerRatios 或 buyerAmounts');
|
|
454
|
+
}
|
|
455
|
+
if (buyerRatios && buyerRatios.length !== buyerPrivateKeys.length) {
|
|
456
|
+
throw new Error(`buyerRatios 长度 (${buyerRatios.length}) 与 buyerPrivateKeys 长度 (${buyerPrivateKeys.length}) 不匹配`);
|
|
457
|
+
}
|
|
458
|
+
if (buyerAmounts && buyerAmounts.length !== buyerPrivateKeys.length) {
|
|
459
|
+
throw new Error(`buyerAmounts 长度 (${buyerAmounts.length}) 与 buyerPrivateKeys 长度 (${buyerPrivateKeys.length}) 不匹配`);
|
|
460
|
+
}
|
|
461
|
+
const chainIdNum = config.chainId ?? 56;
|
|
462
|
+
const provider = new ethers.JsonRpcProvider(config.rpcUrl, {
|
|
463
|
+
chainId: chainIdNum,
|
|
464
|
+
name: 'BSC'
|
|
465
|
+
});
|
|
466
|
+
const seller = new Wallet(sellerPrivateKey, provider);
|
|
467
|
+
const buyers = buyerPrivateKeys.map(pk => new Wallet(pk, provider));
|
|
468
|
+
const nonceManager = new NonceManager(provider);
|
|
469
|
+
// 创建适配的配置对象
|
|
470
|
+
const bundleConfig = {
|
|
471
|
+
minGasPriceGwei: config.minGasPriceGwei,
|
|
472
|
+
maxGasPriceGwei: config.maxGasPriceGwei,
|
|
473
|
+
gasLimit: typeof config.gasLimit === 'bigint' ? Number(config.gasLimit) : config.gasLimit,
|
|
474
|
+
gasLimitMultiplier: config.gasLimitMultiplier,
|
|
475
|
+
txType: config.txType,
|
|
476
|
+
chainId: config.chainId
|
|
477
|
+
};
|
|
478
|
+
const finalGasLimit = getGasLimit(bundleConfig);
|
|
479
|
+
const txType = getTxType(bundleConfig);
|
|
480
|
+
// ✅ 并行获取:卖出数量、gasPrice
|
|
481
|
+
const [sellAmountResult, gasPrice] = await Promise.all([
|
|
482
|
+
calculateSellAmount(provider, tokenAddress, seller.address, sellAmount, sellPercentage),
|
|
483
|
+
getOptimizedGasPrice(provider, getGasPriceConfig(bundleConfig))
|
|
484
|
+
]);
|
|
485
|
+
const { amount: sellAmountWei, decimals } = sellAmountResult;
|
|
486
|
+
// ✅ 获取卖出报价
|
|
487
|
+
const helper3 = new Contract(ADDRESSES.BSC.TokenManagerHelper3, HELPER3_ABI, provider);
|
|
488
|
+
const sellQuote = await helper3.trySell(tokenAddress, sellAmountWei);
|
|
489
|
+
const estimatedBNBOut = sellQuote.funds;
|
|
490
|
+
console.log(`[fourQuickBatchSwapMerkle] 卖出数量: ${ethers.formatUnits(sellAmountWei, decimals)}`);
|
|
491
|
+
console.log(`[fourQuickBatchSwapMerkle] 预估卖出所得: ${ethers.formatEther(estimatedBNBOut)} BNB`);
|
|
492
|
+
// ✅ 计算利润(万分之三)
|
|
493
|
+
const profitRateBps = PROFIT_CONFIG.RATE_BPS_CAPITAL;
|
|
494
|
+
const profitAmount = estimatedBNBOut > 0n
|
|
495
|
+
? (estimatedBNBOut * BigInt(profitRateBps)) / 10000n
|
|
496
|
+
: 0n;
|
|
497
|
+
const distributableAmount = estimatedBNBOut - profitAmount;
|
|
498
|
+
// ✅ 计算每个买方分到的金额
|
|
499
|
+
let transferAmountsWei;
|
|
500
|
+
if (buyerAmounts && buyerAmounts.length === buyers.length) {
|
|
501
|
+
// 数量模式
|
|
502
|
+
transferAmountsWei = buyerAmounts.map(amt => ethers.parseEther(amt));
|
|
503
|
+
const totalTransfer = transferAmountsWei.reduce((a, b) => a + b, 0n);
|
|
504
|
+
if (totalTransfer > distributableAmount) {
|
|
505
|
+
throw new Error(`指定的买入总金额超过可分配金额 (${ethers.formatEther(distributableAmount)} BNB)`);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
else if (buyerRatios && buyerRatios.length === buyers.length) {
|
|
509
|
+
// 比例模式
|
|
510
|
+
transferAmountsWei = buyerRatios.map(ratio => {
|
|
511
|
+
return (distributableAmount * BigInt(Math.round(ratio * 10000))) / 10000n;
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
else {
|
|
515
|
+
throw new Error('必须提供 buyerRatios 或 buyerAmounts');
|
|
516
|
+
}
|
|
517
|
+
// ✅ 获取贿赂金额
|
|
518
|
+
const bribeAmount = getBribeAmount(config);
|
|
519
|
+
// ✅ 验证主钱包余额
|
|
520
|
+
const sellerBalance = await seller.provider.getBalance(seller.address);
|
|
521
|
+
// 主钱包只需要支付:贿赂 + Gas(卖出后有 BNB 可用于转账和利润)
|
|
522
|
+
const sellerGasCost = gasPrice * (21000n + finalGasLimit + 21000n * BigInt(buyers.length) + 21000n);
|
|
523
|
+
const sellerRequired = bribeAmount + sellerGasCost;
|
|
524
|
+
if (sellerBalance < sellerRequired) {
|
|
525
|
+
throw new Error(`主钱包 BNB 余额不足: 需要约 ${ethers.formatEther(sellerRequired)} BNB (贿赂: ${ethers.formatEther(bribeAmount)}, Gas: ${ethers.formatEther(sellerGasCost)}), 实际 ${ethers.formatEther(sellerBalance)} BNB`);
|
|
526
|
+
}
|
|
527
|
+
// ==================== 规划 Nonce ====================
|
|
528
|
+
let sellerNonce = await nonceManager.getNextNonce(seller);
|
|
529
|
+
// ==================== 1. 贿赂交易 ====================
|
|
530
|
+
let bribeTx = null;
|
|
531
|
+
if (bribeAmount > 0n) {
|
|
532
|
+
bribeTx = await seller.signTransaction({
|
|
533
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
534
|
+
value: bribeAmount,
|
|
535
|
+
nonce: sellerNonce++,
|
|
536
|
+
gasPrice,
|
|
537
|
+
gasLimit: 21000n,
|
|
538
|
+
chainId: chainIdNum,
|
|
539
|
+
type: txType
|
|
540
|
+
});
|
|
541
|
+
console.log(`[fourQuickBatchSwapMerkle] 贿赂交易已签名`);
|
|
542
|
+
}
|
|
543
|
+
// ==================== 2. 卖出交易 ====================
|
|
544
|
+
const tmSeller = new Contract(TM_ADDRESS, TM_ABI, seller);
|
|
545
|
+
const sellUnsigned = await tmSeller.sellToken.populateTransaction(0n, tokenAddress, sellAmountWei, 0n);
|
|
546
|
+
const signedSell = await seller.signTransaction({
|
|
547
|
+
...sellUnsigned,
|
|
548
|
+
from: seller.address,
|
|
549
|
+
nonce: sellerNonce++,
|
|
550
|
+
gasLimit: finalGasLimit,
|
|
551
|
+
gasPrice,
|
|
552
|
+
chainId: chainIdNum,
|
|
553
|
+
type: txType
|
|
554
|
+
});
|
|
555
|
+
console.log(`[fourQuickBatchSwapMerkle] 卖出交易已签名`);
|
|
556
|
+
// ==================== 3. 转账交易(并行签名)====================
|
|
557
|
+
const reserveGas = ethers.parseEther((config.reserveGasBNB || 0.0005).toString());
|
|
558
|
+
const buyerGasCost = gasPrice * finalGasLimit;
|
|
559
|
+
// ✅ 预分配 nonce,然后并行签名
|
|
560
|
+
const transferNonces = buyers.map((_, i) => sellerNonce + i);
|
|
561
|
+
sellerNonce += buyers.length; // 更新 sellerNonce
|
|
562
|
+
const transferTxs = await Promise.all(buyers.map((buyer, i) => {
|
|
563
|
+
const transferValue = transferAmountsWei[i] + reserveGas + buyerGasCost;
|
|
564
|
+
return seller.signTransaction({
|
|
565
|
+
to: buyer.address,
|
|
566
|
+
value: transferValue,
|
|
567
|
+
nonce: transferNonces[i],
|
|
568
|
+
gasPrice,
|
|
569
|
+
gasLimit: 21000n,
|
|
570
|
+
chainId: chainIdNum,
|
|
571
|
+
type: txType
|
|
572
|
+
});
|
|
573
|
+
}));
|
|
574
|
+
console.log(`[fourQuickBatchSwapMerkle] ${transferTxs.length} 笔转账交易已签名`);
|
|
575
|
+
// ==================== 4. 买入交易 ====================
|
|
576
|
+
const buyerNonces = await Promise.all(buyers.map(buyer => nonceManager.getNextNonce(buyer)));
|
|
577
|
+
const signedBuys = await Promise.all(buyers.map(async (buyer, i) => {
|
|
578
|
+
const buyAmount = transferAmountsWei[i];
|
|
579
|
+
const tmBuyer = new Contract(TM_ADDRESS, TM_ABI, buyer);
|
|
580
|
+
const buyUnsigned = await tmBuyer.buyTokenAMAP.populateTransaction(0n, tokenAddress, buyer.address, buyAmount, 0n, { value: buyAmount });
|
|
581
|
+
return buyer.signTransaction({
|
|
582
|
+
...buyUnsigned,
|
|
583
|
+
from: buyer.address,
|
|
584
|
+
nonce: buyerNonces[i],
|
|
585
|
+
gasLimit: finalGasLimit,
|
|
586
|
+
gasPrice,
|
|
587
|
+
chainId: chainIdNum,
|
|
588
|
+
type: txType,
|
|
589
|
+
value: buyAmount
|
|
590
|
+
});
|
|
591
|
+
}));
|
|
592
|
+
console.log(`[fourQuickBatchSwapMerkle] ${signedBuys.length} 笔买入交易已签名`);
|
|
593
|
+
// ==================== 5. 利润交易 ====================
|
|
594
|
+
let profitTx = null;
|
|
595
|
+
if (profitAmount > 0n) {
|
|
596
|
+
profitTx = await seller.signTransaction({
|
|
597
|
+
to: PROFIT_CONFIG.RECIPIENT,
|
|
598
|
+
value: profitAmount,
|
|
599
|
+
nonce: sellerNonce++,
|
|
600
|
+
gasPrice,
|
|
601
|
+
gasLimit: 21000n,
|
|
602
|
+
chainId: chainIdNum,
|
|
603
|
+
type: txType
|
|
604
|
+
});
|
|
605
|
+
console.log(`[fourQuickBatchSwapMerkle] 利润交易已签名`);
|
|
606
|
+
}
|
|
607
|
+
nonceManager.clearTemp();
|
|
608
|
+
// ==================== 组装交易数组 ====================
|
|
609
|
+
const signedTransactions = [];
|
|
610
|
+
if (bribeTx)
|
|
611
|
+
signedTransactions.push(bribeTx);
|
|
612
|
+
signedTransactions.push(signedSell);
|
|
613
|
+
signedTransactions.push(...transferTxs);
|
|
614
|
+
signedTransactions.push(...signedBuys);
|
|
615
|
+
if (profitTx)
|
|
616
|
+
signedTransactions.push(profitTx);
|
|
617
|
+
console.log(`[fourQuickBatchSwapMerkle] 交易组装完成: ${signedTransactions.length} 笔`);
|
|
618
|
+
console.log(` - 贿赂: ${bribeTx ? 1 : 0}`);
|
|
619
|
+
console.log(` - 卖出: 1`);
|
|
620
|
+
console.log(` - 转账: ${transferTxs.length}`);
|
|
621
|
+
console.log(` - 买入: ${signedBuys.length}`);
|
|
622
|
+
console.log(` - 利润: ${profitTx ? 1 : 0}`);
|
|
623
|
+
return {
|
|
624
|
+
signedTransactions,
|
|
625
|
+
metadata: {
|
|
626
|
+
sellerAddress: seller.address,
|
|
627
|
+
buyerAddresses: buyers.map(b => b.address),
|
|
628
|
+
sellAmount: ethers.formatUnits(sellAmountWei, decimals),
|
|
629
|
+
estimatedBNBOut: ethers.formatEther(estimatedBNBOut),
|
|
630
|
+
transferAmounts: transferAmountsWei.map(amt => ethers.formatEther(amt)),
|
|
631
|
+
profitAmount: profitAmount > 0n ? ethers.formatEther(profitAmount) : undefined
|
|
632
|
+
}
|
|
633
|
+
};
|
|
634
|
+
}
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import { ethers, Wallet, Contract } from 'ethers';
|
|
2
2
|
// import { MerkleClient } from '../../clients/merkle.js';
|
|
3
3
|
import { getOptimizedGasPrice, NonceManager } from '../../utils/bundle-helpers.js';
|
|
4
|
-
import {
|
|
4
|
+
import { PROFIT_CONFIG } from '../../utils/constants.js';
|
|
5
|
+
import { getTxType, getGasPriceConfig, shouldExtractProfit, getProfitRecipient } from './config.js';
|
|
5
6
|
import { getErc20DecimalsMerkle as _getErc20DecimalsMerkle, generateHopWallets as _generateHopWallets, normalizeAmounts as _normalizeAmounts, batchGetBalances as _batchGetBalances, calculateGasLimit as _calculateGasLimit, isNativeTokenAddress as _isNativeTokenAddress } from './internal.js';
|
|
7
|
+
// ==================== 本地利润计算(万分之三)====================
|
|
8
|
+
/**
|
|
9
|
+
* 计算利润金额(万分之三)
|
|
10
|
+
* ✅ 归集和分散专用:3 bps = 0.03%
|
|
11
|
+
*/
|
|
12
|
+
function calculateProfit(amount) {
|
|
13
|
+
const profit = (amount * BigInt(PROFIT_CONFIG.RATE_BPS_CAPITAL)) / 10000n;
|
|
14
|
+
const remaining = amount - profit;
|
|
15
|
+
return { profit, remaining };
|
|
16
|
+
}
|
|
6
17
|
// ==================== ERC20 → 原生代币报价 ====================
|
|
7
18
|
// BSC 链常量
|
|
8
19
|
const BSC_PANCAKE_V2_ROUTER = '0x10ED43C718714eb63d5aA57B78B54704E256024E';
|
|
@@ -122,7 +133,7 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
122
133
|
totalAmountBeforeProfit += originalAmount;
|
|
123
134
|
let actualAmount = originalAmount;
|
|
124
135
|
if (extractProfit) {
|
|
125
|
-
const { profit, remaining } = calculateProfit(originalAmount
|
|
136
|
+
const { profit, remaining } = calculateProfit(originalAmount);
|
|
126
137
|
actualAmount = remaining;
|
|
127
138
|
totalProfit += profit;
|
|
128
139
|
}
|
|
@@ -163,7 +174,7 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
163
174
|
totalAmountBeforeProfit += originalAmount;
|
|
164
175
|
let actualAmount = originalAmount;
|
|
165
176
|
if (extractProfit) {
|
|
166
|
-
const { profit, remaining } = calculateProfit(originalAmount
|
|
177
|
+
const { profit, remaining } = calculateProfit(originalAmount);
|
|
167
178
|
actualAmount = remaining;
|
|
168
179
|
totalTokenProfit += profit; // 累计 ERC20 代币利润
|
|
169
180
|
}
|
|
@@ -249,7 +260,7 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
249
260
|
totalAmountBeforeProfit += originalAmountWei;
|
|
250
261
|
let amountWei = originalAmountWei;
|
|
251
262
|
if (extractProfit) {
|
|
252
|
-
const { profit, remaining } = calculateProfit(originalAmountWei
|
|
263
|
+
const { profit, remaining } = calculateProfit(originalAmountWei);
|
|
253
264
|
amountWei = remaining;
|
|
254
265
|
if (isNative) {
|
|
255
266
|
totalProfit += profit; // 原生币直接累加
|
|
@@ -519,7 +530,7 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
519
530
|
}
|
|
520
531
|
// 累计总利润
|
|
521
532
|
if (extractProfit && toSend > 0n) {
|
|
522
|
-
const { profit } = calculateProfit(toSend
|
|
533
|
+
const { profit } = calculateProfit(toSend);
|
|
523
534
|
totalProfit += profit;
|
|
524
535
|
}
|
|
525
536
|
}
|
|
@@ -537,7 +548,7 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
537
548
|
totalProfit = 0n;
|
|
538
549
|
for (let i = 0; i < sweepAmounts.length; i++) {
|
|
539
550
|
if (sweepAmounts[i] > 0n) {
|
|
540
|
-
totalProfit += calculateProfit(sweepAmounts[i]
|
|
551
|
+
totalProfit += calculateProfit(sweepAmounts[i]).profit;
|
|
541
552
|
}
|
|
542
553
|
}
|
|
543
554
|
}
|
|
@@ -666,7 +677,7 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
666
677
|
}
|
|
667
678
|
// 累计总代币利润(ERC20 归集)
|
|
668
679
|
if (extractProfit && toSend > 0n) {
|
|
669
|
-
const { profit } = calculateProfit(toSend
|
|
680
|
+
const { profit } = calculateProfit(toSend);
|
|
670
681
|
totalProfit += profit; // 先累计代币利润
|
|
671
682
|
}
|
|
672
683
|
}
|
|
@@ -1013,7 +1024,7 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
1013
1024
|
let totalTokenProfit = 0n;
|
|
1014
1025
|
for (let i = 0; i < sweepAmounts.length; i++) {
|
|
1015
1026
|
if (sweepAmounts[i] > 0n) {
|
|
1016
|
-
const { profit } = calculateProfit(sweepAmounts[i]
|
|
1027
|
+
const { profit } = calculateProfit(sweepAmounts[i]);
|
|
1017
1028
|
if (isNative) {
|
|
1018
1029
|
totalProfit += profit; // 原生币直接累加
|
|
1019
1030
|
}
|
|
@@ -393,26 +393,8 @@ function calculateProfitAmount(expectedFunds) {
|
|
|
393
393
|
if (expectedFunds <= 0n) {
|
|
394
394
|
return 0n;
|
|
395
395
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
async function validateNativeBalances({ sameAddress, buyerBalance, buyerFundsWei, gasLimit, gasPrice, buyer, seller, provider, nativeToken }) {
|
|
399
|
-
const effectiveGasPrice = gasPrice;
|
|
400
|
-
const gasCost = gasLimit * effectiveGasPrice;
|
|
401
|
-
if (sameAddress) {
|
|
402
|
-
const combinedRequired = buyerFundsWei + gasCost * 2n;
|
|
403
|
-
if (buyerBalance < combinedRequired) {
|
|
404
|
-
throw new Error(`账户余额不足: 需要 ${ethers.formatEther(combinedRequired)} ${nativeToken}(${ethers.formatEther(buyerFundsWei)} 购买 + ${ethers.formatEther(gasCost * 2n)} 两笔Gas),实际 ${ethers.formatEther(buyerBalance)} ${nativeToken}`);
|
|
405
|
-
}
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
const buyerRequiredBalance = buyerFundsWei + gasCost;
|
|
409
|
-
if (buyerBalance < buyerRequiredBalance) {
|
|
410
|
-
throw new Error(`买方余额不足: 需要 ${ethers.formatEther(buyerRequiredBalance)} ${nativeToken}(${ethers.formatEther(buyerFundsWei)} 购买 + ${ethers.formatEther(gasCost)} Gas),实际 ${ethers.formatEther(buyerBalance)} ${nativeToken}`);
|
|
411
|
-
}
|
|
412
|
-
const sellerBalance = await provider.getBalance(seller.address);
|
|
413
|
-
if (sellerBalance < gasCost) {
|
|
414
|
-
throw new Error(`卖方余额不足: 需要 ${ethers.formatEther(gasCost)} ${nativeToken} (Gas),实际 ${ethers.formatEther(sellerBalance)} ${nativeToken}`);
|
|
415
|
-
}
|
|
396
|
+
// 万分之三
|
|
397
|
+
return (expectedFunds * BigInt(PROFIT_CONFIG.RATE_BPS_CAPITAL)) / 10000n;
|
|
416
398
|
}
|
|
417
399
|
/**
|
|
418
400
|
* ✅ 优化:使用批量 nonce 获取
|
|
@@ -101,3 +101,48 @@ export interface FlapBatchSwapResult {
|
|
|
101
101
|
* 限制:最多 24 个买方(服务器限制 25 笔交易,包含 1 笔利润交易)
|
|
102
102
|
*/
|
|
103
103
|
export declare function flapBatchSwapMerkle(params: FlapBatchSwapSignParams): Promise<FlapBatchSwapResult>;
|
|
104
|
+
/**
|
|
105
|
+
* Flap 快捷批量换手参数
|
|
106
|
+
*/
|
|
107
|
+
export interface FlapQuickBatchSwapSignParams {
|
|
108
|
+
chain: FlapChain;
|
|
109
|
+
sellerPrivateKey: string;
|
|
110
|
+
sellAmount?: string;
|
|
111
|
+
sellPercentage?: number;
|
|
112
|
+
buyerPrivateKeys: string[];
|
|
113
|
+
buyerRatios?: number[];
|
|
114
|
+
buyerAmounts?: string[];
|
|
115
|
+
tokenAddress: string;
|
|
116
|
+
config: FlapSwapSignConfig;
|
|
117
|
+
quoteToken?: string;
|
|
118
|
+
quoteTokenDecimals?: number;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Flap 快捷批量换手结果
|
|
122
|
+
*/
|
|
123
|
+
export interface FlapQuickBatchSwapResult {
|
|
124
|
+
signedTransactions: string[];
|
|
125
|
+
metadata?: {
|
|
126
|
+
sellerAddress: string;
|
|
127
|
+
buyerAddresses: string[];
|
|
128
|
+
sellAmount: string;
|
|
129
|
+
estimatedOutput: string;
|
|
130
|
+
transferAmounts: string[];
|
|
131
|
+
profitAmount?: string;
|
|
132
|
+
useNativeToken: boolean;
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Flap 内盘快捷批量换手(资金利用率模式)
|
|
137
|
+
*
|
|
138
|
+
* 流程:[贿赂] → [卖出] → [转账1, 转账2, ...] → [买入1, 买入2, ...] → [利润]
|
|
139
|
+
*
|
|
140
|
+
* 特点:
|
|
141
|
+
* - 子钱包不需要预先有余额
|
|
142
|
+
* - 资金来自主钱包卖出代币所得
|
|
143
|
+
* - 提升资金利用率
|
|
144
|
+
* - 支持原生代币(BNB/OKB/ETH)和 ERC20(如 USDT)两种模式
|
|
145
|
+
*
|
|
146
|
+
* 限制:最多 23 个买方(2N + 3 ≤ 50)
|
|
147
|
+
*/
|
|
148
|
+
export declare function flapQuickBatchSwapMerkle(params: FlapQuickBatchSwapSignParams): Promise<FlapQuickBatchSwapResult>;
|
|
@@ -458,7 +458,7 @@ function calculateProfitAmount(quotedNative) {
|
|
|
458
458
|
if (quotedNative <= 0n) {
|
|
459
459
|
return 0n;
|
|
460
460
|
}
|
|
461
|
-
return (quotedNative * BigInt(PROFIT_CONFIG.
|
|
461
|
+
return (quotedNative * BigInt(PROFIT_CONFIG.RATE_BPS_CAPITAL)) / 10000n;
|
|
462
462
|
}
|
|
463
463
|
function countTruthy(values) {
|
|
464
464
|
return values.filter(Boolean).length;
|
|
@@ -668,3 +668,279 @@ export async function flapBatchSwapMerkle(params) {
|
|
|
668
668
|
}
|
|
669
669
|
};
|
|
670
670
|
}
|
|
671
|
+
/**
|
|
672
|
+
* Flap 内盘快捷批量换手(资金利用率模式)
|
|
673
|
+
*
|
|
674
|
+
* 流程:[贿赂] → [卖出] → [转账1, 转账2, ...] → [买入1, 买入2, ...] → [利润]
|
|
675
|
+
*
|
|
676
|
+
* 特点:
|
|
677
|
+
* - 子钱包不需要预先有余额
|
|
678
|
+
* - 资金来自主钱包卖出代币所得
|
|
679
|
+
* - 提升资金利用率
|
|
680
|
+
* - 支持原生代币(BNB/OKB/ETH)和 ERC20(如 USDT)两种模式
|
|
681
|
+
*
|
|
682
|
+
* 限制:最多 23 个买方(2N + 3 ≤ 50)
|
|
683
|
+
*/
|
|
684
|
+
export async function flapQuickBatchSwapMerkle(params) {
|
|
685
|
+
const { chain, sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKeys, buyerRatios, buyerAmounts, tokenAddress, config, quoteToken, quoteTokenDecimals = 18 } = params;
|
|
686
|
+
// ✅ 校验买方数量
|
|
687
|
+
// BNB 模式:贿赂(1) + 卖出(1) + 转账(N) + 买入(N) + 利润(1) ≤ 50 → 2N + 3 ≤ 50 → N ≤ 23
|
|
688
|
+
// ERC20 模式:贿赂(1) + 卖出(1) + ERC20转账(N) + 买入(N) + 利润(1) ≤ 50 → 2N + 3 ≤ 50 → N ≤ 23
|
|
689
|
+
const MAX_BUYERS = 23;
|
|
690
|
+
if (buyerPrivateKeys.length === 0) {
|
|
691
|
+
throw new Error('至少需要一个买方钱包');
|
|
692
|
+
}
|
|
693
|
+
if (buyerPrivateKeys.length > MAX_BUYERS) {
|
|
694
|
+
throw new Error(`资金利用率模式买方钱包数量超过限制: ${buyerPrivateKeys.length} > ${MAX_BUYERS}`);
|
|
695
|
+
}
|
|
696
|
+
// ✅ 校验分配模式
|
|
697
|
+
if (!buyerRatios && !buyerAmounts) {
|
|
698
|
+
throw new Error('必须提供 buyerRatios 或 buyerAmounts');
|
|
699
|
+
}
|
|
700
|
+
if (buyerRatios && buyerRatios.length !== buyerPrivateKeys.length) {
|
|
701
|
+
throw new Error(`buyerRatios 长度 (${buyerRatios.length}) 与 buyerPrivateKeys 长度 (${buyerPrivateKeys.length}) 不匹配`);
|
|
702
|
+
}
|
|
703
|
+
if (buyerAmounts && buyerAmounts.length !== buyerPrivateKeys.length) {
|
|
704
|
+
throw new Error(`buyerAmounts 长度 (${buyerAmounts.length}) 与 buyerPrivateKeys 长度 (${buyerPrivateKeys.length}) 不匹配`);
|
|
705
|
+
}
|
|
706
|
+
// ✅ 判断是否使用原生代币
|
|
707
|
+
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
708
|
+
const outputToken = useNativeToken ? ZERO_ADDRESS : quoteToken;
|
|
709
|
+
const ERC20_TRANSFER_GAS = 65000n;
|
|
710
|
+
const chainContext = createChainContext(chain, config);
|
|
711
|
+
const seller = new Wallet(sellerPrivateKey, chainContext.provider);
|
|
712
|
+
const buyers = buyerPrivateKeys.map(pk => new Wallet(pk, chainContext.provider));
|
|
713
|
+
const nonceManager = new NonceManager(chainContext.provider);
|
|
714
|
+
const finalGasLimit = getGasLimit(config);
|
|
715
|
+
const txType = getTxType(config);
|
|
716
|
+
// ✅ 并行获取:卖出数量、gasPrice
|
|
717
|
+
const [sellAmountResult, gasPrice] = await Promise.all([
|
|
718
|
+
calculateSellAmount(chainContext.provider, tokenAddress, seller.address, sellAmount, sellPercentage),
|
|
719
|
+
getOptimizedGasPrice(chainContext.provider, getGasPriceConfig(config))
|
|
720
|
+
]);
|
|
721
|
+
const { amount: sellAmountWei, decimals } = sellAmountResult;
|
|
722
|
+
const priorityFee = gasPrice / 10n === 0n ? 1n : gasPrice / 10n;
|
|
723
|
+
// ✅ 获取卖出报价
|
|
724
|
+
const quote = await quoteSellOutput({
|
|
725
|
+
portalAddress: chainContext.portalAddress,
|
|
726
|
+
tokenAddress,
|
|
727
|
+
sellAmountWei,
|
|
728
|
+
provider: chainContext.provider,
|
|
729
|
+
skipQuoteOnError: config.skipQuoteOnError,
|
|
730
|
+
outputToken
|
|
731
|
+
});
|
|
732
|
+
const estimatedOutput = quote.quotedNative;
|
|
733
|
+
const outputFormatted = useNativeToken
|
|
734
|
+
? ethers.formatEther(estimatedOutput)
|
|
735
|
+
: ethers.formatUnits(estimatedOutput, quoteTokenDecimals);
|
|
736
|
+
console.log(`[flapQuickBatchSwapMerkle] 模式: ${useNativeToken ? '原生代币' : 'ERC20'}`);
|
|
737
|
+
console.log(`[flapQuickBatchSwapMerkle] 卖出数量: ${ethers.formatUnits(sellAmountWei, decimals)}`);
|
|
738
|
+
console.log(`[flapQuickBatchSwapMerkle] 预估卖出所得: ${outputFormatted}`);
|
|
739
|
+
// ✅ 计算利润(万分之三)
|
|
740
|
+
let tokenProfitAmount = calculateProfitAmount(estimatedOutput);
|
|
741
|
+
let nativeProfitAmount = tokenProfitAmount;
|
|
742
|
+
if (!useNativeToken && tokenProfitAmount > 0n) {
|
|
743
|
+
// ERC20 模式:将代币利润转换为等值原生代币
|
|
744
|
+
nativeProfitAmount = await getTokenToNativeQuote(chainContext.provider, quoteToken, tokenProfitAmount, chainContext.chainId);
|
|
745
|
+
console.log(`[flapQuickBatchSwapMerkle] ERC20→原生代币 报价: ${ethers.formatUnits(tokenProfitAmount, quoteTokenDecimals)} → ${ethers.formatEther(nativeProfitAmount)}`);
|
|
746
|
+
}
|
|
747
|
+
const distributableAmount = useNativeToken
|
|
748
|
+
? estimatedOutput - tokenProfitAmount
|
|
749
|
+
: estimatedOutput; // ERC20 模式利润从原生代币扣
|
|
750
|
+
// ✅ 计算每个买方分到的金额
|
|
751
|
+
let transferAmountsWei;
|
|
752
|
+
if (buyerAmounts && buyerAmounts.length === buyers.length) {
|
|
753
|
+
// 数量模式
|
|
754
|
+
transferAmountsWei = buyerAmounts.map(amt => useNativeToken
|
|
755
|
+
? ethers.parseEther(amt)
|
|
756
|
+
: ethers.parseUnits(amt, quoteTokenDecimals));
|
|
757
|
+
const totalTransfer = transferAmountsWei.reduce((a, b) => a + b, 0n);
|
|
758
|
+
if (totalTransfer > distributableAmount) {
|
|
759
|
+
const formatted = useNativeToken
|
|
760
|
+
? ethers.formatEther(distributableAmount)
|
|
761
|
+
: ethers.formatUnits(distributableAmount, quoteTokenDecimals);
|
|
762
|
+
throw new Error(`指定的买入总金额超过可分配金额 (${formatted})`);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
else if (buyerRatios && buyerRatios.length === buyers.length) {
|
|
766
|
+
// 比例模式
|
|
767
|
+
transferAmountsWei = buyerRatios.map(ratio => {
|
|
768
|
+
return (distributableAmount * BigInt(Math.round(ratio * 10000))) / 10000n;
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
else {
|
|
772
|
+
throw new Error('必须提供 buyerRatios 或 buyerAmounts');
|
|
773
|
+
}
|
|
774
|
+
// ✅ 获取贿赂金额
|
|
775
|
+
const bribeAmount = getBribeAmount(config);
|
|
776
|
+
// ✅ 验证主钱包余额
|
|
777
|
+
const sellerBalance = await seller.provider.getBalance(seller.address);
|
|
778
|
+
let sellerGasCost;
|
|
779
|
+
let sellerRequired;
|
|
780
|
+
if (useNativeToken) {
|
|
781
|
+
// 原生代币模式:贿赂 + Gas(卖出 + 转账 + 利润)
|
|
782
|
+
sellerGasCost = gasPrice * (21000n + finalGasLimit + 21000n * BigInt(buyers.length) + 21000n);
|
|
783
|
+
sellerRequired = bribeAmount + sellerGasCost;
|
|
784
|
+
}
|
|
785
|
+
else {
|
|
786
|
+
// ERC20 模式:贿赂 + Gas(卖出 + ERC20转账 + 利润)
|
|
787
|
+
sellerGasCost = gasPrice * (21000n + finalGasLimit + ERC20_TRANSFER_GAS * BigInt(buyers.length) + 21000n);
|
|
788
|
+
sellerRequired = bribeAmount + sellerGasCost;
|
|
789
|
+
}
|
|
790
|
+
if (sellerBalance < sellerRequired) {
|
|
791
|
+
throw new Error(`主钱包 ${chainContext.nativeToken} 余额不足: 需要约 ${ethers.formatEther(sellerRequired)} (贿赂: ${ethers.formatEther(bribeAmount)}, Gas: ${ethers.formatEther(sellerGasCost)}), 实际 ${ethers.formatEther(sellerBalance)}`);
|
|
792
|
+
}
|
|
793
|
+
// ==================== 规划 Nonce ====================
|
|
794
|
+
let sellerNonce = await nonceManager.getNextNonce(seller);
|
|
795
|
+
// ==================== 1. 贿赂交易 ====================
|
|
796
|
+
let bribeTx = null;
|
|
797
|
+
if (bribeAmount > 0n) {
|
|
798
|
+
bribeTx = await seller.signTransaction({
|
|
799
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
800
|
+
value: bribeAmount,
|
|
801
|
+
nonce: sellerNonce++,
|
|
802
|
+
gasPrice,
|
|
803
|
+
gasLimit: 21000n,
|
|
804
|
+
chainId: chainContext.chainId,
|
|
805
|
+
type: txType
|
|
806
|
+
});
|
|
807
|
+
console.log(`[flapQuickBatchSwapMerkle] 贿赂交易已签名`);
|
|
808
|
+
}
|
|
809
|
+
// ==================== 2. 卖出交易 ====================
|
|
810
|
+
const portalSeller = new Contract(chainContext.portalAddress, PORTAL_ABI, seller);
|
|
811
|
+
const sellUnsigned = await portalSeller.swapExactInput.populateTransaction({
|
|
812
|
+
inputToken: tokenAddress,
|
|
813
|
+
outputToken,
|
|
814
|
+
inputAmount: sellAmountWei,
|
|
815
|
+
minOutputAmount: 0,
|
|
816
|
+
permitData: '0x'
|
|
817
|
+
});
|
|
818
|
+
const sellTx = buildTransactionRequest(sellUnsigned, {
|
|
819
|
+
from: seller.address,
|
|
820
|
+
nonce: sellerNonce++,
|
|
821
|
+
gasLimit: finalGasLimit,
|
|
822
|
+
gasPrice,
|
|
823
|
+
priorityFee,
|
|
824
|
+
chainId: chainContext.chainId,
|
|
825
|
+
txType,
|
|
826
|
+
value: 0n
|
|
827
|
+
});
|
|
828
|
+
const signedSell = await seller.signTransaction(sellTx);
|
|
829
|
+
console.log(`[flapQuickBatchSwapMerkle] 卖出交易已签名`);
|
|
830
|
+
// ==================== 3. 转账交易(并行签名)====================
|
|
831
|
+
const reserveGas = ethers.parseEther((config.reserveGasETH || 0.0005).toString());
|
|
832
|
+
const buyerGasCost = gasPrice * finalGasLimit;
|
|
833
|
+
// ✅ 预分配 nonce,然后并行签名
|
|
834
|
+
const transferNonces = buyers.map((_, i) => sellerNonce + i);
|
|
835
|
+
sellerNonce += buyers.length; // 更新 sellerNonce
|
|
836
|
+
let transferTxs;
|
|
837
|
+
if (useNativeToken) {
|
|
838
|
+
// 原生代币模式:直接转账(并行签名)
|
|
839
|
+
transferTxs = await Promise.all(buyers.map((buyer, i) => {
|
|
840
|
+
const transferValue = transferAmountsWei[i] + reserveGas + buyerGasCost;
|
|
841
|
+
return seller.signTransaction({
|
|
842
|
+
to: buyer.address,
|
|
843
|
+
value: transferValue,
|
|
844
|
+
nonce: transferNonces[i],
|
|
845
|
+
gasPrice,
|
|
846
|
+
gasLimit: 21000n,
|
|
847
|
+
chainId: chainContext.chainId,
|
|
848
|
+
type: txType
|
|
849
|
+
});
|
|
850
|
+
}));
|
|
851
|
+
}
|
|
852
|
+
else {
|
|
853
|
+
// ERC20 模式:ERC20 transfer(并行签名)
|
|
854
|
+
const erc20Interface = new ethers.Interface([
|
|
855
|
+
'function transfer(address to, uint256 amount) returns (bool)'
|
|
856
|
+
]);
|
|
857
|
+
transferTxs = await Promise.all(buyers.map((buyer, i) => {
|
|
858
|
+
const transferData = erc20Interface.encodeFunctionData('transfer', [
|
|
859
|
+
buyer.address,
|
|
860
|
+
transferAmountsWei[i]
|
|
861
|
+
]);
|
|
862
|
+
return seller.signTransaction({
|
|
863
|
+
to: quoteToken,
|
|
864
|
+
data: transferData,
|
|
865
|
+
value: 0n,
|
|
866
|
+
nonce: transferNonces[i],
|
|
867
|
+
gasPrice,
|
|
868
|
+
gasLimit: ERC20_TRANSFER_GAS,
|
|
869
|
+
chainId: chainContext.chainId,
|
|
870
|
+
type: txType
|
|
871
|
+
});
|
|
872
|
+
}));
|
|
873
|
+
}
|
|
874
|
+
console.log(`[flapQuickBatchSwapMerkle] ${transferTxs.length} 笔转账交易已签名`);
|
|
875
|
+
// ==================== 4. 买入交易 ====================
|
|
876
|
+
const buyerNonces = await Promise.all(buyers.map(buyer => nonceManager.getNextNonce(buyer)));
|
|
877
|
+
const signedBuys = await Promise.all(buyers.map(async (buyer, i) => {
|
|
878
|
+
const buyAmount = transferAmountsWei[i];
|
|
879
|
+
const portalBuyer = new Contract(chainContext.portalAddress, PORTAL_ABI, buyer);
|
|
880
|
+
const buyUnsigned = await portalBuyer.swapExactInput.populateTransaction({
|
|
881
|
+
inputToken: outputToken,
|
|
882
|
+
outputToken: tokenAddress,
|
|
883
|
+
inputAmount: buyAmount,
|
|
884
|
+
minOutputAmount: 0,
|
|
885
|
+
permitData: '0x'
|
|
886
|
+
}, useNativeToken ? { value: buyAmount } : {});
|
|
887
|
+
const buyTx = buildTransactionRequest(buyUnsigned, {
|
|
888
|
+
from: buyer.address,
|
|
889
|
+
nonce: buyerNonces[i],
|
|
890
|
+
gasLimit: finalGasLimit,
|
|
891
|
+
gasPrice,
|
|
892
|
+
priorityFee,
|
|
893
|
+
chainId: chainContext.chainId,
|
|
894
|
+
txType,
|
|
895
|
+
value: useNativeToken ? buyAmount : 0n
|
|
896
|
+
});
|
|
897
|
+
return buyer.signTransaction(buyTx);
|
|
898
|
+
}));
|
|
899
|
+
console.log(`[flapQuickBatchSwapMerkle] ${signedBuys.length} 笔买入交易已签名`);
|
|
900
|
+
// ==================== 5. 利润交易 ====================
|
|
901
|
+
let profitTx = null;
|
|
902
|
+
if (nativeProfitAmount > 0n) {
|
|
903
|
+
profitTx = await seller.signTransaction({
|
|
904
|
+
to: getProfitRecipient(),
|
|
905
|
+
value: nativeProfitAmount,
|
|
906
|
+
nonce: sellerNonce++,
|
|
907
|
+
gasPrice,
|
|
908
|
+
gasLimit: 21000n,
|
|
909
|
+
chainId: chainContext.chainId,
|
|
910
|
+
type: txType
|
|
911
|
+
});
|
|
912
|
+
console.log(`[flapQuickBatchSwapMerkle] 利润交易已签名`);
|
|
913
|
+
}
|
|
914
|
+
nonceManager.clearTemp();
|
|
915
|
+
// ==================== 组装交易数组 ====================
|
|
916
|
+
const signedTransactions = [];
|
|
917
|
+
if (bribeTx)
|
|
918
|
+
signedTransactions.push(bribeTx);
|
|
919
|
+
signedTransactions.push(signedSell);
|
|
920
|
+
signedTransactions.push(...transferTxs);
|
|
921
|
+
signedTransactions.push(...signedBuys);
|
|
922
|
+
if (profitTx)
|
|
923
|
+
signedTransactions.push(profitTx);
|
|
924
|
+
console.log(`[flapQuickBatchSwapMerkle] 交易组装完成: ${signedTransactions.length} 笔`);
|
|
925
|
+
console.log(` - 贿赂: ${bribeTx ? 1 : 0}`);
|
|
926
|
+
console.log(` - 卖出: 1`);
|
|
927
|
+
console.log(` - 转账: ${transferTxs.length}`);
|
|
928
|
+
console.log(` - 买入: ${signedBuys.length}`);
|
|
929
|
+
console.log(` - 利润: ${profitTx ? 1 : 0}`);
|
|
930
|
+
return {
|
|
931
|
+
signedTransactions,
|
|
932
|
+
metadata: {
|
|
933
|
+
sellerAddress: seller.address,
|
|
934
|
+
buyerAddresses: buyers.map(b => b.address),
|
|
935
|
+
sellAmount: ethers.formatUnits(sellAmountWei, decimals),
|
|
936
|
+
estimatedOutput: useNativeToken
|
|
937
|
+
? ethers.formatEther(estimatedOutput)
|
|
938
|
+
: ethers.formatUnits(estimatedOutput, quoteTokenDecimals),
|
|
939
|
+
transferAmounts: transferAmountsWei.map(amt => useNativeToken
|
|
940
|
+
? ethers.formatEther(amt)
|
|
941
|
+
: ethers.formatUnits(amt, quoteTokenDecimals)),
|
|
942
|
+
profitAmount: nativeProfitAmount > 0n ? ethers.formatEther(nativeProfitAmount) : undefined,
|
|
943
|
+
useNativeToken
|
|
944
|
+
}
|
|
945
|
+
};
|
|
946
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -36,8 +36,8 @@ export { validatePrivateKeys, type PrivateKeyValidation } from './utils/wallet.j
|
|
|
36
36
|
export { stealthTransfer, type StealthTransferResult, type StealthTransferSimpleParams } from './utils/stealth-transfer.js';
|
|
37
37
|
export { inspectTokenLP, getFactoryFromRouter, registerDYORSwap, registerDex, getChainConfig, type LPInfo, type LPPlatform, type InspectOptions, type DexConfig, type ChainDexConfig, } from './utils/lp-inspect.js';
|
|
38
38
|
export { disperseWithBundle, sweepWithBundle, type DisperseSignParams, type SweepSignParams, type SignedTransactionsResult, type DisperseParams, type SweepParams, type BundleSubmitResult } from './utils/airdrop-sweep.js';
|
|
39
|
-
export { fourBundleSwapMerkle, fourBatchSwapMerkle, type FourSwapSignConfig, type FourSwapConfig, type FourBundleSwapSignParams, type FourBundleSwapParams, type FourSwapResult, type FourBatchSwapSignParams, type FourBatchSwapResult } from './contracts/tm-bundle-merkle/swap.js';
|
|
40
|
-
export { flapBundleSwapMerkle, flapBatchSwapMerkle, type FlapSwapSignConfig, type FlapSwapConfig, type FlapBundleSwapSignParams, type FlapBundleSwapParams, type FlapSwapResult, type FlapBatchSwapSignParams, type FlapBatchSwapResult } from './flap/portal-bundle-merkle/swap.js';
|
|
39
|
+
export { fourBundleSwapMerkle, fourBatchSwapMerkle, fourQuickBatchSwapMerkle, type FourSwapSignConfig, type FourSwapConfig, type FourBundleSwapSignParams, type FourBundleSwapParams, type FourSwapResult, type FourBatchSwapSignParams, type FourBatchSwapResult, type FourQuickBatchSwapSignParams, type FourQuickBatchSwapResult } from './contracts/tm-bundle-merkle/swap.js';
|
|
40
|
+
export { flapBundleSwapMerkle, flapBatchSwapMerkle, flapQuickBatchSwapMerkle, type FlapSwapSignConfig, type FlapSwapConfig, type FlapBundleSwapSignParams, type FlapBundleSwapParams, type FlapSwapResult, type FlapBatchSwapSignParams, type FlapBatchSwapResult, type FlapQuickBatchSwapSignParams, type FlapQuickBatchSwapResult } from './flap/portal-bundle-merkle/swap.js';
|
|
41
41
|
export { pancakeBundleSwapMerkle, pancakeBatchSwapMerkle, pancakeQuickBatchSwapMerkle, type PancakeSwapSignConfig, type PancakeBundleSwapSignParams, type PancakeSwapConfig, type PancakeBundleSwapParams, type PancakeSwapResult, type PancakeBatchSwapSignParams, type PancakeBatchSwapResult, type PancakeQuickBatchSwapParams, type PancakeQuickBatchSwapResult, type SwapRouteType, type V2RouteParams, type V3SingleRouteParams, type V3MultiRouteParams, type RouteParams } from './pancake/bundle-swap.js';
|
|
42
42
|
export { fourBundleBuyFirstMerkle, type FourBuyFirstConfig, type FourBundleBuyFirstParams, type FourBuyFirstSignConfig, type FourBundleBuyFirstSignParams, type FourBuyFirstResult } from './contracts/tm-bundle-merkle/swap-buy-first.js';
|
|
43
43
|
export { flapBundleBuyFirstMerkle, type FlapBuyFirstSignConfig, type FlapBuyFirstConfig, type FlapBundleBuyFirstSignParams, type FlapBundleBuyFirstParams, type FlapBuyFirstResult } from './flap/portal-bundle-merkle/swap-buy-first.js';
|
package/dist/index.js
CHANGED
|
@@ -52,9 +52,9 @@ export { disperseWithBundle, sweepWithBundle } from './utils/airdrop-sweep.js';
|
|
|
52
52
|
// 捆绑换手功能(Bundle Swap)
|
|
53
53
|
// ============================================================
|
|
54
54
|
// Four内盘换手
|
|
55
|
-
export { fourBundleSwapMerkle, fourBatchSwapMerkle } from './contracts/tm-bundle-merkle/swap.js';
|
|
55
|
+
export { fourBundleSwapMerkle, fourBatchSwapMerkle, fourQuickBatchSwapMerkle } from './contracts/tm-bundle-merkle/swap.js';
|
|
56
56
|
// Flap内盘换手
|
|
57
|
-
export { flapBundleSwapMerkle, flapBatchSwapMerkle } from './flap/portal-bundle-merkle/swap.js';
|
|
57
|
+
export { flapBundleSwapMerkle, flapBatchSwapMerkle, flapQuickBatchSwapMerkle } from './flap/portal-bundle-merkle/swap.js';
|
|
58
58
|
// PancakeSwap V2/V3通用换手
|
|
59
59
|
export { pancakeBundleSwapMerkle, pancakeBatchSwapMerkle, pancakeQuickBatchSwapMerkle } from './pancake/bundle-swap.js';
|
|
60
60
|
// 先买后卖(Buy-First)入口
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { ethers, Contract, Wallet } from 'ethers';
|
|
7
7
|
import { NonceManager } from '../utils/bundle-helpers.js';
|
|
8
8
|
import { ADDRESSES, PROFIT_CONFIG } from '../utils/constants.js';
|
|
9
|
+
import { quoteV2, quoteV3, getTokenToNativeQuote, getWrappedNativeAddress } from '../utils/quote-helpers.js';
|
|
9
10
|
// ✅ BlockRazor Builder EOA 地址(用于贿赂)
|
|
10
11
|
// 参考文档: https://blockrazor.gitbook.io/blockrazor/bsc/block-builder/send-bundle
|
|
11
12
|
const BLOCKRAZOR_BUILDER_EOA = '0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20';
|
|
@@ -38,18 +39,7 @@ const PANCAKE_PROXY_ABI = [
|
|
|
38
39
|
'function swapV3Single(address tokenIn, address tokenOut, uint24 fee, uint256 amountIn, uint256 amountOutMin, address to) payable returns (uint256)',
|
|
39
40
|
'function swapV3MultiHop(address[] calldata lpAddresses, address exactTokenIn, uint256 amountIn, uint256 amountOutMin, address to) payable returns (uint256)'
|
|
40
41
|
];
|
|
41
|
-
// Pancake V2 Router ABI(用于报价)
|
|
42
|
-
const PANCAKE_V2_ROUTER_ABI = [
|
|
43
|
-
'function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)'
|
|
44
|
-
];
|
|
45
|
-
// Pancake V3 QuoterV2 ABI(用于报价)
|
|
46
|
-
const PANCAKE_V3_QUOTER_ABI = [
|
|
47
|
-
'function quoteExactInputSingle((address tokenIn, address tokenOut, uint256 amountIn, uint24 fee, uint160 sqrtPriceLimitX96)) external returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate)',
|
|
48
|
-
'function quoteExactInput(bytes memory path, uint256 amountIn) external returns (uint256 amountOut, uint160[] memory sqrtPriceX96AfterList, uint32[] memory initializedTicksCrossedList, uint256 gasEstimate)'
|
|
49
|
-
];
|
|
50
42
|
const PANCAKE_PROXY_ADDRESS = ADDRESSES.BSC.PancakeProxy;
|
|
51
|
-
const PANCAKE_V2_ROUTER_ADDRESS = '0x10ED43C718714eb63d5aA57B78B54704E256024E';
|
|
52
|
-
const PANCAKE_V3_QUOTER_ADDRESS = '0xB048Bbc1Ee6b733FFfCFb9e9CeF7375518e25997';
|
|
53
43
|
const FLAT_FEE = 0n; // ✅ 已移除合约固定手续费
|
|
54
44
|
const WBNB_ADDRESS = '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
|
|
55
45
|
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
@@ -96,7 +86,8 @@ export async function pancakeBundleBuyFirstMerkle(params) {
|
|
|
96
86
|
const quotedNative = await quoteSellerNative({
|
|
97
87
|
provider: context.provider,
|
|
98
88
|
tokenAddress,
|
|
99
|
-
sellAmountToken: quoteResult.quotedTokenOut
|
|
89
|
+
sellAmountToken: quoteResult.quotedTokenOut,
|
|
90
|
+
routeParams // ✅ 传递路由参数
|
|
100
91
|
});
|
|
101
92
|
const buyerNeed = calculateBuyerNeed({
|
|
102
93
|
quotedNative,
|
|
@@ -123,9 +114,11 @@ export async function pancakeBundleBuyFirstMerkle(params) {
|
|
|
123
114
|
const estimatedProfitFromSell = await estimateProfitAmount({
|
|
124
115
|
provider: context.provider,
|
|
125
116
|
tokenAddress,
|
|
126
|
-
sellAmountToken: quoteResult.quotedTokenOut
|
|
117
|
+
sellAmountToken: quoteResult.quotedTokenOut,
|
|
118
|
+
routeParams // ✅ 传递路由参数
|
|
127
119
|
});
|
|
128
|
-
|
|
120
|
+
// 万分之三
|
|
121
|
+
const profitBase = estimatedProfitFromSell > 0n ? estimatedProfitFromSell : (buyerFundsInfo.buyerFundsWei * BigInt(PROFIT_CONFIG.RATE_BPS_CAPITAL)) / 10000n;
|
|
129
122
|
const profitAmount = profitBase;
|
|
130
123
|
// ✅ 获取贿赂金额
|
|
131
124
|
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
@@ -277,56 +270,55 @@ async function calculateBuyerFunds({ buyer, buyerFunds, buyerFundsPercentage, re
|
|
|
277
270
|
}
|
|
278
271
|
return { buyerFundsWei, buyerBalance, reserveGas: reserveGasWei };
|
|
279
272
|
}
|
|
273
|
+
/**
|
|
274
|
+
* ✅ 使用 quote-helpers 统一报价
|
|
275
|
+
*/
|
|
280
276
|
async function quoteTokenOutput({ routeParams, buyerFundsWei, provider }) {
|
|
277
|
+
// V2 路由
|
|
281
278
|
if (routeParams.routeType === 'v2') {
|
|
282
279
|
const { v2Path } = routeParams;
|
|
283
|
-
const
|
|
284
|
-
const
|
|
285
|
-
|
|
280
|
+
const tokenIn = v2Path[0];
|
|
281
|
+
const tokenOut = v2Path[v2Path.length - 1];
|
|
282
|
+
const result = await quoteV2(provider, tokenIn, tokenOut, buyerFundsWei, 'BSC');
|
|
283
|
+
if (result.amountOut <= 0n) {
|
|
284
|
+
throw new Error('V2 报价失败');
|
|
285
|
+
}
|
|
286
|
+
return { quotedTokenOut: result.amountOut };
|
|
286
287
|
}
|
|
288
|
+
// V3 Single 路由
|
|
287
289
|
if (routeParams.routeType === 'v3-single') {
|
|
288
290
|
const paramsV3 = routeParams;
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
tokenIn: paramsV3.v3TokenIn,
|
|
293
|
-
tokenOut: paramsV3.v3TokenOut,
|
|
294
|
-
amountIn: buyerFundsWei,
|
|
295
|
-
fee: paramsV3.v3Fee,
|
|
296
|
-
sqrtPriceLimitX96: 0
|
|
297
|
-
});
|
|
298
|
-
return { quotedTokenOut: result[0] };
|
|
299
|
-
}
|
|
300
|
-
catch {
|
|
301
|
-
if (paramsV3.v2Path && paramsV3.v2Path.length >= 2) {
|
|
302
|
-
const v2Router = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, provider);
|
|
303
|
-
const amounts = await v2Router.getAmountsOut(buyerFundsWei, paramsV3.v2Path);
|
|
304
|
-
return { quotedTokenOut: amounts[amounts.length - 1] };
|
|
305
|
-
}
|
|
306
|
-
throw new Error('V3 报价失败且未提供 v2Path 备用');
|
|
291
|
+
const result = await quoteV3(provider, paramsV3.v3TokenIn, paramsV3.v3TokenOut, buyerFundsWei, 'BSC', paramsV3.v3Fee);
|
|
292
|
+
if (result.amountOut <= 0n) {
|
|
293
|
+
throw new Error('V3 报价失败');
|
|
307
294
|
}
|
|
295
|
+
return { quotedTokenOut: result.amountOut };
|
|
308
296
|
}
|
|
297
|
+
// V3 Multi 路由
|
|
309
298
|
const paramsV3m = routeParams;
|
|
310
|
-
if (paramsV3m.v2Path
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
299
|
+
if (!paramsV3m.v2Path || paramsV3m.v2Path.length < 2) {
|
|
300
|
+
throw new Error('V3 多跳需要提供 v2Path 用于路径推断');
|
|
301
|
+
}
|
|
302
|
+
const tokenIn = paramsV3m.v2Path[0];
|
|
303
|
+
const tokenOut = paramsV3m.v2Path[paramsV3m.v2Path.length - 1];
|
|
304
|
+
const result = await quoteV3(provider, tokenIn, tokenOut, buyerFundsWei, 'BSC');
|
|
305
|
+
if (result.amountOut <= 0n) {
|
|
306
|
+
throw new Error('V3 多跳报价失败');
|
|
314
307
|
}
|
|
315
|
-
|
|
308
|
+
return { quotedTokenOut: result.amountOut };
|
|
316
309
|
}
|
|
317
310
|
async function getTokenDecimals(provider, tokenAddress) {
|
|
318
311
|
const erc20 = new Contract(tokenAddress, ['function decimals() view returns (uint8)'], provider);
|
|
319
312
|
return await erc20.decimals();
|
|
320
313
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
}
|
|
314
|
+
/**
|
|
315
|
+
* ✅ 使用 quote-helpers 统一报价(卖出 → 原生代币)
|
|
316
|
+
*/
|
|
317
|
+
async function quoteSellerNative({ provider, tokenAddress, sellAmountToken, routeParams }) {
|
|
318
|
+
const wbnb = getWrappedNativeAddress('BSC');
|
|
319
|
+
const version = routeParams.routeType === 'v2' ? 'v2' : 'v3';
|
|
320
|
+
const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
|
|
321
|
+
return await getTokenToNativeQuote(provider, tokenAddress, sellAmountToken, 'BSC', version, fee);
|
|
330
322
|
}
|
|
331
323
|
function calculateBuyerNeed({ quotedNative, buyerBalance, reserveGas }) {
|
|
332
324
|
// ✅ 已移除滑点保护:直接使用报价金额
|
|
@@ -391,12 +383,19 @@ async function buildRouteTransactions({ routeParams, buyerFundsWei, sellAmountTo
|
|
|
391
383
|
const sellUnsigned = await proxySeller.swapV3MultiHop.populateTransaction(v3LpAddresses, exactTokenOut, sellAmountToken, 0n, seller.address, { value: FLAT_FEE });
|
|
392
384
|
return { buyUnsigned, sellUnsigned };
|
|
393
385
|
}
|
|
394
|
-
|
|
386
|
+
/**
|
|
387
|
+
* ✅ 使用 quote-helpers 统一报价(估算利润)
|
|
388
|
+
*/
|
|
389
|
+
async function estimateProfitAmount({ provider, tokenAddress, sellAmountToken, routeParams }) {
|
|
395
390
|
try {
|
|
396
|
-
const
|
|
397
|
-
const
|
|
398
|
-
const estimatedSellFunds =
|
|
399
|
-
|
|
391
|
+
const version = routeParams.routeType === 'v2' ? 'v2' : 'v3';
|
|
392
|
+
const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
|
|
393
|
+
const estimatedSellFunds = await getTokenToNativeQuote(provider, tokenAddress, sellAmountToken, 'BSC', version, fee);
|
|
394
|
+
if (estimatedSellFunds <= 0n) {
|
|
395
|
+
return 0n;
|
|
396
|
+
}
|
|
397
|
+
// 万分之三
|
|
398
|
+
return (estimatedSellFunds * BigInt(PROFIT_CONFIG.RATE_BPS_CAPITAL)) / 10000n;
|
|
400
399
|
}
|
|
401
400
|
catch {
|
|
402
401
|
return 0n;
|
|
@@ -161,7 +161,7 @@ function calculateProfitAmount(estimatedBNBOut) {
|
|
|
161
161
|
if (estimatedBNBOut <= 0n) {
|
|
162
162
|
return 0n;
|
|
163
163
|
}
|
|
164
|
-
return (estimatedBNBOut * BigInt(PROFIT_CONFIG.
|
|
164
|
+
return (estimatedBNBOut * BigInt(PROFIT_CONFIG.RATE_BPS_CAPITAL)) / 10000n;
|
|
165
165
|
}
|
|
166
166
|
/**
|
|
167
167
|
* ✅ 获取 ERC20 代币 → 原生代币(BNB)的报价
|
|
@@ -779,7 +779,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
779
779
|
? ethers.formatEther(estimatedOutput)
|
|
780
780
|
: ethers.formatUnits(estimatedOutput, quoteTokenDecimals);
|
|
781
781
|
console.log(`[pancakeQuickBatchSwapMerkle] 预估卖出所得: ${outputFormatted} ${useNativeToken ? 'BNB' : 'ERC20'}`);
|
|
782
|
-
// ✅
|
|
782
|
+
// ✅ 计算利润(万分之三)
|
|
783
783
|
let profitAmount;
|
|
784
784
|
if (useNativeToken) {
|
|
785
785
|
profitAmount = calculateProfitAmount(estimatedOutput);
|
|
@@ -881,47 +881,48 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
881
881
|
type: txType
|
|
882
882
|
});
|
|
883
883
|
console.log(`[pancakeQuickBatchSwapMerkle] 卖出交易已签名`);
|
|
884
|
-
// ==================== 3.
|
|
885
|
-
const
|
|
884
|
+
// ==================== 3. 转账交易(并行签名)====================
|
|
885
|
+
const buyerGasCost = gasPrice * finalGasLimit;
|
|
886
|
+
// ✅ 预分配 nonce,然后并行签名
|
|
887
|
+
const transferNonces = buyers.map((_, i) => sellerNonce + i);
|
|
888
|
+
sellerNonce += buyers.length; // 更新 sellerNonce
|
|
889
|
+
let transferTxs;
|
|
886
890
|
if (useNativeToken) {
|
|
887
|
-
// ✅ 原生代币模式:直接 BNB
|
|
888
|
-
|
|
889
|
-
const buyerGasCost = gasPrice * finalGasLimit;
|
|
891
|
+
// ✅ 原生代币模式:直接 BNB 转账(并行签名)
|
|
892
|
+
transferTxs = await Promise.all(buyers.map((buyer, i) => {
|
|
890
893
|
const transferValue = transferAmountsWei[i] + FLAT_FEE + buyerGasCost;
|
|
891
|
-
|
|
892
|
-
to:
|
|
894
|
+
return seller.signTransaction({
|
|
895
|
+
to: buyer.address,
|
|
893
896
|
value: transferValue,
|
|
894
|
-
nonce:
|
|
897
|
+
nonce: transferNonces[i],
|
|
895
898
|
gasPrice,
|
|
896
899
|
gasLimit: 21000n,
|
|
897
900
|
chainId: context.chainId,
|
|
898
901
|
type: txType
|
|
899
902
|
});
|
|
900
|
-
|
|
901
|
-
}
|
|
903
|
+
}));
|
|
902
904
|
}
|
|
903
905
|
else {
|
|
904
|
-
// ✅ ERC20 模式:ERC20 transfer
|
|
906
|
+
// ✅ ERC20 模式:ERC20 transfer(并行签名)
|
|
905
907
|
const erc20Interface = new ethers.Interface([
|
|
906
908
|
'function transfer(address to, uint256 amount) returns (bool)'
|
|
907
909
|
]);
|
|
908
|
-
|
|
910
|
+
transferTxs = await Promise.all(buyers.map((buyer, i) => {
|
|
909
911
|
const transferData = erc20Interface.encodeFunctionData('transfer', [
|
|
910
|
-
|
|
912
|
+
buyer.address,
|
|
911
913
|
transferAmountsWei[i]
|
|
912
914
|
]);
|
|
913
|
-
|
|
915
|
+
return seller.signTransaction({
|
|
914
916
|
to: quoteToken,
|
|
915
917
|
data: transferData,
|
|
916
918
|
value: 0n,
|
|
917
|
-
nonce:
|
|
919
|
+
nonce: transferNonces[i],
|
|
918
920
|
gasPrice,
|
|
919
921
|
gasLimit: ERC20_TRANSFER_GAS,
|
|
920
922
|
chainId: context.chainId,
|
|
921
923
|
type: txType
|
|
922
924
|
});
|
|
923
|
-
|
|
924
|
-
}
|
|
925
|
+
}));
|
|
925
926
|
}
|
|
926
927
|
console.log(`[pancakeQuickBatchSwapMerkle] ${transferTxs.length} 笔转账交易已签名`);
|
|
927
928
|
// ==================== 4. 买入交易 ====================
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export declare const PROFIT_CONFIG: {
|
|
2
2
|
/** 利润接收地址 */
|
|
3
3
|
readonly RECIPIENT: "0xe8D0334fAf713884133640CAEe4ECdd2106AF103";
|
|
4
|
-
/** 利润比例(基点):30 bps = 0.3% =
|
|
4
|
+
/** 利润比例(基点):30 bps = 0.3% = 千分之三(普通模式) */
|
|
5
5
|
readonly RATE_BPS: 30;
|
|
6
|
+
/** 利润比例(基点):3 bps = 0.03% = 万分之三(资金利用率模式) */
|
|
7
|
+
readonly RATE_BPS_CAPITAL: 3;
|
|
6
8
|
};
|
|
7
9
|
export declare const CHAIN: {
|
|
8
10
|
BSC: {
|
package/dist/utils/constants.js
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
export const PROFIT_CONFIG = {
|
|
3
3
|
/** 利润接收地址 */
|
|
4
4
|
RECIPIENT: '0xe8D0334fAf713884133640CAEe4ECdd2106AF103',
|
|
5
|
-
/** 利润比例(基点):30 bps = 0.3% =
|
|
5
|
+
/** 利润比例(基点):30 bps = 0.3% = 千分之三(普通模式) */
|
|
6
6
|
RATE_BPS: 30,
|
|
7
|
+
/** 利润比例(基点):3 bps = 0.03% = 万分之三(资金利用率模式) */
|
|
8
|
+
RATE_BPS_CAPITAL: 3,
|
|
7
9
|
};
|
|
8
10
|
export const CHAIN = {
|
|
9
11
|
BSC: {
|