abstract_sdk/apis/
adapter.rs

1#![allow(dead_code)]
2
3use abstract_std::{adapter::AdapterRequestMsg, objects::module::ModuleId};
4use cosmwasm_std::{wasm_execute, CosmosMsg, Deps};
5use serde::{de::DeserializeOwned, Serialize};
6
7use super::AbstractApi;
8use crate::{
9    cw_helpers::ApiQuery, features::ModuleIdentification, AbstractSdkResult, ModuleInterface,
10};
11
12/// Interact with other modules on the Account.
13pub trait AdapterInterface: ModuleInterface + ModuleIdentification {
14    /**
15        API for accessing Abstract Adapters 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 adapters: Adapters<MockModule>  = module.adapters(deps.as_ref());
28        ```
29    */
30    fn adapters<'a>(&'a self, deps: Deps<'a>) -> Adapters<'a, Self> {
31        Adapters { base: self, deps }
32    }
33}
34
35impl<T> AdapterInterface for T where T: ModuleInterface + ModuleIdentification {}
36
37impl<T: AdapterInterface> AbstractApi<T> for Adapters<'_, T> {
38    const API_ID: &'static str = "Adapters";
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 Adapters 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 adapters: Adapters<MockModule>  = module.adapters(deps.as_ref());
62    ```
63*/
64#[derive(Clone)]
65pub struct Adapters<'a, T: AdapterInterface> {
66    base: &'a T,
67    deps: Deps<'a>,
68}
69
70impl<T: AdapterInterface> Adapters<'_, T> {
71    /// Interactions with Abstract Adapters
72    /// Construct an adapter execute message.
73    pub fn execute<M: Serialize + Into<abstract_std::adapter::ExecuteMsg<M>>>(
74        &self,
75        adapter_id: ModuleId,
76        message: M,
77    ) -> AbstractSdkResult<CosmosMsg> {
78        let modules = self.base.modules(self.deps);
79        modules.assert_module_dependency(adapter_id)?;
80        let adapter_msg = abstract_std::adapter::ExecuteMsg::<_>::Module(AdapterRequestMsg::new(
81            Some(self.base.account(self.deps)?.into_addr().into_string()),
82            message,
83        ));
84        let adapter_address = modules.module_address(adapter_id)?;
85        Ok(wasm_execute(adapter_address, &adapter_msg, vec![])?.into())
86    }
87
88    /// Smart query an Adapter
89    pub fn query<Q: Serialize, R: DeserializeOwned>(
90        &self,
91        adapter_id: ModuleId,
92        query: impl Into<abstract_std::adapter::QueryMsg<Q>>,
93    ) -> AbstractSdkResult<R> {
94        let adapter_query: abstract_std::adapter::QueryMsg<Q> = query.into();
95        let modules = self.base.modules(self.deps);
96        let adapter_address = modules.module_address(adapter_id)?;
97        self.smart_query(adapter_address, &adapter_query)
98    }
99}
100
101#[cfg(test)]
102mod tests {
103
104    use abstract_testing::prelude::*;
105    use cosmwasm_std::*;
106
107    use super::*;
108    use crate::{apis::traits::test::abstract_api_test, mock_module::*};
109
110    pub fn fail_when_not_dependency_test<T: std::fmt::Debug>(
111        modules_fn: impl FnOnce(&MockModule, Deps) -> AbstractSdkResult<T>,
112        fake_module: ModuleId,
113    ) {
114        let (deps, _, app) = mock_module_setup();
115
116        let _mods = app.adapters(deps.as_ref());
117
118        let res = modules_fn(&app, deps.as_ref());
119
120        assert!(res
121            .unwrap_err()
122            .to_string()
123            .contains(&fake_module.to_string()));
124    }
125    mod adapter_request {
126        use super::*;
127
128        use crate::std::adapter;
129
130        #[coverage_helper::test]
131        fn should_return_err_if_not_dependency() {
132            fail_when_not_dependency_test(
133                |app, deps| {
134                    let mods = app.adapters(deps);
135                    mods.execute(FAKE_MODULE_ID, MockModuleExecuteMsg {})
136                },
137                FAKE_MODULE_ID,
138            );
139        }
140
141        #[coverage_helper::test]
142        fn expected_adapter_request() {
143            let (deps, account, app) = mock_module_setup();
144            let abstr = AbstractMockAddrs::new(deps.api);
145
146            let mods = app.adapters(deps.as_ref());
147
148            let res = mods.execute(TEST_MODULE_ID, MockModuleExecuteMsg {});
149
150            let expected_msg: adapter::ExecuteMsg<_> =
151                adapter::ExecuteMsg::Module(AdapterRequestMsg {
152                    account_address: Some(account.addr().to_string()),
153                    request: MockModuleExecuteMsg {},
154                });
155
156            assert_eq!(
157                res,
158                Ok(CosmosMsg::Wasm(WasmMsg::Execute {
159                    contract_addr: abstr.module_address.to_string(),
160                    msg: to_json_binary(&expected_msg).unwrap(),
161                    funds: vec![],
162                }))
163            );
164        }
165    }
166
167    mod query_api {
168        use super::*;
169
170        #[coverage_helper::test]
171        fn should_return_err_if_not_dependency() {
172            fail_when_not_dependency_test(
173                |app, deps| {
174                    let mods = app.adapters(deps);
175                    mods.query::<_, Empty>(FAKE_MODULE_ID, Empty {})
176                },
177                FAKE_MODULE_ID,
178            );
179        }
180
181        #[coverage_helper::test]
182        fn expected_adapter_query() {
183            let (deps, _, app) = mock_module_setup();
184
185            let mods = app.adapters(deps.as_ref());
186
187            let inner_msg = Empty {};
188
189            let res = mods.query::<_, String>(TEST_MODULE_ID, inner_msg);
190
191            assert_eq!(res, Ok(TEST_MODULE_RESPONSE.to_string()));
192        }
193    }
194
195    #[coverage_helper::test]
196    fn abstract_api() {
197        let (deps, _, app) = mock_module_setup();
198        let adapters = app.adapters(deps.as_ref());
199
200        abstract_api_test(adapters);
201    }
202}