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.
@@ -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, getProfitRateBps, getBribeAmount } from './config.js';
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 = getProfitRateBps();
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 获取(JSON-RPC 批量请求)
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
- : (async () => {
111
- // 优化:一次性获取两个地址的初始 nonce(单次网络往返)
112
- const initialNonces = await nonceManager.getNextNoncesForWallets([buyer, seller]);
109
+ : nonceManager.getNextNoncesForWallets([buyer, seller])
110
+ .then(initialNonces => ({
113
111
  // buyer 只需要 1 个 nonce
114
- const buyerNonces = [initialNonces[0]];
112
+ buyerNonces: [initialNonces[0]],
115
113
  // seller 需要 sellerNonceCount 个连续 nonce,从初始 nonce 开始
116
- const sellerNonces = Array.from({ length: sellerNonceCount }, (_, i) => initialNonces[1] + i);
117
- // 更新 NonceManager 缓存
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.RATE_BPS;
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.RATE_BPS;
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 { getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, getProfitRecipient } from './config.js';
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, config);
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, config);
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, config);
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, config);
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], config).profit;
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, config);
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], config);
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
- return (expectedFunds * BigInt(PROFIT_CONFIG.RATE_BPS)) / 10000n;
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.RATE_BPS)) / 10000n;
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
- const profitBase = estimatedProfitFromSell > 0n ? estimatedProfitFromSell : (buyerFundsInfo.buyerFundsWei * BigInt(PROFIT_CONFIG.RATE_BPS)) / 10000n;
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 v2Router = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, provider);
284
- const amounts = await v2Router.getAmountsOut(buyerFundsWei, v2Path);
285
- return { quotedTokenOut: amounts[amounts.length - 1] };
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 quoter = new Contract(PANCAKE_V3_QUOTER_ADDRESS, PANCAKE_V3_QUOTER_ABI, provider);
290
- try {
291
- const result = await quoter.quoteExactInputSingle.staticCall({
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 && paramsV3m.v2Path.length >= 2) {
311
- const v2Router = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, provider);
312
- const amounts = await v2Router.getAmountsOut(buyerFundsWei, paramsV3m.v2Path);
313
- return { quotedTokenOut: amounts[amounts.length - 1] };
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
- throw new Error('V3 多跳需要提供 v2Path 用于价格预估');
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
- async function quoteSellerNative({ provider, tokenAddress, sellAmountToken }) {
322
- try {
323
- const v2Router = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, provider);
324
- const amounts = await v2Router.getAmountsOut(sellAmountToken, [tokenAddress, WBNB_ADDRESS]);
325
- return amounts[amounts.length - 1];
326
- }
327
- catch {
328
- return 0n;
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
- async function estimateProfitAmount({ provider, tokenAddress, sellAmountToken }) {
386
+ /**
387
+ * ✅ 使用 quote-helpers 统一报价(估算利润)
388
+ */
389
+ async function estimateProfitAmount({ provider, tokenAddress, sellAmountToken, routeParams }) {
395
390
  try {
396
- const v2Router = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, provider);
397
- const amounts = await v2Router.getAmountsOut(sellAmountToken, [tokenAddress, WBNB_ADDRESS]);
398
- const estimatedSellFunds = amounts[amounts.length - 1];
399
- return (estimatedSellFunds * BigInt(PROFIT_CONFIG.RATE_BPS)) / 10000n;
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.RATE_BPS)) / 10000n;
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
- // ✅ 计算利润(基于 BNB 价值)
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 transferTxs = [];
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
- for (let i = 0; i < buyers.length; i++) {
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
- const transferTx = await seller.signTransaction({
892
- to: buyers[i].address,
894
+ return seller.signTransaction({
895
+ to: buyer.address,
893
896
  value: transferValue,
894
- nonce: sellerNonce++,
897
+ nonce: transferNonces[i],
895
898
  gasPrice,
896
899
  gasLimit: 21000n,
897
900
  chainId: context.chainId,
898
901
  type: txType
899
902
  });
900
- transferTxs.push(transferTx);
901
- }
903
+ }));
902
904
  }
903
905
  else {
904
- // ✅ ERC20 模式:ERC20 transfer 调用(子钱包已预留 BNB,不需要转 Gas)
906
+ // ✅ ERC20 模式:ERC20 transfer(并行签名)
905
907
  const erc20Interface = new ethers.Interface([
906
908
  'function transfer(address to, uint256 amount) returns (bool)'
907
909
  ]);
908
- for (let i = 0; i < buyers.length; i++) {
910
+ transferTxs = await Promise.all(buyers.map((buyer, i) => {
909
911
  const transferData = erc20Interface.encodeFunctionData('transfer', [
910
- buyers[i].address,
912
+ buyer.address,
911
913
  transferAmountsWei[i]
912
914
  ]);
913
- const transferTx = await seller.signTransaction({
915
+ return seller.signTransaction({
914
916
  to: quoteToken,
915
917
  data: transferData,
916
918
  value: 0n,
917
- nonce: sellerNonce++,
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
- transferTxs.push(transferTx);
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: {
@@ -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: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.4.15",
3
+ "version": "1.4.17",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",