use crate::{cosmos_modules, error::DaemonError, Daemon};
use cosmos_modules::ibc_channel;
use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest;
use cosmrs::proto::ibc::{
applications::transfer::v1::{DenomTrace, QueryDenomHashResponse, QueryDenomTraceResponse},
core::{
channel::v1::QueryPacketCommitmentResponse,
client::v1::{IdentifiedClientState, QueryClientStatesResponse},
connection::v1::{ConnectionEnd, IdentifiedConnection, State},
},
lightclients::tendermint::v1::ClientState,
};
use cw_orch_core::environment::{Querier, QuerierGetter};
use prost::Message;
use tokio::runtime::Handle;
use tonic::transport::Channel;
pub struct Ibc {
pub channel: Channel,
pub rt_handle: Option<Handle>,
}
impl Ibc {
pub fn new(daemon: &Daemon) -> Self {
Self {
channel: daemon.channel(),
rt_handle: Some(daemon.rt_handle.clone()),
}
}
pub fn new_async(channel: Channel) -> Self {
Self {
channel,
rt_handle: None,
}
}
}
impl Querier for Ibc {
type Error = DaemonError;
}
impl QuerierGetter<Ibc> for Daemon {
fn querier(&self) -> Ibc {
Ibc::new(self)
}
}
impl Ibc {
pub async fn _denom_trace(&self, hash: String) -> Result<DenomTrace, DaemonError> {
let denom_trace: QueryDenomTraceResponse = cosmos_query!(
self,
ibc_transfer,
denom_trace,
QueryDenomTraceRequest { hash: hash }
);
Ok(denom_trace.denom_trace.unwrap())
}
pub async fn _denom_hash(&self, trace: String) -> Result<String, DaemonError> {
let denom_hash: QueryDenomHashResponse = cosmos_query!(
self,
ibc_transfer,
denom_hash,
QueryDenomHashRequest { trace: trace }
);
Ok(denom_hash.hash)
}
pub async fn _clients(&self) -> Result<Vec<IdentifiedClientState>, DaemonError> {
let ibc_clients: QueryClientStatesResponse = cosmos_query!(
self,
ibc_client,
client_states,
QueryClientStatesRequest { pagination: None }
);
Ok(ibc_clients.client_states)
}
pub async fn _client_state(
&self,
client_id: impl ToString,
) -> Result<cosmos_modules::ibc_client::QueryClientStateResponse, DaemonError> {
let response: cosmos_modules::ibc_client::QueryClientStateResponse = cosmos_query!(
self,
ibc_client,
client_state,
QueryClientStateRequest {
client_id: client_id.to_string(),
}
);
Ok(response)
}
pub async fn _consensus_states(
&self,
client_id: impl ToString,
) -> Result<cosmos_modules::ibc_client::QueryConsensusStatesResponse, DaemonError> {
let client_id = client_id.to_string();
let response: cosmos_modules::ibc_client::QueryConsensusStatesResponse = cosmos_query!(
self,
ibc_client,
consensus_states,
QueryConsensusStatesRequest {
client_id: client_id,
pagination: None,
}
);
Ok(response)
}
pub async fn _client_status(
&self,
client_id: impl ToString,
) -> Result<cosmos_modules::ibc_client::QueryClientStatusResponse, DaemonError> {
let response: cosmos_modules::ibc_client::QueryClientStatusResponse = cosmos_query!(
self,
ibc_client,
client_status,
QueryClientStatusRequest {
client_id: client_id.to_string(),
}
);
Ok(response)
}
pub async fn _client_params(
&self,
) -> Result<cosmos_modules::ibc_client::QueryClientParamsResponse, DaemonError> {
let response: cosmos_modules::ibc_client::QueryClientParamsResponse =
cosmos_query!(self, ibc_client, client_params, QueryClientParamsRequest {});
Ok(response)
}
pub async fn _connections(&self) -> Result<Vec<IdentifiedConnection>, DaemonError> {
use cosmos_modules::ibc_connection::QueryConnectionsResponse;
let ibc_connections: QueryConnectionsResponse = cosmos_query!(
self,
ibc_connection,
connections,
QueryConnectionsRequest { pagination: None }
);
Ok(ibc_connections.connections)
}
pub async fn _open_connections(
&self,
client_chain_id: impl ToString,
) -> Result<Vec<IdentifiedConnection>, DaemonError> {
let connections = self._connections().await?;
let mut open_connections = Vec::new();
for connection in connections {
if connection.state() == State::Open {
open_connections.push(connection);
}
}
let mut filtered_connections = Vec::new();
for connection in open_connections {
let client_state = self._connection_client(&connection.id).await?;
if client_state.chain_id == client_chain_id.to_string() {
filtered_connections.push(connection);
}
}
Ok(filtered_connections)
}
pub async fn _connection_end(
&self,
connection_id: impl Into<String>,
) -> Result<Option<ConnectionEnd>, DaemonError> {
use cosmos_modules::ibc_connection::QueryConnectionResponse;
let connection_id = connection_id.into();
let ibc_client_connections: QueryConnectionResponse = cosmos_query!(
self,
ibc_connection,
connection,
QueryConnectionRequest {
connection_id: connection_id.clone()
}
);
Ok(ibc_client_connections.connection)
}
pub async fn _client_connections(
&self,
client_id: impl Into<String>,
) -> Result<Vec<String>, DaemonError> {
use cosmos_modules::ibc_connection::QueryClientConnectionsResponse;
let client_id = client_id.into();
let ibc_client_connections: QueryClientConnectionsResponse = cosmos_query!(
self,
ibc_connection,
client_connections,
QueryClientConnectionsRequest {
client_id: client_id.clone()
}
);
Ok(ibc_client_connections.connection_paths)
}
pub async fn _connection_client(
&self,
connection_id: impl Into<String>,
) -> Result<ClientState, DaemonError> {
use cosmos_modules::ibc_connection::QueryConnectionClientStateResponse;
let connection_id = connection_id.into();
let ibc_connection_client: QueryConnectionClientStateResponse = cosmos_query!(
self,
ibc_connection,
connection_client_state,
QueryConnectionClientStateRequest {
connection_id: connection_id.clone()
}
);
let client_state =
ibc_connection_client
.identified_client_state
.ok_or(DaemonError::ibc_err(format!(
"error identifying client for connection {}",
connection_id
)))?;
let client_state = ClientState::decode(client_state.client_state.unwrap().value.as_slice())
.map_err(|e| DaemonError::ibc_err(format!("error decoding client state: {}", e)))?;
Ok(client_state)
}
pub async fn _channel(
&self,
port_id: impl Into<String>,
channel_id: impl Into<String>,
) -> Result<ibc_channel::Channel, DaemonError> {
use cosmos_modules::ibc_channel::QueryChannelResponse;
let port_id = port_id.into();
let channel_id = channel_id.into();
let ibc_channel: QueryChannelResponse = cosmos_query!(
self,
ibc_channel,
channel,
QueryChannelRequest {
port_id: port_id.clone(),
channel_id: channel_id.clone(),
}
);
ibc_channel.channel.ok_or(DaemonError::ibc_err(format!(
"error fetching channel {} on port {}",
channel_id, port_id
)))
}
pub async fn _channels(
&self,
pagination: Option<PageRequest>,
) -> Result<Vec<ibc_channel::IdentifiedChannel>, DaemonError> {
use cosmos_modules::ibc_channel::QueryChannelsResponse;
let ibc_channels: QueryChannelsResponse = cosmos_query!(
self,
ibc_channel,
channels,
QueryChannelsRequest {
pagination: pagination
}
);
Ok(ibc_channels.channels)
}
pub async fn _connection_channels(
&self,
connection_id: impl Into<String>,
) -> Result<Vec<ibc_channel::IdentifiedChannel>, DaemonError> {
use cosmos_modules::ibc_channel::QueryConnectionChannelsResponse;
let connection_id = connection_id.into();
let ibc_connection_channels: QueryConnectionChannelsResponse = cosmos_query!(
self,
ibc_channel,
connection_channels,
QueryConnectionChannelsRequest {
connection: connection_id.clone(),
pagination: None,
}
);
Ok(ibc_connection_channels.channels)
}
pub async fn _channel_client_state(
&self,
port_id: impl Into<String>,
channel_id: impl Into<String>,
) -> Result<IdentifiedClientState, DaemonError> {
use cosmos_modules::ibc_channel::QueryChannelClientStateResponse;
let port_id = port_id.into();
let channel_id = channel_id.into();
let ibc_channel_client_state: QueryChannelClientStateResponse = cosmos_query!(
self,
ibc_channel,
channel_client_state,
QueryChannelClientStateRequest {
port_id: port_id.clone(),
channel_id: channel_id.clone(),
}
);
ibc_channel_client_state
.identified_client_state
.ok_or(DaemonError::ibc_err(format!(
"error identifying client for channel {} on port {}",
channel_id, port_id
)))
}
pub async fn _packet_commitments(
&self,
port_id: impl Into<String>,
channel_id: impl Into<String>,
) -> Result<Vec<ibc_channel::PacketState>, DaemonError> {
use cosmos_modules::ibc_channel::QueryPacketCommitmentsResponse;
let port_id = port_id.into();
let channel_id = channel_id.into();
let ibc_packet_commitments: QueryPacketCommitmentsResponse = cosmos_query!(
self,
ibc_channel,
packet_commitments,
QueryPacketCommitmentsRequest {
port_id: port_id.clone(),
channel_id: channel_id.clone(),
pagination: None,
}
);
Ok(ibc_packet_commitments.commitments)
}
pub async fn _packet_commitment(
&self,
port_id: impl Into<String>,
channel_id: impl Into<String>,
sequence: u64,
) -> Result<QueryPacketCommitmentResponse, DaemonError> {
let port_id = port_id.into();
let channel_id = channel_id.into();
let ibc_packet_commitment: QueryPacketCommitmentResponse = cosmos_query!(
self,
ibc_channel,
packet_commitment,
QueryPacketCommitmentRequest {
port_id: port_id.clone(),
channel_id: channel_id.clone(),
sequence: sequence,
}
);
Ok(ibc_packet_commitment)
}
pub async fn _packet_receipt(
&self,
port_id: impl Into<String>,
channel_id: impl Into<String>,
sequence: u64,
) -> Result<bool, DaemonError> {
let port_id = port_id.into();
let channel_id = channel_id.into();
let ibc_packet_receipt: ibc_channel::QueryPacketReceiptResponse = cosmos_query!(
self,
ibc_channel,
packet_receipt,
QueryPacketReceiptRequest {
port_id: port_id.clone(),
channel_id: channel_id.clone(),
sequence: sequence,
}
);
Ok(ibc_packet_receipt.received)
}
pub async fn _packet_acknowledgements(
&self,
port_id: impl Into<String>,
channel_id: impl Into<String>,
packet_commitment_sequences: Vec<u64>,
) -> Result<Vec<ibc_channel::PacketState>, DaemonError> {
use cosmos_modules::ibc_channel::QueryPacketAcknowledgementsResponse;
let port_id = port_id.into();
let channel_id = channel_id.into();
let ibc_packet_acknowledgements: QueryPacketAcknowledgementsResponse = cosmos_query!(
self,
ibc_channel,
packet_acknowledgements,
QueryPacketAcknowledgementsRequest {
port_id: port_id.clone(),
channel_id: channel_id.clone(),
packet_commitment_sequences: packet_commitment_sequences,
pagination: None,
}
);
Ok(ibc_packet_acknowledgements.acknowledgements)
}
pub async fn _packet_acknowledgement(
&self,
port_id: impl Into<String>,
channel_id: impl Into<String>,
sequence: u64,
) -> Result<Vec<u8>, DaemonError> {
let port_id = port_id.into();
let channel_id = channel_id.into();
let ibc_packet_acknowledgement: ibc_channel::QueryPacketAcknowledgementResponse = cosmos_query!(
self,
ibc_channel,
packet_acknowledgement,
QueryPacketAcknowledgementRequest {
port_id: port_id.clone(),
channel_id: channel_id.clone(),
sequence: sequence,
}
);
Ok(ibc_packet_acknowledgement.acknowledgement)
}
pub async fn _unreceived_packets(
&self,
port_id: impl Into<String>,
channel_id: impl Into<String>,
packet_commitment_sequences: Vec<u64>,
) -> Result<Vec<u64>, DaemonError> {
use cosmos_modules::ibc_channel::QueryUnreceivedPacketsResponse;
let port_id = port_id.into();
let channel_id = channel_id.into();
let ibc_packet_unreceived: QueryUnreceivedPacketsResponse = cosmos_query!(
self,
ibc_channel,
unreceived_packets,
QueryUnreceivedPacketsRequest {
port_id: port_id.clone(),
channel_id: channel_id.clone(),
packet_commitment_sequences: packet_commitment_sequences,
}
);
Ok(ibc_packet_unreceived.sequences)
}
pub async fn _unreceived_acks(
&self,
port_id: impl Into<String>,
channel_id: impl Into<String>,
packet_ack_sequences: Vec<u64>,
) -> Result<Vec<u64>, DaemonError> {
let port_id = port_id.into();
let channel_id = channel_id.into();
let ibc_packet_unreceived: ibc_channel::QueryUnreceivedAcksResponse = cosmos_query!(
self,
ibc_channel,
unreceived_acks,
QueryUnreceivedAcksRequest {
port_id: port_id.clone(),
channel_id: channel_id.clone(),
packet_ack_sequences: packet_ack_sequences,
}
);
Ok(ibc_packet_unreceived.sequences)
}
pub async fn _next_sequence_receive(
&self,
port_id: impl Into<String>,
channel_id: impl Into<String>,
) -> Result<u64, DaemonError> {
let port_id = port_id.into();
let channel_id = channel_id.into();
let next_receive: ibc_channel::QueryNextSequenceReceiveResponse = cosmos_query!(
self,
ibc_channel,
next_sequence_receive,
QueryNextSequenceReceiveRequest {
port_id: port_id.clone(),
channel_id: channel_id.clone(),
}
);
Ok(next_receive.next_sequence_receive)
}
}