four-flap-meme-sdk 1.3.69 → 1.3.70
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/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/pancake/bundle-swap.d.ts +39 -0
- package/dist/pancake/bundle-swap.js +214 -38
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -37,7 +37,7 @@ export { inspectTokenLP, getFactoryFromRouter, registerDYORSwap, registerDex, ge
|
|
|
37
37
|
export { disperseWithBundle, sweepWithBundle, type DisperseSignParams, type SweepSignParams, type SignedTransactionsResult, type DisperseParams, type SweepParams, type BundleSubmitResult } from './utils/airdrop-sweep.js';
|
|
38
38
|
export { fourBundleSwapMerkle, type FourSwapSignConfig, type FourSwapConfig, type FourBundleSwapSignParams, type FourBundleSwapParams, type FourSwapResult } from './contracts/tm-bundle-merkle/swap.js';
|
|
39
39
|
export { flapBundleSwapMerkle, type FlapSwapSignConfig, type FlapSwapConfig, type FlapBundleSwapSignParams, type FlapBundleSwapParams, type FlapSwapResult } from './flap/portal-bundle-merkle/swap.js';
|
|
40
|
-
export { pancakeBundleSwapMerkle, type PancakeSwapSignConfig, type PancakeBundleSwapSignParams, type PancakeSwapConfig, type PancakeBundleSwapParams, type PancakeSwapResult, type SwapRouteType, type V2RouteParams, type V3SingleRouteParams, type V3MultiRouteParams, type RouteParams } from './pancake/bundle-swap.js';
|
|
40
|
+
export { pancakeBundleSwapMerkle, pancakeBatchSwapMerkle, type PancakeSwapSignConfig, type PancakeBundleSwapSignParams, type PancakeSwapConfig, type PancakeBundleSwapParams, type PancakeSwapResult, type PancakeBatchSwapSignParams, type PancakeBatchSwapResult, type SwapRouteType, type V2RouteParams, type V3SingleRouteParams, type V3MultiRouteParams, type RouteParams } from './pancake/bundle-swap.js';
|
|
41
41
|
export { fourBundleBuyFirstMerkle, type FourBuyFirstConfig, type FourBundleBuyFirstParams, type FourBuyFirstSignConfig, type FourBundleBuyFirstSignParams, type FourBuyFirstResult } from './contracts/tm-bundle-merkle/swap-buy-first.js';
|
|
42
42
|
export { flapBundleBuyFirstMerkle, type FlapBuyFirstSignConfig, type FlapBuyFirstConfig, type FlapBundleBuyFirstSignParams, type FlapBundleBuyFirstParams, type FlapBuyFirstResult } from './flap/portal-bundle-merkle/swap-buy-first.js';
|
|
43
43
|
export { pancakeBundleBuyFirstMerkle, type PancakeBuyFirstSignConfig, type PancakeBuyFirstConfig, type PancakeBundleBuyFirstSignParams, type PancakeBundleBuyFirstParams, type PancakeBuyFirstResult } from './pancake/bundle-buy-first.js';
|
package/dist/index.js
CHANGED
|
@@ -55,7 +55,7 @@ export { fourBundleSwapMerkle } from './contracts/tm-bundle-merkle/swap.js';
|
|
|
55
55
|
// Flap内盘换手
|
|
56
56
|
export { flapBundleSwapMerkle } from './flap/portal-bundle-merkle/swap.js';
|
|
57
57
|
// PancakeSwap V2/V3通用换手
|
|
58
|
-
export { pancakeBundleSwapMerkle } from './pancake/bundle-swap.js';
|
|
58
|
+
export { pancakeBundleSwapMerkle, pancakeBatchSwapMerkle } from './pancake/bundle-swap.js';
|
|
59
59
|
// 先买后卖(Buy-First)入口
|
|
60
60
|
export { fourBundleBuyFirstMerkle } from './contracts/tm-bundle-merkle/swap-buy-first.js';
|
|
61
61
|
export { flapBundleBuyFirstMerkle } from './flap/portal-bundle-merkle/swap-buy-first.js';
|
|
@@ -78,3 +78,42 @@ export type PancakeSwapResult = {
|
|
|
78
78
|
* ✅ 支持 quoteToken:传入 USDT 等地址时,卖出得到该代币,买入使用该代币
|
|
79
79
|
*/
|
|
80
80
|
export declare function pancakeBundleSwapMerkle(params: PancakeBundleSwapSignParams): Promise<PancakeSwapResult>;
|
|
81
|
+
/**
|
|
82
|
+
* 批量换手参数(一卖多买)
|
|
83
|
+
*/
|
|
84
|
+
export interface PancakeBatchSwapSignParams {
|
|
85
|
+
sellerPrivateKey: string;
|
|
86
|
+
sellAmount?: string;
|
|
87
|
+
sellPercentage?: number;
|
|
88
|
+
buyerPrivateKeys: string[];
|
|
89
|
+
buyerAmounts?: string[];
|
|
90
|
+
tokenAddress: string;
|
|
91
|
+
routeParams: RouteParams;
|
|
92
|
+
slippageTolerance?: number;
|
|
93
|
+
config: PancakeSwapSignConfig;
|
|
94
|
+
quoteToken?: string;
|
|
95
|
+
quoteTokenDecimals?: number;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* 批量换手结果
|
|
99
|
+
*/
|
|
100
|
+
export interface PancakeBatchSwapResult {
|
|
101
|
+
signedTransactions: string[];
|
|
102
|
+
metadata?: {
|
|
103
|
+
sellerAddress: string;
|
|
104
|
+
buyerAddresses: string[];
|
|
105
|
+
sellAmount: string;
|
|
106
|
+
buyAmounts: string[];
|
|
107
|
+
hasApproval?: boolean;
|
|
108
|
+
profitAmount?: string;
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* PancakeSwap 批量换手(一卖多买)
|
|
113
|
+
*
|
|
114
|
+
* 功能:主钱包一次卖出 → 多个子钱包同时买入 → 在同一个区块中完成
|
|
115
|
+
* 限制:最多 24 个买方(服务器限制 25 笔交易,包含 1 笔利润交易)
|
|
116
|
+
*
|
|
117
|
+
* 交易顺序:[授权(可选)] → [卖出] → [买入1, 买入2, ..., 买入N] → [利润]
|
|
118
|
+
*/
|
|
119
|
+
export declare function pancakeBatchSwapMerkle(params: PancakeBatchSwapSignParams): Promise<PancakeBatchSwapResult>;
|
|
@@ -250,17 +250,6 @@ async function getGasPrice(provider, config) {
|
|
|
250
250
|
}
|
|
251
251
|
return gasPrice;
|
|
252
252
|
}
|
|
253
|
-
/**
|
|
254
|
-
* 编码 V3 路径(用于报价)
|
|
255
|
-
*/
|
|
256
|
-
function encodeV3Path(tokens, fees) {
|
|
257
|
-
let path = tokens[0];
|
|
258
|
-
for (let i = 0; i < fees.length; i++) {
|
|
259
|
-
const feeHex = fees[i].toString(16).padStart(6, '0');
|
|
260
|
-
path = ethers.solidityPacked(['bytes', 'uint24', 'address'], [path, fees[i], tokens[i + 1]]);
|
|
261
|
-
}
|
|
262
|
-
return path;
|
|
263
|
-
}
|
|
264
253
|
// PancakeSwapProxy ABI(只需要 ExactInput 方法)
|
|
265
254
|
const PANCAKE_PROXY_ABI = [
|
|
266
255
|
'function swapV2(uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline) payable returns (uint256)',
|
|
@@ -276,33 +265,6 @@ const PANCAKE_V3_QUOTER_ABI = [
|
|
|
276
265
|
'function quoteExactInputSingle((address tokenIn, address tokenOut, uint256 amountIn, uint24 fee, uint160 sqrtPriceLimitX96)) external returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate)',
|
|
277
266
|
'function quoteExactInput(bytes memory path, uint256 amountIn) external returns (uint256 amountOut, uint160[] memory sqrtPriceX96AfterList, uint32[] memory initializedTicksCrossedList, uint256 gasEstimate)'
|
|
278
267
|
];
|
|
279
|
-
const PANCAKE_CHAIN_ADDRESSES = {
|
|
280
|
-
BSC: {
|
|
281
|
-
PancakeProxy: ADDRESSES.BSC.PancakeProxy,
|
|
282
|
-
V2Router: '0x10ED43C718714eb63d5aA57B78B54704E256024E',
|
|
283
|
-
V3Router: '0x13f4EA83D0bd40E75C8222255bc855a974568Dd4',
|
|
284
|
-
V3Quoter: '0xB048Bbc1Ee6b733FFfCFb9e9CeF7375518e25997',
|
|
285
|
-
WETH: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c',
|
|
286
|
-
chainId: 56,
|
|
287
|
-
},
|
|
288
|
-
MONAD: {
|
|
289
|
-
PancakeProxy: '0x20B89e7e088db3e06e0893Ce23162E475b9d8c7c', // ✅ 已部署
|
|
290
|
-
V2Router: '0xb1bc24c34e88f7d43d5923034e3a14b24daacff9',
|
|
291
|
-
V3Router: '0x1b81d678ffb9c0263b24a97847620c99d213eb14',
|
|
292
|
-
V3Quoter: '0xb048bbc1ee6b733fffcfb9e9cef7375518e25997',
|
|
293
|
-
WETH: '0x3bd359c1119da7da1d913d1c4d2b7c461115433a', // WMON
|
|
294
|
-
chainId: 143,
|
|
295
|
-
},
|
|
296
|
-
};
|
|
297
|
-
// ✅ 根据链获取地址
|
|
298
|
-
function getChainAddresses(chainId) {
|
|
299
|
-
if (chainId === 56)
|
|
300
|
-
return PANCAKE_CHAIN_ADDRESSES.BSC;
|
|
301
|
-
if (chainId === 143)
|
|
302
|
-
return PANCAKE_CHAIN_ADDRESSES.MONAD;
|
|
303
|
-
// 默认返回 BSC
|
|
304
|
-
return PANCAKE_CHAIN_ADDRESSES.BSC;
|
|
305
|
-
}
|
|
306
268
|
// 兼容旧代码:默认使用 BSC 地址
|
|
307
269
|
const PANCAKE_PROXY_ADDRESS = ADDRESSES.BSC.PancakeProxy;
|
|
308
270
|
const PANCAKE_V2_ROUTER_ADDRESS = '0x10ED43C718714eb63d5aA57B78B54704E256024E';
|
|
@@ -443,3 +405,217 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
443
405
|
}
|
|
444
406
|
};
|
|
445
407
|
}
|
|
408
|
+
/**
|
|
409
|
+
* PancakeSwap 批量换手(一卖多买)
|
|
410
|
+
*
|
|
411
|
+
* 功能:主钱包一次卖出 → 多个子钱包同时买入 → 在同一个区块中完成
|
|
412
|
+
* 限制:最多 24 个买方(服务器限制 25 笔交易,包含 1 笔利润交易)
|
|
413
|
+
*
|
|
414
|
+
* 交易顺序:[授权(可选)] → [卖出] → [买入1, 买入2, ..., 买入N] → [利润]
|
|
415
|
+
*/
|
|
416
|
+
export async function pancakeBatchSwapMerkle(params) {
|
|
417
|
+
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKeys, buyerAmounts, tokenAddress, routeParams, slippageTolerance = 0.5, config, quoteToken, quoteTokenDecimals = 18 } = params;
|
|
418
|
+
// ✅ 校验买方数量(最多 24 个)
|
|
419
|
+
const MAX_BUYERS = 24;
|
|
420
|
+
if (buyerPrivateKeys.length === 0) {
|
|
421
|
+
throw new Error('至少需要一个买方钱包');
|
|
422
|
+
}
|
|
423
|
+
if (buyerPrivateKeys.length > MAX_BUYERS) {
|
|
424
|
+
throw new Error(`买方钱包数量超过限制: ${buyerPrivateKeys.length} > ${MAX_BUYERS}`);
|
|
425
|
+
}
|
|
426
|
+
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
427
|
+
console.log('🔍 PancakeSwap BatchSwap - 买方数量:', buyerPrivateKeys.length);
|
|
428
|
+
console.log('🔍 PancakeSwap BatchSwap - useNativeToken:', useNativeToken);
|
|
429
|
+
const context = createPancakeContext(config);
|
|
430
|
+
const seller = new Wallet(sellerPrivateKey, context.provider);
|
|
431
|
+
const buyers = buyerPrivateKeys.map(pk => new Wallet(pk, context.provider));
|
|
432
|
+
// ✅ 创建共享资源
|
|
433
|
+
const nonceManager = new NonceManager(context.provider);
|
|
434
|
+
const finalGasLimit = getGasLimit(config);
|
|
435
|
+
const gasPrice = await getGasPrice(context.provider, config);
|
|
436
|
+
const txType = config.txType ?? 0;
|
|
437
|
+
// ✅ 计算卖出数量
|
|
438
|
+
const { amount: sellAmountWei, decimals } = await calculateSellAmount(context.provider, tokenAddress, seller.address, sellAmount, sellPercentage);
|
|
439
|
+
// ✅ 构建授权交易(如果需要)
|
|
440
|
+
const approvalTx = await ensureSellerApproval({
|
|
441
|
+
tokenAddress,
|
|
442
|
+
seller,
|
|
443
|
+
provider: context.provider,
|
|
444
|
+
decimals,
|
|
445
|
+
chainId: context.chainId,
|
|
446
|
+
config,
|
|
447
|
+
nonceManager,
|
|
448
|
+
gasPrice,
|
|
449
|
+
txType
|
|
450
|
+
});
|
|
451
|
+
// ✅ 获取卖出报价
|
|
452
|
+
const quoteResult = await quoteSellOutput({
|
|
453
|
+
routeParams,
|
|
454
|
+
sellAmountWei,
|
|
455
|
+
provider: context.provider
|
|
456
|
+
});
|
|
457
|
+
const estimatedBNBOut = quoteResult.estimatedBNBOut;
|
|
458
|
+
console.log('📊 预估卖出所得:', ethers.formatEther(estimatedBNBOut), 'BNB');
|
|
459
|
+
// ✅ 计算每个买方的买入金额
|
|
460
|
+
let buyAmountsWei;
|
|
461
|
+
if (buyerAmounts && buyerAmounts.length === buyers.length) {
|
|
462
|
+
// 使用指定的买入金额
|
|
463
|
+
buyAmountsWei = buyerAmounts.map(amt => useNativeToken
|
|
464
|
+
? ethers.parseEther(amt)
|
|
465
|
+
: ethers.parseUnits(amt, quoteTokenDecimals));
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
// 平均分配(考虑滑点)
|
|
469
|
+
const totalBuyAmount = applySlippage(estimatedBNBOut, slippageTolerance);
|
|
470
|
+
const amountPerBuyer = totalBuyAmount / BigInt(buyers.length);
|
|
471
|
+
buyAmountsWei = buyers.map(() => amountPerBuyer);
|
|
472
|
+
}
|
|
473
|
+
// ✅ 验证所有买方余额
|
|
474
|
+
const reserveGas = ethers.parseEther((config.reserveGasBNB ?? 0.0005).toString());
|
|
475
|
+
for (let i = 0; i < buyers.length; i++) {
|
|
476
|
+
const buyer = buyers[i];
|
|
477
|
+
const buyAmount = buyAmountsWei[i];
|
|
478
|
+
let buyerBalance;
|
|
479
|
+
if (useNativeToken) {
|
|
480
|
+
buyerBalance = await buyer.provider.getBalance(buyer.address);
|
|
481
|
+
const required = buyAmount + FLAT_FEE + reserveGas;
|
|
482
|
+
if (buyerBalance < required) {
|
|
483
|
+
throw new Error(`买方 ${i + 1} 余额不足: 需要 ${ethers.formatEther(required)} BNB, 实际 ${ethers.formatEther(buyerBalance)} BNB`);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
const erc20 = new Contract(quoteToken, ERC20_BALANCE_OF_ABI, context.provider);
|
|
488
|
+
buyerBalance = await erc20.balanceOf(buyer.address);
|
|
489
|
+
const required = buyAmount + FLAT_FEE;
|
|
490
|
+
if (buyerBalance < required) {
|
|
491
|
+
throw new Error(`买方 ${i + 1} 代币余额不足: 需要 ${ethers.formatUnits(required, quoteTokenDecimals)}, 实际 ${ethers.formatUnits(buyerBalance, quoteTokenDecimals)}`);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
// ✅ 构建卖出交易
|
|
496
|
+
const proxySeller = new Contract(PANCAKE_PROXY_ADDRESS, PANCAKE_PROXY_ABI, seller);
|
|
497
|
+
const deadline = Math.floor(Date.now() / 1000) + 600;
|
|
498
|
+
let sellUnsigned;
|
|
499
|
+
if (routeParams.routeType === 'v2') {
|
|
500
|
+
const { v2Path } = routeParams;
|
|
501
|
+
sellUnsigned = await proxySeller.swapV2.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline, { value: FLAT_FEE });
|
|
502
|
+
}
|
|
503
|
+
else if (routeParams.routeType === 'v3-single') {
|
|
504
|
+
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
505
|
+
sellUnsigned = await proxySeller.swapV3Single.populateTransaction(v3TokenIn, v3TokenOut, v3Fee, sellAmountWei, 0n, seller.address, { value: FLAT_FEE });
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
const { v3LpAddresses, v3ExactTokenIn } = routeParams;
|
|
509
|
+
sellUnsigned = await proxySeller.swapV3MultiHop.populateTransaction(v3LpAddresses, v3ExactTokenIn, sellAmountWei, 0n, seller.address, { value: FLAT_FEE });
|
|
510
|
+
}
|
|
511
|
+
// ✅ 构建多个买入交易
|
|
512
|
+
const buyUnsignedList = [];
|
|
513
|
+
for (let i = 0; i < buyers.length; i++) {
|
|
514
|
+
const buyer = buyers[i];
|
|
515
|
+
const buyAmount = buyAmountsWei[i];
|
|
516
|
+
const proxyBuyer = new Contract(PANCAKE_PROXY_ADDRESS, PANCAKE_PROXY_ABI, buyer);
|
|
517
|
+
const buyValue = useNativeToken ? buyAmount + FLAT_FEE : FLAT_FEE;
|
|
518
|
+
let buyUnsigned;
|
|
519
|
+
if (routeParams.routeType === 'v2') {
|
|
520
|
+
const { v2Path } = routeParams;
|
|
521
|
+
const reversePath = [...v2Path].reverse();
|
|
522
|
+
buyUnsigned = await proxyBuyer.swapV2.populateTransaction(buyAmount, 0n, reversePath, buyer.address, deadline, { value: buyValue });
|
|
523
|
+
}
|
|
524
|
+
else if (routeParams.routeType === 'v3-single') {
|
|
525
|
+
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
526
|
+
buyUnsigned = await proxyBuyer.swapV3Single.populateTransaction(v3TokenOut, // 买入时反向
|
|
527
|
+
v3TokenIn, v3Fee, buyAmount, 0n, buyer.address, { value: buyValue });
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
const { v3LpAddresses } = routeParams;
|
|
531
|
+
const exactTokenOut = tokenAddress;
|
|
532
|
+
buyUnsigned = await proxyBuyer.swapV3MultiHop.populateTransaction(v3LpAddresses, exactTokenOut, buyAmount, 0n, buyer.address, { value: buyValue });
|
|
533
|
+
}
|
|
534
|
+
buyUnsignedList.push(buyUnsigned);
|
|
535
|
+
}
|
|
536
|
+
// ✅ 规划 Nonce
|
|
537
|
+
// 卖方: [授权(可选)] → [卖出]
|
|
538
|
+
const sellerNonces = [];
|
|
539
|
+
if (approvalTx) {
|
|
540
|
+
// 授权已经在 ensureSellerApproval 中消耗了一个 nonce
|
|
541
|
+
}
|
|
542
|
+
const sellNonce = await nonceManager.getNextNonce(seller);
|
|
543
|
+
sellerNonces.push(sellNonce);
|
|
544
|
+
// 买方: 每个买方一个 nonce
|
|
545
|
+
const buyerNonces = [];
|
|
546
|
+
for (const buyer of buyers) {
|
|
547
|
+
const nonce = await nonceManager.getNextNonce(buyer);
|
|
548
|
+
buyerNonces.push(nonce);
|
|
549
|
+
}
|
|
550
|
+
// 利润交易:从第一个买方发送(或卖方,取决于谁有足够余额)
|
|
551
|
+
const profitAmount = calculateProfitAmount(estimatedBNBOut);
|
|
552
|
+
let profitTx = null;
|
|
553
|
+
let profitPayerIndex = 0; // 默认第一个买方支付利润
|
|
554
|
+
if (profitAmount > 0n) {
|
|
555
|
+
// 选择第一个买方作为利润支付者
|
|
556
|
+
const profitPayer = buyers[0];
|
|
557
|
+
const profitNonce = await nonceManager.getNextNonce(profitPayer);
|
|
558
|
+
profitTx = await profitPayer.signTransaction({
|
|
559
|
+
to: PROFIT_CONFIG.RECIPIENT,
|
|
560
|
+
value: profitAmount,
|
|
561
|
+
nonce: profitNonce,
|
|
562
|
+
gasPrice,
|
|
563
|
+
gasLimit: 21000n,
|
|
564
|
+
chainId: context.chainId,
|
|
565
|
+
type: txType
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
nonceManager.clearTemp();
|
|
569
|
+
// ✅ 签名所有交易
|
|
570
|
+
const signedTransactions = [];
|
|
571
|
+
// 1. 授权交易(如果有)
|
|
572
|
+
if (approvalTx) {
|
|
573
|
+
signedTransactions.push(approvalTx);
|
|
574
|
+
}
|
|
575
|
+
// 2. 卖出交易
|
|
576
|
+
const signedSell = await seller.signTransaction({
|
|
577
|
+
...sellUnsigned,
|
|
578
|
+
from: seller.address,
|
|
579
|
+
nonce: sellNonce,
|
|
580
|
+
gasLimit: finalGasLimit,
|
|
581
|
+
gasPrice,
|
|
582
|
+
chainId: context.chainId,
|
|
583
|
+
type: txType
|
|
584
|
+
});
|
|
585
|
+
signedTransactions.push(signedSell);
|
|
586
|
+
// 3. 多个买入交易
|
|
587
|
+
for (let i = 0; i < buyers.length; i++) {
|
|
588
|
+
const buyer = buyers[i];
|
|
589
|
+
const buyUnsigned = buyUnsignedList[i];
|
|
590
|
+
const buyNonce = buyerNonces[i];
|
|
591
|
+
const signedBuy = await buyer.signTransaction({
|
|
592
|
+
...buyUnsigned,
|
|
593
|
+
from: buyer.address,
|
|
594
|
+
nonce: buyNonce,
|
|
595
|
+
gasLimit: finalGasLimit,
|
|
596
|
+
gasPrice,
|
|
597
|
+
chainId: context.chainId,
|
|
598
|
+
type: txType
|
|
599
|
+
});
|
|
600
|
+
signedTransactions.push(signedBuy);
|
|
601
|
+
}
|
|
602
|
+
// 4. 利润交易(最后)
|
|
603
|
+
if (profitTx) {
|
|
604
|
+
signedTransactions.push(profitTx);
|
|
605
|
+
}
|
|
606
|
+
console.log(`✅ 批量换手签名完成: ${signedTransactions.length} 笔交易`);
|
|
607
|
+
console.log(` - 授权: ${approvalTx ? 1 : 0}, 卖出: 1, 买入: ${buyers.length}, 利润: ${profitTx ? 1 : 0}`);
|
|
608
|
+
return {
|
|
609
|
+
signedTransactions,
|
|
610
|
+
metadata: {
|
|
611
|
+
sellerAddress: seller.address,
|
|
612
|
+
buyerAddresses: buyers.map(b => b.address),
|
|
613
|
+
sellAmount: ethers.formatUnits(sellAmountWei, decimals),
|
|
614
|
+
buyAmounts: buyAmountsWei.map(amt => useNativeToken
|
|
615
|
+
? ethers.formatEther(amt)
|
|
616
|
+
: ethers.formatUnits(amt, quoteTokenDecimals)),
|
|
617
|
+
hasApproval: !!approvalTx,
|
|
618
|
+
profitAmount: profitAmount > 0n ? ethers.formatEther(profitAmount) : undefined
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
}
|