four-flap-meme-sdk 1.3.80 → 1.3.82
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/dist/contracts/tm-bundle-merkle/approve-tokenmanager.d.ts +1 -0
- package/dist/contracts/tm-bundle-merkle/approve-tokenmanager.js +30 -28
- package/dist/contracts/tm-bundle-merkle/internal.d.ts +9 -1
- package/dist/contracts/tm-bundle-merkle/internal.js +53 -11
- package/dist/contracts/tm-bundle-merkle/private.d.ts +4 -0
- package/dist/contracts/tm-bundle-merkle/private.js +195 -183
- package/dist/contracts/tm-bundle-merkle/submit.d.ts +2 -2
- package/dist/contracts/tm-bundle-merkle/submit.js +18 -20
- package/dist/contracts/tm-bundle.js +224 -113
- package/dist/flap/portal-bundle-merkle/core.js +3 -3
- package/dist/flap/portal-bundle-merkle/private.js +87 -107
- package/dist/utils/airdrop-sweep.js +86 -76
- package/dist/utils/erc20.d.ts +1 -0
- package/dist/utils/erc20.js +22 -20
- package/dist/utils/lp-inspect.js +77 -65
- package/dist/utils/stealth-transfer.js +34 -37
- package/dist/utils/swap-helpers.js +8 -4
- package/package.json +1 -1
|
@@ -21,5 +21,6 @@ export type ApproveFourTokenManagerBatchResult = {
|
|
|
21
21
|
* ✅ 不需要 Merkle API Key,使用普通 RPC 即可
|
|
22
22
|
* ✅ 专门用于授权给 TokenManagerOriginal 合约
|
|
23
23
|
* ✅ 用于 fourBatchPrivateSell 和 fourBatchSellWithBundleMerkle
|
|
24
|
+
* ✅ 优化:并行获取 gasPrice、decimals、allowances
|
|
24
25
|
*/
|
|
25
26
|
export declare function approveFourTokenManagerBatch(params: ApproveFourTokenManagerBatchParams): Promise<ApproveFourTokenManagerBatchResult>;
|
|
@@ -15,6 +15,7 @@ const ERC20_ABI = [
|
|
|
15
15
|
* ✅ 不需要 Merkle API Key,使用普通 RPC 即可
|
|
16
16
|
* ✅ 专门用于授权给 TokenManagerOriginal 合约
|
|
17
17
|
* ✅ 用于 fourBatchPrivateSell 和 fourBatchSellWithBundleMerkle
|
|
18
|
+
* ✅ 优化:并行获取 gasPrice、decimals、allowances
|
|
18
19
|
*/
|
|
19
20
|
export async function approveFourTokenManagerBatch(params) {
|
|
20
21
|
const { privateKeys, tokenAddress, amounts, config } = params;
|
|
@@ -28,27 +29,25 @@ export async function approveFourTokenManagerBatch(params) {
|
|
|
28
29
|
chainId: chainId,
|
|
29
30
|
name: 'BSC'
|
|
30
31
|
});
|
|
31
|
-
const gasPrice = await getOptimizedGasPrice(provider, getGasPriceConfig(config));
|
|
32
|
-
// ✅ 优化:如果所有金额都是 'max',则不需要查询 decimals
|
|
33
|
-
const allMax = amounts.every(a => a === 'max');
|
|
34
|
-
let decimals = 18; // 默认值
|
|
35
|
-
if (!allMax) {
|
|
36
|
-
try {
|
|
37
|
-
const tokenContract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
|
|
38
|
-
decimals = await tokenContract.decimals();
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
throw new Error(`查询代币 decimals 失败: ${error.message}`);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
32
|
const wallets = privateKeys.map(k => new Wallet(k, provider));
|
|
33
|
+
const allMax = amounts.every(a => a === 'max');
|
|
34
|
+
// ✅ 优化:并行获取 gasPrice、decimals(如果需要)、allowances
|
|
35
|
+
const [gasPrice, decimals, allowances] = await Promise.all([
|
|
36
|
+
getOptimizedGasPrice(provider, getGasPriceConfig(config)),
|
|
37
|
+
allMax
|
|
38
|
+
? Promise.resolve(18) // 如果都是 'max',不需要查询 decimals
|
|
39
|
+
: (async () => {
|
|
40
|
+
try {
|
|
41
|
+
const tokenContract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
|
|
42
|
+
return await tokenContract.decimals();
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
throw new Error(`查询代币 decimals 失败: ${error.message}`);
|
|
46
|
+
}
|
|
47
|
+
})(),
|
|
48
|
+
batchCheckAllowances(provider, tokenAddress, wallets.map(w => w.address), tmAddr)
|
|
49
|
+
]);
|
|
45
50
|
const amountsBigInt = amounts.map(a => a === 'max' ? ethers.MaxUint256 : ethers.parseUnits(a, decimals));
|
|
46
|
-
// ✅ 检查是否有非 'max' 的授权
|
|
47
|
-
const hasNonMaxAmount = amounts.some(a => a !== 'max');
|
|
48
|
-
if (hasNonMaxAmount) {
|
|
49
|
-
}
|
|
50
|
-
// 检查现有授权,过滤掉已经足够的
|
|
51
|
-
const allowances = await batchCheckAllowances(provider, tokenAddress, wallets.map(w => w.address), tmAddr);
|
|
52
51
|
// 只授权不足的
|
|
53
52
|
// ✅ 策略:如果授权额度 < MaxUint256 / 2n,就重新授权(确保授权足够大)
|
|
54
53
|
const APPROVAL_THRESHOLD = ethers.MaxUint256 / 2n;
|
|
@@ -64,10 +63,7 @@ export async function approveFourTokenManagerBatch(params) {
|
|
|
64
63
|
message: '所有钱包已授权,无需重复授权'
|
|
65
64
|
};
|
|
66
65
|
}
|
|
67
|
-
//
|
|
68
|
-
const needApprovalTokens = needApproval.map(w => new ethers.Contract(tokenAddress, ERC20_ABI, w));
|
|
69
|
-
const unsignedApprovals = await Promise.all(needApprovalTokens.map((token, i) => token.approve.populateTransaction(tmAddr, needApprovalAmounts[i])));
|
|
70
|
-
// 使用前端传入的 gasLimit,否则使用默认值
|
|
66
|
+
// ✅ 优化:计算 gasLimit(不需要 RPC)
|
|
71
67
|
const getGasLimit = (cfg) => {
|
|
72
68
|
if (cfg.gasLimit)
|
|
73
69
|
return BigInt(cfg.gasLimit);
|
|
@@ -76,9 +72,15 @@ export async function approveFourTokenManagerBatch(params) {
|
|
|
76
72
|
return 60000n; // 默认授权 gas limit
|
|
77
73
|
};
|
|
78
74
|
const finalGasLimit = getGasLimit(config);
|
|
79
|
-
|
|
75
|
+
const txType = getTxType(config);
|
|
76
|
+
// ✅ 优化:并行获取 nonces 和构建未签名交易
|
|
80
77
|
const nonceManager = new NonceManager(provider);
|
|
81
|
-
const
|
|
78
|
+
const needApprovalTokens = needApproval.map(w => new ethers.Contract(tokenAddress, ERC20_ABI, w));
|
|
79
|
+
const [nonces, unsignedApprovals] = await Promise.all([
|
|
80
|
+
nonceManager.getNextNoncesForWallets(needApproval), // ✅ 批量获取 nonces(JSON-RPC 批量请求)
|
|
81
|
+
Promise.all(needApprovalTokens.map((token, i) => token.approve.populateTransaction(tmAddr, needApprovalAmounts[i])))
|
|
82
|
+
]);
|
|
83
|
+
// ✅ 并行签名所有授权交易
|
|
82
84
|
const signedTxs = await Promise.all(unsignedApprovals.map((unsigned, i) => needApproval[i].signTransaction({
|
|
83
85
|
...unsigned,
|
|
84
86
|
from: needApproval[i].address,
|
|
@@ -86,14 +88,14 @@ export async function approveFourTokenManagerBatch(params) {
|
|
|
86
88
|
gasLimit: finalGasLimit,
|
|
87
89
|
gasPrice,
|
|
88
90
|
chainId,
|
|
89
|
-
type:
|
|
91
|
+
type: txType
|
|
90
92
|
})));
|
|
91
93
|
nonceManager.clearTemp();
|
|
92
94
|
try {
|
|
93
|
-
//
|
|
95
|
+
// ✅ 并行广播所有授权交易
|
|
94
96
|
const txResponses = await Promise.all(signedTxs.map(tx => provider.broadcastTransaction(tx)));
|
|
95
97
|
const txHashes = txResponses.map(r => r.hash);
|
|
96
|
-
//
|
|
98
|
+
// ✅ 并行等待所有授权交易确认(至少1个确认)
|
|
97
99
|
await Promise.all(txResponses.map(tx => tx.wait(1)));
|
|
98
100
|
return {
|
|
99
101
|
success: true,
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { JsonRpcProvider } from 'ethers';
|
|
2
2
|
import type { AmountLike } from './types.js';
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* 获取 ERC20 代币精度(带缓存)
|
|
5
|
+
* ✅ 优化:避免每次调用都获取 network,使用 provider._network 或传入 chainId
|
|
6
|
+
*/
|
|
7
|
+
export declare function getErc20DecimalsMerkle(provider: JsonRpcProvider, token: string, chainId?: number): Promise<number>;
|
|
4
8
|
export declare function generateHopWallets(recipientCount: number, hopCount: number | number[]): string[][] | null;
|
|
5
9
|
export declare function normalizeAmounts(recipients: string[], amount?: AmountLike, amounts?: AmountLike[]): string[];
|
|
10
|
+
/**
|
|
11
|
+
* 批量获取余额(原生代币或 ERC20)
|
|
12
|
+
* ✅ 优化:原生代币也使用 Multicall3 批量获取,减少 RPC 调用
|
|
13
|
+
*/
|
|
6
14
|
export declare function batchGetBalances(provider: JsonRpcProvider, addresses: string[], tokenAddress?: string): Promise<bigint[]>;
|
|
7
15
|
export declare function calculateGasLimit(config: any, isNative: boolean, hasHops: boolean, hopCount?: number): bigint;
|
|
8
16
|
export declare function isNativeTokenAddress(tokenAddress?: string): boolean;
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { ethers, Wallet } from 'ethers';
|
|
2
2
|
// 内部缓存:ERC20 小数位
|
|
3
3
|
const decimalsCache = new Map();
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
/**
|
|
5
|
+
* 获取 ERC20 代币精度(带缓存)
|
|
6
|
+
* ✅ 优化:避免每次调用都获取 network,使用 provider._network 或传入 chainId
|
|
7
|
+
*/
|
|
8
|
+
export async function getErc20DecimalsMerkle(provider, token, chainId) {
|
|
9
|
+
// ✅ 优化:优先使用传入的 chainId,其次使用 provider 内部缓存的 network
|
|
10
|
+
const resolvedChainId = chainId ?? (provider._network?.chainId ? Number(provider._network.chainId) : 56);
|
|
11
|
+
const key = `${resolvedChainId}_${token.toLowerCase()}`;
|
|
7
12
|
if (decimalsCache.has(key))
|
|
8
13
|
return decimalsCache.get(key);
|
|
9
14
|
try {
|
|
@@ -44,18 +49,49 @@ export function normalizeAmounts(recipients, amount, amounts) {
|
|
|
44
49
|
}
|
|
45
50
|
throw new Error('Either amount or amounts must be provided');
|
|
46
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* 批量获取余额(原生代币或 ERC20)
|
|
54
|
+
* ✅ 优化:原生代币也使用 Multicall3 批量获取,减少 RPC 调用
|
|
55
|
+
*/
|
|
47
56
|
export async function batchGetBalances(provider, addresses, tokenAddress) {
|
|
48
57
|
if (addresses.length === 0)
|
|
49
58
|
return [];
|
|
59
|
+
const MULTICALL3_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11';
|
|
60
|
+
const MULTICALL3_ABI = [
|
|
61
|
+
'function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) view returns (tuple(bool success, bytes returnData)[] returnData)',
|
|
62
|
+
'function getEthBalance(address addr) view returns (uint256 balance)' // ✅ 新增:原生代币余额查询
|
|
63
|
+
];
|
|
64
|
+
const multicall = new ethers.Contract(MULTICALL3_ADDRESS, MULTICALL3_ABI, provider);
|
|
50
65
|
if (!tokenAddress) {
|
|
51
|
-
|
|
66
|
+
// ✅ 优化:原生代币使用 Multicall3.getEthBalance 批量获取(单次 RPC 调用)
|
|
67
|
+
const multicallIface = new ethers.Interface(MULTICALL3_ABI);
|
|
68
|
+
const calls = addresses.map(addr => ({
|
|
69
|
+
target: MULTICALL3_ADDRESS,
|
|
70
|
+
allowFailure: true,
|
|
71
|
+
callData: multicallIface.encodeFunctionData('getEthBalance', [addr])
|
|
72
|
+
}));
|
|
73
|
+
try {
|
|
74
|
+
const results = await multicall.aggregate3(calls);
|
|
75
|
+
return results.map((result) => {
|
|
76
|
+
if (result.success && result.returnData && result.returnData !== '0x') {
|
|
77
|
+
try {
|
|
78
|
+
const decoded = multicallIface.decodeFunctionResult('getEthBalance', result.returnData);
|
|
79
|
+
return decoded[0];
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return 0n;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return 0n;
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// ✅ 回退:并行调用(如果 Multicall3 失败)
|
|
90
|
+
return Promise.all(addresses.map(addr => provider.getBalance(addr).catch(() => 0n)));
|
|
91
|
+
}
|
|
52
92
|
}
|
|
53
93
|
else {
|
|
54
|
-
|
|
55
|
-
const MULTICALL3_ABI = [
|
|
56
|
-
'function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) view returns (tuple(bool success, bytes returnData)[] returnData)'
|
|
57
|
-
];
|
|
58
|
-
const multicall = new ethers.Contract(MULTICALL3_ADDRESS, MULTICALL3_ABI, provider);
|
|
94
|
+
// ERC20 余额:使用 Multicall3 批量获取
|
|
59
95
|
const ERC20_ABI = ['function balanceOf(address) view returns (uint256)'];
|
|
60
96
|
const iface = new ethers.Interface(ERC20_ABI);
|
|
61
97
|
const calls = addresses.map(addr => ({
|
|
@@ -66,13 +102,19 @@ export async function batchGetBalances(provider, addresses, tokenAddress) {
|
|
|
66
102
|
try {
|
|
67
103
|
const results = await multicall.aggregate3(calls);
|
|
68
104
|
return results.map((result) => {
|
|
69
|
-
if (result.success) {
|
|
70
|
-
|
|
105
|
+
if (result.success && result.returnData && result.returnData !== '0x') {
|
|
106
|
+
try {
|
|
107
|
+
return iface.decodeFunctionResult('balanceOf', result.returnData)[0];
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return 0n;
|
|
111
|
+
}
|
|
71
112
|
}
|
|
72
113
|
return 0n;
|
|
73
114
|
});
|
|
74
115
|
}
|
|
75
116
|
catch {
|
|
117
|
+
// ✅ 回退:并行调用(如果 Multicall3 失败)
|
|
76
118
|
const fallbackCalls = addresses.map(addr => provider
|
|
77
119
|
.call({ to: tokenAddress, data: iface.encodeFunctionData('balanceOf', [addr]) })
|
|
78
120
|
.then(raw => iface.decodeFunctionResult('balanceOf', raw)[0])
|
|
@@ -2,22 +2,26 @@ import { FourPrivateBuySignParams, FourPrivateSellSignParams, FourPrivateTransac
|
|
|
2
2
|
/**
|
|
3
3
|
* 私有购买(单笔)(仅签名版本 - 不依赖 Merkle)
|
|
4
4
|
* ✅ 精简版:只负责签名交易,不提交到 Merkle
|
|
5
|
+
* ✅ 优化:并行获取 gasPrice 和 nonce
|
|
5
6
|
*/
|
|
6
7
|
export declare function fourPrivateBuyMerkle(params: FourPrivateBuySignParams): Promise<FourPrivateTransactionResult>;
|
|
7
8
|
/**
|
|
8
9
|
* 私有卖出(单笔)(仅签名版本 - 不依赖 Merkle)
|
|
9
10
|
* ✅ 精简版:只负责签名交易,不提交到 Merkle
|
|
10
11
|
* ✅ 自动检查授权,智能处理授权+卖出
|
|
12
|
+
* ✅ 优化:并行获取 gasPrice、allowance、nonce
|
|
11
13
|
*/
|
|
12
14
|
export declare function fourPrivateSellMerkle(params: FourPrivateSellSignParams): Promise<FourPrivateTransactionResult>;
|
|
13
15
|
/**
|
|
14
16
|
* 批量私有购买(仅签名版本 - 不依赖 Merkle)
|
|
15
17
|
* ✅ 精简版:只负责签名交易,不提交到 Merkle
|
|
18
|
+
* ✅ 优化:并行获取 gasPrice 和 nonces,批量构建交易
|
|
16
19
|
*/
|
|
17
20
|
export declare function fourBatchPrivateBuyMerkle(params: FourBatchPrivateBuySignParams): Promise<FourBatchPrivateMerkleResult>;
|
|
18
21
|
/**
|
|
19
22
|
* 批量私有卖出(仅签名版本 - 不依赖 Merkle)
|
|
20
23
|
* ✅ 精简版:只负责签名交易,不提交到 Merkle
|
|
21
24
|
* ✅ 自动包含授权交易,无需前端单独调用
|
|
25
|
+
* ✅ 优化:并行获取所有查询数据,批量获取 nonces
|
|
22
26
|
*/
|
|
23
27
|
export declare function fourBatchPrivateSellMerkle(params: FourBatchPrivateSellSignParams): Promise<FourBatchPrivateMerkleResult>;
|