abstract_extension/
state.rs

1use std::{collections::HashSet, fmt::Debug};
2
3use abstract_sdk::{
4    base::{
5        AbstractContract, ExecuteHandlerFn, IbcCallbackHandlerFn, InstantiateHandlerFn,
6        QueryHandlerFn, ReceiveHandlerFn, ReplyHandlerFn,
7    },
8    feature_objects::AnsHost,
9    namespaces::BASE_STATE,
10    os::version_control::Core,
11};
12
13use cosmwasm_std::{Addr, Empty, StdError, StdResult, Storage};
14
15use cw_storage_plus::{Item, Map};
16use schemars::JsonSchema;
17use serde::{Deserialize, Serialize};
18
19use crate::ExtensionError;
20
21pub const TRADER_NAMESPACE: &str = "traders";
22
23/// The BaseState contains the main addresses needed for sending and verifying messages
24#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
25pub struct ExtensionState {
26    /// Used to verify requests
27    pub version_control: Addr,
28    /// AnsHost contract struct (address)
29    pub ans_host: AnsHost,
30}
31/// The state variables for our ExtensionContract.
32pub struct ExtensionContract<
33    Error: From<cosmwasm_std::StdError> + From<ExtensionError> + 'static,
34    CustomExecMsg: 'static = Empty,
35    CustomInitMsg: 'static = Empty,
36    CustomQueryMsg: 'static = Empty,
37    Receive: 'static = Empty,
38> {
39    pub(crate) contract:
40        AbstractContract<Self, Error, CustomExecMsg, CustomInitMsg, CustomQueryMsg, Empty, Receive>,
41    pub(crate) base_state: Item<'static, ExtensionState>,
42    // Map ProxyAddr -> WhitelistedTraders
43    pub traders: Map<'static, Addr, HashSet<Addr>>,
44    // Every DApp should use the provided ans_host contract for token/contract address resolution
45    /// Stores the extension version
46    pub target_os: Option<Core>,
47}
48
49/// Constructor
50impl<
51        Error: From<cosmwasm_std::StdError> + From<ExtensionError>,
52        CustomExecMsg,
53        CustomInitMsg,
54        CustomQueryMsg,
55        ReceiveMsg,
56    > ExtensionContract<Error, CustomExecMsg, CustomInitMsg, CustomQueryMsg, ReceiveMsg>
57{
58    pub const fn new(name: &'static str, version: &'static str) -> Self {
59        Self {
60            contract: AbstractContract::new(name, version),
61            base_state: Item::new(BASE_STATE),
62            traders: Map::new(TRADER_NAMESPACE),
63            target_os: None,
64        }
65    }
66
67    /// add dependencies to the contract
68    pub const fn with_dependencies(mut self, dependencies: &'static [&'static str]) -> Self {
69        self.contract = self.contract.with_dependencies(dependencies);
70        self
71    }
72
73    pub const fn with_replies(
74        mut self,
75        reply_handlers: &'static [(u64, ReplyHandlerFn<Self, Error>)],
76    ) -> Self {
77        self.contract = self.contract.with_replies([&[], reply_handlers]);
78        self
79    }
80
81    /// add IBC callback handler to contract
82    pub const fn with_ibc_callbacks(
83        mut self,
84        callbacks: &'static [(&'static str, IbcCallbackHandlerFn<Self, Error>)],
85    ) -> Self {
86        self.contract = self.contract.with_ibc_callbacks(callbacks);
87        self
88    }
89    pub const fn with_instantiate(
90        mut self,
91        instantiate_handler: InstantiateHandlerFn<Self, CustomInitMsg, Error>,
92    ) -> Self {
93        self.contract = self.contract.with_instantiate(instantiate_handler);
94        self
95    }
96
97    pub const fn with_receive(
98        mut self,
99        receive_handler: ReceiveHandlerFn<Self, ReceiveMsg, Error>,
100    ) -> Self {
101        self.contract = self.contract.with_receive(receive_handler);
102        self
103    }
104
105    pub const fn with_execute(
106        mut self,
107        execute_handler: ExecuteHandlerFn<Self, CustomExecMsg, Error>,
108    ) -> Self {
109        self.contract = self.contract.with_execute(execute_handler);
110        self
111    }
112
113    pub const fn with_query(mut self, query_handler: QueryHandlerFn<Self, CustomQueryMsg>) -> Self {
114        self.contract = self.contract.with_query(query_handler);
115        self
116    }
117
118    pub fn state(&self, store: &dyn Storage) -> StdResult<ExtensionState> {
119        self.base_state.load(store)
120    }
121
122    pub fn target(&self) -> Result<&Addr, ExtensionError> {
123        Ok(&self
124            .target_os
125            .as_ref()
126            .ok_or_else(|| StdError::generic_err("No target OS specified to execute on."))?
127            .proxy)
128    }
129}