Solidstate contract library. Part of the Solidstate Solidity monorepo.
Note: An old version of this library has been audited by Hacken. More details are available in the report.
Install the package as a development dependency:
npm install --save-dev @solidstate/contracts
# or
yarn add --dev @solidstate/contracts
All contracts are designed to either be deployed through the standard constructor
method, or referenced by a proxy. To this end, the diamond storage pattern is employed exclusively.
Each of the Solidstate contracts is split into multiple "layers" across multiple files: external contracts, internal contracts, external interfaces, internal interfaces, and storage libraries.
This is done to give the developer granular control over which functions are available in each context. Some examples:
- A proxy may need to be initialized by calling some
internal
functions. It should not inherit anyexternal
functions because these should be defined on its implementation contract. Instead, it can inheritinternal
functions only from an internal contract and call them in itsconstructor
. - A diamond proxy may require that some code be shared among its implementation contracts. To avoid code duplication or making external calls between implementations, each implementation can inherit the shared
internal
functions from a single internal contract. - On the client side, a diamond proxy is typically interacted with through the use of a composite ABI which includes the functions, errors, and events from each of the implementation contracts. The compiler can create such an ABI if the implementations are all inherited into a single contract, but this composite contract may exceed the size limit. Instead, the implementations' respective interfaces may be imported and combined without running this risk.
- An upgradeable contract might change drastically over its lifetime, and some of its functions (including
internal
functions) might be removed entirely. The storage data corresponding to old code will remain, however, and can be accessed using a storage library.
An overview of the uses of each layer is as follows:
layer | contents | description | example |
---|---|---|---|
External Contract | external functions |
set of externally callable functions | FungibleToken.sol |
Internal Contract | internal functions |
set of internal functions that define a module's core logic; may be called by inheriting contracts | _FungibleToken.sol |
External Interface | external function declarations and NatSpec documentation |
set of function declarations that constitute a module's external interface | IFungibleToken.sol |
Internal Interface | event , error , enum , struct |
set of non-function elements of a module's interface | _IFungibleToken.sol |
Storage Library | storage layout struct (Layout ), getter function (layout() ), and standard storage location (DEFAULT_STORAGE_SLOT ) |
library for accessing and modifying storage; useful when sharing access to storage between implementation contracts that will be deployed separately (such as in the "diamond" proxy architecture) | ERC20Storage.sol |
Solidstate maintains "recommended" implementations of various standards, which are suitable for most users. Internally, these implementations may be composed of several modules, which themselves may be composed of several "visibility layers". Visibility layers are subject to a consistent naming convention so that their purposes may be easily identified.
For example, the SolidstateFungibleToken
contract contains FungibleToken
, FungibleTokenExtended
and FungibleTokenMetadata
modules (among others), which are recommended for most projects.
The repository also contains a set of standard interfaces for interacting with third-party contracts. These interfaces are typically taken directly from their EIPs (IERC20
, IERC721
) sometimes correspond to conventions that aren't defined standards (IWETH
). These are found in the interfaces/ directory, and also follow the layers pattern.