Skip to main content

Facets

Facets are how we extend our contract functionality.

What are Facets

Facets are smaller, modular contracts that form the building blocks of a Diamond contract in the EIP-2535 Diamond standard. They contain the actual functionality or implementation of the Diamond contract, which is composed of multiple Facets.

Facets are considered stateless because they do not store any data or state themselves. Instead, they rely on the Diamond contract to manage the storage for all its Facets. This separation of functionality and state enables the Diamond contract to upgrade or extend its functionality by adding, replacing, or removing Facets without affecting the contract's data or state.

Swappable

Here's an analogy to help clarify the concept: Imagine a car (the Diamond contract) with various interchangeable parts (the Facets). Each part has a specific function, like an engine for power or wheels for movement, but none of the parts store any data about the car. The car itself (the Diamond contract) holds all the data and state, such as its mileage or fuel level. By swapping out different parts (Facets), you can upgrade or modify the car without changing its inherent data or state.

In summary, Facets are stateless, modular contracts that provide the functionality of a Diamond contract in the EIP-2535 Diamond standard. They allow for easy upgradeability and extensibility of the Diamond contract without affecting its data or state.

The facets that 1o1 supports

We have built the following standard facets that we bundle into presets to install. You can see a list of the current facets we support via the UX advanced mode option when you create a token, or you can see them via Reference

Can you create your own facets and add them

Certainly, if you deploy a smart contract that adheres to the diamond standard facet storage organization.

In EIP-2535 Diamonds, the Diamond contract holds the storage for all its Facets, and the storage management is typically implemented using a combination of storage pointer locations and hashing techniques.

To avoid storage collisions, a common approach is to use a unique storage slot for each Facet. This is achieved by defining a storage pointer location and combining it with the hash of the Facet's contract address or a unique identifier. This process generates a unique storage slot in the Diamond contract for each Facet.

Here's a quick storage primer example

In this example, both FacetA and FacetB use the keccak256 hashing function to create a unique storage position based on a string identifier. Then, they use inline assembly to set the storage slot for their respective Storage struct. This approach ensures that the storage slots for FacetA and FacetB do not collide and can coexist within the Diamond contract.

Storage Example

pragma solidity ^0.8.0;

contract FacetA {
bytes32 constant FACET_A_STORAGE_POSITION = keccak256("diamond.facetA");

struct Storage {
uint256 data;
}

function _facetAStorage() internal pure returns (Storage storage store) {
bytes32 position = FACET_A_STORAGE_POSITION;
assembly {
store.slot := position
}
}

function setData(uint256 _data) external {
_facetAStorage().data = _data;
}

function getData() external view returns (uint256) {
return _facetAStorage().data;
}
}

contract FacetB {
bytes32 constant FACET_B_STORAGE_POSITION = keccak256("diamond.facetB");

struct Storage {
uint256 data;
}

function _facetBStorage() internal pure returns (Storage storage store) {
bytes32 position = FACET_B_STORAGE_POSITION;
assembly {
store.slot := position
}
}

function setData(uint256 _data) external {
_facetBStorage().data = _data;
}

function getData() external view returns (uint256) {
return _facetBStorage().data;
}
}

Reference

How Diamond Storage Works