abstract_interface/
ibc.rsuse crate::{AbstractInterfaceError, IbcClient, IbcHost, Registry};
use abstract_std::{IBC_CLIENT, IBC_HOST};
use cw_orch::prelude::*;
#[derive(Clone)]
pub struct AbstractIbc<Chain: CwEnv> {
pub client: IbcClient<Chain>,
pub host: IbcHost<Chain>,
}
impl<Chain: CwEnv> AbstractIbc<Chain> {
pub fn new(chain: &Chain) -> Self {
let ibc_client = IbcClient::new(IBC_CLIENT, chain.clone());
let ibc_host = IbcHost::new(IBC_HOST, chain.clone());
Self {
client: ibc_client,
host: ibc_host,
}
}
pub fn upload(&self) -> Result<(), crate::AbstractInterfaceError> {
self.client.upload()?;
self.host.upload()?;
Ok(())
}
pub fn instantiate(&self, admin: &Addr) -> Result<(), CwOrchError> {
self.client.instantiate(
&abstract_std::ibc_client::InstantiateMsg {},
Some(admin),
&[],
)?;
self.host
.instantiate(&abstract_std::ibc_host::InstantiateMsg {}, Some(admin), &[])?;
Ok(())
}
pub fn register(&self, registry: &Registry<Chain>) -> Result<(), AbstractInterfaceError> {
registry.register_natives(vec![
(
self.client.as_instance(),
ibc_client::contract::CONTRACT_VERSION.to_string(),
),
(
self.host.as_instance(),
ibc_host::contract::CONTRACT_VERSION.to_string(),
),
])
}
pub fn call_as(&self, sender: &<Chain as TxHandler>::Sender) -> Self {
Self {
client: self.client.call_as(sender),
host: self.host.call_as(sender),
}
}
}
#[cfg(feature = "interchain")]
pub mod connection {
use crate::Abstract;
use super::*;
use abstract_std::ibc_client::{ExecuteMsgFns, QueryMsgFns};
use abstract_std::ibc_host::ExecuteMsgFns as _;
use abstract_std::objects::TruncatedChainId;
use cw_orch::environment::Environment;
use cw_orch_interchain::prelude::*;
use cw_orch_polytone::interchain::PolytoneConnection;
impl<Chain: IbcQueryHandler> Abstract<Chain> {
pub fn connect_to<IBC: InterchainEnv<Chain>>(
&self,
remote_abstr: &Abstract<Chain>,
interchain: &IBC,
) -> Result<(), AbstractInterfaceError> {
connect_one_way_to(self, remote_abstr, interchain)?;
connect_one_way_to(remote_abstr, self, interchain)?;
Ok(())
}
}
impl AbstractIbc<Daemon> {
pub fn register_infrastructure(&self) -> Result<(), AbstractInterfaceError> {
let register_infrastructures =
connection::list_ibc_infrastructures(self.host.environment().clone());
for (chain, ibc_infrastructure) in register_infrastructures.counterparts {
use abstract_std::ibc_client::ExecuteMsgFns;
self.client.register_infrastructure(
chain,
ibc_infrastructure.remote_abstract_host,
ibc_infrastructure.polytone_note,
)?;
}
Ok(())
}
}
pub fn connect_one_way_to<Chain: IbcQueryHandler, IBC: InterchainEnv<Chain>>(
abstr_client: &Abstract<Chain>,
abstr_host: &Abstract<Chain>,
interchain: &IBC,
) -> Result<(), AbstractInterfaceError> {
let chain1_id = abstr_client.ibc.client.environment().chain_id();
let chain1_name = TruncatedChainId::from_chain_id(&chain1_id);
let chain2_id = abstr_host.ibc.client.environment().chain_id();
let chain2_name = TruncatedChainId::from_chain_id(&chain2_id);
let polytone_connection =
PolytoneConnection::deploy_between_if_needed(interchain, &chain1_id, &chain2_id)?;
let proxy_tx_result = abstr_client.ibc.client.register_infrastructure(
chain2_name.clone(),
abstr_host.ibc.host.address()?.to_string(),
polytone_connection.note.address()?.to_string(),
)?;
interchain.await_and_check_packets(&chain1_id, proxy_tx_result)?;
let proxy_address = abstr_client.ibc.client.host(chain2_name)?;
abstr_host
.ibc
.host
.register_chain_proxy(chain1_name, proxy_address.remote_polytone_proxy.unwrap())?;
Ok(())
}
pub fn list_ibc_infrastructures<Chain: CwEnv>(
chain: Chain,
) -> abstract_std::ibc_client::ListIbcInfrastructureResponse {
let Ok(polytone) = cw_orch_polytone::Polytone::load_from(chain) else {
return abstract_std::ibc_client::ListIbcInfrastructureResponse {
counterparts: vec![],
};
};
let abstract_state = crate::AbstractDaemonState::default();
let deployment_id = "default".to_owned();
let mut counterparts = vec![];
for connected_polytone in polytone.connected_polytones() {
let Ok(chain_info) = networks::parse_network(&connected_polytone.chain_id) else {
continue;
};
let chain_name = chain_info.network_info.chain_name.to_owned();
let env_info = EnvironmentInfo {
chain_id: connected_polytone.chain_id.clone(),
chain_name,
deployment_id: deployment_id.clone(),
};
if let Some(remote_abstract_host) = abstract_state.contract_addr(&env_info, IBC_HOST) {
let truncated_chain_id =
abstract_std::objects::TruncatedChainId::from_chain_id(&env_info.chain_id);
counterparts.push((
truncated_chain_id,
abstract_std::ibc_client::state::IbcInfrastructure {
polytone_note: connected_polytone.note,
remote_abstract_host: remote_abstract_host.into(),
remote_proxy: None,
},
))
}
}
abstract_std::ibc_client::ListIbcInfrastructureResponse { counterparts }
}
}
#[cfg(feature = "interchain")]
#[cfg(test)]
mod test {
use super::connection::*;
use super::*;
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";
#[test]
fn list_ibc() {
use networks::JUNO_1;
let juno = DaemonBuilder::new(JUNO_1)
.mnemonic(LOCAL_MNEMONIC)
.build()
.unwrap();
let l = list_ibc_infrastructures(juno);
l.counterparts.iter().any(|(chain_id, ibc_infra)| {
chain_id.as_str() == "osmosis"
&& ibc_infra.remote_abstract_host.starts_with("osmo")
&& ibc_infra.polytone_note.to_string().starts_with("juno")
});
}
}