Dados on-chain e SQL · Aula 4 de 9
Transações brutas, recibos e logs do Ethereum
9 min de leitura
No Ethereum e em outras redes EVM, uma transação registra o que um remetente solicitou, enquanto o recibo, os logs e os rastros registram o que realmente aconteceu. Movimentações de tokens, chamadas que falharam e valores movimentados por contratos nunca aparecem na própria transação. Um analista que lê apenas transações deixa de observar a maior parte da atividade na rede; quem conhece os quatro objetos pode dizer precisamente o que foi movimentado, quem iniciou a movimentação e se ela foi bem-sucedida.
Campos das transações e os cinco tipos
Desde a EIP-2718 (2021), uma transação Ethereum é um envelope tipado: um byte de tipo seguido de uma carga útil cujos campos dependem do tipo. As transações legadas, que antecedem o envelope, são consideradas do tipo 0.
| Tipo | Introdução | Campos de taxas | Campos adicionais |
|---|---|---|---|
0x0 legada | Original; ID da rede adicionado pela EIP-155 (2016) | gasPrice | ID da rede incorporado a v |
0x1 lista de acesso | EIP-2930, Berlin (abril de 2021) | gasPrice | accessList |
0x2 taxa dinâmica | EIP-1559, London (agosto de 2021) | maxFeePerGas, maxPriorityFeePerGas | accessList |
0x3 blob | EIP-4844, Dencun (março de 2024) | Como no tipo 2, mais maxFeePerBlobGas | blobVersionedHashes |
0x4 definição de código | EIP-7702, Pectra (maio de 2025) | Como no tipo 2 | authorizationList |
Todos os tipos compartilham nonce (o contador de transações do remetente), gas (o limite de gas), to, value (em wei), input (dados da chamada) e uma assinatura (v ou yParity, r, s). Observe que from não é um campo: os nós recuperam o remetente a partir da assinatura e o adicionam às respostas da API. Em uma transação legada, o ID da rede é codificado em v como chainId × 2 + 35 ou 36; transações tipadas contêm um chainId explícito.
Dois tipos mais recentes são importantes para os analistas:
- Transações blob (tipo 3) transportam grandes blobs de dados para rollups. Os blobs não fazem parte do bloco de execução: eles trafegam como sidecars da camada de consenso que os nós precisam manter por apenas 4.096 épocas, cerca de 18 dias. A transação mantém apenas o hash versionado de cada blob (primeiro byte
0x01). Uma transação do tipo 3 deve ter um endereçoto, portanto não pode criar um contrato. - Transações de definição de código (tipo 4) permitem que uma conta de propriedade externa (EOA) delegue a um código de contrato. Cada entrada da lista de autorizações é assinada pelo proprietário da conta e contém
chainId,address,nonce,yParity,res. Após o processamento, o código da conta é o designador de delegação de 23 bytes0xef0100seguido do endereço do delegado; delegar ao endereço zero remove esse código. A partir daí, chamadas a essa EOA executam o código do delegado, e a conta que assinou a autorização não precisa ser a mesma que pagou o gas. Delegações maliciosas têm sido usadas para esvaziar carteiras, portanto vale observar quandoeth_getCoderetorna0xef0100…para um endereço aparentemente comum. A aula O modelo de contas e a reconstrução de saldos discute as consequências.
Veja uma transação do tipo 2 conforme retornada por eth_getTransactionByHash, com valores longos 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 as quantidades são strings hexadecimais: o limite de gas 0x186a0 é 100.000, a taxa máxima 0x9502f9000 é 40.000.000.000 wei (40 gwei) por gas e a gorjeta 0x3b9aca00 é 1 gwei. O value é zero e to é um contrato: trata-se de uma chamada, não de um pagamento em ETH.
Recibos e status
Um recibo é produzido quando a transação é executada. eth_getTransactionReceipt retorna:
| Campo | Significado |
|---|---|
status | 0x1 para sucesso ou 0x0 para falha (EIP-658, Byzantium, outubro de 2017; recibos mais antigos contêm root em vez disso) |
gasUsed, cumulativeGasUsed | Gas usado por esta transação e pelo bloco até ela, inclusive |
effectiveGasPrice | Preço efetivamente pago por gas |
logs, logsBloom | Eventos emitidos e seu filtro de Bloom |
contractAddress | Endereço criado, para implantações; caso contrário, nulo |
blobGasUsed, blobGasPrice | Presentes em transações blob |
type, from, to, blockHash, blockNumber, transactionIndex | Identificadores |
Para uma transação do tipo 2, o preço efetivo é o menor valor entre maxFeePerGas e a taxa base mais maxPriorityFeePerGas. Se nosso exemplo foi executado com uma taxa base de 12 gwei e usou 52.000 gas, o preço foi min(40, 12 + 1) = 13 gwei e a taxa foi 52.000 × 13 = 676.000 gwei, ou 0,000676 ETH. Uma transação blob também paga blobGasUsed × blobGasPrice.
Quando status é 0x0, o valor não foi movimentado, todos os logs foram descartados e as alterações de estado foram desfeitas, mas o nonce foi consumido e a taxa foi paga. status descreve apenas a transação como um todo: uma chamada interna pode falhar enquanto a transação é bem-sucedida, algo que apenas os rastros revelam.
Logs e tópicos
Um log (evento) é um registro que um contrato grava durante a execução. Cada um contém:
address: o contrato que o emitiu;topics: de zero a quatro valores de 32 bytes;data: os bytes restantes codificados segundo a ABI;- campos de posição:
logIndex(posição no bloco),transactionHash,transactionIndex,blockNumber,blockHasheremoved, verdadeiro quando uma reorganização removeu o log.
Para um evento Solidity padrão (não anônimo), topics[0] é o hash Keccak-256 da assinatura do evento e os tópicos seguintes contêm até três parâmetros indexed. O evento Transfer do ERC-20 é decodificado assim:
{
"address": "0x5d3a…7c10",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000a1b2…009f",
"0x000000000000000000000000c3d4…22e1"
],
"data": "0x000000000000000000000000000000000000000000000000000000009502f900",
"logIndex": "0x1c", "removed": false
}
topics[0] é o keccak256 de Transfer(address,address,uint256). Os tópicos 1 e 2 são os endereços from e to, preenchidos à esquerda até 32 bytes: remova os primeiros 12 bytes para recuperá-los. data contém o value não indexado: 0x9502f900 corresponde a 2.500.000.000 de unidades brutas, o que equivale a 2.500 tokens se o contrato informar 6 casas decimais. Sempre verifique address. Qualquer pessoa pode implantar um contrato que emita um evento Transfer perfeitamente formado, e tokens falsos que imitam tokens conhecidos fazem exatamente isso; o significado do evento depende do contrato que o gravou.
Dados de entrada e fundamentos da decodificação ABI
O campo input informa ao contrato qual função executar. Seus primeiros 4 bytes são o seletor de função, o início do hash Keccak-256 da assinatura da função: transfer(address,uint256) gera 0xa9059cbb. Os argumentos vêm em seguida, em palavras de 32 bytes, conforme as regras de codificação ABI: aqui, o destinatário preenchido c3d4…22e1, seguido da quantia 0x9502f900.
A decodificação exige a ABI do contrato (a descrição de sua interface), publicada com o código-fonte verificado ou reconstruída a partir de bases de dados públicas de seletores. Os seletores têm apenas 4 bytes, portanto assinaturas diferentes podem colidir, e uma correspondência em uma base de dados é uma hipótese, não uma prova. Trate os dados de entrada como a solicitação e os logs, os rastros e o estado como o resultado: uma chamada a transfer que foi revertida não movimentou nada. A aula Decodificação de interações com contratos inteligentes aborda proxies, multicall e código-fonte verificado.
Rastros de chamadas internas
Quando um contrato chama outro contrato ou envia ETH, essa chamada interna não é uma transação e não produz log, a menos que o código emita um. Ela só é visível ao reexecutar a transação com rastreamento. Existem dois estilos de saída:
- Estilo Geth:
debug_traceTransactioncom ocallTracerretorna uma árvore aninhada de quadros de chamada, cada um comtype(CALL, STATICCALL, DELEGATECALL, CREATE, CREATE2, SELFDESTRUCT),from,to,value,gas,gasUsed,input,output,errorecallsfilhas. - Estilo Parity:
trace_transactionetrace_block, oferecidos por clientes como Erigon, Nethermind e Reth, retornam uma lista plana na qualtraceAddressinforma a posição de cada chamada na árvore eaction.callType, seu tipo.
Os rastros respondem a perguntas que nenhum outro registro consegue responder: ETH pago por um contrato, qual subchamada falhou e em qual contexto o código foi executado (uma DELEGATECALL executa o código de outro contrato usando o armazenamento e o saldo de quem fez a chamada). Rastrear transações antigas exige um nó capaz de reconstruir o estado histórico, normalmente um nó de arquivo. Filtrar os quadros que contêm um error, bem como seus descendentes, evita contabilizar valores que nunca foram movimentados; a aula Consulta de dados UTXO e de logs e rastros EVM mostra como fazer isso em SQL.
Combinando os quatro objetos
A leitura do exemplo usado ao longo da aula, do início ao fim, mostra como os objetos se complementam. A transação informa que 0xa1b2…009f solicitou ao contrato 0x5d3a…7c10 a execução de transfer para o destinatário c3d4…22e1 e 2.500.000.000 de unidades brutas, sem pagar ETH. O recibo informa que a solicitação foi bem-sucedida (status 0x1) e custou 0,000676 ETH. O log, emitido pelo mesmo contrato, confirma que os saldos do token mudaram: 2.500 tokens de a1b2…009f para c3d4…22e1. Como a chamada foi uma transferência direta de tokens, um rastro mostraria apenas um quadro e não acrescentaria nada.
Altere um detalhe e a leitura muda. Se o usuário tivesse chamado um roteador de DEX, a transação mostraria apenas o endereço do roteador e uma solicitação de troca; os logs mostrariam vários eventos Transfer de diferentes contratos de tokens; e qualquer ETH pago apareceria apenas nos rastros. A regra prática: comece pela transação, verifique o status do recibo, obtenha as movimentações de tokens a partir dos logs e as movimentações de ETH a partir de value e dos rastros.
Foco no exame: os cinco tipos de transação (0 a 4), suas EIPs, atualizações e campos distintivos; o fato de quefromé recuperado a partir da assinatura; transações blob, sidecars e a retenção de blobs por aproximadamente 18 dias; autorizações da EIP-7702 e o designador de delegação0xef0100; campos do recibo,status, o preço efetivo do gas e o que uma transação que falha ainda custa; estrutura dos logs,topics[0]como o hash da assinatura do evento e decodificação do evento Transfer; seletores de função e por que correspondências de seletores são hipóteses; rastros nos estilos Geth e Parity para chamadas internas.
Questões de prática
Verifique o que você aprendeu. Estas questões não contam para o exame.
Entre para salvar seu progresso e continuar de onde parou. É grátis. Entrar