use abstract_std::objects::{ans_host::AnsHostResult, AnsEntryConvertor};
use cosmwasm_std::{Addr, QuerierWrapper};
use cw_asset::{Asset, AssetInfo};
use crate::std::objects::{
ans_host::AnsHost, pool_metadata::ResolvedPoolMetadata, AnsAsset, AssetEntry, ChannelEntry,
ContractEntry, DexAssetPairing, LpToken, PoolMetadata, PoolReference, UniquePoolId,
};
pub trait Resolve {
type Output;
fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output>;
fn is_registered(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> bool {
self.resolve(querier, ans_host).is_ok()
}
fn assert_registered(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<()> {
self.resolve(querier, ans_host).map(|_| ())
}
}
impl Resolve for AssetEntry {
type Output = AssetInfo;
fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
ans_host.query_asset(querier, self)
}
}
impl Resolve for LpToken {
type Output = AssetInfo;
fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
let asset_entry = AnsEntryConvertor::new(self.clone()).asset_entry();
ans_host.query_asset(querier, &asset_entry)
}
}
impl Resolve for ContractEntry {
type Output = Addr;
fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
ans_host.query_contract(querier, self)
}
}
impl Resolve for ChannelEntry {
type Output = String;
fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
ans_host.query_channel(querier, self)
}
}
impl Resolve for DexAssetPairing {
type Output = Vec<PoolReference>;
fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
ans_host.query_asset_pairing(querier, self)
}
}
impl Resolve for UniquePoolId {
type Output = PoolMetadata;
fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
ans_host.query_pool_metadata(querier, *self)
}
}
impl Resolve for AnsAsset {
type Output = Asset;
fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
Ok(Asset::new(
ans_host.query_asset(querier, &self.name)?,
self.amount,
))
}
}
impl Resolve for AssetInfo {
type Output = AssetEntry;
fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
ans_host.query_asset_reverse(querier, self)
}
}
impl Resolve for Asset {
type Output = AnsAsset;
fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
Ok(AnsAsset {
name: self.info.resolve(querier, ans_host)?,
amount: self.amount,
})
}
}
impl Resolve for PoolMetadata {
type Output = ResolvedPoolMetadata;
fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
Ok(ResolvedPoolMetadata {
assets: self.assets.resolve(querier, ans_host)?,
dex: self.dex.clone(),
pool_type: self.pool_type,
})
}
}
impl<T> Resolve for Vec<T>
where
T: Resolve,
{
type Output = Vec<T::Output>;
fn resolve(&self, querier: &QuerierWrapper, ans_host: &AnsHost) -> AnsHostResult<Self::Output> {
self.iter()
.map(|entry| entry.resolve(querier, ans_host))
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
use abstract_std::ans_host::state::ASSET_ADDRESSES;
use abstract_testing::prelude::*;
use cosmwasm_std::{
testing::{mock_dependencies, MockApi},
Binary, Empty,
};
use std::fmt::Debug;
fn default_test_querier(ans_host: &AnsHost) -> MockQuerier {
let ans_host_addr = ans_host.address.clone();
MockQuerierBuilder::default()
.with_fallback_raw_handler(move |contract, _| {
if contract == ans_host_addr {
Ok(Binary::default())
} else {
Err("unexpected contract".into())
}
})
.build()
}
fn mock_ans_host(mock_api: MockApi) -> AnsHost {
AnsHost {
address: AbstractMockAddrs::new(mock_api).ans_host,
}
}
fn mock_deps_with_default_querier() -> MockDeps {
let mut deps = mock_dependencies();
let ans_host = mock_ans_host(deps.api);
deps.querier = default_test_querier(&ans_host);
deps
}
pub fn test_resolve<R: Resolve>(
ans_host: &AnsHost,
querier: &MockQuerier<Empty>,
entry: &R,
) -> AnsHostResult<R::Output> {
entry.resolve(&wrap_querier(querier), ans_host)
}
fn test_dne<R: Resolve>(ans_host: &AnsHost, nonexistent: &R)
where
<R as Resolve>::Output: Debug,
{
let res = test_resolve(ans_host, &default_test_querier(ans_host), nonexistent);
assert!(res.unwrap_err().to_string().contains("not found"));
}
mod is_registered {
use super::*;
#[coverage_helper::test]
fn exists() {
let mock_api = MockApi::default();
let ans_host = mock_ans_host(mock_api);
let test_asset_entry = AssetEntry::new("aoeu");
let querier = MockQuerierBuilder::default()
.with_contract_map_entry(
&ans_host.address,
ASSET_ADDRESSES,
(&test_asset_entry, AssetInfo::native("abc")),
)
.build();
let is_registered =
test_asset_entry.is_registered(&QuerierWrapper::new(&querier), &ans_host);
assert!(is_registered);
let assert_registered =
test_asset_entry.assert_registered(&QuerierWrapper::new(&querier), &ans_host);
assert!(assert_registered.is_ok())
}
#[coverage_helper::test]
fn does_not_exist() {
let mock_api = MockApi::default();
let ans_host = mock_ans_host(mock_api);
let not_exist_asset = AssetEntry::new("aoeu");
let querier = default_test_querier(&ans_host);
let wrapper = wrap_querier(&querier);
let is_registered = not_exist_asset.is_registered(&wrapper, &ans_host);
assert!(!is_registered);
let assert_registered = not_exist_asset.assert_registered(&wrapper, &ans_host);
assert!(assert_registered.is_err());
}
}
mod asset_entry {
use super::*;
#[coverage_helper::test]
fn exists() {
let mock_api = MockApi::default();
let ans_host = mock_ans_host(mock_api);
let expected_addr = Addr::unchecked("result");
let test_asset_entry = AssetEntry::new("aoeu");
let expected_value = AssetInfo::cw20(expected_addr.clone());
let querier = MockQuerierBuilder::default()
.with_contract_map_entry(
&ans_host.address,
ASSET_ADDRESSES,
(&test_asset_entry, expected_value.clone()),
)
.build();
let res = test_resolve(&ans_host, &querier, &test_asset_entry);
assert_eq!(res, Ok(expected_value));
let ans_asset_res =
test_resolve(&ans_host, &querier, &AnsAsset::new("aoeu", 52256u128));
assert_eq!(ans_asset_res, Ok(Asset::cw20(expected_addr, 52256u128)));
}
#[coverage_helper::test]
fn does_not_exist() {
let deps = mock_deps_with_default_querier();
let ans_host = mock_ans_host(deps.api);
let not_exist_asset = AssetEntry::new("aoeu");
test_dne(&ans_host, ¬_exist_asset);
}
#[coverage_helper::test]
fn array() {
let mock_api = MockApi::default();
let ans_host = mock_ans_host(mock_api);
let expected_entries = vec![
(
AssetEntry::new("aoeu"),
AssetInfo::cw20(mock_api.addr_make("aoeu")),
),
(
AssetEntry::new("snth"),
AssetInfo::cw20(mock_api.addr_make("snth")),
),
];
let querier = MockQuerierBuilder::default()
.with_contract_map_entries(
&ans_host.address,
ASSET_ADDRESSES,
expected_entries
.iter()
.map(|(k, v)| (k, v.clone()))
.collect(),
)
.build();
let (keys, values): (Vec<_>, Vec<_>) = expected_entries.into_iter().unzip();
let res = keys.resolve(&wrap_querier(&querier), &ans_host);
assert_eq!(res, Ok(values));
}
}
mod lp_token {
use super::*;
#[coverage_helper::test]
fn exists() {
let mock_api = MockApi::default();
let ans_host = mock_ans_host(mock_api);
let lp_token_address = mock_api.addr_make("result");
let assets = vec!["atom", "juno"];
let test_lp_token = LpToken::new("junoswap", assets);
let asset_entry = AnsEntryConvertor::new(test_lp_token.clone()).asset_entry();
let expected_value = AssetInfo::cw20(lp_token_address);
let querier = MockQuerierBuilder::default()
.with_contract_map_entry(
&ans_host.address,
ASSET_ADDRESSES,
(&asset_entry, expected_value.clone()),
)
.build();
let res = test_resolve(&ans_host, &querier, &test_lp_token);
assert_eq!(res, Ok(expected_value));
}
#[coverage_helper::test]
fn does_not_exist() {
let mock_api = MockApi::default();
let ans_host = mock_ans_host(mock_api);
let not_exist_lp_token = LpToken::new("terraswap", vec!["rest", "peacefully"]);
test_dne(&ans_host, ¬_exist_lp_token);
}
}
mod pool_metadata {
use super::*;
use crate::std::objects::PoolType;
#[coverage_helper::test]
fn exists() {
let mock_api = MockApi::default();
let ans_host = mock_ans_host(mock_api);
let assets = vec!["atom", "juno"];
let atom_addr = AssetInfo::cw20(mock_api.addr_make("atom_address"));
let juno_addr = AssetInfo::cw20(mock_api.addr_make("juno_address"));
let resolved_assets = vec![
(AssetEntry::new("atom"), &atom_addr),
(AssetEntry::new("juno"), &juno_addr),
];
let dex = "junoswap";
let pool_type = PoolType::ConstantProduct;
let test_pool_metadata = PoolMetadata::new(dex, pool_type, assets);
let querier = MockQuerierBuilder::new(mock_api)
.assets(
resolved_assets
.iter()
.map(|(k, v)| (k, (*v).clone()))
.collect(),
)
.build();
let expected_value = ResolvedPoolMetadata {
dex: dex.into(),
pool_type,
assets: resolved_assets
.into_iter()
.map(|(_, b)| b.clone())
.collect(),
};
let res = test_resolve(&ans_host, &querier, &test_pool_metadata);
assert_eq!(res, Ok(expected_value));
}
#[coverage_helper::test]
fn does_not_exist() {
let mock_api = MockApi::default();
let ans_host = mock_ans_host(mock_api);
let not_exist_md = PoolMetadata::new(
"junoswap",
PoolType::ConstantProduct,
vec![AssetEntry::new("juno")],
);
test_dne(&ans_host, ¬_exist_md);
}
}
mod pools {
use abstract_std::ans_host::state::{ASSET_PAIRINGS, POOL_METADATA};
use super::*;
use crate::std::objects::{PoolAddress, PoolType};
#[coverage_helper::test]
fn exists() {
let mock_api = MockApi::default();
let ans_host = mock_ans_host(mock_api);
let assets = vec!["atom", "juno"];
let dex = "boogerswap";
let pairing =
DexAssetPairing::new(AssetEntry::new(assets[0]), AssetEntry::new(assets[1]), dex);
let unique_pool_id: UniquePoolId = 1u64.into();
let pool_address: PoolAddress = mock_api.addr_make("pool_address").into();
let pool_reference = PoolReference::new(unique_pool_id, pool_address);
let pool_metadata = PoolMetadata::new(dex, PoolType::ConstantProduct, assets.clone());
let querier = MockQuerierBuilder::default()
.with_contract_map_entry(
&ans_host.address,
ASSET_PAIRINGS,
(&pairing, vec![pool_reference]),
)
.with_contract_map_entry(
&ans_host.address,
POOL_METADATA,
(unique_pool_id, pool_metadata.clone()),
)
.build();
let unique_pool_id_res = test_resolve(&ans_host, &querier, &unique_pool_id);
assert_eq!(unique_pool_id_res, Ok(pool_metadata));
}
#[coverage_helper::test]
fn does_not_exist() {
let mock_api = MockApi::default();
let ans_host = mock_ans_host(mock_api);
let not_exist_pool = UniquePoolId::new(1u64);
test_dne(&ans_host, ¬_exist_pool);
}
}
mod contract_entry {
use super::*;
use crate::std::ans_host::state::CONTRACT_ADDRESSES;
#[coverage_helper::test]
fn exists() {
let mock_api = MockApi::default();
let ans_host = mock_ans_host(mock_api);
let test_contract_entry = ContractEntry {
protocol: "protocol".to_string(),
contract: "contract".to_string(),
};
let expected_value = mock_api.addr_make("address");
let querier = MockQuerierBuilder::default()
.with_contract_map_entry(
&ans_host.address,
CONTRACT_ADDRESSES,
(&test_contract_entry, expected_value.clone()),
)
.build();
let res = test_resolve(&ans_host, &querier, &test_contract_entry);
assert_eq!(res, Ok(expected_value));
}
#[coverage_helper::test]
fn does_not_exist() {
let mock_api = MockApi::default();
let ans_host = mock_ans_host(mock_api);
let not_exist_contract = ContractEntry {
protocol: "protocol".to_string(),
contract: "contract".to_string(),
};
test_dne(&ans_host, ¬_exist_contract);
}
#[coverage_helper::test]
fn array() {
let mock_api = MockApi::default();
let ans_host = mock_ans_host(mock_api);
let expected_addr = mock_api.addr_make("result");
let expected_entries = vec![
(
ContractEntry {
protocol: "junoswap".to_string(),
contract: "something".to_string(),
},
expected_addr.clone(),
),
(
ContractEntry {
protocol: "astroport".to_string(),
contract: "something".to_string(),
},
expected_addr,
),
];
let querier = MockQuerierBuilder::default()
.with_contract_map_entries(
&ans_host.address,
CONTRACT_ADDRESSES,
expected_entries
.iter()
.map(|(k, v)| (k, v.clone()))
.collect(),
)
.build();
let (keys, values): (Vec<_>, Vec<_>) = expected_entries.into_iter().unzip();
let res = keys.resolve(&wrap_querier(&querier), &ans_host);
assert_eq!(res, Ok(values));
}
}
mod channel_entry {
use std::str::FromStr;
use abstract_std::objects::TruncatedChainId;
use super::*;
use crate::std::ans_host::state::CHANNELS;
#[coverage_helper::test]
fn exists() {
let mock_api = MockApi::default();
let ans_host = mock_ans_host(mock_api);
let test_channel_entry = ChannelEntry {
protocol: "protocol".to_string(),
connected_chain: TruncatedChainId::from_str("abstract").unwrap(),
};
let expected_value = "channel-id".to_string();
let querier = MockQuerierBuilder::default()
.with_contract_map_entry(
&ans_host.address,
CHANNELS,
(&test_channel_entry, expected_value.clone()),
)
.build();
let res = test_resolve(&ans_host, &querier, &test_channel_entry);
assert_eq!(res, Ok(expected_value));
}
#[coverage_helper::test]
fn does_not_exist() {
let mock_api = MockApi::default();
let ans_host = mock_ans_host(mock_api);
let not_exist_channel = ChannelEntry {
protocol: "protocol".to_string(),
connected_chain: TruncatedChainId::from_str("chain").unwrap(),
};
test_dne(&ans_host, ¬_exist_channel);
}
}
mod asset_info_and_asset {
use super::*;
use crate::std::ans_host::state::REV_ASSET_ADDRESSES;
#[coverage_helper::test]
fn exists() {
let mock_api = MockApi::default();
let ans_host = mock_ans_host(mock_api);
let expected_address = mock_api.addr_make("address");
let test_asset_info = AssetInfo::cw20(expected_address.clone());
let expected_value = AssetEntry::new("chinachinachina");
let querier = MockQuerierBuilder::default()
.with_contract_map_entry(
&ans_host.address,
REV_ASSET_ADDRESSES,
(&test_asset_info, expected_value.clone()),
)
.build();
let res = test_resolve(&ans_host, &querier, &test_asset_info);
assert_eq!(res, Ok(expected_value));
let asset_res = test_resolve(
&ans_host,
&querier,
&Asset::cw20(expected_address, 12345u128),
);
assert_eq!(asset_res, Ok(AnsAsset::new("chinachinachina", 12345u128)));
}
#[coverage_helper::test]
fn does_not_exist() {
let mock_api = MockApi::default();
let ans_host = mock_ans_host(mock_api);
let not_exist_asset_info = AssetInfo::cw20(mock_api.addr_make("address"));
test_dne(&ans_host, ¬_exist_asset_info);
}
#[coverage_helper::test]
fn array() {
let mock_api = MockApi::default();
let ans_host = mock_ans_host(mock_api);
let expected_entries = vec![
(
AssetInfo::cw20(mock_api.addr_make("boop")),
AssetEntry::new("beepboop"),
),
(
AssetInfo::cw20(mock_api.addr_make("iloveabstract")),
AssetEntry::new("robinrocks!"),
),
];
let querier = MockQuerierBuilder::default()
.with_contract_map_entries(
&ans_host.address,
REV_ASSET_ADDRESSES,
expected_entries
.iter()
.map(|(k, v)| (k, v.clone()))
.collect(),
)
.build();
let (keys, values): (Vec<_>, Vec<_>) = expected_entries.into_iter().unzip();
let res = keys.resolve(&wrap_querier(&querier), &ans_host);
assert_eq!(res, Ok(values));
}
}
}