four-flap-meme-sdk 4.3.12 → 4.3.14
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.
- package/README.zh-CN.md +2 -2
- package/dist/bundle/four-meme/utils-disperse.js +5 -5
- package/dist/bundle/four-meme/utils-pairwise.js +2 -2
- package/dist/bundle/four-meme/utils-sweep/with-hops.js +3 -5
- package/dist/chains/bsc/constants.d.ts +1 -1
- package/dist/chains/bsc/pancake/bundle-swap-types.d.ts +1 -1
- package/dist/chains/bsc/pancake/quote.d.ts +2 -0
- package/dist/chains/bsc/pancake/quote.js +6 -0
- package/dist/chains/bsc/pancake/quote.test.js +7 -1
- package/dist/chains/bsc/pancake/swap-cross.d.ts +1 -0
- package/dist/chains/bsc/pancake/swap-cross.js +2 -1
- package/dist/chains/bsc/pancake/swap-quick.d.ts +2 -0
- package/dist/chains/bsc/pancake/swap-quick.js +4 -4
- package/dist/chains/eni/constants.d.ts +1 -1
- package/dist/chains/monad/constants.d.ts +1 -1
- package/dist/chains/xlayer/constants.d.ts +1 -1
- package/dist/core/constants/gas.d.ts +2 -2
- package/dist/core/constants/gas.js +2 -2
- package/dist/domains/holders/internal/constants.d.ts +3 -2
- package/dist/domains/holders/internal/constants.js +2 -1
- package/dist/domains/holders/internal/constants.test.d.ts +1 -0
- package/dist/domains/holders/internal/constants.test.js +10 -0
- package/dist/utils/hop-chains.d.ts +5 -2
- package/dist/utils/hop-chains.js +13 -4
- package/dist/utils/hop-chains.test.d.ts +1 -0
- package/dist/utils/hop-chains.test.js +16 -0
- 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(默认
|
|
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(默认
|
|
4397
|
+
transferGasLimit?: bigint; // ERC20(默认 120000n)
|
|
4398
4398
|
nativeGasLimit?: bigint; // 原生(默认 21000n)
|
|
4399
4399
|
blockOffset?: number;
|
|
4400
4400
|
skipIfInsufficient?: boolean; // 默认 true(余额不足跳过)
|
|
@@ -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
|
-
// -
|
|
139
|
-
// - ERC20
|
|
140
|
-
const nativeTransferGasLimit =
|
|
141
|
-
const erc20TransferGasLimit =
|
|
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) :
|
|
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
|
-
|
|
41
|
-
|
|
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:
|
|
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 */
|
|
@@ -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:
|
|
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 | {
|
|
@@ -8,6 +8,8 @@ export declare function prefixSumAmounts(amounts: bigint[]): bigint[];
|
|
|
8
8
|
/** 前缀报价差:第 i 笔顺序成交 ≈ quote(sum 0..i) - quote(sum 0..i-1)。 */
|
|
9
9
|
export declare function sequentialPrefixOutputs(prefixQuoted: bigint[]): bigint[];
|
|
10
10
|
export declare function applyTaxBps(amount: bigint, taxRateBps: number): bigint;
|
|
11
|
+
/** 税币卖出时 pair 只收到扣税后的 amountIn,资金利用率必须按这个数量报价再转计价币。 */
|
|
12
|
+
export declare function taxedSellQuoteInput(sellAmountWei: bigint, taxRateBps: number): bigint;
|
|
11
13
|
/** SupportingFeeOnTransfer 按收款余额检查,minOut 必须先扣税再滑点。 */
|
|
12
14
|
export declare function sequentialBuyAmountOutMins(sequentialBuyOuts: bigint[], taxRateBps: number, slippageBps: number): bigint[];
|
|
13
15
|
/**
|
|
@@ -39,6 +39,12 @@ export function applyTaxBps(amount, taxRateBps) {
|
|
|
39
39
|
return amount;
|
|
40
40
|
return (amount * BigInt(10000 - bps)) / 10000n;
|
|
41
41
|
}
|
|
42
|
+
/** 税币卖出时 pair 只收到扣税后的 amountIn,资金利用率必须按这个数量报价再转计价币。 */
|
|
43
|
+
export function taxedSellQuoteInput(sellAmountWei, taxRateBps) {
|
|
44
|
+
if (sellAmountWei <= 0n)
|
|
45
|
+
return 0n;
|
|
46
|
+
return applyTaxBps(sellAmountWei, taxRateBps);
|
|
47
|
+
}
|
|
42
48
|
/** SupportingFeeOnTransfer 按收款余额检查,minOut 必须先扣税再滑点。 */
|
|
43
49
|
export function sequentialBuyAmountOutMins(sequentialBuyOuts, taxRateBps, slippageBps) {
|
|
44
50
|
return sequentialBuyOuts.map((out) => applyPancakeSlippage(applyTaxBps(out, taxRateBps), slippageBps));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
2
|
import { reverseBuyRouteParams } from './bundle-buy-first-quote.js';
|
|
3
|
-
import { applyPancakeSlippage, applyTaxBps, isSameWalletBuySell, resolveBuyFirstSellMins, sequentialBuyAmountOutMins, sequentialPrefixOutputs, pairSameWalletSellAmounts, splitQuotedMins, } from './quote.js';
|
|
3
|
+
import { applyPancakeSlippage, applyTaxBps, taxedSellQuoteInput, isSameWalletBuySell, resolveBuyFirstSellMins, sequentialBuyAmountOutMins, sequentialPrefixOutputs, pairSameWalletSellAmounts, splitQuotedMins, } from './quote.js';
|
|
4
4
|
describe('applyPancakeSlippage', () => {
|
|
5
5
|
it('drops 1% by default', () => {
|
|
6
6
|
expect(applyPancakeSlippage(1000000n)).toBe(990000n);
|
|
@@ -30,6 +30,12 @@ describe('sequentialBuyAmountOutMins', () => {
|
|
|
30
30
|
expect(applyTaxBps(1000000n, 100)).toBe(990000n);
|
|
31
31
|
});
|
|
32
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
|
+
});
|
|
33
39
|
describe('pairSameWalletSellAmounts', () => {
|
|
34
40
|
it('pairs each wash wallet sell to that wallet guaranteed buy out', () => {
|
|
35
41
|
expect(pairSameWalletSellAmounts(true, [900n, 700n], 2)).toEqual([900n, 700n]);
|
|
@@ -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);
|
|
@@ -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
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
13
|
+
* 标准 ERC20 ~45-55k,USDT ~65k,SPCXB 等股票币代理实测 ~96k
|
|
14
14
|
*/
|
|
15
|
-
readonly ERC20_TRANSFER:
|
|
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
|
|
18
|
+
* 标准 ERC20 ~45-55k,USDT ~65k,SPCXB 等股票币代理实测 ~96k
|
|
19
19
|
*/
|
|
20
|
-
ERC20_TRANSFER:
|
|
20
|
+
ERC20_TRANSFER: 120000n,
|
|
21
21
|
// ==================== 贿赂类 ====================
|
|
22
22
|
/**
|
|
23
23
|
* 贿赂交易(简单原生转账)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export declare const ERC20_TRANSFER_GAS_LIMIT:
|
|
2
|
-
|
|
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
|
-
|
|
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,8 +8,11 @@ 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:
|
|
11
|
+
export declare const ERC20_TRANSFER_GAS_LIMIT_HOP: 120000n;
|
|
12
12
|
export declare const BRIBE_GAS_LIMIT: 21000n;
|
|
13
|
+
/** 末跳且买地址自备 gas 时,hop 不再转发原生币,ERC20 必须从 nonce 0 起。 */
|
|
14
|
+
export declare function hopForwardsNativeGas(index: number, hopCount: number, finalGasAmount: bigint): boolean;
|
|
15
|
+
export declare function hopErc20Nonce(index: number, hopCount: number, finalGasAmount: bigint): number;
|
|
13
16
|
/**
|
|
14
17
|
* 给交易对象附加 gasPrice/maxFeePerGas(根据 txType)
|
|
15
18
|
*/
|
|
@@ -25,7 +28,7 @@ export declare function buildNativeHopChain(payer: Wallet, path: DisperseHopPath
|
|
|
25
28
|
/**
|
|
26
29
|
* 构建 ERC20 多跳转账链(需要配合 BNB 多跳使用)
|
|
27
30
|
*/
|
|
28
|
-
export declare function buildERC20HopChain(payer: Wallet, path: DisperseHopPath, erc20Address: string, erc20Amount: bigint, gasPrice: bigint, chainId: number, txType: 0 | 2, payerNonce: number): Promise<string[]>;
|
|
31
|
+
export declare function buildERC20HopChain(payer: Wallet, path: DisperseHopPath, erc20Address: string, erc20Amount: bigint, gasPrice: bigint, chainId: number, txType: 0 | 2, payerNonce: number, finalNativeForwardAmount?: bigint): Promise<string[]>;
|
|
29
32
|
/**
|
|
30
33
|
* 构建 BNB 多跳转账链(为 ERC20 中间钱包预留 gas)
|
|
31
34
|
*/
|
package/dist/utils/hop-chains.js
CHANGED
|
@@ -11,6 +11,15 @@ import { generateWallets } from './wallet.js';
|
|
|
11
11
|
export const NATIVE_TRANSFER_GAS_LIMIT = GAS_LIMITS.NATIVE_TRANSFER;
|
|
12
12
|
export const ERC20_TRANSFER_GAS_LIMIT_HOP = GAS_LIMITS.ERC20_TRANSFER;
|
|
13
13
|
export const BRIBE_GAS_LIMIT = GAS_LIMITS.BRIBE;
|
|
14
|
+
/** 末跳且买地址自备 gas 时,hop 不再转发原生币,ERC20 必须从 nonce 0 起。 */
|
|
15
|
+
export function hopForwardsNativeGas(index, hopCount, finalGasAmount) {
|
|
16
|
+
if (hopCount <= 0 || index < 0 || index >= hopCount)
|
|
17
|
+
return false;
|
|
18
|
+
return !(index === hopCount - 1 && finalGasAmount === 0n);
|
|
19
|
+
}
|
|
20
|
+
export function hopErc20Nonce(index, hopCount, finalGasAmount) {
|
|
21
|
+
return hopForwardsNativeGas(index, hopCount, finalGasAmount) ? 1 : 0;
|
|
22
|
+
}
|
|
14
23
|
/**
|
|
15
24
|
* 给交易对象附加 gasPrice/maxFeePerGas(根据 txType)
|
|
16
25
|
*/
|
|
@@ -107,7 +116,7 @@ export async function buildNativeHopChain(payer, path, finalAmount, gasPrice, ch
|
|
|
107
116
|
/**
|
|
108
117
|
* 构建 ERC20 多跳转账链(需要配合 BNB 多跳使用)
|
|
109
118
|
*/
|
|
110
|
-
export async function buildERC20HopChain(payer, path, erc20Address, erc20Amount, gasPrice, chainId, txType, payerNonce) {
|
|
119
|
+
export async function buildERC20HopChain(payer, path, erc20Address, erc20Amount, gasPrice, chainId, txType, payerNonce, finalNativeForwardAmount = 0n) {
|
|
111
120
|
const signedTxs = [];
|
|
112
121
|
const hopCount = path.hopWallets.length;
|
|
113
122
|
const erc20Interface = new ethers.Interface(ERC20_ABI);
|
|
@@ -139,7 +148,7 @@ export async function buildERC20HopChain(payer, path, erc20Address, erc20Amount,
|
|
|
139
148
|
};
|
|
140
149
|
applyTxTypeGas(firstTx, gasPrice, txType);
|
|
141
150
|
signedTxs.push(await payer.signTransaction(firstTx));
|
|
142
|
-
// hop1 → hop2 → ... → target
|
|
151
|
+
// hop1 → hop2 → ... → target。末跳若不再转发 BNB,ERC20 用 nonce=0。
|
|
143
152
|
for (let i = 0; i < hopCount; i++) {
|
|
144
153
|
const fromWallet = path.hopWallets[i];
|
|
145
154
|
const toAddress = i === hopCount - 1 ? path.targetAddress : path.hopWallets[i + 1].address;
|
|
@@ -148,7 +157,7 @@ export async function buildERC20HopChain(payer, path, erc20Address, erc20Amount,
|
|
|
148
157
|
to: erc20Address,
|
|
149
158
|
data,
|
|
150
159
|
value: 0n,
|
|
151
|
-
nonce:
|
|
160
|
+
nonce: hopErc20Nonce(i, hopCount, finalNativeForwardAmount),
|
|
152
161
|
gasLimit: ERC20_TRANSFER_GAS_LIMIT_HOP,
|
|
153
162
|
chainId,
|
|
154
163
|
type: txType,
|
|
@@ -197,7 +206,7 @@ export async function buildBNBHopChainForERC20(payer, path, finalGasAmount, gasP
|
|
|
197
206
|
const fromWallet = path.hopWallets[i];
|
|
198
207
|
const toAddress = i === hopCount - 1 ? path.targetAddress : path.hopWallets[i + 1].address;
|
|
199
208
|
const amount = i === hopCount - 1 ? finalGasAmount : amountsPerHop[i + 1];
|
|
200
|
-
if (i
|
|
209
|
+
if (!hopForwardsNativeGas(i, hopCount, finalGasAmount))
|
|
201
210
|
continue;
|
|
202
211
|
const tx = {
|
|
203
212
|
to: toAddress,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { hopErc20Nonce, hopForwardsNativeGas } from './hop-chains.js';
|
|
3
|
+
describe('hopErc20Nonce', () => {
|
|
4
|
+
it('uses nonce 0 on a single hop when the buyer keeps its own gas', () => {
|
|
5
|
+
expect(hopForwardsNativeGas(0, 1, 0n)).toBe(false);
|
|
6
|
+
expect(hopErc20Nonce(0, 1, 0n)).toBe(0);
|
|
7
|
+
});
|
|
8
|
+
it('keeps nonce 1 on intermediate hops and 0 on the last hop', () => {
|
|
9
|
+
expect(hopErc20Nonce(0, 2, 0n)).toBe(1);
|
|
10
|
+
expect(hopErc20Nonce(1, 2, 0n)).toBe(0);
|
|
11
|
+
});
|
|
12
|
+
it('uses nonce 1 on the last hop when it also forwards native gas', () => {
|
|
13
|
+
expect(hopForwardsNativeGas(0, 1, 1n)).toBe(true);
|
|
14
|
+
expect(hopErc20Nonce(0, 1, 1n)).toBe(1);
|
|
15
|
+
});
|
|
16
|
+
});
|