four-flap-meme-sdk 1.4.42 → 1.4.44
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/utils/holders-maker.d.ts +13 -25
- package/dist/utils/holders-maker.js +158 -322
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* ============================================================================
|
|
3
3
|
* 刷持有人(Holders Maker)
|
|
4
4
|
* ============================================================================
|
|
5
|
-
* 一键完成:生成钱包 → 分发资金 →
|
|
5
|
+
* 一键完成:生成钱包 → 分发资金 → 批量买入(同一个 bundle 完成)
|
|
6
6
|
* 支持原生代币(BNB/MON)和 ERC20 代币(USDT/USDC)作为购买资金
|
|
7
7
|
*/
|
|
8
8
|
import { type GeneratedWallet } from './wallet.js';
|
|
@@ -34,8 +34,6 @@ export type HoldersMakerConfig = {
|
|
|
34
34
|
maxWalletsPerBatch?: number;
|
|
35
35
|
/** Four API URL */
|
|
36
36
|
fourApiUrl?: string;
|
|
37
|
-
/** ✅ 多跳数量(0=不多跳,1-10=经过N个中转钱包) */
|
|
38
|
-
hopCount?: number;
|
|
39
37
|
/** V2 路由路径(如 [WBNB, TOKEN]) */
|
|
40
38
|
v2Path?: string[];
|
|
41
39
|
/** V3 单跳输入代币 */
|
|
@@ -70,7 +68,6 @@ export type HoldersMakerParams = {
|
|
|
70
68
|
export type BatchResult = {
|
|
71
69
|
batchIndex: number;
|
|
72
70
|
success: boolean;
|
|
73
|
-
phase: 'disperse_native' | 'disperse_erc20' | 'approve' | 'buy';
|
|
74
71
|
signedTransactions?: string[];
|
|
75
72
|
txHashes?: string[];
|
|
76
73
|
error?: string;
|
|
@@ -82,15 +79,8 @@ export type HoldersMakerResult = {
|
|
|
82
79
|
success: boolean;
|
|
83
80
|
/** 生成的新钱包 */
|
|
84
81
|
newWallets: GeneratedWallet[];
|
|
85
|
-
/**
|
|
86
|
-
|
|
87
|
-
disperseNative: string[][];
|
|
88
|
-
disperseErc20?: string[][];
|
|
89
|
-
approve?: string[][];
|
|
90
|
-
buy: string[][];
|
|
91
|
-
};
|
|
92
|
-
/** ✅ 多跳钱包私钥(按批次分组,每批次按接收者分组) */
|
|
93
|
-
hopWallets: string[][][];
|
|
82
|
+
/** 签名交易(每批一个数组) */
|
|
83
|
+
signedTransactions: string[][];
|
|
94
84
|
/** 批次结果 */
|
|
95
85
|
batchResults: BatchResult[];
|
|
96
86
|
/** 成功的批次数 */
|
|
@@ -101,16 +91,18 @@ export type HoldersMakerResult = {
|
|
|
101
91
|
error?: string;
|
|
102
92
|
};
|
|
103
93
|
/**
|
|
104
|
-
*
|
|
94
|
+
* 刷持有人(一个 bundle 完成分发+买入)
|
|
105
95
|
*
|
|
106
|
-
*
|
|
107
|
-
* 1.
|
|
108
|
-
* 2. 分发原生代币(
|
|
109
|
-
* 3.
|
|
110
|
-
* 4.
|
|
111
|
-
* 5. 批量买入目标代币
|
|
96
|
+
* 流程(同一个 bundle):
|
|
97
|
+
* 1. 贿赂交易
|
|
98
|
+
* 2. 分发原生代币(dev → 新钱包1, dev → 新钱包2, ...)
|
|
99
|
+
* 3. 买入交易(新钱包1 买入, 新钱包2 买入, ...)
|
|
100
|
+
* 4. 利润多跳
|
|
112
101
|
*
|
|
113
|
-
*
|
|
102
|
+
* ⚠️ 仅支持原生代币(BNB/MON)模式
|
|
103
|
+
* ERC20 模式需要授权,无法在同一个 bundle 中完成
|
|
104
|
+
*
|
|
105
|
+
* @returns 包含所有签名交易的结果,由前端提交
|
|
114
106
|
*/
|
|
115
107
|
export declare function holdersMaker(params: HoldersMakerParams): Promise<HoldersMakerResult>;
|
|
116
108
|
/**
|
|
@@ -121,12 +113,8 @@ export declare function estimateHoldersMakerCost(params: {
|
|
|
121
113
|
buyAmountPerHolder: string;
|
|
122
114
|
gasLimit?: number;
|
|
123
115
|
gasPriceGwei?: number;
|
|
124
|
-
baseToken?: BaseTokenType;
|
|
125
|
-
hopCount?: number;
|
|
126
116
|
}): {
|
|
127
117
|
totalNativeCost: string;
|
|
128
|
-
totalBaseTokenCost: string;
|
|
129
118
|
gasEstimate: string;
|
|
130
119
|
batchCount: number;
|
|
131
|
-
hopWalletCount: number;
|
|
132
120
|
};
|
|
@@ -2,23 +2,29 @@
|
|
|
2
2
|
* ============================================================================
|
|
3
3
|
* 刷持有人(Holders Maker)
|
|
4
4
|
* ============================================================================
|
|
5
|
-
* 一键完成:生成钱包 → 分发资金 →
|
|
5
|
+
* 一键完成:生成钱包 → 分发资金 → 批量买入(同一个 bundle 完成)
|
|
6
6
|
* 支持原生代币(BNB/MON)和 ERC20 代币(USDT/USDC)作为购买资金
|
|
7
7
|
*/
|
|
8
|
-
import { JsonRpcProvider } from 'ethers';
|
|
8
|
+
import { ethers, JsonRpcProvider, Wallet } from 'ethers';
|
|
9
9
|
import { generateWallets } from './wallet.js';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { pancakeProxyBatchBuyMerkle } from '../flap/portal-bundle-merkle/pancake-proxy.js';
|
|
14
|
-
import { approveTokenBatch } from './erc20.js';
|
|
10
|
+
import { NonceManager, getOptimizedGasPrice, buildProfitHopTransactions, PROFIT_HOP_COUNT } from './bundle-helpers.js';
|
|
11
|
+
import { BLOCKRAZOR_BUILDER_EOA, PROFIT_CONFIG } from './constants.js';
|
|
12
|
+
import { FLAP_PORTAL_ADDRESSES } from '../flap/constants.js';
|
|
15
13
|
// ============================================================================
|
|
16
14
|
// 常量
|
|
17
15
|
// ============================================================================
|
|
18
16
|
const DEFAULT_GAS_LIMIT = 800000;
|
|
19
17
|
const DEFAULT_GAS_PRICE_GWEI = 3;
|
|
20
|
-
const DEFAULT_MAX_WALLETS_PER_BATCH = 46; // 50 - 1(bribe) - 3(profit hops)
|
|
21
18
|
const GAS_BUFFER_MULTIPLIER = 2; // 2倍 Gas 费安全系数
|
|
19
|
+
// Bundle 限制计算:
|
|
20
|
+
// - 贿赂 1 笔
|
|
21
|
+
// - 利润多跳 PROFIT_HOP_COUNT + 1 笔(payer → hop1 → hop2 → recipient)
|
|
22
|
+
// - 分发 N 笔
|
|
23
|
+
// - 买入 N 笔
|
|
24
|
+
// 1 + (PROFIT_HOP_COUNT + 1) + 2N ≤ 50
|
|
25
|
+
// 2N ≤ 50 - 2 - PROFIT_HOP_COUNT
|
|
26
|
+
// N ≤ (48 - PROFIT_HOP_COUNT) / 2
|
|
27
|
+
const DEFAULT_MAX_WALLETS_PER_BATCH = Math.floor((48 - PROFIT_HOP_COUNT) / 2);
|
|
22
28
|
// ============================================================================
|
|
23
29
|
// 辅助函数
|
|
24
30
|
// ============================================================================
|
|
@@ -32,325 +38,173 @@ function chunkArray(arr, size) {
|
|
|
32
38
|
}
|
|
33
39
|
return chunks;
|
|
34
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* 构建原生代币转账交易
|
|
43
|
+
*/
|
|
44
|
+
async function buildNativeTransferTx(wallet, to, amount, nonce, gasPrice, chainId, txType) {
|
|
45
|
+
const tx = {
|
|
46
|
+
to,
|
|
47
|
+
value: amount,
|
|
48
|
+
nonce,
|
|
49
|
+
gasLimit: 21000n,
|
|
50
|
+
chainId
|
|
51
|
+
};
|
|
52
|
+
if (txType === 2) {
|
|
53
|
+
tx.maxFeePerGas = gasPrice;
|
|
54
|
+
tx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
|
|
55
|
+
tx.type = 2;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
tx.gasPrice = gasPrice;
|
|
59
|
+
tx.type = 0;
|
|
60
|
+
}
|
|
61
|
+
return await wallet.signTransaction(tx);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 构建 Flap 买入交易
|
|
65
|
+
*/
|
|
66
|
+
async function buildFlapBuyTx(wallet, tokenAddress, buyAmount, nonce, gasPrice, gasLimit, chainId, txType, chain) {
|
|
67
|
+
const portalAddress = FLAP_PORTAL_ADDRESSES[chain];
|
|
68
|
+
const iface = new ethers.Interface([
|
|
69
|
+
'function buyToken(bytes32 origin, address token, address to, uint256 funds, uint256 minAmount) payable'
|
|
70
|
+
]);
|
|
71
|
+
const data = iface.encodeFunctionData('buyToken', [
|
|
72
|
+
ethers.ZeroHash, // origin
|
|
73
|
+
tokenAddress,
|
|
74
|
+
wallet.address, // to
|
|
75
|
+
buyAmount,
|
|
76
|
+
0n // minAmount (no slippage protection)
|
|
77
|
+
]);
|
|
78
|
+
const tx = {
|
|
79
|
+
to: portalAddress,
|
|
80
|
+
data,
|
|
81
|
+
value: buyAmount,
|
|
82
|
+
nonce,
|
|
83
|
+
gasLimit: BigInt(gasLimit),
|
|
84
|
+
chainId
|
|
85
|
+
};
|
|
86
|
+
if (txType === 2) {
|
|
87
|
+
tx.maxFeePerGas = gasPrice;
|
|
88
|
+
tx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
|
|
89
|
+
tx.type = 2;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
tx.gasPrice = gasPrice;
|
|
93
|
+
tx.type = 0;
|
|
94
|
+
}
|
|
95
|
+
return await wallet.signTransaction(tx);
|
|
96
|
+
}
|
|
35
97
|
// ============================================================================
|
|
36
98
|
// 主方法
|
|
37
99
|
// ============================================================================
|
|
38
100
|
/**
|
|
39
|
-
*
|
|
101
|
+
* 刷持有人(一个 bundle 完成分发+买入)
|
|
40
102
|
*
|
|
41
|
-
*
|
|
42
|
-
* 1.
|
|
43
|
-
* 2. 分发原生代币(
|
|
44
|
-
* 3.
|
|
45
|
-
* 4.
|
|
46
|
-
* 5. 批量买入目标代币
|
|
103
|
+
* 流程(同一个 bundle):
|
|
104
|
+
* 1. 贿赂交易
|
|
105
|
+
* 2. 分发原生代币(dev → 新钱包1, dev → 新钱包2, ...)
|
|
106
|
+
* 3. 买入交易(新钱包1 买入, 新钱包2 买入, ...)
|
|
107
|
+
* 4. 利润多跳
|
|
47
108
|
*
|
|
48
|
-
*
|
|
109
|
+
* ⚠️ 仅支持原生代币(BNB/MON)模式
|
|
110
|
+
* ERC20 模式需要授权,无法在同一个 bundle 中完成
|
|
111
|
+
*
|
|
112
|
+
* @returns 包含所有签名交易的结果,由前端提交
|
|
49
113
|
*/
|
|
50
114
|
export async function holdersMaker(params) {
|
|
51
|
-
const { payerPrivateKey, holdersCount, buyAmountPerHolder, tokenAddress, baseToken = 'native',
|
|
52
|
-
const { rpcUrl, chain = 'BSC', chainId: configChainId, tradeType = '
|
|
53
|
-
// ✅ V2/V3 外盘路由参数
|
|
54
|
-
v2Path, v3TokenIn, v3Fee, v3LpAddresses, v3Tokens } = config;
|
|
115
|
+
const { payerPrivateKey, holdersCount, buyAmountPerHolder, tokenAddress, baseToken = 'native', config } = params;
|
|
116
|
+
const { rpcUrl, chain = 'BSC', chainId: configChainId, tradeType = 'flap', gasLimit = DEFAULT_GAS_LIMIT, gasPriceGwei = DEFAULT_GAS_PRICE_GWEI, bribeAmount = 0.000001, txType = 0, maxWalletsPerBatch = DEFAULT_MAX_WALLETS_PER_BATCH } = config;
|
|
55
117
|
const result = {
|
|
56
118
|
success: false,
|
|
57
119
|
newWallets: [],
|
|
58
|
-
|
|
59
|
-
disperseNative: [],
|
|
60
|
-
buy: []
|
|
61
|
-
},
|
|
62
|
-
hopWallets: [], // ✅ 多跳钱包
|
|
120
|
+
signedTransactions: [],
|
|
63
121
|
batchResults: [],
|
|
64
122
|
successBatchCount: 0,
|
|
65
123
|
totalBatchCount: 0
|
|
66
124
|
};
|
|
125
|
+
// ⚠️ 目前仅支持原生代币模式
|
|
126
|
+
if (baseToken !== 'native') {
|
|
127
|
+
result.error = '一个 bundle 模式仅支持原生代币(BNB/MON),ERC20 模式需要分开提交';
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
// ⚠️ 目前仅支持 Flap 内盘
|
|
131
|
+
if (tradeType !== 'flap') {
|
|
132
|
+
result.error = '一个 bundle 模式目前仅支持 Flap 内盘';
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
67
135
|
try {
|
|
68
|
-
// 1. 初始化
|
|
136
|
+
// 1. 初始化
|
|
69
137
|
const provider = new JsonRpcProvider(rpcUrl);
|
|
70
138
|
const chainId = configChainId || Number((await provider.getNetwork()).chainId);
|
|
139
|
+
const payer = new Wallet(payerPrivateKey, provider);
|
|
140
|
+
const nonceManager = new NonceManager(provider);
|
|
71
141
|
console.log(`[HoldersMaker] 开始刷持有人: chain=${chain}, chainId=${chainId}, holdersCount=${holdersCount}`);
|
|
72
142
|
// 2. 生成新钱包
|
|
73
143
|
const newWallets = generateWallets(holdersCount);
|
|
74
144
|
result.newWallets = newWallets;
|
|
75
145
|
console.log(`[HoldersMaker] 生成 ${newWallets.length} 个新钱包`);
|
|
76
|
-
// 3.
|
|
77
|
-
const
|
|
78
|
-
const gasFeePerWallet = (gasLimit * gasPriceGwei
|
|
79
|
-
const gasWithBuffer = gasFeePerWallet * GAS_BUFFER_MULTIPLIER;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
//
|
|
146
|
+
// 3. 计算金额
|
|
147
|
+
const buyAmountWei = ethers.parseEther(buyAmountPerHolder);
|
|
148
|
+
const gasFeePerWallet = BigInt(Math.floor(gasLimit * gasPriceGwei * 1e9));
|
|
149
|
+
const gasWithBuffer = gasFeePerWallet * BigInt(GAS_BUFFER_MULTIPLIER);
|
|
150
|
+
const transferAmountPerWallet = buyAmountWei + gasWithBuffer;
|
|
151
|
+
console.log(`[HoldersMaker] 每个钱包分发: ${ethers.formatEther(transferAmountPerWallet)} BNB (买入 ${buyAmountPerHolder} + Gas ${ethers.formatEther(gasWithBuffer)})`);
|
|
152
|
+
// 4. 获取 Gas Price
|
|
153
|
+
const gasPrice = await getOptimizedGasPrice(provider, { minGasPrice: BigInt(gasPriceGwei) * 1000000000n });
|
|
154
|
+
const bribeAmountWei = ethers.parseEther(String(bribeAmount));
|
|
155
|
+
// 5. 分批处理
|
|
86
156
|
const walletBatches = chunkArray(newWallets, maxWalletsPerBatch);
|
|
87
157
|
result.totalBatchCount = walletBatches.length;
|
|
88
|
-
console.log(`[HoldersMaker] 分 ${walletBatches.length}
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
|
|
158
|
+
console.log(`[HoldersMaker] 分 ${walletBatches.length} 批处理,每批最多 ${maxWalletsPerBatch} 个钱包`);
|
|
159
|
+
// 6. 计算利润
|
|
160
|
+
const totalBuyAmount = buyAmountWei * BigInt(holdersCount);
|
|
161
|
+
const profitRateBps = PROFIT_CONFIG.RATE_BPS_SWAP;
|
|
162
|
+
const totalProfit = (totalBuyAmount * BigInt(profitRateBps)) / 10000n;
|
|
163
|
+
const profitPerBatch = totalProfit / BigInt(walletBatches.length);
|
|
164
|
+
console.log(`[HoldersMaker] 总利润: ${ethers.formatEther(totalProfit)} BNB`);
|
|
165
|
+
// 7. 并行生成所有批次的签名
|
|
166
|
+
const batchPromises = walletBatches.map(async (batch, batchIdx) => {
|
|
95
167
|
try {
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
batchIndex: batchIdx,
|
|
111
|
-
success: true,
|
|
112
|
-
phase: 'disperse_native',
|
|
113
|
-
signedTransactions: disperseResult.signedTransactions,
|
|
114
|
-
hopWallets: disperseResult.hopWallets,
|
|
115
|
-
walletCount: recipients.length
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
catch (error) {
|
|
119
|
-
return {
|
|
120
|
-
batchIndex: batchIdx,
|
|
121
|
-
success: false,
|
|
122
|
-
phase: 'disperse_native',
|
|
123
|
-
error: error.message,
|
|
124
|
-
walletCount: recipients.length
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
const disperseNativeResults = await Promise.all(disperseNativePromises);
|
|
129
|
-
// 处理分发原生代币结果
|
|
130
|
-
for (const res of disperseNativeResults) {
|
|
131
|
-
if (res.success && res.signedTransactions) {
|
|
132
|
-
result.allSignedTransactions.disperseNative.push(res.signedTransactions);
|
|
133
|
-
if (res.hopWallets && res.hopWallets.length > 0) {
|
|
134
|
-
result.hopWallets.push(res.hopWallets);
|
|
168
|
+
const signedTxs = [];
|
|
169
|
+
// 计算这批需要的 nonce 数量
|
|
170
|
+
// 贿赂 1 + 分发 N + 利润 (PROFIT_HOP_COUNT + 1)
|
|
171
|
+
const payerNonceCount = 1 + batch.length + PROFIT_HOP_COUNT + 1;
|
|
172
|
+
const payerNonces = await nonceManager.getNextNonceBatch(payer, payerNonceCount);
|
|
173
|
+
let payerNonceIdx = 0;
|
|
174
|
+
// (1) 贿赂交易
|
|
175
|
+
const bribeTx = await buildNativeTransferTx(payer, BLOCKRAZOR_BUILDER_EOA, bribeAmountWei, payerNonces[payerNonceIdx++], gasPrice, chainId, txType);
|
|
176
|
+
signedTxs.push(bribeTx);
|
|
177
|
+
// (2) 分发交易
|
|
178
|
+
for (const newWallet of batch) {
|
|
179
|
+
const transferTx = await buildNativeTransferTx(payer, newWallet.address, transferAmountPerWallet, payerNonces[payerNonceIdx++], gasPrice, chainId, txType);
|
|
180
|
+
signedTxs.push(transferTx);
|
|
135
181
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
error: res.error,
|
|
143
|
-
walletCount: res.walletCount
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
const successDisperseNative = disperseNativeResults.filter(r => r.success);
|
|
147
|
-
console.log(`[HoldersMaker] 阶段1完成: ${successDisperseNative.length}/${walletBatches.length} 批成功`);
|
|
148
|
-
// ============================================================
|
|
149
|
-
// ✅ 阶段2:如果是 ERC20,并行生成分发 ERC20 签名
|
|
150
|
-
// ============================================================
|
|
151
|
-
if (!isNativeBase && baseTokenAddress) {
|
|
152
|
-
console.log(`[HoldersMaker] 阶段2: 并行生成 ${walletBatches.length} 批 ERC20 分发签名...`);
|
|
153
|
-
const disperseErc20Promises = walletBatches.map(async (batch, batchIdx) => {
|
|
154
|
-
const recipients = batch.map(w => w.address);
|
|
155
|
-
try {
|
|
156
|
-
const disperseResult = await disperseWithBundleMerkle({
|
|
157
|
-
fromPrivateKey: payerPrivateKey,
|
|
158
|
-
recipients,
|
|
159
|
-
amount: buyAmountPerHolder,
|
|
160
|
-
tokenAddress: baseTokenAddress,
|
|
161
|
-
tokenDecimals: baseTokenDecimals,
|
|
162
|
-
hopCount,
|
|
163
|
-
config: {
|
|
164
|
-
rpcUrl,
|
|
165
|
-
gasLimit,
|
|
166
|
-
minGasPriceGwei: gasPriceGwei,
|
|
167
|
-
txType,
|
|
168
|
-
bribeAmount
|
|
169
|
-
}
|
|
170
|
-
});
|
|
171
|
-
return {
|
|
172
|
-
batchIndex: batchIdx,
|
|
173
|
-
success: true,
|
|
174
|
-
phase: 'disperse_erc20',
|
|
175
|
-
signedTransactions: disperseResult.signedTransactions,
|
|
176
|
-
walletCount: recipients.length
|
|
177
|
-
};
|
|
182
|
+
// (3) 买入交易(新钱包,nonce=0)
|
|
183
|
+
for (const newWallet of batch) {
|
|
184
|
+
const buyerWallet = new Wallet(newWallet.privateKey, provider);
|
|
185
|
+
const buyTx = await buildFlapBuyTx(buyerWallet, tokenAddress, buyAmountWei, 0, // 新钱包 nonce=0
|
|
186
|
+
gasPrice, gasLimit, chainId, txType, chain);
|
|
187
|
+
signedTxs.push(buyTx);
|
|
178
188
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
for (const res of disperseErc20Results) {
|
|
192
|
-
if (res.success && res.signedTransactions) {
|
|
193
|
-
result.allSignedTransactions.disperseErc20.push(res.signedTransactions);
|
|
194
|
-
}
|
|
195
|
-
result.batchResults.push({
|
|
196
|
-
batchIndex: res.batchIndex,
|
|
197
|
-
success: res.success,
|
|
198
|
-
phase: res.phase,
|
|
199
|
-
signedTransactions: res.signedTransactions,
|
|
200
|
-
error: res.error,
|
|
201
|
-
walletCount: res.walletCount
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
const successDisperseErc20 = disperseErc20Results.filter(r => r.success);
|
|
205
|
-
console.log(`[HoldersMaker] 阶段2完成: ${successDisperseErc20.length}/${walletBatches.length} 批成功`);
|
|
206
|
-
// ============================================================
|
|
207
|
-
// ✅ 阶段3:并行生成授权签名(每批内也是并行)
|
|
208
|
-
// ============================================================
|
|
209
|
-
console.log(`[HoldersMaker] 阶段3: 并行生成 ${walletBatches.length} 批授权签名...`);
|
|
210
|
-
// ✅ 转换 tradeType 为 approveTokenBatch 支持的 platform 格式
|
|
211
|
-
const approvePlatform = tradeType === 'v2' ? 'pancake-v2'
|
|
212
|
-
: tradeType === 'v3' ? 'pancake-v3'
|
|
213
|
-
: tradeType;
|
|
214
|
-
const approvePromises = walletBatches.map(async (batch, batchIdx) => {
|
|
215
|
-
try {
|
|
216
|
-
const approveResult = await approveTokenBatch({
|
|
217
|
-
chain: chain,
|
|
218
|
-
platform: approvePlatform,
|
|
219
|
-
rpcUrl,
|
|
220
|
-
privateKeys: batch.map(w => w.privateKey),
|
|
221
|
-
tokenAddress: baseTokenAddress,
|
|
222
|
-
amounts: batch.map(() => 'max'),
|
|
223
|
-
signOnly: true
|
|
224
|
-
});
|
|
225
|
-
return {
|
|
226
|
-
batchIndex: batchIdx,
|
|
227
|
-
success: true,
|
|
228
|
-
phase: 'approve',
|
|
229
|
-
signedTransactions: approveResult.signedTransactions,
|
|
230
|
-
walletCount: batch.length
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
catch (error) {
|
|
234
|
-
return {
|
|
235
|
-
batchIndex: batchIdx,
|
|
236
|
-
success: false,
|
|
237
|
-
phase: 'approve',
|
|
238
|
-
error: error.message,
|
|
239
|
-
walletCount: batch.length
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
});
|
|
243
|
-
const approveResults = await Promise.all(approvePromises);
|
|
244
|
-
result.allSignedTransactions.approve = [];
|
|
245
|
-
for (const res of approveResults) {
|
|
246
|
-
if (res.success && res.signedTransactions) {
|
|
247
|
-
result.allSignedTransactions.approve.push(res.signedTransactions);
|
|
248
|
-
}
|
|
249
|
-
result.batchResults.push({
|
|
250
|
-
batchIndex: res.batchIndex,
|
|
251
|
-
success: res.success,
|
|
252
|
-
phase: res.phase,
|
|
253
|
-
signedTransactions: res.signedTransactions,
|
|
254
|
-
error: res.error,
|
|
255
|
-
walletCount: res.walletCount
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
const successApprove = approveResults.filter(r => r.success);
|
|
259
|
-
console.log(`[HoldersMaker] 阶段3完成: ${successApprove.length}/${walletBatches.length} 批成功`);
|
|
260
|
-
}
|
|
261
|
-
// ============================================================
|
|
262
|
-
// ✅ 阶段4:并行生成所有批次的买入签名
|
|
263
|
-
// ============================================================
|
|
264
|
-
console.log(`[HoldersMaker] 阶段4: 并行生成 ${walletBatches.length} 批买入签名...`);
|
|
265
|
-
const buyPromises = walletBatches.map(async (batch, batchIdx) => {
|
|
266
|
-
try {
|
|
267
|
-
let buyResult;
|
|
268
|
-
if (tradeType === 'flap') {
|
|
269
|
-
// ✅ Flap 内盘:支持原生代币和 ERC20
|
|
270
|
-
buyResult = await flapBatchBuy({
|
|
271
|
-
chain: chain,
|
|
272
|
-
privateKeys: batch.map(w => w.privateKey),
|
|
273
|
-
buyAmounts: batch.map(() => buyAmountPerHolder),
|
|
274
|
-
tokenAddress,
|
|
275
|
-
quoteToken: isNativeBase ? undefined : baseTokenAddress,
|
|
276
|
-
quoteTokenDecimals: isNativeBase ? undefined : baseTokenDecimals,
|
|
277
|
-
config: {
|
|
278
|
-
rpcUrl,
|
|
279
|
-
gasLimit,
|
|
280
|
-
minGasPriceGwei: gasPriceGwei,
|
|
281
|
-
txType,
|
|
282
|
-
bribeAmount
|
|
283
|
-
}
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
else if (tradeType === 'v2') {
|
|
287
|
-
// ✅ PancakeSwap V2 外盘
|
|
288
|
-
buyResult = await pancakeProxyBatchBuyMerkle({
|
|
289
|
-
chain: chain,
|
|
290
|
-
privateKeys: batch.map(w => w.privateKey),
|
|
291
|
-
buyAmounts: batch.map(() => buyAmountPerHolder),
|
|
292
|
-
tokenAddress,
|
|
293
|
-
routeType: 'v2',
|
|
294
|
-
v2Path: v2Path,
|
|
295
|
-
quoteToken: isNativeBase ? undefined : baseTokenAddress,
|
|
296
|
-
quoteTokenDecimals: isNativeBase ? undefined : baseTokenDecimals,
|
|
297
|
-
config: {
|
|
298
|
-
rpcUrl,
|
|
299
|
-
gasLimit,
|
|
300
|
-
minGasPriceGwei: gasPriceGwei,
|
|
301
|
-
txType,
|
|
302
|
-
bribeAmount
|
|
303
|
-
}
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
else if (tradeType === 'v3') {
|
|
307
|
-
// ✅ PancakeSwap V3 外盘
|
|
308
|
-
const hasMultiHop = v3LpAddresses && v3LpAddresses.length > 0;
|
|
309
|
-
buyResult = await pancakeProxyBatchBuyMerkle({
|
|
310
|
-
chain: chain,
|
|
311
|
-
privateKeys: batch.map(w => w.privateKey),
|
|
312
|
-
buyAmounts: batch.map(() => buyAmountPerHolder),
|
|
313
|
-
tokenAddress,
|
|
314
|
-
routeType: hasMultiHop ? 'v3-multi' : 'v3-single',
|
|
315
|
-
v3TokenIn: v3TokenIn,
|
|
316
|
-
v3Fee: v3Fee,
|
|
317
|
-
v3LpAddresses: v3LpAddresses,
|
|
318
|
-
v3Tokens: v3Tokens,
|
|
319
|
-
quoteToken: isNativeBase ? undefined : baseTokenAddress,
|
|
320
|
-
quoteTokenDecimals: isNativeBase ? undefined : baseTokenDecimals,
|
|
321
|
-
config: {
|
|
322
|
-
rpcUrl,
|
|
323
|
-
gasLimit,
|
|
324
|
-
minGasPriceGwei: gasPriceGwei,
|
|
325
|
-
txType,
|
|
326
|
-
bribeAmount
|
|
327
|
-
}
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
else {
|
|
331
|
-
// ✅ Four 内盘:仅支持原生代币(BNB)
|
|
332
|
-
if (!isNativeBase) {
|
|
333
|
-
throw new Error('Four 内盘仅支持原生代币(BNB)购买,不支持 USDT/USDC');
|
|
334
|
-
}
|
|
335
|
-
buyResult = await fourBatchBuy({
|
|
336
|
-
privateKeys: batch.map(w => w.privateKey),
|
|
337
|
-
buyAmounts: batch.map(() => buyAmountPerHolder),
|
|
338
|
-
tokenAddress,
|
|
339
|
-
config: {
|
|
340
|
-
rpcUrl,
|
|
341
|
-
gasLimit,
|
|
342
|
-
minGasPriceGwei: gasPriceGwei,
|
|
343
|
-
txType,
|
|
344
|
-
bribeAmount,
|
|
345
|
-
fourApiUrl
|
|
346
|
-
}
|
|
189
|
+
// (4) 利润多跳
|
|
190
|
+
if (profitPerBatch > 0n) {
|
|
191
|
+
const profitHopResult = await buildProfitHopTransactions({
|
|
192
|
+
provider,
|
|
193
|
+
payerWallet: payer,
|
|
194
|
+
profitAmount: profitPerBatch,
|
|
195
|
+
profitRecipient: PROFIT_CONFIG.RECIPIENT,
|
|
196
|
+
hopCount: PROFIT_HOP_COUNT,
|
|
197
|
+
gasPrice,
|
|
198
|
+
chainId,
|
|
199
|
+
txType,
|
|
200
|
+
startNonce: payerNonces[payerNonceIdx]
|
|
347
201
|
});
|
|
202
|
+
signedTxs.push(...profitHopResult.signedTransactions);
|
|
348
203
|
}
|
|
349
204
|
return {
|
|
350
205
|
batchIndex: batchIdx,
|
|
351
206
|
success: true,
|
|
352
|
-
|
|
353
|
-
signedTransactions: buyResult.signedTransactions,
|
|
207
|
+
signedTransactions: signedTxs,
|
|
354
208
|
walletCount: batch.length
|
|
355
209
|
};
|
|
356
210
|
}
|
|
@@ -358,29 +212,26 @@ export async function holdersMaker(params) {
|
|
|
358
212
|
return {
|
|
359
213
|
batchIndex: batchIdx,
|
|
360
214
|
success: false,
|
|
361
|
-
phase: 'buy',
|
|
362
215
|
error: error.message,
|
|
363
216
|
walletCount: batch.length
|
|
364
217
|
};
|
|
365
218
|
}
|
|
366
219
|
});
|
|
367
|
-
const
|
|
368
|
-
|
|
220
|
+
const batchResults = await Promise.all(batchPromises);
|
|
221
|
+
// 8. 处理结果
|
|
222
|
+
for (const res of batchResults) {
|
|
369
223
|
if (res.success && res.signedTransactions) {
|
|
370
|
-
result.
|
|
224
|
+
result.signedTransactions.push(res.signedTransactions);
|
|
371
225
|
result.successBatchCount++;
|
|
372
226
|
}
|
|
373
227
|
result.batchResults.push({
|
|
374
228
|
batchIndex: res.batchIndex,
|
|
375
229
|
success: res.success,
|
|
376
|
-
phase: res.phase,
|
|
377
230
|
signedTransactions: res.signedTransactions,
|
|
378
231
|
error: res.error,
|
|
379
232
|
walletCount: res.walletCount
|
|
380
233
|
});
|
|
381
234
|
}
|
|
382
|
-
const successBuy = buyResults.filter(r => r.success);
|
|
383
|
-
console.log(`[HoldersMaker] 阶段4完成: ${successBuy.length}/${walletBatches.length} 批成功`);
|
|
384
235
|
result.success = result.successBatchCount > 0;
|
|
385
236
|
console.log(`[HoldersMaker] 完成: ${result.successBatchCount}/${result.totalBatchCount} 批成功`);
|
|
386
237
|
}
|
|
@@ -394,33 +245,18 @@ export async function holdersMaker(params) {
|
|
|
394
245
|
* 获取刷持有人预估费用
|
|
395
246
|
*/
|
|
396
247
|
export function estimateHoldersMakerCost(params) {
|
|
397
|
-
const { holdersCount, buyAmountPerHolder, gasLimit = DEFAULT_GAS_LIMIT, gasPriceGwei = DEFAULT_GAS_PRICE_GWEI
|
|
398
|
-
const isNativeBase = baseToken === 'native';
|
|
248
|
+
const { holdersCount, buyAmountPerHolder, gasLimit = DEFAULT_GAS_LIMIT, gasPriceGwei = DEFAULT_GAS_PRICE_GWEI } = params;
|
|
399
249
|
const gasFeePerWallet = (gasLimit * gasPriceGwei) / 1e9;
|
|
400
250
|
const gasWithBuffer = gasFeePerWallet * GAS_BUFFER_MULTIPLIER;
|
|
401
251
|
const buyAmount = parseFloat(buyAmountPerHolder);
|
|
402
|
-
//
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
const hopGasPerTransfer = isNativeBase ? 21000 : 65000;
|
|
406
|
-
const hopGasCost = (hopGasPerTransfer * gasPriceGwei * hopCount) / 1e9;
|
|
407
|
-
// 每个钱包的原生代币成本(包含多跳 Gas)
|
|
408
|
-
const nativePerWallet = isNativeBase
|
|
409
|
-
? buyAmount + gasWithBuffer + hopGasCost
|
|
410
|
-
: gasWithBuffer + hopGasCost;
|
|
411
|
-
// 总原生代币成本
|
|
412
|
-
const totalNativeCost = nativePerWallet * holdersCount;
|
|
413
|
-
// 总基础代币成本(如果是 ERC20)
|
|
414
|
-
const totalBaseTokenCost = isNativeBase ? 0 : buyAmount * holdersCount;
|
|
252
|
+
// 每个钱包成本 = 购买金额 + Gas 费
|
|
253
|
+
const costPerWallet = buyAmount + gasWithBuffer;
|
|
254
|
+
const totalNativeCost = costPerWallet * holdersCount;
|
|
415
255
|
// 批次数
|
|
416
256
|
const batchCount = Math.ceil(holdersCount / DEFAULT_MAX_WALLETS_PER_BATCH);
|
|
417
|
-
// ✅ 多跳钱包总数
|
|
418
|
-
const hopWalletCount = holdersCount * hopCount;
|
|
419
257
|
return {
|
|
420
258
|
totalNativeCost: totalNativeCost.toFixed(6),
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
batchCount,
|
|
424
|
-
hopWalletCount
|
|
259
|
+
gasEstimate: (gasWithBuffer * holdersCount).toFixed(6),
|
|
260
|
+
batchCount
|
|
425
261
|
};
|
|
426
262
|
}
|