Storage Modules

An Extendable contract by itself contains no variables or stores any state. As the main accessors of state, Extensions must be able to read and write state in an Extendable contract, and do so correctly. Extensions are re-usable and can be shared between contracts, so variable access must target the right contract. We use Storage Modules to help define where state should be written.

Storage modules are smart contract libraries that define what variables are being used and what types they are. The library exposes a single _getState() function that is callable by any Extension to fetch the state variables for reading and/or writing.

The storage module for the Extendable looks like the following:

struct ExtendableState {
    // Array of full interfaceIds extended by the Extendable contract instance
    bytes4[] implementedInterfaces;

    // Array of function selectors extended by the Extendable contract instance
    mapping(bytes4 => bytes4[]) implementedFunctionsByInterfaceId;

    // Mapping of interfaceId/functionSelector to the extension address that implements it
    mapping(bytes4 => address) extensionContracts;
}

/**
 * @dev Storage library to access storage slot for the state struct
 */
library ExtendableStorage {
    bytes32 constant private STORAGE_NAME = keccak256("extendable.framework.v1:extendable-state");

    function _getState()
        internal 
        view
        returns (ExtendableState storage extendableState) 
    {
        bytes32 position = keccak256(abi.encodePacked(address(this), STORAGE_NAME));
        assembly {
            extendableState.slot := position
        }
    }
}

The struct on line 1 contains all the variables we need and the library starting on line 15 gives us the ability to access those variables.

Storage layout using this method looks like the following.

Each storage library will specify where variables will be written to by memory address. These memory addresses are indexed by smart contract address also, meaning that each smart contract will have its own addressable storage space. By using the shared storage model, every smart contract will use pre-determined address spaces for variable storage.

In this way Extensions will leverage the variable definitions provided by storage libraries, and gain access to them using the _getState() function.

Last updated