four-flap-meme-sdk 1.1.93 → 1.1.95
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 +8 -1
- package/dist/contracts/tm-bundle-merkle/approve-tokenmanager.js +48 -13
- package/dist/contracts/tm-bundle-merkle/core.js +64 -14
- package/dist/contracts/tm-bundle-merkle/pancake-proxy.js +43 -9
- package/dist/contracts/tm-bundle-merkle/private.js +1 -1
- package/dist/contracts/tm-bundle-merkle/types.d.ts +7 -2
- package/dist/flap/portal-bundle-merkle/pancake-proxy.js +43 -9
- package/dist/flap/portal-bundle-merkle/types.d.ts +7 -2
- package/package.json +1 -1
|
@@ -5,13 +5,20 @@ export type ApproveFourTokenManagerBatchParams = {
|
|
|
5
5
|
config: {
|
|
6
6
|
apiKey: string;
|
|
7
7
|
customRpcUrl?: string;
|
|
8
|
+
chainId?: number;
|
|
9
|
+
bundleBlockOffset?: number;
|
|
8
10
|
gasLimit?: number | bigint;
|
|
9
11
|
gasLimitMultiplier?: number;
|
|
12
|
+
gasPriceMultiplierPercent?: number;
|
|
13
|
+
minGasPriceGwei?: number;
|
|
10
14
|
txType?: 0 | 2;
|
|
11
15
|
};
|
|
12
16
|
};
|
|
13
17
|
export type ApproveFourTokenManagerBatchResult = {
|
|
14
|
-
|
|
18
|
+
success: boolean;
|
|
19
|
+
approvedCount: number;
|
|
20
|
+
bundleHash?: string;
|
|
21
|
+
message: string;
|
|
15
22
|
};
|
|
16
23
|
/**
|
|
17
24
|
* 批量授权代币给 TokenManager(用于 Private Buy/Sell)
|
|
@@ -21,9 +21,10 @@ export async function approveFourTokenManagerBatch(params) {
|
|
|
21
21
|
throw new Error('Private key count and amount count must match');
|
|
22
22
|
}
|
|
23
23
|
const tmAddr = ADDRESSES.BSC.TokenManagerOriginal;
|
|
24
|
+
const chainId = config.chainId ?? 56;
|
|
24
25
|
const merkle = new MerkleClient({
|
|
25
26
|
apiKey: config.apiKey,
|
|
26
|
-
chainId:
|
|
27
|
+
chainId: chainId,
|
|
27
28
|
customRpcUrl: config.customRpcUrl
|
|
28
29
|
});
|
|
29
30
|
const provider = merkle.getProvider();
|
|
@@ -44,19 +45,26 @@ export async function approveFourTokenManagerBatch(params) {
|
|
|
44
45
|
console.log(` - 需要授权: ${needApproval.length}`);
|
|
45
46
|
console.log(` - 已有授权: ${wallets.length - needApproval.length}`);
|
|
46
47
|
if (needApproval.length === 0) {
|
|
47
|
-
|
|
48
|
-
console.log(`✅ 所有钱包已授权给 TokenManager`);
|
|
48
|
+
console.log('✅ 所有钱包已授权,无需再次授权。');
|
|
49
49
|
return {
|
|
50
|
-
|
|
50
|
+
success: true,
|
|
51
|
+
approvedCount: 0,
|
|
52
|
+
message: '所有钱包已授权'
|
|
51
53
|
};
|
|
52
54
|
}
|
|
53
55
|
// 构建授权交易
|
|
54
56
|
const needApprovalTokens = needApproval.map(w => new ethers.Contract(tokenAddress, ERC20_ABI, w));
|
|
55
57
|
const unsignedApprovals = await Promise.all(needApprovalTokens.map((token, i) => token.approve.populateTransaction(tmAddr, needApprovalAmounts[i])));
|
|
56
|
-
//
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
// 使用前端传入的 gasLimit,否则使用默认值
|
|
59
|
+
const getGasLimit = (cfg) => {
|
|
60
|
+
if (cfg.gasLimit)
|
|
61
|
+
return BigInt(cfg.gasLimit);
|
|
62
|
+
if (cfg.gasLimitMultiplier)
|
|
63
|
+
return BigInt(Math.ceil(21000 * cfg.gasLimitMultiplier));
|
|
64
|
+
return 21000n; // 默认授权 gas limit
|
|
65
|
+
};
|
|
66
|
+
const finalGasLimit = getGasLimit(config);
|
|
67
|
+
// 签名授权交易
|
|
60
68
|
const nonceManager = new NonceManager(provider);
|
|
61
69
|
const nonces = await Promise.all(needApproval.map(w => nonceManager.getNextNonce(w)));
|
|
62
70
|
const signedTxs = await Promise.all(unsignedApprovals.map((unsigned, i) => needApproval[i].signTransaction({
|
|
@@ -65,13 +73,40 @@ export async function approveFourTokenManagerBatch(params) {
|
|
|
65
73
|
nonce: nonces[i],
|
|
66
74
|
gasLimit: finalGasLimit,
|
|
67
75
|
gasPrice,
|
|
68
|
-
chainId
|
|
76
|
+
chainId,
|
|
69
77
|
type: getTxType(config)
|
|
70
78
|
})));
|
|
71
79
|
nonceManager.clearTemp();
|
|
72
80
|
console.log(`✅ 已生成 ${signedTxs.length} 个授权交易签名`);
|
|
73
|
-
// ✅
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
// ✅ 内部提交到 Merkle
|
|
82
|
+
const currentBlock = await provider.getBlockNumber();
|
|
83
|
+
const targetBlock = currentBlock + (config.bundleBlockOffset ?? 2);
|
|
84
|
+
console.log(`📤 正在提交授权交易到 Merkle...`);
|
|
85
|
+
const bundleResult = await merkle.sendBundle({
|
|
86
|
+
transactions: signedTxs,
|
|
87
|
+
targetBlock: targetBlock
|
|
88
|
+
});
|
|
89
|
+
console.log(`✅ 授权交易已提交,bundleHash: ${bundleResult.bundleHash}`);
|
|
90
|
+
// ✅ 等待授权确认
|
|
91
|
+
console.log(`⏳ 等待授权交易确认(约5-10秒)...`);
|
|
92
|
+
const confirmResults = await merkle.waitForBundleConfirmation(bundleResult.txHashes, 1, 60000);
|
|
93
|
+
const successCount = confirmResults.filter(r => r.success).length;
|
|
94
|
+
if (successCount > 0) {
|
|
95
|
+
console.log(`✅ 授权已确认,共 ${successCount} 笔交易成功`);
|
|
96
|
+
return {
|
|
97
|
+
success: true,
|
|
98
|
+
approvedCount: successCount,
|
|
99
|
+
bundleHash: bundleResult.bundleHash,
|
|
100
|
+
message: `授权成功,共 ${successCount} 个钱包`
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
console.log(`⚠️ 授权交易未确认`);
|
|
105
|
+
return {
|
|
106
|
+
success: false,
|
|
107
|
+
approvedCount: 0,
|
|
108
|
+
bundleHash: bundleResult.bundleHash,
|
|
109
|
+
message: '授权交易未确认'
|
|
110
|
+
};
|
|
111
|
+
}
|
|
77
112
|
}
|
|
@@ -322,28 +322,73 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
322
322
|
const gasPrice = await getOptimizedGasPrice(provider, getGasPriceConfig(config));
|
|
323
323
|
const sellers = privateKeys.map((k) => new Wallet(k, provider));
|
|
324
324
|
const amountsWei = sellAmounts.map((a) => ethers.parseUnits(a, 18));
|
|
325
|
-
// ✅
|
|
325
|
+
// ✅ 自动获取报价以计算预期收益和利润(使用 PancakeSwap Router)
|
|
326
|
+
const PANCAKE_ROUTER = '0x10ED43C718714eb63d5aA57B78B54704E256024E';
|
|
327
|
+
const ROUTER_ABI = [
|
|
328
|
+
'function getAmountsOut(uint amountIn, address[] memory path) view returns (uint[] memory amounts)'
|
|
329
|
+
];
|
|
330
|
+
const WBNB = '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
|
|
331
|
+
const router = new ethers.Contract(PANCAKE_ROUTER, ROUTER_ABI, provider);
|
|
332
|
+
// 并行获取所有报价
|
|
333
|
+
console.log(`📊 开始获取 ${amountsWei.length} 个卖单的报价...`);
|
|
334
|
+
const quotedOutputs = await Promise.all(amountsWei.map(async (amount, index) => {
|
|
335
|
+
try {
|
|
336
|
+
const path = [tokenAddress, WBNB];
|
|
337
|
+
const amounts = await router.getAmountsOut(amount, path);
|
|
338
|
+
const bnbAmount = amounts[1];
|
|
339
|
+
console.log(` ✅ 钱包 ${index}: 卖出 ${ethers.formatUnits(amount, 18)} 代币 → 预计获得 ${ethers.formatEther(bnbAmount)} BNB`);
|
|
340
|
+
return bnbAmount; // 返回 BNB 数量
|
|
341
|
+
}
|
|
342
|
+
catch (error) {
|
|
343
|
+
console.log(` ❌ 钱包 ${index}: 报价失败 - ${error}`);
|
|
344
|
+
return 0n;
|
|
345
|
+
}
|
|
346
|
+
}));
|
|
347
|
+
// ✅ 使用报价结果或用户提供的 minOutputAmounts
|
|
326
348
|
let minOuts;
|
|
327
349
|
if (minOutputAmounts && minOutputAmounts.length === sellers.length) {
|
|
350
|
+
// 用户提供了 minOutputAmounts,优先使用
|
|
328
351
|
minOuts = minOutputAmounts.map(m => typeof m === 'string' ? ethers.parseEther(m) : m);
|
|
352
|
+
console.log(`📝 使用用户提供的 minOutputAmounts`);
|
|
329
353
|
}
|
|
330
354
|
else {
|
|
331
|
-
|
|
355
|
+
// 使用报价结果作为 minOutputAmount(保守起见,使用 95% 的报价金额)
|
|
356
|
+
minOuts = quotedOutputs.map(quoted => quoted * 95n / 100n);
|
|
357
|
+
console.log(`📝 使用自动报价的 95% 作为 minOutputAmount(5%滑点保护)`);
|
|
332
358
|
}
|
|
333
|
-
|
|
359
|
+
console.log(`💰 最终 minOutputAmounts:`);
|
|
360
|
+
minOuts.forEach((minOut, i) => {
|
|
361
|
+
console.log(` 钱包 ${i}: ${ethers.formatEther(minOut)} BNB`);
|
|
362
|
+
});
|
|
363
|
+
console.log('');
|
|
364
|
+
// ✅ Step 1: 检查代币余额和授权状态
|
|
334
365
|
const ERC20_ABI = [
|
|
335
366
|
'function approve(address spender, uint256 amount) returns (bool)',
|
|
336
|
-
'function allowance(address owner, address spender) view returns (uint256)'
|
|
367
|
+
'function allowance(address owner, address spender) view returns (uint256)',
|
|
368
|
+
'function balanceOf(address owner) view returns (uint256)'
|
|
337
369
|
];
|
|
338
|
-
|
|
370
|
+
// 检查代币余额
|
|
371
|
+
console.log('🔍 检查代币余额...');
|
|
372
|
+
const tokenContract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
|
|
373
|
+
const balances = await Promise.all(sellers.map(w => tokenContract.balanceOf(w.address)));
|
|
374
|
+
for (let i = 0; i < sellers.length; i++) {
|
|
375
|
+
const balance = balances[i];
|
|
376
|
+
const sellAmount = amountsWei[i];
|
|
377
|
+
console.log(` 钱包 ${i}: 余额 ${ethers.formatUnits(balance, 18)}, 卖出 ${ethers.formatUnits(sellAmount, 18)}`);
|
|
378
|
+
if (balance < sellAmount) {
|
|
379
|
+
throw new Error(`钱包 ${i} (${sellers[i].address.slice(0, 8)}...) 代币余额不足:需要 ${ethers.formatUnits(sellAmount, 18)},实际 ${ethers.formatUnits(balance, 18)}`);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
console.log('\n🔍 使用 Multicall3 批量检查授权状态...');
|
|
339
383
|
const allowances = await batchCheckAllowances(provider, tokenAddress, sellers.map(w => w.address), tmAddr);
|
|
340
384
|
// 找出需要授权的钱包索引
|
|
341
|
-
//
|
|
385
|
+
// ✅ 检查授权额度是否足够卖出
|
|
342
386
|
const needApprovalIndexes = [];
|
|
343
|
-
const APPROVAL_THRESHOLD = ethers.MaxUint256 / 2n;
|
|
344
387
|
for (let i = 0; i < sellers.length; i++) {
|
|
345
|
-
|
|
388
|
+
// ✅ 检查授权额度是否 >= 卖出数量
|
|
389
|
+
if (allowances[i] < amountsWei[i]) {
|
|
346
390
|
needApprovalIndexes.push(i);
|
|
391
|
+
console.log(` ⚠️ 钱包 ${i}: 授权不足 (${ethers.formatUnits(allowances[i], 18)} < ${ethers.formatUnits(amountsWei[i], 18)})`);
|
|
347
392
|
}
|
|
348
393
|
}
|
|
349
394
|
console.log(` - 总钱包数: ${sellers.length}`);
|
|
@@ -352,7 +397,7 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
352
397
|
// ✅ Step 2: 批量授权(如果需要)
|
|
353
398
|
// ⚠️ 注意:授权需要单独提交,SDK只返回签名交易
|
|
354
399
|
if (needApprovalIndexes.length > 0) {
|
|
355
|
-
console.log(
|
|
400
|
+
console.log(`⚠️ 警告:检测到 ${needApprovalIndexes.length} 个钱包需要授权`);
|
|
356
401
|
console.log(' 请先使用这些钱包的私钥调用授权方法,授权完成后再调用卖出方法');
|
|
357
402
|
throw new Error(`需要授权: ${needApprovalIndexes.length} 个钱包尚未授权。请先完成授权后再卖出。`);
|
|
358
403
|
}
|
|
@@ -376,14 +421,17 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
376
421
|
type: getTxType(config)
|
|
377
422
|
})));
|
|
378
423
|
signedTxs.push(...signedList);
|
|
379
|
-
// ✅
|
|
424
|
+
// ✅ 基于报价金额为每个钱包添加利润转账
|
|
380
425
|
const extractProfit = shouldExtractProfit(config);
|
|
381
|
-
if (extractProfit &&
|
|
382
|
-
|
|
426
|
+
if (extractProfit && quotedOutputs.length > 0) {
|
|
427
|
+
console.log(`💰 提取利润交易...`);
|
|
428
|
+
let totalProfitExtracted = 0n;
|
|
429
|
+
// 为每个钱包添加利润转账(基于完整报价金额,而非 minOut)
|
|
383
430
|
for (let i = 0; i < sellers.length; i++) {
|
|
384
|
-
if (
|
|
385
|
-
const { profit } = calculateProfit(
|
|
431
|
+
if (quotedOutputs[i] > 0n) { // 只对报价成功的交易提取利润
|
|
432
|
+
const { profit } = calculateProfit(quotedOutputs[i], config);
|
|
386
433
|
if (profit > 0n) {
|
|
434
|
+
console.log(` ✅ 钱包 ${i}: 从 ${ethers.formatEther(quotedOutputs[i])} BNB 中提取 ${ethers.formatEther(profit)} BNB (${config.profitRateBps / 100}%)`);
|
|
387
435
|
const profitNonce = await nonceManager.getNextNonce(sellers[i]);
|
|
388
436
|
const profitTx = await sellers[i].signTransaction({
|
|
389
437
|
to: config.profitRecipient,
|
|
@@ -395,9 +443,11 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
395
443
|
type: getTxType(config)
|
|
396
444
|
});
|
|
397
445
|
signedTxs.push(profitTx);
|
|
446
|
+
totalProfitExtracted += profit;
|
|
398
447
|
}
|
|
399
448
|
}
|
|
400
449
|
}
|
|
450
|
+
console.log(` 💵 总利润: ${ethers.formatEther(totalProfitExtracted)} BNB → ${config.profitRecipient}\n`);
|
|
401
451
|
}
|
|
402
452
|
// ✅ 清理临时 nonce 缓存
|
|
403
453
|
nonceManager.clearTemp();
|
|
@@ -216,18 +216,24 @@ export async function approveFourPancakeProxyBatch(params) {
|
|
|
216
216
|
// 只授权不足的
|
|
217
217
|
const needApproval = wallets.filter((_, i) => allowances[i] < amountsBigInt[i]);
|
|
218
218
|
const needApprovalAmounts = amountsBigInt.filter((amount, i) => allowances[i] < amount);
|
|
219
|
+
console.log(`🔍 PancakeProxy 授权检查:`);
|
|
220
|
+
console.log(` - 总钱包数: ${wallets.length}`);
|
|
221
|
+
console.log(` - 需要授权: ${needApproval.length}`);
|
|
222
|
+
console.log(` - 已有授权: ${wallets.length - needApproval.length}`);
|
|
219
223
|
if (needApproval.length === 0) {
|
|
220
|
-
|
|
224
|
+
console.log('✅ 所有钱包已授权,无需再次授权。');
|
|
221
225
|
return {
|
|
222
|
-
|
|
226
|
+
success: true,
|
|
227
|
+
approvedCount: 0,
|
|
228
|
+
message: '所有钱包已授权'
|
|
223
229
|
};
|
|
224
230
|
}
|
|
225
231
|
// 构建授权交易
|
|
226
232
|
const needApprovalTokens = needApproval.map(w => new Contract(tokenAddress, ERC20_ABI, w));
|
|
227
233
|
const unsignedApprovals = await Promise.all(needApprovalTokens.map((token, i) => token.approve.populateTransaction(pancakeProxyAddress, needApprovalAmounts[i])));
|
|
228
|
-
//
|
|
234
|
+
// 使用前端传入的 gasLimit,否则使用默认值
|
|
229
235
|
const finalGasLimit = getGasLimit(config);
|
|
230
|
-
//
|
|
236
|
+
// 签名授权交易
|
|
231
237
|
const nonceManager = new NonceManager(provider);
|
|
232
238
|
const nonces = await Promise.all(needApproval.map(w => nonceManager.getNextNonce(w)));
|
|
233
239
|
const signedTxs = await Promise.all(unsignedApprovals.map((unsigned, i) => needApproval[i].signTransaction({
|
|
@@ -240,10 +246,38 @@ export async function approveFourPancakeProxyBatch(params) {
|
|
|
240
246
|
type: getTxType(config)
|
|
241
247
|
})));
|
|
242
248
|
nonceManager.clearTemp();
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
249
|
+
console.log(`✅ 已生成 ${signedTxs.length} 个授权交易签名`);
|
|
250
|
+
// ✅ 内部提交到 Merkle
|
|
251
|
+
const currentBlock = await provider.getBlockNumber();
|
|
252
|
+
const targetBlock = currentBlock + blockOffset;
|
|
253
|
+
console.log(`📤 正在提交授权交易到 Merkle...`);
|
|
254
|
+
const bundleResult = await merkle.sendBundle({
|
|
255
|
+
transactions: signedTxs,
|
|
256
|
+
targetBlock: targetBlock
|
|
257
|
+
});
|
|
258
|
+
console.log(`✅ 授权交易已提交,bundleHash: ${bundleResult.bundleHash}`);
|
|
259
|
+
// ✅ 等待授权确认
|
|
260
|
+
console.log(`⏳ 等待授权交易确认(约5-10秒)...`);
|
|
261
|
+
const confirmResults = await merkle.waitForBundleConfirmation(bundleResult.txHashes, 1, 60000);
|
|
262
|
+
const successCount = confirmResults.filter(r => r.success).length;
|
|
263
|
+
if (successCount > 0) {
|
|
264
|
+
console.log(`✅ 授权已确认,共 ${successCount} 笔交易成功`);
|
|
265
|
+
return {
|
|
266
|
+
success: true,
|
|
267
|
+
approvedCount: successCount,
|
|
268
|
+
bundleHash: bundleResult.bundleHash,
|
|
269
|
+
message: `授权成功,共 ${successCount} 个钱包`
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
console.log(`⚠️ 授权交易未确认`);
|
|
274
|
+
return {
|
|
275
|
+
success: false,
|
|
276
|
+
approvedCount: 0,
|
|
277
|
+
bundleHash: bundleResult.bundleHash,
|
|
278
|
+
message: '授权交易未确认'
|
|
279
|
+
};
|
|
280
|
+
}
|
|
247
281
|
}
|
|
248
282
|
/**
|
|
249
283
|
* 使用 PancakeSwapProxy 批量购买代币(Merkle 版本)
|
|
@@ -380,7 +414,7 @@ export async function fourPancakeProxyBatchSellMerkle(params) {
|
|
|
380
414
|
// ✅ Step 2: 如果需要授权,抛出错误提示
|
|
381
415
|
// ⚠️ SDK不再处理授权,需要前端先单独授权
|
|
382
416
|
if (needApprovalIndexes.length > 0) {
|
|
383
|
-
console.log(
|
|
417
|
+
console.log(`⚠️ 警告:检测到 ${needApprovalIndexes.length} 个钱包需要授权`);
|
|
384
418
|
console.log(' 请先使用这些钱包完成授权后再调用卖出方法');
|
|
385
419
|
throw new Error(`需要授权: ${needApprovalIndexes.length} 个钱包尚未授权。请先完成授权后再卖出。`);
|
|
386
420
|
}
|
|
@@ -261,7 +261,7 @@ export async function fourBatchPrivateSellMerkle(params) {
|
|
|
261
261
|
// ✅ Step 2: 如果需要授权,抛出错误提示
|
|
262
262
|
// ⚠️ SDK不再处理授权,需要前端先单独授权
|
|
263
263
|
if (needApprovalIndexes.length > 0) {
|
|
264
|
-
console.log(
|
|
264
|
+
console.log(`⚠️ 警告:检测到 ${needApprovalIndexes.length} 个钱包需要授权`);
|
|
265
265
|
console.log(' 请先使用这些钱包完成授权后再调用卖出方法');
|
|
266
266
|
throw new Error(`需要授权: ${needApprovalIndexes.length} 个钱包尚未授权。请先完成授权后再卖出。`);
|
|
267
267
|
}
|
|
@@ -215,5 +215,10 @@ export type FourPancakeProxyApprovalBatchParams = {
|
|
|
215
215
|
amounts: (string | 'max')[];
|
|
216
216
|
config: FourBundleMerkleConfig;
|
|
217
217
|
};
|
|
218
|
-
/** ✅ PancakeProxy
|
|
219
|
-
export type FourPancakeProxyApprovalBatchResult =
|
|
218
|
+
/** ✅ PancakeProxy 批量授权结果 */
|
|
219
|
+
export type FourPancakeProxyApprovalBatchResult = {
|
|
220
|
+
success: boolean;
|
|
221
|
+
approvedCount: number;
|
|
222
|
+
bundleHash?: string;
|
|
223
|
+
message: string;
|
|
224
|
+
};
|
|
@@ -217,18 +217,24 @@ export async function approvePancakeProxyBatch(params) {
|
|
|
217
217
|
// 只授权不足的
|
|
218
218
|
const needApproval = wallets.filter((_, i) => allowances[i] < amountsBigInt[i]);
|
|
219
219
|
const needApprovalAmounts = amountsBigInt.filter((amount, i) => allowances[i] < amount);
|
|
220
|
+
console.log(`🔍 PancakeProxy 授权检查:`);
|
|
221
|
+
console.log(` - 总钱包数: ${wallets.length}`);
|
|
222
|
+
console.log(` - 需要授权: ${needApproval.length}`);
|
|
223
|
+
console.log(` - 已有授权: ${wallets.length - needApproval.length}`);
|
|
220
224
|
if (needApproval.length === 0) {
|
|
221
|
-
|
|
225
|
+
console.log('✅ 所有钱包已授权,无需再次授权。');
|
|
222
226
|
return {
|
|
223
|
-
|
|
227
|
+
success: true,
|
|
228
|
+
approvedCount: 0,
|
|
229
|
+
message: '所有钱包已授权'
|
|
224
230
|
};
|
|
225
231
|
}
|
|
226
232
|
// 构建授权交易
|
|
227
233
|
const needApprovalTokens = needApproval.map(w => new Contract(tokenAddress, ERC20_ABI, w));
|
|
228
234
|
const unsignedApprovals = await Promise.all(needApprovalTokens.map((token, i) => token.approve.populateTransaction(pancakeProxyAddress, needApprovalAmounts[i])));
|
|
229
|
-
//
|
|
235
|
+
// 使用前端传入的 gasLimit,否则使用默认值
|
|
230
236
|
const finalGasLimit = getGasLimit(config);
|
|
231
|
-
//
|
|
237
|
+
// 签名授权交易
|
|
232
238
|
const nonceManager = new NonceManager(provider);
|
|
233
239
|
const nonces = await Promise.all(needApproval.map(w => nonceManager.getNextNonce(w)));
|
|
234
240
|
const signedTxs = await Promise.all(unsignedApprovals.map((unsigned, i) => needApproval[i].signTransaction({
|
|
@@ -241,10 +247,38 @@ export async function approvePancakeProxyBatch(params) {
|
|
|
241
247
|
type: getTxType(config)
|
|
242
248
|
})));
|
|
243
249
|
nonceManager.clearTemp();
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
250
|
+
console.log(`✅ 已生成 ${signedTxs.length} 个授权交易签名`);
|
|
251
|
+
// ✅ 内部提交到 Merkle
|
|
252
|
+
const currentBlock = await provider.getBlockNumber();
|
|
253
|
+
const targetBlock = currentBlock + blockOffset;
|
|
254
|
+
console.log(`📤 正在提交授权交易到 Merkle...`);
|
|
255
|
+
const bundleResult = await merkle.sendBundle({
|
|
256
|
+
transactions: signedTxs,
|
|
257
|
+
targetBlock: targetBlock
|
|
258
|
+
});
|
|
259
|
+
console.log(`✅ 授权交易已提交,bundleHash: ${bundleResult.bundleHash}`);
|
|
260
|
+
// ✅ 等待授权确认
|
|
261
|
+
console.log(`⏳ 等待授权交易确认(约5-10秒)...`);
|
|
262
|
+
const confirmResults = await merkle.waitForBundleConfirmation(bundleResult.txHashes, 1, 60000);
|
|
263
|
+
const successCount = confirmResults.filter(r => r.success).length;
|
|
264
|
+
if (successCount > 0) {
|
|
265
|
+
console.log(`✅ 授权已确认,共 ${successCount} 笔交易成功`);
|
|
266
|
+
return {
|
|
267
|
+
success: true,
|
|
268
|
+
approvedCount: successCount,
|
|
269
|
+
bundleHash: bundleResult.bundleHash,
|
|
270
|
+
message: `授权成功,共 ${successCount} 个钱包`
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
console.log(`⚠️ 授权交易未确认`);
|
|
275
|
+
return {
|
|
276
|
+
success: false,
|
|
277
|
+
approvedCount: 0,
|
|
278
|
+
bundleHash: bundleResult.bundleHash,
|
|
279
|
+
message: '授权交易未确认'
|
|
280
|
+
};
|
|
281
|
+
}
|
|
248
282
|
}
|
|
249
283
|
/**
|
|
250
284
|
* 使用 PancakeSwapProxy 批量购买代币(Merkle 版本)
|
|
@@ -383,7 +417,7 @@ export async function pancakeProxyBatchSellMerkle(params) {
|
|
|
383
417
|
// ✅ Step 2: 如果需要授权,抛出错误提示
|
|
384
418
|
// ⚠️ SDK不再处理授权,需要前端先单独授权
|
|
385
419
|
if (needApprovalIndexes.length > 0) {
|
|
386
|
-
console.log(
|
|
420
|
+
console.log(`⚠️ 警告:检测到 ${needApprovalIndexes.length} 个钱包需要授权`);
|
|
387
421
|
console.log(' 请先使用这些钱包完成授权后再调用卖出方法');
|
|
388
422
|
throw new Error(`需要授权: ${needApprovalIndexes.length} 个钱包尚未授权。请先完成授权后再卖出。`);
|
|
389
423
|
}
|
|
@@ -187,5 +187,10 @@ export type PancakeProxyApprovalBatchParams = {
|
|
|
187
187
|
amounts: (string | 'max')[];
|
|
188
188
|
config: FlapBundleMerkleConfig;
|
|
189
189
|
};
|
|
190
|
-
/** ✅ PancakeProxy
|
|
191
|
-
export type PancakeProxyApprovalBatchResult =
|
|
190
|
+
/** ✅ PancakeProxy 批量授权结果 */
|
|
191
|
+
export type PancakeProxyApprovalBatchResult = {
|
|
192
|
+
success: boolean;
|
|
193
|
+
approvedCount: number;
|
|
194
|
+
bundleHash?: string;
|
|
195
|
+
message: string;
|
|
196
|
+
};
|