four-flap-meme-sdk 1.4.24 → 1.4.26

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.
Files changed (46) hide show
  1. package/dist/abis/common.d.ts +85 -0
  2. package/dist/abis/common.js +242 -0
  3. package/dist/abis/index.d.ts +1 -0
  4. package/dist/abis/index.js +2 -0
  5. package/dist/contracts/tm-bundle-merkle/approve-tokenmanager.js +1 -6
  6. package/dist/contracts/tm-bundle-merkle/core.js +9 -16
  7. package/dist/contracts/tm-bundle-merkle/internal.js +6 -8
  8. package/dist/contracts/tm-bundle-merkle/pancake-proxy.d.ts +12 -6
  9. package/dist/contracts/tm-bundle-merkle/pancake-proxy.js +224 -166
  10. package/dist/contracts/tm-bundle-merkle/private.js +6 -19
  11. package/dist/contracts/tm-bundle-merkle/swap-buy-first.js +2 -7
  12. package/dist/contracts/tm-bundle-merkle/swap-internal.d.ts +1 -1
  13. package/dist/contracts/tm-bundle-merkle/swap-internal.js +9 -2
  14. package/dist/contracts/tm-bundle-merkle/swap.js +1 -3
  15. package/dist/contracts/tm-bundle-merkle/types.d.ts +20 -0
  16. package/dist/contracts/tm-bundle-merkle/utils.js +164 -175
  17. package/dist/dex/direct-router.d.ts +2 -1
  18. package/dist/dex/direct-router.js +25 -140
  19. package/dist/flap/constants.d.ts +2 -1
  20. package/dist/flap/constants.js +2 -1
  21. package/dist/flap/meta.js +6 -4
  22. package/dist/flap/portal-bundle-merkle/config.js +6 -12
  23. package/dist/flap/portal-bundle-merkle/core.js +8 -11
  24. package/dist/flap/portal-bundle-merkle/pancake-proxy.d.ts +12 -10
  25. package/dist/flap/portal-bundle-merkle/pancake-proxy.js +307 -370
  26. package/dist/flap/portal-bundle-merkle/private.js +1 -1
  27. package/dist/flap/portal-bundle-merkle/swap-buy-first.js +12 -30
  28. package/dist/flap/portal-bundle-merkle/swap.js +13 -26
  29. package/dist/flap/portal-bundle-merkle/types.d.ts +22 -2
  30. package/dist/flap/portal-bundle-merkle/utils.js +11 -16
  31. package/dist/index.d.ts +3 -2
  32. package/dist/index.js +9 -2
  33. package/dist/pancake/bundle-buy-first.js +56 -38
  34. package/dist/pancake/bundle-swap.js +114 -61
  35. package/dist/utils/bundle-helpers.d.ts +28 -0
  36. package/dist/utils/bundle-helpers.js +64 -0
  37. package/dist/utils/constants.d.ts +23 -1
  38. package/dist/utils/constants.js +37 -7
  39. package/dist/utils/erc20.js +17 -25
  40. package/dist/utils/lp-inspect.js +9 -20
  41. package/dist/utils/private-sale.js +1 -2
  42. package/dist/utils/quote-helpers.js +3 -29
  43. package/dist/utils/swap-helpers.js +1 -6
  44. package/dist/utils/wallet.d.ts +8 -13
  45. package/dist/utils/wallet.js +154 -342
  46. package/package.json +1 -1
@@ -56,34 +56,53 @@ async function quoteSellOutput({ routeParams, sellAmountWei, provider }) {
56
56
  throw new Error(`不支持的路由类型: ${routeParams.routeType}`);
57
57
  }
58
58
  async function buildSwapTransactions({ routeParams, sellAmountWei, buyAmountBNB, buyer, seller, tokenAddress, useNativeToken = true }) {
59
- const proxySeller = new Contract(PANCAKE_PROXY_ADDRESS, PANCAKE_PROXY_ABI, seller);
60
- const proxyBuyer = new Contract(PANCAKE_PROXY_ADDRESS, PANCAKE_PROXY_ABI, buyer);
61
- const deadline = Math.floor(Date.now() / 1000) + 600;
59
+ const deadline = getDeadline();
62
60
  // ✅ ERC20 购买时,value 只需要 FLAT_FEE
63
61
  const buyValue = useNativeToken ? buyAmountBNB + FLAT_FEE : FLAT_FEE;
64
62
  if (routeParams.routeType === 'v2') {
65
63
  const { v2Path } = routeParams;
66
64
  const reversePath = [...v2Path].reverse();
67
- const sellUnsigned = await proxySeller.swapV2.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline, { value: FLAT_FEE });
68
- const buyUnsigned = await proxyBuyer.swapV2.populateTransaction(buyAmountBNB, 0n, reversePath, buyer.address, deadline, { value: buyValue } // ✅ 使用动态 value
69
- );
65
+ // 使用官方 V2 Router
66
+ const v2RouterSeller = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, seller);
67
+ const v2RouterBuyer = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, buyer);
68
+ // 卖出:Token → BNB
69
+ const sellUnsigned = await v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline);
70
+ // 买入:BNB → Token
71
+ const buyUnsigned = await v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, reversePath, buyer.address, deadline, { value: buyValue });
70
72
  return { sellUnsigned, buyUnsigned };
71
73
  }
