four-flap-meme-sdk 1.1.82 → 1.1.84
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/core.js +64 -232
- package/dist/contracts/tm-bundle-merkle/pancake-proxy.js +104 -72
- package/dist/contracts/tm-bundle-merkle/private.js +145 -168
- package/dist/contracts/tm-bundle-merkle/types.d.ts +1 -0
- package/dist/contracts/tm-bundle-merkle/utils.js +49 -75
- package/dist/flap/portal-bundle-merkle/config.d.ts +29 -0
- package/dist/flap/portal-bundle-merkle/config.js +47 -0
- package/dist/flap/portal-bundle-merkle/core.js +48 -118
- package/dist/flap/portal-bundle-merkle/pancake-proxy.js +104 -72
- package/dist/flap/portal-bundle-merkle/private.js +141 -121
- package/dist/flap/portal-bundle-merkle/types.d.ts +10 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@ import { MerkleClient } from '../../clients/merkle.js';
|
|
|
3
3
|
import { NonceManager, getOptimizedGasPrice } from '../../utils/bundle-helpers.js';
|
|
4
4
|
import { ADDRESSES } from '../../utils/constants.js';
|
|
5
5
|
import { FourClient, buildLoginMessage } from '../../clients/four.js';
|
|
6
|
-
import { getErrorMessage, getTxType,
|
|
6
|
+
import { getErrorMessage, getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit } from './config.js';
|
|
7
7
|
// ==================== TokenManager2 ABI(仅需要的方法)====================
|
|
8
8
|
const TM2_ABI = [
|
|
9
9
|
'function createToken(bytes args, bytes signature) payable',
|
|
@@ -201,111 +201,30 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
201
201
|
});
|
|
202
202
|
signedTxs.push(profitTx);
|
|
203
203
|
}
|
|
204
|
-
// ✅
|
|
205
|
-
if (config.skipSubmit) {
|
|
206
|
-
nonceManager.clearTemp();
|
|
207
|
-
const emptyStatus = {
|
|
208
|
-
bundleHash: '',
|
|
209
|
-
txHashes: [],
|
|
210
|
-
results: [],
|
|
211
|
-
successCount: 0,
|
|
212
|
-
totalGasUsed: 0n,
|
|
213
|
-
targetBlock: 0
|
|
214
|
-
};
|
|
215
|
-
return {
|
|
216
|
-
bundleHash: '',
|
|
217
|
-
tokenAddress: predictedTokenAddress,
|
|
218
|
-
status: emptyStatus,
|
|
219
|
-
createTx: signedCreateTx,
|
|
220
|
-
buyTxs: [],
|
|
221
|
-
signedTransactions: signedTxs,
|
|
222
|
-
metadata: extractProfit ? {
|
|
223
|
-
totalBuyAmount: ethers.formatEther(totalBuyAmount),
|
|
224
|
-
profitAmount: ethers.formatEther(totalProfit),
|
|
225
|
-
profitRecipient: config.profitRecipient,
|
|
226
|
-
buyerCount: buyers.length
|
|
227
|
-
} : undefined
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
// 5. 提交 Bundle(使用智能配置)
|
|
231
|
-
const bundleResult = await merkle.sendBundle({
|
|
232
|
-
transactions: signedTxs,
|
|
233
|
-
...getBundleOptions(config, blockOffset)
|
|
234
|
-
});
|
|
235
|
-
// 6. 根据配置决定是否等待确认
|
|
236
|
-
const waitForConfirmation = config.waitForConfirmation ?? false;
|
|
237
|
-
if (!waitForConfirmation) {
|
|
238
|
-
const status = {
|
|
239
|
-
bundleHash: bundleResult.bundleHash,
|
|
240
|
-
txHashes: bundleResult.txHashes,
|
|
241
|
-
results: [],
|
|
242
|
-
successCount: 0,
|
|
243
|
-
totalGasUsed: 0n,
|
|
244
|
-
targetBlock: bundleResult.targetBlock
|
|
245
|
-
};
|
|
246
|
-
// 清理临时 nonce 缓存
|
|
247
|
-
nonceManager.clearTemp();
|
|
248
|
-
return {
|
|
249
|
-
bundleHash: bundleResult.bundleHash,
|
|
250
|
-
tokenAddress: undefined,
|
|
251
|
-
status,
|
|
252
|
-
createTx: signedCreateTx,
|
|
253
|
-
buyTxs
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
// 7. 等待确认
|
|
257
|
-
const results = await merkle.waitForBundleConfirmation(bundleResult.txHashes, 1, config.waitTimeoutMs ?? 120000);
|
|
258
|
-
const successCount = results.filter(r => r.success).length;
|
|
259
|
-
const totalGasUsed = results.reduce((sum, r) => {
|
|
260
|
-
if (r.gasUsed) {
|
|
261
|
-
return sum + BigInt(r.gasUsed);
|
|
262
|
-
}
|
|
263
|
-
return sum;
|
|
264
|
-
}, 0n);
|
|
265
|
-
const status = {
|
|
266
|
-
bundleHash: bundleResult.bundleHash,
|
|
267
|
-
txHashes: bundleResult.txHashes,
|
|
268
|
-
results,
|
|
269
|
-
successCount,
|
|
270
|
-
totalGasUsed,
|
|
271
|
-
targetBlock: bundleResult.targetBlock
|
|
272
|
-
};
|
|
273
|
-
// 8. 解析 TokenCreate 事件
|
|
274
|
-
let createdTokenAddress;
|
|
275
|
-
if (results[0]?.success) {
|
|
276
|
-
try {
|
|
277
|
-
const createTxHash = bundleResult.txHashes[0];
|
|
278
|
-
if (createTxHash) {
|
|
279
|
-
const receipt = await provider.getTransactionReceipt(createTxHash);
|
|
280
|
-
if (receipt && receipt.logs) {
|
|
281
|
-
const tm2Interface = new ethers.Interface(TM2_ABI);
|
|
282
|
-
for (const log of receipt.logs) {
|
|
283
|
-
try {
|
|
284
|
-
const parsed = tm2Interface.parseLog({ topics: log.topics, data: log.data });
|
|
285
|
-
if (parsed && parsed.name === 'TokenCreate') {
|
|
286
|
-
createdTokenAddress = parsed.args.token;
|
|
287
|
-
break;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
catch (e) {
|
|
291
|
-
// 忽略
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
catch (e) {
|
|
298
|
-
console.warn('Failed to parse token address from receipt:', e);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
// 清理临时 nonce 缓存
|
|
204
|
+
// ✅ 清理临时 nonce 缓存
|
|
302
205
|
nonceManager.clearTemp();
|
|
206
|
+
// ✅ 直接返回签名交易(不提交到Merkle)
|
|
207
|
+
const emptyStatus = {
|
|
208
|
+
bundleHash: '',
|
|
209
|
+
txHashes: [],
|
|
210
|
+
results: [],
|
|
211
|
+
successCount: 0,
|
|
212
|
+
totalGasUsed: 0n,
|
|
213
|
+
targetBlock: 0
|
|
214
|
+
};
|
|
303
215
|
return {
|
|
304
|
-
bundleHash:
|
|
305
|
-
tokenAddress:
|
|
306
|
-
status,
|
|
216
|
+
bundleHash: '',
|
|
217
|
+
tokenAddress: predictedTokenAddress,
|
|
218
|
+
status: emptyStatus,
|
|
307
219
|
createTx: signedCreateTx,
|
|
308
|
-
buyTxs
|
|
220
|
+
buyTxs: [],
|
|
221
|
+
signedTransactions: signedTxs,
|
|
222
|
+
metadata: extractProfit ? {
|
|
223
|
+
totalBuyAmount: ethers.formatEther(totalBuyAmount),
|
|
224
|
+
profitAmount: ethers.formatEther(totalProfit),
|
|
225
|
+
profitRecipient: config.profitRecipient,
|
|
226
|
+
buyerCount: buyers.length
|
|
227
|
+
} : undefined
|
|
309
228
|
};
|
|
310
229
|
}
|
|
311
230
|
/**
|
|
@@ -379,67 +298,29 @@ export async function batchBuyWithBundleMerkle(params) {
|
|
|
379
298
|
});
|
|
380
299
|
signedTxs.push(profitTx);
|
|
381
300
|
}
|
|
382
|
-
// ✅
|
|
383
|
-
if (config.skipSubmit) {
|
|
384
|
-
nonceManager.clearTemp();
|
|
385
|
-
const emptyStatus = {
|
|
386
|
-
bundleHash: '',
|
|
387
|
-
txHashes: [],
|
|
388
|
-
results: [],
|
|
389
|
-
successCount: 0,
|
|
390
|
-
totalGasUsed: 0n,
|
|
391
|
-
targetBlock: 0
|
|
392
|
-
};
|
|
393
|
-
return {
|
|
394
|
-
bundleHash: '',
|
|
395
|
-
status: emptyStatus,
|
|
396
|
-
buyTxs: [],
|
|
397
|
-
signedTransactions: signedTxs,
|
|
398
|
-
metadata: extractProfit ? {
|
|
399
|
-
totalBuyAmount: ethers.formatEther(totalBuyAmount),
|
|
400
|
-
profitAmount: ethers.formatEther(totalProfit),
|
|
401
|
-
profitRecipient: config.profitRecipient,
|
|
402
|
-
buyerCount: buyers.length
|
|
403
|
-
} : undefined
|
|
404
|
-
};
|
|
405
|
-
}
|
|
406
|
-
const bundleResult = await merkle.sendBundle({
|
|
407
|
-
transactions: signedTxs,
|
|
408
|
-
...getBundleOptions(config, blockOffset)
|
|
409
|
-
});
|
|
410
|
-
const waitForConfirmation = config.waitForConfirmation ?? false;
|
|
411
|
-
if (!waitForConfirmation) {
|
|
412
|
-
const status = {
|
|
413
|
-
bundleHash: bundleResult.bundleHash,
|
|
414
|
-
txHashes: bundleResult.txHashes,
|
|
415
|
-
results: [],
|
|
416
|
-
successCount: 0,
|
|
417
|
-
totalGasUsed: 0n,
|
|
418
|
-
targetBlock: bundleResult.targetBlock
|
|
419
|
-
};
|
|
420
|
-
// 清理临时 nonce 缓存
|
|
421
|
-
nonceManager.clearTemp();
|
|
422
|
-
return { bundleHash: bundleResult.bundleHash, status, buyTxs: signedTxs };
|
|
423
|
-
}
|
|
424
|
-
const results = await merkle.waitForBundleConfirmation(bundleResult.txHashes, 1, config.waitTimeoutMs ?? 120000);
|
|
425
|
-
const successCount = results.filter(r => r.success).length;
|
|
426
|
-
const totalGasUsed = results.reduce((sum, r) => {
|
|
427
|
-
if (r.gasUsed) {
|
|
428
|
-
return sum + BigInt(r.gasUsed);
|
|
429
|
-
}
|
|
430
|
-
return sum;
|
|
431
|
-
}, 0n);
|
|
432
|
-
const status = {
|
|
433
|
-
bundleHash: bundleResult.bundleHash,
|
|
434
|
-
txHashes: bundleResult.txHashes,
|
|
435
|
-
results,
|
|
436
|
-
successCount,
|
|
437
|
-
totalGasUsed,
|
|
438
|
-
targetBlock: bundleResult.targetBlock
|
|
439
|
-
};
|
|
440
|
-
// 清理临时 nonce 缓存
|
|
301
|
+
// ✅ 清理临时 nonce 缓存
|
|
441
302
|
nonceManager.clearTemp();
|
|
442
|
-
|
|
303
|
+
// ✅ 直接返回签名交易(不提交到Merkle)
|
|
304
|
+
const emptyStatus = {
|
|
305
|
+
bundleHash: '',
|
|
306
|
+
txHashes: [],
|
|
307
|
+
results: [],
|
|
308
|
+
successCount: 0,
|
|
309
|
+
totalGasUsed: 0n,
|
|
310
|
+
targetBlock: 0
|
|
311
|
+
};
|
|
312
|
+
return {
|
|
313
|
+
bundleHash: '',
|
|
314
|
+
status: emptyStatus,
|
|
315
|
+
buyTxs: [],
|
|
316
|
+
signedTransactions: signedTxs,
|
|
317
|
+
metadata: extractProfit ? {
|
|
318
|
+
totalBuyAmount: ethers.formatEther(totalBuyAmount),
|
|
319
|
+
profitAmount: ethers.formatEther(totalProfit),
|
|
320
|
+
profitRecipient: config.profitRecipient,
|
|
321
|
+
buyerCount: buyers.length
|
|
322
|
+
} : undefined
|
|
323
|
+
};
|
|
443
324
|
}
|
|
444
325
|
/**
|
|
445
326
|
* four.meme: 批量卖出(Merkle 版本)
|
|
@@ -484,40 +365,11 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
484
365
|
console.log(` - 需要授权: ${needApprovalIndexes.length}`);
|
|
485
366
|
console.log(` - 已有授权: ${sellers.length - needApprovalIndexes.length}\n`);
|
|
486
367
|
// ✅ Step 2: 批量授权(如果需要)
|
|
368
|
+
// ⚠️ 注意:授权需要单独提交,SDK只返回签名交易
|
|
487
369
|
if (needApprovalIndexes.length > 0) {
|
|
488
|
-
console.log('
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
const needApprovalSellers = needApprovalIndexes.map(i => sellers[i]);
|
|
492
|
-
const tokenContractsForApprove = needApprovalSellers.map((w) => new ethers.Contract(tokenAddress, ERC20_ABI, w));
|
|
493
|
-
const approveUnsigned = await Promise.all(tokenContractsForApprove.map((c) => c.approve.populateTransaction(tmAddr, ethers.MaxUint256 // 授权最大额度
|
|
494
|
-
)));
|
|
495
|
-
const approveGasLimit = getGasLimit(config, 80000);
|
|
496
|
-
const approveNonces = await Promise.all(needApprovalSellers.map((w) => nonceManager.getNextNonce(w)));
|
|
497
|
-
const signedApproves = await Promise.all(approveUnsigned.map((unsigned, i) => needApprovalSellers[i].signTransaction({
|
|
498
|
-
...unsigned,
|
|
499
|
-
from: needApprovalSellers[i].address,
|
|
500
|
-
nonce: approveNonces[i],
|
|
501
|
-
gasLimit: approveGasLimit,
|
|
502
|
-
gasPrice: gasPrice,
|
|
503
|
-
chainId: 56,
|
|
504
|
-
type: getTxType(config)
|
|
505
|
-
})));
|
|
506
|
-
approveSignedTxs.push(...signedApproves);
|
|
507
|
-
nonceManager.clearTemp();
|
|
508
|
-
// 提交授权 Bundle 并等待确认
|
|
509
|
-
const approveBundleResult = await merkle.sendBundle({
|
|
510
|
-
transactions: approveSignedTxs,
|
|
511
|
-
...getBundleOptions(config, blockOffset)
|
|
512
|
-
});
|
|
513
|
-
console.log(` Bundle Hash: ${approveBundleResult.bundleHash}`);
|
|
514
|
-
console.log('⏳ 等待授权确认...');
|
|
515
|
-
const approveResults = await merkle.waitForBundleConfirmation(approveBundleResult.txHashes, 1, config.waitTimeoutMs ?? 120000);
|
|
516
|
-
const approveSuccessCount = approveResults.filter(r => r.success).length;
|
|
517
|
-
console.log(`✅ 授权完成: ${approveSuccessCount}/${needApprovalIndexes.length}\n`);
|
|
518
|
-
if (approveSuccessCount !== needApprovalIndexes.length) {
|
|
519
|
-
throw new Error(`授权失败: 只有 ${approveSuccessCount}/${needApprovalIndexes.length} 笔授权成功`);
|
|
520
|
-
}
|
|
370
|
+
console.log('⚠️ 警告:检测到 ${needApprovalIndexes.length} 个钱包需要授权');
|
|
371
|
+
console.log(' 请先使用这些钱包的私钥调用授权方法,授权完成后再调用卖出方法');
|
|
372
|
+
throw new Error(`需要授权: ${needApprovalIndexes.length} 个钱包尚未授权。请先完成授权后再卖出。`);
|
|
521
373
|
}
|
|
522
374
|
// ✅ Step 3: 批量卖出
|
|
523
375
|
console.log('💰 提交卖出交易...');
|
|
@@ -539,41 +391,21 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
539
391
|
type: getTxType(config)
|
|
540
392
|
})));
|
|
541
393
|
signedTxs.push(...signedList);
|
|
542
|
-
|
|
543
|
-
transactions: signedTxs,
|
|
544
|
-
...getBundleOptions(config, blockOffset)
|
|
545
|
-
});
|
|
546
|
-
const waitForConfirmation = config.waitForConfirmation ?? false;
|
|
547
|
-
if (!waitForConfirmation) {
|
|
548
|
-
const status = {
|
|
549
|
-
bundleHash: bundleResult.bundleHash,
|
|
550
|
-
txHashes: bundleResult.txHashes,
|
|
551
|
-
results: [],
|
|
552
|
-
successCount: 0,
|
|
553
|
-
totalGasUsed: 0n,
|
|
554
|
-
targetBlock: bundleResult.targetBlock
|
|
555
|
-
};
|
|
556
|
-
// 清理临时 nonce 缓存
|
|
557
|
-
nonceManager.clearTemp();
|
|
558
|
-
return { bundleHash: bundleResult.bundleHash, status, sellTxs: signedTxs };
|
|
559
|
-
}
|
|
560
|
-
const results = await merkle.waitForBundleConfirmation(bundleResult.txHashes, 1, config.waitTimeoutMs ?? 120000);
|
|
561
|
-
const successCount = results.filter(r => r.success).length;
|
|
562
|
-
const totalGasUsed = results.reduce((sum, r) => {
|
|
563
|
-
if (r.gasUsed) {
|
|
564
|
-
return sum + BigInt(r.gasUsed);
|
|
565
|
-
}
|
|
566
|
-
return sum;
|
|
567
|
-
}, 0n);
|
|
568
|
-
const status = {
|
|
569
|
-
bundleHash: bundleResult.bundleHash,
|
|
570
|
-
txHashes: bundleResult.txHashes,
|
|
571
|
-
results,
|
|
572
|
-
successCount,
|
|
573
|
-
totalGasUsed,
|
|
574
|
-
targetBlock: bundleResult.targetBlock
|
|
575
|
-
};
|
|
576
|
-
// 清理临时 nonce 缓存
|
|
394
|
+
// ✅ 清理临时 nonce 缓存
|
|
577
395
|
nonceManager.clearTemp();
|
|
578
|
-
|
|
396
|
+
// ✅ 直接返回签名交易(不提交到Merkle)
|
|
397
|
+
const emptyStatus = {
|
|
398
|
+
bundleHash: '',
|
|
399
|
+
txHashes: [],
|
|
400
|
+
results: [],
|
|
401
|
+
successCount: 0,
|
|
402
|
+
totalGasUsed: 0n,
|
|
403
|
+
targetBlock: 0
|
|
404
|
+
};
|
|
405
|
+
return {
|
|
406
|
+
bundleHash: '',
|
|
407
|
+
status: emptyStatus,
|
|
408
|
+
sellTxs: [],
|
|
409
|
+
signedTransactions: signedTxs
|
|
410
|
+
};
|
|
579
411
|
}
|
|
@@ -2,7 +2,7 @@ import { ethers, Wallet, JsonRpcProvider, Contract } from 'ethers';
|
|
|
2
2
|
import { MerkleClient } from '../../clients/merkle.js';
|
|
3
3
|
import { NonceManager, getOptimizedGasPrice } from '../../utils/bundle-helpers.js';
|
|
4
4
|
import { ADDRESSES } from '../../utils/constants.js';
|
|
5
|
-
import { getTxType, getBundleOptions, getGasPriceConfig } from './config.js';
|
|
5
|
+
import { getTxType, getBundleOptions, getGasPriceConfig, shouldExtractProfit, calculateProfit, calculateBatchProfit } from './config.js';
|
|
6
6
|
// 常量
|
|
7
7
|
const WBNB_ADDRESS = '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
|
|
8
8
|
const FLAT_FEE = ethers.parseEther('0.0001'); // 合约固定手续费
|
|
@@ -262,7 +262,11 @@ export async function fourPancakeProxyBatchBuyMerkle(params) {
|
|
|
262
262
|
const blockOffset = config.bundleBlockOffset ?? 2;
|
|
263
263
|
const gasPrice = await getOptimizedGasPrice(provider, getGasPriceConfig(config));
|
|
264
264
|
const buyers = privateKeys.map(k => new Wallet(k, provider));
|
|
265
|
-
const
|
|
265
|
+
const originalAmountsWei = buyAmounts.map(a => ethers.parseEther(a)); // BNB 固定 18 位
|
|
266
|
+
// ✅ 利润提取配置
|
|
267
|
+
const extractProfit = shouldExtractProfit(config);
|
|
268
|
+
const { totalProfit, remainingAmounts } = calculateBatchProfit(originalAmountsWei, config);
|
|
269
|
+
const actualAmountsWei = remainingAmounts; // 扣除利润后用于购买的金额
|
|
266
270
|
// 查询 tokenOut decimals
|
|
267
271
|
const tokenDecimals = await getTokenDecimals(tokenAddress, provider);
|
|
268
272
|
// 计算 minOutputAmounts
|
|
@@ -275,39 +279,74 @@ export async function fourPancakeProxyBatchBuyMerkle(params) {
|
|
|
275
279
|
}
|
|
276
280
|
// 判断是否需要发送 BNB
|
|
277
281
|
const needBNB = needSendBNB(routeType, params);
|
|
278
|
-
//
|
|
282
|
+
// 构建交易(使用扣除利润后的金额)
|
|
279
283
|
const proxies = buyers.map(w => new Contract(pancakeProxyAddress, PANCAKE_PROXY_ABI, w));
|
|
280
284
|
let unsignedBuys;
|
|
281
285
|
if (routeType === 'v2') {
|
|
282
286
|
if (!params.v2Path || params.v2Path.length < 2) {
|
|
283
287
|
throw new Error('v2Path is required for V2 routing');
|
|
284
288
|
}
|
|
285
|
-
unsignedBuys = await buildV2Transactions(proxies, buyers,
|
|
289
|
+
unsignedBuys = await buildV2Transactions(proxies, buyers, actualAmountsWei, minOuts, params.v2Path, true, needBNB);
|
|
286
290
|
}
|
|
287
291
|
else if (routeType === 'v3-single') {
|
|
288
292
|
if (!params.v3TokenIn || !params.v3Fee) {
|
|
289
293
|
throw new Error('v3TokenIn and v3Fee are required for V3 single-hop');
|
|
290
294
|
}
|
|
291
|
-
unsignedBuys = await buildV3SingleTransactions(proxies, buyers, params.v3TokenIn, tokenAddress, params.v3Fee,
|
|
295
|
+
unsignedBuys = await buildV3SingleTransactions(proxies, buyers, params.v3TokenIn, tokenAddress, params.v3Fee, actualAmountsWei, minOuts, true, needBNB);
|
|
292
296
|
}
|
|
293
297
|
else if (routeType === 'v3-multi') {
|
|
294
298
|
if (!params.v3LpAddresses || params.v3LpAddresses.length === 0 || !params.v3ExactTokenIn) {
|
|
295
299
|
throw new Error('v3LpAddresses and v3ExactTokenIn are required for V3 multi-hop');
|
|
296
300
|
}
|
|
297
|
-
unsignedBuys = await buildV3MultiHopTransactions(proxies, buyers, params.v3LpAddresses, params.v3ExactTokenIn,
|
|
301
|
+
unsignedBuys = await buildV3MultiHopTransactions(proxies, buyers, params.v3LpAddresses, params.v3ExactTokenIn, actualAmountsWei, minOuts, true, needBNB);
|
|
298
302
|
}
|
|
299
303
|
else {
|
|
300
304
|
throw new Error(`Unsupported routeType: ${routeType}`);
|
|
301
305
|
}
|
|
302
306
|
// ✅ 使用前端传入的 gasLimit,否则使用默认值
|
|
303
307
|
const finalGasLimit = getGasLimit(config);
|
|
304
|
-
//
|
|
305
|
-
const
|
|
306
|
-
const
|
|
308
|
+
// ✅ 直接签名和提交(内联处理以支持利润转账)
|
|
309
|
+
const nonceManager = new NonceManager(provider);
|
|
310
|
+
const nonces = await Promise.all(buyers.map(w => nonceManager.getNextNonce(w)));
|
|
311
|
+
const signedTxs = await Promise.all(unsignedBuys.map((unsigned, i) => buyers[i].signTransaction({
|
|
312
|
+
...unsigned,
|
|
313
|
+
from: buyers[i].address,
|
|
314
|
+
nonce: nonces[i],
|
|
315
|
+
gasLimit: finalGasLimit,
|
|
316
|
+
gasPrice,
|
|
317
|
+
chainId: 56,
|
|
318
|
+
type: getTxType(config),
|
|
319
|
+
value: unsigned.value // ✅ 显式保留 value(BNB 金额 + 手续费)
|
|
320
|
+
})));
|
|
321
|
+
// ✅ 添加利润转账(从第一个钱包转出总利润)
|
|
322
|
+
if (extractProfit && totalProfit > 0n) {
|
|
323
|
+
const profitNonce = await nonceManager.getNextNonce(buyers[0]);
|
|
324
|
+
const profitTx = await buyers[0].signTransaction({
|
|
325
|
+
to: config.profitRecipient,
|
|
326
|
+
value: totalProfit,
|
|
327
|
+
nonce: profitNonce,
|
|
328
|
+
gasPrice,
|
|
329
|
+
gasLimit: 21000n,
|
|
330
|
+
chainId: 56,
|
|
331
|
+
type: getTxType(config)
|
|
332
|
+
});
|
|
333
|
+
signedTxs.push(profitTx);
|
|
334
|
+
}
|
|
335
|
+
// ✅ 清理临时 nonce 缓存
|
|
336
|
+
nonceManager.clearTemp();
|
|
337
|
+
// ✅ 直接返回签名交易(不提交到Merkle)
|
|
307
338
|
return {
|
|
308
|
-
bundleHash:
|
|
309
|
-
status
|
|
310
|
-
|
|
339
|
+
bundleHash: '',
|
|
340
|
+
status: {
|
|
341
|
+
bundleHash: '',
|
|
342
|
+
txHashes: [],
|
|
343
|
+
results: [],
|
|
344
|
+
successCount: 0,
|
|
345
|
+
totalGasUsed: 0n,
|
|
346
|
+
targetBlock: 0
|
|
347
|
+
},
|
|
348
|
+
buyTxs: [],
|
|
349
|
+
signedTransactions: signedTxs
|
|
311
350
|
};
|
|
312
351
|
}
|
|
313
352
|
/**
|
|
@@ -347,40 +386,12 @@ export async function fourPancakeProxyBatchSellMerkle(params) {
|
|
|
347
386
|
console.log(` - 总钱包数: ${sellers.length}`);
|
|
348
387
|
console.log(` - 需要授权: ${needApprovalIndexes.length}`);
|
|
349
388
|
console.log(` - 已有授权: ${sellers.length - needApprovalIndexes.length}\n`);
|
|
350
|
-
// ✅ Step 2:
|
|
389
|
+
// ✅ Step 2: 如果需要授权,抛出错误提示
|
|
390
|
+
// ⚠️ SDK不再处理授权,需要前端先单独授权
|
|
351
391
|
if (needApprovalIndexes.length > 0) {
|
|
352
|
-
console.log('
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
const needApprovalSellers = needApprovalIndexes.map(i => sellers[i]);
|
|
356
|
-
const tokenContractsForApprove = needApprovalSellers.map((w) => new Contract(tokenAddress, ERC20_ABI, w));
|
|
357
|
-
const approveUnsigned = await Promise.all(tokenContractsForApprove.map((c) => c.approve.populateTransaction(pancakeProxyAddress, ethers.MaxUint256)));
|
|
358
|
-
const approveGasLimit = getGasLimit(config, 80000);
|
|
359
|
-
const approveNonces = await Promise.all(needApprovalSellers.map((w) => nonceManager.getNextNonce(w)));
|
|
360
|
-
const signedApproves = await Promise.all(approveUnsigned.map((unsigned, i) => needApprovalSellers[i].signTransaction({
|
|
361
|
-
...unsigned,
|
|
362
|
-
from: needApprovalSellers[i].address,
|
|
363
|
-
nonce: approveNonces[i],
|
|
364
|
-
gasLimit: approveGasLimit,
|
|
365
|
-
gasPrice: gasPrice,
|
|
366
|
-
chainId: 56,
|
|
367
|
-
type: getTxType(config)
|
|
368
|
-
})));
|
|
369
|
-
approveSignedTxs.push(...signedApproves);
|
|
370
|
-
nonceManager.clearTemp();
|
|
371
|
-
// 提交授权 Bundle 并等待确认
|
|
372
|
-
const approveBundleResult = await merkle.sendBundle({
|
|
373
|
-
transactions: approveSignedTxs,
|
|
374
|
-
...getBundleOptions(config, blockOffset)
|
|
375
|
-
});
|
|
376
|
-
console.log(` Bundle Hash: ${approveBundleResult.bundleHash}`);
|
|
377
|
-
console.log('⏳ 等待授权确认...');
|
|
378
|
-
const approveResults = await merkle.waitForBundleConfirmation(approveBundleResult.txHashes, 1, config.waitTimeoutMs ?? 120000);
|
|
379
|
-
const approveSuccessCount = approveResults.filter(r => r.success).length;
|
|
380
|
-
console.log(`✅ 授权完成: ${approveSuccessCount}/${needApprovalIndexes.length}\n`);
|
|
381
|
-
if (approveSuccessCount !== needApprovalIndexes.length) {
|
|
382
|
-
throw new Error(`授权失败: 只有 ${approveSuccessCount}/${needApprovalIndexes.length} 笔授权成功`);
|
|
383
|
-
}
|
|
392
|
+
console.log('⚠️ 警告:检测到 ${needApprovalIndexes.length} 个钱包需要授权');
|
|
393
|
+
console.log(' 请先使用这些钱包完成授权后再调用卖出方法');
|
|
394
|
+
throw new Error(`需要授权: ${needApprovalIndexes.length} 个钱包尚未授权。请先完成授权后再卖出。`);
|
|
384
395
|
}
|
|
385
396
|
// ✅ Step 3: 批量卖出
|
|
386
397
|
console.log('💰 提交卖出交易...');
|
|
@@ -478,36 +489,57 @@ export async function fourPancakeProxyBatchSellMerkle(params) {
|
|
|
478
489
|
}
|
|
479
490
|
// ✅ 使用前端传入的 gasLimit,否则使用默认值
|
|
480
491
|
const finalGasLimit = getGasLimit(config);
|
|
481
|
-
//
|
|
482
|
-
console.log('🚀
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
if (
|
|
503
|
-
|
|
492
|
+
// ✅ 直接签名和提交(内联处理以支持利润转账)
|
|
493
|
+
console.log('🚀 签名卖出交易...\n');
|
|
494
|
+
const nonceManager = new NonceManager(provider);
|
|
495
|
+
const nonces = await Promise.all(sellers.map(w => nonceManager.getNextNonce(w)));
|
|
496
|
+
const signedTxs = await Promise.all(unsignedSells.map((unsigned, i) => sellers[i].signTransaction({
|
|
497
|
+
...unsigned,
|
|
498
|
+
from: sellers[i].address,
|
|
499
|
+
nonce: nonces[i],
|
|
500
|
+
gasLimit: finalGasLimit,
|
|
501
|
+
gasPrice,
|
|
502
|
+
chainId: 56,
|
|
503
|
+
type: getTxType(config),
|
|
504
|
+
value: unsigned.value // ✅ 显式保留 value(手续费)
|
|
505
|
+
})));
|
|
506
|
+
// ✅ 基于 minFunds 为每个钱包添加利润转账(如果设置且 > 0)
|
|
507
|
+
const extractProfit = shouldExtractProfit(config);
|
|
508
|
+
if (extractProfit && minOuts.length > 0) {
|
|
509
|
+
// 为每个有 minOut > 0 的钱包添加利润转账
|
|
510
|
+
for (let i = 0; i < sellers.length; i++) {
|
|
511
|
+
if (minOuts[i] > 0n) {
|
|
512
|
+
const { profit } = calculateProfit(minOuts[i], config);
|
|
513
|
+
if (profit > 0n) {
|
|
514
|
+
const profitNonce = await nonceManager.getNextNonce(sellers[i]);
|
|
515
|
+
const profitTx = await sellers[i].signTransaction({
|
|
516
|
+
to: config.profitRecipient,
|
|
517
|
+
value: profit,
|
|
518
|
+
nonce: profitNonce,
|
|
519
|
+
gasPrice,
|
|
520
|
+
gasLimit: 21000n,
|
|
521
|
+
chainId: 56,
|
|
522
|
+
type: getTxType(config)
|
|
523
|
+
});
|
|
524
|
+
signedTxs.push(profitTx);
|
|
504
525
|
}
|
|
505
|
-
}
|
|
526
|
+
}
|
|
506
527
|
}
|
|
507
528
|
}
|
|
529
|
+
// ✅ 清理临时 nonce 缓存
|
|
530
|
+
nonceManager.clearTemp();
|
|
531
|
+
// ✅ 直接返回签名交易(不提交到Merkle)
|
|
508
532
|
return {
|
|
509
|
-
bundleHash:
|
|
510
|
-
status
|
|
511
|
-
|
|
533
|
+
bundleHash: '',
|
|
534
|
+
status: {
|
|
535
|
+
bundleHash: '',
|
|
536
|
+
txHashes: [],
|
|
537
|
+
results: [],
|
|
538
|
+
successCount: 0,
|
|
539
|
+
totalGasUsed: 0n,
|
|
540
|
+
targetBlock: 0
|
|
541
|
+
},
|
|
542
|
+
sellTxs: [],
|
|
543
|
+
signedTransactions: signedTxs
|
|
512
544
|
};
|
|
513
545
|
}
|