hermes_cli_components/impls/commands/queries/
client_state.rs

1use core::marker::PhantomData;
2
3use cgp::prelude::*;
4use hermes_logging_components::traits::has_logger::HasLogger;
5use hermes_logging_components::traits::logger::CanLog;
6use hermes_logging_components::types::level::LevelInfo;
7use hermes_relayer_components::build::traits::builders::chain_builder::CanBuildChain;
8use hermes_relayer_components::chain::traits::queries::chain_status::CanQueryChainHeight;
9use hermes_relayer_components::chain::traits::queries::client_state::CanQueryClientState;
10use hermes_relayer_components::chain::traits::types::chain_id::HasChainIdType;
11use hermes_relayer_components::chain::traits::types::client_state::HasClientStateType;
12use hermes_relayer_components::multi::types::index::Index;
13
14use crate::traits::any_counterparty::HasAnyCounterparty;
15use crate::traits::build::{CanLoadBuilder, HasBuilderType};
16use crate::traits::command::CommandRunner;
17use crate::traits::output::CanProduceOutput;
18use crate::traits::parse::CanParseArg;
19
20pub struct RunQueryClientStateCommand;
21
22#[derive(Debug, clap::Parser, HasField)]
23pub struct QueryClientStateArgs {
24    /// Identifier of the host chain
25    #[clap(
26        long = "chain",
27        required = true,
28        value_name = "CHAIN_ID",
29        help_heading = "REQUIRED"
30    )]
31    chain_id: String,
32
33    /// Identifier of the client on the host chain
34    #[clap(
35        long = "client",
36        required = true,
37        value_name = "CLIENT_ID",
38        help_heading = "REQUIRED"
39    )]
40    client_id: String,
41
42    #[clap(
43        long = "height",
44        value_name = "HEIGHT",
45        help = "The height at which to query the client state. If not specified, the latest height is used."
46    )]
47    height: Option<String>,
48}
49
50impl<App, Args, Build, Chain, Counterparty> CommandRunner<App, Args> for RunQueryClientStateCommand
51where
52    App: HasBuilderType<Builder = Build>
53        + CanLoadBuilder
54        + HasLogger
55        + HasAnyCounterparty<AnyCounterparty = Counterparty>
56        + CanProduceOutput<Counterparty::ClientState>
57        + CanParseArg<Args, symbol!("chain_id"), Parsed = Chain::ChainId>
58        + CanParseArg<Args, symbol!("client_id"), Parsed = Chain::ClientId>
59        + CanParseArg<Args, symbol!("height"), Parsed = Option<Chain::Height>>
60        + CanRaiseError<Build::Error>
61        + CanRaiseError<Chain::Error>,
62    Args: Async,
63    Build: CanBuildChain<0, Chain = Chain>,
64    Chain: HasChainIdType + CanQueryChainHeight + CanQueryClientState<Counterparty>,
65    Counterparty: HasClientStateType<Chain>,
66    App::Logger: CanLog<LevelInfo>,
67{
68    async fn run_command(app: &App, args: &Args) -> Result<App::Output, App::Error> {
69        let chain_id = app.parse_arg(args, PhantomData::<symbol!("chain_id")>)?;
70        let client_id = app.parse_arg(args, PhantomData::<symbol!("client_id")>)?;
71        let m_height = app.parse_arg(args, PhantomData::<symbol!("height")>)?;
72
73        let logger = app.logger();
74        let builder = app.load_builder().await?;
75
76        let chain = builder
77            .build_chain(Index::<0>, &chain_id)
78            .await
79            .map_err(App::raise_error)?;
80
81        let query_height = match m_height {
82            Some(height) => height,
83            None => chain.query_chain_height().await.map_err(App::raise_error)?,
84        };
85
86        let client_state = chain
87            .query_client_state(&client_id, &query_height)
88            .await
89            .map_err(App::raise_error)?;
90
91        logger
92            .log(
93                &format!("Found client state for client `{client_id}` on chain `{chain_id}`!"),
94                &LevelInfo,
95            )
96            .await;
97
98        Ok(app.produce_output(client_state))
99    }
100}