UNPKG

four-flap-meme-sdk

Version:

SDK for Flap bonding curve and four.meme TokenManager

269 lines (268 loc) 11.9 kB
/** * ============================================================================ * 刷持有人(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 }; }