abstract_sdk/apis/
version_registry.rs

1use abstract_std::{
2    objects::{
3        module::{Module, ModuleInfo},
4        module_reference::ModuleReference,
5        module_version::MODULE,
6        namespace::Namespace,
7        registry::RegistryContract,
8        AccountId,
9    },
10    registry::{ModuleConfiguration, ModuleResponse, NamespaceResponse, NamespacesResponse},
11};
12use cosmwasm_std::{Addr, Deps};
13
14use super::AbstractApi;
15use crate::{
16    cw_helpers::ApiQuery,
17    features::{AbstractRegistryAccess, ModuleIdentification},
18    AbstractSdkError, AbstractSdkResult,
19};
20
21/// Access the Abstract Registry and access module information.
22pub trait ModuleRegistryInterface: AbstractRegistryAccess + ModuleIdentification {
23    /**
24        API for querying module information from the Abstract registry contract.
25
26        # Example
27        ```
28        use abstract_sdk::prelude::*;
29        # use cosmwasm_std::testing::mock_dependencies;
30        # use abstract_sdk::mock_module::MockModule;
31        # use abstract_testing::prelude::*;
32        # let deps = mock_dependencies();
33        # let account = admin_account(deps.api);
34        # let module = MockModule::new(deps.api, account);
35
36        let mod_registry: ModuleRegistry<MockModule>  = module.module_registry(deps.as_ref()).unwrap();
37        ```
38    */
39    fn module_registry<'a>(
40        &'a self,
41        deps: Deps<'a>,
42    ) -> AbstractSdkResult<ModuleRegistry<'a, Self>> {
43        let vc = self.abstract_registry(deps)?;
44        Ok(ModuleRegistry {
45            base: self,
46            deps,
47            registry: vc,
48        })
49    }
50}
51
52impl<T> ModuleRegistryInterface for T where T: AbstractRegistryAccess + ModuleIdentification {}
53
54impl<T: ModuleRegistryInterface> AbstractApi<T> for ModuleRegistry<'_, T> {
55    const API_ID: &'static str = "ModuleRegistry";
56
57    fn base(&self) -> &T {
58        self.base
59    }
60    fn deps(&self) -> Deps {
61        self.deps
62    }
63}
64
65#[derive(Clone)]
66/**
67    API for querying module information from the Abstract registry contract.
68
69    # Example
70    ```
71    use abstract_sdk::prelude::*;
72    # use cosmwasm_std::testing::mock_dependencies;
73    # use abstract_sdk::mock_module::MockModule;
74    # use abstract_testing::prelude::*;
75    # let deps = mock_dependencies();
76    # let account = admin_account(deps.api);
77    # let module = MockModule::new(deps.api, account);
78
79    let mod_registry: ModuleRegistry<MockModule>  = module.module_registry(deps.as_ref()).unwrap();
80    ```
81*/
82pub struct ModuleRegistry<'a, T: ModuleRegistryInterface> {
83    base: &'a T,
84    deps: Deps<'a>,
85    registry: RegistryContract,
86}
87
88impl<T: ModuleRegistryInterface> ModuleRegistry<'_, T> {
89    /// Raw query for a module reference
90    pub fn query_module_reference_raw(
91        &self,
92        module_info: &ModuleInfo,
93    ) -> AbstractSdkResult<ModuleReference> {
94        self.registry
95            .query_module_reference_raw(module_info, &self.deps.querier)
96            .map_err(|error| self.wrap_query_error(error))
97    }
98
99    /// Smart query for a module
100    pub fn query_module(&self, module_info: ModuleInfo) -> AbstractSdkResult<Module> {
101        Ok(self
102            .query_modules_configs(vec![module_info])?
103            .swap_remove(0)
104            .module)
105    }
106
107    /// Smart query for a module config
108    pub fn query_config(&self, module_info: ModuleInfo) -> AbstractSdkResult<ModuleConfiguration> {
109        Ok(self
110            .query_modules_configs(vec![module_info])?
111            .swap_remove(0)
112            .config)
113    }
114
115    /// Smart query for a modules and its configurations
116    pub fn query_modules_configs(
117        &self,
118        infos: Vec<ModuleInfo>,
119    ) -> AbstractSdkResult<Vec<ModuleResponse>> {
120        self.registry
121            .query_modules_configs(infos, &self.deps.querier)
122            .map_err(|error| self.wrap_query_error(error))
123    }
124
125    /// Queries the account that owns the namespace
126    /// Is also returns the base modules of that account (Account)
127    pub fn query_namespace(&self, namespace: Namespace) -> AbstractSdkResult<NamespaceResponse> {
128        self.registry
129            .query_namespace(namespace, &self.deps.querier)
130            .map_err(|error| self.wrap_query_error(error))
131    }
132
133    /// Queries the account_id that owns the namespace
134    pub fn query_namespace_raw(
135        &self,
136        namespace: Namespace,
137    ) -> AbstractSdkResult<Option<AccountId>> {
138        self.registry
139            .query_namespace_raw(namespace, &self.deps.querier)
140            .map_err(|error| self.wrap_query_error(error))
141    }
142
143    /// Queries the namespaces owned by accounts
144    pub fn query_namespaces(
145        &self,
146        accounts: Vec<AccountId>,
147    ) -> AbstractSdkResult<NamespacesResponse> {
148        self.registry
149            .query_namespaces(accounts, &self.deps.querier)
150            .map_err(|error| self.wrap_query_error(error))
151    }
152
153    /// Queries the module info of the standalone code id
154    pub fn query_standalone_info_raw(&self, code_id: u64) -> AbstractSdkResult<ModuleInfo> {
155        self.registry
156            .query_standalone_info_raw(code_id, &self.deps.querier)
157            .map_err(|error| self.wrap_query_error(error))
158    }
159
160    /// Queries the Module information for an address.
161    /// This will error if the Address is not an Abstract Module (Native, Account, App, Adapter or Standalone)
162    pub fn module_info(&self, address: Addr) -> AbstractSdkResult<Module> {
163        // We start by testing if the address is a module
164        let module_response = MODULE
165            .query(&self.deps.querier, address.clone())
166            .map_err(|e| AbstractSdkError::NotAModule {
167                addr: address.clone(),
168                err: e.to_string(),
169            })?;
170
171        // We verify the module is indeed registered inside the version registry
172        let module = self.query_module(ModuleInfo::from_id(
173            &module_response.module,
174            module_response.version.into(),
175        )?)?;
176
177        match module.reference.clone() {
178            ModuleReference::Adapter(queried_address)
179            | ModuleReference::Native(queried_address)
180            | ModuleReference::Service(queried_address) => {
181                if queried_address == address {
182                    Ok(module)
183                } else {
184                    Err(AbstractSdkError::WrongModuleInfo {
185                        addr: address.clone(),
186                        module: module.to_string(),
187                        err: format!("Expected address {queried_address}, got address {address}",),
188                    })
189                }
190            }
191            ModuleReference::App(queried_code_id)
192            | ModuleReference::Standalone(queried_code_id)
193            | ModuleReference::Account(queried_code_id) => {
194                let request_contract = self.deps.querier.query_wasm_contract_info(&address)?;
195                if queried_code_id == request_contract.code_id {
196                    Ok(module)
197                } else {
198                    Err(AbstractSdkError::WrongModuleInfo {
199                        addr: address,
200                        module: module.to_string(),
201                        err: format!(
202                            "Expected code_id {queried_code_id}, got code_id {}",
203                            request_contract.code_id
204                        ),
205                    })
206                }
207            }
208            _ => Err(AbstractSdkError::NotAModule {
209                addr: address,
210                err: "got an un-implemented module reference".to_string(),
211            }),
212        }
213    }
214}
215
216#[cfg(test)]
217mod test {
218    use super::*;
219
220    use crate::{apis::traits::test::abstract_api_test, mock_module::mock_module_setup};
221
222    use abstract_std::{
223        objects::{
224            module::{ModuleId, Monetization},
225            module_version::ModuleData,
226            namespace::ABSTRACT_NAMESPACE,
227            ABSTRACT_ACCOUNT_ID,
228        },
229        registry::ModulesResponse,
230    };
231    use abstract_testing::prelude::*;
232    use cosmwasm_std::testing::mock_dependencies;
233
234    struct MockBinding {}
235
236    impl AbstractRegistryAccess for MockBinding {
237        fn abstract_registry(&self, deps: Deps) -> AbstractSdkResult<RegistryContract> {
238            RegistryContract::new(deps, 1).map_err(Into::into)
239        }
240    }
241
242    impl ModuleIdentification for MockBinding {
243        fn module_id(&self) -> ModuleId<'static> {
244            ModuleId::from(TEST_MODULE_ID)
245        }
246    }
247
248    #[coverage_helper::test]
249    fn query_module_reference_raw() {
250        let mut deps = mock_dependencies();
251        deps.querier = abstract_mock_querier(deps.api);
252
253        let binding = MockBinding {};
254        let module_registry = binding.module_registry(deps.as_ref()).unwrap();
255        let module_reference = module_registry
256            .query_module_reference_raw(
257                &ModuleInfo::from_id(abstract_std::ACCOUNT, TEST_VERSION.parse().unwrap()).unwrap(),
258            )
259            .unwrap();
260        assert_eq!(module_reference, ModuleReference::Account(1));
261    }
262
263    #[coverage_helper::test]
264    fn query_namespace() {
265        let mut deps = mock_dependencies();
266        let abstr = AbstractMockAddrs::new(deps.api);
267
268        deps.querier = abstract_mock_querier_builder(deps.api)
269            .with_smart_handler(&abstr.registry, |_| {
270                Ok(to_json_binary(&NamespaceResponse::Unclaimed {}).unwrap())
271            })
272            .build();
273
274        let binding = MockBinding {};
275        let module_registry = binding.module_registry(deps.as_ref()).unwrap();
276        let namespace = module_registry
277            .query_namespace(Namespace::new(ABSTRACT_NAMESPACE).unwrap())
278            .unwrap();
279        assert_eq!(namespace, NamespaceResponse::Unclaimed {});
280    }
281
282    #[coverage_helper::test]
283    fn query_namespaces() {
284        let mut deps = mock_dependencies();
285        let abstr = AbstractMockAddrs::new(deps.api);
286
287        deps.querier = abstract_mock_querier_builder(deps.api)
288            .with_smart_handler(&abstr.registry, |_| {
289                Ok(to_json_binary(&NamespacesResponse {
290                    namespaces: vec![(
291                        Namespace::new(ABSTRACT_NAMESPACE).unwrap(),
292                        ABSTRACT_ACCOUNT_ID,
293                    )],
294                })
295                .unwrap())
296            })
297            .build();
298
299        let binding = MockBinding {};
300        let module_registry = binding.module_registry(deps.as_ref()).unwrap();
301        let namespaces = module_registry
302            .query_namespaces(vec![ABSTRACT_ACCOUNT_ID])
303            .unwrap();
304        assert_eq!(
305            namespaces,
306            NamespacesResponse {
307                namespaces: vec![(
308                    Namespace::new(ABSTRACT_NAMESPACE).unwrap(),
309                    ABSTRACT_ACCOUNT_ID,
310                )],
311            }
312        );
313    }
314
315    #[coverage_helper::test]
316    fn query_modules() {
317        let mut deps = mock_dependencies();
318        let account = test_account(deps.api);
319        let abstr = AbstractMockAddrs::new(deps.api);
320
321        deps.querier = abstract_mock_querier_builder(deps.api)
322            // Setup the addresses as if the Account was registered
323            .account(&account, TEST_ACCOUNT_ID)
324            .with_contract_item(
325                &abstr.module_address,
326                MODULE,
327                &ModuleData {
328                    module: TEST_MODULE_ID.to_owned(),
329                    version: TEST_VERSION.to_owned(),
330                    dependencies: vec![],
331                    metadata: None,
332                },
333            )
334            .with_smart_handler(&abstr.registry, move |_| {
335                Ok(to_json_binary(&ModulesResponse {
336                    modules: vec![
337                        ModuleResponse {
338                            module: Module {
339                                info: ModuleInfo::from_id(TEST_MODULE_ID, "0.1.0".parse().unwrap())
340                                    .unwrap(),
341                                reference: ModuleReference::App(1),
342                            },
343                            config: ModuleConfiguration::new(
344                                Monetization::None,
345                                Some("metadata".to_owned()),
346                                vec![],
347                            ),
348                        },
349                        ModuleResponse {
350                            module: Module {
351                                info: ModuleInfo::from_id("test:module", "0.1.0".parse().unwrap())
352                                    .unwrap(),
353                                reference: ModuleReference::Standalone(2),
354                            },
355                            config: ModuleConfiguration::new(
356                                Monetization::None,
357                                Some("metadata2".to_owned()),
358                                vec![],
359                            ),
360                        },
361                    ],
362                })
363                .unwrap())
364            })
365            .build();
366
367        let binding = MockBinding {};
368
369        let module_info1 = ModuleInfo::from_id(TEST_MODULE_ID, "0.1.0".parse().unwrap()).unwrap();
370        let module_info2 = ModuleInfo::from_id("test:module", "0.1.0".parse().unwrap()).unwrap();
371
372        let module_registry = binding.module_registry(deps.as_ref()).unwrap();
373        let module = module_registry.query_module(module_info1.clone()).unwrap();
374        assert_eq!(
375            module,
376            Module {
377                info: ModuleInfo::from_id(TEST_MODULE_ID, "0.1.0".parse().unwrap()).unwrap(),
378                reference: ModuleReference::App(1),
379            }
380        );
381
382        let module_config = module_registry.query_config(module_info1.clone()).unwrap();
383        assert_eq!(
384            module_config,
385            ModuleConfiguration::new(Monetization::None, Some("metadata".to_owned()), vec![])
386        );
387
388        let modules_configs = module_registry
389            .query_modules_configs(vec![module_info1, module_info2])
390            .unwrap();
391        assert_eq!(
392            modules_configs,
393            vec![
394                ModuleResponse {
395                    module: Module {
396                        info: ModuleInfo::from_id(TEST_MODULE_ID, "0.1.0".parse().unwrap())
397                            .unwrap(),
398                        reference: ModuleReference::App(1),
399                    },
400                    config: ModuleConfiguration::new(
401                        Monetization::None,
402                        Some("metadata".to_owned()),
403                        vec![]
404                    )
405                },
406                ModuleResponse {
407                    module: Module {
408                        info: ModuleInfo::from_id("test:module", "0.1.0".parse().unwrap()).unwrap(),
409                        reference: ModuleReference::Standalone(2),
410                    },
411                    config: ModuleConfiguration::new(
412                        Monetization::None,
413                        Some("metadata2".to_owned()),
414                        vec![]
415                    )
416                }
417            ]
418        );
419        let module_info = module_registry.module_info(abstr.module_address).unwrap();
420        assert_eq!(
421            module_info,
422            Module {
423                info: ModuleInfo::from_id(TEST_MODULE_ID, "0.1.0".parse().unwrap()).unwrap(),
424                reference: ModuleReference::App(1),
425            }
426        )
427    }
428
429    #[coverage_helper::test]
430    fn abstract_api() {
431        let (deps, _, app) = mock_module_setup();
432        let module_registry = app.module_registry(deps.as_ref()).unwrap();
433
434        abstract_api_test(module_registry);
435    }
436}