abstract_adapter/
state.rs

1use abstract_sdk::features::ModuleIdentification;
2use abstract_sdk::{
3    base::{
4        AbstractContract, ExecuteHandlerFn, Handler, IbcCallbackHandlerFn, InstantiateHandlerFn,
5        ModuleIbcHandlerFn, QueryHandlerFn, ReplyHandlerFn, SudoHandlerFn,
6    },
7    namespaces::BASE_STATE,
8    std::registry::Account,
9    AbstractSdkError,
10};
11use abstract_std::{
12    adapter::AdapterState,
13    objects::{dependency::StaticDependency, module::ModuleInfo},
14    AbstractError, AbstractResult,
15};
16use cosmwasm_std::{Addr, Empty, StdError, StdResult, Storage};
17use cw_storage_plus::{Item, Map};
18
19use crate::AdapterError;
20
21pub const AUTHORIZED_ADDRESSES_NAMESPACE: &str = "authorized_addresses";
22pub const MAXIMUM_AUTHORIZED_ADDRESSES: u32 = 15;
23
24pub trait ContractError:
25    From<cosmwasm_std::StdError>
26    + From<AdapterError>
27    + From<AbstractSdkError>
28    + From<AbstractError>
29    + 'static
30{
31}
32impl<T> ContractError for T where
33    T: From<cosmwasm_std::StdError>
34        + From<AdapterError>
35        + From<AbstractSdkError>
36        + From<AbstractError>
37        + 'static
38{
39}
40
41/// The state variables for our AdapterContract.
42pub struct AdapterContract<
43    Error: ContractError,
44    CustomInitMsg: 'static,
45    CustomExecMsg: 'static,
46    CustomQueryMsg: 'static,
47    SudoMsg: 'static = Empty,
48> where
49    Self: Handler,
50{
51    pub(crate) contract: AbstractContract<Self, Error>,
52    pub(crate) base_state: Item<AdapterState>,
53    /// Map AccountAddr -> AuthorizedAddrs
54    pub authorized_addresses: Map<Addr, Vec<Addr>>,
55    /// The Account on which commands are executed. Set each time in the [`abstract_std::adapter::ExecuteMsg::Base`] handler.
56    pub target_account: Option<Account>,
57}
58
59/// Constructor
60impl<Error: ContractError, CustomInitMsg, CustomExecMsg, CustomQueryMsg, SudoMsg>
61    AdapterContract<Error, CustomInitMsg, CustomExecMsg, CustomQueryMsg, SudoMsg>
62{
63    pub const fn new(
64        name: &'static str,
65        version: &'static str,
66        metadata: Option<&'static str>,
67    ) -> Self {
68        Self {
69            contract: AbstractContract::new(name, version, metadata),
70            base_state: Item::new(BASE_STATE),
71            authorized_addresses: Map::new(AUTHORIZED_ADDRESSES_NAMESPACE),
72            target_account: None,
73        }
74    }
75
76    pub fn version(&self) -> &'static str {
77        self.contract.info().1
78    }
79
80    pub fn module_info(&self) -> AbstractResult<ModuleInfo> {
81        ModuleInfo::from_id(self.module_id(), self.version().into())
82    }
83
84    pub fn state(&self, store: &dyn Storage) -> StdResult<AdapterState> {
85        self.base_state.load(store)
86    }
87
88    /// Return the address of the account for the Account associated with this Adapter.
89    /// Set each time in the [`abstract_std::adapter::ExecuteMsg::Base`] handler.
90    pub fn target(&self) -> Result<&Addr, AdapterError> {
91        Ok(self
92            .target_account
93            .as_ref()
94            .ok_or_else(|| StdError::generic_err("No target Account specified to execute on."))?
95            .addr())
96    }
97    /// add dependencies to the contract
98    pub const fn with_dependencies(mut self, dependencies: &'static [StaticDependency]) -> Self {
99        self.contract = self.contract.with_dependencies(dependencies);
100        self
101    }
102
103    pub const fn with_instantiate(
104        mut self,
105        instantiate_handler: InstantiateHandlerFn<Self, CustomInitMsg, Error>,
106    ) -> Self {
107        self.contract = self.contract.with_instantiate(instantiate_handler);
108        self
109    }
110
111    pub const fn with_execute(
112        mut self,
113        execute_handler: ExecuteHandlerFn<Self, CustomExecMsg, Error>,
114    ) -> Self {
115        self.contract = self.contract.with_execute(execute_handler);
116        self
117    }
118
119    pub const fn with_query(
120        mut self,
121        query_handler: QueryHandlerFn<Self, CustomQueryMsg, Error>,
122    ) -> Self {
123        self.contract = self.contract.with_query(query_handler);
124        self
125    }
126
127    pub const fn with_replies(
128        mut self,
129        reply_handlers: &'static [(u64, ReplyHandlerFn<Self, Error>)],
130    ) -> Self {
131        self.contract = self.contract.with_replies([&[], reply_handlers]);
132        self
133    }
134
135    pub const fn with_sudo(mut self, sudo_handler: SudoHandlerFn<Self, SudoMsg, Error>) -> Self {
136        self.contract = self.contract.with_sudo(sudo_handler);
137        self
138    }
139
140    /// add IBC callback handler to contract
141    pub const fn with_ibc_callback(mut self, callback: IbcCallbackHandlerFn<Self, Error>) -> Self {
142        self.contract = self.contract.with_ibc_callback(callback);
143        self
144    }
145
146    /// add Module IBC to contract
147    pub const fn with_module_ibc(
148        mut self,
149        module_handler: ModuleIbcHandlerFn<Self, Error>,
150    ) -> Self {
151        self.contract = self.contract.with_module_ibc(module_handler);
152        self
153    }
154}
155
156#[cfg(test)]
157mod tests {
158
159    use abstract_testing::prelude::*;
160    use cosmwasm_std::Response;
161
162    use super::*;
163    use crate::mock::{AdapterMockResult, MOCK_ADAPTER, TEST_METADATA};
164
165    #[coverage_helper::test]
166    fn set_and_get_target() -> AdapterMockResult {
167        let mut mock = MOCK_ADAPTER;
168        let target = Addr::unchecked("target");
169        mock.target_account = Some(Account::new(target.clone()));
170        assert_eq!(mock.target()?, &target);
171        Ok(())
172    }
173
174    #[coverage_helper::test]
175    fn builder_functions() {
176        crate::mock::MockAdapterContract::new(TEST_MODULE_ID, TEST_VERSION, Some(TEST_METADATA))
177            .with_instantiate(|_, _, _, _, _| Ok(Response::new().set_data("mock_init".as_bytes())))
178            .with_execute(|_, _, _, _, _| Ok(Response::new().set_data("mock_exec".as_bytes())))
179            .with_query(|_, _, _, _| cosmwasm_std::to_json_binary("mock_query").map_err(Into::into))
180            .with_sudo(|_, _, _, _| Ok(Response::new().set_data("mock_sudo".as_bytes())))
181            .with_ibc_callback(|_, _, _, _, _| {
182                Ok(Response::new().set_data("mock_callback".as_bytes()))
183            })
184            .with_replies(&[(1u64, |_, _, _, msg| {
185                #[allow(deprecated)]
186                Ok(Response::new().set_data(msg.result.unwrap().data.unwrap()))
187            })]);
188    }
189}