four-flap-meme-sdk 1.4.83 → 1.4.84

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.
@@ -12,6 +12,19 @@ import { NonceManager, getOptimizedGasPrice, buildProfitHopTransactions, PROFIT_
12
12
  import { FLAP_PORTAL_ADDRESSES, FLAP_ORIGINAL_PORTAL_ADDRESSES } from '../constants.js';
13
13
  import { PROFIT_CONFIG, ADDRESSES, ZERO_ADDRESS } from '../../utils/constants.js';
14
14
  import { getGasPriceConfig, getTxType, getProfitRecipient, getBribeAmount, BLOCKRAZOR_BUILDER_EOA, PORTAL_ABI } from './config.js';
15
+ // ==================== ERC20 ABI ====================
16
+ const ERC20_ABI = [
17
+ {
18
+ type: "function",
19
+ name: "approve",
20
+ inputs: [
21
+ { name: "spender", type: "address" },
22
+ { name: "amount", type: "uint256" }
23
+ ],
24
+ outputs: [{ name: "", type: "bool" }],
25
+ stateMutability: "nonpayable"
26
+ }
27
+ ];
15
28
  // ==================== 链常量 ====================
16
29
  const BSC_PANCAKE_V2_ROUTER = ADDRESSES.BSC.PancakeV2Router;
17
30
  const BSC_PANCAKE_V3_ROUTER = ADDRESSES.BSC.PancakeV3Router;
@@ -57,9 +70,36 @@ const PANCAKE_V3_ROUTER_ABI = [
57
70
  ],
58
71
  "outputs": [{ "name": "amountOut", "type": "uint256" }],
59
72
  "stateMutability": "payable"
60
- }
73
+ },
61
74
  ];
75
+ // ==================== V3 费率配置 ====================
76
+ // USD1/USDT 使用 100 bps (0.01%),其他使用 2500 bps (0.25%)
77
+ const USD1_V3_FEE = 100;
78
+ const DEFAULT_V3_FEE = 2500;
62
79
  // ==================== 工具函数 ====================
