Three examples on how to handle updates and state migration:
- State Migration: How to implement a
migrate
method to migrate state between contract updates. - State Versioning: How to use readily use versioning on a state, to simplify updating it later.
- Self Update: How to implement a contract that can update itself.
The examples at ./contracts/basic-updates show how to handle state-breaking changes between contract updates.
It is composed by 2 contracts:
- Base: A Guest Book were people can write messages.
- Update: An update in which we remove a parameter and change the internal structure.
#[private]
#[init(ignore_state)]
pub fn migrate() -> Self {
let old_state: OldState = env::state_read().expect("failed");
let mut new_messages: Vector<PostedMessage> = Vector::new(b"p");
// iterate through the messages of the previous state
for (idx, posted) in old_state.messages.iter().enumerate() {
// get the payment using the message index
let payment = old_state.payments.get(idx as u64).unwrap_or(0);
// Create a PostedMessage with the new format and push it
new_messages.push(&PostedMessage {
payment,
premium: posted.premium,
sender: posted.sender,
text: posted.text,
})
}
// return the new state
Self {
messages: new_messages,
}
}
The example at ./contracts/enum-updates/ shows how to use Enums to implement versioning.
Versioning simplifies updating the contract since you only need to add a new new version of the structure. All versions can coexist, thus you will not need to change previously existing structures.
The example is composed by 2 contracts:
- Base: The guest-book contract using versioned
PostedMessages
(PostedMessagesV1
). - Update: An update that adds a new version of
PostedMessages
(PostedMessagesV2
).
#[derive(BorshSerialize, BorshDeserialize)]
pub enum VersionedPostedMessage {
V1(PostedMessageV1),
V2(PostedMessageV2),
}
impl From<VersionedPostedMessage> for PostedMessageV2 {
fn from(message: VersionedPostedMessage) -> Self {
match message {
VersionedPostedMessage::V2(posted) => posted,
VersionedPostedMessage::V1(posted) => PostedMessageV2 {
payment: if posted.premium { POINT_ONE } else { 0 },
premium: posted.premium,
sender: posted.sender,
text: posted.text,
},
}
}
}
3. Self Update
The examples at ./contracts/self-updates shows how to implement a contract that can update itself.
It is composed by 2 contracts:
- Base: A Guest Book were people can write messages, implementing a
update_contract
method. - Update: An update in which we remove a parameter and change the internal structure.
pub fn update_contract(&self) -> Promise {
// Check the caller is authorized to update the code
assert!(
env::predecessor_account_id() == self.manager,
"Only the manager can update the code"
);
// Receive the code directly from the input to avoid the
// GAS overhead of deserializing parameters
let code = env::input().expect("Error: No input").to_vec();
// Deploy the contract on self
Promise::new(env::current_account_id())
.deploy_contract(code)
.function_call(
"migrate".to_string(),
NO_ARGS,
0,
CALL_GAS
)
.as_return()
}
Clone this repository locally or open it in gitpod. Then follow these steps:
npm install
Deploy your contract in a sandbox and simulate interactions from users.
npm test
- Learn more on each contract's README.
- Check our documentation.