EIP1153 - Transient storage opcodes
# Abstract
This proposal introduces transient storage opcodes, which manipulate state that behaves identically to storage,
but is discarded after every transaction. Transient storage is cheaper since it never requires disk access.
Transient storage is accessible to smart contracts via 2 new opcodes: TLOAD
and TSTORE
, where “T” stands for "transient."
# Motivation
Running a transaction in Ethereum can generate multiple nested frames of execution, each created by CALL
(or similar) instructions.
Contracts can be re-entered during the same transaction, in which case there are more than one frame belonging to one contract.
Currently, these frames can communicate in two ways - via inputs/outputs passed via CALL
instructions, and via storage updates.
If there is an intermediate frame belonging to another untrusted contract, communication via inputs/outputs is not secure.
Notable example is a reentrancy lock which cannot rely on the intermediate frame to pass through the state of the lock.
Communication via storage (SSTORE
/SLOAD
) is costly. Transient storage is a dedicated and gas efficient solution to the problem of inter frame communication.
Storage refunds accumulated due to inter frame communication are also limited to 20% of gas spent by a transaction as of EIP-3529 included in the London hard fork. This greatly reduces the refunds for transiently-set storage slots in otherwise low-cost transactions. For example: in order to receive the full refund of one re-entrancy lock, the transaction must spend ~80k gas on other operations.
Language support could be added in relatively easy way. For example, in Solidity, a qualifier “transient” can be introduced
(similar to the existing qualifiers “memory” and “storage”, and Java's own transient
keyword with a similar meaning).
Since the addressing scheme of TSTORE
and TLOAD
is the same as for SSTORE
and SLOAD
,
code generation routines that exist for storage variables, can be easily generalised to also support transient storage.
Potential use cases enabled or improved by this EIP include:
- Reentrancy lock
- Constructor arguments loaded from the factory contract (for on-chain-computable CREATE2 addresses as in Uniswap V3)
- Single transaction ERC20 approvals, e.g.
#approveAndCall(address callee, uint256 amount, bytes memory data)
- Passing error codes and messages from the execution frames up the execution stack
- More generic libraries that use callbacks, for example generalised sorting with functions
Less
andSwap
defined. - Contracts that require control before and after method execution (e.g. via callbacks)
- Shared memory (borrowed from early draft of similar EIP by @holiman). When implementing contract-proxies using
DELEGATECALL
, all direct arguments are relayed from the caller to the callee via theCALLDATA
, leaving no room for meta-data between the proxy and the proxee. Also, the proxy must be careful aboutstorage
access to avoid collision withtarget
storage
-slots. Sincetransient storage
would be shared, it would be possible to usetransient storage
to pass information between theproxy
and thetarget
.
These opcodes are more efficient to execute than the SSTORE
and SLOAD
opcodes because the original value never needs to be loaded from storage (i.e. is always 0).
The gas accounting rules are also simpler, and do not involve refunds.
# Specification
Two new opcodes are added to EVM, TLOAD
and TSTORE
.
They use the same arguments on stack as SLOAD
(0x54
) and SSTORE
(0x55
).
TLOAD
pops one 32-byte word from the top of the stack, treats this value as the address, fetches 32-byte word from the transient storage at that address, and pops the value on top of the stack.
TSTORE
pops two 32-byte words from the top of the stack. The word on the top is the address, and the next is the value. TSTORE
saves the value at the given address in the transient storage.
Addressing is the same as SLOAD
and SSTORE
. i.e. each 32-byte address points to a unique 32-byte word.
Gas cost for TSTORE
is the same as a hot dirty SSTORE
(original value is not new value and is not current value, currently 100 gas),
and gas cost of TLOAD
is the same as a hot SLOAD
(value has been read before, currently 100 gas). Gas cost cannot be on par with memory access due to transient storage's interactions with reverts.
All values in transient storage are discarded at the end of the transaction.
Transient storage is private to the contract that owns it, in the same way as persistent storage. Only owning contract frames may access their transient storage. And when they do, all the frames access the same transient store, in the same way as persistent storage, but unlike memory.
When transient storage is used in the context of DELEGATECALL
or CALLCODE
, then the owning contract of the transient
storage is the contract that issued DELEGATECALL
or CALLCODE
instruction (the caller) as with persistent storage.
When transient storage is used in the context of CALL
or STATICCALL
, then the owning contract of the transient storage
is the contract that is the target of the CALL
or STATICCALL
instruction (the callee).
If a frame reverts, all writes to transient storage within that frame are also reverted, as with persistent storage.
# Rationale
Another option to solve the problem of inter-frame communication is repricing the SSTORE
and SLOAD
opcodes to be cheaper
for the transient storage use case. This has already been done as of EIP-2200. However, EIP-3529
reduced the maximum refund to only 20% of the transaction gas cost, which means this use case is severely limited.
Another approach is to keep the refund counter for transient storage separate from the refund counter for other storage uses, and remove the refund cap for transient storage. However, that approach is more complex to implement and understand. For example, the 20% refund cap must be applied to the gas used after subtracting the uncapped gas refund. Otherwise, the refund amount available subject to the 20% refund cap could be increased by making a few transient storage writes. Thus it is preferable to have a separate mechanism that does not interact with the refund counter. Future hard forks can remove the complex refund behavior meant to support the transient storage use case, encouraging migration to contracts that are more efficient for clients to execute.
Relative cons of this transient storage EIP:
- Does not address transient usages of storage in existing contracts
- New code in the clients
- New concept for the yellow paper (more to update)
Relative pros of this transient storage EIP:
- Transient storage opcodes are considered separately in protocol upgrades and not inadvertently broken (e.g. EIP-3529)
- Clients do not need to load the original value
- No upfront gas cost to account for non-transient writes
- Does not change the semantics of the existing operations
- No need to clear storage slots after usage
- Simpler gas accounting rules
# Backwards Compatibility
This EIP requires a hard fork to implement.
Since this EIP does not change semantics of any existing opcodes, it does not pose risk of backwards incompatibility for existing deployed contracts.
# Test Cases
TBD
# Reference Implementation
Because the transient storage must behave identically to storage within the context of a single transaction with regards to revert behavior, it is necessary to be able to revert to a previous state of transient storage within a transaction.
A stack of maps is one possible implementation:
- When a call frame is entered, a new empty map is pushed onto the stack in constant time
- All transient storage writes are written to the map at the top of the stack in constant time
- When a call reverts, the map at the top of the stack is popped and discarded in constant time
- When a call frame exits successfully, the map at the top of the stack is popped and merged with the map now at the top of the stack in linear time with the number of keys written in that call frame
# Security Considerations
There are no security considerations for existing contracts not containing the new TSTORE
or TLOAD
opcodes.
New contracts that use the TSTORE
and TLOAD
opcodes operate under all the same assumptions as with storage. This EIP
introduces no additional mental overhead for the developer. In some cases, transient storage simplifies contracts in that contract
transient storage does not need to be cleared at the end of every transaction, e.g. in the temporary approvals use case.
# Copyright
Copyright and related rights waived via CC0 (opens new window).