use abstract_std::{manager::state::ACCOUNT_MODULES, objects::module::ModuleId};
use cosmwasm_std::{Addr, Deps, QueryRequest, WasmQuery};
use cw2::{ContractVersion, CONTRACT};
use super::{AbstractApi, ApiIdentification};
use crate::{
features::{AccountIdentification, Dependencies, ModuleIdentification},
AbstractSdkResult,
};
pub trait ModuleInterface: AccountIdentification + Dependencies + ModuleIdentification {
fn modules<'a>(&'a self, deps: Deps<'a>) -> Modules<Self> {
Modules { base: self, deps }
}
}
impl<T> ModuleInterface for T where T: AccountIdentification + Dependencies + ModuleIdentification {}
impl<'a, T: ModuleInterface> AbstractApi<T> for Modules<'a, T> {
fn base(&self) -> &T {
self.base
}
fn deps(&self) -> Deps {
self.deps
}
}
impl<'a, T: ModuleInterface> ApiIdentification for Modules<'a, T> {
fn api_id() -> String {
"Modules".to_owned()
}
}
#[derive(Clone)]
pub struct Modules<'a, T: ModuleInterface> {
base: &'a T,
deps: Deps<'a>,
}
impl<'a, T: ModuleInterface> Modules<'a, T> {
pub fn module_address(&self, module_id: ModuleId) -> AbstractSdkResult<Addr> {
let manager_addr = self.base.manager_address(self.deps)?;
let maybe_module_addr =
ACCOUNT_MODULES.query(&self.deps.querier, manager_addr, module_id)?;
let Some(module_addr) = maybe_module_addr else {
return Err(crate::AbstractSdkError::MissingModule {
module: module_id.to_string(),
});
};
Ok(module_addr)
}
pub fn module_version(&self, module_id: ModuleId) -> AbstractSdkResult<ContractVersion> {
let module_address = self.module_address(module_id)?;
let req = QueryRequest::Wasm(WasmQuery::Raw {
contract_addr: module_address.into(),
key: CONTRACT.as_slice().into(),
});
self.deps
.querier
.query::<ContractVersion>(&req)
.map_err(Into::into)
}
pub fn assert_module_dependency(&self, module_id: ModuleId) -> AbstractSdkResult<()> {
let is_dependency = Dependencies::dependencies(self.base)
.iter()
.map(|d| d.id)
.any(|x| x == module_id);
match is_dependency {
true => Ok(()),
false => Err(crate::AbstractSdkError::MissingDependency {
module: module_id.to_string(),
}),
}
}
}
#[cfg(test)]
mod test {
use abstract_testing::prelude::*;
use speculoos::prelude::*;
use super::*;
use crate::mock_module::*;
mod assert_module_dependency {
use cosmwasm_std::testing::*;
use super::*;
#[test]
fn should_return_ok_if_dependency() {
let deps = mock_dependencies();
let app = MockModule::new();
let mods = app.modules(deps.as_ref());
let res = mods.assert_module_dependency(TEST_MODULE_ID);
assert_that!(res).is_ok();
}
#[test]
fn should_return_err_if_not_dependency() {
let deps = mock_dependencies();
let app = MockModule::new();
let mods = app.modules(deps.as_ref());
let fake_module = "lol_no_chance";
let res = mods.assert_module_dependency(fake_module);
assert_that!(res).is_err().matches(|e| {
e.to_string()
.contains(&format!("{fake_module} is not a dependency"))
});
}
}
}