Migrate
This is another special endpoint. It is, just like instantiate
, not called frequently.
migrate
is only called once you upload a new version to the chain and lets you run all the
required changes to the storage.
Let’s say your storage has the following layout, expressed as JSON for simplicity’s sake:
{
"user_count": 205,
"call_count": 543,
"balance": 43
}
But then you notice “Hey! Why don’t I nest all the counts into an own object? That way I don’t have that redundant postfix, making the keys smaller”.
So you go ahead and rework your logic to query the data from the following structure:
{
"count": {
"user": 205,
"call": 543
},
"balance": 43
}
But your storage on-chain still stores the old format. You need to somehow transform it.
That’s what you do in the migrate
entrypoint. You transform the structure of the storage in there.
Example
For CosmWasm v2.2.0
and newer the new migrate info feature can be used:
const MIGRATE_VERSION: u64 = 2;
#[cfg_attr(not(feature = "library"), entry_point)]
#[migrate_version(MIGRATE_VERSION)]
pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg, migrate_info: MigrateInfo) -> StdResult<Response> {
match migrate_info.old_migrate_version {
Some(1) | None => {
// If the old on-chain version of the contract don't use the
// `migrate_version` macro, there will be no version provided here
// (it's the `None` variant).
// Load the old data
let Some(old_data) = deps.storage.get(b"persisted_data") else {
return Err(StdError::generic_err("Data not found"));
};
// Deserialize it from the old format
let old_data: OldData = cosmwasm_std::from_json(&old_data)?;
// Transform it
let new_data = transform(old_data);
// Serialize the new data
let new_data = cosmwasm_std::to_json_vec(&new_data)?;
// Store the new data
deps.storage.set(b"persisted_data", &new_data);
}
Some(x) if x >= MIGRATE_VERSION => {
// Assume we don't support downgrading the contract's state version
// Note that `migrate_info.old_migrate_version` is never eq to `MIGRATE_VERSION`.
return Err(StdError::generic_err("Downgrades are not supported for this contract."));
}
_ => {
return Err(StdError::generic_err("Unexpected migrate version."));
}
}
Ok(Response::default())
}
For CosmWasm versions older than v2.2.0
the old method looks like this:
const STATE_VERSION: &str = "v2";
const CONTRACT_NAME: &str = "my_contract";
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult<Response> {
// Check if the state version is older than the current one and update it
cw2::ensure_from_older_version(deps.storage, CONTRACT_NAME, STATE_VERSION)?;
// Load the old data
let Some(old_data) = deps.storage.get(b"persisted_data") else {
return Err(StdError::generic_err("Data not found"));
};
// Deserialize it from the old format
let old_data: OldData = cosmwasm_std::from_json(&old_data)?;
// Transform it
let new_data = transform(old_data);
// Serialize the new data
let new_data = cosmwasm_std::to_json_vec(&new_data)?;
// Store the new data
deps.storage.set(b"persisted_data", &new_data);
Ok(Response::default())
}
Definition
To get the additional migrate info, the new signature can be used (CW v2.2.0
and newer):
const MIGRATE_VERSION: u64 = 2;
#[cfg_attr(not(feature = "library"), entry_point)]
#[migrate_version(MIGRATE_VERSION)]
pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg, _migrate_info: MigrateInfo) -> StdResult<Response> {
Ok(Response::default())
}
The legacy migrate entry-point function signature is defined as:
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult<Response> {
Ok(Response::default())
}