abstract_core/native/
version_control.rs

1//! # Version Control
2//!
3//! `abstract_core::version_control` stores chain-specific code-ids, addresses and an account_id map.
4//!
5//! ## Description
6//! Code-ids and api-contract addresses are stored on this address. This data can not be changed and allows for complex factory logic.
7//! Both code-ids and addresses are stored on a per-module version basis which allows users to easily upgrade their modules.
8//!
9//! An internal account-id store provides external verification for manager and proxy addresses.  
10
11pub type ModuleMapEntry = (ModuleInfo, ModuleReference);
12
13/// Contains configuration info of version control.
14#[cosmwasm_schema::cw_serde]
15pub struct Config {
16    pub account_factory_address: Option<Addr>,
17    pub allow_direct_module_registration_and_updates: bool,
18    pub namespace_registration_fee: Option<Coin>,
19}
20
21pub mod state {
22    use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, MultiIndex};
23
24    use super::{AccountBase, Config, ModuleConfiguration, ModuleDefaultConfiguration};
25    use crate::objects::{
26        account::AccountId, module::ModuleInfo, module_reference::ModuleReference,
27        namespace::Namespace,
28    };
29
30    pub const CONFIG: Item<Config> = Item::new("cfg");
31
32    // Modules waiting for approvals
33    pub const PENDING_MODULES: Map<&ModuleInfo, ModuleReference> = Map::new("pendm");
34    // We can iterate over the map giving just the prefix to get all the versions
35    pub const REGISTERED_MODULES: Map<&ModuleInfo, ModuleReference> = Map::new("lib");
36    // Reverse map for module info of standalone modules
37    pub const STANDALONE_INFOS: Map<u64, ModuleInfo> = Map::new("stli");
38    // Yanked Modules
39    pub const YANKED_MODULES: Map<&ModuleInfo, ModuleReference> = Map::new("yknd");
40    // Modules Configuration
41    pub const MODULE_CONFIG: Map<&ModuleInfo, ModuleConfiguration> = Map::new("cfg");
42    // Modules Default Configuration
43    pub const MODULE_DEFAULT_CONFIG: Map<(&Namespace, &str), ModuleDefaultConfiguration> =
44        Map::new("dcfg");
45    /// Maps Account ID to the address of its core contracts
46    pub const ACCOUNT_ADDRESSES: Map<&AccountId, AccountBase> = Map::new("accs");
47
48    /// Sub indexes for namespaces.
49    // TODO: move to a two maps, we don't need multiindex for accountid
50    pub struct NamespaceIndexes<'a> {
51        pub account_id: MultiIndex<'a, AccountId, AccountId, &'a Namespace>,
52    }
53
54    impl<'a> IndexList<AccountId> for NamespaceIndexes<'a> {
55        fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<AccountId>> + '_> {
56            let v: Vec<&dyn Index<AccountId>> = vec![&self.account_id];
57            Box::new(v.into_iter())
58        }
59    }
60
61    /// Primary index for namespaces.
62    pub const NAMESPACES_INFO: IndexedMap<&Namespace, AccountId, NamespaceIndexes> =
63        IndexedMap::new(
64            "nmspc",
65            NamespaceIndexes {
66                account_id: MultiIndex::new(|_pk, d| d.clone(), "nmspc", "nmspc_a"),
67            },
68        );
69}
70
71use cosmwasm_schema::QueryResponses;
72use cosmwasm_std::{Addr, Coin, Storage};
73use cw_clearable::Clearable;
74
75use self::state::{MODULE_CONFIG, MODULE_DEFAULT_CONFIG};
76use crate::objects::{
77    account::AccountId,
78    module::{Module, ModuleInfo, ModuleMetadata, ModuleStatus, Monetization},
79    module_reference::ModuleReference,
80    namespace::Namespace,
81};
82
83/// Contains the minimal Abstract Account contract addresses.
84#[cosmwasm_schema::cw_serde]
85pub struct AccountBase {
86    pub manager: Addr,
87    pub proxy: Addr,
88}
89
90/// Version Control Instantiate Msg
91#[cosmwasm_schema::cw_serde]
92pub struct InstantiateMsg {
93    pub admin: String,
94    /// allows users to directly register modules without going through approval
95    /// Also allows them to change the module reference of an existing module
96    /// SHOULD ONLY BE `true` FOR TESTING
97    pub allow_direct_module_registration_and_updates: Option<bool>,
98    pub namespace_registration_fee: Option<Coin>,
99}
100
101/// Version Control Execute Msg
102#[cw_ownable::cw_ownable_execute]
103#[cosmwasm_schema::cw_serde]
104#[cfg_attr(feature = "interface", derive(cw_orch::ExecuteFns))]
105pub enum ExecuteMsg {
106    /// Remove some version of a module
107    RemoveModule { module: ModuleInfo },
108    /// Yank a version of a module so that it may not be installed
109    /// Only callable by Admin
110    YankModule { module: ModuleInfo },
111    /// Propose new modules to the version registry
112    /// Namespaces need to be claimed by the Account before proposing modules
113    /// Once proposed, the modules need to be approved by the Admin via [`ExecuteMsg::ApproveOrRejectModules`]
114    ProposeModules { modules: Vec<ModuleMapEntry> },
115    /// Sets the metadata configuration for a module.
116    /// Only callable by namespace admin
117    UpdateModuleConfiguration {
118        module_name: String,
119        namespace: Namespace,
120        update_module: UpdateModule,
121    },
122    /// Approve or reject modules
123    /// This takes the modules in the pending_modules map and
124    /// moves them to the registered_modules map or yanked_modules map
125    ApproveOrRejectModules {
126        approves: Vec<ModuleInfo>,
127        rejects: Vec<ModuleInfo>,
128    },
129    /// Claim namespaces
130    ClaimNamespace {
131        account_id: AccountId,
132        namespace: String,
133    },
134    /// Remove namespace claims
135    /// Only admin or root user can call this
136    RemoveNamespaces { namespaces: Vec<String> },
137    /// Register a new Account to the deployed Accounts.
138    /// Claims namespace if provided.  
139    /// Only Factory can call this
140    AddAccount {
141        account_id: AccountId,
142        account_base: AccountBase,
143        namespace: Option<String>,
144    },
145    /// Updates configuration of the VC contract
146    UpdateConfig {
147        /// Address of the account factory
148        account_factory_address: Option<String>,
149        /// Whether the contract allows direct module registration
150        allow_direct_module_registration_and_updates: Option<bool>,
151        /// The fee charged when registering a namespace
152        namespace_registration_fee: Option<Clearable<Coin>>,
153    },
154}
155
156#[non_exhaustive]
157#[cosmwasm_schema::cw_serde]
158pub enum UpdateModule {
159    /// Updates the default metadata for the module
160    Default { metadata: ModuleMetadata },
161    /// Update configuration for specified version
162    Versioned {
163        /// Module version
164        version: String,
165        /// Update the metadata for this version
166        metadata: Option<ModuleMetadata>,
167        /// Update the monetization for this version
168        monetization: Option<Monetization>,
169        /// Update the init_funds for this version
170        instantiation_funds: Option<Vec<Coin>>,
171    },
172}
173
174/// A ModuleFilter that mirrors the [`ModuleInfo`] struct.
175#[derive(Default)]
176#[cosmwasm_schema::cw_serde]
177pub struct ModuleFilter {
178    pub namespace: Option<String>,
179    pub name: Option<String>,
180    pub version: Option<String>,
181    pub status: Option<ModuleStatus>,
182}
183
184/// Version Control Query Msg
185#[cw_ownable::cw_ownable_query]
186#[cosmwasm_schema::cw_serde]
187#[derive(QueryResponses)]
188#[cfg_attr(feature = "interface", derive(cw_orch::QueryFns))]
189pub enum QueryMsg {
190    /// Query Core of an Account
191    /// Returns [`AccountBaseResponse`]
192    #[returns(AccountBaseResponse)]
193    AccountBase { account_id: AccountId },
194    /// Queries module information
195    /// Modules that are yanked are not returned
196    /// Returns [`ModulesResponse`]
197    #[returns(ModulesResponse)]
198    Modules { infos: Vec<ModuleInfo> },
199    /// Queries namespaces for an account
200    /// Returns [`NamespacesResponse`]
201    #[returns(NamespacesResponse)]
202    Namespaces { accounts: Vec<AccountId> },
203    /// Queries information about the namespace
204    /// Returns [`NamespaceResponse`]
205    #[returns(NamespaceResponse)]
206    Namespace { namespace: Namespace },
207    /// Returns [`ConfigResponse`]
208    #[returns(ConfigResponse)]
209    Config {},
210    /// Returns [`ModulesListResponse`]
211    #[returns(ModulesListResponse)]
212    ModuleList {
213        filter: Option<ModuleFilter>,
214        start_after: Option<ModuleInfo>,
215        limit: Option<u8>,
216    },
217    /// Returns [`NamespaceListResponse`]
218    #[returns(NamespaceListResponse)]
219    NamespaceList {
220        start_after: Option<String>,
221        limit: Option<u8>,
222    },
223}
224
225#[cosmwasm_schema::cw_serde]
226pub struct AccountBaseResponse {
227    pub account_base: AccountBase,
228}
229
230#[cosmwasm_schema::cw_serde]
231pub struct ModulesResponse {
232    pub modules: Vec<ModuleResponse>,
233}
234
235#[cosmwasm_schema::cw_serde]
236pub struct ModuleResponse {
237    pub module: Module,
238    pub config: ModuleConfiguration,
239}
240
241#[non_exhaustive]
242#[cosmwasm_schema::cw_serde]
243#[derive(Default)]
244pub struct ModuleConfiguration {
245    pub monetization: Monetization,
246    pub metadata: Option<ModuleMetadata>,
247    pub instantiation_funds: Vec<Coin>,
248}
249
250#[non_exhaustive]
251#[cosmwasm_schema::cw_serde]
252pub struct ModuleDefaultConfiguration {
253    pub metadata: ModuleMetadata,
254}
255
256impl ModuleDefaultConfiguration {
257    pub fn new(metadata: ModuleMetadata) -> Self {
258        Self { metadata }
259    }
260}
261
262impl ModuleConfiguration {
263    pub fn new(
264        monetization: Monetization,
265        metadata: Option<ModuleMetadata>,
266        instantiation_funds: Vec<Coin>,
267    ) -> Self {
268        Self {
269            monetization,
270            metadata,
271            instantiation_funds,
272        }
273    }
274
275    pub fn from_storage(
276        storage: &dyn Storage,
277        module: &ModuleInfo,
278    ) -> cosmwasm_std::StdResult<Self> {
279        let mut mod_cfg = MODULE_CONFIG.may_load(storage, module)?.unwrap_or_default();
280
281        if mod_cfg.metadata.is_none() {
282            // Destructure so we notice any field changes at compile time
283            if let Some(ModuleDefaultConfiguration { metadata }) =
284                MODULE_DEFAULT_CONFIG.may_load(storage, (&module.namespace, &module.name))?
285            {
286                mod_cfg.metadata = Some(metadata);
287            }
288        }
289
290        Ok(mod_cfg)
291    }
292}
293
294#[cosmwasm_schema::cw_serde]
295pub struct ModulesListResponse {
296    pub modules: Vec<ModuleResponse>,
297}
298
299#[cosmwasm_schema::cw_serde]
300pub enum NamespaceResponse {
301    Claimed(NamespaceInfo),
302    Unclaimed {},
303}
304
305impl NamespaceResponse {
306    pub fn unwrap(self) -> NamespaceInfo {
307        match self {
308            NamespaceResponse::Claimed(info) => info,
309            NamespaceResponse::Unclaimed {} => {
310                panic!("called `NamespaceResponse::unwrap()` on a `Unclaimed` value")
311            }
312        }
313    }
314}
315
316#[cosmwasm_schema::cw_serde]
317pub struct NamespaceInfo {
318    pub account_id: AccountId,
319    pub account_base: AccountBase,
320}
321
322#[cosmwasm_schema::cw_serde]
323pub struct NamespacesResponse {
324    pub namespaces: Vec<(Namespace, AccountId)>,
325}
326
327#[cosmwasm_schema::cw_serde]
328pub struct NamespaceListResponse {
329    pub namespaces: Vec<(Namespace, AccountId)>,
330}
331
332#[cosmwasm_schema::cw_serde]
333pub struct ConfigResponse {
334    pub account_factory_address: Option<Addr>,
335    pub allow_direct_module_registration_and_updates: bool,
336    pub namespace_registration_fee: Option<Coin>,
337}
338
339#[cosmwasm_schema::cw_serde]
340pub struct MigrateMsg {}