Ethereum event logs are contract-emitted records kept in transaction receipts. “Internal transactions” are explorer labels for selected contract calls or ETH movements reconstructed from execution traces. A trace can include failed calls whose value transfers and logs were rolled back, so neither a displayed call nor a successful outer receipt proves the recipient received funds.
One wallet action can produce many calls and several event logs. These records describe different aspects of the same execution. Keeping them together helps explain where ETH went without treating every displayed row as another payment.
One execution, several records
Start with the question you need to answer.
| Record | Question it helps answer | Important boundary |
|---|---|---|
| Transaction and receipt | Which submitted transaction executed, and did its top-level call succeed? | Success does not establish every nested outcome |
| Event log | What event data survived execution at a particular emitting address? | An event is not a complete list of calls or balance changes |
| Execution trace | Which calls or instructions ran, including failed branches? | Attempted operations can have no surviving state effect |
| Explorer internal-transfer row | Which execution movements did this explorer select and display? | Its label and selection rules belong to that explorer |
The ordinary transaction has a sender authorization, nonce and hash. Contract calls inside its execution do not each become another independently signed transaction. Ethereum’s transaction documentation defines those transaction fields.
A receipt supplies the transaction’s retained log list. The JSON-RPC receipt and log schemas expose the emitting address, topics, data and block context. The receipt’s status reports the top-level outcome, as specified by EIP-658.
A trace provides another view. Geth reconstructs execution by rerunning the transaction locally. Its tracing introduction explains why execution details require this additional work. The trace response is not another list of transactions submitted to the network.
Etherscan specifically describes an internal transaction as an ETH transfer through a smart contract intermediary in its transaction terminology guide. Treat that as a description of its display category. Another explorer or data provider may expose a broader call tree, exclude unsuccessful calls, or arrange rows differently.
Follow a payout with one rejected branch
Consider a hypothetical Ethereum layer 1 transaction from an ordinary account called User to a contract called Payout. Every amount and contract behavior here is invented. This is a state-transition illustration, not an observed transaction.
User begins with 2.000 ETH. Payout, Merchant and Rejector each begin with zero ETH. User sends 1.000 ETH to Payout and separately pays the network fee. Payout is designed to handle a rejected payment and refund all unused ETH.
Assume sufficient gas, no other relevant movements, and no events beyond those listed. Execute these operations in order:
| Step | Operation | Result | Surviving effect |
|---|---|---|---|
| 1 | User calls Payout with 1.000 ETH | Top-level execution begins | Payout receives the funding if execution ultimately succeeds |
| 2 | Payout emits Started | Continues | Started remains in the final receipt |
| 3 | Payout calls Merchant with 0.600 ETH, then emits Paid | Call succeeds | Merchant gains 0.600 ETH; Paid remains |
| 4 | Payout calls Rejector with 0.300 ETH | Rejector emits Attempt, then reverts | Rejector gains zero ETH; Attempt is discarded |
| 5 | Payout handles the failed call and refunds User 0.400 ETH | Refund succeeds | User receives 0.400 ETH |
| 6 | Payout emits Completed and returns successfully | Receipt status is 1 | Completed remains; the transaction commits |
The failed branch is possible because Payout deliberately checks the result of a low-level call and continues. The Solidity 0.8.30 error-handling documentation explains that low-level calls report failure rather than automatically propagating an exception into their caller. This is an explicit behavior of the hypothetical contract, not an assumption about every payout contract.
Rejector temporarily executes with the proposed value, emits its event and then reverts. That execution can be visible in a sufficiently detailed trace. Its state changes and logs do not survive. EIP-140 defines the rollback behavior of REVERT, including the loss of logs from the reverted execution.
The final receipt therefore has three events: Started, Paid and Completed. It has no surviving Attempt event. A call trace still has a failed branch to Rejector. An explorer view that displays only successful internal ETH movements would show the Merchant payment and User refund. That last description is a hypothetical display filter, not a promise about a particular explorer’s current output.
Reconcile value before counting activity
The internal calls attempted 0.600 ETH to Merchant, 0.300 ETH to Rejector and 0.400 ETH back to User. Adding their proposed values gives 1.300 ETH. Filtering for committed value movements leaves 1.000 ETH.
Neither figure is the sender’s net payment to Merchant.
| Measure | Amount | Meaning |
|---|---|---|
| Outer transaction value | 1.000 ETH | Funding delivered to Payout |
| Attempted internal call values | 1.300 ETH | Includes the rejected 0.300 ETH attempt |
| Committed internal value movements | 1.000 ETH | Merchant payment plus refund |
| Merchant’s net receipt | 0.600 ETH | Successful payment to the merchant |
| User’s net value outflow before fees | 0.600 ETH | Funding minus refund |
Adding outer value and committed internal movements produces 2.000 ETH of gross movement along the path. That does not establish 2.000 ETH of economic payment. The same funding entered Payout and then left it. Choose whether the report measures path activity, a recipient’s receipt, or a payer’s net outflow before aggregating.
For this example, execution gas is the only network fee component. Assume the transaction’s receipt records 100,000 gas used at an effective price of 20 gwei. The hypothetical execution fee is 0.002 ETH. Funding and refund leave User with 1.400 ETH before accounting for that fee, or 1.398 ETH after it.
The final balances reconcile as follows: User 1.398 ETH, Merchant 0.600 ETH, Rejector zero and Payout zero. Together these accounts hold 1.998 ETH, which is the initial 2.000 ETH less the network charge. The fee’s distribution or burn lies outside this four-account ledger.
Record the outer receipt’s actual charge once. Internal calls consume work within that execution; they are not separate network submissions with separate transaction fees to add again. The failed-swap gas worksheet explains receipt-based fee accounting across genuinely separate attempts.
A local success can still be rolled back
The Merchant branch above survives because all its enclosing execution completes successfully. Change the scenario so Payout later reverts, and the Merchant payment rolls back too. Looking only at Merchant’s locally successful call would produce the wrong result.
For each proposed movement, check the frame and its ancestors. A failure higher in that branch can erase the state effects of a child that returned successfully. A top-level failure rolls back the transaction’s execution state changes and logs; the included transaction can still incur a network fee.
A trace also contains operations that do not represent native-ETH payments. A zero-value call may execute substantial token or application logic. A DELEGATECALL executes another address’s code in the caller’s context while preserving the calling context’s value. The Solidity 0.8.30 explanation of delegatecall describes this distinction. Do not translate every displayed value field into another ETH transfer.
These checks prevent two different mistakes: counting a reverted payment as delivered, and interpreting a code-execution relationship as a value movement.
Read events as contract statements
An event is emitted because code executed a logging operation. Native ETH transfers do not automatically produce an ERC-20 Transfer event. In the illustration, Payout chooses to emit Paid after Merchant’s payment; a different contract could make the same ETH movement without that custom event.
Solidity’s event documentation, version 0.8.30, explains how events use EVM logs and how topics and data are interpreted. Preserve the raw emitter address and bytes before relying on a decoded event name.
A recognizable event signature also does not establish that the emitting contract is the asset you intended to inspect. Check the chain and contract address, then use the correct event definition. A custom event named Paid is a statement from that implementation, whose logic still needs interpretation.
Token accounting introduces another boundary. Token balances live in contract state; a native-value call tree cannot substitute for token transfer evidence. For that investigation, the guide to receipt success versus token transfer evidence separates execution status, logs and recipient balance changes.
Tracing depends on the endpoint and configuration
Geth’s documented callTracer returns nested frames with call type, addresses, value, errors and child calls. Its onlyTopCall setting deliberately omits subcalls; withLog requests additional per-call log collection. A response configured to omit subcalls cannot support a conclusion that no internal calls occurred.
The debug_traceTransaction documentation describes replaying the original transaction context. Whether your endpoint exposes that method, retains the required state and completes the request is a separate operational question. Record a method error or unavailable history as missing trace evidence, not as an empty execution tree.
Also distinguish a tracer’s diagnostic “logs” from the final receipt log list. Depending on the tracer and options, “logs” may mean instruction records or collected event data. Use the receipt to determine which event records survived, and the trace to investigate how execution reached that result.
Save evidence at the level of the claim
Use this worksheet when an explorer’s activity rows and a wallet balance seem inconsistent.
| Claim to verify | Evidence to save | Validation |
|---|---|---|
| This transaction executed | Chain, hash, receipt and inclusion block hash | Same transaction and canonical inclusion |
| This event survived | Raw receipt log, emitter, topics, data and log index | Present in that receipt; correct decoding |
| This call was attempted | Full trace, frame path, type and proposed value | Trace configuration includes relevant subcalls |
| This ETH payment survived | Frame result and enclosing outcomes | No enclosing rollback; amount reconciles |
| This account gained value | Correctly scoped balance evidence and fee treatment | Isolate this transaction’s effects |
| This explorer omitted a row | Provider, query, filters and response time | Understand selection and coverage before concluding absence |
For balance comparisons, record the exact state being read. Two calls labeled latest can observe changing state; the RPC batch versus block snapshot guide explains how to make multi-read comparisons consistent. Even pinned whole-block balances can include other transactions in that block, so they do not automatically isolate one transaction’s effect.
Finally, keep execution evidence separate from settlement status. The confirmations versus finality guide covers whether the inclusion remains accepted and meets the relevant settlement condition.
Close the record with a precise result: the call was attempted, the event survived, or the recipient gained the reconciled amount. In the payout example, the useful conclusion is that Merchant received 0.600 ETH, Rejector received zero, and User recovered 0.400 ETH while paying one transaction fee.
