State Variable
The pattern works in the following way:
Store AppStorage in a file.
Any of your facets can now import this file to access the state variables.
s
//SPDX-License-Identifier: MIT
pragma solidity =0.8.9;
library MyLib {
function diamondStorage()
internal
pure
returns (DiamondStorage storage ds)
{
assembly {
ds.slot := 0
}
}
function someFunction() internal {
/// the reason for this line is library cannot declare the variable.
AppStorage storage s = diamondStorage();
... do stuff
}
}
// AavegotchiFacet.sol
import {AppStorage} from "../libraries/LibAppStorage.sol";
contract AavegotchiFacet {
AppStorage internal s; // slot 0
contract ProxyA {
function getOwner() internal view returns(address owner) {
bytes32 position = keccak256("owner");
assembly {
owner := sload(position)
}
}
function setOwner(address owner) internal {
bytes32 position = keccak256("owner");
assembly {
sstore(position, owner)
}
}
function getFacet() internal view returns(address facet) {
bytes32 position = keccak256("FacetA");
assembly {
facet := sload(position)
}
}
function setFacet(address facet) internal {
bytes32 position = keccak256("FacetA");
assembly {
sstore(position, facet)
}
}
}
library MyStructStorage {
bytes32 constant MYSTRUCT_POSITION =
keccak256("com.mycompany.projectx.mystruct");
struct MyStruct {
uint var1;
bytes memory var2;
mapping (address => uint) var3
}
function myStructStorage()
internal
pure
returns (MyStruct storage mystruct)
{
bytes32 position = MYSTRUCT_POSITION;
assembly {
mystruct.slot := position
}
}
}
// ExternalFile.sol
function myFunction() external {
MyStructStorage.MyStruct storage mystruct = MyStructStorage.myStructStorage();
mystruct.var1 = 10;
uint var3 = mystruct.var3[address(this)];
// etc
}
Don’t use the same namespace
Because the two structs will overwrite each other in storage.
library Tomo {
bytes32 constant TOMO_POSITION =
keccak256("TOMOSUKE_IS_SO_COOL");
...
}
library TomoBadExample {
bytes32 constant TOMO1_POSITION =
keccak256("TOMOSUKE_IS_SO_COOL"); //same namespace... OVERWRITE!!
...
}
library TomoGoodExample {
bytes32 constant TOMO1_POSITION =
keccak256("TOMOSUKE_IS_SOOOOOOO_COOL"); //not same namespace 👍
...
}
If you add a variable, add the variable to the end of the struct
You won't be able to add new state variables to inner structs in upgrades.
But you can put structs in mappings, and still extend the structs in the future
library LibTomoStorage {
struct Tomosuke {
uint256 height;
uint256 age;
}
struct TomoStorage {
uint256 value;
Tomosuke tomo; // this Tomosuke sturuct cannot add new state variable.
}
}
https://dev.to/mudgen/how-diamond-storage-works-90e
AppStorage is called in slot 0 so this code is doing below
library LibAppStorage {
function diamondStorage() internal pure returns (AppStorage storage ds) {
assembly {
ds.slot := 0
}
}
https://github.com/aavegotchi/aavegotchi-contracts/blob/master/contracts/Aavegotchi/libraries/LibAppStorage.sol#L303
https://github.com/aavegotchi/aavegotchi-contracts/blob/master/contracts/Aavegotchi/facets/AavegotchiFacet.sol#L18