DataStore Library
Storage Management library for dynamic structs based on data types
DataStoreUtils is a storage management tool designed to create a safe and scalable storage layout with the help of ids and keys. Mainly focusing on upgradable contracts with multiple user types to create a sustainable development environment.
In summary, extra gas cost that would be saved with Storage packing are ignored to create upgradable structs.

Storage Layout

IDs:

IDs are the representation of a user with any given key as properties.
Type for ID is not mandatory, not all IDs should have an explicit type. Thus there is no checks of types or keys.
Since we have different types of roles and operations in Portal such as:
* TYPE 0: inactive
* TYPE 1: Senate-proposal
* TYPE 2: Upgrade-proposal
* TYPE 3: **this type is reserved**
* TYPE 4: operator
* TYPE 5: public pool
It was somehow necessary and mostly beneficial to handle that different IDs with different types in a dynamic way. That is what DataStoreLib.sol is all about.
DataStoreLib in Portal basically handles the read and write processes in a dynamic way using mapping. The DataStore struct consist of several types mapping relations.
To be more exact, check out the code below.

The DataStore Struct

DataStoreLib.sol
struct DataStore {
// type[0,1,2,3...] => ID list
mapping(uint256 => uint256[]) allIdsByType;
// keccak(id, key) => data
mapping(bytes32 => uint256) UintData;
mapping(bytes32 => bytes) BytesData;
mapping(bytes32 => address) AddressData;
}
In the struct, there are 4 different mapping to serve different types of storage.

Sample Write Operation

DataStoreLib.sol
function writeUintForId(
DataStore storage self,
uint256 _id,
bytes32 _key,
uint256 data
) public {
self.UintData[keccak256(abi.encodePacked(_id, _key))] = data;
}
Basically, it takes the id and key pair encoded, secures a slot in the mapping for the id&key pair and assigns the data to slot in the UintData mapping where DataStore struct is taken as a storage argument. Every write operation in the library follows the same procedure.

Sample Read Operation

DataStoreLib.sol
function readUintForId(
DataStore storage self,
uint256 _id,
bytes32 _key
) public view returns (uint256 data) {
data = self.UintData[keccak256(abi.encodePacked(_id, _key))];
}
This is also a typical read operation in the library, getting the data from the assigned slot with the given id and key pair returning the data publicly with the view restriction.
Copy link
On this page
Storage Layout
IDs: