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
39pub struct AppContract<
41 Error: ContractError,
42 CustomInitMsg: 'static,
43 CustomExecMsg: 'static,
44 CustomQueryMsg: 'static,
45 CustomMigrateMsg: 'static,
46 SudoMsg: 'static = Empty,
47> {
48 pub admin: NestedAdmin,
50 pub(crate) base_state: Item<AppState>,
51
52 pub(crate) contract: AbstractContract<Self, Error>,
54}
55
56impl<
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 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 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 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}