DCIPs/assets/eip-2535/storage-examples/AppStorage.sol

110 lines
4.3 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Diamond Storage is particularly good for isolating or compartmenting state variables to specific
// facets or functionality. This is great for creating modular facets that can be understood as their
// own units and be added to diamonds. A diamond with a lot of functionality is well organized and
// understandable if each of its facets can be understood in isolation. Diamond Storage helps make that
// possible.
// However, you may want to share state variables specific to your application with facets that are specific
// to your application. It can get somewhat tedious to call a `diamondStorage()` function in every function
// that you want to access state variables.
// `AppStorage` is a specialized version of Diamond Storage. It is a more convenient way to access
// application specific state variables that are shared among facets.
// The pattern works in the following way:
// 1. Define a struct called AppStorage that contains all the state variables specific to your application
// and that you plan to share with different facets. Store AppStorage in a file. Any of your facets can
// now import this file to access the state variables.
struct AppStorage {
uint256 secondVar;
uint256 firstVar;
uint256 lastVar;
// add other state variables ...
}
// 2. In a facet that imports the AppStorage struct declare an AppStorage state variable called `s`.
// This should be the only state variable declared in the facet.
// 3. In your facet you can now access all the state variables in AppStorage by prepending state variables
// with `s.`. Here is example code:
// import { AppStorage } from "./LibAppStorage.sol";
contract AFacet {
AppStorage internal s;
function sumVariables() external {
s.lastVar = s.firstVar + s.secondVar;
}
function getFirsVar() external view returns (uint256) {
return s.firstVar;
}
function setLastVar(uint256 _newValue) external {
s.lastVar = _newValue;
}
}
// Sharing AppStorage in another facet:
// import { AppStorage } from "./LibAppStorage.sol";
contract SomeOtherFacet {
AppStorage internal s;
function getLargerVar() external view returns (uint256) {
uint256 firstVar = s.firstVar;
uint256 secondVar = s.secondVar;
if(firstVar > secondVar) {
return firstVar;
}
else {
return secondVar;
}
}
}
// Using the 's.' prefix to access AppStorage is a nice convention because it makes state variables
// concise, easy to access, and it distinguishes state variables from local variables and prevents
// name clashes/shadowing with local variables and function names. It helps identify and make
// explicit state variables in a convenient and concise way. AppStorage can be used in regualar
// contracts as well as proxy contracts, diamonds, implementation contracts, Solidity libraries and
// facets.
// Since `AppStorage s` is the first and only state variable declared in facets its position in
// contract storage is `0`. This fact can be used to access AppStorage in Solidity libraries using
// diamond storage access. Here's an example of that:
library LibAppStorage {
function appStorage() internal pure returns (AppStorage storage ds) {
assembly { ds.slot := 0 }
}
function someFunction() internal {
AppStorage storage s = appStorage();
s.firstVar = 8;
//... do more stuff
}
}
// `AppStorage s` can be declared as the one and only state variable in facets or it can be declared in a
// contract that facets inherit.
// AppStorage won't work if state variables are declared outside of AppStorage and outside of Diamond Storage.
// It is a common error for a facet to inherit a contract that declares state variables outside AppStorage and
// Diamond Storage. This causes a misalignment of state variables.
// One downside is that state variables can't be declared public in structs so getter functions can't
// automatically be created this way. But it can be nice to make your own getter functions for
// state variables because it is explicit.
// The rules for upgrading AppStorage are the same for Diamond Storage. These rules can be found at
// the end of the file ./DiamondStorage.sol