1use abstract_sdk::{
2 base::{ExecuteEndpoint, Handler, IbcCallbackEndpoint, ModuleIbcEndpoint},
3 features::ModuleIdentification,
4 AbstractResponse, AccountVerification,
5};
6use abstract_std::{
7 account::state::ACCOUNT_MODULES,
8 adapter::{AdapterBaseMsg, AdapterExecuteMsg, AdapterRequestMsg, BaseExecuteMsg, ExecuteMsg},
9 objects::ownership::nested_admin::query_top_level_owner_addr,
10};
11use cosmwasm_std::{Addr, Deps, DepsMut, Env, MessageInfo, QuerierWrapper, Response, StdResult};
12use schemars::JsonSchema;
13use serde::Serialize;
14
15use crate::{
16 error::AdapterError,
17 state::{AdapterContract, ContractError, MAXIMUM_AUTHORIZED_ADDRESSES},
18 AdapterResult,
19};
20
21impl<
22 Error: ContractError,
23 CustomInitMsg,
24 CustomExecMsg: Serialize + JsonSchema + AdapterExecuteMsg,
25 CustomQueryMsg,
26 SudoMsg,
27 > ExecuteEndpoint
28 for AdapterContract<Error, CustomInitMsg, CustomExecMsg, CustomQueryMsg, SudoMsg>
29{
30 type ExecuteMsg = ExecuteMsg<CustomExecMsg>;
31
32 fn execute(
33 mut self,
34 deps: DepsMut,
35 env: Env,
36 info: MessageInfo,
37 msg: Self::ExecuteMsg,
38 ) -> Result<Response, Error> {
39 match msg {
40 ExecuteMsg::Module(request) => self.handle_app_msg(deps, env, info, request),
41 ExecuteMsg::Base(exec_msg) => self
42 .base_execute(deps, env, info, exec_msg)
43 .map_err(From::from),
44 ExecuteMsg::IbcCallback(msg) => self.ibc_callback(deps, env, info, msg),
45 ExecuteMsg::ModuleIbc(msg) => self.module_ibc(deps, env, info, msg),
46 }
47 }
48}
49
50fn is_top_level_owner(querier: &QuerierWrapper, account: Addr, sender: &Addr) -> StdResult<bool> {
51 let owner = query_top_level_owner_addr(querier, account)?;
52 Ok(owner == sender)
53}
54
55impl<Error: ContractError, CustomInitMsg, CustomExecMsg, CustomQueryMsg, SudoMsg>
57 AdapterContract<Error, CustomInitMsg, CustomExecMsg, CustomQueryMsg, SudoMsg>
58{
59 fn base_execute(
60 &mut self,
61 deps: DepsMut,
62 env: Env,
63 info: MessageInfo,
64 message: BaseExecuteMsg,
65 ) -> AdapterResult {
66 let BaseExecuteMsg {
67 account_address,
68 msg,
69 } = message;
70 let account_registry = self.account_registry(deps.as_ref())?;
71 let account = account_registry
72 .assert_is_account_admin(&env, &info.sender)
73 .map_err(|_| AdapterError::UnauthorizedAdapterRequest {
74 adapter: self.module_id().to_string(),
75 sender: info.sender.to_string(),
76 })
77 .or_else(|e| {
78 match account_address {
80 Some(requested_account) => {
81 let account_address = deps.api.addr_validate(&requested_account)?;
82 let account = account_registry.assert_is_account(&account_address)?;
83 if is_top_level_owner(&deps.querier, account.addr().clone(), &info.sender)
84 .unwrap_or(false)
85 {
86 Ok(account)
87 } else {
88 Err(AdapterError::UnauthorizedAdapterRequest {
89 adapter: self.module_id().to_string(),
90 sender: info.sender.to_string(),
91 })
92 }
93 }
94 None => Err(e),
96 }
97 })?;
98
99 self.target_account = Some(account);
100 match msg {
101 AdapterBaseMsg::UpdateAuthorizedAddresses { to_add, to_remove } => {
102 self.update_authorized_addresses(deps, info, to_add, to_remove)
103 }
104 }
105 }
106
107 fn handle_app_msg(
112 mut self,
113 deps: DepsMut,
114 env: Env,
115 info: MessageInfo,
116 request: AdapterRequestMsg<CustomExecMsg>,
117 ) -> Result<Response, Error> {
118 let sender = &info.sender;
119 let unauthorized_sender = || AdapterError::UnauthorizedAddressAdapterRequest {
120 adapter: self.module_id().to_string(),
121 sender: sender.to_string(),
122 };
123
124 let account_registry = self.account_registry(deps.as_ref())?;
125
126 let account = match request.account_address {
127 Some(requested_account) => {
129 let account_address = deps.api.addr_validate(&requested_account)?;
130 let requested_core = account_registry.assert_is_account(&account_address)?;
131
132 if requested_core.addr() == sender {
133 requested_core
136 } else {
137 let authorized = self
139 .authorized_addresses
140 .load(deps.storage, account_address)
141 .unwrap_or_default();
142 if authorized.contains(sender)
143 || is_top_level_owner(&deps.querier, requested_core.addr().clone(), sender)
144 .unwrap_or(false)
145 {
146 requested_core
149 } else {
150 return Err(unauthorized_sender().into());
152 }
153 }
154 }
155 None => account_registry
156 .assert_is_account(sender)
157 .map_err(|_| unauthorized_sender())?,
158 };
159 self.target_account = Some(account);
160 self.execute_handler()?(deps, env, info, self, request.request)
161 }
162
163 fn update_authorized_addresses(
165 &self,
166 deps: DepsMut,
167 info: MessageInfo,
168 to_add: Vec<String>,
169 to_remove: Vec<String>,
170 ) -> AdapterResult {
171 let account = self.target_account.as_ref().unwrap();
172 let account_addr = account.addr().clone();
173
174 let mut authorized_addrs = self
175 .authorized_addresses
176 .may_load(deps.storage, account_addr.clone())?
177 .unwrap_or_default();
178
179 for authorized in to_add {
181 let authorized_addr = get_addr_from_module_id_or_addr(
183 deps.as_ref(),
184 info.sender.clone(),
185 authorized.clone(),
186 )?;
187
188 if authorized_addrs.contains(&authorized_addr) {
189 return Err(AdapterError::AuthorizedAddressOrModuleIdAlreadyPresent {
190 addr_or_module_id: authorized,
191 });
192 } else {
193 authorized_addrs.push(authorized_addr);
194 }
195 }
196
197 for deauthorized in to_remove {
199 let deauthorized_addr = get_addr_from_module_id_or_addr(
200 deps.as_ref(),
201 info.sender.clone(),
202 deauthorized.clone(),
203 )?;
204 if !authorized_addrs.contains(&deauthorized_addr) {
205 return Err(AdapterError::AuthorizedAddressOrModuleIdNotPresent {
206 addr_or_module_id: deauthorized,
207 });
208 } else {
209 authorized_addrs.retain(|addr| deauthorized_addr.ne(addr));
210 }
211 }
212
213 if authorized_addrs.len() > MAXIMUM_AUTHORIZED_ADDRESSES as usize {
214 return Err(AdapterError::TooManyAuthorizedAddresses {
215 max: MAXIMUM_AUTHORIZED_ADDRESSES,
216 });
217 }
218
219 self.authorized_addresses
220 .save(deps.storage, account_addr.clone(), &authorized_addrs)?;
221 Ok(self.custom_response(
222 "update_authorized_addresses",
223 vec![("account", account_addr.as_str())],
224 ))
225 }
226}
227
228fn get_addr_from_module_id_or_addr(
231 deps: Deps,
232 account: Addr,
233 addr_or_module_id: String,
234) -> Result<Addr, AdapterError> {
235 if let Ok(Some(addr)) = ACCOUNT_MODULES.query(&deps.querier, account, &addr_or_module_id) {
237 Ok(addr)
239 } else if let Ok(addr) = deps.api.addr_validate(addr_or_module_id.as_str()) {
240 Ok(addr)
242 } else {
243 Err(AdapterError::AuthorizedAddressOrModuleIdNotValid { addr_or_module_id })
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use abstract_std::adapter;
250 use abstract_testing::prelude::*;
251 use cosmwasm_std::{testing::*, Addr, Storage};
252
253 use super::*;
254 use crate::mock::{mock_init, AdapterMockResult, MockError, MockExecMsg, MOCK_ADAPTER};
255
256 fn execute_as(
257 deps: &mut MockDeps,
258 sender: &Addr,
259 msg: ExecuteMsg<MockExecMsg>,
260 ) -> Result<Response, MockError> {
261 let env = mock_env_validated(deps.api);
262 MOCK_ADAPTER.execute(deps.as_mut(), env, message_info(sender, &[]), msg)
263 }
264
265 fn base_execute_as(
266 deps: &mut MockDeps,
267 sender: &Addr,
268 msg: BaseExecuteMsg,
269 ) -> Result<Response, MockError> {
270 execute_as(deps, sender, adapter::ExecuteMsg::Base(msg))
271 }
272
273 mod update_authorized_addresses {
274 use super::*;
275 use crate::mock::TEST_AUTHORIZED_ADDR;
276
277 fn load_test_account_authorized_addresses(
278 storage: &dyn Storage,
279 account_addr: &Addr,
280 ) -> Vec<Addr> {
281 MOCK_ADAPTER
282 .authorized_addresses
283 .load(storage, account_addr.clone())
284 .unwrap()
285 }
286
287 #[coverage_helper::test]
288 fn authorize_address() -> AdapterMockResult {
289 let mut deps = mock_dependencies();
290 let account = test_account(deps.api);
291 deps.querier = abstract_mock_querier_builder(deps.api)
292 .account(&account, TEST_ACCOUNT_ID)
293 .set_account_admin_call_to(&account)
294 .build();
295
296 mock_init(&mut deps)?;
297
298 let msg = BaseExecuteMsg {
299 msg: AdapterBaseMsg::UpdateAuthorizedAddresses {
300 to_add: vec![deps.api.addr_make(TEST_AUTHORIZED_ADDR).to_string()],
301 to_remove: vec![],
302 },
303 account_address: None,
304 };
305
306 base_execute_as(&mut deps, account.addr(), msg)?;
307
308 let api = MOCK_ADAPTER;
309 assert!(!api.authorized_addresses.is_empty(&deps.storage));
310
311 let test_account_authorized_addrs =
312 load_test_account_authorized_addresses(&deps.storage, account.addr());
313
314 assert_eq!(test_account_authorized_addrs.len(), 1);
315 assert!(
316 test_account_authorized_addrs.contains(&deps.api.addr_make(TEST_AUTHORIZED_ADDR))
317 );
318 Ok(())
319 }
320
321 #[coverage_helper::test]
322 fn revoke_address_authorization() -> AdapterMockResult {
323 let mut deps = mock_dependencies();
324 let account = test_account(deps.api);
325 deps.querier = abstract_mock_querier_builder(deps.api)
326 .account(&account, TEST_ACCOUNT_ID)
327 .set_account_admin_call_to(&account)
328 .build();
329
330 mock_init(&mut deps)?;
331
332 let _api = MOCK_ADAPTER;
333 let msg = BaseExecuteMsg {
334 account_address: None,
335 msg: AdapterBaseMsg::UpdateAuthorizedAddresses {
336 to_add: vec![deps.api.addr_make(TEST_AUTHORIZED_ADDR).to_string()],
337 to_remove: vec![],
338 },
339 };
340
341 base_execute_as(&mut deps, account.addr(), msg)?;
342
343 let authorized_addrs =
344 load_test_account_authorized_addresses(&deps.storage, account.addr());
345 assert_eq!(authorized_addrs.len(), 1);
346
347 let msg = BaseExecuteMsg {
348 account_address: None,
349 msg: AdapterBaseMsg::UpdateAuthorizedAddresses {
350 to_add: vec![],
351 to_remove: vec![deps.api.addr_make(TEST_AUTHORIZED_ADDR).to_string()],
352 },
353 };
354
355 base_execute_as(&mut deps, account.addr(), msg)?;
356 let authorized_addrs =
357 load_test_account_authorized_addresses(&deps.storage, account.addr());
358 assert!(authorized_addrs.is_empty());
359 Ok(())
360 }
361
362 #[coverage_helper::test]
363 fn add_existing_authorized_address() -> AdapterMockResult {
364 let mut deps = mock_dependencies();
365 let account = test_account(deps.api);
366 deps.querier = abstract_mock_querier_builder(deps.api)
367 .account(&account, TEST_ACCOUNT_ID)
368 .set_account_admin_call_to(&account)
369 .build();
370
371 mock_init(&mut deps)?;
372
373 let msg = BaseExecuteMsg {
374 account_address: None,
375 msg: AdapterBaseMsg::UpdateAuthorizedAddresses {
376 to_add: vec![deps.api.addr_make(TEST_AUTHORIZED_ADDR).to_string()],
377 to_remove: vec![],
378 },
379 };
380
381 base_execute_as(&mut deps, account.addr(), msg)?;
382
383 let msg = BaseExecuteMsg {
384 account_address: None,
385 msg: AdapterBaseMsg::UpdateAuthorizedAddresses {
386 to_add: vec![deps.api.addr_make(TEST_AUTHORIZED_ADDR).to_string()],
387 to_remove: vec![],
388 },
389 };
390
391 let res = base_execute_as(&mut deps, account.addr(), msg);
392
393 assert!(matches!(
394 res,
395 Err(MockError::Adapter(
396 AdapterError::AuthorizedAddressOrModuleIdAlreadyPresent {
397 addr_or_module_id: _test_authorized_address_string
398 }
399 ))
400 ));
401
402 Ok(())
403 }
404
405 #[coverage_helper::test]
406 fn add_module_id_authorized_address() -> AdapterMockResult {
407 let mut deps = mock_dependencies();
408 let account = test_account(deps.api);
409 deps.querier = abstract_mock_querier_builder(deps.api)
410 .account(&account, TEST_ACCOUNT_ID)
411 .set_account_admin_call_to(&account)
412 .build();
413 let abstr = AbstractMockAddrs::new(deps.api);
414
415 mock_init(&mut deps)?;
416
417 let _api = MOCK_ADAPTER;
418 let msg = BaseExecuteMsg {
419 account_address: None,
420 msg: AdapterBaseMsg::UpdateAuthorizedAddresses {
421 to_add: vec![TEST_MODULE_ID.into()],
422 to_remove: vec![],
423 },
424 };
425
426 base_execute_as(&mut deps, account.addr(), msg)?;
427
428 let authorized_addrs =
429 load_test_account_authorized_addresses(&deps.storage, account.addr());
430 assert_eq!(authorized_addrs.len(), 1);
431 assert_eq!(
432 authorized_addrs[0].to_string(),
433 abstr.module_address.to_string()
434 );
435
436 Ok(())
437 }
438
439 #[coverage_helper::test]
440 fn remove_authorized_address_dne() -> AdapterMockResult {
441 let mut deps = mock_dependencies();
442 let account = test_account(deps.api);
443 deps.querier = abstract_mock_querier_builder(deps.api)
444 .account(&account, TEST_ACCOUNT_ID)
445 .set_account_admin_call_to(&account)
446 .build();
447
448 mock_init(&mut deps)?;
449 let test_authorized_address_string =
450 deps.api.addr_make(TEST_AUTHORIZED_ADDR).to_string();
451
452 let _api = MOCK_ADAPTER;
453 let msg = BaseExecuteMsg {
454 account_address: None,
455 msg: AdapterBaseMsg::UpdateAuthorizedAddresses {
456 to_add: vec![],
457 to_remove: vec![test_authorized_address_string.clone()],
458 },
459 };
460
461 let res = base_execute_as(&mut deps, account.addr(), msg);
462
463 assert_eq!(
464 res,
465 Err(MockError::Adapter(
466 AdapterError::AuthorizedAddressOrModuleIdNotPresent {
467 addr_or_module_id: test_authorized_address_string
468 }
469 ))
470 );
471 Ok(())
472 }
473 }
474
475 mod execute_app {
476 use super::*;
477
478 use crate::mock::TEST_AUTHORIZED_ADDR;
479 use abstract_std::{
480 objects::{account::AccountTrace, AccountId},
481 registry::Account,
482 };
483 use cosmwasm_std::OwnedDeps;
484
485 fn setup_with_authorized_addresses(
491 deps: &mut OwnedDeps<MockStorage, MockApi, MockQuerier>,
492 authorized: Vec<&str>,
493 ) {
494 mock_init(deps).unwrap();
495
496 let msg = BaseExecuteMsg {
497 account_address: None,
498 msg: AdapterBaseMsg::UpdateAuthorizedAddresses {
499 to_add: authorized
500 .into_iter()
501 .map(|addr| deps.api.addr_make(addr).to_string())
502 .collect(),
503 to_remove: vec![],
504 },
505 };
506
507 let account = test_account(deps.api);
508 base_execute_as(deps, account.addr(), msg).unwrap();
509 }
510
511 #[coverage_helper::test]
512 fn unauthorized_addresses_are_unauthorized() {
513 let mut deps = mock_dependencies();
514 deps.querier = MockQuerierBuilder::new(deps.api)
515 .account(&test_account(deps.api), TEST_ACCOUNT_ID)
516 .set_account_admin_call_to(&test_account(deps.api))
517 .build();
518
519 setup_with_authorized_addresses(&mut deps, vec![]);
520
521 let msg = ExecuteMsg::Module(AdapterRequestMsg {
522 account_address: None,
523 request: MockExecMsg {},
524 });
525
526 let unauthorized = deps.api.addr_make("someoone");
527 let res = execute_as(&mut deps, &unauthorized, msg);
528
529 assert_unauthorized(res);
530 }
531
532 fn assert_unauthorized(res: Result<Response, MockError>) {
533 assert!(matches!(
534 res,
535 Err(MockError::Adapter(
536 AdapterError::UnauthorizedAddressAdapterRequest {
537 sender: _unauthorized,
538 ..
539 }
540 ))
541 ));
542 }
543
544 #[coverage_helper::test]
545 fn executing_as_account_account_is_allowed() {
546 let mut deps = mock_dependencies();
547 let account = test_account(deps.api);
548 deps.querier = MockQuerierBuilder::new(deps.api)
549 .account(&account, TEST_ACCOUNT_ID)
550 .set_account_admin_call_to(&account)
551 .build();
552
553 setup_with_authorized_addresses(&mut deps, vec![]);
554
555 let msg = ExecuteMsg::Module(AdapterRequestMsg {
556 account_address: None,
557 request: MockExecMsg {},
558 });
559
560 let res = execute_as(&mut deps, account.addr(), msg);
561
562 assert!(res.is_ok());
563 }
564
565 #[coverage_helper::test]
566 fn executing_as_authorized_address_not_allowed_without_account() {
567 let mut deps = mock_dependencies();
568 deps.querier = MockQuerierBuilder::new(deps.api)
569 .account(&test_account(deps.api), TEST_ACCOUNT_ID)
570 .set_account_admin_call_to(&test_account(deps.api))
571 .build();
572
573 setup_with_authorized_addresses(&mut deps, vec![TEST_AUTHORIZED_ADDR]);
574
575 let msg = ExecuteMsg::Module(AdapterRequestMsg {
576 account_address: None,
577 request: MockExecMsg {},
578 });
579
580 let authorized = deps.api.addr_make(TEST_AUTHORIZED_ADDR);
581 let res = execute_as(&mut deps, &authorized, msg);
582
583 assert_unauthorized(res);
584 }
585
586 #[coverage_helper::test]
587 fn executing_as_authorized_address_is_allowed_via_account() {
588 let mut deps = mock_dependencies();
589 let account = test_account(deps.api);
590 deps.querier = MockQuerierBuilder::new(deps.api)
591 .account(&account, TEST_ACCOUNT_ID)
592 .set_account_admin_call_to(&account)
593 .build();
594
595 setup_with_authorized_addresses(&mut deps, vec![TEST_AUTHORIZED_ADDR]);
596
597 let msg = ExecuteMsg::Module(AdapterRequestMsg {
598 account_address: Some(account.addr().to_string()),
599 request: MockExecMsg {},
600 });
601
602 let authorized = deps.api.addr_make(TEST_AUTHORIZED_ADDR);
603 let res = execute_as(&mut deps, &authorized, msg);
604
605 assert!(res.is_ok());
606 }
607
608 #[coverage_helper::test]
609 fn executing_as_authorized_address_on_diff_account_should_err() {
610 let mut deps = mock_dependencies();
611 let account = test_account(deps.api);
612 let another_account = Account::new(deps.api.addr_make("some_other_account"));
613 deps.querier = MockQuerierBuilder::new(deps.api)
614 .account(&account, TEST_ACCOUNT_ID)
615 .account(
616 &another_account,
617 AccountId::new(69420u32, AccountTrace::Local).unwrap(),
618 )
619 .set_account_admin_call_to(&account)
620 .build();
621
622 setup_with_authorized_addresses(&mut deps, vec![TEST_AUTHORIZED_ADDR]);
623
624 let msg = ExecuteMsg::Module(AdapterRequestMsg {
625 account_address: Some(another_account.addr().to_string()),
626 request: MockExecMsg {},
627 });
628
629 let authorized = deps.api.addr_make(TEST_AUTHORIZED_ADDR);
630 let res = execute_as(&mut deps, &authorized, msg);
631
632 assert_unauthorized(res);
633 }
634 }
635}