Updating Contracts

To Share and +4 nLEARNs

import {CodeBlock} from ‘@theme/CodeBlock’
import {CodeTabs, Language, Github} from "@site/components/codetabs"
import Tabs from ‘@theme/Tabs’;
import TabItem from ‘@theme/TabItem’;

NEAR accounts separate their logic (contract’s code) from their state (storage), allowing the code to be changed.

Contract’s can be updated in two ways:

  1. Through tools such as NEAR CLI or near-api-js (if you hold the account’s full access key).
  2. Programmatically, by implementing a method that takes the new code and deploys it.

Updating Through Tools

Simply re-deploy another contract using your preferred tool, for example, using NEAR CLI:

# If you already used dev-deploy the same account will be used
near dev-deploy --wasmFile <new-contract>

# If you logged in
near deploy <account-id> --wasmFile <new-contract>

Programmatic Update

A smart contract can also update itself by implementing a method that:

  1. Takes the new wasm contract as input
  2. Creates a Promise to deploy it on itself

How to Invoke Such Method?

# Load the contract’s raw bytes
CONTRACT_BYTES=`cat ./path/to/wasm.wasm | base64`

# Call the update_contract method
near call update_contract “$CONTRACT_BYTES” –base64 –accountId –gas 300000000000000

// Load the contract’s raw bytes
const code = fs.readFileSync(“./path/to/wasm.wasm”);

// Call the update_contract method
await wallet.callMethod({contractId: guestBook, method: “update_contract”, args: code, gas: “300000000000000”});

:::tip DAO Factories
This is how DAO factories update their contracts

Migrating the State

Since the account’s logic (smart contract) is separated from the account’s state (storage),
the account’s state persists when re-deploying a contract.

Because of this, adding methods or modifying existing ones will yield no problems.

However, deploying a contract that modifies or removes structures stored in the state will raise an
error: Cannot deserialize the contract state, in which case you can choose to:

  1. Use a different account
  2. Rollback to the previous contract code
  3. Add a method to migrate the contract’s state

The Migration Method

If you have no option but to migrate the state, then you need to implement a method that:

  1. Reads the current state of the contract
  2. Applies different functions to transform it into the new state
  3. Returns the new state
:::tip DAO Update
This is how DAOs update themselves

Example: Guest Book Migration

Imagine you have a Guest Book where you store messages, and the users can pay for such messages
to be "premium". You keep track of the messages and payments using the following state:

Update Contract

At some point you realize that you could keep track of the payments inside of the PostedMessage itself,
so you change the contract to:

Incompatible States

If you deploy the update into an initialized account the contract will fail to deserialize the account’s state,

  1. There is an extra payments vector saved in the state (from the previous contract)
  2. The stored PostedMessages are missing the payment field (as in the previous contract)

Migrating the State

To fix the problem, you need to implement a method that goes through the old state, removes the payments vector and
adds the information to the PostedMessages:

Notice that migrate is actually an initialization method that ignores the existing state ([#init(ignore_state)]), thus being able to execute and rewrite the state.

You can follow a migration step by step in the official migration example

Scroll to Top
Report a bug👀