abstract_sdk/apis/
verify.rs1use abstract_std::{
4 objects::ownership::nested_admin::assert_account_calling_to_as_admin_is_self,
5 objects::{registry::RegistryContract, AccountId},
6 registry::Account,
7};
8use cosmwasm_std::{Addr, Deps, Env};
9
10use super::AbstractApi;
11use crate::{
12 cw_helpers::ApiQuery,
13 features::{AbstractRegistryAccess, ModuleIdentification},
14 AbstractSdkError, AbstractSdkResult,
15};
16
17pub trait AccountVerification: AbstractRegistryAccess + ModuleIdentification {
19 fn account_registry<'a>(
37 &'a self,
38 deps: Deps<'a>,
39 ) -> AbstractSdkResult<AccountRegistry<'a, Self>> {
40 let vc = self.abstract_registry(deps)?;
41 Ok(AccountRegistry {
42 base: self,
43 deps,
44 registry: vc,
45 })
46 }
47}
48
49impl<T> AccountVerification for T where T: AbstractRegistryAccess + ModuleIdentification {}
50
51impl<T: AccountVerification> AbstractApi<T> for AccountRegistry<'_, T> {
52 const API_ID: &'static str = "AccountRegistry";
53
54 fn base(&self) -> &T {
55 self.base
56 }
57 fn deps(&self) -> Deps {
58 self.deps
59 }
60}
61
62#[derive(Clone)]
80pub struct AccountRegistry<'a, T: AccountVerification> {
81 base: &'a T,
82 deps: Deps<'a>,
83 registry: RegistryContract,
84}
85
86impl<T: AccountVerification> AccountRegistry<'_, T> {
87 pub fn assert_is_account(&self, maybe_account: &Addr) -> AbstractSdkResult<Account> {
89 self.registry
90 .assert_account(maybe_account, &self.deps.querier)
91 .map_err(|error| self.wrap_query_error(error))
92 }
93
94 pub fn account(&self, account_id: &AccountId) -> AbstractSdkResult<Account> {
96 self.registry
97 .account(account_id, &self.deps.querier)
98 .map_err(|error| self.wrap_query_error(error))
99 }
100
101 pub fn account_id(&self, maybe_account_contract_addr: &Addr) -> AbstractSdkResult<AccountId> {
103 self.registry
104 .account_id(maybe_account_contract_addr, &self.deps.querier)
105 .map_err(|error| self.wrap_query_error(error))
106 }
107
108 pub fn namespace_registration_fee(&self) -> AbstractSdkResult<Option<cosmwasm_std::Coin>> {
110 self.registry
111 .namespace_registration_fee(&self.deps.querier)
112 .map_err(|error| self.wrap_query_error(error))
113 }
114
115 pub fn assert_is_account_admin(
117 &self,
118 env: &Env,
119 maybe_account: &Addr,
120 ) -> AbstractSdkResult<Account> {
121 let account = self.assert_is_account(maybe_account)?;
122
123 if !assert_account_calling_to_as_admin_is_self(&self.deps.querier, env, maybe_account) {
124 return Err(AbstractSdkError::OnlyAdmin {});
125 }
126 Ok(account)
127 }
128}
129
130#[cfg(test)]
131mod test {
132 #![allow(clippy::needless_borrows_for_generic_args)]
133 use super::*;
134
135 use crate::{apis::traits::test::abstract_api_test, AbstractSdkError};
136 use abstract_std::{
137 account::state::ACCOUNT_ID,
138 objects::{account::AccountTrace, module::ModuleId, registry::RegistryError},
139 registry::{self, state::ACCOUNT_ADDRESSES},
140 };
141 use abstract_testing::prelude::*;
142 use cosmwasm_std::{testing::*, Coin};
143
144 struct MockBinding {}
145
146 impl AbstractRegistryAccess for MockBinding {
147 fn abstract_registry(&self, deps: Deps) -> AbstractSdkResult<RegistryContract> {
148 RegistryContract::new(deps, 1).map_err(Into::into)
149 }
150 }
151
152 impl ModuleIdentification for MockBinding {
153 fn module_id(&self) -> ModuleId<'static> {
154 ModuleId::from(TEST_MODULE_ID)
155 }
156 }
157
158 pub const SECOND_TEST_ACCOUNT_ID: AccountId = AccountId::const_new(2, AccountTrace::Local);
159
160 mod assert_account {
161
162 use super::*;
163
164 #[coverage_helper::test]
165 fn not_account_fails() {
166 let mut deps = mock_dependencies();
167 let not_account = Account::new(deps.api.addr_make("not_account"));
168 let base = test_account(deps.api);
169
170 deps.querier = MockQuerierBuilder::new(deps.api)
171 .account(¬_account, TEST_ACCOUNT_ID)
173 .account(&base, SECOND_TEST_ACCOUNT_ID)
175 .with_contract_item(not_account.addr(), ACCOUNT_ID, &SECOND_TEST_ACCOUNT_ID)
176 .build();
177
178 let binding = MockBinding {};
179
180 let res = binding
181 .account_registry(deps.as_ref())
182 .unwrap()
183 .assert_is_account(not_account.addr());
184
185 let expected_err = AbstractSdkError::ApiQuery {
186 api: AccountRegistry::<MockBinding>::api_id(),
187 module_id: binding.module_id().to_owned(),
188 error: Box::new(
189 RegistryError::NotAccount(not_account.addr().clone(), SECOND_TEST_ACCOUNT_ID)
190 .into(),
191 ),
192 };
193 assert_eq!(res.unwrap_err(), expected_err);
194 }
195
196 #[coverage_helper::test]
197 fn inactive_account_fails() {
198 let mut deps = mock_dependencies();
199 let abstr = AbstractMockAddrs::new(deps.api);
200
201 deps.querier = MockQuerierBuilder::default()
202 .with_contract_item(abstr.account.addr(), ACCOUNT_ID, &TEST_ACCOUNT_ID)
203 .with_contract_map_key(&abstr.registry, ACCOUNT_ADDRESSES, &TEST_ACCOUNT_ID)
204 .build();
205
206 let binding = MockBinding {};
207
208 let res = binding
209 .account_registry(deps.as_ref())
210 .unwrap()
211 .assert_is_account(abstr.account.addr());
212
213 let expected_err = AbstractSdkError::ApiQuery {
214 api: AccountRegistry::<MockBinding>::api_id(),
215 module_id: binding.module_id().to_owned(),
216 error: Box::new(
217 RegistryError::UnknownAccountId {
218 account_id: TEST_ACCOUNT_ID,
219 registry_addr: abstr.registry,
220 }
221 .into(),
222 ),
223 };
224 assert_eq!(res.unwrap_err(), expected_err);
225 }
226
227 #[coverage_helper::test]
228 fn returns_account() {
229 let mut deps = mock_dependencies();
230 let account = test_account(deps.api);
231
232 deps.querier = abstract_mock_querier_builder(deps.api)
233 .account(&account, TEST_ACCOUNT_ID)
234 .build();
235
236 let binding = MockBinding {};
237
238 let registry = binding.account_registry(deps.as_ref()).unwrap();
239 let res = registry.assert_is_account(account.addr());
240
241 assert_eq!(res, Ok(account.clone()));
242
243 let account_id = registry.account_id(account.addr());
244 assert_eq!(account_id, Ok(TEST_ACCOUNT_ID));
245 }
246 }
247
248 #[coverage_helper::test]
249 fn namespace_fee() {
250 let mut deps = mock_dependencies();
251
252 deps.querier = abstract_mock_querier(deps.api);
253
254 let binding = MockBinding {};
255
256 {
258 let registry = binding.account_registry(deps.as_ref()).unwrap();
259 let res = registry.namespace_registration_fee();
260
261 assert_eq!(res, Ok(None));
262 }
263
264 let abstr = AbstractMockAddrs::new(deps.api);
265 deps.querier = abstract_mock_querier_builder(deps.api)
266 .with_contract_item(
267 &abstr.registry,
268 registry::state::CONFIG,
269 ®istry::Config {
270 security_enabled: false,
271 namespace_registration_fee: Some(Coin::new(42_u128, "foo")),
272 },
273 )
274 .build();
275
276 let registry = binding.account_registry(deps.as_ref()).unwrap();
277 let res = registry.namespace_registration_fee();
278
279 assert_eq!(res, Ok(Some(Coin::new(42_u128, "foo"))));
280 }
281
282 #[coverage_helper::test]
283 fn abstract_api() {
284 let mut deps = mock_dependencies();
285 deps.querier = abstract_mock_querier(deps.api);
286 let module = MockBinding {};
287
288 let account_registry = module.account_registry(deps.as_ref()).unwrap();
289
290 abstract_api_test(account_registry);
291 }
292}