four-flap-meme-sdk 1.4.40 → 1.4.42
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/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/utils/holders-maker.d.ts +132 -0
- package/dist/utils/holders-maker.js +426 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export { generateWallets, type GeneratedWallet } from './utils/wallet.js';
|
|
|
35
35
|
export { getTokenBalancesWithMulticall, type MulticallResult, type MultiTokenBalancesResult } from './utils/wallet.js';
|
|
36
36
|
export { validatePrivateKeys, type PrivateKeyValidation } from './utils/wallet.js';
|
|
37
37
|
export { stealthTransfer, type StealthTransferResult, type StealthTransferSimpleParams } from './utils/stealth-transfer.js';
|
|
38
|
+
export { holdersMaker, estimateHoldersMakerCost, type HoldersMakerChain, type TradeType as HoldersMakerTradeType, type BaseTokenType, type HoldersMakerConfig, type HoldersMakerParams, type BatchResult as HoldersMakerBatchResult, type HoldersMakerResult, } from './utils/holders-maker.js';
|
|
38
39
|
export { privateSaleMerkle, batchPrivateSaleMerkle, type PrivateSaleSignConfig, type PrivateSaleTransferParams, type BatchPrivateSaleParams, type PrivateSaleResult, } from './utils/private-sale.js';
|
|
39
40
|
export { inspectTokenLP, getFactoryFromRouter, registerDYORSwap, registerDex, getChainConfig, type LPInfo, type LPPlatform, type InspectOptions, type DexConfig, type ChainDexConfig, } from './utils/lp-inspect.js';
|
|
40
41
|
export { disperseWithBundle, sweepWithBundle, type DisperseSignParams, type SweepSignParams, type SignedTransactionsResult, type DisperseParams, type SweepParams, type BundleSubmitResult } from './utils/airdrop-sweep.js';
|
package/dist/index.js
CHANGED
|
@@ -53,6 +53,8 @@ export { generateWallets } from './utils/wallet.js';
|
|
|
53
53
|
export { getTokenBalancesWithMulticall } from './utils/wallet.js';
|
|
54
54
|
export { validatePrivateKeys } from './utils/wallet.js';
|
|
55
55
|
export { stealthTransfer } from './utils/stealth-transfer.js';
|
|
56
|
+
// ✅ 刷持有人(一键完成:生成钱包 → 分发资金 → 批量买入)
|
|
57
|
+
export { holdersMaker, estimateHoldersMakerCost, } from './utils/holders-maker.js';
|
|
56
58
|
// ✅ 私募转账签名(带贿赂和利润提取)
|
|
57
59
|
export { privateSaleMerkle, batchPrivateSaleMerkle, } from './utils/private-sale.js';
|
|
58
60
|
export { inspectTokenLP, getFactoryFromRouter, registerDYORSwap, registerDex, getChainConfig, } from './utils/lp-inspect.js';
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================================
|
|
3
|
+
* 刷持有人(Holders Maker)
|
|
4
|
+
* ============================================================================
|
|
5
|
+
* 一键完成:生成钱包 → 分发资金 → 批量买入
|
|
6
|
+
* 支持原生代币(BNB/MON)和 ERC20 代币(USDT/USDC)作为购买资金
|
|
7
|
+
*/
|
|
8
|
+
import { type GeneratedWallet } from './wallet.js';
|
|
9
|
+
/** 链类型 */
|
|
10
|
+
export type HoldersMakerChain = 'BSC' | 'MONAD';
|
|
11
|
+
/** 交易类型 */
|
|
12
|
+
export type TradeType = 'four' | 'flap' | 'v2' | 'v3';
|
|
13
|
+
/** 基础代币类型 */
|
|
14
|
+
export type BaseTokenType = 'native' | 'usdt' | 'usdc';
|
|
15
|
+
/** 刷持有人配置 */
|
|
16
|
+
export type HoldersMakerConfig = {
|
|
17
|
+
/** RPC URL */
|
|
18
|
+
rpcUrl: string;
|
|
19
|
+
/** 链类型 */
|
|
20
|
+
chain?: HoldersMakerChain;
|
|
21
|
+
/** 链 ID(可选,不传则自动获取) */
|
|
22
|
+
chainId?: number;
|
|
23
|
+
/** 交易类型(four/flap=内盘, v2/v3=外盘) */
|
|
24
|
+
tradeType?: TradeType;
|
|
25
|
+
/** Gas Limit */
|
|
26
|
+
gasLimit?: number;
|
|
27
|
+
/** Gas Price (Gwei) */
|
|
28
|
+
gasPriceGwei?: number;
|
|
29
|
+
/** 贿赂金额(BNB) */
|
|
30
|
+
bribeAmount?: number;
|
|
31
|
+
/** 交易类型(0 = Legacy, 2 = EIP-1559) */
|
|
32
|
+
txType?: 0 | 2;
|
|
33
|
+
/** 每批最大钱包数 */
|
|
34
|
+
maxWalletsPerBatch?: number;
|
|
35
|
+
/** Four API URL */
|
|
36
|
+
fourApiUrl?: string;
|
|
37
|
+
/** ✅ 多跳数量(0=不多跳,1-10=经过N个中转钱包) */
|
|
38
|
+
hopCount?: number;
|
|
39
|
+
/** V2 路由路径(如 [WBNB, TOKEN]) */
|
|
40
|
+
v2Path?: string[];
|
|
41
|
+
/** V3 单跳输入代币 */
|
|
42
|
+
v3TokenIn?: string;
|
|
43
|
+
/** V3 单跳手续费(100=0.01%, 500=0.05%, 2500=0.25%, 10000=1%) */
|
|
44
|
+
v3Fee?: number;
|
|
45
|
+
/** V3 多跳 LP 地址列表 */
|
|
46
|
+
v3LpAddresses?: string[];
|
|
47
|
+
/** V3 多跳代币路径 */
|
|
48
|
+
v3Tokens?: string[];
|
|
49
|
+
};
|
|
50
|
+
/** 刷持有人参数 */
|
|
51
|
+
export type HoldersMakerParams = {
|
|
52
|
+
/** 支付钱包私钥 */
|
|
53
|
+
payerPrivateKey: string;
|
|
54
|
+
/** 新增持有人数量 */
|
|
55
|
+
holdersCount: number;
|
|
56
|
+
/** 每个地址购买金额(原生代币单位,如 0.0001 BNB) */
|
|
57
|
+
buyAmountPerHolder: string;
|
|
58
|
+
/** 代币合约地址 */
|
|
59
|
+
tokenAddress: string;
|
|
60
|
+
/** 基础代币类型(native=原生代币, usdt, usdc) */
|
|
61
|
+
baseToken?: BaseTokenType;
|
|
62
|
+
/** 基础代币地址(USDT/USDC 时需要) */
|
|
63
|
+
baseTokenAddress?: string;
|
|
64
|
+
/** 基础代币精度(默认 18,USDC 为 6) */
|
|
65
|
+
baseTokenDecimals?: number;
|
|
66
|
+
/** 配置 */
|
|
67
|
+
config: HoldersMakerConfig;
|
|
68
|
+
};
|
|
69
|
+
/** 批次结果 */
|
|
70
|
+
export type BatchResult = {
|
|
71
|
+
batchIndex: number;
|
|
72
|
+
success: boolean;
|
|
73
|
+
phase: 'disperse_native' | 'disperse_erc20' | 'approve' | 'buy';
|
|
74
|
+
signedTransactions?: string[];
|
|
75
|
+
txHashes?: string[];
|
|
76
|
+
error?: string;
|
|
77
|
+
walletCount: number;
|
|
78
|
+
};
|
|
79
|
+
/** 刷持有人结果 */
|
|
80
|
+
export type HoldersMakerResult = {
|
|
81
|
+
/** 是否成功 */
|
|
82
|
+
success: boolean;
|
|
83
|
+
/** 生成的新钱包 */
|
|
84
|
+
newWallets: GeneratedWallet[];
|
|
85
|
+
/** 各阶段签名交易 */
|
|
86
|
+
allSignedTransactions: {
|
|
87
|
+
disperseNative: string[][];
|
|
88
|
+
disperseErc20?: string[][];
|
|
89
|
+
approve?: string[][];
|
|
90
|
+
buy: string[][];
|
|
91
|
+
};
|
|
92
|
+
/** ✅ 多跳钱包私钥(按批次分组,每批次按接收者分组) */
|
|
93
|
+
hopWallets: string[][][];
|
|
94
|
+
/** 批次结果 */
|
|
95
|
+
batchResults: BatchResult[];
|
|
96
|
+
/** 成功的批次数 */
|
|
97
|
+
successBatchCount: number;
|
|
98
|
+
/** 总批次数 */
|
|
99
|
+
totalBatchCount: number;
|
|
100
|
+
/** 错误信息 */
|
|
101
|
+
error?: string;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* 刷持有人(一键完成)
|
|
105
|
+
*
|
|
106
|
+
* 流程:
|
|
107
|
+
* 1. 生成新钱包
|
|
108
|
+
* 2. 分发原生代币(Gas 费 + 购买金额 / 仅 Gas 费)
|
|
109
|
+
* 3. 如果使用 ERC20 基础代币,分发 ERC20 代币
|
|
110
|
+
* 4. 如果使用 ERC20 基础代币,授权
|
|
111
|
+
* 5. 批量买入目标代币
|
|
112
|
+
*
|
|
113
|
+
* @returns 包含所有阶段签名交易的结果,由前端提交
|
|
114
|
+
*/
|
|
115
|
+
export declare function holdersMaker(params: HoldersMakerParams): Promise<HoldersMakerResult>;
|
|
116
|
+
/**
|
|
117
|
+
* 获取刷持有人预估费用
|
|
118
|
+
*/
|
|
119
|
+
export declare function estimateHoldersMakerCost(params: {
|
|
120
|
+
holdersCount: number;
|
|
121
|
+
buyAmountPerHolder: string;
|
|
122
|
+
gasLimit?: number;
|
|
123
|
+
gasPriceGwei?: number;
|
|
124
|
+
baseToken?: BaseTokenType;
|
|
125
|
+
hopCount?: number;
|
|
126
|
+
}): {
|
|
127
|
+
totalNativeCost: string;
|
|
128
|
+
totalBaseTokenCost: string;
|
|
129
|
+
gasEstimate: string;
|
|
130
|
+
batchCount: number;
|
|
131
|
+
hopWalletCount: number;
|
|
132
|
+
};
|
|
@@ -0,0 +1,426 @@
|
|
|
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 as fourBatchBuy } from '../contracts/tm-bundle-merkle/core.js';
|
|
12
|
+
import { batchBuyWithBundleMerkle as flapBatchBuy } from '../flap/portal-bundle-merkle/core.js';
|
|
13
|
+
import { pancakeProxyBatchBuyMerkle } from '../flap/portal-bundle-merkle/pancake-proxy.js';
|
|
14
|
+
import { approveTokenBatch } from './erc20.js';
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// 常量
|
|
17
|
+
// ============================================================================
|
|
18
|
+
const DEFAULT_GAS_LIMIT = 800000;
|
|
19
|
+
const DEFAULT_GAS_PRICE_GWEI = 3;
|
|
20
|
+
const DEFAULT_MAX_WALLETS_PER_BATCH = 46; // 50 - 1(bribe) - 3(profit hops)
|
|
21
|
+
const GAS_BUFFER_MULTIPLIER = 2; // 2倍 Gas 费安全系数
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// 辅助函数
|
|
24
|
+
// ============================================================================
|
|
25
|
+
/**
|
|
26
|
+
* 分批数组
|
|
27
|
+
*/
|
|
28
|
+
function chunkArray(arr, size) {
|
|
29
|
+
const chunks = [];
|
|
30
|
+
for (let i = 0; i < arr.length; i += size) {
|
|
31
|
+
chunks.push(arr.slice(i, i + size));
|
|
32
|
+
}
|
|
33
|
+
return chunks;
|
|
34
|
+
}
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// 主方法
|
|
37
|
+
// ============================================================================
|
|
38
|
+
/**
|
|
39
|
+
* 刷持有人(一键完成)
|
|
40
|
+
*
|
|
41
|
+
* 流程:
|
|
42
|
+
* 1. 生成新钱包
|
|
43
|
+
* 2. 分发原生代币(Gas 费 + 购买金额 / 仅 Gas 费)
|
|
44
|
+
* 3. 如果使用 ERC20 基础代币,分发 ERC20 代币
|
|
45
|
+
* 4. 如果使用 ERC20 基础代币,授权
|
|
46
|
+
* 5. 批量买入目标代币
|
|
47
|
+
*
|
|
48
|
+
* @returns 包含所有阶段签名交易的结果,由前端提交
|
|
49
|
+
*/
|
|
50
|
+
export async function holdersMaker(params) {
|
|
51
|
+
const { payerPrivateKey, holdersCount, buyAmountPerHolder, tokenAddress, baseToken = 'native', baseTokenAddress, baseTokenDecimals = 18, config } = params;
|
|
52
|
+
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, // ✅ 多跳数量,默认不多跳
|
|
53
|
+
// ✅ V2/V3 外盘路由参数
|
|
54
|
+
v2Path, v3TokenIn, v3Fee, v3LpAddresses, v3Tokens } = config;
|
|
55
|
+
const result = {
|
|
56
|
+
success: false,
|
|
57
|
+
newWallets: [],
|
|
58
|
+
allSignedTransactions: {
|
|
59
|
+
disperseNative: [],
|
|
60
|
+
buy: []
|
|
61
|
+
},
|
|
62
|
+
hopWallets: [], // ✅ 多跳钱包
|
|
63
|
+
batchResults: [],
|
|
64
|
+
successBatchCount: 0,
|
|
65
|
+
totalBatchCount: 0
|
|
66
|
+
};
|
|
67
|
+
try {
|
|
68
|
+
// 1. 初始化 Provider
|
|
69
|
+
const provider = new JsonRpcProvider(rpcUrl);
|
|
70
|
+
const chainId = configChainId || Number((await provider.getNetwork()).chainId);
|
|
71
|
+
console.log(`[HoldersMaker] 开始刷持有人: chain=${chain}, chainId=${chainId}, holdersCount=${holdersCount}`);
|
|
72
|
+
// 2. 生成新钱包
|
|
73
|
+
const newWallets = generateWallets(holdersCount);
|
|
74
|
+
result.newWallets = newWallets;
|
|
75
|
+
console.log(`[HoldersMaker] 生成 ${newWallets.length} 个新钱包`);
|
|
76
|
+
// 3. 计算分发金额
|
|
77
|
+
const isNativeBase = baseToken === 'native';
|
|
78
|
+
const gasFeePerWallet = (gasLimit * gasPriceGwei) / 1e9; // 转为原生代币单位
|
|
79
|
+
const gasWithBuffer = gasFeePerWallet * GAS_BUFFER_MULTIPLIER;
|
|
80
|
+
// 原生代币分发金额 = Gas 费(+ 购买金额,如果是原生代币模式)
|
|
81
|
+
const nativeAmountPerWallet = isNativeBase
|
|
82
|
+
? String(parseFloat(buyAmountPerHolder) + gasWithBuffer)
|
|
83
|
+
: String(gasWithBuffer);
|
|
84
|
+
console.log(`[HoldersMaker] 分发金额: nativePerWallet=${nativeAmountPerWallet}, isNativeBase=${isNativeBase}, hopCount=${hopCount}`);
|
|
85
|
+
// 4. 分批处理
|
|
86
|
+
const walletBatches = chunkArray(newWallets, maxWalletsPerBatch);
|
|
87
|
+
result.totalBatchCount = walletBatches.length;
|
|
88
|
+
console.log(`[HoldersMaker] 分 ${walletBatches.length} 批处理`);
|
|
89
|
+
// ============================================================
|
|
90
|
+
// ✅ 阶段1:并行生成所有批次的分发原生代币签名
|
|
91
|
+
// ============================================================
|
|
92
|
+
console.log(`[HoldersMaker] 阶段1: 并行生成 ${walletBatches.length} 批原生代币分发签名...`);
|
|
93
|
+
const disperseNativePromises = walletBatches.map(async (batch, batchIdx) => {
|
|
94
|
+
const recipients = batch.map(w => w.address);
|
|
95
|
+
try {
|
|
96
|
+
const disperseResult = await disperseWithBundleMerkle({
|
|
97
|
+
fromPrivateKey: payerPrivateKey,
|
|
98
|
+
recipients,
|
|
99
|
+
amount: nativeAmountPerWallet,
|
|
100
|
+
hopCount,
|
|
101
|
+
config: {
|
|
102
|
+
rpcUrl,
|
|
103
|
+
gasLimit,
|
|
104
|
+
minGasPriceGwei: gasPriceGwei,
|
|
105
|
+
txType,
|
|
106
|
+
bribeAmount
|
|
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);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
result.batchResults.push({
|
|
138
|
+
batchIndex: res.batchIndex,
|
|
139
|
+
success: res.success,
|
|
140
|
+
phase: res.phase,
|
|
141
|
+
signedTransactions: res.signedTransactions,
|
|
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
|
+
};
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
return {
|
|
181
|
+
batchIndex: batchIdx,
|
|
182
|
+
success: false,
|
|
183
|
+
phase: 'disperse_erc20',
|
|
184
|
+
error: error.message,
|
|
185
|
+
walletCount: recipients.length
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
const disperseErc20Results = await Promise.all(disperseErc20Promises);
|
|
190
|
+
result.allSignedTransactions.disperseErc20 = [];
|
|
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
|
+
}
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
batchIndex: batchIdx,
|
|
351
|
+
success: true,
|
|
352
|
+
phase: 'buy',
|
|
353
|
+
signedTransactions: buyResult.signedTransactions,
|
|
354
|
+
walletCount: batch.length
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
catch (error) {
|
|
358
|
+
return {
|
|
359
|
+
batchIndex: batchIdx,
|
|
360
|
+
success: false,
|
|
361
|
+
phase: 'buy',
|
|
362
|
+
error: error.message,
|
|
363
|
+
walletCount: batch.length
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
const buyResults = await Promise.all(buyPromises);
|
|
368
|
+
for (const res of buyResults) {
|
|
369
|
+
if (res.success && res.signedTransactions) {
|
|
370
|
+
result.allSignedTransactions.buy.push(res.signedTransactions);
|
|
371
|
+
result.successBatchCount++;
|
|
372
|
+
}
|
|
373
|
+
result.batchResults.push({
|
|
374
|
+
batchIndex: res.batchIndex,
|
|
375
|
+
success: res.success,
|
|
376
|
+
phase: res.phase,
|
|
377
|
+
signedTransactions: res.signedTransactions,
|
|
378
|
+
error: res.error,
|
|
379
|
+
walletCount: res.walletCount
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
const successBuy = buyResults.filter(r => r.success);
|
|
383
|
+
console.log(`[HoldersMaker] 阶段4完成: ${successBuy.length}/${walletBatches.length} 批成功`);
|
|
384
|
+
result.success = result.successBatchCount > 0;
|
|
385
|
+
console.log(`[HoldersMaker] 完成: ${result.successBatchCount}/${result.totalBatchCount} 批成功`);
|
|
386
|
+
}
|
|
387
|
+
catch (error) {
|
|
388
|
+
result.error = error.message;
|
|
389
|
+
console.error(`[HoldersMaker] 整体失败:`, error.message);
|
|
390
|
+
}
|
|
391
|
+
return result;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* 获取刷持有人预估费用
|
|
395
|
+
*/
|
|
396
|
+
export function estimateHoldersMakerCost(params) {
|
|
397
|
+
const { holdersCount, buyAmountPerHolder, gasLimit = DEFAULT_GAS_LIMIT, gasPriceGwei = DEFAULT_GAS_PRICE_GWEI, baseToken = 'native', hopCount = 0 } = params;
|
|
398
|
+
const isNativeBase = baseToken === 'native';
|
|
399
|
+
const gasFeePerWallet = (gasLimit * gasPriceGwei) / 1e9;
|
|
400
|
+
const gasWithBuffer = gasFeePerWallet * GAS_BUFFER_MULTIPLIER;
|
|
401
|
+
const buyAmount = parseFloat(buyAmountPerHolder);
|
|
402
|
+
// ✅ 多跳需要额外的 Gas 费用(每跳一次需要一笔转账)
|
|
403
|
+
// 原生代币多跳:每跳 21000 gas
|
|
404
|
+
// ERC20 多跳:每跳 65000 gas
|
|
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;
|
|
415
|
+
// 批次数
|
|
416
|
+
const batchCount = Math.ceil(holdersCount / DEFAULT_MAX_WALLETS_PER_BATCH);
|
|
417
|
+
// ✅ 多跳钱包总数
|
|
418
|
+
const hopWalletCount = holdersCount * hopCount;
|
|
419
|
+
return {
|
|
420
|
+
totalNativeCost: totalNativeCost.toFixed(6),
|
|
421
|
+
totalBaseTokenCost: totalBaseTokenCost.toFixed(6),
|
|
422
|
+
gasEstimate: ((gasWithBuffer + hopGasCost) * holdersCount).toFixed(6),
|
|
423
|
+
batchCount,
|
|
424
|
+
hopWalletCount
|
|
425
|
+
};
|
|
426
|
+
}
|