four-flap-meme-sdk 1.7.59 → 1.7.60

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.
@@ -330,15 +330,11 @@ export async function bundleSwap(params) {
330
330
  const sellerNonce = nonces[0];
331
331
  const buyerNonce = nonces[1];
332
332
  // ========================================
333
- // 同步签署授权
333
+ // 同步签署授权(只需要卖家和买家,不需要 hop wallets)
334
334
  // ========================================
335
335
  const authorizations = [];
336
336
  // 卖家授权(交易发起者,nonce +1)
337
337
  authorizations.push(signAuthorization(sellerWallet, delegateAddress, BigInt(sellerNonce + 1)));
338
- // 中间钱包授权(新生成的钱包,nonce 都是 0)
339
- for (const hopWallet of hopWallets) {
340
- authorizations.push(signAuthorization(hopWallet, delegateAddress, 0n));
341
- }
342
338
  // 买家授权
343
339
  authorizations.push(signAuthorization(buyerWallet, delegateAddress, BigInt(buyerNonce)));
344
340
  // 等待所有授权签名完成
@@ -354,40 +350,26 @@ export async function bundleSwap(params) {
354
350
  throw new Error('FLAP 内盘模式需要有效的报价,请检查代币地址或稍后重试');
355
351
  }
356
352
  const calls = [];
357
- // ✅ 修复:无论 hopCount 是多少,都需要完整的资金流转(卖出 -> 扣利润 -> 转账 -> 买入)
358
- const firstRecipient = hopWallets.length > 0 ? hopWallets[0].address : buyerWallet.address;
359
- // 1. 卖家卖出
353
+ // ========================================
354
+ // 修复:普通换手模式逻辑
355
+ // 1. 卖家卖出代币 → OKB 留在卖家账户
356
+ // 2. 买家用自己预存的 OKB 买入代币
357
+ // 3. 利润从买家的 OKB 中扣除
358
+ // ========================================
359
+ // 1. 卖家卖出(OKB 留在卖家账户,不转给买家)
360
360
  const sellCall = buildSellCallWithAmountInternal(sellerWallet, sellAmountWei, tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
361
361
  calls.push(sellCall);
362
- // 2. 利润刮取(从卖家余额扣除)
362
+ // 2. 利润刮取(从买家余额扣除,在买入之前)
363
363
  if (profitAmount > 0n) {
364
364
  calls.push({
365
- target: sellerWallet.address,
365
+ target: buyerWallet.address, // ✅ 改为从买家扣利润
366
366
  allowFailure: false,
367
367
  value: 0n,
368
368
  callData: delegateInterface.encodeFunctionData('transferAmount', [profitRecipient, profitAmount]),
369
369
  });
370
370
  }
371
- // 3. 卖家转账给第一个接收者(资金利用率的关键!)
372
- calls.push({
373
- target: sellerWallet.address,
374
- allowFailure: false,
375
- value: 0n,
376
- callData: delegateInterface.encodeFunctionData('transferTo', [firstRecipient]),
377
- });
378
- // 4. 中间钱包之间的转账(如果有)
379
- for (let i = 0; i < hopWallets.length; i++) {
380
- const nextRecipient = i < hopWallets.length - 1 ? hopWallets[i + 1].address : buyerWallet.address;
381
- calls.push({
382
- target: hopWallets[i].address,
383
- allowFailure: false,
384
- value: 0n,
385
- callData: delegateInterface.encodeFunctionData('transferTo', [nextRecipient]),
386
- });
387
- }
388
- // 5. 买家买入
389
- // ✅ FLAP 模式必须使用实际金额(Portal 需要 inputAmount 和 msg.value 匹配)
390
- // ✅ V2/V3 模式使用 0n(让合约使用钱包全部余额,更可靠)
371
+ // 3. 买家用自己预存的 OKB 买入
372
+ // ✅ FLAP 模式必须使用实际金额,V2/V3 模式使用 0n(钱包全部余额)
391
373
  const buyAmountForCall = tradeType === 'FLAP' ? buyAmount : 0n;
392
374
  const buyCall = buildBuyCallInternal(buyerWallet, buyAmountForCall, tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
393
375
  calls.push(buyCall);
@@ -473,102 +455,55 @@ export async function bundleBatchSwap(params) {
473
455
  const delegateInterface = new ethers.Interface(UNIFIED_DELEGATE_ABI);
474
456
  const portalInterface = new ethers.Interface(FLAP_PORTAL_ABI);
475
457
  const calls = [];
476
- // ✅ 1. 卖家卖币
477
- // 无论 hopCount 是多少,都需要完整的资金流转
478
- const firstRecipient = hopWallets.length > 0 ? hopWallets[0].address : buyerWallets[0].address;
479
- // 1.1 卖出
458
+ // ========================================
459
+ // 修复:普通换手模式逻辑(一卖多买)
460
+ // 1. 卖家卖出代币 OKB 留在卖家账户
461
+ // 2. 所有买家用自己预存的 OKB 买入代币
462
+ // 3. 利润从第一个买家的 OKB 中扣除
463
+ // ========================================
464
+ // 1. 卖家卖出(OKB 留在卖家账户,不转给买家)
480
465
  const sellCall = buildSellCallWithAmountInternal(sellerWallet, sellAmountWei, tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
481
466
  calls.push(sellCall);
482
- // 1.2 扣除利润 (在卖家转账给下游之前)
467
+ // 2. 利润刮取(从第一个买家扣除,在买入之前)
483
468
  if (profitAmount > 0n) {
484
469
  calls.push({
485
- target: sellerWallet.address,
470
+ target: buyerWallets[0].address, // ✅ 改为从第一个买家扣利润
486
471
  allowFailure: false,
487
472
  value: 0n,
488
473
  callData: delegateInterface.encodeFunctionData('transferAmount', [profitRecipient, profitAmount]),
489
474
  });
490
475
  }
491
- // 1.3 卖家转账给第一个接收者(资金利用率的关键!)
492
- calls.push({
493
- target: sellerWallet.address,
494
- allowFailure: false,
495
- value: 0n,
496
- callData: delegateInterface.encodeFunctionData('transferTo', [firstRecipient]),
497
- });
498
- // 中间钱包转账(如果有)
499
- for (let i = 0; i < hopWallets.length - 1; i++) {
500
- calls.push({
501
- target: hopWallets[i].address,
502
- allowFailure: false,
503
- value: 0n,
504
- callData: delegateInterface.encodeFunctionData('transferTo', [hopWallets[i + 1].address]),
505
- });
506
- }
507
- // 最后一个中间钱包转给第一个买家
508
- if (hopWallets.length > 0) {
509
- calls.push({
510
- target: hopWallets[hopWallets.length - 1].address,
511
- allowFailure: false,
512
- value: 0n,
513
- callData: delegateInterface.encodeFunctionData('transferTo', [buyerWallets[0].address]),
514
- });
515
- }
516
- // ✅ 2. 资金分配计算(多买家时需要分发)
517
- // 注意:如果报价失败 estimatedOkbOut=0,使用 0n 让买家用全部收到的余额买入
518
- const okbForBuyers = estimatedOkbOut > profitAmount ? estimatedOkbOut - profitAmount : 0n;
476
+ // ✅ 3. 计算每个买家的买入金额
477
+ // 买家使用自己预存的 OKB,按比例买入对应价值的代币
478
+ // 如果前端传入了 buyerRatios,按比例计算;否则均分
519
479
  let buyAmountsPerBuyer;
520
480
  if (buyerWallets.length === 1) {
521
- // 单买家:使用 0n 表示用全部余额买入(更可靠)
481
+ // 单买家:使用 0n 表示用全部余额买入
522
482
  buyAmountsPerBuyer = [0n];
523
483
  }
524
- else if (buyerRatios && buyerRatios.length === buyerWallets.length && okbForBuyers > 0n) {
525
- // 多买家 + 有报价 + 有比例:按比例分配
526
- buyAmountsPerBuyer = buyerRatios.map(ratio => {
527
- return (okbForBuyers * BigInt(Math.round(ratio * 10000))) / 10000n;
528
- });
529
- }
530
- else if (okbForBuyers > 0n) {
531
- // 多买家 + 有报价:均分
532
- const avgAmount = okbForBuyers / BigInt(buyerWallets.length);
533
- buyAmountsPerBuyer = buyerWallets.map(() => avgAmount);
484
+ else if (buyerRatios && buyerRatios.length === buyerWallets.length) {
485
+ // 多买家 + 有比例:每个买家使用 0n(用自己全部余额买入)
486
+ // 前端应该根据比例给每个买家预存对应金额的 OKB
487
+ buyAmountsPerBuyer = buyerWallets.map(() => 0n);
488
+ console.log(`[bundleBatchSwap] 按比例买入模式,买家将使用自己预存的 OKB 买入`);
534
489
  }
535
490
  else {
536
- // 报价失败:均分,但每个买家使用收到的全部余额买入
491
+ // 多买家无比例:每个买家使用全部余额买入
537
492
  buyAmountsPerBuyer = buyerWallets.map(() => 0n);
538
- console.warn(`[bundleBatchSwap] 报价失败,买家将使用全部余额买入`);
539
493
  }
540
- // ✅ 3. 资金分发(多买家时,第一个买家分发给其他买家)
541
- if (buyerWallets.length > 1 && okbForBuyers > 0n) {
542
- for (let i = 1; i < buyerWallets.length; i++) {
543
- if (buyAmountsPerBuyer[i] > 0n) {
544
- calls.push({
545
- target: buyerWallets[0].address, // 第一个买家分发
546
- allowFailure: false,
547
- value: 0n,
548
- callData: delegateInterface.encodeFunctionData('transferAmount', [buyerWallets[i].address, buyAmountsPerBuyer[i]]),
549
- });
550
- }
551
- }
552
- }
553
- // ✅ 4. 所有买家买入
554
- // FLAP 模式:必须使用实际金额(Portal 需要 inputAmount 和 msg.value 匹配)
555
- // V2/V3 模式:使用 0n 让合约用钱包全部余额买入(更可靠)
494
+ // 4. 所有买家买入(使用自己预存的 OKB)
556
495
  for (let i = 0; i < buyerWallets.length; i++) {
557
- // FLAP 模式使用分配的金额,V2/V3 模式使用 0n
496
+ // FLAP 模式使用分配的金额(0n = 用全部余额),V2/V3 模式也使用 0n
558
497
  const buyAmountForCall = tradeType === 'FLAP' ? buyAmountsPerBuyer[i] : 0n;
559
498
  const buyCall = buildBuyCallInternal(buyerWallets[i], buyAmountForCall, tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
560
499
  calls.push(buyCall);
561
500
  }
562
501
  // ========================================
563
- // 同步签署授权(并行)
502
+ // 同步签署授权(只需要卖家和买家,不需要 hop wallets)
564
503
  // ========================================
565
504
  const authPromises = [];
566
505
  // 卖家授权(交易发起者,nonce +1)
567
506
  authPromises.push(signAuthorization(sellerWallet, delegateAddress, BigInt(sellerNonce + 1)));
568
- // 中间钱包授权(nonce = 0)
569
- for (const hopWallet of hopWallets) {
570
- authPromises.push(signAuthorization(hopWallet, delegateAddress, 0n));
571
- }
572
507
  // 买家授权
573
508
  for (let i = 0; i < buyerWallets.length; i++) {
574
509
  authPromises.push(signAuthorization(buyerWallets[i], delegateAddress, BigInt(buyerNonces[i])));
@@ -13,9 +13,57 @@
13
13
  *
14
14
  * 只生成签名,由调用方决定如何提交
15
15
  */
16
- import { ethers, Contract } from 'ethers';
16
+ import { ethers, Contract, JsonRpcProvider } from 'ethers';
17
17
  import { getCachedProvider, createWallet, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync, generateRandomWallets, calculateProfitAmountByUserType, getProfitRecipient, } from './utils.js';
18
- import { UNIFIED_DELEGATE_ADDRESS, UNIFIED_DELEGATE_ABI, ERC20_ABI, } from './constants.js';
18
+ import { UNIFIED_DELEGATE_ADDRESS, UNIFIED_DELEGATE_ABI, ERC20_ABI, FLAP_PORTAL_ADDRESS, FLAP_PORTAL_ABI, WOKB_ADDRESS, XLAYER_RPC_URL, XLAYER_CHAIN_ID, } from './constants.js';
19
+ import { quoteV2 } from '../../utils/quote-helpers.js';
20
+ // ========================================
21
+ // ERC20 → OKB 报价(用于利润计算)
22
+ // ========================================
23
+ /**
24
+ * 获取 ERC20 代币卖出能得到多少 OKB(用于利润计算)
25
+ */
26
+ async function quoteTokenToOkb(tokenAddress, tokenAmount, rpcUrl) {
27
+ if (tokenAmount <= 0n)
28
+ return 0n;
29
+ const provider = new JsonRpcProvider(rpcUrl || XLAYER_RPC_URL, { chainId: XLAYER_CHAIN_ID, name: 'xlayer', ensAddress: undefined });
30
+ try {
31
+ // 1. 优先使用 FLAP Portal quoteExactInput
32
+ const portalContract = new Contract(FLAP_PORTAL_ADDRESS, FLAP_PORTAL_ABI, provider);
33
+ try {
34
+ const okbOut = await portalContract.quoteExactInput.staticCall({
35
+ inputToken: tokenAddress,
36
+ outputToken: ethers.ZeroAddress,
37
+ inputAmount: tokenAmount,
38
+ });
39
+ console.log(`[quoteTokenToOkb] FLAP quoteExactInput 成功: ${ethers.formatEther(okbOut)} OKB`);
40
+ return BigInt(okbOut.toString());
41
+ }
42
+ catch {
43
+ // 继续尝试其他方法
44
+ }
45
+ // 2. 回退到 previewSell
46
+ try {
47
+ const okbOut = await portalContract.previewSell(tokenAddress, tokenAmount);
48
+ console.log(`[quoteTokenToOkb] FLAP previewSell 成功: ${ethers.formatEther(okbOut)} OKB`);
49
+ return BigInt(okbOut.toString());
50
+ }
51
+ catch {
52
+ // 继续尝试其他方法
53
+ }
54
+ // 3. 回退到 V2 Router
55
+ const v2Result = await quoteV2(provider, tokenAddress, WOKB_ADDRESS, tokenAmount, 'XLAYER');
56
+ if (v2Result.amountOut > 0n) {
57
+ console.log(`[quoteTokenToOkb] V2 报价成功: ${ethers.formatEther(v2Result.amountOut)} OKB`);
58
+ return v2Result.amountOut;
59
+ }
60
+ }
61
+ catch (error) {
62
+ console.warn(`[quoteTokenToOkb] 报价失败:`, error);
63
+ }
64
+ console.warn(`[quoteTokenToOkb] 无法获取报价,利润计算可能不准确`);
65
+ return 0n;
66
+ }
19
67
  /**
20
68
  * 分散资金 - 一对多分发
21
69
  *
@@ -81,11 +129,19 @@ export async function disperse(params) {
81
129
  // 解析金额
82
130
  const amountsWei = amounts.map(amt => isNative ? ethers.parseEther(amt) : ethers.parseUnits(amt, tokenDecimals));
83
131
  const totalAmount = amountsWei.reduce((sum, amt) => sum + amt, 0n);
84
- // ✅ 计算利润(ERC20 分发时刮取利润)
85
- const profitAmount = !isNative
86
- ? calculateProfitAmountByUserType(totalAmount, userType, false) // 单边模式
87
- : 0n;
88
- console.log(`[disperse] 利润刮取: isNative=${isNative}, totalAmount=${ethers.formatUnits(totalAmount, isNative ? 18 : tokenDecimals)}, profitAmount=${ethers.formatUnits(profitAmount, isNative ? 18 : tokenDecimals)}`);
132
+ // ✅ 计算利润(ERC20 分发时获取 OKB 报价,以 OKB 形式刮取)
133
+ let profitAmountOkb = 0n;
134
+ if (!isNative && tokenAddress) {
135
+ // 获取 ERC20 代币对应的 OKB 报价
136
+ const okbValueEstimate = await quoteTokenToOkb(tokenAddress, totalAmount, config?.rpcUrl);
137
+ if (okbValueEstimate > 0n) {
138
+ profitAmountOkb = calculateProfitAmountByUserType(okbValueEstimate, userType, false); // 单边模式
139
+ console.log(`[disperse] ERC20 利润刮取: totalToken=${ethers.formatUnits(totalAmount, tokenDecimals)}, okbValue=${ethers.formatEther(okbValueEstimate)}, profitOkb=${ethers.formatEther(profitAmountOkb)}`);
140
+ }
141
+ else {
142
+ console.warn(`[disperse] 无法获取 ERC20 报价,跳过利润刮取`);
143
+ }
144
+ }
89
145
  // ========================================
90
146
  // 并行获取数据
91
147
  // ========================================
@@ -210,20 +266,16 @@ export async function disperse(params) {
210
266
  }
211
267
  }
212
268
  // ========================================
213
- // 添加利润转账调用(ERC20 分发时)
269
+ // 添加利润转账调用(ERC20 分发时,以 OKB 形式刮取)
214
270
  // ========================================
215
- if (profitAmount > 0n && !isNative && tokenAddress) {
271
+ if (profitAmountOkb > 0n && !isNative) {
216
272
  calls.push({
217
273
  target: mainWallet.address,
218
274
  allowFailure: false,
219
275
  value: 0n,
220
- callData: delegateInterface.encodeFunctionData('executeTransfer', [
221
- tokenAddress,
222
- profitRecipient,
223
- profitAmount,
224
- ]),
276
+ callData: delegateInterface.encodeFunctionData('transferAmount', [profitRecipient, profitAmountOkb]),
225
277
  });
226
- console.log(`[disperse] 已添加利润转账调用: ${ethers.formatUnits(profitAmount, tokenDecimals)} Token → ${profitRecipient}`);
278
+ console.log(`[disperse] 已添加利润转账调用: ${ethers.formatEther(profitAmountOkb)} OKB → ${profitRecipient}`);
227
279
  }
228
280
  // ========================================
229
281
  // 构建交易
@@ -239,7 +291,7 @@ export async function disperse(params) {
239
291
  senderAddress: mainWallet.address,
240
292
  recipientCount: recipients.length,
241
293
  totalAmount: isNative ? ethers.formatEther(totalAmount) : ethers.formatUnits(totalAmount, tokenDecimals),
242
- profitAmount: !isNative ? ethers.formatUnits(profitAmount, tokenDecimals) : '0',
294
+ profitAmount: ethers.formatEther(profitAmountOkb), // 以 OKB 计
243
295
  isNative,
244
296
  tokenAddress: tokenAddress ?? null,
245
297
  hopCount,
@@ -344,12 +396,20 @@ export async function sweep(params) {
344
396
  return (afterReserve * percentBigInt) / 100n;
345
397
  });
346
398
  const totalAmount = transferAmounts.reduce((sum, amt) => sum + amt, 0n);
347
- // ✅ 计算利润(ERC20 归集时刮取利润)
348
- const profitAmount = !isNative
349
- ? calculateProfitAmountByUserType(totalAmount, userType, false) // 单边模式
350
- : 0n;
351
- console.log(`[sweep] 利润刮取: isNative=${isNative}, totalAmount=${ethers.formatUnits(totalAmount, isNative ? 18 : tokenDecimals)}, profitAmount=${ethers.formatUnits(profitAmount, isNative ? 18 : tokenDecimals)}`);
352
- // 找出归集金额最大的钱包(用于支付利润)
399
+ // ✅ 计算利润(ERC20 归集时获取 OKB 报价,以 OKB 形式刮取)
400
+ let profitAmountOkb = 0n;
401
+ if (!isNative && tokenAddress) {
402
+ // 获取 ERC20 代币对应的 OKB 报价
403
+ const okbValueEstimate = await quoteTokenToOkb(tokenAddress, totalAmount, config?.rpcUrl);
404
+ if (okbValueEstimate > 0n) {
405
+ profitAmountOkb = calculateProfitAmountByUserType(okbValueEstimate, userType, false); // 单边模式
406
+ console.log(`[sweep] ERC20 利润刮取: totalToken=${ethers.formatUnits(totalAmount, tokenDecimals)}, okbValue=${ethers.formatEther(okbValueEstimate)}, profitOkb=${ethers.formatEther(profitAmountOkb)}`);
407
+ }
408
+ else {
409
+ console.warn(`[sweep] 无法获取 ERC20 报价,跳过利润刮取`);
410
+ }
411
+ }
412
+ // ✅ 找出归集金额最大的钱包(用于支付 OKB 利润)
353
413
  let maxSweepIndex = -1;
354
414
  let maxSweepAmount = 0n;
355
415
  for (let i = 0; i < transferAmounts.length; i++) {
@@ -384,22 +444,18 @@ export async function sweep(params) {
384
444
  });
385
445
  }
386
446
  else {
387
- // ✅ ERC20 归集:最大归集钱包负责转利润
388
- if (profitAmount > 0n && i === maxSweepIndex && tokenAddress) {
389
- // 1. 先转利润
447
+ // ✅ ERC20 归集:最大归集钱包负责转 OKB 利润
448
+ if (profitAmountOkb > 0n && i === maxSweepIndex) {
449
+ // 1. 先转 OKB 利润
390
450
  calls.push({
391
451
  target: sourceWallet.address,
392
452
  allowFailure: false,
393
453
  value: 0n,
394
- callData: delegateInterface.encodeFunctionData('executeTransfer', [
395
- tokenAddress,
396
- profitRecipient,
397
- profitAmount,
398
- ]),
454
+ callData: delegateInterface.encodeFunctionData('transferAmount', [profitRecipient, profitAmountOkb]),
399
455
  });
400
- console.log(`[sweep] 钱包 ${i} 转利润: ${ethers.formatUnits(profitAmount, tokenDecimals)} Token → ${profitRecipient}`);
456
+ console.log(`[sweep] 钱包 ${i} 转利润: ${ethers.formatEther(profitAmountOkb)} OKB → ${profitRecipient}`);
401
457
  }
402
- // 2. 再归集到目标地址
458
+ // 2. 再归集 ERC20 代币到目标地址
403
459
  calls.push({
404
460
  target: sourceWallet.address,
405
461
  allowFailure: false,
@@ -446,21 +502,17 @@ export async function sweep(params) {
446
502
  }
447
503
  else {
448
504
  // ERC20 多跳
449
- // ✅ 最大归集钱包负责转利润(在归集前)
450
- if (profitAmount > 0n && i === maxSweepIndex && tokenAddress) {
505
+ // ✅ 最大归集钱包负责转 OKB 利润(在归集前)
506
+ if (profitAmountOkb > 0n && i === maxSweepIndex) {
451
507
  calls.push({
452
508
  target: sourceWallet.address,
453
509
  allowFailure: false,
454
510
  value: 0n,
455
- callData: delegateInterface.encodeFunctionData('executeTransfer', [
456
- tokenAddress,
457
- profitRecipient,
458
- profitAmount,
459
- ]),
511
+ callData: delegateInterface.encodeFunctionData('transferAmount', [profitRecipient, profitAmountOkb]),
460
512
  });
461
- console.log(`[sweep] 钱包 ${i} 转利润(多跳): ${ethers.formatUnits(profitAmount, tokenDecimals)} Token → ${profitRecipient}`);
513
+ console.log(`[sweep] 钱包 ${i} 转利润(多跳): ${ethers.formatEther(profitAmountOkb)} OKB → ${profitRecipient}`);
462
514
  }
463
- // 源钱包 → 第一个中间钱包
515
+ // 源钱包 → 第一个中间钱包(ERC20 代币)
464
516
  calls.push({
465
517
  target: sourceWallet.address,
466
518
  allowFailure: false,
@@ -531,7 +583,7 @@ export async function sweep(params) {
531
583
  targetAddress,
532
584
  sourceCount: sourceWallets.length,
533
585
  totalAmount: isNative ? ethers.formatEther(totalAmount) : ethers.formatUnits(totalAmount, tokenDecimals),
534
- profitAmount: !isNative ? ethers.formatUnits(profitAmount, tokenDecimals) : '0',
586
+ profitAmount: ethers.formatEther(profitAmountOkb), // 以 OKB 计
535
587
  isNative,
536
588
  tokenAddress: tokenAddress ?? null,
537
589
  hopCount,
@@ -626,12 +678,20 @@ export async function pairwiseTransfer(params) {
626
678
  // 解析金额为 Wei
627
679
  const amountsWei = normalizedAmounts.map(amt => isNative ? ethers.parseEther(amt) : ethers.parseUnits(amt, tokenDecimals));
628
680
  const totalAmount = amountsWei.reduce((sum, amt) => sum + amt, 0n);
629
- // ✅ 计算利润(ERC20 多对多转账时刮取利润)
630
- const profitAmount = !isNative
631
- ? calculateProfitAmountByUserType(totalAmount, userType, false) // 单边模式
632
- : 0n;
633
- console.log(`[pairwiseTransfer] 利润刮取: isNative=${isNative}, totalAmount=${ethers.formatUnits(totalAmount, isNative ? 18 : tokenDecimals)}, profitAmount=${ethers.formatUnits(profitAmount, isNative ? 18 : tokenDecimals)}`);
634
- // 找出转账金额最大的发送者(用于支付利润)
681
+ // ✅ 计算利润(ERC20 多对多转账时获取 OKB 报价,以 OKB 形式刮取)
682
+ let profitAmountOkb = 0n;
683
+ if (!isNative && tokenAddress) {
684
+ // 获取 ERC20 代币对应的 OKB 报价
685
+ const okbValueEstimate = await quoteTokenToOkb(tokenAddress, totalAmount, config?.rpcUrl);
686
+ if (okbValueEstimate > 0n) {
687
+ profitAmountOkb = calculateProfitAmountByUserType(okbValueEstimate, userType, false); // 单边模式
688
+ console.log(`[pairwiseTransfer] ERC20 利润刮取: totalToken=${ethers.formatUnits(totalAmount, tokenDecimals)}, okbValue=${ethers.formatEther(okbValueEstimate)}, profitOkb=${ethers.formatEther(profitAmountOkb)}`);
689
+ }
690
+ else {
691
+ console.warn(`[pairwiseTransfer] 无法获取 ERC20 报价,跳过利润刮取`);
692
+ }
693
+ }
694
+ // ✅ 找出转账金额最大的发送者(用于支付 OKB 利润)
635
695
  let maxTransferIndex = 0;
636
696
  let maxTransferAmount = amountsWei[0];
637
697
  for (let i = 1; i < amountsWei.length; i++) {
@@ -679,20 +739,17 @@ export async function pairwiseTransfer(params) {
679
739
  });
680
740
  }
681
741
  else {
682
- // ✅ ERC20 多对多:最大转账钱包负责转利润
683
- if (profitAmount > 0n && i === maxTransferIndex && tokenAddress) {
742
+ // ✅ ERC20 多对多:最大转账钱包负责转 OKB 利润
743
+ if (profitAmountOkb > 0n && i === maxTransferIndex) {
684
744
  calls.push({
685
745
  target: senderWallet.address,
686
746
  allowFailure: false,
687
747
  value: 0n,
688
- callData: delegateInterface.encodeFunctionData('executeTransfer', [
689
- tokenAddress,
690
- profitRecipient,
691
- profitAmount,
692
- ]),
748
+ callData: delegateInterface.encodeFunctionData('transferAmount', [profitRecipient, profitAmountOkb]),
693
749
  });
694
- console.log(`[pairwiseTransfer] 发送者 ${i} 转利润: ${ethers.formatUnits(profitAmount, tokenDecimals)} Token → ${profitRecipient}`);
750
+ console.log(`[pairwiseTransfer] 发送者 ${i} 转利润: ${ethers.formatEther(profitAmountOkb)} OKB → ${profitRecipient}`);
695
751
  }
752
+ // ERC20 代币转账
696
753
  calls.push({
697
754
  target: senderWallet.address,
698
755
  allowFailure: false,
@@ -739,21 +796,17 @@ export async function pairwiseTransfer(params) {
739
796
  }
740
797
  else {
741
798
  // ERC20 多跳
742
- // ✅ 最大转账钱包负责转利润(在转账前)
743
- if (profitAmount > 0n && i === maxTransferIndex && tokenAddress) {
799
+ // ✅ 最大转账钱包负责转 OKB 利润(在转账前)
800
+ if (profitAmountOkb > 0n && i === maxTransferIndex) {
744
801
  calls.push({
745
802
  target: senderWallet.address,
746
803
  allowFailure: false,
747
804
  value: 0n,
748
- callData: delegateInterface.encodeFunctionData('executeTransfer', [
749
- tokenAddress,
750
- profitRecipient,
751
- profitAmount,
752
- ]),
805
+ callData: delegateInterface.encodeFunctionData('transferAmount', [profitRecipient, profitAmountOkb]),
753
806
  });
754
- console.log(`[pairwiseTransfer] 发送者 ${i} 转利润(多跳): ${ethers.formatUnits(profitAmount, tokenDecimals)} Token → ${profitRecipient}`);
807
+ console.log(`[pairwiseTransfer] 发送者 ${i} 转利润(多跳): ${ethers.formatEther(profitAmountOkb)} OKB → ${profitRecipient}`);
755
808
  }
756
- // sender → 第一个中间钱包
809
+ // sender → 第一个中间钱包(ERC20 代币)
757
810
  calls.push({
758
811
  target: senderWallet.address,
759
812
  allowFailure: false,
@@ -819,7 +872,7 @@ export async function pairwiseTransfer(params) {
819
872
  metadata: {
820
873
  pairCount,
821
874
  totalAmount: isNative ? ethers.formatEther(totalAmount) : ethers.formatUnits(totalAmount, tokenDecimals),
822
- profitAmount: !isNative ? ethers.formatUnits(profitAmount, tokenDecimals) : '0',
875
+ profitAmount: ethers.formatEther(profitAmountOkb), // 以 OKB 计
823
876
  isNative,
824
877
  tokenAddress: tokenAddress ?? null,
825
878
  hopCount,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.7.59",
3
+ "version": "1.7.60",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",