72
74
  if (routeParams.routeType === 'v3-single') {
73
75
  const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
74
- const sellUnsigned = await proxySeller.swapV3Single.populateTransaction(v3TokenIn, v3TokenOut, v3Fee, sellAmountWei, 0n, seller.address, { value: FLAT_FEE });
75
- const buyUnsigned = await proxyBuyer.swapV3Single.populateTransaction(v3TokenOut, v3TokenIn, v3Fee, buyAmountBNB, 0n, buyer.address, { value: buyValue } // ✅ 使用动态 value
76
- );
76
+ const v3RouterIface = new ethers.Interface(PANCAKE_V3_ROUTER_ABI);
77
+ const v3RouterSeller = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, seller);
78
+ const v3RouterBuyer = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, buyer);
79
+ // 卖出:Token → WBNB,需要 unwrapWETH9
80
+ const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
81
+ tokenIn: v3TokenIn,
82
+ tokenOut: v3TokenOut,
83
+ fee: v3Fee,
84
+ recipient: PANCAKE_V3_ROUTER_ADDRESS, // 先发给 Router
85
+ amountIn: sellAmountWei,
86
+ amountOutMinimum: 0n,
87
+ sqrtPriceLimitX96: 0n
88
+ }]);
89
+ const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
90
+ const sellUnsigned = await v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData, sellUnwrapData]);
91
+ // 买入:WBNB → Token
92
+ const buySwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
93
+ tokenIn: v3TokenOut,
94
+ tokenOut: v3TokenIn,
95
+ fee: v3Fee,
96
+ recipient: buyer.address,
97
+ amountIn: buyAmountBNB,
98
+ amountOutMinimum: 0n,
99
+ sqrtPriceLimitX96: 0n
100
+ }]);
101
+ const buyUnsigned = await v3RouterBuyer.multicall.populateTransaction(deadline, [buySwapData], { value: buyValue });
77
102
  return { sellUnsigned, buyUnsigned };
78
103
  }
79
- const { v3LpAddresses, v3ExactTokenIn } = routeParams;
80
- const exactTokenOut = v3ExactTokenIn.toLowerCase() === WBNB_ADDRESS.toLowerCase()
81
- ? tokenAddress
82
- : WBNB_ADDRESS;
83
- const sellUnsigned = await proxySeller.swapV3MultiHop.populateTransaction(v3LpAddresses, v3ExactTokenIn, sellAmountWei, 0n, seller.address, { value: FLAT_FEE });
84
- const buyUnsigned = await proxyBuyer.swapV3MultiHop.populateTransaction(v3LpAddresses, exactTokenOut, buyAmountBNB, 0n, buyer.address, { value: buyValue } // ✅ 使用动态 value
85
- );
86
- return { sellUnsigned, buyUnsigned };
104
+ // V3 多跳暂不支持官方合约
105
+ throw new Error('V3 多跳路由暂不支持官方合约,请使用 V2 路由或 V3 单跳');
87
106
  }
