1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
use crate::{
    state::{ContractError, ACCOUNTS, CLIENT_PROXY, PENDING, PROCESSING_PACKET, RESULTS},
    Host, HostError,
};
use abstract_sdk::{
    base::{Handler, ReplyEndpoint},
    core::{
        abstract_ica::{DispatchResponse, RegisterResponse, StdAck},
        ibc_host::PacketMsg,
    },
};
use cosmwasm_std::{DepsMut, Empty, Env, Reply, Response};
use cw_utils::parse_reply_instantiate_data;

pub const RECEIVE_DISPATCH_ID: u64 = 1234;
pub const INIT_CALLBACK_ID: u64 = 7890;

impl<
        Error: ContractError,
        CustomInitMsg,
        CustomExecMsg,
        CustomQueryMsg,
        CustomMigrateMsg,
        SudoMsg,
        ReceiveMsg,
    > ReplyEndpoint
    for Host<
        Error,
        CustomInitMsg,
        CustomExecMsg,
        CustomQueryMsg,
        CustomMigrateMsg,
        SudoMsg,
        ReceiveMsg,
    >
{
    fn reply(mut self, deps: DepsMut, env: Env, msg: Reply) -> Result<Response, Self::Error> {
        let id = msg.id;
        let maybe_handler = self.maybe_reply_handler(id);
        if let Some(reply_fn) = maybe_handler {
            reply_fn(deps, env, self, msg)
        } else {
            let (packet, channel) = PROCESSING_PACKET.load(deps.storage)?;
            PROCESSING_PACKET.remove(deps.storage);
            let PacketMsg {
                client_chain,
                account_id,
                ..
            } = packet;
            let client_proxy_addr = CLIENT_PROXY.load(deps.storage, (&channel, account_id))?;
            let local_proxy_addr = ACCOUNTS.load(deps.storage, (&channel, account_id))?;
            self.proxy_address = Some(local_proxy_addr);
            // send everything back to client
            let send_back_msg =
                self.send_all_back(deps.as_ref(), env, client_proxy_addr, client_chain)?;

            Ok(Response::new()
                .add_message(send_back_msg)
                .set_data(StdAck::success(&Empty {})))
        }
    }
}

pub fn reply_dispatch_callback<
    Error: ContractError,
    CustomExecMsg,
    CustomInitMsg,
    CustomQueryMsg,
    CustomMigrateMsg,
    SudoMsg,
    ReceiveMsg,
>(
    deps: DepsMut,
    _env: Env,
    _host: Host<
        Error,
        CustomInitMsg,
        CustomExecMsg,
        CustomQueryMsg,
        CustomMigrateMsg,
        SudoMsg,
        ReceiveMsg,
    >,
    reply: Reply,
) -> Result<Response, Error> {
    // add the new result to the current tracker
    let mut results = RESULTS.load(deps.storage)?;
    results.push(reply.result.unwrap().data.unwrap_or_default());
    RESULTS.save(deps.storage, &results)?;

    // update result data if this is the last
    let data = StdAck::success(&DispatchResponse { results });
    Ok(Response::new().set_data(data))
}

pub fn reply_init_callback<
    Error: ContractError,
    CustomExecMsg,
    CustomInitMsg,
    CustomQueryMsg,
    CustomMigrateMsg,
    SudoMsg,
    ReceiveMsg,
>(
    deps: DepsMut,
    _env: Env,
    _host: Host<
        Error,
        CustomInitMsg,
        CustomExecMsg,
        CustomQueryMsg,
        CustomMigrateMsg,
        SudoMsg,
        ReceiveMsg,
    >,

    reply: Reply,
) -> Result<Response, Error> {
    // we use storage to pass info from the caller to the reply
    let (channel, account_id) = PENDING.load(deps.storage)?;
    PENDING.remove(deps.storage);

    // parse contract info from data
    let raw_addr = parse_reply_instantiate_data(reply)
        .map_err(HostError::from)?
        .contract_address;
    let contract_addr = deps.api.addr_validate(&raw_addr)?;

    if ACCOUNTS
        .may_load(deps.storage, (&channel, account_id))?
        .is_some()
    {
        return Err(HostError::ChannelAlreadyRegistered.into());
    }
    ACCOUNTS.save(deps.storage, (&channel, account_id), &contract_addr)?;
    let data = StdAck::success(&RegisterResponse {
        account: contract_addr.into_string(),
    });
    Ok(Response::new().set_data(data))
}