use cosmwasm_std::{Addr, QuerierWrapper};
use thiserror::Error;
use super::{
account::ACCOUNT_ID,
module::{Module, ModuleInfo},
module_reference::ModuleReference,
namespace::Namespace,
AccountId,
};
use crate::version_control::{
state::{ACCOUNT_ADDRESSES, CONFIG, REGISTERED_MODULES, STANDALONE_INFOS},
AccountBase, ModuleConfiguration, ModuleResponse, ModulesResponse, NamespaceResponse,
NamespacesResponse, QueryMsg,
};
#[derive(Error, Debug, PartialEq)]
pub enum VersionControlError {
#[error("Module {module} not found in version registry {registry_addr}.")]
ModuleNotFound { module: String, registry_addr: Addr },
#[error("Failed to query Account id on contract {contract_addr}. Please ensure that the contract is a Manager or Proxy contract.")]
FailedToQueryAccountId { contract_addr: Addr },
#[error("Standalone {code_id} not found in version registry {registry_addr}.")]
StandaloneNotFound { code_id: u64, registry_addr: Addr },
#[error("Unknown Account id {account_id} on version control {registry_addr}. Please ensure that you are using the correct Account id and version control address.")]
UnknownAccountId {
account_id: AccountId,
registry_addr: Addr,
},
#[error("Address {0} is not the Manager of Account {1}.")]
NotManager(Addr, AccountId),
#[error("Address {0} is not the Proxy of Account {1}.")]
NotProxy(Addr, AccountId),
#[error("Query during '{method_name}' failed: {error}")]
QueryFailed {
method_name: String,
error: cosmwasm_std::StdError,
},
}
pub type VersionControlResult<T> = Result<T, VersionControlError>;
#[allow(rustdoc::broken_intra_doc_links)]
#[cosmwasm_schema::cw_serde]
pub struct VersionControlContract {
pub address: Addr,
}
impl VersionControlContract {
pub fn new(address: Addr) -> Self {
Self { address }
}
#[function_name::named]
pub fn query_module_reference_raw(
&self,
module_info: &ModuleInfo,
querier: &QuerierWrapper,
) -> VersionControlResult<ModuleReference> {
let module_reference = REGISTERED_MODULES
.query(querier, self.address.clone(), module_info)
.map_err(|error| VersionControlError::QueryFailed {
method_name: function_name!().to_owned(),
error,
})?;
module_reference.ok_or_else(|| VersionControlError::ModuleNotFound {
module: module_info.to_string(),
registry_addr: self.address.clone(),
})
}
pub fn query_module(
&self,
module_info: ModuleInfo,
querier: &QuerierWrapper,
) -> VersionControlResult<Module> {
Ok(self
.query_modules_configs(vec![module_info], querier)?
.swap_remove(0)
.module)
}
pub fn query_config(
&self,
module_info: ModuleInfo,
querier: &QuerierWrapper,
) -> VersionControlResult<ModuleConfiguration> {
Ok(self
.query_modules_configs(vec![module_info], querier)?
.swap_remove(0)
.config)
}
#[function_name::named]
pub fn query_modules_configs(
&self,
infos: Vec<ModuleInfo>,
querier: &QuerierWrapper,
) -> VersionControlResult<Vec<ModuleResponse>> {
let ModulesResponse { modules } = querier
.query_wasm_smart(self.address.to_string(), &QueryMsg::Modules { infos })
.map_err(|error| VersionControlError::QueryFailed {
method_name: function_name!().to_owned(),
error,
})?;
Ok(modules)
}
#[function_name::named]
pub fn query_namespace(
&self,
namespace: Namespace,
querier: &QuerierWrapper,
) -> VersionControlResult<NamespaceResponse> {
let namespace_response: NamespaceResponse = querier
.query_wasm_smart(self.address.to_string(), &QueryMsg::Namespace { namespace })
.map_err(|error| VersionControlError::QueryFailed {
method_name: function_name!().to_owned(),
error,
})?;
Ok(namespace_response)
}
#[function_name::named]
pub fn query_namespaces(
&self,
accounts: Vec<AccountId>,
querier: &QuerierWrapper,
) -> VersionControlResult<NamespacesResponse> {
let namespaces_response: NamespacesResponse = querier
.query_wasm_smart(self.address.to_string(), &QueryMsg::Namespaces { accounts })
.map_err(|error| VersionControlError::QueryFailed {
method_name: function_name!().to_owned(),
error,
})?;
Ok(namespaces_response)
}
#[function_name::named]
pub fn query_standalone_info_raw(
&self,
code_id: u64,
querier: &QuerierWrapper,
) -> VersionControlResult<ModuleInfo> {
let module_info = STANDALONE_INFOS
.query(querier, self.address.clone(), code_id)
.map_err(|error| VersionControlError::QueryFailed {
method_name: function_name!().to_owned(),
error,
})?;
module_info.ok_or_else(|| VersionControlError::StandaloneNotFound {
code_id,
registry_addr: self.address.clone(),
})
}
pub fn unchecked_account_id(
&self,
maybe_core_contract_addr: &Addr,
querier: &QuerierWrapper,
) -> VersionControlResult<AccountId> {
ACCOUNT_ID
.query(querier, maybe_core_contract_addr.clone())
.map_err(|_| VersionControlError::FailedToQueryAccountId {
contract_addr: maybe_core_contract_addr.clone(),
})
}
pub fn account_id(
&self,
maybe_core_contract_addr: &Addr,
querier: &QuerierWrapper,
) -> VersionControlResult<AccountId> {
let self_reported_account_id =
self.unchecked_account_id(maybe_core_contract_addr, querier)?;
let account_base = self.account_base(&self_reported_account_id, querier)?;
if account_base.manager.ne(maybe_core_contract_addr)
&& account_base.proxy.ne(maybe_core_contract_addr)
{
Err(VersionControlError::FailedToQueryAccountId {
contract_addr: maybe_core_contract_addr.clone(),
})
} else {
Ok(self_reported_account_id)
}
}
#[function_name::named]
pub fn account_base(
&self,
account_id: &AccountId,
querier: &QuerierWrapper,
) -> VersionControlResult<AccountBase> {
let maybe_account = ACCOUNT_ADDRESSES
.query(querier, self.address.clone(), account_id)
.map_err(|error| VersionControlError::QueryFailed {
method_name: function_name!().to_owned(),
error,
})?;
maybe_account.ok_or_else(|| VersionControlError::UnknownAccountId {
account_id: account_id.clone(),
registry_addr: self.address.clone(),
})
}
#[function_name::named]
pub fn namespace_registration_fee(
&self,
querier: &QuerierWrapper,
) -> VersionControlResult<Option<cosmwasm_std::Coin>> {
let config = CONFIG
.query(querier, self.address.clone())
.map_err(|error| VersionControlError::QueryFailed {
method_name: function_name!().to_owned(),
error,
})?;
Ok(config.namespace_registration_fee)
}
pub fn assert_manager(
&self,
maybe_manager: &Addr,
querier: &QuerierWrapper,
) -> VersionControlResult<AccountBase> {
let account_id = self.unchecked_account_id(maybe_manager, querier)?;
let account_base = self.account_base(&account_id, querier)?;
if account_base.manager.ne(maybe_manager) {
Err(VersionControlError::NotManager(
maybe_manager.clone(),
account_id,
))
} else {
Ok(account_base)
}
}
pub fn assert_proxy(
&self,
maybe_proxy: &Addr,
querier: &QuerierWrapper,
) -> VersionControlResult<AccountBase> {
let account_id = self.unchecked_account_id(maybe_proxy, querier)?;
let account_base = self.account_base(&account_id, querier)?;
if account_base.proxy.ne(maybe_proxy) {
Err(VersionControlError::NotProxy(
maybe_proxy.clone(),
account_id,
))
} else {
Ok(account_base)
}
}
}