En Base (OP-Stack), el costo total de una transacción es la suma de una tarifa de ejecución L2 y una tarifa de datos L1. La tarifa de datos L1 pone precio a los bytes que la transacción publica en Ethereum L1, utilizando un escalar relacionado con el precio del gas y un término de tamaño comprimido en lugar del precio del gas L1 directamente. Este artículo explica el modelo de costo de dos partes, muestra de dónde proviene cada entrada vía RPC y proporciona un método de conciliación para verificar la tarifa de datos L1 cobrada contra el recibo de la transacción. Incluye un fragmento ejecutable en Node.js y una lista de verificación para resolver discrepancias comunes.
El modelo de costo de dos partes en cadenas OP-Stack
En Base y otras cadenas OP-Stack, el costo total de una transacción no es solo el gas que ves en tu billetera. Es la suma de dos tarifas distintas: la tarifa de ejecución L2 y la tarifa de datos L1. La tarifa de ejecución L2 se calcula como el gas utilizado multiplicado por el precio efectivo del gas L2, siguiendo la misma forma EIP-1559 que Ethereum. La tarifa de datos L1, en cambio, pone precio a los datos que la transacción publica en Ethereum L1, y se calcula a partir de un escalar relacionado con el precio del gas y un término de tamaño comprimido, no directamente del precio del gas L1.
Esta separación importa porque las dos tarifas se cotizan en unidades diferentes y no deben sumarse como si fueran un solo número de gas. La tarifa de ejecución L2 está en unidades de gas L2, mientras que la tarifa de datos L1 se deriva del tamaño del calldata de la transacción después de la compresión, multiplicado por un escalar que refleja los costos de gas L1. Mezclarlos conduce a una subestimación o sobreestimación sistemática. Para una mirada más profunda sobre cómo Base maneja la finalidad y las etiquetas de bloque, consulta Finalidad y etiquetas de bloque de Base OP-Stack.
La especificación de OP-Stack y la documentación de Base describen este modelo, pero los parámetros exactos del escalar y del contrato oráculo están versionados y son específicos de cada cadena. Trátalos siempre como 'documentado / varía según la cadena' y consulta la documentación propia de la cadena para obtener los valores actuales.
- Tarifa de ejecución L2 = gas utilizado × precio efectivo del gas L2 (estilo EIP-1559).
- Tarifa de datos L1 = tamaño comprimido de la transacción × escalar del precio del gas L1 (de un oráculo).
- Las dos tarifas están en unidades diferentes; no las sumes como una sola cantidad de gas.
Por qué eth_estimateGas y eth_gasPrice solo cubren la ejecución
Los métodos RPC eth_estimateGas y eth_gasPrice están diseñados para estimar únicamente el lado de la ejecución L2. eth_estimateGas simula la transacción para determinar cuánto gas L2 consumirá, y eth_gasPrice devuelve un precio de gas L2 sugerido. Ninguno de los dos métodos tiene en cuenta la tarifa de datos L1, que depende del tamaño del calldata de la transacción y del escalar actual del precio del gas L1.
Si construyes una estimación del costo total usando solo estos métodos, subestimarás sistemáticamente lo que el usuario realmente paga. Esto es especialmente cierto para transacciones con mucho calldata, como despliegues de contratos, transferencias por lotes o transacciones con motivos de reversión largos. Para una discusión más amplia sobre la estimación de tarifas en Ethereum, consulta Estimación del precio del gas con eth_feeHistory.
Para obtener una imagen completa, también debes consultar el contrato oráculo de precio de gas de la cadena, normalmente mediante eth_call, para leer el escalar L1 y el componente de tarifa base L1. El ABI y la dirección del oráculo son específicos de cada cadena y deben tomarse de la documentación de esa cadena en lugar de codificarse de forma fija.
eth_estimateGasdevuelve solo el gas L2 utilizado.eth_gasPricedevuelve solo una sugerencia de precio de gas L2.- La tarifa de datos L1 requiere una llamada separada al oráculo.
Fuentes RPC para cada componente de la tarifa
Para reconstruir el costo total de una transacción, necesitas datos de varias fuentes RPC. El recibo de la transacción en sí contiene los montos cobrados: gasUsed, effectiveGasPrice y, a menudo, un campo para la tarifa L1 (el nombre exacto del campo depende de la cadena y del cliente). Para el lado de la ejecución, eth_feeHistory y eth_gasPrice proporcionan información sobre la tarifa base y la tarifa de prioridad. Para la tarifa de datos L1, debes llamar al contrato oráculo de precio de gas de la cadena, normalmente mediante eth_call, para leer el escalar L1 y el componente de tarifa base L1.
La dirección y el ABI del contrato oráculo no son universales. En Base, por ejemplo, la dirección está documentada en los recursos para desarrolladores de Base. En Optimism, puede ser diferente. Verifica siempre en la documentación oficial de la cadena. Si utilizas un proveedor como OnFinality, asegúrate de que tu endpoint admita eth_call y eth_getTransactionReceipt para la cadena en cuestión. Consulta Guía de endpoints RPC (RPC Assistant) para conocer las capacidades de los endpoints.
Al leer transacciones históricas, fija el número de bloque. Usar una etiqueta latest para una reconstrucción histórica te dará los valores actuales del oráculo, no los valores en el momento de la transacción, lo que lleva a cálculos incorrectos de la tarifa L1.
- Recibo:
gasUsed,effectiveGasPricey campo de tarifa L1 (si está presente). - Lado de ejecución:
eth_feeHistory,eth_gasPrice. - Tarifa de datos L1: contrato oráculo de precio de gas mediante
eth_call(dirección y ABI específicos de la cadena).
Conciliación del recibo con las tarifas calculadas
El método de conciliación convierte una vaga queja de 'las tarifas son altas' en evidencia. Comienza obteniendo el recibo de una transacción conocida usando eth_getTransactionReceipt. Lee los campos gasUsed y effectiveGasPrice, luego calcula la tarifa de ejecución L2 como gasUsed * effectiveGasPrice. A continuación, observa el cambio total de valor reportado por el recibo—normalmente es la diferencia entre el saldo del remitente antes y después de la transacción, o un campo como l1Fee si está presente. Resta la tarifa de ejecución L2 del cambio total de valor; el resto es la tarifa de datos L1 de esa transacción.
Ten en cuenta que el nombre exacto del campo del recibo para la porción L1 depende de la cadena y del cliente. Algunos clientes incluyen un campo l1Fee, otros puede que no. Confírmalo con la documentación propia de la cadena. Si el recibo no expone la tarifa L1 directamente, aún puedes calcularla a partir de los valores del oráculo y el tamaño del calldata de la transacción, pero esto requiere conocer el algoritmo de compresión y el escalar.
Para un ejemplo práctico de seguimiento de eventos entre cadenas, consulta Seguimiento de eventos entre cadenas en Base.
- Obtener recibo:
eth_getTransactionReceipt. - Calcular tarifa de ejecución L2 =
gasUsed * effectiveGasPrice. - Restar del cambio total de valor para aislar la tarifa de datos L1.
- Verificar los nombres de los campos del recibo con la documentación de la cadena.
Ejemplo ejecutable en Node.js: lectura del recibo y del oráculo
El siguiente script de Node.js se conecta a un endpoint RPC, obtiene el recibo de una transacción y llama al contrato oráculo de precio de gas para leer el escalar de tarifa L1 y la tarifa base. Luego imprime una tabla de componentes. Reemplaza la dirección de oráculo de marcador de posición por la correcta de la documentación de tu cadena. Este script usa ethers.js por simplicidad, pero puedes adaptarlo a web3.js o a llamadas JSON-RPC sin procesar.
Asegúrate de que tu endpoint RPC admita eth_call y eth_getTransactionReceipt. Para obtener una lista de métodos admitidos, consulta Guía de endpoints 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);Sensibilidad a los bytes: por qué las transacciones con mucho calldata pagan más tarifa L1
La tarifa de datos L1 es sensible a los bytes. Pone precio a los datos que la transacción publica en Ethereum L1, por lo que las transacciones con calldata grande—como despliegues de contratos, transferencias por lotes o transacciones con motivos de reversión largos—pagan mucha más tarifa L1 que una transferencia simple, incluso cuando su gas de ejecución es similar. Esto se debe a que la tarifa de datos L1 es proporcional al tamaño comprimido del calldata de la transacción, no al gas L2 utilizado.
Esto tiene implicaciones para el procesamiento por lotes y para almacenar datos en la cadena. Si estás agrupando muchas transferencias, el tamaño del calldata crece linealmente con el número de transferencias, y también lo hace la tarifa de datos L1. Almacenar grandes blobs de datos en la cadena incurrirá en tarifas L1 significativas. Los desarrolladores deberían considerar el almacenamiento fuera de la cadena o técnicas de compresión para mitigar los costos.
Para una comprensión más profunda de cómo Base maneja las etiquetas de bloque y la finalidad, consulta Finalidad y etiquetas de bloque de Base OP-Stack.
- La tarifa de datos L1 escala con el tamaño del calldata comprimido.
- Las transferencias por lotes y los despliegues pagan más tarifa L1 que las transferencias simples.
- Considera el almacenamiento fuera de la cadena o la compresión para datos grandes.
Solución de problemas de discrepancias en las tarifas
Cuando tu tarifa calculada no coincide con el recibo, revisa estos modos de fallo comunes. Primero, verifica la dirección del oráculo y el escalar. Usar una dirección de oráculo incorrecta o un escalar obsoleto producirá valores de tarifa L1 incorrectos. Segundo, asegúrate de estar leyendo el recibo de un nodo que esté completamente sincronizado; un nodo retrasado puede devolver datos desactualizados o faltantes. Tercero, evita usar una etiqueta latest para la reconstrucción histórica—fija siempre el número de bloque al bloque de la transacción. Cuarto, verifica si la cadena había subsidiado la tarifa; algunas cadenas o aplicaciones pueden cubrir parte de la tarifa L1, por lo que el recibo puede mostrar un monto menor que el cálculo bruto.
Además, confirma que no estás sumando una tarifa que la cadena ya había subsidiado. Si el recibo incluye un campo de tarifa L1, úsalo como el monto cobrado autoritativo. Si no, calcúlalo a partir de los valores del oráculo, pero ten en cuenta posibles subsidios.
Para problemas relacionados con la latencia que podrían afectar la obtención del recibo, consulta Medición de latencia RPC de Base.
- Dirección de oráculo incorrecta o escalar obsoleto.
- Recibo leído a través de un nodo retrasado.
- Uso de la etiqueta
latestpara reconstrucción histórica. - Sumar una tarifa que ya fue subsidiada.
Limitaciones y compensaciones
El modelo de tarifa de datos L1 tiene limitaciones inherentes. Los escalares y oráculos cambian con el tiempo, por lo que cualquier valor codificado de forma fija quedará obsoleto. Algunos campos del recibo están definidos por la implementación y pueden variar entre clientes o cadenas. Una reproducción de tarifa reproducible debe fijar el número de bloque además del hash de la transacción, porque los valores del oráculo dependen del bloque. Finalmente, el algoritmo de compresión exacto y la aplicación del escalar no siempre están completamente documentados, lo que dificulta la verificación independiente.
Estas compensaciones significan que, si bien puedes conciliar tarifas para una transacción específica, construir un estimador de tarifas de propósito general requiere mantenimiento continuo y configuración específica de la cadena. Consulta siempre la documentación oficial de la cadena para obtener los parámetros más actuales.
- Los escalares y oráculos están versionados y son específicos de cada cadena.
- Los nombres de los campos del recibo varían según el cliente y la cadena.
- Fija el número de bloque para obtener resultados reproducibles.
- Los detalles de compresión pueden no estar documentados.
Tabla de resultados: mide contra tu propio endpoint
Para validar tu comprensión y el comportamiento de tu endpoint RPC, crea una tabla de resultados para un conjunto de transacciones conocidas. Para cada transacción, registra el hash de la transacción, el número de bloque, el gas utilizado, el precio efectivo del gas, la tarifa de ejecución L2 calculada, la tarifa L1 reportada por el recibo (si está disponible) y tu tarifa L1 calculada a partir de los valores del oráculo. Esto te ayudará a identificar discrepancias y confirmar que tu endpoint devuelve datos consistentes.
Usa la siguiente plantilla para completar tus propias mediciones. Este es un método verificado por el lector; aquí no se proporcionan números de referencia porque dependen de tu endpoint específico y del estado de la cadena.
- Hash de transacción | Número de bloque | Gas utilizado | Precio efectivo del gas | Tarifa de ejecución L2 | Tarifa L1 del recibo | Tarifa L1 calculada | Notas
- Completa cada fila con datos de tus propias llamadas RPC.
- Compara la tarifa L1 del recibo con la tarifa L1 calculada para detectar discrepancias.
Próximos pasos: integra la conciencia de tarifas en tu aplicación
Ahora que puedes leer y conciliar la tarifa de datos L1, considera integrar la conciencia de tarifas en tu aplicación. Muestra a los usuarios la tarifa de ejecución L2 y la tarifa de datos L1 por separado, para que comprendan el costo total. Al estimar tarifas para una transacción, consulta el contrato oráculo para incluir el componente L1. Para operaciones por lotes, calcula el tamaño esperado del calldata y su impacto en la tarifa L1.
Para uso en producción, elige un proveedor RPC confiable. OnFinality ofrece servicio de API con endpoints para Base y otras redes. También puedes explorar precios de RPC para encontrar un plan que se ajuste a tus necesidades. Para obtener una lista completa de redes admitidas, consulta networks/base.
Finalmente, mantente atento al centro de aprendizaje de OnFinality para obtener más guías sobre OP-Stack y RPC de Ethereum.
- Muestra las tarifas L2 y L1 por separado a los usuarios.
- Consulta el oráculo para la tarifa L1 en las estimaciones.
- Considera el tamaño del calldata para operaciones por lotes.
- Elige un proveedor RPC confiable como OnFinality.