abstract_core/objects/
version_control.rs

1use cosmwasm_std::{Addr, QuerierWrapper};
2use thiserror::Error;
3
4use super::{
5    account::ACCOUNT_ID,
6    module::{Module, ModuleInfo},
7    module_reference::ModuleReference,
8    namespace::Namespace,
9    AccountId,
10};
11use crate::version_control::{
12    state::{ACCOUNT_ADDRESSES, CONFIG, REGISTERED_MODULES, STANDALONE_INFOS},
13    AccountBase, ModuleConfiguration, ModuleResponse, ModulesResponse, NamespaceResponse,
14    NamespacesResponse, QueryMsg,
15};
16
17#[derive(Error, Debug, PartialEq)]
18pub enum VersionControlError {
19    // module not found in version registry
20    #[error("Module {module} not found in version registry {registry_addr}.")]
21    ModuleNotFound { module: String, registry_addr: Addr },
22
23    // failed to query account id
24    #[error("Failed to query Account id on contract {contract_addr}. Please ensure that the contract is a Manager or Proxy contract.")]
25    FailedToQueryAccountId { contract_addr: Addr },
26
27    // standalone module not found in version registry
28    #[error("Standalone {code_id} not found in version registry {registry_addr}.")]
29    StandaloneNotFound { code_id: u64, registry_addr: Addr },
30
31    // unknown Account id error
32    #[error("Unknown Account id {account_id} on version control {registry_addr}. Please ensure that you are using the correct Account id and version control address.")]
33    UnknownAccountId {
34        account_id: AccountId,
35        registry_addr: Addr,
36    },
37
38    // caller not Manager error
39    #[error("Address {0} is not the Manager of Account {1}.")]
40    NotManager(Addr, AccountId),
41
42    // caller not Proxy error
43    #[error("Address {0} is not the Proxy of Account {1}.")]
44    NotProxy(Addr, AccountId),
45
46    // Query method failed
47    #[error("Query during '{method_name}' failed: {error}")]
48    QueryFailed {
49        method_name: String,
50        error: cosmwasm_std::StdError,
51    },
52}
53
54pub type VersionControlResult<T> = Result<T, VersionControlError>;
55
56/// Store the Version Control contract.
57#[allow(rustdoc::broken_intra_doc_links)]
58/// Implements [`AbstractRegistryAccess`] (defined in abstract-sdk)
59#[cosmwasm_schema::cw_serde]
60pub struct VersionControlContract {
61    /// Address of the version control contract
62    pub address: Addr,
63}
64
65impl VersionControlContract {
66    /// Construct a new version control feature object.
67    pub fn new(address: Addr) -> Self {
68        Self { address }
69    }
70
71    // Module registry
72
73    /// Raw query for a module reference
74    #[function_name::named]
75    pub fn query_module_reference_raw(
76        &self,
77        module_info: &ModuleInfo,
78        querier: &QuerierWrapper,
79    ) -> VersionControlResult<ModuleReference> {
80        let module_reference = REGISTERED_MODULES
81            .query(querier, self.address.clone(), module_info)
82            .map_err(|error| VersionControlError::QueryFailed {
83                method_name: function_name!().to_owned(),
84                error,
85            })?;
86
87        module_reference.ok_or_else(|| VersionControlError::ModuleNotFound {
88            module: module_info.to_string(),
89            registry_addr: self.address.clone(),
90        })
91    }
92
93    /// Smart query for a module
94    pub fn query_module(
95        &self,
96        module_info: ModuleInfo,
97        querier: &QuerierWrapper,
98    ) -> VersionControlResult<Module> {
99        Ok(self
100            .query_modules_configs(vec![module_info], querier)?
101            .swap_remove(0)
102            .module)
103    }
104
105    /// Smart query for a module config
106    pub fn query_config(
107        &self,
108        module_info: ModuleInfo,
109        querier: &QuerierWrapper,
110    ) -> VersionControlResult<ModuleConfiguration> {
111        Ok(self
112            .query_modules_configs(vec![module_info], querier)?
113            .swap_remove(0)
114            .config)
115    }
116
117    /// Smart query for a modules and its configurations
118    #[function_name::named]
119    pub fn query_modules_configs(
120        &self,
121        infos: Vec<ModuleInfo>,
122        querier: &QuerierWrapper,
123    ) -> VersionControlResult<Vec<ModuleResponse>> {
124        let ModulesResponse { modules } = querier
125            .query_wasm_smart(self.address.to_string(), &QueryMsg::Modules { infos })
126            .map_err(|error| VersionControlError::QueryFailed {
127                method_name: function_name!().to_owned(),
128                error,
129            })?;
130        Ok(modules)
131    }
132
133    /// Queries the account that owns the namespace
134    /// Is also returns the base modules of that account (AccountBase)
135    #[function_name::named]
136    pub fn query_namespace(
137        &self,
138        namespace: Namespace,
139        querier: &QuerierWrapper,
140    ) -> VersionControlResult<NamespaceResponse> {
141        let namespace_response: NamespaceResponse = querier
142            .query_wasm_smart(self.address.to_string(), &QueryMsg::Namespace { namespace })
143            .map_err(|error| VersionControlError::QueryFailed {
144                method_name: function_name!().to_owned(),
145                error,
146            })?;
147        Ok(namespace_response)
148    }
149
150    /// Queries the namespaces owned by accounts
151    #[function_name::named]
152    pub fn query_namespaces(
153        &self,
154        accounts: Vec<AccountId>,
155        querier: &QuerierWrapper,
156    ) -> VersionControlResult<NamespacesResponse> {
157        let namespaces_response: NamespacesResponse = querier
158            .query_wasm_smart(self.address.to_string(), &QueryMsg::Namespaces { accounts })
159            .map_err(|error| VersionControlError::QueryFailed {
160                method_name: function_name!().to_owned(),
161                error,
162            })?;
163        Ok(namespaces_response)
164    }
165
166    /// Queries the module info of the standalone code id
167    #[function_name::named]
168    pub fn query_standalone_info_raw(
169        &self,
170        code_id: u64,
171        querier: &QuerierWrapper,
172    ) -> VersionControlResult<ModuleInfo> {
173        let module_info = STANDALONE_INFOS
174            .query(querier, self.address.clone(), code_id)
175            .map_err(|error| VersionControlError::QueryFailed {
176                method_name: function_name!().to_owned(),
177                error,
178            })?;
179        module_info.ok_or_else(|| VersionControlError::StandaloneNotFound {
180            code_id,
181            registry_addr: self.address.clone(),
182        })
183    }
184
185    // AccountRegistry
186
187    /// Get self reported Account id, for checked use
188    /// [`VersionControlContract::account_id`]
189    pub fn unchecked_account_id(
190        &self,
191        maybe_core_contract_addr: &Addr,
192        querier: &QuerierWrapper,
193    ) -> VersionControlResult<AccountId> {
194        ACCOUNT_ID
195            .query(querier, maybe_core_contract_addr.clone())
196            .map_err(|_| VersionControlError::FailedToQueryAccountId {
197                contract_addr: maybe_core_contract_addr.clone(),
198            })
199    }
200
201    /// Get AccountId for given manager or proxy address.
202    /// Also verifies that that address is indeed a manager or proxy.
203    pub fn account_id(
204        &self,
205        maybe_core_contract_addr: &Addr,
206        querier: &QuerierWrapper,
207    ) -> VersionControlResult<AccountId> {
208        let self_reported_account_id =
209            self.unchecked_account_id(maybe_core_contract_addr, querier)?;
210        // now we need to verify that the account id is indeed correct
211        let account_base = self.account_base(&self_reported_account_id, querier)?;
212        if account_base.manager.ne(maybe_core_contract_addr)
213            && account_base.proxy.ne(maybe_core_contract_addr)
214        {
215            Err(VersionControlError::FailedToQueryAccountId {
216                contract_addr: maybe_core_contract_addr.clone(),
217            })
218        } else {
219            Ok(self_reported_account_id)
220        }
221    }
222
223    /// Get the account base for a given account id.
224    #[function_name::named]
225    pub fn account_base(
226        &self,
227        account_id: &AccountId,
228        querier: &QuerierWrapper,
229    ) -> VersionControlResult<AccountBase> {
230        let maybe_account = ACCOUNT_ADDRESSES
231            .query(querier, self.address.clone(), account_id)
232            .map_err(|error| VersionControlError::QueryFailed {
233                method_name: function_name!().to_owned(),
234                error,
235            })?;
236        maybe_account.ok_or_else(|| VersionControlError::UnknownAccountId {
237            account_id: account_id.clone(),
238            registry_addr: self.address.clone(),
239        })
240    }
241
242    /// Get namespace registration fee
243    #[function_name::named]
244    pub fn namespace_registration_fee(
245        &self,
246        querier: &QuerierWrapper,
247    ) -> VersionControlResult<Option<cosmwasm_std::Coin>> {
248        let config = CONFIG
249            .query(querier, self.address.clone())
250            .map_err(|error| VersionControlError::QueryFailed {
251                method_name: function_name!().to_owned(),
252                error,
253            })?;
254        Ok(config.namespace_registration_fee)
255    }
256
257    /// Verify if the provided manager address is indeed a user.
258    pub fn assert_manager(
259        &self,
260        maybe_manager: &Addr,
261        querier: &QuerierWrapper,
262    ) -> VersionControlResult<AccountBase> {
263        let account_id = self.unchecked_account_id(maybe_manager, querier)?;
264        let account_base = self.account_base(&account_id, querier)?;
265        if account_base.manager.ne(maybe_manager) {
266            Err(VersionControlError::NotManager(
267                maybe_manager.clone(),
268                account_id,
269            ))
270        } else {
271            Ok(account_base)
272        }
273    }
274
275    /// Verify if the provided proxy address is indeed a user.
276    pub fn assert_proxy(
277        &self,
278        maybe_proxy: &Addr,
279        querier: &QuerierWrapper,
280    ) -> VersionControlResult<AccountBase> {
281        let account_id = self.unchecked_account_id(maybe_proxy, querier)?;
282        let account_base = self.account_base(&account_id, querier)?;
283        if account_base.proxy.ne(maybe_proxy) {
284            Err(VersionControlError::NotProxy(
285                maybe_proxy.clone(),
286                account_id,
287            ))
288        } else {
289            Ok(account_base)
290        }
291    }
292}