four-flap-meme-sdk 2.0.0 → 2.2.0
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/__tests__/subpath-exports.test.js +64 -0
- package/dist/chains/bsc/iro.d.ts +5 -0
- package/dist/chains/bsc/iro.js +4 -0
- package/dist/chains/eni/flat-aliases.d.ts +10 -0
- package/dist/chains/eni/flat-aliases.js +8 -0
- package/dist/chains/eni/index.d.ts +1 -0
- package/dist/chains/eni/index.js +1 -0
- package/dist/chains/index.d.ts +13 -0
- package/dist/chains/index.js +13 -0
- package/dist/chains/xlayer/eip7702/flat-aliases.d.ts +13 -0
- package/dist/chains/xlayer/eip7702/flat-aliases.js +10 -0
- package/dist/chains/xlayer/eip7702/index.d.ts +1 -0
- package/dist/chains/xlayer/eip7702/index.js +1 -0
- package/dist/chains/xlayer/index.d.ts +3 -2
- package/dist/chains/xlayer/index.js +4 -7
- package/dist/flap/index.d.ts +10 -0
- package/dist/flap/index.js +8 -0
- package/dist/merkle/index.d.ts +12 -0
- package/dist/merkle/index.js +11 -0
- package/dist/shared/constants/index.d.ts +2 -0
- package/dist/shared/index.d.ts +2 -0
- package/dist/vanity/index.d.ts +5 -0
- package/dist/vanity/index.js +5 -0
- package/package.json +93 -2
- package/dist/chains/bsc/four/disperse.d.ts +0 -12
- package/dist/chains/bsc/four/disperse.js +0 -470
- package/dist/chains/bsc/four/pairwise.d.ts +0 -7
- package/dist/chains/bsc/four/pairwise.js +0 -308
- package/dist/chains/bsc/four/submit/blockrazor.d.ts +0 -18
- package/dist/chains/bsc/four/submit/blockrazor.js +0 -86
- package/dist/chains/bsc/four/submit/direct.d.ts +0 -66
- package/dist/chains/bsc/four/submit/direct.js +0 -452
- package/dist/chains/bsc/four/submit/helpers.d.ts +0 -18
- package/dist/chains/bsc/four/submit/helpers.js +0 -57
- package/dist/chains/bsc/four/submit/index.d.ts +0 -12
- package/dist/chains/bsc/four/submit/index.js +0 -11
- package/dist/chains/bsc/four/submit/merkle.d.ts +0 -18
- package/dist/chains/bsc/four/submit/merkle.js +0 -74
- package/dist/chains/bsc/four/submit/types.d.ts +0 -143
- package/dist/chains/bsc/four/swap/index.d.ts +0 -32
- package/dist/chains/bsc/four/swap/index.js +0 -829
- package/dist/chains/bsc/four/swap/types.d.ts +0 -70
- package/dist/chains/bsc/four/swap/types.js +0 -1
- package/dist/chains/bsc/four/sweep.d.ts +0 -13
- package/dist/chains/bsc/four/sweep.js +0 -788
- package/dist/chains/bsc/four/utils/index.d.ts +0 -20
- package/dist/chains/bsc/four/utils/index.js +0 -1558
- package/dist/chains/bsc/four/utils/types.d.ts +0 -1
- package/dist/chains/bsc/four/utils/types.js +0 -1
- package/dist/chains/bsc/pancake/bundle-buy-first/index.d.ts +0 -8
- package/dist/chains/bsc/pancake/bundle-buy-first/index.js +0 -907
- package/dist/chains/bsc/pancake/bundle-buy-first/types.d.ts +0 -73
- package/dist/chains/bsc/pancake/bundle-buy-first/types.js +0 -1
- package/dist/chains/bsc/pancake/bundle-swap/helpers.d.ts +0 -102
- package/dist/chains/bsc/pancake/bundle-swap/helpers.js +0 -572
- package/dist/chains/bsc/pancake/bundle-swap/index.d.ts +0 -50
- package/dist/chains/bsc/pancake/bundle-swap/index.js +0 -1066
- package/dist/chains/bsc/pancake/bundle-swap/types.d.ts +0 -202
- package/dist/chains/bsc/pancake/bundle-swap/types.js +0 -3
- package/dist/chains/xlayer/eip7702/bundle-swap/index.d.ts +0 -72
- package/dist/chains/xlayer/eip7702/bundle-swap/index.js +0 -921
- package/dist/chains/xlayer/eip7702/bundle-swap/types.d.ts +0 -65
- package/dist/chains/xlayer/eip7702/bundle-swap/types.js +0 -1
- package/dist/chains/xlayer/eip7702/multi-hop-transfer/index.d.ts +0 -128
- package/dist/chains/xlayer/eip7702/multi-hop-transfer/index.js +0 -857
- package/dist/chains/xlayer/eip7702/multi-hop-transfer/types.d.ts +0 -85
- package/dist/chains/xlayer/eip7702/multi-hop-transfer/types.js +0 -1
- package/dist/chains/xlayer/eip7702/volume/index.d.ts +0 -96
- package/dist/chains/xlayer/eip7702/volume/index.js +0 -793
- package/dist/chains/xlayer/eip7702/volume/types.d.ts +0 -124
- package/dist/chains/xlayer/eip7702/volume/types.js +0 -1
- package/dist/chains/xlayer/eoa/types-core.d.ts +0 -363
- package/dist/chains/xlayer/eoa/types-core.js +0 -53
- package/dist/chains/xlayer/eoa/types-create.d.ts +0 -413
- package/dist/chains/xlayer/eoa/types-create.js +0 -9
- package/dist/chains/xlayer/eoa/types-volume.d.ts +0 -209
- package/dist/chains/xlayer/eoa/types-volume.js +0 -13
- package/dist/dex/direct-router/index.d.ts +0 -70
- package/dist/dex/direct-router/index.js +0 -1410
- package/dist/dex/direct-router/types.d.ts +0 -81
- package/dist/dex/direct-router/types.js +0 -1
- package/dist/shared/abis/TaxToken.json +0 -969
- package/dist/shared/abis/TokenManager.json +0 -836
- package/dist/shared/abis/TokenManager2.json +0 -136
- package/dist/shared/abis/TokenManagerHelper3.json +0 -993
- package/dist/shared/abis 2/TaxToken.json +0 -105
- package/dist/shared/abis 2/TokenManager.json +0 -836
- package/dist/shared/abis 2/TokenManager2.json +0 -60
- package/dist/shared/abis 2/TokenManagerHelper3.json +0 -993
- package/dist/shared/abis 2/common.d.ts +0 -85
- package/dist/shared/abis 2/common.js +0 -254
- package/dist/shared/abis 2/index.d.ts +0 -8
- package/dist/shared/abis 2/index.js +0 -8
- package/dist/shared/clients 2/blockrazor.d.ts +0 -314
- package/dist/shared/clients 2/blockrazor.js +0 -596
- package/dist/shared/clients 2/club48.d.ts +0 -154
- package/dist/shared/clients 2/club48.js +0 -331
- package/dist/shared/clients 2/emitservice.d.ts +0 -47
- package/dist/shared/clients 2/emitservice.js +0 -44
- package/dist/shared/clients 2/four.d.ts +0 -132
- package/dist/shared/clients 2/four.js +0 -281
- package/dist/shared/clients 2/merkle.d.ts +0 -210
- package/dist/shared/clients 2/merkle.js +0 -400
- package/dist/shared/flap/__tests__/curve.test.d.ts +0 -1
- package/dist/shared/flap/__tests__/curve.test.js +0 -85
- package/dist/shared/flap/portal/index.d.ts +0 -12
- package/dist/shared/flap/portal/index.js +0 -11
- package/dist/shared/flap/portal/portal.d.ts +0 -47
- package/dist/shared/flap/portal/portal.js +0 -218
- package/dist/shared/flap/portal/types.d.ts +0 -227
- package/dist/shared/flap/portal/types.js +0 -80
- package/dist/shared/flap/portal/writer.d.ts +0 -121
- package/dist/shared/flap/portal/writer.js +0 -265
- package/dist/shared/flap/portal-bundle-merkle/core/index.d.ts +0 -18
- package/dist/shared/flap/portal-bundle-merkle/core/index.js +0 -938
- package/dist/shared/flap/portal-bundle-merkle/core/types.d.ts +0 -1
- package/dist/shared/flap/portal-bundle-merkle/core/types.js +0 -1
- package/dist/shared/flap/portal-bundle-merkle/swap/index.d.ts +0 -42
- package/dist/shared/flap/portal-bundle-merkle/swap/index.js +0 -1448
- package/dist/shared/flap/portal-bundle-merkle/swap/types.d.ts +0 -84
- package/dist/shared/flap/portal-bundle-merkle/swap/types.js +0 -1
- package/dist/shared/flap/portal-bundle-merkle/utils/index.d.ts +0 -17
- package/dist/shared/flap/portal-bundle-merkle/utils/index.js +0 -1024
- package/dist/shared/flap/portal-bundle-merkle/utils/types.d.ts +0 -16
- package/dist/shared/flap/portal-bundle-merkle/utils/types.js +0 -1
- package/dist/shared/flap/portal-bundle-merkle/utils-helpers.d.ts +0 -100
- package/dist/shared/flap/portal-bundle-merkle/utils-helpers.js +0 -133
- package/dist/shared/flap 2/__tests__/curve.test.d.ts +0 -1
- package/dist/shared/flap 2/__tests__/curve.test.js +0 -85
- package/dist/shared/flap 2/abi.d.ts +0 -4
- package/dist/shared/flap 2/abi.js +0 -4
- package/dist/shared/flap 2/constants.d.ts +0 -128
- package/dist/shared/flap 2/constants.js +0 -143
- package/dist/shared/flap 2/curve.d.ts +0 -33
- package/dist/shared/flap 2/curve.js +0 -84
- package/dist/shared/flap 2/errors.d.ts +0 -37
- package/dist/shared/flap 2/errors.js +0 -114
- package/dist/shared/flap 2/index.d.ts +0 -22
- package/dist/shared/flap 2/index.js +0 -33
- package/dist/shared/flap 2/ipfs.d.ts +0 -21
- package/dist/shared/flap 2/ipfs.js +0 -38
- package/dist/shared/flap 2/meta.d.ts +0 -30
- package/dist/shared/flap 2/meta.js +0 -195
- package/dist/shared/flap 2/permit.d.ts +0 -16
- package/dist/shared/flap 2/permit.js +0 -67
- package/dist/shared/flap 2/pinata.d.ts +0 -40
- package/dist/shared/flap 2/pinata.js +0 -106
- package/dist/shared/flap 2/portal-bundle-merkle/config.d.ts +0 -79
- package/dist/shared/flap 2/portal-bundle-merkle/config.js +0 -133
- package/dist/shared/flap 2/portal-bundle-merkle/core.d.ts +0 -18
- package/dist/shared/flap 2/portal-bundle-merkle/core.js +0 -938
- package/dist/shared/flap 2/portal-bundle-merkle/create-to-dex.d.ts +0 -125
- package/dist/shared/flap 2/portal-bundle-merkle/create-to-dex.js +0 -665
- package/dist/shared/flap 2/portal-bundle-merkle/curve-to-dex.d.ts +0 -88
- package/dist/shared/flap 2/portal-bundle-merkle/curve-to-dex.js +0 -446
- package/dist/shared/flap 2/portal-bundle-merkle/index.d.ts +0 -14
- package/dist/shared/flap 2/portal-bundle-merkle/index.js +0 -26
- package/dist/shared/flap 2/portal-bundle-merkle/pancake-proxy.d.ts +0 -28
- package/dist/shared/flap 2/portal-bundle-merkle/pancake-proxy.js +0 -701
- package/dist/shared/flap 2/portal-bundle-merkle/private.d.ts +0 -17
- package/dist/shared/flap 2/portal-bundle-merkle/private.js +0 -549
- package/dist/shared/flap 2/portal-bundle-merkle/swap-buy-first.d.ts +0 -65
- package/dist/shared/flap 2/portal-bundle-merkle/swap-buy-first.js +0 -831
- package/dist/shared/flap 2/portal-bundle-merkle/swap.d.ts +0 -201
- package/dist/shared/flap 2/portal-bundle-merkle/swap.js +0 -1359
- package/dist/shared/flap 2/portal-bundle-merkle/types.d.ts +0 -358
- package/dist/shared/flap 2/portal-bundle-merkle/types.js +0 -1
- package/dist/shared/flap 2/portal-bundle-merkle/utils.d.ts +0 -89
- package/dist/shared/flap 2/portal-bundle-merkle/utils.js +0 -963
- package/dist/shared/flap 2/portal-bundle.d.ts +0 -119
- package/dist/shared/flap 2/portal-bundle.js +0 -584
- package/dist/shared/flap 2/portal.d.ts +0 -392
- package/dist/shared/flap 2/portal.js +0 -559
- package/dist/shared/flap 2/vanity.d.ts +0 -48
- package/dist/shared/flap 2/vanity.js +0 -110
- package/dist/shared/flap 2/vault.d.ts +0 -240
- package/dist/shared/flap 2/vault.js +0 -366
- package/dist/shared/four 2/index.d.ts +0 -7
- package/dist/shared/four 2/index.js +0 -22
- package/dist/shared/four 2/tax-token.d.ts +0 -176
- package/dist/shared/four 2/tax-token.js +0 -302
- package/dist/shared/index 2.js +0 -10
- package/dist/shared/index.d 2.ts +0 -10
- package/dist/types/distribute.d.ts +0 -72
- package/dist/types/distribute.js +0 -1
- package/dist/types 2/errors.d.ts +0 -27
- package/dist/types 2/errors.js +0 -34
- package/dist/utils/__tests__/errors.test.d.ts +0 -1
- package/dist/utils/__tests__/errors.test.js +0 -76
- package/dist/utils/airdrop-sweep-types.d.ts +0 -1
- package/dist/utils/airdrop-sweep-types.js +0 -1
- package/dist/utils/erc20/index.d.ts +0 -242
- package/dist/utils/erc20/index.js +0 -645
- package/dist/utils/erc20/types.d.ts +0 -77
- package/dist/utils/erc20/types.js +0 -1
- package/dist/utils/erc20-types.d.ts +0 -1
- package/dist/utils/erc20-types.js +0 -1
- package/dist/utils/holders-maker/helpers.d.ts +0 -43
- package/dist/utils/holders-maker/helpers.js +0 -371
- package/dist/utils/holders-maker/index.d.ts +0 -26
- package/dist/utils/holders-maker/index.js +0 -218
- package/dist/utils/holders-maker/types.d.ts +0 -72
- package/dist/utils/holders-maker/types.js +0 -4
- package/dist/utils/holders-maker-types.d.ts +0 -1
- package/dist/utils/holders-maker-types.js +0 -1
- package/dist/utils/lp-inspect/index.d.ts +0 -44
- package/dist/utils/lp-inspect/index.js +0 -937
- package/dist/utils/lp-inspect/types.d.ts +0 -100
- package/dist/utils/lp-inspect/types.js +0 -1
- package/dist/utils/lp-inspect-types.d.ts +0 -1
- package/dist/utils/lp-inspect-types.js +0 -1
- package/dist/utils/private-sale-types.d.ts +0 -1
- package/dist/utils/private-sale-types.js +0 -1
- package/dist/utils/quote-helpers/index.d.ts +0 -107
- package/dist/utils/quote-helpers/index.js +0 -346
- package/dist/utils/quote-helpers/types.d.ts +0 -16
- package/dist/utils/quote-helpers/types.js +0 -1
- package/dist/utils/quote-helpers-types.d.ts +0 -1
- package/dist/utils/quote-helpers-types.js +0 -1
- /package/dist/{chains/bsc/four/submit/types.js → __tests__/subpath-exports.test.d.ts} +0 -0
|
@@ -1,572 +0,0 @@
|
|
|
1
|
-
import { ethers, Wallet, Contract } from 'ethers';
|
|
2
|
-
import { GAS_LIMITS } from '../../../../shared/constants/index.js';
|
|
3
|
-
import { getDeadline } from '../../../../utils/bundle-helpers.js';
|
|
4
|
-
import { V2_ROUTER_ABI, V3_ROUTER02_ABI, ERC20_BALANCE_ABI, ERC20_ABI } from '../../../../shared/abis/common.js';
|
|
5
|
-
import { ADDRESSES, PROFIT_CONFIG } from '../../../../utils/constants.js';
|
|
6
|
-
import { generateWallets } from '../../../../utils/wallet.js';
|
|
7
|
-
import { quoteV2, quoteV3 } from '../../../../utils/quote-helpers.js';
|
|
8
|
-
const NATIVE_TRANSFER_GAS_LIMIT = GAS_LIMITS.NATIVE_TRANSFER;
|
|
9
|
-
const ERC20_TRANSFER_GAS_LIMIT_HOP = GAS_LIMITS.ERC20_TRANSFER;
|
|
10
|
-
const BRIBE_GAS_LIMIT = GAS_LIMITS.BRIBE;
|
|
11
|
-
const FLAT_FEE = 0n;
|
|
12
|
-
const PANCAKE_V2_ROUTER_ADDRESS = ADDRESSES.BSC.PancakeV2Router;
|
|
13
|
-
const PANCAKE_V3_ROUTER_ADDRESS = ADDRESSES.BSC.PancakeV3Router;
|
|
14
|
-
const PANCAKE_V2_ROUTER_ABI = V2_ROUTER_ABI;
|
|
15
|
-
const PANCAKE_V3_ROUTER_ABI = V3_ROUTER02_ABI;
|
|
16
|
-
const ERC20_BALANCE_OF_ABI = ERC20_BALANCE_ABI;
|
|
17
|
-
const WBNB_ADDRESS = ADDRESSES.BSC.WBNB;
|
|
18
|
-
/**
|
|
19
|
-
* 生成分发多跳路径
|
|
20
|
-
*/
|
|
21
|
-
export function generateDisperseHopPaths(targetAddresses, hopCount, provider) {
|
|
22
|
-
if (hopCount <= 0) {
|
|
23
|
-
return targetAddresses.map(addr => ({
|
|
24
|
-
targetAddress: addr,
|
|
25
|
-
hopWallets: [],
|
|
26
|
-
hopWalletsInfo: []
|
|
27
|
-
}));
|
|
28
|
-
}
|
|
29
|
-
return targetAddresses.map(targetAddress => {
|
|
30
|
-
const hopWalletsInfo = generateWallets(hopCount);
|
|
31
|
-
const hopWallets = hopWalletsInfo.map(w => new Wallet(w.privateKey, provider));
|
|
32
|
-
return { targetAddress, hopWallets, hopWalletsInfo };
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* 构建原生代币多跳转账链
|
|
37
|
-
*/
|
|
38
|
-
export async function buildNativeHopChain(payer, path, finalAmount, gasPrice, chainId, txType, payerNonce) {
|
|
39
|
-
const signedTxs = [];
|
|
40
|
-
const hopCount = path.hopWallets.length;
|
|
41
|
-
if (hopCount === 0) {
|
|
42
|
-
const tx = {
|
|
43
|
-
to: path.targetAddress,
|
|
44
|
-
value: finalAmount,
|
|
45
|
-
nonce: payerNonce,
|
|
46
|
-
gasLimit: NATIVE_TRANSFER_GAS_LIMIT,
|
|
47
|
-
chainId,
|
|
48
|
-
type: txType
|
|
49
|
-
};
|
|
50
|
-
if (txType === 2) {
|
|
51
|
-
tx.maxFeePerGas = gasPrice;
|
|
52
|
-
tx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
tx.gasPrice = gasPrice;
|
|
56
|
-
}
|
|
57
|
-
signedTxs.push(await payer.signTransaction(tx));
|
|
58
|
-
return signedTxs;
|
|
59
|
-
}
|
|
60
|
-
const hopGasCost = NATIVE_TRANSFER_GAS_LIMIT * gasPrice;
|
|
61
|
-
// 计算每跳需要的金额
|
|
62
|
-
const amountsPerHop = [];
|
|
63
|
-
for (let i = 0; i < hopCount; i++) {
|
|
64
|
-
const remainingHops = hopCount - i;
|
|
65
|
-
amountsPerHop.push(finalAmount + hopGasCost * BigInt(remainingHops));
|
|
66
|
-
}
|
|
67
|
-
// payer → hop1
|
|
68
|
-
const firstTx = {
|
|
69
|
-
to: path.hopWallets[0].address,
|
|
70
|
-
value: amountsPerHop[0],
|
|
71
|
-
nonce: payerNonce,
|
|
72
|
-
gasLimit: NATIVE_TRANSFER_GAS_LIMIT,
|
|
73
|
-
chainId,
|
|
74
|
-
type: txType
|
|
75
|
-
};
|
|
76
|
-
if (txType === 2) {
|
|
77
|
-
firstTx.maxFeePerGas = gasPrice;
|
|
78
|
-
firstTx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
firstTx.gasPrice = gasPrice;
|
|
82
|
-
}
|
|
83
|
-
signedTxs.push(await payer.signTransaction(firstTx));
|
|
84
|
-
// hop1 → hop2 → ... → target
|
|
85
|
-
for (let i = 0; i < hopCount; i++) {
|
|
86
|
-
const fromWallet = path.hopWallets[i];
|
|
87
|
-
const toAddress = i === hopCount - 1 ? path.targetAddress : path.hopWallets[i + 1].address;
|
|
88
|
-
const amount = i === hopCount - 1 ? finalAmount : amountsPerHop[i + 1];
|
|
89
|
-
const tx = {
|
|
90
|
-
to: toAddress,
|
|
91
|
-
value: amount,
|
|
92
|
-
nonce: 0,
|
|
93
|
-
gasLimit: NATIVE_TRANSFER_GAS_LIMIT,
|
|
94
|
-
chainId,
|
|
95
|
-
type: txType
|
|
96
|
-
};
|
|
97
|
-
if (txType === 2) {
|
|
98
|
-
tx.maxFeePerGas = gasPrice;
|
|
99
|
-
tx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
tx.gasPrice = gasPrice;
|
|
103
|
-
}
|
|
104
|
-
signedTxs.push(await fromWallet.signTransaction(tx));
|
|
105
|
-
}
|
|
106
|
-
return signedTxs;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* 构建 ERC20 多跳转账链(需要配合 BNB 多跳使用)
|
|
110
|
-
*/
|
|
111
|
-
export async function buildERC20HopChain(payer, path, erc20Address, erc20Amount, gasPrice, chainId, txType, payerNonce) {
|
|
112
|
-
const signedTxs = [];
|
|
113
|
-
const hopCount = path.hopWallets.length;
|
|
114
|
-
const erc20Interface = new ethers.Interface(ERC20_ABI);
|
|
115
|
-
if (hopCount === 0) {
|
|
116
|
-
const data = erc20Interface.encodeFunctionData('transfer', [path.targetAddress, erc20Amount]);
|
|
117
|
-
const tx = {
|
|
118
|
-
to: erc20Address,
|
|
119
|
-
data,
|
|
120
|
-
value: 0n,
|
|
121
|
-
nonce: payerNonce,
|
|
122
|
-
gasLimit: ERC20_TRANSFER_GAS_LIMIT_HOP,
|
|
123
|
-
chainId,
|
|
124
|
-
type: txType
|
|
125
|
-
};
|
|
126
|
-
if (txType === 2) {
|
|
127
|
-
tx.maxFeePerGas = gasPrice;
|
|
128
|
-
tx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
tx.gasPrice = gasPrice;
|
|
132
|
-
}
|
|
133
|
-
signedTxs.push(await payer.signTransaction(tx));
|
|
134
|
-
return signedTxs;
|
|
135
|
-
}
|
|
136
|
-
// payer → hop1
|
|
137
|
-
const firstData = erc20Interface.encodeFunctionData('transfer', [path.hopWallets[0].address, erc20Amount]);
|
|
138
|
-
const firstTx = {
|
|
139
|
-
to: erc20Address,
|
|
140
|
-
data: firstData,
|
|
141
|
-
value: 0n,
|
|
142
|
-
nonce: payerNonce,
|
|
143
|
-
gasLimit: ERC20_TRANSFER_GAS_LIMIT_HOP,
|
|
144
|
-
chainId,
|
|
145
|
-
type: txType
|
|
146
|
-
};
|
|
147
|
-
if (txType === 2) {
|
|
148
|
-
firstTx.maxFeePerGas = gasPrice;
|
|
149
|
-
firstTx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
firstTx.gasPrice = gasPrice;
|
|
153
|
-
}
|
|
154
|
-
signedTxs.push(await payer.signTransaction(firstTx));
|
|
155
|
-
// hop1 → hop2 → ... → target (nonce=1,nonce=0 用于 BNB 转发)
|
|
156
|
-
for (let i = 0; i < hopCount; i++) {
|
|
157
|
-
const fromWallet = path.hopWallets[i];
|
|
158
|
-
const toAddress = i === hopCount - 1 ? path.targetAddress : path.hopWallets[i + 1].address;
|
|
159
|
-
const data = erc20Interface.encodeFunctionData('transfer', [toAddress, erc20Amount]);
|
|
160
|
-
const tx = {
|
|
161
|
-
to: erc20Address,
|
|
162
|
-
data,
|
|
163
|
-
value: 0n,
|
|
164
|
-
nonce: 1, // nonce=0 已用于 BNB 转发
|
|
165
|
-
gasLimit: ERC20_TRANSFER_GAS_LIMIT_HOP,
|
|
166
|
-
chainId,
|
|
167
|
-
type: txType
|
|
168
|
-
};
|
|
169
|
-
if (txType === 2) {
|
|
170
|
-
tx.maxFeePerGas = gasPrice;
|
|
171
|
-
tx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
|
|
172
|
-
}
|
|
173
|
-
else {
|
|
174
|
-
tx.gasPrice = gasPrice;
|
|
175
|
-
}
|
|
176
|
-
signedTxs.push(await fromWallet.signTransaction(tx));
|
|
177
|
-
}
|
|
178
|
-
return signedTxs;
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* 构建 BNB 多跳转账链(为 ERC20 中间钱包预留 gas)
|
|
182
|
-
*/
|
|
183
|
-
export async function buildBNBHopChainForERC20(payer, path, finalGasAmount, gasPrice, chainId, txType, payerNonce) {
|
|
184
|
-
const signedTxs = [];
|
|
185
|
-
const hopCount = path.hopWallets.length;
|
|
186
|
-
if (hopCount === 0) {
|
|
187
|
-
// 无多跳时不需要转 gas
|
|
188
|
-
return signedTxs;
|
|
189
|
-
}
|
|
190
|
-
const hopGasCost = NATIVE_TRANSFER_GAS_LIMIT * gasPrice;
|
|
191
|
-
const erc20GasCost = ERC20_TRANSFER_GAS_LIMIT_HOP * gasPrice;
|
|
192
|
-
// 每个中间钱包需要: BNB 转发 gas + ERC20 转发 gas
|
|
193
|
-
const gasPerHop = hopGasCost + erc20GasCost * 2n; // 2倍安全系数
|
|
194
|
-
// 计算每跳需要的金额
|
|
195
|
-
const amountsPerHop = [];
|
|
196
|
-
for (let i = 0; i < hopCount; i++) {
|
|
197
|
-
const remainingHops = hopCount - i;
|
|
198
|
-
amountsPerHop.push(finalGasAmount + gasPerHop * BigInt(remainingHops));
|
|
199
|
-
}
|
|
200
|
-
// payer → hop1
|
|
201
|
-
const firstTx = {
|
|
202
|
-
to: path.hopWallets[0].address,
|
|
203
|
-
value: amountsPerHop[0],
|
|
204
|
-
nonce: payerNonce,
|
|
205
|
-
gasLimit: NATIVE_TRANSFER_GAS_LIMIT,
|
|
206
|
-
chainId,
|
|
207
|
-
type: txType
|
|
208
|
-
};
|
|
209
|
-
if (txType === 2) {
|
|
210
|
-
firstTx.maxFeePerGas = gasPrice;
|
|
211
|
-
firstTx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
|
|
212
|
-
}
|
|
213
|
-
else {
|
|
214
|
-
firstTx.gasPrice = gasPrice;
|
|
215
|
-
}
|
|
216
|
-
signedTxs.push(await payer.signTransaction(firstTx));
|
|
217
|
-
// hop1 → hop2 → ... → target (nonce=0)
|
|
218
|
-
for (let i = 0; i < hopCount; i++) {
|
|
219
|
-
const fromWallet = path.hopWallets[i];
|
|
220
|
-
const toAddress = i === hopCount - 1 ? path.targetAddress : path.hopWallets[i + 1].address;
|
|
221
|
-
const amount = i === hopCount - 1 ? finalGasAmount : amountsPerHop[i + 1];
|
|
222
|
-
const tx = {
|
|
223
|
-
to: toAddress,
|
|
224
|
-
value: amount,
|
|
225
|
-
nonce: 0,
|
|
226
|
-
gasLimit: NATIVE_TRANSFER_GAS_LIMIT,
|
|
227
|
-
chainId,
|
|
228
|
-
type: txType
|
|
229
|
-
};
|
|
230
|
-
if (txType === 2) {
|
|
231
|
-
tx.maxFeePerGas = gasPrice;
|
|
232
|
-
tx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
|
|
233
|
-
}
|
|
234
|
-
else {
|
|
235
|
-
tx.gasPrice = gasPrice;
|
|
236
|
-
}
|
|
237
|
-
signedTxs.push(await fromWallet.signTransaction(tx));
|
|
238
|
-
}
|
|
239
|
-
return signedTxs;
|
|
240
|
-
}
|
|
241
|
-
export function createPancakeContext(config) {
|
|
242
|
-
const chainId = config.chainId ?? 56;
|
|
243
|
-
const provider = new ethers.JsonRpcProvider(config.rpcUrl, {
|
|
244
|
-
chainId,
|
|
245
|
-
name: 'BSC'
|
|
246
|
-
});
|
|
247
|
-
return { chainId, provider };
|
|
248
|
-
}
|
|
249
|
-
export async function quoteSellOutput({ routeParams, sellAmountWei, provider, v2RouterAddress }) {
|
|
250
|
-
console.log(`[quoteSellOutput] 开始报价, routeType=${routeParams.routeType}, sellAmount=${sellAmountWei} wei`);
|
|
251
|
-
// ==================== V2 报价 ====================
|
|
252
|
-
if (routeParams.routeType === 'v2') {
|
|
253
|
-
const { v2Path } = routeParams;
|
|
254
|
-
const tokenIn = v2Path[0];
|
|
255
|
-
const tokenOut = v2Path[v2Path.length - 1];
|
|
256
|
-
console.log(`[quoteSellOutput] V2 路径: ${v2Path.join(' → ')}`);
|
|
257
|
-
const result = await quoteV2(provider, tokenIn, tokenOut, sellAmountWei, 'BSC', v2RouterAddress);
|
|
258
|
-
if (result.amountOut > 0n) {
|
|
259
|
-
console.log(`[quoteSellOutput] V2 报价成功: ${ethers.formatEther(result.amountOut)} BNB`);
|
|
260
|
-
return { estimatedBNBOut: result.amountOut };
|
|
261
|
-
}
|
|
262
|
-
throw new Error('V2 报价失败: 所有路径均无效');
|
|
263
|
-
}
|
|
264
|
-
// ==================== V3 单跳报价 ====================
|
|
265
|
-
if (routeParams.routeType === 'v3-single') {
|
|
266
|
-
const params = routeParams;
|
|
267
|
-
console.log(`[quoteSellOutput] V3 单跳: ${params.v3TokenIn} → ${params.v3TokenOut}, fee=${params.v3Fee}`);
|
|
268
|
-
// ✅ 只用 V3 报价,不 fallback 到 V2
|
|
269
|
-
const result = await quoteV3(provider, params.v3TokenIn, params.v3TokenOut, sellAmountWei, 'BSC', params.v3Fee);
|
|
270
|
-
if (result.amountOut > 0n) {
|
|
271
|
-
console.log(`[quoteSellOutput] V3 报价成功 (fee=${result.fee}): ${ethers.formatEther(result.amountOut)} BNB`);
|
|
272
|
-
return { estimatedBNBOut: result.amountOut };
|
|
273
|
-
}
|
|
274
|
-
// ❌ V3 报价失败时不再 fallback 到 V2,因为价格可能差异很大
|
|
275
|
-
throw new Error(`V3 单跳报价失败: tokenIn=${params.v3TokenIn}, tokenOut=${params.v3TokenOut}, fee=${params.v3Fee}`);
|
|
276
|
-
}
|
|
277
|
-
// ==================== V3 多跳报价 ====================
|
|
278
|
-
if (routeParams.routeType === 'v3-multi') {
|
|
279
|
-
const params = routeParams;
|
|
280
|
-
console.log(`[quoteSellOutput] V3 多跳: LPs=${params.v3LpAddresses?.join(', ')}`);
|
|
281
|
-
// ✅ V3 多跳:只用 V3 报价,不 fallback 到 V2
|
|
282
|
-
if (params.v3LpAddresses && params.v3LpAddresses.length > 0) {
|
|
283
|
-
const tokenIn = params.v3ExactTokenIn;
|
|
284
|
-
const WBNB = '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
|
|
285
|
-
const result = await quoteV3(provider, tokenIn, WBNB, sellAmountWei, 'BSC');
|
|
286
|
-
if (result.amountOut > 0n) {
|
|
287
|
-
console.log(`[quoteSellOutput] V3 多跳报价成功: ${ethers.formatEther(result.amountOut)} BNB`);
|
|
288
|
-
return { estimatedBNBOut: result.amountOut };
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
// ❌ V3 多跳报价失败时不再 fallback 到 V2,因为价格可能差异很大
|
|
292
|
-
throw new Error(`V3 多跳报价失败: LPs=${params.v3LpAddresses?.join(', ')}, tokenIn=${params.v3ExactTokenIn}`);
|
|
293
|
-
}
|
|
294
|
-
throw new Error(`不支持的路由类型: ${routeParams.routeType}`);
|
|
295
|
-
}
|
|
296
|
-
export async function buildSwapTransactions({ routeParams, sellAmountWei, buyAmountBNB, buyer, seller, tokenAddress, useNativeToken = true, v2RouterAddress }) {
|
|
297
|
-
const deadline = getDeadline();
|
|
298
|
-
const buyValue = useNativeToken ? buyAmountBNB + FLAT_FEE : 0n;
|
|
299
|
-
if (routeParams.routeType === 'v2') {
|
|
300
|
-
const { v2Path } = routeParams;
|
|
301
|
-
const reversePath = [...v2Path].reverse();
|
|
302
|
-
const routerAddr = v2RouterAddress || PANCAKE_V2_ROUTER_ADDRESS;
|
|
303
|
-
const v2RouterSeller = new Contract(routerAddr, PANCAKE_V2_ROUTER_ABI, seller);
|
|
304
|
-
const v2RouterBuyer = new Contract(routerAddr, PANCAKE_V2_ROUTER_ABI, buyer);
|
|
305
|
-
let sellUnsigned;
|
|
306
|
-
let buyUnsigned;
|
|
307
|
-
if (useNativeToken) {
|
|
308
|
-
// ✅ 原生代币模式(BNB)
|
|
309
|
-
// 卖出:Token → WBNB → BNB
|
|
310
|
-
sellUnsigned = await v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline);
|
|
311
|
-
// 买入:BNB → WBNB → Token
|
|
312
|
-
buyUnsigned = await v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, reversePath, buyer.address, deadline, { value: buyValue });
|
|
313
|
-
}
|
|
314
|
-
else {
|
|
315
|
-
// ✅ ERC20 模式(如 USDT)
|
|
316
|
-
// 卖出:Token → USDT
|
|
317
|
-
sellUnsigned = await v2RouterSeller.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline);
|
|
318
|
-
// 买入:USDT → Token
|
|
319
|
-
buyUnsigned = await v2RouterBuyer.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(buyAmountBNB, // USDT 数量
|
|
320
|
-
0n, reversePath, buyer.address, deadline);
|
|
321
|
-
}
|
|
322
|
-
return { sellUnsigned, buyUnsigned };
|
|
323
|
-
}
|
|
324
|
-
if (routeParams.routeType === 'v3-single') {
|
|
325
|
-
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
326
|
-
const v3RouterIface = new ethers.Interface(PANCAKE_V3_ROUTER_ABI);
|
|
327
|
-
const v3RouterSeller = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, seller);
|
|
328
|
-
const v3RouterBuyer = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, buyer);
|
|
329
|
-
// 卖出:Token → WBNB,需要 unwrapWETH9
|
|
330
|
-
const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
|
|
331
|
-
tokenIn: v3TokenIn,
|
|
332
|
-
tokenOut: v3TokenOut,
|
|
333
|
-
fee: v3Fee,
|
|
334
|
-
recipient: PANCAKE_V3_ROUTER_ADDRESS, // 先发给 Router
|
|
335
|
-
amountIn: sellAmountWei,
|
|
336
|
-
amountOutMinimum: 0n,
|
|
337
|
-
sqrtPriceLimitX96: 0n
|
|
338
|
-
}]);
|
|
339
|
-
const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
|
|
340
|
-
const sellUnsigned = await v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData, sellUnwrapData]);
|
|
341
|
-
// 买入:WBNB → Token
|
|
342
|
-
const buySwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
|
|
343
|
-
tokenIn: v3TokenOut,
|
|
344
|
-
tokenOut: v3TokenIn,
|
|
345
|
-
fee: v3Fee,
|
|
346
|
-
recipient: buyer.address,
|
|
347
|
-
amountIn: buyAmountBNB,
|
|
348
|
-
amountOutMinimum: 0n,
|
|
349
|
-
sqrtPriceLimitX96: 0n
|
|
350
|
-
}]);
|
|
351
|
-
const buyUnsigned = await v3RouterBuyer.multicall.populateTransaction(deadline, [buySwapData], { value: buyValue });
|
|
352
|
-
return { sellUnsigned, buyUnsigned };
|
|
353
|
-
}
|
|
354
|
-
// V3 多跳暂不支持官方合约
|
|
355
|
-
throw new Error('V3 多跳路由暂不支持官方合约,请使用 V2 路由或 V3 单跳');
|
|
356
|
-
}
|
|
357
|
-
export async function calculateBuyerBudget({ buyer, quotedBNBOut, reserveGasBNB, useNativeToken = true, quoteToken, quoteTokenDecimals = 18, provider }) {
|
|
358
|
-
const reserveGas = ethers.parseEther((reserveGasBNB ?? 0.0005).toString());
|
|
359
|
-
// ✅ 已移除滑点保护:直接使用报价金额
|
|
360
|
-
const buyAmountBNB = quotedBNBOut;
|
|
361
|
-
// ✅ 根据是否使用原生代币获取不同的余额
|
|
362
|
-
let buyerBalance;
|
|
363
|
-
if (useNativeToken) {
|
|
364
|
-
buyerBalance = await buyer.provider.getBalance(buyer.address);
|
|
365
|
-
const requiredBalance = buyAmountBNB + FLAT_FEE + reserveGas;
|
|
366
|
-
if (buyerBalance < requiredBalance) {
|
|
367
|
-
throw new Error(`买方余额不足: 需要 ${ethers.formatEther(requiredBalance)} BNB, 实际 ${ethers.formatEther(buyerBalance)} BNB`);
|
|
368
|
-
}
|
|
369
|
-
return { buyerBalance, reserveGas, requiredBalance, buyAmountBNB, useNativeToken };
|
|
370
|
-
}
|
|
371
|
-
else {
|
|
372
|
-
// ERC20 代币余额
|
|
373
|
-
const erc20 = new Contract(quoteToken, ERC20_BALANCE_OF_ABI, provider || buyer.provider);
|
|
374
|
-
buyerBalance = await erc20.balanceOf(buyer.address);
|
|
375
|
-
const requiredBalance = buyAmountBNB + FLAT_FEE; // ERC20 不需要预留 Gas 在代币余额中
|
|
376
|
-
if (buyerBalance < requiredBalance) {
|
|
377
|
-
throw new Error(`买方代币余额不足: 需要 ${ethers.formatUnits(requiredBalance, quoteTokenDecimals)}, 实际 ${ethers.formatUnits(buyerBalance, quoteTokenDecimals)}`);
|
|
378
|
-
}
|
|
379
|
-
// ✅ ERC20 购买时,还需要检查买方是否有足够 BNB 支付 Gas
|
|
380
|
-
const buyerBnbBalance = await buyer.provider.getBalance(buyer.address);
|
|
381
|
-
if (buyerBnbBalance < reserveGas) {
|
|
382
|
-
throw new Error(`买方 BNB 余额不足 (用于支付 Gas): 需要 ${ethers.formatEther(reserveGas)} BNB, 实际 ${ethers.formatEther(buyerBnbBalance)} BNB`);
|
|
383
|
-
}
|
|
384
|
-
return { buyerBalance, reserveGas, requiredBalance, buyAmountBNB, useNativeToken };
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
/**
|
|
388
|
-
* ✅ 从前端传入的 startNonces 构建 NoncePlan(用于性能优化)
|
|
389
|
-
* 顺序:[sellerBaseNonce, buyerNonce]
|
|
390
|
-
*/
|
|
391
|
-
export function buildNoncePlanFromNonces(startNonces, sameAddress, approvalExists, profitNeeded, needBribeTx) {
|
|
392
|
-
if (sameAddress) {
|
|
393
|
-
let idx = 0;
|
|
394
|
-
const bribeNonce = needBribeTx ? startNonces[0] + idx++ : undefined;
|
|
395
|
-
if (approvalExists)
|
|
396
|
-
idx++;
|
|
397
|
-
const sellerNonce = startNonces[0] + idx++;
|
|
398
|
-
const buyerNonce = startNonces[0] + idx++;
|
|
399
|
-
const profitNonce = profitNeeded ? startNonces[0] + idx : undefined;
|
|
400
|
-
return { sellerNonce, buyerNonce, bribeNonce, profitNonce };
|
|
401
|
-
}
|
|
402
|
-
let sellerIdx = 0;
|
|
403
|
-
const bribeNonce = needBribeTx ? startNonces[0] + sellerIdx++ : undefined;
|
|
404
|
-
if (approvalExists)
|
|
405
|
-
sellerIdx++;
|
|
406
|
-
const sellerNonce = startNonces[0] + sellerIdx++;
|
|
407
|
-
const profitNonce = profitNeeded ? startNonces[0] + sellerIdx : undefined;
|
|
408
|
-
const buyerNonce = startNonces[1];
|
|
409
|
-
return { sellerNonce, buyerNonce, bribeNonce, profitNonce };
|
|
410
|
-
}
|
|
411
|
-
/**
|
|
412
|
-
* ✅ 规划 nonce
|
|
413
|
-
* 交易顺序:贿赂 → 授权 → 卖出 → 买入 → 利润
|
|
414
|
-
* 贿赂和利润由卖方发送
|
|
415
|
-
*/
|
|
416
|
-
export async function planNonces({ seller, buyer, sameAddress, approvalExists, profitNeeded, needBribeTx, nonceManager }) {
|
|
417
|
-
if (sameAddress) {
|
|
418
|
-
// 同一地址:贿赂(可选) + 授权(可选) + 卖出 + 买入 + 利润(可选)
|
|
419
|
-
const txCount = countTruthy([needBribeTx, approvalExists, true, true, profitNeeded]);
|
|
420
|
-
const nonces = await nonceManager.getNextNonceBatch(buyer, txCount);
|
|
421
|
-
let idx = 0;
|
|
422
|
-
const bribeNonce = needBribeTx ? nonces[idx++] : undefined;
|
|
423
|
-
if (approvalExists)
|
|
424
|
-
idx++;
|
|
425
|
-
const sellerNonce = nonces[idx++];
|
|
426
|
-
const buyerNonce = nonces[idx++];
|
|
427
|
-
const profitNonce = profitNeeded ? nonces[idx] : undefined;
|
|
428
|
-
return { sellerNonce, buyerNonce, bribeNonce, profitNonce };
|
|
429
|
-
}
|
|
430
|
-
if (needBribeTx || approvalExists || profitNeeded) {
|
|
431
|
-
// 卖方需要多个 nonce:贿赂(可选) + 授权(可选) + 卖出 + 利润(可选)
|
|
432
|
-
const sellerTxCount = countTruthy([needBribeTx, approvalExists, true, profitNeeded]);
|
|
433
|
-
// ✅ 并行获取 seller 和 buyer 的 nonce
|
|
434
|
-
const [sellerNonces, buyerNonce] = await Promise.all([
|
|
435
|
-
nonceManager.getNextNonceBatch(seller, sellerTxCount),
|
|
436
|
-
nonceManager.getNextNonce(buyer)
|
|
437
|
-
]);
|
|
438
|
-
let idx = 0;
|
|
439
|
-
const bribeNonce = needBribeTx ? sellerNonces[idx++] : undefined;
|
|
440
|
-
if (approvalExists)
|
|
441
|
-
idx++;
|
|
442
|
-
const sellerNonce = sellerNonces[idx++];
|
|
443
|
-
const profitNonce = profitNeeded ? sellerNonces[idx] : undefined;
|
|
444
|
-
return { sellerNonce, buyerNonce, bribeNonce, profitNonce };
|
|
445
|
-
}
|
|
446
|
-
// ✅ 并行获取 seller 和 buyer 的 nonce
|
|
447
|
-
const [sellerNonce, buyerNonce] = await Promise.all([
|
|
448
|
-
nonceManager.getNextNonce(seller),
|
|
449
|
-
nonceManager.getNextNonce(buyer)
|
|
450
|
-
]);
|
|
451
|
-
return { sellerNonce, buyerNonce };
|
|
452
|
-
}
|
|
453
|
-
/**
|
|
454
|
-
* 根据 userType 计算利润率
|
|
455
|
-
*/
|
|
456
|
-
export function getProfitRateBps(userType) {
|
|
457
|
-
if (userType === 'v0') {
|
|
458
|
-
return PROFIT_CONFIG.RATE_BPS_V0; // 0.06%
|
|
459
|
-
}
|
|
460
|
-
else if (userType === 'v1') {
|
|
461
|
-
return PROFIT_CONFIG.RATE_BPS_V1; // 0.05%
|
|
462
|
-
}
|
|
463
|
-
return PROFIT_CONFIG.RATE_BPS_V0; // 默认 0.06%
|
|
464
|
-
}
|
|
465
|
-
export function calculateProfitAmount(estimatedBNBOut, userType) {
|
|
466
|
-
if (estimatedBNBOut <= 0n) {
|
|
467
|
-
return 0n;
|
|
468
|
-
}
|
|
469
|
-
const rateBps = getProfitRateBps(userType);
|
|
470
|
-
return (estimatedBNBOut * BigInt(rateBps)) / 10000n;
|
|
471
|
-
}
|
|
472
|
-
/**
|
|
473
|
-
* ✅ 获取 ERC20 代币 → 原生代币(BNB)的报价
|
|
474
|
-
* 用于将 ERC20 利润转换为 BNB
|
|
475
|
-
* 使用共享的报价函数
|
|
476
|
-
*/
|
|
477
|
-
export async function getERC20ToNativeQuote(provider, tokenAddress, tokenAmount, version = 'v2', fee) {
|
|
478
|
-
if (tokenAmount <= 0n)
|
|
479
|
-
return 0n;
|
|
480
|
-
const tokenLower = tokenAddress.toLowerCase();
|
|
481
|
-
const wbnbLower = WBNB_ADDRESS.toLowerCase();
|
|
482
|
-
// 如果代币本身就是 WBNB,直接返回
|
|
483
|
-
if (tokenLower === wbnbLower) {
|
|
484
|
-
return tokenAmount;
|
|
485
|
-
}
|
|
486
|
-
// 使用共享的报价函数
|
|
487
|
-
if (version === 'v3') {
|
|
488
|
-
const result = await quoteV3(provider, tokenAddress, WBNB_ADDRESS, tokenAmount, 'BSC', fee);
|
|
489
|
-
if (result.amountOut > 0n) {
|
|
490
|
-
console.log(`[getERC20ToNativeQuote] V3 报价成功: ${ethers.formatEther(result.amountOut)} BNB`);
|
|
491
|
-
return result.amountOut;
|
|
492
|
-
}
|
|
493
|
-
console.warn(`[getERC20ToNativeQuote] V3 报价失败`);
|
|
494
|
-
return 0n;
|
|
495
|
-
}
|
|
496
|
-
// V2 报价
|
|
497
|
-
const result = await quoteV2(provider, tokenAddress, WBNB_ADDRESS, tokenAmount, 'BSC');
|
|
498
|
-
if (result.amountOut > 0n) {
|
|
499
|
-
console.log(`[getERC20ToNativeQuote] V2 报价成功: ${ethers.formatEther(result.amountOut)} BNB`);
|
|
500
|
-
return result.amountOut;
|
|
501
|
-
}
|
|
502
|
-
console.warn(`[getERC20ToNativeQuote] V2 报价失败`);
|
|
503
|
-
return 0n;
|
|
504
|
-
}
|
|
505
|
-
export async function validateFinalBalances({ sameAddress, buyerBalance, buyAmountBNB, reserveGas, gasLimit, gasPrice, useNativeToken = true, quoteTokenDecimals = 18, provider, buyerAddress }) {
|
|
506
|
-
const gasCost = gasLimit * gasPrice;
|
|
507
|
-
if (sameAddress) {
|
|
508
|
-
// 同一地址:需要足够的余额支付两笔交易
|
|
509
|
-
if (useNativeToken) {
|
|
510
|
-
const requiredCombined = buyAmountBNB + FLAT_FEE * 2n + gasCost * 2n;
|
|
511
|
-
if (buyerBalance < requiredCombined) {
|
|
512
|
-
throw new Error(`账户余额不足:\n - 需要: ${ethers.formatEther(requiredCombined)} BNB(含两笔Gas与两笔手续费)\n - 实际: ${ethers.formatEther(buyerBalance)} BNB`);
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
else {
|
|
516
|
-
// ERC20:检查代币余额 + BNB Gas 余额
|
|
517
|
-
const requiredToken = buyAmountBNB + FLAT_FEE * 2n;
|
|
518
|
-
if (buyerBalance < requiredToken) {
|
|
519
|
-
throw new Error(`账户代币余额不足:\n - 需要: ${ethers.formatUnits(requiredToken, quoteTokenDecimals)}\n - 实际: ${ethers.formatUnits(buyerBalance, quoteTokenDecimals)}`);
|
|
520
|
-
}
|
|
521
|
-
// 检查 BNB Gas
|
|
522
|
-
if (provider && buyerAddress) {
|
|
523
|
-
const bnbBalance = await provider.getBalance(buyerAddress);
|
|
524
|
-
const requiredGas = gasCost * 2n;
|
|
525
|
-
if (bnbBalance < requiredGas) {
|
|
526
|
-
throw new Error(`账户 BNB 余额不足 (用于支付 Gas):\n - 需要: ${ethers.formatEther(requiredGas)} BNB\n - 实际: ${ethers.formatEther(bnbBalance)} BNB`);
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
return;
|
|
531
|
-
}
|
|
532
|
-
// 不同地址
|
|
533
|
-
if (useNativeToken) {
|
|
534
|
-
const requiredBuyer = buyAmountBNB + FLAT_FEE + reserveGas;
|
|
535
|
-
if (buyerBalance < requiredBuyer) {
|
|
536
|
-
throw new Error(`买方余额不足:\n - 需要: ${ethers.formatEther(requiredBuyer)} BNB\n - 实际: ${ethers.formatEther(buyerBalance)} BNB`);
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
// ERC20 余额已在 calculateBuyerBudget 中检查过
|
|
540
|
-
}
|
|
541
|
-
export function countTruthy(values) {
|
|
542
|
-
return values.filter(Boolean).length;
|
|
543
|
-
}
|
|
544
|
-
export function getGasLimit(config, defaultGas = 800000) {
|
|
545
|
-
if (config.gasLimit !== undefined) {
|
|
546
|
-
return typeof config.gasLimit === 'bigint' ? config.gasLimit : BigInt(config.gasLimit);
|
|
547
|
-
}
|
|
548
|
-
const multiplier = config.gasLimitMultiplier ?? 1.0;
|
|
549
|
-
const calculatedGas = Math.ceil(defaultGas * multiplier);
|
|
550
|
-
return BigInt(calculatedGas);
|
|
551
|
-
}
|
|
552
|
-
export async function getGasPrice(provider, config) {
|
|
553
|
-
const feeData = await provider.getFeeData();
|
|
554
|
-
let gasPrice = feeData.gasPrice || ethers.parseUnits('3', 'gwei');
|
|
555
|
-
if (config.minGasPriceGwei) {
|
|
556
|
-
const minGas = ethers.parseUnits(config.minGasPriceGwei.toString(), 'gwei');
|
|
557
|
-
if (gasPrice < minGas)
|
|
558
|
-
gasPrice = minGas;
|
|
559
|
-
}
|
|
560
|
-
if (config.maxGasPriceGwei) {
|
|
561
|
-
const maxGas = ethers.parseUnits(config.maxGasPriceGwei.toString(), 'gwei');
|
|
562
|
-
if (gasPrice > maxGas)
|
|
563
|
-
gasPrice = maxGas;
|
|
564
|
-
}
|
|
565
|
-
return gasPrice;
|
|
566
|
-
}
|
|
567
|
-
// ✅ ABI 别名(从公共模块导入)
|
|
568
|
-
// ✅ 使用官方 PancakeSwap Router 地址
|
|
569
|
-
// 常量
|
|
570
|
-
// ✅ getDeadline 从 bundle-helpers.js 导入
|
|
571
|
-
// ✅ STABLE_COINS 和 V3_FEE_TIERS 已移至 ../utils/quote-helpers.ts
|
|
572
|
-
// ✅ 已移除授权相关 ABI - 用户需要提前一键授权
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import type { PancakeBundleSwapSignParams, PancakeSwapResult, PancakeBatchSwapSignParams, PancakeBatchSwapResult, PancakeQuickBatchSwapResult, PancakeCrossSwapParams, PancakeCrossSwapResult } from './types.js';
|
|
2
|
-
export type { UserType, PancakeSwapSignConfig, SwapRouteType, PancakeSwapConfig, V2RouteParams, V3SingleRouteParams, V3MultiRouteParams, RouteParams, PancakeBundleSwapSignParams, PancakeBundleSwapParams, PancakeSwapResult, PancakeBatchSwapSignParams, PancakeBatchSwapResult, PancakeQuickBatchSwapResult, PancakeCrossSwapParams, PancakeCrossSwapResult, } from './types.js';
|
|
3
|
-
export declare function pancakeBundleSwapMerkle(params: PancakeBundleSwapSignParams): Promise<PancakeSwapResult>;
|
|
4
|
-
/**
|
|
5
|
-
* 批量换手参数(一卖多买)
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* 批量换手结果
|
|
9
|
-
*/
|
|
10
|
-
/**
|
|
11
|
-
* PancakeSwap 批量换手(一卖多买)
|
|
12
|
-
*
|
|
13
|
-
* 功能:主钱包一次卖出 → 多个子钱包同时买入 → 在同一个区块中完成
|
|
14
|
-
* 限制:最多 24 个买方(服务器限制 25 笔交易,包含 1 笔利润交易)
|
|
15
|
-
*
|
|
16
|
-
* 交易顺序:[授权(可选)] → [卖出] → [买入1, 买入2, ..., 买入N] → [利润]
|
|
17
|
-
*/
|
|
18
|
-
export declare function pancakeBatchSwapMerkle(params: PancakeBatchSwapSignParams): Promise<PancakeBatchSwapResult>;
|
|
19
|
-
/**
|
|
20
|
-
* 快捷批量换手参数
|
|
21
|
-
* 特点:子钱包不需要预先有余额,资金来自主钱包卖出所得
|
|
22
|
-
*/
|
|
23
|
-
/**
|
|
24
|
-
* 快捷批量换手结果
|
|
25
|
-
*/
|
|
26
|
-
/**
|
|
27
|
-
* PancakeSwap 快捷批量换手(资金自动流转)
|
|
28
|
-
*
|
|
29
|
-
* 流程:[贿赂] → [卖出] → [转账多跳...] → [买入1, 买入2, ...] → [利润]
|
|
30
|
-
*
|
|
31
|
-
* 特点:
|
|
32
|
-
* - 子钱包不需要预先有余额
|
|
33
|
-
* - 资金来自主钱包卖出代币所得
|
|
34
|
-
* - 提升资金利用率
|
|
35
|
-
* - 支持 BNB 和 ERC20(如 USDT)两种模式
|
|
36
|
-
* - ✅ 支持转账多跳,隐藏资金流向
|
|
37
|
-
*
|
|
38
|
-
* 限制:根据多跳数动态计算最大买方数量
|
|
39
|
-
*/
|
|
40
|
-
export declare function pancakeQuickBatchSwapMerkle(params: any): Promise<PancakeQuickBatchSwapResult>;
|
|
41
|
-
/** 交叉换手参数(PancakeSwap V2/V3) */
|
|
42
|
-
/** 交叉换手结果(PancakeSwap V2/V3) */
|
|
43
|
-
/**
|
|
44
|
-
* PancakeSwap 交叉换手(多卖多买,循环执行)
|
|
45
|
-
* - 每个卖方单独卖出,买入金额由卖出所得等分分配给当轮买方
|
|
46
|
-
* - 利润刮取、转账多跳逻辑继承 pancakeQuickBatchSwapMerkle
|
|
47
|
-
* - 结果按顺序拼接 signedTransactions(可交由前端/后端顺序广播)
|
|
48
|
-
* - ✅ 正确处理 nonce:同一钱包在多轮中使用时 nonce 自动递增
|
|
49
|
-
*/
|
|
50
|
-
export declare function pancakeCrossSwapMerkle(params: PancakeCrossSwapParams): Promise<PancakeCrossSwapResult>;
|