four-flap-meme-sdk 1.7.22 → 1.7.23

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.
@@ -10,7 +10,7 @@
10
10
  */
11
11
  import { ethers, Interface, Contract } from 'ethers';
12
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';
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([
@@ -426,7 +428,8 @@ export async function bundleCreateToDex(params) {
426
428
  * @returns 只返回签名后的交易,不发送
427
429
  */
428
430
  export async function bundleGraduateBuy(params) {
429
- const { tokenAddress, privateKeys, payerPrivateKey, amountMode, totalBuyAmount, minAmount, maxAmount, walletAmounts, enableDexBuy = false, extensionData = '0x', curveAddresses, dexAddresses, graduationAmount, config, } = params;
431
+ const { tokenAddress, privateKeys, payerPrivateKey, amountMode, totalBuyAmount, minAmount, maxAmount, walletAmounts, enableDexBuy = false, extensionData = '0x', curveAddresses, dexAddresses, graduationAmount, userType = 'v0', // 用户类型(影响利润率)
432
+ config, } = params;
430
433
  const provider = getCachedProvider(config?.rpcUrl);
431
434
  const delegateAddress = (config?.delegateAddress && config.delegateAddress.trim()) || UNIFIED_DELEGATE_ADDRESS;
432
435
  const delegateInterface = new Interface(UNIFIED_DELEGATE_ABI);
@@ -519,11 +522,11 @@ export async function bundleGraduateBuy(params) {
519
522
  const allWallets = Array.from(allWalletsMap.values());
520
523
  const mainWallet = payerWallet;
521
524
  const mainWalletIndex = allWallets.findIndex(w => w.address.toLowerCase() === mainWallet.address.toLowerCase());
522
- // 计算金额
525
+ // 计算金额和利润(单边模式:只有买入)
523
526
  const curveTotalWei = curveBuyers.reduce((sum, b) => sum + b.amount, 0n);
524
527
  const dexTotalWei = dexBuyers.reduce((sum, b) => sum + b.amount, 0n);
525
528
  const totalAmount = curveTotalWei + dexTotalWei;
526
- const profitAmount = calculateProfitAmount(totalAmount);
529
+ const profitAmount = calculateProfitAmountByUserType(totalAmount, userType, false); // ✅ 支持 userType
527
530
  const profitRecipient = getProfitRecipient(config);
528
531
  // 并行获取 nonce 和 fee data
529
532
  const [nonces, feeData] = await Promise.all([
@@ -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 地址 */
@@ -49,7 +49,7 @@ export declare const V3_FEE_TIERS: {
49
49
  readonly STANDARD: 3000;
50
50
  readonly HIGH: 10000;
51
51
  };
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"];
52
+ 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
53
  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
54
  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
55
  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 地址 */
@@ -75,6 +75,7 @@ export const UNIFIED_DELEGATE_ABI = [
75
75
  // 转账功能
76
76
  // ========================================
77
77
  'function transferTo(address recipient) external payable',
78
+ 'function transferAmount(address recipient, uint256 amount) external',
78
79
  // ========================================
79
80
  // V3 买卖功能 (PotatoSwap V3 / DYORSwap)
80
81
  // ========================================
@@ -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
  }
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.23",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",