abstract_sdk/apis/
modules.rsuse abstract_std::{account::state::ACCOUNT_MODULES, objects::module::ModuleId};
use cosmwasm_std::{Addr, Deps, QueryRequest, WasmQuery};
use cw2::{ContractVersion, CONTRACT};
use super::AbstractApi;
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> {
const API_ID: &'static str = "Modules";
fn base(&self) -> &T {
self.base
}
fn deps(&self) -> Deps {
self.deps
}
}
#[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 account_addr = self.base.account(self.deps)?;
let maybe_module_addr =
ACCOUNT_MODULES.query(&self.deps.querier, account_addr.into_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 {
#![allow(clippy::needless_borrows_for_generic_args)]
use abstract_testing::prelude::*;
use super::*;
use crate::{apis::traits::test::abstract_api_test, mock_module::*};
mod assert_module_dependency {
use super::*;
#[coverage_helper::test]
fn should_return_ok_if_dependency() {
let (deps, _, app) = mock_module_setup();
let mods = app.modules(deps.as_ref());
let res = mods.assert_module_dependency(TEST_MODULE_ID);
assert!(res.is_ok());
}
#[coverage_helper::test]
fn should_return_err_if_not_dependency() {
let (deps, _, app) = mock_module_setup();
let mods = app.modules(deps.as_ref());
let fake_module = "lol_no_chance";
let res = mods.assert_module_dependency(fake_module);
assert!(res
.unwrap_err()
.to_string()
.contains(&format!("{fake_module} is not a dependency")));
}
}
#[coverage_helper::test]
fn abstract_api() {
let (deps, _, app) = mock_module_setup();
let modules = app.modules(deps.as_ref());
abstract_api_test(modules);
}
}