four-flap-meme-sdk 1.3.91 → 1.3.93
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/clients/blockrazor.js +0 -1
- package/dist/contracts/tm-bundle-merkle/config.d.ts +5 -0
- package/dist/contracts/tm-bundle-merkle/config.js +10 -0
- package/dist/contracts/tm-bundle-merkle/core.js +92 -24
- package/dist/contracts/tm-bundle-merkle/pancake-proxy.js +103 -54
- package/dist/contracts/tm-bundle-merkle/swap-buy-first.d.ts +0 -1
- package/dist/contracts/tm-bundle-merkle/swap-buy-first.js +36 -6
- package/dist/contracts/tm-bundle-merkle/swap.d.ts +0 -3
- package/dist/contracts/tm-bundle-merkle/swap.js +59 -6
- package/dist/flap/portal-bundle-merkle/config.d.ts +8 -0
- package/dist/flap/portal-bundle-merkle/config.js +17 -0
- package/dist/flap/portal-bundle-merkle/core.js +120 -68
- package/dist/flap/portal-bundle-merkle/encryption.d.ts +16 -0
- package/dist/flap/portal-bundle-merkle/encryption.js +146 -0
- package/dist/flap/portal-bundle-merkle/pancake-proxy.js +136 -78
- package/dist/flap/portal-bundle-merkle/swap-buy-first.d.ts +0 -2
- package/dist/flap/portal-bundle-merkle/swap-buy-first.js +49 -30
- package/dist/flap/portal-bundle-merkle/swap.d.ts +0 -2
- package/dist/flap/portal-bundle-merkle/swap.js +75 -47
- package/dist/flap/portal-bundle-merkle/types.d.ts +1 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.js +0 -1
- package/dist/pancake/bundle-buy-first.d.ts +1 -1
- package/dist/pancake/bundle-buy-first.js +49 -17
- package/dist/pancake/bundle-swap.d.ts +1 -4
- package/dist/pancake/bundle-swap.js +98 -33
- package/dist/utils/erc20.d.ts +108 -2
- package/dist/utils/erc20.js +65 -17
- package/package.json +4 -39
- package/dist/sol/constants.d.ts +0 -126
- package/dist/sol/constants.js +0 -145
- package/dist/sol/dex/index.d.ts +0 -8
- package/dist/sol/dex/index.js +0 -12
- package/dist/sol/dex/meteora/client.d.ts +0 -76
- package/dist/sol/dex/meteora/client.js +0 -219
- package/dist/sol/dex/meteora/damm-v1-bundle.d.ts +0 -61
- package/dist/sol/dex/meteora/damm-v1-bundle.js +0 -112
- package/dist/sol/dex/meteora/damm-v1.d.ts +0 -118
- package/dist/sol/dex/meteora/damm-v1.js +0 -315
- package/dist/sol/dex/meteora/damm-v2-bundle.d.ts +0 -82
- package/dist/sol/dex/meteora/damm-v2-bundle.js +0 -242
- package/dist/sol/dex/meteora/damm-v2.d.ts +0 -172
- package/dist/sol/dex/meteora/damm-v2.js +0 -632
- package/dist/sol/dex/meteora/dbc-bundle.d.ts +0 -123
- package/dist/sol/dex/meteora/dbc-bundle.js +0 -304
- package/dist/sol/dex/meteora/dbc.d.ts +0 -192
- package/dist/sol/dex/meteora/dbc.js +0 -619
- package/dist/sol/dex/meteora/dlmm-bundle.d.ts +0 -39
- package/dist/sol/dex/meteora/dlmm-bundle.js +0 -189
- package/dist/sol/dex/meteora/dlmm.d.ts +0 -146
- package/dist/sol/dex/meteora/dlmm.js +0 -593
- package/dist/sol/dex/meteora/index.d.ts +0 -25
- package/dist/sol/dex/meteora/index.js +0 -65
- package/dist/sol/dex/meteora/types.d.ts +0 -787
- package/dist/sol/dex/meteora/types.js +0 -110
- package/dist/sol/dex/orca/index.d.ts +0 -10
- package/dist/sol/dex/orca/index.js +0 -16
- package/dist/sol/dex/orca/orca-bundle.d.ts +0 -41
- package/dist/sol/dex/orca/orca-bundle.js +0 -173
- package/dist/sol/dex/orca/orca.d.ts +0 -65
- package/dist/sol/dex/orca/orca.js +0 -474
- package/dist/sol/dex/orca/types.d.ts +0 -263
- package/dist/sol/dex/orca/types.js +0 -38
- package/dist/sol/dex/orca/wavebreak-bundle.d.ts +0 -34
- package/dist/sol/dex/orca/wavebreak-bundle.js +0 -198
- package/dist/sol/dex/orca/wavebreak-types.d.ts +0 -227
- package/dist/sol/dex/orca/wavebreak-types.js +0 -23
- package/dist/sol/dex/orca/wavebreak.d.ts +0 -78
- package/dist/sol/dex/orca/wavebreak.js +0 -497
- package/dist/sol/dex/pump/index.d.ts +0 -9
- package/dist/sol/dex/pump/index.js +0 -14
- package/dist/sol/dex/pump/pump-bundle.d.ts +0 -92
- package/dist/sol/dex/pump/pump-bundle.js +0 -383
- package/dist/sol/dex/pump/pump-swap-bundle.d.ts +0 -103
- package/dist/sol/dex/pump/pump-swap-bundle.js +0 -380
- package/dist/sol/dex/pump/pump-swap.d.ts +0 -46
- package/dist/sol/dex/pump/pump-swap.js +0 -199
- package/dist/sol/dex/pump/pump.d.ts +0 -35
- package/dist/sol/dex/pump/pump.js +0 -352
- package/dist/sol/dex/pump/types.d.ts +0 -215
- package/dist/sol/dex/pump/types.js +0 -5
- package/dist/sol/dex/raydium/index.d.ts +0 -8
- package/dist/sol/dex/raydium/index.js +0 -12
- package/dist/sol/dex/raydium/launchlab.d.ts +0 -68
- package/dist/sol/dex/raydium/launchlab.js +0 -210
- package/dist/sol/dex/raydium/raydium-bundle.d.ts +0 -64
- package/dist/sol/dex/raydium/raydium-bundle.js +0 -324
- package/dist/sol/dex/raydium/raydium.d.ts +0 -40
- package/dist/sol/dex/raydium/raydium.js +0 -366
- package/dist/sol/dex/raydium/types.d.ts +0 -240
- package/dist/sol/dex/raydium/types.js +0 -5
- package/dist/sol/index.d.ts +0 -10
- package/dist/sol/index.js +0 -16
- package/dist/sol/jito/bundle.d.ts +0 -90
- package/dist/sol/jito/bundle.js +0 -263
- package/dist/sol/jito/index.d.ts +0 -7
- package/dist/sol/jito/index.js +0 -7
- package/dist/sol/jito/tip.d.ts +0 -51
- package/dist/sol/jito/tip.js +0 -83
- package/dist/sol/jito/types.d.ts +0 -100
- package/dist/sol/jito/types.js +0 -5
- package/dist/sol/token/create-complete.d.ts +0 -115
- package/dist/sol/token/create-complete.js +0 -235
- package/dist/sol/token/create-token.d.ts +0 -57
- package/dist/sol/token/create-token.js +0 -230
- package/dist/sol/token/index.d.ts +0 -9
- package/dist/sol/token/index.js +0 -14
- package/dist/sol/token/metadata-upload.d.ts +0 -86
- package/dist/sol/token/metadata-upload.js +0 -173
- package/dist/sol/token/metadata.d.ts +0 -92
- package/dist/sol/token/metadata.js +0 -274
- package/dist/sol/token/types.d.ts +0 -153
- package/dist/sol/token/types.js +0 -5
- package/dist/sol/types.d.ts +0 -176
- package/dist/sol/types.js +0 -7
- package/dist/sol/utils/balance.d.ts +0 -160
- package/dist/sol/utils/balance.js +0 -638
- package/dist/sol/utils/connection.d.ts +0 -78
- package/dist/sol/utils/connection.js +0 -168
- package/dist/sol/utils/index.d.ts +0 -9
- package/dist/sol/utils/index.js +0 -9
- package/dist/sol/utils/lp-inspect.d.ts +0 -129
- package/dist/sol/utils/lp-inspect.js +0 -529
- package/dist/sol/utils/transfer.d.ts +0 -125
- package/dist/sol/utils/transfer.js +0 -220
- package/dist/sol/utils/wallet.d.ts +0 -107
- package/dist/sol/utils/wallet.js +0 -210
|
@@ -7,7 +7,7 @@ import { ethers, Contract, Wallet } from 'ethers';
|
|
|
7
7
|
import { NonceManager, getOptimizedGasPrice } from '../../utils/bundle-helpers.js';
|
|
8
8
|
import { FLAP_PORTAL_ADDRESSES } from '../constants.js';
|
|
9
9
|
import { PROFIT_CONFIG } from '../../utils/constants.js';
|
|
10
|
-
import { getGasPriceConfig, getTxType, getProfitRecipient } from './config.js';
|
|
10
|
+
import { getGasPriceConfig, getTxType, getProfitRecipient, getBribeAmount, BLOCKRAZOR_BUILDER_EOA } from './config.js';
|
|
11
11
|
// Portal ABI
|
|
12
12
|
const PORTAL_ABI = [
|
|
13
13
|
'function swapExactInput((address inputToken, address outputToken, uint256 inputAmount, uint256 minOutputAmount, bytes permitData)) payable returns (uint256)',
|
|
@@ -124,12 +124,11 @@ export async function flapBundleBuyFirstMerkle(params) {
|
|
|
124
124
|
]);
|
|
125
125
|
const { buyerFundsWei, buyerBalance } = buyerFundsResult;
|
|
126
126
|
const priorityFee = gasPrice / 10n === 0n ? 1n : gasPrice / 10n;
|
|
127
|
-
// ✅
|
|
127
|
+
// ✅ 获取报价(已移除滑点保护)
|
|
128
128
|
const quoteResult = await quoteBuyerOutput({
|
|
129
129
|
portalAddress: chainContext.portalAddress,
|
|
130
130
|
tokenAddress,
|
|
131
131
|
buyerFundsWei,
|
|
132
|
-
slippageBps: config.slippageBps,
|
|
133
132
|
provider: chainContext.provider,
|
|
134
133
|
skipQuoteOnError: config.skipQuoteOnError,
|
|
135
134
|
inputToken // ✅ 传递输入代币
|
|
@@ -177,12 +176,16 @@ export async function flapBundleBuyFirstMerkle(params) {
|
|
|
177
176
|
gasPrice,
|
|
178
177
|
txType
|
|
179
178
|
});
|
|
180
|
-
// ✅
|
|
179
|
+
// ✅ 获取贿赂金额
|
|
180
|
+
const bribeAmount = getBribeAmount(config);
|
|
181
|
+
const needBribeTx = bribeAmount > 0n;
|
|
182
|
+
// ✅ 修复:根据实际的授权情况规划 nonce(包含贿赂交易)
|
|
181
183
|
const noncePlan = await planNonces({
|
|
182
184
|
buyer,
|
|
183
185
|
seller,
|
|
184
186
|
approvalExists: approvalTx !== null, // ✅ 使用实际值
|
|
185
187
|
extractProfit: true,
|
|
188
|
+
needBribeTx, // ✅ 新增:是否需要贿赂交易
|
|
186
189
|
sameAddress,
|
|
187
190
|
nonceManager
|
|
188
191
|
});
|
|
@@ -216,21 +219,38 @@ export async function flapBundleBuyFirstMerkle(params) {
|
|
|
216
219
|
txType,
|
|
217
220
|
value: 0n // ✅ 卖出交易不发送原生代币
|
|
218
221
|
});
|
|
219
|
-
// ✅
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
seller.signTransaction(
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
profitNonce: noncePlan.profitNonce,
|
|
222
|
+
// ✅ 贿赂交易放在首位
|
|
223
|
+
let bribeTx = null;
|
|
224
|
+
if (needBribeTx && noncePlan.bribeNonce !== undefined) {
|
|
225
|
+
bribeTx = await seller.signTransaction({
|
|
226
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
227
|
+
value: bribeAmount,
|
|
228
|
+
nonce: noncePlan.bribeNonce,
|
|
227
229
|
gasPrice,
|
|
230
|
+
gasLimit: 21000n,
|
|
228
231
|
chainId: chainContext.chainId,
|
|
229
|
-
txType
|
|
230
|
-
})
|
|
232
|
+
type: txType
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
// ✅ 并行签名买入和卖出交易
|
|
236
|
+
const [signedBuy, signedSell] = await Promise.all([
|
|
237
|
+
buyer.signTransaction(buyTx),
|
|
238
|
+
seller.signTransaction(sellTx)
|
|
231
239
|
]);
|
|
240
|
+
// ✅ 利润交易放在末尾
|
|
241
|
+
const profitTx = await buildProfitTransaction({
|
|
242
|
+
seller,
|
|
243
|
+
profitAmount: nativeProfitAmount, // ✅ 使用转换后的原生代币利润
|
|
244
|
+
profitNonce: noncePlan.profitNonce,
|
|
245
|
+
gasPrice,
|
|
246
|
+
chainId: chainContext.chainId,
|
|
247
|
+
txType
|
|
248
|
+
});
|
|
232
249
|
nonceManager.clearTemp();
|
|
250
|
+
// ✅ 组装顺序:贿赂 → 授权 → 买入 → 卖出 → 利润
|
|
233
251
|
const allTransactions = [];
|
|
252
|
+
if (bribeTx)
|
|
253
|
+
allTransactions.push(bribeTx);
|
|
234
254
|
if (approvalTx)
|
|
235
255
|
allTransactions.push(approvalTx);
|
|
236
256
|
allTransactions.push(signedBuy, signedSell);
|
|
@@ -300,35 +320,31 @@ async function calculateBuyerFunds({ buyer, buyerFunds, buyerFundsPercentage, re
|
|
|
300
320
|
}
|
|
301
321
|
return { buyerFundsWei, buyerBalance };
|
|
302
322
|
}
|
|
303
|
-
async function quoteBuyerOutput({ portalAddress, tokenAddress, buyerFundsWei,
|
|
323
|
+
async function quoteBuyerOutput({ portalAddress, tokenAddress, buyerFundsWei, provider, skipQuoteOnError, inputToken = ZERO_ADDRESS // ✅ 默认使用原生代币
|
|
304
324
|
}) {
|
|
305
325
|
let quotedToken = 0n;
|
|
306
|
-
let minOutToken = 0n;
|
|
307
326
|
const portal = new Contract(portalAddress, PORTAL_ABI, provider);
|
|
308
|
-
const safeSlippage = Math.max(0, Math.min(5000, slippageBps ?? 100));
|
|
309
327
|
try {
|
|
310
328
|
quotedToken = await portal.quoteExactInput.staticCall({
|
|
311
329
|
inputToken, // ✅ 使用动态输入代币
|
|
312
330
|
outputToken: tokenAddress,
|
|
313
331
|
inputAmount: buyerFundsWei
|
|
314
332
|
});
|
|
315
|
-
const keep = BigInt(10000 - safeSlippage);
|
|
316
|
-
minOutToken = (quotedToken * keep) / 10000n;
|
|
317
333
|
}
|
|
318
334
|
catch (error) {
|
|
319
335
|
if (skipQuoteOnError ?? true) {
|
|
320
336
|
quotedToken = 0n;
|
|
321
|
-
minOutToken = 0n;
|
|
322
337
|
}
|
|
323
338
|
else {
|
|
324
339
|
throw new Error(`买入报价失败: ${error}`);
|
|
325
340
|
}
|
|
326
341
|
}
|
|
327
|
-
|
|
342
|
+
// ✅ 已移除滑点保护:minOutToken 固定为 0
|
|
343
|
+
const sellAmountWei = quotedToken;
|
|
328
344
|
if (sellAmountWei <= 0n) {
|
|
329
|
-
throw new Error('卖方卖出数量为 0
|
|
345
|
+
throw new Error('卖方卖出数量为 0:报价失败');
|
|
330
346
|
}
|
|
331
|
-
return { quotedToken, minOutToken, sellAmountWei };
|
|
347
|
+
return { quotedToken, minOutToken: 0n, sellAmountWei };
|
|
332
348
|
}
|
|
333
349
|
async function ensureSellerBalance({ tokenAddress, provider, seller, sellAmountWei, skipBalanceCheck }) {
|
|
334
350
|
const erc20 = new Contract(tokenAddress, ERC20_BALANCE_ABI, provider);
|
|
@@ -400,35 +416,38 @@ async function validateNativeBalances({ sameAddress, buyerBalance, buyerFundsWei
|
|
|
400
416
|
}
|
|
401
417
|
/**
|
|
402
418
|
* ✅ 优化:使用批量 nonce 获取
|
|
419
|
+
* 交易顺序:贿赂 → 授权 → 买入 → 卖出 → 利润
|
|
403
420
|
*/
|
|
404
|
-
async function planNonces({ buyer, seller, approvalExists, extractProfit, sameAddress, nonceManager }) {
|
|
421
|
+
async function planNonces({ buyer, seller, approvalExists, extractProfit, needBribeTx, sameAddress, nonceManager }) {
|
|
405
422
|
if (sameAddress) {
|
|
406
423
|
// 同一地址:使用 getNextNonceBatch 获取连续 nonce
|
|
407
|
-
const txCount = countTruthy([approvalExists, true, true, extractProfit]);
|
|
424
|
+
const txCount = countTruthy([needBribeTx, approvalExists, true, true, extractProfit]);
|
|
408
425
|
const nonces = await nonceManager.getNextNonceBatch(buyer, txCount);
|
|
409
426
|
let idx = 0;
|
|
427
|
+
const bribeNonce = needBribeTx ? nonces[idx++] : undefined;
|
|
410
428
|
if (approvalExists)
|
|
411
429
|
idx++;
|
|
412
430
|
const buyerNonce = nonces[idx++];
|
|
413
431
|
const sellerNonce = nonces[idx++];
|
|
414
432
|
const profitNonce = extractProfit ? nonces[idx] : undefined;
|
|
415
|
-
return { buyerNonce, sellerNonce, profitNonce };
|
|
433
|
+
return { buyerNonce, sellerNonce, bribeNonce, profitNonce };
|
|
416
434
|
}
|
|
417
|
-
if (approvalExists || extractProfit) {
|
|
418
|
-
// 卖方需要多个 nonce
|
|
419
|
-
const sellerTxCount = countTruthy([approvalExists, true, extractProfit]);
|
|
435
|
+
if (needBribeTx || approvalExists || extractProfit) {
|
|
436
|
+
// 卖方需要多个 nonce:贿赂(可选) + 授权(可选) + 卖出 + 利润(可选)
|
|
437
|
+
const sellerTxCount = countTruthy([needBribeTx, approvalExists, true, extractProfit]);
|
|
420
438
|
// ✅ 优化:并行获取 buyer 和 seller 的 nonce(JSON-RPC 批量请求)
|
|
421
439
|
const [sellerNonces, buyerNonces] = await Promise.all([
|
|
422
440
|
nonceManager.getNextNonceBatch(seller, sellerTxCount),
|
|
423
441
|
nonceManager.getNextNoncesForWallets([buyer])
|
|
424
442
|
]);
|
|
425
443
|
let idx = 0;
|
|
444
|
+
const bribeNonce = needBribeTx ? sellerNonces[idx++] : undefined;
|
|
426
445
|
if (approvalExists)
|
|
427
446
|
idx++;
|
|
428
447
|
const sellerNonce = sellerNonces[idx++];
|
|
429
448
|
const profitNonce = extractProfit ? sellerNonces[idx] : undefined;
|
|
430
449
|
const buyerNonce = buyerNonces[0];
|
|
431
|
-
return { buyerNonce, sellerNonce, profitNonce };
|
|
450
|
+
return { buyerNonce, sellerNonce, bribeNonce, profitNonce };
|
|
432
451
|
}
|
|
433
452
|
// ✅ 优化:使用 getNextNoncesForWallets 批量获取(单次网络往返)
|
|
434
453
|
const nonces = await nonceManager.getNextNoncesForWallets([buyer, seller]);
|
|
@@ -13,7 +13,6 @@ export interface FlapSwapSignConfig {
|
|
|
13
13
|
txType?: 0 | 2;
|
|
14
14
|
chainId?: number;
|
|
15
15
|
reserveGasETH?: number;
|
|
16
|
-
slippageBps?: number;
|
|
17
16
|
skipQuoteOnError?: boolean;
|
|
18
17
|
skipApprovalCheck?: boolean;
|
|
19
18
|
}
|
|
@@ -23,7 +22,6 @@ export interface FlapSwapConfig extends CommonBundleConfig {
|
|
|
23
22
|
customRpcUrl?: string;
|
|
24
23
|
bundleBlockOffset?: number;
|
|
25
24
|
reserveGasETH?: number;
|
|
26
|
-
slippageBps?: number;
|
|
27
25
|
skipQuoteOnError?: boolean;
|
|
28
26
|
waitForConfirmation?: boolean;
|
|
29
27
|
waitTimeoutMs?: number;
|
|
@@ -8,7 +8,7 @@ import { calculateSellAmount } from '../../utils/swap-helpers.js';
|
|
|
8
8
|
import { NonceManager, getOptimizedGasPrice } from '../../utils/bundle-helpers.js';
|
|
9
9
|
import { FLAP_PORTAL_ADDRESSES } from '../constants.js';
|
|
10
10
|
import { PROFIT_CONFIG } from '../../utils/constants.js';
|
|
11
|
-
import { getGasPriceConfig, getTxType, getProfitRecipient } from './config.js';
|
|
11
|
+
import { getGasPriceConfig, getTxType, getProfitRecipient, getBribeAmount, BLOCKRAZOR_BUILDER_EOA } from './config.js';
|
|
12
12
|
/**
|
|
13
13
|
* 获取 Gas Limit(支持 FlapAnyConfig)
|
|
14
14
|
*/
|
|
@@ -116,7 +116,7 @@ export async function flapBundleSwapMerkle(params) {
|
|
|
116
116
|
]);
|
|
117
117
|
const { amount: sellAmountWei, decimals } = sellAmountResult;
|
|
118
118
|
const priorityFee = gasPrice / 10n === 0n ? 1n : gasPrice / 10n;
|
|
119
|
-
// ✅ 优化:第二批并行 - approval、quote
|
|
119
|
+
// ✅ 优化:第二批并行 - approval、quote(已移除滑点保护)
|
|
120
120
|
const [approvalTx, quote] = await Promise.all([
|
|
121
121
|
config.skipApprovalCheck
|
|
122
122
|
? Promise.resolve(null)
|
|
@@ -134,7 +134,6 @@ export async function flapBundleSwapMerkle(params) {
|
|
|
134
134
|
tokenAddress,
|
|
135
135
|
sellAmountWei,
|
|
136
136
|
provider: chainContext.provider,
|
|
137
|
-
slippageBps: config.slippageBps,
|
|
138
137
|
skipQuoteOnError: config.skipQuoteOnError,
|
|
139
138
|
outputToken // ✅ 传递输出代币
|
|
140
139
|
})
|
|
@@ -144,7 +143,6 @@ export async function flapBundleSwapMerkle(params) {
|
|
|
144
143
|
buyer,
|
|
145
144
|
quotedNative: quote.quotedNative,
|
|
146
145
|
reserveGasEth: config.reserveGasETH,
|
|
147
|
-
slippageBps: config.slippageBps,
|
|
148
146
|
nativeToken: chainContext.nativeToken,
|
|
149
147
|
useNativeToken,
|
|
150
148
|
quoteToken,
|
|
@@ -159,6 +157,9 @@ export async function flapBundleSwapMerkle(params) {
|
|
|
159
157
|
// 将代币利润转换为等值 BNB
|
|
160
158
|
nativeProfitAmount = await getTokenToNativeQuote(chainContext.provider, quoteToken, tokenProfitAmount, chainContext.chainId);
|
|
161
159
|
}
|
|
160
|
+
// ✅ 获取贿赂金额
|
|
161
|
+
const bribeAmount = getBribeAmount(config);
|
|
162
|
+
const needBribeTx = bribeAmount > 0n;
|
|
162
163
|
// ✅ 优化:第四批并行 - 构建交易、获取 nonces、验证余额
|
|
163
164
|
const portalSeller = new Contract(chainContext.portalAddress, PORTAL_ABI, seller);
|
|
164
165
|
const portalBuyer = new Contract(chainContext.portalAddress, PORTAL_ABI, buyer);
|
|
@@ -183,6 +184,7 @@ export async function flapBundleSwapMerkle(params) {
|
|
|
183
184
|
buyer,
|
|
184
185
|
approvalExists: !!approvalTx,
|
|
185
186
|
extractProfit: nativeProfitAmount > 0n,
|
|
187
|
+
needBribeTx, // ✅ 新增:是否需要贿赂交易
|
|
186
188
|
nonceManager
|
|
187
189
|
}),
|
|
188
190
|
validateBalances({
|
|
@@ -218,23 +220,38 @@ export async function flapBundleSwapMerkle(params) {
|
|
|
218
220
|
txType,
|
|
219
221
|
value: useNativeToken ? buyerNeed.maxBuyerValue : 0n // ✅ ERC20 购买时 value=0
|
|
220
222
|
});
|
|
221
|
-
// ✅
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
profitNonce: noncePlan.profitNonce,
|
|
223
|
+
// ✅ 贿赂交易放在首位
|
|
224
|
+
let bribeTx = null;
|
|
225
|
+
if (needBribeTx && noncePlan.bribeNonce !== undefined) {
|
|
226
|
+
bribeTx = await seller.signTransaction({
|
|
227
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
228
|
+
value: bribeAmount,
|
|
229
|
+
nonce: noncePlan.bribeNonce,
|
|
229
230
|
gasPrice,
|
|
231
|
+
gasLimit: 21000n,
|
|
230
232
|
chainId: chainContext.chainId,
|
|
231
|
-
txType
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
|
|
233
|
+
type: txType
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
// ✅ 并行签名卖出和买入交易
|
|
237
|
+
const [signedSell, signedBuy] = await Promise.all([
|
|
238
|
+
seller.signTransaction(sellTx),
|
|
239
|
+
buyer.signTransaction(buyTx)
|
|
240
|
+
]);
|
|
241
|
+
// ✅ 利润交易放在末尾
|
|
242
|
+
const profitTx = await buildProfitTransaction({
|
|
243
|
+
seller,
|
|
244
|
+
profitAmount: nativeProfitAmount, // ✅ 使用转换后的原生代币利润
|
|
245
|
+
profitNonce: noncePlan.profitNonce,
|
|
246
|
+
gasPrice,
|
|
247
|
+
chainId: chainContext.chainId,
|
|
248
|
+
txType
|
|
249
|
+
});
|
|
235
250
|
nonceManager.clearTemp();
|
|
236
|
-
//
|
|
251
|
+
// ✅ 组装顺序:贿赂 → 授权 → 卖出 → 买入 → 利润
|
|
237
252
|
const allTransactions = [];
|
|
253
|
+
if (bribeTx)
|
|
254
|
+
allTransactions.push(bribeTx);
|
|
238
255
|
if (approvalTx)
|
|
239
256
|
allTransactions.push(approvalTx);
|
|
240
257
|
allTransactions.push(signedSell, signedBuy);
|
|
@@ -301,32 +318,28 @@ async function buildApprovalTransaction({ tokenAddress, seller, provider, decima
|
|
|
301
318
|
type: txType
|
|
302
319
|
});
|
|
303
320
|
}
|
|
304
|
-
async function quoteSellOutput({ portalAddress, tokenAddress, sellAmountWei, provider,
|
|
321
|
+
async function quoteSellOutput({ portalAddress, tokenAddress, sellAmountWei, provider, skipQuoteOnError, outputToken = ZERO_ADDRESS // ✅ 默认使用原生代币
|
|
305
322
|
}) {
|
|
306
323
|
const portal = new Contract(portalAddress, PORTAL_ABI, provider);
|
|
307
|
-
const safeSlippage = Math.max(0, Math.min(5000, slippageBps ?? 100));
|
|
308
324
|
try {
|
|
309
325
|
const quotedNative = await portal.quoteExactInput.staticCall({
|
|
310
326
|
inputToken: tokenAddress,
|
|
311
327
|
outputToken, // ✅ 使用动态输出代币
|
|
312
328
|
inputAmount: sellAmountWei
|
|
313
329
|
});
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
return { quotedNative, minOutNative };
|
|
330
|
+
// ✅ 已移除滑点保护:minOutNative 固定为 0
|
|
331
|
+
return { quotedNative, minOutNative: 0n };
|
|
317
332
|
}
|
|
318
333
|
catch (err) {
|
|
319
334
|
if (skipQuoteOnError ?? true) {
|
|
320
|
-
console.warn(`⚠️ 报价失败,使用 minOut = 0: ${err}`);
|
|
321
335
|
return { quotedNative: 0n, minOutNative: 0n };
|
|
322
336
|
}
|
|
323
337
|
throw new Error(`卖出报价失败: ${err}`);
|
|
324
338
|
}
|
|
325
339
|
}
|
|
326
340
|
const ERC20_BALANCE_OF_ABI = ['function balanceOf(address) view returns (uint256)'];
|
|
327
|
-
async function calculateBuyerNeed({ buyer, quotedNative, reserveGasEth,
|
|
341
|
+
async function calculateBuyerNeed({ buyer, quotedNative, reserveGasEth, nativeToken, useNativeToken = true, quoteToken, quoteTokenDecimals = 18, provider }) {
|
|
328
342
|
const reserveGas = ethers.parseEther((reserveGasEth || 0.0005).toString());
|
|
329
|
-
const safeSlippage = Math.max(0, Math.min(5000, slippageBps ?? 100));
|
|
330
343
|
// ✅ 根据是否使用原生代币获取不同的余额
|
|
331
344
|
let buyerBalance;
|
|
332
345
|
if (useNativeToken) {
|
|
@@ -337,11 +350,8 @@ async function calculateBuyerNeed({ buyer, quotedNative, reserveGasEth, slippage
|
|
|
337
350
|
const erc20 = new Contract(quoteToken, ERC20_BALANCE_OF_ABI, provider || buyer.provider);
|
|
338
351
|
buyerBalance = await erc20.balanceOf(buyer.address);
|
|
339
352
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
const increase = BigInt(10000 + safeSlippage);
|
|
343
|
-
estimatedBuyerNeed = (quotedNative * increase) / 10000n;
|
|
344
|
-
}
|
|
353
|
+
// ✅ 已移除滑点保护:直接使用报价金额
|
|
354
|
+
const estimatedBuyerNeed = quotedNative;
|
|
345
355
|
// ✅ 原生代币需要预留 Gas,ERC20 不需要
|
|
346
356
|
const buyerNeedTotal = useNativeToken
|
|
347
357
|
? estimatedBuyerNeed + reserveGas
|
|
@@ -385,23 +395,25 @@ async function validateBalances({ buyerNeed, buyerAddress, portalGasCost, provid
|
|
|
385
395
|
}
|
|
386
396
|
/**
|
|
387
397
|
* ✅ 优化:使用批量 nonce 获取(JSON-RPC 批量请求)
|
|
398
|
+
* 交易顺序:贿赂 → 授权 → 卖出 → 买入 → 利润
|
|
388
399
|
*/
|
|
389
|
-
async function planNonces({ seller, buyer, approvalExists, extractProfit, nonceManager }) {
|
|
390
|
-
if (approvalExists || extractProfit) {
|
|
391
|
-
// 卖方需要多个 nonce
|
|
392
|
-
const sellerTxCount = countTruthy([approvalExists, true, extractProfit]);
|
|
400
|
+
async function planNonces({ seller, buyer, approvalExists, extractProfit, needBribeTx, nonceManager }) {
|
|
401
|
+
if (needBribeTx || approvalExists || extractProfit) {
|
|
402
|
+
// 卖方需要多个 nonce:贿赂(可选) + 授权(可选) + 卖出 + 利润(可选)
|
|
403
|
+
const sellerTxCount = countTruthy([needBribeTx, approvalExists, true, extractProfit]);
|
|
393
404
|
// ✅ 优化:并行获取 seller 和 buyer 的 nonce
|
|
394
405
|
const [sellerNonces, buyerNonces] = await Promise.all([
|
|
395
406
|
nonceManager.getNextNonceBatch(seller, sellerTxCount),
|
|
396
407
|
nonceManager.getNextNoncesForWallets([buyer])
|
|
397
408
|
]);
|
|
398
409
|
let idx = 0;
|
|
410
|
+
const bribeNonce = needBribeTx ? sellerNonces[idx++] : undefined;
|
|
399
411
|
if (approvalExists)
|
|
400
412
|
idx++;
|
|
401
413
|
const sellerNonce = sellerNonces[idx++];
|
|
402
414
|
const profitNonce = extractProfit ? sellerNonces[idx] : undefined;
|
|
403
415
|
const buyerNonce = buyerNonces[0];
|
|
404
|
-
return { sellerNonce, buyerNonce, profitNonce };
|
|
416
|
+
return { sellerNonce, buyerNonce, bribeNonce, profitNonce };
|
|
405
417
|
}
|
|
406
418
|
// ✅ 优化:使用 getNextNoncesForWallets 批量获取(单次网络往返)
|
|
407
419
|
const nonces = await nonceManager.getNextNoncesForWallets([seller, buyer]);
|
|
@@ -483,7 +495,7 @@ export async function flapBatchSwapMerkle(params) {
|
|
|
483
495
|
]);
|
|
484
496
|
const { amount: sellAmountWei, decimals } = sellAmountResult;
|
|
485
497
|
const priorityFee = gasPrice / 10n === 0n ? 1n : gasPrice / 10n;
|
|
486
|
-
// ✅
|
|
498
|
+
// ✅ 并行获取:授权、报价(已移除滑点保护)
|
|
487
499
|
const [approvalTx, quote] = await Promise.all([
|
|
488
500
|
config.skipApprovalCheck
|
|
489
501
|
? Promise.resolve(null)
|
|
@@ -501,15 +513,12 @@ export async function flapBatchSwapMerkle(params) {
|
|
|
501
513
|
tokenAddress,
|
|
502
514
|
sellAmountWei,
|
|
503
515
|
provider: chainContext.provider,
|
|
504
|
-
slippageBps: config.slippageBps,
|
|
505
516
|
skipQuoteOnError: config.skipQuoteOnError,
|
|
506
517
|
outputToken
|
|
507
518
|
})
|
|
508
519
|
]);
|
|
509
|
-
// ✅
|
|
510
|
-
const
|
|
511
|
-
const increase = BigInt(10000 + safeSlippage);
|
|
512
|
-
const totalBuyAmount = (quote.quotedNative * increase) / 10000n;
|
|
520
|
+
// ✅ 计算每个买方的买入金额(按比例分配,已移除滑点保护)
|
|
521
|
+
const totalBuyAmount = quote.quotedNative;
|
|
513
522
|
let buyAmountsWei;
|
|
514
523
|
if (params.buyerRatios && params.buyerRatios.length === buyers.length) {
|
|
515
524
|
// 按比例分配
|
|
@@ -575,12 +584,29 @@ export async function flapBatchSwapMerkle(params) {
|
|
|
575
584
|
// ✅ 并行获取所有 nonce
|
|
576
585
|
const sellNonce = await nonceManager.getNextNonce(seller);
|
|
577
586
|
const buyerNonces = await Promise.all(buyers.map(buyer => nonceManager.getNextNonce(buyer)));
|
|
578
|
-
//
|
|
587
|
+
// ✅ 贿赂交易和利润交易都由卖方发送(保持一致)
|
|
588
|
+
const bribeAmount = getBribeAmount(config);
|
|
589
|
+
let bribeTx = null;
|
|
590
|
+
let bribeNonceOffset = 0;
|
|
591
|
+
if (bribeAmount > 0n) {
|
|
592
|
+
const bribeNonce = sellNonce; // 使用卖方的第一个 nonce
|
|
593
|
+
bribeTx = await seller.signTransaction({
|
|
594
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
595
|
+
value: bribeAmount,
|
|
596
|
+
nonce: bribeNonce,
|
|
597
|
+
gasPrice,
|
|
598
|
+
gasLimit: 21000n,
|
|
599
|
+
chainId: chainContext.chainId,
|
|
600
|
+
type: txType
|
|
601
|
+
});
|
|
602
|
+
bribeNonceOffset = 1; // 卖出交易的 nonce 需要 +1
|
|
603
|
+
}
|
|
604
|
+
// 利润交易放在末尾(由卖方发送,与贿赂交易同一钱包)
|
|
579
605
|
let profitTx = null;
|
|
580
606
|
if (nativeProfitAmount > 0n) {
|
|
581
|
-
|
|
582
|
-
const profitNonce =
|
|
583
|
-
profitTx = await
|
|
607
|
+
// 利润交易 nonce = 卖出 nonce + 1
|
|
608
|
+
const profitNonce = sellNonce + bribeNonceOffset + 1;
|
|
609
|
+
profitTx = await seller.signTransaction({
|
|
584
610
|
to: getProfitRecipient(),
|
|
585
611
|
value: nativeProfitAmount,
|
|
586
612
|
nonce: profitNonce,
|
|
@@ -594,7 +620,7 @@ export async function flapBatchSwapMerkle(params) {
|
|
|
594
620
|
// ✅ 并行签名所有交易
|
|
595
621
|
const sellTx = buildTransactionRequest(sellUnsigned, {
|
|
596
622
|
from: seller.address,
|
|
597
|
-
nonce: sellNonce,
|
|
623
|
+
nonce: sellNonce + bribeNonceOffset, // ✅ 考虑贿赂交易的 nonce 偏移
|
|
598
624
|
gasLimit: finalGasLimit,
|
|
599
625
|
gasPrice,
|
|
600
626
|
priorityFee,
|
|
@@ -618,8 +644,10 @@ export async function flapBatchSwapMerkle(params) {
|
|
|
618
644
|
return buyer.signTransaction(buyTx);
|
|
619
645
|
})
|
|
620
646
|
]);
|
|
621
|
-
// ✅
|
|
647
|
+
// ✅ 按顺序组装交易数组:贿赂 → 授权 → 卖出 → 买入 → 利润
|
|
622
648
|
const signedTransactions = [];
|
|
649
|
+
if (bribeTx)
|
|
650
|
+
signedTransactions.push(bribeTx);
|
|
623
651
|
if (approvalTx)
|
|
624
652
|
signedTransactions.push(approvalTx);
|
|
625
653
|
signedTransactions.push(signedSell);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
export * as Abis from './abis/index.js';
|
|
2
|
-
export * as Sol from './sol/index.js';
|
|
3
2
|
export { ADDRESSES, CHAIN } from './utils/constants.js';
|
|
4
3
|
export { isExclusiveOnChain, isExclusiveOffChain } from './utils/mpcExclusive.js';
|
|
5
|
-
export { ensureSellApprovalV1, checkSellApprovalV1, ensureSellApprovalV2, checkSellApprovalV2, ensureSellApproval, checkSellApproval, ensureFlapSellApproval, checkFlapSellApproval, ensureFlapSellApprovalBatch, checkFlapSellApprovalBatch, checkAllowance, approveToken, checkAllowanceBatch, approveTokenBatch, checkAllowanceRaw, approveTokenRaw, checkAllowanceBatchRaw, approveTokenBatchRaw, type EnsureAllowanceBatchItemResult, type ApproveTokenBatchParams, type ApproveTokenBatchRawParams, type ApproveTokenBatchResult } from './utils/erc20.js';
|
|
4
|
+
export { ensureSellApprovalV1, checkSellApprovalV1, ensureSellApprovalV2, checkSellApprovalV2, ensureSellApproval, checkSellApproval, ensureFlapSellApproval, checkFlapSellApproval, ensureFlapSellApprovalBatch, checkFlapSellApprovalBatch, checkAllowance, approveToken, checkAllowanceBatch, approveTokenBatch, checkAllowanceRaw, approveTokenRaw, checkAllowanceBatchRaw, approveTokenBatchRaw, type EnsureAllowanceBatchItemResult, type ApproveTokenBatchParams, type ApproveTokenBatchRawParams, type ApproveTokenBatchResult, type ApproveTokenBatchSignResult } from './utils/erc20.js';
|
|
6
5
|
export { parseFourError, type FourErrorCode } from './utils/errors.js';
|
|
7
6
|
export { getTokenManagerV1, getTokenManagerV2, getTokenManagerHelper3, getTokenManagerV1Writer, getTokenManagerV2Writer, getTokenManagerHelper3Writer, getTokenManagerAddress, type ChainName } from './utils/contract-factory.js';
|
|
8
7
|
export { FourClient, buildLoginMessage, type FourConfig, type GenerateNonceReq, type LoginReq, type CreateTokenReq, type CreateTokenResp } from './clients/four.js';
|
package/dist/index.js
CHANGED
|
@@ -13,9 +13,9 @@ export interface PancakeBuyFirstSignConfig {
|
|
|
13
13
|
txType?: 0 | 2;
|
|
14
14
|
chainId?: number;
|
|
15
15
|
reserveGasBNB?: number;
|
|
16
|
-
slippageBps?: number;
|
|
17
16
|
skipQuoteOnError?: boolean;
|
|
18
17
|
skipApprovalCheck?: boolean;
|
|
18
|
+
bribeAmount?: number;
|
|
19
19
|
}
|
|
20
20
|
export type SwapRouteType = 'v2' | 'v3-single' | 'v3-multi';
|
|
21
21
|
export interface V2RouteParams {
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
import { ethers, Contract, Wallet } from 'ethers';
|
|
7
7
|
import { NonceManager } from '../utils/bundle-helpers.js';
|
|
8
8
|
import { ADDRESSES, PROFIT_CONFIG } from '../utils/constants.js';
|
|
9
|
+
// ✅ BlockRazor Builder EOA 地址(用于贿赂)
|
|
10
|
+
// 参考文档: https://blockrazor.gitbook.io/blockrazor/bsc/block-builder/send-bundle
|
|
11
|
+
const BLOCKRAZOR_BUILDER_EOA = '0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20';
|
|
9
12
|
function getGasLimit(config, defaultGas = 800000) {
|
|
10
13
|
if (config.gasLimit !== undefined) {
|
|
11
14
|
return typeof config.gasLimit === 'bigint' ? config.gasLimit : BigInt(config.gasLimit);
|
|
@@ -98,8 +101,7 @@ export async function pancakeBundleBuyFirstMerkle(params) {
|
|
|
98
101
|
const buyerNeed = calculateBuyerNeed({
|
|
99
102
|
quotedNative,
|
|
100
103
|
buyerBalance: buyerFundsInfo.buyerBalance,
|
|
101
|
-
reserveGas: buyerFundsInfo.reserveGas
|
|
102
|
-
slippageBps: config.slippageBps
|
|
104
|
+
reserveGas: buyerFundsInfo.reserveGas
|
|
103
105
|
});
|
|
104
106
|
const finalGasLimit = getGasLimit(config);
|
|
105
107
|
const gasPrice = await getGasPrice(context.provider, config);
|
|
@@ -125,14 +127,33 @@ export async function pancakeBundleBuyFirstMerkle(params) {
|
|
|
125
127
|
});
|
|
126
128
|
const profitBase = estimatedProfitFromSell > 0n ? estimatedProfitFromSell : (buyerFundsInfo.buyerFundsWei * BigInt(PROFIT_CONFIG.RATE_BPS)) / 10000n;
|
|
127
129
|
const profitAmount = profitBase;
|
|
130
|
+
// ✅ 获取贿赂金额
|
|
131
|
+
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
132
|
+
? ethers.parseEther(String(config.bribeAmount))
|
|
133
|
+
: 0n;
|
|
134
|
+
const needBribeTx = bribeAmount > 0n;
|
|
128
135
|
const noncePlan = await planNonces({
|
|
129
136
|
buyer,
|
|
130
137
|
seller,
|
|
131
138
|
sameAddress,
|
|
132
139
|
approvalExists: !!approvalTx,
|
|
133
140
|
extractProfit: profitAmount > 0n,
|
|
141
|
+
needBribeTx, // ✅ 新增
|
|
134
142
|
nonceManager
|
|
135
143
|
});
|
|
144
|
+
// ✅ 贿赂交易放在首位(由卖方发送,与利润交易同一钱包)
|
|
145
|
+
let bribeTx = null;
|
|
146
|
+
if (needBribeTx && noncePlan.bribeNonce !== undefined) {
|
|
147
|
+
bribeTx = await seller.signTransaction({
|
|
148
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
149
|
+
value: bribeAmount,
|
|
150
|
+
nonce: noncePlan.bribeNonce,
|
|
151
|
+
gasPrice,
|
|
152
|
+
gasLimit: 21000n,
|
|
153
|
+
chainId: context.chainId,
|
|
154
|
+
type: txType
|
|
155
|
+
});
|
|
156
|
+
}
|
|
136
157
|
const signedBuy = await buyer.signTransaction({
|
|
137
158
|
...swapUnsigned.buyUnsigned,
|
|
138
159
|
from: buyer.address,
|
|
@@ -151,6 +172,7 @@ export async function pancakeBundleBuyFirstMerkle(params) {
|
|
|
151
172
|
chainId: context.chainId,
|
|
152
173
|
type: txType
|
|
153
174
|
});
|
|
175
|
+
// ✅ 利润交易放在末尾
|
|
154
176
|
const profitTx = await buildProfitTransaction({
|
|
155
177
|
seller,
|
|
156
178
|
profitAmount,
|
|
@@ -172,7 +194,10 @@ export async function pancakeBundleBuyFirstMerkle(params) {
|
|
|
172
194
|
provider: context.provider,
|
|
173
195
|
buyerAddress: buyer.address
|
|
174
196
|
});
|
|
197
|
+
// ✅ 组装顺序:贿赂 → 授权 → 买入 → 卖出 → 利润
|
|
175
198
|
const allTransactions = [];
|
|
199
|
+
if (bribeTx)
|
|
200
|
+
allTransactions.push(bribeTx);
|
|
176
201
|
if (approvalTx)
|
|
177
202
|
allTransactions.push(approvalTx);
|
|
178
203
|
allTransactions.push(signedBuy, signedSell);
|
|
@@ -303,13 +328,9 @@ async function quoteSellerNative({ provider, tokenAddress, sellAmountToken }) {
|
|
|
303
328
|
return 0n;
|
|
304
329
|
}
|
|
305
330
|
}
|
|
306
|
-
function calculateBuyerNeed({ quotedNative, buyerBalance, reserveGas
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
const safeSlippage = Math.max(0, Math.min(5000, slippageBps ?? 100));
|
|
310
|
-
const increase = BigInt(10000 + safeSlippage);
|
|
311
|
-
estimatedBuyerNeed = (quotedNative * increase) / 10000n;
|
|
312
|
-
}
|
|
331
|
+
function calculateBuyerNeed({ quotedNative, buyerBalance, reserveGas }) {
|
|
332
|
+
// ✅ 已移除滑点保护:直接使用报价金额
|
|
333
|
+
const estimatedBuyerNeed = quotedNative;
|
|
313
334
|
const buyerNeedTotal = estimatedBuyerNeed + reserveGas;
|
|
314
335
|
if (buyerBalance < buyerNeedTotal) {
|
|
315
336
|
throw new Error(`买方余额不足:\n - 需要: ${ethers.formatEther(buyerNeedTotal)} BNB\n - 实际: ${ethers.formatEther(buyerBalance)} BNB`);
|
|
@@ -381,28 +402,39 @@ async function estimateProfitAmount({ provider, tokenAddress, sellAmountToken })
|
|
|
381
402
|
return 0n;
|
|
382
403
|
}
|
|
383
404
|
}
|
|
384
|
-
|
|
405
|
+
/**
|
|
406
|
+
* ✅ 规划 nonce
|
|
407
|
+
* 交易顺序:贿赂 → 授权 → 买入 → 卖出 → 利润
|
|
408
|
+
*/
|
|
409
|
+
async function planNonces({ buyer, seller, sameAddress, approvalExists, extractProfit, needBribeTx, nonceManager }) {
|
|
385
410
|
if (sameAddress) {
|
|
386
|
-
|
|
411
|
+
// 同一地址:贿赂(可选) + 授权(可选) + 买入 + 卖出 + 利润(可选)
|
|
412
|
+
const txCount = countTruthy([needBribeTx, approvalExists, true, true, extractProfit]);
|
|
387
413
|
const nonces = await nonceManager.getNextNonceBatch(buyer, txCount);
|
|
388
414
|
let idx = 0;
|
|
415
|
+
const bribeNonce = needBribeTx ? nonces[idx++] : undefined;
|
|
389
416
|
if (approvalExists)
|
|
390
417
|
idx++;
|
|
391
418
|
const buyerNonce = nonces[idx++];
|
|
392
419
|
const sellerNonce = nonces[idx++];
|
|
393
420
|
const profitNonce = extractProfit ? nonces[idx] : undefined;
|
|
394
|
-
return { buyerNonce, sellerNonce, profitNonce };
|
|
421
|
+
return { buyerNonce, sellerNonce, bribeNonce, profitNonce };
|
|
395
422
|
}
|
|
396
|
-
if (approvalExists || extractProfit) {
|
|
397
|
-
|
|
398
|
-
const
|
|
423
|
+
if (needBribeTx || approvalExists || extractProfit) {
|
|
424
|
+
// 卖方需要多个 nonce:贿赂(可选) + 授权(可选) + 卖出 + 利润(可选)
|
|
425
|
+
const sellerTxCount = countTruthy([needBribeTx, approvalExists, true, extractProfit]);
|
|
426
|
+
// ✅ 并行获取 seller 和 buyer 的 nonce
|
|
427
|
+
const [sellerNonces, buyerNonce] = await Promise.all([
|
|
428
|
+
nonceManager.getNextNonceBatch(seller, sellerTxCount),
|
|
429
|
+
nonceManager.getNextNonce(buyer)
|
|
430
|
+
]);
|
|
399
431
|
let idx = 0;
|
|
432
|
+
const bribeNonce = needBribeTx ? sellerNonces[idx++] : undefined;
|
|
400
433
|
if (approvalExists)
|
|
401
434
|
idx++;
|
|
402
435
|
const sellerNonce = sellerNonces[idx++];
|
|
403
436
|
const profitNonce = extractProfit ? sellerNonces[idx] : undefined;
|
|
404
|
-
|
|
405
|
-
return { buyerNonce, sellerNonce, profitNonce };
|
|
437
|
+
return { buyerNonce, sellerNonce, bribeNonce, profitNonce };
|
|
406
438
|
}
|
|
407
439
|
const [buyerNonce, sellerNonce] = await Promise.all([
|
|
408
440
|
nonceManager.getNextNonce(buyer),
|