four-flap-meme-sdk 1.4.55 → 1.4.56

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.
@@ -34,6 +34,8 @@ export type HoldersMakerConfig = {
34
34
  maxWalletsPerBatch?: number;
35
35
  /** Four API URL */
36
36
  fourApiUrl?: string;
37
+ /** 分发多跳数(0=直接转账,1=1跳,2=2跳...) */
38
+ disperseHopCount?: number;
37
39
  /** V2 路由路径(如 [WBNB, TOKEN]) */
38
40
  v2Path?: string[];
39
41
  /** V3 单跳输入代币 */
@@ -79,6 +81,8 @@ export type HoldersMakerResult = {
79
81
  success: boolean;
80
82
  /** 生成的新钱包 */
81
83
  newWallets: GeneratedWallet[];
84
+ /** 分发多跳中间钱包(可选,用于导出) */
85
+ disperseHopWallets?: GeneratedWallet[];
82
86
  /** 签名交易(每批一个数组) */
83
87
  signedTransactions: string[][];
84
88
  /** 批次结果 */
@@ -31,26 +31,46 @@ const ERC20_APPROVE_GAS_LIMIT = 50000n;
31
31
  const DEFAULT_GAS_LIMIT = 800000;
32
32
  const DEFAULT_GAS_PRICE_GWEI = 3;
33
33
  const GAS_BUFFER_MULTIPLIER = 2; // 2倍 Gas 费安全系数
34
- // Bundle 限制计算(原生代币模式):
35
- // - 贿赂 1 笔
36
- // - 利润多跳 PROFIT_HOP_COUNT + 1 笔(payer → hop1 → hop2 → recipient)
37
- // - 分发 N 笔
38
- // - 买入 N
39
- // 1 + (PROFIT_HOP_COUNT + 1) + 2N ≤ 50
40
- // 2N ≤ 50 - 2 - PROFIT_HOP_COUNT
41
- // N ≤ (48 - PROFIT_HOP_COUNT) / 2
42
- const DEFAULT_MAX_WALLETS_PER_BATCH_NATIVE = Math.floor((48 - PROFIT_HOP_COUNT) / 2);
43
- // Bundle 限制计算(ERC20 模式):
44
- // - 贿赂 1 笔
45
- // - 利润多跳 PROFIT_HOP_COUNT + 1 笔
46
- // - 分发 BNB N
47
- // - 分发 ERC20 N 笔
48
- // - 授权 N 笔
49
- // - 买入 N 笔
50
- // 1 + (PROFIT_HOP_COUNT + 1) + 4N 50
51
- // 4N ≤ 50 - 2 - PROFIT_HOP_COUNT
52
- // N ≤ (48 - PROFIT_HOP_COUNT) / 4
53
- const DEFAULT_MAX_WALLETS_PER_BATCH_ERC20 = Math.floor((48 - PROFIT_HOP_COUNT) / 4);
34
+ const NATIVE_TRANSFER_GAS_LIMIT = 21055n; // 原生代币转账 Gas Limit
35
+ /**
36
+ * 动态计算每批最大钱包数
37
+ *
38
+ * Bundle 限制 = 50 笔交易
39
+ * 固定开销 = 贿赂 1 + 利润多跳 (PROFIT_HOP_COUNT + 1)
40
+ *
41
+ * 原生模式(无分发多跳):
42
+ * 每钱包开销 = 分发 1 + 买入 1 = 2
43
+ * N (50 - 固定开销) / 2
44
+ *
45
+ * 原生模式(有分发多跳 H):
46
+ * 每钱包开销 = 分发 (H+1) + 买入 1 = H+2
47
+ * N ≤ (50 - 固定开销) / (H+2)
48
+ *
49
+ * ERC20 模式(无分发多跳):
50
+ * 每钱包开销 = 分发BNB 1 + 分发ERC20 1 + 授权 1 + 买入 1 = 4
51
+ * N(50 - 固定开销) / 4
52
+ *
53
+ * ERC20 模式(有分发多跳 H):
54
+ * 每钱包开销 = 分发BNB (H+1) + 分发ERC20 (H+1) + 授权 1 + 买入 1 = 2H+4
55
+ * N ≤ (50 - 固定开销) / (2H+4)
56
+ */
57
+ function calculateMaxWalletsPerBatch(isERC20Mode, disperseHopCount) {
58
+ const fixedOverhead = 1 + PROFIT_HOP_COUNT + 1; // 贿赂 + 利润多跳
59
+ const maxTxs = 50 - fixedOverhead;
60
+ if (isERC20Mode) {
61
+ // ERC20: 分发BNB (H+1) + 分发ERC20 (H+1) + 授权 1 + 买入 1
62
+ const perWalletTxs = 2 * (disperseHopCount + 1) + 2;
63
+ return Math.floor(maxTxs / perWalletTxs);
64
+ }
65
+ else {
66
+ // 原生: 分发 (H+1) + 买入 1
67
+ const perWalletTxs = disperseHopCount + 2;
68
+ return Math.floor(maxTxs / perWalletTxs);
69
+ }
70
+ }
71
+ // 默认值(无分发多跳)
72
+ const DEFAULT_MAX_WALLETS_PER_BATCH_NATIVE = calculateMaxWalletsPerBatch(false, 0);
73
+ const DEFAULT_MAX_WALLETS_PER_BATCH_ERC20 = calculateMaxWalletsPerBatch(true, 0);
54
74
  // ============================================================================
55
75
  // 辅助函数
56
76
  // ============================================================================
@@ -64,6 +84,127 @@ function chunkArray(arr, size) {
64
84
  }
65
85
  return chunks;
66
86
  }
87
+ /**
88
+ * 生成分发多跳路径
89
+ * @param targetWallets 目标钱包地址列表
90
+ * @param hopCount 多跳数量(0=直接转账,1=1个中间钱包,2=2个中间钱包...)
91
+ * @param provider Provider 实例
92
+ * @returns 每个目标钱包的多跳路径
93
+ */
94
+ function generateDisperseHopPaths(targetWallets, hopCount, provider) {
95
+ if (hopCount <= 0) {
96
+ // 无多跳,直接返回空路径
97
+ return targetWallets.map(w => ({
98
+ targetAddress: w.address,
99
+ hopWallets: [],
100
+ hopWalletsInfo: []
101
+ }));
102
+ }
103
+ // 为每个目标钱包生成中间钱包
104
+ return targetWallets.map(targetWallet => {
105
+ const hopWalletsInfo = generateWallets(hopCount);
106
+ const hopWallets = hopWalletsInfo.map(w => new Wallet(w.privateKey, provider));
107
+ return {
108
+ targetAddress: targetWallet.address,
109
+ hopWallets,
110
+ hopWalletsInfo
111
+ };
112
+ });
113
+ }
114
+ /**
115
+ * 构建分发多跳交易链(原生代币)
116
+ *
117
+ * 路径: payer → hop1 → hop2 → ... → target
118
+ *
119
+ * @param isERC20Mode 是否为 ERC20 模式(中间钱包需要预留 ERC20 转账 gas)
120
+ * @returns 签名交易数组
121
+ */
122
+ async function buildDisperseHopChainNative(payer, path, finalAmount, gasPrice, chainId, txType, payerNonce, isERC20Mode = false) {
123
+ const signedTxs = [];
124
+ const hopCount = path.hopWallets.length;
125
+ if (hopCount === 0) {
126
+ // 无多跳,直接转账
127
+ const tx = await buildNativeTransferTx(payer, path.targetAddress, finalAmount, payerNonce, gasPrice, chainId, txType);
128
+ signedTxs.push(tx);
129
+ return { signedTxs, payerNoncesUsed: 1 };
130
+ }
131
+ // 计算每一跳需要转出的金额(需要预留后续跳的 gas 费)
132
+ const hopGasCost = NATIVE_TRANSFER_GAS_LIMIT * gasPrice;
133
+ // ERC20 模式下,中间钱包还需要预留 ERC20 转账的 gas
134
+ const erc20TransferGasCost = isERC20Mode ? ERC20_TRANSFER_GAS_LIMIT * gasPrice : 0n;
135
+ // 从后往前计算每一跳需要的金额
136
+ // 原生模式:
137
+ // 最后一跳(hopN → target)需要: finalAmount + hopGasCost
138
+ // 倒数第二跳需要: finalAmount + 2 * hopGasCost
139
+ // ERC20 模式(中间钱包还需要预留 ERC20 转账 gas):
140
+ // 每个中间钱包额外需要: erc20TransferGasCost
141
+ const amountsPerHop = [];
142
+ for (let i = 0; i < hopCount; i++) {
143
+ const remainingHops = hopCount - i;
144
+ // 中间钱包需要的 BNB = 最终金额 + 后续 BNB 转账 gas + (ERC20模式下) 自己的 ERC20 转账 gas
145
+ const amount = finalAmount
146
+ + hopGasCost * BigInt(remainingHops)
147
+ + (isERC20Mode ? erc20TransferGasCost * BigInt(remainingHops) : 0n);
148
+ amountsPerHop.push(amount);
149
+ }
150
+ // 构建交易链
151
+ // 1. payer → hop1 (使用 payer nonce)
152
+ const firstTx = await buildNativeTransferTx(payer, path.hopWallets[0].address, amountsPerHop[0], payerNonce, gasPrice, chainId, txType);
153
+ signedTxs.push(firstTx);
154
+ // 2. hop1 → hop2 → ... → hopN → target (每个中间钱包 nonce=0)
155
+ for (let i = 0; i < hopCount; i++) {
156
+ const fromWallet = path.hopWallets[i];
157
+ const toAddress = i === hopCount - 1
158
+ ? path.targetAddress // 最后一跳到目标
159
+ : path.hopWallets[i + 1].address; // 中间跳到下一个 hop
160
+ // 最后一跳只需转最终金额,中间跳需要预留后续费用
161
+ const amount = i === hopCount - 1
162
+ ? finalAmount
163
+ : amountsPerHop[i + 1];
164
+ const tx = await buildNativeTransferTx(fromWallet, toAddress, amount, 0, // 中间钱包都是新生成的,nonce=0
165
+ gasPrice, chainId, txType);
166
+ signedTxs.push(tx);
167
+ }
168
+ return { signedTxs, payerNoncesUsed: 1 };
169
+ }
170
+ /**
171
+ * 构建分发多跳交易链(ERC20 代币)
172
+ *
173
+ * 路径: payer → hop1 → hop2 → ... → target
174
+ *
175
+ * 注意:ERC20 多跳需要配合 BNB 多跳使用,中间钱包需要先收到 BNB 作为 gas
176
+ * BNB 多跳先执行,确保中间钱包有 gas 费后再转发 ERC20
177
+ *
178
+ * 中间钱包的 nonce 安排:
179
+ * - nonce=0: BNB 转发(在 buildDisperseHopChainNative 中完成)
180
+ * - nonce=1: ERC20 转发(在本函数中完成)
181
+ */
182
+ async function buildDisperseHopChainERC20(payer, path, erc20Address, erc20Amount, gasPrice, chainId, txType, payerNonce) {
183
+ const signedTxs = [];
184
+ const hopCount = path.hopWallets.length;
185
+ if (hopCount === 0) {
186
+ // 无多跳,直接转账
187
+ const tx = await buildERC20TransferTx(payer, erc20Address, path.targetAddress, erc20Amount, payerNonce, gasPrice, chainId, txType);
188
+ signedTxs.push(tx);
189
+ return { signedTxs, payerNoncesUsed: 1 };
190
+ }
191
+ // 构建交易链
192
+ // payer 发送 ERC20 到 hop1
193
+ const firstTx = await buildERC20TransferTx(payer, erc20Address, path.hopWallets[0].address, erc20Amount, payerNonce, gasPrice, chainId, txType);
194
+ signedTxs.push(firstTx);
195
+ // 每个中间钱包转发 ERC20
196
+ // nonce=1(因为 nonce=0 已经用于 BNB 转发)
197
+ for (let i = 0; i < hopCount; i++) {
198
+ const fromWallet = path.hopWallets[i];
199
+ const toAddress = i === hopCount - 1
200
+ ? path.targetAddress
201
+ : path.hopWallets[i + 1].address;
202
+ const tx = await buildERC20TransferTx(fromWallet, erc20Address, toAddress, erc20Amount, 1, // ✅ nonce=1(nonce=0 用于 BNB 转发)
203
+ gasPrice, chainId, txType);
204
+ signedTxs.push(tx);
205
+ }
206
+ return { signedTxs, payerNoncesUsed: 1 };
207
+ }
67
208
  /**
68
209
  * 获取 ERC20 稳定币地址
69
210
  */
@@ -405,10 +546,12 @@ async function buildV3BuyTxWithERC20(wallet, tokenAddress, baseTokenAddress, buy
405
546
  */
406
547
  export async function holdersMaker(params) {
407
548
  const { payerPrivateKey, holdersCount, buyAmountPerHolder, tokenAddress, baseToken = 'native', baseTokenAddress, baseTokenDecimals = 18, config } = params;
408
- const { rpcUrl, chain = 'BSC', chainId: configChainId, tradeType = 'flap', gasLimit = DEFAULT_GAS_LIMIT, gasPriceGwei = DEFAULT_GAS_PRICE_GWEI, bribeAmount = 0.000001, txType = 0 } = config;
549
+ const { rpcUrl, chain = 'BSC', chainId: configChainId, tradeType = 'flap', gasLimit = DEFAULT_GAS_LIMIT, gasPriceGwei = DEFAULT_GAS_PRICE_GWEI, bribeAmount = 0.000001, txType = 0, disperseHopCount = 0 // ✅ 分发多跳数(默认0=直接转账)
550
+ } = config;
409
551
  const result = {
410
552
  success: false,
411
553
  newWallets: [],
554
+ disperseHopWallets: [], // ✅ 中间钱包列表
412
555
  signedTransactions: [],
413
556
  batchResults: [],
414
557
  successBatchCount: 0,
@@ -426,9 +569,10 @@ export async function holdersMaker(params) {
426
569
  result.error = `不支持的交易类型: ${tradeType},支持: ${supportedTradeTypes.join(', ')}`;
427
570
  return result;
428
571
  }
429
- // 根据模式选择每批最大钱包数
572
+ // ✅ 根据分发多跳数动态计算每批最大钱包数
430
573
  const maxWalletsPerBatch = config.maxWalletsPerBatch ||
431
- (isERC20Mode ? DEFAULT_MAX_WALLETS_PER_BATCH_ERC20 : DEFAULT_MAX_WALLETS_PER_BATCH_NATIVE);
574
+ calculateMaxWalletsPerBatch(isERC20Mode, disperseHopCount);
575
+ console.log(`[HoldersMaker] 分发多跳数: ${disperseHopCount}, 每批最大钱包数: ${maxWalletsPerBatch}`);
432
576
  try {
433
577
  // 1. 初始化
434
578
  const provider = new JsonRpcProvider(rpcUrl);
@@ -479,13 +623,27 @@ export async function holdersMaker(params) {
479
623
  const totalProfit = (totalBuyAmountForProfit * BigInt(profitRateBps)) / 10000n;
480
624
  const profitPerBatch = totalProfit / BigInt(walletBatches.length);
481
625
  console.log(`[HoldersMaker] 总利润: ${ethers.formatEther(totalProfit)} BNB`);
482
- // 6. 并行生成所有批次的签名
626
+ // 6. 生成分发多跳路径(如果启用)
627
+ let allDisperseHopWallets = [];
628
+ const disperseHopPaths = generateDisperseHopPaths(newWallets, disperseHopCount, provider);
629
+ if (disperseHopCount > 0) {
630
+ // 收集所有中间钱包信息用于导出
631
+ for (const path of disperseHopPaths) {
632
+ allDisperseHopWallets.push(...path.hopWalletsInfo);
633
+ }
634
+ result.disperseHopWallets = allDisperseHopWallets;
635
+ console.log(`[HoldersMaker] 分发多跳: ${disperseHopCount} 跳,共生成 ${allDisperseHopWallets.length} 个中间钱包`);
636
+ }
637
+ // 7. 并行生成所有批次的签名
483
638
  const batchPromises = walletBatches.map(async (batch, batchIdx) => {
484
639
  try {
485
640
  const signedTxs = [];
486
641
  // 计算这批需要的 payer nonce 数量
487
- // 原生模式: 贿赂 1 + 分发 N + 利润 (PROFIT_HOP_COUNT + 1)
488
- // ERC20模式: 贿赂 1 + 分发BNB N + 分发ERC20 N + 利润 (PROFIT_HOP_COUNT + 1)
642
+ // 原生模式(无多跳): 贿赂 1 + 分发 N + 利润 (PROFIT_HOP_COUNT + 1)
643
+ // 原生模式(有多跳H): 贿赂 1 + 分发首跳 N + 利润 (PROFIT_HOP_COUNT + 1)
644
+ // ERC20模式(无多跳): 贿赂 1 + 分发BNB N + 分发ERC20 N + 利润 (PROFIT_HOP_COUNT + 1)
645
+ // ERC20模式(有多跳H): 贿赂 1 + 分发BNB首跳 N + 分发ERC20首跳 N + 利润 (PROFIT_HOP_COUNT + 1)
646
+ // 注:多跳中间钱包的交易不消耗 payer nonce
489
647
  const payerNonceCount = isERC20Mode
490
648
  ? 1 + batch.length * 2 + PROFIT_HOP_COUNT + 1
491
649
  : 1 + batch.length + PROFIT_HOP_COUNT + 1;
@@ -494,16 +652,22 @@ export async function holdersMaker(params) {
494
652
  // (1) 贿赂交易
495
653
  const bribeTx = await buildNativeTransferTx(payer, BLOCKRAZOR_BUILDER_EOA, bribeAmountWei, payerNonces[payerNonceIdx++], gasPrice, chainId, txType);
496
654
  signedTxs.push(bribeTx);
497
- // (2) 分发原生代币(gas 费或买入资金)
498
- for (const newWallet of batch) {
499
- const transferTx = await buildNativeTransferTx(payer, newWallet.address, transferNativePerWallet, payerNonces[payerNonceIdx++], gasPrice, chainId, txType);
500
- signedTxs.push(transferTx);
655
+ // 获取当前批次对应的多跳路径
656
+ const batchStartIdx = batchIdx * maxWalletsPerBatch;
657
+ const batchPaths = disperseHopPaths.slice(batchStartIdx, batchStartIdx + batch.length);
658
+ // (2) 分发原生代币(支持多跳)
659
+ for (let i = 0; i < batch.length; i++) {
660
+ const path = batchPaths[i];
661
+ const { signedTxs: hopTxs } = await buildDisperseHopChainNative(payer, path, transferNativePerWallet, gasPrice, chainId, txType, payerNonces[payerNonceIdx++], isERC20Mode // ✅ ERC20 模式下,中间钱包需要预留 ERC20 转账 gas
662
+ );
663
+ signedTxs.push(...hopTxs);
501
664
  }
502
- // (3) ERC20 模式:分发 ERC20 代币
665
+ // (3) ERC20 模式:分发 ERC20 代币(支持多跳)
503
666
  if (isERC20Mode && erc20TokenAddress) {
504
- for (const newWallet of batch) {
505
- const erc20TransferTx = await buildERC20TransferTx(payer, erc20TokenAddress, newWallet.address, buyAmountWei, payerNonces[payerNonceIdx++], gasPrice, chainId, txType);
506
- signedTxs.push(erc20TransferTx);
667
+ for (let i = 0; i < batch.length; i++) {
668
+ const path = batchPaths[i];
669
+ const { signedTxs: hopTxs } = await buildDisperseHopChainERC20(payer, path, erc20TokenAddress, buyAmountWei, gasPrice, chainId, txType, payerNonces[payerNonceIdx++]);
670
+ signedTxs.push(...hopTxs);
507
671
  }
508
672
  }
509
673
  // (4) ERC20 模式:授权交易(新钱包 nonce=0)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.4.55",
3
+ "version": "1.4.56",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",