four-flap-meme-sdk 1.3.93 → 1.3.94

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 (127) hide show
  1. package/dist/clients/blockrazor.js +1 -0
  2. package/dist/contracts/tm-bundle-merkle/config.d.ts +0 -5
  3. package/dist/contracts/tm-bundle-merkle/config.js +0 -10
  4. package/dist/contracts/tm-bundle-merkle/core.js +24 -92
  5. package/dist/contracts/tm-bundle-merkle/pancake-proxy.js +54 -103
  6. package/dist/contracts/tm-bundle-merkle/swap-buy-first.d.ts +1 -0
  7. package/dist/contracts/tm-bundle-merkle/swap-buy-first.js +6 -36
  8. package/dist/contracts/tm-bundle-merkle/swap.d.ts +3 -0
  9. package/dist/contracts/tm-bundle-merkle/swap.js +6 -59
  10. package/dist/flap/portal-bundle-merkle/config.d.ts +0 -8
  11. package/dist/flap/portal-bundle-merkle/config.js +0 -17
  12. package/dist/flap/portal-bundle-merkle/core.js +68 -120
  13. package/dist/flap/portal-bundle-merkle/pancake-proxy.js +78 -136
  14. package/dist/flap/portal-bundle-merkle/swap-buy-first.d.ts +2 -0
  15. package/dist/flap/portal-bundle-merkle/swap-buy-first.js +30 -49
  16. package/dist/flap/portal-bundle-merkle/swap.d.ts +2 -0
  17. package/dist/flap/portal-bundle-merkle/swap.js +47 -75
  18. package/dist/flap/portal-bundle-merkle/types.d.ts +0 -1
  19. package/dist/index.d.ts +2 -1
  20. package/dist/index.js +1 -0
  21. package/dist/pancake/bundle-buy-first.d.ts +1 -1
  22. package/dist/pancake/bundle-buy-first.js +17 -49
  23. package/dist/pancake/bundle-swap.d.ts +4 -1
  24. package/dist/pancake/bundle-swap.js +33 -98
  25. package/dist/sol/constants.d.ts +126 -0
  26. package/dist/sol/constants.js +145 -0
  27. package/dist/sol/dex/index.d.ts +8 -0
  28. package/dist/sol/dex/index.js +12 -0
  29. package/dist/sol/dex/meteora/client.d.ts +76 -0
  30. package/dist/sol/dex/meteora/client.js +219 -0
  31. package/dist/sol/dex/meteora/damm-v1-bundle.d.ts +61 -0
  32. package/dist/sol/dex/meteora/damm-v1-bundle.js +112 -0
  33. package/dist/sol/dex/meteora/damm-v1.d.ts +118 -0
  34. package/dist/sol/dex/meteora/damm-v1.js +315 -0
  35. package/dist/sol/dex/meteora/damm-v2-bundle.d.ts +82 -0
  36. package/dist/sol/dex/meteora/damm-v2-bundle.js +242 -0
  37. package/dist/sol/dex/meteora/damm-v2.d.ts +172 -0
  38. package/dist/sol/dex/meteora/damm-v2.js +632 -0
  39. package/dist/sol/dex/meteora/dbc-bundle.d.ts +123 -0
  40. package/dist/sol/dex/meteora/dbc-bundle.js +304 -0
  41. package/dist/sol/dex/meteora/dbc.d.ts +192 -0
  42. package/dist/sol/dex/meteora/dbc.js +619 -0
  43. package/dist/sol/dex/meteora/dlmm-bundle.d.ts +39 -0
  44. package/dist/sol/dex/meteora/dlmm-bundle.js +189 -0
  45. package/dist/sol/dex/meteora/dlmm.d.ts +157 -0
  46. package/dist/sol/dex/meteora/dlmm.js +671 -0
  47. package/dist/sol/dex/meteora/index.d.ts +25 -0
  48. package/dist/sol/dex/meteora/index.js +65 -0
  49. package/dist/sol/dex/meteora/types.d.ts +787 -0
  50. package/dist/sol/dex/meteora/types.js +110 -0
  51. package/dist/sol/dex/orca/index.d.ts +10 -0
  52. package/dist/sol/dex/orca/index.js +16 -0
  53. package/dist/sol/dex/orca/orca-bundle.d.ts +41 -0
  54. package/dist/sol/dex/orca/orca-bundle.js +173 -0
  55. package/dist/sol/dex/orca/orca.d.ts +65 -0
  56. package/dist/sol/dex/orca/orca.js +474 -0
  57. package/dist/sol/dex/orca/types.d.ts +263 -0
  58. package/dist/sol/dex/orca/types.js +38 -0
  59. package/dist/sol/dex/orca/wavebreak-bundle.d.ts +34 -0
  60. package/dist/sol/dex/orca/wavebreak-bundle.js +198 -0
  61. package/dist/sol/dex/orca/wavebreak-types.d.ts +227 -0
  62. package/dist/sol/dex/orca/wavebreak-types.js +23 -0
  63. package/dist/sol/dex/orca/wavebreak.d.ts +78 -0
  64. package/dist/sol/dex/orca/wavebreak.js +497 -0
  65. package/dist/sol/dex/pump/index.d.ts +9 -0
  66. package/dist/sol/dex/pump/index.js +14 -0
  67. package/dist/sol/dex/pump/pump-bundle.d.ts +92 -0
  68. package/dist/sol/dex/pump/pump-bundle.js +383 -0
  69. package/dist/sol/dex/pump/pump-swap-bundle.d.ts +103 -0
  70. package/dist/sol/dex/pump/pump-swap-bundle.js +380 -0
  71. package/dist/sol/dex/pump/pump-swap.d.ts +46 -0
  72. package/dist/sol/dex/pump/pump-swap.js +199 -0
  73. package/dist/sol/dex/pump/pump.d.ts +35 -0
  74. package/dist/sol/dex/pump/pump.js +352 -0
  75. package/dist/sol/dex/pump/types.d.ts +215 -0
  76. package/dist/sol/dex/pump/types.js +5 -0
  77. package/dist/sol/dex/raydium/index.d.ts +8 -0
  78. package/dist/sol/dex/raydium/index.js +12 -0
  79. package/dist/sol/dex/raydium/launchlab.d.ts +68 -0
  80. package/dist/sol/dex/raydium/launchlab.js +210 -0
  81. package/dist/sol/dex/raydium/raydium-bundle.d.ts +64 -0
  82. package/dist/sol/dex/raydium/raydium-bundle.js +324 -0
  83. package/dist/sol/dex/raydium/raydium.d.ts +40 -0
  84. package/dist/sol/dex/raydium/raydium.js +366 -0
  85. package/dist/sol/dex/raydium/types.d.ts +240 -0
  86. package/dist/sol/dex/raydium/types.js +5 -0
  87. package/dist/sol/index.d.ts +10 -0
  88. package/dist/sol/index.js +16 -0
  89. package/dist/sol/jito/bundle.d.ts +90 -0
  90. package/dist/sol/jito/bundle.js +263 -0
  91. package/dist/sol/jito/index.d.ts +7 -0
  92. package/dist/sol/jito/index.js +7 -0
  93. package/dist/sol/jito/tip.d.ts +51 -0
  94. package/dist/sol/jito/tip.js +83 -0
  95. package/dist/sol/jito/types.d.ts +100 -0
  96. package/dist/sol/jito/types.js +5 -0
  97. package/dist/sol/token/create-complete.d.ts +115 -0
  98. package/dist/sol/token/create-complete.js +235 -0
  99. package/dist/sol/token/create-token.d.ts +57 -0
  100. package/dist/sol/token/create-token.js +230 -0
  101. package/dist/sol/token/index.d.ts +9 -0
  102. package/dist/sol/token/index.js +14 -0
  103. package/dist/sol/token/metadata-upload.d.ts +86 -0
  104. package/dist/sol/token/metadata-upload.js +173 -0
  105. package/dist/sol/token/metadata.d.ts +92 -0
  106. package/dist/sol/token/metadata.js +274 -0
  107. package/dist/sol/token/types.d.ts +153 -0
  108. package/dist/sol/token/types.js +5 -0
  109. package/dist/sol/types.d.ts +176 -0
  110. package/dist/sol/types.js +7 -0
  111. package/dist/sol/utils/balance.d.ts +160 -0
  112. package/dist/sol/utils/balance.js +638 -0
  113. package/dist/sol/utils/connection.d.ts +78 -0
  114. package/dist/sol/utils/connection.js +168 -0
  115. package/dist/sol/utils/index.d.ts +9 -0
  116. package/dist/sol/utils/index.js +9 -0
  117. package/dist/sol/utils/lp-inspect.d.ts +129 -0
  118. package/dist/sol/utils/lp-inspect.js +900 -0
  119. package/dist/sol/utils/transfer.d.ts +125 -0
  120. package/dist/sol/utils/transfer.js +220 -0
  121. package/dist/sol/utils/wallet.d.ts +107 -0
  122. package/dist/sol/utils/wallet.js +210 -0
  123. package/dist/utils/erc20.d.ts +2 -108
  124. package/dist/utils/erc20.js +17 -65
  125. package/package.json +39 -4
  126. package/dist/flap/portal-bundle-merkle/encryption.d.ts +0 -16
  127. package/dist/flap/portal-bundle-merkle/encryption.js +0 -146
