Contract functions are upgradable, but not states, for example
#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)]
pub struct Contract {
owner_id: AccountId
}
If I want to add another field, say saving_id
, it gives error
#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)]
pub struct Contract {
owner_id: AccountId,
saving_id: AccountId
}
How can I write upgradable contract?
You should write a migrate function similar to the init function, but instead of creating the state from scratch, first you have access to the old state, perform migrations and dump the new state.
To do this from a rust contract you need to annotate the migration function with #[init(ignore_state)]
(available in near-sdk-rs in 3.0.1
). This will execute the function without checking if the contract is initialised first.
The full example took from near-sdk-rs:
#[near_bindgen]
impl Contract {
#[init(ignore_state)]
pub fn migrate_state(new_data: String) -> Self {
// Deserialize the state using the old contract structure.
let old_contract: OldContract = env::state_read().expect("Old state doesn't exist");
// Verify that the migration can only be done by the owner.
// This is not necessary, if the upgrade is done internally.
assert_eq!(
&env::predecessor_account_id(),
&old_contract.owner_id,
"Can only be called by the owner"
);
// Create the new contract using the data from the old contract.
Self { owner_id: old_contract.owner_id, data: old_contract.data, new_data }
}
}