Data Locations
Transient storage (EIP-1153, Cancun) behaves like storage but is discarded at the end of the transaction, at 100 gas per read/write.
9.1 The four locations
state variables, storage pointers temporary copies, return values no (read-only) external function parameters (cheapest) reentrancy locks, per-tx caches (0.8.28+ for state variables)
9.2 Assignment semantics
storage → storage (state var to state var) storage → local storage variable reference (pointer) storage → memory memory → memory memory → storage calldata → memory calldata → calldata
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Locations {
struct User { string name; uint256 score; }
User[] public users;
function add(string calldata name) external { // calldata: no copy, read-only
users.push(User(name, 0));
}
function bumpStorage(uint256 i) external {
User storage u = users[i]; // pointer
u.score += 1; // persists
}
function bumpMemory(uint256 i) external view returns (uint256) {
User memory u = users[i]; // copy
u.score += 1; // does NOT persist
return u.score;
}
function sum(uint256[] calldata xs) external pure returns (uint256 total) {
for (uint256 i; i < xs.length; ++i) total += xs[i];
}
}9.3 Transient storage
Transient storage (EIP-1153, Cancun) behaves like storage but is discarded at the end of the transaction, at 100 gas per read/write.
Before 0.8.28 (0.8.24+), use assembly:
Transient storage requires the chain to support Cancun opcodes. Constants and immutables cannot be transient; reference types are not yet supported as transient.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract TransientLock {
bool transient locked; // 0.8.28+: transient state variable (value types only)
modifier nonReentrant() {
require(!locked, "reentrant");
locked = true;
_;
locked = false; // still reset: a later call in the same tx would see `true`
}
function guarded() external nonReentrant { /* ... */ }
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract TransientAsm {
bytes32 constant SLOT = keccak256("transient.lock");
function _set(uint256 v) internal { bytes32 s = SLOT; assembly { tstore(s, v) } }
function _get() internal view returns (uint256 v) { bytes32 s = SLOT; assembly { v := tload(s) } }
}9.4 Rules of thumb
External function parameters of reference type: use calldata unless you need to modify them.
Use storage pointers to update structs/arrays in place.
Avoid copying large storage arrays into memory — it costs an SLOAD per slot.