Today, the EVM exposes 32-byte slots, but hardware works in ~4KB pages. Reading one slot pulls in an entire page, so most of the bandwidth is unused. Hashing keys also destroys locality, so related data ends up scattered.
We instead treat a 4096-byte (128 slot) page as the unit of access and commitment. Storage is grouped into pages, and each page is committed with a binary tree.
Gas follows access patterns. The first touch to a page is expensive. Once loaded, all slots in that page are warm. This makes contiguous layouts (arrays, structs) naturally cheaper, without breaking sparse ones.
Any feedback or discussion is appreciated on:
exact gas schedule for page-level SLOAD / SSTORE and how to track net state growth
commitment scheme details especially single-slot proof size under the BLAKE3 tree
worst-case read amplification if logical pages do not align with physical I/O
how this changes Solidity storage patterns in practice
From prior discussion, once one slot in the page has been loaded, then all other slots in that page would be charged the low warm access storage load cost, rather than the high cold access cost.
This is bringing some exciting new possibilities. With a little bit of assembly magic, page-aware arrays, and mappings to arrays or structs will definitely unlock some new patterns.
To make MIP-8 easier to understand for everyone out there, I built an interactive explainer for MIP-8: [pageified-storage.vercel.app](https://pageified-storage.vercel.app) It walks through the core idea with a few interactive demos.
Shoutout to Ben from CL for the great feedback that shaped this into its current form, and the broader CL team for clean docs on this mip.
Would love feedback on accuracy, missing examples, or anything that could make the concept click better for developers seeing MIP-8 for the first time.
Shouldn’t the 0 -> Y -> 0 case still consume BASE_SSTORE_COST? It makes sense to bypass the growth fee and decrement slot_delta_counter[P] since the tx-local growth is being undone, but the operation is still an SSTORE.
I am very curious about how the existing data will be switched from the current MonadDB MPT structure to the MIP8-based structure. At the moment, it seems that the existing data is still stored using hash(slot). If switching to hash(page_index), will a large amount of data need to be migrated during the upgrade? Or will there be a read-time migration compatibility layer?
You are correct here. This is underspecified here. This will be corrected along with a slight change to the gas schedule to address another under-specified issue.
The shift to page-aware storage is a logical step for hardware efficiency, but I’m interested in the ‘net state growth’ aspect mentioned. If subsequent slots in a 4KB page are significantly cheaper after the first touch, does this create an incentive for developers to pack more data into the state than they otherwise would? I’d be interested in seeing a simulation of how this gas schedule affects the long-term growth rate of the Monad state compared to the standard 32-byte slot model.
The cost that is being amortized is the i/o cost. So slots in a “hot” page are cheaper to read/write to. However, the state growth cost remains functionally the same. So writing to k slots pre-mip 8 will cost roughly the same as writing to k slots post mip 8 in terms of state growth cost.
However, there is no storage refunds on Monad due to async execution. So to mimic a refund, if a user frees a slot then adds another slot in the page, the result is that there is no new state growth. The state growth was paid in a previous transaction. This is the net state growth cost. So the incentive structure is unchanged if i/o cost is factored out.