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.
@@ -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, getBundleOptions, getGasPriceConfig, shouldExtractProfit, calculateProfit } from './config.js';
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
- // ✅ 如果配置了 skipSubmit,直接返回签名交易
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: bundleResult.bundleHash,
305
- tokenAddress: createdTokenAddress,
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
- // ✅ 如果配置了 skipSubmit,直接返回签名交易
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
- return { bundleHash: bundleResult.bundleHash, status, buyTxs: signedTxs };
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
- const nonceManager = new NonceManager(provider);
490
- const approveSignedTxs = [];
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
- const bundleResult = await merkle.sendBundle({
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
- return { bundleHash: bundleResult.bundleHash, status, sellTxs: signedTxs };
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 amountsWei = buyAmounts.map(a => ethers.parseEther(a)); // BNB 固定 18 位
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, amountsWei, minOuts, params.v2Path, true, needBNB);
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, amountsWei, minOuts, true, needBNB);
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, amountsWei, minOuts, true, needBNB);
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 bundleResult = await signAndSendBundle(merkle, buyers, unsignedBuys, gasPrice, finalGasLimit, 56, config, blockOffset);
306
- const status = await waitForBundleResult(merkle, bundleResult, config.waitForConfirmation ?? false, config.waitTimeoutMs ?? 120000);
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: bundleResult.bundleHash,
309
- status,
310
- buyTxs: []
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
- const nonceManager = new NonceManager(provider);
354
- const approveSignedTxs = [];
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('🚀 签名并提交卖出 Bundle...\n');
483
- let bundleResult;
484
- try {
485
- bundleResult = await signAndSendBundle(merkle, sellers, unsignedSells, gasPrice, finalGasLimit, 56, config, blockOffset);
486
- console.log(` ✅ Bundle 已提交: ${bundleResult.bundleHash}`);
487
- }
488
- catch (error) {
489
- console.error('❌ 提交 Bundle 失败:', error.message);
490
- throw new Error(`卖出 Bundle 提交失败: ${error.message}`);
491
- }
492
- const status = await waitForBundleResult(merkle, bundleResult, config.waitForConfirmation ?? false, config.waitTimeoutMs ?? 120000);
493
- if (status.successCount === sellers.length) {
494
- console.log(`✅ 卖出完成: ${status.successCount}/${sellers.length} 全部成功\n`);
495
- }
496
- else {
497
- console.log(`⚠️ 卖出完成: ${status.successCount}/${sellers.length} 成功`);
498
- console.log(` 失败数量: ${sellers.length - status.successCount}\n`);
499
- // 打印失败详情
500
- if (status.results && status.results.length > 0) {
501
- status.results.forEach((result, i) => {
502
- if (!result.success && result.error) {
503
- console.log(` 交易 ${i + 1} 失败: ${result.error}`);
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: bundleResult.bundleHash,
510
- status,
511
- sellTxs: []
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
  }