Skip to main content

Storage

Every full node in the network stores every byte written to MOI, for as long as that byte exists. Computation is paid for once, when it runs. Storage is different: its cost continues long after the interaction that created it. If a network charges only for computation, one payment puts a permanent load on every node.

MOI solves this with collateral. An account does not pay a fee for storage. Instead, it must hold KMOI in proportion to the bytes it occupies. Nothing is paid, and nothing is held in escrow. The tokens simply cannot be spent while the storage exists. Free the storage, and the tokens become spendable again.

This page explains why storage has a price, what the collateral model requires from an account, and how MOI tracks and attributes storage. This includes Logic and Asset accounts, which many users write to.

Why Storage Must Have a Price

When on-chain storage is free, users and the network have opposite incentives:

  1. Unbounded state growth: users get no incentive for removing old data, so they do not remove it.
  2. Higher node costs: disk, bandwidth, and state-management costs increase as the state grows.
  3. Weaker decentralization: as a node becomes more costly to operate, fewer participants can run one. This weakens the security of the network.

A price on storage breaks this cycle. The participant who occupies the space pays its cost, and releasing space becomes worth doing.

How Other Protocols Price Storage

Protocols use three general approaches. Each one solves the incentive problem to a different degree.

ModelProtocolMechanismTrade-off
Storage FundSuiPay at the start, 99% rebate on deletionComplex rebate calculation, sensitive to price changes
Per-TransactionEthereumStorage opcodes have a price in gasNo continued cost, so the state keeps growing
Storage DepositSolanaDeposit held in the account must back the bytes it storesCorrect incentives, with the deposit locked inside each data-account it funds
CollateralMOIHold KMOI in proportion to the bytes occupiedSimple, fully reversible, correct incentives

Sui has the correct incentive, but its deposit must be moved and refunded, with a complex rebate calculation. Solana does the same as MOI: its deposit must back the bytes an account stores. Ethereum never charges a continued cost, so the state keeps growing. Collateral keeps the incentive without the machinery: nothing moves and nothing is refunded - only a balance requirement that rises and falls with usage.

The Collateral Model

Storage on MOI does not have a price in the usual sense. It has a balance requirement: at all times, an account must hold enough KMOI to back the bytes it occupies.

The Balance Requirement

Two governance parameters set the price of a byte:

ParameterMeaning
ANUPerByteBase cost for each byte, in ANUs (the smallest unit of KMOI)
StorageMultiplierScale factor for economic adjustment

Because both are governance parameters, the price of storage can be adjusted as hardware costs change, without a protocol change.

The requirement is a single inequality:

Balance[KMOI] ≥ StorageConsumed × StorageMultiplier × ANUPerByte

In simple terms: multiply the bytes an account occupies (its StorageConsumed, covered next) by the current price per byte. The account's KMOI balance must be at least that number.

The protocol checks this after every interaction. If the check fails, the interaction is reverted. This has two effects:

  • An interaction cannot push an account's storage beyond what its balance can back. There is no later settlement, no debt, and no grace period.
  • Because every interaction is checked, the requirement holds at every point in the chain's history. You never need to ask whether an account is behind on storage - by construction, it cannot be.
note

The collateral is never moved, it is locked in the account itself serving as a floor on the account's spendable balance, and the moment storage is freed, the floor drops and those tokens become spendable again.

What Each Account Tracks

The requirement needs one number per account: the bytes it occupies. MOI counts storage per account, in line with its participant-centric state model. Every account has a StorageConsumed field. It records the bytes the account occupies on-chain right now, rather than the bytes it has ever written. After each interaction, the protocol updates the field with the net effect of that execution:

StorageConsumed = StorageConsumed − StorageReleased + StorageAdded

A write adds bytes, and a deletion releases them. When you overwrite a value, only the difference between the old size and the new size counts. A new key is charged for both the key and the value. An overwrite does not change the key, so only the change in the value counts:

OperationBytes addedBytes released
Write a new key with a 100-byte valuekey + 1000
Overwrite that value with 50 bytes50100
Delete the key0key + 50

Shared Accounts and the Storage Registry

A single StorageConsumed field is enough when one participant owns the data. Logic and Asset accounts are different: many users may write into shared-storage, so the account's total does not show which user is resposible for which bytes.

