four-flap-meme-sdk 1.4.72 → 1.4.73

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.
@@ -139,13 +139,16 @@ export async function fourBundleBuyFirstMerkle(params) {
139
139
  if (!estimatedTokenAmount || estimatedTokenAmount <= 0n) {
140
140
  throw new Error('报价失败:无法估算可买入的代币数量');
141
141
  }
142
- // ✅ 多笔买入时添加安全系数:减少 1% 卖出数量以应对报价误差/税/滑点
143
- // 原因:多笔买入会产生滑点累积,实际获得的代币可能少于报价
144
- // 单笔买入时不需要安全系数(与普通捆绑换手一致)
142
+ // ✅ 多笔交易时添加安全系数:减少卖出数量以应对报价误差/税/滑点
143
+ // 原因:多笔买入会产生滑点累积(每笔买入后 AMM 价格上涨),实际获得的代币少于报价
144
+ // 单笔买入+单笔卖出时不需要安全系数(与普通捆绑换手一致)
145
145
  let sellAmountWei = estimatedTokenAmount;
146
- if (buyCount > 1) {
147
- const SELL_SAFETY_BPS = 100n; // 1% = 100 bps(仅多笔时)
148
- sellAmountWei = estimatedTokenAmount * (10000n - SELL_SAFETY_BPS) / 10000n;
146
+ if (buyCount > 1 || sellCount > 1) {
147
+ // 根据买入笔数动态调整安全系数:2% + 每多一笔额外 0.5%
148
+ const baseSafetyBps = 200n; // 2% 基础安全系数
149
+ const extraSafetyBps = BigInt((buyCount - 1) * 50); // 每多一笔买入额外 0.5%
150
+ const totalSafetyBps = baseSafetyBps + extraSafetyBps;
151
+ sellAmountWei = estimatedTokenAmount * (10000n - totalSafetyBps) / 10000n;
149
152
  }
150
153
  // 卖方余额检查
151
154
  if (!sameAddress && sellerTokenBal < sellAmountWei) {
@@ -140,13 +140,16 @@ export async function pancakeBundleBuyFirstMerkle(params) {
140
140
  buyerFundsWei: buyerFundsInfo.buyerFundsWei,
141
141
  provider: context.provider
142
142
  });
143
- // ✅ 多笔买入时添加安全系数:减少 1% 卖出数量以应对报价误差/税/滑点
144
- // 原因:多笔买入会产生滑点累积,实际获得的代币可能少于报价
145
- // 单笔买入时不需要安全系数(与普通捆绑换手一致)
143
+ // ✅ 多笔交易时添加安全系数:减少卖出数量以应对报价误差/税/滑点
144
+ // 原因:多笔买入会产生滑点累积(每笔买入后 AMM 价格上涨),实际获得的代币少于报价
145
+ // 单笔买入+单笔卖出时不需要安全系数(与普通捆绑换手一致)
146
146
  let adjustedSellAmount = quoteResult.quotedTokenOut;
147
- if (buyCount > 1) {
148
- const SELL_SAFETY_BPS = 100n; // 1% = 100 bps(仅多笔时)
149
- adjustedSellAmount = quoteResult.quotedTokenOut * (10000n - SELL_SAFETY_BPS) / 10000n;
147
+ if (buyCount > 1 || sellCount > 1) {
148
+ // 根据买入笔数动态调整安全系数:2% + 每多一笔额外 0.5%
149
+ const baseSafetyBps = 200n; // 2% 基础安全系数
150
+ const extraSafetyBps = BigInt((buyCount - 1) * 50); // 每多一笔买入额外 0.5%
151
+ const totalSafetyBps = baseSafetyBps + extraSafetyBps;
152
+ adjustedSellAmount = quoteResult.quotedTokenOut * (10000n - totalSafetyBps) / 10000n;
150
153
  }
151
154
  // ✅ 拆分买入和卖出金额
152
155
  const buyAmountsWei = splitAmount(buyerFundsInfo.buyerFundsWei, buyCount);
@@ -409,26 +412,34 @@ function calculateBuyerNeed({ quotedNative, buyerBalance, reserveGas }) {
409
412
  }
410
413
  async function buildRouteTransactions({ routeParams, buyerFundsWei, sellAmountToken, buyer, seller, tokenAddress, useNativeToken = true }) {
411
414
  const deadline = getDeadline();
412
- // ✅ ERC20 购买时,value 只需要 FLAT_FEE
413
- const buyValue = useNativeToken ? buyerFundsWei + FLAT_FEE : FLAT_FEE;
414
415
  if (routeParams.routeType === 'v2') {
415
416
  const { v2Path } = routeParams;
416
417
  const reversePath = [...v2Path].reverse();
417
418
  // ✅ 使用官方 V2 Router
418
419
  const v2RouterBuyer = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, buyer);
419
420
  const v2RouterSeller = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, seller);
420
- // 买入:BNB → Token
421
- const buyUnsigned = await v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, v2Path, buyer.address, deadline, { value: buyValue });
422
- // 卖出:Token BNB
423
- const sellUnsigned = await v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmountToken, 0n, reversePath, seller.address, deadline);
424
- return { buyUnsigned, sellUnsigned };
421
+ if (useNativeToken) {
422
+ // BNB 池子:使用 swapExactETHForTokens / swapExactTokensForETH
423
+ const buyValue = buyerFundsWei + FLAT_FEE;
424
+ const buyUnsigned = await v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, v2Path, buyer.address, deadline, { value: buyValue });
425
+ const sellUnsigned = await v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmountToken, 0n, reversePath, seller.address, deadline);
426
+ return { buyUnsigned, sellUnsigned };
427
+ }
428
+ else {
429
+ // ✅ ERC20 池子(如 USDT):使用 swapExactTokensForTokens
430
+ const buyUnsigned = await v2RouterBuyer.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(buyerFundsWei, 0n, v2Path, buyer.address, deadline);
431
+ const sellUnsigned = await v2RouterSeller.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(sellAmountToken, 0n, reversePath, seller.address, deadline);
432
+ return { buyUnsigned, sellUnsigned };
433
+ }
425
434
  }
