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:
TSTORE, where “T” stands for "transient."
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 (
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
TLOAD is the same as for
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
- 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 the
CALLDATA, leaving no room for meta-data between the proxy and the proxee. Also, the proxy must be careful about
storageaccess to avoid collision with
transient storagewould be shared, it would be possible to use
transient storageto pass information between the
These opcodes are more efficient to execute than the
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.
Two new opcodes are added to EVM,
They use the same arguments on stack as
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
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
CALLCODE, then the owning contract of the transient
storage is the contract that issued
CALLCODE instruction (the caller) as with persistent storage.
When transient storage is used in the context of
STATICCALL, then the owning contract of the transient storage
is the contract that is the target of the
STATICCALL instruction (the callee).
If a frame reverts, all writes to transient storage within that frame are also reverted, as with persistent storage.
Another option to solve the problem of inter-frame communication is repricing the
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
# 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
New contracts that use the
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 and related rights waived via CC0 (opens new window).