four-flap-meme-sdk
Version:
SDK for Flap bonding curve and four.meme TokenManager
269 lines (268 loc) • 11.9 kB
JavaScript
/**
* ============================================================================
* 刷持有人(Holders Maker)
* ============================================================================
* 一键完成:生成钱包 → 分发资金 → 批量买入(同一个 bundle 完成)
* 支持原生代币(BNB/MON)和 ERC20 代币(USDT/USDC)作为购买资金
*/
import { ethers, JsonRpcProvider, Wallet } from 'ethers';
import { generateWallets } from './wallet.js';
import { NonceManager, getOptimizedGasPrice, buildProfitHopTransactions, PROFIT_HOP_COUNT } from './bundle-helpers.js';
import { BLOCKRAZOR_BUILDER_EOA, PROFIT_CONFIG } from './constants.js';
import { FLAP_PORTAL_ADDRESSES } from '../flap/constants.js';
// ============================================================================
// 常量
// ============================================================================
const DEFAULT_GAS_LIMIT = 800000;
const DEFAULT_GAS_PRICE_GWEI = 3;
const GAS_BUFFER_MULTIPLIER = 2; // 2倍 Gas 费安全系数
// Bundle 限制计算:
// - 贿赂 1 笔
// - 利润多跳 PROFIT_HOP_COUNT + 1 笔(payer → hop1 → hop2 → recipient)
// - 分发 N 笔
// - 买入 N 笔
// 1 + (PROFIT_HOP_COUNT + 1) + 2N ≤ 50
// 2N ≤ 50 - 2 - PROFIT_HOP_COUNT
// N ≤ (48 - PROFIT_HOP_COUNT) / 2
const DEFAULT_MAX_WALLETS_PER_BATCH = Math.floor((48 - PROFIT_HOP_COUNT) / 2);
// ============================================================================
// 辅助函数
// ============================================================================
/**
* 分批数组
*/
function chunkArray(arr, size) {
const chunks = [];
for (let i = 0; i < arr.length; i += size) {
chunks.push(arr.slice(i, i + size));
}
return chunks;
}
/**
* 构建原生代币转账交易
*/
async function buildNativeTransferTx(wallet, to, amount, nonce, gasPrice, chainId, txType) {
const tx = {
to,
value: amount,
nonce,
gasLimit: 21000n,
chainId
};
if (txType === 2) {
tx.maxFeePerGas = gasPrice;
tx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
tx.type = 2;
}
else {
tx.gasPrice = gasPrice;
tx.type = 0;
}
return await wallet.signTransaction(tx);
}
/**
* 构建 Flap 买入交易
* 使用 swapExactInput 方法
*/
async function buildFlapBuyTx(wallet, tokenAddress, buyAmount, nonce, gasPrice, gasLimit, chainId, txType, chain) {
const portalAddress = FLAP_PORTAL_ADDRESSES[chain];
// ✅ 使用正确的 Flap Portal ABI
const iface = new ethers.Interface([
'function swapExactInput((address inputToken,address outputToken,uint256 inputAmount,uint256 minOutputAmount,bytes permitData)) external payable returns (uint256)'
]);
// ✅ 构建 swapExactInput 参数
// inputToken = 0x0 表示原生代币(BNB)
// outputToken = tokenAddress(要买入的代币)
const data = iface.encodeFunctionData('swapExactInput', [{
inputToken: ethers.ZeroAddress, // 原生代币(BNB)
outputToken: tokenAddress, // 要买入的代币
inputAmount: buyAmount,
minOutputAmount: 0n, // 不设滑点保护
permitData: '0x'
}]);
const tx = {
to: portalAddress,
data,
value: buyAmount,
nonce,
gasLimit: BigInt(gasLimit),
chainId
};
if (txType === 2) {
tx.maxFeePerGas = gasPrice;
tx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
tx.type = 2;
}
else {
tx.gasPrice = gasPrice;
tx.type = 0;
}
return await wallet.signTransaction(tx);
}
// ============================================================================
// 主方法
// ============================================================================
/**
* 刷持有人(一个 bundle 完成分发+买入)
*
* 流程(同一个 bundle):
* 1. 贿赂交易
* 2. 分发原生代币(dev → 新钱包1, dev → 新钱包2, ...)
* 3. 买入交易(新钱包1 买入, 新钱包2 买入, ...)
* 4. 利润多跳
*
* ⚠️ 仅支持原生代币(BNB/MON)模式
* ERC20 模式需要授权,无法在同一个 bundle 中完成
*
* @returns 包含所有签名交易的结果,由前端提交
*/
export async function holdersMaker(params) {
const { payerPrivateKey, holdersCount, buyAmountPerHolder, tokenAddress, baseToken = 'native', config } = params;
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;
const result = {
success: false,
newWallets: [],
signedTransactions: [],
batchResults: [],
successBatchCount: 0,
totalBatchCount: 0
};
// ⚠️ 目前仅支持原生代币模式
if (baseToken !== 'native') {
result.error = '一个 bundle 模式仅支持原生代币(BNB/MON),ERC20 模式需要分开提交';
return result;
}
// ⚠️ 目前仅支持 Flap 内盘
if (tradeType !== 'flap') {
result.error = '一个 bundle 模式目前仅支持 Flap 内盘';
return result;
}
try {
// 1. 初始化
const provider = new JsonRpcProvider(rpcUrl);
const chainId = configChainId || Number((await provider.getNetwork()).chainId);
const payer = new Wallet(payerPrivateKey, provider);
const nonceManager = new NonceManager(provider);
console.log(`[HoldersMaker] 开始刷持有人: chain=${chain}, chainId=${chainId}, holdersCount=${holdersCount}`);
// 2. 生成新钱包
const newWallets = generateWallets(holdersCount);
result.newWallets = newWallets;
console.log(`[HoldersMaker] 生成 ${newWallets.length} 个新钱包`);
// 3. 计算金额
const buyAmountWei = ethers.parseEther(buyAmountPerHolder);
const gasFeePerWallet = BigInt(Math.floor(gasLimit * gasPriceGwei * 1e9));
const gasWithBuffer = gasFeePerWallet * BigInt(GAS_BUFFER_MULTIPLIER);
const transferAmountPerWallet = buyAmountWei + gasWithBuffer;
console.log(`[HoldersMaker] 每个钱包分发: ${ethers.formatEther(transferAmountPerWallet)} BNB (买入 ${buyAmountPerHolder} + Gas ${ethers.formatEther(gasWithBuffer)})`);
// 4. 获取 Gas Price(支持小数 Gwei,如 0.1 Gwei)
const gasPriceWei = BigInt(Math.floor(gasPriceGwei * 1e9));
const gasPrice = await getOptimizedGasPrice(provider, { minGasPrice: gasPriceWei });
const bribeAmountWei = ethers.parseEther(String(bribeAmount));
// 5. 分批处理
const walletBatches = chunkArray(newWallets, maxWalletsPerBatch);
result.totalBatchCount = walletBatches.length;
console.log(`[HoldersMaker] 分 ${walletBatches.length} 批处理,每批最多 ${maxWalletsPerBatch} 个钱包`);
// 6. 计算利润
const totalBuyAmount = buyAmountWei * BigInt(holdersCount);
const profitRateBps = PROFIT_CONFIG.RATE_BPS_SWAP;
const totalProfit = (totalBuyAmount * BigInt(profitRateBps)) / 10000n;
const profitPerBatch = totalProfit / BigInt(walletBatches.length);
console.log(`[HoldersMaker] 总利润: ${ethers.formatEther(totalProfit)} BNB`);
// 7. 并行生成所有批次的签名
const batchPromises = walletBatches.map(async (batch, batchIdx) => {
try {
const signedTxs = [];
// 计算这批需要的 nonce 数量
// 贿赂 1 + 分发 N + 利润 (PROFIT_HOP_COUNT + 1)
const payerNonceCount = 1 + batch.length + PROFIT_HOP_COUNT + 1;
const payerNonces = await nonceManager.getNextNonceBatch(payer, payerNonceCount);
let payerNonceIdx = 0;
// (1) 贿赂交易
const bribeTx = await buildNativeTransferTx(payer, BLOCKRAZOR_BUILDER_EOA, bribeAmountWei, payerNonces[payerNonceIdx++], gasPrice, chainId, txType);
signedTxs.push(bribeTx);
// (2) 分发交易
for (const newWallet of batch) {
const transferTx = await buildNativeTransferTx(payer, newWallet.address, transferAmountPerWallet, payerNonces[payerNonceIdx++], gasPrice, chainId, txType);
signedTxs.push(transferTx);
}
// (3) 买入交易(新钱包,nonce=0)
for (const newWallet of batch) {
const buyerWallet = new Wallet(newWallet.privateKey, provider);
const buyTx = await buildFlapBuyTx(buyerWallet, tokenAddress, buyAmountWei, 0, // 新钱包 nonce=0
gasPrice, gasLimit, chainId, txType, chain);
signedTxs.push(buyTx);
}
// (4) 利润多跳
if (profitPerBatch > 0n) {
const profitHopResult = await buildProfitHopTransactions({
provider,
payerWallet: payer,
profitAmount: profitPerBatch,
profitRecipient: PROFIT_CONFIG.RECIPIENT,
hopCount: PROFIT_HOP_COUNT,
gasPrice,
chainId,
txType,
startNonce: payerNonces[payerNonceIdx]
});
signedTxs.push(...profitHopResult.signedTransactions);
}
return {
batchIndex: batchIdx,
success: true,
signedTransactions: signedTxs,
walletCount: batch.length
};
}
catch (error) {
return {
batchIndex: batchIdx,
success: false,
error: error.message,
walletCount: batch.length
};
}
});
const batchResults = await Promise.all(batchPromises);
// 8. 处理结果
for (const res of batchResults) {
if (res.success && res.signedTransactions) {
result.signedTransactions.push(res.signedTransactions);
result.successBatchCount++;
}
result.batchResults.push({
batchIndex: res.batchIndex,
success: res.success,
signedTransactions: res.signedTransactions,
error: res.error,
walletCount: res.walletCount
});
}
result.success = result.successBatchCount > 0;
console.log(`[HoldersMaker] 完成: ${result.successBatchCount}/${result.totalBatchCount} 批成功`);
}
catch (error) {
result.error = error.message;
console.error(`[HoldersMaker] 整体失败:`, error.message);
}
return result;
}
/**
* 获取刷持有人预估费用
*/
export function estimateHoldersMakerCost(params) {
const { holdersCount, buyAmountPerHolder, gasLimit = DEFAULT_GAS_LIMIT, gasPriceGwei = DEFAULT_GAS_PRICE_GWEI } = params;
const gasFeePerWallet = (gasLimit * gasPriceGwei) / 1e9;
const gasWithBuffer = gasFeePerWallet * GAS_BUFFER_MULTIPLIER;
const buyAmount = parseFloat(buyAmountPerHolder);
// 每个钱包成本 = 购买金额 + Gas 费
const costPerWallet = buyAmount + gasWithBuffer;
const totalNativeCost = costPerWallet * holdersCount;
// 批次数
const batchCount = Math.ceil(holdersCount / DEFAULT_MAX_WALLETS_PER_BATCH);
return {
totalNativeCost: totalNativeCost.toFixed(6),
gasEstimate: (gasWithBuffer * holdersCount).toFixed(6),
batchCount
};
}