abstract_sdk/
ans_resolve.rs

1//! # AnsHost Entry
2//! An entry (value) in the ans_host key-value store.
3
4use 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
13/// Resolve an [`AbstractNameService`](crate::features::AbstractNameService) entry into its value.
14pub trait Resolve {
15    /// Result of resolving an entry.
16    type Output;
17    /// Resolve an entry into its value.
18    fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output>;
19    /// Check if the entry is registered in the ANS.
20    fn is_registered(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> bool {
21        self.resolve(querier, ans_host).is_ok()
22    }
23    /// Assert that a given entry is registered in the ANS.
24    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    /// Querier builder with the ans host contract known.
160    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, &not_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, &not_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, &not_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, &not_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, &not_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, &not_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, &not_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}