426
435
  if (routeParams.routeType === 'v3-single') {
427
436
  const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
428
437
  const v3RouterIface = new ethers.Interface(PANCAKE_V3_ROUTER_ABI);
429
438
  const v3RouterBuyer = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, buyer);
430
439
  const v3RouterSeller = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, seller);
431
- // 买入:WBNB Token
440
+ // V3 池子的处理
441
+ const buyValue = useNativeToken ? buyerFundsWei + FLAT_FEE : FLAT_FEE;
442
+ // 买入交易
432
443
  const buySwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
433
444
  tokenIn: v3TokenIn,
434
445
  tokenOut: v3TokenOut,
@@ -439,19 +450,36 @@ async function buildRouteTransactions({ routeParams, buyerFundsWei, sellAmountTo
439
450
  sqrtPriceLimitX96: 0n
440
451
  }]);
441
452
  const buyUnsigned = await v3RouterBuyer.multicall.populateTransaction(deadline, [buySwapData], { value: buyValue });
442
- // 卖出:Token → WBNB → BNB
443
- const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
444
- tokenIn: v3TokenOut,
445
- tokenOut: v3TokenIn,
446
- fee: v3Fee,
447
- recipient: PANCAKE_V3_ROUTER_ADDRESS,
448
- amountIn: sellAmountToken,
449
- amountOutMinimum: 0n,
450
- sqrtPriceLimitX96: 0n
451
- }]);
452
- const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
453
- const sellUnsigned = await v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData, sellUnwrapData]);
454
- return { buyUnsigned, sellUnsigned };
453
+ // 卖出交易
454
+ if (useNativeToken) {
455
+ // ✅ BNB 池子:卖出后 unwrap WBNB 为 BNB
456
+ const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
457
+ tokenIn: v3TokenOut,
458
+ tokenOut: v3TokenIn,
459
+ fee: v3Fee,
460
+ recipient: PANCAKE_V3_ROUTER_ADDRESS,
461
+ amountIn: sellAmountToken,
462
+ amountOutMinimum: 0n,
463
+ sqrtPriceLimitX96: 0n
464
+ }]);
465
+ const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
466
+ const sellUnsigned = await v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData, sellUnwrapData]);
467
+ return { buyUnsigned, sellUnsigned };
468
+ }
469
+ else {
470
+ // ✅ ERC20 池子:卖出后直接获得 ERC20
471
+ const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
472
+ tokenIn: v3TokenOut,
473
+ tokenOut: v3TokenIn,
474
+ fee: v3Fee,
475
+ recipient: seller.address, // 直接发送到卖方地址
476
+ amountIn: sellAmountToken,
477
+ amountOutMinimum: 0n,
478
+ sqrtPriceLimitX96: 0n
479
+ }]);
480
+ const sellUnsigned = await v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData]);
481
+ return { buyUnsigned, sellUnsigned };
482
+ }
455
483
  }
456
484
  // V3 多跳暂不支持官方合约
457
485
  throw new Error('V3 多跳路由暂不支持官方合约,请使用 V2 路由或 V3 单跳');
@@ -466,14 +494,22 @@ async function buildMultiRouteTransactions({ routeParams, buyAmountsWei, sellAmo
466
494
  const reversePath = [...v2Path].reverse();
467
495
  const v2RouterBuyer = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, buyer);
468
496
  const v2RouterSeller = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, seller);
