four-flap-meme-sdk 1.3.32 → 1.3.34

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,7 +15,8 @@ export declare const DIRECT_ROUTERS: {
15
15
  readonly WBNB: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c";
16
16
  };
17
17
  readonly MONAD: {
18
- readonly PANCAKESWAP_V2: "0xb1bc24c34e88f7d43d5923034e3a14b24daacff9";
18
+ readonly PANCAKESWAP_V2: "0xB1Bc24c34e88f7D43D5923034E3a14B24DaACfF9";
19
+ readonly PANCAKESWAP_V3: "0x1b81D678ffb9C0263b24A97847620C99d213eB14";
19
20
  readonly UNISWAP_V2: "0x4b2ab38dbf28d31d467aa8993f6c2585981d6804";
20
21
  readonly UNISWAP_V3: "0xd6145b2d3f379919e8cdeda7b97e37c4b2ca9c40";
21
22
  readonly WMON: "0x3bd359c1119da7da1d913d1c4d2b7c461115433a";
@@ -97,10 +98,18 @@ export declare function directV2BatchBuy(params: DirectV2BuyParams): Promise<Dir
97
98
  export declare function directV2BatchSell(params: DirectV2SellParams): Promise<DirectRouterResult>;
98
99
  /**
99
100
  * V3 批量买入(直接调用 Router)
101
+ *
102
+ * 自动识别 SwapRouter 版本:
103
+ * - SwapRouter02 (PancakeSwap V3): exactInputSingle 不含 deadline,multicall 含 deadline
104
+ * - SwapRouter (旧版 Uniswap V3): exactInputSingle 含 deadline,multicall 不含 deadline
100
105
  */
101
106
  export declare function directV3BatchBuy(params: DirectV3BuyParams): Promise<DirectRouterResult>;
102
107
  /**
103
108
  * V3 批量卖出(直接调用 Router)
109
+ *
110
+ * 自动识别 SwapRouter 版本:
111
+ * - SwapRouter02 (PancakeSwap V3): exactInputSingle 不含 deadline,multicall 含 deadline
112
+ * - SwapRouter (旧版 Uniswap V3): exactInputSingle 含 deadline,multicall 不含 deadline
104
113
  */
105
114
  export declare function directV3BatchSell(params: DirectV3SellParams): Promise<DirectRouterResult>;
106
115
  export type DexKey = 'PANCAKESWAP' | 'UNISWAP';
@@ -24,9 +24,13 @@ export const DIRECT_ROUTERS = {
24
24
  WBNB: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c',
25
25
  },
26
26
  MONAD: {
27
- PANCAKESWAP_V2: '0xb1bc24c34e88f7d43d5923034e3a14b24daacff9',
27
+ // PancakeSwap
28
+ PANCAKESWAP_V2: '0xB1Bc24c34e88f7D43D5923034E3a14B24DaACfF9',
29
+ PANCAKESWAP_V3: '0x1b81D678ffb9C0263b24A97847620C99d213eB14', // ✅ SwapRouter
30
+ // Uniswap
28
31
  UNISWAP_V2: '0x4b2ab38dbf28d31d467aa8993f6c2585981d6804',
29
32
  UNISWAP_V3: '0xd6145b2d3f379919e8cdeda7b97e37c4b2ca9c40',
33
+ // Wrapped Native
30
34
  WMON: '0x3bd359c1119da7da1d913d1c4d2b7c461115433a',
31
35
  }
32
36
  };
@@ -61,16 +65,97 @@ const V2_ROUTER_ABI = [
61
65
  // 报价
62
66
  'function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)',
63
67
  ];