The Storage Registry adds this per-user accounting on top of the account-level total. Every account maintains a registry tree, and each entry in it maps a participant's account ID to a StorageMetric, recording the capacity the user was granted, the bytes they occupy, and the tokens they deposited:

type StorageMetric struct {
StorageGranted big.Int // bytes the user is allowed to occupy on this account
StorageConsumed big.Int // bytes the user currently occupies on this account
TokensDeposited big.Int // KMOI deposited to back the grant
}

At first glance, TokensDeposited looks redundant: the grant is already recorded in bytes, and the price per byte would seem enough to reconstruct the deposit. But the price of a byte is a governance parameter, and it can change between the deposit and the withdrawal.

Suppose a user deposits 1,000 KMOI when a byte costs 1 KMOI, receiving a grant of 1,000 bytes. Governance later raises the price to 2 KMOI per byte, maybe as a consequence of the real-world value of KMOI dropping. If refunds were computed from bytes at the current price, releasing those 1,000 bytes would return 2,000 KMOI - twice what the user put in, minted out of a parameter change. TokensDeposited closes this gap: it records exactly what the user paid in, and withdrawals are settled against that record, so over the lifetime of a grant the total refunded can never exceed the total deposited.

Users manage their own allocation on a shared account with two interactions:

InteractionEffect
IxStorageDepositDeposit KMOI into a Logic or Asset account and receive storage capacity
IxStorageWithdrawRelease unused capacity and get the deposited KMOI back

A write on a shared account must therefore pass two checks. The user's writes must stay within their grant (StorageConsumed ≤ StorageGranted in their entry), and the account as a whole must still satisfy the balance requirement above. If a caller has used all their granted capacity, a logic cannot write on their behalf until they deposit more.

How the Protocol Counts Bytes

The bytes in StorageConsumed are logical, not physical. That means instead of measuring what a write actually costs on disk, it calculates the cost from the types and sizes of the fields being written, plus a markup. The markup covers serialization, tree nodes, and indexing overhead:

LogicalCost = BaseFieldSize + Markup
RationaleBenefit
DeterministicThe same input always gives the same cost.
PredictableUsers can calculate costs before execution.
SimpleNo measurement of disk I/O is necessary.
AdjustableGovernance can adjust the markup values.

Who Pays for a Byte

Every byte has a payer, and the payer is not always the account that holds the data. The logic developer decides which of the two it is:

ScenarioData lives onCharged toRegistry
User writes to their own account via a logicUser accountUserNo
User writes to a logic's storageLogic accountUserYes
User calls Logic A, which writes into Logic BLogic BUserYes
Logic sponsors its users' storage (self-pay)Logic accountLogic itselfNo

The registry is involved whenever the payer differs from the account that holds the data. The last row matters most for application design. A logic can pay for its users' writes to its own state, backing the collateral from its own balance. End users then never need to hold KMOI for the data an application keeps on their behalf.

The protocol collects the attribution during execution and applies it only when the interaction succeeds. If the interaction reverts, no storage counts change.

How to Query Storage

Three JSON-RPC methods expose the storage state to clients:

MethodReturns
moi.AccountStateAccount state, including its storage_consumed
moi.StorageMetricA user's StorageMetric on a Logic or Asset account
moi.StoragePricingCurrent pricing parameters (ANUPerByte, StorageMultiplier)

Together, they answer three practical questions: how much storage is in use, how much has been granted, and what a byte costs right now.

What It Means for Node Operators

Users deposit KMOI, write bytes, clean up, and get every token back. None of it flows to node operators, who are paid through fuel fees and network rewards. The collateral model helps them in two ways:

  • Limited state growth. Every byte on disk is backed by tokens that someone would rather spend, so there is a standing reason to remove old data. The total state is limited by what participants are willing to lock up, and so are each operator's disk and bandwidth costs.
  • Reduced circulating supply. KMOI backing storage stays out of circulation while the data exists. A smaller supply supports the value of the token, so the fuel fees and rewards operators earn are worth more.

Where Next

  • Storage-Cost Tutorial: a tutorial that shows how to experiment with storage costs on MOI.
  • Interactions: how accounts start and authorize state transitions.
  • Assets: how MOI represents ownership natively at the account level.
  • Logics: how developers build and deploy applications on MOI.