abstract_version_control/
contract.rs1use abstract_macros::abstract_response;
2use abstract_sdk::{execute_update_ownership, query_ownership};
3pub(crate) use abstract_std::objects::namespace::ABSTRACT_NAMESPACE;
4use abstract_std::{
5 objects::namespace::Namespace,
6 version_control::{state::NAMESPACES_INFO, Config},
7};
8use abstract_std::{
9 objects::ABSTRACT_ACCOUNT_ID,
10 version_control::{state::CONFIG, ConfigResponse, ExecuteMsg, InstantiateMsg, QueryMsg},
11 VERSION_CONTROL,
12};
13use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response};
14
15use crate::{commands::*, error::VCError, queries};
16
17pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
18
19pub type VCResult<T = Response> = Result<T, VCError>;
20
21#[abstract_response(VERSION_CONTROL)]
22pub struct VcResponse;
23
24#[cfg_attr(feature = "export", cosmwasm_std::entry_point)]
25pub fn instantiate(deps: DepsMut, _env: Env, _info: MessageInfo, msg: InstantiateMsg) -> VCResult {
26 cw2::set_contract_version(deps.storage, VERSION_CONTROL, CONTRACT_VERSION)?;
27
28 let InstantiateMsg {
29 admin,
30 security_disabled,
31 namespace_registration_fee,
32 } = msg;
33
34 CONFIG.save(
35 deps.storage,
36 &Config {
37 account_factory_address: None,
39 security_disabled: security_disabled.unwrap_or(false),
40 namespace_registration_fee,
41 },
42 )?;
43
44 cw_ownable::initialize_owner(deps.storage, deps.api, Some(&admin))?;
46
47 NAMESPACES_INFO.save(
49 deps.storage,
50 &Namespace::new(ABSTRACT_NAMESPACE)?,
51 &ABSTRACT_ACCOUNT_ID,
52 )?;
53
54 Ok(VcResponse::action("instantiate"))
55}
56
57#[cfg_attr(feature = "export", cosmwasm_std::entry_point)]
58pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> VCResult {
59 match msg {
60 ExecuteMsg::ProposeModules { modules } => propose_modules(deps, info, modules),
61 ExecuteMsg::ApproveOrRejectModules { approves, rejects } => {
62 approve_or_reject_modules(deps, info, approves, rejects)
63 }
64 ExecuteMsg::RemoveModule { module } => remove_module(deps, info, module),
65 ExecuteMsg::YankModule { module } => yank_module(deps, info, module),
66 ExecuteMsg::UpdateModuleConfiguration {
67 module_name,
68 namespace,
69 update_module,
70 } => update_module_config(deps, info, module_name, namespace, update_module),
71 ExecuteMsg::ClaimNamespace {
72 namespace,
73 account_id,
74 } => claim_namespace(deps, info, account_id, namespace),
75 ExecuteMsg::RemoveNamespaces { namespaces } => remove_namespaces(deps, info, namespaces),
76 ExecuteMsg::AddAccount {
77 account_id,
78 account_base: base,
79 namespace,
80 } => add_account(deps, info, account_id, base, namespace),
81 ExecuteMsg::UpdateConfig {
82 account_factory_address,
83 security_disabled,
84 namespace_registration_fee,
85 } => update_config(
86 deps,
87 info,
88 account_factory_address,
89 security_disabled,
90 namespace_registration_fee,
91 ),
92 ExecuteMsg::UpdateOwnership(action) => {
93 execute_update_ownership!(VcResponse, deps, env, info, action)
94 }
95 }
96}
97
98#[cfg_attr(feature = "export", cosmwasm_std::entry_point)]
99pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> VCResult<Binary> {
100 match msg {
101 QueryMsg::AccountBase { account_id } => {
102 to_json_binary(&queries::handle_account_address_query(deps, account_id)?)
103 }
104 QueryMsg::Modules { infos } => to_json_binary(&queries::handle_modules_query(deps, infos)?),
105 QueryMsg::Namespaces { accounts } => {
106 to_json_binary(&queries::handle_namespaces_query(deps, accounts)?)
107 }
108 QueryMsg::Namespace { namespace } => {
109 to_json_binary(&queries::handle_namespace_query(deps, namespace)?)
110 }
111 QueryMsg::Config {} => {
112 let config = CONFIG.load(deps.storage)?;
113 to_json_binary(&ConfigResponse {
114 account_factory_address: config.account_factory_address,
115 security_disabled: config.security_disabled,
116 namespace_registration_fee: config.namespace_registration_fee,
117 })
118 }
119 QueryMsg::ModuleList {
120 filter,
121 start_after,
122 limit,
123 } => to_json_binary(&queries::handle_module_list_query(
124 deps,
125 start_after,
126 limit,
127 filter,
128 )?),
129 QueryMsg::NamespaceList { start_after, limit } => {
130 let start_after = start_after.map(Namespace::try_from).transpose()?;
131 to_json_binary(&queries::handle_namespace_list_query(
132 deps,
133 start_after,
134 limit,
135 )?)
136 }
137 QueryMsg::Ownership {} => query_ownership!(deps),
138 }
139 .map_err(Into::into)
140}
141
142#[cfg(test)]
143mod tests {
144 use abstract_std::objects::ABSTRACT_ACCOUNT_ID;
145 use cosmwasm_std::testing::*;
146 use speculoos::prelude::*;
147
148 use super::*;
149 use crate::testing::*;
150
151 mod instantiate {
152 use super::*;
153
154 #[test]
155 fn sets_abstract_namespace() -> VCResult<()> {
156 let mut deps = mock_dependencies();
157 mock_init(deps.as_mut())?;
158
159 let account_id = NAMESPACES_INFO.load(
160 deps.as_ref().storage,
161 &Namespace::try_from(ABSTRACT_NAMESPACE)?,
162 )?;
163 assert_that!(account_id).is_equal_to(ABSTRACT_ACCOUNT_ID);
164
165 Ok(())
166 }
167 }
168
169 mod migrate {
170 use abstract_std::{version_control::MigrateMsg, AbstractError};
171 use cw_semver::Version;
172
173 use super::*;
174
175 #[test]
176 fn disallow_same_version() -> VCResult<()> {
177 let mut deps = mock_dependencies();
178 mock_old_init(deps.as_mut())?;
179
180 let version: Version = CONTRACT_VERSION.parse().unwrap();
181
182 let res = crate::migrate::migrate(deps.as_mut(), mock_env(), MigrateMsg {});
183
184 assert_that!(res).is_err().is_equal_to(VCError::Abstract(
185 AbstractError::CannotDowngradeContract {
186 contract: VERSION_CONTROL.to_string(),
187 from: version.to_string().parse().unwrap(),
188 to: version.to_string().parse().unwrap(),
189 },
190 ));
191
192 Ok(())
193 }
194
195 #[test]
196 fn disallow_downgrade() -> VCResult<()> {
197 let mut deps = mock_dependencies();
198 mock_old_init(deps.as_mut())?;
199
200 let big_version = "999.999.999";
201 cw2::set_contract_version(deps.as_mut().storage, VERSION_CONTROL, big_version)?;
202
203 let version: Version = CONTRACT_VERSION.parse().unwrap();
204
205 let res = crate::migrate::migrate(deps.as_mut(), mock_env(), MigrateMsg {});
206
207 assert_that!(res).is_err().is_equal_to(VCError::Abstract(
208 AbstractError::CannotDowngradeContract {
209 contract: VERSION_CONTROL.to_string(),
210 from: big_version.parse().unwrap(),
211 to: version.to_string().parse().unwrap(),
212 },
213 ));
214
215 Ok(())
216 }
217
218 #[test]
219 fn disallow_name_change() -> VCResult<()> {
220 let mut deps = mock_dependencies();
221 mock_old_init(deps.as_mut())?;
222
223 let old_version = "0.0.0";
224 let old_name = "old:contract";
225 cw2::set_contract_version(deps.as_mut().storage, old_name, old_version)?;
226
227 let res = crate::migrate::migrate(deps.as_mut(), mock_env(), MigrateMsg {});
228
229 assert_that!(res).is_err().is_equal_to(VCError::Abstract(
230 AbstractError::ContractNameMismatch {
231 from: old_name.to_string(),
232 to: VERSION_CONTROL.to_string(),
233 },
234 ));
235
236 Ok(())
237 }
238
239 #[test]
240 fn works() -> VCResult<()> {
241 let mut deps = mock_dependencies();
242 mock_old_init(deps.as_mut())?;
243
244 let version: Version = CONTRACT_VERSION.parse().unwrap();
245
246 let small_version = Version {
247 minor: version.minor - 1,
248 ..version.clone()
249 }
250 .to_string();
251 cw2::set_contract_version(deps.as_mut().storage, VERSION_CONTROL, small_version)?;
252
253 let res = crate::migrate::migrate(deps.as_mut(), mock_env(), MigrateMsg {})?;
254 assert_that!(res.messages).has_length(0);
255
256 assert_that!(cw2::get_contract_version(&deps.storage)?.version)
257 .is_equal_to(version.to_string());
258 Ok(())
259 }
260 }
261}