four-flap-meme-sdk 1.7.80 → 1.7.82
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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ethers, Wallet } from 'ethers';
|
|
2
2
|
import { getOptimizedGasPrice, NonceManager, buildProfitHopTransactions, PROFIT_HOP_COUNT } from '../../utils/bundle-helpers.js';
|
|
3
|
-
import { PROFIT_CONFIG, ZERO_ADDRESS } from '../../utils/constants.js';
|
|
4
|
-
import { getTxType, getGasPriceConfig, shouldExtractProfit, getProfitRecipient } from './config.js';
|
|
3
|
+
import { PROFIT_CONFIG, ZERO_ADDRESS, BLOCKRAZOR_BUILDER_EOA } from '../../utils/constants.js';
|
|
4
|
+
import { getTxType, getGasPriceConfig, shouldExtractProfit, getProfitRecipient, getBribeAmount } from './config.js';
|
|
5
5
|
import { getErc20DecimalsMerkle as _getErc20DecimalsMerkle, generateHopWallets as _generateHopWallets, normalizeAmounts as _normalizeAmounts, batchGetBalances as _batchGetBalances, calculateGasLimit as _calculateGasLimit, isNativeTokenAddress as _isNativeTokenAddress } from './internal.js';
|
|
6
6
|
/**
|
|
7
7
|
* 根据用户类型获取利润费率(bps)
|
|
@@ -27,8 +27,8 @@ function calculateProfit(amount, userType) {
|
|
|
27
27
|
import { quote, QUOTE_CONFIG } from '../../utils/quote-helpers.js';
|
|
28
28
|
import { Helper3 } from '../helper3.js';
|
|
29
29
|
import { FlapPortal } from '../../flap/portal.js';
|
|
30
|
-
/** 最低利润(Wei
|
|
31
|
-
const MIN_PROFIT_WEI =
|
|
30
|
+
/** 最低利润(Wei):报价成功但金额过低时的阈值,0.00000001 BNB */
|
|
31
|
+
const MIN_PROFIT_WEI = 10000000000n; // 0.00000001 BNB = 10^10 wei(降低阈值以支持小额利润多跳)
|
|
32
32
|
/** 链 ID → 链名称 映射 */
|
|
33
33
|
const CHAIN_ID_TO_NAME = {
|
|
34
34
|
56: 'BSC',
|
|
@@ -261,7 +261,10 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
261
261
|
const nonceManager = new NonceManager(provider);
|
|
262
262
|
if (!preparedHops) {
|
|
263
263
|
// ========== 无多跳:直接批量转账 ==========
|
|
264
|
-
|
|
264
|
+
// ✅ BSC 链贿赂支持
|
|
265
|
+
const bribeAmount = chainIdNum === 56 ? getBribeAmount(config) : 0n;
|
|
266
|
+
const needBribeTx = bribeAmount > 0n;
|
|
267
|
+
const extraTxCount = (extractProfit ? 1 : 0) + (needBribeTx ? 1 : 0);
|
|
265
268
|
const totalTxCount = recipients.length + extraTxCount;
|
|
266
269
|
// ✅ 优化:并行获取 gasPrice 和 nonces
|
|
267
270
|
const [gasPrice, nonces] = await Promise.all([
|
|
@@ -270,6 +273,21 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
270
273
|
? Promise.resolve(Array.from({ length: totalTxCount }, (_, i) => startNonce + i))
|
|
271
274
|
: nonceManager.getNextNonceBatch(mainWallet, totalTxCount)
|
|
272
275
|
]);
|
|
276
|
+
// ✅ 贿赂交易放在最前面
|
|
277
|
+
let nonceOffset = 0;
|
|
278
|
+
if (needBribeTx) {
|
|
279
|
+
const bribeTx = await mainWallet.signTransaction({
|
|
280
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
281
|
+
value: bribeAmount,
|
|
282
|
+
nonce: nonces[nonceOffset++],
|
|
283
|
+
gasPrice,
|
|
284
|
+
gasLimit: 21000n,
|
|
285
|
+
chainId: chainIdNum,
|
|
286
|
+
type: txType
|
|
287
|
+
});
|
|
288
|
+
signedTxs.push(bribeTx);
|
|
289
|
+
console.log(`[disperse] 贿赂交易已添加: ${ethers.formatEther(bribeAmount)} BNB`);
|
|
290
|
+
}
|
|
273
291
|
if (isNative) {
|
|
274
292
|
// ✅ 原生币:先计算所有金额和利润(同步),再并行签名
|
|
275
293
|
const txDataList = recipients.map((to, i) => {
|
|
@@ -281,7 +299,7 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
281
299
|
actualAmount = remaining;
|
|
282
300
|
totalProfit += profit;
|
|
283
301
|
}
|
|
284
|
-
return { to, value: actualAmount, nonce: nonces[i] };
|
|
302
|
+
return { to, value: actualAmount, nonce: nonces[nonceOffset + i] };
|
|
285
303
|
});
|
|
286
304
|
// ✅ 并行签名所有交易
|
|
287
305
|
const txPromises = txDataList.map(({ to, value, nonce }) => mainWallet.signTransaction({
|
|
@@ -305,7 +323,7 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
305
323
|
gasPrice,
|
|
306
324
|
chainId: chainIdNum,
|
|
307
325
|
txType,
|
|
308
|
-
startNonce: nonces[recipients.length]
|
|
326
|
+
startNonce: nonces[nonceOffset + recipients.length]
|
|
309
327
|
});
|
|
310
328
|
signedTxs.push(...profitHopResult.signedTransactions);
|
|
311
329
|
profitHopWallets = profitHopResult.hopWallets; // ✅ 收集利润多跳钱包
|
|
@@ -355,7 +373,7 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
355
373
|
}
|
|
356
374
|
// 报价成功:全额分发 ERC20(不扣 ERC20)
|
|
357
375
|
const data = iface.encodeFunctionData('transfer', [to, actualAmount]);
|
|
358
|
-
return { data, nonce: nonces[i] };
|
|
376
|
+
return { data, nonce: nonces[nonceOffset + i] };
|
|
359
377
|
});
|
|
360
378
|
// ✅ 并行签名所有交易
|
|
361
379
|
const txPromises = txDataList.map(({ data, nonce }) => mainWallet.signTransaction({
|
|
@@ -369,7 +387,7 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
369
387
|
type: txType
|
|
370
388
|
}));
|
|
371
389
|
signedTxs.push(...(await Promise.all(txPromises)));
|
|
372
|
-
// ✅ 利润多跳转账(强制 2 跳中转)- 仅在报价成功时扣 OKB
|
|
390
|
+
// ✅ 利润多跳转账(强制 2 跳中转)- 仅在报价成功时扣 OKB/BNB
|
|
373
391
|
if (extractProfit && profitIsNative && nativeProfitAmount > 0n) {
|
|
374
392
|
const profitHopResult = await buildProfitHopTransactions({
|
|
375
393
|
provider,
|
|
@@ -380,15 +398,19 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
380
398
|
gasPrice,
|
|
381
399
|
chainId: chainIdNum,
|
|
382
400
|
txType,
|
|
383
|
-
startNonce: nonces[recipients.length]
|
|
401
|
+
startNonce: nonces[nonceOffset + recipients.length]
|
|
384
402
|
});
|
|
385
403
|
signedTxs.push(...profitHopResult.signedTransactions);
|
|
386
404
|
profitHopWallets = profitHopResult.hopWallets; // ✅ 收集利润多跳钱包
|
|
405
|
+
console.log(`[disperse ERC20] 利润多跳已添加: ${ethers.formatEther(nativeProfitAmount)} 原生币`);
|
|
387
406
|
}
|
|
388
407
|
}
|
|
389
408
|
}
|
|
390
409
|
else {
|
|
391
410
|
// ========== 有多跳:构建跳转链 ==========
|
|
411
|
+
// ✅ BSC 链贿赂支持
|
|
412
|
+
const bribeAmountHop = chainIdNum === 56 ? getBribeAmount(config) : 0n;
|
|
413
|
+
const needBribeTxHop = bribeAmountHop > 0n;
|
|
392
414
|
// ✅ 优化:并行获取 gasPrice 和 decimals
|
|
393
415
|
const [gasPrice, decimals] = await Promise.all([
|
|
394
416
|
getOptimizedGasPrice(provider, getGasPriceConfig(config)),
|
|
@@ -409,6 +431,9 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
409
431
|
// - 原生代币多跳:主钱包只需要 1 个 nonce(一笔转账包含后续所有 gas)
|
|
410
432
|
// - ERC20 多跳:主钱包需要 2 个 nonce(转 gas + 转 ERC20)
|
|
411
433
|
let mainWalletNonceCount = 0;
|
|
434
|
+
// 贿赂交易需要 1 个额外的 nonce
|
|
435
|
+
if (needBribeTxHop)
|
|
436
|
+
mainWalletNonceCount += 1;
|
|
412
437
|
for (let i = 0; i < recipients.length; i++) {
|
|
413
438
|
const hopChain = preparedHops[i];
|
|
414
439
|
if (hopChain.length === 0) {
|
|
@@ -430,6 +455,20 @@ export async function disperseWithBundleMerkle(params) {
|
|
|
430
455
|
? Array.from({ length: mainWalletNonceCount }, (_, i) => startNonce + i)
|
|
431
456
|
: await nonceManager.getNextNonceBatch(mainWallet, mainWalletNonceCount);
|
|
432
457
|
let mainNonceIdx = 0;
|
|
458
|
+
// ✅ 贿赂交易放在最前面(多跳场景)
|
|
459
|
+
if (needBribeTxHop) {
|
|
460
|
+
const bribeTx = await mainWallet.signTransaction({
|
|
461
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
462
|
+
value: bribeAmountHop,
|
|
463
|
+
nonce: allMainNonces[mainNonceIdx++],
|
|
464
|
+
gasPrice,
|
|
465
|
+
gasLimit: 21000n,
|
|
466
|
+
chainId: chainIdNum,
|
|
467
|
+
type: txType
|
|
468
|
+
});
|
|
469
|
+
signedTxs.push(bribeTx);
|
|
470
|
+
console.log(`[disperse with hops] 贿赂交易已添加: ${ethers.formatEther(bribeAmountHop)} BNB`);
|
|
471
|
+
}
|
|
433
472
|
const txsToSign = [];
|
|
434
473
|
// ✅ ERC20 多跳:累计代币利润,最后统一转换为原生代币
|
|
435
474
|
let totalTokenProfit = 0n;
|
|
@@ -727,6 +766,28 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
727
766
|
let totalProfit = 0n;
|
|
728
767
|
let totalAmountBeforeProfit = 0n;
|
|
729
768
|
let profitHopWallets; // ✅ 收集利润多跳钱包
|
|
769
|
+
// ✅ BSC 链贿赂支持(由 targetWallet 支付,如果提供了 targetPrivateKey)
|
|
770
|
+
const bribeAmount = chainIdNum === 56 ? getBribeAmount(config) : 0n;
|
|
771
|
+
const needBribeTx = bribeAmount > 0n && targetWallet !== null;
|
|
772
|
+
const bribeSignedTx = [];
|
|
773
|
+
// ✅ 贿赂交易放在最前面(由 targetWallet 签名)
|
|
774
|
+
if (needBribeTx && targetWallet) {
|
|
775
|
+
const [bribeGasPrice, bribeNonce] = await Promise.all([
|
|
776
|
+
getOptimizedGasPrice(provider, getGasPriceConfig(config)),
|
|
777
|
+
provider.getTransactionCount(targetWallet.address)
|
|
778
|
+
]);
|
|
779
|
+
const bribeTx = await targetWallet.signTransaction({
|
|
780
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
781
|
+
value: bribeAmount,
|
|
782
|
+
nonce: bribeNonce,
|
|
783
|
+
gasPrice: bribeGasPrice,
|
|
784
|
+
gasLimit: 21000n,
|
|
785
|
+
chainId: chainIdNum,
|
|
786
|
+
type: txType
|
|
787
|
+
});
|
|
788
|
+
bribeSignedTx.push(bribeTx);
|
|
789
|
+
console.log(`[sweep] 贿赂交易已添加: ${ethers.formatEther(bribeAmount)} BNB (by targetWallet)`);
|
|
790
|
+
}
|
|
730
791
|
if (!preparedHops) {
|
|
731
792
|
// ========== 无多跳:直接批量归集 ==========
|
|
732
793
|
const wallets = actualKeys.map(pk => new Wallet(pk, provider));
|
|
@@ -1403,9 +1464,9 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
1403
1464
|
}
|
|
1404
1465
|
}
|
|
1405
1466
|
}
|
|
1406
|
-
// ✅
|
|
1467
|
+
// ✅ 简化返回:贿赂交易放在最前面,然后是签名交易、多跳钱包和元数据
|
|
1407
1468
|
return {
|
|
1408
|
-
signedTransactions: signedTxs,
|
|
1469
|
+
signedTransactions: [...bribeSignedTx, ...signedTxs],
|
|
1409
1470
|
hopWallets: preparedHops || undefined,
|
|
1410
1471
|
profitHopWallets, // ✅ 返回利润多跳钱包
|
|
1411
1472
|
metadata: extractProfit ? {
|
|
@@ -1414,7 +1475,9 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
1414
1475
|
profitRecipient: getProfitRecipient(),
|
|
1415
1476
|
sourceCount: actualKeys.length,
|
|
1416
1477
|
isNative,
|
|
1417
|
-
tokenAddress: isNative ? undefined : tokenAddress
|
|
1478
|
+
tokenAddress: isNative ? undefined : tokenAddress,
|
|
1479
|
+
hasBribeTx: bribeSignedTx.length > 0, // ✅ 标记是否有贿赂交易
|
|
1480
|
+
bribeAmount: bribeSignedTx.length > 0 ? ethers.formatEther(bribeAmount) : undefined
|
|
1418
1481
|
} : undefined
|
|
1419
1482
|
};
|
|
1420
1483
|
}
|
|
@@ -1498,6 +1561,10 @@ export async function pairwiseTransferWithBundleMerkle(params) {
|
|
|
1498
1561
|
if (!isNative && extractProfit && totalTokenProfit > 0n) {
|
|
1499
1562
|
totalProfitNative = await getTokenToNativeQuote(provider, tokenAddress, totalTokenProfit, chainIdNum, tokenPoolType, quoteToken, config.rpcUrl);
|
|
1500
1563
|
}
|
|
1564
|
+
// ✅ BSC 链贿赂支持(由第一个 sender 支付)
|
|
1565
|
+
const bribeAmount = chainIdNum === 56 ? getBribeAmount(config) : 0n;
|
|
1566
|
+
const needBribeTx = bribeAmount > 0n;
|
|
1567
|
+
const bribeSignedTx = [];
|
|
1501
1568
|
// 计算每个 sender 需要的 nonce 数量(聚合到地址)
|
|
1502
1569
|
const senderWallets = senderPrivateKeys.map(pk => new Wallet(pk, provider));
|
|
1503
1570
|
const senderAddrLowerList = senderWallets.map(w => w.address.toLowerCase());
|
|
@@ -1517,6 +1584,12 @@ export async function pairwiseTransferWithBundleMerkle(params) {
|
|
|
1517
1584
|
else
|
|
1518
1585
|
nonceNeedBySender.set(addrLower, { wallet: senderWallets[i], count: perPairTxCount });
|
|
1519
1586
|
}
|
|
1587
|
+
// ✅ 贿赂交易需要 1 个额外的 nonce
|
|
1588
|
+
if (needBribeTx) {
|
|
1589
|
+
const cur = nonceNeedBySender.get(firstSenderAddrLower);
|
|
1590
|
+
if (cur)
|
|
1591
|
+
cur.count += 1;
|
|
1592
|
+
}
|
|
1520
1593
|
if (extractProfit && totalProfitNative > 0n) {
|
|
1521
1594
|
const cur = nonceNeedBySender.get(firstSenderAddrLower);
|
|
1522
1595
|
if (cur)
|
|
@@ -1671,6 +1744,22 @@ export async function pairwiseTransferWithBundleMerkle(params) {
|
|
|
1671
1744
|
}
|
|
1672
1745
|
}
|
|
1673
1746
|
}
|
|
1747
|
+
// ✅ 贿赂交易签名(放在最前面)
|
|
1748
|
+
if (needBribeTx) {
|
|
1749
|
+
const firstSenderWallet = senderWallets[0];
|
|
1750
|
+
const bribeNonce = popNonce(firstSenderAddrLower);
|
|
1751
|
+
const bribeTx = await firstSenderWallet.signTransaction({
|
|
1752
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
1753
|
+
value: bribeAmount,
|
|
1754
|
+
nonce: bribeNonce,
|
|
1755
|
+
gasPrice,
|
|
1756
|
+
gasLimit: 21000n,
|
|
1757
|
+
chainId: chainIdNum,
|
|
1758
|
+
type: txType
|
|
1759
|
+
});
|
|
1760
|
+
bribeSignedTx.push(bribeTx);
|
|
1761
|
+
console.log(`[pairwise] 贿赂交易已添加: ${ethers.formatEther(bribeAmount)} BNB`);
|
|
1762
|
+
}
|
|
1674
1763
|
const signedTxs = await Promise.all(txsToSign.map(({ wallet, tx }) => wallet.signTransaction(tx)));
|
|
1675
1764
|
// 利润多跳(固定 2 hop)
|
|
1676
1765
|
let profitHopWallets;
|
|
@@ -1692,7 +1781,7 @@ export async function pairwiseTransferWithBundleMerkle(params) {
|
|
|
1692
1781
|
profitHopWallets = profitHopResult.hopWallets;
|
|
1693
1782
|
}
|
|
1694
1783
|
return {
|
|
1695
|
-
signedTransactions: signedTxs,
|
|
1784
|
+
signedTransactions: [...bribeSignedTx, ...signedTxs], // ✅ 贿赂交易放在最前面
|
|
1696
1785
|
hopWallets: preparedHops || undefined,
|
|
1697
1786
|
profitHopWallets,
|
|
1698
1787
|
metadata: extractProfit ? {
|
|
@@ -1702,6 +1791,8 @@ export async function pairwiseTransferWithBundleMerkle(params) {
|
|
|
1702
1791
|
totalAmount: isNative ? ethers.formatEther(totalAmountBeforeProfit) : ethers.formatUnits(totalAmountBeforeProfit, decimals),
|
|
1703
1792
|
profitAmount: ethers.formatEther(totalProfitNative),
|
|
1704
1793
|
profitRecipient: getProfitRecipient(),
|
|
1794
|
+
hasBribeTx: bribeSignedTx.length > 0, // ✅ 标记是否有贿赂交易
|
|
1795
|
+
bribeAmount: bribeSignedTx.length > 0 ? ethers.formatEther(bribeAmount) : undefined
|
|
1705
1796
|
} : undefined
|
|
1706
1797
|
};
|
|
1707
1798
|
}
|
|
@@ -305,10 +305,7 @@ export async function flapBundleCreateToDex(params) {
|
|
|
305
305
|
// V4: 提供了 dexId 或 lpFeeProfile(支持 DEX 选择和 LP 费率配置)
|
|
306
306
|
// V3: 提供了 extensionID(支持扩展数据)
|
|
307
307
|
// V2: 默认
|
|
308
|
-
console.log('[create-to-dex] 🔍 taxV2Config:', params.taxV2Config);
|
|
309
|
-
console.log('[create-to-dex] 🔍 taxRate:', params.taxRate);
|
|
310
308
|
const useV5 = params.taxV2Config !== undefined && (params.taxRate ?? 0) > 0;
|
|
311
|
-
console.log('[create-to-dex] 🔍 useV5:', useV5);
|
|
312
309
|
const useV4 = !useV5 && (params.dexId !== undefined || params.lpFeeProfile !== undefined);
|
|
313
310
|
const useV3 = !useV5 && !useV4 && !!params.extensionID;
|
|
314
311
|
let createUnsigned;
|