Datos en cadena y SQL · Lección 4 de 9
Transacciones de Ethereum en bruto, recibos y registros
9 min de lectura
En Ethereum y otras cadenas EVM, una transacción registra lo que solicitó un remitente, mientras que el recibo, los registros y las trazas documentan lo que ocurrió realmente. Los movimientos de tokens, las llamadas fallidas y el valor transferido por contratos nunca aparecen en la propia transacción. Un analista que solo lee las transacciones pasa por alto la mayor parte de la actividad de la cadena; uno que conoce los cuatro objetos puede indicar con precisión qué se movió, quién lo desencadenó y si tuvo éxito.
Campos de las transacciones y los cinco tipos
Desde EIP-2718 (2021), una transacción de Ethereum es un contenedor tipado: un byte de tipo seguido de una carga útil cuyos campos dependen del tipo. Las transacciones heredadas, anteriores a este contenedor, se consideran de tipo 0.
| Tipo | Introducción | Campos de comisiones | Campos adicionales |
|---|---|---|---|
0x0 heredada | Original; ID de cadena añadido por EIP-155 (2016) | gasPrice | ID de cadena incorporado en v |
0x1 lista de acceso | EIP-2930, Berlin (abril de 2021) | gasPrice | accessList |
0x2 comisión dinámica | EIP-1559, London (agosto de 2021) | maxFeePerGas, maxPriorityFeePerGas | accessList |
0x3 blob | EIP-4844, Dencun (marzo de 2024) | Como el tipo 2, más maxFeePerBlobGas | blobVersionedHashes |
0x4 establecimiento de código | EIP-7702, Pectra (mayo de 2025) | Como el tipo 2 | authorizationList |
Todos los tipos comparten nonce (el contador de transacciones del remitente), gas (el límite de gas), to, value (en wei), input (los datos de llamada) y una firma (v o yParity, r, s). Tenga en cuenta que from no es un campo: los nodos recuperan el remitente a partir de la firma y lo añaden a las respuestas de la API. En una transacción heredada, el ID de cadena se codifica en v como chainId × 2 + 35 o 36; las transacciones tipadas incluyen un chainId explícito.
Dos tipos más recientes son importantes para los analistas:
- Las transacciones de blobs (tipo 3) transportan grandes blobs de datos para rollups. Los blobs no forman parte del bloque de ejecución: se transmiten como archivos auxiliares de la capa de consenso que los nodos solo deben conservar durante 4.096 épocas, unos 18 días. La transacción conserva únicamente el hash versionado de cada blob (primer byte
0x01). Una transacción de tipo 3 debe tener una direcciónto, por lo que no puede crear un contrato. - Las transacciones de establecimiento de código (tipo 4) permiten que una cuenta de propiedad externa (EOA) delegue en código de contrato. Cada entrada de la lista de autorizaciones está firmada por el propietario de la cuenta y contiene
chainId,address,nonce,yParity,rys. Tras el procesamiento, el código de la cuenta es el designador de delegación de 23 bytes0xef0100seguido de la dirección del delegado; delegar en la dirección cero lo elimina. A partir de ese momento, las llamadas a esa EOA ejecutan el código del delegado, y la cuenta que firmó la autorización no tiene por qué ser la que pagó el gas. Se han utilizado delegaciones maliciosas para vaciar billeteras, por lo que conviene prestar atención sieth_getCodedevuelve0xef0100…para una dirección aparentemente ordinaria. La lección El modelo de cuentas y la reconstrucción de saldos analiza las consecuencias.
Esta es una transacción de tipo 2 tal como la devuelve eth_getTransactionByHash, con los valores largos truncados:
{
"type": "0x2", "chainId": "0x1", "nonce": "0x2a",
"hash": "0x7e4b…a3c0", "blockNumber": "0x1406f52", "transactionIndex": "0x5",
"from": "0xa1b2…009f", "to": "0x5d3a…7c10", "value": "0x0",
"gas": "0x186a0", "maxFeePerGas": "0x9502f9000", "maxPriorityFeePerGas": "0x3b9aca00",
"input": "0xa9059cbb000000000000000000000000c3d4…22e1000…009502f900",
"accessList": [], "yParity": "0x1", "r": "0x8f1e…", "s": "0x2c07…"
}
Todas las cantidades son cadenas hexadecimales: el límite de gas 0x186a0 es 100.000, la comisión máxima 0x9502f9000 es 40.000.000.000 wei (40 gwei) por unidad de gas y la propina 0x3b9aca00 es 1 gwei. El campo value es cero y to es un contrato: se trata de una llamada, no de un pago en ETH.
Recibos y estado
Se genera un recibo cuando se ejecuta la transacción. eth_getTransactionReceipt devuelve:
| Campo | Significado |
|---|---|
status | 0x1 éxito o 0x0 fallo (EIP-658, Byzantium, octubre de 2017; los recibos anteriores incluyen root en su lugar) |
gasUsed, cumulativeGasUsed | Gas utilizado por esta transacción y por el bloque hasta ella inclusive |
effectiveGasPrice | Precio realmente pagado por unidad de gas |
logs, logsBloom | Eventos emitidos y su filtro de Bloom |
contractAddress | Dirección creada, en los despliegues; en los demás casos, null |
blobGasUsed, blobGasPrice | Presentes en las transacciones de blobs |
type, from, to, blockHash, blockNumber, transactionIndex | Identificadores |
Para una transacción de tipo 2, el precio efectivo es el menor entre maxFeePerGas y la comisión base más maxPriorityFeePerGas. Si nuestro ejemplo se ejecutó con una comisión base de 12 gwei y utilizó 52.000 unidades de gas, el precio fue min(40, 12 + 1) = 13 gwei y la comisión fue 52.000 × 13 = 676.000 gwei, o 0,000676 ETH. Una transacción de blobs también paga blobGasUsed × blobGasPrice.
Cuando status es 0x0, el valor no se transfirió, se descartaron todos los registros y se deshicieron los cambios de estado, pero el nonce se consumió y la comisión se pagó. status solo describe la transacción en su conjunto: una llamada interna puede fallar aunque la transacción tenga éxito, algo que solo revelan las trazas.
Registros y temas
Un registro (evento) es una entrada que un contrato escribe durante la ejecución. Cada uno tiene:
address: el contrato que lo emitió;topics: de cero a cuatro valores de 32 bytes;data: los bytes restantes codificados según la ABI;- campos de posición:
logIndex(posición en el bloque),transactionHash,transactionIndex,blockNumber,blockHashyremoved, que es true cuando una reorganización eliminó el registro.
Para un evento estándar de Solidity (no anónimo), topics[0] es el hash Keccak-256 de la firma del evento y los temas siguientes contienen hasta tres parámetros indexed. El evento Transfer de ERC-20 se decodifica así:
{
"address": "0x5d3a…7c10",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000a1b2…009f",
"0x000000000000000000000000c3d4…22e1"
],
"data": "0x000000000000000000000000000000000000000000000000000000009502f900",
"logIndex": "0x1c", "removed": false
}
topics[0] es el keccak256 de Transfer(address,address,uint256). Los temas 1 y 2 son las direcciones from y to, rellenadas a la izquierda hasta alcanzar 32 bytes: elimine los primeros 12 bytes para recuperarlas. data contiene el value no indexado: 0x9502f900 equivale a 2.500.000.000 unidades en bruto, es decir, 2.500 tokens si el contrato indica 6 decimales. Compruebe siempre address. Cualquiera puede desplegar un contrato que emita un evento Transfer perfectamente formado, y los tokens falsos que imitan a otros conocidos hacen precisamente eso; la relevancia del evento depende del contrato que lo escribió.
Datos de entrada y fundamentos de la decodificación ABI
El campo input indica a un contrato qué función debe ejecutar. Sus primeros 4 bytes son el selector de función, el inicio del hash Keccak-256 de la firma de la función: transfer(address,uint256) produce 0xa9059cbb. Los argumentos siguen en palabras de 32 bytes según las reglas de codificación ABI: aquí, el destinatario con relleno c3d4…22e1 y, después, la cantidad 0x9502f900.
La decodificación requiere la ABI del contrato (la descripción de su interfaz), publicada junto con el código fuente verificado o reconstruida a partir de bases de datos públicas de selectores. Los selectores solo tienen 4 bytes, por lo que distintas firmas pueden colisionar, y una coincidencia en una base de datos es una hipótesis, no una prueba. Trate los datos de entrada como la solicitud, y los registros, las trazas y el estado como el resultado: una llamada a transfer que se revirtió no transfirió nada. La lección Decodificación de interacciones con contratos inteligentes aborda los proxies, multicall y el código fuente verificado.
Trazas de llamadas internas
Cuando un contrato llama a otro contrato o envía ETH, esa llamada interna no es una transacción y no genera ningún registro, a menos que el código emita uno. Solo es visible al volver a ejecutar la transacción con generación de trazas. Existen dos estilos de salida:
- Estilo Geth:
debug_traceTransactionconcallTracerdevuelve un árbol anidado de marcos de llamada, cada uno contype(CALL, STATICCALL, DELEGATECALL, CREATE, CREATE2, SELFDESTRUCT),from,to,value,gas,gasUsed,input,output,errory llamadas secundariascalls. - Estilo Parity:
trace_transactionytrace_block, ofrecidos por clientes como Erigon, Nethermind y Reth, devuelven una lista plana en la quetraceAddressindica la posición de cada llamada en el árbol yaction.callType, su tipo.
Las trazas responden a preguntas que ninguna otra fuente puede resolver: cuánto ETH pagó un contrato, qué subllamada falló y en qué contexto se ejecutó el código (una DELEGATECALL ejecuta el código de otro contrato sobre el almacenamiento y el saldo del contrato que llama). Para generar trazas de transacciones antiguas se necesita un nodo capaz de reconstruir el estado histórico, normalmente un nodo de archivo. Excluir los marcos que contienen un error, así como sus descendientes, evita contabilizar valor que nunca se transfirió; la lección Consultas de datos UTXO y de registros y trazas EVM muestra cómo hacerlo en SQL.
Cómo combinar los cuatro objetos
Leer el ejemplo completo de principio a fin muestra cómo se complementan los objetos. La transacción indica que 0xa1b2…009f solicitó al contrato 0x5d3a…7c10 ejecutar transfer para el destinatario c3d4…22e1 por 2.500.000.000 unidades en bruto, sin pagar ETH. El recibo indica que la solicitud tuvo éxito (status 0x1) y costó 0,000676 ETH. El registro, emitido por el mismo contrato, confirma que los saldos del token cambiaron: 2.500 tokens de a1b2…009f a c3d4…22e1. Como la llamada fue una transferencia directa de tokens, una traza mostraría un solo marco y no aportaría nada más.
Si cambia un detalle, cambia la interpretación. Si el usuario hubiera llamado a un enrutador DEX, la transacción solo mostraría la dirección del enrutador y una solicitud de intercambio; los registros mostrarían varios eventos Transfer de distintos contratos de tokens; y cualquier ETH pagado aparecería únicamente en las trazas. La regla práctica: comience por la transacción, compruebe el estado del recibo, obtenga los movimientos de tokens de los registros y los movimientos de ETH de value y de las trazas.
Enfoque del examen: los cinco tipos de transacciones (del 0 al 4), sus EIP, actualizaciones y campos distintivos; quefromse recupera a partir de la firma; las transacciones de blobs, los archivos auxiliares y la conservación de blobs durante unos 18 días; las autorizaciones de EIP-7702 y el designador de delegación0xef0100; los campos del recibo,status, el precio efectivo del gas y lo que sigue costando una transacción fallida; la estructura de los registros,topics[0]como hash de la firma del evento y la decodificación del evento Transfer; los selectores de función y por qué las coincidencias de selectores son hipótesis; las trazas de estilo Geth y de estilo Parity para las llamadas internas.
Preguntas de práctica
Comprueba lo que has aprendido. Estas preguntas no cuentan para el examen.
Inicia sesión para guardar tu progreso y continuar donde lo dejaste. Es gratis. Iniciar sesión