80
+ /** 构建 ERC20 approve 交易 */
81
+ async function buildApproveTransaction(wallet, tokenAddress, spenderAddress, amount, nonce, gasPrice, txType) {
82
+ const erc20Interface = new ethers.Interface(ERC20_ABI);
83
+ const data = erc20Interface.encodeFunctionData('approve', [spenderAddress, amount]);
84
+ const tx = {
85
+ to: tokenAddress,
86
+ data,
87
+ from: wallet.address,
88
+ nonce,
89
+ gasLimit: BigInt(100000), // approve 交易 gas 较低
90
+ chainId: 56,
91
+ type: txType
92
+ };
93
+ if (txType === 2) {
94
+ const priorityFee = gasPrice / 10n === 0n ? 1n : gasPrice / 10n;
95
+ tx.maxFeePerGas = gasPrice;
96
+ tx.maxPriorityFeePerGas = priorityFee;
97
+ }
98
+ else {
99
+ tx.gasPrice = gasPrice;
100
+ }
101
+ return wallet.signTransaction(tx);
102
+ }
63
103
  function getGasLimit(config, defaultGas = 800000) {
64
104
  if (config.gasLimit !== undefined) {
65
105
  return typeof config.gasLimit === 'bigint' ? config.gasLimit : BigInt(config.gasLimit);
@@ -232,17 +272,27 @@ export async function flapBundleCreateToDex(params) {
232
272
  }
233
273
  // 3. 内盘买入交易(包含毕业触发)
234
274
  // 最后一个买入交易会触发毕业,需要更多 gas
275
+ // ✅ ERC20 需要先 approve,再买入
235
276
  const curveBuyTxPromises = curveWallets.map(async ({ wallet }, i) => {
236
277
  const addr = wallet.address.toLowerCase();
237
- const nonce = noncesMap.get(addr);
238
- noncesMap.set(addr, nonce + 1);
278
+ const signedTxs = [];
279
+ // ERC20: 先构建 approve 交易
280
+ if (!useNativeToken) {
281
+ const approveNonce = noncesMap.get(addr);
282
+ noncesMap.set(addr, approveNonce + 1);
283
+ const approveTx = await buildApproveTransaction(wallet, quoteToken, portalAddress, curveBuyAmounts[i], approveNonce, gasPrice, txType);
284
+ signedTxs.push(approveTx);
285
+ }
286
+ // 构建买入交易
287
+ const buyNonce = noncesMap.get(addr);
288
+ noncesMap.set(addr, buyNonce + 1);
239
289
  const portal = new Contract(portalAddress, PORTAL_ABI, wallet);
240
290
  const unsigned = await portal.swapExactInput.populateTransaction({
241
291
  inputToken,
242
292
  outputToken: tokenAddress,
243
293
  inputAmount: curveBuyAmounts[i],
244
294
  minOutputAmount: 0n,
245
- permitData: '0x'
295
+ permitData: '0x' // 不使用 permit,使用链上 approve
246
296
  }, useNativeToken ? { value: curveBuyAmounts[i] } : {});
247
297
  // 最后一个买家触发毕业,需要更多 gas
248
298
  const isLastBuyer = i === curveWallets.length - 1;
@@ -250,7 +300,7 @@ export async function flapBundleCreateToDex(params) {
250
300
  const tx = {
251
301
  ...unsigned,
252
302
  from: wallet.address,
253
- nonce,
303
+ nonce: buyNonce,
254
304
  gasLimit: buyGasLimit,
255
305
  chainId: 56,
256
306
  type: txType,
@@ -263,67 +313,63 @@ export async function flapBundleCreateToDex(params) {
263
313
  else {
264
314
  tx.gasPrice = gasPrice;
265
315
  }
266
- return wallet.signTransaction(tx);
316
+ const signedBuy = await wallet.signTransaction(tx);
317
+ signedTxs.push(signedBuy);
318
+ return signedTxs;
267
319
  });
268
- const signedCurveBuys = await Promise.all(curveBuyTxPromises);
269
- allTransactions.push(...signedCurveBuys);
320
+ const curveBuyResults = await Promise.all(curveBuyTxPromises);
321
+ // 展平数组:每个钱包可能有 [approve, buy] 或只有 [buy]
322
+ curveBuyResults.forEach(txs => allTransactions.push(...txs));
270
323
  // 4. 外盘买入交易(PancakeSwap)
324
+ // ✅ ERC20 需要先 approve,再 swap
271
325
  if (enableDexBuy && dexWallets.length > 0) {
272
326
  const dexBuyTxPromises = dexWallets.map(async ({ wallet }, i) => {
273
327
  const addr = wallet.address.toLowerCase();
274
- const nonce = noncesMap.get(addr);
275
- noncesMap.set(addr, nonce + 1);
328
+ const signedTxs = [];
276
329
  const buyAmount = dexBuyAmounts[i];
277
330
  const smartRouter = dexPoolType === 'v3' ? BSC_PANCAKE_V3_ROUTER : BSC_PANCAKE_V2_ROUTER;
278
- let calldata;
331
+ // ✅ 根据 quoteToken 选择 V3 费率
332
+ const actualV3Fee = v3Fee || (useNativeToken ? DEFAULT_V3_FEE : USD1_V3_FEE);
333
+ // ✅ ERC20: 先构建 approve 交易
334
+ if (!useNativeToken) {
335
+ const approveNonce = noncesMap.get(addr);
336
+ noncesMap.set(addr, approveNonce + 1);
337
+ const approveTx = await buildApproveTransaction(wallet, quoteToken, smartRouter, buyAmount, approveNonce, gasPrice, txType);
338
+ signedTxs.push(approveTx);
339
+ }
340
+ // 构建买入交易
341
+ const buyNonce = noncesMap.get(addr);
342
+ noncesMap.set(addr, buyNonce + 1);
343
+ let multicallData = [];
279
344
  let value;
345
+ const routerInterface = new ethers.Interface(PANCAKE_V3_ROUTER_ABI);
280
346
  if (dexPoolType === 'v3') {
281
- if (useNativeToken) {
282
- const params = {
283
- tokenIn: BSC_WBNB,
284
- tokenOut: tokenAddress,
285
- fee: v3Fee,
286
- recipient: wallet.address,
287
- amountIn: buyAmount,
288
- amountOutMinimum: 0n,
289
- sqrtPriceLimitX96: 0n
290
- };
291
- const exactInputSingleData = new ethers.Interface(PANCAKE_V3_ROUTER_ABI).encodeFunctionData('exactInputSingle', [params]);
292
- calldata = new ethers.Interface(PANCAKE_V3_ROUTER_ABI).encodeFunctionData('multicall', [[exactInputSingleData]]);
293
- value = buyAmount;
294
- }
295
- else {
296
- const params = {
297
- tokenIn: quoteToken,
298
- tokenOut: tokenAddress,
299
- fee: v3Fee,
300
- recipient: wallet.address,
301
- amountIn: buyAmount,
302
- amountOutMinimum: 0n,
303
- sqrtPriceLimitX96: 0n
304
- };
305
- const exactInputSingleData = new ethers.Interface(PANCAKE_V3_ROUTER_ABI).encodeFunctionData('exactInputSingle', [params]);
306
- calldata = new ethers.Interface(PANCAKE_V3_ROUTER_ABI).encodeFunctionData('multicall', [[exactInputSingleData]]);
307
- value = 0n;
308
- }
347
+ // V3: exactInputSingle
348
+ const params = {
349
+ tokenIn: useNativeToken ? BSC_WBNB : quoteToken,
350
+ tokenOut: tokenAddress,
351
+ fee: actualV3Fee,
352
+ recipient: wallet.address,
353
+ amountIn: buyAmount,
354
+ amountOutMinimum: 0n,
355
+ sqrtPriceLimitX96: 0n
356
+ };
357
+ multicallData = [routerInterface.encodeFunctionData('exactInputSingle', [params])];
358
+ value = useNativeToken ? buyAmount : 0n;
309
359
  }
310
360
  else {
311
- if (useNativeToken) {
312
- const swapData = new ethers.Interface(PANCAKE_V3_ROUTER_ABI).encodeFunctionData('swapExactTokensForTokens', [buyAmount, 0n, [BSC_WBNB, tokenAddress], wallet.address]);
313
- calldata = new ethers.Interface(PANCAKE_V3_ROUTER_ABI).encodeFunctionData('multicall', [[swapData]]);
314
- value = buyAmount;
315
- }
316
- else {
317
- const swapData = new ethers.Interface(PANCAKE_V3_ROUTER_ABI).encodeFunctionData('swapExactTokensForTokens', [buyAmount, 0n, [quoteToken, tokenAddress], wallet.address]);
318
- calldata = new ethers.Interface(PANCAKE_V3_ROUTER_ABI).encodeFunctionData('multicall', [[swapData]]);
319
- value = 0n;
320
- }
361
+ // V2: swapExactTokensForTokens
362
+ const path = useNativeToken ? [BSC_WBNB, tokenAddress] : [quoteToken, tokenAddress];
363
+ const swapData = routerInterface.encodeFunctionData('swapExactTokensForTokens', [buyAmount, 0n, path, wallet.address]);
364
+ multicallData = [swapData];
365
+ value = useNativeToken ? buyAmount : 0n;
321
366
  }
367
+ const calldata = routerInterface.encodeFunctionData('multicall', [multicallData]);
322
368
  const tx = {
323
369
  to: smartRouter,
324
370
  data: calldata,
325
371
  from: wallet.address,
326
- nonce,
372
+ nonce: buyNonce,
327
373
  gasLimit: BigInt(500000),
328
374
  chainId: 56,
329
375
  type: txType,
@@ -336,10 +382,13 @@ export async function flapBundleCreateToDex(params) {
336
382
  else {
337
383
  tx.gasPrice = gasPrice;
338
384
  }
339
- return wallet.signTransaction(tx);
385
+ const signedBuy = await wallet.signTransaction(tx);
386
+ signedTxs.push(signedBuy);
387
+ return signedTxs;
340
388
  });
341
- const signedDexBuys = await Promise.all(dexBuyTxPromises);
342
- allTransactions.push(...signedDexBuys);
389
+ const dexBuyResults = await Promise.all(dexBuyTxPromises);
390
+ // 展平数组:每个钱包可能有 [approve, buy] 或只有 [buy]
391
+ dexBuyResults.forEach(txs => allTransactions.push(...txs));
343
392
  }
344
393
  // 5. 利润多跳转账
345
394
  let profitHopWallets;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.4.83",
3
+ "version": "1.4.84",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",