64
- /** V3 SwapRouter ABI */
65
- const V3_ROUTER_ABI = [
66
- // 单跳
67
- 'function exactInputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96)) external payable returns (uint256 amountOut)',
68
- // 多跳
69
- 'function exactInput((bytes path, address recipient, uint256 amountIn, uint256 amountOutMinimum)) external payable returns (uint256 amountOut)',
70
- // Multicall (用于 refundETH)
68
+ /**
69
+ * V3 SwapRouter02 ABI (PancakeSwap V3, 新版 Uniswap)
70
+ * - exactInputSingle: 不含 deadline(deadline 通过 multicall 传递)
71
+ * - multicall: 有两个版本,推荐使用带 deadline 的版本
72
+ */
73
+ const V3_ROUTER02_ABI = [
74
+ // SwapRouter02 exactInputSingle - 不含 deadline
75
+ {
76
+ "inputs": [{
77
+ "components": [
78
+ { "name": "tokenIn", "type": "address" },
79
+ { "name": "tokenOut", "type": "address" },
80
+ { "name": "fee", "type": "uint24" },
81
+ { "name": "recipient", "type": "address" },
82
+ { "name": "amountIn", "type": "uint256" },
83
+ { "name": "amountOutMinimum", "type": "uint256" },
84
+ { "name": "sqrtPriceLimitX96", "type": "uint160" }
85
+ ],
86
+ "name": "params",
87
+ "type": "tuple"
88
+ }],
89
+ "name": "exactInputSingle",
90
+ "outputs": [{ "name": "amountOut", "type": "uint256" }],
91
+ "stateMutability": "payable",
92
+ "type": "function"
93
+ },
94
+ // Multicall - 带 deadline 版本 (SwapRouter02)
95
+ 'function multicall(uint256 deadline, bytes[] calldata data) external payable returns (bytes[] memory results)',
96
+ 'function refundETH() external payable',
97
+ 'function unwrapWETH9(uint256 amountMinimum, address recipient) external payable',
98
+ ];
99
+ /**
100
+ * V3 SwapRouter(旧版)ABI (Monad Uniswap V3)
101
+ * - exactInputSingle: 包含 deadline 在 struct 内部
102
+ * - multicall: 只有 bytes[] 版本,不带 deadline
103
+ */
104
+ const V3_ROUTER_LEGACY_ABI = [
105
+ // 旧版 SwapRouter 的 exactInputSingle - 包含 deadline
106
+ {
107
+ "inputs": [{
108
+ "components": [
109
+ { "name": "tokenIn", "type": "address" },
110
+ { "name": "tokenOut", "type": "address" },
111
+ { "name": "fee", "type": "uint24" },
112
+ { "name": "recipient", "type": "address" },
113
+ { "name": "deadline", "type": "uint256" }, // ✅ 旧版包含 deadline
114
+ { "name": "amountIn", "type": "uint256" },
115
+ { "name": "amountOutMinimum", "type": "uint256" },
116
+ { "name": "sqrtPriceLimitX96", "type": "uint160" }
117
+ ],
118
+ "name": "params",
119
+ "type": "tuple"
120
+ }],
121
+ "name": "exactInputSingle",
122
+ "outputs": [{ "name": "amountOut", "type": "uint256" }],
123
+ "stateMutability": "payable",
124
+ "type": "function"
125
+ },
126
+ // 旧版 Multicall - 只有 bytes[] 版本,不带 deadline
71
127
  'function multicall(bytes[] calldata data) external payable returns (bytes[] memory results)',
72
128
  'function refundETH() external payable',
129
+ 'function unwrapWETH9(uint256 amountMinimum, address recipient) external payable',
73
130
  ];
131
+ /**
132
+ * 判断是否使用旧版 SwapRouter
133
+ *
134
+ * 旧版特征:
135
+ * - exactInputSingle 包含 deadline 字段
136
+ * - multicall 只有 bytes[] 参数
137
+ *
138
+ * Monad 上的 V3 DEX 都使用旧版:
139
+ * - Uniswap V3: 0xd6145b2d3f379919e8cdeda7b97e37c4b2ca9c40
140
+ * - PancakeSwap V3: 0x1b81D678ffb9C0263b24A97847620C99d213eB14
141
+ *
142
+ * BSC PancakeSwap V3 使用新版 SwapRouter02
143
+ */
144
+ function isLegacySwapRouter(chain, routerAddress) {
145
+ const chainUpper = chain.toUpperCase();
146
+ const routerLower = routerAddress.toLowerCase();
147
+ // ✅ Monad 上的所有 V3 Router 都是旧版
148
+ if (chainUpper === 'MONAD') {
149
+ if (routerLower === DIRECT_ROUTERS.MONAD.UNISWAP_V3.toLowerCase()) {
150
+ return true;
151
+ }
152
+ if (routerLower === DIRECT_ROUTERS.MONAD.PANCAKESWAP_V3.toLowerCase()) {
153
+ return true;
154
+ }
155
+ }
156
+ // BSC PancakeSwap V3 是 SwapRouter02(新版)
157
+ return false;
158
+ }
74
159
  /** ERC20 ABI */
75
160
  const ERC20_ABI = [
76
161
  'function approve(address spender, uint256 amount) external returns (bool)',
@@ -346,6 +431,10 @@ export async function directV2BatchSell(params) {
346
431
  // ============================================================================
347
432
  /**
348
433
  * V3 批量买入(直接调用 Router)
434
+ *
435
+ * 自动识别 SwapRouter 版本:
436
+ * - SwapRouter02 (PancakeSwap V3): exactInputSingle 不含 deadline,multicall 含 deadline
437
+ * - SwapRouter (旧版 Uniswap V3): exactInputSingle 含 deadline,multicall 不含 deadline
349
438
  */
350
439
  export async function directV3BatchBuy(params) {
351
440
  const { chain, privateKeys, buyAmounts, tokenAddress, routerAddress, fee, quoteToken, quoteTokenDecimals = 18, config, } = params;
@@ -356,42 +445,73 @@ export async function directV3BatchBuy(params) {
356
445
  const provider = new JsonRpcProvider(config.rpcUrl, { chainId, name: chain });
357
446
  const useNative = isNativeToken(quoteToken);
358
447
  const wrappedNative = getWrappedNative(chain);
448
+ // ✅ 判断是否使用旧版 SwapRouter(Monad Uniswap V3)
449
+ const useLegacyRouter = isLegacySwapRouter(chain, routerAddress);
450
+ const routerAbi = useLegacyRouter ? V3_ROUTER_LEGACY_ABI : V3_ROUTER02_ABI;
451
+ console.log(`[V3 Buy] Router: ${routerAddress}, Legacy: ${useLegacyRouter}`);
359
452
  const wallets = privateKeys.map(pk => new Wallet(pk, provider));
360
453
  const nonceManager = new NonceManager(provider);
361
454
  const nonces = await nonceManager.getNextNoncesForWallets(wallets);
362
455
  const gasPrice = await getGasPrice(provider, config);
363
456
  const gasLimit = getGasLimit(config, 300000);
364
457
  const txType = config.txType ?? 0;
365
- const routerIface = new ethers.Interface(V3_ROUTER_ABI);
458
+ const routerIface = new ethers.Interface(routerAbi);
366
459
  const inputToken = useNative ? wrappedNative : quoteToken;
367
460
  const signedTxs = [];
368
461
  let totalFlowWei = 0n;
462
+ const deadline = getDeadline();
369
463
  for (let i = 0; i < wallets.length; i++) {
370
464
  const wallet = wallets[i];
371
465
  const amountWei = ethers.parseUnits(buyAmounts[i], quoteTokenDecimals);
372
466
  totalFlowWei += amountWei;
373
- // V3 exactInputSingle 参数
374
- const swapParams = {
375
- tokenIn: inputToken,
376
- tokenOut: tokenAddress,
377
- fee: fee,
378
- recipient: wallet.address,
379
- amountIn: amountWei,
380
- amountOutMinimum: 0n,
381
- sqrtPriceLimitX96: 0n,
382
- };
383
467
  let txData;
384
468
  let txValue;
385
- if (useNative) {
386
- // 使用 multicall 包装 exactInputSingle + refundETH
387
- const swapData = routerIface.encodeFunctionData('exactInputSingle', [swapParams]);
388
- const refundData = routerIface.encodeFunctionData('refundETH', []);
389
- txData = routerIface.encodeFunctionData('multicall', [[swapData, refundData]]);
390
- txValue = amountWei;
469
+ if (useLegacyRouter) {
470
+ // 旧版 SwapRouter: exactInputSingle 包含 deadline
471
+ const swapParams = {
472
+ tokenIn: inputToken,
473
+ tokenOut: tokenAddress,
474
+ fee: fee,
475
+ recipient: wallet.address,
476
+ deadline: deadline, // ✅ 旧版在 struct 内部
477
+ amountIn: amountWei,
478
+ amountOutMinimum: 0n, // 设为 0,依赖 MEV 保护
479
+ sqrtPriceLimitX96: 0n,
480
+ };
481
+ if (useNative) {
482
+ // 旧版 multicall 不带 deadline
483
+ const swapData = routerIface.encodeFunctionData('exactInputSingle', [swapParams]);
484
+ const refundData = routerIface.encodeFunctionData('refundETH', []);
485
+ txData = routerIface.encodeFunctionData('multicall(bytes[])', [[swapData, refundData]]);
486
+ txValue = amountWei;
487
+ }
488
+ else {
489
+ txData = routerIface.encodeFunctionData('exactInputSingle', [swapParams]);
490
+ txValue = 0n;
491
+ }
391
492
  }
392
493
  else {
393
- txData = routerIface.encodeFunctionData('exactInputSingle', [swapParams]);
394
- txValue = 0n;
494
+ // SwapRouter02: exactInputSingle 不含 deadline
495
+ const swapParams = {
496
+ tokenIn: inputToken,
497
+ tokenOut: tokenAddress,
498
+ fee: fee,
499
+ recipient: wallet.address,
500
+ amountIn: amountWei,
501
+ amountOutMinimum: 0n,
502
+ sqrtPriceLimitX96: 0n,
503
+ };
504
+ if (useNative) {
505
+ // SwapRouter02 的 multicall 带 deadline
506
+ const swapData = routerIface.encodeFunctionData('exactInputSingle', [swapParams]);
507
+ const refundData = routerIface.encodeFunctionData('refundETH', []);
508
+ txData = routerIface.encodeFunctionData('multicall(uint256,bytes[])', [deadline, [swapData, refundData]]);
509
+ txValue = amountWei;
510
+ }
511
+ else {
512
+ txData = routerIface.encodeFunctionData('exactInputSingle', [swapParams]);
513
+ txValue = 0n;
514
+ }
395
515
  }
396
516
  const tx = {
397
517
  to: routerAddress,
@@ -422,6 +542,10 @@ export async function directV3BatchBuy(params) {
422
542
  }
423
543
  /**
424
544
  * V3 批量卖出(直接调用 Router)
545
+ *
546
+ * 自动识别 SwapRouter 版本:
547
+ * - SwapRouter02 (PancakeSwap V3): exactInputSingle 不含 deadline,multicall 含 deadline
548
+ * - SwapRouter (旧版 Uniswap V3): exactInputSingle 含 deadline,multicall 不含 deadline
425
549
  */
426
550
  export async function directV3BatchSell(params) {
427
551
  const { chain, privateKeys, sellPercentages, sellAmounts, tokenAddress, tokenDecimals = 18, routerAddress, fee, quoteToken, config, } = params;
@@ -429,6 +553,10 @@ export async function directV3BatchSell(params) {
429
553
  const provider = new JsonRpcProvider(config.rpcUrl, { chainId, name: chain });
430
554
  const useNativeOutput = isNativeToken(quoteToken);
431
555
  const wrappedNative = getWrappedNative(chain);
556
+ // ✅ 判断是否使用旧版 SwapRouter(Monad Uniswap V3)
557
+ const useLegacyRouter = isLegacySwapRouter(chain, routerAddress);
558
+ const routerAbi = useLegacyRouter ? V3_ROUTER_LEGACY_ABI : V3_ROUTER02_ABI;
559
+ console.log(`[V3 Sell] Router: ${routerAddress}, Legacy: ${useLegacyRouter}`);
432
560
  const wallets = privateKeys.map(pk => new Wallet(pk, provider));
433
561
  const tokenContract = new Contract(tokenAddress, ERC20_ABI, provider);
434
562
  // 获取余额
@@ -452,12 +580,13 @@ export async function directV3BatchSell(params) {
452
580
  const gasPrice = await getGasPrice(provider, config);
453
581
  const gasLimit = getGasLimit(config, 350000);
454
582
  const txType = config.txType ?? 0;
455
- const routerIface = new ethers.Interface(V3_ROUTER_ABI);
583
+ const routerIface = new ethers.Interface(routerAbi);
456
584
  const approveIface = new ethers.Interface(ERC20_ABI);
457
585
  const outputToken = useNativeOutput ? wrappedNative : quoteToken;
458
586
  const signedTxs = [];
459
587
  let totalOutputEstimate = 0n;
460
588
  const currentNonceOffset = new Array(wallets.length).fill(0);
589
+ const deadline = getDeadline();
461
590
  for (let i = 0; i < wallets.length; i++) {
462
591
  const wallet = wallets[i];
463
592
  const sellAmount = sellAmountsWei[i];
@@ -481,25 +610,49 @@ export async function directV3BatchSell(params) {
481
610
  currentNonceOffset[i]++;
482
611
  }
483
612
  }
484
- // V3 卖出参数
485
- const swapParams = {
486
- tokenIn: tokenAddress,
487
- tokenOut: outputToken,
488
- fee: fee,
489
- recipient: useNativeOutput ? routerAddress : wallet.address, // 如果输出是原生币,先发到 router
490
- amountIn: sellAmount,
491
- amountOutMinimum: 0n,
492
- sqrtPriceLimitX96: 0n,
493
- };
494
613
  let txData;
495
- if (useNativeOutput) {
496
- // 卖出后 unwrap WETH 并发送给用户
497
- const swapData = routerIface.encodeFunctionData('exactInputSingle', [swapParams]);
498
- // 注意:V3 Router 的 unwrapWETH9 需要额外处理,这里简化使用 multicall
499
- txData = swapData; // 实际项目中可能需要 multicall 包装 unwrapWETH9
614
+ if (useLegacyRouter) {
615
+ // 旧版 SwapRouter: exactInputSingle 包含 deadline
616
+ const swapParams = {
617
+ tokenIn: tokenAddress,
618
+ tokenOut: outputToken,
619
+ fee: fee,
620
+ recipient: useNativeOutput ? routerAddress : wallet.address,
621
+ deadline: deadline, // ✅ 旧版在 struct 内部
622
+ amountIn: sellAmount,
623
+ amountOutMinimum: 0n, // 设为 0,依赖 MEV 保护
624
+ sqrtPriceLimitX96: 0n,
625
+ };
626
+ if (useNativeOutput) {
627
+ // 旧版 multicall 不带 deadline
628
+ const swapData = routerIface.encodeFunctionData('exactInputSingle', [swapParams]);
629
+ const unwrapData = routerIface.encodeFunctionData('unwrapWETH9', [0n, wallet.address]);
630
+ txData = routerIface.encodeFunctionData('multicall(bytes[])', [[swapData, unwrapData]]);
631
+ }
632
+ else {
633
+ txData = routerIface.encodeFunctionData('exactInputSingle', [swapParams]);
634
+ }
500
635
  }
501
636
  else {
502
- txData = routerIface.encodeFunctionData('exactInputSingle', [swapParams]);
637
+ // SwapRouter02: exactInputSingle 不含 deadline
638
+ const swapParams = {
639
+ tokenIn: tokenAddress,
640
+ tokenOut: outputToken,
641
+ fee: fee,
642
+ recipient: useNativeOutput ? routerAddress : wallet.address,
643
+ amountIn: sellAmount,
644
+ amountOutMinimum: 0n,
645
+ sqrtPriceLimitX96: 0n,
646
+ };
647
+ if (useNativeOutput) {
648
+ // SwapRouter02 的 multicall 带 deadline
649
+ const swapData = routerIface.encodeFunctionData('exactInputSingle', [swapParams]);
650
+ const unwrapData = routerIface.encodeFunctionData('unwrapWETH9', [0n, wallet.address]);
651
+ txData = routerIface.encodeFunctionData('multicall(uint256,bytes[])', [deadline, [swapData, unwrapData]]);
652
+ }
653
+ else {
654
+ txData = routerIface.encodeFunctionData('exactInputSingle', [swapParams]);
655
+ }
503
656
  }
504
657
  const sellTx = {
505
658
  to: routerAddress,
@@ -547,8 +700,10 @@ export function getRouterAddress(chain, dexKey, version) {
547
700
  return DIRECT_ROUTERS.MONAD.UNISWAP_V2;
548
701
  return DIRECT_ROUTERS.MONAD.UNISWAP_V3;
549
702
  }
550
- // PancakeSwap on Monad
551
- return DIRECT_ROUTERS.MONAD.PANCAKESWAP_V2;
703
+ // PancakeSwap on Monad - 支持 V2 和 V3
704
+ if (version === 'v2')
705
+ return DIRECT_ROUTERS.MONAD.PANCAKESWAP_V2;
706
+ return DIRECT_ROUTERS.MONAD.PANCAKESWAP_V3;
552
707
  }
553
708
  throw new Error(`Unsupported chain/dex combination: ${chain}/${dexKey}`);
554
709
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.3.32",
3
+ "version": "1.3.34",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",