88
107
  async function calculateBuyerBudget({ buyer, quotedBNBOut, reserveGasBNB, useNativeToken = true, quoteToken, quoteTokenDecimals = 18, provider }) {
89
108
  const reserveGas = ethers.parseEther((reserveGasBNB ?? 0.0005).toString());
@@ -267,12 +286,10 @@ function countTruthy(values) {
267
286
  */
268
287
  import { ethers, Contract, Wallet } from 'ethers';
269
288
  import { calculateSellAmount } from '../utils/swap-helpers.js';
270
- import { NonceManager } from '../utils/bundle-helpers.js';
271
- import { ADDRESSES, PROFIT_CONFIG } from '../utils/constants.js';
289
+ import { NonceManager, getDeadline } from '../utils/bundle-helpers.js';
290
+ import { ADDRESSES, PROFIT_CONFIG, BLOCKRAZOR_BUILDER_EOA } from '../utils/constants.js';
272
291
  import { quoteV2, quoteV3 } from '../utils/quote-helpers.js';
273
- // BlockRazor Builder EOA 地址(用于贿赂)
274
- // 参考文档: https://blockrazor.gitbook.io/blockrazor/bsc/block-builder/send-bundle
275
- const BLOCKRAZOR_BUILDER_EOA = '0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20';
292
+ import { V2_ROUTER_ABI, V3_ROUTER02_ABI, ERC20_BALANCE_ABI } from '../abis/common.js';
276
293
  /**
277
294
  * 获取 Gas Limit
278
295
  */
@@ -299,20 +316,18 @@ async function getGasPrice(provider, config) {
299
316
  }
300
317
  return gasPrice;
301
318
  }
302
- // PancakeSwapProxy ABI(只需要 ExactInput 方法)
303
- const PANCAKE_PROXY_ABI = [
304
- 'function swapV2(uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline) payable returns (uint256)',
305
- 'function swapV3Single(address tokenIn, address tokenOut, uint24 fee, uint256 amountIn, uint256 amountOutMin, address to) payable returns (uint256)',
306
- 'function swapV3MultiHop(address[] calldata lpAddresses, address exactTokenIn, uint256 amountIn, uint256 amountOutMin, address to) payable returns (uint256)'
307
- ];
308
- // 兼容旧代码:默认使用 BSC 地址
309
- const PANCAKE_PROXY_ADDRESS = ADDRESSES.BSC.PancakeProxy;
310
- // V3 Quoter ABI 和地址已移至 ../utils/quote-helpers.ts
311
- // 代理合约手续费
312
- const FLAT_FEE = 0n; // ✅ 已移除合约固定手续费
313
- const WBNB_ADDRESS = '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
319
+ // ABI 别名(从公共模块导入)
320
+ const PANCAKE_V2_ROUTER_ABI = V2_ROUTER_ABI;
321
+ const PANCAKE_V3_ROUTER_ABI = V3_ROUTER02_ABI;
322
+ const ERC20_BALANCE_OF_ABI = ERC20_BALANCE_ABI;
323
+ // 使用官方 PancakeSwap Router 地址
324
+ const PANCAKE_V2_ROUTER_ADDRESS = ADDRESSES.BSC.PancakeV2Router;
325
+ const PANCAKE_V3_ROUTER_ADDRESS = ADDRESSES.BSC.PancakeV3Router;
326
+ // 常量
327
+ const FLAT_FEE = 0n;
328
+ const WBNB_ADDRESS = ADDRESSES.BSC.WBNB;
314
329
  const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
315
- const ERC20_BALANCE_OF_ABI = ['function balanceOf(address) view returns (uint256)'];
330
+ // ERC20_BALANCE_OF_ABI 已在文件开头定义
316
331
  /**
317
332
  * PancakeSwap捆绑换手(V2/V3通用)
318
333
  * ✅ 支持 quoteToken:传入 USDT 等地址时,卖出得到该代币,买入使用该代币
@@ -564,41 +579,59 @@ export async function pancakeBatchSwapMerkle(params) {
564
579
  }
565
580
  }
566
581
  }));
567
- // ✅ 构建卖出交易
568
- const proxySeller = new Contract(PANCAKE_PROXY_ADDRESS, PANCAKE_PROXY_ABI, seller);
569
- const deadline = Math.floor(Date.now() / 1000) + 600;
582
+ // ✅ 构建卖出交易(使用官方 Router)
583
+ const deadline = getDeadline();
584
+ const v3RouterIface = new ethers.Interface(PANCAKE_V3_ROUTER_ABI);
570
585
  let sellUnsigned;
571
586
  if (routeParams.routeType === 'v2') {
572
587
  const { v2Path } = routeParams;
573
- sellUnsigned = await proxySeller.swapV2.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline, { value: FLAT_FEE });
588
+ const v2RouterSeller = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, seller);
589
+ sellUnsigned = await v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline);
574
590
  }
575
591
  else if (routeParams.routeType === 'v3-single') {
576
592
  const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
577
- sellUnsigned = await proxySeller.swapV3Single.populateTransaction(v3TokenIn, v3TokenOut, v3Fee, sellAmountWei, 0n, seller.address, { value: FLAT_FEE });
593
+ const v3RouterSeller = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, seller);
594
+ const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
595
+ tokenIn: v3TokenIn,
596
+ tokenOut: v3TokenOut,
597
+ fee: v3Fee,
598
+ recipient: PANCAKE_V3_ROUTER_ADDRESS,
599
+ amountIn: sellAmountWei,
600
+ amountOutMinimum: 0n,
601
+ sqrtPriceLimitX96: 0n
602
+ }]);
603
+ const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
604
+ sellUnsigned = await v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData, sellUnwrapData]);
578
605
  }
579
606
  else {
580
- const { v3LpAddresses, v3ExactTokenIn } = routeParams;
581
- sellUnsigned = await proxySeller.swapV3MultiHop.populateTransaction(v3LpAddresses, v3ExactTokenIn, sellAmountWei, 0n, seller.address, { value: FLAT_FEE });
607
+ throw new Error('V3 多跳路由暂不支持官方合约,请使用 V2 路由或 V3 单跳');
582
608
  }
583
- // ✅ 并行构建多个买入交易
609
+ // ✅ 并行构建多个买入交易(使用官方 Router)
584
610
  const buyUnsignedList = await Promise.all(buyers.map(async (buyer, i) => {
585
611
  const buyAmount = buyAmountsWei[i];
586
- const proxyBuyer = new Contract(PANCAKE_PROXY_ADDRESS, PANCAKE_PROXY_ABI, buyer);
587
612
  const buyValue = useNativeToken ? buyAmount + FLAT_FEE : FLAT_FEE;
588
613
  if (routeParams.routeType === 'v2') {
589
614
  const { v2Path } = routeParams;
590
615
  const reversePath = [...v2Path].reverse();
591
- return await proxyBuyer.swapV2.populateTransaction(buyAmount, 0n, reversePath, buyer.address, deadline, { value: buyValue });
616
+ const v2RouterBuyer = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, buyer);
617
+ return await v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, reversePath, buyer.address, deadline, { value: buyValue });
592
618
  }
593
619
  else if (routeParams.routeType === 'v3-single') {
594
620
  const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
595
- return await proxyBuyer.swapV3Single.populateTransaction(v3TokenOut, // 买入时反向
596
- v3TokenIn, v3Fee, buyAmount, 0n, buyer.address, { value: buyValue });
621
+ const v3RouterBuyer = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, buyer);
622
+ const buySwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
623
+ tokenIn: v3TokenOut,
624
+ tokenOut: v3TokenIn,
625
+ fee: v3Fee,
626
+ recipient: buyer.address,
627
+ amountIn: buyAmount,
628
+ amountOutMinimum: 0n,
629
+ sqrtPriceLimitX96: 0n
630
+ }]);
631
+ return await v3RouterBuyer.multicall.populateTransaction(deadline, [buySwapData], { value: buyValue });
597
632
  }
598
633
  else {
599
- const { v3LpAddresses } = routeParams;
600
- const exactTokenOut = tokenAddress;
601
- return await proxyBuyer.swapV3MultiHop.populateTransaction(v3LpAddresses, exactTokenOut, buyAmount, 0n, buyer.address, { value: buyValue });
634
+ throw new Error('V3 多跳路由暂不支持官方合约,请使用 V2 路由或 V3 单跳');
602
635
  }
603
636
  }));
604
637
  // ✅ 获取贿赂金额
@@ -902,20 +935,31 @@ export async function pancakeQuickBatchSwapMerkle(params) {
902
935
  });
903
936
  console.log(`[pancakeQuickBatchSwapMerkle] 贿赂交易已签名`);
904
937
  }
905
- // ==================== 2. 卖出交易 ====================
906
- const proxySeller = new Contract(PANCAKE_PROXY_ADDRESS, PANCAKE_PROXY_ABI, seller);
938
+ // ==================== 2. 卖出交易(使用官方 Router)====================
939
+ const v3RouterIface2 = new ethers.Interface(PANCAKE_V3_ROUTER_ABI);
907
940
  let sellUnsigned;
908
941
  if (routeParams.routeType === 'v2') {
909
942
  const { v2Path } = routeParams;
910
- sellUnsigned = await proxySeller.swapV2.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline, { value: FLAT_FEE });
943
+ const v2RouterSeller = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, seller);
944
+ sellUnsigned = await v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline);
911
945
  }
912
946
  else if (routeParams.routeType === 'v3-single') {
913
947
  const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
914
- sellUnsigned = await proxySeller.swapV3Single.populateTransaction(v3TokenIn, v3TokenOut, v3Fee, sellAmountWei, 0n, seller.address, { value: FLAT_FEE });
948
+ const v3RouterSeller = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, seller);
949
+ const sellSwapData = v3RouterIface2.encodeFunctionData('exactInputSingle', [{
950
+ tokenIn: v3TokenIn,
951
+ tokenOut: v3TokenOut,
952
+ fee: v3Fee,
953
+ recipient: PANCAKE_V3_ROUTER_ADDRESS,
954
+ amountIn: sellAmountWei,
955
+ amountOutMinimum: 0n,
956
+ sqrtPriceLimitX96: 0n
957
+ }]);
958
+ const sellUnwrapData = v3RouterIface2.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
959
+ sellUnsigned = await v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData, sellUnwrapData]);
915
960
  }
916
961
  else {
917
- const { v3LpAddresses, v3ExactTokenIn } = routeParams;
918
- sellUnsigned = await proxySeller.swapV3MultiHop.populateTransaction(v3LpAddresses, v3ExactTokenIn, sellAmountWei, 0n, seller.address, { value: FLAT_FEE });
962
+ throw new Error('V3 多跳路由暂不支持官方合约,请使用 V2 路由或 V3 单跳');
919
963
  }
920
964
  const signedSell = await seller.signTransaction({
921
965
  ...sellUnsigned,
@@ -978,7 +1022,6 @@ export async function pancakeQuickBatchSwapMerkle(params) {
978
1022
  : await Promise.all(buyers.map(buyer => nonceManager.getNextNonce(buyer)));
979
1023
  const signedBuys = await Promise.all(buyers.map(async (buyer, i) => {
980
1024
  const buyAmount = transferAmountsWei[i];
981
- const proxyBuyer = new Contract(PANCAKE_PROXY_ADDRESS, PANCAKE_PROXY_ABI, buyer);
982
1025
  // BNB 模式:value = buyAmount + FLAT_FEE
983
1026
  // ERC20 模式:value = FLAT_FEE(买入金额通过授权支付)
984
1027
  const buyValue = useNativeToken ? buyAmount + FLAT_FEE : FLAT_FEE;
@@ -986,15 +1029,25 @@ export async function pancakeQuickBatchSwapMerkle(params) {
986
1029
  if (routeParams.routeType === 'v2') {
987
1030
  const { v2Path } = routeParams;
988
1031
  const reversePath = [...v2Path].reverse();
989
- buyUnsigned = await proxyBuyer.swapV2.populateTransaction(buyAmount, 0n, reversePath, buyer.address, deadline, { value: buyValue });
1032
+ const v2RouterBuyer = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, buyer);
1033
+ buyUnsigned = await v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, reversePath, buyer.address, deadline, { value: buyValue });
990
1034
  }
991
1035
  else if (routeParams.routeType === 'v3-single') {
992
1036
  const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
993
- buyUnsigned = await proxyBuyer.swapV3Single.populateTransaction(v3TokenOut, v3TokenIn, v3Fee, buyAmount, 0n, buyer.address, { value: buyValue });
1037
+ const v3RouterBuyer = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, buyer);
1038
+ const buySwapData = v3RouterIface2.encodeFunctionData('exactInputSingle', [{
1039
+ tokenIn: v3TokenOut,
1040
+ tokenOut: v3TokenIn,
1041
+ fee: v3Fee,
1042
+ recipient: buyer.address,
1043
+ amountIn: buyAmount,
1044
+ amountOutMinimum: 0n,
1045
+ sqrtPriceLimitX96: 0n
1046
+ }]);
1047
+ buyUnsigned = await v3RouterBuyer.multicall.populateTransaction(deadline, [buySwapData], { value: buyValue });
994
1048
  }
995
1049
  else {
996
- const { v3LpAddresses } = routeParams;
997
- buyUnsigned = await proxyBuyer.swapV3MultiHop.populateTransaction(v3LpAddresses, tokenAddress, buyAmount, 0n, buyer.address, { value: buyValue });
1050
+ throw new Error('V3 多跳路由暂不支持官方合约,请使用 V2 路由或 V3 单跳');
998
1051
  }
999
1052
  return buyer.signTransaction({
1000
1053
  ...buyUnsigned,
@@ -160,3 +160,31 @@ export declare function signTransactionsBatchMultiWallet(wallets: Wallet[], tran
160
160
  * @returns 是否全部有效
161
161
  */
162
162
  export declare function validateSignedTransactions(signedTxs: string[]): boolean;
163
+ /**
164
+ * 获取交易 deadline(Unix 时间戳秒)
165
+ * @param minutes 有效分钟数,默认 20
166
+ * @returns Unix 时间戳(秒)
167
+ */
168
+ export declare function getDeadline(minutes?: number): number;
169
+ /**
170
+ * 编码 V3 多跳 path
171
+ * 格式:token0 (20 bytes) + fee (3 bytes) + token1 (20 bytes) + fee (3 bytes) + token2 (20 bytes) ...
172
+ *
173
+ * @param tokens 代币地址数组 [tokenIn, tokenMid1, tokenMid2, ..., tokenOut]
174
+ * @param fees 费率数组 [fee0, fee1, ...] - 长度应该是 tokens.length - 1
175
+ * @returns 编码后的 path(hex string)
176
+ *
177
+ * @example
178
+ * // WBNB → USDT(500) → TOKEN(2500)
179
+ * encodeV3Path(['0xWBNB...', '0xUSDT...', '0xTOKEN...'], [500, 2500])
180
+ */
181
+ export declare function encodeV3Path(tokens: string[], fees: number[]): string;
182
+ /**
183
+ * 解码 V3 path 为代币和费率数组
184
+ * @param path 编码后的 path
185
+ * @returns { tokens, fees }
186
+ */
187
+ export declare function decodeV3Path(path: string): {
188
+ tokens: string[];
189
+ fees: number[];
190
+ };
@@ -301,3 +301,67 @@ export function validateSignedTransactions(signedTxs) {
301
301
  }
302
302
  return true;
303
303
  }
