use super::dependency::{Dependency, StaticDependency};
use crate::AbstractError;
use cosmwasm_std::{
ensure, ensure_eq, Empty, Querier, QuerierWrapper, QueryRequest, StdResult, Storage, WasmQuery,
};
use cw2::{get_contract_version, ContractVersion};
use cw_storage_plus::Item;
use semver::Version;
use serde::{Deserialize, Serialize};
pub const MODULE: Item<ModuleData> = Item::new("module_data");
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ModuleData {
pub module: String,
pub version: String,
pub dependencies: Vec<Dependency>,
pub metadata: Option<String>,
}
pub fn get_module_data(store: &dyn Storage) -> StdResult<ModuleData> {
MODULE.load(store).map_err(Into::into)
}
pub fn set_module_data<T: Into<String>, U: Into<String>, M: Into<String>>(
store: &mut dyn Storage,
name: T,
version: U,
dependencies: &[StaticDependency],
metadata: Option<M>,
) -> StdResult<()> {
let val = ModuleData {
module: name.into(),
version: version.into(),
dependencies: dependencies.iter().map(Into::into).collect(),
metadata: metadata.map(Into::into),
};
MODULE.save(store, &val).map_err(Into::into)
}
pub fn assert_contract_upgrade(
storage: &dyn Storage,
to_contract: impl ToString,
to_version: Version,
) -> Result<(), AbstractError> {
let ContractVersion {
version: from_version,
contract,
} = get_contract_version(storage)?;
let to_contract = to_contract.to_string();
ensure_eq!(
contract,
to_contract,
AbstractError::ContractNameMismatch {
from: contract,
to: to_contract,
}
);
let from_version = from_version.parse().unwrap();
ensure!(
to_version > from_version,
AbstractError::CannotDowngradeContract {
contract,
from: from_version,
to: to_version,
}
);
Ok(())
}
pub fn assert_cw_contract_upgrade(
storage: &dyn Storage,
to_contract: impl ToString,
to_version: cw_semver::Version,
) -> Result<(), AbstractError> {
assert_contract_upgrade(
storage,
to_contract,
to_version.to_string().parse().unwrap(),
)
}
pub fn migrate_module_data(
store: &mut dyn Storage,
name: &str,
version: &str,
metadata: Option<String>,
) -> StdResult<()> {
let old_module_data = MODULE.may_load(store)?;
let val = old_module_data.map_or(
ModuleData {
module: name.into(),
version: version.into(),
dependencies: vec![],
metadata: None,
},
|data| ModuleData {
module: name.into(),
version: version.into(),
dependencies: data.dependencies,
metadata: metadata.or(data.metadata),
},
);
MODULE.save(store, &val).map_err(Into::into)
}
pub fn query_module_data<Q: Querier, T: Into<String>>(
querier: &Q,
contract_addr: T,
) -> StdResult<ModuleData> {
let req = QueryRequest::Wasm(WasmQuery::Raw {
contract_addr: contract_addr.into(),
key: MODULE.as_slice().into(),
});
QuerierWrapper::<Empty>::new(querier)
.query(&req)
.map_err(Into::into)
}
#[cfg(test)]
mod tests {
use super::*;
use cosmwasm_std::testing::MockStorage;
#[test]
fn get_and_set_work() {
let mut store = MockStorage::new();
assert!(get_module_data(&store).is_err());
let contract_name = "crate:cw20-base";
let contract_version = "0.2.0";
let metadata = Some("https://example.com");
const REQUIREMENT: [&str; 1] = [">1"];
const DEPENDENCIES: &[StaticDependency; 1] = &[StaticDependency {
id: "abstact::dex",
version_req: &REQUIREMENT,
}];
set_module_data(
&mut store,
contract_name,
contract_version,
DEPENDENCIES,
metadata,
)
.unwrap();
let loaded = get_module_data(&store).unwrap();
let expected = ModuleData {
module: contract_name.to_string(),
version: contract_version.to_string(),
dependencies: DEPENDENCIES.iter().map(Into::into).collect(),
metadata: metadata.map(Into::into),
};
assert_eq!(expected, loaded);
}
}