469
- // 构建多笔买入交易
470
- const buyUnsignedArray = await Promise.all(buyAmountsWei.map(amount => {
471
- const buyValue = useNativeToken ? amount + FLAT_FEE : FLAT_FEE;
472
- return v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, v2Path, buyer.address, deadline, { value: buyValue });
473
- }));
474
- // 构建多笔卖出交易
475
- const sellUnsignedArray = await Promise.all(sellAmountsWei.map(amount => v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(amount, 0n, reversePath, seller.address, deadline)));
476
- return { buyUnsignedArray, sellUnsignedArray };
497
+ // ✅ 根据是否使用原生代币选择不同的函数
498
+ if (useNativeToken) {
499
+ // BNB 池子:使用 swapExactETHForTokens / swapExactTokensForETH
500
+ const buyUnsignedArray = await Promise.all(buyAmountsWei.map(amount => {
501
+ const buyValue = amount + FLAT_FEE;
502
+ return v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, v2Path, buyer.address, deadline, { value: buyValue });
503
+ }));
504
+ const sellUnsignedArray = await Promise.all(sellAmountsWei.map(amount => v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(amount, 0n, reversePath, seller.address, deadline)));
505
+ return { buyUnsignedArray, sellUnsignedArray };
506
+ }
507
+ else {
508
+ // ✅ ERC20 池子(如 USDT):使用 swapExactTokensForTokens
509
+ const buyUnsignedArray = await Promise.all(buyAmountsWei.map(amount => v2RouterBuyer.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(amount, 0n, v2Path, buyer.address, deadline)));
510
+ const sellUnsignedArray = await Promise.all(sellAmountsWei.map(amount => v2RouterSeller.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(amount, 0n, reversePath, seller.address, deadline)));
511
+ return { buyUnsignedArray, sellUnsignedArray };
512
+ }
477
513
  }
478
514
  if (routeParams.routeType === 'v3-single') {
479
515
  const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
@@ -494,21 +530,40 @@ async function buildMultiRouteTransactions({ routeParams, buyAmountsWei, sellAmo
494
530
  }]);
495
531
  return v3RouterBuyer.multicall.populateTransaction(deadline, [buySwapData], { value: buyValue });
496
532
  }));
497
- // 构建多笔卖出交易
498
- const sellUnsignedArray = await Promise.all(sellAmountsWei.map(amount => {
499
- const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
500
- tokenIn: v3TokenOut,
501
- tokenOut: v3TokenIn,
502
- fee: v3Fee,
503
- recipient: PANCAKE_V3_ROUTER_ADDRESS,
504
- amountIn: amount,
505
- amountOutMinimum: 0n,
506
- sqrtPriceLimitX96: 0n
507
- }]);
508
- const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
509
- return v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData, sellUnwrapData]);
510
- }));
511
- return { buyUnsignedArray, sellUnsignedArray };
533
+ // ✅ 根据是否使用原生代币构建不同的卖出交易
534
+ if (useNativeToken) {
535
+ // BNB 池子:卖出后 unwrap WBNB 为 BNB
536
+ const sellUnsignedArray = await Promise.all(sellAmountsWei.map(amount => {
537
+ const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
538
+ tokenIn: v3TokenOut,
539
+ tokenOut: v3TokenIn,
540
+ fee: v3Fee,
541
+ recipient: PANCAKE_V3_ROUTER_ADDRESS,
542
+ amountIn: amount,
543
+ amountOutMinimum: 0n,
544
+ sqrtPriceLimitX96: 0n
545
+ }]);
546
+ const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
547
+ return v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData, sellUnwrapData]);
548
+ }));
549
+ return { buyUnsignedArray, sellUnsignedArray };
550
+ }
551
+ else {
552
+ // ✅ ERC20 池子:卖出后直接获得 ERC20
553
+ const sellUnsignedArray = await Promise.all(sellAmountsWei.map(amount => {
554
+ const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
555
+ tokenIn: v3TokenOut,
556
+ tokenOut: v3TokenIn,
557
+ fee: v3Fee,
558
+ recipient: seller.address, // 直接发送到卖方地址
559
+ amountIn: amount,
560
+ amountOutMinimum: 0n,
561
+ sqrtPriceLimitX96: 0n
562
+ }]);
563
+ return v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData]);
564
+ }));
565
+ return { buyUnsignedArray, sellUnsignedArray };
566
+ }
512
567
  }
513
568
  throw new Error('V3 多跳路由暂不支持官方合约,请使用 V2 路由或 V3 单跳');
514
569
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.4.72",
3
+ "version": "1.4.73",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",