ERC-8042 Diamond Storage
Final · Other · created 2025-10-11
Standardizes the diamond storage pattern that uses the keccak256 hash of a human-readable string as the storage location of a struct. It lets a single contract manage its storage divided into separate regions.
Why it is needed
Smart contracts keep their data in a space called storage. In a proxy structure, where several functional modules share one storage, different modules can write to the same location and overwrite each other's data. In March 2020 the Solidity compiler made it possible to place a struct at an arbitrary location in storage, which gave rise to the Diamond Storage pattern, where a readable string is hashed to determine the location, and it became widely used through ERC-2535 Diamonds. However, this approach was not a formal standard for a long time. ERC-7201, published in 2023, standardized a pattern with the same purpose but proposed a different calculation formula from the Diamond Storage already in use. As a result, existing implementations in production did not receive the protection of a standard. This standard codifies the simple, proven original method so that new projects can use it, and it also recognizes past implementations as compliant.
In depth
Storage location formula and identifier rules
A Diamond Storage identifier is a string containing only printable ASCII characters (0x20 through 0x7E). The storage location of the struct is determined by the keccak256 hash of this identifier. That is, the formula is the single expression erc8042(id) = keccak256(id), which in Solidity is written as keccak256(id). Unlike the multi-step formula of ERC-7201, the location comes from a single hash, so the calculation is simple and easy to verify.
Four recommendations
First, using a Solidity string literal is recommended. This is because the compiler enforces that only printable ASCII is allowed, and it is recommended to place it as a compile-time constant such as bytes32 constant STORAGE_POSITION = keccak256("myproject.erc721.registry"). Hexadecimal (\xNN) and Unicode (\uNNNN) escapes must not be used. Second, the identifier must be a unique, human-readable and meaningful string. A random string such as "car.fish.piano.run" is not a meaningful string. Third, Unicode literals (unicode"...") can contain invisible or non-ASCII characters, so they must not be used. Fourth, the space character (0x20) is discouraged because it can conflict with tools such as NatSpec tags.
NatSpec tag erc8042
It uses the @custom:storage-location <FORMULA_ID>:<NAMESPACE_ID> tag scheme defined by ERC-7201 as it is. The formula identifier erc8042 is defined as erc8042(id: string literal) = keccak256(id), and the annotation takes the form @custom:storage-location erc8042:<NAMESPACE_ID>. For example, @custom:storage-location erc8042:myproject.erc721.registry marks Diamond Storage rooted at the hash location of the identifier "myproject.erc721.registry". Tools can read this tag to determine where data lives in storage.
Comparison with ERC-7201
The namespace id in ERC-7201 only needs to contain no spaces and has no other restrictions, so random bytes can also be used. Its location formula is keccak256(abi.encode(uint256(keccak256(bytes(namespace id))) - 1)) & ~bytes32(uint256(0xff)). The first part avoids overlapping with the inputs that Solidity types produce with keccak256, which allows arbitrary byte ids, and the last part aligns the final location to a multiple of 256, leaving room for future gas optimizations. ERC-8042, by contrast, restricts the identifier to printable ASCII and derives the location with a single keccak256. If you want to use machine-generated random ids, ERC-7201 fits; if you want readable identifiers enforced as ASCII and a simple calculation, ERC-8042 fits.
Access pattern of the reference implementation
The reference implementation ERC721Registry sets keccak256("myproject.erc721.registry") as the constant STORAGE_POSITION and holds an erc721Contracts array and a registryLimit value in the ERC721RegistryStorage struct. When the getStorage() function runs s.slot := position in inline assembly to connect the struct to that slot, later functions read and write with ordinary struct access syntax such as getStorage().registryLimit. This pattern started out for proxies, but it can be used to organize storage and data access in any smart contract.
Backward compatibility
The Diamond Storage described by this standard has been used in practice for five years. This standard recognizes and standardizes all past uses of Diamond Storage that conform to the specification. Existing projects therefore become standard-compliant without changing their code.
Things to watch out for
- Duplicate identifiers: If different contracts or libraries use the same string, they point to the same storage slot, and data can overlap or be corrupted. Identifiers must be unique within a contract system, and prefixing them with a project or organization name, as in "myproject.erc721.registry", is the common method.
- Collision prevention through the ASCII restriction: If an identifier could contain non-printable bytes such as null bytes, a malicious identifier such as "config\x00\x00..." could collide with the storage location inputs that mappings, dynamic arrays, string and bytes compute with keccak256, causing state overwrites or security vulnerabilities. Restricting identifiers to printable ASCII (0x20 to 0x7E) removes this collision path. This is not a guarantee that collisions are mathematically impossible, but the actual probability of one occurring is effectively close to zero.
- No escapes when using string literals: String literals are recommended because the compiler allows only printable ASCII, but Unicode escapes (\uNNNN) and hexadecimal escapes (\xNN) must not be used.
- No Unicode literals: A unicode"..." literal can contain non-printable bytes, and it can be abused to disguise an identifier with look-alike characters, just as the Cyrillic letter а (\u0430) looks identical to the ASCII a (\u0061). Some characters also control text display direction, so they must not be used.
- keccak256 collision resistance: One might worry that a Diamond Storage struct could accidentally overlap an existing data location, but the 256-bit storage address space is so vast that this is statistically unlikely. Solidity mappings, dynamic arrays, string and bytes also determine their locations with the same keccak256.