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.