four-flap-meme-sdk 1.4.39 → 1.4.41
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/contracts/tm-bundle-merkle/core.js +36 -28
- package/dist/contracts/tm-bundle-merkle/pancake-proxy.js +25 -19
- package/dist/contracts/tm-bundle-merkle/private.js +47 -37
- package/dist/contracts/tm-bundle-merkle/swap-buy-first.js +17 -19
- package/dist/contracts/tm-bundle-merkle/swap.js +51 -52
- package/dist/contracts/tm-bundle-merkle/utils.js +70 -58
- package/dist/contracts/tm-bundle.js +35 -25
- package/dist/dex/direct-router.js +37 -34
- package/dist/flap/portal-bundle-merkle/core.js +34 -28
- package/dist/flap/portal-bundle-merkle/pancake-proxy.js +25 -17
- package/dist/flap/portal-bundle-merkle/private.js +73 -53
- package/dist/flap/portal-bundle-merkle/swap-buy-first.js +26 -20
- package/dist/flap/portal-bundle-merkle/swap.js +60 -55
- package/dist/flap/portal-bundle-merkle/utils.js +69 -44
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/pancake/bundle-buy-first.js +27 -20
- package/dist/pancake/bundle-swap.js +56 -56
- package/dist/utils/bundle-helpers.d.ts +38 -0
- package/dist/utils/bundle-helpers.js +85 -1
- package/dist/utils/holders-maker.d.ts +122 -0
- package/dist/utils/holders-maker.js +350 -0
- package/dist/utils/private-sale.js +22 -19
- package/package.json +1 -1
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================================
|
|
3
|
+
* 刷持有人(Holders Maker)
|
|
4
|
+
* ============================================================================
|
|
5
|
+
* 一键完成:生成钱包 → 分发资金 → 批量买入
|
|
6
|
+
* 支持原生代币(BNB/MON)和 ERC20 代币(USDT/USDC)作为购买资金
|
|
7
|
+
*/
|
|
8
|
+
import { JsonRpcProvider } from 'ethers';
|
|
9
|
+
import { generateWallets } from './wallet.js';
|
|
10
|
+
import { disperseWithBundleMerkle } from '../contracts/tm-bundle-merkle/utils.js';
|
|
11
|
+
import { batchBuyWithBundleMerkle } from '../contracts/tm-bundle-merkle/core.js';
|
|
12
|
+
import { approveTokenBatch } from './erc20.js';
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// 常量
|
|
15
|
+
// ============================================================================
|
|
16
|
+
const DEFAULT_GAS_LIMIT = 800000;
|
|
17
|
+
const DEFAULT_GAS_PRICE_GWEI = 3;
|
|
18
|
+
const DEFAULT_MAX_WALLETS_PER_BATCH = 46; // 50 - 1(bribe) - 3(profit hops)
|
|
19
|
+
const GAS_BUFFER_MULTIPLIER = 2; // 2倍 Gas 费安全系数
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// 辅助函数
|
|
22
|
+
// ============================================================================
|
|
23
|
+
/**
|
|
24
|
+
* 分批数组
|
|
25
|
+
*/
|
|
26
|
+
function chunkArray(arr, size) {
|
|
27
|
+
const chunks = [];
|
|
28
|
+
for (let i = 0; i < arr.length; i += size) {
|
|
29
|
+
chunks.push(arr.slice(i, i + size));
|
|
30
|
+
}
|
|
31
|
+
return chunks;
|
|
32
|
+
}
|
|
33
|
+
// ============================================================================
|
|
34
|
+
// 主方法
|
|
35
|
+
// ============================================================================
|
|
36
|
+
/**
|
|
37
|
+
* 刷持有人(一键完成)
|
|
38
|
+
*
|
|
39
|
+
* 流程:
|
|
40
|
+
* 1. 生成新钱包
|
|
41
|
+
* 2. 分发原生代币(Gas 费 + 购买金额 / 仅 Gas 费)
|
|
42
|
+
* 3. 如果使用 ERC20 基础代币,分发 ERC20 代币
|
|
43
|
+
* 4. 如果使用 ERC20 基础代币,授权
|
|
44
|
+
* 5. 批量买入目标代币
|
|
45
|
+
*
|
|
46
|
+
* @returns 包含所有阶段签名交易的结果,由前端提交
|
|
47
|
+
*/
|
|
48
|
+
export async function holdersMaker(params) {
|
|
49
|
+
const { payerPrivateKey, holdersCount, buyAmountPerHolder, tokenAddress, baseToken = 'native', baseTokenAddress, baseTokenDecimals = 18, config } = params;
|
|
50
|
+
const { rpcUrl, chain = 'BSC', chainId: configChainId, tradeType = 'four', gasLimit = DEFAULT_GAS_LIMIT, gasPriceGwei = DEFAULT_GAS_PRICE_GWEI, bribeAmount = 0.000001, txType = 0, maxWalletsPerBatch = DEFAULT_MAX_WALLETS_PER_BATCH, fourApiUrl, hopCount = 0 // ✅ 多跳数量,默认不多跳
|
|
51
|
+
} = config;
|
|
52
|
+
const result = {
|
|
53
|
+
success: false,
|
|
54
|
+
newWallets: [],
|
|
55
|
+
allSignedTransactions: {
|
|
56
|
+
disperseNative: [],
|
|
57
|
+
buy: []
|
|
58
|
+
},
|
|
59
|
+
hopWallets: [], // ✅ 多跳钱包
|
|
60
|
+
batchResults: [],
|
|
61
|
+
successBatchCount: 0,
|
|
62
|
+
totalBatchCount: 0
|
|
63
|
+
};
|
|
64
|
+
try {
|
|
65
|
+
// 1. 初始化 Provider
|
|
66
|
+
const provider = new JsonRpcProvider(rpcUrl);
|
|
67
|
+
const chainId = configChainId || Number((await provider.getNetwork()).chainId);
|
|
68
|
+
console.log(`[HoldersMaker] 开始刷持有人: chain=${chain}, chainId=${chainId}, holdersCount=${holdersCount}`);
|
|
69
|
+
// 2. 生成新钱包
|
|
70
|
+
const newWallets = generateWallets(holdersCount);
|
|
71
|
+
result.newWallets = newWallets;
|
|
72
|
+
console.log(`[HoldersMaker] 生成 ${newWallets.length} 个新钱包`);
|
|
73
|
+
// 3. 计算分发金额
|
|
74
|
+
const isNativeBase = baseToken === 'native';
|
|
75
|
+
const gasFeePerWallet = (gasLimit * gasPriceGwei) / 1e9; // 转为原生代币单位
|
|
76
|
+
const gasWithBuffer = gasFeePerWallet * GAS_BUFFER_MULTIPLIER;
|
|
77
|
+
// 原生代币分发金额 = Gas 费(+ 购买金额,如果是原生代币模式)
|
|
78
|
+
const nativeAmountPerWallet = isNativeBase
|
|
79
|
+
? String(parseFloat(buyAmountPerHolder) + gasWithBuffer)
|
|
80
|
+
: String(gasWithBuffer);
|
|
81
|
+
console.log(`[HoldersMaker] 分发金额: nativePerWallet=${nativeAmountPerWallet}, isNativeBase=${isNativeBase}, hopCount=${hopCount}`);
|
|
82
|
+
// 4. 分批处理
|
|
83
|
+
const walletBatches = chunkArray(newWallets, maxWalletsPerBatch);
|
|
84
|
+
result.totalBatchCount = walletBatches.length;
|
|
85
|
+
console.log(`[HoldersMaker] 分 ${walletBatches.length} 批处理`);
|
|
86
|
+
// ============================================================
|
|
87
|
+
// ✅ 阶段1:并行生成所有批次的分发原生代币签名
|
|
88
|
+
// ============================================================
|
|
89
|
+
console.log(`[HoldersMaker] 阶段1: 并行生成 ${walletBatches.length} 批原生代币分发签名...`);
|
|
90
|
+
const disperseNativePromises = walletBatches.map(async (batch, batchIdx) => {
|
|
91
|
+
const recipients = batch.map(w => w.address);
|
|
92
|
+
try {
|
|
93
|
+
const disperseResult = await disperseWithBundleMerkle({
|
|
94
|
+
fromPrivateKey: payerPrivateKey,
|
|
95
|
+
recipients,
|
|
96
|
+
amount: nativeAmountPerWallet,
|
|
97
|
+
hopCount,
|
|
98
|
+
config: {
|
|
99
|
+
rpcUrl,
|
|
100
|
+
gasLimit,
|
|
101
|
+
minGasPriceGwei: gasPriceGwei,
|
|
102
|
+
txType,
|
|
103
|
+
bribeAmount
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
return {
|
|
107
|
+
batchIndex: batchIdx,
|
|
108
|
+
success: true,
|
|
109
|
+
phase: 'disperse_native',
|
|
110
|
+
signedTransactions: disperseResult.signedTransactions,
|
|
111
|
+
hopWallets: disperseResult.hopWallets,
|
|
112
|
+
walletCount: recipients.length
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
return {
|
|
117
|
+
batchIndex: batchIdx,
|
|
118
|
+
success: false,
|
|
119
|
+
phase: 'disperse_native',
|
|
120
|
+
error: error.message,
|
|
121
|
+
walletCount: recipients.length
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
const disperseNativeResults = await Promise.all(disperseNativePromises);
|
|
126
|
+
// 处理分发原生代币结果
|
|
127
|
+
for (const res of disperseNativeResults) {
|
|
128
|
+
if (res.success && res.signedTransactions) {
|
|
129
|
+
result.allSignedTransactions.disperseNative.push(res.signedTransactions);
|
|
130
|
+
if (res.hopWallets && res.hopWallets.length > 0) {
|
|
131
|
+
result.hopWallets.push(res.hopWallets);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
result.batchResults.push({
|
|
135
|
+
batchIndex: res.batchIndex,
|
|
136
|
+
success: res.success,
|
|
137
|
+
phase: res.phase,
|
|
138
|
+
signedTransactions: res.signedTransactions,
|
|
139
|
+
error: res.error,
|
|
140
|
+
walletCount: res.walletCount
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
const successDisperseNative = disperseNativeResults.filter(r => r.success);
|
|
144
|
+
console.log(`[HoldersMaker] 阶段1完成: ${successDisperseNative.length}/${walletBatches.length} 批成功`);
|
|
145
|
+
// ============================================================
|
|
146
|
+
// ✅ 阶段2:如果是 ERC20,并行生成分发 ERC20 签名
|
|
147
|
+
// ============================================================
|
|
148
|
+
if (!isNativeBase && baseTokenAddress) {
|
|
149
|
+
console.log(`[HoldersMaker] 阶段2: 并行生成 ${walletBatches.length} 批 ERC20 分发签名...`);
|
|
150
|
+
const disperseErc20Promises = walletBatches.map(async (batch, batchIdx) => {
|
|
151
|
+
const recipients = batch.map(w => w.address);
|
|
152
|
+
try {
|
|
153
|
+
const disperseResult = await disperseWithBundleMerkle({
|
|
154
|
+
fromPrivateKey: payerPrivateKey,
|
|
155
|
+
recipients,
|
|
156
|
+
amount: buyAmountPerHolder,
|
|
157
|
+
tokenAddress: baseTokenAddress,
|
|
158
|
+
tokenDecimals: baseTokenDecimals,
|
|
159
|
+
hopCount,
|
|
160
|
+
config: {
|
|
161
|
+
rpcUrl,
|
|
162
|
+
gasLimit,
|
|
163
|
+
minGasPriceGwei: gasPriceGwei,
|
|
164
|
+
txType,
|
|
165
|
+
bribeAmount
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
return {
|
|
169
|
+
batchIndex: batchIdx,
|
|
170
|
+
success: true,
|
|
171
|
+
phase: 'disperse_erc20',
|
|
172
|
+
signedTransactions: disperseResult.signedTransactions,
|
|
173
|
+
walletCount: recipients.length
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
return {
|
|
178
|
+
batchIndex: batchIdx,
|
|
179
|
+
success: false,
|
|
180
|
+
phase: 'disperse_erc20',
|
|
181
|
+
error: error.message,
|
|
182
|
+
walletCount: recipients.length
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
const disperseErc20Results = await Promise.all(disperseErc20Promises);
|
|
187
|
+
result.allSignedTransactions.disperseErc20 = [];
|
|
188
|
+
for (const res of disperseErc20Results) {
|
|
189
|
+
if (res.success && res.signedTransactions) {
|
|
190
|
+
result.allSignedTransactions.disperseErc20.push(res.signedTransactions);
|
|
191
|
+
}
|
|
192
|
+
result.batchResults.push({
|
|
193
|
+
batchIndex: res.batchIndex,
|
|
194
|
+
success: res.success,
|
|
195
|
+
phase: res.phase,
|
|
196
|
+
signedTransactions: res.signedTransactions,
|
|
197
|
+
error: res.error,
|
|
198
|
+
walletCount: res.walletCount
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
const successDisperseErc20 = disperseErc20Results.filter(r => r.success);
|
|
202
|
+
console.log(`[HoldersMaker] 阶段2完成: ${successDisperseErc20.length}/${walletBatches.length} 批成功`);
|
|
203
|
+
// ============================================================
|
|
204
|
+
// ✅ 阶段3:并行生成授权签名(每批内也是并行)
|
|
205
|
+
// ============================================================
|
|
206
|
+
console.log(`[HoldersMaker] 阶段3: 并行生成 ${walletBatches.length} 批授权签名...`);
|
|
207
|
+
const approvePromises = walletBatches.map(async (batch, batchIdx) => {
|
|
208
|
+
try {
|
|
209
|
+
const approveResult = await approveTokenBatch({
|
|
210
|
+
chain: chain,
|
|
211
|
+
platform: tradeType,
|
|
212
|
+
rpcUrl,
|
|
213
|
+
privateKeys: batch.map(w => w.privateKey),
|
|
214
|
+
tokenAddress: baseTokenAddress,
|
|
215
|
+
amounts: batch.map(() => 'max'),
|
|
216
|
+
signOnly: true
|
|
217
|
+
});
|
|
218
|
+
return {
|
|
219
|
+
batchIndex: batchIdx,
|
|
220
|
+
success: true,
|
|
221
|
+
phase: 'approve',
|
|
222
|
+
signedTransactions: approveResult.signedTransactions,
|
|
223
|
+
walletCount: batch.length
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
return {
|
|
228
|
+
batchIndex: batchIdx,
|
|
229
|
+
success: false,
|
|
230
|
+
phase: 'approve',
|
|
231
|
+
error: error.message,
|
|
232
|
+
walletCount: batch.length
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
const approveResults = await Promise.all(approvePromises);
|
|
237
|
+
result.allSignedTransactions.approve = [];
|
|
238
|
+
for (const res of approveResults) {
|
|
239
|
+
if (res.success && res.signedTransactions) {
|
|
240
|
+
result.allSignedTransactions.approve.push(res.signedTransactions);
|
|
241
|
+
}
|
|
242
|
+
result.batchResults.push({
|
|
243
|
+
batchIndex: res.batchIndex,
|
|
244
|
+
success: res.success,
|
|
245
|
+
phase: res.phase,
|
|
246
|
+
signedTransactions: res.signedTransactions,
|
|
247
|
+
error: res.error,
|
|
248
|
+
walletCount: res.walletCount
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
const successApprove = approveResults.filter(r => r.success);
|
|
252
|
+
console.log(`[HoldersMaker] 阶段3完成: ${successApprove.length}/${walletBatches.length} 批成功`);
|
|
253
|
+
}
|
|
254
|
+
// ============================================================
|
|
255
|
+
// ✅ 阶段4:并行生成所有批次的买入签名
|
|
256
|
+
// ============================================================
|
|
257
|
+
console.log(`[HoldersMaker] 阶段4: 并行生成 ${walletBatches.length} 批买入签名...`);
|
|
258
|
+
const buyPromises = walletBatches.map(async (batch, batchIdx) => {
|
|
259
|
+
try {
|
|
260
|
+
const buyResult = await batchBuyWithBundleMerkle({
|
|
261
|
+
privateKeys: batch.map(w => w.privateKey),
|
|
262
|
+
buyAmounts: batch.map(() => buyAmountPerHolder),
|
|
263
|
+
tokenAddress,
|
|
264
|
+
config: {
|
|
265
|
+
rpcUrl,
|
|
266
|
+
gasLimit,
|
|
267
|
+
minGasPriceGwei: gasPriceGwei,
|
|
268
|
+
txType,
|
|
269
|
+
bribeAmount,
|
|
270
|
+
fourApiUrl
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
return {
|
|
274
|
+
batchIndex: batchIdx,
|
|
275
|
+
success: true,
|
|
276
|
+
phase: 'buy',
|
|
277
|
+
signedTransactions: buyResult.signedTransactions,
|
|
278
|
+
walletCount: batch.length
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
return {
|
|
283
|
+
batchIndex: batchIdx,
|
|
284
|
+
success: false,
|
|
285
|
+
phase: 'buy',
|
|
286
|
+
error: error.message,
|
|
287
|
+
walletCount: batch.length
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
const buyResults = await Promise.all(buyPromises);
|
|
292
|
+
for (const res of buyResults) {
|
|
293
|
+
if (res.success && res.signedTransactions) {
|
|
294
|
+
result.allSignedTransactions.buy.push(res.signedTransactions);
|
|
295
|
+
result.successBatchCount++;
|
|
296
|
+
}
|
|
297
|
+
result.batchResults.push({
|
|
298
|
+
batchIndex: res.batchIndex,
|
|
299
|
+
success: res.success,
|
|
300
|
+
phase: res.phase,
|
|
301
|
+
signedTransactions: res.signedTransactions,
|
|
302
|
+
error: res.error,
|
|
303
|
+
walletCount: res.walletCount
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
const successBuy = buyResults.filter(r => r.success);
|
|
307
|
+
console.log(`[HoldersMaker] 阶段4完成: ${successBuy.length}/${walletBatches.length} 批成功`);
|
|
308
|
+
result.success = result.successBatchCount > 0;
|
|
309
|
+
console.log(`[HoldersMaker] 完成: ${result.successBatchCount}/${result.totalBatchCount} 批成功`);
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
result.error = error.message;
|
|
313
|
+
console.error(`[HoldersMaker] 整体失败:`, error.message);
|
|
314
|
+
}
|
|
315
|
+
return result;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* 获取刷持有人预估费用
|
|
319
|
+
*/
|
|
320
|
+
export function estimateHoldersMakerCost(params) {
|
|
321
|
+
const { holdersCount, buyAmountPerHolder, gasLimit = DEFAULT_GAS_LIMIT, gasPriceGwei = DEFAULT_GAS_PRICE_GWEI, baseToken = 'native', hopCount = 0 } = params;
|
|
322
|
+
const isNativeBase = baseToken === 'native';
|
|
323
|
+
const gasFeePerWallet = (gasLimit * gasPriceGwei) / 1e9;
|
|
324
|
+
const gasWithBuffer = gasFeePerWallet * GAS_BUFFER_MULTIPLIER;
|
|
325
|
+
const buyAmount = parseFloat(buyAmountPerHolder);
|
|
326
|
+
// ✅ 多跳需要额外的 Gas 费用(每跳一次需要一笔转账)
|
|
327
|
+
// 原生代币多跳:每跳 21000 gas
|
|
328
|
+
// ERC20 多跳:每跳 65000 gas
|
|
329
|
+
const hopGasPerTransfer = isNativeBase ? 21000 : 65000;
|
|
330
|
+
const hopGasCost = (hopGasPerTransfer * gasPriceGwei * hopCount) / 1e9;
|
|
331
|
+
// 每个钱包的原生代币成本(包含多跳 Gas)
|
|
332
|
+
const nativePerWallet = isNativeBase
|
|
333
|
+
? buyAmount + gasWithBuffer + hopGasCost
|
|
334
|
+
: gasWithBuffer + hopGasCost;
|
|
335
|
+
// 总原生代币成本
|
|
336
|
+
const totalNativeCost = nativePerWallet * holdersCount;
|
|
337
|
+
// 总基础代币成本(如果是 ERC20)
|
|
338
|
+
const totalBaseTokenCost = isNativeBase ? 0 : buyAmount * holdersCount;
|
|
339
|
+
// 批次数
|
|
340
|
+
const batchCount = Math.ceil(holdersCount / DEFAULT_MAX_WALLETS_PER_BATCH);
|
|
341
|
+
// ✅ 多跳钱包总数
|
|
342
|
+
const hopWalletCount = holdersCount * hopCount;
|
|
343
|
+
return {
|
|
344
|
+
totalNativeCost: totalNativeCost.toFixed(6),
|
|
345
|
+
totalBaseTokenCost: totalBaseTokenCost.toFixed(6),
|
|
346
|
+
gasEstimate: ((gasWithBuffer + hopGasCost) * holdersCount).toFixed(6),
|
|
347
|
+
batchCount,
|
|
348
|
+
hopWalletCount
|
|
349
|
+
};
|
|
350
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { ethers, Wallet, JsonRpcProvider } from 'ethers';
|
|
9
9
|
import { PROFIT_CONFIG, BLOCKRAZOR_BUILDER_EOA } from './constants.js';
|
|
10
|
-
import { NonceManager } from './bundle-helpers.js';
|
|
10
|
+
import { NonceManager, buildProfitHopTransactions, PROFIT_HOP_COUNT } from './bundle-helpers.js';
|
|
11
11
|
// ============================================================================
|
|
12
12
|
// 核心功能
|
|
13
13
|
// ============================================================================
|
|
@@ -94,10 +94,10 @@ export async function batchPrivateSaleMerkle(params) {
|
|
|
94
94
|
}
|
|
95
95
|
// 利润交易的 nonce
|
|
96
96
|
const profitNonce = hasProfit ? currentNonces.get(firstWalletKey) : 0;
|
|
97
|
-
// ✅
|
|
98
|
-
const
|
|
97
|
+
// ✅ 签名交易数组
|
|
98
|
+
const signedTransactions = [];
|
|
99
99
|
// 1. 贿赂交易签名
|
|
100
|
-
|
|
100
|
+
const bribeTx = await firstWallet.signTransaction({
|
|
101
101
|
to: BLOCKRAZOR_BUILDER_EOA,
|
|
102
102
|
value: bribeAmount,
|
|
103
103
|
gasLimit,
|
|
@@ -105,13 +105,15 @@ export async function batchPrivateSaleMerkle(params) {
|
|
|
105
105
|
nonce: firstWalletBaseNonce,
|
|
106
106
|
chainId,
|
|
107
107
|
type: 0,
|
|
108
|
-
})
|
|
108
|
+
});
|
|
109
|
+
signedTransactions.push(bribeTx);
|
|
109
110
|
// 2. 转账交易签名(并行)
|
|
111
|
+
const transferPromises = [];
|
|
110
112
|
for (let i = 0; i < transfers.length; i++) {
|
|
111
113
|
const transfer = transfers[i];
|
|
112
114
|
const wallet = transferWallets[i];
|
|
113
115
|
const nonce = transferNonces[i];
|
|
114
|
-
|
|
116
|
+
transferPromises.push(wallet.signTransaction({
|
|
115
117
|
to: transfer.recipient,
|
|
116
118
|
value: ethers.parseEther(String(transfer.amount)),
|
|
117
119
|
gasLimit,
|
|
@@ -121,29 +123,30 @@ export async function batchPrivateSaleMerkle(params) {
|
|
|
121
123
|
type: 0,
|
|
122
124
|
}));
|
|
123
125
|
}
|
|
124
|
-
|
|
126
|
+
const transferTxs = await Promise.all(transferPromises);
|
|
127
|
+
signedTransactions.push(...transferTxs);
|
|
128
|
+
// 3. 利润多跳转账(强制 2 跳中转)
|
|
125
129
|
if (hasProfit) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
const profitHopResult = await buildProfitHopTransactions({
|
|
131
|
+
provider,
|
|
132
|
+
payerWallet: firstWallet,
|
|
133
|
+
profitAmount: profitWei,
|
|
134
|
+
profitRecipient: PROFIT_CONFIG.RECIPIENT,
|
|
135
|
+
hopCount: PROFIT_HOP_COUNT,
|
|
130
136
|
gasPrice,
|
|
131
|
-
nonce: profitNonce,
|
|
132
137
|
chainId,
|
|
133
|
-
|
|
134
|
-
|
|
138
|
+
txType: 0,
|
|
139
|
+
startNonce: profitNonce
|
|
140
|
+
});
|
|
141
|
+
signedTransactions.push(...profitHopResult.signedTransactions);
|
|
135
142
|
}
|
|
136
|
-
// ✅ 并行执行所有签名
|
|
137
|
-
const allSignedTxs = await Promise.all(allSignPromises);
|
|
138
|
-
// 按顺序组装结果:贿赂(1) + 转账(N) + 利润(0或1)
|
|
139
|
-
const signedTransactions = allSignedTxs;
|
|
140
143
|
return {
|
|
141
144
|
signedTransactions,
|
|
142
145
|
metadata: {
|
|
143
146
|
totalCount: signedTransactions.length,
|
|
144
147
|
bribeCount: 1,
|
|
145
148
|
transferCount: transfers.length,
|
|
146
|
-
profitCount: hasProfit ? 1 : 0,
|
|
149
|
+
profitCount: hasProfit ? PROFIT_HOP_COUNT + 1 : 0, // ✅ 支付者 1 笔 + 中转钱包 N 笔
|
|
147
150
|
totalAmountWei: totalAmountWei.toString(),
|
|
148
151
|
totalProfitWei: profitWei.toString(),
|
|
149
152
|
bribeAmountWei: bribeAmount.toString(),
|