1use abstract_std::objects::{ans_host::AnsHostResult, AnsEntryConvertor};
5use cosmwasm_std::{Addr, QuerierWrapper};
6use cw_asset::{Asset, AssetInfo};
7
8use crate::std::objects::{
9 ans_host::AnsHost, pool_metadata::ResolvedPoolMetadata, AnsAsset, AssetEntry, ChannelEntry,
10 ContractEntry, DexAssetPairing, LpToken, PoolMetadata, PoolReference, UniquePoolId,
11};
12
13pub trait Resolve {
15 type Output;
17 fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output>;
19 fn is_registered(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> bool {
21 self.resolve(querier, ans_host).is_ok()
22 }
23 fn assert_registered(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<()> {
25 self.resolve(querier, ans_host).map(|_| ())
26 }
27}
28
29impl Resolve for AssetEntry {
30 type Output = AssetInfo;
31 fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
32 ans_host.query_asset(querier, self)
33 }
34}
35
36impl Resolve for LpToken {
37 type Output = AssetInfo;
38
39 fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
40 let asset_entry = AnsEntryConvertor::new(self.clone()).asset_entry();
41 ans_host.query_asset(querier, &asset_entry)
42 }
43}
44
45impl Resolve for ContractEntry {
46 type Output = Addr;
47 fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
48 ans_host.query_contract(querier, self)
49 }
50}
51
52impl Resolve for ChannelEntry {
53 type Output = String;
54 fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
55 ans_host.query_channel(querier, self)
56 }
57}
58
59impl Resolve for DexAssetPairing {
60 type Output = Vec<PoolReference>;
61 fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
62 ans_host.query_asset_pairing(querier, self)
63 }
64}
65
66impl Resolve for UniquePoolId {
67 type Output = PoolMetadata;
68 fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
69 ans_host.query_pool_metadata(querier, *self)
70 }
71}
72
73impl Resolve for AnsAsset {
74 type Output = Asset;
75
76 fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
77 Ok(Asset::new(
78 ans_host.query_asset(querier, &self.name)?,
79 self.amount,
80 ))
81 }
82}
83
84impl Resolve for AssetInfo {
85 type Output = AssetEntry;
86
87 fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
88 ans_host.query_asset_reverse(querier, self)
89 }
90}
91
92impl Resolve for Asset {
93 type Output = AnsAsset;
94
95 fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
96 Ok(AnsAsset {
97 name: self.info.resolve(querier, ans_host)?,
98 amount: self.amount,
99 })
100 }
101}
102
103impl Resolve for PoolMetadata {
104 type Output = ResolvedPoolMetadata;
105
106 fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
107 Ok(ResolvedPoolMetadata {
108 assets: self.assets.resolve(querier, ans_host)?,
109 dex: self.dex.clone(),
110 pool_type: self.pool_type,
111 })
112 }
113}
114
115impl<T> Resolve for Vec<T>
116where
117 T: Resolve,
118{
119 type Output = Vec<T::Output>;
120
121 fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
122 self.iter()
123 .map(|entry| entry.resolve(querier, ans_host))
124 .collect()
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 use abstract_std::ans_host::state::ASSET_ADDRESSES;
133 use abstract_testing::prelude::*;
134 use cosmwasm_std::{
135 testing::{mock_dependencies, MockApi},
136 Binary, Empty,
137 };
138 use std::fmt::Debug;
139
140 fn default_test_querier(ans_host: &AnsHost) -> MockQuerier {
141 let ans_host_addr = ans_host.address.clone();
142 MockQuerierBuilder::default()
143 .with_fallback_raw_handler(move |contract, _| {
144 if contract == ans_host_addr {
145 Ok(Binary::default())
146 } else {
147 Err("unexpected contract".into())
148 }
149 })
150 .build()
151 }
152
153 fn mock_ans_host(mock_api: MockApi) -> AnsHost {
154 AnsHost {
155 address: AbstractMockAddrs::new(mock_api).ans_host,
156 }
157 }
158
159 fn mock_deps_with_default_querier() -> MockDeps {
161 let mut deps = mock_dependencies();
162 let ans_host = mock_ans_host(deps.api);
163 deps.querier = default_test_querier(&ans_host);
164 deps
165 }
166
167 pub fn test_resolve<R: Resolve>(
168 ans_host: &AnsHost,
169 querier: &MockQuerier<Empty>,
170 entry: &R,
171 ) -> AnsHostResult<R::Output> {
172 entry.resolve(&wrap_querier(querier), ans_host)
173 }
174
175 fn test_dne<R: Resolve>(ans_host: &AnsHost, nonexistent: &R)
176 where
177 <R as Resolve>::Output: Debug,
178 {
179 let res = test_resolve(ans_host, &default_test_querier(ans_host), nonexistent);
180
181 assert!(res.unwrap_err().to_string().contains("not found"));
182 }
183
184 mod is_registered {
185 use super::*;
186
187 #[coverage_helper::test]
188 fn exists() {
189 let mock_api = MockApi::default();
190 let ans_host = mock_ans_host(mock_api);
191
192 let test_asset_entry = AssetEntry::new("aoeu");
193 let querier = MockQuerierBuilder::default()
194 .with_contract_map_entry(
195 &ans_host.address,
196 ASSET_ADDRESSES,
197 (&test_asset_entry, AssetInfo::native("abc")),
198 )
199 .build();
200
201 let is_registered =
202 test_asset_entry.is_registered(&QuerierWrapper::new(&querier), &ans_host);
203 assert!(is_registered);
204
205 let assert_registered =
206 test_asset_entry.assert_registered(&QuerierWrapper::new(&querier), &ans_host);
207 assert!(assert_registered.is_ok())
208 }
209
210 #[coverage_helper::test]
211 fn does_not_exist() {
212 let mock_api = MockApi::default();
213 let ans_host = mock_ans_host(mock_api);
214
215 let not_exist_asset = AssetEntry::new("aoeu");
216 let querier = default_test_querier(&ans_host);
217 let wrapper = wrap_querier(&querier);
218
219 let is_registered = not_exist_asset.is_registered(&wrapper, &ans_host);
220 assert!(!is_registered);
221 let assert_registered = not_exist_asset.assert_registered(&wrapper, &ans_host);
222 assert!(assert_registered.is_err());
223 }
224 }
225
226 mod asset_entry {
227 use super::*;
228
229 #[coverage_helper::test]
230 fn exists() {
231 let mock_api = MockApi::default();
232 let ans_host = mock_ans_host(mock_api);
233
234 let expected_addr = Addr::unchecked("result");
235 let test_asset_entry = AssetEntry::new("aoeu");
236 let expected_value = AssetInfo::cw20(expected_addr.clone());
237 let querier = MockQuerierBuilder::default()
238 .with_contract_map_entry(
239 &ans_host.address,
240 ASSET_ADDRESSES,
241 (&test_asset_entry, expected_value.clone()),
242 )
243 .build();
244
245 let res = test_resolve(&ans_host, &querier, &test_asset_entry);
246 assert_eq!(res, Ok(expected_value));
247
248 let ans_asset_res =
249 test_resolve(&ans_host, &querier, &AnsAsset::new("aoeu", 52256u128));
250 assert_eq!(ans_asset_res, Ok(Asset::cw20(expected_addr, 52256u128)));
251 }
252
253 #[coverage_helper::test]
254 fn does_not_exist() {
255 let deps = mock_deps_with_default_querier();
256 let ans_host = mock_ans_host(deps.api);
257
258 let not_exist_asset = AssetEntry::new("aoeu");
259
260 test_dne(&ans_host, ¬_exist_asset);
261 }
262
263 #[coverage_helper::test]
264 fn array() {
265 let mock_api = MockApi::default();
266 let ans_host = mock_ans_host(mock_api);
267
268 let expected_entries = vec![
269 (
270 AssetEntry::new("aoeu"),
271 AssetInfo::cw20(mock_api.addr_make("aoeu")),
272 ),
273 (
274 AssetEntry::new("snth"),
275 AssetInfo::cw20(mock_api.addr_make("snth")),
276 ),
277 ];
278 let querier = MockQuerierBuilder::default()
279 .with_contract_map_entries(
280 &ans_host.address,
281 ASSET_ADDRESSES,
282 expected_entries
283 .iter()
284 .map(|(k, v)| (k, v.clone()))
285 .collect(),
286 )
287 .build();
288
289 let (keys, values): (Vec<_>, Vec<_>) = expected_entries.into_iter().unzip();
290
291 let res = keys.resolve(&wrap_querier(&querier), &ans_host);
292
293 assert_eq!(res, Ok(values));
294 }
295 }
296
297 mod lp_token {
298 use super::*;
299
300 #[coverage_helper::test]
301 fn exists() {
302 let mock_api = MockApi::default();
303 let ans_host = mock_ans_host(mock_api);
304
305 let lp_token_address = mock_api.addr_make("result");
306 let assets = vec!["atom", "juno"];
307
308 let test_lp_token = LpToken::new("junoswap", assets);
309 let asset_entry = AnsEntryConvertor::new(test_lp_token.clone()).asset_entry();
310 let expected_value = AssetInfo::cw20(lp_token_address);
311 let querier = MockQuerierBuilder::default()
312 .with_contract_map_entry(
313 &ans_host.address,
314 ASSET_ADDRESSES,
315 (&asset_entry, expected_value.clone()),
316 )
317 .build();
318
319 let res = test_resolve(&ans_host, &querier, &test_lp_token);
320 assert_eq!(res, Ok(expected_value));
321 }
322
323 #[coverage_helper::test]
324 fn does_not_exist() {
325 let mock_api = MockApi::default();
326 let ans_host = mock_ans_host(mock_api);
327
328 let not_exist_lp_token = LpToken::new("terraswap", vec!["rest", "peacefully"]);
329
330 test_dne(&ans_host, ¬_exist_lp_token);
331 }
332 }
333
334 mod pool_metadata {
335 use super::*;
336 use crate::std::objects::PoolType;
337
338 #[coverage_helper::test]
339 fn exists() {
340 let mock_api = MockApi::default();
341 let ans_host = mock_ans_host(mock_api);
342
343 let assets = vec!["atom", "juno"];
344
345 let atom_addr = AssetInfo::cw20(mock_api.addr_make("atom_address"));
346 let juno_addr = AssetInfo::cw20(mock_api.addr_make("juno_address"));
347 let resolved_assets = vec![
348 (AssetEntry::new("atom"), &atom_addr),
349 (AssetEntry::new("juno"), &juno_addr),
350 ];
351
352 let dex = "junoswap";
353 let pool_type = PoolType::ConstantProduct;
354 let test_pool_metadata = PoolMetadata::new(dex, pool_type, assets);
355 let querier = MockQuerierBuilder::new(mock_api)
356 .assets(
357 resolved_assets
358 .iter()
359 .map(|(k, v)| (k, (*v).clone()))
360 .collect(),
361 )
362 .build();
363
364 let expected_value = ResolvedPoolMetadata {
365 dex: dex.into(),
366 pool_type,
367 assets: resolved_assets
368 .into_iter()
369 .map(|(_, b)| b.clone())
370 .collect(),
371 };
372
373 let res = test_resolve(&ans_host, &querier, &test_pool_metadata);
374 assert_eq!(res, Ok(expected_value));
375 }
376
377 #[coverage_helper::test]
378 fn does_not_exist() {
379 let mock_api = MockApi::default();
380 let ans_host = mock_ans_host(mock_api);
381
382 let not_exist_md = PoolMetadata::new(
383 "junoswap",
384 PoolType::ConstantProduct,
385 vec![AssetEntry::new("juno")],
386 );
387
388 test_dne(&ans_host, ¬_exist_md);
389 }
390 }
391
392 mod pools {
393 use abstract_std::ans_host::state::{ASSET_PAIRINGS, POOL_METADATA};
394
395 use super::*;
396 use crate::std::objects::{PoolAddress, PoolType};
397
398 #[coverage_helper::test]
399 fn exists() {
400 let mock_api = MockApi::default();
401 let ans_host = mock_ans_host(mock_api);
402
403 let assets = vec!["atom", "juno"];
404 let dex = "boogerswap";
405 let pairing =
406 DexAssetPairing::new(AssetEntry::new(assets[0]), AssetEntry::new(assets[1]), dex);
407
408 let unique_pool_id: UniquePoolId = 1u64.into();
409 let pool_address: PoolAddress = mock_api.addr_make("pool_address").into();
410 let pool_reference = PoolReference::new(unique_pool_id, pool_address);
411 let pool_metadata = PoolMetadata::new(dex, PoolType::ConstantProduct, assets.clone());
412
413 let querier = MockQuerierBuilder::default()
414 .with_contract_map_entry(
415 &ans_host.address,
416 ASSET_PAIRINGS,
417 (&pairing, vec![pool_reference]),
418 )
419 .with_contract_map_entry(
420 &ans_host.address,
421 POOL_METADATA,
422 (unique_pool_id, pool_metadata.clone()),
423 )
424 .build();
425
426 let unique_pool_id_res = test_resolve(&ans_host, &querier, &unique_pool_id);
427 assert_eq!(unique_pool_id_res, Ok(pool_metadata));
428 }
429
430 #[coverage_helper::test]
431 fn does_not_exist() {
432 let mock_api = MockApi::default();
433 let ans_host = mock_ans_host(mock_api);
434
435 let not_exist_pool = UniquePoolId::new(1u64);
436
437 test_dne(&ans_host, ¬_exist_pool);
438 }
439 }
440
441 mod contract_entry {
442 use super::*;
443 use crate::std::ans_host::state::CONTRACT_ADDRESSES;
444
445 #[coverage_helper::test]
446 fn exists() {
447 let mock_api = MockApi::default();
448 let ans_host = mock_ans_host(mock_api);
449
450 let test_contract_entry = ContractEntry {
451 protocol: "protocol".to_string(),
452 contract: "contract".to_string(),
453 };
454
455 let expected_value = mock_api.addr_make("address");
456 let querier = MockQuerierBuilder::default()
457 .with_contract_map_entry(
458 &ans_host.address,
459 CONTRACT_ADDRESSES,
460 (&test_contract_entry, expected_value.clone()),
461 )
462 .build();
463
464 let res = test_resolve(&ans_host, &querier, &test_contract_entry);
465
466 assert_eq!(res, Ok(expected_value));
467 }
468
469 #[coverage_helper::test]
470 fn does_not_exist() {
471 let mock_api = MockApi::default();
472 let ans_host = mock_ans_host(mock_api);
473
474 let not_exist_contract = ContractEntry {
475 protocol: "protocol".to_string(),
476 contract: "contract".to_string(),
477 };
478
479 test_dne(&ans_host, ¬_exist_contract);
480 }
481
482 #[coverage_helper::test]
483 fn array() {
484 let mock_api = MockApi::default();
485 let ans_host = mock_ans_host(mock_api);
486
487 let expected_addr = mock_api.addr_make("result");
488 let expected_entries = vec![
489 (
490 ContractEntry {
491 protocol: "junoswap".to_string(),
492 contract: "something".to_string(),
493 },
494 expected_addr.clone(),
495 ),
496 (
497 ContractEntry {
498 protocol: "astroport".to_string(),
499 contract: "something".to_string(),
500 },
501 expected_addr,
502 ),
503 ];
504 let querier = MockQuerierBuilder::default()
505 .with_contract_map_entries(
506 &ans_host.address,
507 CONTRACT_ADDRESSES,
508 expected_entries
509 .iter()
510 .map(|(k, v)| (k, v.clone()))
511 .collect(),
512 )
513 .build();
514
515 let (keys, values): (Vec<_>, Vec<_>) = expected_entries.into_iter().unzip();
516
517 let res = keys.resolve(&wrap_querier(&querier), &ans_host);
518
519 assert_eq!(res, Ok(values));
520 }
521 }
522
523 mod channel_entry {
524 use std::str::FromStr;
525
526 use abstract_std::objects::TruncatedChainId;
527
528 use super::*;
529 use crate::std::ans_host::state::CHANNELS;
530
531 #[coverage_helper::test]
532 fn exists() {
533 let mock_api = MockApi::default();
534 let ans_host = mock_ans_host(mock_api);
535
536 let test_channel_entry = ChannelEntry {
537 protocol: "protocol".to_string(),
538 connected_chain: TruncatedChainId::from_str("abstract").unwrap(),
539 };
540
541 let expected_value = "channel-id".to_string();
542 let querier = MockQuerierBuilder::default()
543 .with_contract_map_entry(
544 &ans_host.address,
545 CHANNELS,
546 (&test_channel_entry, expected_value.clone()),
547 )
548 .build();
549
550 let res = test_resolve(&ans_host, &querier, &test_channel_entry);
551
552 assert_eq!(res, Ok(expected_value));
553 }
554
555 #[coverage_helper::test]
556 fn does_not_exist() {
557 let mock_api = MockApi::default();
558 let ans_host = mock_ans_host(mock_api);
559
560 let not_exist_channel = ChannelEntry {
561 protocol: "protocol".to_string(),
562 connected_chain: TruncatedChainId::from_str("chain").unwrap(),
563 };
564
565 test_dne(&ans_host, ¬_exist_channel);
566 }
567 }
568
569 mod asset_info_and_asset {
570 use super::*;
571 use crate::std::ans_host::state::REV_ASSET_ADDRESSES;
572
573 #[coverage_helper::test]
574 fn exists() {
575 let mock_api = MockApi::default();
576 let ans_host = mock_ans_host(mock_api);
577
578 let expected_address = mock_api.addr_make("address");
579 let test_asset_info = AssetInfo::cw20(expected_address.clone());
580
581 let expected_value = AssetEntry::new("chinachinachina");
582 let querier = MockQuerierBuilder::default()
583 .with_contract_map_entry(
584 &ans_host.address,
585 REV_ASSET_ADDRESSES,
586 (&test_asset_info, expected_value.clone()),
587 )
588 .build();
589
590 let res = test_resolve(&ans_host, &querier, &test_asset_info);
591 assert_eq!(res, Ok(expected_value));
592
593 let asset_res = test_resolve(
594 &ans_host,
595 &querier,
596 &Asset::cw20(expected_address, 12345u128),
597 );
598 assert_eq!(asset_res, Ok(AnsAsset::new("chinachinachina", 12345u128)));
599 }
600
601 #[coverage_helper::test]
602 fn does_not_exist() {
603 let mock_api = MockApi::default();
604 let ans_host = mock_ans_host(mock_api);
605
606 let not_exist_asset_info = AssetInfo::cw20(mock_api.addr_make("address"));
607
608 test_dne(&ans_host, ¬_exist_asset_info);
609 }
610
611 #[coverage_helper::test]
612 fn array() {
613 let mock_api = MockApi::default();
614 let ans_host = mock_ans_host(mock_api);
615
616 let expected_entries = vec![
617 (
618 AssetInfo::cw20(mock_api.addr_make("boop")),
619 AssetEntry::new("beepboop"),
620 ),
621 (
622 AssetInfo::cw20(mock_api.addr_make("iloveabstract")),
623 AssetEntry::new("robinrocks!"),
624 ),
625 ];
626 let querier = MockQuerierBuilder::default()
627 .with_contract_map_entries(
628 &ans_host.address,
629 REV_ASSET_ADDRESSES,
630 expected_entries
631 .iter()
632 .map(|(k, v)| (k, v.clone()))
633 .collect(),
634 )
635 .build();
636
637 let (keys, values): (Vec<_>, Vec<_>) = expected_entries.into_iter().unzip();
638
639 let res = keys.resolve(&wrap_querier(&querier), &ans_host);
640
641 assert_eq!(res, Ok(values));
642 }
643 }
644}