1use abstract_macros::abstract_response;
2use abstract_sdk::{
3 feature_objects::AnsHost,
4 std::{
5 objects::account::ACCOUNT_ID,
6 proxy::{
7 state::{State, ADMIN, ANS_HOST, STATE},
8 AssetConfigResponse, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg,
9 },
10 PROXY,
11 },
12};
13use abstract_std::objects::{
14 module_version::assert_contract_upgrade, oracle::Oracle, price_source::UncheckedPriceSource,
15};
16use cosmwasm_std::{
17 to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, SubMsgResult,
18};
19use semver::Version;
20
21use crate::{commands::*, error::ProxyError, queries::*, reply};
22
23pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
24pub(crate) const RESPONSE_REPLY_ID: u64 = 1;
25
26#[abstract_response(PROXY)]
27pub struct ProxyResponse;
28
29pub type ProxyResult<T = Response> = Result<T, ProxyError>;
31
32#[cfg_attr(feature = "export", cosmwasm_std::entry_point)]
38pub fn instantiate(
39 mut deps: DepsMut,
40 _env: Env,
41 _info: MessageInfo,
42 msg: InstantiateMsg,
43) -> ProxyResult {
44 cw2::set_contract_version(deps.storage, PROXY, CONTRACT_VERSION)?;
46
47 let manager_addr = deps.api.addr_validate(&msg.manager_addr)?;
48 ACCOUNT_ID.save(deps.storage, &msg.account_id)?;
49 STATE.save(
50 deps.storage,
51 &State {
52 modules: vec![manager_addr.clone()],
53 },
54 )?;
55 let ans_host = AnsHost {
56 address: deps.api.addr_validate(&msg.ans_host_address)?,
57 };
58 ANS_HOST.save(deps.storage, &ans_host)?;
59 let admin_addr = Some(manager_addr);
60 ADMIN.set(deps.branch(), admin_addr)?;
61
62 if let Some(base_asset) = msg.base_asset {
63 let oracle = Oracle::new();
64 oracle.update_assets(
65 deps,
66 &ans_host,
67 vec![(base_asset, UncheckedPriceSource::None)],
68 vec![],
69 )?;
70 }
71 Ok(Response::default())
72}
73
74#[cfg_attr(feature = "export", cosmwasm_std::entry_point)]
75pub fn execute(deps: DepsMut, _env: Env, info: MessageInfo, msg: ExecuteMsg) -> ProxyResult {
76 match msg {
77 ExecuteMsg::ModuleAction { msgs } => execute_module_action(deps, info, msgs),
78 ExecuteMsg::ModuleActionWithData { msg } => execute_module_action_response(deps, info, msg),
79 ExecuteMsg::IbcAction { msg } => execute_ibc_action(deps, info, msg),
80 ExecuteMsg::SetAdmin { admin } => set_admin(deps, info, &admin),
81 ExecuteMsg::AddModules { modules } => add_modules(deps, info, modules),
82 ExecuteMsg::RemoveModule { module } => remove_module(deps, info, module),
83 ExecuteMsg::UpdateAssets { to_add, to_remove } => {
84 update_assets(deps, info, to_add, to_remove)
85 }
86 }
87}
88
89#[cfg_attr(feature = "export", cosmwasm_std::entry_point)]
90pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> ProxyResult {
91 let version: Version = CONTRACT_VERSION.parse().unwrap();
92
93 assert_contract_upgrade(deps.storage, PROXY, version)?;
94 cw2::set_contract_version(deps.storage, PROXY, CONTRACT_VERSION)?;
95 Ok(Response::default())
96}
97
98#[cfg_attr(feature = "export", cosmwasm_std::entry_point)]
99pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> ProxyResult<Binary> {
100 match msg {
101 QueryMsg::Config {} => to_json_binary(&query_config(deps)?),
102 QueryMsg::TotalValue {} => to_json_binary(&query_total_value(deps, env)?),
103 QueryMsg::HoldingAmount { identifier } => {
104 to_json_binary(&query_holding_amount(deps, env, identifier)?)
105 }
106 QueryMsg::TokenValue { identifier } => {
107 to_json_binary(&query_token_value(deps, env, identifier)?)
108 }
109 QueryMsg::AssetConfig { identifier } => to_json_binary(&AssetConfigResponse {
110 price_source: Oracle::new().asset_config(deps, &identifier)?,
111 }),
112 QueryMsg::AssetsConfig { start_after, limit } => {
113 to_json_binary(&query_oracle_asset_config(deps, start_after, limit)?)
114 }
115 QueryMsg::AssetsInfo { start_after, limit } => {
116 to_json_binary(&query_oracle_asset_info(deps, start_after, limit)?)
117 }
118 QueryMsg::BaseAsset {} => to_json_binary(&query_base_asset(deps)?),
119 }
120 .map_err(Into::into)
121}
122
123#[cfg_attr(feature = "export", cosmwasm_std::entry_point)]
125pub fn reply(_deps: DepsMut, _env: Env, msg: Reply) -> ProxyResult {
126 match &msg {
127 Reply {
128 id: RESPONSE_REPLY_ID,
129 result: SubMsgResult::Ok(_),
130 } => reply::forward_response_data(msg),
131 _ => Err(ProxyError::UnexpectedReply {}),
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use cosmwasm_std::testing::*;
138 use speculoos::prelude::*;
139
140 use super::*;
141 use crate::{contract, test_common::*};
142
143 mod migrate {
144 use abstract_std::AbstractError;
145
146 use super::*;
147
148 #[test]
149 fn disallow_same_version() -> ProxyResult<()> {
150 let mut deps = mock_dependencies();
151 mock_init(deps.as_mut());
152
153 let version: Version = CONTRACT_VERSION.parse().unwrap();
154
155 let res = contract::migrate(deps.as_mut(), mock_env(), MigrateMsg {});
156
157 assert_that!(res).is_err().is_equal_to(ProxyError::Abstract(
158 AbstractError::CannotDowngradeContract {
159 contract: PROXY.to_string(),
160 from: version.clone(),
161 to: version,
162 },
163 ));
164
165 Ok(())
166 }
167
168 #[test]
169 fn disallow_downgrade() -> ProxyResult<()> {
170 let mut deps = mock_dependencies();
171 mock_init(deps.as_mut());
172
173 let big_version = "999.999.999";
174 cw2::set_contract_version(deps.as_mut().storage, PROXY, big_version)?;
175
176 let version: Version = CONTRACT_VERSION.parse().unwrap();
177
178 let res = contract::migrate(deps.as_mut(), mock_env(), MigrateMsg {});
179
180 assert_that!(res).is_err().is_equal_to(ProxyError::Abstract(
181 AbstractError::CannotDowngradeContract {
182 contract: PROXY.to_string(),
183 from: big_version.parse().unwrap(),
184 to: version,
185 },
186 ));
187
188 Ok(())
189 }
190
191 #[test]
192 fn disallow_name_change() -> ProxyResult<()> {
193 let mut deps = mock_dependencies();
194 mock_init(deps.as_mut());
195
196 let old_version = "0.0.0";
197 let old_name = "old:contract";
198 cw2::set_contract_version(deps.as_mut().storage, old_name, old_version)?;
199
200 let res = contract::migrate(deps.as_mut(), mock_env(), MigrateMsg {});
201
202 assert_that!(res).is_err().is_equal_to(ProxyError::Abstract(
203 AbstractError::ContractNameMismatch {
204 from: old_name.parse().unwrap(),
205 to: PROXY.parse().unwrap(),
206 },
207 ));
208
209 Ok(())
210 }
211
212 #[test]
213 fn works() -> ProxyResult<()> {
214 let mut deps = mock_dependencies();
215 mock_init(deps.as_mut());
216
217 let version: Version = CONTRACT_VERSION.parse().unwrap();
218
219 let small_version = Version {
220 minor: version.minor - 1,
221 ..version.clone()
222 }
223 .to_string();
224 cw2::set_contract_version(deps.as_mut().storage, PROXY, small_version)?;
225
226 let res = contract::migrate(deps.as_mut(), mock_env(), MigrateMsg {})?;
227 assert_that!(res.messages).has_length(0);
228
229 assert_that!(cw2::get_contract_version(&deps.storage)?.version)
230 .is_equal_to(version.to_string());
231 Ok(())
232 }
233 }
234}