Skip to content
by Unity Nodes
docs.near.orgsmart contracts › anatomy › storage

State

Updated 01-11-2026

Explore how NEAR smart contracts manage their state.

Smart contracts store data in their account's state, which is public on the chain. The storage starts empty until a contract is deployed and the state is initialized.

It is important to know that the account's code and account's storage are independent. Updating the code does not erase the state.

Defining the State

The attributes of the struct marked as the contract define the data that will be stored.

The contract can store all native types (e.g. u8, string, HashMap, Vector) as well as complex objects.

For example, our Auction contract stores when the auction ends, and an object representing the highest bid.

[!NOTE]

The structures that will be saved need a special macro that tells the SDK to store them serialized in Borsh.

[!NOTE]

The SDK also provides collections to efficiently store collections of data.

#[near(serializers = [json, borsh])]
#[derive(Clone)]
pub struct Bid {
    pub bidder: AccountId,
    pub bid: NearToken,
}

#[near(contract_state)]
#[derive(PanicOnDefault)]
pub struct Contract {
    highest_bid: Bid,
    auction_end_time: U64,
    auctioneer: AccountId,
    claimed: bool,
}

See code on GitHub

[!WARNING]

Contracts pay for their storage by locking part of their balance.

It currently costs ~1Ⓝ to store 100KB of data.

Initializing the State

After the contract is deployed, its state is empty and needs to be initialized with some initial values.

There are two ways to initialize a state:

  1. By creating an initialization function
  2. By setting default values

I. Initialization Functions

An option to initialize the state is to create an initialization function, which needs to be called before executing any other function.

In our Auction example, the contract has an initialization function that sets when the auction ends. The contract derives the PanicOnDefault, which forces the user to call the init method denoted by the #[init] macro.

[!NOTE]

It is a good practice to mark initialization functions as private. We will cover function types in the functions section.

#[near(contract_state)]
#[derive(PanicOnDefault)]
pub struct Contract {
    highest_bid: Bid,
    auction_end_time: U64,
    auctioneer: AccountId,
    claimed: bool,
}

See code on GitHub

#[init]
pub fn init(end_time: U64, auctioneer: AccountId) -> Self {
    Self {
        highest_bid: Bid {
            bidder: env::current_account_id(),
            bid: NearToken::from_yoctonear(1),
        },
        auction_end_time: end_time,
        claimed: false,
        auctioneer,
    }
}

See code on GitHub

II. Default State

Another option to initialize the state is to create a Default version of our contract's struct.

For example, our "Hello World" contract has a default state with a greeting set to "Hello".

The first time the contract executes, the Default will be stored in the state, and the state will be considered initialized.

[!NOTE]

The state can only be initialized once.

#[near(contract_state)]
pub struct Contract {
    greeting: String,
}

See code on GitHub

impl Default for Contract {
    fn default() -> Self {
        Self {
            greeting: "Hello".to_string(),
        }
    }
}

See code on GitHub

[!NOTE]

Lifecycle of the State

When a function is called, the contract's state is loaded from the storage and put into memory.

The state is actually stored serialized, and the SDK takes a bit of time to deserialize it before the method can access it.

When the method finishes executing successfully, all the changes to the state are serialized, and saved back to the storage.

[!WARNING]

State and Code

In NEAR, the contract's code and contract's storage are independent.

Updating the code of a contract does not erase the state, and can indeed lead to unexpected behavior or errors.

Make sure to read the updating a contract if you run into issues.