在 Base(OP-Stack)上,交易的总成本是 L2 执行费用与 L1 数据费用之和。L1 数据费用为交易发布到以太坊 L1 的字节定价,使用与 gas 价格相关的标量(scalar)和压缩后大小项,而非直接使用 L1 gas 价格。本文解释这种两部分成本模型,展示每个输入在 RPC 中的来源,并提供一种对账方法,用于根据交易回执验证实际收取的 L1 数据费用。文中包含可运行的 Node.js 代码片段和常见差异的故障排除清单。
OP-Stack 链上的两部分成本模型
在 Base 和其他 OP-Stack 链上,交易的总成本不仅仅是你在钱包中看到的 gas。它是两种不同费用之和:L2 执行费用和 L1 数据费用。L2 执行费用按已用 gas 乘以有效 L2 gas 价格计算,遵循与以太坊相同的 EIP-1559 形式。相比之下,L1 数据费用为交易发布到以太坊 L1 的数据定价,它由与 gas 价格相关的标量和压缩后大小项计算得出,而非直接来自 L1 gas 价格。
这种区分很重要,因为两种费用的计价单位不同,不能像单一 gas 数值那样相加。L2 执行费用以 L2 gas 为单位,而 L1 数据费用则来自交易 calldata 压缩后的大小,乘以反映 L1 gas 成本的标量。将两者混为一谈会导致系统性的低估或高估。如需深入了解 Base 如何处理最终性和区块标签,请参阅 Base OP-Stack 最终性与区块标签。
OP-Stack 规范和 Base 文档描述了这一模型,但确切的标量和预言机合约参数是版本化且因链而异的。请始终将其视为“有文档记录 / 因链而异”,并查阅该链的官方文档以获取当前值。
- L2 执行费用 = 已用 gas × 有效 L2 gas 价格(EIP-1559 形式)。
- L1 数据费用 = 压缩后的交易大小 × L1 gas 价格标量(来自预言机)。
- 两种费用单位不同;不要将它们作为单一 gas 数量相加。
为什么 eth_estimateGas 和 eth_gasPrice 只覆盖执行部分
RPC 方法 eth_estimateGas 和 eth_gasPrice 仅用于估算 L2 执行部分。eth_estimateGas 模拟交易以确定它将消耗多少 L2 gas,而 eth_gasPrice 返回建议的 L2 gas 价格。这两种方法都不考虑 L1 数据费用,后者取决于交易 calldata 的大小和当前的 L1 gas 价格标量。
如果仅使用这些方法构建总成本估算,你将系统性地低估用户实际支付的费用。对于 calldata 密集型的交易尤其如此,例如合约部署、批量转账或带有长 revert 原因的交易。关于以太坊费用估算的更广泛讨论,请参阅使用 eth_feeHistory 估算 gas 价格。
要获得完整情况,你还必须查询链的 gas-price-oracle 合约,通常通过 eth_call,以读取 L1 标量和 L1 基础费用组成部分。预言机的 ABI 和地址因链而异,必须从该链的文档中获取,而不是硬编码。
eth_estimateGas仅返回 L2 已用 gas。eth_gasPrice仅返回 L2 gas 价格建议。- L1 数据费用需要单独的预言机调用。
每个费用组成部分的 RPC 来源
要重建交易的总成本,你需要来自多个 RPC 来源的数据。交易回执本身包含已收取的金额:gasUsed、effectiveGasPrice,以及通常还有一个 L1 费用字段(确切字段名取决于链和客户端)。对于执行部分,eth_feeHistory 和 eth_gasPrice 提供基础费用和优先费用信息。对于 L1 数据费用,你必须调用链的 gas-price-oracle 合约,通常通过 eth_call,以读取 L1 标量和 L1 基础费用组成部分。
预言机合约地址和 ABI 并非通用。例如在 Base 上,地址记录在 Base 的开发者资源中。在 Optimism 上,它可能不同。请始终从链的官方文档中核实。如果你使用像 OnFinality 这样的提供商,请确保你的端点支持相关链的 eth_call 和 eth_getTransactionReceipt。有关端点功能,请参阅 RPC 端点指南(RPC Assistant)。
读取历史交易时,请固定区块号。对历史重建使用 latest 标签会得到当前的预言机值,而不是交易发生时的值,从而导致 L1 费用计算错误。
- 回执:
gasUsed、effectiveGasPrice以及 L1 费用字段(如果存在)。 - 执行部分:
eth_feeHistory、eth_gasPrice。 - L1 数据费用:通过
eth_call调用 gas-price-oracle 合约(地址和 ABI 因链而异)。
将回执与计算出的费用对账
对账方法将模糊的“费用太高”抱怨转化为证据。首先使用 eth_getTransactionReceipt 获取已知交易的收据。读取 gasUsed 和 effectiveGasPrice 字段,然后计算 L2 执行费用为 gasUsed * effectiveGasPrice。接下来,查看回执报告的总价值变化——这通常是发送者交易前后余额之差,或者如果存在的话,像 l1Fee 这样的字段。从总价值变化中减去 L2 执行费用;剩余部分就是该交易的 L1 数据费用。
请注意,回执中 L1 部分的确切字段命名取决于链和客户端。有些客户端包含 l1Fee 字段,其他可能没有。请对照链自己的文档进行确认。如果回执没有直接暴露 L1 费用,你仍然可以从预言机值和交易的 calldata 大小计算它,但这需要知道压缩算法和标量。
关于跟踪跨链事件的实际示例,请参阅跟踪 Base 跨链事件。
- 获取回执:
eth_getTransactionReceipt。 - 计算 L2 执行费用 =
gasUsed * effectiveGasPrice。 - 从总价值变化中减去以分离出 L1 数据费用。
- 对照链文档核实回执字段名称。
可运行的 Node.js 示例:读取回执和预言机
以下 Node.js 脚本连接到 RPC 端点,获取交易回执,并调用 gas-price-oracle 合约以读取 L1 费用标量和基础费用。然后打印组成部分的表格。将占位符预言机地址替换为来自你链文档的正确地址。此脚本为简单起见使用 ethers.js,但你可以将其适配到 web3.js 或原始 JSON-RPC 调用。
确保你的 RPC 端点支持 eth_call 和 eth_getTransactionReceipt。有关支持的方法列表,请参阅 RPC 端点指南(RPC Assistant)。
const { ethers } = require('ethers');
// Replace with your RPC endpoint (e.g., from OnFinality)
const RPC_URL = 'https://base-mainnet.example.com';
// Replace with the gas-price-oracle address from the chain's docs
const ORACLE_ADDRESS = '0x0000000000000000000000000000000000000000';
// Minimal ABI for the oracle (check chain docs for exact function names)
const ORACLE_ABI = [
'function l1BaseFee() view returns (uint256)',
'function scalar() view returns (uint256)'
];
async function main() {
const provider = new ethers.JsonRpcProvider(RPC_URL);
const txHash = '0x...'; // Replace with a known transaction hash
const receipt = await provider.getTransactionReceipt(txHash);
if (!receipt) {
console.error('Receipt not found');
return;
}
const gasUsed = receipt.gasUsed;
const effectiveGasPrice = receipt.effectiveGasPrice;
const l2ExecutionFee = gasUsed * effectiveGasPrice;
const oracle = new ethers.Contract(ORACLE_ADDRESS, ORACLE_ABI, provider);
const l1BaseFee = await oracle.l1BaseFee();
const scalar = await oracle.scalar();
console.log('Component | Value');
console.log('-------------------------|--------------------------');
console.log(`Gas Used | ${gasUsed.toString()}`);
console.log(`Effective Gas Price | ${effectiveGasPrice.toString()}`);
console.log(`L2 Execution Fee (wei) | ${l2ExecutionFee.toString()}`);
console.log(`L1 Base Fee (wei) | ${l1BaseFee.toString()}`);
console.log(`L1 Scalar | ${scalar.toString()}`);
// Note: L1 data fee calculation requires compressed size; see chain docs.
}
main().catch(console.error);字节敏感性:为什么 calldata 密集的交易支付更多 L1 费用
L1 数据费用对字节敏感。它为交易发布到以太坊 L1 的数据定价,因此具有大 calldata 的交易——例如合约部署、批量转账或带有长 revert 原因的交易——即使执行 gas 相似,也会比普通转账支付多得多的 L1 费用。这是因为 L1 数据费用与交易 calldata 的压缩后大小成正比,而不是与使用的 L2 gas 成正比。
这对批处理和链上数据存储有影响。如果你批量处理许多转账,calldata 大小会随转账数量线性增长,L1 数据费用也会如此。在链上存储大数据块将产生显著的 L1 费用。开发者应考虑链下存储或压缩技术以降低成本。
要更深入地了解 Base 如何处理区块标签和最终性,请参阅 Base OP-Stack 最终性与区块标签。
- L1 数据费用随压缩后的 calldata 大小扩展。
- 批量转账和部署比简单转账支付更多 L1 费用。
- 对于大数据,考虑链下存储或压缩。
费用差异的故障排除
当计算出的费用与回执不一致时,请检查这些常见故障模式。首先,核实预言机地址和标量。使用错误的预言机地址或过时的标量会产生不正确的 L1 费用值。其次,确保你从完全同步的节点读取回执;滞后的节点可能返回过时或缺失的数据。第三,避免对历史重建使用 latest 标签——始终将区块号固定为交易所在的区块。第四,检查链是否补贴了费用;某些链或应用可能承担部分 L1 费用,因此回执可能显示比原始计算更低的金额。
此外,确认你没有将链已经补贴的费用相加。如果回执包含 L1 费用字段,请将其作为权威的已收取金额。如果没有,则从预言机值计算,但要注意可能的补贴。
对于可能影响回执获取的延迟相关问题,请参阅 Base RPC 延迟测量。
- 错误的预言机地址或过时的标量。
- 通过滞后节点读取回执。
- 对历史重建使用
latest标签。 - 将已经补贴的费用相加。
局限性与权衡
L1 数据费用模型有固有的局限性。标量和预言机会随时间变化,因此任何硬编码的值都会过时。某些回执字段是实现定义的,可能因客户端或链而异。可复现的费用重现必须固定区块号以及交易哈希,因为预言机值依赖于区块。最后,确切的压缩算法和标量应用并不总是有完整文档,使得独立验证具有挑战性。
这些权衡意味着,虽然你可以对特定交易进行费用对账,但构建通用费用估算器需要持续维护和链特定配置。请始终参考链的官方文档以获取最新参数。
- 标量和预言机是版本化且因链而异的。
- 回执字段名称因客户端和链而异。
- 固定区块号以获得可复现的结果。
- 压缩细节可能没有文档记录。
结果表:针对你自己的端点进行测量
为了验证你的理解和你 RPC 端点的行为,为一组已知交易创建结果表。对于每笔交易,记录交易哈希、区块号、已用 gas、有效 gas 价格、计算出的 L2 执行费用、回执报告的 L1 费用(如果可用)以及你从预言机值计算出的 L1 费用。这将帮助你识别差异并确认你的端点返回一致的数据。
使用以下模板填写你自己的测量结果。这是一种由读者验证的方法;此处不提供基准数字,因为它们取决于你的特定端点和链状态。
- 交易哈希 | 区块号 | 已用 Gas | 有效 Gas 价格 | L2 执行费用 | 回执 L1 费用 | 计算出的 L1 费用 | 备注
- 用你自己 RPC 调用中的数据填写每一行。
- 比较回执 L1 费用与计算出的 L1 费用以发现差异。
后续步骤:将费用意识集成到你的应用中
现在你可以读取并对账 L1 数据费用,考虑将费用意识集成到你的应用中。向用户分别显示 L2 执行费用和 L1 数据费用,以便他们了解总成本。在估算交易费用时,查询预言机合约以包含 L1 组成部分。对于批量操作,计算预期的 calldata 大小及其 L1 费用影响。
对于生产使用,选择可靠的 RPC 提供商。OnFinality 提供 API 服务,包含 Base 和其他网络的端点。你还可以探索 RPC 定价 以找到适合你需求的方案。有关支持网络的完整列表,请参阅 networks/base。
最后,请关注 OnFinality Learn 中心 以获取更多关于 OP-Stack 和以太坊 RPC 的指南。
- 向用户分别显示 L2 和 L1 费用。
- 在估算中查询预言机以获取 L1 费用。
- 考虑批量操作的 calldata 大小。
- 选择像 OnFinality 这样可靠的 RPC 提供商。