0003. ERC-165 registration writes a precomputed map slot
- Status: Accepted
- Date: in the first module (2026-04-25); written down as the
registerInterfacestandard on 2026-07-06; recorded 2026-10-09
Context
A diamond answers supportsInterface from diamond-lib's ERC-165 storage: a
mapping(bytes4 => bool) in the ERC-7201 namespace diamond.lib.storage.ERC165, whose root is
0x9ca7f3e2e2bfb15fdf072b85dde92837cddacee6cf2f6b38cd06c9457c1c4200. Each module's init registers the
module's interfaceId in that mapping. Every init of every diamond pays for the registration, and a wrong
location is silent: the flag lands somewhere supportsInterface never reads.
Options considered
- A precomputed file-level constant per interface and one
sstore. - Compute
keccak256(abi.encode(interfaceId, root))at run time. Correct by construction, but it hashes on every registration. - Write
supportedInterfaces[id] = truethrough diamond-lib'sERC165Storagestruct. Readable, but Solidity hashes the mapping key at run time, as in option 2. diamond-lib's ownERC165LibregistersIERC165with a precomputed slot, as option 1 does.
Decision
- A library declares
ERC165_MAP_<INTERFACE-NAME-UPPERCASED>_SLOT, the value ofkeccak256(abi.encode(bytes4(<id>), <root>)), with the interfaceId and the full derivation in its@devcomment. registerInterface()is onesstore(<constant>, true)in assembly. Several interfaces meanregisterInterfaces()with onesstoreper constant.- No runtime
keccak, no bare hex literal inside thesstore, and no local copy of the ERC-165 root. - A module with an error-only interface (interfaceId
0x00000000) registers nothing. - An adapter that implements a shared interface declares the same constant in its own file.
Consequences
- Registration costs one storage write and no hashing.
- The constant is only as good as its derivation, so each one needs a test that recomputes it.
- An interface change changes its id and therefore its map slot, which makes the change visible in the slot test (0008).
- Many older libraries still declare a local copy of the root. The standard forbids new ones; the
add-a-module checklist names
CCTPBridgeAdapterLibas the model to copy.
Confirmation
StorageSlotVerificationTestrecomputes every map-slot constant (test_Erc165MapI<Module>Slot) and asserts they are unique (test_AllErc165MapSlotsAreUnique).- Each module's ERC-165 test (usually
test_SupportsInterface) asks the diamond, whose read path recomputes the hash at run time, so a wrong constant fails it.