four-flap-meme-sdk 1.5.96 → 1.5.98

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.
@@ -9,6 +9,7 @@ export { fourPrivateBuyMerkle, fourPrivateSellMerkle, fourBatchPrivateBuyMerkle,
9
9
  export { disperseWithBundleMerkle, sweepWithBundleMerkle, pairwiseTransferWithBundleMerkle } from './utils.js';
10
10
  export { fourPancakeProxyBatchBuyMerkle, fourPancakeProxyBatchSellMerkle, approveFourPancakeProxy, approveFourPancakeProxyBatch } from './pancake-proxy.js';
11
11
  export { approveFourTokenManagerBatch, type ApproveFourTokenManagerBatchParams, type ApproveFourTokenManagerBatchResult } from './approve-tokenmanager.js';
12
- export { submitBundleToMerkle, submitMultipleBundles, submitMultipleBundlesParallel, type MerkleSubmitConfig, type SubmitBundleResult, submitBundleToBlockRazor, submitMultipleBundlesToBlockRazor, submitMultipleBundlesToBlockRazorParallel, type BlockRazorSubmitConfig, type BlockRazorSubmitResult, submitDirectToRpc, submitDirectToRpcSequential, // ✅ 新增:顺序广播并等待确认(用于多跳)
12
+ export { submitBundleToMerkle, submitMultipleBundles, submitMultipleBundlesParallel, type MerkleSubmitConfig, type SubmitBundleResult, submitBundleToBlockRazor, submitMultipleBundlesToBlockRazor, submitMultipleBundlesToBlockRazorParallel, type BlockRazorSubmitConfig, type BlockRazorSubmitResult, submitDirectToRpc, submitDirectToRpcSequential, // ✅ 顺序广播并等待确认(用于多跳)
13
+ submitDirectToRpcSequentialNoWait, // ✅ 顺序广播但不等待确认(用于 AA handleOps + tail)
13
14
  submitDirectToRpcParallel, type DirectSubmitConfig, type DirectSubmitResult, type DirectTxResult } from './submit.js';
14
15
  export { fourBundleBuyFirstMerkle, type FourBundleBuyFirstSignParams, type FourBuyFirstSignConfig, type FourBuyFirstResult } from './swap-buy-first.js';
@@ -22,7 +22,8 @@ submitBundleToMerkle, submitMultipleBundles, submitMultipleBundlesParallel,
22
22
  // ✅ BlockRazor 提交方法
23
23
  submitBundleToBlockRazor, submitMultipleBundlesToBlockRazor, submitMultipleBundlesToBlockRazorParallel,
24
24
  // ✅ Monad 等链的逐笔广播方法
25
- submitDirectToRpc, submitDirectToRpcSequential, // ✅ 新增:顺序广播并等待确认(用于多跳)
25
+ submitDirectToRpc, submitDirectToRpcSequential, // ✅ 顺序广播并等待确认(用于多跳)
26
+ submitDirectToRpcSequentialNoWait, // ✅ 顺序广播但不等待确认(用于 AA handleOps + tail)
26
27
  submitDirectToRpcParallel } from './submit.js';
27
28
  // 先买后卖换手方法
28
29
  export { fourBundleBuyFirstMerkle } from './swap-buy-first.js';
@@ -260,6 +260,17 @@ export declare function submitDirectToRpc(signedTransactions: string[], config:
260
260
  * @returns 广播结果
261
261
  */
262
262
  export declare function submitDirectToRpcSequential(signedTransactions: string[], config: DirectSubmitConfig): Promise<DirectSubmitResult>;
263
+ /**
264
+ * ✅ 顺序广播但不等待确认(用于 AA handleOps + tail 等 nonce 连续的场景)
265
+ *
266
+ * 按顺序逐笔发送交易,每笔只等待广播成功(RPC 返回 txHash),不等待链上确认。
267
+ * 这样可以保证交易按顺序进入 mempool,同时避免等待确认超时。
268
+ *
269
+ * @param signedTransactions 签名后的交易数组
270
+ * @param config 直接广播配置
271
+ * @returns 广播结果
272
+ */
273
+ export declare function submitDirectToRpcSequentialNoWait(signedTransactions: string[], config: DirectSubmitConfig): Promise<DirectSubmitResult>;
263
274
  /**
264
275
  * 并行逐笔广播到 RPC(速度更快,但可能有 nonce 冲突)
265
276
  *
@@ -510,6 +510,94 @@ export async function submitDirectToRpcSequential(signedTransactions, config) {
510
510
  errorSummary: errors.length > 0 ? errors.join('; ') : undefined
511
511
  };
512
512
  }
513
+ /**
514
+ * ✅ 顺序广播但不等待确认(用于 AA handleOps + tail 等 nonce 连续的场景)
515
+ *
516
+ * 按顺序逐笔发送交易,每笔只等待广播成功(RPC 返回 txHash),不等待链上确认。
517
+ * 这样可以保证交易按顺序进入 mempool,同时避免等待确认超时。
518
+ *
519
+ * @param signedTransactions 签名后的交易数组
520
+ * @param config 直接广播配置
521
+ * @returns 广播结果
522
+ */
523
+ export async function submitDirectToRpcSequentialNoWait(signedTransactions, config) {
524
+ const totalTransactions = signedTransactions?.length ?? 0;
525
+ if (!signedTransactions || signedTransactions.length === 0) {
526
+ return {
527
+ code: false,
528
+ totalTransactions: 0,
529
+ successCount: 0,
530
+ failedCount: 0,
531
+ txHashes: [],
532
+ results: [],
533
+ errorSummary: 'signedTransactions cannot be empty'
534
+ };
535
+ }
536
+ if (!config.rpcUrl) {
537
+ return {
538
+ code: false,
539
+ totalTransactions,
540
+ successCount: 0,
541
+ failedCount: totalTransactions,
542
+ txHashes: [],
543
+ results: [],
544
+ errorSummary: 'rpcUrl is required in config'
545
+ };
546
+ }
547
+ const chainId = config.chainId ?? 143;
548
+ const chainName = config.chainName ?? 'MONAD';
549
+ const provider = new ethers.JsonRpcProvider(config.rpcUrl, {
550
+ chainId,
551
+ name: chainName
552
+ });
553
+ const results = [];
554
+ const txHashes = [];
555
+ const errors = [];
556
+ // ✅ 顺序广播,每笔只等待广播成功(不等待链上确认)
557
+ for (let i = 0; i < signedTransactions.length; i++) {
558
+ const signedTx = signedTransactions[i];
559
+ try {
560
+ // 广播交易,等待 RPC 返回 txHash
561
+ const txResponse = await provider.broadcastTransaction(signedTx);
562
+ results.push({
563
+ index: i,
564
+ success: true,
565
+ txHash: txResponse.hash
566
+ });
567
+ txHashes.push(txResponse.hash);
568
+ console.log(`✅ [${chainName}] 交易 ${i + 1}/${totalTransactions} 广播成功: ${txResponse.hash}`);
569
+ }
570
+ catch (error) {
571
+ const errorMessage = error?.message || String(error);
572
+ console.error(`❌ [${chainName}] 交易 ${i + 1}/${totalTransactions} 广播失败:`, errorMessage);
573
+ // ✅ 兼容:nonce too low / already known 等情况下,交易可能已经被接收
574
+ const recovered = await recoverTxHashIfAlreadySeen(provider, signedTx, errorMessage);
575
+ if (recovered) {
576
+ results.push({ index: i, success: true, txHash: recovered, error: errorMessage });
577
+ txHashes.push(recovered);
578
+ continue;
579
+ }
580
+ results.push({
581
+ index: i,
582
+ success: false,
583
+ error: errorMessage
584
+ });
585
+ errors.push(`交易 ${i + 1}: ${errorMessage}`);
586
+ // ✅ 如果某笔失败,继续尝试后续交易(可能是 nonce 问题可以自动恢复)
587
+ }
588
+ }
589
+ const successCount = txHashes.length;
590
+ const failedCount = totalTransactions - successCount;
591
+ return {
592
+ code: successCount > 0,
593
+ totalTransactions,
594
+ successCount,
595
+ failedCount,
596
+ txHashes,
597
+ results,
598
+ errorSummary: errors.length > 0 ? errors.join('; ') : undefined
599
+ };
600
+ }
513
601
  /**
514
602
  * 并行逐笔广播到 RPC(速度更快,但可能有 nonce 冲突)
515
603
  *
package/dist/index.d.ts CHANGED
@@ -49,7 +49,8 @@ export { flapBundleCurveToDex, type CurveToDexChain, type DexPoolType, type Curv
49
49
  export { flapBundleCreateToDex, type CreateToDexChain, type CreateToDexSignConfig, type CreateTokenInfo, type FlapCreateToDexParams, type FlapCreateToDexResult } from './flap/portal-bundle-merkle/create-to-dex.js';
50
50
  export { PROFIT_CONFIG } from './utils/constants.js';
51
51
  export { quoteV2, quoteV3, quote, getTokenToNativeQuote, getNativeToTokenQuote, getWrappedNativeAddress, getStableCoins, V3_FEE_TIERS, QUOTE_CONFIG, type QuoteParams, type QuoteResult, type SupportedChain, } from './utils/quote-helpers.js';
52
- export { submitDirectToRpc, submitDirectToRpcSequential, // ✅ 新增:顺序广播并等待确认(用于多跳)
52
+ export { submitDirectToRpc, submitDirectToRpcSequential, // ✅ 顺序广播并等待确认(用于多跳)
53
+ submitDirectToRpcSequentialNoWait, // ✅ 顺序广播但不等待确认(用于 AA handleOps + tail)
53
54
  submitDirectToRpcParallel, type DirectSubmitConfig, type DirectSubmitResult, type DirectTxResult } from './contracts/tm-bundle-merkle/submit.js';
54
55
  export { directV2BatchBuy, directV2BatchSell, directV3BatchBuy, directV3BatchSell, getRouterAddress, DIRECT_ROUTERS, type DirectV2BuyParams, type DirectV2SellParams, type DirectV3BuyParams, type DirectV3SellParams, type DirectRouterResult, type DirectRouterSignConfig, type DexKey, type RouterVersion, } from './dex/index.js';
55
56
  export * as XLayer from './xlayer/index.js';
package/dist/index.js CHANGED
@@ -82,7 +82,8 @@ export { PROFIT_CONFIG } from './utils/constants.js';
82
82
  // ✅ V2/V3 报价工具(统一管理)
83
83
  export { quoteV2, quoteV3, quote, getTokenToNativeQuote, getNativeToTokenQuote, getWrappedNativeAddress, getStableCoins, V3_FEE_TIERS, QUOTE_CONFIG, } from './utils/quote-helpers.js';
84
84
  // ✅ Monad 等不支持 Bundle 的链:逐笔 RPC 广播方法(服务器端使用)
85
- export { submitDirectToRpc, submitDirectToRpcSequential, // ✅ 新增:顺序广播并等待确认(用于多跳)
85
+ export { submitDirectToRpc, submitDirectToRpcSequential, // ✅ 顺序广播并等待确认(用于多跳)
86
+ submitDirectToRpcSequentialNoWait, // ✅ 顺序广播但不等待确认(用于 AA handleOps + tail)
86
87
  submitDirectToRpcParallel } from './contracts/tm-bundle-merkle/submit.js';
87
88
  // ============================================================
88
89
  // ✅ DEX 直接交易(不走代理合约)
@@ -10,7 +10,7 @@
10
10
  import { Wallet, Interface, Contract, ethers } from 'ethers';
11
11
  import { FLAP_PORTAL, ENTRYPOINT_ABI, PORTAL_ABI, DEFAULT_CALL_GAS_LIMIT_SELL, DEFAULT_WITHDRAW_RESERVE, WOKB, POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, } from './constants.js';
12
12
  import { AAAccountManager, encodeExecute, encodeExecuteBatch, createWallet } from './aa-account.js';
13
- import { encodeBuyCall, encodeBuyCallV3, encodeSellCall, encodeApproveCall, encodeTransferCall, encodeCreateCallV2, encodeCreateCallV3, encodeCreateCallV4, PortalQuery, parseOkb, formatOkb, lpFeeProfileToV3Fee, } from './portal-ops.js';
13
+ import { encodeBuyCallV3, encodeSellCall, encodeApproveCall, encodeTransferCall, encodeCreateCallV2, encodeCreateCallV3, encodeCreateCallV4, PortalQuery, parseOkb, formatOkb, lpFeeProfileToV3Fee, } from './portal-ops.js';
14
14
  import { mapWithConcurrency } from '../utils/concurrency.js';
15
15
  import { PROFIT_CONFIG, ZERO_ADDRESS } from '../utils/constants.js';
16
16
  import { DexQuery } from './dex.js';
@@ -1569,9 +1569,11 @@ export class BundleExecutor {
1569
1569
  });
1570
1570
  totalCurveBuyWei = curveBuyData.reduce((sum, d) => sum + d.buyWei, 0n);
1571
1571
  // ✅ 并行构建和签名所有内盘买入 UserOps
1572
+ // ✅ 使用 encodeBuyCallV3(支持 extensionData),与 V4 代币创建保持一致
1573
+ const extensionData = params.extensionData ?? '0x';
1572
1574
  const signedCurveBuyOps = await mapWithConcurrency(curveBuyData, 5, async (data) => {
1573
1575
  const { wallet, info, buyWei, gasLimit } = data;
1574
- const buyData = encodeBuyCall(tokenAddress, buyWei, 0n);
1576
+ const buyData = encodeBuyCallV3(tokenAddress, buyWei, 0n, extensionData);
1575
1577
  const buyCallData = encodeExecute(FLAP_PORTAL, buyWei, buyData);
1576
1578
  const buyOpRes = await aaManager.buildUserOpWithFixedGas({
1577
1579
  ownerWallet: wallet,
@@ -1586,17 +1588,48 @@ export class BundleExecutor {
1586
1588
  return signedBuyOp.userOp;
1587
1589
  });
1588
1590
  ops1.push(...signedCurveBuyOps);
1591
+ // === 向内盘买家 AA 账户转入 OKB(prefund + 买入金额)===
1592
+ // ✅ 修复 AA21 didn't pay prefund 错误
1593
+ const feeData = await provider.getFeeData();
1594
+ const gasPrice = feeData.gasPrice ?? 100000000n;
1595
+ const chainId = (await provider.getNetwork()).chainId;
1596
+ let currentNonce = params.payerStartNonce ?? (await provider.getTransactionCount(payerWallet.address, 'pending'));
1597
+ const DEFAULT_PREFUND_ESTIMATE = parseOkb('0.002'); // 预估每个 UserOp 的 prefund
1598
+ const BUFFER_WEI = parseOkb('0.0005'); // 缓冲金额
1599
+ for (let i = 0; i < curveBuyerWallets.length; i++) {
1600
+ const buyerSender = curveBuyerInfos[i].sender;
1601
+ const buyWei = curveBuyData[i].buyWei;
1602
+ // 计算需要转入的金额:买入金额 + prefund + 缓冲
1603
+ const requiredAmount = buyWei + DEFAULT_PREFUND_ESTIMATE + BUFFER_WEI;
1604
+ // 检查买家 AA 账户当前余额
1605
+ const currentBalance = await provider.getBalance(buyerSender);
1606
+ if (currentBalance < requiredAmount) {
1607
+ const transferAmount = requiredAmount - currentBalance;
1608
+ console.log(`[内盘买家${i + 1}] 向 AA 账户 ${buyerSender} 转入 ${formatOkb(transferAmount)} OKB`);
1609
+ const fundTxRequest = {
1610
+ to: buyerSender,
1611
+ value: transferAmount,
1612
+ nonce: currentNonce,
1613
+ gasLimit: 21000n,
1614
+ gasPrice,
1615
+ chainId
1616
+ };
1617
+ const signedFundTx = await payerWallet.signTransaction(fundTxRequest);
1618
+ signedTransactions.push(signedFundTx);
1619
+ currentNonce++;
1620
+ }
1621
+ }
1589
1622
  // 签名第一个 handleOps
1590
- const startNonce = params.payerStartNonce ?? (await provider.getTransactionCount(payerWallet.address, 'pending'));
1591
1623
  const signedMainTx = await this.signHandleOpsTx({
1592
1624
  ops: ops1,
1593
1625
  payerWallet: payerWallet,
1594
1626
  beneficiary: params.beneficiary ?? payerWallet.address,
1595
- nonce: startNonce,
1627
+ nonce: currentNonce,
1596
1628
  gasLimit: effConfig.gasLimit,
1597
1629
  gasPrice: effConfig.minGasPriceGwei ? ethers.parseUnits(effConfig.minGasPriceGwei.toString(), 'gwei') : undefined
1598
1630
  });
1599
1631
  signedTransactions.push(signedMainTx);
1632
+ currentNonce++;
1600
1633
  // --- 4. 构建外盘买入 handleOps (如果启用) ---
1601
1634
  if (enableDexBuy && dexBuyerPrivateKeys.length > 0) {
1602
1635
  const dexBuyerWallets = dexBuyerPrivateKeys.map(pk => createWallet(pk, config));
@@ -1614,10 +1647,34 @@ export class BundleExecutor {
1614
1647
  return { wallet, info: dexBuyerInfos[i], buyWei };
1615
1648
  });
1616
1649
  totalDexBuyWei = dexBuyData.reduce((sum, d) => sum + d.buyWei, 0n);
1650
+ // === 向外盘买家 AA 账户转入 OKB(prefund + 买入金额)===
1651
+ for (let i = 0; i < dexBuyerWallets.length; i++) {
1652
+ const buyerSender = dexBuyerInfos[i].sender;
1653
+ const buyWei = dexBuyData[i].buyWei;
1654
+ // 计算需要转入的金额:买入金额 + prefund + 缓冲
1655
+ const requiredAmount = buyWei + DEFAULT_PREFUND_ESTIMATE + BUFFER_WEI;
1656
+ // 检查买家 AA 账户当前余额
1657
+ const dexBuyerBalance = await provider.getBalance(buyerSender);
1658
+ if (dexBuyerBalance < requiredAmount) {
1659
+ const transferAmount = requiredAmount - dexBuyerBalance;
1660
+ console.log(`[外盘买家${i + 1}] 向 AA 账户 ${buyerSender} 转入 ${formatOkb(transferAmount)} OKB`);
1661
+ const fundTxRequest = {
1662
+ to: buyerSender,
1663
+ value: transferAmount,
1664
+ nonce: currentNonce,
1665
+ gasLimit: 21000n,
1666
+ gasPrice,
1667
+ chainId
1668
+ };
1669
+ const signedFundTx = await payerWallet.signTransaction(fundTxRequest);
1670
+ signedTransactions.push(signedFundTx);
1671
+ currentNonce++;
1672
+ }
1673
+ }
1617
1674
  // ✅ 并行构建和签名所有外盘买入 UserOps
1618
1675
  const signedDexBuyOps = await mapWithConcurrency(dexBuyData, 5, async (data) => {
1619
1676
  const { wallet, info, buyWei } = data;
1620
- // ✅ 外盘买入:使用 Portal swapExactInput 进行自动路由
1677
+ // ✅ 外盘买入:使用 DEX Router 进行交换
1621
1678
  let swapData;
1622
1679
  let routerAddress;
1623
1680
  if (dexType === 'V3') {
@@ -1655,11 +1712,12 @@ export class BundleExecutor {
1655
1712
  ops: ops2,
1656
1713
  payerWallet: payerWallet,
1657
1714
  beneficiary: params.beneficiary ?? payerWallet.address,
1658
- nonce: startNonce + 1,
1715
+ nonce: currentNonce,
1659
1716
  gasLimit: effConfig.gasLimit,
1660
1717
  gasPrice: effConfig.minGasPriceGwei ? ethers.parseUnits(effConfig.minGasPriceGwei.toString(), 'gwei') : undefined
1661
1718
  });
1662
1719
  signedTransactions.push(signedDexTx);
1720
+ currentNonce++;
1663
1721
  }
1664
1722
  // --- 5. 利润提取 (Direct EOA transfer) ---
1665
1723
  let totalProfitWei = 0n;
@@ -1669,12 +1727,11 @@ export class BundleExecutor {
1669
1727
  totalProfitWei += calculateProfitWei(totalDexBuyWei, profitSettings.profitBps);
1670
1728
  }
1671
1729
  if (totalProfitWei > 0n) {
1672
- const profitTxNonce = startNonce + (enableDexBuy ? 2 : 1);
1673
1730
  const signedProfitTx = await this.signProfitTransaction({
1674
1731
  payerWallet,
1675
1732
  profitWei: totalProfitWei,
1676
1733
  recipient: profitSettings.profitRecipient,
1677
- nonce: profitTxNonce
1734
+ nonce: currentNonce
1678
1735
  });
1679
1736
  signedTransactions.push(signedProfitTx);
1680
1737
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.5.96",
3
+ "version": "1.5.98",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",