Unformatierte Blockchain-Daten lesen · Lektion 4 von 6
Rohe Ethereum-Transaktionen, Receipts und Logs
8 Min. Lesezeit
Auf Ethereum und anderen EVM-Chains hält eine Transaktion fest, was ein Sender angefordert hat, während Receipt, Logs und Traces dokumentieren, was tatsächlich geschehen ist. Token-Bewegungen, fehlgeschlagene Aufrufe und von Contracts übertragene Werte erscheinen niemals in der Transaktion selbst. Wer nur Transaktionen liest, übersieht den Großteil der Aktivitäten auf der Chain; wer die vier Objekte kennt, kann genau sagen, was bewegt wurde, wer es ausgelöst hat und ob es erfolgreich war.
Transaktionsfelder und die fünf Typen
Seit EIP-2718 (2021) ist eine Ethereum-Transaktion ein typisiertes Envelope: ein Typ-Byte gefolgt von einer Payload, deren Felder vom Typ abhängen. Legacy-Transaktionen, die dem Envelope vorausgingen, zählen als Typ 0.
| Typ | Eingeführt | Gebührenfelder | Zusätzliche Felder |
|---|---|---|---|
0x0 Legacy | Ursprünglich; Chain-ID durch EIP-155 ergänzt (2016) | gasPrice | Chain-ID in v eingebettet |
0x1 Access List | EIP-2930, Berlin (April 2021) | gasPrice | accessList |
0x2 Dynamische Gebühr | EIP-1559, London (August 2021) | maxFeePerGas, maxPriorityFeePerGas | accessList |
0x3 Blob | EIP-4844, Dencun (März 2024) | Wie Typ 2, zuzüglich maxFeePerBlobGas | blobVersionedHashes |
0x4 Set Code | EIP-7702, Pectra (Mai 2025) | Wie Typ 2 | authorizationList |
Alle Typen haben gemeinsam: nonce (Transaktionszähler des Senders), gas (Gaslimit), to, value (in Wei), input (Aufrufdaten) und eine Signatur (v oder yParity, r, s). Beachten Sie, dass from kein Feld ist: Nodes leiten den Sender aus der Signatur ab und fügen ihn API-Antworten hinzu. Bei einer Legacy-Transaktion ist die Chain-ID in v als chainId × 2 + 35 oder 36 codiert; typisierte Transaktionen enthalten eine explizite chainId.
Zwei neuere Typen sind für Analysten wichtig:
- Blob-Transaktionen (Typ 3) enthalten große Daten-Blobs für Rollups. Die Blobs sind nicht Teil des Ausführungsblocks: Sie werden als Sidecars der Consensus Layer übertragen, die Nodes nur 4.096 Epochen lang, also etwa 18 Tage, aufbewahren müssen. Die Transaktion enthält nur den versionierten Hash jedes Blobs (erstes Byte
0x01). Eine Transaktion vom Typ 3 muss eineto-Adresse haben und kann daher keinen Contract erstellen. - Set-Code-Transaktionen (Typ 4) ermöglichen es einem extern verwalteten Konto (EOA), an Contract-Code zu delegieren. Jeder Eintrag der Autorisierungsliste ist vom Kontoinhaber signiert und enthält
chainId,address,nonce,yParity,runds. Nach der Verarbeitung besteht der Code des Kontos aus dem 23 Byte langen Delegierungs-Designator0xef0100, gefolgt von der Adresse des Delegierten; eine Delegierung an die Nulladresse entfernt ihn. Von da an führen Aufrufe an dieses EOA den Code des Delegierten aus, und das Konto, das die Autorisierung signiert hat, muss nicht das Konto sein, das die Gasgebühr bezahlt hat. Böswillige Delegierungen wurden zum Leeren von Wallets eingesetzt. Daher ist es erwähnenswert, wenneth_getCodefür eine scheinbar gewöhnliche Adresse0xef0100…zurückgibt. Die Lektion Das Kontomodell für Analysten behandelt die Folgen.
Hier ist eine Transaktion vom Typ 2, wie eth_getTransactionByHash sie zurückgibt, mit gekürzten langen Werten:
{
"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…"
}
Alle Mengenangaben sind hexadezimale Zeichenfolgen: Das Gaslimit 0x186a0 entspricht 100.000, die maximale Gebühr 0x9502f9000 entspricht 40.000.000.000 Wei (40 Gwei) pro Gas und das Trinkgeld 0x3b9aca00 entspricht 1 Gwei. Der value ist null und to ist ein Contract: Es handelt sich um einen Aufruf, nicht um eine ETH-Zahlung.
Receipts und Status
Ein Receipt wird erzeugt, wenn die Transaktion ausgeführt wird. eth_getTransactionReceipt gibt Folgendes zurück:
| Feld | Bedeutung |
|---|---|
status | 0x1 Erfolg oder 0x0 Fehlschlag (EIP-658, Byzantium, Oktober 2017; ältere Receipts enthalten stattdessen root) |
gasUsed, cumulativeGasUsed | Von dieser Transaktion verbrauchtes Gas und das vom Block bis einschließlich dieser Transaktion verbrauchte Gas |
effectiveGasPrice | Tatsächlich pro Gas bezahlter Preis |
logs, logsBloom | Ausgegebene Events und ihr Bloom-Filter |
contractAddress | Bei Deployments die erstellte Adresse, andernfalls null |
blobGasUsed, blobGasPrice | Bei Blob-Transaktionen vorhanden |
type, from, to, blockHash, blockNumber, transactionIndex | Kennungen |
Bei einer Transaktion vom Typ 2 entspricht der effektive Preis dem niedrigeren Wert aus maxFeePerGas und Basisgebühr plus maxPriorityFeePerGas. Wenn unser Beispiel mit einer Basisgebühr von 12 Gwei ausgeführt worden wäre und 52.000 Gas verbraucht hätte, hätte der Preis min(40, 12 + 1) = 13 Gwei betragen und die Gebühr 52.000 × 13 = 676.000 Gwei oder 0,000676 ETH. Eine Blob-Transaktion zahlt zusätzlich blobGasUsed × blobGasPrice.
Wenn status den Wert 0x0 hat, wurde der Wert nicht übertragen, jedes Log wurde verworfen und die Zustandsänderungen wurden rückgängig gemacht. Die Nonce wurde jedoch verbraucht und die Gebühr bezahlt. status beschreibt nur die Transaktion als Ganzes: Ein interner Aufruf kann fehlschlagen, während die Transaktion erfolgreich ist – das zeigen nur Traces.
Logs und Topics
Ein Log (Event) ist ein Datensatz, den ein Contract während der Ausführung schreibt. Jeder Log enthält:
address: den Contract, der ihn ausgegeben hat;topics: null bis vier 32-Byte-Werte;data: die übrigen ABI-kodierten Bytes;- Positionsfelder:
logIndex(Position im Block),transactionHash,transactionIndex,blockNumber,blockHashundremoved, das den Wert true hat, wenn eine Reorganisation den Log entfernt hat.
Bei einem standardmäßigen (nicht anonymen) Solidity-Event ist topics[0] der Keccak-256-Hash der Event-Signatur, und die folgenden Topics enthalten bis zu drei indexed-Parameter. Das ERC-20-Transfer-Event wird wie folgt dekodiert:
{
"address": "0x5d3a…7c10",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000a1b2…009f",
"0x000000000000000000000000c3d4…22e1"
],
"data": "0x000000000000000000000000000000000000000000000000000000009502f900",
"logIndex": "0x1c", "removed": false
}
topics[0] ist der keccak256-Hash von Transfer(address,address,uint256). Topics 1 und 2 sind die from- und to-Adressen, links mit Nullen auf 32 Byte aufgefüllt: Entfernen Sie die ersten 12 Byte, um sie wiederherzustellen. data enthält den nicht indizierten value: 0x9502f900 entspricht 2.500.000.000 Rohwerteinheiten, also 2.500 Token, wenn der Contract 6 Dezimalstellen angibt. Prüfen Sie immer address. Jeder kann einen Contract bereitstellen, der ein vollkommen korrekt aufgebautes Transfer-Event ausgibt; gefälschte Token, die bekannte Token nachahmen, tun genau das. Wie aussagekräftig das Event ist, hängt allein vom Contract ab, der es geschrieben hat.
Input-Daten und Grundlagen der ABI-Dekodierung
Das Feld input teilt einem Contract mit, welche Funktion ausgeführt werden soll. Seine ersten 4 Byte sind der Funktionsselektor, der Anfang des Keccak-256-Hashes der Funktionssignatur: transfer(address,uint256) ergibt 0xa9059cbb. Die Argumente folgen gemäß den ABI-Kodierungsregeln in 32-Byte-Wörtern: hier zuerst der aufgefüllte Empfänger c3d4…22e1, dann der Betrag 0x9502f900.
Für die Dekodierung wird die ABI des Contracts benötigt (seine Schnittstellenbeschreibung), die mit verifiziertem Quellcode veröffentlicht oder anhand öffentlicher Selector-Datenbanken rekonstruiert werden kann. Selektoren sind nur 4 Byte lang, sodass unterschiedliche Signaturen kollidieren können. Ein Treffer in einer Datenbank ist daher eine Hypothese und kein Beweis. Behandeln Sie input als Anforderung und Logs, Traces und Zustand als Ergebnis: Ein Aufruf von transfer, der zurückgesetzt wurde, hat nichts bewegt. Die Lektion Dekodierung von Smart-Contract-Interaktionen behandelt Proxies, Multicall und verifizierten Quellcode.
Traces für interne Aufrufe
Wenn ein Contract einen anderen Contract aufruft oder ETH sendet, ist dieser interne Aufruf keine Transaktion und erzeugt kein Log, es sei denn, der Code gibt eines aus. Sichtbar wird er nur, wenn die Transaktion mit Tracing erneut ausgeführt wird. Es gibt zwei Ausgabestile:
- Geth-Stil:
debug_traceTransactionmit demcallTracergibt einen verschachtelten Baum von Aufrufrahmen zurück. Jeder enthälttype(CALL, STATICCALL, DELEGATECALL, CREATE, CREATE2, SELFDESTRUCT),from,to,value,gas,gasUsed,input,output,errorund untergeordnetecalls. - Parity-Stil:
trace_transactionundtrace_block, angeboten von Clients wie Erigon, Nethermind und Reth, geben eine flache Liste zurück, in dertraceAddressdie Position jedes Aufrufs im Baum undaction.callTypedessen Art angibt.
Traces beantworten Fragen, die sich mit nichts anderem beantworten lassen: ETH-Auszahlungen eines Contracts, welcher Unteraufruf fehlgeschlagen ist und in welchem Kontext Code ausgeführt wurde (ein DELEGATECALL führt den Code eines anderen Contracts mit dem Speicher und Guthaben des aufrufenden Contracts aus). Für das Tracing alter Transaktionen wird ein Node benötigt, der den historischen Zustand rekonstruieren kann, üblicherweise ein Archive-Node. Frames mit einem error und ihre untergeordneten Frames herauszufiltern, verhindert, dass Werte gezählt werden, die nie bewegt wurden. Die Lektion EVM-Logs und Traces abfragen zeigt, wie sich dies in SQL umsetzen lässt.
Die vier Objekte zusammenführen
Das durchgängige Lesen des Beispiels zeigt, wie sich die Objekte ergänzen. Die Transaktion besagt, dass 0xa1b2…009f den Contract 0x5d3a…7c10 aufgefordert hat, transfer für den Empfänger c3d4…22e1 und 2.500.000.000 Rohwerteinheiten auszuführen, ohne ETH zu bezahlen. Das Receipt besagt, dass die Anfrage erfolgreich war (status 0x1) und 0,000676 ETH gekostet hat. Das vom selben Contract ausgegebene Log bestätigt, dass sich die Token-Guthaben geändert haben: 2.500 Token von a1b2…009f zu c3d4…22e1. Da es sich um eine direkte Token-Übertragung handelte, würde ein Trace nur einen Aufrufrahmen zeigen und keine zusätzlichen Informationen liefern.
Ändert man ein Detail, ändert sich die Auswertung. Hätte der Nutzer stattdessen einen DEX-Router aufgerufen, würde die Transaktion nur die Router-Adresse und eine Swap-Anfrage zeigen; die Logs würden mehrere Transfer-Events verschiedener Token-Contracts zeigen; und ausgezahltes ETH wäre nur in den Traces sichtbar. Als Faustregel gilt: Beginnen Sie mit der Transaktion, prüfen Sie den Status im Receipt und ermitteln Sie Token-Bewegungen aus Logs sowie ETH-Bewegungen aus value und Traces.
Prüfungsschwerpunkt: die fünf Transaktionstypen (0 bis 4), ihre EIPs, Upgrades und unterscheidenden Felder; dassfromaus der Signatur abgeleitet wird; Blob-Transaktionen, Sidecars und die etwa 18-tägige Aufbewahrung von Blobs; EIP-7702-Autorisierungen und der Delegierungs-Designator0xef0100; Receipt-Felder,status, der effektive Gaspreis und die Kosten einer fehlgeschlagenen Transaktion; die Struktur von Logs,topics[0]als Hash der Event-Signatur und die Dekodierung des Transfer-Events; Funktionsselektoren und weshalb Treffer bei Selektoren nur Hypothesen sind; Geth- und Parity-Traces für interne Aufrufe.