Modular contracts are composed of two core components:
- Core Contracts
- Module Contracts
Core contracts serve as the foundation upon which module contracts are installed. Module contracts are smart contracts that provide additional functionality and can be installed or uninstalled from a core contract. You can think of core and module contracts as building blocks, like Lego pieces.
Once installed, module contracts provide additional functionality by creating fallback
and callback
functions that the core contract can interact with through delegate calls.
- Fallback functions are independent functions that can be called through the core contract.
- Callback functions are dependent functions that must be called within the context of a core contract function. These functions enhance the core contract's functionality by executing additional logic either before or after the main core contract logic.
Like Lego pieces, modular contracts must "fit" together properly. If the pieces don't align—meaning the contracts aren't compatible—they cannot be installed together.
This raises the question:
Two factors determine compatibility: interfaces and callback functions.
- If a module contract has a callback function that the core contract doesn’t support (or is already in use), it can’t be installed.
- Similarly, if a module contract requires a certain interface to be supported by the core contract and it doesn’t have it, then it can’t be installed.
Core contracts expose their supported callback functions and interfaces through two methods:
getSupportedCallbackFunctions
- to expose the core contract’s supported callback functions.supportsInterface
- to expose the core contract’s supported interfaces.
Module contracts also expose their supplied callback and fallback functions, along with the required interfaces, via the getModuleConfig
function.
Let’s walk through an example with three contracts:
ERC721Core
MintableModule
TransferableModule
ERC721Core
Example
The ERC721Core
contract supports the beforeMint
callback and the ERC-721 interface (0x80ac58cd
).
MintableModule
Example
The MintableModule
provides the beforeMint
callback and requires the ERC-721 interface.
TransferableModule
Example
The TransferableModule
provides the beforeTransfer
callback and also requires the ERC-721 interface.
The ERC721Core
contract supports the ERC-721 interface (0x80ac58cd
), which is required by both the MintableModule
and the TransferableModule
.
However, since ERC721Core
only supports the beforeMint
callback, only the MintableModule
can be installed. The TransferableModule
, which provides the beforeTransfer
callback, cannot be installed because ERC721Core
does not support it.
Modular contracts work by allowing core contracts to interact with module contracts through delegate calls to fallback
and callback
functions. Compatibility between core and module contracts is determined by their supported interfaces and callback functions. This modular architecture provides flexibility, allowing functionality to be added or removed as needed, like assembling or disassembling Lego pieces.