abstract_interface/
ibc.rs1use crate::{AbstractInterfaceError, IbcClient, IbcHost, Registry};
2use abstract_std::{IBC_CLIENT, IBC_HOST};
3use cw_orch::prelude::*;
4
5#[derive(Clone)]
6pub struct AbstractIbc<Chain: CwEnv> {
7 pub client: IbcClient<Chain>,
8 pub host: IbcHost<Chain>,
9}
10
11impl<Chain: CwEnv> AbstractIbc<Chain> {
12 pub fn new(chain: &Chain) -> Self {
13 let ibc_client = IbcClient::new(IBC_CLIENT, chain.clone());
14 let ibc_host = IbcHost::new(IBC_HOST, chain.clone());
15 Self {
16 client: ibc_client,
17 host: ibc_host,
18 }
19 }
20
21 pub fn upload(&self) -> Result<(), crate::AbstractInterfaceError> {
22 self.client.upload()?;
23 self.host.upload()?;
24 Ok(())
25 }
26
27 pub fn instantiate(&self, admin: &Addr) -> Result<(), CwOrchError> {
28 self.client.instantiate(
29 &abstract_std::ibc_client::InstantiateMsg {},
30 Some(admin),
31 &[],
32 )?;
33
34 self.host
35 .instantiate(&abstract_std::ibc_host::InstantiateMsg {}, Some(admin), &[])?;
36 Ok(())
37 }
38
39 pub fn register(&self, registry: &Registry<Chain>) -> Result<(), AbstractInterfaceError> {
40 registry.register_natives(vec![
41 (
42 self.client.as_instance(),
43 ibc_client::contract::CONTRACT_VERSION.to_string(),
44 ),
45 (
46 self.host.as_instance(),
47 ibc_host::contract::CONTRACT_VERSION.to_string(),
48 ),
49 ])
50 }
51
52 pub fn call_as(&self, sender: &<Chain as TxHandler>::Sender) -> Self {
53 Self {
54 client: self.client.call_as(sender),
55 host: self.host.call_as(sender),
56 }
57 }
58}
59
60#[cfg(feature = "interchain")]
61pub mod connection {
63 use crate::Abstract;
64
65 use super::*;
66 use abstract_std::ibc_client::{ExecuteMsgFns, QueryMsgFns};
67 use abstract_std::ibc_host::ExecuteMsgFns as _;
68 use abstract_std::objects::TruncatedChainId;
69 use cw_orch::environment::Environment;
70 use cw_orch_interchain::prelude::*;
71 use cw_orch_polytone::interchain::PolytoneConnection;
72
73 impl<Chain: IbcQueryHandler> Abstract<Chain> {
74 pub fn connect_to<IBC: InterchainEnv<Chain>>(
80 &self,
81 remote_abstr: &Abstract<Chain>,
82 interchain: &IBC,
83 ) -> Result<(), AbstractInterfaceError> {
84 connect_one_way_to(self, remote_abstr, interchain)?;
85 connect_one_way_to(remote_abstr, self, interchain)?;
86 Ok(())
87 }
88
89 pub fn disconnect_from(
91 &self,
92 remote_abstr: &Abstract<Chain>,
93 ) -> Result<(), AbstractInterfaceError> {
94 disconnect_one_way_from(self, remote_abstr)?;
95 disconnect_one_way_from(remote_abstr, self)?;
96 Ok(())
97 }
98 }
99
100 impl AbstractIbc<Daemon> {
101 pub fn register_infrastructure(&self) -> Result<(), AbstractInterfaceError> {
103 let register_infrastructures =
104 connection::list_ibc_infrastructures(self.host.environment().clone());
105
106 for (chain, ibc_infrastructure) in register_infrastructures.counterparts {
107 use abstract_std::ibc_client::ExecuteMsgFns;
108
109 self.client.register_infrastructure(
110 chain,
111 ibc_infrastructure.remote_abstract_host,
112 ibc_infrastructure.polytone_note,
113 )?;
114 }
115 Ok(())
116 }
117 }
118
119 pub fn connect_one_way_to<Chain: IbcQueryHandler, IBC: InterchainEnv<Chain>>(
120 abstr_client: &Abstract<Chain>,
121 abstr_host: &Abstract<Chain>,
122 interchain: &IBC,
123 ) -> Result<(), AbstractInterfaceError> {
124 let chain1_id = abstr_client.ibc.client.environment().chain_id();
126 let chain1_name = TruncatedChainId::from_chain_id(&chain1_id);
127
128 let chain2_id = abstr_host.ibc.client.environment().chain_id();
129 let chain2_name = TruncatedChainId::from_chain_id(&chain2_id);
130
131 let polytone_connection =
133 PolytoneConnection::deploy_between_if_needed(interchain, &chain1_id, &chain2_id)?;
134
135 let proxy_tx_result = abstr_client.ibc.client.register_infrastructure(
139 chain2_name.clone(),
140 abstr_host.ibc.host.address()?.to_string(),
141 polytone_connection.note.address()?.to_string(),
142 )?;
143 interchain.await_and_check_packets(&chain1_id, proxy_tx_result)?;
145
146 let proxy_address = abstr_client.ibc.client.host(chain2_name)?;
148
149 abstr_host
150 .ibc
151 .host
152 .register_chain_proxy(chain1_name, proxy_address.remote_polytone_proxy.unwrap())?;
153
154 Ok(())
155 }
156
157 pub fn disconnect_one_way_from<Chain: IbcQueryHandler>(
158 abstr_client: &Abstract<Chain>,
159 abstr_host: &Abstract<Chain>,
160 ) -> Result<(), AbstractInterfaceError> {
161 let chain1_id = abstr_client.ibc.client.environment().chain_id();
163 let chain1_name = TruncatedChainId::from_chain_id(&chain1_id);
164
165 let chain2_id = abstr_host.ibc.client.environment().chain_id();
166 let chain2_name = TruncatedChainId::from_chain_id(&chain2_id);
167
168 abstr_client.ibc.client.remove_host(chain2_name.clone())?;
170
171 abstr_host.ibc.host.remove_chain_proxy(chain1_name)?;
173
174 Ok(())
175 }
176
177 pub fn list_ibc_infrastructures<Chain: CwEnv>(
178 chain: Chain,
179 ) -> abstract_std::ibc_client::ListIbcInfrastructureResponse {
180 let Ok(polytone) = cw_orch_polytone::Polytone::load_from(chain) else {
181 return abstract_std::ibc_client::ListIbcInfrastructureResponse {
182 counterparts: vec![],
183 };
184 };
185 let abstract_state = crate::AbstractDaemonState::default();
186
187 let mut counterparts = vec![];
188 for connected_polytone in polytone.connected_polytones() {
189 if let Some(remote_abstract_host) =
190 abstract_state.contract_addr(&connected_polytone.chain_id, IBC_HOST)
191 {
192 let truncated_chain_id = abstract_std::objects::TruncatedChainId::from_chain_id(
193 &connected_polytone.chain_id,
194 );
195 counterparts.push((
196 truncated_chain_id,
197 abstract_std::ibc_client::state::IbcInfrastructure {
198 polytone_note: connected_polytone.note,
199 remote_abstract_host: remote_abstract_host.into(),
200 remote_proxy: None,
201 },
202 ))
203 }
204 }
205 abstract_std::ibc_client::ListIbcInfrastructureResponse { counterparts }
206 }
207}
208
209#[cfg(feature = "interchain")]
210#[cfg(test)]
211mod test {
212 use super::connection::*;
213 use super::*;
214
215 const LOCAL_MNEMONIC: &str = "clip hire initial neck maid actor venue client foam budget lock catalog sweet steak waste crater broccoli pipe steak sister coyote moment obvious choose";
217
218 #[test]
219 fn list_ibc() {
220 use networks::JUNO_1;
221
222 let juno = DaemonBuilder::new(JUNO_1)
224 .mnemonic(LOCAL_MNEMONIC)
225 .build()
226 .unwrap();
227 let l = list_ibc_infrastructures(juno);
228 l.counterparts.iter().any(|(chain_id, ibc_infra)| {
229 chain_id.as_str() == "osmosis"
230 && ibc_infra.remote_abstract_host.starts_with("osmo")
231 && ibc_infra.polytone_note.to_string().starts_with("juno")
232 });
233 }
234}