# Migrating Contracts
This guide explains what is needed to upgrade contracts when migrating over
major releases of cosmwasm
. Note that you can also view the
complete CHANGELOG to understand the differences.
# 0.12 -> 0.13
The minimum Rust supported version for 0.13 is 1.47.0.
Verify your Rust version is >= 1.47.0:
Update CosmWasm dependencies in Cargo.toml (skip the ones you don't use):
# 0.11 -> 0.12
Update CosmWasm dependencies in Cargo.toml (skip the ones you don't use):
In your contract's
.cargo/config
remove--features backtraces
, which is now available in Rust nightly only:In order to use backtraces for debugging, run
RUST_BACKTRACE=1 cargo +nightly unit-test --features backtraces
.Rename the type
Extern
toDeps
, and radically simplify theinit
/handle
/migrate
/query
entrypoints. Rather than&mut Extern<S, A, Q>
, useDepsMut
. And instead of&Extern<S, A, Q>
, useDeps
. If you ever pass eg.foo<A: Api>(api: A)
around, you must now use dynamic trait pointers:foo(api: &dyn Api)
. Here is the quick search-replace guide on how to fixcontract.rs
:In production (non-test) code:
<S: Storage, A: Api, Q: Querier>
=> ``&mut Extern<S, A, Q>
=>DepsMut
&Extern<S, A, Q>
=>Deps
&mut deps.storage
=>deps.storage
where passing intostate.rs
helpers&deps.storage
=>deps.storage
where passing intostate.rs
helpers
On the top, remove
use cosmwasm_std::{Api, Extern, Querier, Storage}
. Adduse cosmwasm_std::{Deps, DepsMut}
.In test code only:
&mut deps,
=>deps.as_mut(),
&deps,
=>deps.as_ref(),
You may have to add
use cosmwasm_std::{Storage}
if the compile complains about the traitIf you use cosmwasm-storage, in
state.rs
:<S: Storage>
=> ``<S: ReadonlyStorage>
=> ``<S,
=><
&mut S
=>&mut dyn Storage
&S
=>&dyn Storage
If you have any references to
ReadonlyStorage
left after the above, please replace them withStorage
# 0.10 -> 0.11
Update CosmWasm dependencies in Cargo.toml (skip the ones you don't use):
Contracts now support any custom error type
E: ToString + From<StdError>
. Previously this has beenStdError
, which you can still use. However, you can now create a much more structured error experience for your unit tests that handels exactly the error cases of your contract. In order to get a convenient implementation forToString
andFrom<StdError>
, we use the crate thiserror (opens new window), which needs to be added to the contracts dependencies inCargo.toml
. To create the custom error, create an error modulesrc/errors.rs
as follows:Then add
mod errors;
tosrc/lib.rs
anduse crate::errors::MyCustomError;
tosrc/contract.rs
. Now adapt the return types as follows:fn init
:Result<InitResponse, MyCustomError>
,fn migrate
(if you have it):Result<MigrateResponse, MyCustomError>
,fn handle
:Result<HandleResponse, MyCustomError>
,fn query
:Result<Binary, MyCustomError>
.
If one of your funtions does not use the custom error, you can continue to use
StdError
as before. I.e. you can havehandle
returningResult<HandleResponse, MyCustomError>
andquery
returningStdResult<Binary>
.You can have a top-hevel
init
/migrate
/handle
/query
that returns a custom error but some of its implementations only return errors from the standard library (StdResult<HandleResponse>
aka.Result<HandleResponse, StdError>
). Then useOk(std_result?)
to convert between the result types. E.g.or
Once you got familiar with the concept, you can create different error types for each of the contract's functions.
You can also try a different error library than thiserror (opens new window). The staking development contract (opens new window) shows how this would look like using snafu (opens new window).
Change order of arguments such that
storage
is always first followed by namespace inBucket::new
,Bucket::multilevel
,ReadonlyBucket::new
,ReadonlyBucket::multilevel
,PrefixedStorage::new
,PrefixedStorage::multilevel
,ReadonlyPrefixedStorage::new
,ReadonlyPrefixedStorage::multilevel
,bucket
,bucket_read
,prefixed
andprefixed_read
.Rename
InitResponse::log
,MigrateResponse::log
andHandleResponse::log
toInitResponse::attributes
,MigrateResponse::attributes
andHandleResponse::attributes
. Replace calls tolog
withattr
:Rename
Context::add_log
toContext::add_attribute
:Add result type to
Bucket::update
andSingleton::update
:Remove all
canonical_length
arguments from mock APIs in tests:Add
MessageInfo
as separate arg afterEnv
forinit
,handle
,migrate
. AddEnv
arg toquery
. Useinfo.sender
instead ofenv.message.sender
andinfo.sent_funds
rather thanenv.message.sent_funds
. Just changing the function signatures of the 3-4 export functions should be enough, then the compiler will warn you anywhere you useenv.message
Test code now has
mock_info
which takes the same argsmock_env
used to. You can just passmock_env()
directly into the function calls unless you need to change height/time.One more object to pass in for both unit and integration tests. To do this quickly, I just highlight all copies of
env
and replace them withinfo
(using Ctrl+D in VSCode or Alt+J in IntelliJ). Then I select alldeps, info
sections and replace that withdeps, mock_env(), info
. This fixes up allinit
andhandle
calls, then just add an extramock_env()
to the query calls.
# 0.9 -> 0.10
Update CosmWasm dependencies in Cargo.toml (skip the ones you don't use):
Integration tests:
Calls to
Api::human_address
andApi::canonical_address
now return a pair of result and gas information. ChangeThe same applies for all calls of
Querier
andStorage
.
All Tests:
All usages of mock_env
will have to remove the first argument (no need of
API).
Contracts:
- All code that uses
message.sender
orcontract.address
should deal withHumanAddr
notCanonicalAddr
. Many times this means you can remove a conversion step.
# 0.8 -> 0.9
Update CosmWasm dependencies in Cargo.toml (skip the ones you don't use):
lib.rs
:
The C export boilerplate can now be reduced to the following code (see e.g. in hackatom/src/lib.rs (opens new window)):
Contract code and uni tests:
cosmwasm_storage::get_with_prefix
,cosmwasm_storage::set_with_prefix
,cosmwasm_storage::RepLog::commit
,cosmwasm_std::ReadonlyStorage::get
,cosmwasm_std::ReadonlyStorage::range
,cosmwasm_std::Storage::set
andcosmwasm_std::Storage::remove
now return the value directly that was wrapped in a result before.- Error creator functions are now in type itself, e.g.
StdError::invalid_base64
instead ofinvalid_base64
. The free functions are deprecated and will be removed before 1.0. - Remove
InitResponse.data
ininit
. Before 0.9 this was not stored to chain but ignored. - Use
cosmwasm_storage::transactional
instead of the removedcosmwasm_storage::transactional_deps
. - Replace
cosmwasm_std::Never
withcosmwasm_std::Empty
.
Integration tests:
- Replace
cosmwasm_vm::ReadonlyStorage
withcosmwasm_vm::Storage
, which now contains all backend storage methods. - Storage getters (and iterators) now return a result of
(Option<Vec<u8>>, u64)
, where the first component is the element and the second one is the gas cost. Thus in a few places.0
must be added to access the element.
# 0.7.2 -> 0.8
# Update wasm code
Cargo.toml
dependencies:
- Update to
schemars = "0.7"
- Replace
cosmwasm = "0.7"
withcosmwasm-std = "0.8"
- Replace
cosmwasm-vm = "0.7"
withcosmwasm-vm = "0.8"
- Replace
cw-storage = "0.2"
withcosmwasm-storage = "0.8"
- Remove explicit
snafu
dependency.cosmwasm_std
still uses it internally but doesn't expose snafu specifics anymore. See more details on errors below.
(Note: until release of 0.8
, you need to use git references for all
cosmwasm_*
packages)
Cargo.toml
features:
- Replace
"cosmwasm/backtraces"
with"cosmwasm-std/backtraces"
Imports:
- Replace all
use cosmwasm::X::Y
withuse cosmwasm_std::Y
, except for mock - Replace all
use cosmwasm::mock::Y
withuse cosmwasm_std::testing::Y
. This should only be used in test code. - Replace
cw_storage:X
withcosmwasm_storage::X
- Replace
cosmwasm_std::Response
withcosmwasm_std::HandleResponse
andcosmwasm_std::InitResponse
(different type for each call)
src/lib.rs
:
This has been re-written, but is generic boilerplate and should be (almost) the same in all contracts:
- copy the new version from
contracts/queue
(opens new window) - Add
pub mod XYZ
directives for any modules you use besidescontract
Contract Code:
Add query to extern:
- Before:
my_func<S: Storage, A: Api>(deps: &Extern<S, A>, ...
- After:
my_func<S: Storage, A: Api, Q: Querier>(deps: &Extern<S, A, Q>, ...
- Remember to add
use cosmwasm_std::Querier;
- Before:
query
now returnsStdResult<Binary>
instead ofResult<Vec<u8>>
- You can also replace
to_vec(...)
withto_binary(...)
- You can also replace
No
.context(...)
is required afterfrom_slice
andto_vec
, they return propercosmwasm_std::Error
variants on errors.env.message.signer
becomesenv.message.sender
.If you used
env.contract.balance
, you must now use the querier. The following code block should work:Update the
CosmosMsg
enums used:CosmosMsg::Send{}
=>CosmosMsg::Bank(BankMsg::Send{})
CosmosMsg::Opaque{ data }
=>CosmosMsg::Native{ msg }
CosmosMsg::Contract
=>CosmosMsg::Wasm(WasmMsg::Execute{})
Complete overhaul of
cosmwasm::Error
intocosmwasm_std::StdError
:- Auto generated snafu error constructor structs like
NotFound
/ParseErr
/… have been privatized in favour of error generation helpers likenot_found
/parse_err
/… - All error generator functions now return errors instead of results, such
that e.g.
return unauthorized();
becomesreturn Err(unauthorized());
- Error cases don't contain
source
fields anymore. Instead source errors are converted to standard types likeString
. For this reason, bothsnafu::ResultExt
andsnafu::OptionExt
cannot be used anymore. An error wrapper now looks like.map_err(invalid_base64)
and anOption::None
to error mapping looks like.ok_or_else(|| not_found("State"))
. - Backtraces became optional. Use
RUST_BACKTRACE=1
to enable them for unit tests. Utf8Err
/Utf8StringErr
merged intoStdError::InvalidUtf8
Base64Err
renamed intoStdError::InvalidBase64
ContractErr
/DynContractErr
merged intoStdError::GenericErr
, thus bothcontract_err
anddyn_contract_err
must be replaced withgeneric_err
.- The unused
ValidationErr
was removed
- Auto generated snafu error constructor structs like
At this point cargo wasm
should pass.
# Update test code
Both:
Update all imports from
cosmwasm::mock::*
tocosmwasm_std::testing::*
Use
from_binary
notfrom_slice
on all query responses (update imports)from_slice(res.as_slice())
->from_binary(&res)
Replace
coin("123", "FOO")
withcoins(123, "FOO")
. We renamed it to coins to be more explicit that it returnsVec<Coin>
, and now accept au128
as the first argument for better type-safety.coin
is now an alias toCoin::new
and returns oneCoin
.Remove the 4th argument (contract balance) from all calls to
mock_env
, this is no longer stored in the environment.dependencies
was renamed tomock_dependencies
.mock_dependencies
andmock_instance
take a 2nd argument to set the contract balance (visible for the querier). If you need to set more balances, usemock_XX_with_balances
. The follow code block explains:
Unit Tests:
- Replace
dependencies
withmock_dependencies
Integration Tests:
- We no longer check errors as strings but have rich types:
- Before:
match err { ContractResult::Err(msg) => assert_eq!(msg, "Unauthorized"), ... }
- After:
match err { Err(StdError::Unauthorized{ .. }) => {}, ... }
- Before:
- Remove all imports / use of
ContractResult
- You must specify
CosmosMsg::Native
type when callingcosmwasm_vm::testing::{handle, init}
. You will want touse cosmwasm_std::{HandleResult, InitResult}
oruse cosmwasm_std::{HandleResponse, InitResponse}
. If you don't use custom native types, simply update calls as follows:let res = init(...)
=>let res: InitResult = init(...)
let res = init(...).unwrap()
=>let res: InitResponse = init(...).unwrap()
let res = handle(...)
=>let res: HandleResult = handle(...)
let res = handle(...).unwrap()
=>let res: HandleResponse = handle(...).unwrap()
# Update schema code
All helper functions have been moved into a new cosmwasm-schema
package.
- Add
cosmwasm-schema = "0.8"
to[dev-dependencies]
inCargo.toml
- Remove
serde_json
[dev-dependency]
if there, as cosmwasm-schema will handle JSON output internally. - Update
examples/schema.rs
to look more like queue (opens new window), but replacing all the imports and type names with those you currently have. - Regenerate schemas with
cargo schema
# Polishing
After so many changes, remember to let the linters do their jobs.
cargo fmt
cargo clippy