Storage Management

NEAR uses storage staking which means that a contract account must have sufficient balance to cover all storage added over time. This standard provides a uniform way to pass storage costs onto users. It allows accounts and contracts to:

  1. Check an account's storage balance.

  2. Determine the minimum storage needed to add account information such that the account can interact as expected with a contract.

  3. Add storage balance for an account; either one's own or another.

  4. Withdraw some storage deposit by removing associated account data from the contract and then making a call to remove unused deposit.

  5. Unregister an account to recover full storage balance.

Reference-level explanation

  • All amounts, balances and allowance are limited by U128 (max value 2128 - 1).

  • This storage standard uses JSON for serialization of arguments and results.

  • Amounts in arguments and results are serialized as Base-10 strings, e.g. "100". This is done to avoid JSON limitation of max integer value of 253.

  • To prevent the deployed contract from being modified or deleted, it should not have any access keys on its account.

// The structure that will be returned for the methods:
// * `storage_deposit`
// * `storage_withdraw`
// * `storage_balance_of`
// The `total` and `available` values are string representations of unsigned
// 128-bit integers showing the balance of a specific account in yoctoⓃ.
type StorageBalance = {
   total: string;
   available: string;
}

// The below structure will be returned for the method `storage_balance_bounds`.
// Both `min` and `max` are string representations of unsigned 128-bit integers.
//
// `min` is the amount of tokens required to start using this contract at all
// (eg to register with the contract). If a new contract user attaches `min`
// NEAR to a `storage_deposit` call, subsequent calls to `storage_balance_of`
// for this user must show their `total` equal to `min` and `available=0` .
//
// A contract may implement `max` equal to `min` if it only charges for initial
// registration, and does not adjust per-user storage over time. A contract
// which implements `max` must refund deposits that would increase a user's
// storage balance beyond this amount.
type StorageBalanceBounds = {
    min: string;
    max: string|null;
}

/************************************/
/* CHANGE METHODS on fungible token */
/************************************/
// Payable method that receives an attached deposit of Ⓝ for a given account.
//
// If `account_id` is omitted, the deposit MUST go toward predecessor account.
// If provided, deposit MUST go toward this account. If invalid, contract MUST
// panic.
//
// If `registration_only=true`, contract MUST refund above the minimum balance
// if the account wasn't registered and refund full deposit if already
// registered.
//
// The `storage_balance_of.total` + `attached_deposit` in excess of
// `storage_balance_bounds.max` must be refunded to predecessor account.
//
// Returns the StorageBalance structure showing updated balances.
function storage_deposit(
    account_id: string|null,
    registration_only: boolean|null
): StorageBalance {}

// Withdraw specified amount of available Ⓝ for predecessor account.
//
// This method is safe to call. It MUST NOT remove data.
//
// `amount` is sent as a string representing an unsigned 128-bit integer. If
// omitted, contract MUST refund full `available` balance. If `amount` exceeds
// predecessor account's available balance, contract MUST panic.
//
// If predecessor account not registered, contract MUST panic.
//
// MUST require exactly 1 yoctoNEAR attached balance to prevent restricted
// function-call access-key call (UX wallet security)
//
// Returns the StorageBalance structure showing updated balances.
function storage_withdraw(amount: string|null): StorageBalance {}

// Unregisters the predecessor account and returns the storage NEAR deposit.
//
// If the predecessor account is not registered, the function MUST return
// `false` without panic.
//
// If `force=true` the function SHOULD ignore existing account data, such as
// non-zero balances on an FT contract (that is, it should burn such balances),
// and close the account. Contract MAY panic if it doesn't support forced
// unregistration, or if it can't force unregister for the particular situation
// (example: too much data to delete at once).
//
// If `force=false` or `force` is omitted, the contract MUST panic if caller
// has existing account data, such as a positive registered balance (eg token
// holdings).
//
// MUST require exactly 1 yoctoNEAR attached balance to prevent restricted
// function-call access-key call (UX wallet security)
//
// Returns `true` iff the account was successfully unregistered.
// Returns `false` iff account was not registered before.
function storage_unregister(force: boolean|null): boolean {}

/****************/
/* VIEW METHODS */
/****************/
// Returns minimum and maximum allowed balance amounts to interact with this
// contract. See StorageBalanceBounds.
function storage_balance_bounds(): StorageBalanceBounds {}

// Returns the StorageBalance structure of the valid `account_id`
// provided. Must panic if `account_id` is invalid.
//
// If `account_id` is not registered, must return `null`.
function storage_balance_of(account_id: string): StorageBalance|null {}

Drawbacks

  • The idea may confuse contract developers at first until they understand how a system with storage staking works.

  • Some folks in the community would rather see the storage deposit only done for the sender. That is, that no one else should be able to add storage for another user. This stance wasn't adopted in this standard, but others may have similar concerns in the future.

Examples

Last updated