four-flap-meme-sdk 1.3.12 → 1.3.13

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.
@@ -1,8 +1,12 @@
1
- import { ethers, Wallet } from 'ethers';
1
+ import { ethers, Wallet, Contract, Interface } from 'ethers';
2
2
  import { NonceManager, getOptimizedGasPrice } from '../../utils/bundle-helpers.js';
3
3
  import { FLAP_PORTAL_ADDRESSES, FLAP_ORIGINAL_PORTAL_ADDRESSES } from '../constants.js';
4
4
  import { CHAIN_ID_MAP, PORTAL_ABI, getErrorMessage, getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, getProfitRecipient } from './config.js';
5
5
  const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
6
+ const MULTICALL3_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11';
7
+ const MULTICALL3_ABI = [
8
+ 'function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) external payable returns (tuple(bool success, bytes returnData)[])'
9
+ ];
6
10
  /**
7
11
  * 获取 Gas Limit
8
12
  * 优先使用 config.gasLimit,否则使用默认值 * multiplier
@@ -233,11 +237,11 @@ export async function batchSellWithBundleMerkle(params) {
233
237
  const portals = wallets.map(w => new ethers.Contract(portalAddr, PORTAL_ABI, w));
234
238
  // ✅ 确定 outputToken:如果传入了 quoteToken 且非零地址,则使用它;否则使用零地址(原生代币)
235
239
  const outputToken = quoteToken && quoteToken !== ZERO_ADDRESS ? quoteToken : ZERO_ADDRESS;
236
- // ✅ 优化:并行执行 gasPrice、quoteSellOutputs nonces(三个最耗时的 RPC 操作)
240
+ // ✅ 优化:并行执行 gasPrice、quoteSellOutputs(Multicall3)和 nonces(批量获取)
237
241
  const [gasPrice, quotedOutputs, nonces] = await Promise.all([
238
242
  resolveGasPrice(provider, config),
239
243
  quoteSellOutputsWithQuote(readOnlyPortal, tokenAddress, amountsWei, outputToken),
240
- Promise.all(wallets.map(w => nonceManager.getNextNonce(w)))
244
+ nonceManager.getNextNoncesForWallets(wallets) // ✅ 批量获取 nonce
241
245
  ]);
242
246
  const minOuts = resolveMinOutputs(minOutputAmounts, wallets.length, quotedOutputs);
243
247
  // ✅ 优化:构建未签名交易(这里是本地操作,但仍然并行执行以提高效率)
@@ -399,21 +403,70 @@ function buildProfitMetadata(extractProfit, totalBuyAmount, totalProfit, buyerCo
399
403
  };
400
404
  }
401
405
  /**
402
- * ✅ 支持 quoteToken 的卖出报价
406
+ * ✅ 使用 Multicall3 批量获取卖出报价(单次 RPC)
407
+ * 比 Promise.all 更高效,N 个报价只需 1 次网络请求
403
408
  */
404
409
  async function quoteSellOutputsWithQuote(portal, tokenAddress, amountsWei, outputToken) {
405
- return await Promise.all(amountsWei.map(async (amount) => {
410
+ if (amountsWei.length === 0)
411
+ return [];
412
+ // 如果只有 1 个,直接调用(避免 multicall 开销)
413
+ if (amountsWei.length === 1) {
406
414
  try {
407
- return await portal.quoteExactInput.staticCall({
415
+ const result = await portal.quoteExactInput.staticCall({
408
416
  inputToken: tokenAddress,
409
- outputToken, // ✅ 使用动态 outputToken
410
- inputAmount: amount
417
+ outputToken,
418
+ inputAmount: amountsWei[0]
411
419
  });
420
+ return [result];
412
421
  }
413
422
  catch {
414
- return 0n;
423
+ return [0n];
415
424
  }
425
+ }
426
+ const provider = portal.runner;
427
+ const portalAddress = await portal.getAddress();
428
+ const portalIface = new Interface(PORTAL_ABI);
429
+ const multicall = new Contract(MULTICALL3_ADDRESS, MULTICALL3_ABI, provider);
430
+ // 构建批量调用
431
+ const calls = amountsWei.map(amount => ({
432
+ target: portalAddress,
433
+ allowFailure: true, // 允许单个失败
434
+ callData: portalIface.encodeFunctionData('quoteExactInput', [{
435
+ inputToken: tokenAddress,
436
+ outputToken,
437
+ inputAmount: amount
438
+ }])
416
439
  }));
440
+ try {
441
+ const results = await multicall.aggregate3.staticCall(calls);
442
+ return results.map((r) => {
443
+ if (r.success && r.returnData && r.returnData !== '0x') {
444
+ try {
445
+ const decoded = portalIface.decodeFunctionResult('quoteExactInput', r.returnData);
446
+ return BigInt(decoded[0]);
447
+ }
448
+ catch {
449
+ return 0n;
450
+ }
451
+ }
452
+ return 0n;
453
+ });
454
+ }
455
+ catch {
456
+ // Multicall 失败,回退到并行调用
457
+ return await Promise.all(amountsWei.map(async (amount) => {
458
+ try {
459
+ return await portal.quoteExactInput.staticCall({
460
+ inputToken: tokenAddress,
461
+ outputToken,
462
+ inputAmount: amount
463
+ });
464
+ }
465
+ catch {
466
+ return 0n;
467
+ }
468
+ }));
469
+ }
417
470
  }
418
471
  function resolveMinOutputs(provided, walletCount, quotedOutputs) {
419
472
  if (provided && provided.length === walletCount) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.3.12",
3
+ "version": "1.3.13",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",