@@ -1,7 +1,7 @@
1
1
  import { ethers, Wallet, JsonRpcProvider, Contract, Interface } from 'ethers';
2
2
  import { NonceManager, getOptimizedGasPrice } from '../../utils/bundle-helpers.js';
3
3
  import { ADDRESSES } from '../../utils/constants.js';
4
- import { CHAIN_ID_MAP, getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, calculateBatchProfit, getProfitRecipient, getBribeAmount, BLOCKRAZOR_BUILDER_EOA } from './config.js';
4
+ import { CHAIN_ID_MAP, getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, calculateBatchProfit, getProfitRecipient } from './config.js';
5
5
  import { batchCheckAllowances } from '../../utils/erc20.js';
6
6
  const MULTICALL3_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11';
7
7
  const MULTICALL3_ABI = [
@@ -56,27 +56,16 @@ function getGasLimit(config, defaultGas = DEFAULT_GAS_LIMIT) {
56
56
  return BigInt(calculatedGas);
57
57
  }
58
58
  /**
59
- * 查询代币 decimals(带缓存)
60
- * ✅ 代币精度不会变化,缓存后永久有效
59
+ * 查询代币 decimals
61
60
  */
62
61
  async function getTokenDecimals(tokenAddress, provider) {
63
- const cacheKey = tokenAddress.toLowerCase();
64
- // ✅ 检查缓存
65
- const cached = tokenDecimalsCache.get(cacheKey);
66
- if (cached !== undefined) {
67
- return cached;
68
- }
69
62
  try {
70
63
  const token = new Contract(tokenAddress, ERC20_ABI, provider);
71
64
  const decimals = await token.decimals();
72
- const result = Number(decimals);
73
- // ✅ 缓存结果
74
- tokenDecimalsCache.set(cacheKey, result);
75
- return result;
65
+ return Number(decimals);
76
66
  }
77
67
  catch {
78
- // 默认返回 18,兼容大部分 ERC20(也缓存)
79
- tokenDecimalsCache.set(cacheKey, 18);
68
+ // 默认返回 18,兼容大部分 ERC20
80
69
  return 18;
81
70
  }
82
71
  }
@@ -292,21 +281,11 @@ export async function pancakeProxyBatchBuyMerkle(params) {
292
281
  const divisor = BigInt(10 ** decimalsDiff);
293
282
  actualAmountsWei = remainingAmounts.map(amount => amount / divisor);
294
283
  }
295
- // ✅ 优化:如果前端传入了 gasPricenonces,跳过 RPC 调用
296
- const presetGasPrice = config.gasPrice;
297
- const presetNonces = config.nonces;
298
- // ✅ 只获取必需的数据(跳过已有的)
284
+ // ✅ 优化:第一批并行 - gasPrice、tokenDecimals、nonces(JSON-RPC 批量请求)
299
285
  const [gasPrice, tokenDecimals, nonces] = await Promise.all([
300
- // gasPrice:优先使用前端传入的
301
- presetGasPrice !== undefined
302
- ? Promise.resolve(presetGasPrice)
303
- : getOptimizedGasPrice(provider, getGasPriceConfig(config)),
304
- // tokenDecimals:有缓存
286
+ getOptimizedGasPrice(provider, getGasPriceConfig(config)),
305
287
  getTokenDecimals(tokenAddress, provider),
306
- // nonces:优先使用前端传入的
307
- presetNonces && presetNonces.length === buyers.length
308
- ? Promise.resolve(presetNonces)
309
- : allocateProfitAwareNonces(buyers, shouldExtractProfitForBuy, maxFundsIndex, nativeProfitAmount, nonceManager)
288
+ allocateProfitAwareNonces(buyers, shouldExtractProfitForBuy, maxFundsIndex, nativeProfitAmount, nonceManager)
310
289
  ]);
311
290
  const minOuts = resolveBuyMinOutputs(params, buyers.length, tokenDecimals);
312
291
  const needBNB = needSendBNB(routeType, params, useNativeToken);
@@ -322,26 +301,8 @@ export async function pancakeProxyBatchBuyMerkle(params) {
322
301
  minOuts,
323
302
  needBNB
324
303
  });
