four-flap-meme-sdk 1.2.3 → 1.2.5

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.
@@ -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);
@@ -104,16 +104,22 @@ export async function createTokenWithBundleBuyMerkle(params) {
104
104
  const extractProfit = shouldExtractProfit(config);
105
105
  let totalProfit = 0n;
106
106
  let totalBuyAmount = 0n;
107
- // 计算每个买家的实际购买金额(扣除利润)
108
- const fundsList = buyAmounts.map((v) => {
107
+ // 计算每个买家的实际购买金额和利润
108
+ const fundsList = [];
109
+ const profitList = [];
110
+ buyAmounts.forEach((v) => {
109
111
  const originalAmount = ethers.parseEther(v);
110
112
  totalBuyAmount += originalAmount;
111
113
  if (extractProfit) {
112
114
  const { remaining, profit } = calculateProfit(originalAmount, config);
113
115
  totalProfit += profit;
114
- return remaining; // ✅ 扣除利润后的金额用于购买
116
+ fundsList.push(remaining); // ✅ 扣除利润后的金额用于购买
117
+ profitList.push(profit); // ✅ 记录每个买家的利润
118
+ }
119
+ else {
120
+ fundsList.push(originalAmount);
121
+ profitList.push(0n);
115
122
  }
116
- return originalAmount;
117
123
  });
118
124
  const minOuts = new Array(buyers.length).fill(0n);
119
125
  const buyPortals = buyers.map(w => new ethers.Contract(portalAddr, PORTAL_ABI, w));
@@ -141,19 +147,23 @@ export async function createTokenWithBundleBuyMerkle(params) {
141
147
  })));
142
148
  signedTxs.push(...signedBuys);
143
149
  buyTxs.push(...signedBuys);
144
- // ✅ 添加利润转账(汇总所有利润,由第一个买家转账)
150
+ // ✅ 添加利润转账(每个买家单独转自己的利润)
145
151
  if (extractProfit && totalProfit > 0n && buyers.length > 0) {
146
- const profitNonce = await nonceManager.getNextNonce(buyers[0]);
147
- const profitTx = await buyers[0].signTransaction({
148
- to: config.profitRecipient,
149
- value: totalProfit,
150
- nonce: profitNonce,
151
- gasPrice,
152
- gasLimit: 21000n,
153
- chainId,
154
- type: getTxType(config)
155
- });
156
- signedTxs.push(profitTx);
152
+ for (let i = 0; i < buyers.length; i++) {
153
+ if (profitList[i] > 0n) {
154
+ const profitNonce = await nonceManager.getNextNonce(buyers[i]);
155
+ const profitTx = await buyers[i].signTransaction({
156
+ to: config.profitRecipient,
157
+ value: profitList[i], // ✅ 每个买家转自己的利润
158
+ nonce: profitNonce,
159
+ gasPrice,
160
+ gasLimit: 21000n,
161
+ chainId,
162
+ type: getTxType(config)
163
+ });
164
+ signedTxs.push(profitTx);
165
+ }
166
+ }
157
167
  }
158
168
  // ✅ 清理临时 nonce 缓存
159
169
  nonceManager.clearTemp();
@@ -227,12 +227,7 @@ export async function approvePancakeProxyBatch(params) {
227
227
  // 只授权不足的
228
228
  const needApproval = wallets.filter((_, i) => allowances[i] < amountsBigInt[i]);
229
229
  const needApprovalAmounts = amountsBigInt.filter((amount, i) => allowances[i] < amount);
230
- console.log(`🔍 PancakeProxy 授权检查:`);
231
- console.log(` - 总钱包数: ${wallets.length}`);
232
- console.log(` - 需要授权: ${needApproval.length}`);
233
- console.log(` - 已有授权: ${wallets.length - needApproval.length}`);
234
230
  if (needApproval.length === 0) {
235
- console.log('✅ 所有钱包已授权,无需再次授权。');
236
231
  return {
237
232
  success: true,
238
233
  approvedCount: 0,
@@ -257,22 +252,17 @@ export async function approvePancakeProxyBatch(params) {
257
252
  type: getTxType(config)
258
253
  })));
259
254
  nonceManager.clearTemp();
260
- console.log(`✅ 已生成 ${signedTxs.length} 个授权交易签名`);
261
255
  // ✅ 内部提交到 Merkle
262
256
  const currentBlock = await provider.getBlockNumber();
263
257
  const targetBlock = currentBlock + blockOffset;
264
- console.log(`📤 正在提交授权交易到 Merkle...`);
265
258
  const bundleResult = await merkle.sendBundle({
266
259
  transactions: signedTxs,
267
260
  targetBlock: targetBlock
268
261
  });
269
- console.log(`✅ 授权交易已提交,bundleHash: ${bundleResult.bundleHash}`);
270
262
  // ✅ 等待授权确认
271
- console.log(`⏳ 等待授权交易确认(约5-10秒)...`);
272
263
  const confirmResults = await merkle.waitForBundleConfirmation(bundleResult.txHashes, 1, 60000);
273
264
  const successCount = confirmResults.filter(r => r.success).length;
274
265
  if (successCount > 0) {
275
- console.log(`✅ 授权已确认,共 ${successCount} 笔交易成功`);
276
266
  return {
277
267
  success: true,
278
268
  approvedCount: successCount,
@@ -281,7 +271,6 @@ export async function approvePancakeProxyBatch(params) {
281
271
  };
282
272
  }
283
273
  else {
284
- console.log(`⚠️ 授权交易未确认`);
285
274
  return {
286
275
  success: false,
287
276
  approvedCount: 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",