Skip to content

Storage Slot Collisions in Diamond-Compatible Delegatecall Architecture (Upgradeable Contracts) #5681

Open
@EricForgy

Description

@EricForgy

🧐 Motivation
OpenZeppelin’s upgradeable contracts are widely used in standard proxy-based deployments. However, when used in a modular delegatecall architecture (such as the Diamond Standard or similar plugin-style routers), fixed storage slot constants across multiple modules can cause critical storage collisions.

Each module is executed in the same storage context (the proxy/Router), so repeated use of hardcoded slot identifiers like INITIALIZABLE_STORAGE leads to overlapping writes between otherwise isolated modules. This makes current upgradeable contracts unsafe in modular delegatecall setups — an increasingly common pattern in modern contract systems.


📝 Details
I propose a backward-compatible enhancement to support diamond-compatible usage by deriving storage slot positions uniquely per module based on their deployed address.

This can be done using the following pattern:

address private immutable __self = address(this);

bytes32 private immutable INITIALIZABLE_STORAGE =
    keccak256(
        abi.encode(
            uint256(keccak256(abi.encodePacked(__self, "openzeppelin.storage.Initializable"))) - 1
        )
    ) & ~bytes32(uint256(0xff));

Why this works:

  • Safe: Each module gets a deterministic, unique slot namespace.
  • Aligned: The bitmask aligns with EIP-1967 slot layout expectations.
  • Minimal Overhead: Only requires switching some pure functions to view due to __self, with no storage reads and negligible gas impact.
  • Compatible: Initialization checks are typically only called once, so runtime cost is not a concern.

Suggested Implementation

Introduce a new base contract or modifier version (e.g. DiamondCompatibleInitializable) that mirrors Initializable but uses per-module slot derivation. This could live in an experimental or diamond subdirectory to avoid impacting the existing suite.


Let me know if you want this as a full PR — happy to help.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions