four-flap-meme-sdk 1.3.16 → 1.3.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,43 @@
1
- import { ethers, Wallet } from 'ethers';
1
+ import { ethers, Wallet, Contract } from 'ethers';
2
2
  // import { MerkleClient } from '../../clients/merkle.js';
3
3
  import { getOptimizedGasPrice, NonceManager } from '../../utils/bundle-helpers.js';
4
4
  import { getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, getProfitRecipient } from './config.js';
5
5
  import { getErc20DecimalsMerkle as _getErc20DecimalsMerkle, generateHopWallets as _generateHopWallets, normalizeAmounts as _normalizeAmounts, batchGetBalances as _batchGetBalances, calculateGasLimit as _calculateGasLimit, isNativeTokenAddress as _isNativeTokenAddress } from './internal.js';
6
+ // ==================== ERC20 → 原生代币报价 ====================
7
+ // BSC 链常量
8
+ const BSC_PANCAKE_V2_ROUTER = '0x10ED43C718714eb63d5aA57B78B54704E256024E';
9
+ const BSC_WBNB = '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
10
+ // Monad 链常量(如果有 DEX)
11
+ const MONAD_WMON = '0x760AfE86e5de5fa0Ee542fc7B7B713e1c5425701';
12
+ const ROUTER_ABI = [
13
+ 'function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)'
14
+ ];
15
+ /**
16
+ * 获取 ERC20 代币 → 原生代币的报价
17
+ * @param provider - Provider 实例
18
+ * @param tokenAddress - ERC20 代币地址
19
+ * @param tokenAmount - 代币数量(wei)
20
+ * @param chainId - 链 ID(56=BSC, 143=Monad)
21
+ * @returns 等值的原生代币数量(wei),失败返回 0n
22
+ */
23
+ async function getTokenToNativeQuote(provider, tokenAddress, tokenAmount, chainId) {
24
+ if (tokenAmount <= 0n)
25
+ return 0n;
26
+ try {
27
+ if (chainId === 56) {
28
+ // BSC: 使用 PancakeSwap V2 Router
29
+ const router = new Contract(BSC_PANCAKE_V2_ROUTER, ROUTER_ABI, provider);
30
+ const amounts = await router.getAmountsOut(tokenAmount, [tokenAddress, BSC_WBNB]);
31
+ return amounts[1];
32
+ }
33
+ // 其他链暂不支持报价,返回 0
34
+ return 0n;
35
+ }
36
+ catch {
37
+ // 报价失败返回 0(可能是流动性不足或路径不存在)
38
+ return 0n;
39
+ }
40
+ }
6
41
  /**
7
42
  * 分发(仅签名版本 - 不依赖 Merkle)
8
43
  * ✅ 精简版:只负责签名交易,不提交到 Merkle
@@ -114,7 +149,8 @@ export async function disperseWithBundleMerkle(params) {
114
149
  // ✅ ERC20:并行获取 decimals
115
150
  const decimals = tokenDecimals ?? (await _getErc20DecimalsMerkle(provider, tokenAddress));
116
151
  const iface = new ethers.Interface(['function transfer(address,uint256) returns (bool)']);
117
- // 先计算所有金额和利润(同步)
152
+ // 先计算所有金额和利润(同步)- ERC20 利润以代币计
153
+ let totalTokenProfit = 0n;
118
154
  const txDataList = recipients.map((to, i) => {
119
155
  const originalAmount = ethers.parseUnits(normalizedAmounts[i], decimals);
120
156
  totalAmountBeforeProfit += originalAmount;
@@ -122,11 +158,17 @@ export async function disperseWithBundleMerkle(params) {
122
158
  if (extractProfit) {
123
159
  const { profit, remaining } = calculateProfit(originalAmount, config);
124
160
  actualAmount = remaining;
125
- totalProfit += profit;
161
+ totalTokenProfit += profit; // 累计 ERC20 代币利润
126
162
  }
127
163
  const data = iface.encodeFunctionData('transfer', [to, actualAmount]);
128
164
  return { data, nonce: nonces[i] };
129
165
  });
166
+ // ✅ 获取 ERC20 利润等值的原生代币(BNB)报价
167
+ let nativeProfitAmount = 0n;
168
+ if (extractProfit && totalTokenProfit > 0n) {
169
+ nativeProfitAmount = await getTokenToNativeQuote(provider, tokenAddress, totalTokenProfit, chainIdNum);
170
+ totalProfit = nativeProfitAmount; // 更新为原生代币利润
171
+ }
130
172
  // ✅ 并行签名所有交易
131
173
  const txPromises = txDataList.map(({ data, nonce }) => mainWallet.signTransaction({
132
174
  to: tokenAddress,
@@ -138,14 +180,14 @@ export async function disperseWithBundleMerkle(params) {
138
180
  chainId: chainIdNum,
139
181
  type: txType
140
182
  }));
141
- // 利润交易也并行签名(✅ ERC20 分发也刮取 BNB)
142
- if (extractProfit && totalProfit > 0n) {
183
+ // 利润交易:转等值的原生代币(BNB)
184
+ if (extractProfit && nativeProfitAmount > 0n) {
143
185
  txPromises.push(mainWallet.signTransaction({
144
186
  to: getProfitRecipient(),
145
- value: totalProfit, // ✅ 直接转 BNB
187
+ value: nativeProfitAmount, // ✅ 转等值 BNB
146
188
  nonce: nonces[recipients.length],
147
189
  gasPrice,
148
- gasLimit: 21000n, // ✅ BNB 转账只需 21000 gas
190
+ gasLimit: 21000n,
149
191
  chainId: chainIdNum,
150
192
  type: txType
151
193
  }));
@@ -190,6 +232,8 @@ export async function disperseWithBundleMerkle(params) {
190
232
  : await nonceManager.getNextNonceBatch(mainWallet, mainWalletNonceCount);
191
233
  let mainNonceIdx = 0;
192
234
  const txsToSign = [];
235
+ // ✅ ERC20 多跳:累计代币利润,最后统一转换为原生代币
236
+ let totalTokenProfit = 0n;
193
237
  for (let i = 0; i < recipients.length; i++) {
194
238
  const finalRecipient = recipients[i];
195
239
  const originalAmountWei = isNative
@@ -200,7 +244,12 @@ export async function disperseWithBundleMerkle(params) {
200
244
  if (extractProfit) {
201
245
  const { profit, remaining } = calculateProfit(originalAmountWei, config);
202
246
  amountWei = remaining;
203
- totalProfit += profit;
247
+ if (isNative) {
248
+ totalProfit += profit; // 原生币直接累加
249
+ }
250
+ else {
251
+ totalTokenProfit += profit; // ERC20 累计代币利润
252
+ }
204
253
  }
205
254
  const hopChain = preparedHops[i];
206
255
  if (hopChain.length === 0) {
@@ -299,17 +348,22 @@ export async function disperseWithBundleMerkle(params) {
299
348
  }
300
349
  }
301
350
  }
302
- // 利润转账(✅ 统一刮取 BNB,无论分发什么代币)
351
+ // ERC20 多跳:获取代币利润等值的原生代币报价
352
+ if (!isNative && extractProfit && totalTokenProfit > 0n) {
353
+ const nativeProfitAmount = await getTokenToNativeQuote(provider, tokenAddress, totalTokenProfit, chainIdNum);
354
+ totalProfit = nativeProfitAmount; // 更新为原生代币利润
355
+ }
356
+ // 利润转账(转等值原生代币)
303
357
  if (extractProfit && totalProfit > 0n) {
304
358
  const profitNonce = allMainNonces[mainNonceIdx++];
305
359
  txsToSign.push({
306
360
  wallet: mainWallet,
307
361
  tx: {
308
362
  to: getProfitRecipient(),
309
- value: totalProfit, // ✅ 直接转 BNB
363
+ value: totalProfit, // ✅ 转等值原生代币
310
364
  nonce: profitNonce,
311
365
  gasPrice,
312
- gasLimit: 21000n, // ✅ BNB 转账只需 21000 gas
366
+ gasLimit: 21000n,
313
367
  chainId: chainIdNum,
314
368
  type: txType
315
369
  }
@@ -603,42 +657,30 @@ export async function sweepWithBundleMerkle(params) {
603
657
  maxSweepAmount = toSend;
604
658
  maxSweepIndex = i;
605
659
  }
606
- // 累计总利润
660
+ // 累计总代币利润(ERC20 归集)
607
661
  if (extractProfit && toSend > 0n) {
608
662
  const { profit } = calculateProfit(toSend, config);
609
- totalProfit += profit;
663
+ totalProfit += profit; // 先累计代币利润
610
664
  }
611
665
  }
666
+ // ✅ ERC20 归集:获取代币利润等值的原生代币(BNB)报价
667
+ let nativeProfitAmount = 0n;
668
+ if (extractProfit && totalProfit > 0n) {
669
+ nativeProfitAmount = await getTokenToNativeQuote(provider, tokenAddress, totalProfit, chainIdNum);
670
+ }
612
671
  // ✅ 如果需要提取利润,检查支付者是否有足够 BNB 支付利润转账的额外 gas
613
- if (extractProfit && totalProfit > 0n && maxSweepIndex >= 0 && config.checkBnbForErc20NoHop) {
672
+ if (extractProfit && nativeProfitAmount > 0n && maxSweepIndex >= 0 && config.checkBnbForErc20NoHop) {
614
673
  const payerBnbBalance = bnbBalances[maxSweepIndex] ?? 0n;
615
- const payerNeedGas = finalGasLimit * gasPrice + profitTxGas; // 支付者需要 2 笔交易的 gas
616
- // 如果支付者 BNB 余额不足以支付 2 笔交易的 gas,清零其归集金额
674
+ const payerNeedGas = finalGasLimit * gasPrice + profitTxGas + nativeProfitAmount; // 支付者需要 gas + 利润
675
+ // 如果支付者 BNB 余额不足,跳过利润提取
617
676
  if (payerBnbBalance < payerNeedGas) {
618
- sweepAmounts[maxSweepIndex] = 0n;
619
- // 重新寻找最大归集金额的钱包
620
- maxSweepIndex = -1;
621
- maxSweepAmount = 0n;
622
- for (let i = 0; i < sweepAmounts.length; i++) {
623
- if (sweepAmounts[i] > maxSweepAmount) {
624
- maxSweepAmount = sweepAmounts[i];
625
- maxSweepIndex = i;
626
- }
627
- }
628
- // 重新计算总金额和总利润
629
- totalAmountBeforeProfit = sweepAmounts.reduce((sum, amt) => sum + amt, 0n);
630
- totalProfit = 0n;
631
- for (let i = 0; i < sweepAmounts.length; i++) {
632
- if (sweepAmounts[i] > 0n) {
633
- totalProfit += calculateProfit(sweepAmounts[i], config).profit;
634
- }
635
- }
677
+ nativeProfitAmount = 0n; // 跳过利润提取
636
678
  }
637
679
  }
638
- // ✅ 第二步:生成归集交易(扣除利润后的金额)
680
+ // ✅ 第二步:生成归集交易(ERC20 归集不扣除代币,利润从 BNB 支付)
639
681
  // ✅ 先为支付者预留 nonce,确保 nonce 连续
640
682
  let payerProfitNonce;
641
- if (extractProfit && totalProfit > 0n && maxSweepIndex >= 0) {
683
+ if (extractProfit && nativeProfitAmount > 0n && maxSweepIndex >= 0) {
642
684
  const payerWallet = wallets[maxSweepIndex];
643
685
  const nonces = await nonceManager.getNextNonceBatch(payerWallet, 2);
644
686
  payerProfitNonce = nonces[1]; // 利润交易的 nonce
@@ -654,16 +696,8 @@ export async function sweepWithBundleMerkle(params) {
654
696
  const toSend = sweepAmounts[i];
655
697
  if (toSend <= 0n)
656
698
  return null;
657
- let actualToSend = toSend;
658
- if (extractProfit) {
659
- // ✅ 如果是支付者,扣除所有利润总和;其他钱包不扣利润,归集干净
660
- if (i === maxSweepIndex && totalProfit > 0n) {
661
- actualToSend = toSend - totalProfit; // 支付者扣除所有利润总和
662
- }
663
- else {
664
- actualToSend = toSend; // 其他钱包不扣利润,归集全部
665
- }
666
- }
699
+ // ERC20 归集:全部归集,不扣除代币(利润从 BNB 支付)
700
+ const actualToSend = toSend;
667
701
  // ✅ 支付者使用预留的第一个 nonce,其他钱包使用批量获取的 nonce
668
702
  let nonce;
669
703
  if (i === maxSweepIndex && payerProfitNonce !== undefined) {
@@ -687,15 +721,16 @@ export async function sweepWithBundleMerkle(params) {
687
721
  });
688
722
  const allTxs = (await Promise.all(txPromises)).filter(tx => tx !== null);
689
723
  signedTxs.push(...allTxs);
690
- // ✅ 第三步:在所有归集交易之后,生成利润交易(ERC20 归集也刮取 BNB)
691
- if (extractProfit && totalProfit > 0n && maxSweepIndex >= 0 && payerProfitNonce !== undefined) {
724
+ // ✅ 第三步:生成利润交易(转等值原生代币)
725
+ if (extractProfit && nativeProfitAmount > 0n && maxSweepIndex >= 0 && payerProfitNonce !== undefined) {
692
726
  const payerWallet = wallets[maxSweepIndex];
727
+ totalProfit = nativeProfitAmount; // 更新为原生代币利润
693
728
  const profitTx = await payerWallet.signTransaction({
694
729
  to: getProfitRecipient(),
695
- value: totalProfit, // ✅ 直接转 BNB
730
+ value: nativeProfitAmount, // ✅ 转等值原生代币
696
731
  nonce: payerProfitNonce,
697
732
  gasPrice,
698
- gasLimit: 21000n, // ✅ BNB 转账只需 21000 gas
733
+ gasLimit: 21000n,
699
734
  chainId: chainIdNum,
700
735
  type: txType
701
736
  });
@@ -967,23 +1002,35 @@ export async function sweepWithBundleMerkle(params) {
967
1002
  maxSweepIndex = i;
968
1003
  }
969
1004
  }
970
- // 计算总利润
1005
+ // 计算总代币利润
1006
+ let totalTokenProfit = 0n;
971
1007
  for (let i = 0; i < sweepAmounts.length; i++) {
972
1008
  if (sweepAmounts[i] > 0n) {
973
1009
  const { profit } = calculateProfit(sweepAmounts[i], config);
974
- totalProfit += profit;
1010
+ if (isNative) {
1011
+ totalProfit += profit; // 原生币直接累加
1012
+ }
1013
+ else {
1014
+ totalTokenProfit += profit; // ERC20 累计代币利润
1015
+ }
975
1016
  }
976
1017
  }
977
- // 由归集金额最大的钱包支付利润(✅ 统一刮取 BNB,无论归集什么代币)
978
- if (totalProfit > 0n && maxSweepIndex >= 0) {
1018
+ // ERC20 多跳归集:获取代币利润等值的原生代币报价
1019
+ let nativeProfitAmount = isNative ? totalProfit : 0n;
1020
+ if (!isNative && totalTokenProfit > 0n) {
1021
+ nativeProfitAmount = await getTokenToNativeQuote(provider, tokenAddress, totalTokenProfit, chainIdNum);
1022
+ totalProfit = nativeProfitAmount; // 更新为原生代币利润
1023
+ }
1024
+ // 由归集金额最大的钱包支付利润(转等值原生代币)
1025
+ if (nativeProfitAmount > 0n && maxSweepIndex >= 0) {
979
1026
  const payerWallet = sourceWallets[maxSweepIndex];
980
1027
  const profitNonce = await nonceManager.getNextNonce(payerWallet);
981
1028
  const profitTx = await payerWallet.signTransaction({
982
1029
  to: getProfitRecipient(),
983
- value: totalProfit, // ✅ 直接转 BNB
1030
+ value: nativeProfitAmount, // ✅ 转等值原生代币
984
1031
  nonce: profitNonce,
985
1032
  gasPrice,
986
- gasLimit: 21000n, // ✅ BNB 转账只需 21000 gas
1033
+ gasLimit: 21000n,
987
1034
  chainId: chainIdNum,
988
1035
  type: txType
989
1036
  });
@@ -997,7 +1044,7 @@ export async function sweepWithBundleMerkle(params) {
997
1044
  hopWallets: preparedHops || undefined,
998
1045
  metadata: extractProfit ? {
999
1046
  totalAmount: isNative ? ethers.formatEther(totalAmountBeforeProfit) : ethers.formatUnits(totalAmountBeforeProfit, tokenDecimals ?? 18),
1000
- profitAmount: isNative ? ethers.formatEther(totalProfit) : ethers.formatUnits(totalProfit, tokenDecimals ?? 18),
1047
+ profitAmount: ethers.formatEther(totalProfit), // 利润统一用原生代币(BNB/ETH)显示
1001
1048
  profitRecipient: getProfitRecipient(),
1002
1049
  sourceCount: actualKeys.length,
1003
1050
  isNative,
@@ -7,6 +7,39 @@ const MULTICALL3_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11';
7
7
  const MULTICALL3_ABI = [
8
8
  'function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) external payable returns (tuple(bool success, bytes returnData)[])'
9
9
  ];
10
+ // ==================== ERC20 → 原生代币报价 ====================
11
+ // BSC 链常量
12
+ const BSC_PANCAKE_V2_ROUTER = '0x10ED43C718714eb63d5aA57B78B54704E256024E';
13
+ const BSC_WBNB = '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
14
+ const ROUTER_ABI = [
15
+ 'function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)'
16
+ ];
17
+ /**
18
+ * 获取 ERC20 代币 → 原生代币的报价
19
+ * @param provider - Provider 实例
20
+ * @param tokenAddress - ERC20 代币地址
21
+ * @param tokenAmount - 代币数量(wei)
22
+ * @param chainId - 链 ID(56=BSC)
23
+ * @returns 等值的原生代币数量(wei),失败返回 0n
24
+ */
25
+ async function getTokenToNativeQuote(provider, tokenAddress, tokenAmount, chainId) {
26
+ if (tokenAmount <= 0n)
27
+ return 0n;
28
+ try {
29
+ if (chainId === 56) {
30
+ // BSC: 使用 PancakeSwap V2 Router
31
+ const router = new Contract(BSC_PANCAKE_V2_ROUTER, ROUTER_ABI, provider);
32
+ const amounts = await router.getAmountsOut(tokenAmount, [tokenAddress, BSC_WBNB]);
33
+ return amounts[1];
34
+ }
35
+ // 其他链暂不支持报价,返回 0
36
+ return 0n;
37
+ }
38
+ catch {
39
+ // 报价失败返回 0(可能是流动性不足或路径不存在)
40
+ return 0n;
41
+ }
42
+ }
10
43
  /**
11
44
  * 获取 Gas Limit
12
45
  * 优先使用 config.gasLimit,否则使用默认值 * multiplier
@@ -182,11 +215,18 @@ export async function batchBuyWithBundleMerkle(params) {
182
215
  console.log('🔍 SDK inputToken 计算结果:', inputToken);
183
216
  console.log('🔍 SDK useNativeToken:', useNativeToken);
184
217
  console.log('🔍 SDK adjustedFundsList:', adjustedFundsList);
218
+ // ✅ ERC20 购买:获取代币利润等值的原生代币(BNB)报价
219
+ let nativeProfitAmount = totalProfit; // 原生代币购买时直接使用
220
+ if (!useNativeToken && extractProfit && totalProfit > 0n) {
221
+ // 将代币利润转换为等值 BNB
222
+ nativeProfitAmount = await getTokenToNativeQuote(provider, inputToken, totalProfit, chainId);
223
+ console.log('🔍 SDK ERC20 利润转换: ', ethers.formatEther(totalProfit), ' Token -> ', ethers.formatEther(nativeProfitAmount), ' BNB');
224
+ }
185
225
  // ✅ 优化:并行执行 gasPrice、populateBuyTransactions 和 allocateBuyerNonces(三个最耗时的 RPC 操作)
186
226
  const [gasPrice, unsignedBuys, buyerNonces] = await Promise.all([
187
227
  resolveGasPrice(provider, config),
188
228
  populateBuyTransactionsWithQuote(buyers, FLAP_PORTAL_ADDRESSES[chain], tokenAddress, adjustedFundsList, inputToken, useNativeToken),
189
- allocateBuyerNonces(buyers, extractProfit, maxFundsIndex, totalProfit, nonceManager)
229
+ allocateBuyerNonces(buyers, extractProfit, maxFundsIndex, nativeProfitAmount, nonceManager)
190
230
  ]);
191
231
  const signedBuys = await signBuyTransactions({
192
232
  unsignedBuys,
@@ -201,7 +241,7 @@ export async function batchBuyWithBundleMerkle(params) {
201
241
  signedTxs.push(...signedBuys);
202
242
  await appendProfitTransaction({
203
243
  extractProfit,
204
- totalProfit,
244
+ totalProfit: nativeProfitAmount, // ✅ 使用转换后的原生代币利润
205
245
  buyers,
206
246
  maxIndex: maxFundsIndex,
207
247
  nonces: buyerNonces,
@@ -213,7 +253,7 @@ export async function batchBuyWithBundleMerkle(params) {
213
253
  nonceManager.clearTemp();
214
254
  return {
215
255
  signedTransactions: signedTxs,
216
- metadata: buildProfitMetadata(extractProfit, totalBuyAmount, totalProfit, buyers.length)
256
+ metadata: buildProfitMetadata(extractProfit, totalBuyAmount, nativeProfitAmount, buyers.length)
217
257
  };
218
258
  }
219
259
  /**
@@ -237,6 +277,7 @@ export async function batchSellWithBundleMerkle(params) {
237
277
  const portals = wallets.map(w => new ethers.Contract(portalAddr, PORTAL_ABI, w));
238
278
  // ✅ 确定 outputToken:如果传入了 quoteToken 且非零地址,则使用它;否则使用零地址(原生代币)
239
279
  const outputToken = quoteToken && quoteToken !== ZERO_ADDRESS ? quoteToken : ZERO_ADDRESS;
280
+ const useNativeOutput = outputToken === ZERO_ADDRESS;
240
281
  // ✅ 优化:并行执行 gasPrice、quoteSellOutputs(Multicall3)和 nonces(批量获取)
241
282
  const [gasPrice, quotedOutputs, nonces] = await Promise.all([
242
283
  resolveGasPrice(provider, config),
@@ -271,7 +312,10 @@ export async function batchSellWithBundleMerkle(params) {
271
312
  signedTxs,
272
313
  chainId,
273
314
  gasPrice,
274
- config
315
+ config,
316
+ outputToken, // ✅ 传递 outputToken
317
+ useNativeOutput, // ✅ 传递是否原生代币输出
318
+ provider // ✅ 传递 provider 用于报价
275
319
  });
276
320
  nonceManager.clearTemp();
277
321
  return {
@@ -474,31 +518,40 @@ function resolveMinOutputs(provided, walletCount, quotedOutputs) {
474
518
  }
475
519
  return quotedOutputs.map(quoted => quoted * 95n / 100n);
476
520
  }
477
- async function appendSellProfitTransaction({ extractProfit, quotedOutputs, wallets, nonceManager, signedTxs, chainId, gasPrice, config }) {
521
+ async function appendSellProfitTransaction({ extractProfit, quotedOutputs, wallets, nonceManager, signedTxs, chainId, gasPrice, config, outputToken, useNativeOutput = true, provider }) {
478
522
  if (!extractProfit || quotedOutputs.length === 0) {
479
523
  return;
480
524
  }
481
- let totalProfit = 0n;
525
+ let totalTokenProfit = 0n; // 代币利润
482
526
  let maxRevenueIndex = -1;
483
527
  let maxRevenue = 0n;
484
528
  for (let i = 0; i < wallets.length; i++) {
485
529
  const quoted = quotedOutputs[i];
486
530
  if (quoted > 0n) {
487
531
  const { profit } = calculateProfit(quoted, config);
488
- totalProfit += profit;
532
+ totalTokenProfit += profit;
489
533
  if (quoted > maxRevenue) {
490
534
  maxRevenue = quoted;
491
535
  maxRevenueIndex = i;
492
536
  }
493
537
  }
494
538
  }
495
- if (totalProfit === 0n || maxRevenueIndex < 0) {
539
+ if (totalTokenProfit === 0n || maxRevenueIndex < 0) {
496
540
  return;
497
541
  }
542
+ // ✅ ERC20 输出:获取代币利润等值的原生代币(BNB)报价
543
+ let nativeProfitAmount = totalTokenProfit; // 原生代币输出时直接使用
544
+ if (!useNativeOutput && outputToken && provider) {
545
+ nativeProfitAmount = await getTokenToNativeQuote(provider, outputToken, totalTokenProfit, chainId);
546
+ // 如果报价失败(返回 0),跳过利润提取
547
+ if (nativeProfitAmount === 0n) {
548
+ return;
549
+ }
550
+ }
498
551
  const profitNonce = await nonceManager.getNextNonce(wallets[maxRevenueIndex]);
499
552
  const profitTx = await wallets[maxRevenueIndex].signTransaction({
500
553
  to: getProfitRecipient(),
501
- value: totalProfit,
554
+ value: nativeProfitAmount, // ✅ 转等值原生代币
502
555
  nonce: profitNonce,
503
556
  gasPrice,
504
557
  gasLimit: 21000n,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.3.16",
3
+ "version": "1.3.18",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",