---
eip: 7906
title: Transaction Assertions via State Diff Opcode
description: An opcode that provides a mechanism to restrict the outcomes of transaction execution
author: Alex Forshtat (@forshtat), Shahaf Nacson (@shahafn), Dror Tirosh (@drortirosh), Yoav Weiss (@yoavw)
discussions-to: https://ethereum-magicians.org/t/eip-restricted-behavior-transaction-type/23130
status: Draft
type: Standards Track
category: Core
created: 2025-02-21
---

## Abstract

This proposal introduces a new opcode that allows contracts to inspect the transaction outcomes on-chain. This opcode will allow contract deveolopres to define assertions for state changes that can be enforced on-chain. These can protect Ethereum users by restricting the behavior of the smart contracts they are interacting with. 

## Motivation

The total value of crypto assets that have been stolen to date exceeds the yearly GDP of a medium-sized nation. This level of loss and waste is indefensible and has a long list of negative consequences for everyone around the world.

The ability of an average user or a Wallet application to find, collect, review, and analyze the EVM code the transaction will execute is very limited.

This leaves the users with no mechanism to enforce any restrictions on what the transaction actually does once it is signed. This leads users to perform de-facto blind signing every time they interact with Ethereum, exposing themselves to significant risks.

By providing the Wallets and dApps with the ability to observe and restrict the possible **outcomes** of a transaction, we create a tool that users and  apply to reduce their risk levels.

## Specification

### Constants

| Name            | Value |
|-----------------|-------|
| TXTRACE_GAS_COST| TBD   |
| EVENTDATACOPY_GAS_COST   | TBD   |

### Transaction Trace Opcode 

We introduce a new `TXTRACE` opcode.

It can be used to retrieve the full state diff of the current transaction up to this point.

It accepts a `(param, index)` inputs similar to the `FRAMEPARAM` opcode from [EIP-8141](./eip-8141.md).
The available parameters are listed in the table below.

| `param` | `in2`                         | Return value                                                                |
|---------|-------------------------------|-----------------------------------------------------------------------------|
| 0x00    | must be 0                     | `balances_changed` - the total number of changed balances                   |
| 0x01    | must be 0                     | `slots_changed` - the total number of changes storage slots                 |
| 0x02    | must be 0                     | `contracts_deployed` - the total number of newly deployed contracts         |
| 0x03    | index in `balances_changed`   | `change_address` - the address of the account with balance change           |
| 0x04    | index in `balances_changed`   | `balance_before` - the balance of the address at the start of the transaction |
| 0x05    | index in `balances_changed`   | `balance_after` - the balance of the address as of this `TXTRACE` call      |
| 0x06    | index in `slots_changed`      | `change_address` - the address of the account with storage change           |
| 0x07    | index in `slots_changed`      | `slot_key` - the storage slot key that was changed                          |
| 0x08    | index in `slots_changed`      | `slot_value_before` - the value of the slot at the start of the transaction |
| 0x09    | index in `slots_changed`      | `slot_value_after` - the value of the slot as of this `TXTRACE` call        |
| 0x0A    | index in `contracts_deployed` | `deployed_address` - the address of the newly deployed contract             |
| 0x0B    | index in `contracts_deployed` | `codehash_after` - the codehash of the newly deployed contract              |
| 0x0C    | must be 0                     | `events_count` - the total number of emitted events                         |
| 0x0D    | index in `events_count`       | `events_address` - the address of the contract that emitted the event       |
| 0x0E    | index in `events_count`       | `events_topics` - the topics of the event (packed 32-byte values)           |
| 0x0F    | index in `events_count`       | `events_data` - the non-indexed data of the event                           |

#### State Difference Semantics

The `before` values reflect the transaction prestate values recorded before the start of entire transaction's execution, before any state writes made in relation to this transaction. The `after` values reflect the current state as of the `TXTRACE` opcode call. Intermediary writes between transaction start and the `TXTRACE` call are not observable separately.

An address will appear in `balances_changed` when its balance at the time of the `TXTRACE` call differs from its balance at transaction start. This includes the gas fee pre-charge applied to the gas payer address. Callers computing the net ETH transferred to or from an address should account for this difference equal to total transaction gas pre-charge.

#### `EVENTDATACOPY` opcode

This opcode copies event data into memory. The gas cost matches `CALLDATACOPY`, i.e. the operation has a fixed cost of 3 and a variable cost that accounts for the memory expansion and copying.

##### Stack

| Stack      | Value           |
|------------|-----------------|
| `top - 0`  | `event_index`   |
| `top - 1`  | `memOffset`     |
| `top - 2`  | `dataOffset`    |
| `top - 3`  | `length`        |

No stack output value is produced.

##### Behavior

The operation semantics match `CALLDATACOPY`, copying `length` bytes from the event's non-indexed data, starting at the given byte `dataOffset`, into a memory region starting at `memOffset`.

- If `event_index >= events_count`, an exceptional halt occurs.
- If `dataOffset + length` exceeds the event's data length, an exceptional halt occurs.

## Rationale

### Selection Parameter Design

The `TXTRACE` opcode follows the same `(param, index)` two-argument pattern used by `FRAMEPARAM` in [EIP-8141](./eip-8141.md). This keeps the interface consistent and avoids introducing a separate opcode for every piece of trace information.

### Use with Frame Transactions

When used within an [EIP-8141](./eip-8141.md) frame transaction, placing the assertion logic in the last frame ensures the diff is final and the assertion can reason about the full transaction outcome.

### Per-contract Usage

Individual contracts can use the `TXTRACE` opcode to inspect the state changes made internally, using a pattern similar to "reentrancy guard" modifier for their external functions.

### `EVENTDATACOPY` as a Companion Opcode

Event non-indexed data is variable-length and cannot be returned as a single 32-byte stack word. A memory-copy opcode with the same semantics as `CALLDATACOPY` is the idiomatic EVM approach for variable-length data access.

## Backwards Compatibility

`TXTRACE` and `EVENTDATACOPY` occupy previously unused opcode slots. No changes are made to existing opcodes, transaction types, or precompiles, so existing contracts and tooling are unaffected.

## Security Considerations

### Insufficiently Restrictive Assertions

The main risk is a false sense of security: an assertion contract that checks too little may mislead users into believing a transaction is safe when it is not.

Wallets and dApps that build on `TXTRACE` must ensure their assertion logic covers all relevant state changes for the protected operation. It is critical that the ecosystem treats incomplete assertions as no better than no assertion at all.

## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).
