abstract_core/objects/
nested_admin.rs1use cosmwasm_std::{
2 attr, Addr, CustomQuery, Deps, DepsMut, MessageInfo, QuerierWrapper, Response, StdError,
3 StdResult,
4};
5use cw_controllers::{Admin, AdminError, AdminResponse};
6use schemars::JsonSchema;
7
8use crate::{
9 manager::{self, state::AccountInfo},
10 objects::gov_type::GovernanceDetails,
11};
12
13pub const MAX_ADMIN_RECURSION: usize = 2;
15
16pub struct NestedAdmin<'a>(Admin<'a>);
21
22impl<'a> NestedAdmin<'a> {
23 pub const fn new(namespace: &'a str) -> Self {
24 NestedAdmin(Admin::new(namespace))
25 }
26
27 pub fn set<Q: CustomQuery>(&self, deps: DepsMut<Q>, admin: Option<Addr>) -> StdResult<()> {
28 self.0.set(deps, admin)
29 }
30
31 pub fn get<Q: CustomQuery>(&self, deps: Deps<Q>) -> StdResult<Option<Addr>> {
32 self.0.get(deps)
33 }
34
35 pub fn is_admin<Q: CustomQuery>(&self, deps: Deps<Q>, caller: &Addr) -> StdResult<bool> {
36 match self.0.get(deps)? {
37 Some(owner) => {
38 if caller == owner {
40 Ok(true)
41 } else {
42 Ok(query_top_level_owner(&deps.querier, owner)
44 .map(|owner| owner == caller)
45 .unwrap_or(false))
46 }
47 }
48 None => Ok(false),
49 }
50 }
51
52 pub fn assert_admin<Q: CustomQuery>(
53 &self,
54 deps: Deps<Q>,
55 caller: &Addr,
56 ) -> Result<(), AdminError> {
57 if !self.is_admin(deps, caller)? {
58 Err(AdminError::NotAdmin {})
59 } else {
60 Ok(())
61 }
62 }
63
64 pub fn execute_update_admin<C, Q: CustomQuery>(
65 &self,
66 deps: DepsMut<Q>,
67 info: MessageInfo,
68 new_admin: Option<Addr>,
69 ) -> Result<Response<C>, AdminError>
70 where
71 C: Clone + core::fmt::Debug + PartialEq + JsonSchema,
72 {
73 self.assert_admin(deps.as_ref(), &info.sender)?;
74
75 let admin_str = match new_admin.as_ref() {
76 Some(admin) => admin.to_string(),
77 None => "None".to_string(),
78 };
79 let attributes = vec![
80 attr("action", "update_admin"),
81 attr("admin", admin_str),
82 attr("sender", info.sender),
83 ];
84
85 self.set(deps, new_admin)?;
86
87 Ok(Response::new().add_attributes(attributes))
88 }
89
90 pub fn query_admin<Q: CustomQuery>(&self, deps: Deps<Q>) -> StdResult<AdminResponse> {
92 self.0.query_admin(deps)
93 }
94
95 pub fn query_account_owner<Q: CustomQuery>(&self, deps: Deps<Q>) -> StdResult<AdminResponse> {
97 let admin = match self.0.get(deps)? {
98 Some(owner) => Some(query_top_level_owner(&deps.querier, owner).map_err(|_| {
99 StdError::generic_err(
100 "Failed to query top level owner. Make sure this module is owned by the manager",
101 )
102 })?),
103 None => None,
104 };
105 Ok(AdminResponse {
106 admin: admin.map(|addr| addr.into_string()),
107 })
108 }
109}
110
111pub fn query_top_level_owner<Q: CustomQuery>(
112 querier: &QuerierWrapper<Q>,
113 maybe_manager: Addr,
114) -> StdResult<Addr> {
115 let mut current = manager::state::INFO.query(querier, maybe_manager.clone());
117 for _ in 0..MAX_ADMIN_RECURSION {
119 match ¤t {
120 Ok(AccountInfo {
121 governance_details: GovernanceDetails::SubAccount { manager, .. },
122 ..
123 }) => {
124 current = manager::state::INFO.query(querier, manager.clone());
125 }
126 _ => break,
127 }
128 }
129
130 current.and_then(|info| {
132 info.governance_details
133 .owner_address()
134 .ok_or(StdError::generic_err("Top level account got renounced"))
135 })
136}
137
138#[cosmwasm_schema::cw_serde]
139pub struct TopLevelOwnerResponse {
140 pub address: Addr,
141}