abstract_ibc_host/endpoints/
packet.rs

1use abstract_sdk::feature_objects::RegistryContract;
2use abstract_std::{
3    base::ExecuteMsg as MiddlewareExecMsg,
4    ibc::{ModuleIbcInfo, ModuleIbcMsg},
5    ibc_client::InstalledModuleIdentification,
6    ibc_host::{
7        state::{ActionAfterCreationCache, TEMP_ACTION_AFTER_CREATION},
8        HelperAction, HostAction, InternalAction,
9    },
10    native_addrs,
11    objects::{
12        account::AccountTrace, module::ModuleInfo, module_reference::ModuleReference, AccountId,
13        TruncatedChainId,
14    },
15};
16use cosmwasm_std::{
17    to_json_vec, wasm_execute, Binary, ContractResult, Deps, DepsMut, Empty, Env, QueryRequest,
18    Response, StdError, SystemResult, WasmQuery,
19};
20
21use crate::{
22    account_commands::{self, receive_dispatch, receive_register, receive_send_all_back},
23    contract::HostResult,
24    HostError,
25};
26
27/// Handle actions that are passed to the IBC host contract
28/// This function is not permissioned and access control needs to be handled outside of it
29/// Usually the `src_chain` argument needs to be derived from the message sender
30pub fn handle_host_action(
31    deps: DepsMut,
32    env: Env,
33    src_chain: TruncatedChainId,
34    account_address: String,
35    received_account_id: AccountId,
36    host_action: HostAction,
37) -> HostResult {
38    // Push the client chain to the account trace
39    let account_id = {
40        let mut account_id = received_account_id.clone();
41        account_id.push_chain(src_chain.clone());
42        account_id
43    };
44
45    // get the local account information
46    match host_action {
47        HostAction::Internal(InternalAction::Register {
48            description,
49            link,
50            name,
51            namespace,
52            install_modules,
53        }) => receive_register(
54            deps,
55            env,
56            account_id,
57            name,
58            description,
59            link,
60            namespace,
61            install_modules,
62            false,
63            vec![],
64        ),
65
66        action => {
67            // If this account already exists, we can propagate the action
68            if let Ok(account) = account_commands::get_account(deps.as_ref(), &env, &account_id) {
69                match action {
70                    HostAction::Dispatch { account_msgs } => {
71                        receive_dispatch(deps, account, account_msgs)
72                    }
73                    HostAction::Helpers(helper_action) => match helper_action {
74                        HelperAction::SendAllBack => {
75                            receive_send_all_back(deps, env, account, account_address, src_chain)
76                        }
77                        _ => unimplemented!(""),
78                    },
79                    HostAction::Internal(InternalAction::Register { .. }) => {
80                        unreachable!("This action is handled above")
81                    }
82                    _ => unimplemented!(""),
83                }
84            } else {
85                // If no account is created already, we create one and execute the action on reply
86                // The account metadata are not set with this call
87                // One will have to change them at a later point if they decide to
88                let name = format!(
89                    "Remote Abstract Account for {}/{}",
90                    src_chain.as_str(),
91                    account_id
92                );
93
94                // We save the action they wanted to dispatch for the reply triggered by the receive_register function
95                TEMP_ACTION_AFTER_CREATION.save(
96                    deps.storage,
97                    &ActionAfterCreationCache {
98                        action,
99                        client_account_address: account_address,
100                        account_id: received_account_id,
101                        chain_name: src_chain,
102                    },
103                )?;
104                receive_register(
105                    deps,
106                    env,
107                    account_id,
108                    Some(name),
109                    None,
110                    None,
111                    None,
112                    vec![],
113                    true,
114                    vec![],
115                )
116            }
117        }
118    }
119    .map_err(Into::into)
120}
121
122/// Handle actions that are passed to the IBC host contract and originate from a registered module
123pub fn handle_module_execute(
124    deps: DepsMut,
125    env: Env,
126    src_chain: TruncatedChainId,
127    source_module: InstalledModuleIdentification,
128    target_module: ModuleInfo,
129    msg: Binary,
130) -> HostResult {
131    // We resolve the target module
132    let abstract_code_id =
133        native_addrs::abstract_code_id(&deps.querier, env.contract.address.clone())?;
134
135    let registry = RegistryContract::new(deps.as_ref(), abstract_code_id)?;
136
137    let target_module = InstalledModuleIdentification {
138        module_info: target_module,
139        // Account can only call modules that are installed on its ICAA.
140        // If the calling module is account-specific then we map the calling account-id to the host.
141        account_id: source_module
142            .account_id
143            .map(|a| client_to_host_module_account_id(&env, src_chain.clone(), a)),
144    };
145
146    let target_module_resolved = target_module.addr(deps.as_ref(), registry)?;
147
148    match target_module_resolved.reference {
149        ModuleReference::Account(_) | ModuleReference::Native(_) | ModuleReference::Service(_) => {
150            return Err(HostError::WrongModuleAction(
151                "Can't send module-to-module message to an account, service or a native module"
152                    .to_string(),
153            ))
154        }
155        _ => {}
156    }
157
158    let response = Response::new().add_attribute("action", "module-ibc-call");
159    // We pass the message on to the module
160    let msg = wasm_execute(
161        target_module_resolved.address,
162        &MiddlewareExecMsg::ModuleIbc::<Empty, Empty>(ModuleIbcMsg {
163            src_module_info: ModuleIbcInfo {
164                chain: src_chain,
165                module: source_module.module_info,
166            },
167            msg,
168        }),
169        vec![],
170    )?;
171
172    Ok(response.add_message(msg))
173}
174
175/// Handle actions that are passed to the IBC host contract and originate from a registered module
176pub fn handle_host_module_query(
177    deps: Deps,
178    env: Env,
179    target_module: InstalledModuleIdentification,
180    msg: Binary,
181) -> HostResult<Binary> {
182    // We resolve the target module
183    let abstract_code_id =
184        native_addrs::abstract_code_id(&deps.querier, env.contract.address.clone())?;
185
186    let registry = RegistryContract::new(deps, abstract_code_id)?;
187
188    let target_module_resolved = target_module.addr(deps, registry)?;
189
190    let query = QueryRequest::<Empty>::from(WasmQuery::Smart {
191        contract_addr: target_module_resolved.address.into_string(),
192        msg,
193    });
194    let bin = match deps.querier.raw_query(&to_json_vec(&query)?) {
195        SystemResult::Err(system_err) => Err(StdError::generic_err(format!(
196            "Querier system error: {system_err}"
197        ))),
198        SystemResult::Ok(ContractResult::Err(contract_err)) => Err(StdError::generic_err(format!(
199            "Querier contract error: {contract_err}"
200        ))),
201        SystemResult::Ok(ContractResult::Ok(value)) => Ok(value),
202    }?;
203    Ok(bin)
204}
205
206/// We need to figure what trace module is implying here
207pub fn client_to_host_module_account_id(
208    env: &Env,
209    remote_chain: TruncatedChainId,
210    mut account_id: AccountId,
211) -> AccountId {
212    let account_trace = account_id.trace_mut();
213    match account_trace {
214        AccountTrace::Local => account_trace.push_chain(remote_chain),
215        AccountTrace::Remote(trace) => {
216            let current_chain_name = TruncatedChainId::from_chain_id(&env.block.chain_id);
217            // If current chain_name == last trace in account_id it means we got response back from remote chain
218            if current_chain_name.eq(trace.last().unwrap()) {
219                trace.pop();
220                if trace.is_empty() {
221                    *account_trace = AccountTrace::Local;
222                }
223            } else {
224                trace.push(remote_chain);
225            }
226        }
227    };
228    account_id
229}