four-flap-meme-sdk 1.7.22 → 1.7.24

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,8 +9,8 @@
9
9
  * 所有函数只返回签名后的交易,由调用方决定如何提交
10
10
  */
11
11
  import { ethers, Interface, Contract } from 'ethers';
12
- import { FLAP_PORTAL_ADDRESS, UNIFIED_DELEGATE_ADDRESS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, WOKB_ADDRESS, POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, V3_FEE_TIERS, } from './constants.js';
13
- import { getCachedProvider, createWallet, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync, calculateProfitAmount, getProfitRecipient, } from './utils.js';
12
+ import { FLAP_PORTAL_ADDRESS, UNIFIED_DELEGATE_ADDRESS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, WOKB_ADDRESS, POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, V3_FEE_TIERS, GAS_ESTIMATE, } from './constants.js';
13
+ import { getCachedProvider, createWallet, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync, calculateProfitAmountByUserType, getProfitRecipient, } from './utils.js';
14
14
  // ========================================
15
15
  // 常量
16
16
  // ========================================
@@ -125,7 +125,8 @@ function buildProfitCall(walletAddress, profitAmount, profitRecipient, delegateI
125
125
  * @returns 只返回签名后的交易,不发送
126
126
  */
127
127
  export async function bundleCreateBuy(params) {
128
- const { tokenInfo, tokenAddress = ZERO_ADDRESS, devPrivateKey, buyerPrivateKeys, buyAmounts, config, } = params;
128
+ const { tokenInfo, tokenAddress = ZERO_ADDRESS, devPrivateKey, buyerPrivateKeys, buyAmounts, userType = 'v0', // 用户类型(影响利润率)
129
+ config, } = params;
129
130
  if (buyerPrivateKeys.length !== buyAmounts.length) {
130
131
  throw new Error('buyerPrivateKeys 和 buyAmounts 长度必须相同');
131
132
  }
@@ -137,10 +138,10 @@ export async function bundleCreateBuy(params) {
137
138
  const buyerWallets = buyerPrivateKeys.map(pk => createWallet(pk, provider));
138
139
  const allWallets = [devWallet, ...buyerWallets];
139
140
  const mainWallet = devWallet;
140
- // 计算买入金额和利润
141
+ // 计算买入金额和利润(单边模式:只有买入)
141
142
  const buyAmountsWei = buyAmounts.map(amt => ethers.parseEther(amt));
142
143
  const totalBuyAmount = buyAmountsWei.reduce((sum, amt) => sum + amt, 0n);
143
- const profitAmount = calculateProfitAmount(totalBuyAmount);
144
+ const profitAmount = calculateProfitAmountByUserType(totalBuyAmount, userType, false); // ✅ 支持 userType
144
145
  const profitRecipient = getProfitRecipient(config);
145
146
  // 并行获取 nonce 和 fee data
146
147
  const [nonces, feeData] = await Promise.all([
@@ -272,7 +273,8 @@ export async function bundleCreateBuy(params) {
272
273
  * @returns 只返回签名后的交易,不发送
273
274
  */
274
275
  export async function bundleCreateToDex(params) {
275
- const { tokenInfo, tokenAddress = ZERO_ADDRESS, payerPrivateKey, curveBuyerPrivateKeys, curveBuyAmounts, enableDexBuy = false, dexBuyerPrivateKeys = [], dexBuyAmounts = [], config, } = params;
276
+ const { tokenInfo, tokenAddress = ZERO_ADDRESS, payerPrivateKey, curveBuyerPrivateKeys, curveBuyAmounts, enableDexBuy = false, dexBuyerPrivateKeys = [], dexBuyAmounts = [], userType = 'v0', // 用户类型(影响利润率)
277
+ config, } = params;
276
278
  if (curveBuyerPrivateKeys.length !== curveBuyAmounts.length) {
277
279
  throw new Error('curveBuyerPrivateKeys 和 curveBuyAmounts 长度必须相同');
278
280
  }
@@ -294,13 +296,13 @@ export async function bundleCreateToDex(params) {
294
296
  const allWallets = Array.from(allWalletsMap.values());
295
297
  const mainWallet = payerWallet;
296
298
  const mainWalletIndex = allWallets.findIndex(w => w.address.toLowerCase() === mainWallet.address.toLowerCase());
297
- // 计算金额
299
+ // 计算金额和利润(单边模式:只有买入)
298
300
  const curveBuyAmountsWei = curveBuyAmounts.map(amt => ethers.parseEther(amt));
299
301
  const dexBuyAmountsWei = dexBuyAmounts.map(amt => ethers.parseEther(amt));
300
302
  const totalCurveBuyAmount = curveBuyAmountsWei.reduce((sum, amt) => sum + amt, 0n);
301
303
  const totalDexBuyAmount = dexBuyAmountsWei.reduce((sum, amt) => sum + amt, 0n);
302
304
  const totalBuyAmount = totalCurveBuyAmount + totalDexBuyAmount;
303
- const profitAmount = calculateProfitAmount(totalBuyAmount);
305
+ const profitAmount = calculateProfitAmountByUserType(totalBuyAmount, userType, false); // ✅ 支持 userType
304
306
  const profitRecipient = getProfitRecipient(config);
305
307
  // 并行获取 nonce 和 fee data
306
308
  const [nonces, feeData] = await Promise.all([
@@ -396,8 +398,12 @@ export async function bundleCreateToDex(params) {
396
398
  }
397
399
  // 计算总价值
398
400
  const totalValue = totalBuyAmount + profitAmount + (params.quoteAmt ?? 0n);
399
- // 构建签名交易
400
- const signedTransaction = buildEIP7702TransactionSync(mainWallet, authorizations, calls, totalValue, nonces[mainWalletIndex], feeData, config);
401
+ // 构建签名交易(一键发射会触发毕业,使用高 gas limit)
402
+ const toDexConfig = {
403
+ ...config,
404
+ gasLimit: GAS_ESTIMATE.GRADUATE, // ✅ 一键发射(创建+毕业)需要约 800 万 gas
405
+ };
406
+ const signedTransaction = buildEIP7702TransactionSync(mainWallet, authorizations, calls, totalValue, nonces[mainWalletIndex], feeData, toDexConfig);
401
407
  return {
402
408
  signedTransaction,
403
409
  tokenAddress,
@@ -426,7 +432,8 @@ export async function bundleCreateToDex(params) {
426
432
  * @returns 只返回签名后的交易,不发送
427
433
  */
428
434
  export async function bundleGraduateBuy(params) {
429
- const { tokenAddress, privateKeys, payerPrivateKey, amountMode, totalBuyAmount, minAmount, maxAmount, walletAmounts, enableDexBuy = false, extensionData = '0x', curveAddresses, dexAddresses, graduationAmount, config, } = params;
435
+ const { tokenAddress, privateKeys, payerPrivateKey, amountMode, totalBuyAmount, minAmount, maxAmount, walletAmounts, enableDexBuy = false, extensionData = '0x', curveAddresses, dexAddresses, graduationAmount, userType = 'v0', // 用户类型(影响利润率)
436
+ config, } = params;
430
437
  const provider = getCachedProvider(config?.rpcUrl);
431
438
  const delegateAddress = (config?.delegateAddress && config.delegateAddress.trim()) || UNIFIED_DELEGATE_ADDRESS;
432
439
  const delegateInterface = new Interface(UNIFIED_DELEGATE_ABI);
@@ -519,11 +526,11 @@ export async function bundleGraduateBuy(params) {
519
526
  const allWallets = Array.from(allWalletsMap.values());
520
527
  const mainWallet = payerWallet;
521
528
  const mainWalletIndex = allWallets.findIndex(w => w.address.toLowerCase() === mainWallet.address.toLowerCase());
522
- // 计算金额
529
+ // 计算金额和利润(单边模式:只有买入)
523
530
  const curveTotalWei = curveBuyers.reduce((sum, b) => sum + b.amount, 0n);
524
531
  const dexTotalWei = dexBuyers.reduce((sum, b) => sum + b.amount, 0n);
525
532
  const totalAmount = curveTotalWei + dexTotalWei;
526
- const profitAmount = calculateProfitAmount(totalAmount);
533
+ const profitAmount = calculateProfitAmountByUserType(totalAmount, userType, false); // ✅ 支持 userType
527
534
  const profitRecipient = getProfitRecipient(config);
528
535
  // 并行获取 nonce 和 fee data
529
536
  const [nonces, feeData] = await Promise.all([
@@ -586,8 +593,12 @@ export async function bundleGraduateBuy(params) {
586
593
  }
587
594
  // 计算总价值
588
595
  const totalValue = totalAmount + profitAmount;
589
- // 构建签名交易
590
- const signedTransaction = buildEIP7702TransactionSync(mainWallet, authorizations, calls, totalValue, nonces[mainWalletIndex], feeData, config);
596
+ // 构建签名交易(毕业操作强制使用高 gas limit)
597
+ const graduateConfig = {
598
+ ...config,
599
+ gasLimit: GAS_ESTIMATE.GRADUATE, // ✅ 毕业操作需要约 800 万 gas
600
+ };
601
+ const signedTransaction = buildEIP7702TransactionSync(mainWallet, authorizations, calls, totalValue, nonces[mainWalletIndex], feeData, graduateConfig);
591
602
  return {
592
603
  signedTransaction,
593
604
  metadata: {
@@ -6,9 +6,10 @@
6
6
  *
7
7
  * 只生成签名,由调用方决定如何提交
8
8
  */
9
- import { ethers, Contract } from 'ethers';
10
- import { getCachedProvider, createWallet, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync, calculateProfitAmountByTxCount, getProfitRecipient, } from './utils.js';
11
- import { UNIFIED_DELEGATE_ADDRESS, POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, FLAP_PORTAL_ADDRESS, WOKB_ADDRESS, V3_FEE_TIERS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, ERC20_ABI, } from './constants.js';
9
+ import { ethers, Contract, JsonRpcProvider } from 'ethers';
10
+ import { getCachedProvider, createWallet, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync, calculateProfitAmountByUserType, getProfitRecipient, } from './utils.js';
11
+ import { UNIFIED_DELEGATE_ADDRESS, POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, FLAP_PORTAL_ADDRESS, WOKB_ADDRESS, V3_FEE_TIERS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, ERC20_ABI, XLAYER_RPC_URL, XLAYER_CHAIN_ID, } from './constants.js';
12
+ import { quoteV2, quoteV3 } from '../../utils/quote-helpers.js';
12
13
  // ========================================
13
14
  // 路由地址获取
14
15
  // ========================================
@@ -23,6 +24,88 @@ function getDefaultRouter(tradeType) {
23
24
  }
24
25
  }
25
26
  // ========================================
27
+ // 卖出报价(Token → OKB)
28
+ // ========================================
29
+ /**
30
+ * 获取卖出报价:预估卖出代币能得到多少 OKB
31
+ *
32
+ * @param totalSellAmount - 总卖出代币数量(wei)
33
+ * @param tokenAddress - 代币地址
34
+ * @param tradeType - 交易类型(FLAP/V2/V3)
35
+ * @param fee - V3 费率
36
+ * @param rpcUrl - RPC URL
37
+ * @returns 预估得到的 OKB 数量(wei)
38
+ */
39
+ async function quoteSellOutput(totalSellAmount, tokenAddress, tradeType, fee, rpcUrl) {
40
+ if (totalSellAmount <= 0n)
41
+ return 0n;
42
+ const provider = new JsonRpcProvider(rpcUrl || XLAYER_RPC_URL, { chainId: XLAYER_CHAIN_ID, name: 'xlayer', ensAddress: undefined });
43
+ try {
44
+ switch (tradeType) {
45
+ case 'FLAP': {
46
+ // FLAP Portal 报价
47
+ const portalContract = new Contract(FLAP_PORTAL_ADDRESS, FLAP_PORTAL_ABI, provider);
48
+ // 先尝试 quoteSell(Token → OKB)
49
+ try {
50
+ const okbOut = await portalContract.quoteSell(tokenAddress, totalSellAmount);
51
+ console.log(`[Bundle Sell] FLAP quoteSell 成功: ${ethers.formatEther(okbOut)} OKB`);
52
+ return BigInt(okbOut.toString());
53
+ }
54
+ catch {
55
+ console.warn(`[Bundle Sell] FLAP quoteSell 失败,尝试 quoteExactInput...`);
56
+ }
57
+ // 再尝试 quoteExactInput
58
+ try {
59
+ const okbOut = await portalContract.quoteExactInput({
60
+ inputToken: tokenAddress,
61
+ outputToken: ethers.ZeroAddress, // OKB
62
+ inputAmount: totalSellAmount,
63
+ });
64
+ console.log(`[Bundle Sell] FLAP quoteExactInput 成功: ${ethers.formatEther(okbOut)} OKB`);
65
+ return BigInt(okbOut.toString());
66
+ }
67
+ catch {
68
+ console.warn(`[Bundle Sell] FLAP quoteExactInput 失败,尝试 V2...`);
69
+ }
70
+ // 回退到 V2
71
+ const v2Result = await quoteV2(provider, tokenAddress, WOKB_ADDRESS, totalSellAmount, 'XLAYER');
72
+ if (v2Result.amountOut > 0n) {
73
+ console.log(`[Bundle Sell] FLAP 回退 V2 报价成功: ${ethers.formatEther(v2Result.amountOut)} OKB`);
74
+ return v2Result.amountOut;
75
+ }
76
+ break;
77
+ }
78
+ case 'V2': {
79
+ const result = await quoteV2(provider, tokenAddress, WOKB_ADDRESS, totalSellAmount, 'XLAYER');
80
+ if (result.amountOut > 0n) {
81
+ console.log(`[Bundle Sell] V2 报价成功: ${ethers.formatEther(result.amountOut)} OKB`);
82
+ return result.amountOut;
83
+ }
84
+ break;
85
+ }
86
+ case 'V3': {
87
+ const result = await quoteV3(provider, tokenAddress, WOKB_ADDRESS, totalSellAmount, 'XLAYER', fee);
88
+ if (result.amountOut > 0n) {
89
+ console.log(`[Bundle Sell] V3 报价成功 (fee=${result.fee}): ${ethers.formatEther(result.amountOut)} OKB`);
90
+ return result.amountOut;
91
+ }
92
+ // 回退到 V2
93
+ const v2Result = await quoteV2(provider, tokenAddress, WOKB_ADDRESS, totalSellAmount, 'XLAYER');
94
+ if (v2Result.amountOut > 0n) {
95
+ console.log(`[Bundle Sell] V3 回退 V2 报价成功: ${ethers.formatEther(v2Result.amountOut)} OKB`);
96
+ return v2Result.amountOut;
97
+ }
98
+ break;
99
+ }
100
+ }
101
+ }
102
+ catch (error) {
103
+ console.error(`[Bundle Sell] 报价失败:`, error);
104
+ }
105
+ console.warn(`[Bundle Sell] 无法获取报价,利润计算可能不准确`);
106
+ return 0n;
107
+ }
108
+ // ========================================
26
109
  // 代币余额查询
27
110
  // ========================================
28
111
  async function getTokenBalances(wallets, tokenAddress) {
@@ -255,27 +338,60 @@ export async function bundleSell(params) {
255
338
  }
256
339
  const totalSellAmount = sellAmountsWei.reduce((sum, amt) => sum + amt, 0n);
257
340
  const validSellCount = wallets.filter((_, i) => sellAmountsWei[i] > 0n).length;
258
- // ✅ 计算利润(卖出模式:根据卖出交易数量计算)
259
- // 注意:卖出后的 OKB 我们无法预估,所以基于代币数量计算(与买入对称)
260
- const profitAmount = calculateProfitAmountByTxCount(totalSellAmount, validSellCount, userType);
341
+ // ✅ 修复:获取卖出报价,基于预估的 OKB 输出计算利润
342
+ // 1. 先获取报价:卖出 totalSellAmount 代币能得到多少 OKB
343
+ const estimatedOkbOut = await quoteSellOutput(totalSellAmount, tokenAddress, tradeType, fee, config?.rpcUrl);
344
+ // 2. 基于预估的 OKB 输出计算利润(单边模式,isDoubleMode = false)
345
+ const profitAmount = estimatedOkbOut > 0n
346
+ ? calculateProfitAmountByUserType(estimatedOkbOut, userType, false)
347
+ : 0n;
348
+ console.log(`[Bundle Sell] 卖出数量: ${ethers.formatUnits(totalSellAmount, tokenDecimals)} Token`);
349
+ console.log(`[Bundle Sell] 预估获得: ${ethers.formatEther(estimatedOkbOut)} OKB`);
350
+ console.log(`[Bundle Sell] 利润: ${ethers.formatEther(profitAmount)} OKB (userType=${userType})`);
261
351
  // ========================================
262
352
  // 同步构建授权和调用
263
353
  // ========================================
264
354
  const authorizations = signAuthorizationsWithNonces(allWallets, walletNonces, delegateAddress, mainWalletIndex === -1 ? 0 : mainWalletIndex);
265
- // ✅ 构建调用列表(利润刮取 + 卖出调用)
355
+ // ✅ 计算每个钱包的利润(按卖出数量比例分配)
356
+ const profitPerWallet = [];
357
+ if (profitAmount > 0n && totalSellAmount > 0n) {
358
+ for (let i = 0; i < wallets.length; i++) {
359
+ const sellAmount = sellAmountsWei[i];
360
+ if (sellAmount > 0n) {
361
+ // 按比例分配利润
362
+ const walletProfit = (profitAmount * sellAmount) / totalSellAmount;
363
+ profitPerWallet.push(walletProfit);
364
+ }
365
+ else {
366
+ profitPerWallet.push(0n);
367
+ }
368
+ }
369
+ }
370
+ else {
371
+ for (let i = 0; i < wallets.length; i++) {
372
+ profitPerWallet.push(0n);
373
+ }
374
+ }
375
+ // ✅ 构建调用列表(卖出调用 + 每个钱包的利润刮取)
266
376
  const calls = [];
267
- // 利润刮取(从主钱包)- 注意:卖出后利润从所得 OKB 中扣,需要在卖出后执行
268
- // 由于 EIP-7702 是单笔交易原子执行,利润刮取调用放在卖出调用之后
377
+ // 1. 先执行卖出,每个钱包卖出代币获得 OKB
269
378
  const sellCalls = buildSellCalls(wallets, sellAmountsWei, tokenAddress, tradeType, actualRouter, fee);
270
379
  calls.push(...sellCalls);
271
- // ✅ 卖出后从主钱包扣除利润
272
- if (profitAmount > 0n) {
273
- calls.push({
274
- target: mainWallet.address,
275
- allowFailure: false,
276
- value: 0n, // 卖出后扣除,不需要额外 value
277
- callData: delegateInterface.encodeFunctionData('transferTo', [profitRecipient]),
278
- });
380
+ // 2. 每个卖出钱包从自己的卖出所得中扣除利润
381
+ // 使用 transferAmount 传递精确金额
382
+ for (let i = 0; i < wallets.length; i++) {
383
+ const walletProfit = profitPerWallet[i];
384
+ if (walletProfit > 0n) {
385
+ calls.push({
386
+ target: wallets[i].address,
387
+ allowFailure: false,
388
+ value: 0n,
389
+ callData: delegateInterface.encodeFunctionData('transferAmount', [
390
+ profitRecipient,
391
+ walletProfit,
392
+ ]),
393
+ });
394
+ }
279
395
  }
280
396
  // ========================================
281
397
  // 同步构建并签名交易
@@ -286,7 +402,10 @@ export async function bundleSell(params) {
286
402
  metadata: {
287
403
  totalSellAmount: ethers.formatUnits(totalSellAmount, tokenDecimals),
288
404
  walletCount: validSellCount,
289
- profitAmount: ethers.formatUnits(profitAmount, tokenDecimals),
405
+ estimatedOkbOut: ethers.formatEther(estimatedOkbOut), // ✅ 预估卖出所得 OKB
406
+ profitAmount: ethers.formatEther(profitAmount), // ✅ 利润是 OKB,用 18 位精度
407
+ profitRecipient,
408
+ userType,
290
409
  },
291
410
  };
292
411
  }
@@ -9,9 +9,10 @@
9
9
  * 支持 FLAP 内盘、V2 和 V3 三种交易类型
10
10
  * 只生成签名,由调用方决定如何提交
11
11
  */
12
- import { ethers } from 'ethers';
13
- import { getCachedProvider, createWallet, signAuthorization, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync, calculateProfitAmountByTxCount, getProfitRecipient, generateRandomWallets, } from './utils.js';
14
- import { UNIFIED_DELEGATE_ADDRESS, POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, FLAP_PORTAL_ADDRESS, WOKB_ADDRESS, V3_FEE_TIERS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, ERC20_ABI, } from './constants.js';
12
+ import { ethers, Contract, JsonRpcProvider } from 'ethers';
13
+ import { quoteV2, quoteV3 } from '../../utils/quote-helpers.js';
14
+ import { getCachedProvider, createWallet, signAuthorization, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync, calculateProfitAmountByUserType, calculateProfitAmountByTxCount, getProfitRecipient, generateRandomWallets, } from './utils.js';
15
+ import { UNIFIED_DELEGATE_ADDRESS, POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, FLAP_PORTAL_ADDRESS, WOKB_ADDRESS, V3_FEE_TIERS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, ERC20_ABI, XLAYER_RPC_URL, XLAYER_CHAIN_ID, } from './constants.js';
15
16
  // ========================================
16
17
  // 路由地址获取
17
18
  // ========================================
@@ -26,6 +27,81 @@ function getDefaultRouter(tradeType) {
26
27
  }
27
28
  }
28
29
  // ========================================
30
+ // 卖出报价(Token → OKB)
31
+ // ========================================
32
+ /**
33
+ * 获取卖出报价:预估卖出代币能得到多少 OKB
34
+ */
35
+ async function quoteSellOutput(sellAmount, tokenAddress, tradeType, fee, rpcUrl) {
36
+ if (sellAmount <= 0n)
37
+ return 0n;
38
+ const provider = new JsonRpcProvider(rpcUrl || XLAYER_RPC_URL, { chainId: XLAYER_CHAIN_ID, name: 'xlayer', ensAddress: undefined });
39
+ try {
40
+ switch (tradeType) {
41
+ case 'FLAP': {
42
+ // FLAP Portal 报价
43
+ const portalContract = new Contract(FLAP_PORTAL_ADDRESS, FLAP_PORTAL_ABI, provider);
44
+ // 先尝试 quoteSell
45
+ try {
46
+ const okbOut = await portalContract.quoteSell(tokenAddress, sellAmount);
47
+ console.log(`[Bundle Swap] FLAP quoteSell 成功: ${ethers.formatEther(okbOut)} OKB`);
48
+ return BigInt(okbOut.toString());
49
+ }
50
+ catch {
51
+ console.warn(`[Bundle Swap] FLAP quoteSell 失败,尝试 quoteExactInput...`);
52
+ }
53
+ // 再尝试 quoteExactInput
54
+ try {
55
+ const okbOut = await portalContract.quoteExactInput({
56
+ inputToken: tokenAddress,
57
+ outputToken: ethers.ZeroAddress,
58
+ inputAmount: sellAmount,
59
+ });
60
+ console.log(`[Bundle Swap] FLAP quoteExactInput 成功: ${ethers.formatEther(okbOut)} OKB`);
61
+ return BigInt(okbOut.toString());
62
+ }
63
+ catch {
64
+ console.warn(`[Bundle Swap] FLAP quoteExactInput 失败,尝试 V2...`);
65
+ }
66
+ // 回退到 V2
67
+ const v2Result = await quoteV2(provider, tokenAddress, WOKB_ADDRESS, sellAmount, 'XLAYER');
68
+ if (v2Result.amountOut > 0n) {
69
+ console.log(`[Bundle Swap] FLAP 回退 V2 报价成功: ${ethers.formatEther(v2Result.amountOut)} OKB`);
70
+ return v2Result.amountOut;
71
+ }
72
+ break;
73
+ }
74
+ case 'V2': {
75
+ const result = await quoteV2(provider, tokenAddress, WOKB_ADDRESS, sellAmount, 'XLAYER');
76
+ if (result.amountOut > 0n) {
77
+ console.log(`[Bundle Swap] V2 报价成功: ${ethers.formatEther(result.amountOut)} OKB`);
78
+ return result.amountOut;
79
+ }
80
+ break;
81
+ }
82
+ case 'V3': {
83
+ const result = await quoteV3(provider, tokenAddress, WOKB_ADDRESS, sellAmount, 'XLAYER', fee);
84
+ if (result.amountOut > 0n) {
85
+ console.log(`[Bundle Swap] V3 报价成功 (fee=${result.fee}): ${ethers.formatEther(result.amountOut)} OKB`);
86
+ return result.amountOut;
87
+ }
88
+ // 回退到 V2
89
+ const v2Result = await quoteV2(provider, tokenAddress, WOKB_ADDRESS, sellAmount, 'XLAYER');
90
+ if (v2Result.amountOut > 0n) {
91
+ console.log(`[Bundle Swap] V3 回退 V2 报价成功: ${ethers.formatEther(v2Result.amountOut)} OKB`);
92
+ return v2Result.amountOut;
93
+ }
94
+ break;
95
+ }
96
+ }
97
+ }
98
+ catch (error) {
99
+ console.error(`[Bundle Swap] 报价失败:`, error);
100
+ }
101
+ console.warn(`[Bundle Swap] 无法获取报价,利润计算可能不准确`);
102
+ return 0n;
103
+ }
104
+ // ========================================
29
105
  // 构建卖出并转账调用
30
106
  // ========================================
31
107
  function buildSellAndTransferCall(sellerWallet, sellAmount, tokenAddress, recipient, tradeType, routerAddress, fee) {
@@ -202,10 +278,16 @@ export async function bundleSwap(params) {
202
278
  const hopWalletInfos = hopCount > 0 ? generateRandomWallets(hopCount) : [];
203
279
  const hopWallets = hopWalletInfos.map(info => createWallet(info.privateKey, provider));
204
280
  const sellAmountWei = ethers.parseUnits(sellAmount, tokenDecimals);
205
- // ✅ 计算利润(换手模式:2笔交易,使用双边费率)
206
- const totalTxCount = 2; // 1 + 1 买
207
- const profitAmount = calculateProfitAmountByTxCount(sellAmountWei, totalTxCount, userType);
281
+ // ✅ 修复:获取卖出报价,基于预估的 OKB 输出计算利润
282
+ const estimatedOkbOut = await quoteSellOutput(sellAmountWei, tokenAddress, tradeType, fee, config?.rpcUrl);
283
+ // 使用双边费率计算利润(换手 = + 买)
284
+ const profitAmount = estimatedOkbOut > 0n
285
+ ? calculateProfitAmountByUserType(estimatedOkbOut, userType, true) // isDoubleMode = true
286
+ : 0n;
208
287
  const profitRecipient = getProfitRecipient(config);
288
+ console.log(`[Bundle Swap] 卖出数量: ${sellAmount} Token`);
289
+ console.log(`[Bundle Swap] 预估获得: ${ethers.formatEther(estimatedOkbOut)} OKB`);
290
+ console.log(`[Bundle Swap] 利润: ${ethers.formatEther(profitAmount)} OKB (userType=${userType})`);
209
291
  // ========================================
210
292
  // 并行获取所有异步数据
211
293
  // ========================================
@@ -260,8 +342,11 @@ export async function bundleSwap(params) {
260
342
  sellerAddress: sellerWallet.address,
261
343
  buyerAddresses: [buyerWallet.address],
262
344
  sellAmount: ethers.formatUnits(sellAmountWei, tokenDecimals),
345
+ estimatedOkbOut: ethers.formatEther(estimatedOkbOut), // ✅ 预估卖出所得 OKB
263
346
  estimatedBuyAmount: '0',
264
- profitAmount: ethers.formatEther(profitAmount),
347
+ profitAmount: ethers.formatEther(profitAmount), // ✅ 利润是 OKB
348
+ profitRecipient,
349
+ userType,
265
350
  },
266
351
  };
267
352
  }
@@ -296,10 +381,16 @@ export async function bundleBatchSwap(params) {
296
381
  const hopWalletInfos = hopCount > 0 ? generateRandomWallets(hopCount) : [];
297
382
  const hopWallets = hopWalletInfos.map(info => createWallet(info.privateKey, provider));
298
383
  const sellAmountWei = ethers.parseUnits(sellAmount, tokenDecimals);
299
- // ✅ 计算利润(换手模式:1卖 + N买,根据交易数量计算)
300
- const totalTxCount = 1 + buyerWallets.length; // 1 卖 + N 买
301
- const profitAmount = calculateProfitAmountByTxCount(sellAmountWei, totalTxCount, userType);
384
+ // ✅ 修复:获取卖出报价,基于预估的 OKB 输出计算利润
385
+ const estimatedOkbOut = await quoteSellOutput(sellAmountWei, tokenAddress, tradeType, fee, config?.rpcUrl);
386
+ // 使用双边费率计算利润(换手 = + 买)
387
+ const profitAmount = estimatedOkbOut > 0n
388
+ ? calculateProfitAmountByUserType(estimatedOkbOut, userType, true) // isDoubleMode = true
389
+ : 0n;
302
390
  const profitRecipient = getProfitRecipient(config);
391
+ console.log(`[Bundle Batch Swap] 卖出数量: ${sellAmount} Token`);
392
+ console.log(`[Bundle Batch Swap] 预估获得: ${ethers.formatEther(estimatedOkbOut)} OKB`);
393
+ console.log(`[Bundle Batch Swap] 利润: ${ethers.formatEther(profitAmount)} OKB (userType=${userType})`);
303
394
  // ========================================
304
395
  // 并行获取所有异步数据
305
396
  // ========================================
@@ -387,8 +478,11 @@ export async function bundleBatchSwap(params) {
387
478
  sellerAddress: sellerWallet.address,
388
479
  buyerAddresses: buyerWallets.map(w => w.address),
389
480
  sellAmount: ethers.formatUnits(sellAmountWei, tokenDecimals),
481
+ estimatedOkbOut: ethers.formatEther(estimatedOkbOut), // ✅ 预估卖出所得 OKB
390
482
  estimatedBuyAmount: '0',
391
- profitAmount: ethers.formatEther(profitAmount),
483
+ profitAmount: ethers.formatEther(profitAmount), // ✅ 利润是 OKB
484
+ profitRecipient,
485
+ userType,
392
486
  },
393
487
  };
394
488
  }
@@ -4,7 +4,7 @@
4
4
  export declare const XLAYER_CHAIN_ID = 196;
5
5
  export declare const XLAYER_RPC_URL = "https://xlayerrpc.okx.com";
6
6
  /** 已部署的 UnifiedDelegate 合约地址 */
7
- export declare const UNIFIED_DELEGATE_ADDRESS = "0x32dF8c0811D370A22D517b9BD000748643Baa804";
7
+ export declare const UNIFIED_DELEGATE_ADDRESS = "0x33C577Fa6A701E30F35B4B0DE3A361bfF400F0Cc";
8
8
  /** Multicall3 地址 (标准部署) */
9
9
  export declare const MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
10
10
  /** WOKB 地址 */
@@ -42,6 +42,30 @@ export declare const PROFIT_CONFIG: {
42
42
  export declare const DEFAULT_GAS_LIMIT = 2000000n;
43
43
  export declare const DEFAULT_DEADLINE_MINUTES = 20;
44
44
  export declare const DEFAULT_SLIPPAGE_BPS = 100;
45
+ export declare const GAS_ESTIMATE: {
46
+ /** 基础 gas(交易本身 + Multicall3 调用) */
47
+ readonly BASE: 80000n;
48
+ /** 每个 EIP-7702 授权的 gas */
49
+ readonly PER_AUTHORIZATION: 25000n;
50
+ /** 每个 Multicall 调用的基础 gas */
51
+ readonly PER_CALL_BASE: 50000n;
52
+ /** 买入操作额外 gas(DEX swap) */
53
+ readonly BUY_EXTRA: 150000n;
54
+ /** 卖出操作额外 gas(DEX swap + approve) */
55
+ readonly SELL_EXTRA: 180000n;
56
+ /** 转账操作 gas */
57
+ readonly TRANSFER: 30000n;
58
+ /** 创建代币额外 gas(普通创建) */
59
+ readonly CREATE_TOKEN: 500000n;
60
+ /** 毕业操作额外 gas(流动性迁移到 DEX,约 800 万) */
61
+ readonly GRADUATE: 8000000n;
62
+ /** 最小 gas limit */
63
+ readonly MIN: 200000n;
64
+ /** 最大 gas limit */
65
+ readonly MAX: 15000000n;
66
+ /** 安全系数(1.3 = 130%) */
67
+ readonly SAFETY_MULTIPLIER: 1.3;
68
+ };
45
69
  export declare const V3_FEE_TIERS: {
46
70
  readonly LOWEST: 100;
47
71
  readonly LOW: 500;
@@ -49,7 +73,7 @@ export declare const V3_FEE_TIERS: {
49
73
  readonly STANDARD: 3000;
50
74
  readonly HIGH: 10000;
51
75
  };
52
- export declare const UNIFIED_DELEGATE_ABI: readonly ["function transferTo(address recipient) external payable", "function executeBuy(address router, address wokb, address tokenOut, uint24 fee, uint256 amountIn) external payable", "function executeSell(address router, address tokenIn, address wokb, uint24 fee, uint256 amountIn) external", "function executeSellAndTransfer(address router, address tokenIn, address wokb, uint24 fee, uint256 amountIn, address recipient) external", "function executeBuyV2(address router, address[] calldata path, uint256 amountIn) external payable", "function executeSellV2(address router, address[] calldata path, uint256 amountIn) external", "function executeSellAndTransferV2(address router, address[] calldata path, uint256 amountIn, address payable recipient) external", "function executeApprove(address token, address spender, uint256 amount) external", "function executeTransfer(address token, address to, uint256 amount) external", "function executeTransferAll(address token, address to) external", "function executeBatch((address target, uint256 value, bytes data)[] calls) external payable"];
76
+ export declare const UNIFIED_DELEGATE_ABI: readonly ["function transferTo(address recipient) external payable", "function transferAmount(address recipient, uint256 amount) external", "function executeBuy(address router, address wokb, address tokenOut, uint24 fee, uint256 amountIn) external payable", "function executeSell(address router, address tokenIn, address wokb, uint24 fee, uint256 amountIn) external", "function executeSellAndTransfer(address router, address tokenIn, address wokb, uint24 fee, uint256 amountIn, address recipient) external", "function executeBuyV2(address router, address[] calldata path, uint256 amountIn) external payable", "function executeSellV2(address router, address[] calldata path, uint256 amountIn) external", "function executeSellAndTransferV2(address router, address[] calldata path, uint256 amountIn, address payable recipient) external", "function executeApprove(address token, address spender, uint256 amount) external", "function executeTransfer(address token, address to, uint256 amount) external", "function executeTransferAll(address token, address to) external", "function executeBatch((address target, uint256 value, bytes data)[] calls) external payable"];
53
77
  export declare const ERC20_ABI: readonly ["function approve(address spender, uint256 amount) external returns (bool)", "function transfer(address to, uint256 amount) external returns (bool)", "function transferFrom(address from, address to, uint256 amount) external returns (bool)", "function balanceOf(address account) external view returns (uint256)", "function allowance(address owner, address spender) external view returns (uint256)", "function decimals() external view returns (uint8)", "function symbol() external view returns (string)"];
54
78
  export declare const MULTICALL3_ABI: readonly ["function aggregate3Value((address target, bool allowFailure, uint256 value, bytes callData)[] calls) external payable returns ((bool success, bytes returnData)[] returnData)"];
55
79
  export declare const V3_ROUTER_ABI: readonly ["function exactInputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96)) external payable returns (uint256 amountOut)", "function multicall(uint256 deadline, bytes[] data) external payable returns (bytes[] results)", "function unwrapWETH9(uint256 amountMinimum, address recipient) external payable", "function refundETH() external payable"];
@@ -10,7 +10,7 @@ export const XLAYER_RPC_URL = 'https://xlayerrpc.okx.com';
10
10
  // 合约地址
11
11
  // ========================================
12
12
  /** 已部署的 UnifiedDelegate 合约地址 */
13
- export const UNIFIED_DELEGATE_ADDRESS = '0x32dF8c0811D370A22D517b9BD000748643Baa804';
13
+ export const UNIFIED_DELEGATE_ADDRESS = '0x33C577Fa6A701E30F35B4B0DE3A361bfF400F0Cc';
14
14
  /** Multicall3 地址 (标准部署) */
15
15
  export const MULTICALL3_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11';
16
16
  /** WOKB 地址 */
@@ -58,6 +58,33 @@ export const DEFAULT_GAS_LIMIT = 2000000n;
58
58
  export const DEFAULT_DEADLINE_MINUTES = 20;
59
59
  export const DEFAULT_SLIPPAGE_BPS = 100; // 1%
60
60
  // ========================================
61
+ // Gas 估算配置(动态计算)
62
+ // ========================================
63
+ export const GAS_ESTIMATE = {
64
+ /** 基础 gas(交易本身 + Multicall3 调用) */
65
+ BASE: 80000n,
66
+ /** 每个 EIP-7702 授权的 gas */
67
+ PER_AUTHORIZATION: 25000n,
68
+ /** 每个 Multicall 调用的基础 gas */
69
+ PER_CALL_BASE: 50000n,
70
+ /** 买入操作额外 gas(DEX swap) */
71
+ BUY_EXTRA: 150000n,
72
+ /** 卖出操作额外 gas(DEX swap + approve) */
73
+ SELL_EXTRA: 180000n,
74
+ /** 转账操作 gas */
75
+ TRANSFER: 30000n,
76
+ /** 创建代币额外 gas(普通创建) */
77
+ CREATE_TOKEN: 500000n,
78
+ /** 毕业操作额外 gas(流动性迁移到 DEX,约 800 万) */
79
+ GRADUATE: 8000000n,
80
+ /** 最小 gas limit */
81
+ MIN: 200000n,
82
+ /** 最大 gas limit */
83
+ MAX: 15000000n,
84
+ /** 安全系数(1.3 = 130%) */
85
+ SAFETY_MULTIPLIER: 1.3,
86
+ };
87
+ // ========================================
61
88
  // 费率档位
62
89
  // ========================================
63
90
  export const V3_FEE_TIERS = {
@@ -75,6 +102,7 @@ export const UNIFIED_DELEGATE_ABI = [
75
102
  // 转账功能
76
103
  // ========================================
77
104
  'function transferTo(address recipient) external payable',
105
+ 'function transferAmount(address recipient, uint256 amount) external',
78
106
  // ========================================
79
107
  // V3 买卖功能 (PotatoSwap V3 / DYORSwap)
80
108
  // ========================================
@@ -15,7 +15,7 @@
15
15
  */
16
16
  export * from './constants.js';
17
17
  export * from './types.js';
18
- export { bigintToBytes, addressToBytes, hexToBytes, trimLeadingZeros, getCachedProvider, clearProviderCache, createWallet, generateRandomWallet, generateRandomWallets, signAuthorization, signAuthorizationWithNonce, signAuthorizationsBatch, buildEIP7702Transaction, calculateProfitAmount, calculateProfitAmountByUserType, calculateProfitAmountByTxCount, getProfitRateBps, getProfitRateBpsDouble, getProfitRecipient, getDeadline, delay, type UserType, } from './utils.js';
18
+ export { bigintToBytes, addressToBytes, hexToBytes, trimLeadingZeros, getCachedProvider, clearProviderCache, createWallet, generateRandomWallet, generateRandomWallets, signAuthorization, signAuthorizationWithNonce, signAuthorizationsBatch, buildEIP7702Transaction, estimateGasLimit, estimateGasLimitSimple, calculateProfitAmount, calculateProfitAmountByUserType, calculateProfitAmountByTxCount, getProfitRateBps, getProfitRateBpsDouble, getProfitRecipient, getDeadline, delay, type UserType, type OperationType, } from './utils.js';
19
19
  export { bundleBuy, } from './bundle-buy.js';
20
20
  export { bundleSell, } from './bundle-sell.js';
21
21
  export { bundleSwap, bundleBatchSwap, bundleMultiSwap, type BundleMultiSwapParams, type BundleMultiSwapResult, } from './bundle-swap.js';
@@ -24,7 +24,11 @@ export * from './types.js';
24
24
  // ============================================================================
25
25
  // 工具函数
26
26
  // ============================================================================
27
- export { bigintToBytes, addressToBytes, hexToBytes, trimLeadingZeros, getCachedProvider, clearProviderCache, createWallet, generateRandomWallet, generateRandomWallets, signAuthorization, signAuthorizationWithNonce, signAuthorizationsBatch, buildEIP7702Transaction, calculateProfitAmount, calculateProfitAmountByUserType, calculateProfitAmountByTxCount, getProfitRateBps, getProfitRateBpsDouble, getProfitRecipient, getDeadline, delay, } from './utils.js';
27
+ export { bigintToBytes, addressToBytes, hexToBytes, trimLeadingZeros, getCachedProvider, clearProviderCache, createWallet, generateRandomWallet, generateRandomWallets, signAuthorization, signAuthorizationWithNonce, signAuthorizationsBatch, buildEIP7702Transaction,
28
+ // Gas 估算
29
+ estimateGasLimit, estimateGasLimitSimple,
30
+ // 利润计算
31
+ calculateProfitAmount, calculateProfitAmountByUserType, calculateProfitAmountByTxCount, getProfitRateBps, getProfitRateBpsDouble, getProfitRecipient, getDeadline, delay, } from './utils.js';
28
32
  // ============================================================================
29
33
  // 批量买入
30
34
  // ============================================================================
@@ -97,9 +97,18 @@ export interface BundleSellResult {
97
97
  txHash?: string;
98
98
  /** 元数据 */
99
99
  metadata: {
100
+ /** 总卖出代币数量 */
100
101
  totalSellAmount: string;
102
+ /** 参与卖出的钱包数量 */
101
103
  walletCount: number;
104
+ /** 预估卖出获得的 OKB 数量 */
105
+ estimatedOkbOut: string;
106
+ /** 利润金额(OKB) */
102
107
  profitAmount: string;
108
+ /** 利润接收地址 */
109
+ profitRecipient: string;
110
+ /** 用户类型 */
111
+ userType: string;
103
112
  };
104
113
  }
105
114
  export interface BundleApproveParams {
@@ -186,8 +195,15 @@ export interface BundleSwapResult {
186
195
  sellerAddress: string;
187
196
  buyerAddresses: string[];
188
197
  sellAmount: string;
198
+ /** 预估卖出获得的 OKB 数量 */
199
+ estimatedOkbOut?: string;
189
200
  estimatedBuyAmount: string;
201
+ /** 利润金额(OKB) */
190
202
  profitAmount: string;
203
+ /** 利润接收地址 */
204
+ profitRecipient?: string;
205
+ /** 用户类型 */
206
+ userType?: string;
191
207
  };
192
208
  }
193
209
  export interface MultiHopTransferParams {
@@ -362,6 +378,8 @@ export interface BundleCreateBuyParams extends CreateTokenParams {
362
378
  buyerPrivateKeys: string[];
363
379
  /** 每个买入钱包的金额 (OKB) */
364
380
  buyAmounts: string[];
381
+ /** 用户类型(影响利润率) */
382
+ userType?: 'v0' | 'v1' | 'default';
365
383
  /** 配置 */
366
384
  config?: EIP7702Config;
367
385
  }
@@ -391,6 +409,8 @@ export interface BundleCreateToDexParams extends CreateTokenParams {
391
409
  dexBuyerPrivateKeys?: string[];
392
410
  /** 外盘买入金额 (OKB) */
393
411
  dexBuyAmounts?: string[];
412
+ /** 用户类型(影响利润率) */
413
+ userType?: 'v0' | 'v1' | 'default';
394
414
  /** 配置 */
395
415
  config?: EIP7702Config;
396
416
  }
@@ -438,6 +458,8 @@ export interface BundleGraduateBuyParams {
438
458
  dexAddresses?: string[];
439
459
  /** 毕业金额(OKB) */
440
460
  graduationAmount?: number;
461
+ /** 用户类型(影响利润率) */
462
+ userType?: 'v0' | 'v1' | 'default';
441
463
  /** 配置 */
442
464
  config?: EIP7702Config;
443
465
  }
@@ -9,6 +9,24 @@ export declare function hexToBytes(hex: string): Uint8Array;
9
9
  export declare function trimLeadingZeros(bytes: Uint8Array): Uint8Array;
10
10
  export declare function getCachedProvider(rpcUrl?: string): JsonRpcProvider;
11
11
  export declare function clearProviderCache(): void;
12
+ /**
13
+ * 操作类型(用于 gas 估算)
14
+ */
15
+ export type OperationType = 'transfer' | 'buy' | 'sell' | 'swap' | 'create' | 'approve' | 'graduate' | 'unknown';
16
+ /**
17
+ * 动态估算 Gas Limit
18
+ *
19
+ * @param authorizationCount 授权数量
20
+ * @param calls Multicall 调用列表
21
+ * @param configGasLimit 配置的 gas limit(可选,优先使用)
22
+ * @returns 估算的 gas limit
23
+ */
24
+ export declare function estimateGasLimit(authorizationCount: number, calls: Call3Value[], configGasLimit?: bigint): bigint;
25
+ /**
26
+ * 简化版 gas 估算(仅根据调用数量)
27
+ * 适用于无法分析 callData 的场景
28
+ */
29
+ export declare function estimateGasLimitSimple(authorizationCount: number, callCount: number, operationType?: 'simple' | 'buy' | 'sell' | 'swap' | 'create' | 'graduate', configGasLimit?: bigint): bigint;
12
30
  export declare function createWallet(privateKey: string, provider?: JsonRpcProvider): Wallet;
13
31
  export declare function generateRandomWallet(): GeneratedWallet;
14
32
  export declare function generateRandomWallets(count: number): GeneratedWallet[];
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { ethers, Wallet, JsonRpcProvider } from 'ethers';
5
5
  import * as rlp from '@ethereumjs/rlp';
6
- import { XLAYER_CHAIN_ID, XLAYER_RPC_URL, MULTICALL3_ADDRESS, DEFAULT_GAS_LIMIT, EIP7702_TX_TYPE, EIP7702_AUTH_PREFIX, MULTICALL3_ABI, PROFIT_CONFIG, DEFAULT_DEADLINE_MINUTES, } from './constants.js';
6
+ import { XLAYER_CHAIN_ID, XLAYER_RPC_URL, MULTICALL3_ADDRESS, GAS_ESTIMATE, EIP7702_TX_TYPE, EIP7702_AUTH_PREFIX, MULTICALL3_ABI, PROFIT_CONFIG, DEFAULT_DEADLINE_MINUTES, } from './constants.js';
7
7
  // ========================================
8
8
  // 字节转换工具
9
9
  // ========================================
@@ -49,6 +49,157 @@ export function getCachedProvider(rpcUrl = XLAYER_RPC_URL) {
49
49
  export function clearProviderCache() {
50
50
  providerCache.clear();
51
51
  }
52
+ /**
53
+ * 分析 callData 推断操作类型
54
+ */
55
+ function inferOperationType(callData) {
56
+ if (!callData || callData === '0x')
57
+ return 'transfer';
58
+ // 根据函数签名推断操作类型
59
+ const selector = callData.slice(0, 10).toLowerCase();
60
+ // 常见的函数选择器
61
+ const selectors = {
62
+ // 转账
63
+ '0xa9059cbb': 'transfer', // transfer(address,uint256)
64
+ // 买入相关
65
+ '0x7ff36ab5': 'buy', // swapExactETHForTokens
66
+ '0xb6f9de95': 'buy', // swapExactETHForTokensSupportingFeeOnTransferTokens
67
+ '0x414bf389': 'buy', // exactInputSingle (V3)
68
+ '0xc04b8d59': 'buy', // exactInput (V3)
69
+ // 卖出相关
70
+ '0x18cbafe5': 'sell', // swapExactTokensForETH
71
+ '0x791ac947': 'sell', // swapExactTokensForETHSupportingFeeOnTransferTokens
72
+ '0xdb3e2198': 'sell', // exactOutputSingle (V3)
73
+ // 授权
74
+ '0x095ea7b3': 'approve', // approve(address,uint256)
75
+ // 创建代币
76
+ '0x': 'create', // 需要更精确的匹配
77
+ };
78
+ // UnifiedDelegate 函数选择器(计算得到的 4 字节选择器)
79
+ const delegateSelectors = {
80
+ '0x3edd1128': 'transfer', // transferTo(address)
81
+ '0x2ada9a96': 'transfer', // transferAmount(address,uint256)
82
+ '0x2cdaa6d3': 'buy', // executeBuy(address,address,address,uint24,uint256)
83
+ '0x2e87f7de': 'buy', // executeBuyV2(address,address[],uint256)
84
+ '0x6fcda3a1': 'sell', // executeSell(address,address,address,uint24,uint256)
85
+ '0x0a3e9e95': 'sell', // executeSellV2(address,address[],uint256)
86
+ '0x72c18b75': 'sell', // executeSellAndTransfer(address,address,address,uint24,uint256,address)
87
+ '0x9db6b375': 'sell', // executeSellAndTransferV2(address,address[],uint256,address)
88
+ };
89
+ // 先检查标准选择器
90
+ if (selectors[selector]) {
91
+ return selectors[selector];
92
+ }
93
+ // 检查 UnifiedDelegate 选择器
94
+ if (delegateSelectors[selector]) {
95
+ return delegateSelectors[selector];
96
+ }
97
+ // 检查 callData 长度和模式
98
+ if (callData.length < 20) {
99
+ return 'transfer';
100
+ }
101
+ // 默认根据 callData 长度估算
102
+ if (callData.length > 500) {
103
+ return 'swap'; // 复杂调用可能是 swap
104
+ }
105
+ return 'unknown';
106
+ }
107
+ /**
108
+ * 根据操作类型获取额外 gas
109
+ */
110
+ function getExtraGasForOperation(opType) {
111
+ switch (opType) {
112
+ case 'transfer':
113
+ return GAS_ESTIMATE.TRANSFER;
114
+ case 'buy':
115
+ return GAS_ESTIMATE.BUY_EXTRA;
116
+ case 'sell':
117
+ return GAS_ESTIMATE.SELL_EXTRA;
118
+ case 'swap':
119
+ return GAS_ESTIMATE.BUY_EXTRA + GAS_ESTIMATE.SELL_EXTRA;
120
+ case 'approve':
121
+ return 50000n;
122
+ case 'create':
123
+ return GAS_ESTIMATE.CREATE_TOKEN;
124
+ default:
125
+ return GAS_ESTIMATE.PER_CALL_BASE;
126
+ }
127
+ }
128
+ /**
129
+ * 动态估算 Gas Limit
130
+ *
131
+ * @param authorizationCount 授权数量
132
+ * @param calls Multicall 调用列表
133
+ * @param configGasLimit 配置的 gas limit(可选,优先使用)
134
+ * @returns 估算的 gas limit
135
+ */
136
+ export function estimateGasLimit(authorizationCount, calls, configGasLimit) {
137
+ // 如果明确指定了 gasLimit,优先使用
138
+ if (configGasLimit && configGasLimit > 0n) {
139
+ return configGasLimit;
140
+ }
141
+ // 基础 gas
142
+ let estimated = GAS_ESTIMATE.BASE;
143
+ // 授权 gas
144
+ estimated += BigInt(authorizationCount) * GAS_ESTIMATE.PER_AUTHORIZATION;
145
+ // 每个调用的 gas
146
+ for (const call of calls) {
147
+ // 基础调用 gas
148
+ estimated += GAS_ESTIMATE.PER_CALL_BASE;
149
+ // 根据操作类型添加额外 gas
150
+ const opType = inferOperationType(call.callData);
151
+ estimated += getExtraGasForOperation(opType);
152
+ }
153
+ // 应用安全系数
154
+ const withSafety = BigInt(Math.ceil(Number(estimated) * GAS_ESTIMATE.SAFETY_MULTIPLIER));
155
+ // 限制在最小和最大值之间
156
+ if (withSafety < GAS_ESTIMATE.MIN) {
157
+ return GAS_ESTIMATE.MIN;
158
+ }
159
+ if (withSafety > GAS_ESTIMATE.MAX) {
160
+ return GAS_ESTIMATE.MAX;
161
+ }
162
+ return withSafety;
163
+ }
164
+ /**
165
+ * 简化版 gas 估算(仅根据调用数量)
166
+ * 适用于无法分析 callData 的场景
167
+ */
168
+ export function estimateGasLimitSimple(authorizationCount, callCount, operationType = 'simple', configGasLimit) {
169
+ if (configGasLimit && configGasLimit > 0n) {
170
+ return configGasLimit;
171
+ }
172
+ // 毕业操作需要固定的高 gas(约 800 万)
173
+ if (operationType === 'graduate') {
174
+ return GAS_ESTIMATE.GRADUATE;
175
+ }
176
+ let estimated = GAS_ESTIMATE.BASE;
177
+ estimated += BigInt(authorizationCount) * GAS_ESTIMATE.PER_AUTHORIZATION;
178
+ estimated += BigInt(callCount) * GAS_ESTIMATE.PER_CALL_BASE;
179
+ // 根据操作类型添加额外 gas
180
+ switch (operationType) {
181
+ case 'buy':
182
+ estimated += BigInt(callCount) * GAS_ESTIMATE.BUY_EXTRA;
183
+ break;
184
+ case 'sell':
185
+ estimated += BigInt(callCount) * GAS_ESTIMATE.SELL_EXTRA;
186
+ break;
187
+ case 'swap':
188
+ estimated += BigInt(callCount) * (GAS_ESTIMATE.BUY_EXTRA + GAS_ESTIMATE.SELL_EXTRA);
189
+ break;
190
+ case 'create':
191
+ estimated += GAS_ESTIMATE.CREATE_TOKEN;
192
+ break;
193
+ default:
194
+ estimated += BigInt(callCount) * GAS_ESTIMATE.TRANSFER;
195
+ }
196
+ const withSafety = BigInt(Math.ceil(Number(estimated) * GAS_ESTIMATE.SAFETY_MULTIPLIER));
197
+ if (withSafety < GAS_ESTIMATE.MIN)
198
+ return GAS_ESTIMATE.MIN;
199
+ if (withSafety > GAS_ESTIMATE.MAX)
200
+ return GAS_ESTIMATE.MAX;
201
+ return withSafety;
202
+ }
52
203
  // ========================================
53
204
  // 钱包工具
54
205
  // ========================================
@@ -152,7 +303,8 @@ export async function batchGetNonces(addresses, provider) {
152
303
  export async function buildEIP7702Transaction(mainWallet, authorizations, calls, totalValue, config) {
153
304
  const provider = getCachedProvider(config?.rpcUrl);
154
305
  const chainId = config?.chainId ?? XLAYER_CHAIN_ID;
155
- const gasLimit = config?.gasLimit ?? DEFAULT_GAS_LIMIT;
306
+ // 动态 Gas 估算:根据授权数量和调用复杂度自动计算
307
+ const gasLimit = estimateGasLimit(authorizations.length, calls, config?.gasLimit);
156
308
  // ✅ 并行获取 nonce 和 fee data
157
309
  const [nonce, feeData] = await Promise.all([
158
310
  provider.getTransactionCount(mainWallet.address, 'pending'),
@@ -214,7 +366,8 @@ export async function buildEIP7702Transaction(mainWallet, authorizations, calls,
214
366
  */
215
367
  export function buildEIP7702TransactionSync(mainWallet, authorizations, calls, totalValue, nonce, feeData, config) {
216
368
  const chainId = config?.chainId ?? XLAYER_CHAIN_ID;
217
- const gasLimit = config?.gasLimit ?? DEFAULT_GAS_LIMIT;
369
+ // 动态 Gas 估算:根据授权数量和调用复杂度自动计算
370
+ const gasLimit = estimateGasLimit(authorizations.length, calls, config?.gasLimit);
218
371
  const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas || ethers.parseUnits('1', 'gwei');
219
372
  const maxFeePerGas = feeData.maxFeePerGas || ethers.parseUnits('50', 'gwei');
220
373
  const multicall3 = new ethers.Interface(MULTICALL3_ABI);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.7.22",
3
+ "version": "1.7.24",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",