abstract_interface/native/
registry.rs

1pub use abstract_std::registry::{
2    ExecuteMsgFns as RegistryExecFns, QueryMsgFns as RegistryQueryFns,
3};
4use abstract_std::{
5    objects::{
6        dependency::StaticDependency,
7        module::{Module, ModuleId, ModuleInfo, ModuleStatus, ModuleVersion},
8        module_reference::ModuleReference,
9        namespace::{Namespace, ABSTRACT_NAMESPACE},
10        AccountId,
11    },
12    registry::*,
13    REGISTRY,
14};
15use cw_orch::{contract::Contract, interface, prelude::*};
16
17use crate::AccountI;
18
19type VersionString = String;
20
21#[interface(InstantiateMsg, ExecuteMsg, QueryMsg, MigrateMsg)]
22pub struct Registry<Chain>;
23
24impl<Chain: CwEnv> cw_blob::interface::DeterministicInstantiation<Chain> for Registry<Chain> {}
25
26impl<Chain: CwEnv> Uploadable for Registry<Chain> {
27    fn wrapper() -> <Mock as ::cw_orch::environment::TxHandler>::ContractSource {
28        Box::new(
29            ContractWrapper::new_with_empty(
30                ::registry::contract::execute,
31                ::registry::contract::instantiate,
32                ::registry::contract::query,
33            )
34            .with_migrate(::registry::migrate::migrate),
35        )
36    }
37    fn wasm(_chain: &ChainInfoOwned) -> WasmPath {
38        artifacts_dir_from_workspace!()
39            .find_wasm_path("registry")
40            .unwrap()
41    }
42}
43
44impl<Chain: CwEnv> Registry<Chain> {
45    pub fn load(chain: Chain, address: &Addr) -> Self {
46        let contract = cw_orch::contract::Contract::new(REGISTRY, chain);
47        contract.set_address(address);
48        Self(contract)
49    }
50
51    /// Query a single module
52    pub fn module(&self, info: ModuleInfo) -> Result<Module, crate::AbstractInterfaceError> {
53        let ModulesResponse { mut modules } = self.modules(vec![info])?;
54
55        Ok(modules.swap_remove(0).module)
56    }
57
58    /// Query a single module registered or pending
59    pub fn registered_or_pending_module(
60        &self,
61        info: ModuleInfo,
62    ) -> Result<Module, crate::AbstractInterfaceError> {
63        let mut module_list_response = self.module_list(
64            Some(ModuleFilter {
65                namespace: Some(info.namespace.to_string()),
66                name: Some(info.name.clone()),
67                version: Some(info.version.to_string()),
68                status: Some(ModuleStatus::Registered),
69            }),
70            None,
71            None,
72        )?;
73
74        if !module_list_response.modules.is_empty() {
75            // Return if it's registered module else it's pending or neither registered or pending
76            Ok(module_list_response.modules.swap_remove(0).module)
77        } else {
78            let mut module_list_response = self.module_list(
79                Some(ModuleFilter {
80                    namespace: Some(info.namespace.to_string()),
81                    name: Some(info.name),
82                    version: Some(info.version.to_string()),
83                    status: Some(ModuleStatus::Pending),
84                }),
85                None,
86                None,
87            )?;
88            if !module_list_response.modules.is_empty() {
89                Ok(module_list_response.modules.swap_remove(0).module)
90            } else {
91                Err(crate::AbstractInterfaceError::Std(
92                    cosmwasm_std::StdError::generic_err("Module not found"),
93                ))
94            }
95        }
96    }
97
98    /// Get module status or return `None` if not deployed
99    pub fn module_status(
100        &self,
101        info: ModuleInfo,
102    ) -> Result<Option<ModuleStatus>, crate::AbstractInterfaceError> {
103        let is_module_status = |m: ModuleStatus| -> Result<bool, crate::AbstractInterfaceError> {
104            let is_module_status = !self
105                .module_list(
106                    Some(ModuleFilter {
107                        namespace: Some(info.namespace.to_string()),
108                        name: Some(info.name.clone()),
109                        version: Some(info.version.to_string()),
110                        status: Some(m),
111                    }),
112                    None,
113                    None,
114                )?
115                .modules
116                .is_empty();
117            Ok(is_module_status)
118        };
119
120        if is_module_status(ModuleStatus::Registered)? {
121            Ok(Some(ModuleStatus::Registered))
122        } else if is_module_status(ModuleStatus::Pending)? {
123            Ok(Some(ModuleStatus::Pending))
124        } else if is_module_status(ModuleStatus::Yanked)? {
125            Ok(Some(ModuleStatus::Yanked))
126        } else {
127            // Not deployed
128            Ok(None)
129        }
130    }
131
132    /// Return list of registered module versions
133    pub fn module_versions(
134        &self,
135        module_id: ModuleId,
136    ) -> Result<Vec<semver::Version>, crate::AbstractInterfaceError> {
137        let parts: Vec<&str> = module_id.split(':').collect();
138        if parts.len() != 2 {
139            return Err(abstract_std::AbstractError::FormattingError {
140                object: "module_id".to_string(),
141                expected: "namespace:module".to_string(),
142                actual: module_id.to_string(),
143            }
144            .into());
145        }
146
147        let mut start_after = None;
148        let mut versions: Vec<semver::Version> = vec![];
149        loop {
150            let modules_page = self
151                .module_list(
152                    Some(ModuleFilter {
153                        namespace: Some(parts[0].to_owned()),
154                        name: Some(parts[1].to_owned()),
155                        version: None,
156                        status: Some(ModuleStatus::Registered),
157                    }),
158                    None,
159                    start_after,
160                )?
161                .modules;
162            if modules_page.is_empty() {
163                break;
164            }
165            start_after = modules_page.last().map(|module| module.module.info.clone());
166            let versions_page = modules_page
167                .into_iter()
168                .map(|module| module.module.info.version.try_into().unwrap());
169
170            versions.extend(versions_page)
171        }
172        Ok(versions)
173    }
174
175    // Check that module dependencies deployed on chain
176    pub fn assert_dependencies_deployed(
177        &self,
178        dependencies: &[StaticDependency],
179    ) -> Result<(), crate::AbstractInterfaceError> {
180        for dependency in dependencies {
181            let module_versions = self.module_versions(dependency.id)?;
182            // Check if at least one version matches
183            let matches = module_versions
184                .iter()
185                .any(|version| dependency.matches(version));
186            if !matches {
187                return Err(crate::AbstractInterfaceError::NoMatchingModule(
188                    dependency.clone(),
189                ));
190            }
191        }
192        Ok(())
193    }
194
195    pub fn register_base(
196        &self,
197        account: &AccountI<Chain>,
198    ) -> Result<(), crate::AbstractInterfaceError> {
199        let account = account.as_instance();
200        let account_module = (
201            ModuleInfo::from_id(
202                &account.id,
203                ModuleVersion::Version(account::contract::CONTRACT_VERSION.to_string()),
204            )?,
205            ModuleReference::Account(account.code_id()?),
206        );
207        self.propose_modules(vec![account_module])?;
208
209        log::info!("Module {} registered", account.id);
210        Ok(())
211    }
212
213    /// Register account module
214    pub fn register_account(
215        &self,
216        account: &Contract<Chain>,
217        version: VersionString,
218    ) -> Result<(), crate::AbstractInterfaceError> {
219        let to_register = self.contracts_into_module_entries(vec![(account, version)], |c| {
220            ModuleReference::Account(c.code_id().unwrap())
221        })?;
222        self.propose_modules(to_register)?;
223        Ok(())
224    }
225
226    /// Register native modules
227    pub fn register_natives(
228        &self,
229        natives: Vec<(&Contract<Chain>, VersionString)>,
230    ) -> Result<(), crate::AbstractInterfaceError> {
231        let to_register = self.contracts_into_module_entries(natives, |c| {
232            ModuleReference::Native(c.address().unwrap())
233        })?;
234        let to_register_module_ids = to_register
235            .iter()
236            .map(|to_register| to_register.0.id())
237            .collect::<Vec<String>>()
238            .join(",");
239        log::info!("Modules {to_register_module_ids} registered");
240        self.propose_modules(to_register)?;
241        Ok(())
242    }
243
244    /// Register services modules
245    pub fn register_services(
246        &self,
247        services: Vec<(&Contract<Chain>, VersionString)>,
248    ) -> Result<(), crate::AbstractInterfaceError> {
249        let to_register = self.contracts_into_module_entries(services, |c| {
250            ModuleReference::Service(c.address().unwrap())
251        })?;
252        self.propose_modules(to_register)?;
253        Ok(())
254    }
255
256    pub fn register_apps(
257        &self,
258        apps: Vec<(&Contract<Chain>, VersionString)>,
259    ) -> Result<(), crate::AbstractInterfaceError> {
260        let to_register = self
261            .contracts_into_module_entries(apps, |c| ModuleReference::App(c.code_id().unwrap()))?;
262        self.propose_modules(to_register)?;
263        Ok(())
264    }
265
266    pub fn register_adapters(
267        &self,
268        adapters: Vec<(&Contract<Chain>, VersionString)>,
269    ) -> Result<(), crate::AbstractInterfaceError> {
270        let to_register = self.contracts_into_module_entries(adapters, |c| {
271            ModuleReference::Adapter(c.address().unwrap())
272        })?;
273        self.propose_modules(to_register)?;
274        Ok(())
275    }
276
277    pub fn register_standalones(
278        &self,
279        standalones: Vec<(&Contract<Chain>, VersionString)>,
280    ) -> Result<(), crate::AbstractInterfaceError> {
281        let to_register = self.contracts_into_module_entries(standalones, |c| {
282            ModuleReference::Standalone(c.code_id().unwrap())
283        })?;
284        self.propose_modules(to_register)?;
285        Ok(())
286    }
287
288    /// Approve any abstract-namespaced pending modules.
289    pub fn approve_any_abstract_modules(&self) -> Result<(), crate::AbstractInterfaceError> {
290        self.approve_all_modules_for_namespace(Namespace::unchecked(ABSTRACT_NAMESPACE))
291    }
292
293    /// Approve any "namespace" pending modules.
294    pub fn approve_all_modules_for_namespace(
295        &self,
296        namespace: Namespace,
297    ) -> Result<(), crate::AbstractInterfaceError> {
298        let proposed_namespace_modules = self.module_list(
299            Some(ModuleFilter {
300                namespace: Some(namespace.to_string()),
301                status: Some(ModuleStatus::Pending),
302                ..Default::default()
303            }),
304            None,
305            None,
306        )?;
307
308        if proposed_namespace_modules.modules.is_empty() {
309            return Ok(());
310        }
311
312        self.approve_or_reject_modules(
313            proposed_namespace_modules
314                .modules
315                .into_iter()
316                .map(|m| m.module.info)
317                .collect(),
318            vec![],
319        )?;
320        Ok(())
321    }
322
323    fn contracts_into_module_entries<RefFn>(
324        &self,
325        modules: Vec<(&Contract<Chain>, VersionString)>,
326        ref_fn: RefFn,
327    ) -> Result<Vec<(ModuleInfo, ModuleReference)>, crate::AbstractInterfaceError>
328    where
329        RefFn: Fn(&&Contract<Chain>) -> ModuleReference,
330    {
331        let modules_to_register: Result<
332            Vec<(ModuleInfo, ModuleReference)>,
333            crate::AbstractInterfaceError,
334        > = modules
335            .iter()
336            .map(|(contract, version)| {
337                Ok((
338                    ModuleInfo::from_id(&contract.id, ModuleVersion::Version(version.to_owned()))?,
339                    ref_fn(contract),
340                ))
341            })
342            .collect();
343        modules_to_register
344    }
345
346    pub fn account(&self, account_id: AccountId) -> Result<Account, crate::AbstractInterfaceError> {
347        let resp: AccountsResponse = self.accounts(vec![account_id.clone()])?;
348        // If the account id is not registered, an err is returned, so we can safely get index 0 in the vec
349        Ok(resp.accounts[0].clone())
350    }
351
352    /// Retrieves an Adapter's address from registry given the module **id** and **version**.
353    pub fn get_adapter_addr(
354        &self,
355        id: &str,
356        version: ModuleVersion,
357    ) -> Result<Addr, crate::AbstractInterfaceError> {
358        let module: Module = self.module(ModuleInfo::from_id(id, version)?)?;
359
360        Ok(module.reference.unwrap_adapter()?)
361    }
362
363    /// Retrieves an APP's code id from registry given the module **id** and **version**.
364    pub fn get_app_code(
365        &self,
366        id: &str,
367        version: ModuleVersion,
368    ) -> Result<u64, crate::AbstractInterfaceError> {
369        let module: Module = self.module(ModuleInfo::from_id(id, version)?)?;
370
371        Ok(module.reference.unwrap_app()?)
372    }
373
374    /// Retrieves an APP's code id from registry given the module **id** and **version**.
375    pub fn get_standalone_code(
376        &self,
377        id: &str,
378        version: ModuleVersion,
379    ) -> Result<u64, crate::AbstractInterfaceError> {
380        let module: Module = self.module(ModuleInfo::from_id(id, version)?)?;
381
382        Ok(module.reference.unwrap_standalone()?)
383    }
384
385    /// Retrieves latest Account's code id from registry.
386    pub fn get_account_code(&self) -> Result<u64, crate::AbstractInterfaceError> {
387        let module: Module = self.module(ModuleInfo::from_id_latest(abstract_std::ACCOUNT)?)?;
388
389        Ok(module.reference.unwrap_account()?)
390    }
391}
392
393impl Registry<Mock> {
394    /// Approve any pending modules.
395    pub fn approve_any_modules(&self) -> Result<(), crate::AbstractInterfaceError> {
396        let proposed_abstract_modules = self.module_list(
397            Some(ModuleFilter {
398                status: Some(ModuleStatus::Pending),
399                ..Default::default()
400            }),
401            None,
402            None,
403        )?;
404
405        if proposed_abstract_modules.modules.is_empty() {
406            return Ok(());
407        }
408
409        let owner = self.ownership()?;
410        self.call_as(&Addr::unchecked(owner.owner.unwrap()))
411            .approve_or_reject_modules(
412                proposed_abstract_modules
413                    .modules
414                    .iter()
415                    .map(|m| m.module.info.clone())
416                    .collect(),
417                vec![],
418            )?;
419        Ok(())
420    }
421}