书接上文,这一篇直接上代码,注意先安装依赖:
npm install web3
使用的node版本: 18
估算的费用包括基础费用和优先费用,其中基础费用可以直接用Web3的函数得到,而优先费用则参考历史区块的数据来估算。代码中的estimateGasFees函数可以自行指定取样的百分位。
完整代码:
import Web3 from "web3";
const rpcUrl = "https://eth.llamarpc.com";
const web3 = new Web3(rpcUrl);
/**
* 根据Gas费用历史数据来进行估算
* @param {number} blocks - 取样的历史区块数
* @param {number[]} percentiles - 不同百分位(默认60%, 75%, 90%)的优先费用平均值
* @returns {Promise<Object>} - 估算的Gas费用
*/
async function estimateGasFees(blocks = 10, percentiles = [60, 75, 90]) {
try {
// 获取当前基础费用
const pendingBlock = await web3.eth.getBlock("pending");
const baseFeePerGas = Number(pendingBlock.baseFeePerGas);
const feeHistory = await web3.eth.getFeeHistory(blocks, "pending", percentiles); // 获取历史费用数据
const formattedBlocks = formatFeeHistory(feeHistory, blocks, false); // 格式化数据
const priorityFeeEstimates = {}; // 各百分位的平均优先费用
// 历史优先费用平均值
for (let i = 0; i < percentiles.length; i++) {
const percentilePriorityFees = formattedBlocks.map(block => block.priorityFeePerGas[i]);
const sum = percentilePriorityFees.reduce((acc, val) => acc + val, 0);
priorityFeeEstimates[percentiles[i]] = Math.round(sum / percentilePriorityFees.length);
}
// 各百分位的Gas费用上限(基础费用加各百分位的优先费用)
const maxFeeEstimates = {};
for (const percentile of percentiles) {
maxFeeEstimates[percentile] = baseFeePerGas + priorityFeeEstimates[percentile];
}
return {
baseFeePerGas,
priorityFeeEstimates,
maxFeeEstimates
};
} catch (error) {
console.error("Error estimating gas fees:", error);
throw error;
}
}
/**
* 格式化历史费用数据
* @param {Object} result - 历史费用数据
* @param {number} blocksToAnalyze - 取样的区块数
* @param {boolean} includePending - 是否包括当前区块
* @returns {Array} - 格式化后的历史费用
*/
function formatFeeHistory(result, blocksToAnalyze, includePending) {
let blockNum = Number(result.oldestBlock);
let index = 0;
const blocks = [];
const oldestBlockNum = Number(result.oldestBlock);
while (blockNum < oldestBlockNum + blocksToAnalyze) {
if (index < result.baseFeePerGas.length - 1) {
blocks.push({
number: blockNum,
baseFeePerGas: Number(result.baseFeePerGas[index]),
gasUsedRatio: result.gasUsedRatio[index],
priorityFeePerGas: result.reward[index].map(x => Number(x)),
});
}
blockNum += 1;
index += 1;
}
if (includePending && result.baseFeePerGas.length > blocksToAnalyze) {
blocks.push({
number: "pending",
baseFeePerGas: Number(result.baseFeePerGas[blocksToAnalyze]),
gasUsedRatio: NaN,
priorityFeePerGas: [],
});
}
return blocks;
}
async function getGasFeeEstimates() {
try {
const estimates = await estimateGasFees();
console.log("===== Gas 费用估算 =====");
console.log(`基础费用: ${web3.utils.fromWei(estimates.baseFeePerGas.toString(), 'gwei')} Gwei`);
console.log("\n优先费用 (Gwei):");
for (const [percentile, fee] of Object.entries(estimates.priorityFeeEstimates)) {
console.log(`${percentile}th percentile: ${web3.utils.fromWei(fee.toString(), 'gwei')} Gwei`);
}
console.log("\nGas费用上限 (Gwei):");
for (const [percentile, fee] of Object.entries(estimates.maxFeeEstimates)) {
console.log(`${percentile}th percentile: ${web3.utils.fromWei(fee.toString(), 'gwei')} Gwei`);
}
return estimates;
} catch (error) {
console.error("Failed to get gas fee estimates:", error);
}
}
// 执行
getGasFeeEstimates().catch(console.error);
export { estimateGasFees, formatFeeHistory, getGasFeeEstimates };
运行该脚本后效果:
输出三个百分位的估算结果,但最后设定Gas费用上限的策略(激进、保守或者平衡)还需要额外的权衡和微调,不过这就不是这篇文章的讨论内容了。