Skip to content

Migrate examples to tutorials: How to migrate state on your contract #2691

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 52 additions & 49 deletions docs/tutorials/examples/update.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
---
id: update-contract-migrate-state
title: Self Upgrade & State Migration
description: "Learn how to implement upgradeable smart contracts on NEAR Protocol, including migration patterns and state management during contract updates."
id: migrate-state
title: How to Migrate State on Your Contract
description: "Learn how to migrate state when updating smart contracts on NEAR Protocol, including implementation, trade-offs, alternatives, and testing."
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import {CodeTabs, Language, Github} from "@site/src/components/codetabs"

Three examples on how to handle updates and [state migration](../../smart-contracts/release/upgrade.md):
1. [State Migration](https://github.com/near-examples/update-migrate-rust/tree/main/basic-updates): How to implement a `migrate` method to migrate state between contract updates.
2. [State Versioning](https://github.com/near-examples/update-migrate-rust/tree/main/enum-updates): How to use readily use versioning on a state, to simplify updating it later.
3. [Self Update](https://github.com/near-examples/update-migrate-rust/tree/main/self-updates): How to implement a contract that can update itself.
## Understanding State Migration
When updating a smart contract on NEAR, changes to the state structure (e.g., adding/removing fields) can break compatibility with existing data. State migration allows you to transform old state to fit the new contract version during the upgrade process.

---
This is done via a `migrate` method, called after deploying the new contract code. It deserializes the old state, transforms it, and serializes the new state.

## State Migration
The [State Migration example](https://github.com/near-examples/update-migrate-rust/tree/main/basic-updates) shows how to handle state-breaking changes
between contract updates.
### Key Concept: The Migration Method
Implement a migration method as an initialization function that ignores the existing state and rewrites it. For example, starting from a basic Guest Book contract (old state: messages without payment), migrate to a version that adds a `payment` field.

It is composed by 2 contracts:
1. Base: A Guest Book where people can write messages.
2. Update: An update in which we remove a parameter and change the internal structure.
The example uses two contracts:
- Base: Simple Guest Book.
- Update: Adds `payment` to messages and restructures internally.

<CodeTabs>
<Language value="rust" language="rust">
Expand All @@ -30,49 +28,54 @@ It is composed by 2 contracts:
</Language>
</CodeTabs>

#### The Migration Method
The migration method deserializes the current state (`OldState`) and iterates through the messages, updating them
to the new `PostedMessage` that includes the `payment` field.

:::tip
Notice that migrate is actually an [initialization method](../../smart-contracts/anatomy/storage.md) that ignores the existing state (`[#init(ignore_state)]`), thus being able to execute and rewrite the state.
The `migrate` method uses `#[init(ignore_state)]` to overwrite the state safely.
:::

---
In this code, it iterates through old messages, adds a default `payment` (e.g., 0), and saves the updated structure.

## State Versioning
The [State Versioning example](https://github.com/near-examples/update-migrate-rust/tree/main/enum-updates) shows how to use
[Enums](https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html) to implement state versioning on a contract.
## Trade-Offs
- **Gas Consumption**: Migration runs in a single transaction, so large states may exceed gas limits. Break it into batches if needed.
- **Downtime**: The contract is paused during migration; users can't interact until complete.
- **Complexity**: Manual transformation risks errors, like data loss if fields are mismatched.

Versioning simplifies updating the contract since you only need to add a new version of the structure.
All versions can coexist, thus you will not need to change previously existing structures.
## Alternatives
- **State Versioning with Enums**: Wrap state in an enum (e.g., `VersionedState`). Add new variants for updates without migrating old data. All versions coexist, simplifying future changes.

The example is composed by 2 contracts:
1. Base: The Guest Book contract using versioned `PostedMessages` (`PostedMessagesV1`).
2. Update: An update that adds a new version of `PostedMessages` (`PostedMessagesV2`).
Example: Evolve `PostedMessage` from V1 to V2.

<CodeTabs>
<Language value="rust" language="rust">
<Github fname="versioned_msg.rs"
url="https://github.com/near-examples/update-migrate-rust/blob/main/enum-updates/update/src/versioned_msg.rs"
start="18" end="36" />
</Language>
</CodeTabs>
<CodeTabs>
<Language value="rust" language="rust">
<Github fname="versioned_msg.rs"
url="https://github.com/near-examples/update-migrate-rust/blob/main/enum-updates/update/src/versioned_msg.rs"
start="18" end="36" />
</Language>
</CodeTabs>

---
- **Self-Updating Contracts**: Implement an `update_contract` method to deploy new code and trigger migration internally.

## Self Update
The [Self Update example](https://github.com/near-examples/update-migrate-rust/tree/main/self-updates) shows how to implement a contract
that can update itself.
<CodeTabs>
<Language value="rust" language="rust">
<Github fname="update.rs"
url="https://github.com/near-examples/update-migrate-rust/blob/main/self-updates/base/src/update.rs"
start="10" end="31" />
</Language>
</CodeTabs>

It is composed by 2 contracts:
1. Base: A Guest Book were people can write messages, implementing a `update_contract` method.
2. Update: An update in which we remove a parameter and change the internal structure.
This avoids external deployment but requires careful access control (e.g., only owner can update).

<CodeTabs>
<Language value="rust" language="rust">
<Github fname="update.rs"
url="https://github.com/near-examples/update-migrate-rust/blob/main/self-updates/base/src/update.rs"
start="10" end="31" />
</Language>
</CodeTabs>
Choose versioning for frequent small changes; migration for major overhauls.

## How to Test State Migration
1. Deploy the base contract and add sample data (e.g., post messages).
2. Deploy the updated contract code over the same account.
3. Call the `migrate` method (attach enough gas for large states).
4. Query the state to verify: Old data should be transformed (e.g., new fields added), and no data lost.
5. Test edge cases: Empty state, maximum state size, and invalid old data.

Use NEAR's testnet for gas estimation. Tools like `near-cli` can simulate calls without deploying.

For full code examples:
- [State Migration](https://github.com/near-examples/update-migrate-rust/tree/main/basic-updates)
- [State Versioning](https://github.com/near-examples/update-migrate-rust/tree/main/enum-updates)
- [Self Update](https://github.com/near-examples/update-migrate-rust/tree/main/self-updates)