four-flap-meme-sdk
Version:
SDK for Flap bonding curve and four.meme TokenManager
114 lines (113 loc) • 5.29 kB
JavaScript
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}`
};
}
}