four-flap-meme-sdk 1.4.36 → 1.4.37
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.
|
@@ -12,6 +12,24 @@ export declare function normalizeAmounts(recipients: string[], amount?: AmountLi
|
|
|
12
12
|
* ✅ 优化:原生代币也使用 Multicall3 批量获取,减少 RPC 调用
|
|
13
13
|
*/
|
|
14
14
|
export declare function batchGetBalances(provider: JsonRpcProvider, addresses: string[], tokenAddress?: string): Promise<bigint[]>;
|
|
15
|
+
/**
|
|
16
|
+
* 通过模拟交易获取 ERC20 转账的最小 Gas Limit
|
|
17
|
+
* ✅ 使用 eth_estimateGas 预估实际需要的 gas,然后加一个小的缓冲量
|
|
18
|
+
*
|
|
19
|
+
* @param provider - Provider 实例
|
|
20
|
+
* @param tokenAddress - ERC20 代币地址
|
|
21
|
+
* @param from - 发送方地址
|
|
22
|
+
* @param to - 接收方地址
|
|
23
|
+
* @param amount - 转账金额(wei)
|
|
24
|
+
* @param bufferPercent - 缓冲百分比(默认 5%)
|
|
25
|
+
* @returns 预估的 gas limit
|
|
26
|
+
*/
|
|
27
|
+
export declare function estimateErc20TransferGas(provider: JsonRpcProvider, tokenAddress: string, from: string, to: string, amount: bigint, bufferPercent?: number): Promise<bigint>;
|
|
28
|
+
/**
|
|
29
|
+
* 批量预估多个 ERC20 转账的 Gas Limit
|
|
30
|
+
* ✅ 选取最大值作为统一的 gas limit(确保所有转账都能成功)
|
|
31
|
+
*/
|
|
32
|
+
export declare function estimateMaxErc20TransferGas(provider: JsonRpcProvider, tokenAddress: string, from: string, recipients: string[], amounts: bigint[], bufferPercent?: number): Promise<bigint>;
|
|
15
33
|
/**
|
|
16
34
|
* 计算 Gas Limit
|
|
17
35
|
* - 原生代币转账:21000 gas(固定)
|
|
@@ -122,6 +122,70 @@ export async function batchGetBalances(provider, addresses, tokenAddress) {
|
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* 通过模拟交易获取 ERC20 转账的最小 Gas Limit
|
|
127
|
+
* ✅ 使用 eth_estimateGas 预估实际需要的 gas,然后加一个小的缓冲量
|
|
128
|
+
*
|
|
129
|
+
* @param provider - Provider 实例
|
|
130
|
+
* @param tokenAddress - ERC20 代币地址
|
|
131
|
+
* @param from - 发送方地址
|
|
132
|
+
* @param to - 接收方地址
|
|
133
|
+
* @param amount - 转账金额(wei)
|
|
134
|
+
* @param bufferPercent - 缓冲百分比(默认 5%)
|
|
135
|
+
* @returns 预估的 gas limit
|
|
136
|
+
*/
|
|
137
|
+
export async function estimateErc20TransferGas(provider, tokenAddress, from, to, amount, bufferPercent = 5) {
|
|
138
|
+
try {
|
|
139
|
+
const iface = new ethers.Interface(['function transfer(address,uint256) returns (bool)']);
|
|
140
|
+
const data = iface.encodeFunctionData('transfer', [to, amount]);
|
|
141
|
+
const estimatedGas = await provider.estimateGas({
|
|
142
|
+
from,
|
|
143
|
+
to: tokenAddress,
|
|
144
|
+
data,
|
|
145
|
+
value: 0n
|
|
146
|
+
});
|
|
147
|
+
// 添加缓冲量(默认 5%)
|
|
148
|
+
const buffer = (estimatedGas * BigInt(bufferPercent)) / 100n;
|
|
149
|
+
const finalGas = estimatedGas + buffer;
|
|
150
|
+
console.log(`[estimateErc20TransferGas] 预估 gas: ${estimatedGas}, 最终 gas limit: ${finalGas} (+${bufferPercent}%)`);
|
|
151
|
+
return finalGas;
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
console.warn(`[estimateErc20TransferGas] 预估失败,使用默认值:`, error);
|
|
155
|
+
// 回退到默认值
|
|
156
|
+
return 52000n;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* 批量预估多个 ERC20 转账的 Gas Limit
|
|
161
|
+
* ✅ 选取最大值作为统一的 gas limit(确保所有转账都能成功)
|
|
162
|
+
*/
|
|
163
|
+
export async function estimateMaxErc20TransferGas(provider, tokenAddress, from, recipients, amounts, bufferPercent = 5) {
|
|
164
|
+
if (recipients.length === 0)
|
|
165
|
+
return 52000n;
|
|
166
|
+
// 为了减少 RPC 调用,最多预估前 3 个和最后 1 个
|
|
167
|
+
const sampleIndices = [];
|
|
168
|
+
sampleIndices.push(0); // 第一个
|
|
169
|
+
if (recipients.length > 1)
|
|
170
|
+
sampleIndices.push(Math.min(1, recipients.length - 1));
|
|
171
|
+
if (recipients.length > 2)
|
|
172
|
+
sampleIndices.push(Math.min(2, recipients.length - 1));
|
|
173
|
+
if (recipients.length > 3)
|
|
174
|
+
sampleIndices.push(recipients.length - 1); // 最后一个
|
|
175
|
+
// 去重
|
|
176
|
+
const uniqueIndices = [...new Set(sampleIndices)];
|
|
177
|
+
try {
|
|
178
|
+
const estimates = await Promise.all(uniqueIndices.map(i => estimateErc20TransferGas(provider, tokenAddress, from, recipients[i], amounts[i], bufferPercent)));
|
|
179
|
+
// 取最大值
|
|
180
|
+
const maxGas = estimates.reduce((max, gas) => gas > max ? gas : max, 0n);
|
|
181
|
+
console.log(`[estimateMaxErc20TransferGas] 样本数: ${uniqueIndices.length}, 最大 gas limit: ${maxGas}`);
|
|
182
|
+
return maxGas;
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
console.warn(`[estimateMaxErc20TransferGas] 批量预估失败,使用默认值:`, error);
|
|
186
|
+
return 52000n;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
125
189
|
/**
|
|
126
190
|
* 计算 Gas Limit
|
|
127
191
|
* - 原生代币转账:21000 gas(固定)
|
|
@@ -11,6 +11,7 @@ export type FourSignConfig = {
|
|
|
11
11
|
chainId?: number;
|
|
12
12
|
prefer21000ForNative?: boolean;
|
|
13
13
|
checkBnbForErc20NoHop?: boolean;
|
|
14
|
+
estimateGas?: boolean;
|
|
14
15
|
customSubmitFn?: (signedTxs: string[]) => Promise<string>;
|
|
15
16
|
spPrivateKey?: string;
|
|
16
17
|
spMode?: 'none' | 'timestampPersonalSign' | 'concatTxHash' | 'rawTimestamp';
|
|
@@ -2,7 +2,7 @@ import { ethers, Wallet } from 'ethers';
|
|
|
2
2
|
import { getOptimizedGasPrice, NonceManager } from '../../utils/bundle-helpers.js';
|
|
3
3
|
import { PROFIT_CONFIG, ZERO_ADDRESS } from '../../utils/constants.js';
|
|
4
4
|
import { getTxType, getGasPriceConfig, shouldExtractProfit, getProfitRecipient } from './config.js';
|
|
5
|
-
import { getErc20DecimalsMerkle as _getErc20DecimalsMerkle, generateHopWallets as _generateHopWallets, normalizeAmounts as _normalizeAmounts, batchGetBalances as _batchGetBalances, calculateGasLimit as _calculateGasLimit, isNativeTokenAddress as _isNativeTokenAddress } from './internal.js';
|
|
5
|
+
import { getErc20DecimalsMerkle as _getErc20DecimalsMerkle, generateHopWallets as _generateHopWallets, normalizeAmounts as _normalizeAmounts, batchGetBalances as _batchGetBalances, calculateGasLimit as _calculateGasLimit, isNativeTokenAddress as _isNativeTokenAddress, estimateErc20TransferGas as _estimateErc20TransferGas } from './internal.js';
|
|
6
6
|
// ==================== 本地利润计算(万分之三)====================
|
|
7
7
|
/**
|
|
8
8
|
* 计算利润金额(万分之三)
|
|
@@ -357,9 +357,23 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
357
357
|
const iface = isNative ? null : new ethers.Interface(['function transfer(address,uint256) returns (bool)']);
|
|
358
358
|
// ✅ Gas limit 设置
|
|
359
359
|
// - 原生代币转账:21000(固定)
|
|
360
|
-
// - ERC20
|
|
360
|
+
// - ERC20 转账:通过模拟交易获取最小 gas limit(减少中转钱包 BNB 残留)
|
|
361
361
|
const nativeTransferGasLimit = 21000n;
|
|
362
|
-
|
|
362
|
+
// ✅ ERC20 多跳:模拟交易获取精确的 gas limit
|
|
363
|
+
let erc20TransferGasLimit = finalGasLimit;
|
|
364
|
+
if (!isNative && config.estimateGas !== false) {
|
|
365
|
+
try {
|
|
366
|
+
// 使用第一个接收者模拟一笔转账,获取精确的 gas limit
|
|
367
|
+
const sampleAmount = ethers.parseUnits(normalizedAmounts[0], decimals);
|
|
368
|
+
const estimatedGas = await _estimateErc20TransferGas(provider, tokenAddress, mainWallet.address, recipients[0], sampleAmount, 0 // ✅ 不加缓冲,最大化减少多跳钱包残留
|
|
369
|
+
);
|
|
370
|
+
erc20TransferGasLimit = estimatedGas;
|
|
371
|
+
console.log(`[disperseWithBundleMerkle] 使用模拟估算的 ERC20 gas limit: ${erc20TransferGasLimit}`);
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
console.warn(`[disperseWithBundleMerkle] 模拟估算失败,使用默认值:`, error);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
363
377
|
// ✅ 原生代币多跳的 gas 费(每跳只需要 21000)
|
|
364
378
|
const nativeHopGasFee = nativeTransferGasLimit * gasPrice;
|
|
365
379
|
// ✅ ERC20 多跳时,中转钱包需要执行 2 笔交易(转 gas + 转 ERC20)
|
|
@@ -989,7 +1003,24 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
989
1003
|
const iface = isNative ? null : new ethers.Interface(['function transfer(address,uint256) returns (bool)']);
|
|
990
1004
|
// ✅ Gas limit 设置(与分散函数保持一致)
|
|
991
1005
|
const nativeTransferGasLimit = 21000n;
|
|
992
|
-
|
|
1006
|
+
// ✅ ERC20 多跳:模拟交易获取精确的 gas limit
|
|
1007
|
+
let erc20TransferGasLimit = finalGasLimit;
|
|
1008
|
+
if (!isNative && config.estimateGas !== false) {
|
|
1009
|
+
try {
|
|
1010
|
+
// 找到第一个有余额的钱包来模拟转账
|
|
1011
|
+
const firstWithBalance = balances.findIndex(b => b > 0n);
|
|
1012
|
+
if (firstWithBalance >= 0) {
|
|
1013
|
+
const sampleAmount = balances[firstWithBalance] > 0n ? balances[firstWithBalance] / 2n : 1n;
|
|
1014
|
+
const estimatedGas = await _estimateErc20TransferGas(provider, tokenAddress, sourceAddresses[firstWithBalance], target, sampleAmount, 0 // ✅ 不加缓冲,最大化减少多跳钱包残留
|
|
1015
|
+
);
|
|
1016
|
+
erc20TransferGasLimit = estimatedGas;
|
|
1017
|
+
console.log(`[sweepWithBundleMerkle] 使用模拟估算的 ERC20 gas limit: ${erc20TransferGasLimit}`);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
catch (error) {
|
|
1021
|
+
console.warn(`[sweepWithBundleMerkle] 模拟估算失败,使用默认值:`, error);
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
993
1024
|
// ✅ 原生代币多跳的 gas 费(每跳只需要 21000)
|
|
994
1025
|
const nativeHopGasFee = nativeTransferGasLimit * gasPrice;
|
|
995
1026
|
// ✅ ERC20 多跳时,中转钱包需要执行 2 笔交易(转 gas + 转 ERC20)
|