304
+ // ============================================================================
305
+ // 交易时间和路径工具
306
+ // ============================================================================
307
+ import { DEFAULT_DEADLINE_MINUTES } from './constants.js';
308
+ /**
309
+ * 获取交易 deadline(Unix 时间戳秒)
310
+ * @param minutes 有效分钟数,默认 20
311
+ * @returns Unix 时间戳(秒)
312
+ */
313
+ export function getDeadline(minutes = DEFAULT_DEADLINE_MINUTES) {
314
+ return Math.floor(Date.now() / 1000) + 60 * minutes;
315
+ }
316
+ /**
317
+ * 编码 V3 多跳 path
318
+ * 格式:token0 (20 bytes) + fee (3 bytes) + token1 (20 bytes) + fee (3 bytes) + token2 (20 bytes) ...
319
+ *
320
+ * @param tokens 代币地址数组 [tokenIn, tokenMid1, tokenMid2, ..., tokenOut]
321
+ * @param fees 费率数组 [fee0, fee1, ...] - 长度应该是 tokens.length - 1
322
+ * @returns 编码后的 path(hex string)
323
+ *
324
+ * @example
325
+ * // WBNB → USDT(500) → TOKEN(2500)
326
+ * encodeV3Path(['0xWBNB...', '0xUSDT...', '0xTOKEN...'], [500, 2500])
327
+ */
328
+ export function encodeV3Path(tokens, fees) {
329
+ if (tokens.length < 2) {
330
+ throw new Error('V3 path 需要至少 2 个代币');
331
+ }
332
+ if (fees.length !== tokens.length - 1) {
333
+ throw new Error(`费率数量 (${fees.length}) 必须等于代币数量 - 1 (${tokens.length - 1})`);
334
+ }
335
+ let path = '0x';
336
+ for (let i = 0; i < tokens.length; i++) {
337
+ // 添加代币地址 (20 bytes)
338
+ path += tokens[i].slice(2).toLowerCase().padStart(40, '0');
339
+ // 添加费率 (3 bytes) - 除了最后一个代币
340
+ if (i < fees.length) {
341
+ path += fees[i].toString(16).padStart(6, '0');
342
+ }
343
+ }
344
+ return path;
345
+ }
346
+ /**
347
+ * 解码 V3 path 为代币和费率数组
348
+ * @param path 编码后的 path
349
+ * @returns { tokens, fees }
350
+ */
351
+ export function decodeV3Path(path) {
352
+ const cleanPath = path.startsWith('0x') ? path.slice(2) : path;
353
+ const tokens = [];
354
+ const fees = [];
355
+ let offset = 0;
356
+ while (offset < cleanPath.length) {
357
+ // 读取代币地址 (40 hex chars = 20 bytes)
358
+ tokens.push('0x' + cleanPath.slice(offset, offset + 40));
359
+ offset += 40;
360
+ // 读取费率 (6 hex chars = 3 bytes)
361
+ if (offset < cleanPath.length) {
362
+ fees.push(parseInt(cleanPath.slice(offset, offset + 6), 16));
363
+ offset += 6;
364
+ }
365
+ }
366
+ return { tokens, fees };
367
+ }
@@ -1,3 +1,17 @@
1
+ /** BlockRazor Builder EOA 地址(用于 BSC 链贿赂) */
2
+ export declare const BLOCKRAZOR_BUILDER_EOA = "0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20";
3
+ /** 零地址 */
4
+ export declare const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
5
+ /** 交易 deadline 默认分钟数 */
6
+ export declare const DEFAULT_DEADLINE_MINUTES = 20;
7
+ /** V3 常用费率档位 */
8
+ export declare const V3_FEE_TIERS: {
9
+ readonly LOWEST: 100;
10
+ readonly LOW: 500;
11
+ readonly MEDIUM: 2500;
12
+ readonly HIGH: 10000;
13
+ };
14
+ /** ✅ 硬编码:利润提取配置(统一管理,所有方法强制使用) */
1
15
  export declare const PROFIT_CONFIG: {
2
16
  /** 利润接收地址 */
3
17
  readonly RECIPIENT: "0xe8D0334fAf713884133640CAEe4ECdd2106AF103";
@@ -34,6 +48,10 @@ export declare const CHAIN: {
34
48
  };
35
49
  export declare const ADDRESSES: {
36
50
  readonly BSC: {
51
+ readonly WBNB: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c";
52
+ readonly USDT: "0x55d398326f99059fF775485246999027B3197955";
53
+ readonly USDC: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d";
54
+ readonly BUSD: "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56";
37
55
  readonly TokenManagerOriginal: "0x5c952063c7fc8610FFDB798152D69F0B9550762b";
38
56
  readonly TokenManagerV1Proxy: "0xf7F823d0E790219dBf727bDb971837574655fCB0";
39
57
  readonly TokenManagerV2Proxy: "0x342399a59943B5815849657Aa0e06D7058D9d5C6";
@@ -41,7 +59,11 @@ export declare const ADDRESSES: {
41
59
  readonly TokenManagerV2: "0x342399a59943B5815849657Aa0e06D7058D9d5C6";
42
60
  readonly TokenManagerHelper3: "0xF251F83e40a78868FcfA3FA4599Dad6494E46034";
43
61
  readonly FlapPortal: "0xe2cE6ab80874Fa9Fa2aAE65D277Dd6B8e65C9De0";
44
- readonly PancakeProxy: "0xaC4fA4E0b6f6a45B199cFf43A21a69a374857682";
62
+ readonly PancakeV2Router: "0x10ED43C718714eb63d5aA57B78B54704E256024E";
63
+ readonly PancakeV2Factory: "0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73";
64
+ readonly PancakeV3Router: "0x13f4EA83D0bd40E75C8222255bc855a974568Dd4";
65
+ readonly PancakeV3Quoter: "0xB048Bbc1Ee6b733FFfCFb9e9CeF7375518e25997";
66
+ readonly PancakeV3Factory: "0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865";
45
67
  readonly Multicall3: "0xcA11bde05977b3631167028862bE2a173976CA11";
46
68
  };
47
69
  readonly BASE: {
@@ -1,4 +1,23 @@
1
- // ✅ 硬编码:利润提取配置(统一管理,所有方法强制使用)
1
+ // ============================================================================
2
+ // 公共常量 - 各链通用
3
+ // ============================================================================
4
+ /** BlockRazor Builder EOA 地址(用于 BSC 链贿赂) */
5
+ export const BLOCKRAZOR_BUILDER_EOA = '0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20';
6
+ /** 零地址 */
7
+ export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
8
+ /** 交易 deadline 默认分钟数 */
9
+ export const DEFAULT_DEADLINE_MINUTES = 20;
10
+ /** V3 常用费率档位 */
11
+ export const V3_FEE_TIERS = {
12
+ LOWEST: 100, // 0.01%
13
+ LOW: 500, // 0.05%
14
+ MEDIUM: 2500, // 0.25%
15
+ HIGH: 10000, // 1%
16
+ };
17
+ // ============================================================================
18
+ // 利润配置
19
+ // ============================================================================
20
+ /** ✅ 硬编码:利润提取配置(统一管理,所有方法强制使用) */
2
21
  export const PROFIT_CONFIG = {
3
22
  /** 利润接收地址 */
4
23
  RECIPIENT: '0xe8D0334fAf713884133640CAEe4ECdd2106AF103',
@@ -21,9 +40,16 @@ export const CHAIN = {
21
40
  };
22
41
  export const ADDRESSES = {
23
42
  BSC: {
24
- // Four.meme 原始合约(TokenManager2,创建代币专用,不收费)
43
+ // ========== 原生代币 ==========
44
+ WBNB: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c',
45
+ // ========== 稳定币 ==========
46
+ USDT: '0x55d398326f99059fF775485246999027B3197955',
47
+ USDC: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d',
48
+ BUSD: '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56',
49
+ // ========== Four.meme 合约 ==========
50
+ // 原始合约(TokenManager2,创建代币专用,不收费)
25
51
  TokenManagerOriginal: '0x5c952063c7fc8610FFDB798152D69F0B9550762b',
26
- // Four.meme 代理合约(收费版,仅交易)
52
+ // 代理合约(收费版,仅交易)
27
53
  TokenManagerV1Proxy: '0xf7F823d0E790219dBf727bDb971837574655fCB0',
28
54
  TokenManagerV2Proxy: '0x342399a59943B5815849657Aa0e06D7058D9d5C6',
29
55
  // ✅ 向后兼容别名(保持旧代码可用)
@@ -31,11 +57,15 @@ export const ADDRESSES = {
31
57
  TokenManagerV2: '0x342399a59943B5815849657Aa0e06D7058D9d5C6',
32
58
  // 查询辅助合约(Helper3)
33
59
  TokenManagerHelper3: '0xF251F83e40a78868FcfA3FA4599Dad6494E46034',
34
- // Flap Portal 代理合约(收费版)
60
+ // ========== Flap Portal ==========
35
61
  FlapPortal: '0xe2cE6ab80874Fa9Fa2aAE65D277Dd6B8e65C9De0',
36
- // PancakeSwap 代理合约
37
- PancakeProxy: '0xaC4fA4E0b6f6a45B199cFf43A21a69a374857682',
38
- // Multicall3 合约
62
+ // ========== PancakeSwap 官方合约 ==========
63
+ PancakeV2Router: '0x10ED43C718714eb63d5aA57B78B54704E256024E',
64
+ PancakeV2Factory: '0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73',
65
+ PancakeV3Router: '0x13f4EA83D0bd40E75C8222255bc855a974568Dd4',
66
+ PancakeV3Quoter: '0xB048Bbc1Ee6b733FFfCFb9e9CeF7375518e25997',
67
+ PancakeV3Factory: '0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865',
68
+ // ========== 通用合约 ==========
39
69
  Multicall3: '0xcA11bde05977b3631167028862bE2a173976CA11',
40
70
  },
41
71
  BASE: {
@@ -1,12 +1,7 @@
1
1
  import { Contract, Wallet, JsonRpcProvider, Interface, parseUnits } from 'ethers';
2
- import { ADDRESSES } from './constants.js';
2
+ import { ADDRESSES, ZERO_ADDRESS } from './constants.js';
3
3
  import { NonceManager } from './bundle-helpers.js';
4
- const ERC20_ABI = [
5
- { "constant": false, "inputs": [{ "name": "spender", "type": "address" }, { "name": "value", "type": "uint256" }], "name": "approve", "outputs": [{ "name": "", "type": "bool" }], "type": "function" },
6
- { "constant": true, "inputs": [{ "name": "owner", "type": "address" }, { "name": "spender", "type": "address" }], "name": "allowance", "outputs": [{ "name": "", "type": "uint256" }], "type": "function" },
7
- { "constant": true, "inputs": [{ "name": "account", "type": "address" }], "name": "balanceOf", "outputs": [{ "name": "", "type": "uint256" }], "type": "function" },
8
- { "constant": true, "inputs": [], "name": "decimals", "outputs": [{ "name": "", "type": "uint8" }], "type": "function" }
9
- ];
4
+ import { ERC20_ABI, MULTICALL3_ABI } from '../abis/common.js';
10
5
  /**
11
6
  * 验证合约地址是否有效(是否部署了代码)
12
7
  */
@@ -265,12 +260,9 @@ export async function checkFlapSellApprovalBatch(chain, rpcUrl, token, owners, r
265
260
  * @returns 每个地址的授权额度数组
266
261
  */
267
262
  export async function batchCheckAllowances(provider, tokenAddress, owners, spender) {
268
- // ✅ 动态获取 Multicall3 地址(所有链使用相同地址)
269
- const multicall3Address = '0xca11bde05977b3631167028862be2a173976ca11';
270
- const multicall3ABI = [
271
- 'function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) view returns (tuple(bool success, bytes returnData)[] returnData)'
272
- ];
273
- const multicall3 = new Contract(multicall3Address, multicall3ABI, provider);
263
+ // ✅ 使用公共常量和 ABI
264
+ const multicall3Address = ADDRESSES.BSC.Multicall3;
265
+ const multicall3 = new Contract(multicall3Address, MULTICALL3_ABI, provider);
274
266
  // 编码 allowance(owner, spender) 调用数据
275
267
  const erc20Interface = new Contract(tokenAddress, ERC20_ABI, provider).interface;
276
268
  const calls = owners.map(owner => ({
@@ -310,29 +302,29 @@ function resolveSpenderAddress(chain, platform) {
310
302
  // Four.meme 使用 TokenManagerV2Proxy(默认)
311
303
  BSC: ADDRESSES.BSC.TokenManagerV2Proxy,
312
304
  BASE: ADDRESSES.BASE.TokenManagerHelper3,
313
- XLAYER: '0x0000000000000000000000000000000000000000', // XLAYER 暂不支持 Four.meme
314
- MORPH: '0x0000000000000000000000000000000000000000', // MORPH 暂不支持 Four.meme
315
- MONAD: '0x0000000000000000000000000000000000000000', // MONAD 不支持 Four.meme
305
+ XLAYER: ZERO_ADDRESS, // XLAYER 暂不支持 Four.meme
306
+ MORPH: ZERO_ADDRESS, // MORPH 暂不支持 Four.meme
307
+ MONAD: ZERO_ADDRESS, // MONAD 不支持 Four.meme
316
308
  },
317
309
  'pancake-v2': {
318
310
  // PancakeSwap V2 Router 地址
319
- BSC: '0x10ED43C718714eb63d5aA57B78B54704E256024E', // BSC PancakeSwap V2 Router
311
+ BSC: ADDRESSES.BSC.PancakeV2Router,
320
312
  BASE: '0x8cFe327CEc66d1C090Dd72bd0FF11d690C33a2Eb', // BASE PancakeSwap V2 Router
321
- XLAYER: '0x0000000000000000000000000000000000000000', // XLAYER 暂不支持
322
- MORPH: '0x0000000000000000000000000000000000000000', // MORPH 暂不支持
323
- MONAD: '0xb1bc24c34e88f7d43d5923034e3a14b24daacff9', // Monad PancakeSwap V2 Router
313
+ XLAYER: ZERO_ADDRESS, // XLAYER 暂不支持
314
+ MORPH: ZERO_ADDRESS, // MORPH 暂不支持
315
+ MONAD: '0xb1bc24c34e88f7d43d5923034e3a14b24daacff9', // Monad PancakeSwap V2 Router
324
316
  },
325
317
  'pancake-v3': {
326
318
  // PancakeSwap V3 SmartRouter 地址
327
- BSC: '0x13f4EA83D0bd40E75C8222255bc855a974568Dd4', // BSC PancakeSwap V3 SmartRouter
319
+ BSC: ADDRESSES.BSC.PancakeV3Router,
328
320
  BASE: '0x678Aa4bF4E210cf2166753e054d5b7c31cc7fa86', // BASE PancakeSwap V3 SmartRouter
329
- XLAYER: '0x0000000000000000000000000000000000000000', // XLAYER 暂不支持
330
- MORPH: '0x0000000000000000000000000000000000000000', // MORPH 暂不支持
331
- MONAD: '0x1b81d678ffb9c0263b24a97847620c99d213eb14', // Monad PancakeSwap V3 Router
321
+ XLAYER: ZERO_ADDRESS, // XLAYER 暂不支持
322
+ MORPH: ZERO_ADDRESS, // MORPH 暂不支持
323
+ MONAD: '0x1b81d678ffb9c0263b24a97847620c99d213eb14', // Monad PancakeSwap V3 Router
332
324
  },
333
325
  };
334
326
  const spender = spenderMap[platform]?.[chain];
335
- if (!spender || spender === '0x0000000000000000000000000000000000000000') {
327
+ if (!spender || spender === ZERO_ADDRESS) {
336
328
  throw new Error(`❌ 不支持的链或平台: ${chain} / ${platform}`);
337
329
  }
338
330
  return spender;
@@ -1,5 +1,6 @@
1
1
  import { Contract, JsonRpcProvider, Interface, formatUnits } from 'ethers';
2
- import { ADDRESSES } from './constants.js';
2
+ import { ADDRESSES, ZERO_ADDRESS } from './constants.js';
3
+ import { MULTICALL3_ABI, V2_FACTORY_ABI, V2_PAIR_ABI, V3_FACTORY_ABI, ERC20_ABI } from '../abis/common.js';
3
4
  import { Helper3 } from '../contracts/helper3.js';
4
5
  import { FlapPortal } from '../flap/portal.js';
5
6
  // ============================================================================
@@ -206,28 +207,16 @@ export function getQuoteTokens(chain) {
206
207
  ];
207
208
  }
208
209
  // ============================================================================
209
- // ABIs
210
+ // ABI 别名(从公共模块导入)
210
211
  // ============================================================================
211
- const I_UNIV2_FACTORY_ABI = ['function getPair(address,address) view returns (address)'];
212
- const I_UNIV2_PAIR_ABI = [
213
- 'function token0() view returns (address)',
214
- 'function token1() view returns (address)',
215
- 'function getReserves() view returns (uint112,uint112,uint32)'
216
- ];
217
- const I_ERC20_ABI = [
218
- 'function balanceOf(address) view returns (uint256)',
219
- 'function decimals() view returns (uint8)',
220
- 'function symbol() view returns (string)'
221
- ];
222
- const MULTICALL3_ABI = [
223
- 'function aggregate((address target, bytes callData)[]) public returns (uint256 blockNumber, bytes[] returnData)'
224
- ];
225
- const I_UNIV3_FACTORY_ABI = ['function getPool(address,address,uint24) view returns (address)'];
212
+ const I_UNIV2_FACTORY_ABI = V2_FACTORY_ABI;
213
+ const I_UNIV2_PAIR_ABI = V2_PAIR_ABI;
214
+ const I_ERC20_ABI = ERC20_ABI;
215
+ const I_UNIV3_FACTORY_ABI = V3_FACTORY_ABI;
226
216
  // ============================================================================
227
- // 工具函数
217
+ // 常量(从公共模块导入)
228
218
  // ============================================================================
229
- const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
230
- const MULTICALL3_ADDRESS = '0xca11bde05977b3631167028862be2a173976ca11';
219
+ const MULTICALL3_ADDRESS = ADDRESSES.BSC.Multicall3;
231
220
  /** Provider 缓存 */
232
221
  const providerCache = new Map();
233
222
  function getProvider(rpcUrl) {
@@ -6,8 +6,7 @@
6
6
  * - 利润提取:千分之三(30 bps)
7
7
  */
8
8
  import { ethers, Wallet, JsonRpcProvider } from 'ethers';
9
- import { PROFIT_CONFIG } from './constants.js';
10
- import { BLOCKRAZOR_BUILDER_EOA } from '../clients/blockrazor.js';
9
+ import { PROFIT_CONFIG, BLOCKRAZOR_BUILDER_EOA } from './constants.js';
11
10
  import { NonceManager } from './bundle-helpers.js';
12
11
  // ============================================================================
13
12
  // 核心功能