Store is an alternative to Solidity's storage engine.
It enforces a data model that can be mapped directly to a relational database, enables automatic indexing by emitting events on each storage operation, and packs data more tightly than Solidity's storage engine.
It also allows external contract storage to be read onchain without being limited by existing
view functions and without a new opcode (opens in a new tab).
Each piece of data in
Store is stored as a record in a table.
You can think of tables in two ways, either as a relational database or as a key-value store.
- Each table is identified by a unique
- Each record in a table is identified by a unique
bytes32 keyTuple. You can think of the key tuple as a composite key in a relational database, or as a nested mapping in a key-value store.
- Each table has a
ValueSchemathat defines the types of data stored in the table. You can think of the value schema as the column types in a table in a relational database, or the type of structs stored in a key-value store.
Note that when exposing
IStoreWrite methods to external callers, it is necessary to implement an access control mechanism, or use the access control mechanisms provided by
Due to the lack of generics in Solidity, the only way to allow functions to act on data of different types is to cast the data to raw "untyped"
To improve the developer experience,
Store automatically generates a library for each table which acts as a type wrapper.
These libraries provide getter and setter functions with strong types for the table's keys and values, encode them using the Store encoding before passing them to the Store, and decode them before passing them back to the user.
// Example: reading and writing data via table libraries // The Position table library turns the typed // address parameter into a bytes32 keyTuple, // and decodes the return value to (uint32, uint32). (uint32 x, uint32 y) = Position.get(msg.sender); // The Position table library turns the typed // address parameter into a bytes32 keyTuple, // and encodes the (uint32,uint32) tuple into // a tightly packed bytes blob. Position.set(msg.sender, x, y);
Unlike Solidity's storage engine, which requires the storage types to be known at compile time,
Store allows new tables with new schemas to be registered after the
Store contract has been deployed.
This allows advanced use cases like the
Store automatically emits events on every write operation, including when a new table is registered in the
Store at runtime.
These events allow automatic indexers to replicate the onchain state of each table in each
Store contract in a relational database for offchain use, without the need for custom integrations.