Store

Understanding how the Store library works is the first task to tackle when working with MUD. Any MUD app — regardless of how many MUD features it decides to bundle — must use Store.

What is Store?

Store is an on-chain database. When building on MUD, developers save contract state into Store**.** It replaces the Solidity compiler-driven data storage: the mapping, variables and arrays defined at the top of a contract.

Solidity compiler-driven storage

// declaring
mapping(address => uint) balances;
// storing
balances[address(0)] = 10;
// getting
return balances[address(0)];

MUD Store

// declaring in the Store config
// Balances: SchemaType.UINT256,
// storing
Balances.set(address(0), 10);
// getting
return Balances.get(address(0));

Store is an embedded EVM database: you can think of it like SQLite, but for the EVM. Contracts can store all their application data — like variables, maps, and arrays — in Store; and any data-model implementable in a SQL database can be represented into Store (ECS, EAV, Graph, etc).

Store is also introspectable: other smart-contracts and off-chain applications can discover the schemas, tables, and records (=rows) of any Store using a standard data-format and EVM event format. This allows for zero-code indexing and frontend networking (no subgraphs, no manual events on data change, no rewriting your storage logic multiple times on your frontend / TheGraph / on-chain).

Finally, Store is gas-efficient: it introduces conservative limits over the Solidity compiler-driven storage enabling additional tighter storage encoding, leading to cheaper storage than native Solidity in some conditions.

Store’s core data model

Store is a tuple-key columnar database. A Store is made out of tables*.* Tables have two different kinds of columns: value columns and key columns. Columns are set when the table is created, but some migrations are possible.

[diagram with tables, record, key columns value columns]

Each table can contain an unlimited amount of records; which are read from and written to by providing all their key columns, which can be thought of as primary keys. Indices are available, but never created by default, more on this in the Index section.

Columns support the same types as Solidity: signed and unsigned integers of all size, strings, bools, bytes, and arrays. Store allows consumers to push and pop any of the record’s arrays, along with accessing the value at a specific index.

Unlike the Solidity compiler-driven storage, tables are created at runtime: they are not hardcoded in the contract’s code. Default tables can be created in the contract’s constructor; while additional tables can be created during the lifetime of the contract. As mentioned earlier, migrations are also supported: columns can be renamed and their types can be changed.

Reading from a Store doesn’t need any ABI definitions: all decoding related information can be found on-chain, making it such that any tool and frontend can fully decode the content of a Store with strongly typed records.

Why we built Store

Store is an attempt at fixing some of the hardest state-related problems of on-chain applications: