four-flap-meme-sdk 1.2.1 → 1.2.3
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.
|
@@ -48,23 +48,7 @@ export async function approveFourTokenManagerBatch(params) {
|
|
|
48
48
|
const APPROVAL_THRESHOLD = ethers.MaxUint256 / 2n;
|
|
49
49
|
const needApproval = wallets.filter((_, i) => allowances[i] < APPROVAL_THRESHOLD);
|
|
50
50
|
const needApprovalAmounts = amountsBigInt.filter((_, i) => allowances[i] < APPROVAL_THRESHOLD);
|
|
51
|
-
console.log(`🔍 TokenManager 授权检查:`);
|
|
52
|
-
console.log(` - 总钱包数: ${wallets.length}`);
|
|
53
|
-
console.log(` - 需要授权: ${needApproval.length}`);
|
|
54
|
-
console.log(` - 已有授权: ${wallets.length - needApproval.length}`);
|
|
55
|
-
// ✅ 显示每个钱包的授权状态
|
|
56
|
-
for (let i = 0; i < wallets.length; i++) {
|
|
57
|
-
const current = allowances[i];
|
|
58
|
-
const requested = amountsBigInt[i];
|
|
59
|
-
if (current < APPROVAL_THRESHOLD) {
|
|
60
|
-
console.log(` ⚠️ 钱包 ${i}: 当前授权 ${ethers.formatUnits(current, decimals)} → 需要重新授权为 ${requested === ethers.MaxUint256 ? 'MAX' : ethers.formatUnits(requested, decimals)}`);
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
console.log(` ✅ 钱包 ${i}: 授权充足 (${current === ethers.MaxUint256 ? 'MAX' : ethers.formatUnits(current, decimals)})`);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
51
|
if (needApproval.length === 0) {
|
|
67
|
-
console.log('✅ 所有钱包已授权,无需再次授权。');
|
|
68
52
|
return {
|
|
69
53
|
success: true,
|
|
70
54
|
approvedCount: 0,
|
|
@@ -5,6 +5,7 @@ import { ADDRESSES } from '../../utils/constants.js';
|
|
|
5
5
|
import { FourClient, buildLoginMessage } from '../../clients/four.js';
|
|
6
6
|
import { getErrorMessage, getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit } from './config.js';
|
|
7
7
|
import { batchCheckAllowances } from '../../utils/erc20.js';
|
|
8
|
+
import { trySell } from '../tm.js';
|
|
8
9
|
// ==================== TokenManager2 ABI(仅需要的方法)====================
|
|
9
10
|
const TM2_ABI = [
|
|
10
11
|
'function createToken(bytes args, bytes signature) payable',
|
|
@@ -322,24 +323,29 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
322
323
|
const gasPrice = await getOptimizedGasPrice(provider, getGasPriceConfig(config));
|
|
323
324
|
const sellers = privateKeys.map((k) => new Wallet(k, provider));
|
|
324
325
|
const amountsWei = sellAmounts.map((a) => ethers.parseUnits(a, 18));
|
|
325
|
-
// ⚠️ Four.meme 内盘代币通过 TokenManager bonding curve
|
|
326
|
+
// ⚠️ Four.meme 内盘代币通过 TokenManager bonding curve 定价
|
|
327
|
+
// ✅ 优先使用用户提供的 minOutputAmounts,否则自动调用 trySell 获取
|
|
326
328
|
let minOuts;
|
|
327
329
|
let quotedOutputs = [];
|
|
328
330
|
if (minOutputAmounts && minOutputAmounts.length === sellers.length) {
|
|
329
331
|
// 用户提供了 minOutputAmounts,用于滑点保护和利润计算
|
|
330
332
|
minOuts = minOutputAmounts.map(m => typeof m === 'string' ? ethers.parseEther(m) : m);
|
|
331
|
-
// 反推实际收益(假设 minOut 是 95% 的收益)
|
|
332
333
|
quotedOutputs = minOuts.map(m => m * 100n / 95n);
|
|
333
334
|
}
|
|
334
335
|
else {
|
|
335
|
-
//
|
|
336
|
-
|
|
337
|
-
quotedOutputs =
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
336
|
+
// ✅ 自动调用 trySell 获取每个钱包的预期收益
|
|
337
|
+
const rpcUrl = config.customRpcUrl;
|
|
338
|
+
quotedOutputs = await Promise.all(amountsWei.map(async (amount, i) => {
|
|
339
|
+
try {
|
|
340
|
+
const result = await trySell('BSC', rpcUrl, tokenAddress, amount);
|
|
341
|
+
return result.funds;
|
|
342
|
+
}
|
|
343
|
+
catch (error) {
|
|
344
|
+
return 0n;
|
|
345
|
+
}
|
|
346
|
+
}));
|
|
347
|
+
// minOuts 设为预期收益的 95%(作为滑点保护)
|
|
348
|
+
minOuts = quotedOutputs.map(q => q * 95n / 100n);
|
|
343
349
|
}
|
|
344
350
|
// ✅ Step 1: 检查代币余额和授权状态
|
|
345
351
|
const ERC20_ABI = [
|
|
@@ -4,6 +4,7 @@ import { NonceManager, getOptimizedGasPrice } from '../../utils/bundle-helpers.j
|
|
|
4
4
|
import { ADDRESSES } from '../../utils/constants.js';
|
|
5
5
|
import { getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, calculateBatchProfit } from './config.js';
|
|
6
6
|
import { batchCheckAllowances } from '../../utils/erc20.js';
|
|
7
|
+
import { trySell } from '../tm.js';
|
|
7
8
|
// ==================== TokenManager2 ABI(仅需要的方法)====================
|
|
8
9
|
const TM2_ABI = [
|
|
9
10
|
'function buyTokenAMAP(uint256 origin, address token, address to, uint256 funds, uint256 minAmount) payable',
|
|
@@ -229,16 +230,6 @@ export async function fourBatchPrivateSellMerkle(params) {
|
|
|
229
230
|
if (privateKeys.length !== amounts.length) {
|
|
230
231
|
throw new Error('privateKeys and amounts length mismatch');
|
|
231
232
|
}
|
|
232
|
-
// ⚠️ 友好提示:利润提取需要 minFundsEach
|
|
233
|
-
if (!minFundsEach && shouldExtractProfit(config)) {
|
|
234
|
-
console.log('\n⚠️ 重要提示:未传入 minFundsEach 参数,将无法提取利润!');
|
|
235
|
-
console.log(' Four.meme 内盘代币无法自动报价,需要手动估算预期收益');
|
|
236
|
-
console.log(' 建议步骤:');
|
|
237
|
-
console.log(' 1. 在 four.meme 网站查看卖出预期收益');
|
|
238
|
-
console.log(' 2. 传入 minFundsEach 参数');
|
|
239
|
-
console.log(' 示例: minFundsEach: 0.1 (预计每个钱包收到 0.1 BNB)');
|
|
240
|
-
console.log(' 如果只是测试卖出,可以忽略此提示\n');
|
|
241
|
-
}
|
|
242
233
|
const merkle = new MerkleClient({
|
|
243
234
|
apiKey: config.apiKey,
|
|
244
235
|
chainId: 56,
|
|
@@ -248,12 +239,35 @@ export async function fourBatchPrivateSellMerkle(params) {
|
|
|
248
239
|
const tmAddr = ADDRESSES.BSC.TokenManagerOriginal;
|
|
249
240
|
const gasPrice = await getOptimizedGasPrice(provider, getGasPriceConfig(config));
|
|
250
241
|
const gasMultiplier = config.gasLimitMultiplier ?? 1.0;
|
|
251
|
-
const minOut = minFundsEach ?? 0n;
|
|
252
242
|
const sellGasLimit = BigInt(Math.ceil(800000 * gasMultiplier));
|
|
253
243
|
const signedTxs = [];
|
|
254
244
|
const nonceManager = new NonceManager(provider);
|
|
255
245
|
const wallets = privateKeys.map((k) => new Wallet(k, provider));
|
|
256
246
|
const amountsWei = amounts.map((a) => ethers.parseUnits(a, 18));
|
|
247
|
+
// ✅ 自动获取每个钱包的预期收益(用于利润计算和滑点保护)
|
|
248
|
+
let quotedOutputs;
|
|
249
|
+
let minOuts;
|
|
250
|
+
if (minFundsEach !== undefined) {
|
|
251
|
+
// 用户提供了 minFundsEach,所有钱包使用相同的 minOut
|
|
252
|
+
const minOutWei = typeof minFundsEach === 'string' ? ethers.parseEther(minFundsEach) : minFundsEach;
|
|
253
|
+
minOuts = new Array(wallets.length).fill(minOutWei);
|
|
254
|
+
quotedOutputs = minOuts.map(m => m * 100n / 95n);
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
// ✅ 自动调用 trySell 获取每个钱包的预期收益
|
|
258
|
+
const rpcUrl = config.customRpcUrl;
|
|
259
|
+
quotedOutputs = await Promise.all(amountsWei.map(async (amount, i) => {
|
|
260
|
+
try {
|
|
261
|
+
const result = await trySell('BSC', rpcUrl, tokenAddress, amount);
|
|
262
|
+
return result.funds;
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
return 0n;
|
|
266
|
+
}
|
|
267
|
+
}));
|
|
268
|
+
// minOuts 设为预期收益的 95%(作为滑点保护)
|
|
269
|
+
minOuts = quotedOutputs.map(q => q * 95n / 100n);
|
|
270
|
+
}
|
|
257
271
|
// ✅ Step 0: 检查代币余额
|
|
258
272
|
const tokenContract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
|
|
259
273
|
const balances = await Promise.all(wallets.map(w => tokenContract.balanceOf(w.address)));
|
|
@@ -288,7 +302,7 @@ export async function fourBatchPrivateSellMerkle(params) {
|
|
|
288
302
|
}
|
|
289
303
|
// ✅ Step 3: 构建卖出交易
|
|
290
304
|
const tm2Contracts = wallets.map((w) => new ethers.Contract(tmAddr, TM2_ABI, w));
|
|
291
|
-
const sellUnsigned = await Promise.all(tm2Contracts.map((c, i) => c.sellToken.populateTransaction(0n, tokenAddress, amountsWei[i],
|
|
305
|
+
const sellUnsigned = await Promise.all(tm2Contracts.map((c, i) => c.sellToken.populateTransaction(0n, tokenAddress, amountsWei[i], minOuts[i])));
|
|
292
306
|
const sellGasLimits = new Array(wallets.length).fill(sellGasLimit);
|
|
293
307
|
const sellNonces = await Promise.all(wallets.map((w) => nonceManager.getNextNonce(w)));
|
|
294
308
|
const signedSells = await Promise.all(sellUnsigned.map((unsigned, i) => wallets[i].signTransaction({
|
|
@@ -301,22 +315,26 @@ export async function fourBatchPrivateSellMerkle(params) {
|
|
|
301
315
|
type: getTxType(config)
|
|
302
316
|
})));
|
|
303
317
|
signedTxs.push(...signedSells);
|
|
304
|
-
// ✅
|
|
318
|
+
// ✅ 提取利润(基于每个钱包的预期收益)
|
|
305
319
|
const extractProfit = shouldExtractProfit(config);
|
|
306
|
-
if (extractProfit
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
+
if (extractProfit) {
|
|
321
|
+
for (let i = 0; i < wallets.length; i++) {
|
|
322
|
+
if (quotedOutputs[i] > 0n) {
|
|
323
|
+
const { profit } = calculateProfit(quotedOutputs[i], config);
|
|
324
|
+
if (profit > 0n) {
|
|
325
|
+
const profitNonce = await nonceManager.getNextNonce(wallets[i]);
|
|
326
|
+
const profitTx = await wallets[i].signTransaction({
|
|
327
|
+
to: config.profitRecipient,
|
|
328
|
+
value: profit,
|
|
329
|
+
nonce: profitNonce,
|
|
330
|
+
gasPrice,
|
|
331
|
+
gasLimit: 21000n,
|
|
332
|
+
chainId: 56,
|
|
333
|
+
type: getTxType(config)
|
|
334
|
+
});
|
|
335
|
+
signedTxs.push(profitTx);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
320
338
|
}
|
|
321
339
|
}
|
|
322
340
|
nonceManager.clearTemp();
|
|
@@ -15,6 +15,16 @@ const PANCAKE_PROXY_ABI = [
|
|
|
15
15
|
'function swapV3Single(address tokenIn, address tokenOut, uint24 fee, uint256 amountIn, uint256 amountOutMin, address to) external payable returns (uint256 amountOut)',
|
|
16
16
|
'function swapV3MultiHop(address[] calldata lpAddresses, address exactTokenIn, uint256 amountIn, uint256 amountOutMin, address to) external payable returns (uint256 amountOut)'
|
|
17
17
|
];
|
|
18
|
+
// PancakeSwap V2 Router ABI(用于报价)
|
|
19
|
+
const PANCAKE_V2_ROUTER_ABI = [
|
|
20
|
+
'function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)'
|
|
21
|
+
];
|
|
22
|
+
// PancakeSwap V3 QuoterV2 ABI(用于报价)
|
|
23
|
+
const PANCAKE_V3_QUOTER_ABI = [
|
|
24
|
+
'function quoteExactInputSingle((address tokenIn, address tokenOut, uint256 amountIn, uint24 fee, uint160 sqrtPriceLimitX96)) external returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate)'
|
|
25
|
+
];
|
|
26
|
+
const PANCAKE_V2_ROUTER_ADDRESS = '0x10ED43C718714eb63d5aA57B78B54704E256024E';
|
|
27
|
+
const PANCAKE_V3_QUOTER_ADDRESS = '0xB048Bbc1Ee6b733FFfCFb9e9CeF7375518e25997';
|
|
18
28
|
// ERC20 ABI
|
|
19
29
|
const ERC20_ABI = [
|
|
20
30
|
'function approve(address spender, uint256 amount) external returns (bool)',
|
|
@@ -400,8 +410,6 @@ export async function pancakeProxyBatchSellMerkle(params) {
|
|
|
400
410
|
// 查询 tokenIn decimals
|
|
401
411
|
const tokenDecimals = await getTokenDecimals(tokenAddress, provider);
|
|
402
412
|
const amountsWei = sellAmounts.map(a => ethers.parseUnits(a, tokenDecimals));
|
|
403
|
-
// ✅ Step 1: 使用 Multicall3 批量检查授权状态
|
|
404
|
-
console.log('🔍 使用 Multicall3 批量检查授权状态...');
|
|
405
413
|
const allowances = await batchCheckAllowances(provider, tokenAddress, sellers.map(w => w.address), pancakeProxyAddress);
|
|
406
414
|
// 找出需要授权的钱包索引
|
|
407
415
|
const needApprovalIndexes = [];
|
|
@@ -411,9 +419,6 @@ export async function pancakeProxyBatchSellMerkle(params) {
|
|
|
411
419
|
needApprovalIndexes.push(i);
|
|
412
420
|
}
|
|
413
421
|
}
|
|
414
|
-
console.log(` - 总钱包数: ${sellers.length}`);
|
|
415
|
-
console.log(` - 需要授权: ${needApprovalIndexes.length}`);
|
|
416
|
-
console.log(` - 已有授权: ${sellers.length - needApprovalIndexes.length}\n`);
|
|
417
422
|
// ✅ Step 2: 如果需要授权,抛出错误提示
|
|
418
423
|
// ⚠️ SDK不再处理授权,需要前端先单独授权
|
|
419
424
|
if (needApprovalIndexes.length > 0) {
|
|
@@ -421,65 +426,53 @@ export async function pancakeProxyBatchSellMerkle(params) {
|
|
|
421
426
|
console.log(' 请先使用这些钱包完成授权后再调用卖出方法');
|
|
422
427
|
throw new Error(`需要授权: ${needApprovalIndexes.length} 个钱包尚未授权。请先完成授权后再卖出。`);
|
|
423
428
|
}
|
|
424
|
-
// ✅
|
|
425
|
-
console.log('💰 提交卖出交易...');
|
|
426
|
-
// 🔍 调试信息 - 详细参数
|
|
427
|
-
console.log('📊 卖出详情:');
|
|
428
|
-
console.log(` [基本信息]`);
|
|
429
|
-
console.log(` - 链: ${chain}`);
|
|
430
|
-
console.log(` - 代币地址: ${tokenAddress}`);
|
|
431
|
-
console.log(` - PancakeProxy: ${pancakeProxyAddress}`);
|
|
432
|
-
console.log(` - 卖家数量: ${sellers.length}`);
|
|
433
|
-
console.log(` - 路由类型: ${routeType}`);
|
|
434
|
-
console.log(`\n [卖家信息]`);
|
|
435
|
-
sellers.forEach((seller, i) => {
|
|
436
|
-
console.log(` - 钱包 ${i + 1}: ${seller.address}`);
|
|
437
|
-
});
|
|
438
|
-
console.log(`\n [卖出金额]`);
|
|
439
|
-
sellAmounts.forEach((amount, i) => {
|
|
440
|
-
console.log(` - 钱包 ${i + 1}: ${amount} TOKEN (${amountsWei[i].toString()} Wei)`);
|
|
441
|
-
});
|
|
442
|
-
// 计算 minOutputAmounts(通常是 BNB/WBNB,18 位)
|
|
429
|
+
// ✅ 自动获取报价或使用用户提供的 minOutputAmounts
|
|
443
430
|
let minOuts;
|
|
431
|
+
let quotedOutputs;
|
|
444
432
|
if (params.minOutputAmounts && params.minOutputAmounts.length === sellers.length) {
|
|
433
|
+
// 用户提供了 minOutputAmounts
|
|
445
434
|
minOuts = params.minOutputAmounts.map(m => typeof m === 'string' ? ethers.parseEther(m) : m);
|
|
446
|
-
|
|
447
|
-
params.minOutputAmounts.forEach((min, i) => {
|
|
448
|
-
console.log(` - 钱包 ${i + 1}: ${min} BNB`);
|
|
449
|
-
});
|
|
435
|
+
quotedOutputs = minOuts.map(m => m * 100n / 95n); // 反推预期收益
|
|
450
436
|
}
|
|
451
437
|
else {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
438
|
+
// ✅ 自动调用 PancakeSwap 获取报价
|
|
439
|
+
quotedOutputs = await Promise.all(amountsWei.map(async (amount) => {
|
|
440
|
+
try {
|
|
441
|
+
if (routeType === 'v2') {
|
|
442
|
+
const v2Router = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, provider);
|
|
443
|
+
const amounts = await v2Router.getAmountsOut(amount, params.v2Path);
|
|
444
|
+
return amounts[amounts.length - 1];
|
|
445
|
+
}
|
|
446
|
+
else if (routeType === 'v3-single') {
|
|
447
|
+
const quoter = new Contract(PANCAKE_V3_QUOTER_ADDRESS, PANCAKE_V3_QUOTER_ABI, provider);
|
|
448
|
+
const result = await quoter.quoteExactInputSingle.staticCall({
|
|
449
|
+
tokenIn: tokenAddress,
|
|
450
|
+
tokenOut: params.v3TokenOut,
|
|
451
|
+
amountIn: amount,
|
|
452
|
+
fee: params.v3Fee,
|
|
453
|
+
sqrtPriceLimitX96: 0
|
|
454
|
+
});
|
|
455
|
+
return result[0];
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
// v3-multi 使用 v2 备选
|
|
459
|
+
if (params.v2Path && params.v2Path.length >= 2) {
|
|
460
|
+
const v2Router = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, provider);
|
|
461
|
+
const amounts = await v2Router.getAmountsOut(amount, params.v2Path);
|
|
462
|
+
return amounts[amounts.length - 1];
|
|
463
|
+
}
|
|
464
|
+
return 0n;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
catch (error) {
|
|
468
|
+
return 0n;
|
|
469
|
+
}
|
|
470
|
+
}));
|
|
471
|
+
// minOuts 设为预期收益的 95%(滑点保护)
|
|
472
|
+
minOuts = quotedOutputs.map(q => q * 95n / 100n);
|
|
466
473
|
}
|
|
467
474
|
// 卖出不需要发送 BNB,只需要 flatFee
|
|
468
475
|
const needBNB = false;
|
|
469
|
-
console.log(`\n [交易费用]`);
|
|
470
|
-
console.log(` - 需要发送 BNB: ${needBNB ? '是' : '否'}`);
|
|
471
|
-
console.log(` - 每笔手续费: 0.0001 BNB`);
|
|
472
|
-
console.log(` - 总手续费: ${ethers.formatEther(BigInt(sellers.length) * ethers.parseEther('0.0001'))} BNB`);
|
|
473
|
-
console.log(`\n [Gas 配置]`);
|
|
474
|
-
console.log(` - Gas Price: ${ethers.formatUnits(gasPrice, 'gwei')} Gwei`);
|
|
475
|
-
console.log(` - Gas Limit: ${config.gasLimit || 'Auto (800000 * multiplier)'}`);
|
|
476
|
-
if (config.gasLimitMultiplier) {
|
|
477
|
-
console.log(` - Gas Multiplier: ${config.gasLimitMultiplier}x`);
|
|
478
|
-
}
|
|
479
|
-
console.log(`\n [Bundle 配置]`);
|
|
480
|
-
console.log(` - Block Offset: ${blockOffset}`);
|
|
481
|
-
console.log(` - Wait Confirmation: ${config.waitForConfirmation ?? false}`);
|
|
482
|
-
console.log(` - Timeout: ${(config.waitTimeoutMs ?? 120000) / 1000}s\n`);
|
|
483
476
|
// 构建交易
|
|
484
477
|
const proxies = sellers.map(w => new Contract(pancakeProxyAddress, PANCAKE_PROXY_ABI, w));
|
|
485
478
|
let unsignedSells;
|
|
@@ -505,20 +498,15 @@ export async function pancakeProxyBatchSellMerkle(params) {
|
|
|
505
498
|
throw new Error(`Unsupported routeType: ${routeType}`);
|
|
506
499
|
}
|
|
507
500
|
// ✅ Step 4: 验证钱包余额(确保有足够 BNB 支付手续费)
|
|
508
|
-
console.log('🔍 验证钱包余额...');
|
|
509
501
|
const balances = await Promise.all(sellers.map(w => provider.getBalance(w.address)));
|
|
510
502
|
const minRequiredBNB = ethers.parseEther('0.001'); // 至少需要 0.001 BNB(包括 gas 和手续费)
|
|
511
503
|
const insufficientBalances = balances.filter(b => b < minRequiredBNB);
|
|
512
504
|
if (insufficientBalances.length > 0) {
|
|
513
|
-
|
|
514
|
-
balances.forEach((b, i) => {
|
|
515
|
-
console.log(` 钱包 ${i + 1}: ${ethers.formatEther(b)} BNB`);
|
|
516
|
-
});
|
|
505
|
+
throw new Error(`${insufficientBalances.length} 个钱包 BNB 余额不足 0.001 BNB`);
|
|
517
506
|
}
|
|
518
507
|
// ✅ 使用前端传入的 gasLimit,否则使用默认值
|
|
519
508
|
const finalGasLimit = getGasLimit(config);
|
|
520
509
|
// ✅ 直接签名和提交(内联处理以支持利润转账)
|
|
521
|
-
console.log('🚀 签名卖出交易...\n');
|
|
522
510
|
const nonceManager = new NonceManager(provider);
|
|
523
511
|
const nonces = await Promise.all(sellers.map(w => nonceManager.getNextNonce(w)));
|
|
524
512
|
const signedTxs = await Promise.all(unsignedSells.map((unsigned, i) => sellers[i].signTransaction({
|
|
@@ -531,13 +519,12 @@ export async function pancakeProxyBatchSellMerkle(params) {
|
|
|
531
519
|
type: getTxType(config),
|
|
532
520
|
value: unsigned.value // ✅ 显式保留 value(手续费)
|
|
533
521
|
})));
|
|
534
|
-
// ✅
|
|
522
|
+
// ✅ 基于预期收益为每个钱包添加利润转账
|
|
535
523
|
const extractProfit = shouldExtractProfit(config);
|
|
536
|
-
if (extractProfit &&
|
|
537
|
-
// 为每个钱包添加利润转账(如果 minOut > 0)
|
|
524
|
+
if (extractProfit && quotedOutputs.length > 0) {
|
|
538
525
|
for (let i = 0; i < sellers.length; i++) {
|
|
539
|
-
if (
|
|
540
|
-
const { profit } = calculateProfit(
|
|
526
|
+
if (quotedOutputs[i] > 0n) {
|
|
527
|
+
const { profit } = calculateProfit(quotedOutputs[i], config);
|
|
541
528
|
if (profit > 0n) {
|
|
542
529
|
const profitNonce = await nonceManager.getNextNonce(sellers[i]);
|
|
543
530
|
const profitTx = await sellers[i].signTransaction({
|