Skip to main content

storage-layout

EVM storage layout expertise for struct packing, proxy storage patterns, and collision avoidance. Use when designing data structures, working with upgradeable proxies, optimizing storage costs, or debugging storage-related issues.

Ir a la instalación

Datos de origen

Repositorio
ccashwell/evm-cortex
Última actividad en el origen
10 de abril de 2026 a las 16:31
Idioma detectado de SKILL.md
inglés
Estrellas
131
Forks
18

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
storage-layout
description
EVM storage layout expertise for struct packing, proxy storage patterns, and collision avoidance. Use when designing data structures, working with upgradeable proxies, optimizing storage costs, or debugging storage-related issues.
# Storage Layout ## EVM Storage Fundamentals Each storage slot is 32 bytes (256 bits). State variables are assigned slots sequentially starting at slot 0. Multiple variables smaller than 32 bytes are packed into a single slot when possible. ```solidity // Slot 0: addr (20 bytes) + isActive (1 byte) + role (1 byte) = 22 bytes — fits one slot // Slot 1: balance (32 bytes) — full slot // Slot 2: lastUpdate (8 bytes) — new slot (won't pack with previous full slot) contract Packed { address addr; // 20 bytes | slot 0 bool isActive; // 1 byte | slot 0 uint8 role; // 1 byte | slot 0 uint256 balance; // 32 bytes | slot 1 uint64 lastUpdate; // 8 bytes | slot 2 } ``` ## Struct Packing Order fields from largest to smallest alignment, grouping sub-32-byte fields together. ```solidity // BAD: 4 slots (128 bytes) struct UserBad { bool isActive; // 1 byte | slot 0 (31 bytes wasted) uint256 balance; // 32 bytes | slot 1 address wallet; // 20 bytes | slot 2 (12 bytes wasted) uint256 rewards; // 32 bytes | slot 3 } // GOOD: 3 slots (96 bytes) struct UserGood { uint256 balance; // 32 bytes | slot 0 uint256 rewards; // 32 bytes | slot 1 address wallet; // 20 bytes | slot 2 bool isActive; // 1 byte | slot 2 (packed with wallet) } ``` ### Aggressive Packing Example ```solidity // 1 slot (32 bytes) for a position record struct Position { address owner; // 20 bytes uint48 openedAt; // 6 bytes (good until year 8.9M) uint40 collateral; // 5 bytes (up to ~1.1T with scaling) bool isLong; // 1 byte } // Total: 20 + 6 + 5 + 1 = 32 bytes = 1 slot ``` ### Timestamp Sizing Guide | Type | Bytes | Max Value | Overflow Date | |------|-------|-----------|---------------| | uint32 | 4 | 4.29B | Feb 2106 | | uint40 | 5 | 1.1T | Year ~36812 | | uint48 | 6 | 281T | Year ~8.9M | Use `uint48` for timestamps in most protocol designs. `uint32` overflows in 2106. ## Storage Slot Calculation ```solidity // Fixed-size variables: sequential slots starting at 0 // Mappings: keccak256(key . slot) // Dynamic arrays: length at slot, elements at keccak256(slot) + index // Nested mappings: keccak256(innerKey . keccak256(outerKey . slot)) // Example: mapping(address => mapping(uint256 => uint256)) at slot 5 // Value at [addr][id] is stored at: // keccak256(abi.encode(id, keccak256(abi.encode(addr, 5)))) ``` ## Inspecting Storage Layout ```bash # Forge: inspect storage layout forge inspect src/MyContract.sol:MyContract storage-layout --pretty # Output shows slot, offset, type, and variable name # Use this to verify packing and detect collisions before deployment ``` ## Proxy Storage Patterns ### EIP-1967 Standard Slots ```solidity // Implementation slot bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; // bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1) // Admin slot bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; // bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1) // Beacon slot bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; // bytes32(uint256(keccak256("eip1967.proxy.beacon")) - 1) ``` ### Storage Gaps for Upgradeable Contracts ```solidity abstract contract StorageGapExample { uint256 public value1; address public admin; // Reserve 48 slots for future storage variables // When adding new variables, reduce the gap size accordingly uint256[48] private __gap; } // In V2, adding a new variable: abstract contract StorageGapExampleV2 { uint256 public value1; address public admin; uint256 public newValue; // added in V2 uint256[47] private __gap; // reduced from 48 to 47 } ``` ### Unstructured Storage (Diamond/EIP-2535) ```solidity library LibDiamond { bytes32 constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage"); struct DiamondStorage { mapping(bytes4 => address) selectorToFacet; mapping(bytes4 => uint96) selectorToSlotPosition; bytes4[] selectors; mapping(bytes4 => bool) supportedInterfaces; address contractOwner; } function diamondStorage() internal pure returns (DiamondStorage storage ds) { bytes32 position = DIAMOND_STORAGE_POSITION; assembly { ds.slot := position } } } ``` ## Namespace Storage Pattern (ERC-7201) ```solidity /// @custom:storage-location erc7201:myprotocol.storage.vault struct VaultStorage { mapping(address => uint256) balances; uint256 totalDeposited; bool paused; } // keccak256(abi.encode(uint256(keccak256("myprotocol.storage.vault")) - 1)) // & ~bytes32(uint256(0xff)) bytes32 private constant VAULT_STORAGE_LOCATION = 0x...; function _getVaultStorage() private pure returns (VaultStorage storage $) { assembly { $.slot := VAULT_STORAGE_LOCATION } } ``` ## Common Mistakes - **Reordering variables** in an upgradeable contract (shifts all subsequent slots) - **Forgetting to reduce `__gap`** when adding variables - **Changing variable types** (e.g., `uint128` to `uint256` shifts layout) - **Inheriting in different order** between proxy versions - **Not running `forge inspect`** before deploying upgrades ## Storage Layout Verification Checklist - [ ] Run `forge inspect --storage-layout` on both old and new versions - [ ] Diff the layouts to verify no slot collisions - [ ] New variables only appended (never inserted) - [ ] `__gap` reduced by exactly the number of new slots used - [ ] Inheritance order unchanged between versions - [ ] Use EIP-7201 namespaced storage for new diamond/modular patterns - [ ] Struct field order optimized for packing
Ver en GitHub