abstract_interface/native/
ans_host.rs

1pub use abstract_std::ans_host::ExecuteMsgFns;
2use abstract_std::{
3    ans_host::*,
4    objects::{
5        pool_metadata::ResolvedPoolMetadata, AnsAsset, AnsEntryConvertor, AssetEntry, ChannelEntry,
6        ContractEntry, DexAssetPairing, LpToken, PoolMetadata, PoolReference, UniquePoolId,
7    },
8    ANS_HOST,
9};
10use cosmwasm_std::Uint128;
11use cw_address_like::AddressLike;
12use cw_asset::{Asset, AssetInfo, AssetInfoBase, AssetInfoUnchecked, AssetUnchecked};
13use cw_orch::{interface, prelude::*};
14
15#[interface(InstantiateMsg, ExecuteMsg, QueryMsg, MigrateMsg)]
16pub struct AnsHost<Chain>;
17
18impl<Chain: CwEnv> cw_blob::interface::DeterministicInstantiation<Chain> for AnsHost<Chain> {}
19
20impl<Chain: CwEnv> AnsHost<Chain> {
21    pub fn balance(&self, addr: &Addr, asset: &AssetEntry) -> Result<Uint128, CwOrchError> {
22        let asset: AssetInfo = self.resolve(asset)?;
23        let chain = self.environment();
24        match asset {
25            AssetInfoBase::Native(denom) => chain
26                .balance(addr, Some(denom))
27                .map(|coins| coins[0].amount)
28                .map_err(Into::into),
29            AssetInfoBase::Cw20(cw20_addr) => {
30                let res: cw20::BalanceResponse = chain
31                    .query(
32                        &cw20::Cw20QueryMsg::Balance {
33                            address: addr.to_string(),
34                        },
35                        &cw20_addr,
36                    )
37                    .map_err(Into::into)?;
38                Ok(res.balance)
39            }
40            _ => unimplemented!(),
41        }
42    }
43}
44
45impl<Chain: CwEnv> AnsHost<Chain> {
46    pub fn resolve<R: ClientResolve<Chain>>(&self, item: &R) -> Result<R::Output, CwOrchError> {
47        item.resolve(self)
48    }
49}
50
51impl<Chain: CwEnv> Uploadable for AnsHost<Chain> {
52    fn wrapper() -> <Mock as ::cw_orch::environment::TxHandler>::ContractSource {
53        Box::new(
54            ContractWrapper::new_with_empty(
55                ::ans_host::contract::execute,
56                ::ans_host::contract::instantiate,
57                ::ans_host::contract::query,
58            )
59            .with_migrate(::ans_host::contract::migrate),
60        )
61    }
62    fn wasm(_chain: &ChainInfoOwned) -> WasmPath {
63        artifacts_dir_from_workspace!()
64            .find_wasm_path("ans_host")
65            .unwrap()
66    }
67}
68
69impl<Chain: CwEnv> AnsHost<Chain>
70where
71    TxResponse<Chain>: IndexResponse,
72{
73    pub fn load(chain: Chain, address: &Addr) -> Self {
74        let contract = cw_orch::contract::Contract::new(ANS_HOST, chain);
75        contract.set_address(address);
76        Self(contract)
77    }
78}
79
80pub trait ClientResolve<Chain: CwEnv> {
81    /// Result of resolving an entry.
82    type Output;
83    /// Resolve an entry into its value.
84    fn resolve(&self, ans_host: &AnsHost<Chain>) -> Result<Self::Output, CwOrchError>;
85}
86
87// cw-multi-test doesn't support raw queries, so we will have to do smart queries instead
88
89impl<Chain: CwEnv> ClientResolve<Chain> for AssetEntry {
90    type Output = AssetInfo;
91
92    fn resolve(&self, ans_host: &AnsHost<Chain>) -> Result<Self::Output, CwOrchError> {
93        let mut assets: AssetsResponse = ans_host.query(&QueryMsg::Assets {
94            names: vec![self.to_string()],
95        })?;
96        Ok(assets.assets.pop().unwrap().1)
97    }
98}
99
100impl<Chain: CwEnv> ClientResolve<Chain> for LpToken {
101    type Output = AssetInfo;
102
103    fn resolve(&self, ans_host: &AnsHost<Chain>) -> Result<Self::Output, CwOrchError> {
104        let asset_entry = AnsEntryConvertor::new(self.clone()).asset_entry();
105        asset_entry.resolve(ans_host)
106    }
107}
108
109impl<Chain: CwEnv> ClientResolve<Chain> for ContractEntry {
110    type Output = Addr;
111
112    fn resolve(&self, ans_host: &AnsHost<Chain>) -> Result<Self::Output, CwOrchError> {
113        let mut contracts: ContractsResponse = ans_host.query(&QueryMsg::Contracts {
114            entries: vec![self.clone()],
115        })?;
116        Ok(contracts.contracts.pop().unwrap().1)
117    }
118}
119
120impl<Chain: CwEnv> ClientResolve<Chain> for ChannelEntry {
121    type Output = String;
122
123    fn resolve(&self, ans_host: &AnsHost<Chain>) -> Result<Self::Output, CwOrchError> {
124        let mut channels: ChannelsResponse = ans_host.query(&QueryMsg::Channels {
125            entries: vec![self.clone()],
126        })?;
127        Ok(channels.channels.pop().unwrap().1)
128    }
129}
130
131impl<Chain: CwEnv> ClientResolve<Chain> for DexAssetPairing {
132    type Output = Vec<PoolReference>;
133
134    fn resolve(&self, ans_host: &AnsHost<Chain>) -> Result<Self::Output, CwOrchError> {
135        let mut pool_address_list: PoolAddressListResponse =
136            ans_host.query(&QueryMsg::PoolList {
137                filter: Some(AssetPairingFilter {
138                    asset_pair: Some((self.asset_x().clone(), self.asset_y().clone())),
139                    dex: Some(self.dex().to_owned()),
140                }),
141                start_after: None,
142                limit: None,
143            })?;
144        let found = pool_address_list
145            .pools
146            .pop()
147            .ok_or(CwOrchError::StdErr(format!(
148                "Pool reference for {self} not found"
149            )))?;
150        Ok(found.1.clone())
151    }
152}
153
154impl<Chain: CwEnv> ClientResolve<Chain> for UniquePoolId {
155    type Output = PoolMetadata;
156
157    fn resolve(&self, ans_host: &AnsHost<Chain>) -> Result<Self::Output, CwOrchError> {
158        let mut pool_metadatas: PoolMetadatasResponse =
159            ans_host.query(&QueryMsg::PoolMetadatas { ids: vec![*self] })?;
160        Ok(pool_metadatas.metadatas.pop().unwrap().1)
161    }
162}
163
164impl<Chain: CwEnv> ClientResolve<Chain> for AnsAsset {
165    type Output = Asset;
166
167    fn resolve(&self, ans_host: &AnsHost<Chain>) -> Result<Self::Output, CwOrchError> {
168        Ok(Asset::new(self.name.resolve(ans_host)?, self.amount))
169    }
170}
171
172impl<Chain: CwEnv, T: AddressLike> ClientResolve<Chain> for AssetInfoBase<T> {
173    type Output = AssetEntry;
174
175    fn resolve(&self, ans_host: &AnsHost<Chain>) -> Result<Self::Output, CwOrchError> {
176        let asset_info_unchecked = match self {
177            AssetInfoBase::Native(native) => AssetInfoUnchecked::native(native),
178            AssetInfoBase::Cw20(cw20) => AssetInfoUnchecked::cw20(cw20.to_string()),
179            _ => panic!("Not supported asset type"),
180        };
181        let mut assets: AssetInfosResponse = ans_host.query(&QueryMsg::AssetInfos {
182            infos: vec![asset_info_unchecked],
183        })?;
184        Ok(assets.infos.pop().unwrap().1)
185    }
186}
187
188impl<Chain: CwEnv> ClientResolve<Chain> for AssetUnchecked {
189    type Output = AnsAsset;
190
191    fn resolve(&self, ans_host: &AnsHost<Chain>) -> Result<Self::Output, CwOrchError> {
192        Ok(AnsAsset {
193            name: self.info.resolve(ans_host)?,
194            amount: self.amount,
195        })
196    }
197}
198
199impl<Chain: CwEnv> ClientResolve<Chain> for PoolMetadata {
200    type Output = ResolvedPoolMetadata;
201
202    fn resolve(&self, ans_host: &AnsHost<Chain>) -> Result<Self::Output, CwOrchError> {
203        Ok(ResolvedPoolMetadata {
204            assets: self.assets.resolve(ans_host)?,
205            dex: self.dex.clone(),
206            pool_type: self.pool_type,
207        })
208    }
209}
210
211impl<Chain: CwEnv, T> ClientResolve<Chain> for Vec<T>
212where
213    T: ClientResolve<Chain>,
214{
215    type Output = Vec<T::Output>;
216
217    fn resolve(&self, ans_host: &AnsHost<Chain>) -> Result<Self::Output, CwOrchError> {
218        self.iter().map(|entry| entry.resolve(ans_host)).collect()
219    }
220}