UNPKG

four-flap-meme-sdk

Version:

SDK for Flap bonding curve and four.meme TokenManager

114 lines (113 loc) 5.29 kB
import { ethers, Wallet } from 'ethers'; import { NonceManager, getOptimizedGasPrice } from '../../utils/bundle-helpers.js'; import { ADDRESSES } from '../../utils/constants.js'; import { ERC20_ABI } from '../../abis/common.js'; import { getTxType, getGasPriceConfig } from './config.js'; import { batchCheckAllowances } from '../../utils/erc20.js'; /** * 批量授权代币给 TokenManager(用于 Private Buy/Sell) * ✅ 直接通过 RPC 提交到链上并等待确认 * ✅ 不需要 Merkle API Key,使用普通 RPC 即可 * ✅ 专门用于授权给 TokenManagerOriginal 合约 * ✅ 用于 fourBatchPrivateSell 和 fourBatchSellWithBundleMerkle * ✅ 优化:并行获取 gasPrice、decimals、allowances */ export async function approveFourTokenManagerBatch(params) { const { privateKeys, tokenAddress, amounts, config } = params; if (privateKeys.length === 0 || amounts.length !== privateKeys.length) { throw new Error('Private key count and amount count must match'); } const tmAddr = ADDRESSES.BSC.TokenManagerOriginal; const chainId = config.chainId ?? 56; // ✅ 直接使用传入的 RPC URL 创建 provider(不依赖 Merkle) const provider = new ethers.JsonRpcProvider(config.rpcUrl, { chainId: chainId, name: 'BSC' }); const wallets = privateKeys.map(k => new Wallet(k, provider)); const allMax = amounts.every(a => a === 'max'); // ✅ 优化:并行获取 gasPrice、decimals(如果需要)、allowances const [gasPrice, decimals, allowances] = await Promise.all([ getOptimizedGasPrice(provider, getGasPriceConfig(config)), allMax ? Promise.resolve(18) // 如果都是 'max',不需要查询 decimals : (async () => { try { const tokenContract = new ethers.Contract(tokenAddress, ERC20_ABI, provider); return await tokenContract.decimals(); } catch (error) { throw new Error(`查询代币 decimals 失败: ${error.message}`); } })(), batchCheckAllowances(provider, tokenAddress, wallets.map(w => w.address), tmAddr) ]); const amountsBigInt = amounts.map(a => a === 'max' ? ethers.MaxUint256 : ethers.parseUnits(a, decimals)); // 只授权不足的 // ✅ 策略:如果授权额度 < MaxUint256 / 2n,就重新授权(确保授权足够大) const APPROVAL_THRESHOLD = ethers.MaxUint256 / 2n; const needApproval = wallets.filter((_, i) => allowances[i] < APPROVAL_THRESHOLD); const needApprovalAmounts = amountsBigInt.filter((_, i) => allowances[i] < APPROVAL_THRESHOLD); const skippedCount = wallets.length - needApproval.length; if (needApproval.length === 0) { return { success: true, signedTransactions: [], approvedCount: 0, skippedCount: skippedCount, message: '所有钱包已授权,无需重复授权' }; } // ✅ 优化:计算 gasLimit(不需要 RPC) const getGasLimit = (cfg) => { if (cfg.gasLimit) return BigInt(cfg.gasLimit); if (cfg.gasLimitMultiplier) return BigInt(Math.ceil(60000 * cfg.gasLimitMultiplier)); return 60000n; // 默认授权 gas limit }; const finalGasLimit = getGasLimit(config); const txType = getTxType(config); // ✅ 优化:并行获取 nonces 和构建未签名交易 const nonceManager = new NonceManager(provider); const needApprovalTokens = needApproval.map(w => new ethers.Contract(tokenAddress, ERC20_ABI, w)); const [nonces, unsignedApprovals] = await Promise.all([ nonceManager.getNextNoncesForWallets(needApproval), // ✅ 批量获取 nonces(JSON-RPC 批量请求) Promise.all(needApprovalTokens.map((token, i) => token.approve.populateTransaction(tmAddr, needApprovalAmounts[i]))) ]); // ✅ 并行签名所有授权交易 const signedTxs = await Promise.all(unsignedApprovals.map((unsigned, i) => needApproval[i].signTransaction({ ...unsigned, from: needApproval[i].address, nonce: nonces[i], gasLimit: finalGasLimit, gasPrice, chainId, type: txType }))); nonceManager.clearTemp(); try { // ✅ 并行广播所有授权交易 const txResponses = await Promise.all(signedTxs.map(tx => provider.broadcastTransaction(tx))); const txHashes = txResponses.map(r => r.hash); // ✅ 并行等待所有授权交易确认(至少1个确认) await Promise.all(txResponses.map(tx => tx.wait(1))); return { success: true, signedTransactions: signedTxs, approvedCount: signedTxs.length, skippedCount: skippedCount, txHashes: txHashes, message: `授权成功!共 ${signedTxs.length} 个钱包已授权,跳过 ${skippedCount} 个已授权钱包` }; } catch (error) { return { success: false, signedTransactions: signedTxs, approvedCount: 0, skippedCount: skippedCount, message: `授权提交失败: ${error.message}` }; } }