A modular smart contract system that can be upgraded/extended after deployment, and have virtually no size limit. More technically, a diamond is a contract with external functions that are supplied by contracts called facets/implementation contracts. Facets are separate, independent contracts that can share internal functions, libraries, and state variables.
- A single address for unlimited contract functionality.
- Your contract can exceed the 24KB maximum contract size.
- Better organize contract code and data.
- Allows for upgrading functionality or fixing vulnerabilities.
The core proxy contract which is responsible for delegateCall
ing the target function depending on the functionSelector
i.e. the first 4 bytes of the transaction calldata.
DiamondStorage
is a struct storing the function registry and other common states of the proxy system which will be shared by the facet and proxy contracts. DiamondStorage
struct is stored in an arbitrary storage location in DiamondProxy
contract storage to avoid storage collisions with other state variables of the system. This is know as the Diamond Storage pattern.
struct DiamondStorage {
mapping(bytes4 => address) functionSelectorAndFacet;
address contractOwner;
mapping(address => uint256) userBalances;
}
We're assuming
contractOwner
to be a single entity for this implementation but can be replaced by more sophisticated entities like DAO to avoid centralisation.
It is nothing but a simple mapping between function selectors => facet address
. Facet address is the address to the contract that provides the implementation logic of that particular function.
You can consider them as info objects for actions like adding, replacing and removing function selectors from the function registry, being performed by the administrative authority of the proxy system (which in our case is a single owner address).
/// @dev Defines the action to be performed for a particular FacetCut
// 0: ADD, 1: REPLACE, 2: REMOVE
enum FacetCutAction {
ADD,
REPLACE,
REMOVE
}
/// @dev Struct defining a facet action and its corresponding info
struct FacetCut {
address facetAddress;
bytes4[] functionSelectors;
FacetCutAction action;
}
git clone https://github.com/alfheimrShiven/Wasserstoff-Task-2-2024-Blockchain-Interviews.git
cd Wasserstoff-Task-2-2024-Blockchain-Interviews
make start
make anvil
This will default to your local node which is running the Anvil chain. You need to have it running in another terminal in order for it to deploy.
make deploy
- Unit
- Integration
forge test
forge coverage
- Setup environment variables
You'll want to set your SEPOLIA_RPC_URL
and SEPOLIA_PRIVATE_KEY
as environment variables. You can add them to a .env
file.
Optionally, add your ETHERSCAN_API_KEY
if you want to verify your contract on Etherscan.
- Deploy on Sepolia
make deploy ARGS="--network sepolia"
- Diamond Proxy Pattern: The Diamond pattern was a perfect fit for the problem statement.
- Transparent Proxy Pattern: This pattern is implemented to avoid function selector clashes (Preventing users from calling
performFacetAction()
and preventing admins from calling facet contracts). Refer commit. Did not useonlyOwner
modifier as it can prevent all function call forwarding to a facet incase of a function clash, when called by any non-owner. - LibDiamond: Abstracted all facet actions in this library. Read code.
Shivendra Singh