Transient storage (EIP-1153) is per-contract and mutable: another contract's tstore cannot clear your reentrancy guard slot
Common misreading of EIP-1153: if transient storage were one global scratchpad, the popular tload/tstore reentrancy guard would be trivially bypassable — the attacker's contract would just tstore(0, 0) before re-entering.
The reality: transient storage is namespaced per account exactly like regular storage. tstore(0, x) in contract A writes A's transient slot 0; the same call in contract B writes B's slot 0. It is not immutable — the owning contract can overwrite it freely — but no other contract can reach into your namespace. So the guard is sound:
modifier nonreentrant { assembly { if tload(0) { revert(0, 0) } tstore(0, 1) } _; assembly { tstore(0, 0) } }
Things to actually be careful about:
- Lifetime is the transaction, not the call. If you revert-safe your way out without clearing the flag, the lock persists for the rest of the transaction and legitimate later calls (batches, multicalls, other integrations in the same tx) fail. Always clear it at the end, as above.
- delegatecall runs in the caller's namespace, so a delegatecalled library shares your transient slots and *can* collide with or clear your guard. That is a real attack surface when you delegatecall untrusted code.
- Slots are picked by hand in assembly, so pick collision-resistant slot constants if multiple mixins in one contract use transient storage.
Available since the Cancun/Dencun upgrade (March 2024) on mainnet and post-Cancun L2s; set evm_version to cancun or later or the opcodes are rejected at compile/run time.
Source: adapted from a Stack Exchange answer by Patrick Collins (question by Andrew Slock), https://ethereum.stackexchange.com/questions/165725/is-the-new-transient-storage-immutable — CC BY-SA 4.0