abstract_std/objects/ownership/
nested_admin.rs1use crate::{
2 account::state::{CALLING_TO_AS_ADMIN, CALLING_TO_AS_ADMIN_WILD_CARD},
3 objects::{gov_type::GovernanceDetails, ownership::Ownership},
4};
5
6use cosmwasm_std::{
7 attr, Addr, CustomQuery, Deps, DepsMut, Env, MessageInfo, QuerierWrapper, Response, StdError,
8 StdResult,
9};
10use cw_controllers::{Admin, AdminError, AdminResponse};
11use schemars::JsonSchema;
12
13use super::query_ownership;
14
15pub const MAX_ADMIN_RECURSION: usize = 2;
17
18pub struct NestedAdmin(Admin);
27
28impl NestedAdmin {
29 pub const fn new(namespace: &'static str) -> Self {
30 NestedAdmin(Admin::new(namespace))
31 }
32
33 pub fn set<Q: CustomQuery>(&self, deps: DepsMut<Q>, admin: Option<Addr>) -> StdResult<()> {
34 self.0.set(deps, admin)
35 }
36
37 pub fn get<Q: CustomQuery>(&self, deps: Deps<Q>) -> StdResult<Option<Addr>> {
38 self.0.get(deps)
39 }
40
41 pub fn is_admin<Q: CustomQuery>(
43 &self,
44 deps: Deps<Q>,
45 env: &Env,
46 caller: &Addr,
47 ) -> StdResult<bool> {
48 match self.0.get(deps)? {
49 Some(admin) => Self::is_admin_custom(&deps.querier, env, caller, admin),
50 None => Ok(false),
51 }
52 }
53
54 pub fn is_admin_custom<Q: CustomQuery>(
56 querier: &QuerierWrapper<Q>,
57 env: &Env,
58 caller: &Addr,
59 admin: Addr,
60 ) -> StdResult<bool> {
61 if caller == admin && assert_account_calling_to_as_admin_is_self(querier, env, caller) {
63 Ok(true)
67 } else {
68 Ok(query_top_level_owner_addr(querier, admin)
70 .map(|admin| admin == caller)
71 .unwrap_or(false))
72 }
73 }
74
75 pub fn assert_admin<Q: CustomQuery>(
82 &self,
83 deps: Deps<Q>,
84 env: &Env,
85 caller: &Addr,
86 ) -> Result<(), AdminError> {
87 if !self.is_admin(deps, env, caller)? {
88 Err(AdminError::NotAdmin {})
89 } else {
90 Ok(())
91 }
92 }
93
94 pub fn assert_admin_custom<Q: CustomQuery>(
101 querier: &QuerierWrapper<Q>,
102 env: &Env,
103 caller: &Addr,
104 admin: Addr,
105 ) -> Result<(), AdminError> {
106 if !Self::is_admin_custom(querier, env, caller, admin)? {
107 Err(AdminError::NotAdmin {})
108 } else {
109 Ok(())
110 }
111 }
112
113 pub fn execute_update_admin<C, Q: CustomQuery>(
114 &self,
115 deps: DepsMut<Q>,
116 env: &Env,
117 info: MessageInfo,
118 new_admin: Option<Addr>,
119 ) -> Result<Response<C>, AdminError>
120 where
121 C: Clone + core::fmt::Debug + PartialEq + JsonSchema,
122 {
123 self.assert_admin(deps.as_ref(), env, &info.sender)?;
124
125 let admin_str = match new_admin.as_ref() {
126 Some(admin) => admin.to_string(),
127 None => "None".to_string(),
128 };
129 let attributes = vec![
130 attr("action", "update_admin"),
131 attr("admin", admin_str),
132 attr("sender", info.sender),
133 ];
134
135 self.set(deps, new_admin)?;
136
137 Ok(Response::new().add_attributes(attributes))
138 }
139
140 pub fn query_admin<Q: CustomQuery>(&self, deps: Deps<Q>) -> StdResult<AdminResponse> {
142 self.0.query_admin(deps)
143 }
144
145 pub fn query_account_owner<Q: CustomQuery>(&self, deps: Deps<Q>) -> StdResult<AdminResponse> {
147 let admin = match self.0.get(deps)? {
148 Some(owner) => Some(query_top_level_owner_addr(&deps.querier, owner).map_err(|_| {
149 StdError::generic_err(
150 "Failed to query top level owner. Make sure this module is owned by the account",
151 )
152 })?),
153 None => None,
154 };
155 Ok(AdminResponse {
156 admin: admin.map(|addr| addr.into_string()),
157 })
158 }
159}
160
161pub fn query_top_level_owner_addr<Q: CustomQuery>(
162 querier: &QuerierWrapper<Q>,
163 maybe_account: Addr,
164) -> StdResult<Addr> {
165 query_top_level_owner(querier, maybe_account).and_then(|ownership| {
167 ownership
168 .owner
169 .owner_address(&querier.into_empty())
170 .ok_or(StdError::generic_err("Top level account got renounced"))
171 })
172}
173
174pub fn query_top_level_owner<Q: CustomQuery>(
175 querier: &QuerierWrapper<Q>,
176 maybe_account: Addr,
177) -> StdResult<Ownership<Addr>> {
178 let mut current = query_ownership(querier, maybe_account);
180 for _ in 0..MAX_ADMIN_RECURSION {
182 match current {
183 Ok(Ownership {
184 owner: GovernanceDetails::SubAccount { account },
185 ..
186 }) => {
187 current = query_ownership(querier, account);
188 }
189 _ => break,
190 }
191 }
192
193 current
194}
195
196pub fn assert_account_calling_to_as_admin_is_self<Q: CustomQuery>(
198 querier: &QuerierWrapper<Q>,
199 env: &Env,
200 maybe_account: &Addr,
201) -> bool {
202 CALLING_TO_AS_ADMIN
203 .query(querier, maybe_account.clone())
204 .map(|admin_call_to| {
205 admin_call_to == env.contract.address
206 || admin_call_to.as_str() == CALLING_TO_AS_ADMIN_WILD_CARD
207 })
208 .unwrap_or(false)
209}