Understanding Ethereum Smart Contract Storage

A key/value store mapping 32-byte keys to 32-byte values will do the job nicely

This is incentivized in smart contracts with a gas refund when you change a value to zero.

These slots are determined at compile time, strictly based on the order in which the variables appear in the contract code.

contract StorageTest {
    uint256 a;
    uint256[2] b;

    struct Entry {
        uint256 id;
        uint256 value;
    }
    Entry c;
}

Untitled

Locating Dynamically-Sized Values

Using reserved slots works well for fixed-size state variables, but it doesn’t work for dynamically-sized arrays and mapping because there’s no way of knowing how many slots to reserve.

contract StorageTest {
    uint256 a;     // slot 0
    uint256[2] b;  // slots 1-2

    struct Entry {
        uint256 id;
        uint256 value;
    }
    Entry c;       // slots 3-4
    Entry[] d;
}

Untitled

//The following Solidity function computes the location of an element of a dynamically-sized array:
function arrLocation(uint256 slot, uint256 index, uint256 elementSize)
    public
    pure
    returns (uint256)
{
    return uint256(keccak256(slot)) + (index * elementSize);
}
// The following Solidity function computes the location of a value:
function mapLocation(uint256 slot, uint256 key) public pure returns (uint256) {
    return uint256(keccak256(key, slot));
}

Summary