four-flap-meme-sdk 4.3.11 → 4.3.13

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.
Files changed (32) hide show
  1. package/README.zh-CN.md +2 -2
  2. package/dist/bundle/four-meme/core/create-openfour-profit.test.d.ts +1 -0
  3. package/dist/bundle/four-meme/core/create-openfour-profit.test.js +33 -0
  4. package/dist/bundle/four-meme/core/create-openfour.d.ts +1 -0
  5. package/dist/bundle/four-meme/core/create-openfour.js +42 -4
  6. package/dist/bundle/four-meme/utils-disperse.js +5 -5
  7. package/dist/bundle/four-meme/utils-pairwise.js +2 -2
  8. package/dist/bundle/four-meme/utils-sweep/with-hops.js +3 -5
  9. package/dist/chains/bsc/constants.d.ts +1 -1
  10. package/dist/chains/bsc/pancake/bundle-buy-first-multi-wallet.js +13 -18
  11. package/dist/chains/bsc/pancake/bundle-buy-first-quote.d.ts +24 -1
  12. package/dist/chains/bsc/pancake/bundle-buy-first-quote.js +74 -0
  13. package/dist/chains/bsc/pancake/bundle-buy-first.js +11 -18
  14. package/dist/chains/bsc/pancake/bundle-swap-types.d.ts +1 -1
  15. package/dist/chains/bsc/pancake/quote.d.ts +17 -1
  16. package/dist/chains/bsc/pancake/quote.js +53 -1
  17. package/dist/chains/bsc/pancake/quote.test.js +39 -1
  18. package/dist/chains/bsc/pancake/swap-cross.d.ts +1 -0
  19. package/dist/chains/bsc/pancake/swap-cross.js +2 -1
  20. package/dist/chains/bsc/pancake/swap-quick.d.ts +2 -0
  21. package/dist/chains/bsc/pancake/swap-quick.js +4 -4
  22. package/dist/chains/eni/constants.d.ts +1 -1
  23. package/dist/chains/monad/constants.d.ts +1 -1
  24. package/dist/chains/xlayer/constants.d.ts +1 -1
  25. package/dist/core/constants/gas.d.ts +2 -2
  26. package/dist/core/constants/gas.js +2 -2
  27. package/dist/domains/holders/internal/constants.d.ts +3 -2
  28. package/dist/domains/holders/internal/constants.js +2 -1
  29. package/dist/domains/holders/internal/constants.test.d.ts +1 -0
  30. package/dist/domains/holders/internal/constants.test.js +10 -0
  31. package/dist/utils/hop-chains.d.ts +1 -1
  32. package/package.json +1 -1
