In NearProtocol, how to migrate contract state
Asked Answered
F

2

6

Assume there's a contract written in near-sdk-rs, deployed, has state defined as:

#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize)]
pub struct NFT {
    pub tokens: UnorderedMap<TokenId, Token>,
}

#[derive(BorshDeserialize, BorshSerialize)]
pub struct Token {
   pub owner: AccountId
}

Now there're some usage of this contract, as a result some records of tokens stored on chain. Then I'd like to update this contract by adding a field to Token:

pub struct Token {
   pub owner: AccountId
   pub name: String // For existing ones, this will be set to ""
}

How to do this with existing state kept (similar of doing a database migration)?

Fleischer answered 11/4, 2021 at 5:51 Comment(0)
D
6

You can also see how some of the examples we've created use versioning.

See BerryClub:

#[derive(BorshDeserialize, BorshSerialize)]
pub struct AccountVersionAvocado {
    pub account_id: AccountId,
    pub account_index: AccountIndex,
    pub balance: u128,
    pub num_pixels: u32,
    pub claim_timestamp: u64,
}

impl From<AccountVersionAvocado> for Account {
    fn from(account: AccountVersionAvocado) -> Self {
        Self {
            account_id: account.account_id,
            account_index: account.account_index,
            balances: vec![account.balance, 0],
            num_pixels: account.num_pixels,
            claim_timestamp: account.claim_timestamp,
            farming_preference: Berry::Avocado,
        }
    }
}

https://github.com/evgenykuzyakov/berryclub/blob/ad2b37045b14fa72181ab92831fb741a7c40234b/contract-rs/pixel-board/src/account.rs#L19-L39

There are others but will have to come back to this once I find them

Donee answered 12/4, 2021 at 11:52 Comment(2)
Good example, to make this example more complete there's also this in berryclub: ``` /// in the contract's struct: pub legacy_accounts: Vector<AccountVersionAvocado>, pub accounts: LookupMap<u32, UpgradableAccount>, ``` so you save both version of accounts in contracts and handle both in codeFleischer
Also this one from SputnikDAO v2 github.com/near-daos/sputnik-dao-contract/blob/…Donee
B
4

So far I can only suggest to deploy a temporary contract with old Token struct kept as is, and NewToken implement NewToken::from_token, and a change method:

impl Token {
    pub fn migrate(&mut self) {
        near_sdk::env::storage_write(
            "STATE",
            NewToken::from_token(self).try_into_vec().unwrap()
        );
    }
}

After you migrate the state, you can deploy a contract with the NewToken instead of Token.

Broke answered 12/4, 2021 at 11:18 Comment(2)
This is more close to the idea of migration. However if state has grown very big, would it be over block gas limit to call migrate?Fleischer
With large state one possible solution is to run the migration in chunks.Zacharia

© 2022 - 2024 — McMap. All rights reserved.