abstract_ibc_host/
account_commands.rs

1use abstract_sdk::{
2    feature_objects::{AnsHost, RegistryContract},
3    std::{objects::ChannelEntry, ICS20},
4    Resolve,
5};
6use abstract_std::{
7    account::{self, ModuleInstallConfig},
8    ibc::PACKET_LIFETIME,
9    native_addrs,
10    objects::{module::ModuleInfo, module_reference::ModuleReference, AccountId, TruncatedChainId},
11    registry::Account,
12    ACCOUNT,
13};
14use cosmwasm_std::{
15    instantiate2_address, to_json_binary, wasm_execute, Coin, CosmosMsg, Deps, DepsMut, Empty, Env,
16    IbcMsg, Response, SubMsg, WasmMsg,
17};
18
19use crate::{
20    contract::{HostResponse, HostResult},
21    endpoints::reply::{INIT_BEFORE_ACTION_REPLY_ID, RESPONSE_REPLY_ID},
22    HostError,
23};
24
25/// Creates and registers account for remote Account
26#[allow(clippy::too_many_arguments)]
27pub fn receive_register(
28    deps: DepsMut,
29    env: Env,
30    account_id: AccountId,
31    name: Option<String>,
32    description: Option<String>,
33    link: Option<String>,
34    namespace: Option<String>,
35    install_modules: Vec<ModuleInstallConfig>,
36    with_reply: bool,
37    funds: Vec<Coin>,
38) -> HostResult {
39    let abstract_code_id =
40        native_addrs::abstract_code_id(&deps.querier, env.contract.address.clone())?;
41
42    let registry = RegistryContract::new(deps.as_ref(), abstract_code_id)?;
43    // verify that the origin last chain is the chain related to this channel, and that it is not `Local`
44    account_id.trace().verify_remote()?;
45    let salt = cosmwasm_std::to_json_binary(&account_id)?;
46
47    let account_module_info = ModuleInfo::from_id_latest(ACCOUNT)?;
48    let ModuleReference::Account(code_id) = registry
49        .query_module(account_module_info.clone(), &deps.querier)?
50        .reference
51    else {
52        return Err(HostError::RegistryError(
53            abstract_std::objects::registry::RegistryError::InvalidReference(account_module_info),
54        ));
55    };
56    let checksum = deps.querier.query_wasm_code_info(code_id)?.checksum;
57    let self_canon_addr = deps.api.addr_canonicalize(env.contract.address.as_str())?;
58
59    let create_account_msg = account::InstantiateMsg::<cosmwasm_std::Empty> {
60        code_id,
61        owner: abstract_std::objects::gov_type::GovernanceDetails::External {
62            governance_address: env.contract.address.into_string(),
63            governance_type: "abstract-ibc".into(), // at least 4 characters
64        },
65        name,
66        description,
67        link,
68        // provide the origin chain id
69        account_id: Some(account_id.clone()),
70        install_modules,
71        namespace,
72        authenticator: None,
73    };
74
75    let account_canon_addr =
76        instantiate2_address(checksum.as_slice(), &self_canon_addr, salt.as_slice())?;
77    let account_addr = deps.api.addr_humanize(&account_canon_addr)?;
78
79    // create the message to instantiate the remote account
80    let account_creation_message = WasmMsg::Instantiate2 {
81        admin: Some(account_addr.to_string()),
82        code_id,
83        label: account_id.to_string(),
84        msg: to_json_binary(&create_account_msg)?,
85        funds,
86        salt,
87    };
88
89    // If we were ordered to have a reply after account creation
90    let sub_msg = if with_reply {
91        SubMsg::reply_on_success(account_creation_message, INIT_BEFORE_ACTION_REPLY_ID)
92    } else {
93        SubMsg::new(account_creation_message)
94    };
95
96    Ok(Response::new()
97        .add_submessage(sub_msg)
98        .add_attribute("action", "register"))
99}
100
101/// Execute account message on local account.
102pub fn receive_dispatch(
103    _deps: DepsMut,
104    account: Account,
105    account_msgs: Vec<account::ExecuteMsg>,
106) -> HostResult {
107    // execute the message on the account
108    let msgs = account_msgs
109        .into_iter()
110        .map(|msg| wasm_execute(account.addr(), &msg, vec![]))
111        .collect::<Result<Vec<_>, _>>()?;
112
113    let response = Response::new()
114        .add_attribute("action", "receive_dispatch")
115        // This is used to forward the data of the calling message
116        // This means that only the last present data of will be forwarded
117        .add_submessages(
118            msgs.into_iter()
119                .map(|m| SubMsg::reply_on_success(m.clone(), RESPONSE_REPLY_ID)),
120        );
121
122    Ok(response)
123}
124
125/// processes PacketMsg::SendAllBack variant
126pub fn receive_send_all_back(
127    deps: DepsMut,
128    env: Env,
129    account: Account,
130    client_account_address: String,
131    src_chain: TruncatedChainId,
132) -> HostResult {
133    let wasm_msg = send_all_back(
134        deps.as_ref(),
135        env,
136        account,
137        client_account_address,
138        src_chain,
139    )?;
140
141    Ok(HostResponse::action("receive_dispatch").add_message(wasm_msg))
142}
143
144/// construct the msg to send all the assets back
145pub fn send_all_back(
146    deps: Deps,
147    env: Env,
148    account: Account,
149    client_account_address: String,
150    src_chain: TruncatedChainId,
151) -> Result<CosmosMsg, HostError> {
152    // get the ICS20 channel information
153    let abstract_code_id =
154        native_addrs::abstract_code_id(&deps.querier, env.contract.address.clone())?;
155
156    let ans = AnsHost::new(deps, abstract_code_id)?;
157    let ics20_channel_entry = ChannelEntry {
158        connected_chain: src_chain,
159        protocol: ICS20.to_string(),
160    };
161    let ics20_channel_id = ics20_channel_entry.resolve(&deps.querier, &ans)?;
162    // get all the coins for the account
163    let coins = deps.querier.query_all_balances(account.addr())?;
164    // Construct ics20 messages to send all the coins back
165    let mut msgs: Vec<CosmosMsg> = vec![];
166    for coin in coins {
167        msgs.push(
168            IbcMsg::Transfer {
169                channel_id: ics20_channel_id.clone(),
170                to_address: client_account_address.to_string(),
171                amount: coin,
172                timeout: env.block.time.plus_seconds(PACKET_LIFETIME).into(),
173                memo: None,
174            }
175            .into(),
176        )
177    }
178    // call the message to send everything back through the account
179    let account_msg = wasm_execute(
180        account.into_addr(),
181        &account::ExecuteMsg::<Empty>::Execute { msgs },
182        vec![],
183    )?;
184    Ok(account_msg.into())
185}
186
187/// get the account from the registry contract
188pub fn get_account(deps: Deps, env: &Env, account_id: &AccountId) -> Result<Account, HostError> {
189    let abstract_code_id =
190        native_addrs::abstract_code_id(&deps.querier, env.contract.address.clone())?;
191
192    let registry = RegistryContract::new(deps, abstract_code_id)?;
193    let account = registry.account(account_id, &deps.querier)?;
194    Ok(account)
195}