abstract_sdk/apis/
app.rs

1#![allow(unused)]
2use abstract_std::{app as msg, objects::module::ModuleId};
3use cosmwasm_std::{wasm_execute, CosmosMsg, Deps, Empty};
4use serde::{de::DeserializeOwned, Serialize};
5
6use super::AbstractApi;
7use crate::{
8    cw_helpers::ApiQuery, features::ModuleIdentification, AbstractSdkResult, AccountAction,
9    ModuleInterface,
10};
11
12/// Interact with other modules on the Account.
13pub trait AppInterface: ModuleInterface + ModuleIdentification {
14    /**
15        API for accessing Abstract Apps installed on the account.
16
17        # Example
18        ```
19        use abstract_sdk::prelude::*;
20        # use cosmwasm_std::testing::mock_dependencies;
21        # use abstract_sdk::mock_module::MockModule;
22        # use abstract_testing::prelude::*;
23        # let deps = mock_dependencies();
24        # let account = admin_account(deps.api);
25        # let module = MockModule::new(deps.api, account);
26
27        let apps: Apps<MockModule>  = module.apps(deps.as_ref());
28        ```
29    */
30    fn apps<'a>(&'a self, deps: Deps<'a>) -> Apps<'a, Self> {
31        Apps { base: self, deps }
32    }
33}
34
35impl<T> AppInterface for T where T: ModuleInterface + ModuleIdentification {}
36
37impl<T: AppInterface> AbstractApi<T> for Apps<'_, T> {
38    const API_ID: &'static str = "Apps";
39
40    fn base(&self) -> &T {
41        self.base
42    }
43    fn deps(&self) -> Deps {
44        self.deps
45    }
46}
47
48/**
49    API for accessing Abstract Apps installed on the account.
50
51    # Example
52    ```
53    use abstract_sdk::prelude::*;
54    # use cosmwasm_std::testing::mock_dependencies;
55    # use abstract_sdk::mock_module::MockModule;
56    # use abstract_testing::prelude::*;
57    # let deps = mock_dependencies();
58    # let account = admin_account(deps.api);
59    # let module = MockModule::new(deps.api, account);
60
61    let apps: Apps<MockModule>  = module.apps(deps.as_ref());
62    ```
63*/
64#[derive(Clone)]
65pub struct Apps<'a, T: AppInterface> {
66    base: &'a T,
67    deps: Deps<'a>,
68}
69
70impl<T: AppInterface> Apps<'_, T> {
71    /// Construct an app request message.
72    pub fn execute<M: Serialize>(
73        &self,
74        app_id: ModuleId,
75        message: impl Into<msg::ExecuteMsg<M>>,
76    ) -> AbstractSdkResult<CosmosMsg> {
77        let modules = self.base.modules(self.deps);
78        modules.assert_module_dependency(app_id)?;
79        let app_msg: msg::ExecuteMsg<M> = message.into();
80        let app_address = modules.module_address(app_id)?;
81        Ok(wasm_execute(app_address, &app_msg, vec![])?.into())
82    }
83
84    /// Smart query an app
85    pub fn query<Q: Serialize, R: DeserializeOwned>(
86        &self,
87        app_id: ModuleId,
88        query: impl Into<msg::QueryMsg<Q>>,
89    ) -> AbstractSdkResult<R> {
90        let modules = self.base.modules(self.deps);
91        let app_query: msg::QueryMsg<Q> = query.into();
92        let app_address = modules.module_address(app_id)?;
93        self.smart_query(app_address, &app_query)
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use abstract_std::registry::Account;
100    use abstract_testing::{abstract_mock_querier_builder, prelude::*};
101    use cosmwasm_std::{testing::*, *};
102
103    pub use super::*;
104    use crate::{apis::traits::test::abstract_api_test, mock_module::*};
105
106    /// Helper to check that the method is not callable when the module is not a dependency
107    fn fail_when_not_dependency_test<T: std::fmt::Debug>(
108        modules_fn: impl FnOnce(&MockModule, Deps) -> AbstractSdkResult<T>,
109        fake_module: ModuleId,
110    ) {
111        let (deps, _, app) = mock_module_setup();
112
113        let _mods = app.apps(deps.as_ref());
114
115        let res = modules_fn(&app, deps.as_ref());
116
117        assert!(res
118            .unwrap_err()
119            .to_string()
120            .contains(&fake_module.to_string()));
121    }
122
123    mod app_request {
124        use super::*;
125        use crate::{mock_module::MockModuleExecuteMsg, std::app};
126
127        #[coverage_helper::test]
128        fn should_return_err_if_not_dependency() {
129            fail_when_not_dependency_test(
130                |app, deps| {
131                    let mods = app.apps(deps);
132                    mods.execute(FAKE_MODULE_ID, MockModuleExecuteMsg {})
133                },
134                FAKE_MODULE_ID,
135            );
136        }
137
138        #[coverage_helper::test]
139        fn expected_app_request() {
140            let (deps, _, app) = mock_module_setup();
141
142            let abstr = AbstractMockAddrs::new(deps.api);
143
144            let mods = app.apps(deps.as_ref());
145
146            let res = mods.execute(TEST_MODULE_ID, MockModuleExecuteMsg {});
147
148            let expected_msg: app::ExecuteMsg<_> = app::ExecuteMsg::Module(MockModuleExecuteMsg {});
149
150            assert_eq!(
151                res,
152                Ok(CosmosMsg::Wasm(WasmMsg::Execute {
153                    contract_addr: abstr.module_address.into(),
154                    msg: to_json_binary(&expected_msg).unwrap(),
155                    funds: vec![],
156                }))
157            );
158        }
159    }
160
161    mod query_app {
162        use super::*;
163
164        #[coverage_helper::test]
165        fn should_return_err_if_not_dependency() {
166            fail_when_not_dependency_test(
167                |app, deps| {
168                    let mods = app.apps(deps);
169                    mods.query::<_, Empty>(FAKE_MODULE_ID, Empty {})
170                },
171                FAKE_MODULE_ID,
172            );
173        }
174
175        #[coverage_helper::test]
176        fn expected_app_query() {
177            let (deps, _, app) = mock_module_setup();
178
179            let mods = app.apps(deps.as_ref());
180
181            let res = mods.query::<_, String>(TEST_MODULE_ID, Empty {});
182
183            assert_eq!(res, Ok(TEST_MODULE_RESPONSE.to_string()));
184        }
185    }
186
187    #[coverage_helper::test]
188    fn abstract_api() {
189        let (deps, _, app) = mock_module_setup();
190        let apps = app.apps(deps.as_ref());
191
192        abstract_api_test(apps);
193    }
194}