52 |
53 | ### Contract Composition Workshop - August 26, 2020 {#contract-composition-workshop---august-26-2020}
54 |
55 | *Date: August 26, 2020*
56 |
57 | In this workshop, we chose the topic ¨Contract composition deep dive¨ as per our poll result.
58 |
59 | 1) We will show the basic staking derivative
60 | 2) And demonstrate how to wrap it in a CW20 token
61 | 3) Then we could use that token to interact with other contracts.
62 |
63 | ### Deep Dive Part 1 - CosmWasm template {#deep-dive-part-1---cosmwasm-template}
64 |
65 |
66 |
67 |
68 |
69 | ### Deep Dive Part 2 - CW1 Subkeys {#deep-dive-part-2---cw1-subkeys}
70 |
71 |
72 |
73 |
74 |
75 | ### Deep Dive Part 3 - CW20 Staking features {#deep-dive-part-3---cw20-staking-features}
76 |
77 |
78 |
79 |
80 |
81 | ### Building Multi-chain Smart-Contracts Using CosmWasm - HackAtom V India Workshop {#building-multi-chain-smart-contracts-using-cosmwasm---hackatom-v-india-workshop}
82 |
83 | *Date: September 14, 2020*
84 |
85 | #### Intro {#intro}
86 |
87 |
183 |
184 | ### Whiteboard Series with NEAR | Ep: 38 Ethan Frey from CosmWasm {#whiteboard-series-with-near--ep-38-ethan-frey-from-cosmwasm}
185 |
186 |
187 |
189 |
190 |
191 | ### Introducción al Ecosistema Cosmos, CosmWasm y presentación del Riddlethon {#introducción-al-ecosistema-cosmos-cosmwasm-y-presentación-del-riddlethon}
192 |
193 |
194 |
195 |
197 |
198 |
199 | ### Riddlethon I: Ask Me Anything about Confio & CosmWasm {#riddlethon-i-ask-me-anything-about-confio--cosmwasm}
200 |
201 |
202 |
204 |
205 |
--------------------------------------------------------------------------------
/docs/04-smart-contracts/11-migration.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 11
3 | ---
4 |
5 | # Migration
6 |
7 | Migration is the process through which a given smart contracts code can be swapped out or 'upgraded'.
8 |
9 | CosmWasm made contract migration a first-class experience. When instantiating a contract, there is an optional admin field that you can set. If it is left empty, the contract is immutable. If it is set (to an external account or governance contract), that account can trigger a migration. The admin can also change admin or even make the contract fully immutable after some time. However, when we wish to migrate from contract A to contract B, contract B needs to be aware somehow of how the state was encoded.
10 |
11 | This is where CW2 specification comes in. It specifies one special `Singleton` to be stored on disk by all contracts on instantiate. When the migrate function is called, then the new contract can read that data and see if this is an expected contract we can migrate from. And also contain extra version information if we support multiple migrate paths.
12 |
13 | Working with CW2 is quite straightforward in that, as a smart contract developer you need only perform a couple of steps.
14 |
15 | The CW2 Spec provides a `set_contract_version` which should be used in instantiate to store the original version of a contract. It is important to also `set_contract_version` as a part of the `pub fn migrate(...)` logic this time (as opposed to `instantiate`) for the contract version to be updated after a succesful migration.
16 |
17 | ```rust
18 | const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME");
19 | const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
20 |
21 |
22 | #[cfg_attr(not(feature = "library"), entry_point)]
23 | pub fn instantiate(deps: DepsMut, env: Env, info: MessageInfo, msg: InstantiateMsg) -> Response {
24 | // Use CW2 to set the contract version, this is needed for migrations
25 | set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
26 | }
27 | ```
28 |
29 | Additionally, `get_contract_version` is provided in CW2 which can and should be used in the `migrate` function of the contract when you need to know the previous version of the contract. Both methods work on a `Item` data structure from `cw_storage_plus` which operates over this object:
30 |
31 | ```rust
32 | #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
33 | pub struct ContractVersion {
34 | /// contract is the crate name of the implementing contract, eg. `crate:cw20-base`
35 | /// we will use other prefixes for other languages, and their standard global namespacing
36 | pub contract: String,
37 | /// version is any string that this implementation knows. It may be simple counter "1", "2".
38 | /// or semantic version on release tags "v0.7.0", or some custom feature flag list.
39 | /// the only code that needs to understand the version parsing is code that knows how to
40 | /// migrate from the given contract (and is tied to it's implementation somehow)
41 | pub version: String,
42 | }
43 | ```
44 |
45 | ## Setting up a contract for migrations
46 |
47 | Performing a contract migration is a three step process. First, you must write a newer version of the contract you wish to update. Second, you can upload the new code as you did before, but don’t instantiate it. Third, you use a dedicated [MsgMigrateContract](https://github.com/CosmWasm/wasmd/blob/v0.20.0/proto/cosmwasm/wasm/v1/tx.proto#L94-L104) transaction to point this contract to use the new code. And you are done!
48 |
49 | During the migration process, the migrate function defined on the new code is executed, never the one from the old code. Both the source and the destination `code_id`'s may be migratable, but it is necessary that the new code has a `migrate` function defined and properly exported as an `entry_point`: #[cfg_attr(not(feature = "library"), entry_point)].
50 |
51 | The `migrate` function itself, exposes the ability to make any granular changes needed to the State, akin to a database migration or any other things you might want to do.
52 |
53 | If the migrate function returns an error, the transaction aborts, all state changes are reverted and the migration is not performed.
54 |
55 | Provided below are a few variants on migrations you could do ranging from a very simple one, to a more restricted one by code iD and type.
56 |
57 | ### Basic Contract Migration
58 |
59 | This migration will be the most common you may see. It simply is used to swap out the code of a contract. Safety checks may not be performed if you do not also use `cw2::set_contract_version`.
60 |
61 | ```rust
62 | const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME");
63 | const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
64 |
65 | #[entry_point]
66 | pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result {
67 | // No state migrations performed, just returned a Response
68 | Ok(Response::default())
69 | }
70 | ```
71 |
72 | ### Restricted Migration by code version and name
73 |
74 | This migration is a more complete and restricted example where the `cw2` package is used and the `migrate` function ensures that:
75 |
76 | - We are migrating from the same type of contract; checking its name
77 | - We are upgrading from an older version of the contract; checking its version
78 |
79 | ```rust
80 | const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME");
81 | const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
82 |
83 | #[entry_point]
84 | pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result {
85 | let ver = cw2::get_contract_version(deps.storage)?;
86 | // ensure we are migrating from an allowed contract
87 | if ver.contract != CONTRACT_NAME {
88 | return Err(StdError::generic_err("Can only upgrade from same type").into());
89 | }
90 | // note: better to do proper semver compare, but string compare *usually* works
91 | #[allow(clippy::cmp_owned)]
92 | if ver.version >= CONTRACT_VERSION {
93 | return Err(StdError::generic_err("Cannot upgrade from a newer version").into());
94 | }
95 |
96 | // set the new version
97 | cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
98 |
99 | // do any desired state migrations...
100 |
101 | Ok(Response::default())
102 | }
103 | ```
104 |
105 | ### Migrate which updates the version only if newer
106 |
107 | This migration is a less restrictive example than above. In this case the `cw2` package is used and the `migrate` function ensures that:
108 |
109 | - If the contract version has incremented from the stored one, perform needed migrations but store the new contract version
110 | - Uses Semver instead of String compare
111 |
112 | ```rust
113 | const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME");
114 | const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
115 |
116 | #[entry_point]
117 | pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result {
118 | let version: Version = CONTRACT_VERSION.parse()?;
119 | let storage_version: Version = get_contract_version(deps.storage)?.version.parse()?;
120 |
121 | if storage_version < version {
122 | set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
123 |
124 | // If state structure changed in any contract version in the way migration is needed, it
125 | // should occur here
126 | }
127 | Ok(Response::default())
128 | }
129 | ```
130 |
131 | This example uses Semver to help with version checks, you would also need to add the semver dependency to your cargo deps:
132 |
133 | ```toml
134 | [dependencies]
135 | semver = "1"
136 | ```
137 |
138 | And also implement Semver custom errors for your contract package:
139 |
140 | ```rust
141 | #[derive(Error, Debug, PartialEq)]
142 | pub enum ContractError {
143 |
144 | #[error("Semver parsing error: {0}")]
145 | SemVer(String),
146 |
147 | }
148 | impl From for ContractError {
149 | fn from(err: semver::Error) -> Self {
150 | Self::SemVer(err.to_string())
151 | }
152 | }
153 | ```
154 |
155 | ### Using migrate to update otherwise immutable state
156 |
157 | This example shows how a migration can be used to update a value that generally should not be changed. This allows for the immutable value to be changed only during a migration if that functionality is needed by your team.
158 |
159 | ```rust
160 | #[entry_point]
161 | pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result {
162 | let data = deps
163 | .storage
164 | .get(CONFIG_KEY)
165 | .ok_or_else(|| StdError::not_found("State"))?;
166 | let mut config: State = from_slice(&data)?;
167 | config.verifier = deps.api.addr_validate(&msg.verifier)?;
168 | deps.storage.set(CONFIG_KEY, &to_vec(&config)?);
169 |
170 | Ok(Response::default())
171 | }
172 | ```
173 |
174 | In the above example, our `MigrateMsg` has a `verifier` field which contains the new value for our contracts `verifier` field located in the State. Provided your contract does not also expose an `UpdateState` or something like `UpdateVerifier` ExecuteMsgs then this provides the only method to change the `verifier` value.
175 |
176 | ### Using migrations to 'burn' a contract
177 |
178 | Migrations can also be used to completely abandon an old contract and burn its state. This has varying uses but in the event you need it you can find an example [here](https://github.com/CosmWasm/cosmwasm/blob/main/contracts/burner/src/contract.rs#L20):
179 |
180 | ```rust
181 | #[entry_point]
182 | pub fn migrate(deps: DepsMut, env: Env, msg: MigrateMsg) -> StdResult {
183 | // delete all state
184 | let keys: Vec<_> = deps
185 | .storage
186 | .range(None, None, Order::Ascending)
187 | .map(|(k, _)| k)
188 | .collect();
189 | let count = keys.len();
190 | for k in keys {
191 | deps.storage.remove(&k);
192 | }
193 |
194 | // get balance and send all to recipient
195 | let balance = deps.querier.query_all_balances(env.contract.address)?;
196 | let send = BankMsg::Send {
197 | to_address: msg.payout.clone(),
198 | amount: balance,
199 | };
200 |
201 | let data_msg = format!("burnt {} keys", count).into_bytes();
202 |
203 | Ok(Response::new()
204 | .add_message(send)
205 | .add_attribute("action", "burn")
206 | .add_attribute("payout", msg.payout)
207 | .set_data(data_msg))
208 | }
209 |
210 | ```
211 |
212 | In the above example, when the migration occurs the State is completely deleted. Additionally all the balance of contract is send to a nominated `payout` address provided in the `MigrationMsg`. In this case all funds are drained and all state removed effectively burning the contract.
213 |
214 | ## Platform Specific Variations
215 |
216 | Different chains and hubs in the Cosmos ecosystem may have some variations on how migrations are done on their respective networks. This section will attempt to outline those.
217 |
218 | ### Terra
219 |
220 | Terra has some specific differences in how they manage migrations. Firstly; the contract must have been set as migratable on instantiation. The contract needs to have an admin for migratability similar to the standard procedure for migrations.
221 | Specifically migration in this case for Terra refers to swapping out the code id for a new one that is considered 'compatible' (CW2 helps with this). [Source](https://github.com/terra-money/terrain#migrating-cosmwasm-contracts-on-terra).
222 |
223 | > Note: In Terra, it is also possible to migrate a code_id across chains (COL4->5 for example). This operation is atomic and can only be performed once. Its intention is to migrate your code to the new chain and preserve its old code ID. This process helps to prevent downstream breakages of other contracts on the new network which depend on your old code ID.
224 | > Example command for migrating across chains :
225 | >
226 | > ```rust
227 | > terrad tx wasm store ./{code.wasm} --from {keyname} \
228 | > --migrate-code-id {codeID}
229 | > ```
230 |
--------------------------------------------------------------------------------