four-flap-meme-sdk 1.3.94 → 1.3.95
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/internal.d.ts +1 -1
- package/dist/contracts/tm-bundle-merkle/internal.js +4 -3
- 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/contracts/tm-bundle-merkle/utils.js +7 -7
- 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 -157
- package/dist/sol/dex/meteora/dlmm.js +0 -671
- 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 -900
- 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
|
@@ -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),
|
|
@@ -8,8 +8,8 @@ export interface PancakeSwapSignConfig {
|
|
|
8
8
|
txType?: 0 | 2;
|
|
9
9
|
chainId?: number;
|
|
10
10
|
reserveGasBNB?: number;
|
|
11
|
-
slippageTolerance?: number;
|
|
12
11
|
skipApprovalCheck?: boolean;
|
|
12
|
+
bribeAmount?: number;
|
|
13
13
|
}
|
|
14
14
|
export type SwapRouteType = 'v2' | 'v3-single' | 'v3-multi';
|
|
15
15
|
export interface PancakeSwapConfig extends CommonBundleConfig {
|
|
@@ -46,7 +46,6 @@ export interface PancakeBundleSwapSignParams {
|
|
|
46
46
|
buyerPrivateKey: string;
|
|
47
47
|
tokenAddress: string;
|
|
48
48
|
routeParams: RouteParams;
|
|
49
|
-
slippageTolerance?: number;
|
|
50
49
|
config: PancakeSwapSignConfig;
|
|
51
50
|
quoteToken?: string;
|
|
52
51
|
quoteTokenDecimals?: number;
|
|
@@ -58,7 +57,6 @@ export interface PancakeBundleSwapParams {
|
|
|
58
57
|
buyerPrivateKey: string;
|
|
59
58
|
tokenAddress: string;
|
|
60
59
|
routeParams: RouteParams;
|
|
61
|
-
slippageTolerance?: number;
|
|
62
60
|
config: PancakeSwapConfig;
|
|
63
61
|
}
|
|
64
62
|
/** ✅ Pancake Swap 结果(简化版) */
|
|
@@ -90,7 +88,6 @@ export interface PancakeBatchSwapSignParams {
|
|
|
90
88
|
buyerRatios?: number[];
|
|
91
89
|
tokenAddress: string;
|
|
92
90
|
routeParams: RouteParams;
|
|
93
|
-
slippageTolerance?: number;
|
|
94
91
|
config: PancakeSwapSignConfig;
|
|
95
92
|
quoteToken?: string;
|
|
96
93
|
quoteTokenDecimals?: number;
|