| eip | 9999 |
|---|---|
| title | Invariant Layout Guard Opcode |
| description | Introduce a protocol-level state safety mechanism through a new opcode. |
| author | Helkomine (@Helkomine) |
| discussions-to | https://ethereum-magicians.org/t/eip-xxxx-invariant-layout-guard-opcode/27714 |
| status | Draft |
| type | Standards Track |
| category | Core |
| created | 2026-02-11 |
This EIP introduces a new opcode, MUTABLE, which restricts state changes to an explicitly defined scope. Any attempt to modify state outside the permitted scope MUST cause execution to revert.
Unintended state mutation during execution is a persistent security risk in smart contract systems. This risk is especially pronounced in proxy-based architectures that rely on DELEGATECALL, where the calling contract effectively relinquishes control over which state changes may occur in the callee’s execution context.
At the protocol level, there is currently no mechanism to stabilize or constrain state layout during execution. Introducing such a mechanism enables safer composition of contracts, supports future extensibility, and improves overall robustness for an increasingly dynamic Layer 1 ecosystem.
At present, there exist contract-level approaches for constraining state mutation, which we refer to as an inner guard. These mechanisms offer strong and programmable protection for explicitly declared locations but fundamentally cannot defend against mutations outside the specified set. Because the number of potentially mutable locations is unbounded, attempting full coverage at the contract level is infeasible under current gas constraints.
To achieve comprehensive protection, a complementary outer guard is required. This can only be implemented at the protocol level. By combining an inner guard with the proposed outer guard, contracts can form a robust firewall against unintended or malicious side effects arising from external calls.
BASE_OPCODE_COST : 3
MUTABLE (0x2f)
offset: Memory offset at which the RLP-encoded data begins.length: Maximum number of bytes that may be read from memory.isGuard: Boolean flag indicating whether the guard mechanism is enabled.
The guardOrigin variable tracks the origin and propagation of mutation restrictions throughout execution frames within a transaction. It SHALL take one of the following values:
NONE: No mutation restrictions are active.LOCAL: Mutation restrictions are active and were established in the current execution frame.INHERITED: Mutation restrictions are active and were inherited from a parent execution frame.
The MUTABLE opcode interprets RLP-encoded data located in the caller’s memory.
Option:uint8Allowed:boolAddress:bytes20AllowedStorage:bytes32AllowedTransientStorage:bytes32
Option is a uint8 value with the following meanings:
0x00: Code0x01: Nonce0x02: Balance0x03: Storage0x04: TransientStorage
The Payload field SHALL be interpreted according to the associated Option value:
- For
0x00,0x01,0x02: the payload MUST be empty. - For
0x03: the payload MUST be an RLP list ofAllowedStoragevalues. - For
0x04: the payload MUST be an RLP list ofAllowedTransientStoragevalues.
PolicyEntry:
[ Option, Allowed, Payload ]
MutableSet:
[ Address, List[PolicyEntry] ]
MutableSetList:
List[MutableSet]
If multiple PolicyEntry elements for the same Option appear within a MutableSet, only the last occurrence in RLP order SHALL be applied.
If multiple MutableSet elements reference the same Address, they SHALL be processed in RLP order, with later entries overriding earlier ones.
At the start of transaction execution, guardOrigin SHALL be initialized to NONE, and the effective MutableSetList SHALL be empty in the top-level execution frame.
When an execution frame spawns a child frame via CALL, DELEGATECALL, CALLCODE, STATICCALL, CREATE, or CREATE2, both guardOrigin and the effective MutableSetList SHALL be propagated as follows:
- If
guardOriginisNONE, the child frame SHALL receiveguardOrigin = NONE. - If
guardOriginisLOCALorINHERITED, the child frame SHALL receiveguardOrigin = INHERITED.
When executing the MUTABLE opcode, the EVM SHALL decode the RLP-encoded MutableSetList from memory starting at offset, reading at most length bytes.
If isGuard is set to false, the EVM SHALL set guardOrigin to NONE only if the current value of guardOrigin is NONE or LOCAL. If guardOrigin is INHERITED, this operation SHALL have no effect.
If isGuard is set to true:
- If
guardOriginisNONEorLOCAL, the decodedMutableSetListSHALL replace the currentMutableSetList, andguardOriginSHALL be set toLOCAL. - If
guardOriginisINHERITED, the effectiveMutableSetListSHALL be computed as the intersection of the decodedMutableSetListand the inheritedMutableSetListfrom the parent execution frame, andguardOriginSHALLremainINHERITED.
While executing an execution frame where guardOrigin is LOCAL or INHERITED, any instruction that attempts to mutate state outside the permitted scope defined by the effective MutableSetList SHALL result in an exceptional halt equivalent to an out-of-gas condition.
The following operations SHALL always result in an exceptional halt when guardOrigin is not NONE:
- Execution of
CALLCODE. - Execution of
SELFDESTRUCT.
The following operations SHALL result in an exceptional halt unless explicitly permitted by the effective MutableSetList:
CALLwith a non-zero value, unless mutation ofBalanceis permitted for both the caller address and the target address.CREATEorCREATE2, unless mutation ofCodeandNonceis permitted for the created address, and mutation ofNonceis permitted for the caller address. If a non-zero value is transferred, mutation ofBalanceMUST also be permitted for both addresses.SSTORE, unless mutation ofStorageis permitted for the executing address and the target storage slot is explicitly allowed.TSTORE, unless mutation ofTransientStorageis permitted for the executing address and the target transient storage slot is explicitly allowed.
Any future opcode or protocol change that introduces new forms of state mutation MUST define its interaction with MUTABLE in a manner consistent with the enforcement rules defined herein.
Execution of MUTABLE SHALL result in an exceptional halt if any of the following occur:
- Out-of-gas.
- Insufficient stack items.
- Any attempt to decode RLP data that would require reading beyond
lengthbytes. - The RLP payload does not conform to the
MutableSetListstructure defined in this specification.
The gas cost of the MUTABLE opcode consists of:
- The base opcode cost
BASE_OPCODE_COST. - Memory expansion costs, calculated according to existing EVM rules.
- A per-chunk (32-byte) cost proportional to
length, to account for RLP decoding overhead.
The initial design goal was to enable safe and transparent use of modular smart contracts, allowing modules to be freely integrated while preserving the safety guarantees of the host contract.
Based on this idea, we developed InvariantGuard, a contract-level pattern that provides powerful and flexible modifiers for protecting state with fine-grained control. However, this and similar patterns were found to be inherently limited: they cannot protect unspecified state locations, and the number of such locations is too large to enumerate exhaustively.
Therefore, achieving full state coverage requires direct support from the protocol. The MUTABLE opcode addresses this gap by enforcing invariants at the execution environment level.
The MUTABLE opcode only configures execution constraints and does not itself introduce new state changes. As such, there is no technical justification for prohibiting its use in a STATICCALL execution context.
Both CALLCODE and SELFDESTRUCT are considered deprecated. Consequently, only minimal checks are applied to these opcodes to ensure that no unintended bypass of MUTABLE constraints is possible.
The MutableSetList structure is explicitly designed to be extensible. This allows new invariant types or state categories to be added in the future without breaking contracts that rely on earlier versions of the MUTABLE opcode, and without introducing significant additional gas costs.
Since RLP encoding is self-describing, the length parameter serves solely as a hint for clients to quickly determine an upper bound on the data size when decoding a MutableSetList. It does not alter semantic interpretation.
Allowing later options to override earlier ones preserves flexibility and reduces the likelihood of unexpected failures caused by minor execution changes.
Introducing threshold limits for disallowed state locations provides no clear benefit. For allowed locations, programmable limits can already be effectively enforced using contract-level patterns such as InvariantGuard.
An additional gas cost is introduced to account for RLP parsing and invariant management throughout execution. This cost is comparable to the JUMPDEST analysis cost applied to init_code during contract creation.
State-affecting opcodes are constrained by MUTABLE in a manner analogous to their behavior under STATICCALL. These effects are strictly transaction-scoped and do not alter the intrinsic semantics of the opcodes outside the configured execution environment.
Because MutableSetList relies on extensible Option encoding, future additions to the invariant system do not change the behavior of contracts that use earlier versions of MUTABLE.
To be announced.
To be announced.
For maximum protection, contracts must precisely specify all permitted state mutations. This is a non-trivial task for most dApps. Accordingly, this EIP allows dApps to omit fine-grained checks on individual storage and transient storage slots to simplify integration. In such cases, wallets or dApps are expected to assess whether the target contract is sufficiently safe for user interaction. While this approach carries higher risk than exact slot-level enforcement, the risk can be mitigated through rigorous contract auditing.
Compilers should provide ergonomic high-level access to state under invariant constraints. At present, Solidity does not support complex transient storage variables, which significantly limits the usability of advanced safety mechanisms. Close coordination between protocol upgrades and compiler enhancements is therefore essential to fully realize the security benefits of this design.
Copyright and related rights waived via CC0.