abstract_app/
state.rs

1use abstract_sdk::{
2    base::{ModuleIbcHandlerFn, SudoHandlerFn},
3    namespaces::{ADMIN_NAMESPACE, BASE_STATE},
4    AbstractSdkError,
5};
6use abstract_std::{
7    app::AppState,
8    objects::{
9        dependency::StaticDependency, module::ModuleInfo, ownership::nested_admin::NestedAdmin,
10    },
11    AbstractError, AbstractResult,
12};
13use cosmwasm_std::{Empty, StdResult, Storage};
14use cw_storage_plus::Item;
15
16use crate::{
17    AbstractContract, AppError, ExecuteHandlerFn, IbcCallbackHandlerFn, InstantiateHandlerFn,
18    MigrateHandlerFn, QueryHandlerFn, ReplyHandlerFn,
19};
20
21pub trait ContractError:
22    From<cosmwasm_std::StdError>
23    + From<AppError>
24    + From<AbstractSdkError>
25    + From<AbstractError>
26    + 'static
27{
28}
29
30impl<T> ContractError for T where
31    T: From<cosmwasm_std::StdError>
32        + From<AppError>
33        + From<AbstractSdkError>
34        + From<AbstractError>
35        + 'static
36{
37}
38
39/// The state variables for our AppContract.
40pub struct AppContract<
41    Error: ContractError,
42    CustomInitMsg: 'static,
43    CustomExecMsg: 'static,
44    CustomQueryMsg: 'static,
45    CustomMigrateMsg: 'static,
46    SudoMsg: 'static = Empty,
47> {
48    // Custom state for every App
49    pub admin: NestedAdmin,
50    pub(crate) base_state: Item<AppState>,
51
52    // Scaffolding contract that handles type safety and provides helper methods
53    pub(crate) contract: AbstractContract<Self, Error>,
54}
55
56/// Constructor
57impl<
58        Error: ContractError,
59        CustomInitMsg,
60        CustomExecMsg,
61        CustomQueryMsg,
62        CustomMigrateMsg,
63        SudoMsg,
64    > AppContract<Error, CustomInitMsg, CustomExecMsg, CustomQueryMsg, CustomMigrateMsg, SudoMsg>
65{
66    pub const fn new(
67        name: &'static str,
68        version: &'static str,
69        metadata: Option<&'static str>,
70    ) -> Self {
71        Self {
72            base_state: Item::new(BASE_STATE),
73            admin: NestedAdmin::new(ADMIN_NAMESPACE),
74            contract: AbstractContract::new(name, version, metadata),
75        }
76    }
77
78    pub fn module_id(&self) -> &'static str {
79        self.contract.info().0
80    }
81
82    pub fn version(&self) -> &'static str {
83        self.contract.info().1
84    }
85
86    pub fn module_info(&self) -> AbstractResult<ModuleInfo> {
87        ModuleInfo::from_id(self.module_id(), self.version().into())
88    }
89
90    pub fn load_state(&self, store: &dyn Storage) -> StdResult<AppState> {
91        self.base_state.load(store)
92    }
93
94    /// add dependencies to the contract
95    pub const fn with_dependencies(mut self, dependencies: &'static [StaticDependency]) -> Self {
96        self.contract = self.contract.with_dependencies(dependencies);
97        self
98    }
99
100    pub const fn with_instantiate(
101        mut self,
102        instantiate_handler: InstantiateHandlerFn<Self, CustomInitMsg, Error>,
103    ) -> Self {
104        self.contract = self.contract.with_instantiate(instantiate_handler);
105        self
106    }
107
108    pub const fn with_execute(
109        mut self,
110        execute_handler: ExecuteHandlerFn<Self, CustomExecMsg, Error>,
111    ) -> Self {
112        self.contract = self.contract.with_execute(execute_handler);
113        self
114    }
115
116    pub const fn with_query(
117        mut self,
118        query_handler: QueryHandlerFn<Self, CustomQueryMsg, Error>,
119    ) -> Self {
120        self.contract = self.contract.with_query(query_handler);
121        self
122    }
123
124    pub const fn with_migrate(
125        mut self,
126        migrate_handler: MigrateHandlerFn<Self, CustomMigrateMsg, Error>,
127    ) -> Self {
128        self.contract = self.contract.with_migrate(migrate_handler);
129        self
130    }
131
132    pub const fn with_replies(
133        mut self,
134        reply_handlers: &'static [(u64, ReplyHandlerFn<Self, Error>)],
135    ) -> Self {
136        self.contract = self.contract.with_replies([&[], reply_handlers]);
137        self
138    }
139
140    pub const fn with_sudo(mut self, sudo_handler: SudoHandlerFn<Self, SudoMsg, Error>) -> Self {
141        self.contract = self.contract.with_sudo(sudo_handler);
142        self
143    }
144
145    /// add IBC callback handler to contract
146    pub const fn with_ibc_callback(mut self, callback: IbcCallbackHandlerFn<Self, Error>) -> Self {
147        self.contract = self.contract.with_ibc_callback(callback);
148        self
149    }
150
151    /// add Module IBC to contract
152    pub const fn with_module_ibc(
153        mut self,
154        module_handler: ModuleIbcHandlerFn<Self, Error>,
155    ) -> Self {
156        self.contract = self.contract.with_module_ibc(module_handler);
157        self
158    }
159}
160
161#[cfg(test)]
162mod tests {
163    use abstract_testing::prelude::*;
164    use cosmwasm_std::Response;
165
166    use crate::mock::MockAppContract;
167
168    #[coverage_helper::test]
169    fn builder() {
170        let app = MockAppContract::new(TEST_MODULE_ID, TEST_VERSION, None)
171            .with_instantiate(|_, _, _, _, _| Ok(Response::new().set_data("mock_init".as_bytes())))
172            .with_execute(|_, _, _, _, _| Ok(Response::new().set_data("mock_exec".as_bytes())))
173            .with_query(|_, _, _, _| cosmwasm_std::to_json_binary("mock_query").map_err(Into::into))
174            .with_sudo(|_, _, _, _| Ok(Response::new().set_data("mock_sudo".as_bytes())))
175            .with_ibc_callback(|_, _, _, _, _| {
176                Ok(Response::new().set_data("mock_callback".as_bytes()))
177            })
178            .with_replies(&[(1u64, |_, _, _, msg| {
179                #[allow(deprecated)]
180                Ok(Response::new().set_data(msg.result.unwrap().data.unwrap()))
181            })])
182            .with_migrate(|_, _, _, _| Ok(Response::new().set_data("mock_migrate".as_bytes())));
183
184        assert_eq!(app.module_id(), TEST_MODULE_ID);
185        assert_eq!(app.version(), TEST_VERSION);
186    }
187}