The Diamond standard is a finalized Ethereum Improvement Proposal () that aims to make it easier for developers to modularize and upgrade their smart contracts.
The core idea is that you can control many implementation contracts from your single Diamond contract (proxy contract). Some key features of the Diamond standard include:
A single gateway to make proxy calls to n number of implementation contracts
Upgrade a single or multiple smart contract atomically
No storage limit to how many implementation contracts you can add to your Diamond
A log history of all the upgrades made on the Diamond
Can reduce gas costs (i.e., by reducing the number of external function calls)
Diamond
The diamond is the smart contract that serves as the proxy. It calls (delegatecall) other implementation contracts known as facets.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/******************************************************************************\
* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
*
* Implementation of a diamond.
/******************************************************************************/
import { LibDiamond } from "./libraries/LibDiamond.sol";
import { IDiamondCut } from "./interfaces/IDiamondCut.sol";
contract Diamond {
constructor(address _contractOwner, address _diamondCutFacet) payable {
LibDiamond.setContractOwner(_contractOwner);
// Add the diamondCut external function from the diamondCutFacet
IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1);
bytes4[] memory functionSelectors = new bytes4[](1);
functionSelectors[0] = IDiamondCut.diamondCut.selector;
cut[0] = IDiamondCut.FacetCut({
facetAddress: _diamondCutFacet,
action: IDiamondCut.FacetCutAction.Add,
functionSelectors: functionSelectors
});
LibDiamond.diamondCut(cut, address(0), "");
}
// Find facet for function that is called and execute the
// function if a facet is found and return any value.
fallback() external payable {
LibDiamond.DiamondStorage storage ds;
bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION;
// get diamond storage
assembly {
ds.slot := position
}
// get facet from function selector
address facet = ds.selectorToFacetAndPosition[msg.sig].facetAddress;
require(facet != address(0), "Diamond: Function does not exist");
// Execute external function from facet using delegatecall and return any value.
assembly {
// copy function selector and any arguments
calldatacopy(0, 0, calldatasize())
// execute function call using the facet
let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
// get any return value
returndatacopy(0, 0, returndatasize())
// return any return value or error back to the caller
switch result
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
receive() external payable {}
}
Facets
Implementation contract or lib -> contracts that hold external fn, that the proxy(diamond) will call to.
No limit on number of facets
Facets are deployed separately from your Diamond contract and can be added into your Diamond using diamondCut
The benefit of Facets within a Diamond contract is that it allows you to modularize your code and only update what's needed. Unlike a typical proxy pattern where you would need to redeploy a new implementation contract, with Facets since you can have many implementation contracts, you can update multiple Facets (e.g., multiple implementation contracts) or an individual Facet.