Diamond Collision Remediation
Overview
In an EIP-2535 diamond, every facet executes against the diamond’s single storage context through delegatecall. Facets that declare state variables with the default sequential layout all start at slot 0, so a variable in one facet silently overwrites a variable in another. The fix is to stop using sequential layout for shared facets: each facet keeps its state in a struct anchored at a hashed, collision-resistant slot (the diamond storage pattern).
Related Detector: Diamond Collision
Recommended Fix
Before (Vulnerable)
contract FacetA {
uint256 public valueA; // slot 0
}
contract FacetB {
address public adminB; // slot 0 — same slot as valueA
}
After (Fixed)
// Each facet owns a struct at a unique, hashed position
library LibFacetA {
bytes32 constant STORAGE_POSITION = keccak256("myapp.facet.a.storage");
struct Storage {
uint256 valueA;
}
function s() internal pure returns (Storage storage ds) {
bytes32 pos = STORAGE_POSITION;
assembly { ds.slot := pos }
}
}
contract FacetA {
function setValue(uint256 v) external {
LibFacetA.s().valueA = v; // anchored away from other facets
}
}
Because the base slot is keccak256 of a facet-specific string, two facets cannot land on the same region unless they deliberately share a library.
Alternative Mitigations
AppStorage pattern — instead of one struct per facet, define a single shared AppStorage struct anchored at one hashed slot, and have every facet reference it as the first state variable. This makes the shared layout explicit and centrally reviewed, which is often easier to reason about than many independent namespaces.
Append-only layout discipline — when upgrading via diamondCut, only add new fields to the end of a storage struct. Reordering or inserting fields shifts every subsequent field and reintroduces collisions against already-stored data.
Common Mistakes
Mixing sequential and namespaced storage in the same facet — declaring even one ordinary state variable alongside a diamond-storage struct puts that variable back at slot 0. Keep all of a facet’s state inside its struct.
Reusing a namespace string across unrelated facets — identical STORAGE_POSITION strings produce identical base slots, recreating the collision the pattern is meant to prevent. Make each string unique.