four-flap-meme-sdk 1.4.25 → 1.4.26
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/abis/common.d.ts +85 -0
- package/dist/abis/common.js +242 -0
- package/dist/abis/index.d.ts +1 -0
- package/dist/abis/index.js +2 -0
- package/dist/contracts/tm-bundle-merkle/approve-tokenmanager.js +1 -6
- package/dist/contracts/tm-bundle-merkle/core.js +9 -16
- package/dist/contracts/tm-bundle-merkle/internal.js +6 -8
- package/dist/contracts/tm-bundle-merkle/pancake-proxy.d.ts +12 -6
- package/dist/contracts/tm-bundle-merkle/pancake-proxy.js +224 -166
- package/dist/contracts/tm-bundle-merkle/private.js +6 -19
- package/dist/contracts/tm-bundle-merkle/swap-buy-first.js +2 -7
- package/dist/contracts/tm-bundle-merkle/swap-internal.d.ts +1 -1
- package/dist/contracts/tm-bundle-merkle/swap-internal.js +9 -2
- package/dist/contracts/tm-bundle-merkle/swap.js +1 -3
- package/dist/contracts/tm-bundle-merkle/types.d.ts +20 -0
- package/dist/contracts/tm-bundle-merkle/utils.js +2 -3
- package/dist/dex/direct-router.d.ts +2 -1
- package/dist/dex/direct-router.js +25 -140
- package/dist/flap/constants.d.ts +2 -1
- package/dist/flap/constants.js +2 -1
- package/dist/flap/meta.js +6 -4
- package/dist/flap/portal-bundle-merkle/config.js +6 -12
- package/dist/flap/portal-bundle-merkle/core.js +8 -11
- package/dist/flap/portal-bundle-merkle/pancake-proxy.d.ts +12 -10
- package/dist/flap/portal-bundle-merkle/pancake-proxy.js +307 -370
- package/dist/flap/portal-bundle-merkle/private.js +1 -1
- package/dist/flap/portal-bundle-merkle/swap-buy-first.js +12 -30
- package/dist/flap/portal-bundle-merkle/swap.js +13 -26
- package/dist/flap/portal-bundle-merkle/types.d.ts +22 -2
- package/dist/flap/portal-bundle-merkle/utils.js +11 -16
- package/dist/index.d.ts +3 -2
- package/dist/index.js +9 -2
- package/dist/pancake/bundle-buy-first.js +56 -38
- package/dist/pancake/bundle-swap.js +114 -61
- package/dist/utils/bundle-helpers.d.ts +28 -0
- package/dist/utils/bundle-helpers.js +64 -0
- package/dist/utils/constants.d.ts +23 -1
- package/dist/utils/constants.js +37 -7
- package/dist/utils/erc20.js +17 -25
- package/dist/utils/lp-inspect.js +9 -20
- package/dist/utils/private-sale.js +1 -2
- package/dist/utils/quote-helpers.js +3 -29
- package/dist/utils/swap-helpers.js +1 -6
- package/dist/utils/wallet.d.ts +8 -13
- package/dist/utils/wallet.js +154 -342
- package/package.json +1 -1
|
@@ -56,34 +56,53 @@ async function quoteSellOutput({ routeParams, sellAmountWei, provider }) {
|
|
|
56
56
|
throw new Error(`不支持的路由类型: ${routeParams.routeType}`);
|
|
57
57
|
}
|
|
58
58
|
async function buildSwapTransactions({ routeParams, sellAmountWei, buyAmountBNB, buyer, seller, tokenAddress, useNativeToken = true }) {
|
|
59
|
-
const
|
|
60
|
-
const proxyBuyer = new Contract(PANCAKE_PROXY_ADDRESS, PANCAKE_PROXY_ABI, buyer);
|
|
61
|
-
const deadline = Math.floor(Date.now() / 1000) + 600;
|
|
59
|
+
const deadline = getDeadline();
|
|
62
60
|
// ✅ ERC20 购买时,value 只需要 FLAT_FEE
|
|
63
61
|
const buyValue = useNativeToken ? buyAmountBNB + FLAT_FEE : FLAT_FEE;
|
|
64
62
|
if (routeParams.routeType === 'v2') {
|
|
65
63
|
const { v2Path } = routeParams;
|
|
66
64
|
const reversePath = [...v2Path].reverse();
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
);
|
|
65
|
+
// ✅ 使用官方 V2 Router
|
|
66
|
+
const v2RouterSeller = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, seller);
|
|
67
|
+
const v2RouterBuyer = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, buyer);
|
|
68
|
+
// 卖出:Token → BNB
|
|
69
|
+
const sellUnsigned = await v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline);
|
|
70
|
+
// 买入:BNB → Token
|
|
71
|
+
const buyUnsigned = await v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, reversePath, buyer.address, deadline, { value: buyValue });
|
|
70
72
|
return { sellUnsigned, buyUnsigned };
|
|
71
73
|
}
|
|
72
74
|
if (routeParams.routeType === 'v3-single') {
|
|
73
75
|
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
);
|
|
76
|
+
const v3RouterIface = new ethers.Interface(PANCAKE_V3_ROUTER_ABI);
|
|
77
|
+
const v3RouterSeller = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, seller);
|
|
78
|
+
const v3RouterBuyer = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, buyer);
|
|
79
|
+
// 卖出:Token → WBNB,需要 unwrapWETH9
|
|
80
|
+
const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
|
|
81
|
+
tokenIn: v3TokenIn,
|
|
82
|
+
tokenOut: v3TokenOut,
|
|
83
|
+
fee: v3Fee,
|
|
84
|
+
recipient: PANCAKE_V3_ROUTER_ADDRESS, // 先发给 Router
|
|
85
|
+
amountIn: sellAmountWei,
|
|
86
|
+
amountOutMinimum: 0n,
|
|
87
|
+
sqrtPriceLimitX96: 0n
|
|
88
|
+
}]);
|
|
89
|
+
const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
|
|
90
|
+
const sellUnsigned = await v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData, sellUnwrapData]);
|
|
91
|
+
// 买入:WBNB → Token
|
|
92
|
+
const buySwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
|
|
93
|
+
tokenIn: v3TokenOut,
|
|
94
|
+
tokenOut: v3TokenIn,
|
|
95
|
+
fee: v3Fee,
|
|
96
|
+
recipient: buyer.address,
|
|
97
|
+
amountIn: buyAmountBNB,
|
|
98
|
+
amountOutMinimum: 0n,
|
|
99
|
+
sqrtPriceLimitX96: 0n
|
|
100
|
+
}]);
|
|
101
|
+
const buyUnsigned = await v3RouterBuyer.multicall.populateTransaction(deadline, [buySwapData], { value: buyValue });
|
|
77
102
|
return { sellUnsigned, buyUnsigned };
|
|
78
103
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
? tokenAddress
|
|
82
|
-
: WBNB_ADDRESS;
|
|
83
|
-
const sellUnsigned = await proxySeller.swapV3MultiHop.populateTransaction(v3LpAddresses, v3ExactTokenIn, sellAmountWei, 0n, seller.address, { value: FLAT_FEE });
|
|
84
|
-
const buyUnsigned = await proxyBuyer.swapV3MultiHop.populateTransaction(v3LpAddresses, exactTokenOut, buyAmountBNB, 0n, buyer.address, { value: buyValue } // ✅ 使用动态 value
|
|
85
|
-
);
|
|
86
|
-
return { sellUnsigned, buyUnsigned };
|
|
104
|
+
// V3 多跳暂不支持官方合约
|
|
105
|
+
throw new Error('V3 多跳路由暂不支持官方合约,请使用 V2 路由或 V3 单跳');
|
|
87
106
|
}
|
|
88
107
|
async function calculateBuyerBudget({ buyer, quotedBNBOut, reserveGasBNB, useNativeToken = true, quoteToken, quoteTokenDecimals = 18, provider }) {
|
|
89
108
|
const reserveGas = ethers.parseEther((reserveGasBNB ?? 0.0005).toString());
|
|
@@ -267,12 +286,10 @@ function countTruthy(values) {
|
|
|
267
286
|
*/
|
|
268
287
|
import { ethers, Contract, Wallet } from 'ethers';
|
|
269
288
|
import { calculateSellAmount } from '../utils/swap-helpers.js';
|
|
270
|
-
import { NonceManager } from '../utils/bundle-helpers.js';
|
|
271
|
-
import { ADDRESSES, PROFIT_CONFIG } from '../utils/constants.js';
|
|
289
|
+
import { NonceManager, getDeadline } from '../utils/bundle-helpers.js';
|
|
290
|
+
import { ADDRESSES, PROFIT_CONFIG, BLOCKRAZOR_BUILDER_EOA } from '../utils/constants.js';
|
|
272
291
|
import { quoteV2, quoteV3 } from '../utils/quote-helpers.js';
|
|
273
|
-
|
|
274
|
-
// 参考文档: https://blockrazor.gitbook.io/blockrazor/bsc/block-builder/send-bundle
|
|
275
|
-
const BLOCKRAZOR_BUILDER_EOA = '0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20';
|
|
292
|
+
import { V2_ROUTER_ABI, V3_ROUTER02_ABI, ERC20_BALANCE_ABI } from '../abis/common.js';
|
|
276
293
|
/**
|
|
277
294
|
* 获取 Gas Limit
|
|
278
295
|
*/
|
|
@@ -299,20 +316,18 @@ async function getGasPrice(provider, config) {
|
|
|
299
316
|
}
|
|
300
317
|
return gasPrice;
|
|
301
318
|
}
|
|
302
|
-
//
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const FLAT_FEE = 0n; // ✅ 已移除合约固定手续费
|
|
313
|
-
const WBNB_ADDRESS = '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
|
|
319
|
+
// ✅ ABI 别名(从公共模块导入)
|
|
320
|
+
const PANCAKE_V2_ROUTER_ABI = V2_ROUTER_ABI;
|
|
321
|
+
const PANCAKE_V3_ROUTER_ABI = V3_ROUTER02_ABI;
|
|
322
|
+
const ERC20_BALANCE_OF_ABI = ERC20_BALANCE_ABI;
|
|
323
|
+
// ✅ 使用官方 PancakeSwap Router 地址
|
|
324
|
+
const PANCAKE_V2_ROUTER_ADDRESS = ADDRESSES.BSC.PancakeV2Router;
|
|
325
|
+
const PANCAKE_V3_ROUTER_ADDRESS = ADDRESSES.BSC.PancakeV3Router;
|
|
326
|
+
// 常量
|
|
327
|
+
const FLAT_FEE = 0n;
|
|
328
|
+
const WBNB_ADDRESS = ADDRESSES.BSC.WBNB;
|
|
314
329
|
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
315
|
-
|
|
330
|
+
// ✅ ERC20_BALANCE_OF_ABI 已在文件开头定义
|
|
316
331
|
/**
|
|
317
332
|
* PancakeSwap捆绑换手(V2/V3通用)
|
|
318
333
|
* ✅ 支持 quoteToken:传入 USDT 等地址时,卖出得到该代币,买入使用该代币
|
|
@@ -564,41 +579,59 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
564
579
|
}
|
|
565
580
|
}
|
|
566
581
|
}));
|
|
567
|
-
// ✅
|
|
568
|
-
const
|
|
569
|
-
const
|
|
582
|
+
// ✅ 构建卖出交易(使用官方 Router)
|
|
583
|
+
const deadline = getDeadline();
|
|
584
|
+
const v3RouterIface = new ethers.Interface(PANCAKE_V3_ROUTER_ABI);
|
|
570
585
|
let sellUnsigned;
|
|
571
586
|
if (routeParams.routeType === 'v2') {
|
|
572
587
|
const { v2Path } = routeParams;
|
|
573
|
-
|
|
588
|
+
const v2RouterSeller = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, seller);
|
|
589
|
+
sellUnsigned = await v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline);
|
|
574
590
|
}
|
|
575
591
|
else if (routeParams.routeType === 'v3-single') {
|
|
576
592
|
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
577
|
-
|
|
593
|
+
const v3RouterSeller = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, seller);
|
|
594
|
+
const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
|
|
595
|
+
tokenIn: v3TokenIn,
|
|
596
|
+
tokenOut: v3TokenOut,
|
|
597
|
+
fee: v3Fee,
|
|
598
|
+
recipient: PANCAKE_V3_ROUTER_ADDRESS,
|
|
599
|
+
amountIn: sellAmountWei,
|
|
600
|
+
amountOutMinimum: 0n,
|
|
601
|
+
sqrtPriceLimitX96: 0n
|
|
602
|
+
}]);
|
|
603
|
+
const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
|
|
604
|
+
sellUnsigned = await v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData, sellUnwrapData]);
|
|
578
605
|
}
|
|
579
606
|
else {
|
|
580
|
-
|
|
581
|
-
sellUnsigned = await proxySeller.swapV3MultiHop.populateTransaction(v3LpAddresses, v3ExactTokenIn, sellAmountWei, 0n, seller.address, { value: FLAT_FEE });
|
|
607
|
+
throw new Error('V3 多跳路由暂不支持官方合约,请使用 V2 路由或 V3 单跳');
|
|
582
608
|
}
|
|
583
|
-
// ✅
|
|
609
|
+
// ✅ 并行构建多个买入交易(使用官方 Router)
|
|
584
610
|
const buyUnsignedList = await Promise.all(buyers.map(async (buyer, i) => {
|
|
585
611
|
const buyAmount = buyAmountsWei[i];
|
|
586
|
-
const proxyBuyer = new Contract(PANCAKE_PROXY_ADDRESS, PANCAKE_PROXY_ABI, buyer);
|
|
587
612
|
const buyValue = useNativeToken ? buyAmount + FLAT_FEE : FLAT_FEE;
|
|
588
613
|
if (routeParams.routeType === 'v2') {
|
|
589
614
|
const { v2Path } = routeParams;
|
|
590
615
|
const reversePath = [...v2Path].reverse();
|
|
591
|
-
|
|
616
|
+
const v2RouterBuyer = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, buyer);
|
|
617
|
+
return await v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, reversePath, buyer.address, deadline, { value: buyValue });
|
|
592
618
|
}
|
|
593
619
|
else if (routeParams.routeType === 'v3-single') {
|
|
594
620
|
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
595
|
-
|
|
596
|
-
|
|
621
|
+
const v3RouterBuyer = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, buyer);
|
|
622
|
+
const buySwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
|
|
623
|
+
tokenIn: v3TokenOut,
|
|
624
|
+
tokenOut: v3TokenIn,
|
|
625
|
+
fee: v3Fee,
|
|
626
|
+
recipient: buyer.address,
|
|
627
|
+
amountIn: buyAmount,
|
|
628
|
+
amountOutMinimum: 0n,
|
|
629
|
+
sqrtPriceLimitX96: 0n
|
|
630
|
+
}]);
|
|
631
|
+
return await v3RouterBuyer.multicall.populateTransaction(deadline, [buySwapData], { value: buyValue });
|
|
597
632
|
}
|
|
598
633
|
else {
|
|
599
|
-
|
|
600
|
-
const exactTokenOut = tokenAddress;
|
|
601
|
-
return await proxyBuyer.swapV3MultiHop.populateTransaction(v3LpAddresses, exactTokenOut, buyAmount, 0n, buyer.address, { value: buyValue });
|
|
634
|
+
throw new Error('V3 多跳路由暂不支持官方合约,请使用 V2 路由或 V3 单跳');
|
|
602
635
|
}
|
|
603
636
|
}));
|
|
604
637
|
// ✅ 获取贿赂金额
|
|
@@ -902,20 +935,31 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
902
935
|
});
|
|
903
936
|
console.log(`[pancakeQuickBatchSwapMerkle] 贿赂交易已签名`);
|
|
904
937
|
}
|
|
905
|
-
// ==================== 2.
|
|
906
|
-
const
|
|
938
|
+
// ==================== 2. 卖出交易(使用官方 Router)====================
|
|
939
|
+
const v3RouterIface2 = new ethers.Interface(PANCAKE_V3_ROUTER_ABI);
|
|
907
940
|
let sellUnsigned;
|
|
908
941
|
if (routeParams.routeType === 'v2') {
|
|
909
942
|
const { v2Path } = routeParams;
|
|
910
|
-
|
|
943
|
+
const v2RouterSeller = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, seller);
|
|
944
|
+
sellUnsigned = await v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline);
|
|
911
945
|
}
|
|
912
946
|
else if (routeParams.routeType === 'v3-single') {
|
|
913
947
|
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
914
|
-
|
|
948
|
+
const v3RouterSeller = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, seller);
|
|
949
|
+
const sellSwapData = v3RouterIface2.encodeFunctionData('exactInputSingle', [{
|
|
950
|
+
tokenIn: v3TokenIn,
|
|
951
|
+
tokenOut: v3TokenOut,
|
|
952
|
+
fee: v3Fee,
|
|
953
|
+
recipient: PANCAKE_V3_ROUTER_ADDRESS,
|
|
954
|
+
amountIn: sellAmountWei,
|
|
955
|
+
amountOutMinimum: 0n,
|
|
956
|
+
sqrtPriceLimitX96: 0n
|
|
957
|
+
}]);
|
|
958
|
+
const sellUnwrapData = v3RouterIface2.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
|
|
959
|
+
sellUnsigned = await v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData, sellUnwrapData]);
|
|
915
960
|
}
|
|
916
961
|
else {
|
|
917
|
-
|
|
918
|
-
sellUnsigned = await proxySeller.swapV3MultiHop.populateTransaction(v3LpAddresses, v3ExactTokenIn, sellAmountWei, 0n, seller.address, { value: FLAT_FEE });
|
|
962
|
+
throw new Error('V3 多跳路由暂不支持官方合约,请使用 V2 路由或 V3 单跳');
|
|
919
963
|
}
|
|
920
964
|
const signedSell = await seller.signTransaction({
|
|
921
965
|
...sellUnsigned,
|
|
@@ -978,7 +1022,6 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
978
1022
|
: await Promise.all(buyers.map(buyer => nonceManager.getNextNonce(buyer)));
|
|
979
1023
|
const signedBuys = await Promise.all(buyers.map(async (buyer, i) => {
|
|
980
1024
|
const buyAmount = transferAmountsWei[i];
|
|
981
|
-
const proxyBuyer = new Contract(PANCAKE_PROXY_ADDRESS, PANCAKE_PROXY_ABI, buyer);
|
|
982
1025
|
// BNB 模式:value = buyAmount + FLAT_FEE
|
|
983
1026
|
// ERC20 模式:value = FLAT_FEE(买入金额通过授权支付)
|
|
984
1027
|
const buyValue = useNativeToken ? buyAmount + FLAT_FEE : FLAT_FEE;
|
|
@@ -986,15 +1029,25 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
986
1029
|
if (routeParams.routeType === 'v2') {
|
|
987
1030
|
const { v2Path } = routeParams;
|
|
988
1031
|
const reversePath = [...v2Path].reverse();
|
|
989
|
-
|
|
1032
|
+
const v2RouterBuyer = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, buyer);
|
|
1033
|
+
buyUnsigned = await v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, reversePath, buyer.address, deadline, { value: buyValue });
|
|
990
1034
|
}
|
|
991
1035
|
else if (routeParams.routeType === 'v3-single') {
|
|
992
1036
|
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
993
|
-
|
|
1037
|
+
const v3RouterBuyer = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, buyer);
|
|
1038
|
+
const buySwapData = v3RouterIface2.encodeFunctionData('exactInputSingle', [{
|
|
1039
|
+
tokenIn: v3TokenOut,
|
|
1040
|
+
tokenOut: v3TokenIn,
|
|
1041
|
+
fee: v3Fee,
|
|
1042
|
+
recipient: buyer.address,
|
|
1043
|
+
amountIn: buyAmount,
|
|
1044
|
+
amountOutMinimum: 0n,
|
|
1045
|
+
sqrtPriceLimitX96: 0n
|
|
1046
|
+
}]);
|
|
1047
|
+
buyUnsigned = await v3RouterBuyer.multicall.populateTransaction(deadline, [buySwapData], { value: buyValue });
|
|
994
1048
|
}
|
|
995
1049
|
else {
|
|
996
|
-
|
|
997
|
-
buyUnsigned = await proxyBuyer.swapV3MultiHop.populateTransaction(v3LpAddresses, tokenAddress, buyAmount, 0n, buyer.address, { value: buyValue });
|
|
1050
|
+
throw new Error('V3 多跳路由暂不支持官方合约,请使用 V2 路由或 V3 单跳');
|
|
998
1051
|
}
|
|
999
1052
|
return buyer.signTransaction({
|
|
1000
1053
|
...buyUnsigned,
|
|
@@ -160,3 +160,31 @@ export declare function signTransactionsBatchMultiWallet(wallets: Wallet[], tran
|
|
|
160
160
|
* @returns 是否全部有效
|
|
161
161
|
*/
|
|
162
162
|
export declare function validateSignedTransactions(signedTxs: string[]): boolean;
|
|
163
|
+
/**
|
|
164
|
+
* 获取交易 deadline(Unix 时间戳秒)
|
|
165
|
+
* @param minutes 有效分钟数,默认 20
|
|
166
|
+
* @returns Unix 时间戳(秒)
|
|
167
|
+
*/
|
|
168
|
+
export declare function getDeadline(minutes?: number): number;
|
|
169
|
+
/**
|
|
170
|
+
* 编码 V3 多跳 path
|
|
171
|
+
* 格式:token0 (20 bytes) + fee (3 bytes) + token1 (20 bytes) + fee (3 bytes) + token2 (20 bytes) ...
|
|
172
|
+
*
|
|
173
|
+
* @param tokens 代币地址数组 [tokenIn, tokenMid1, tokenMid2, ..., tokenOut]
|
|
174
|
+
* @param fees 费率数组 [fee0, fee1, ...] - 长度应该是 tokens.length - 1
|
|
175
|
+
* @returns 编码后的 path(hex string)
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* // WBNB → USDT(500) → TOKEN(2500)
|
|
179
|
+
* encodeV3Path(['0xWBNB...', '0xUSDT...', '0xTOKEN...'], [500, 2500])
|
|
180
|
+
*/
|
|
181
|
+
export declare function encodeV3Path(tokens: string[], fees: number[]): string;
|
|
182
|
+
/**
|
|
183
|
+
* 解码 V3 path 为代币和费率数组
|
|
184
|
+
* @param path 编码后的 path
|
|
185
|
+
* @returns { tokens, fees }
|
|
186
|
+
*/
|
|
187
|
+
export declare function decodeV3Path(path: string): {
|
|
188
|
+
tokens: string[];
|
|
189
|
+
fees: number[];
|
|
190
|
+
};
|
|
@@ -301,3 +301,67 @@ export function validateSignedTransactions(signedTxs) {
|
|
|
301
301
|
}
|
|
302
302
|
return true;
|
|
303
303
|
}
|
|
304
|
+
// ============================================================================
|
|
305
|
+
// 交易时间和路径工具
|
|
306
|
+
// ============================================================================
|
|
307
|
+
import { DEFAULT_DEADLINE_MINUTES } from './constants.js';
|
|
308
|
+
/**
|
|
309
|
+
* 获取交易 deadline(Unix 时间戳秒)
|
|
310
|
+
* @param minutes 有效分钟数,默认 20
|
|
311
|
+
* @returns Unix 时间戳(秒)
|
|
312
|
+
*/
|
|
313
|
+
export function getDeadline(minutes = DEFAULT_DEADLINE_MINUTES) {
|
|
314
|
+
return Math.floor(Date.now() / 1000) + 60 * minutes;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* 编码 V3 多跳 path
|
|
318
|
+
* 格式:token0 (20 bytes) + fee (3 bytes) + token1 (20 bytes) + fee (3 bytes) + token2 (20 bytes) ...
|
|
319
|
+
*
|
|
320
|
+
* @param tokens 代币地址数组 [tokenIn, tokenMid1, tokenMid2, ..., tokenOut]
|
|
321
|
+
* @param fees 费率数组 [fee0, fee1, ...] - 长度应该是 tokens.length - 1
|
|
322
|
+
* @returns 编码后的 path(hex string)
|
|
323
|
+
*
|
|
324
|
+
* @example
|
|
325
|
+
* // WBNB → USDT(500) → TOKEN(2500)
|
|
326
|
+
* encodeV3Path(['0xWBNB...', '0xUSDT...', '0xTOKEN...'], [500, 2500])
|
|
327
|
+
*/
|
|
328
|
+
export function encodeV3Path(tokens, fees) {
|
|
329
|
+
if (tokens.length < 2) {
|
|
330
|
+
throw new Error('V3 path 需要至少 2 个代币');
|
|
331
|
+
}
|
|
332
|
+
if (fees.length !== tokens.length - 1) {
|
|
333
|
+
throw new Error(`费率数量 (${fees.length}) 必须等于代币数量 - 1 (${tokens.length - 1})`);
|
|
334
|
+
}
|
|
335
|
+
let path = '0x';
|
|
336
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
337
|
+
// 添加代币地址 (20 bytes)
|
|
338
|
+
path += tokens[i].slice(2).toLowerCase().padStart(40, '0');
|
|
339
|
+
// 添加费率 (3 bytes) - 除了最后一个代币
|
|
340
|
+
if (i < fees.length) {
|
|
341
|
+
path += fees[i].toString(16).padStart(6, '0');
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return path;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* 解码 V3 path 为代币和费率数组
|
|
348
|
+
* @param path 编码后的 path
|
|
349
|
+
* @returns { tokens, fees }
|
|
350
|
+
*/
|
|
351
|
+
export function decodeV3Path(path) {
|
|
352
|
+
const cleanPath = path.startsWith('0x') ? path.slice(2) : path;
|
|
353
|
+
const tokens = [];
|
|
354
|
+
const fees = [];
|
|
355
|
+
let offset = 0;
|
|
356
|
+
while (offset < cleanPath.length) {
|
|
357
|
+
// 读取代币地址 (40 hex chars = 20 bytes)
|
|
358
|
+
tokens.push('0x' + cleanPath.slice(offset, offset + 40));
|
|
359
|
+
offset += 40;
|
|
360
|
+
// 读取费率 (6 hex chars = 3 bytes)
|
|
361
|
+
if (offset < cleanPath.length) {
|
|
362
|
+
fees.push(parseInt(cleanPath.slice(offset, offset + 6), 16));
|
|
363
|
+
offset += 6;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return { tokens, fees };
|
|
367
|
+
}
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
/** BlockRazor Builder EOA 地址(用于 BSC 链贿赂) */
|
|
2
|
+
export declare const BLOCKRAZOR_BUILDER_EOA = "0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20";
|
|
3
|
+
/** 零地址 */
|
|
4
|
+
export declare const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
5
|
+
/** 交易 deadline 默认分钟数 */
|
|
6
|
+
export declare const DEFAULT_DEADLINE_MINUTES = 20;
|
|
7
|
+
/** V3 常用费率档位 */
|
|
8
|
+
export declare const V3_FEE_TIERS: {
|
|
9
|
+
readonly LOWEST: 100;
|
|
10
|
+
readonly LOW: 500;
|
|
11
|
+
readonly MEDIUM: 2500;
|
|
12
|
+
readonly HIGH: 10000;
|
|
13
|
+
};
|
|
14
|
+
/** ✅ 硬编码:利润提取配置(统一管理,所有方法强制使用) */
|
|
1
15
|
export declare const PROFIT_CONFIG: {
|
|
2
16
|
/** 利润接收地址 */
|
|
3
17
|
readonly RECIPIENT: "0xe8D0334fAf713884133640CAEe4ECdd2106AF103";
|
|
@@ -34,6 +48,10 @@ export declare const CHAIN: {
|
|
|
34
48
|
};
|
|
35
49
|
export declare const ADDRESSES: {
|
|
36
50
|
readonly BSC: {
|
|
51
|
+
readonly WBNB: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c";
|
|
52
|
+
readonly USDT: "0x55d398326f99059fF775485246999027B3197955";
|
|
53
|
+
readonly USDC: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d";
|
|
54
|
+
readonly BUSD: "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56";
|
|
37
55
|
readonly TokenManagerOriginal: "0x5c952063c7fc8610FFDB798152D69F0B9550762b";
|
|
38
56
|
readonly TokenManagerV1Proxy: "0xf7F823d0E790219dBf727bDb971837574655fCB0";
|
|
39
57
|
readonly TokenManagerV2Proxy: "0x342399a59943B5815849657Aa0e06D7058D9d5C6";
|
|
@@ -41,7 +59,11 @@ export declare const ADDRESSES: {
|
|
|
41
59
|
readonly TokenManagerV2: "0x342399a59943B5815849657Aa0e06D7058D9d5C6";
|
|
42
60
|
readonly TokenManagerHelper3: "0xF251F83e40a78868FcfA3FA4599Dad6494E46034";
|
|
43
61
|
readonly FlapPortal: "0xe2cE6ab80874Fa9Fa2aAE65D277Dd6B8e65C9De0";
|
|
44
|
-
readonly
|
|
62
|
+
readonly PancakeV2Router: "0x10ED43C718714eb63d5aA57B78B54704E256024E";
|
|
63
|
+
readonly PancakeV2Factory: "0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73";
|
|
64
|
+
readonly PancakeV3Router: "0x13f4EA83D0bd40E75C8222255bc855a974568Dd4";
|
|
65
|
+
readonly PancakeV3Quoter: "0xB048Bbc1Ee6b733FFfCFb9e9CeF7375518e25997";
|
|
66
|
+
readonly PancakeV3Factory: "0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865";
|
|
45
67
|
readonly Multicall3: "0xcA11bde05977b3631167028862bE2a173976CA11";
|
|
46
68
|
};
|
|
47
69
|
readonly BASE: {
|
package/dist/utils/constants.js
CHANGED
|
@@ -1,4 +1,23 @@
|
|
|
1
|
-
//
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// 公共常量 - 各链通用
|
|
3
|
+
// ============================================================================
|
|
4
|
+
/** BlockRazor Builder EOA 地址(用于 BSC 链贿赂) */
|
|
5
|
+
export const BLOCKRAZOR_BUILDER_EOA = '0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20';
|
|
6
|
+
/** 零地址 */
|
|
7
|
+
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
8
|
+
/** 交易 deadline 默认分钟数 */
|
|
9
|
+
export const DEFAULT_DEADLINE_MINUTES = 20;
|
|
10
|
+
/** V3 常用费率档位 */
|
|
11
|
+
export const V3_FEE_TIERS = {
|
|
12
|
+
LOWEST: 100, // 0.01%
|
|
13
|
+
LOW: 500, // 0.05%
|
|
14
|
+
MEDIUM: 2500, // 0.25%
|
|
15
|
+
HIGH: 10000, // 1%
|
|
16
|
+
};
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// 利润配置
|
|
19
|
+
// ============================================================================
|
|
20
|
+
/** ✅ 硬编码:利润提取配置(统一管理,所有方法强制使用) */
|
|
2
21
|
export const PROFIT_CONFIG = {
|
|
3
22
|
/** 利润接收地址 */
|
|
4
23
|
RECIPIENT: '0xe8D0334fAf713884133640CAEe4ECdd2106AF103',
|
|
@@ -21,9 +40,16 @@ export const CHAIN = {
|
|
|
21
40
|
};
|
|
22
41
|
export const ADDRESSES = {
|
|
23
42
|
BSC: {
|
|
24
|
-
//
|
|
43
|
+
// ========== 原生代币 ==========
|
|
44
|
+
WBNB: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c',
|
|
45
|
+
// ========== 稳定币 ==========
|
|
46
|
+
USDT: '0x55d398326f99059fF775485246999027B3197955',
|
|
47
|
+
USDC: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d',
|
|
48
|
+
BUSD: '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56',
|
|
49
|
+
// ========== Four.meme 合约 ==========
|
|
50
|
+
// 原始合约(TokenManager2,创建代币专用,不收费)
|
|
25
51
|
TokenManagerOriginal: '0x5c952063c7fc8610FFDB798152D69F0B9550762b',
|
|
26
|
-
//
|
|
52
|
+
// 代理合约(收费版,仅交易)
|
|
27
53
|
TokenManagerV1Proxy: '0xf7F823d0E790219dBf727bDb971837574655fCB0',
|
|
28
54
|
TokenManagerV2Proxy: '0x342399a59943B5815849657Aa0e06D7058D9d5C6',
|
|
29
55
|
// ✅ 向后兼容别名(保持旧代码可用)
|
|
@@ -31,11 +57,15 @@ export const ADDRESSES = {
|
|
|
31
57
|
TokenManagerV2: '0x342399a59943B5815849657Aa0e06D7058D9d5C6',
|
|
32
58
|
// 查询辅助合约(Helper3)
|
|
33
59
|
TokenManagerHelper3: '0xF251F83e40a78868FcfA3FA4599Dad6494E46034',
|
|
34
|
-
// Flap Portal
|
|
60
|
+
// ========== Flap Portal ==========
|
|
35
61
|
FlapPortal: '0xe2cE6ab80874Fa9Fa2aAE65D277Dd6B8e65C9De0',
|
|
36
|
-
// PancakeSwap
|
|
37
|
-
|
|
38
|
-
|
|
62
|
+
// ========== PancakeSwap 官方合约 ==========
|
|
63
|
+
PancakeV2Router: '0x10ED43C718714eb63d5aA57B78B54704E256024E',
|
|
64
|
+
PancakeV2Factory: '0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73',
|
|
65
|
+
PancakeV3Router: '0x13f4EA83D0bd40E75C8222255bc855a974568Dd4',
|
|
66
|
+
PancakeV3Quoter: '0xB048Bbc1Ee6b733FFfCFb9e9CeF7375518e25997',
|
|
67
|
+
PancakeV3Factory: '0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865',
|
|
68
|
+
// ========== 通用合约 ==========
|
|
39
69
|
Multicall3: '0xcA11bde05977b3631167028862bE2a173976CA11',
|
|
40
70
|
},
|
|
41
71
|
BASE: {
|
package/dist/utils/erc20.js
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { Contract, Wallet, JsonRpcProvider, Interface, parseUnits } from 'ethers';
|
|
2
|
-
import { ADDRESSES } from './constants.js';
|
|
2
|
+
import { ADDRESSES, ZERO_ADDRESS } from './constants.js';
|
|
3
3
|
import { NonceManager } from './bundle-helpers.js';
|
|
4
|
-
|
|
5
|
-
{ "constant": false, "inputs": [{ "name": "spender", "type": "address" }, { "name": "value", "type": "uint256" }], "name": "approve", "outputs": [{ "name": "", "type": "bool" }], "type": "function" },
|
|
6
|
-
{ "constant": true, "inputs": [{ "name": "owner", "type": "address" }, { "name": "spender", "type": "address" }], "name": "allowance", "outputs": [{ "name": "", "type": "uint256" }], "type": "function" },
|
|
7
|
-
{ "constant": true, "inputs": [{ "name": "account", "type": "address" }], "name": "balanceOf", "outputs": [{ "name": "", "type": "uint256" }], "type": "function" },
|
|
8
|
-
{ "constant": true, "inputs": [], "name": "decimals", "outputs": [{ "name": "", "type": "uint8" }], "type": "function" }
|
|
9
|
-
];
|
|
4
|
+
import { ERC20_ABI, MULTICALL3_ABI } from '../abis/common.js';
|
|
10
5
|
/**
|
|
11
6
|
* 验证合约地址是否有效(是否部署了代码)
|
|
12
7
|
*/
|
|
@@ -265,12 +260,9 @@ export async function checkFlapSellApprovalBatch(chain, rpcUrl, token, owners, r
|
|
|
265
260
|
* @returns 每个地址的授权额度数组
|
|
266
261
|
*/
|
|
267
262
|
export async function batchCheckAllowances(provider, tokenAddress, owners, spender) {
|
|
268
|
-
// ✅
|
|
269
|
-
const multicall3Address =
|
|
270
|
-
const
|
|
271
|
-
'function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) view returns (tuple(bool success, bytes returnData)[] returnData)'
|
|
272
|
-
];
|
|
273
|
-
const multicall3 = new Contract(multicall3Address, multicall3ABI, provider);
|
|
263
|
+
// ✅ 使用公共常量和 ABI
|
|
264
|
+
const multicall3Address = ADDRESSES.BSC.Multicall3;
|
|
265
|
+
const multicall3 = new Contract(multicall3Address, MULTICALL3_ABI, provider);
|
|
274
266
|
// 编码 allowance(owner, spender) 调用数据
|
|
275
267
|
const erc20Interface = new Contract(tokenAddress, ERC20_ABI, provider).interface;
|
|
276
268
|
const calls = owners.map(owner => ({
|
|
@@ -310,29 +302,29 @@ function resolveSpenderAddress(chain, platform) {
|
|
|
310
302
|
// Four.meme 使用 TokenManagerV2Proxy(默认)
|
|
311
303
|
BSC: ADDRESSES.BSC.TokenManagerV2Proxy,
|
|
312
304
|
BASE: ADDRESSES.BASE.TokenManagerHelper3,
|
|
313
|
-
XLAYER:
|
|
314
|
-
MORPH:
|
|
315
|
-
MONAD:
|
|
305
|
+
XLAYER: ZERO_ADDRESS, // XLAYER 暂不支持 Four.meme
|
|
306
|
+
MORPH: ZERO_ADDRESS, // MORPH 暂不支持 Four.meme
|
|
307
|
+
MONAD: ZERO_ADDRESS, // MONAD 不支持 Four.meme
|
|
316
308
|
},
|
|
317
309
|
'pancake-v2': {
|
|
318
310
|
// PancakeSwap V2 Router 地址
|
|
319
|
-
BSC:
|
|
311
|
+
BSC: ADDRESSES.BSC.PancakeV2Router,
|
|
320
312
|
BASE: '0x8cFe327CEc66d1C090Dd72bd0FF11d690C33a2Eb', // BASE PancakeSwap V2 Router
|
|
321
|
-
XLAYER:
|
|
322
|
-
MORPH:
|
|
323
|
-
MONAD: '0xb1bc24c34e88f7d43d5923034e3a14b24daacff9', //
|
|
313
|
+
XLAYER: ZERO_ADDRESS, // XLAYER 暂不支持
|
|
314
|
+
MORPH: ZERO_ADDRESS, // MORPH 暂不支持
|
|
315
|
+
MONAD: '0xb1bc24c34e88f7d43d5923034e3a14b24daacff9', // Monad PancakeSwap V2 Router
|
|
324
316
|
},
|
|
325
317
|
'pancake-v3': {
|
|
326
318
|
// PancakeSwap V3 SmartRouter 地址
|
|
327
|
-
BSC:
|
|
319
|
+
BSC: ADDRESSES.BSC.PancakeV3Router,
|
|
328
320
|
BASE: '0x678Aa4bF4E210cf2166753e054d5b7c31cc7fa86', // BASE PancakeSwap V3 SmartRouter
|
|
329
|
-
XLAYER:
|
|
330
|
-
MORPH:
|
|
331
|
-
MONAD: '0x1b81d678ffb9c0263b24a97847620c99d213eb14', //
|
|
321
|
+
XLAYER: ZERO_ADDRESS, // XLAYER 暂不支持
|
|
322
|
+
MORPH: ZERO_ADDRESS, // MORPH 暂不支持
|
|
323
|
+
MONAD: '0x1b81d678ffb9c0263b24a97847620c99d213eb14', // Monad PancakeSwap V3 Router
|
|
332
324
|
},
|
|
333
325
|
};
|
|
334
326
|
const spender = spenderMap[platform]?.[chain];
|
|
335
|
-
if (!spender || spender ===
|
|
327
|
+
if (!spender || spender === ZERO_ADDRESS) {
|
|
336
328
|
throw new Error(`❌ 不支持的链或平台: ${chain} / ${platform}`);
|
|
337
329
|
}
|
|
338
330
|
return spender;
|
package/dist/utils/lp-inspect.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Contract, JsonRpcProvider, Interface, formatUnits } from 'ethers';
|
|
2
|
-
import { ADDRESSES } from './constants.js';
|
|
2
|
+
import { ADDRESSES, ZERO_ADDRESS } from './constants.js';
|
|
3
|
+
import { MULTICALL3_ABI, V2_FACTORY_ABI, V2_PAIR_ABI, V3_FACTORY_ABI, ERC20_ABI } from '../abis/common.js';
|
|
3
4
|
import { Helper3 } from '../contracts/helper3.js';
|
|
4
5
|
import { FlapPortal } from '../flap/portal.js';
|
|
5
6
|
// ============================================================================
|
|
@@ -206,28 +207,16 @@ export function getQuoteTokens(chain) {
|
|
|
206
207
|
];
|
|
207
208
|
}
|
|
208
209
|
// ============================================================================
|
|
209
|
-
//
|
|
210
|
+
// ABI 别名(从公共模块导入)
|
|
210
211
|
// ============================================================================
|
|
211
|
-
const I_UNIV2_FACTORY_ABI =
|
|
212
|
-
const I_UNIV2_PAIR_ABI =
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
'function getReserves() view returns (uint112,uint112,uint32)'
|
|
216
|
-
];
|
|
217
|
-
const I_ERC20_ABI = [
|
|
218
|
-
'function balanceOf(address) view returns (uint256)',
|
|
219
|
-
'function decimals() view returns (uint8)',
|
|
220
|
-
'function symbol() view returns (string)'
|
|
221
|
-
];
|
|
222
|
-
const MULTICALL3_ABI = [
|
|
223
|
-
'function aggregate((address target, bytes callData)[]) public returns (uint256 blockNumber, bytes[] returnData)'
|
|
224
|
-
];
|
|
225
|
-
const I_UNIV3_FACTORY_ABI = ['function getPool(address,address,uint24) view returns (address)'];
|
|
212
|
+
const I_UNIV2_FACTORY_ABI = V2_FACTORY_ABI;
|
|
213
|
+
const I_UNIV2_PAIR_ABI = V2_PAIR_ABI;
|
|
214
|
+
const I_ERC20_ABI = ERC20_ABI;
|
|
215
|
+
const I_UNIV3_FACTORY_ABI = V3_FACTORY_ABI;
|
|
226
216
|
// ============================================================================
|
|
227
|
-
//
|
|
217
|
+
// 常量(从公共模块导入)
|
|
228
218
|
// ============================================================================
|
|
229
|
-
const
|
|
230
|
-
const MULTICALL3_ADDRESS = '0xca11bde05977b3631167028862be2a173976ca11';
|
|
219
|
+
const MULTICALL3_ADDRESS = ADDRESSES.BSC.Multicall3;
|
|
231
220
|
/** Provider 缓存 */
|
|
232
221
|
const providerCache = new Map();
|
|
233
222
|
function getProvider(rpcUrl) {
|
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
* - 利润提取:千分之三(30 bps)
|
|
7
7
|
*/
|
|
8
8
|
import { ethers, Wallet, JsonRpcProvider } from 'ethers';
|
|
9
|
-
import { PROFIT_CONFIG } from './constants.js';
|
|
10
|
-
import { BLOCKRAZOR_BUILDER_EOA } from '../clients/blockrazor.js';
|
|
9
|
+
import { PROFIT_CONFIG, BLOCKRAZOR_BUILDER_EOA } from './constants.js';
|
|
11
10
|
import { NonceManager } from './bundle-helpers.js';
|
|
12
11
|
// ============================================================================
|
|
13
12
|
// 核心功能
|