use crate::Abstract;
use crate::AdapterDeployer;
use crate::AppDeployer;
use abstract_core::module_factory::ModuleInstallConfig;
use abstract_core::ABSTRACT_EVENT_TYPE;
use cw_orch::deploy::Deploy;
mod manager;
mod proxy;
use std::collections::HashSet;
use abstract_core::{manager::ManagerModuleInfo, objects::AccountId};
use cosmwasm_std::Addr;
use cw_orch::prelude::*;
use serde::Serialize;
use speculoos::prelude::*;
use crate::{get_account_contracts, VersionControl};
pub use self::{manager::*, proxy::*};
pub struct AbstractAccount<Chain: CwEnv> {
pub manager: Manager<Chain>,
pub proxy: Proxy<Chain>,
}
impl<Chain: CwEnv> AbstractAccount<Chain> {
pub fn upload(&mut self) -> Result<(), crate::AbstractInterfaceError> {
self.manager.upload()?;
self.proxy.upload()?;
Ok(())
}
}
impl<Chain: CwEnv> AbstractAccount<Chain> {
pub fn new(abstract_deployment: &Abstract<Chain>, account_id: Option<AccountId>) -> Self {
let (manager, proxy) =
get_account_contracts(&abstract_deployment.version_control, account_id);
Self { manager, proxy }
}
pub fn register(
&self,
version_control: &VersionControl<Chain>,
) -> Result<(), crate::AbstractInterfaceError> {
version_control.register_base(self)
}
pub fn install_module<TInitMsg: Serialize>(
&self,
module_id: &str,
init_msg: &TInitMsg,
funds: Option<&[Coin]>,
) -> Result<Chain::Response, crate::AbstractInterfaceError> {
self.manager.install_module(module_id, init_msg, funds)
}
pub fn install_modules(
&self,
modules: Vec<ModuleInstallConfig>,
funds: Option<&[Coin]>,
) -> Result<Chain::Response, crate::AbstractInterfaceError> {
self.manager.install_modules(modules, funds)
}
pub fn install_modules_auto(
&self,
modules: Vec<ModuleInstallConfig>,
) -> Result<Chain::Response, crate::AbstractInterfaceError> {
self.manager.install_modules_auto(modules)
}
pub fn expect_modules(
&self,
module_addrs: Vec<String>,
) -> Result<Vec<ManagerModuleInfo>, crate::AbstractInterfaceError> {
let abstract_core::manager::ModuleInfosResponse {
module_infos: manager_modules,
} = self.manager.module_infos(None, None)?;
let expected_module_addrs = module_addrs
.into_iter()
.map(Addr::unchecked)
.chain(std::iter::once(self.proxy.address()?))
.collect::<HashSet<_>>();
let actual_module_addrs = manager_modules
.iter()
.map(|module_info| module_info.address.clone())
.collect::<HashSet<_>>();
assert_that!(expected_module_addrs).is_equal_to(actual_module_addrs);
Ok(manager_modules)
}
pub fn expect_whitelist(
&self,
whitelisted_addrs: Vec<String>,
) -> Result<Vec<String>, crate::AbstractInterfaceError> {
let expected_whitelisted_addrs = whitelisted_addrs
.into_iter()
.chain(std::iter::once(self.manager.address()?.into_string()))
.collect::<HashSet<_>>();
let abstract_core::proxy::ConfigResponse {
modules: proxy_whitelist,
} = self.proxy.config()?;
let actual_proxy_whitelist = HashSet::from_iter(proxy_whitelist.clone());
assert_eq!(actual_proxy_whitelist, expected_whitelisted_addrs);
Ok(proxy_whitelist)
}
pub fn id(&self) -> Result<AccountId, crate::AbstractInterfaceError> {
Ok(self.manager.config()?.account_id)
}
pub fn install_adapter<CustomInitMsg: Serialize, T: AdapterDeployer<Chain, CustomInitMsg>>(
&self,
adapter: T,
custom_init_msg: &CustomInitMsg,
funds: Option<&[Coin]>,
) -> Result<Addr, crate::AbstractInterfaceError> {
let abstr = Abstract::load_from(self.manager.get_chain().to_owned())?;
let init_msg = abstract_core::adapter::InstantiateMsg {
module: custom_init_msg,
base: abstract_core::adapter::BaseInstantiateMsg {
ans_host_address: abstr.ans_host.address()?.into(),
version_control_address: abstr.version_control.address()?.into(),
},
};
self.install_module_parse_addr(adapter, &init_msg, funds)
}
pub fn install_app<CustomInitMsg: Serialize, T: AppDeployer<Chain>>(
&self,
app: T,
custom_init_msg: &CustomInitMsg,
funds: Option<&[Coin]>,
) -> Result<Addr, crate::AbstractInterfaceError> {
let abstr = Abstract::load_from(self.manager.get_chain().to_owned())?;
let init_msg = abstract_core::app::InstantiateMsg {
module: custom_init_msg,
base: abstract_core::app::BaseInstantiateMsg {
ans_host_address: abstr.ans_host.addr_str()?,
version_control_address: abstr.version_control.addr_str()?,
},
};
self.install_module_parse_addr(app, &init_msg, funds)
}
fn install_module_parse_addr<InitMsg: Serialize, T: ContractInstance<Chain>>(
&self,
module: T,
init_msg: &InitMsg,
funds: Option<&[Coin]>,
) -> Result<Addr, crate::AbstractInterfaceError> {
let resp = self.install_module(&module.id(), &init_msg, funds)?;
let module_address = resp.event_attr_value(ABSTRACT_EVENT_TYPE, "new_modules")?;
let module_address = Addr::unchecked(module_address);
module.set_address(&module_address);
Ok(module_address)
}
}
#[cfg(feature = "daemon")]
use crate::AbstractInterfaceError;
#[cfg(feature = "daemon")]
impl AbstractAccount<Daemon> {
pub fn upload_and_register_if_needed(
&self,
version_control: &VersionControl<Daemon>,
) -> Result<(), AbstractInterfaceError> {
let mut modules_to_register = Vec::with_capacity(2);
if self.manager.upload_if_needed()?.is_some() {
modules_to_register.push((
self.manager.as_instance(),
::manager::contract::CONTRACT_VERSION.to_string(),
));
};
if self.proxy.upload_if_needed()?.is_some() {
modules_to_register.push((
self.proxy.as_instance(),
::proxy::contract::CONTRACT_VERSION.to_string(),
));
};
if !modules_to_register.is_empty() {
version_control.register_account_mods(modules_to_register)?;
}
Ok(())
}
}