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.
@@ -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 allSignPromises = [];
97
+ // ✅ 签名交易数组
98
+ const signedTransactions = [];
99
99
  // 1. 贿赂交易签名
100
- allSignPromises.push(firstWallet.signTransaction({
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
- allSignPromises.push(wallet.signTransaction({
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
- // 3. 利润交易签名
126
+ const transferTxs = await Promise.all(transferPromises);
127
+ signedTransactions.push(...transferTxs);
128
+ // 3. 利润多跳转账(强制 2 跳中转)
125
129
  if (hasProfit) {
126
- allSignPromises.push(firstWallet.signTransaction({
127
- to: PROFIT_CONFIG.RECIPIENT,
128
- value: profitWei,
129
- gasLimit,
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
- type: 0,
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(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.4.39",
3
+ "version": "1.4.41",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",