use abstract_sdk::{
std::{
objects::AssetEntry,
proxy::{
state::{ANS_HOST, STATE},
AssetsInfoResponse, ConfigResponse,
},
},
Resolve,
};
use abstract_std::{
objects::oracle::{AccountValue, Oracle},
proxy::{
AssetsConfigResponse, BaseAssetResponse, HoldingAmountResponse, OracleAsset,
TokenValueResponse,
},
};
use cosmwasm_std::{Addr, Deps, Env, StdResult};
use cw_asset::{Asset, AssetInfo};
use crate::contract::ProxyResult;
pub fn query_oracle_asset_info(
deps: Deps,
last_asset: Option<AssetInfo>,
limit: Option<u8>,
) -> ProxyResult<AssetsInfoResponse> {
let oracle = Oracle::new();
let assets = oracle.paged_asset_info(deps, last_asset, limit)?;
Ok(AssetsInfoResponse {
assets: assets
.into_iter()
.map(|(a, (p, c))| {
(
a,
OracleAsset {
complexity: c,
price_source: p,
},
)
})
.collect(),
})
}
pub fn query_oracle_asset_config(
deps: Deps,
last_asset: Option<AssetEntry>,
limit: Option<u8>,
) -> ProxyResult<AssetsConfigResponse> {
let oracle = Oracle::new();
let assets = oracle.paged_asset_config(deps, last_asset, limit)?;
Ok(AssetsConfigResponse { assets })
}
pub fn query_config(deps: Deps) -> StdResult<ConfigResponse> {
let state = STATE.load(deps.storage)?;
let modules: Vec<Addr> = state.modules;
let resp = ConfigResponse {
modules: modules
.iter()
.map(|module| -> String { module.to_string() })
.collect(),
};
Ok(resp)
}
pub fn query_token_value(
deps: Deps,
env: Env,
asset_entry: AssetEntry,
) -> ProxyResult<TokenValueResponse> {
let oracle = Oracle::new();
let ans_host = ANS_HOST.load(deps.storage)?;
let asset_info = asset_entry.resolve(&deps.querier, &ans_host)?;
let balance = asset_info.query_balance(&deps.querier, env.contract.address)?;
let value = oracle.asset_value(deps, Asset::new(asset_info, balance))?;
Ok(TokenValueResponse { value })
}
pub fn query_total_value(deps: Deps, env: Env) -> ProxyResult<AccountValue> {
let mut oracle = Oracle::new();
oracle
.account_value(deps, &env.contract.address)
.map_err(Into::into)
}
pub fn query_base_asset(deps: Deps) -> ProxyResult<BaseAssetResponse> {
let oracle = Oracle::new();
let base_asset = oracle.base_asset(deps)?;
Ok(BaseAssetResponse { base_asset })
}
pub fn query_holding_amount(
deps: Deps,
env: Env,
identifier: AssetEntry,
) -> ProxyResult<HoldingAmountResponse> {
let ans_host = ANS_HOST.load(deps.storage)?;
let asset_info = identifier.resolve(&deps.querier, &ans_host)?;
Ok(HoldingAmountResponse {
amount: asset_info.query_balance(&deps.querier, env.contract.address)?,
})
}
#[cfg(test)]
mod test {
use super::*;
use crate::contract::{execute, instantiate, query};
use abstract_std::{
objects::price_source::{PriceSource, UncheckedPriceSource},
proxy::{AssetConfigResponse, ExecuteMsg, InstantiateMsg},
};
use abstract_testing::prelude::*;
use cosmwasm_std::{
coin,
testing::{mock_dependencies, mock_env, mock_info, MockApi, MOCK_CONTRACT_ADDR},
Decimal, DepsMut, OwnedDeps,
};
type MockDeps = OwnedDeps<MockStorage, MockApi, MockQuerier>;
pub fn base_asset() -> (AssetEntry, UncheckedPriceSource) {
(AssetEntry::from(USD), UncheckedPriceSource::None)
}
pub fn asset_as_half() -> (AssetEntry, UncheckedPriceSource) {
let asset = AssetEntry::from(EUR);
let price_source = UncheckedPriceSource::ValueAs {
asset: AssetEntry::new(USD),
multiplier: Decimal::percent(50),
};
(asset, price_source)
}
fn mock_init(deps: DepsMut) {
let info = mock_info(OWNER, &[]);
let msg = InstantiateMsg {
account_id: TEST_ACCOUNT_ID,
ans_host_address: TEST_ANS_HOST.to_string(),
manager_addr: TEST_MANAGER.to_string(),
base_asset: None,
};
let _res = instantiate(deps, mock_env(), info, msg).unwrap();
}
pub fn execute_as_admin(deps: &mut MockDeps, msg: ExecuteMsg) -> ProxyResult {
let info = mock_info(TEST_MANAGER, &[]);
execute(deps.as_mut(), mock_env(), info, msg)
}
#[test]
fn query_base_asset() {
let mut deps = mock_dependencies();
deps.querier = MockAnsHost::new().with_defaults().to_querier();
mock_init(deps.as_mut());
execute_as_admin(
&mut deps,
ExecuteMsg::UpdateAssets {
to_add: vec![base_asset()],
to_remove: vec![],
},
)
.unwrap();
let base_asset: BaseAssetResponse = from_json(
query(
deps.as_ref(),
mock_env(),
abstract_std::proxy::QueryMsg::BaseAsset {},
)
.unwrap(),
)
.unwrap();
assert_eq!(
base_asset,
BaseAssetResponse {
base_asset: cw_asset::AssetInfoBase::Native(USD.to_string())
}
);
}
#[test]
fn query_config() {
let mut deps = mock_dependencies();
deps.querier = MockAnsHost::new().with_defaults().to_querier();
mock_init(deps.as_mut());
execute_as_admin(
&mut deps,
ExecuteMsg::AddModules {
modules: vec!["test_module".to_string()],
},
)
.unwrap();
let config: ConfigResponse = from_json(
query(
deps.as_ref(),
mock_env(),
abstract_std::proxy::QueryMsg::Config {},
)
.unwrap(),
)
.unwrap();
assert_eq!(
config,
ConfigResponse {
modules: vec!["manager_address".to_string(), "test_module".to_string()],
}
);
}
#[test]
fn query_oracle() {
let mut deps = mock_dependencies();
deps.querier = MockAnsHost::new().with_defaults().to_querier();
mock_init(deps.as_mut());
execute_as_admin(
&mut deps,
ExecuteMsg::UpdateAssets {
to_add: vec![base_asset()],
to_remove: vec![],
},
)
.unwrap();
deps.querier
.update_balance(MOCK_CONTRACT_ADDR, vec![coin(1000, USD)]);
let holding_amount: HoldingAmountResponse = from_json(
query(
deps.as_ref(),
mock_env(),
abstract_std::proxy::QueryMsg::HoldingAmount {
identifier: AssetEntry::from(USD),
},
)
.unwrap(),
)
.unwrap();
assert_eq!(holding_amount.amount.u128(), 1000);
let account_value: AccountValue = from_json(
query(
deps.as_ref(),
mock_env(),
abstract_std::proxy::QueryMsg::TotalValue {},
)
.unwrap(),
)
.unwrap();
assert_eq!(
account_value.total_value,
Asset::new(AssetInfo::native(USD), 1000u128)
);
let token_value: TokenValueResponse = from_json(
query(
deps.as_ref(),
mock_env(),
abstract_std::proxy::QueryMsg::TokenValue {
identifier: AssetEntry::from(USD),
},
)
.unwrap(),
)
.unwrap();
assert_eq!(token_value.value.u128(), 1000u128);
let asset_config: AssetConfigResponse = from_json(
query(
deps.as_ref(),
mock_env(),
abstract_std::proxy::QueryMsg::AssetConfig {
identifier: AssetEntry::from(USD),
},
)
.unwrap(),
)
.unwrap();
assert_eq!(asset_config.price_source, UncheckedPriceSource::None);
}
#[test]
fn query_asset_configs() {
let mut deps = mock_dependencies();
deps.querier = MockAnsHost::new().with_defaults().to_querier();
mock_init(deps.as_mut());
execute_as_admin(
&mut deps,
ExecuteMsg::UpdateAssets {
to_add: vec![base_asset(), asset_as_half()],
to_remove: vec![],
},
)
.unwrap();
let assets: AssetsConfigResponse = from_json(
query(
deps.as_ref(),
mock_env(),
abstract_std::proxy::QueryMsg::AssetsConfig {
start_after: None,
limit: None,
},
)
.unwrap(),
)
.unwrap();
assert_eq!(
assets,
AssetsConfigResponse {
assets: vec![asset_as_half(), base_asset()]
}
);
}
#[test]
fn query_asset_infos() {
let mut deps = mock_dependencies();
deps.querier = MockAnsHost::new().with_defaults().to_querier();
mock_init(deps.as_mut());
execute_as_admin(
&mut deps,
ExecuteMsg::UpdateAssets {
to_add: vec![base_asset(), asset_as_half()],
to_remove: vec![],
},
)
.unwrap();
let assets: AssetsInfoResponse = from_json(
query(
deps.as_ref(),
mock_env(),
abstract_std::proxy::QueryMsg::AssetsInfo {
start_after: None,
limit: None,
},
)
.unwrap(),
)
.unwrap();
assert_eq!(
assets,
AssetsInfoResponse {
assets: vec![
(
AssetInfo::native(EUR),
OracleAsset {
complexity: 1,
price_source: PriceSource::ValueAs {
asset: AssetInfo::native(USD),
multiplier: Decimal::percent(50),
},
}
),
(
AssetInfo::native(USD),
OracleAsset {
complexity: 0,
price_source: PriceSource::None,
}
),
]
}
);
}
}