use crate::{features::AbstractRegistryAccess, AbstractSdkError, AbstractSdkResult};
use abstract_core::{
manager::state::ACCOUNT_ID,
version_control::{state::ACCOUNT_ADDRESSES, AccountBase},
};
use cosmwasm_std::{Addr, Deps};
pub trait AccountVerification: AbstractRegistryAccess {
fn account_registry<'a>(&'a self, deps: Deps<'a>) -> AccountRegistry<Self> {
AccountRegistry { base: self, deps }
}
}
impl<T> AccountVerification for T where T: AbstractRegistryAccess {}
#[derive(Clone)]
pub struct AccountRegistry<'a, T: AccountVerification> {
base: &'a T,
deps: Deps<'a>,
}
impl<'a, T: AccountVerification> AccountRegistry<'a, T> {
pub fn assert_manager(&self, maybe_manager: &Addr) -> AbstractSdkResult<AccountBase> {
let account_id = self.account_id(maybe_manager)?;
let account_base = self.account_base(account_id)?;
if account_base.manager.ne(maybe_manager) {
Err(AbstractSdkError::NotManager(
maybe_manager.clone(),
account_id,
))
} else {
Ok(account_base)
}
}
pub fn assert_proxy(&self, maybe_proxy: &Addr) -> AbstractSdkResult<AccountBase> {
let account_id = self.account_id(maybe_proxy)?;
let account_base = self.account_base(account_id)?;
if account_base.proxy.ne(maybe_proxy) {
Err(AbstractSdkError::NotProxy(maybe_proxy.clone(), account_id))
} else {
Ok(account_base)
}
}
pub fn proxy_address(&self, account_id: u32) -> AbstractSdkResult<Addr> {
self.account_base(account_id)
.map(|account_base| account_base.proxy)
}
pub fn manager_address(&self, account_id: u32) -> AbstractSdkResult<Addr> {
self.account_base(account_id)
.map(|account_base| account_base.manager)
}
pub fn account_base(&self, account_id: u32) -> AbstractSdkResult<AccountBase> {
let maybe_account = ACCOUNT_ADDRESSES.query(
&self.deps.querier,
self.base.abstract_registry(self.deps)?,
account_id,
)?;
match maybe_account {
None => Err(AbstractSdkError::UnknownAccountId {
account_id,
version_control_addr: self.base.abstract_registry(self.deps)?,
}),
Some(account_base) => Ok(account_base),
}
}
fn account_id(&self, maybe_core_contract_addr: &Addr) -> AbstractSdkResult<u32> {
ACCOUNT_ID
.query(&self.deps.querier, maybe_core_contract_addr.clone())
.map_err(|_| AbstractSdkError::FailedToQueryAccountId {
contract_addr: maybe_core_contract_addr.clone(),
})
}
}
#[cfg(test)]
mod test {
use super::*;
use abstract_testing::*;
use cosmwasm_std::testing::*;
use abstract_testing::prelude::*;
use speculoos::prelude::*;
struct MockBinding;
impl AbstractRegistryAccess for MockBinding {
fn abstract_registry(&self, _deps: Deps) -> AbstractSdkResult<Addr> {
Ok(Addr::unchecked(TEST_VERSION_CONTROL))
}
}
mod assert_proxy {
use super::*;
#[test]
fn not_proxy_fails() {
let mut deps = mock_dependencies();
deps.querier = mocked_account_querier_builder()
.account("not_manager", "not_proxy", TEST_ACCOUNT_ID)
.account(TEST_MANAGER, TEST_PROXY, 2)
.builder()
.with_contract_item("not_proxy", ACCOUNT_ID, &2)
.build();
let binding = MockBinding;
let res = binding
.account_registry(deps.as_ref())
.assert_proxy(&Addr::unchecked("not_proxy"));
assert_that!(res)
.is_err()
.matches(|e| matches!(e, AbstractSdkError::NotProxy(..)))
.matches(|e| e.to_string().contains("not_proxy"));
}
#[test]
fn inactive_account_fails() {
let mut deps = mock_dependencies();
deps.querier = MockQuerierBuilder::default()
.with_contract_item(TEST_PROXY, ACCOUNT_ID, &TEST_ACCOUNT_ID)
.with_contract_map_key(TEST_VERSION_CONTROL, ACCOUNT_ADDRESSES, TEST_ACCOUNT_ID)
.build();
let binding = MockBinding;
let res = binding
.account_registry(deps.as_ref())
.assert_proxy(&Addr::unchecked(TEST_PROXY));
assert_that!(res)
.is_err()
.matches(|e| matches!(e, AbstractSdkError::UnknownAccountId { .. }))
.matches(|e| {
e.to_string()
.contains(format!("Unknown Account id {}", TEST_ACCOUNT_ID).as_str())
});
}
#[test]
fn returns_core() {
let mut deps = mock_dependencies();
deps.querier = MockQuerierBuilder::default()
.with_contract_item(TEST_PROXY, ACCOUNT_ID, &TEST_ACCOUNT_ID)
.with_contract_map_entry(
TEST_VERSION_CONTROL,
ACCOUNT_ADDRESSES,
(TEST_ACCOUNT_ID, test_account_base()),
)
.build();
let binding = MockBinding;
let res = binding
.account_registry(deps.as_ref())
.assert_proxy(&Addr::unchecked(TEST_PROXY));
assert_that!(res).is_ok().is_equal_to(test_account_base());
}
#[test]
fn errors_when_not_manager_of_returned_os() {
let mut deps = mock_dependencies();
deps.querier = MockQuerierBuilder::default()
.with_contract_item(TEST_PROXY, ACCOUNT_ID, &TEST_ACCOUNT_ID)
.with_contract_map_entry(
TEST_VERSION_CONTROL,
ACCOUNT_ADDRESSES,
(
TEST_ACCOUNT_ID,
AccountBase {
manager: Addr::unchecked(TEST_MANAGER),
proxy: Addr::unchecked("not_poxry"),
},
),
)
.build();
let binding = MockBinding;
let res = binding
.account_registry(deps.as_ref())
.assert_proxy(&Addr::unchecked(TEST_PROXY));
assert_that!(res)
.is_err()
.matches(|e| e.to_string().contains("not the Proxy"));
}
}
mod assert_manager {
use super::*;
#[test]
fn not_manager_fails() {
let mut deps = mock_dependencies();
deps.querier = mocked_account_querier_builder()
.account("not_manager", "not_proxy", TEST_ACCOUNT_ID)
.account(TEST_MANAGER, TEST_PROXY, 1)
.builder()
.with_contract_item("not_manager", ACCOUNT_ID, &1)
.build();
let binding = MockBinding;
let res = binding
.account_registry(deps.as_ref())
.assert_manager(&Addr::unchecked("not_manager"));
assert_that!(res)
.is_err()
.matches(|e| matches!(e, AbstractSdkError::NotManager(..)))
.matches(|e| e.to_string().contains("not_manager is not the Manager"));
}
#[test]
fn inactive_account_fails() {
let mut deps = mock_dependencies();
deps.querier = MockQuerierBuilder::default()
.with_contract_item(TEST_MANAGER, ACCOUNT_ID, &TEST_ACCOUNT_ID)
.with_contract_map_key(TEST_VERSION_CONTROL, ACCOUNT_ADDRESSES, TEST_ACCOUNT_ID)
.build();
let binding = MockBinding;
let res = binding
.account_registry(deps.as_ref())
.assert_manager(&Addr::unchecked(TEST_MANAGER));
assert_that!(res)
.is_err()
.matches(|e| matches!(e, AbstractSdkError::UnknownAccountId { .. }))
.matches(|e| {
e.to_string().contains(&format!(
"Unknown Account id {TEST_ACCOUNT_ID} on version control {TEST_VERSION_CONTROL}"
))
});
}
#[test]
fn returns_core() {
let mut deps = mock_dependencies();
deps.querier = MockQuerierBuilder::default()
.with_contract_item(TEST_MANAGER, ACCOUNT_ID, &TEST_ACCOUNT_ID)
.with_contract_map_entry(
TEST_VERSION_CONTROL,
ACCOUNT_ADDRESSES,
(TEST_ACCOUNT_ID, test_account_base()),
)
.build();
let binding = MockBinding;
let res = binding
.account_registry(deps.as_ref())
.assert_manager(&Addr::unchecked(TEST_MANAGER));
assert_that!(res).is_ok().is_equal_to(test_account_base());
}
#[test]
fn errors_when_not_manager_of_returned_os() {
let mut deps = mock_dependencies();
deps.querier = MockQuerierBuilder::default()
.with_contract_item(TEST_MANAGER, ACCOUNT_ID, &TEST_ACCOUNT_ID)
.with_contract_map_entry(
TEST_VERSION_CONTROL,
ACCOUNT_ADDRESSES,
(
TEST_ACCOUNT_ID,
AccountBase {
manager: Addr::unchecked("not_manager"),
proxy: Addr::unchecked(TEST_PROXY),
},
),
)
.build();
let binding = MockBinding;
let res = binding
.account_registry(deps.as_ref())
.assert_manager(&Addr::unchecked(TEST_MANAGER));
assert_that!(res)
.is_err()
.matches(|e| matches!(e, AbstractSdkError::NotManager(..)))
.matches(|e| e.to_string().contains("not the Manager"))
.matches(|e| e.to_string().contains(TEST_MANAGER));
}
}
}