four-flap-meme-sdk 1.3.46 → 1.3.48
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/dex/direct-router.js +52 -35
- package/dist/flap/portal-bundle-merkle/core.js +103 -73
- package/package.json +1 -1
|
@@ -82,31 +82,30 @@ const V2_ROUTER_ABI = [
|
|
|
82
82
|
/**
|
|
83
83
|
* SwapRouter02 的 V2 方法 ABI (PotatoSwap)
|
|
84
84
|
*
|
|
85
|
-
*
|
|
85
|
+
* 重要:根据实际 ABI,SwapRouter02 的 V2 方法只有:
|
|
86
86
|
* - swapExactTokensForTokens(amountIn, amountOutMin, path[], to)
|
|
87
|
-
* - swapExactETHForTokens(amountOutMin, path[], to) - ETH 换代币,自动 wrap
|
|
88
|
-
* - swapExactTokensForETH(amountIn, amountOutMin, path[], to) - 代币换 ETH,自动 unwrap
|
|
89
87
|
* - swapTokensForExactTokens(amountOut, amountInMax, path[], to)
|
|
90
|
-
*
|
|
91
|
-
*
|
|
88
|
+
*
|
|
89
|
+
* 没有 swapExactETHForTokens / swapExactTokensForETH!
|
|
90
|
+
* 必须手动处理 ETH/WETH 转换:
|
|
91
|
+
* - 买入(ETH→Token): wrapETH + swapExactTokensForTokens,通过 multicall 组合
|
|
92
|
+
* - 卖出(Token→ETH): swapExactTokensForTokens(to=ADDRESS_THIS) + unwrapWETH9,通过 multicall 组合
|
|
92
93
|
*/
|
|
93
94
|
const SWAP_ROUTER02_V2_ABI = [
|
|
94
|
-
// V2
|
|
95
|
+
// V2 交换方法(只有 token-to-token)
|
|
95
96
|
'function swapExactTokensForTokens(uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to) external payable returns (uint256 amountOut)',
|
|
96
|
-
'function swapExactETHForTokens(uint256 amountOutMin, address[] calldata path, address to) external payable returns (uint256 amountOut)',
|
|
97
|
-
'function swapExactTokensForETH(uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to) external payable returns (uint256 amountOut)',
|
|
98
|
-
// V2 交换方法 - 精确输出
|
|
99
97
|
'function swapTokensForExactTokens(uint256 amountOut, uint256 amountInMax, address[] calldata path, address to) external payable returns (uint256 amountIn)',
|
|
100
|
-
'function swapTokensForExactETH(uint256 amountOut, uint256 amountInMax, address[] calldata path, address to) external payable returns (uint256 amountIn)',
|
|
101
|
-
'function swapETHForExactTokens(uint256 amountOut, address[] calldata path, address to) external payable returns (uint256 amountIn)',
|
|
102
98
|
// Multicall - 多种重载
|
|
103
99
|
'function multicall(uint256 deadline, bytes[] calldata data) external payable returns (bytes[] memory results)',
|
|
100
|
+
'function multicall(bytes32 previousBlockhash, bytes[] calldata data) external payable returns (bytes[] memory results)',
|
|
104
101
|
'function multicall(bytes[] calldata data) external payable returns (bytes[] memory results)',
|
|
105
|
-
// 辅助方法
|
|
102
|
+
// 辅助方法 - ETH/WETH 转换
|
|
106
103
|
'function wrapETH(uint256 value) external payable',
|
|
107
104
|
'function unwrapWETH9(uint256 amountMinimum, address recipient) external payable',
|
|
108
105
|
'function unwrapWETH9(uint256 amountMinimum) external payable',
|
|
109
106
|
'function refundETH() external payable',
|
|
107
|
+
// 代币操作
|
|
108
|
+
'function pull(address token, uint256 value) external payable',
|
|
110
109
|
'function sweepToken(address token, uint256 amountMinimum, address recipient) external payable',
|
|
111
110
|
'function sweepToken(address token, uint256 amountMinimum) external payable',
|
|
112
111
|
];
|
|
@@ -270,20 +269,24 @@ async function buildProfitTransaction(wallet, profitAmountWei, nonce, gasPrice,
|
|
|
270
269
|
// V2 直接交易
|
|
271
270
|
// ============================================================================
|
|
272
271
|
/**
|
|
273
|
-
* 判断是否是 SwapRouter02 (
|
|
274
|
-
*
|
|
272
|
+
* 判断是否是 SwapRouter02 (仅 XLayer PotatoSwap)
|
|
273
|
+
*
|
|
274
|
+
* 只有 XLayer PotatoSwap 使用 SwapRouter02 进行 V2 交易
|
|
275
|
+
* BSC/Monad 的 V2 交易使用传统的 V2 Router(带 deadline 参数)
|
|
276
|
+
*
|
|
277
|
+
* SwapRouter02 的 V2 方法签名不同:
|
|
278
|
+
* - swapExactETHForTokens(amountOutMin, path[], to) - 没有 deadline
|
|
279
|
+
* - swapExactTokensForETH(amountIn, amountOutMin, path[], to) - 没有 deadline
|
|
275
280
|
*/
|
|
276
281
|
function isSwapRouter02(chain, routerAddress) {
|
|
277
282
|
const chainUpper = chain.toUpperCase();
|
|
278
283
|
const addrLower = routerAddress.toLowerCase();
|
|
279
|
-
// XLayer PotatoSwap SwapRouter02
|
|
284
|
+
// ✅ 只有 XLayer PotatoSwap SwapRouter02 走这个逻辑
|
|
280
285
|
if (chainUpper === 'XLAYER' && addrLower === DIRECT_ROUTERS.XLAYER.POTATOSWAP_V2.toLowerCase()) {
|
|
281
286
|
return true;
|
|
282
287
|
}
|
|
283
|
-
// BSC PancakeSwap
|
|
284
|
-
|
|
285
|
-
return true;
|
|
286
|
-
}
|
|
288
|
+
// ❌ BSC V2 交易使用传统 PancakeSwap V2 Router,不走 SwapRouter02 逻辑
|
|
289
|
+
// ❌ Monad V2 交易使用传统 V2 Router,不走 SwapRouter02 逻辑
|
|
287
290
|
return false;
|
|
288
291
|
}
|
|
289
292
|
/**
|
|
@@ -327,17 +330,19 @@ export async function directV2BatchBuy(params) {
|
|
|
327
330
|
let txData;
|
|
328
331
|
let txValue;
|
|
329
332
|
if (useSwapRouter02) {
|
|
330
|
-
// ✅ SwapRouter02:
|
|
333
|
+
// ✅ SwapRouter02: 使用 multicall(deadline, bytes[]) 组合调用
|
|
334
|
+
// ABI 中没有 swapExactETHForTokens,只有 swapExactTokensForTokens
|
|
331
335
|
if (useNative) {
|
|
332
|
-
// ETH ->
|
|
333
|
-
//
|
|
334
|
-
//
|
|
335
|
-
const swapData = routerIface.encodeFunctionData('
|
|
336
|
+
// ETH -> 代币:直接用 swapExactTokensForTokens(参考之前成功的交易)
|
|
337
|
+
// 之前成功的交易只用了一个 swapExactTokensForTokens,没有 wrapETH
|
|
338
|
+
// SwapRouter02 会自动处理 msg.value 的 ETH
|
|
339
|
+
const swapData = routerIface.encodeFunctionData('swapExactTokensForTokens', [
|
|
340
|
+
amountWei,
|
|
336
341
|
0n, // amountOutMin
|
|
337
342
|
path, // path: [WETH, ..., tokenOut]
|
|
338
343
|
wallet.address, // to
|
|
339
344
|
]);
|
|
340
|
-
// 使用 multicall
|
|
345
|
+
// 使用 multicall(uint256 deadline, bytes[]) - 带 deadline 的版本
|
|
341
346
|
txData = routerIface.encodeFunctionData('multicall(uint256,bytes[])', [
|
|
342
347
|
deadline,
|
|
343
348
|
[swapData],
|
|
@@ -345,7 +350,7 @@ export async function directV2BatchBuy(params) {
|
|
|
345
350
|
txValue = amountWei;
|
|
346
351
|
}
|
|
347
352
|
else {
|
|
348
|
-
// 代币 ->
|
|
353
|
+
// 代币 -> 代币:也用 multicall 包装
|
|
349
354
|
const swapData = routerIface.encodeFunctionData('swapExactTokensForTokens', [
|
|
350
355
|
amountWei,
|
|
351
356
|
0n, // amountOutMin
|
|
@@ -484,25 +489,37 @@ export async function directV2BatchSell(params) {
|
|
|
484
489
|
// 卖出交易
|
|
485
490
|
let txData;
|
|
486
491
|
if (useSwapRouter02) {
|
|
487
|
-
// ✅ SwapRouter02:
|
|
492
|
+
// ✅ SwapRouter02: 使用 multicall(deadline, bytes[]) 组合调用
|
|
493
|
+
// ABI 中没有 swapExactTokensForETH,只有 swapExactTokensForTokens
|
|
488
494
|
if (useNativeOutput) {
|
|
489
|
-
// 代币 -> ETH
|
|
490
|
-
|
|
491
|
-
//
|
|
492
|
-
|
|
495
|
+
// 代币 -> ETH:swapExactTokensForTokens + unwrapWETH9
|
|
496
|
+
const multicallData = [];
|
|
497
|
+
// SwapRouter02 特殊地址约定:
|
|
498
|
+
// address(2) = ADDRESS_THIS = Router 合约自己
|
|
499
|
+
const ADDRESS_THIS = '0x0000000000000000000000000000000000000002';
|
|
500
|
+
// 1. swapExactTokensForTokens - Token -> WETH,发送到 Router 自己
|
|
501
|
+
// path 最后一个元素是 WETH
|
|
502
|
+
const swapData = routerIface.encodeFunctionData('swapExactTokensForTokens', [
|
|
493
503
|
sellAmount,
|
|
494
504
|
0n, // amountOutMin
|
|
495
505
|
path, // path: [token, ..., WETH]
|
|
496
|
-
|
|
506
|
+
ADDRESS_THIS, // to = Router 自己,WETH 留在 Router 中
|
|
497
507
|
]);
|
|
498
|
-
|
|
508
|
+
multicallData.push(swapData);
|
|
509
|
+
// 2. unwrapWETH9 - 将 Router 中的 WETH 解包为 ETH 发送给用户
|
|
510
|
+
const unwrapData = routerIface.encodeFunctionData('unwrapWETH9(uint256,address)', [
|
|
511
|
+
0n, // amountMinimum
|
|
512
|
+
wallet.address, // recipient = 用户
|
|
513
|
+
]);
|
|
514
|
+
multicallData.push(unwrapData);
|
|
515
|
+
// 使用 multicall(uint256 deadline, bytes[]) - 带 deadline 的版本
|
|
499
516
|
txData = routerIface.encodeFunctionData('multicall(uint256,bytes[])', [
|
|
500
517
|
deadline,
|
|
501
|
-
|
|
518
|
+
multicallData,
|
|
502
519
|
]);
|
|
503
520
|
}
|
|
504
521
|
else {
|
|
505
|
-
// 代币 ->
|
|
522
|
+
// 代币 -> 代币:也用 multicall 包装
|
|
506
523
|
const swapData = routerIface.encodeFunctionData('swapExactTokensForTokens', [
|
|
507
524
|
sellAmount,
|
|
508
525
|
0n, // amountOutMin
|
|
@@ -297,17 +297,62 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
297
297
|
const useNativeOutput = outputToken === ZERO_ADDRESS;
|
|
298
298
|
// ✅ 优化:如果前端传入了 nonces,直接使用(跳过 RPC 调用)
|
|
299
299
|
const presetNonces = config.nonces;
|
|
300
|
-
|
|
301
|
-
|
|
300
|
+
const extractProfit = shouldExtractProfit(config);
|
|
301
|
+
// ✅ 并行执行 gasPrice 和 quoteSellOutputs(Multicall3)
|
|
302
|
+
const [gasPrice, quotedOutputs] = await Promise.all([
|
|
302
303
|
resolveGasPrice(provider, config),
|
|
303
|
-
quoteSellOutputsWithQuote(readOnlyPortal, tokenAddress, amountsWei, outputToken)
|
|
304
|
-
presetNonces && presetNonces.length === wallets.length
|
|
305
|
-
? Promise.resolve(presetNonces) // ✅ 使用前端传入的 nonces
|
|
306
|
-
: nonceManager.getNextNoncesForWallets(wallets)
|
|
304
|
+
quoteSellOutputsWithQuote(readOnlyPortal, tokenAddress, amountsWei, outputToken)
|
|
307
305
|
]);
|
|
308
|
-
|
|
306
|
+
// ✅ 计算利润和 maxRevenueIndex(用于 nonce 分配)
|
|
307
|
+
let totalTokenProfit = 0n;
|
|
308
|
+
let maxRevenueIndex = -1;
|
|
309
|
+
let maxRevenue = 0n;
|
|
310
|
+
if (extractProfit && quotedOutputs.length > 0) {
|
|
311
|
+
for (let i = 0; i < wallets.length; i++) {
|
|
312
|
+
const quoted = quotedOutputs[i];
|
|
313
|
+
if (quoted > 0n) {
|
|
314
|
+
const { profit } = calculateProfit(quoted, config);
|
|
315
|
+
totalTokenProfit += profit;
|
|
316
|
+
if (quoted > maxRevenue) {
|
|
317
|
+
maxRevenue = quoted;
|
|
318
|
+
maxRevenueIndex = i;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
// ✅ 修复:根据是否需要利润交易,统一分配 nonces
|
|
324
|
+
const needProfitTx = extractProfit && totalTokenProfit > 0n && maxRevenueIndex >= 0;
|
|
325
|
+
let nonces;
|
|
326
|
+
let profitNonce;
|
|
327
|
+
if (presetNonces && presetNonces.length === wallets.length) {
|
|
328
|
+
nonces = presetNonces;
|
|
309
329
|
console.log('🚀 SDK 使用前端传入的 nonces:', presetNonces);
|
|
310
330
|
}
|
|
331
|
+
else if (needProfitTx) {
|
|
332
|
+
// maxRevenueIndex 钱包需要 2 个连续 nonce(卖出 + 利润)
|
|
333
|
+
const maxRevenueNonces = await nonceManager.getNextNonceBatch(wallets[maxRevenueIndex], 2);
|
|
334
|
+
// 其他钱包各需要 1 个 nonce
|
|
335
|
+
const otherWallets = wallets.filter((_, i) => i !== maxRevenueIndex);
|
|
336
|
+
const otherNonces = otherWallets.length > 0
|
|
337
|
+
? await nonceManager.getNextNoncesForWallets(otherWallets)
|
|
338
|
+
: [];
|
|
339
|
+
// 组装最终的 nonces 数组(保持原顺序)
|
|
340
|
+
nonces = [];
|
|
341
|
+
let otherIdx = 0;
|
|
342
|
+
for (let i = 0; i < wallets.length; i++) {
|
|
343
|
+
if (i === maxRevenueIndex) {
|
|
344
|
+
nonces.push(maxRevenueNonces[0]); // 卖出交易用第一个 nonce
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
nonces.push(otherNonces[otherIdx++]);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
profitNonce = maxRevenueNonces[1]; // 利润交易用第二个 nonce
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
// 不需要利润交易,所有钱包各 1 个 nonce
|
|
354
|
+
nonces = await nonceManager.getNextNoncesForWallets(wallets);
|
|
355
|
+
}
|
|
311
356
|
const minOuts = resolveMinOutputs(minOutputAmounts, wallets.length, quotedOutputs);
|
|
312
357
|
// ✅ 优化:构建未签名交易(这里是本地操作,但仍然并行执行以提高效率)
|
|
313
358
|
const unsignedList = await Promise.all(portals.map((portal, i) => portal.swapExactInput.populateTransaction({
|
|
@@ -330,19 +375,31 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
330
375
|
value: 0n // ✅ 卖出交易不发送原生代币
|
|
331
376
|
})));
|
|
332
377
|
signedTxs.push(...signedList);
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
378
|
+
// ✅ 修复:使用预先分配的 profitNonce 添加利润交易
|
|
379
|
+
if (needProfitTx && profitNonce !== undefined) {
|
|
380
|
+
// ERC20 输出时:获取代币利润等值的原生代币(BNB)报价
|
|
381
|
+
let nativeProfitAmount = totalTokenProfit;
|
|
382
|
+
if (!useNativeOutput && outputToken) {
|
|
383
|
+
nativeProfitAmount = await getTokenToNativeQuote(provider, outputToken, totalTokenProfit, chainId);
|
|
384
|
+
console.log('🔍 SDK ERC20 卖出利润转换: ', ethers.formatEther(totalTokenProfit), ' Token -> ', ethers.formatEther(nativeProfitAmount), ' BNB');
|
|
385
|
+
// 如果报价失败(返回 0),跳过利润提取
|
|
386
|
+
if (nativeProfitAmount === 0n) {
|
|
387
|
+
console.log('🔍 SDK ERC20 卖出利润转换失败,跳过利润提取');
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (nativeProfitAmount > 0n) {
|
|
391
|
+
const profitTx = await wallets[maxRevenueIndex].signTransaction({
|
|
392
|
+
to: getProfitRecipient(),
|
|
393
|
+
value: nativeProfitAmount,
|
|
394
|
+
nonce: profitNonce,
|
|
395
|
+
gasPrice,
|
|
396
|
+
gasLimit: 23000n,
|
|
397
|
+
chainId,
|
|
398
|
+
type: getTxType(config)
|
|
399
|
+
});
|
|
400
|
+
signedTxs.push(profitTx);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
346
403
|
nonceManager.clearTemp();
|
|
347
404
|
return {
|
|
348
405
|
signedTransactions: signedTxs
|
|
@@ -434,19 +491,33 @@ function buildGasLimitList(length, config) {
|
|
|
434
491
|
return new Array(length).fill(gasLimit);
|
|
435
492
|
}
|
|
436
493
|
/**
|
|
437
|
-
* ✅
|
|
438
|
-
* 使用 getNextNoncesForWallets 批量获取,比 Promise.all 更高效
|
|
494
|
+
* ✅ 修复:明确分配 nonces,避免隐式状态依赖
|
|
439
495
|
*/
|
|
440
496
|
async function allocateBuyerNonces(buyers, extractProfit, maxIndex, totalProfit, nonceManager) {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
if (extractProfit && totalProfit > 0n && maxIndex >= 0 && maxIndex < buyers.length) {
|
|
446
|
-
// 再获取一个 nonce 给利润交易(从缓存中获取,不会触发 RPC)
|
|
447
|
-
await nonceManager.getNextNonce(buyers[maxIndex]);
|
|
497
|
+
const needProfitTx = extractProfit && totalProfit > 0n && maxIndex >= 0 && maxIndex < buyers.length;
|
|
498
|
+
if (!needProfitTx) {
|
|
499
|
+
// 不需要利润交易,所有钱包各 1 个 nonce
|
|
500
|
+
return await nonceManager.getNextNoncesForWallets(buyers);
|
|
448
501
|
}
|
|
449
|
-
|
|
502
|
+
// 需要利润交易:maxIndex 钱包需要 2 个连续 nonce(买入 + 利润)
|
|
503
|
+
const maxIndexNonces = await nonceManager.getNextNonceBatch(buyers[maxIndex], 2);
|
|
504
|
+
// 其他钱包各需要 1 个 nonce
|
|
505
|
+
const otherBuyers = buyers.filter((_, i) => i !== maxIndex);
|
|
506
|
+
const otherNonces = otherBuyers.length > 0
|
|
507
|
+
? await nonceManager.getNextNoncesForWallets(otherBuyers)
|
|
508
|
+
: [];
|
|
509
|
+
// 组装最终的 nonces 数组(保持原顺序)
|
|
510
|
+
const nonces = [];
|
|
511
|
+
let otherIdx = 0;
|
|
512
|
+
for (let i = 0; i < buyers.length; i++) {
|
|
513
|
+
if (i === maxIndex) {
|
|
514
|
+
nonces.push(maxIndexNonces[0]); // 买入交易用第一个 nonce
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
nonces.push(otherNonces[otherIdx++]);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return nonces;
|
|
450
521
|
}
|
|
451
522
|
async function signBuyTransactions({ unsignedBuys, buyers, nonces, gasLimits, gasPrice, chainId, config, fundsList, useNativeToken = true // ✅ 默认使用原生代币
|
|
452
523
|
}) {
|
|
@@ -471,7 +542,7 @@ async function appendProfitTransaction({ extractProfit, totalProfit, buyers, max
|
|
|
471
542
|
value: totalProfit,
|
|
472
543
|
nonce: profitNonce,
|
|
473
544
|
gasPrice,
|
|
474
|
-
gasLimit:
|
|
545
|
+
gasLimit: 23000n,
|
|
475
546
|
chainId,
|
|
476
547
|
type: getTxType(config)
|
|
477
548
|
});
|
|
@@ -562,45 +633,4 @@ function resolveMinOutputs(provided, walletCount, _quotedOutputs) {
|
|
|
562
633
|
// 原因:大额交易时 5% 滑点可能不够,导致交易失败
|
|
563
634
|
return Array(walletCount).fill(0n);
|
|
564
635
|
}
|
|
565
|
-
|
|
566
|
-
if (!extractProfit || quotedOutputs.length === 0) {
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
569
|
-
let totalTokenProfit = 0n; // 代币利润
|
|
570
|
-
let maxRevenueIndex = -1;
|
|
571
|
-
let maxRevenue = 0n;
|
|
572
|
-
for (let i = 0; i < wallets.length; i++) {
|
|
573
|
-
const quoted = quotedOutputs[i];
|
|
574
|
-
if (quoted > 0n) {
|
|
575
|
-
const { profit } = calculateProfit(quoted, config);
|
|
576
|
-
totalTokenProfit += profit;
|
|
577
|
-
if (quoted > maxRevenue) {
|
|
578
|
-
maxRevenue = quoted;
|
|
579
|
-
maxRevenueIndex = i;
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
if (totalTokenProfit === 0n || maxRevenueIndex < 0) {
|
|
584
|
-
return;
|
|
585
|
-
}
|
|
586
|
-
// ✅ ERC20 输出:获取代币利润等值的原生代币(BNB)报价
|
|
587
|
-
let nativeProfitAmount = totalTokenProfit; // 原生代币输出时直接使用
|
|
588
|
-
if (!useNativeOutput && outputToken && provider) {
|
|
589
|
-
nativeProfitAmount = await getTokenToNativeQuote(provider, outputToken, totalTokenProfit, chainId);
|
|
590
|
-
// 如果报价失败(返回 0),跳过利润提取
|
|
591
|
-
if (nativeProfitAmount === 0n) {
|
|
592
|
-
return;
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
const profitNonce = await nonceManager.getNextNonce(wallets[maxRevenueIndex]);
|
|
596
|
-
const profitTx = await wallets[maxRevenueIndex].signTransaction({
|
|
597
|
-
to: getProfitRecipient(),
|
|
598
|
-
value: nativeProfitAmount, // ✅ 转等值原生代币
|
|
599
|
-
nonce: profitNonce,
|
|
600
|
-
gasPrice,
|
|
601
|
-
gasLimit: 21000n,
|
|
602
|
-
chainId,
|
|
603
|
-
type: getTxType(config)
|
|
604
|
-
});
|
|
605
|
-
signedTxs.push(profitTx);
|
|
606
|
-
}
|
|
636
|
+
// ✅ appendSellProfitTransaction 已内联到 batchSellWithBundleMerkle 中,避免 nonce 竞争问题
|