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
21pub trait ModuleRegistryInterface: AbstractRegistryAccess + ModuleIdentification {
23 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)]
66pub struct ModuleRegistry<'a, T: ModuleRegistryInterface> {
83 base: &'a T,
84 deps: Deps<'a>,
85 registry: RegistryContract,
86}
87
88impl<T: ModuleRegistryInterface> ModuleRegistry<'_, T> {
89 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 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 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 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 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 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 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 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 pub fn module_info(&self, address: Addr) -> AbstractSdkResult<Module> {
163 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 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 .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}