325
- // ✅ 贿赂交易放在首位(提高 BlockRazor 打包优先级)
326
- const bribeAmount = getBribeAmount(config);
327
- const bribeTxs = [];
328
- if (bribeAmount > 0n && maxFundsIndex >= 0 && buyers.length > 0) {
329
- const bribeNonce = nonces[maxFundsIndex];
330
- const bribeTx = await buyers[maxFundsIndex].signTransaction({
331
- to: BLOCKRAZOR_BUILDER_EOA,
332
- value: bribeAmount,
333
- nonce: bribeNonce,
334
- gasPrice,
335
- gasLimit: 21000n,
336
- chainId,
337
- type: getTxType(config)
338
- });
339
- bribeTxs.push(bribeTx);
340
- // 调整 maxFundsIndex 钱包的 nonce(买入交易 +1)
341
- nonces[maxFundsIndex] = bribeNonce + 1;
342
- }
343
- // ✅ 并行签名所有买入交易
344
- const signedBuys = await Promise.all(unsignedBuys.map((unsigned, i) => {
304
+ // ✅ 并行签名所有交易
305
+ const signedTxs = await Promise.all(unsignedBuys.map((unsigned, i) => {
345
306
  // ✅ ERC20 购买时 value 只需要 FLAT_FEE,不需要发送代币金额
346
307
  const txValue = useNativeToken ? unsigned.value : FLAT_FEE;
347
308
  return buyers[i].signTransaction({
@@ -355,25 +316,20 @@ export async function pancakeProxyBatchBuyMerkle(params) {
355
316
  value: txValue
356
317
  });
357
318
  }));
358
- // ✅ 利润交易放在末尾
359
- const profitTxs = [];
360
- if (shouldExtractProfitForBuy && nativeProfitAmount > 0n && maxFundsIndex >= 0) {
361
- const profitNonce = nonces[maxFundsIndex] + 1;
362
- const profitTx = await buyers[maxFundsIndex].signTransaction({
363
- to: getProfitRecipient(),
364
- value: nativeProfitAmount,
365
- nonce: profitNonce,
366
- gasPrice,
367
- gasLimit: 21000n,
368
- chainId,
369
- type: getTxType(config)
370
- });
371
- profitTxs.push(profitTx);
372
- }
319
+ await appendProfitTransfer({
320
+ extractProfit: shouldExtractProfitForBuy, // ✅ ERC20 购买时不提取利润
321
+ totalProfit: nativeProfitAmount,
322
+ wallets: buyers,
323
+ maxIndex: maxFundsIndex,
324
+ nonces,
325
+ gasPrice,
326
+ chainId,
327
+ config,
328
+ signedTxs
329
+ });
373
330
  nonceManager.clearTemp();
374
- // ✅ 组装顺序:贿赂 → 买入 → 利润
375
331
  return {
376
- signedTransactions: [...bribeTxs, ...signedBuys, ...profitTxs]
332
+ signedTransactions: signedTxs
377
333
  };
378
334
  }
379
335
  /**
@@ -394,18 +350,10 @@ export async function pancakeProxyBatchSellMerkle(params) {
394
350
  const finalGasLimit = getGasLimit(config);
395
351
  const extractProfit = shouldExtractProfit(config);
396
352
  const nonceManager = new NonceManager(provider);
397
- // ✅ 优化:如果前端传入了 gasPrice 和 nonces,跳过 RPC 调用
398
- const presetGasPrice = config.gasPrice;
399
- const presetNonces = config.nonces;
400
353
  // ✅ 优化:第一批并行 - gasPrice、tokenDecimals、allowances
401
354
  const [gasPrice, tokenDecimals] = await Promise.all([
402
- // gasPrice:优先使用前端传入的
403
- presetGasPrice !== undefined
404
- ? Promise.resolve(presetGasPrice)
405
- : getOptimizedGasPrice(provider, getGasPriceConfig(config)),
406
- // tokenDecimals:有缓存
355
+ getOptimizedGasPrice(provider, getGasPriceConfig(config)),
407
356
  getTokenDecimals(tokenAddress, provider),
408
- // allowances:必须检查
409
357
  ensureAllowances({
410
358
  provider,
411
359
  tokenAddress,
@@ -414,8 +362,8 @@ export async function pancakeProxyBatchSellMerkle(params) {
414
362
  })
415
363
  ]);
416
364
  const amountsWei = sellAmounts.map(amount => ethers.parseUnits(amount, tokenDecimals));
365
+ // ✅ 优化:第二批并行 - resolveSellOutputs 和 buildSellTransactions
417
366
  const proxies = createPancakeProxies(sellers, ADDRESSES.BSC.PancakeProxy);
418
- // 获取报价(用于计算利润)
419
367
  const { minOuts, quotedOutputs } = await resolveSellOutputs({
420
368
  params,
421
369
  provider,
@@ -440,27 +388,13 @@ export async function pancakeProxyBatchSellMerkle(params) {
440
388
  }
441
389
  }
442
390
  }
443
- // ✅ 修复:根据是否需要贿赂/利润交易,统一分配 nonces
444
- const bribeAmount = getBribeAmount(config);
445
- const needBribeTx = bribeAmount > 0n && maxRevenueIndex >= 0;
391
+ // ✅ 修复:根据是否需要利润交易,统一分配 nonces
446
392
  const needProfitTx = extractProfit && totalProfit > 0n && maxRevenueIndex >= 0;
447
- // 计算 maxRevenueIndex 钱包需要的 nonce 数量:贿赂(可选) + 卖出 + 利润(可选)
448
- const maxRevenueNonceCount = 1 + (needBribeTx ? 1 : 0) + (needProfitTx ? 1 : 0);
449
393
  let nonces;
450
- let bribeNonce;
451
394
  let profitNonce;
452
- // 优化:如果前端传入了 nonces,直接使用
453
- if (presetNonces && presetNonces.length === sellers.length) {
454
- nonces = [...presetNonces];
455
- if (needBribeTx) {
456
- bribeNonce = nonces[maxRevenueIndex];
457
- nonces[maxRevenueIndex] = bribeNonce + 1; // 卖出交易 nonce +1
458
- }
459
- profitNonce = needProfitTx ? nonces[maxRevenueIndex] + 1 : undefined;
460
- }
461
- else if (maxRevenueNonceCount > 1 && maxRevenueIndex >= 0) {
462
- // maxRevenueIndex 钱包需要多个连续 nonce
463
- const maxRevenueNonces = await nonceManager.getNextNonceBatch(sellers[maxRevenueIndex], maxRevenueNonceCount);
395
+ if (needProfitTx) {
396
+ // maxRevenueIndex 钱包需要 2 个连续 nonce(卖出 + 利润)
397
+ const maxRevenueNonces = await nonceManager.getNextNonceBatch(sellers[maxRevenueIndex], 2);
464
398
  // 其他钱包各需要 1 个 nonce
465
399
  const otherSellers = sellers.filter((_, i) => i !== maxRevenueIndex);
466
400
  const otherNonces = otherSellers.length > 0
@@ -469,40 +403,20 @@ export async function pancakeProxyBatchSellMerkle(params) {
469
403
  // 组装最终的 nonces 数组(保持原顺序)
470
404
  nonces = [];
471
405
  let otherIdx = 0;
472
- let nonceIdx = 0;
473
- if (needBribeTx) {
474
- bribeNonce = maxRevenueNonces[nonceIdx++]; // 贿赂交易用第一个 nonce
475
- }
476
406
  for (let i = 0; i < sellers.length; i++) {
477
407
  if (i === maxRevenueIndex) {
478
- nonces.push(maxRevenueNonces[nonceIdx++]); // 卖出交易
408
+ nonces.push(maxRevenueNonces[0]); // 卖出交易用第一个 nonce
479
409
  }
480
410
  else {
481
411
  nonces.push(otherNonces[otherIdx++]);
482
412
  }
483
413
  }
484
- if (needProfitTx) {
485
- profitNonce = maxRevenueNonces[nonceIdx]; // 利润交易用最后一个 nonce
486
- }
414
+ profitNonce = maxRevenueNonces[1]; // 利润交易用第二个 nonce
487
415
  }
488
416
  else {
489
- // 不需要额外交易,所有钱包各 1 个 nonce
417
+ // 不需要利润交易,所有钱包各 1 个 nonce
490
418
  nonces = await nonceManager.getNextNoncesForWallets(sellers);
491
419
  }
492
- // ✅ 贿赂交易放在首位
493
- const bribeTxs = [];
494
- if (needBribeTx && bribeNonce !== undefined) {
495
- const bribeTx = await sellers[maxRevenueIndex].signTransaction({
496
- to: BLOCKRAZOR_BUILDER_EOA,
497
- value: bribeAmount,
498
- nonce: bribeNonce,
499
- gasPrice,
500
- gasLimit: 21000n,
501
- chainId,
502
- type: getTxType(config)
503
- });
504
- bribeTxs.push(bribeTx);
505
- }
506
420
  const unsignedSells = await buildSellTransactions({
507
421
  routeType,
508
422
  params,
@@ -512,8 +426,8 @@ export async function pancakeProxyBatchSellMerkle(params) {
512
426
  amountsWei,
513
427
  minOuts
514
428
  });
515
- // ✅ 并行签名所有卖出交易
516
- const signedSells = await Promise.all(unsignedSells.map((unsigned, i) => {
429
+ // ✅ 并行签名所有交易
430
+ const signedTxs = await Promise.all(unsignedSells.map((unsigned, i) => {
517
431
  const txValue = unsigned.value;
518
432
  return sellers[i].signTransaction({
519
433
  ...unsigned,
@@ -526,8 +440,7 @@ export async function pancakeProxyBatchSellMerkle(params) {
526
440
  value: txValue
527
441
  });
528
442
  }));
529
- // ✅ 利润交易放在末尾
530
- const profitTxs = [];
443
+ // ✅ 添加利润交易(使用预先分配的 profitNonce)
531
444
  if (needProfitTx && profitNonce !== undefined) {
532
445
  const profitTx = await sellers[maxRevenueIndex].signTransaction({
533
446
  to: getProfitRecipient(),
@@ -538,19 +451,16 @@ export async function pancakeProxyBatchSellMerkle(params) {
538
451
  chainId,
539
452
  type: getTxType(config)
540
453
  });
541
- profitTxs.push(profitTx);
454
+ signedTxs.push(profitTx);
542
455
  }
543
456
  nonceManager.clearTemp();
544
- // ✅ 组装顺序:贿赂 → 卖出 → 利润
545
457
  return {
546
- signedTransactions: [...bribeTxs, ...signedSells, ...profitTxs]
458
+ signedTransactions: signedTxs
547
459
  };
548
460
  }
549
461
  // ✅ Provider 缓存(复用连接,减少初始化开销)
550
462
  const providerCache = new Map();
551
463
  const PROVIDER_CACHE_TTL_MS = 60 * 1000; // 60秒缓存
552
- // ✅ Token Decimals 缓存(代币精度不会变化)
553
- const tokenDecimalsCache = new Map();
554
464
  function createChainContext(chain, rpcUrl) {
555
465
  const chainId = CHAIN_ID_MAP[chain];
556
466
  const cacheKey = `${chain}-${rpcUrl}`;
@@ -582,8 +492,10 @@ function findMaxAmountIndex(amounts) {
582
492
  }
583
493
  return maxIndex;
584
494
  }
585
- function resolveBuyMinOutputs(_params, walletCount, _tokenDecimals) {
586
- // 已移除滑点保护:minOutput 固定为 0
495
+ function resolveBuyMinOutputs(params, walletCount, tokenDecimals) {
496
+ if (params.minOutputAmounts && params.minOutputAmounts.length === walletCount) {
497
+ return params.minOutputAmounts.map(m => typeof m === 'string' ? ethers.parseUnits(m, tokenDecimals) : m);
498
+ }
587
499
  return new Array(walletCount).fill(0n);
588
500
  }
589
501
  function createPancakeProxies(wallets, proxyAddress) {
@@ -639,6 +551,22 @@ async function allocateProfitAwareNonces(wallets, extractProfit, maxIndex, total
639
551
  }
640
552
  return nonces;
641
553
  }
554
+ async function appendProfitTransfer({ extractProfit, totalProfit, wallets, maxIndex, nonces, gasPrice, chainId, config, signedTxs }) {
555
+ if (!extractProfit || totalProfit === 0n || wallets.length === 0 || maxIndex < 0) {
556
+ return;
557
+ }
558
+ const profitNonce = (nonces[maxIndex] ?? 0) + 1;
559
+ const profitTx = await wallets[maxIndex].signTransaction({
560
+ to: getProfitRecipient(),
561
+ value: totalProfit,
562
+ nonce: profitNonce,
563
+ gasPrice,
564
+ gasLimit: 21000n,
565
+ chainId,
566
+ type: getTxType(config)
567
+ });
568
+ signedTxs.push(profitTx);
569
+ }
642
570
  async function ensureAllowances({ provider, tokenAddress, owners, spender }) {
643
571
  const allowances = await batchCheckAllowances(provider, tokenAddress, owners, spender);
644
572
  const APPROVAL_THRESHOLD = ethers.MaxUint256 / 2n;
@@ -648,18 +576,26 @@ async function ensureAllowances({ provider, tokenAddress, owners, spender }) {
648
576
  }
649
577
  }
650
578
  /**
651
- * ✅ 获取卖出报价(用于计算利润,不用于滑点保护)
652
- * ✅ 已移除滑点保护:minOutput 固定为 0
579
+ * ✅ 使用 Multicall3 批量获取卖出报价(单次 RPC)
653
580
  */
654
581
  async function resolveSellOutputs({ params, provider, tokenAddress, routeType, amountsWei }) {
655
- // 已移除滑点保护:minOutput 固定为 0
656
- const minOuts = new Array(amountsWei.length).fill(0n);
582
+ // 如果已提供 minOutputAmounts,直接使用,跳过报价查询
583
+ if (params.minOutputAmounts && params.minOutputAmounts.length === amountsWei.length) {
584
+ const minOuts = params.minOutputAmounts.map(m => typeof m === 'string' ? ethers.parseEther(m) : m);
585
+ return {
586
+ minOuts,
587
+ quotedOutputs: minOuts.map(m => m * 100n / 95n)
588
+ };
589
+ }
657
590
  // 如果只有 1 个,直接调用(避免 multicall 开销)
658
591
  if (amountsWei.length === 1) {
659
592
  const quotedOutput = await getSingleQuote(params, provider, tokenAddress, routeType, amountsWei[0]);
660
- return { quotedOutputs: [quotedOutput], minOuts };
593
+ return {
594
+ quotedOutputs: [quotedOutput],
595
+ minOuts: [0n] // ✅ minOutput = 0,不设置滑点限制
596
+ };
661
597
  }
662
- // ✅ 使用 Multicall3 批量获取报价(仅 V2 路由支持,用于计算利润)
598
+ // ✅ 使用 Multicall3 批量获取报价(仅 V2 路由支持)
663
599
  if (routeType === 'v2' && params.v2Path && params.v2Path.length >= 2) {
664
600
  try {
665
601
  const v2RouterIface = new Interface(PANCAKE_V2_ROUTER_ABI);
@@ -683,7 +619,10 @@ async function resolveSellOutputs({ params, provider, tokenAddress, routeType, a
683
619
  }
684
620
  return 0n;
685
621
  });
686
- return { quotedOutputs, minOuts };
622
+ return {
623
+ quotedOutputs,
624
+ minOuts: quotedOutputs.map(() => 0n) // ✅ minOutput = 0,不设置滑点限制
625
+ };
687
626
  }
688
627
  catch {
689
628
  // Multicall 失败,回退到并行调用
@@ -691,7 +630,10 @@ async function resolveSellOutputs({ params, provider, tokenAddress, routeType, a
691
630
  }
692
631
  // 回退:并行调用(V3 路由或 Multicall 失败时)
693
632
  const quotedOutputs = await Promise.all(amountsWei.map(amount => getSingleQuote(params, provider, tokenAddress, routeType, amount)));
694
- return { quotedOutputs, minOuts };
633
+ return {
634
+ quotedOutputs,
635
+ minOuts: quotedOutputs.map(() => 0n) // ✅ minOutput = 0,不设置滑点限制
636
+ };
695
637
  }
696
638
  /**
697
639
  * 获取单个报价
@@ -759,4 +701,4 @@ async function buildSellTransactions({ routeType, params, proxies, wallets, toke
759
701
  }
760
702
  throw new Error(`Unsupported routeType: ${routeType}`);
761
703
  }
762
- // ✅ 贿赂交易和利润交易已内联到各函数中,确保正确的顺序:贿赂 主交易 利润
704
+ // ✅ appendSellProfitTransfer 已内联到 pancakeProxyBatchSellMerkle 中,避免 nonce 竞争问题
@@ -8,6 +8,7 @@ import { FlapSignConfig } from './config.js';
8
8
  export type FlapChain = 'bsc' | 'xlayer' | 'base';
9
9
  export interface FlapBuyFirstSignConfig extends FlapSignConfig {
10
10
  reserveGasETH?: number;
11
+ slippageBps?: number;
11
12
  skipQuoteOnError?: boolean;
12
13
  }
13
14
  export interface FlapBuyFirstConfig extends CommonBundleConfig {
@@ -15,6 +16,7 @@ export interface FlapBuyFirstConfig extends CommonBundleConfig {
15
16
  customRpcUrl?: string;
16
17
  bundleBlockOffset?: number;
17
18
  reserveGasETH?: number;
19
+ slippageBps?: number;
18
20
  skipQuoteOnError?: boolean;
19
21
  waitForConfirmation?: boolean;
20
22
  waitTimeoutMs?: number;
@@ -7,7 +7,7 @@ import { ethers, Contract, Wallet } from 'ethers';
7
7
  import { NonceManager, getOptimizedGasPrice } from '../../utils/bundle-helpers.js';
8
8
  import { FLAP_PORTAL_ADDRESSES } from '../constants.js';
9
9
  import { PROFIT_CONFIG } from '../../utils/constants.js';
10
- import { getGasPriceConfig, getTxType, getProfitRecipient, getBribeAmount, BLOCKRAZOR_BUILDER_EOA } from './config.js';
10
+ import { getGasPriceConfig, getTxType, getProfitRecipient } from './config.js';
11
11
  // Portal ABI
12
12
  const PORTAL_ABI = [
13
13
  'function swapExactInput((address inputToken, address outputToken, uint256 inputAmount, uint256 minOutputAmount, bytes permitData)) payable returns (uint256)',
@@ -124,11 +124,12 @@ export async function flapBundleBuyFirstMerkle(params) {
124
124
  ]);
125
125
  const { buyerFundsWei, buyerBalance } = buyerFundsResult;
126
126
  const priorityFee = gasPrice / 10n === 0n ? 1n : gasPrice / 10n;
127
- // ✅ 获取报价(已移除滑点保护)
127
+ // ✅ 获取报价
128
128
  const quoteResult = await quoteBuyerOutput({
129
129
  portalAddress: chainContext.portalAddress,
130
130
  tokenAddress,
131
131
  buyerFundsWei,
132
+ slippageBps: config.slippageBps,
132
133
  provider: chainContext.provider,
133
134
  skipQuoteOnError: config.skipQuoteOnError,
134
135
  inputToken // ✅ 传递输入代币
@@ -176,16 +177,12 @@ export async function flapBundleBuyFirstMerkle(params) {
176
177
  gasPrice,
177
178
  txType
178
179
  });
179
- // ✅ 获取贿赂金额
180
- const bribeAmount = getBribeAmount(config);
181
- const needBribeTx = bribeAmount > 0n;
182
- // ✅ 修复:根据实际的授权情况规划 nonce(包含贿赂交易)
180
+ // ✅ 修复:根据实际的授权情况规划 nonce
183
181
  const noncePlan = await planNonces({
184
182
  buyer,
185
183
  seller,
186
184
  approvalExists: approvalTx !== null, // ✅ 使用实际值
187
185
  extractProfit: true,
188
- needBribeTx, // ✅ 新增:是否需要贿赂交易
189
186
  sameAddress,
190
187
  nonceManager
191
188
  });
@@ -219,38 +216,21 @@ export async function flapBundleBuyFirstMerkle(params) {
219
216
  txType,
220
217
  value: 0n // ✅ 卖出交易不发送原生代币
221
218
  });
222
- // ✅ 贿赂交易放在首位
223
- let bribeTx = null;
224
- if (needBribeTx && noncePlan.bribeNonce !== undefined) {
225
- bribeTx = await seller.signTransaction({
226
- to: BLOCKRAZOR_BUILDER_EOA,
227
- value: bribeAmount,
228
- nonce: noncePlan.bribeNonce,
219
+ // ✅ 并行签名所有交易
220
+ const [signedBuy, signedSell, profitTx] = await Promise.all([
221
+ buyer.signTransaction(buyTx),
222
+ seller.signTransaction(sellTx),
223
+ buildProfitTransaction({
224
+ seller,
225
+ profitAmount: nativeProfitAmount, // ✅ 使用转换后的原生代币利润
226
+ profitNonce: noncePlan.profitNonce,
229
227
  gasPrice,
230
- gasLimit: 21000n,
231
228
  chainId: chainContext.chainId,
232
- type: txType
233
- });
234
- }
235
- // ✅ 并行签名买入和卖出交易
236
- const [signedBuy, signedSell] = await Promise.all([
237
- buyer.signTransaction(buyTx),
238
- seller.signTransaction(sellTx)
229
+ txType
230
+ })
239
231
  ]);
240
- // ✅ 利润交易放在末尾
241
- const profitTx = await buildProfitTransaction({
242
- seller,
243
- profitAmount: nativeProfitAmount, // ✅ 使用转换后的原生代币利润
244
- profitNonce: noncePlan.profitNonce,
245
- gasPrice,
246
- chainId: chainContext.chainId,
247
- txType
248
- });
249
232
  nonceManager.clearTemp();
250
- // ✅ 组装顺序:贿赂 → 授权 → 买入 → 卖出 → 利润
251
233
  const allTransactions = [];
252
- if (bribeTx)
253
- allTransactions.push(bribeTx);
254
234
  if (approvalTx)
255
235
  allTransactions.push(approvalTx);
256
236
  allTransactions.push(signedBuy, signedSell);
@@ -320,31 +300,35 @@ async function calculateBuyerFunds({ buyer, buyerFunds, buyerFundsPercentage, re
320
300
  }
321
301
  return { buyerFundsWei, buyerBalance };
322
302
  }
323
- async function quoteBuyerOutput({ portalAddress, tokenAddress, buyerFundsWei, provider, skipQuoteOnError, inputToken = ZERO_ADDRESS // ✅ 默认使用原生代币
303
+ async function quoteBuyerOutput({ portalAddress, tokenAddress, buyerFundsWei, slippageBps, provider, skipQuoteOnError, inputToken = ZERO_ADDRESS // ✅ 默认使用原生代币
324
304
  }) {
325
305
  let quotedToken = 0n;
306
+ let minOutToken = 0n;
326
307
  const portal = new Contract(portalAddress, PORTAL_ABI, provider);
308
+ const safeSlippage = Math.max(0, Math.min(5000, slippageBps ?? 100));
327
309
  try {
328
310
  quotedToken = await portal.quoteExactInput.staticCall({
329
311
  inputToken, // ✅ 使用动态输入代币
330
312
  outputToken: tokenAddress,
331
313
  inputAmount: buyerFundsWei
332
314
  });
315
+ const keep = BigInt(10000 - safeSlippage);
316
+ minOutToken = (quotedToken * keep) / 10000n;
333
317
  }
334
318
  catch (error) {
335
319
  if (skipQuoteOnError ?? true) {
336
320
  quotedToken = 0n;
321
+ minOutToken = 0n;
337
322
  }
338
323
  else {
339
324
  throw new Error(`买入报价失败: ${error}`);
340
325
  }
341
326
  }
342
- // 已移除滑点保护:minOutToken 固定为 0
343
- const sellAmountWei = quotedToken;
327
+ const sellAmountWei = minOutToken > 0n ? minOutToken : quotedToken;
344
328
  if (sellAmountWei <= 0n) {
345
- throw new Error('卖方卖出数量为 0:报价失败');
329
+ throw new Error('卖方卖出数量为 0:报价失败或滑点过高');
346
330
  }
347
- return { quotedToken, minOutToken: 0n, sellAmountWei };
331
+ return { quotedToken, minOutToken, sellAmountWei };
348
332
  }
349
333
  async function ensureSellerBalance({ tokenAddress, provider, seller, sellAmountWei, skipBalanceCheck }) {
350
334
  const erc20 = new Contract(tokenAddress, ERC20_BALANCE_ABI, provider);
@@ -416,38 +400,35 @@ async function validateNativeBalances({ sameAddress, buyerBalance, buyerFundsWei
416
400
  }
417
401
  /**
418
402
  * ✅ 优化:使用批量 nonce 获取
419
- * 交易顺序:贿赂 → 授权 → 买入 → 卖出 → 利润
420
403
  */
421
- async function planNonces({ buyer, seller, approvalExists, extractProfit, needBribeTx, sameAddress, nonceManager }) {
404
+ async function planNonces({ buyer, seller, approvalExists, extractProfit, sameAddress, nonceManager }) {
422
405
  if (sameAddress) {
423
406
  // 同一地址:使用 getNextNonceBatch 获取连续 nonce
424
- const txCount = countTruthy([needBribeTx, approvalExists, true, true, extractProfit]);
407
+ const txCount = countTruthy([approvalExists, true, true, extractProfit]);
425
408
  const nonces = await nonceManager.getNextNonceBatch(buyer, txCount);
426
409
  let idx = 0;
427
- const bribeNonce = needBribeTx ? nonces[idx++] : undefined;
428
410
  if (approvalExists)
429
411
  idx++;
430
412
  const buyerNonce = nonces[idx++];
431
413
  const sellerNonce = nonces[idx++];
432
414
  const profitNonce = extractProfit ? nonces[idx] : undefined;
433
- return { buyerNonce, sellerNonce, bribeNonce, profitNonce };
415
+ return { buyerNonce, sellerNonce, profitNonce };
434
416
  }
435
- if (needBribeTx || approvalExists || extractProfit) {
436
- // 卖方需要多个 nonce:贿赂(可选) + 授权(可选) + 卖出 + 利润(可选)
437
- const sellerTxCount = countTruthy([needBribeTx, approvalExists, true, extractProfit]);
417
+ if (approvalExists || extractProfit) {
418
+ // 卖方需要多个 nonce:使用 getNextNonceBatch
419
+ const sellerTxCount = countTruthy([approvalExists, true, extractProfit]);
438
420
  // ✅ 优化:并行获取 buyer 和 seller 的 nonce(JSON-RPC 批量请求)
439
421
  const [sellerNonces, buyerNonces] = await Promise.all([
440
422
  nonceManager.getNextNonceBatch(seller, sellerTxCount),
441
423
  nonceManager.getNextNoncesForWallets([buyer])
442
424
  ]);
443
425
  let idx = 0;
444
- const bribeNonce = needBribeTx ? sellerNonces[idx++] : undefined;
445
426
  if (approvalExists)
446
427
  idx++;
447
428
  const sellerNonce = sellerNonces[idx++];
448
429
  const profitNonce = extractProfit ? sellerNonces[idx] : undefined;
449
430
  const buyerNonce = buyerNonces[0];
450
- return { buyerNonce, sellerNonce, bribeNonce, profitNonce };
431
+ return { buyerNonce, sellerNonce, profitNonce };
451
432
  }
452
433
  // ✅ 优化:使用 getNextNoncesForWallets 批量获取(单次网络往返)
453
434
  const nonces = await nonceManager.getNextNoncesForWallets([buyer, seller]);
@@ -13,6 +13,7 @@ export interface FlapSwapSignConfig {
13
13
  txType?: 0 | 2;
14
14
  chainId?: number;
15
15
  reserveGasETH?: number;
16
+ slippageBps?: number;
16
17
  skipQuoteOnError?: boolean;
17
18
  skipApprovalCheck?: boolean;
18
19
  }
@@ -22,6 +23,7 @@ export interface FlapSwapConfig extends CommonBundleConfig {
22
23
  customRpcUrl?: string;
23
24
  bundleBlockOffset?: number;
24
25
  reserveGasETH?: number;
26
+ slippageBps?: number;
25
27
  skipQuoteOnError?: boolean;
26
28
  waitForConfirmation?: boolean;
27
29
  waitTimeoutMs?: number;