four-flap-meme-sdk 2.0.0 → 2.1.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 +34 -0
- package/dist/chains/index.d.ts +13 -0
- package/dist/chains/index.js +13 -0
- package/dist/chains/xlayer/eip7702/index.d.ts +2 -0
- package/dist/flap/index.d.ts +10 -0
- package/dist/flap/index.js +8 -0
- package/dist/shared/constants/index.d.ts +2 -0
- package/dist/shared/index.d.ts +2 -0
- package/package.json +66 -1
- 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,665 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Flap Protocol 发币 + 内盘买到外盘捆绑交易
|
|
3
|
-
*
|
|
4
|
-
* 功能:在一个 Bundle 中完成:
|
|
5
|
-
* 1. 发币(创建新代币)
|
|
6
|
-
* 2. 多个钱包在 Bonding Curve(内盘)买入代币
|
|
7
|
-
* 3. 买到毕业(代币上 DEX)
|
|
8
|
-
* 4. 多个钱包在 PancakeSwap(外盘)继续买入
|
|
9
|
-
*/
|
|
10
|
-
import { ethers, Contract, Wallet } from 'ethers';
|
|
11
|
-
import { NonceManager, getOptimizedGasPrice, buildProfitHopTransactions, PROFIT_HOP_COUNT } from '../../../utils/bundle-helpers.js';
|
|
12
|
-
import { FLAP_PORTAL_ADDRESSES, FLAP_ORIGINAL_PORTAL_ADDRESSES } from '../constants.js';
|
|
13
|
-
import { buildVaultParams, VAULT_PORTAL_ADDRESSES, VAULT_PORTAL_ABI } from '../vault.js';
|
|
14
|
-
import { PROFIT_CONFIG, ZERO_ADDRESS } from '../../../utils/constants.js';
|
|
15
|
-
import { GAS_LIMITS } from '../../constants/index.js';
|
|
16
|
-
import { getGasPriceConfig, getTxType, getProfitRecipient, getBribeAmount, BLOCKRAZOR_BUILDER_EOA, PORTAL_ABI } from './config.js';
|
|
17
|
-
// ==================== ERC20 ABI ====================
|
|
18
|
-
const ERC20_ABI = [
|
|
19
|
-
{
|
|
20
|
-
type: "function",
|
|
21
|
-
name: "approve",
|
|
22
|
-
inputs: [
|
|
23
|
-
{ name: "spender", type: "address" },
|
|
24
|
-
{ name: "amount", type: "uint256" }
|
|
25
|
-
],
|
|
26
|
-
outputs: [{ name: "", type: "bool" }],
|
|
27
|
-
stateMutability: "nonpayable"
|
|
28
|
-
}
|
|
29
|
-
];
|
|
30
|
-
// ==================== PancakeSwap Router ABI ====================
|
|
31
|
-
const PANCAKE_ROUTER_ABI = [
|
|
32
|
-
{
|
|
33
|
-
type: "function",
|
|
34
|
-
name: "exactInputSingle",
|
|
35
|
-
inputs: [
|
|
36
|
-
{
|
|
37
|
-
name: "params",
|
|
38
|
-
type: "tuple",
|
|
39
|
-
components: [
|
|
40
|
-
{ name: "tokenIn", type: "address" },
|
|
41
|
-
{ name: "tokenOut", type: "address" },
|
|
42
|
-
{ name: "fee", type: "uint24" },
|
|
43
|
-
{ name: "recipient", type: "address" },
|
|
44
|
-
{ name: "amountIn", type: "uint256" },
|
|
45
|
-
{ name: "amountOutMinimum", type: "uint256" },
|
|
46
|
-
{ name: "sqrtPriceLimitX96", type: "uint160" }
|
|
47
|
-
]
|
|
48
|
-
}
|
|
49
|
-
],
|
|
50
|
-
outputs: [{ name: "amountOut", type: "uint256" }],
|
|
51
|
-
stateMutability: "payable"
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
type: "function",
|
|
55
|
-
name: "multicall",
|
|
56
|
-
inputs: [{ name: "data", type: "bytes[]" }],
|
|
57
|
-
outputs: [{ name: "results", type: "bytes[]" }],
|
|
58
|
-
stateMutability: "payable"
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
type: "function",
|
|
62
|
-
name: "swapExactTokensForTokens",
|
|
63
|
-
inputs: [
|
|
64
|
-
{ name: "amountIn", type: "uint256" },
|
|
65
|
-
{ name: "amountOutMin", type: "uint256" },
|
|
66
|
-
{ name: "path", type: "address[]" },
|
|
67
|
-
{ name: "to", type: "address" }
|
|
68
|
-
],
|
|
69
|
-
outputs: [{ name: "amountOut", type: "uint256" }],
|
|
70
|
-
stateMutability: "payable"
|
|
71
|
-
}
|
|
72
|
-
];
|
|
73
|
-
// ==================== DEX 地址常量 ====================
|
|
74
|
-
const DEX_ADDRESSES = {
|
|
75
|
-
BSC: {
|
|
76
|
-
PANCAKE_SMART_ROUTER: '0x13f4EA83D0bd40E75C8222255bc855a974568Dd4',
|
|
77
|
-
WBNB: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c',
|
|
78
|
-
},
|
|
79
|
-
XLAYER: {
|
|
80
|
-
// xLayer 使用 PotatoSwap
|
|
81
|
-
PANCAKE_SMART_ROUTER: '0x0000000000000000000000000000000000000000', // TODO: 添加 xLayer DEX 地址
|
|
82
|
-
WBNB: '0x0000000000000000000000000000000000000000',
|
|
83
|
-
},
|
|
84
|
-
BASE: {
|
|
85
|
-
// ✅ Base 使用 Uniswap V3 SwapRouter02
|
|
86
|
-
UNISWAP_SMART_ROUTER: '0x2626664c2603336E57B271c5C0b26F421741e481',
|
|
87
|
-
WETH: '0x4200000000000000000000000000000000000006',
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
// ==================== 链常量 ====================
|
|
91
|
-
// 获取链配置
|
|
92
|
-
function getChainConfig(chain) {
|
|
93
|
-
switch (chain) {
|
|
94
|
-
case 'xlayer':
|
|
95
|
-
return { chainId: 196, nativeSymbol: 'OKB' };
|
|
96
|
-
case 'base':
|
|
97
|
-
return { chainId: 8453, nativeSymbol: 'ETH' };
|
|
98
|
-
case 'bsc':
|
|
99
|
-
default:
|
|
100
|
-
return { chainId: 56, nativeSymbol: 'BNB' };
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
// ==================== 工具函数 ====================
|
|
104
|
-
/** 构建 ERC20 approve 交易 */
|
|
105
|
-
async function buildApproveTransaction(wallet, tokenAddress, spenderAddress, amount, nonce, gasPrice, txType, chainId = 56 // ✅ 添加 chainId 参数
|
|
106
|
-
) {
|
|
107
|
-
const erc20Interface = new ethers.Interface(ERC20_ABI);
|
|
108
|
-
const data = erc20Interface.encodeFunctionData('approve', [spenderAddress, amount]);
|
|
109
|
-
const tx = {
|
|
110
|
-
to: tokenAddress,
|
|
111
|
-
data,
|
|
112
|
-
from: wallet.address,
|
|
113
|
-
nonce,
|
|
114
|
-
gasLimit: BigInt(100000), // approve 交易 gas 较低
|
|
115
|
-
chainId,
|
|
116
|
-
type: txType
|
|
117
|
-
};
|
|
118
|
-
if (txType === 2) {
|
|
119
|
-
const priorityFee = gasPrice / 10n === 0n ? 1n : gasPrice / 10n;
|
|
120
|
-
tx.maxFeePerGas = gasPrice;
|
|
121
|
-
tx.maxPriorityFeePerGas = priorityFee;
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
tx.gasPrice = gasPrice;
|
|
125
|
-
}
|
|
126
|
-
return wallet.signTransaction(tx);
|
|
127
|
-
}
|
|
128
|
-
function getGasLimit(config, defaultGas = 800000) {
|
|
129
|
-
if (config.gasLimit !== undefined) {
|
|
130
|
-
return typeof config.gasLimit === 'bigint' ? config.gasLimit : BigInt(config.gasLimit);
|
|
131
|
-
}
|
|
132
|
-
const multiplier = config.gasLimitMultiplier ?? 1.0;
|
|
133
|
-
return BigInt(Math.ceil(defaultGas * multiplier));
|
|
134
|
-
}
|
|
135
|
-
/** 将金额拆分成多份(权重随机) */
|
|
136
|
-
function splitAmount(totalAmount, count) {
|
|
137
|
-
if (count <= 0)
|
|
138
|
-
throw new Error('拆分份数必须大于 0');
|
|
139
|
-
if (count === 1)
|
|
140
|
-
return [totalAmount];
|
|
141
|
-
const weights = [];
|
|
142
|
-
for (let i = 0; i < count; i++) {
|
|
143
|
-
weights.push(0.5 + Math.random());
|
|
144
|
-
}
|
|
145
|
-
const totalWeight = weights.reduce((a, b) => a + b, 0);
|
|
146
|
-
const amounts = [];
|
|
147
|
-
let allocated = 0n;
|
|
148
|
-
for (let i = 0; i < count - 1; i++) {
|
|
149
|
-
const ratio = weights[i] / totalWeight;
|
|
150
|
-
const amount = BigInt(Math.floor(Number(totalAmount) * ratio));
|
|
151
|
-
amounts.push(amount);
|
|
152
|
-
allocated += amount;
|
|
153
|
-
}
|
|
154
|
-
amounts.push(totalAmount - allocated);
|
|
155
|
-
return amounts;
|
|
156
|
-
}
|
|
157
|
-
/** 解析输入金额字符串为 bigint(按是否原生币选择 parseEther / parseUnits) */
|
|
158
|
-
function parseQuoteAmount(amount, useNativeToken, quoteTokenDecimals) {
|
|
159
|
-
const v = String(amount ?? '').trim();
|
|
160
|
-
if (!v)
|
|
161
|
-
return 0n;
|
|
162
|
-
return useNativeToken ? ethers.parseEther(v) : ethers.parseUnits(v, quoteTokenDecimals);
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* 计算每个买家的买入金额(优先使用 params 传入的 buyAmount;否则 fallback 到按总额随机拆分)
|
|
166
|
-
* 说明:memeweb 前端已根据 average/random/custom 计算出每个钱包 buyAmount;
|
|
167
|
-
* 若 SDK 这里再次 splitAmount,会导致实际 buyAmount 与 UI/预期不一致,并引发余额不足(insufficient funds)。
|
|
168
|
-
*/
|
|
169
|
-
function resolveBuyerAmounts(buyers, totalAmountStr, useNativeToken, quoteTokenDecimals) {
|
|
170
|
-
const provided = buyers.map(b => b?.buyAmount).filter(v => v != null && String(v).trim() !== '');
|
|
171
|
-
const allProvided = provided.length === buyers.length;
|
|
172
|
-
if (allProvided) {
|
|
173
|
-
// ✅ 所有买家都显式提供了 buyAmount:按传入金额构建,但要修正“浮点/四舍五入导致总和偏差”
|
|
174
|
-
const amounts = buyers.map(b => parseQuoteAmount(String(b.buyAmount), useNativeToken, quoteTokenDecimals));
|
|
175
|
-
const totalWei = parseQuoteAmount(totalAmountStr, useNativeToken, quoteTokenDecimals);
|
|
176
|
-
const sum = amounts.reduce((a, b) => a + b, 0n);
|
|
177
|
-
const delta = totalWei - sum;
|
|
178
|
-
if (delta !== 0n && amounts.length > 0) {
|
|
179
|
-
// 优先修正最后一个金额(对应 create-to-dex 里“最后一单可能触发毕业”的假设)
|
|
180
|
-
const lastIdx = amounts.length - 1;
|
|
181
|
-
const next = amounts[lastIdx] + delta;
|
|
182
|
-
if (next <= 0n) {
|
|
183
|
-
throw new Error(`买入金额总和与总额不一致且无法修正:sum=${sum} total=${totalWei} delta=${delta}`);
|
|
184
|
-
}
|
|
185
|
-
amounts[lastIdx] = next;
|
|
186
|
-
}
|
|
187
|
-
return amounts;
|
|
188
|
-
}
|
|
189
|
-
const totalWei = parseQuoteAmount(totalAmountStr, useNativeToken, quoteTokenDecimals);
|
|
190
|
-
return splitAmount(totalWei, buyers.length);
|
|
191
|
-
}
|
|
192
|
-
// ==================== 主函数 ====================
|
|
193
|
-
/**
|
|
194
|
-
* Flap 发币 + 一键买到外盘捆绑交易
|
|
195
|
-
*
|
|
196
|
-
* 交易顺序:
|
|
197
|
-
* 1. 贿赂交易(BlockRazor)
|
|
198
|
-
* 2. 发币交易(Dev 钱包创建代币)
|
|
199
|
-
* 3. 内盘买入(多个钱包在 Bonding Curve 买入,买到毕业)
|
|
200
|
-
* 4. 外盘买入(多个钱包在 PancakeSwap 买入)
|
|
201
|
-
* 5. 利润多跳转账
|
|
202
|
-
*/
|
|
203
|
-
export async function flapBundleCreateToDex(params) {
|
|
204
|
-
const { chain, devPrivateKey, tokenInfo, tokenAddress, quoteToken, quoteTokenDecimals = 18, curveBuyers, curveTotalBuyAmount, enableDexBuy = false, dexBuyers = [], dexTotalBuyAmount, dexPoolType = 'v3', v3Fee = 2500, config } = params;
|
|
205
|
-
// 验证参数
|
|
206
|
-
if (curveBuyers.length === 0) {
|
|
207
|
-
throw new Error('至少需要一个内盘买入钱包');
|
|
208
|
-
}
|
|
209
|
-
// 判断是否使用原生代币
|
|
210
|
-
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
211
|
-
const inputToken = useNativeToken ? ZERO_ADDRESS : quoteToken;
|
|
212
|
-
// ✅ 计算实际使用的 V3 费率
|
|
213
|
-
// 优先级:lpFeeProfile > v3Fee > 默认 2500
|
|
214
|
-
// lpFeeProfile: 0=STANDARD(0.25%), 1=LOW(0.01%), 2=HIGH(1%)
|
|
215
|
-
let actualV3Fee;
|
|
216
|
-
if (params.lpFeeProfile !== undefined) {
|
|
217
|
-
switch (params.lpFeeProfile) {
|
|
218
|
-
case 1:
|
|
219
|
-
actualV3Fee = 100;
|
|
220
|
-
break; // LOW: 0.01%
|
|
221
|
-
case 2:
|
|
222
|
-
actualV3Fee = 10000;
|
|
223
|
-
break; // HIGH: 1%
|
|
224
|
-
default:
|
|
225
|
-
actualV3Fee = 2500;
|
|
226
|
-
break; // STANDARD: 0.25%
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
else {
|
|
230
|
-
actualV3Fee = v3Fee ?? 2500;
|
|
231
|
-
}
|
|
232
|
-
// ✅ 获取链配置
|
|
233
|
-
const chainConfig = getChainConfig(chain);
|
|
234
|
-
const chainId = chainConfig.chainId;
|
|
235
|
-
// 创建 Provider
|
|
236
|
-
const provider = new ethers.JsonRpcProvider(config.rpcUrl, {
|
|
237
|
-
chainId,
|
|
238
|
-
name: chain.toUpperCase()
|
|
239
|
-
});
|
|
240
|
-
const portalAddress = FLAP_PORTAL_ADDRESSES[chain.toUpperCase()];
|
|
241
|
-
const originalPortalAddress = FLAP_ORIGINAL_PORTAL_ADDRESSES[chain.toUpperCase()];
|
|
242
|
-
const nonceManager = new NonceManager(provider);
|
|
243
|
-
// 创建钱包实例
|
|
244
|
-
const devWallet = new Wallet(devPrivateKey, provider);
|
|
245
|
-
const curveWallets = curveBuyers.map(b => ({
|
|
246
|
-
wallet: new Wallet(b.privateKey, provider),
|
|
247
|
-
buyAmount: b.buyAmount
|
|
248
|
-
}));
|
|
249
|
-
const dexWallets = (enableDexBuy ? dexBuyers : []).map(b => ({
|
|
250
|
-
wallet: new Wallet(b.privateKey, provider),
|
|
251
|
-
buyAmount: b.buyAmount
|
|
252
|
-
}));
|
|
253
|
-
// ✅ 并行获取 gasPrice + 所有钱包 nonces
|
|
254
|
-
const allWallets = [
|
|
255
|
-
devWallet,
|
|
256
|
-
...curveWallets.map(c => c.wallet),
|
|
257
|
-
...dexWallets.map(d => d.wallet)
|
|
258
|
-
];
|
|
259
|
-
const uniqueWallets = allWallets.filter((w, i) => {
|
|
260
|
-
const addr = w.address.toLowerCase();
|
|
261
|
-
return allWallets.findIndex(x => x.address.toLowerCase() === addr) === i;
|
|
262
|
-
});
|
|
263
|
-
const [gasPrice, noncesArray] = await Promise.all([
|
|
264
|
-
getOptimizedGasPrice(provider, getGasPriceConfig(config)),
|
|
265
|
-
nonceManager.getNextNoncesForWallets(uniqueWallets)
|
|
266
|
-
]);
|
|
267
|
-
// 构建 nonces Map
|
|
268
|
-
const noncesMap = new Map();
|
|
269
|
-
uniqueWallets.forEach((wallet, i) => {
|
|
270
|
-
noncesMap.set(wallet.address.toLowerCase(), noncesArray[i]);
|
|
271
|
-
});
|
|
272
|
-
const finalGasLimit = getGasLimit(config);
|
|
273
|
-
const txType = getTxType(config);
|
|
274
|
-
const priorityFee = gasPrice / 10n === 0n ? 1n : gasPrice / 10n;
|
|
275
|
-
const bribeAmount = getBribeAmount(config);
|
|
276
|
-
const needBribeTx = bribeAmount > 0n;
|
|
277
|
-
// ✅ 计算内盘买入金额
|
|
278
|
-
const curveBuyAmounts = resolveBuyerAmounts(curveBuyers, curveTotalBuyAmount, useNativeToken, quoteTokenDecimals);
|
|
279
|
-
// ✅ 计算外盘买入金额
|
|
280
|
-
let dexBuyAmounts = [];
|
|
281
|
-
if (enableDexBuy && dexBuyers.length > 0 && dexTotalBuyAmount) {
|
|
282
|
-
dexBuyAmounts = resolveBuyerAmounts(dexBuyers, dexTotalBuyAmount, useNativeToken, quoteTokenDecimals);
|
|
283
|
-
}
|
|
284
|
-
// ✅ 计算利润
|
|
285
|
-
const totalBuyAmount = curveBuyAmounts.reduce((a, b) => a + b, 0n)
|
|
286
|
-
+ dexBuyAmounts.reduce((a, b) => a + b, 0n);
|
|
287
|
-
const profitAmount = (totalBuyAmount * BigInt(PROFIT_CONFIG.RATE_BPS)) / 10000n;
|
|
288
|
-
// ==================== 构建交易 ====================
|
|
289
|
-
const allTransactions = [];
|
|
290
|
-
// 使用第一个内盘买家作为贿赂支付者(提高 bundle 打包优先级)
|
|
291
|
-
const mainBuyerWallet = curveWallets[0].wallet;
|
|
292
|
-
// 1. 贿赂交易
|
|
293
|
-
if (needBribeTx) {
|
|
294
|
-
const mainAddr = mainBuyerWallet.address.toLowerCase();
|
|
295
|
-
const bribeNonce = noncesMap.get(mainAddr);
|
|
296
|
-
noncesMap.set(mainAddr, bribeNonce + 1);
|
|
297
|
-
const bribeTx = await mainBuyerWallet.signTransaction({
|
|
298
|
-
to: BLOCKRAZOR_BUILDER_EOA,
|
|
299
|
-
value: bribeAmount,
|
|
300
|
-
nonce: bribeNonce,
|
|
301
|
-
gasPrice,
|
|
302
|
-
gasLimit: GAS_LIMITS.BRIBE,
|
|
303
|
-
chainId,
|
|
304
|
-
type: txType
|
|
305
|
-
});
|
|
306
|
-
allTransactions.push(bribeTx);
|
|
307
|
-
}
|
|
308
|
-
// 2. 发币交易
|
|
309
|
-
{
|
|
310
|
-
const devAddr = devWallet.address.toLowerCase();
|
|
311
|
-
const devNonce = noncesMap.get(devAddr);
|
|
312
|
-
noncesMap.set(devAddr, devNonce + 1);
|
|
313
|
-
const originalPortal = new Contract(originalPortalAddress, PORTAL_ABI, devWallet);
|
|
314
|
-
// ✅ 判断使用哪个版本的 newToken
|
|
315
|
-
// V5: 提供了 taxV2Config(Tax Token V2 高级税收分配)
|
|
316
|
-
// V4: 提供了 dexId 或 lpFeeProfile(支持 DEX 选择和 LP 费率配置)
|
|
317
|
-
// V3: 提供了 extensionID(支持扩展数据)
|
|
318
|
-
// V2: 默认
|
|
319
|
-
const useV5 = params.taxV2Config !== undefined && (params.taxRate ?? 0) > 0;
|
|
320
|
-
const useV4 = !useV5 && (params.dexId !== undefined || params.lpFeeProfile !== undefined);
|
|
321
|
-
const useV3 = !useV5 && !useV4 && !!params.extensionID;
|
|
322
|
-
let createUnsigned;
|
|
323
|
-
if (useV5) {
|
|
324
|
-
// V5: Tax Token V2 高级税收分配
|
|
325
|
-
const tv2 = params.taxV2Config;
|
|
326
|
-
// 验证税收分配总计必须为 10000
|
|
327
|
-
const total = tv2.mktBps + tv2.deflationBps + tv2.dividendBps + tv2.lpBps;
|
|
328
|
-
if (total !== 10000) {
|
|
329
|
-
throw new Error(`Tax distribution must sum to 10000 (100%), got ${total}`);
|
|
330
|
-
}
|
|
331
|
-
// ✅ 判断是否使用金库模式(VaultPortal.newTaxTokenWithVault)
|
|
332
|
-
const hasVault = tv2.vaultConfig && tv2.vaultConfig.vaultType !== 'none';
|
|
333
|
-
if (hasVault) {
|
|
334
|
-
// ✅ 金库模式:通过 VaultPortal.newTaxTokenWithVault 创建代币
|
|
335
|
-
const chainKey = chain.toUpperCase();
|
|
336
|
-
const vaultPortalAddr = VAULT_PORTAL_ADDRESSES[chainKey];
|
|
337
|
-
if (!vaultPortalAddr) {
|
|
338
|
-
throw new Error(`链 "${chain}" 不支持 Tax Vault 金库功能`);
|
|
339
|
-
}
|
|
340
|
-
const vaultPortal = new Contract(vaultPortalAddr, VAULT_PORTAL_ABI, devWallet);
|
|
341
|
-
const { vaultFactory, vaultData } = buildVaultParams(tv2.vaultConfig, chainKey);
|
|
342
|
-
createUnsigned = await vaultPortal.newTaxTokenWithVault.populateTransaction({
|
|
343
|
-
name: tokenInfo.name,
|
|
344
|
-
symbol: tokenInfo.symbol,
|
|
345
|
-
meta: tokenInfo.meta,
|
|
346
|
-
dexThresh: (params.dexThresh ?? 1) & 0xff,
|
|
347
|
-
salt: params.salt ?? '0x' + '00'.repeat(32),
|
|
348
|
-
taxRate: (params.taxRate ?? 0) & 0xffff,
|
|
349
|
-
migratorType: (params.migratorType ?? 0) & 0xff,
|
|
350
|
-
quoteToken: inputToken,
|
|
351
|
-
quoteAmt: 0n,
|
|
352
|
-
permitData: '0x',
|
|
353
|
-
extensionID: '0x' + '00'.repeat(32),
|
|
354
|
-
extensionData: '0x',
|
|
355
|
-
dexId: (params.dexId ?? 0) & 0xff,
|
|
356
|
-
lpFeeProfile: (params.lpFeeProfile ?? 0) & 0xff,
|
|
357
|
-
taxDuration: BigInt(tv2.taxDuration),
|
|
358
|
-
antiFarmerDuration: BigInt(tv2.antiFarmerDuration),
|
|
359
|
-
mktBps: tv2.mktBps & 0xffff,
|
|
360
|
-
deflationBps: tv2.deflationBps & 0xffff,
|
|
361
|
-
dividendBps: tv2.dividendBps & 0xffff,
|
|
362
|
-
lpBps: tv2.lpBps & 0xffff,
|
|
363
|
-
minimumShareBalance: BigInt(tv2.minimumShareBalance ?? 10000) * (10n ** 18n),
|
|
364
|
-
vaultFactory,
|
|
365
|
-
vaultData,
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
else {
|
|
369
|
-
// 标准 V5: Portal.newTokenV5(无金库)
|
|
370
|
-
createUnsigned = await originalPortal.newTokenV5.populateTransaction({
|
|
371
|
-
name: tokenInfo.name,
|
|
372
|
-
symbol: tokenInfo.symbol,
|
|
373
|
-
meta: tokenInfo.meta,
|
|
374
|
-
dexThresh: (params.dexThresh ?? 1) & 0xff,
|
|
375
|
-
salt: params.salt ?? '0x' + '00'.repeat(32),
|
|
376
|
-
taxRate: (params.taxRate ?? 0) & 0xffff,
|
|
377
|
-
migratorType: (params.migratorType ?? 0) & 0xff,
|
|
378
|
-
quoteToken: inputToken,
|
|
379
|
-
quoteAmt: 0n,
|
|
380
|
-
beneficiary: devWallet.address,
|
|
381
|
-
permitData: '0x',
|
|
382
|
-
extensionID: params.extensionID ?? '0x' + '00'.repeat(32),
|
|
383
|
-
extensionData: params.extensionData ?? '0x',
|
|
384
|
-
dexId: (params.dexId ?? 0) & 0xff,
|
|
385
|
-
lpFeeProfile: (params.lpFeeProfile ?? 0) & 0xff,
|
|
386
|
-
taxDuration: BigInt(tv2.taxDuration),
|
|
387
|
-
antiFarmerDuration: BigInt(tv2.antiFarmerDuration),
|
|
388
|
-
mktBps: tv2.mktBps & 0xffff,
|
|
389
|
-
deflationBps: tv2.deflationBps & 0xffff,
|
|
390
|
-
dividendBps: tv2.dividendBps & 0xffff,
|
|
391
|
-
lpBps: tv2.lpBps & 0xffff,
|
|
392
|
-
minimumShareBalance: BigInt(tv2.minimumShareBalance ?? 10000) * (10n ** 18n),
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
else if (useV4) {
|
|
397
|
-
// V4: 支持 DEX ID 和 LP 费率配置(用于选择池子费率)
|
|
398
|
-
createUnsigned = await originalPortal.newTokenV4.populateTransaction({
|
|
399
|
-
name: tokenInfo.name,
|
|
400
|
-
symbol: tokenInfo.symbol,
|
|
401
|
-
meta: tokenInfo.meta,
|
|
402
|
-
dexThresh: (params.dexThresh ?? 1) & 0xff,
|
|
403
|
-
salt: params.salt ?? '0x' + '00'.repeat(32),
|
|
404
|
-
taxRate: (params.taxRate ?? 0) & 0xffff,
|
|
405
|
-
migratorType: (params.migratorType ?? 0) & 0xff,
|
|
406
|
-
quoteToken: inputToken,
|
|
407
|
-
quoteAmt: 0n,
|
|
408
|
-
beneficiary: devWallet.address,
|
|
409
|
-
permitData: '0x',
|
|
410
|
-
extensionID: params.extensionID ?? '0x' + '00'.repeat(32),
|
|
411
|
-
extensionData: params.extensionData ?? '0x',
|
|
412
|
-
dexId: (params.dexId ?? 0) & 0xff,
|
|
413
|
-
lpFeeProfile: (params.lpFeeProfile ?? 0) & 0xff
|
|
414
|
-
});
|
|
415
|
-
}
|
|
416
|
-
else if (useV3) {
|
|
417
|
-
// V3: 支持扩展数据
|
|
418
|
-
createUnsigned = await originalPortal.newTokenV3.populateTransaction({
|
|
419
|
-
name: tokenInfo.name,
|
|
420
|
-
symbol: tokenInfo.symbol,
|
|
421
|
-
meta: tokenInfo.meta,
|
|
422
|
-
dexThresh: (params.dexThresh ?? 1) & 0xff,
|
|
423
|
-
salt: params.salt ?? '0x' + '00'.repeat(32),
|
|
424
|
-
taxRate: (params.taxRate ?? 0) & 0xffff,
|
|
425
|
-
migratorType: (params.migratorType ?? 0) & 0xff,
|
|
426
|
-
quoteToken: inputToken,
|
|
427
|
-
quoteAmt: 0n,
|
|
428
|
-
beneficiary: devWallet.address,
|
|
429
|
-
permitData: '0x',
|
|
430
|
-
extensionID: params.extensionID,
|
|
431
|
-
extensionData: params.extensionData ?? '0x'
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
else {
|
|
435
|
-
// V2: 基础版本
|
|
436
|
-
createUnsigned = await originalPortal.newTokenV2.populateTransaction({
|
|
437
|
-
name: tokenInfo.name,
|
|
438
|
-
symbol: tokenInfo.symbol,
|
|
439
|
-
meta: tokenInfo.meta,
|
|
440
|
-
dexThresh: (params.dexThresh ?? 1) & 0xff,
|
|
441
|
-
salt: params.salt ?? '0x' + '00'.repeat(32),
|
|
442
|
-
taxRate: (params.taxRate ?? 0) & 0xffff,
|
|
443
|
-
migratorType: (params.migratorType ?? 0) & 0xff,
|
|
444
|
-
quoteToken: inputToken,
|
|
445
|
-
quoteAmt: 0n,
|
|
446
|
-
beneficiary: devWallet.address,
|
|
447
|
-
permitData: '0x'
|
|
448
|
-
});
|
|
449
|
-
}
|
|
450
|
-
const createTx = {
|
|
451
|
-
...createUnsigned,
|
|
452
|
-
from: devWallet.address,
|
|
453
|
-
nonce: devNonce,
|
|
454
|
-
gasLimit: finalGasLimit,
|
|
455
|
-
chainId,
|
|
456
|
-
type: txType
|
|
457
|
-
};
|
|
458
|
-
if (txType === 2) {
|
|
459
|
-
createTx.maxFeePerGas = gasPrice;
|
|
460
|
-
createTx.maxPriorityFeePerGas = priorityFee;
|
|
461
|
-
}
|
|
462
|
-
else {
|
|
463
|
-
createTx.gasPrice = gasPrice;
|
|
464
|
-
}
|
|
465
|
-
const signedCreate = await devWallet.signTransaction(createTx);
|
|
466
|
-
allTransactions.push(signedCreate);
|
|
467
|
-
}
|
|
468
|
-
// 3. 内盘买入交易(包含毕业触发)
|
|
469
|
-
// 最后一个买入交易会触发毕业,需要更多 gas
|
|
470
|
-
// ✅ ERC20 需要先 approve,再买入
|
|
471
|
-
const curveBuyTxPromises = curveWallets.map(async ({ wallet }, i) => {
|
|
472
|
-
const addr = wallet.address.toLowerCase();
|
|
473
|
-
const signedTxs = [];
|
|
474
|
-
// ✅ ERC20: 先构建 approve 交易
|
|
475
|
-
if (!useNativeToken) {
|
|
476
|
-
const approveNonce = noncesMap.get(addr);
|
|
477
|
-
noncesMap.set(addr, approveNonce + 1);
|
|
478
|
-
const approveTx = await buildApproveTransaction(wallet, quoteToken, portalAddress, curveBuyAmounts[i], approveNonce, gasPrice, txType, chainId // ✅ 传递 chainId
|
|
479
|
-
);
|
|
480
|
-
signedTxs.push(approveTx);
|
|
481
|
-
}
|
|
482
|
-
// 构建买入交易
|
|
483
|
-
const buyNonce = noncesMap.get(addr);
|
|
484
|
-
noncesMap.set(addr, buyNonce + 1);
|
|
485
|
-
const portal = new Contract(portalAddress, PORTAL_ABI, wallet);
|
|
486
|
-
// ✅ 交易阶段统一优先使用 swapExactInputV3(支持 extensionData;无扩展时传 0x 即可)
|
|
487
|
-
// 说明:这里的 V3 指“交易接口版本(扩展支持)”,不是“外盘 V3 池子”
|
|
488
|
-
const extensionData = params.extensionData ?? '0x';
|
|
489
|
-
const unsigned = await portal.swapExactInputV3.populateTransaction({
|
|
490
|
-
inputToken,
|
|
491
|
-
outputToken: tokenAddress,
|
|
492
|
-
inputAmount: curveBuyAmounts[i],
|
|
493
|
-
minOutputAmount: 0n,
|
|
494
|
-
permitData: '0x', // 不使用 permit,使用链上 approve
|
|
495
|
-
extensionData
|
|
496
|
-
}, useNativeToken ? { value: curveBuyAmounts[i] } : {});
|
|
497
|
-
// 最后一个买家触发毕业,需要更多 gas
|
|
498
|
-
const isLastBuyer = i === curveWallets.length - 1;
|
|
499
|
-
// ⚠️ 触发毕业(创建 Pancake V3 池 + Mint 初始流动性)会非常耗 gas。
|
|
500
|
-
// 链上成功样例 0x29b173... 的 gasLimit=8,000,000,gasUsed≈6,647,149。
|
|
501
|
-
// Flap 官方示例脚本也使用 last_buy=8_000_000。
|
|
502
|
-
const LAST_BUY_GAS_LIMIT = BigInt(8000000);
|
|
503
|
-
const buyGasLimit = isLastBuyer
|
|
504
|
-
? (finalGasLimit > LAST_BUY_GAS_LIMIT ? finalGasLimit : LAST_BUY_GAS_LIMIT)
|
|
505
|
-
: finalGasLimit;
|
|
506
|
-
const tx = {
|
|
507
|
-
...unsigned,
|
|
508
|
-
from: wallet.address,
|
|
509
|
-
nonce: buyNonce,
|
|
510
|
-
gasLimit: buyGasLimit,
|
|
511
|
-
chainId,
|
|
512
|
-
type: txType,
|
|
513
|
-
value: useNativeToken ? curveBuyAmounts[i] : 0n
|
|
514
|
-
};
|
|
515
|
-
if (txType === 2) {
|
|
516
|
-
tx.maxFeePerGas = gasPrice;
|
|
517
|
-
tx.maxPriorityFeePerGas = priorityFee;
|
|
518
|
-
}
|
|
519
|
-
else {
|
|
520
|
-
tx.gasPrice = gasPrice;
|
|
521
|
-
}
|
|
522
|
-
const signedBuy = await wallet.signTransaction(tx);
|
|
523
|
-
signedTxs.push(signedBuy);
|
|
524
|
-
return signedTxs;
|
|
525
|
-
});
|
|
526
|
-
const curveBuyResults = await Promise.all(curveBuyTxPromises);
|
|
527
|
-
// 展平数组:每个钱包可能有 [approve, buy] 或只有 [buy]
|
|
528
|
-
curveBuyResults.forEach(txs => allTransactions.push(...txs));
|
|
529
|
-
// 4. 外盘买入交易(直接调用 PancakeSwap Router)
|
|
530
|
-
// ✅ 重要修改:直接调用 PancakeSwap SmartRouter
|
|
531
|
-
// 参考官方代码: flap-bundle-release/app/v2/pvp/001_launch_buy.ts
|
|
532
|
-
//
|
|
533
|
-
// ⚠️ 关键:税代币(migratorType=1)毕业到 V2 池,必须用 swapExactTokensForTokens
|
|
534
|
-
// 普通代币(migratorType=0)毕业到 V3 池,使用 exactInputSingle
|
|
535
|
-
if (enableDexBuy && dexWallets.length > 0) {
|
|
536
|
-
// 获取 DEX 地址(Base 使用 Uniswap V3 SwapRouter02,BSC 使用 PancakeSwap SmartRouter)
|
|
537
|
-
let smartRouterAddress;
|
|
538
|
-
let wrappedNativeAddress;
|
|
539
|
-
if (chain === 'base') {
|
|
540
|
-
smartRouterAddress = DEX_ADDRESSES.BASE.UNISWAP_SMART_ROUTER;
|
|
541
|
-
wrappedNativeAddress = DEX_ADDRESSES.BASE.WETH;
|
|
542
|
-
}
|
|
543
|
-
else if (chain === 'xlayer') {
|
|
544
|
-
smartRouterAddress = DEX_ADDRESSES.XLAYER.PANCAKE_SMART_ROUTER;
|
|
545
|
-
wrappedNativeAddress = DEX_ADDRESSES.XLAYER.WBNB;
|
|
546
|
-
}
|
|
547
|
-
else {
|
|
548
|
-
smartRouterAddress = DEX_ADDRESSES.BSC.PANCAKE_SMART_ROUTER;
|
|
549
|
-
wrappedNativeAddress = DEX_ADDRESSES.BSC.WBNB;
|
|
550
|
-
}
|
|
551
|
-
// ✅ 根据 migratorType 确定实际的 DEX 池类型
|
|
552
|
-
// migratorType === 1 (V2_MIGRATOR) → 毕业到 V2 池 → 必须使用 V2 买入
|
|
553
|
-
// migratorType === 0 (V3_MIGRATOR) → 毕业到 V3 池 → 使用 V3 买入
|
|
554
|
-
// 参考: flap-bundle-release 中税代币使用 PoolType.V2,普通代币使用 PoolType.V3
|
|
555
|
-
const effectiveDexPoolType = (params.migratorType === 1) ? 'v2' : (dexPoolType || 'v3');
|
|
556
|
-
// 获取 V3 费率(仅 V3 池使用)
|
|
557
|
-
const dexV3Fee = actualV3Fee;
|
|
558
|
-
const dexBuyTxPromises = dexWallets.map(async ({ wallet }, i) => {
|
|
559
|
-
const addr = wallet.address.toLowerCase();
|
|
560
|
-
const signedTxs = [];
|
|
561
|
-
const buyAmount = dexBuyAmounts[i];
|
|
562
|
-
// ✅ ERC20: 先构建 approve 交易(approve 给 PancakeSwap Router)
|
|
563
|
-
if (!useNativeToken) {
|
|
564
|
-
const approveNonce = noncesMap.get(addr);
|
|
565
|
-
noncesMap.set(addr, approveNonce + 1);
|
|
566
|
-
const approveTx = await buildApproveTransaction(wallet, quoteToken, smartRouterAddress, // ✅ approve 给 PancakeSwap SmartRouter
|
|
567
|
-
buyAmount, approveNonce, gasPrice, txType, chainId);
|
|
568
|
-
signedTxs.push(approveTx);
|
|
569
|
-
}
|
|
570
|
-
// 构建 PancakeSwap 买入交易(使用 multicall 方式,与官方一致)
|
|
571
|
-
const buyNonce = noncesMap.get(addr);
|
|
572
|
-
noncesMap.set(addr, buyNonce + 1);
|
|
573
|
-
// 对于原生代币(BNB/ETH),使用 Wrapped 版本作为 tokenIn(Router 会自动 wrap)
|
|
574
|
-
const tokenIn = useNativeToken ? wrappedNativeAddress : quoteToken;
|
|
575
|
-
const routerInterface = new ethers.Interface(PANCAKE_ROUTER_ABI);
|
|
576
|
-
let multicallData;
|
|
577
|
-
if (effectiveDexPoolType === 'v2') {
|
|
578
|
-
// ✅ V2 池:使用 swapExactTokensForTokens
|
|
579
|
-
// 参考: flap-bundle-release/app/v2/pvp/001_launch_buy.ts (PoolType.V2 分支)
|
|
580
|
-
const swapData = routerInterface.encodeFunctionData('swapExactTokensForTokens', [
|
|
581
|
-
buyAmount,
|
|
582
|
-
0n, // amountOutMin: 不设置滑点保护(Bundle 中可以接受)
|
|
583
|
-
[tokenIn, tokenAddress], // path: WBNB/QuoteToken → Token
|
|
584
|
-
wallet.address // recipient
|
|
585
|
-
]);
|
|
586
|
-
multicallData = routerInterface.encodeFunctionData('multicall', [[swapData]]);
|
|
587
|
-
}
|
|
588
|
-
else {
|
|
589
|
-
// ✅ V3 池:使用 exactInputSingle
|
|
590
|
-
const exactInputSingleParams = {
|
|
591
|
-
tokenIn,
|
|
592
|
-
tokenOut: tokenAddress,
|
|
593
|
-
fee: dexV3Fee, // ✅ 使用与 newTokenV4 创建池子匹配的费率
|
|
594
|
-
recipient: wallet.address,
|
|
595
|
-
amountIn: buyAmount,
|
|
596
|
-
amountOutMinimum: 0n, // 不设置滑点保护(Bundle 中可以接受)
|
|
597
|
-
sqrtPriceLimitX96: 0n
|
|
598
|
-
};
|
|
599
|
-
const exactInputSingleData = routerInterface.encodeFunctionData('exactInputSingle', [exactInputSingleParams]);
|
|
600
|
-
multicallData = routerInterface.encodeFunctionData('multicall', [[exactInputSingleData]]);
|
|
601
|
-
}
|
|
602
|
-
// ⚠️ Pancake SmartRouter 外盘买的 gas 不能太小,否则 bundle 预执行会直接失败。
|
|
603
|
-
// Flap 官方示例配置 dex_buy=5_000_000(flap-bundle-release/app/configs/global.ts)。
|
|
604
|
-
const DEX_BUY_GAS_LIMIT = BigInt(8000000);
|
|
605
|
-
const tx = {
|
|
606
|
-
to: smartRouterAddress,
|
|
607
|
-
data: multicallData,
|
|
608
|
-
nonce: buyNonce,
|
|
609
|
-
gasLimit: DEX_BUY_GAS_LIMIT,
|
|
610
|
-
chainId,
|
|
611
|
-
type: txType,
|
|
612
|
-
value: useNativeToken ? buyAmount : 0n
|
|
613
|
-
};
|
|
614
|
-
if (txType === 2) {
|
|
615
|
-
tx.maxFeePerGas = gasPrice;
|
|
616
|
-
tx.maxPriorityFeePerGas = priorityFee;
|
|
617
|
-
}
|
|
618
|
-
else {
|
|
619
|
-
tx.gasPrice = gasPrice;
|
|
620
|
-
}
|
|
621
|
-
const signedBuy = await wallet.signTransaction(tx);
|
|
622
|
-
signedTxs.push(signedBuy);
|
|
623
|
-
return signedTxs;
|
|
624
|
-
});
|
|
625
|
-
const dexBuyResults = await Promise.all(dexBuyTxPromises);
|
|
626
|
-
// 展平数组:每个钱包可能有 [approve, buy] 或只有 [buy]
|
|
627
|
-
dexBuyResults.forEach(txs => allTransactions.push(...txs));
|
|
628
|
-
}
|
|
629
|
-
// 5. 利润多跳转账
|
|
630
|
-
let profitHopWallets;
|
|
631
|
-
if (profitAmount > 0n) {
|
|
632
|
-
// ✅ 调整:利润从 Dev 钱包(devPrivateKey)支付,避免第一个内盘买家余额不足导致 want 过高
|
|
633
|
-
// 注意:利润交易会额外消耗原生币(profitAmount + gas),请确保 Dev 钱包留足余额
|
|
634
|
-
const devAddr = devWallet.address.toLowerCase();
|
|
635
|
-
const profitNonce = noncesMap.get(devAddr);
|
|
636
|
-
const profitResult = await buildProfitHopTransactions({
|
|
637
|
-
provider,
|
|
638
|
-
payerWallet: devWallet,
|
|
639
|
-
profitAmount,
|
|
640
|
-
profitRecipient: getProfitRecipient(),
|
|
641
|
-
hopCount: PROFIT_HOP_COUNT,
|
|
642
|
-
gasPrice,
|
|
643
|
-
chainId,
|
|
644
|
-
txType,
|
|
645
|
-
startNonce: profitNonce
|
|
646
|
-
});
|
|
647
|
-
allTransactions.push(...profitResult.signedTransactions);
|
|
648
|
-
profitHopWallets = profitResult.hopWallets;
|
|
649
|
-
}
|
|
650
|
-
nonceManager.clearTemp();
|
|
651
|
-
// 返回结果
|
|
652
|
-
return {
|
|
653
|
-
signedTransactions: allTransactions,
|
|
654
|
-
tokenAddress,
|
|
655
|
-
profitHopWallets,
|
|
656
|
-
metadata: {
|
|
657
|
-
curveBuyerCount: curveBuyers.length,
|
|
658
|
-
curveTotalBuy: ethers.formatEther(curveBuyAmounts.reduce((a, b) => a + b, 0n)),
|
|
659
|
-
enableDexBuy,
|
|
660
|
-
dexBuyerCount: dexBuyers.length,
|
|
661
|
-
dexTotalBuy: dexBuyAmounts.length > 0 ? ethers.formatEther(dexBuyAmounts.reduce((a, b) => a + b, 0n)) : '0',
|
|
662
|
-
profitAmount: profitAmount > 0n ? ethers.formatEther(profitAmount) : undefined
|
|
663
|
-
}
|
|
664
|
-
};
|
|
665
|
-
}
|