DEV Community

Nick Mudge
Nick Mudge

Posted on

Understanding EIP-2535 Diamonds: The Future of Smart Contract Design

In the ever-evolving world of blockchain technology, Ethereum Improvement Proposals (EIPs) play a crucial role in advancing the capabilities and functionalities of the Ethereum network. One such significant proposal is EIP-2535 Diamonds. This proposal introduces a standard for organizing and managing complex smart contracts, offering unprecedented flexibility and scalability. In this blog post, we'll explore what EIP-2535 Diamonds are, their benefits, and how you can use them in your smart contract development.

What is EIP-2535?

EIP-2535, proposed by Nick Mudge in 2020, introduces the concept of "Diamonds," a modular and scalable architecture for smart contracts on the Ethereum blockchain. Unlike traditional smart contracts, which can become cumbersome and difficult to manage as they grow in complexity, Diamonds allow developers to break down their contracts into smaller, more manageable pieces called "facets."

Key Components of EIP-2535

  1. Diamond Contract: This is the core contract that acts as a hub, managing the various facets. It delegates function calls to the appropriate facet based on the function signature.

  2. Facets: These are modular contracts that contain specific functionalities. A single Diamond can have multiple facets, each responsible for different parts of the overall logic.

  3. Selectors: Function selectors are used to route calls to the correct facet. The Diamond contract maintains a lookup table of function selectors to the corresponding facet addresses.

  4. Loupe Functions: These are introspection functions that allow developers to query the Diamond for information about its facets and the functions they provide.

Benefits of EIP-2535

  1. Modularity: By breaking down a smart contract into smaller facets, developers can manage and update specific parts of the contract without affecting the entire system.

  2. Scalability: Diamonds enable the development of large and complex systems that can grow organically. New functionalities can be added by deploying new facets.

  3. Upgradeability: Individual facets can be upgraded independently, allowing for flexible and seamless updates without redeploying the entire contract.

  4. Reduced Gas Costs: Since only the facets that are updated need to be redeployed, the overall gas costs for contract upgrades can be significantly lower.

  5. Organization: By organizing code into facets, developers can keep their codebase clean and easier to navigate, enhancing maintainability.

How to Use EIP-2535 Diamonds

Using EIP-2535 Diamonds involves several steps, from setting up your development environment to deploying and interacting with your Diamond contract. Here's a step-by-step guide:

1. Setting Up Your Environment

First, ensure you have a development environment set up with tools like Hardhat or Truffle, and a coding editor like VSCode.

npm install --save-dev hardhat
Enter fullscreen mode Exit fullscreen mode

2. Create Your Diamond Contract

Create the Diamond contract that will act as the central hub. This contract includes the logic to delegate function calls to the appropriate facets.

// Diamond.sol
pragma solidity ^0.8.0;

import "./IDiamondCut.sol";

contract Diamond {
    // Diamond storage
    struct Facet {
        address facetAddress;
        bytes4[] selectors;
    }

    mapping(bytes4 => address) public selectorToFacet;

    constructor(IDiamondCut.FacetCut[] memory _diamondCut) {
        // Implement diamond cut
    }

    fallback() external payable {
        address facet = selectorToFacet[msg.sig];
        require(facet != address(0), "Function does not exist");
        assembly {
            calldatacopy(0, 0, calldatasize())
            let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
            returndatacopy(0, 0, returndatasize())
            switch result
            case 0 {revert(0, returndatasize())}
            default {return(0, returndatasize())}
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Define Your Facets

Create facet contracts that contain specific functionality. For example, you might have one facet for token management and another for governance.

// TokenFacet.sol
pragma solidity ^0.8.0;

contract TokenFacet {
    mapping(address => uint256) balances;

    function transfer(address to, uint256 amount) external {
        require(balances[msg.sender] >= amount, "Insufficient balance");
        balances[msg.sender] -= amount;
        balances[to] += amount;
    }

    function balanceOf(address account) external view returns (uint256) {
        return balances[account];
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Deploy Your Diamond and Facets

Deploy your Diamond contract and then the facet contracts. After deploying, you need to link the facets to the Diamond using the Diamond Cut mechanism.

async function main() {
    const Diamond = await ethers.getContractFactory("Diamond");
    const diamond = await Diamond.deploy();
    await diamond.deployed();
    console.log("Diamond deployed to:", diamond.address);

    const TokenFacet = await ethers.getContractFactory("TokenFacet");
    const tokenFacet = await TokenFacet.deploy();
    await tokenFacet.deployed();
    console.log("TokenFacet deployed to:", tokenFacet.address);

    // Add facets to the Diamond
    await diamond.diamondCut([
        {
            facetAddress: tokenFacet.address,
            action: 0, // Add
            functionSelectors: getSelectors(tokenFacet)
        }
    ], ethers.constants.AddressZero, "0x");
}
Enter fullscreen mode Exit fullscreen mode

5. Interact with Your Diamond

Once deployed, you can interact with your Diamond contract. Calls to the Diamond will be routed to the appropriate facet.

const diamond = await ethers.getContractAt("Diamond", diamondAddress);
const tokenFacet = await ethers.getContractAt("TokenFacet", diamondAddress);

await tokenFacet.transfer(receiverAddress, amount);
const balance = await tokenFacet.balanceOf(receiverAddress);
console.log("Balance:", balance.toString());
Enter fullscreen mode Exit fullscreen mode

A fully working and complete implementation of EIP-2535 Diamonds is here: https://github.com/mudgen/diamond-1-hardhat.

To date more than 100 projects have used EIP-2535 Diamonds. A list is available here: https://github.com/mudgen/awesome-diamonds?tab=readme-ov-file#projects-using-diamonds

Conclusion

EIP-2535 Diamonds provide a powerful framework for building modular, scalable, and upgradeable smart contracts on Ethereum. By breaking down contracts into facets, developers can manage complex systems more effectively, reduce gas costs, and ensure seamless upgrades. Whether you are building decentralized finance (DeFi) applications, gaming platforms, or any other type of dApp, Diamonds offer a robust solution to the challenges of smart contract development. Start exploring EIP-2535 Diamonds today and take your blockchain projects to the next level!

Top comments (0)