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 #[error("Module {module} not found in version registry {registry_addr}.")]
21 ModuleNotFound { module: String, registry_addr: Addr },
22
23 #[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 #[error("Standalone {code_id} not found in version registry {registry_addr}.")]
29 StandaloneNotFound { code_id: u64, registry_addr: Addr },
30
31 #[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 #[error("Address {0} is not the Manager of Account {1}.")]
40 NotManager(Addr, AccountId),
41
42 #[error("Address {0} is not the Proxy of Account {1}.")]
44 NotProxy(Addr, AccountId),
45
46 #[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#[allow(rustdoc::broken_intra_doc_links)]
58#[cosmwasm_schema::cw_serde]
60pub struct VersionControlContract {
61 pub address: Addr,
63}
64
65impl VersionControlContract {
66 pub fn new(address: Addr) -> Self {
68 Self { address }
69 }
70
71 #[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 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 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 #[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 #[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 #[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 #[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 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 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 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 #[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 #[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 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 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}