
EIP-2535 diamonds turn a fallback function into a selector router
Most proxy designs assume one implementation contract. That gets awkward once a protocol grows beyond the 24 KB bytecode limit or needs to upgrade one module without replacing the rest.
An EIP-2535 diamond keeps one stateful address and maps each four-byte function selector to a facet contract. The fallback reads msg.sig, finds the facet, and runs it with delegatecall. msg.sender and msg.value stay intact, while every storage read and write still lands in the diamond.
The routing is straightforward. Storage is where the risk moves.
Facets do not own isolated state. If two facets assume incompatible layouts, an otherwise valid upgrade can corrupt the same slots. I use namespaced storage libraries and test the selector-to-facet map before and after every diamondCut.
diamondCut also lets you add, replace, or remove selectors and run initialization in one transaction. Loupe functions then give tooling a way to verify which facet owns each selector.
I put together a Foundry walkthrough that deploys the diamond and facets, adds a new selector, and checks the routing:
For teams that have used diamonds in production, what caused more trouble: storage coordination, selector governance, or the larger audit surface?