diff --git a/Cargo.lock b/Cargo.lock index 45bc9e6..d4a2b32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -225,7 +225,7 @@ dependencies = [ [[package]] name = "cw-constructor" -version = "0.1.0" +version = "0.1.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -236,6 +236,7 @@ dependencies = [ "cw721-base 0.13.4", "itertools 0.12.1", "schemars", + "semver", "serde", "serde_json", "thiserror", @@ -243,18 +244,19 @@ dependencies = [ [[package]] name = "cw-fiend-frens-constructor" -version = "0.1.0" +version = "0.1.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw-constructor", "cw-fiend-frens-shared", "cw2 1.1.2", + "semver", ] [[package]] name = "cw-fiend-frens-minter" -version = "0.1.0" +version = "0.1.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -265,7 +267,7 @@ dependencies = [ [[package]] name = "cw-fiend-frens-shared" -version = "0.1.0" +version = "0.1.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -275,7 +277,7 @@ dependencies = [ [[package]] name = "cw-fiend-frens-trait-minter" -version = "0.1.0" +version = "0.1.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -286,7 +288,7 @@ dependencies = [ [[package]] name = "cw-minter" -version = "0.1.0" +version = "0.1.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -551,7 +553,7 @@ dependencies = [ [[package]] name = "cw721-fiend-frens" -version = "0.1.0" +version = "0.1.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -562,7 +564,7 @@ dependencies = [ [[package]] name = "cw721-fiend-frens-trait" -version = "0.1.0" +version = "0.1.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", diff --git a/Cargo.toml b/Cargo.toml index e716161..b200582 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ members = [ ] [workspace.package] -version = "0.1.0" +version = "0.1.1" edition = "2021" license = "MIT" authors = [ "Pau Yankovski " ] diff --git a/core/Cargo.toml b/core/Cargo.toml index e6935c3..1f18352 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -28,6 +28,7 @@ serde = { version = "1.0.103", default-features = false, features = [ "derive" ] serde_json = "1.0" schemars = "0.8.1" thiserror = "1.0" +semver = "1.0" cosmwasm-schema = "1.1.4" itertools = "0.12.0" diff --git a/core/src/error.rs b/core/src/error.rs index a3fb761..c9b22e6 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -6,6 +6,23 @@ pub enum ContractError { #[error("{0}")] Std(#[from] StdError), + // Migration errors + #[error("Semver parsing error: {0}")] + SemVer(String), + + #[error("Cannot change contract name from \"{original_name}\" to \"{new_name}\"! The name should be immutable")] + InvalidMigrationContractName { + original_name: String, + new_name: String, + }, + + #[error("Cannot change contract version from {current_version} to {new_version}! New version should be higher than the current one")] + InvalidMigrationVersion { + current_version: String, + new_version: String, + }, + + // Execution errors #[error("Unauthorized! {sender} is not contract admin")] NotAdmin { sender: Addr }, @@ -40,5 +57,11 @@ pub enum ContractError { NotEquipped {}, } +impl From for ContractError { + fn from(err: semver::Error) -> Self { + Self::SemVer(err.to_string()) + } +} + pub type ContractResult = Result; pub type ContractResponse = ContractResult; diff --git a/example/client/package.json b/example/client/package.json index c90c3bb..7a8a56d 100644 --- a/example/client/package.json +++ b/example/client/package.json @@ -1,7 +1,7 @@ { "name": "fiend-frens-client", "type": "module", - "version": "0.1.0", + "version": "0.1.1", "description": "A demo website to view, equip and unequip trait tokens for the **Fiend Frens** NFTs", "scripts": { "dev": "nuxt dev", diff --git a/example/contracts/cw-fiend-frens-constructor/Cargo.toml b/example/contracts/cw-fiend-frens-constructor/Cargo.toml index 479467b..3a2eb1b 100644 --- a/example/contracts/cw-fiend-frens-constructor/Cargo.toml +++ b/example/contracts/cw-fiend-frens-constructor/Cargo.toml @@ -25,3 +25,4 @@ cosmwasm-schema = "^1.2" cosmwasm-std = "^1.2" cw2 = "^1.1" cw-fiend-frens-shared = { path = "../shared" } +semver = "1" diff --git a/example/contracts/cw-fiend-frens-constructor/src/bin/schema.rs b/example/contracts/cw-fiend-frens-constructor/src/bin/schema.rs index 076ab3f..b6d9c53 100644 --- a/example/contracts/cw-fiend-frens-constructor/src/bin/schema.rs +++ b/example/contracts/cw-fiend-frens-constructor/src/bin/schema.rs @@ -1,10 +1,12 @@ use cosmwasm_schema::write_api; +use cosmwasm_std::Empty; use cw_constructor::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; fn main() { write_api! { instantiate: InstantiateMsg, execute: ExecuteMsg, - query: QueryMsg + query: QueryMsg, + migrate: Empty } } diff --git a/example/contracts/cw-fiend-frens-constructor/src/lib.rs b/example/contracts/cw-fiend-frens-constructor/src/lib.rs index a9e3c12..99d7a11 100644 --- a/example/contracts/cw-fiend-frens-constructor/src/lib.rs +++ b/example/contracts/cw-fiend-frens-constructor/src/lib.rs @@ -2,6 +2,7 @@ use cw_constructor::contract::Contract as ConstructorContract; use cw_constructor::error::ContractResponse; use cw_constructor::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; use cw_fiend_frens_shared::metadata::{Extension, MergedExtension, TraitExtension}; +use semver::Version; // Version info for migration const CONTRACT_NAME: &str = "fiend-frens-constructor"; @@ -14,7 +15,8 @@ pub mod entry { #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; - use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, StdResult}; + use cosmwasm_std::{Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdResult}; + use cw_constructor::error::ContractError; #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( @@ -45,4 +47,35 @@ pub mod entry { let contract = Contract::default(); contract.query(deps, env, msg) } + + #[cfg_attr(not(feature = "library"), entry_point)] + pub fn migrate(deps: DepsMut, _env: Env, _msg: Empty) -> ContractResponse { + // Current contract state + let current = cw2::get_contract_version(deps.storage)?; + let original_name = current.contract; + let current_version = current.version; + let current_version_semver: Version = current_version.parse()?; + + // New state to migrate to + let new_name = CONTRACT_NAME.to_string(); + let new_version: String = CONTRACT_VERSION.to_string(); + let new_version_semver: Version = new_version.parse()?; + + // Validate migration params + if original_name != new_name { + return Err(ContractError::InvalidMigrationContractName { + original_name, + new_name, + }); + } + if current_version_semver >= new_version_semver { + return Err(ContractError::InvalidMigrationVersion { + current_version, + new_version, + }); + } + + cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + Ok(Response::default()) + } } diff --git a/example/scripts/instantiate.config.json b/example/scripts/instantiate.config.json index 9bb1238..7f77cc7 100644 --- a/example/scripts/instantiate.config.json +++ b/example/scripts/instantiate.config.json @@ -326,7 +326,7 @@ "tx": "278E32692E897F8EF1EC27C214572DD095DC7973913D6102D57F65753B77BAEC" }, "fiend-frens-constructor": { - "code_id": "2566", + "code_id": "2912", "address": "archway13cj0z0zgca02wdlum855fapu9u7hfprwqcczefr2mf0ep75lh7ms3anpjy", "admin": { "$var": "sender" diff --git a/example/scripts/package.json b/example/scripts/package.json index 2c00cdc..50bb15c 100644 --- a/example/scripts/package.json +++ b/example/scripts/package.json @@ -1,7 +1,7 @@ { "name": "fiend-frens-scripts", "type": "module", - "version": "0.1.0", + "version": "0.1.1", "description": "Scripts to upload NFTs' assets on IPFS, instantiate example contracts etc", "scripts": { "upload": "esno ./src/upload/index.ts", diff --git a/package.json b/package.json index a580b45..44c7b3a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cw-constructor", "type": "module", - "version": "0.1.0", + "version": "0.1.1", "description": "Constructor contract to bind NFTs as traits for another NFT", "author": "Pau Yankovski ", "license": "MIT",