four-flap-meme-sdk 1.2.2 → 1.2.4

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.
@@ -48,23 +48,7 @@ export async function approveFourTokenManagerBatch(params) {
48
48
  const APPROVAL_THRESHOLD = ethers.MaxUint256 / 2n;
49
49
  const needApproval = wallets.filter((_, i) => allowances[i] < APPROVAL_THRESHOLD);
50
50
  const needApprovalAmounts = amountsBigInt.filter((_, i) => allowances[i] < APPROVAL_THRESHOLD);
51
- console.log(`🔍 TokenManager 授权检查:`);
52
- console.log(` - 总钱包数: ${wallets.length}`);
53
- console.log(` - 需要授权: ${needApproval.length}`);
54
- console.log(` - 已有授权: ${wallets.length - needApproval.length}`);
55
- // ✅ 显示每个钱包的授权状态
56
- for (let i = 0; i < wallets.length; i++) {
57
- const current = allowances[i];
58
- const requested = amountsBigInt[i];
59
- if (current < APPROVAL_THRESHOLD) {
60
- console.log(` ⚠️ 钱包 ${i}: 当前授权 ${ethers.formatUnits(current, decimals)} → 需要重新授权为 ${requested === ethers.MaxUint256 ? 'MAX' : ethers.formatUnits(requested, decimals)}`);
61
- }
62
- else {
63
- console.log(` ✅ 钱包 ${i}: 授权充足 (${current === ethers.MaxUint256 ? 'MAX' : ethers.formatUnits(current, decimals)})`);
64
- }
65
- }
66
51
  if (needApproval.length === 0) {
67
- console.log('✅ 所有钱包已授权,无需再次授权。');
68
52
  return {
69
53
  success: true,
70
54
  approvedCount: 0,
@@ -334,23 +334,18 @@ export async function batchSellWithBundleMerkle(params) {
334
334
  }
335
335
  else {
336
336
  // ✅ 自动调用 trySell 获取每个钱包的预期收益
337
- console.log('💰 自动获取卖出报价...');
338
337
  const rpcUrl = config.customRpcUrl;
339
338
  quotedOutputs = await Promise.all(amountsWei.map(async (amount, i) => {
340
339
  try {
341
340
  const result = await trySell('BSC', rpcUrl, tokenAddress, amount);
342
- const quoted = result.funds; // 预期收益
343
- console.log(` 钱包 ${i}: 卖出 ${ethers.formatUnits(amount, 18)} 代币 → 预期收益 ${ethers.formatEther(quoted)} BNB`);
344
- return quoted;
341
+ return result.funds;
345
342
  }
346
343
  catch (error) {
347
- console.log(` ⚠️ 钱包 ${i}: 报价失败,使用 minOut = 0`);
348
344
  return 0n;
349
345
  }
350
346
  }));
351
347
  // minOuts 设为预期收益的 95%(作为滑点保护)
352
348
  minOuts = quotedOutputs.map(q => q * 95n / 100n);
353
- console.log('');
354
349
  }
355
350
  // ✅ Step 1: 检查代币余额和授权状态
356
351
  const ERC20_ABI = [
@@ -15,6 +15,16 @@ const PANCAKE_PROXY_ABI = [
15
15
  'function swapV3Single(address tokenIn, address tokenOut, uint24 fee, uint256 amountIn, uint256 amountOutMin, address to) external payable returns (uint256 amountOut)',
16
16
  'function swapV3MultiHop(address[] calldata lpAddresses, address exactTokenIn, uint256 amountIn, uint256 amountOutMin, address to) external payable returns (uint256 amountOut)'
17
17
  ];
18
+ // PancakeSwap V2 Router ABI(用于报价)
19
+ const PANCAKE_V2_ROUTER_ABI = [
20
+ 'function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)'
21
+ ];
22
+ // PancakeSwap V3 QuoterV2 ABI(用于报价)
23
+ const PANCAKE_V3_QUOTER_ABI = [
24
+ 'function quoteExactInputSingle((address tokenIn, address tokenOut, uint256 amountIn, uint24 fee, uint160 sqrtPriceLimitX96)) external returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate)'
25
+ ];
26
+ const PANCAKE_V2_ROUTER_ADDRESS = '0x10ED43C718714eb63d5aA57B78B54704E256024E';
27
+ const PANCAKE_V3_QUOTER_ADDRESS = '0xB048Bbc1Ee6b733FFfCFb9e9CeF7375518e25997';
18
28
  // ERC20 ABI
19
29
  const ERC20_ABI = [
20
30
  'function approve(address spender, uint256 amount) external returns (bool)',
@@ -398,7 +408,6 @@ export async function fourPancakeProxyBatchSellMerkle(params) {
398
408
  const tokenDecimals = await getTokenDecimals(tokenAddress, provider);
399
409
  const amountsWei = sellAmounts.map(a => ethers.parseUnits(a, tokenDecimals));
400
410
  // ✅ Step 1: 使用 Multicall3 批量检查授权状态
401
- console.log('🔍 使用 Multicall3 批量检查授权状态...');
402
411
  const allowances = await batchCheckAllowances(provider, tokenAddress, sellers.map(w => w.address), pancakeProxyAddress);
403
412
  // 找出需要授权的钱包索引
404
413
  const needApprovalIndexes = [];
@@ -408,75 +417,58 @@ export async function fourPancakeProxyBatchSellMerkle(params) {
408
417
  needApprovalIndexes.push(i);
409
418
  }
410
419
  }
411
- console.log(` - 总钱包数: ${sellers.length}`);
412
- console.log(` - 需要授权: ${needApprovalIndexes.length}`);
413
- console.log(` - 已有授权: ${sellers.length - needApprovalIndexes.length}\n`);
414
420
  // ✅ Step 2: 如果需要授权,抛出错误提示
415
421
  // ⚠️ SDK不再处理授权,需要前端先单独授权
416
422
  if (needApprovalIndexes.length > 0) {
417
- console.log(`⚠️ 警告:检测到 ${needApprovalIndexes.length} 个钱包需要授权`);
418
- console.log(' 请先使用这些钱包完成授权后再调用卖出方法');
419
423
  throw new Error(`需要授权: ${needApprovalIndexes.length} 个钱包尚未授权。请先完成授权后再卖出。`);
420
424
  }
421
- // ✅ Step 3: 批量卖出
422
- console.log('💰 提交卖出交易...');
423
- // 🔍 调试信息 - 详细参数
424
- console.log('📊 卖出详情:');
425
- console.log(` [基本信息]`);
426
- console.log(` - 链: BSC`);
427
- console.log(` - 代币地址: ${tokenAddress}`);
428
- console.log(` - PancakeProxy: ${pancakeProxyAddress}`);
429
- console.log(` - 卖家数量: ${sellers.length}`);
430
- console.log(` - 路由类型: ${routeType}`);
431
- console.log(`\n [卖家信息]`);
432
- sellers.forEach((seller, i) => {
433
- console.log(` - 钱包 ${i + 1}: ${seller.address}`);
434
- });
435
- console.log(`\n [卖出金额]`);
436
- sellAmounts.forEach((amount, i) => {
437
- console.log(` - 钱包 ${i + 1}: ${amount} TOKEN (${amountsWei[i].toString()} Wei)`);
438
- });
439
- // 计算 minOutputAmounts(通常是 BNB/WBNB,18 位)
425
+ // ✅ 自动获取报价或使用用户提供的 minOutputAmounts
440
426
  let minOuts;
427
+ let quotedOutputs;
441
428
  if (params.minOutputAmounts && params.minOutputAmounts.length === sellers.length) {
429
+ // 用户提供了 minOutputAmounts
442
430
  minOuts = params.minOutputAmounts.map(m => typeof m === 'string' ? ethers.parseEther(m) : m);
443
- console.log(`\n [最小输出]`);
444
- params.minOutputAmounts.forEach((min, i) => {
445
- console.log(` - 钱包 ${i + 1}: ${min} BNB`);
446
- });
431
+ quotedOutputs = minOuts.map(m => m * 100n / 95n); // 反推预期收益
447
432
  }
448
433
  else {
449
- minOuts = new Array(sellers.length).fill(0n);
450
- console.log(`\n [最小输出] 未设置(默认 0,无滑点保护)`);
451
- }
452
- console.log(`\n [路由信息]`);
453
- if (routeType === 'v2' && params.v2Path) {
454
- console.log(` - V2 路径: ${params.v2Path.join(' → ')}`);
455
- }
456
- else if (routeType === 'v3-single') {
457
- console.log(` - V3 单跳: ${tokenAddress} ${params.v3TokenOut}`);
458
- console.log(` - 费率: ${params.v3Fee}`);
459
- }
460
- else if (routeType === 'v3-multi') {
461
- console.log(` - V3 多跳 LP 数量: ${params.v3LpAddresses?.length}`);
462
- console.log(` - 起始代币: ${params.v3ExactTokenIn}`);
434
+ // 自动调用 PancakeSwap 获取报价
435
+ quotedOutputs = await Promise.all(amountsWei.map(async (amount) => {
436
+ try {
437
+ if (routeType === 'v2') {
438
+ const v2Router = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, provider);
439
+ const amounts = await v2Router.getAmountsOut(amount, params.v2Path);
440
+ return amounts[amounts.length - 1];
441
+ }
442
+ else if (routeType === 'v3-single') {
443
+ const quoter = new Contract(PANCAKE_V3_QUOTER_ADDRESS, PANCAKE_V3_QUOTER_ABI, provider);
444
+ const result = await quoter.quoteExactInputSingle.staticCall({
445
+ tokenIn: tokenAddress,
446
+ tokenOut: params.v3TokenOut,
447
+ amountIn: amount,
448
+ fee: params.v3Fee,
449
+ sqrtPriceLimitX96: 0
450
+ });
451
+ return result[0];
452
+ }
453
+ else {
454
+ // v3-multi 使用 v2 备选
455
+ if (params.v2Path && params.v2Path.length >= 2) {
456
+ const v2Router = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, provider);
457
+ const amounts = await v2Router.getAmountsOut(amount, params.v2Path);
458
+ return amounts[amounts.length - 1];
459
+ }
460
+ return 0n;
461
+ }
462
+ }
463
+ catch (error) {
464
+ return 0n;
465
+ }
466
+ }));
467
+ // minOuts 设为预期收益的 95%(滑点保护)
468
+ minOuts = quotedOutputs.map(q => q * 95n / 100n);
463
469
  }
464
470
  // 卖出不需要发送 BNB,只需要 flatFee
465
471
  const needBNB = false;
466
- console.log(`\n [交易费用]`);
467
- console.log(` - 需要发送 BNB: ${needBNB ? '是' : '否'}`);
468
- console.log(` - 每笔手续费: 0.0001 BNB`);
469
- console.log(` - 总手续费: ${ethers.formatEther(BigInt(sellers.length) * ethers.parseEther('0.0001'))} BNB`);
470
- console.log(`\n [Gas 配置]`);
471
- console.log(` - Gas Price: ${ethers.formatUnits(gasPrice, 'gwei')} Gwei`);
472
- console.log(` - Gas Limit: ${config.gasLimit || 'Auto (800000 * multiplier)'}`);
473
- if (config.gasLimitMultiplier) {
474
- console.log(` - Gas Multiplier: ${config.gasLimitMultiplier}x`);
475
- }
476
- console.log(`\n [Bundle 配置]`);
477
- console.log(` - Block Offset: ${blockOffset}`);
478
- console.log(` - Wait Confirmation: ${config.waitForConfirmation ?? false}`);
479
- console.log(` - Timeout: ${(config.waitTimeoutMs ?? 120000) / 1000}s\n`);
480
472
  // 构建交易
481
473
  const proxies = sellers.map(w => new Contract(pancakeProxyAddress, PANCAKE_PROXY_ABI, w));
482
474
  let unsignedSells;
@@ -502,20 +494,15 @@ export async function fourPancakeProxyBatchSellMerkle(params) {
502
494
  throw new Error(`Unsupported routeType: ${routeType}`);
503
495
  }
504
496
  // ✅ Step 4: 验证钱包余额(确保有足够 BNB 支付手续费)
505
- console.log('🔍 验证钱包余额...');
506
497
  const balances = await Promise.all(sellers.map(w => provider.getBalance(w.address)));
507
498
  const minRequiredBNB = ethers.parseEther('0.001'); // 至少需要 0.001 BNB(包括 gas 和手续费)
508
499
  const insufficientBalances = balances.filter(b => b < minRequiredBNB);
509
500
  if (insufficientBalances.length > 0) {
510
- console.warn(`⚠️ 警告: ${insufficientBalances.length} 个钱包 BNB 余额不足 0.001 BNB`);
511
- balances.forEach((b, i) => {
512
- console.log(` 钱包 ${i + 1}: ${ethers.formatEther(b)} BNB`);
513
- });
501
+ throw new Error(`${insufficientBalances.length} 个钱包 BNB 余额不足 0.001 BNB`);
514
502
  }
515
503
  // ✅ 使用前端传入的 gasLimit,否则使用默认值
516
504
  const finalGasLimit = getGasLimit(config);
517
505
  // ✅ 直接签名和提交(内联处理以支持利润转账)
518
- console.log('🚀 签名卖出交易...\n');
519
506
  const nonceManager = new NonceManager(provider);
520
507
  const nonces = await Promise.all(sellers.map(w => nonceManager.getNextNonce(w)));
521
508
  const signedTxs = await Promise.all(unsignedSells.map((unsigned, i) => sellers[i].signTransaction({
@@ -528,13 +515,12 @@ export async function fourPancakeProxyBatchSellMerkle(params) {
528
515
  type: getTxType(config),
529
516
  value: unsigned.value // ✅ 显式保留 value(手续费)
530
517
  })));
531
- // ✅ 基于 minFunds 为每个钱包添加利润转账
518
+ // ✅ 基于预期收益为每个钱包添加利润转账
532
519
  const extractProfit = shouldExtractProfit(config);
533
- if (extractProfit && minOuts.length > 0) {
534
- // 为每个钱包添加利润转账(如果 minOut > 0)
520
+ if (extractProfit && quotedOutputs.length > 0) {
535
521
  for (let i = 0; i < sellers.length; i++) {
536
- if (minOuts[i] > 0n) { // ⚠️ 保留这个判断:只对设置了 minOut 的钱包提取利润
537
- const { profit } = calculateProfit(minOuts[i], config);
522
+ if (quotedOutputs[i] > 0n) {
523
+ const { profit } = calculateProfit(quotedOutputs[i], config);
538
524
  if (profit > 0n) {
539
525
  const profitNonce = await nonceManager.getNextNonce(sellers[i]);
540
526
  const profitTx = await sellers[i].signTransaction({
@@ -106,15 +106,11 @@ export async function fourPrivateSellMerkle(params) {
106
106
  // ✅ Step 2: 如果需要授权,抛出错误提示
107
107
  // ⚠️ SDK不再处理授权,需要前端先单独授权
108
108
  if (currentAllowance < APPROVAL_THRESHOLD) {
109
- console.log('⚠️ 警告:检测到钱包需要授权');
110
- console.log(' 请先完成授权后再调用卖出方法');
111
109
  throw new Error(`需要授权:钱包 ${wallet.address} 尚未授权。请先完成授权后再卖出。`);
112
110
  }
113
111
  else {
114
- console.log('✅ 已有足够授权额度,跳过授权\n');
115
112
  }
116
113
  // ✅ Step 3: 卖出交易
117
- console.log('💰 提交卖出交易...');
118
114
  const nonceManager = new NonceManager(provider);
119
115
  const signedTxs = [];
120
116
  const tm2 = new ethers.Contract(tmAddr, TM2_ABI, wallet);
@@ -255,23 +251,18 @@ export async function fourBatchPrivateSellMerkle(params) {
255
251
  }
256
252
  else {
257
253
  // ✅ 自动调用 trySell 获取每个钱包的预期收益
258
- console.log('💰 自动获取卖出报价...');
259
254
  const rpcUrl = config.customRpcUrl;
260
255
  quotedOutputs = await Promise.all(amountsWei.map(async (amount, i) => {
261
256
  try {
262
257
  const result = await trySell('BSC', rpcUrl, tokenAddress, amount);
263
- const quoted = result.funds;
264
- console.log(` 钱包 ${i}: 卖出 ${ethers.formatUnits(amount, 18)} 代币 → 预期收益 ${ethers.formatEther(quoted)} BNB`);
265
- return quoted;
258
+ return result.funds;
266
259
  }
267
260
  catch (error) {
268
- console.log(` ⚠️ 钱包 ${i}: 报价失败,使用 minOut = 0`);
269
261
  return 0n;
270
262
  }
271
263
  }));
272
264
  // minOuts 设为预期收益的 95%(作为滑点保护)
273
265
  minOuts = quotedOutputs.map(q => q * 95n / 100n);
274
- console.log('');
275
266
  }
276
267
  // ✅ Step 0: 检查代币余额
277
268
  const tokenContract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
@@ -15,6 +15,16 @@ const PANCAKE_PROXY_ABI = [
15
15
  'function swapV3Single(address tokenIn, address tokenOut, uint24 fee, uint256 amountIn, uint256 amountOutMin, address to) external payable returns (uint256 amountOut)',
16
16
  'function swapV3MultiHop(address[] calldata lpAddresses, address exactTokenIn, uint256 amountIn, uint256 amountOutMin, address to) external payable returns (uint256 amountOut)'
17
17
  ];
18
+ // PancakeSwap V2 Router ABI(用于报价)
19
+ const PANCAKE_V2_ROUTER_ABI = [
20
+ 'function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)'
21
+ ];
22
+ // PancakeSwap V3 QuoterV2 ABI(用于报价)
23
+ const PANCAKE_V3_QUOTER_ABI = [
24
+ 'function quoteExactInputSingle((address tokenIn, address tokenOut, uint256 amountIn, uint24 fee, uint160 sqrtPriceLimitX96)) external returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate)'
25
+ ];
26
+ const PANCAKE_V2_ROUTER_ADDRESS = '0x10ED43C718714eb63d5aA57B78B54704E256024E';
27
+ const PANCAKE_V3_QUOTER_ADDRESS = '0xB048Bbc1Ee6b733FFfCFb9e9CeF7375518e25997';
18
28
  // ERC20 ABI
19
29
  const ERC20_ABI = [
20
30
  'function approve(address spender, uint256 amount) external returns (bool)',
@@ -217,12 +227,7 @@ export async function approvePancakeProxyBatch(params) {
217
227
  // 只授权不足的
218
228
  const needApproval = wallets.filter((_, i) => allowances[i] < amountsBigInt[i]);
219
229
  const needApprovalAmounts = amountsBigInt.filter((amount, i) => allowances[i] < amount);
220
- console.log(`🔍 PancakeProxy 授权检查:`);
221
- console.log(` - 总钱包数: ${wallets.length}`);
222
- console.log(` - 需要授权: ${needApproval.length}`);
223
- console.log(` - 已有授权: ${wallets.length - needApproval.length}`);
224
230
  if (needApproval.length === 0) {
225
- console.log('✅ 所有钱包已授权,无需再次授权。');
226
231
  return {
227
232
  success: true,
228
233
  approvedCount: 0,
@@ -247,22 +252,17 @@ export async function approvePancakeProxyBatch(params) {
247
252
  type: getTxType(config)
248
253
  })));
249
254
  nonceManager.clearTemp();
250
- console.log(`✅ 已生成 ${signedTxs.length} 个授权交易签名`);
251
255
  // ✅ 内部提交到 Merkle
252
256
  const currentBlock = await provider.getBlockNumber();
253
257
  const targetBlock = currentBlock + blockOffset;
254
- console.log(`📤 正在提交授权交易到 Merkle...`);
255
258
  const bundleResult = await merkle.sendBundle({
256
259
  transactions: signedTxs,
257
260
  targetBlock: targetBlock
258
261
  });
259
- console.log(`✅ 授权交易已提交,bundleHash: ${bundleResult.bundleHash}`);
260
262
  // ✅ 等待授权确认
261
- console.log(`⏳ 等待授权交易确认(约5-10秒)...`);
262
263
  const confirmResults = await merkle.waitForBundleConfirmation(bundleResult.txHashes, 1, 60000);
263
264
  const successCount = confirmResults.filter(r => r.success).length;
264
265
  if (successCount > 0) {
265
- console.log(`✅ 授权已确认,共 ${successCount} 笔交易成功`);
266
266
  return {
267
267
  success: true,
268
268
  approvedCount: successCount,
@@ -271,7 +271,6 @@ export async function approvePancakeProxyBatch(params) {
271
271
  };
272
272
  }
273
273
  else {
274
- console.log(`⚠️ 授权交易未确认`);
275
274
  return {
276
275
  success: false,
277
276
  approvedCount: 0,
@@ -400,8 +399,6 @@ export async function pancakeProxyBatchSellMerkle(params) {
400
399
  // 查询 tokenIn decimals
401
400
  const tokenDecimals = await getTokenDecimals(tokenAddress, provider);
402
401
  const amountsWei = sellAmounts.map(a => ethers.parseUnits(a, tokenDecimals));
403
- // ✅ Step 1: 使用 Multicall3 批量检查授权状态
404
- console.log('🔍 使用 Multicall3 批量检查授权状态...');
405
402
  const allowances = await batchCheckAllowances(provider, tokenAddress, sellers.map(w => w.address), pancakeProxyAddress);
406
403
  // 找出需要授权的钱包索引
407
404
  const needApprovalIndexes = [];
@@ -411,9 +408,6 @@ export async function pancakeProxyBatchSellMerkle(params) {
411
408
  needApprovalIndexes.push(i);
412
409
  }
413
410
  }
414
- console.log(` - 总钱包数: ${sellers.length}`);
415
- console.log(` - 需要授权: ${needApprovalIndexes.length}`);
416
- console.log(` - 已有授权: ${sellers.length - needApprovalIndexes.length}\n`);
417
411
  // ✅ Step 2: 如果需要授权,抛出错误提示
418
412
  // ⚠️ SDK不再处理授权,需要前端先单独授权
419
413
  if (needApprovalIndexes.length > 0) {
@@ -421,65 +415,53 @@ export async function pancakeProxyBatchSellMerkle(params) {
421
415
  console.log(' 请先使用这些钱包完成授权后再调用卖出方法');
422
416
  throw new Error(`需要授权: ${needApprovalIndexes.length} 个钱包尚未授权。请先完成授权后再卖出。`);
423
417
  }
424
- // ✅ Step 3: 批量卖出
425
- console.log('💰 提交卖出交易...');
426
- // 🔍 调试信息 - 详细参数
427
- console.log('📊 卖出详情:');
428
- console.log(` [基本信息]`);
429
- console.log(` - 链: ${chain}`);
430
- console.log(` - 代币地址: ${tokenAddress}`);
431
- console.log(` - PancakeProxy: ${pancakeProxyAddress}`);
432
- console.log(` - 卖家数量: ${sellers.length}`);
433
- console.log(` - 路由类型: ${routeType}`);
434
- console.log(`\n [卖家信息]`);
435
- sellers.forEach((seller, i) => {
436
- console.log(` - 钱包 ${i + 1}: ${seller.address}`);
437
- });
438
- console.log(`\n [卖出金额]`);
439
- sellAmounts.forEach((amount, i) => {
440
- console.log(` - 钱包 ${i + 1}: ${amount} TOKEN (${amountsWei[i].toString()} Wei)`);
441
- });
442
- // 计算 minOutputAmounts(通常是 BNB/WBNB,18 位)
418
+ // ✅ 自动获取报价或使用用户提供的 minOutputAmounts
443
419
  let minOuts;
420
+ let quotedOutputs;
444
421
  if (params.minOutputAmounts && params.minOutputAmounts.length === sellers.length) {
422
+ // 用户提供了 minOutputAmounts
445
423
  minOuts = params.minOutputAmounts.map(m => typeof m === 'string' ? ethers.parseEther(m) : m);
446
- console.log(`\n [最小输出]`);
447
- params.minOutputAmounts.forEach((min, i) => {
448
- console.log(` - 钱包 ${i + 1}: ${min} BNB`);
449
- });
424
+ quotedOutputs = minOuts.map(m => m * 100n / 95n); // 反推预期收益
450
425
  }
451
426
  else {
452
- minOuts = new Array(sellers.length).fill(0n);
453
- console.log(`\n [最小输出] 未设置(默认 0,无滑点保护)`);
454
- }
455
- console.log(`\n [路由信息]`);
456
- if (routeType === 'v2' && params.v2Path) {
457
- console.log(` - V2 路径: ${params.v2Path.join(' → ')}`);
458
- }
459
- else if (routeType === 'v3-single') {
460
- console.log(` - V3 单跳: ${tokenAddress} ${params.v3TokenOut}`);
461
- console.log(` - 费率: ${params.v3Fee}`);
462
- }
463
- else if (routeType === 'v3-multi') {
464
- console.log(` - V3 多跳 LP 数量: ${params.v3LpAddresses?.length}`);
465
- console.log(` - 起始代币: ${params.v3ExactTokenIn}`);
427
+ // 自动调用 PancakeSwap 获取报价
428
+ quotedOutputs = await Promise.all(amountsWei.map(async (amount) => {
429
+ try {
430
+ if (routeType === 'v2') {
431
+ const v2Router = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, provider);
432
+ const amounts = await v2Router.getAmountsOut(amount, params.v2Path);
433
+ return amounts[amounts.length - 1];
434
+ }
435
+ else if (routeType === 'v3-single') {
436
+ const quoter = new Contract(PANCAKE_V3_QUOTER_ADDRESS, PANCAKE_V3_QUOTER_ABI, provider);
437
+ const result = await quoter.quoteExactInputSingle.staticCall({
438
+ tokenIn: tokenAddress,
439
+ tokenOut: params.v3TokenOut,
440
+ amountIn: amount,
441
+ fee: params.v3Fee,
442
+ sqrtPriceLimitX96: 0
443
+ });
444
+ return result[0];
445
+ }
446
+ else {
447
+ // v3-multi 使用 v2 备选
448
+ if (params.v2Path && params.v2Path.length >= 2) {
449
+ const v2Router = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, provider);
450
+ const amounts = await v2Router.getAmountsOut(amount, params.v2Path);
451
+ return amounts[amounts.length - 1];
452
+ }
453
+ return 0n;
454
+ }
455
+ }
456
+ catch (error) {
457
+ return 0n;
458
+ }
459
+ }));
460
+ // minOuts 设为预期收益的 95%(滑点保护)
461
+ minOuts = quotedOutputs.map(q => q * 95n / 100n);
466
462
  }
467
463
  // 卖出不需要发送 BNB,只需要 flatFee
468
464
  const needBNB = false;
469
- console.log(`\n [交易费用]`);
470
- console.log(` - 需要发送 BNB: ${needBNB ? '是' : '否'}`);
471
- console.log(` - 每笔手续费: 0.0001 BNB`);
472
- console.log(` - 总手续费: ${ethers.formatEther(BigInt(sellers.length) * ethers.parseEther('0.0001'))} BNB`);
473
- console.log(`\n [Gas 配置]`);
474
- console.log(` - Gas Price: ${ethers.formatUnits(gasPrice, 'gwei')} Gwei`);
475
- console.log(` - Gas Limit: ${config.gasLimit || 'Auto (800000 * multiplier)'}`);
476
- if (config.gasLimitMultiplier) {
477
- console.log(` - Gas Multiplier: ${config.gasLimitMultiplier}x`);
478
- }
479
- console.log(`\n [Bundle 配置]`);
480
- console.log(` - Block Offset: ${blockOffset}`);
481
- console.log(` - Wait Confirmation: ${config.waitForConfirmation ?? false}`);
482
- console.log(` - Timeout: ${(config.waitTimeoutMs ?? 120000) / 1000}s\n`);
483
465
  // 构建交易
484
466
  const proxies = sellers.map(w => new Contract(pancakeProxyAddress, PANCAKE_PROXY_ABI, w));
485
467
  let unsignedSells;
@@ -505,20 +487,15 @@ export async function pancakeProxyBatchSellMerkle(params) {
505
487
  throw new Error(`Unsupported routeType: ${routeType}`);
506
488
  }
507
489
  // ✅ Step 4: 验证钱包余额(确保有足够 BNB 支付手续费)
508
- console.log('🔍 验证钱包余额...');
509
490
  const balances = await Promise.all(sellers.map(w => provider.getBalance(w.address)));
510
491
  const minRequiredBNB = ethers.parseEther('0.001'); // 至少需要 0.001 BNB(包括 gas 和手续费)
511
492
  const insufficientBalances = balances.filter(b => b < minRequiredBNB);
512
493
  if (insufficientBalances.length > 0) {
513
- console.warn(`⚠️ 警告: ${insufficientBalances.length} 个钱包 BNB 余额不足 0.001 BNB`);
514
- balances.forEach((b, i) => {
515
- console.log(` 钱包 ${i + 1}: ${ethers.formatEther(b)} BNB`);
516
- });
494
+ throw new Error(`${insufficientBalances.length} 个钱包 BNB 余额不足 0.001 BNB`);
517
495
  }
518
496
  // ✅ 使用前端传入的 gasLimit,否则使用默认值
519
497
  const finalGasLimit = getGasLimit(config);
520
498
  // ✅ 直接签名和提交(内联处理以支持利润转账)
521
- console.log('🚀 签名卖出交易...\n');
522
499
  const nonceManager = new NonceManager(provider);
523
500
  const nonces = await Promise.all(sellers.map(w => nonceManager.getNextNonce(w)));
524
501
  const signedTxs = await Promise.all(unsignedSells.map((unsigned, i) => sellers[i].signTransaction({
@@ -531,13 +508,12 @@ export async function pancakeProxyBatchSellMerkle(params) {
531
508
  type: getTxType(config),
532
509
  value: unsigned.value // ✅ 显式保留 value(手续费)
533
510
  })));
534
- // ✅ 基于 minFunds 为每个钱包添加利润转账
511
+ // ✅ 基于预期收益为每个钱包添加利润转账
535
512
  const extractProfit = shouldExtractProfit(config);
536
- if (extractProfit && minOuts.length > 0) {
537
- // 为每个钱包添加利润转账(如果 minOut > 0)
513
+ if (extractProfit && quotedOutputs.length > 0) {
538
514
  for (let i = 0; i < sellers.length; i++) {
539
- if (minOuts[i] > 0n) { // ⚠️ 保留这个判断:只对设置了 minOut 的钱包提取利润
540
- const { profit } = calculateProfit(minOuts[i], config);
515
+ if (quotedOutputs[i] > 0n) {
516
+ const { profit } = calculateProfit(quotedOutputs[i], config);
541
517
  if (profit > 0n) {
542
518
  const profitNonce = await nonceManager.getNextNonce(sellers[i]);
543
519
  const profitTx = await sellers[i].signTransaction({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",