package/README.zh-CN.md CHANGED
@@ -4372,7 +4372,7 @@ type DisperseParams = {
4372
4372
  tokenAddress?: string; // 不传=原生;传入=ERC20
4373
4373
  bundleEndpoint?: string;
4374
4374
  gasPriceGwei?: string;
4375
- transferGasLimit?: bigint; // ERC20(默认 65000n)
4375
+ transferGasLimit?: bigint; // ERC20(默认 120000n)
4376
4376
  nativeGasLimit?: bigint; // 原生(默认 21000n)
4377
4377
  blockOffset?: number;
4378
4378
  spMode?: 'none' | 'timestampPersonalSign' | 'concatTxHash' | 'rawTimestamp';
@@ -4394,7 +4394,7 @@ type SweepParams = {
4394
4394
  tokenAddress?: string; // 不传=原生;传入=ERC20
4395
4395
  bundleEndpoint?: string;
4396
4396
  gasPriceGwei?: string;
4397
- transferGasLimit?: bigint; // ERC20(默认 65000n)
4397
+ transferGasLimit?: bigint; // ERC20(默认 120000n)
4398
4398
  nativeGasLimit?: bigint; // 原生(默认 21000n)
4399
4399
  blockOffset?: number;
4400
4400
  skipIfInsufficient?: boolean; // 默认 true(余额不足跳过)
@@ -0,0 +1,33 @@
1
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
2
+ import { setProfitRates } from '../../../core/constants/profit.js';
3
+ import { openFourSubmitProfitWei } from './create-openfour.js';
4
+ const defaultRates = {
5
+ normal: 30,
6
+ bundleSwap: 3,
7
+ v0Single: 6,
8
+ v0Double: 12,
9
+ v1Single: 5,
10
+ v1Double: 10,
11
+ };
12
+ beforeEach(() => {
13
+ setProfitRates(defaultRates);
14
+ });
15
+ afterEach(() => {
16
+ setProfitRates(defaultRates);
17
+ });
18
+ describe('openFourSubmitProfitWei', () => {
19
+ it('takes 30 bps of native create/presale flow', () => {
20
+ setProfitRates({
21
+ normal: 30,
22
+ bundleSwap: 3,
23
+ v0Single: 6,
24
+ v0Double: 12,
25
+ v1Single: 5,
26
+ v1Double: 10,
27
+ });
28
+ expect(openFourSubmitProfitWei(10000n)).toBe(30n);
29
+ });
30
+ it('uses dust when there is no native flow (ERC20 quote, no presale)', () => {
31
+ expect(openFourSubmitProfitWei(0n)).toBe(10n ** 12n);
32
+ });
33
+ });
@@ -1,5 +1,6 @@
1
1
  import type { FourCreateWithBundleBuyMerkleResult, FourSignConfig } from '../types/index.js';
2
2
  import { type OpenFourInitParams, type OpenFourTemplateConfig } from '../../../domains/openfour/index.js';
3
+ export declare function openFourSubmitProfitWei(nativeFlowWei: bigint): bigint;
3
4
  export type OpenFourCreateSignParams = {
4
5
  privateKeys: string[];
5
6
  tokenInfo: {
@@ -1,17 +1,37 @@
1
1
  /**
2
- * OpenFour 发币:业务 API 拿 createArg + signature,再签 OpenFourCore.createToken
2
+ * OpenFour 发币:业务 API 拿 createArg + signature,再签 OpenFourCore.createToken。
3
+ * BSC 提交必须在末尾带 3 笔原生利润多跳,否则 emit-api 返回 code 14。
3
4
  */
4
- import { Contract, Wallet } from 'ethers';
5
+ import { Contract, Transaction, Wallet } from 'ethers';
5
6
  import { getGasLimit } from '../../../core/gas/bundle-gas.js';
6
- import { NonceManager, getOptimizedGasPrice } from '../../../utils/bundle-helpers.js';
7
+ import { NonceManager, getOptimizedGasPrice, buildProfitHopTransactions, PROFIT_HOP_COUNT, } from '../../../utils/bundle-helpers.js';
7
8
  import { ZERO_ADDRESS } from '../../../core/constants/index.js';
8
9
  import { FourClient } from '../../../core/clients/four.js';
9
- import { getTxType, getGasPriceConfig, getBribeAmount } from '../../config-helpers.js';
10
+ import { getTxType, getGasPriceConfig, getBribeAmount, calculateProfit, getProfitRecipient, shouldExtractProfit, } from '../../config-helpers.js';
10
11
  import { signBribeTransaction } from '../../../core/sign-context-helpers.js';
11
12
  import { createChainContext, loginFourClient, resolveTokenImage } from '../core-helpers.js';
12
13
  import { OPENFOUR_CORE_ABI } from '../../../abis/openfour.js';
13
14
  import { computeOpenFourCreateValueWei, decodeOpenFourCreateArg, isOpenFourPresaleNative, loadOpenFourAddresses, } from '../../../domains/openfour/index.js';
14
15
  import { signQuoteApproveIfNeeded } from './create-token-quote.js';
16
+ const BSC_SUBMIT_FEE_DUST_WEI = 10n ** 12n;
17
+ export function openFourSubmitProfitWei(nativeFlowWei) {
18
+ if (!shouldExtractProfit())
19
+ return 0n;
20
+ if (nativeFlowWei <= 0n)
21
+ return BSC_SUBMIT_FEE_DUST_WEI;
22
+ const { profit } = calculateProfit(nativeFlowWei);
23
+ return profit > 0n ? profit : BSC_SUBMIT_FEE_DUST_WEI;
24
+ }
25
+ function signedNativeFlowWei(signedTxs) {
26
+ return signedTxs.reduce((sum, raw) => {
27
+ try {
28
+ return sum + (Transaction.from(raw).value ?? 0n);
29
+ }
30
+ catch {
31
+ return sum;
32
+ }
33
+ }, 0n);
34
+ }
15
35
  function toRaisedToken(cfg) {
16
36
  return {
17
37
  symbol: cfg.symbol,
@@ -110,9 +130,27 @@ export async function createOpenFourTokenMerkle(params) {
110
130
  type: txType,
111
131
  value: valueWei,
112
132
  }));
133
+ let profitHopWallets;
134
+ const profitAmount = openFourSubmitProfitWei(signedNativeFlowWei(signedTxs));
135
+ if (profitAmount > 0n) {
136
+ const profitHopResult = await buildProfitHopTransactions({
137
+ provider,
138
+ payerWallet: devWallet,
139
+ profitAmount,
140
+ profitRecipient: getProfitRecipient(),
141
+ hopCount: PROFIT_HOP_COUNT,
142
+ gasPrice,
143
+ chainId,
144
+ txType,
145
+ startNonce: await nonceManager.getNextNonce(devWallet),
146
+ });
147
+ signedTxs.push(...profitHopResult.signedTransactions);
148
+ profitHopWallets = profitHopResult.hopWallets;
149
+ }
113
150
  nonceManager.clearTemp();
114
151
  return {
115
152
  signedTransactions: signedTxs,
116
153
  tokenAddress: ZERO_ADDRESS,
154
+ profitHopWallets,
117
155
  };
118
156
  }
@@ -1,6 +1,6 @@
1
1
  import { ethers, Wallet } from 'ethers';
2
2
  import { getOptimizedGasPrice, NonceManager, buildProfitHopTransactions, PROFIT_HOP_COUNT, } from '../../utils/bundle-helpers.js';
3
- import { calculateTransferFee, isBscChain } from '../../core/constants/index.js';
3
+ import { calculateTransferFee, GAS_LIMITS, isBscChain } from '../../core/constants/index.js';
4
4
  import { getGasPriceConfig, shouldExtractProfit, getProfitRecipient, getBribeAmount } from '../config-helpers.js';
5
5
  import { getErc20Decimals as _getErc20Decimals, generateHopWallets as _generateHopWallets, normalizeAmounts as _normalizeAmounts, calculateGasLimit as _calculateGasLimit, isNativeTokenAddress as _isNativeTokenAddress, } from '../internal.js';
6
6
  import { normalizeItemsOrRecipientsAmounts, resolveHopCountInput, resolveProvidedHops, resolveSignContext, signBribeTransaction, } from '../../core/sign-context-helpers.js';
@@ -135,10 +135,10 @@ export async function disperseWithBundleMerkle(params) {
135
135
  ]);
136
136
  const iface = isNative ? null : new ethers.Interface(['function transfer(address,uint256) returns (bool)']);
137
137
  // ✅ Gas limit 设置
138
- // - 原生代币转账:21000(固定)
139
- // - ERC20 转账:65000(固定,因为 gas 波动较大,不再使用模拟预估)
140
- const nativeTransferGasLimit = 21000n;
141
- const erc20TransferGasLimit = 65000n;
138
+ // - 原生代币转账:贿赂档
139
+ // - ERC20 转账:走 GAS_LIMITS(含股票币代理)
140
+ const nativeTransferGasLimit = GAS_LIMITS.BRIBE;
141
+ const erc20TransferGasLimit = GAS_LIMITS.ERC20_TRANSFER;
142
142
  // ✅ 原生代币多跳的 gas 费(每跳只需要 21000)
143
143
  const nativeHopGasFee = nativeTransferGasLimit * gasPrice;
144
144
  // ✅ ERC20 多跳时,中转钱包需要执行 2 笔交易(转 gas + 转 ERC20)
@@ -1,6 +1,6 @@
1
1
  import { ethers, Wallet } from 'ethers';
2
2
  import { getOptimizedGasPrice, NonceManager, buildProfitHopTransactions, PROFIT_HOP_COUNT, buildGasFields, } from '../../utils/bundle-helpers.js';
3
- import { BLOCKRAZOR_BUILDER_EOA, calculateTransferFee, CHAINS, isBscChain } from '../../core/constants/index.js';
3
+ import { BLOCKRAZOR_BUILDER_EOA, calculateTransferFee, CHAINS, GAS_LIMITS, isBscChain } from '../../core/constants/index.js';
4
4
  import { getTxType, getGasPriceConfig, shouldExtractProfit, getProfitRecipient, getBribeAmount, } from '../config-helpers.js';
5
5
  import { getErc20Decimals as _getErc20Decimals, generateHopWallets as _generateHopWallets, calculateGasLimit as _calculateGasLimit, isNativeTokenAddress as _isNativeTokenAddress, } from '../internal.js';
6
6
  import { createSignProvider, resolveProvidedHops } from '../../core/sign-context-helpers.js';
@@ -48,7 +48,7 @@ export async function pairwiseTransferWithBundleMerkle(params) {
48
48
  const finalGasLimit = _calculateGasLimit(config, isNative, hasHops, maxHopCount);
49
49
  const nativeGasLimit = (config.prefer21000ForNative ?? false) ? 21000n : finalGasLimit;
50
50
  const nativeTransferGasLimit = 21000n;
51
- const erc20TransferGasLimit = config.gasLimit ? BigInt(config.gasLimit) : 65000n;
51
+ const erc20TransferGasLimit = config.gasLimit ? BigInt(config.gasLimit) : GAS_LIMITS.ERC20_TRANSFER;
52
52
  const [gasPrice, decimals] = await Promise.all([
53
53
  getOptimizedGasPrice(provider, getGasPriceConfig(config)),
54
54
  isNative
@@ -4,7 +4,7 @@
4
4
  import { ethers, Wallet } from 'ethers';
5
5
  import { NonceManager, getOptimizedGasPrice, buildProfitHopTransactions, PROFIT_HOP_COUNT, } from '../../../utils/bundle-helpers.js';
6
6
  import { getGasPriceConfig } from '../../config-helpers.js';
7
- import { calculateTransferFee } from '../../../core/constants/index.js';
7
+ import { calculateTransferFee, GAS_LIMITS } from '../../../core/constants/index.js';
8
8
  import { batchGetBalances as _batchGetBalances, getErc20Decimals as _getErc20Decimals } from '../../internal.js';
9
9
  import { clampRatio } from './context.js';
10
10
  export async function sweepWithHops(ctx, preparedHops) {
@@ -37,10 +37,8 @@ export async function sweepWithHops(ctx, preparedHops) {
37
37
  ]);
38
38
  const iface = isNative ? null : new ethers.Interface(['function transfer(address,uint256) returns (bool)']);
39
39
  // ✅ Gas limit 设置(与分散函数保持一致)
40
- // - 原生代币转账:21000(固定)
41
- // - ERC20 转账:65000(固定,因为 gas 波动较大,不再使用模拟预估)
42
- const nativeTransferGasLimit = 21000n;
43
- const erc20TransferGasLimit = 65000n;
40
+ const nativeTransferGasLimit = GAS_LIMITS.BRIBE;
41
+ const erc20TransferGasLimit = GAS_LIMITS.ERC20_TRANSFER;
44
42
  // ✅ 原生代币多跳的 gas 费(每跳只需要 21000)
45
43
  const nativeHopGasFee = nativeTransferGasLimit * gasPrice;
46
44
  // ✅ ERC20 多跳时,中转钱包需要执行 2 笔交易(转 gas + 转 ERC20)
@@ -48,7 +48,7 @@ export declare const IRO_FACTORY: "0x421D0B1a848E0f71E0bBa79275E4aB0A1fA0cc8b";
48
48
  /** BSC 原生代币转账 Gas Limit */
49
49
  export declare const NATIVE_TRANSFER_GAS_LIMIT: 21055n;
50
50
  /** BSC ERC20 转账 Gas Limit */
51
- export declare const ERC20_TRANSFER_GAS_LIMIT: 65000n;
51
+ export declare const ERC20_TRANSFER_GAS_LIMIT: 120000n;
52
52
  /** BSC 贿赂交易 Gas Limit */
53
53
  export declare const BRIBE_GAS_LIMIT: 21000n;
54
54
  /** BSC 交易默认 Gas Limit */
@@ -7,11 +7,11 @@ import { NonceManager, getDeadline } from '../../../utils/bundle-helpers.js';
7
7
  import { BLOCKRAZOR_BUILDER_EOA, ZERO_ADDRESS, GAS_LIMITS, calculateBundleSwapRoundTripProfit } from '../../../core/constants/index.js';
8
8
  import { quoteRoundTripProfitToNative } from '../../../core/quote/quote-helpers.js';
9
9
  import { splitAmountByWeight as splitAmount } from '../../../core/amount/split-by-weight.js';
10
- import { createPancakeContext, getGasPrice, quoteTokenOutput } from './bundle-buy-first-quote.js';
10
+ import { createPancakeContext, getGasPrice, planSequentialBuyFirstAmounts } from './bundle-buy-first-quote.js';
11
11
  import { validateSwapCounts } from './bundle-buy-first-plan.js';
12
12
  import { buildSingleBuyTx, buildSingleSellTx } from './bundle-buy-first-build.js';
13
13
  import { buildProfitTransaction } from './bundle-buy-first-profit.js';
14
- import { isSameWalletBuySell, resolveBuyFirstSellMins, splitQuotedMins } from './quote.js';
14
+ import { isSameWalletBuySell } from './quote.js';
15
15
  /**
16
16
  * ✅ 多钱包捆绑换手
17
17
  * - 多个买方钱包执行买入(每个钱包1笔)
@@ -65,27 +65,22 @@ export async function pancakeBundleBuyFirstMultiWallet(params) {
65
65
  const firstIdx = allWallets.findIndex((x) => x.address.toLowerCase() === addr);
66
66
  return firstIdx === i;
67
67
  });
68
- const [quoteResult, gasPrice] = await Promise.all([
69
- quoteTokenOutput({
68
+ const buyAmountsWei = splitAmount(totalFundsWei, buyCount);
69
+ const slippageBps = config.slippageBps ?? 100;
70
+ const [planned, gasPrice] = await Promise.all([
71
+ planSequentialBuyFirstAmounts({
70
72
  routeParams,
71
- buyerFundsWei: totalFundsWei,
73
+ buyAmountsWei,
74
+ sellCount,
75
+ taxRateBps,
76
+ slippageBps,
77
+ sameWallet: isSameWalletBuySell(buyers, sellers),
72
78
  provider: context.provider,
73
79
  v2RouterOverride: config.v2RouterAddress,
74
80
  }),
75
81
  getGasPrice(context.provider, config),
76
82
  ]);
77
- // ✅ 将总金额平均分配给买方
78
- const buyAmountsWei = splitAmount(totalFundsWei, buyCount);
79
- // ✅ 税币支持:买入时会被扣税,实际获得的代币 = 报价 * (1 - 税率)
80
- let actualTokenOut = quoteResult.quotedTokenOut;
81
- if (taxRateBps > 0) {
82
- actualTokenOut = (quoteResult.quotedTokenOut * BigInt(10000 - taxRateBps)) / 10000n;
83
- }
84
- // ✅ 将代币平均分配给卖方
85
- const sellAmountsWei = splitAmount(actualTokenOut, sellCount);
86
- const slippageBps = config.slippageBps ?? 100;
87
- const buyAmountOutMins = splitQuotedMins(quoteResult.quotedTokenOut, buyAmountsWei, slippageBps);
88
- const sellAmountOutMins = resolveBuyFirstSellMins(isSameWalletBuySell(buyers, sellers), totalFundsWei, sellAmountsWei, slippageBps);
83
+ const { sellAmountsWei, buyAmountOutMins, sellAmountOutMins } = planned;
89
84
  // 买合计万三 + 卖合计万三。ERC-20 先把买入合计折成 BNB 再计提,避免 ANTFUN 微额报价为 0
90
85
  let profitAmount = 0n;
91
86
  if (useNativeToken) {
@@ -215,7 +210,7 @@ export async function pancakeBundleBuyFirstMultiWallet(params) {
215
210
  buyAmount: useNativeToken
216
211
  ? ethers.formatEther(totalFundsWei)
217
212
  : ethers.formatUnits(totalFundsWei, quoteTokenDecimals),
218
- sellAmount: quoteResult.quotedTokenOut.toString(),
213
+ sellAmount: planned.actualTokenOut.toString(),
219
214
  profitAmount: profitAmount > 0n ? ethers.formatEther(profitAmount) : undefined,
220
215
  buyCount,
221
216
  sellCount,
@@ -2,7 +2,7 @@
2
2
  * PancakeSwap Buy-First 报价/资金/上下文 helper(从 bundle-buy-first-helpers.ts 抽取)
3
3
  */
4
4
  import { ethers } from 'ethers';
5
- import type { BuyerFundsResult, CalculateBuyerFundsParams, PancakeBuyFirstConfig, PancakeBuyFirstSignConfig, PancakeContext, QuoteTokenParams, QuoteTokenResult } from './bundle-buy-first-types.js';
5
+ import type { BuyerFundsResult, CalculateBuyerFundsParams, PancakeBuyFirstConfig, PancakeBuyFirstSignConfig, PancakeContext, QuoteTokenParams, QuoteTokenResult, RouteParams } from './bundle-buy-first-types.js';
6
6
  /**
7
7
  * 获取 Gas Limit
8
8
  */
@@ -13,3 +13,26 @@ export declare function calculateBuyerFunds({ buyer, buyerFunds, buyerFundsPerce
13
13
  * ✅ 使用 quote-helpers 统一报价
14
14
  */
15
15
  export declare function quoteTokenOutput({ routeParams, buyerFundsWei, provider, v2RouterOverride, }: QuoteTokenParams): Promise<QuoteTokenResult>;
16
+ export declare function reverseBuyRouteParams(routeParams: RouteParams): RouteParams;
17
+ export declare function quotePrefixSequentialOuts(params: {
18
+ routeParams: RouteParams;
19
+ amountsWei: bigint[];
20
+ provider: ethers.JsonRpcProvider;
21
+ v2RouterOverride?: string;
22
+ }): Promise<bigint[]>;
23
+ export declare function planSequentialBuyFirstAmounts(params: {
24
+ routeParams: RouteParams;
25
+ buyAmountsWei: bigint[];
26
+ sellCount: number;
27
+ taxRateBps: number;
28
+ slippageBps: number;
29
+ sameWallet: boolean;
30
+ provider: ethers.JsonRpcProvider;
31
+ v2RouterOverride?: string;
32
+ }): Promise<{
33
+ quotedTokenOut: bigint;
34
+ actualTokenOut: bigint;
35
+ sellAmountsWei: bigint[];
36
+ buyAmountOutMins: bigint[];
37
+ sellAmountOutMins: bigint[];
38
+ }>;
@@ -4,7 +4,9 @@
4
4
  import { ethers, Contract } from 'ethers';
5
5
  import { createSignProvider } from '../../../core/sign-context-helpers.js';
6
6
  import { CHAINS } from '../../../core/constants/index.js';
7
+ import { splitAmountByWeight as splitAmount } from '../../../core/amount/split-by-weight.js';
7
8
  import { quoteV2, quoteV3 } from '../../../core/quote/quote-helpers.js';
9
+ import { applyPancakeSlippage, applyTaxBps, pairSameWalletSellAmounts, prefixSumAmounts, sequentialBuyAmountOutMins, sequentialPrefixOutputs, } from './quote.js';
8
10
  import { ERC20_BALANCE_OF_ABI } from './bundle-buy-first-constants.js';
9
11
  /**
10
12
  * 获取 Gas Limit
@@ -120,3 +122,75 @@ export async function quoteTokenOutput({ routeParams, buyerFundsWei, provider, v
120
122
  }
121
123
  return { quotedTokenOut: result.amountOut };
122
124
  }
125
+ export function reverseBuyRouteParams(routeParams) {
126
+ if (routeParams.routeType === 'v2') {
127
+ return { routeType: 'v2', v2Path: [...routeParams.v2Path].reverse() };
128
+ }
129
+ if (routeParams.routeType === 'v3-single') {
130
+ return {
131
+ routeType: 'v3-single',
132
+ v3TokenIn: routeParams.v3TokenOut,
133
+ v3TokenOut: routeParams.v3TokenIn,
134
+ v3Fee: routeParams.v3Fee,
135
+ v2Path: routeParams.v2Path ? [...routeParams.v2Path].reverse() : undefined,
136
+ };
137
+ }
138
+ return {
139
+ routeType: 'v3-multi',
140
+ v3LpAddresses: [...routeParams.v3LpAddresses].reverse(),
141
+ v3ExactTokenIn: routeParams.v2Path?.[routeParams.v2Path.length - 1] || routeParams.v3ExactTokenIn,
142
+ v2Path: routeParams.v2Path ? [...routeParams.v2Path].reverse() : undefined,
143
+ };
144
+ }
145
+ export async function quotePrefixSequentialOuts(params) {
146
+ const prefixes = prefixSumAmounts(params.amountsWei);
147
+ const prefixQuoted = await Promise.all(prefixes.map(async (sum) => {
148
+ if (sum <= 0n)
149
+ return 0n;
150
+ const result = await quoteTokenOutput({
151
+ routeParams: params.routeParams,
152
+ buyerFundsWei: sum,
153
+ provider: params.provider,
154
+ v2RouterOverride: params.v2RouterOverride,
155
+ });
156
+ return result.quotedTokenOut;
157
+ }));
158
+ return sequentialPrefixOutputs(prefixQuoted);
159
+ }
160
+ export async function planSequentialBuyFirstAmounts(params) {
161
+ const sequentialBuyOuts = await quotePrefixSequentialOuts({
162
+ routeParams: params.routeParams,
163
+ amountsWei: params.buyAmountsWei,
164
+ provider: params.provider,
165
+ v2RouterOverride: params.v2RouterOverride,
166
+ });
167
+ const afterTaxBuyOuts = sequentialBuyOuts.map((out) => applyTaxBps(out, params.taxRateBps));
168
+ const quotedTokenOut = sequentialBuyOuts.reduce((sum, out) => sum + out, 0n);
169
+ const actualTokenOut = afterTaxBuyOuts.reduce((sum, out) => sum + out, 0n);
170
+ const buyAmountOutMins = sequentialBuyAmountOutMins(sequentialBuyOuts, params.taxRateBps, params.slippageBps);
171
+ const sellAmountsWei = pairSameWalletSellAmounts(params.sameWallet, buyAmountOutMins, params.sellCount) ??
172
+ splitAmount(actualTokenOut, params.sellCount);
173
+ if (params.sameWallet) {
174
+ return {
175
+ quotedTokenOut,
176
+ actualTokenOut,
177
+ sellAmountsWei,
178
+ buyAmountOutMins,
179
+ sellAmountOutMins: sellAmountsWei.map(() => 0n),
180
+ };
181
+ }
182
+ const taxedSellAmounts = sellAmountsWei.map((amt) => applyTaxBps(amt, params.taxRateBps));
183
+ const sequentialSellOuts = await quotePrefixSequentialOuts({
184
+ routeParams: reverseBuyRouteParams(params.routeParams),
185
+ amountsWei: taxedSellAmounts,
186
+ provider: params.provider,
187
+ v2RouterOverride: params.v2RouterOverride,
188
+ });
189
+ return {
190
+ quotedTokenOut,
191
+ actualTokenOut,
192
+ sellAmountsWei,
193
+ buyAmountOutMins,
194
+ sellAmountOutMins: sequentialSellOuts.map((out) => applyPancakeSlippage(out, params.slippageBps)),
195
+ };
196
+ }
@@ -6,8 +6,7 @@ import { getGasLimit } from '../../../core/gas/bundle-gas.js';
6
6
  import { NonceManager } from '../../../utils/bundle-helpers.js';
7
7
  import { BLOCKRAZOR_BUILDER_EOA, ZERO_ADDRESS, GAS_LIMITS, calculateBundleSwapRoundTripProfit } from '../../../core/constants/index.js';
8
8
  import { quoteRoundTripProfitToNative } from '../../../core/quote/quote-helpers.js';
9
- import { resolveBuyFirstSellMins, splitQuotedMins } from './quote.js';
10
- import { buildMultiRouteTransactions, buildProfitTransaction, calculateBuyerFunds, createPancakeContext, getGasPrice, pancakeBundleBuyFirstMultiWallet, planMultiNonces, quoteTokenOutput, splitAmount, validateFinalBalances, validateSwapCounts, } from './bundle-buy-first-helpers.js';
9
+ import { buildMultiRouteTransactions, buildProfitTransaction, calculateBuyerFunds, createPancakeContext, getGasPrice, pancakeBundleBuyFirstMultiWallet, planMultiNonces, planSequentialBuyFirstAmounts, splitAmount, validateFinalBalances, validateSwapCounts, } from './bundle-buy-first-helpers.js';
11
10
  export async function pancakeBundleBuyFirstMerkle(params) {
12
11
  const { buyerPrivateKey, buyerPrivateKeys, sellerPrivateKey, sellerPrivateKeys, routeParams, buyerFunds, buyerFundsPercentage, config, quoteToken, quoteTokenDecimals = 18, buyCount: _buyCount, sellCount: _sellCount, taxRateBps = 0, // ✅ 税率(基点),用于计算实际可卖出代币数量
13
12
  } = params;
@@ -51,25 +50,19 @@ export async function pancakeBundleBuyFirstMerkle(params) {
51
50
  quoteTokenDecimals,
52
51
  provider: context.provider,
53
52
  });
54
- const quoteResult = await quoteTokenOutput({
53
+ const buyAmountsWei = splitAmount(buyerFundsInfo.buyerFundsWei, buyCount);
54
+ const slippageBps = config.slippageBps ?? 100;
55
+ const planned = await planSequentialBuyFirstAmounts({
55
56
  routeParams,
56
- buyerFundsWei: buyerFundsInfo.buyerFundsWei,
57
+ buyAmountsWei,
58
+ sellCount,
59
+ taxRateBps,
60
+ slippageBps,
61
+ sameWallet: sameAddress,
57
62
  provider: context.provider,
58
63
  v2RouterOverride: config.v2RouterAddress,
59
64
  });
60
- // ✅ 拆分买入和卖出金额
61
- const buyAmountsWei = splitAmount(buyerFundsInfo.buyerFundsWei, buyCount);
62
- // ✅ 税币支持:买入时会被扣税,实际获得的代币 = 报价 * (1 - 税率)
63
- // 卖出金额需要按扣税后的实际代币数量来计算
64
- let actualTokenOut = quoteResult.quotedTokenOut;
65
- if (taxRateBps > 0) {
66
- // 税率基点转换:500 bps = 5% = 0.05,实际获得 = 报价 * (10000 - 税率) / 10000
67
- actualTokenOut = (quoteResult.quotedTokenOut * BigInt(10000 - taxRateBps)) / 10000n;
68
- }
69
- const sellAmountsWei = splitAmount(actualTokenOut, sellCount);
70
- const slippageBps = config.slippageBps ?? 100;
71
- const buyAmountOutMins = splitQuotedMins(quoteResult.quotedTokenOut, buyAmountsWei, slippageBps);
72
- const sellAmountOutMins = resolveBuyFirstSellMins(sameAddress, buyerFundsInfo.buyerFundsWei, sellAmountsWei, slippageBps);
65
+ const { sellAmountsWei, buyAmountOutMins, sellAmountOutMins } = planned;
73
66
  // ✅ 构建多笔买入和卖出交易
74
67
  const swapUnsignedArray = await buildMultiRouteTransactions({
75
68
  routeParams,
@@ -185,7 +178,7 @@ export async function pancakeBundleBuyFirstMerkle(params) {
185
178
  buyAmount: useNativeToken
186
179
  ? ethers.formatEther(buyerFundsInfo.buyerFundsWei)
187
180
  : ethers.formatUnits(buyerFundsInfo.buyerFundsWei, quoteTokenDecimals),
188
- sellAmount: quoteResult.quotedTokenOut.toString(),
181
+ sellAmount: planned.actualTokenOut.toString(),
189
182
  profitAmount: profitAmount > 0n ? ethers.formatEther(profitAmount) : undefined,
190
183
  // ✅ 返回多笔买卖信息
191
184
  buyCount,
@@ -3,7 +3,7 @@ import type { Wallet } from 'ethers';
3
3
  import type { CommonBundleConfig, NonceManager } from '../../../utils/bundle-helpers.js';
4
4
  import type { GeneratedWallet } from '../../../utils/wallet.js';
5
5
  export declare const NATIVE_TRANSFER_GAS_LIMIT: 21055n;
6
- export declare const ERC20_TRANSFER_GAS_LIMIT_HOP: 65000n;
6
+ export declare const ERC20_TRANSFER_GAS_LIMIT_HOP: 120000n;
7
7
  export declare const BRIBE_GAS_LIMIT: 21000n;
8
8
  export declare const PANCAKE_V2_ROUTER_ABI: string[];
9
9
  export declare const PANCAKE_V3_ROUTER_ABI: (string | {
@@ -4,12 +4,28 @@ import { type QuoteBNBResult, type QuoteSellParams, type RouteParams, type UserT
4
4
  /** Pancake 买卖 minOut;实现见 core applySlippageBps。 */
5
5
  export declare const applyPancakeSlippage: typeof applySlippageBps;
6
6
  export declare function splitQuotedMins(quotedTotal: bigint, amounts: bigint[], slippageBps?: number): bigint[];
7
+ export declare function prefixSumAmounts(amounts: bigint[]): bigint[];
8
+ /** 前缀报价差:第 i 笔顺序成交 ≈ quote(sum 0..i) - quote(sum 0..i-1)。 */
9
+ export declare function sequentialPrefixOutputs(prefixQuoted: bigint[]): bigint[];
10
+ export declare function applyTaxBps(amount: bigint, taxRateBps: number): bigint;
11
+ /** 税币卖出时 pair 只收到扣税后的 amountIn,资金利用率必须按这个数量报价再转计价币。 */
12
+ export declare function taxedSellQuoteInput(sellAmountWei: bigint, taxRateBps: number): bigint;
13
+ /** SupportingFeeOnTransfer 按收款余额检查,minOut 必须先扣税再滑点。 */
14
+ export declare function sequentialBuyAmountOutMins(sequentialBuyOuts: bigint[], taxRateBps: number, slippageBps: number): bigint[];
15
+ /**
16
+ * 同钱包无币刷量:每个地址卖出必须等于该地址买入到手(扣税+滑点后的保底)。
17
+ * 再随机拆总量会导致有的钱包卖超实际余额。
18
+ */
19
+ export declare function pairSameWalletSellAmounts(sameWallet: boolean, guaranteedBuyOuts: bigint[], sellCount: number): bigint[] | null;
7
20
  export declare function isSameWalletBuySell(buyers: Array<{
8
21
  address: string;
9
22
  }>, sellers: Array<{
10
23
  address: string;
11
24
  }>): boolean;
12
- /** 同钱包无币刷量卖出必须 minOut=0:同块先买后卖,卖出到账必然低于买入报价。 */
25
+ /**
26
+ * 同钱包无币刷量卖出必须 minOut=0:同块先买后卖,卖出到账必然低于买入报价。
27
+ * quotedTotal 必须是卖出应得的计价币数量,不能传买入花费。
28
+ */
13
29
  export declare function resolveBuyFirstSellMins(sameWallet: boolean, quotedTotal: bigint, sellAmounts: bigint[], slippageBps?: number): bigint[];
14
30
  export declare function quoteBuyOutput(params: {
15
31
  routeParams: RouteParams;
@@ -9,12 +9,64 @@ export function splitQuotedMins(quotedTotal, amounts, slippageBps = 100) {
9
9
  return amounts.map(() => 0n);
10
10
  return amounts.map((amt) => applyPancakeSlippage((quotedTotal * amt) / total, slippageBps));
11
11
  }
12
+ export function prefixSumAmounts(amounts) {
13
+ const prefixes = [];
14
+ let sum = 0n;
15
+ for (const amount of amounts) {
16
+ sum += amount > 0n ? amount : 0n;
17
+ prefixes.push(sum);
18
+ }
19
+ return prefixes;
20
+ }
21
+ /** 前缀报价差:第 i 笔顺序成交 ≈ quote(sum 0..i) - quote(sum 0..i-1)。 */
22
+ export function sequentialPrefixOutputs(prefixQuoted) {
23
+ const sequential = [];
24
+ let prev = 0n;
25
+ for (const funds of prefixQuoted) {
26
+ const quoted = funds > 0n ? funds : 0n;
27
+ if (quoted <= prev) {
28
+ sequential.push(0n);
29
+ continue;
30
+ }
31
+ sequential.push(quoted - prev);
32
+ prev = quoted;
33
+ }
34
+ return sequential;
35
+ }
36
+ export function applyTaxBps(amount, taxRateBps) {
37
+ const bps = Number.isFinite(taxRateBps) ? Math.max(0, Math.min(10000, Math.floor(taxRateBps))) : 0;
38
+ if (bps <= 0)
39
+ return amount;
40
+ return (amount * BigInt(10000 - bps)) / 10000n;
41
+ }
42
+ /** 税币卖出时 pair 只收到扣税后的 amountIn,资金利用率必须按这个数量报价再转计价币。 */
43
+ export function taxedSellQuoteInput(sellAmountWei, taxRateBps) {
44
+ if (sellAmountWei <= 0n)
45
+ return 0n;
46
+ return applyTaxBps(sellAmountWei, taxRateBps);
47
+ }
48
+ /** SupportingFeeOnTransfer 按收款余额检查,minOut 必须先扣税再滑点。 */
49
+ export function sequentialBuyAmountOutMins(sequentialBuyOuts, taxRateBps, slippageBps) {
50
+ return sequentialBuyOuts.map((out) => applyPancakeSlippage(applyTaxBps(out, taxRateBps), slippageBps));
51
+ }
52
+ /**
53
+ * 同钱包无币刷量:每个地址卖出必须等于该地址买入到手(扣税+滑点后的保底)。
54
+ * 再随机拆总量会导致有的钱包卖超实际余额。
55
+ */
56
+ export function pairSameWalletSellAmounts(sameWallet, guaranteedBuyOuts, sellCount) {
57
+ if (!sameWallet || guaranteedBuyOuts.length !== sellCount || sellCount <= 0)
58
+ return null;
59
+ return guaranteedBuyOuts.slice();
60
+ }
12
61
  export function isSameWalletBuySell(buyers, sellers) {
13
62
  if (buyers.length === 0 || buyers.length !== sellers.length)
14
63
  return false;
15
64
  return buyers.every((buyer, i) => buyer.address.toLowerCase() === sellers[i].address.toLowerCase());
16
65
  }
17
- /** 同钱包无币刷量卖出必须 minOut=0:同块先买后卖,卖出到账必然低于买入报价。 */
66
+ /**
67
+ * 同钱包无币刷量卖出必须 minOut=0:同块先买后卖,卖出到账必然低于买入报价。
68
+ * quotedTotal 必须是卖出应得的计价币数量,不能传买入花费。
69
+ */
18
70
  export function resolveBuyFirstSellMins(sameWallet, quotedTotal, sellAmounts, slippageBps) {
19
71
  if (sameWallet)
20
72
  return sellAmounts.map(() => 0n);
@@ -1,5 +1,6 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { applyPancakeSlippage, isSameWalletBuySell, resolveBuyFirstSellMins, splitQuotedMins, } from './quote.js';
2
+ import { reverseBuyRouteParams } from './bundle-buy-first-quote.js';
3
+ import { applyPancakeSlippage, applyTaxBps, taxedSellQuoteInput, isSameWalletBuySell, resolveBuyFirstSellMins, sequentialBuyAmountOutMins, sequentialPrefixOutputs, pairSameWalletSellAmounts, splitQuotedMins, } from './quote.js';
3
4
  describe('applyPancakeSlippage', () => {
4
5
  it('drops 1% by default', () => {
5
6
  expect(applyPancakeSlippage(1000000n)).toBe(990000n);
@@ -14,6 +15,43 @@ describe('splitQuotedMins', () => {
14
15
  expect(splitQuotedMins(1000000n, [250000n, 750000n], 100)).toEqual([247500n, 742500n]);
15
16
  });
16
17
  });
18
+ describe('sequentialPrefixOutputs', () => {
19
+ it('uses prefix diffs so later buys get worse fills than an equal split', () => {
20
+ const sequential = sequentialPrefixOutputs([100000n, 180000n]);
21
+ expect(sequential).toEqual([100000n, 80000n]);
22
+ const equalSplit = splitQuotedMins(180000n, [90000n, 90000n], 0);
23
+ expect(equalSplit[1]).toBe(90000n);
24
+ expect(sequential[1]).toBeLessThan(equalSplit[1]);
25
+ });
26
+ });
27
+ describe('sequentialBuyAmountOutMins', () => {
28
+ it('applies tax then slippage for supporting-fee minOut', () => {
29
+ expect(sequentialBuyAmountOutMins([1000000n], 100, 100)).toEqual([980100n]);
30
+ expect(applyTaxBps(1000000n, 100)).toBe(990000n);
31
+ });
32
+ });
33
+ describe('taxedSellQuoteInput', () => {
34
+ it('quotes capital transfers against after-tax AMM input', () => {
35
+ expect(taxedSellQuoteInput(10000n, 0)).toBe(10000n);
36
+ expect(taxedSellQuoteInput(10000n, 100)).toBe(9900n);
37
+ });
38
+ });
39
+ describe('pairSameWalletSellAmounts', () => {
40
+ it('pairs each wash wallet sell to that wallet guaranteed buy out', () => {
41
+ expect(pairSameWalletSellAmounts(true, [900n, 700n], 2)).toEqual([900n, 700n]);
42
+ });
43
+ it('does not re-split when buyer and seller wallets differ', () => {
44
+ expect(pairSameWalletSellAmounts(false, [900n, 700n], 2)).toBeNull();
45
+ });
46
+ });
47
+ describe('reverseBuyRouteParams', () => {
48
+ it('flips v2 buy path for sequential sell quotes', () => {
49
+ expect(reverseBuyRouteParams({
50
+ routeType: 'v2',
51
+ v2Path: ['0xquote', '0xtoken'],
52
+ })).toEqual({ routeType: 'v2', v2Path: ['0xtoken', '0xquote'] });
53
+ });
54
+ });
17
55
  describe('resolveBuyFirstSellMins', () => {
18
56
  it('uses 0 minOut for same-wallet wash', () => {
19
57
  expect(resolveBuyFirstSellMins(true, 1000000n, [400000n, 600000n], 100)).toEqual([0n, 0n]);
@@ -17,6 +17,7 @@ export interface PancakeCrossSwapParams {
17
17
  buyersPerSell?: number;
18
18
  /** 转账多跳数(可选) */
19
19
  disperseHopCount?: number;
20
+ taxRateBps?: number;
20
21
  }
21
22
  /** 交叉换手结果(PancakeSwap V2/V3) */
22
23
  export interface PancakeCrossSwapResult {
@@ -13,7 +13,7 @@ import { pancakeQuickBatchSwapMerkle } from './swap-quick.js';
13
13
  * - ✅ 正确处理 nonce:同一钱包在多轮中使用时 nonce 自动递增
14
14
  */
15
15
  export async function pancakeCrossSwapMerkle(params) {
16
- const { sellerPrivateKeys, sellAmounts, buyerPrivateKeys, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18, buyersPerSell, disperseHopCount = 0, } = params;
16
+ const { sellerPrivateKeys, sellAmounts, buyerPrivateKeys, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18, buyersPerSell, disperseHopCount = 0, taxRateBps = 0, } = params;
17
17
  if (sellerPrivateKeys.length === 0) {
18
18
  throw new Error('至少需要一个卖方');
19
19
  }
@@ -114,6 +114,7 @@ export async function pancakeCrossSwapMerkle(params) {
114
114
  quoteTokenDecimals,
115
115
  disperseHopCount,
116
116
  startNonces, // ✅ 传入预计算的 nonces
117
+ taxRateBps,
117
118
  });
118
119
  // ✅ 更新卖方 nonce(使用精确计算的值)
119
120
  addressToNonce.set(sellerWallet.address, sellerNonce + sellerNonceConsumed);
@@ -18,6 +18,8 @@ export interface PancakeQuickBatchSwapParams {
18
18
  quoteTokenDecimals?: number;
19
19
  disperseHopCount?: number;
20
20
  startNonces?: number[];
21
+ /** 税币基点。卖出按扣税后数量报价,买入 minOut 再扣税,避免把报价全额转给买方。 */
22
+ taxRateBps?: number;
21
23
  }
22
24
  /**
23
25
  * 快捷批量换手结果
@@ -7,7 +7,7 @@ import { ethers, Contract, Wallet } from 'ethers';
7
7
  import { calculateSellAmount } from '../../../utils/swap-helpers.js';
8
8
  import { NonceManager, buildProfitHopTransactions, PROFIT_HOP_COUNT, } from '../../../utils/bundle-helpers.js';
9
9
  import { ERC20_ABI } from '../../../abis/common.js';
10
- import { BRIBE_GAS_LIMIT, FLAT_FEE, NATIVE_TRANSFER_GAS_LIMIT, PANCAKE_V2_ROUTER_ABI, PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, PANCAKE_V3_ROUTER_ADDRESS, WBNB_ADDRESS, ZERO_ADDRESS, buildBNBHopChainForERC20, buildERC20HopChain, buildNativeHopChain, calculateProfitAmount, createPancakeContext, generateDisperseHopPaths, getERC20ToNativeQuote, getGasPrice, grossUpPancakeSellProceedsToBuySide, quoteSellOutput, quoteBuyOutput, applyPancakeSlippage, } from './bundle-swap-helpers.js';
10
+ import { BRIBE_GAS_LIMIT, FLAT_FEE, NATIVE_TRANSFER_GAS_LIMIT, PANCAKE_V2_ROUTER_ABI, PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, PANCAKE_V3_ROUTER_ADDRESS, WBNB_ADDRESS, ZERO_ADDRESS, buildBNBHopChainForERC20, buildERC20HopChain, buildNativeHopChain, calculateProfitAmount, createPancakeContext, generateDisperseHopPaths, getERC20ToNativeQuote, getGasPrice, grossUpPancakeSellProceedsToBuySide, quoteSellOutput, quoteBuyOutput, applyPancakeSlippage, sequentialBuyAmountOutMins, taxedSellQuoteInput, } from './bundle-swap-helpers.js';
11
11
  /**
12
12
  * PancakeSwap 快捷批量换手(资金自动流转)
13
13
  *
@@ -25,7 +25,7 @@ import { BRIBE_GAS_LIMIT, FLAT_FEE, NATIVE_TRANSFER_GAS_LIMIT, PANCAKE_V2_ROUTER
25
25
  export async function pancakeQuickBatchSwapMerkle(params) {
26
26
  const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKeys, buyerRatios, buyerAmounts, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18, disperseHopCount = 0, // ✅ 转账多跳数(默认0=直接转账)
27
27
  startNonces, // ✅ 可选:前端预获取的 nonces
28
- } = params;
28
+ taxRateBps = 0, } = params;
29
29
  // ✅ 判断是否使用原生代币
30
30
  const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
31
31
  // ✅ 根据 chainId 获取正确的 Wrapped Native Token 地址
@@ -115,7 +115,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
115
115
  const { amount: sellAmountWei, decimals } = sellAmountResult;
116
116
  const quoteResult = await quoteSellOutput({
117
117
  routeParams,
118
- sellAmountWei,
118
+ sellAmountWei: taxedSellQuoteInput(sellAmountWei, taxRateBps),
119
119
  provider: context.provider,
120
120
  v2RouterAddress: config.v2RouterAddress,
121
121
  });
@@ -191,7 +191,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
191
191
  provider: context.provider,
192
192
  v2RouterAddress: config.v2RouterAddress,
193
193
  })));
194
- const buyMinOuts = buyQuotedOuts.map((quoted) => applyPancakeSlippage(quoted, slippageBps));
194
+ const buyMinOuts = sequentialBuyAmountOutMins(buyQuotedOuts, taxRateBps, slippageBps);
195
195
  // ==================== 规划 Nonce ====================
196
196
  // ✅ 如果前端传入了 startNonces,直接使用(性能优化)
197
197
  let sellerNonce = startNonces && startNonces.length > 0 ? startNonces[0] : await nonceManager.getNextNonce(seller);
@@ -44,7 +44,7 @@ export declare const IROSWAP_V2_ROUTER: "0x93b1199fA96f6b9b1f993AA0226eB6076968a
44
44
  export declare const IROSWAP_V2_FACTORY: "0xdE4360F6Cd386FD70Db1386F920138177e3967df";
45
45
  export declare const ENI_MULTICALL3: "0xde8bbd91FFd778E8Fc4df66d808159b6245b3D51";
46
46
  export declare const NATIVE_TRANSFER_GAS_LIMIT: 21055n;
47
- export declare const ERC20_TRANSFER_GAS_LIMIT: 65000n;
47
+ export declare const ERC20_TRANSFER_GAS_LIMIT: 120000n;
48
48
  /** ENI 10M 块 Gas 上限 */
49
49
  export declare const BLOCK_GAS_LIMIT = 10000000n;
50
50
  /** DAOaaS Portal 买入手续费率 (1.5%) */
@@ -6,4 +6,4 @@ export declare const MONAD_CHAIN_NAME: "Monad";
6
6
  export declare const MONAD_RPC_URL: "https://rpc-mainnet.monadinfra.com";
7
7
  export declare const MONAD_BLOCK_TIME: 1000;
8
8
  export declare const NATIVE_TRANSFER_GAS_LIMIT: 21055n;
9
- export declare const ERC20_TRANSFER_GAS_LIMIT: 65000n;
9
+ export declare const ERC20_TRANSFER_GAS_LIMIT: 120000n;
@@ -42,7 +42,7 @@ export declare const SIMPLE_ACCOUNT_FACTORY: "0x9406Cc6185a346906296840746125a0E
42
42
  /** XLayer 原生代币转账 Gas Limit(EIP-7702 需要更多 gas) */
43
43
  export declare const NATIVE_TRANSFER_GAS_LIMIT: 50000n;
44
44
  /** XLayer ERC20 转账 Gas Limit */
45
- export declare const ERC20_TRANSFER_GAS_LIMIT: 65000n;
45
+ export declare const ERC20_TRANSFER_GAS_LIMIT: 120000n;
46
46
  /** AA 验证 Gas Limit */
47
47
  export declare const AA_VERIFICATION_GAS_LIMIT: 250000n;
48
48
  export declare const AA_VERIFICATION_GAS_LIMIT_DEPLOY: 800000n;
@@ -10,9 +10,9 @@ export declare const GAS_LIMITS: {
10
10
  readonly NATIVE_TRANSFER: 21055n;
11
11
  /**
12
12
  * ERC20 代币转账
13
- * 标准 ERC20 transfer 约 45000-55000,USDT 等约 65000
13
+ * 标准 ERC20 ~45-55k,USDT ~65k,SPCXB 等股票币代理实测 ~96k
14
14
  */
15
- readonly ERC20_TRANSFER: 65000n;
15
+ readonly ERC20_TRANSFER: 120000n;
16
16
  /**
17
17
  * 贿赂交易(简单原生转账)
18
18
  * 固定使用 21000 作为贿赂交易的 gas limit
@@ -15,9 +15,9 @@ export const GAS_LIMITS = {
15
15
  NATIVE_TRANSFER: 21055n,
16
16
  /**
17
17
  * ERC20 代币转账
18
- * 标准 ERC20 transfer 约 45000-55000,USDT 等约 65000
18
+ * 标准 ERC20 ~45-55k,USDT ~65k,SPCXB 等股票币代理实测 ~96k
19
19
  */
20
- ERC20_TRANSFER: 65000n,
20
+ ERC20_TRANSFER: 120000n,
21
21
  // ==================== 贿赂类 ====================
22
22
  /**
23
23
  * 贿赂交易(简单原生转账)
@@ -1,5 +1,6 @@
1
- export declare const ERC20_TRANSFER_GAS_LIMIT: 65000n;
2
- export declare const ERC20_APPROVE_GAS_LIMIT = 50000n;
1
+ export declare const ERC20_TRANSFER_GAS_LIMIT: 120000n;
2
+ /** SPCXB 等股票币 approve 实测 >50k,空钱包 eth_estimate ~55k */
3
+ export declare const ERC20_APPROVE_GAS_LIMIT = 80000n;
3
4
  export declare const DEFAULT_GAS_LIMIT = 800000;
4
5
  export declare const DEFAULT_GAS_PRICE_GWEI = 3;
5
6
  export declare const GAS_BUFFER_MULTIPLIER = 2;
@@ -1,6 +1,7 @@
1
1
  import { GAS_LIMITS } from '../../../core/constants/index.js';
2
2
  export const ERC20_TRANSFER_GAS_LIMIT = GAS_LIMITS.ERC20_TRANSFER;
3
- export const ERC20_APPROVE_GAS_LIMIT = 50000n;
3
+ /** SPCXB 等股票币 approve 实测 >50k,空钱包 eth_estimate ~55k */
4
+ export const ERC20_APPROVE_GAS_LIMIT = 80000n;
4
5
  export const DEFAULT_GAS_LIMIT = 800000;
5
6
  export const DEFAULT_GAS_PRICE_GWEI = 3;
6
7
  export const GAS_BUFFER_MULTIPLIER = 2;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { GAS_LIMITS } from '../../../core/constants/gas.js';
3
+ import { ERC20_APPROVE_GAS_LIMIT, ERC20_TRANSFER_GAS_LIMIT } from './constants.js';
4
+ describe('holders ERC20 gas', () => {
5
+ it('covers SPCXB-class transfer (~95819) and approve (~55532)', () => {
6
+ expect(ERC20_TRANSFER_GAS_LIMIT).toBe(GAS_LIMITS.ERC20_TRANSFER);
7
+ expect(ERC20_TRANSFER_GAS_LIMIT).toBeGreaterThanOrEqual(120000n);
8
+ expect(ERC20_APPROVE_GAS_LIMIT).toBeGreaterThanOrEqual(80000n);
9
+ });
10
+ });
@@ -8,7 +8,7 @@ import { ethers, Wallet, JsonRpcProvider } from 'ethers';
8
8
  import type { DisperseHopPath } from './types/hop-chains.js';
9
9
  export type { DisperseHopPath } from './types/hop-chains.js';
10
10
  export declare const NATIVE_TRANSFER_GAS_LIMIT: 21055n;
11
- export declare const ERC20_TRANSFER_GAS_LIMIT_HOP: 65000n;
11
+ export declare const ERC20_TRANSFER_GAS_LIMIT_HOP: 120000n;
12
12
  export declare const BRIBE_GAS_LIMIT: 21000n;
13
13
  /**
14
14
  * 给交易对象附加 gasPrice/maxFeePerGas(根据 txType)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "4.3.11",
3
+ "version": "4.3.13",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",