Skip to content

Commit

Permalink
feat: added helpers.rs and basic migration logic
Browse files Browse the repository at this point in the history
  • Loading branch information
srdtrk authored Oct 22, 2023
1 parent f4197c9 commit bef4d34
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 1 deletion.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ serde = { version = "1.0", default-features = false, features = ["derive"] }
serde-json-wasm = "0.5.1"
thiserror = { version = "1.0.31" }
cosmos-sdk-proto = { version = "0.20.0", default-features = false }
semver = "1.0"

[dev-dependencies]
base64 = "0.13.1"
38 changes: 37 additions & 1 deletion src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use cosmwasm_std::entry_point;
use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult};

use crate::types::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};
use crate::types::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
use crate::types::state::{
CallbackCounter, ChannelState, ContractState, CALLBACK_COUNTER, CHANNEL_STATE, STATE,
};
Expand Down Expand Up @@ -75,6 +75,18 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
}
}

/// Migrate contract if version is lower than current version
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
migrate::validate_semver(deps.as_ref())?;

cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
// If state structure changed in any contract version in the way migration is needed, it
// should occur here

Ok(Response::default())
}

mod execute {
use cosmwasm_std::coins;

Expand Down Expand Up @@ -168,6 +180,30 @@ mod query {
}
}

mod migrate {
use super::*;

pub fn validate_semver(deps: Deps) -> Result<(), ContractError> {
let prev_cw2_version = cw2::get_contract_version(deps.storage)?;
if prev_cw2_version.contract != CONTRACT_NAME {
return Err(ContractError::InvalidMigrationVersion {
expected: CONTRACT_NAME.to_string(),
actual: prev_cw2_version.contract,
});
}

let version: semver::Version = CONTRACT_VERSION.parse()?;
let prev_version: semver::Version = prev_cw2_version.version.parse()?;
if prev_version >= version {
return Err(ContractError::InvalidMigrationVersion {
expected: format!("> {}", prev_version),
actual: CONTRACT_VERSION.to_string(),
});
}
Ok(())
}
}

#[cfg(test)]
mod tests {
use crate::ibc::types::{metadata::TxEncoding, packet::IcaPacketData};
Expand Down
142 changes: 142 additions & 0 deletions src/helpers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//! This file contains helper functions for working with this contract from
//! external contracts.
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use cosmwasm_std::{to_binary, Addr, Binary, CosmosMsg, QuerierWrapper, StdResult, WasmMsg};

use crate::types::{msg, state};

/// CwIcaControllerContract is a wrapper around Addr that provides helpers
/// for working with this contract.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct CwIcaControllerContract(pub Addr);

/// CwIcaControllerCodeId is a wrapper around u64 that provides helpers for
/// initializing this contract.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct CwIcaControllerCode(pub u64);

impl CwIcaControllerContract {
/// new creates a new [`CwIcaControllerContract`]
pub fn new(addr: Addr) -> Self {
Self(addr)
}

/// addr returns the address of the contract
pub fn addr(&self) -> Addr {
self.0.clone()
}

/// call creates a [`WasmMsg::Execute`] message targeting this contract,
pub fn call(&self, msg: impl Into<msg::ExecuteMsg>) -> StdResult<CosmosMsg> {
let msg = to_binary(&msg.into())?;
Ok(WasmMsg::Execute {
contract_addr: self.addr().into(),
msg,
funds: vec![],
}
.into())
}

/// query_channel queries the [`state::ChannelState`] of this contract
pub fn query_channel(&self, querier: QuerierWrapper) -> StdResult<state::ChannelState> {
querier.query_wasm_smart(self.addr(), &msg::QueryMsg::GetChannel {})
}

/// query_state queries the [`state::ContractState`] of this contract
pub fn query_state(&self, querier: QuerierWrapper) -> StdResult<state::ContractState> {
querier.query_wasm_smart(self.addr(), &msg::QueryMsg::GetContractState {})
}

/// query_callback_counter queries the [`state::CallbackCounter`] of this contract
pub fn query_callback_counter(
&self,
querier: QuerierWrapper,
) -> StdResult<state::CallbackCounter> {
querier.query_wasm_smart(self.addr(), &msg::QueryMsg::GetCallbackCounter {})
}

/// update_admin creates a [`WasmMsg::UpdateAdmin`] message targeting this contract
pub fn update_admin(&self, admin: impl Into<String>) -> StdResult<CosmosMsg> {
Ok(WasmMsg::UpdateAdmin {
contract_addr: self.addr().into(),
admin: admin.into(),
}
.into())
}

/// clear_admin creates a [`WasmMsg::ClearAdmin`] message targeting this contract
pub fn clear_admin(&self) -> StdResult<CosmosMsg> {
Ok(WasmMsg::ClearAdmin {
contract_addr: self.addr().into(),
}
.into())
}

/// migrate creates a [`WasmMsg::Migrate`] message targeting this contract
pub fn migrate(
&self,
msg: impl Into<msg::MigrateMsg>,
new_code_id: u64,
) -> StdResult<CosmosMsg> {
let msg = to_binary(&msg.into())?;
Ok(WasmMsg::Migrate {
contract_addr: self.addr().into(),
new_code_id,
msg,
}
.into())
}
}

impl CwIcaControllerCode {
/// new creates a new [`CwIcaControllerCode`]
pub fn new(code_id: u64) -> Self {
Self(code_id)
}

/// code_id returns the code id of this code
pub fn code_id(&self) -> u64 {
self.0
}

/// instantiate creates a [`WasmMsg::Instantiate`] message targeting this code
pub fn instantiate(
&self,
msg: impl Into<msg::InstantiateMsg>,
label: impl Into<String>,
admin: Option<impl Into<String>>,
) -> StdResult<CosmosMsg> {
let msg = to_binary(&msg.into())?;
Ok(WasmMsg::Instantiate {
code_id: self.code_id(),
msg,
funds: vec![],
label: label.into(),
admin: admin.map(|s| s.into()),
}
.into())
}

/// instantiate2 creates a [`WasmMsg::Instantiate2`] message targeting this code
pub fn instantiate2(
&self,
msg: impl Into<msg::InstantiateMsg>,
label: impl Into<String>,
admin: Option<impl Into<String>>,
salt: impl Into<Binary>,
) -> StdResult<CosmosMsg> {
let msg = to_binary(&msg.into())?;
Ok(WasmMsg::Instantiate2 {
code_id: self.code_id(),
msg,
funds: vec![],
label: label.into(),
admin: admin.map(|s| s.into()),
salt: salt.into(),
}
.into())
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
#![deny(missing_docs)]

pub mod contract;
pub mod helpers;
pub mod ibc;
pub mod types;
6 changes: 6 additions & 0 deletions src/types/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,15 @@ pub enum ContractError {
#[error("prost encoding error: {0}")]
ProstEncodeError(#[from] cosmos_sdk_proto::prost::EncodeError),

#[error("semver parse error: {0}")]
SemverError(#[from] semver::Error),

#[error("unauthorized")]
Unauthorized {},

#[error("invalid migration version: expected {expected}, got {actual}")]
InvalidMigrationVersion { expected: String, actual: String },

#[error("invalid channel ordering")]
InvalidChannelOrdering {},

Expand Down
4 changes: 4 additions & 0 deletions src/types/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,7 @@ pub enum QueryMsg {
#[returns(crate::types::state::CallbackCounter)]
GetCallbackCounter {},
}

/// The message to migrate this contract.
#[cw_serde]
pub struct MigrateMsg {}

0 comments on commit bef4d34

Please sign in to comment.