EVM -Storage
Storage Layout
1.
contract X {
uint256 public a = 1; // Slot 0
uint128 public b = 2; // Slot 1 (partially filled)
uint128 public c = 3; // Slot 1 (same as 'b', packed)
bool public d = true; // Slot 2 (partially filled)
address public e = msg.sender; // Slot 3
struct Data {
uint256 x;
uint256 y;
}
Data public data = Data(10, 20); // Slot 4 & 5 (separate storage)
mapping(address => uint256) public balances; // Uses keccak256(address + slot)
string public text = "Hello"; // Stored separately, reference stored in slot 6
}
Every Slot has: 256 bits (32 Bytes)
| Slot | Data |
|---|---|
| Slot 0 | a (32 bytes) |
| Slot 1 | b and c (16+16 bytes) |
| Slot 2 | d (1 byte) (31 waste) |
| Slot 3 | e |
| Slot 4 | x in struct |
| Slot 5 | y in struct |
2.
contract x {
struct User {
uint128 id; // 16 bytes
bool active; // 1 byte
uint8 level; // 1 byte
uint256 score; // 32 bytes (New slot)
}
}
| Slot | Data |
|---|---|
| Slot 0 | id (uint128) + active (bool) + level (uint8) |
| Slot 1 | score (uint256) |
3.
contract {
mapping(address => uint256) public balances; // Slot 0
function setBalance(address user, uint256 amount) public {
balances[user] = amount;
}
}
Storage slot = keccak256(abi.encode(0xABC, 0))
| Slot | Data |
|---|---|
| Slot 0 | |
| Slot n |
Gas Optimization
1.
Unsafe:
uint128 a; // Slot 0
uint256 b; // Slot 1
uint128 c; // Slot 2
Safe:
uint128 a;
uint128 c;
uint256 b; // This ensures `a` and `c` are packed into one slot.
Rule For Storage
- Every storage slot can store up to 32 bytes of data.
- If a variable is ≤ 32 bytes, it fits into a single slot.
- If a variable is > 32 bytes, it gets its own slot and additional storage.