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
use ibc_relayer::chain::handle::ChainHandle;
use ibc_relayer::chain::tracking::TrackedMsgs;
use ibc_relayer_types::applications::ics27_ica::msgs::register::MsgRegisterInterchainAccount;
use ibc_relayer_types::core::ics04_channel::version::Version;
use ibc_relayer_types::events::IbcEvent;
use ibc_relayer_types::tx_msg::Msg;

use crate::chain::cli::ica::{query_interchain_account, register_interchain_account_cli};
use crate::chain::driver::ChainDriver;
use crate::error::Error;
use crate::prelude::*;
use crate::types::tagged::*;
use crate::types::wallet::WalletAddress;

pub trait InterchainAccountMethodsExt<Chain> {
    fn register_interchain_account_cli<Counterparty>(
        &self,
        from: &MonoTagged<Chain, &WalletAddress>,
        connection_id: &TaggedConnectionIdRef<Chain, Counterparty>,
    ) -> Result<(), Error>;

    fn query_interchain_account<Counterparty>(
        &self,
        from: &MonoTagged<Chain, &WalletAddress>,
        connection_id: &TaggedConnectionIdRef<Chain, Counterparty>,
    ) -> Result<MonoTagged<Counterparty, WalletAddress>, Error>;
}

impl<'a, Chain: Send> InterchainAccountMethodsExt<Chain> for MonoTagged<Chain, &'a ChainDriver> {
    fn register_interchain_account_cli<Counterparty>(
        &self,
        from: &MonoTagged<Chain, &WalletAddress>,
        connection_id: &TaggedConnectionIdRef<Chain, Counterparty>,
    ) -> Result<(), Error> {
        let driver = *self.value();
        register_interchain_account_cli(
            driver.chain_id.as_str(),
            &driver.command_path,
            &driver.home_path,
            &driver.rpc_listen_address(),
            from.value().as_str(),
            connection_id.value().as_str(),
        )
    }

    fn query_interchain_account<Counterparty>(
        &self,
        from: &MonoTagged<Chain, &WalletAddress>,
        connection_id: &TaggedConnectionIdRef<Chain, Counterparty>,
    ) -> Result<MonoTagged<Counterparty, WalletAddress>, Error> {
        let driver = *self.value();
        let address = query_interchain_account(
            driver.chain_id.as_str(),
            &driver.command_path,
            &driver.home_path,
            &driver.rpc_listen_address(),
            from.value().as_str(),
            connection_id.value().as_str(),
        )?;

        Ok(MonoTagged::new(WalletAddress(address)))
    }
}

#[allow(clippy::type_complexity)]
pub fn register_interchain_account<Chain: ChainHandle, Counterparty: ChainHandle>(
    chain: &MonoTagged<Chain, FullNode>,
    handle: &Chain,
    connection: &ConnectedConnection<Chain, Counterparty>,
) -> Result<
    (
        MonoTagged<Chain, Wallet>,
        TaggedChannelId<Chain, Counterparty>,
        TaggedPortId<Chain, Counterparty>,
    ),
    Error,
> {
    let wallet = chain.wallets().relayer().cloned();

    let owner = handle.get_signer()?;

    let version_str = format!("{{\"version\":\"ics27-1\",\"encoding\":\"proto3\",\"tx_type\":\"sdk_multi_msg\",\"controller_connection_id\":\"{}\",\"host_connection_id\":\"{}\"}}", connection.connection_id_a.0, connection.connection_id_b.0);
    let msg = MsgRegisterInterchainAccount {
        owner,
        connection_id: connection.connection_id_a.0.clone(),
        version: Version::new(version_str),
    };

    let msg_any = msg.to_any();

    let tm = TrackedMsgs::new_static(vec![msg_any], "RegisterInterchainAccount");

    let events = handle
        .send_messages_and_wait_commit(tm)
        .map_err(Error::relayer)?;

    for event in events.iter() {
        if let IbcEvent::OpenInitChannel(open_init) = &event.event {
            let channel_id = open_init.channel_id.clone().ok_or(()).map_err(|_| Error::generic(eyre!("channel_id is empty in the event response after sending  MsgRegisterInterchainAccount")))?;
            return Ok((
                wallet,
                TaggedChannelId::new(channel_id),
                TaggedPortId::new(open_init.port_id.clone()),
            ));
        }
    }

    Err(Error::generic(eyre!("could not retrieve an OpenInitChannel event resonse after sending MsgRegisterInterchainAccount")))
}