Unstructured Storage
Storage pattern using keccak256 hash-derived slots to prevent collisions between proxy and implementation contract variables.
Unstructured storage is a technique used in proxy contracts to store critical variables (like implementation address and admin) in pseudo-random storage slots, preventing accidental collisions with implementation contract storage. Instead of using sequential storage slots, variables are stored at slots derived from keccak256 hashes.
The Storage Collision Problem
Traditional Sequential Storage
Solidity normally assigns storage slots sequentially:
1contract SequentialStorage {2 address public owner; // Slot 03 uint256 public balance; // Slot 14 bool public paused; // Slot 25 mapping(address => uint256) balances; // Slot 36}
Collision in Proxy Patterns
When proxy and implementation use sequential storage, collisions occur:
1// Proxy contract2contract VulnerableProxy {3 address public implementation; // Slot 04 address public admin; // Slot 15}67// Implementation contract8contract Implementation {9 address public owner; // Slot 0 - COLLISION with implementation!10 uint256 public totalSupply; // Slot 1 - COLLISION with admin!11}1213// When proxy delegates to implementation:14// - Implementation thinks owner is at slot 015// - But slot 0 contains implementation address16// - Implementation thinks totalSupply is at slot 117// - But slot 1 contains admin address18// Result: Data corruption and unpredictable behavior
Unstructured Storage Solution
Hash-Based Slot Calculation
Use keccak256 to derive storage slots from unique strings:
1contract UnstructuredProxy {2 // EIP-1967 standard slots3 bytes32 private constant IMPLEMENTATION_SLOT =4 keccak256("eip1967.proxy.implementation");5 bytes32 private constant ADMIN_SLOT =6 keccak256("eip1967.proxy.admin");78 function _getImplementation() internal view returns (address impl) {9 bytes32 slot = IMPLEMENTATION_SLOT;10 assembly {11 impl := sload(slot)12 }13 }1415 function _setImplementation(address newImplementation) internal {16 bytes32 slot = IMPLEMENTATION_SLOT;17 assembly {18 sstore(slot, newImplementation)19 }20 }2122 function _getAdmin() internal view returns (address admin) {23 bytes32 slot = ADMIN_SLOT;24 assembly {25 admin := sload(slot)26 }27 }28}
Slot Calculation Example
1// Calculate actual slot values:2IMPLEMENTATION_SLOT = keccak256("eip1967.proxy.implementation")3 = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc45ADMIN_SLOT = keccak256("eip1967.proxy.admin")6 = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103
These slots are effectively random and extremely unlikely to collide with implementation storage.
EIP-1967 Standard
Standard Slot Definitions
EIP-1967 defines standard unstructured storage slots for proxy patterns:
1// Implementation slot2bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)3= 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc45// Admin slot6bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)7= 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610389// Beacon slot (for beacon proxy pattern)10bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)11= 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50
Why Subtract 1?
Subtracting 1 ensures the slot is never 0, which has special meaning in Solidity storage (uninitialized state).
Implementation Patterns
Basic Unstructured Storage
1contract UnstructuredStorageProxy {2 bytes32 private constant IMPLEMENTATION_SLOT =3 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;45 constructor(address _logic, bytes memory _data) {6 _setImplementation(_logic);7 if (_data.length > 0) {8 Address.functionDelegateCall(_logic, _data);9 }10 }1112 function _implementation() internal view returns (address impl) {13 bytes32 slot = IMPLEMENTATION_SLOT;14 assembly {15 impl := sload(slot)16 }17 }1819 function _setImplementation(address newImplementation) private {20 require(21 Address.isContract(newImplementation),22 "Implementation not a contract"23 );2425 bytes32 slot = IMPLEMENTATION_SLOT;26 assembly {27 sstore(slot, newImplementation)28 }2930 emit Upgraded(newImplementation);31 }32}
Advanced Pattern with Multiple Variables
1contract AdvancedUnstructuredProxy {2 // Custom slots for additional variables3 bytes32 private constant VERSION_SLOT =4 keccak256("zealynx.proxy.version");5 bytes32 private constant UPGRADE_TIMESTAMP_SLOT =6 keccak256("zealynx.proxy.upgradeTimestamp");7 bytes32 private constant EMERGENCY_ADMIN_SLOT =8 keccak256("zealynx.proxy.emergencyAdmin");910 function _getVersion() internal view returns (uint256 version) {11 bytes32 slot = VERSION_SLOT;12 assembly {13 version := sload(slot)14 }15 }1617 function _setVersion(uint256 newVersion) internal {18 bytes32 slot = VERSION_SLOT;19 assembly {20 sstore(slot, newVersion)21 }22 }2324 function _getUpgradeTimestamp() internal view returns (uint256 timestamp) {25 bytes32 slot = UPGRADE_TIMESTAMP_SLOT;26 assembly {27 timestamp := sload(slot)28 }29 }3031 function _recordUpgrade() internal {32 bytes32 slot = UPGRADE_TIMESTAMP_SLOT;33 assembly {34 sstore(slot, timestamp())35 }36 }37}
Security Benefits
1. Collision Prevention
Unstructured storage eliminates accidental storage collisions:
1// Safe: Implementation can use any storage layout2contract SafeImplementation {3 address public owner; // Slot 0 - No collision4 uint256 public totalSupply; // Slot 1 - No collision5 bool public paused; // Slot 2 - No collision67 // Proxy variables are in completely different slots:8 // implementation: slot 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc9 // admin: slot 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610310}
2. Future-Proof Storage
Implementation contracts can add variables without worrying about proxy storage:
1// Implementation V12contract ImplV1 {3 address public owner;4 uint256 public balance;5}67// Implementation V2 - Safe to add variables8contract ImplV2 {9 address public owner; // Same slot as V110 uint256 public balance; // Same slot as V111 bool public newFeature; // New slot - no proxy collision12 mapping(address => bool) public whitelist; // New slot - no proxy collision13}
3. Cross-Implementation Compatibility
Multiple implementation contracts can coexist safely:
1// TokenImplementation2contract TokenImpl {3 string public name;4 string public symbol;5 mapping(address => uint256) public balances;6}78// GovernanceImplementation9contract GovernanceImpl {10 uint256 public votingDelay;11 mapping(uint256 => Proposal) public proposals;12}1314// Both can be used with same proxy without storage conflicts
Common Mistakes
1. Hardcoding Wrong Slot Values
Using incorrect slot calculations:
1// WRONG: Manual calculation error2bytes32 private constant WRONG_SLOT =3 0x1234567890abcdef; // Random value, not derived from hash45// CORRECT: Use proper keccak256 calculation6bytes32 private constant CORRECT_SLOT =7 keccak256("eip1967.proxy.implementation");
2. Not Following EIP-1967 Standard
Using custom slot derivation instead of standard:
1// AVOID: Custom derivation (not compatible with tooling)2bytes32 private constant CUSTOM_SLOT =3 keccak256("myproxy.implementation");45// PREFERRED: EIP-1967 standard (compatible with tools/explorers)6bytes32 private constant STANDARD_SLOT =7 keccak256("eip1967.proxy.implementation");
3. Assembly Usage Errors
Incorrect assembly for storage operations:
1// WRONG: Using wrong assembly operations2function badGet() internal view returns (address) {3 bytes32 slot = IMPLEMENTATION_SLOT;4 assembly {5 return(slot, 0x20) // WRONG: return instead of sload6 }7}89// CORRECT: Proper assembly usage10function goodGet() internal view returns (address impl) {11 bytes32 slot = IMPLEMENTATION_SLOT;12 assembly {13 impl := sload(slot) // CORRECT: sload to read storage14 }15}
Testing Unstructured Storage
1describe("Unstructured Storage", function() {2 it("should store implementation in correct slot", async function() {3 const expectedSlot = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";45 // Deploy proxy with implementation6 const proxy = await deployProxy(Implementation, []);78 // Read storage at unstructured slot9 const implementationAddress = await ethers.provider.getStorageAt(10 proxy.address,11 expectedSlot12 );1314 // Convert to address format and verify15 const actualImpl = ethers.utils.getAddress(16 ethers.utils.hexDataSlice(implementationAddress, 12)17 );1819 expect(actualImpl).to.equal(implementation.address);20 });2122 it("should not conflict with implementation storage", async function() {23 // Deploy proxy and set implementation variables24 await proxy.setOwner(owner.address);25 await proxy.setBalance(1000);2627 // Verify proxy admin and implementation are separate28 const proxyAdmin = await proxy.admin();29 const implOwner = await proxy.owner();3031 expect(proxyAdmin).to.not.equal(implOwner);32 });3334 it("should survive implementation upgrades", async function() {35 const originalAdmin = await proxy.admin();3637 // Upgrade implementation38 await proxy.upgradeTo(newImplementation.address);3940 // Admin should be unchanged (different storage slot)41 const afterUpgradeAdmin = await proxy.admin();42 expect(afterUpgradeAdmin).to.equal(originalAdmin);43 });44});
Advanced Usage Patterns
Storage Slot Libraries
Create reusable libraries for common unstructured storage patterns:
1library UnstructuredStorage {2 function getAddressSlot(bytes32 slot) internal pure returns (StorageSlot.AddressSlot storage) {3 return StorageSlot.getAddressSlot(slot);4 }56 function getUint256Slot(bytes32 slot) internal pure returns (StorageSlot.Uint256Slot storage) {7 return StorageSlot.getUint256Slot(slot);8 }910 function getBooleanSlot(bytes32 slot) internal pure returns (StorageSlot.BooleanSlot storage) {11 return StorageSlot.getBooleanSlot(slot);12 }13}1415contract ModernProxy {16 bytes32 private constant IMPLEMENTATION_SLOT =17 keccak256("eip1967.proxy.implementation");1819 function _getImplementation() internal view returns (address) {20 return UnstructuredStorage.getAddressSlot(IMPLEMENTATION_SLOT).value;21 }2223 function _setImplementation(address impl) internal {24 UnstructuredStorage.getAddressSlot(IMPLEMENTATION_SLOT).value = impl;25 }26}
Multiple Storage Namespaces
Organize related variables in storage namespaces:
1contract NamespacedProxy {2 // Core proxy variables3 bytes32 private constant PROXY_NAMESPACE = keccak256("proxy");4 bytes32 private constant IMPLEMENTATION_SLOT =5 keccak256(abi.encode(PROXY_NAMESPACE, "implementation"));6 bytes32 private constant ADMIN_SLOT =7 keccak256(abi.encode(PROXY_NAMESPACE, "admin"));89 // Security variables10 bytes32 private constant SECURITY_NAMESPACE = keccak256("security");11 bytes32 private constant EMERGENCY_ADMIN_SLOT =12 keccak256(abi.encode(SECURITY_NAMESPACE, "emergencyAdmin"));13 bytes32 private constant PAUSED_SLOT =14 keccak256(abi.encode(SECURITY_NAMESPACE, "paused"));1516 // Upgrade variables17 bytes32 private constant UPGRADE_NAMESPACE = keccak256("upgrade");18 bytes32 private constant TIMELOCK_SLOT =19 keccak256(abi.encode(UPGRADE_NAMESPACE, "timelock"));20 bytes32 private constant PENDING_UPGRADE_SLOT =21 keccak256(abi.encode(UPGRADE_NAMESPACE, "pendingUpgrade"));22}
Unstructured storage is fundamental to secure proxy patterns. By using hash-derived storage slots, proxy contracts can safely coexist with any implementation contract storage layout, preventing dangerous collisions that could corrupt data or create security vulnerabilities.
Articles Using This Term
Learn more about Unstructured Storage in these articles:
Related Terms
Proxy Pattern
Smart contract design separating storage and logic, enabling upgrades by changing implementation while preserving state.
Storage Collision
Critical vulnerability in proxy contracts where proxy and implementation use the same storage slot for different variables, causing state corruption.
Implementation Contract
The logic contract containing actual business functions that executes in proxy storage context via delegatecall.
Storage Slot
32-byte memory location in EVM persistent storage where contract state variables are stored, with access costs significantly higher than memory.
Need expert guidance on Unstructured Storage?
Our team at Zealynx has deep expertise in blockchain security and DeFi protocols. Whether you need an audit or consultation, we're here to help.
Get a Quote

