From bc00263cba095b86de853be29a2ed613790d1b48 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Thu, 17 Aug 2023 10:48:12 +0200 Subject: [PATCH 1/8] Added rpc querier test for future support --- cw-orch/tests/rpc-querier.rs | 40 ++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 cw-orch/tests/rpc-querier.rs diff --git a/cw-orch/tests/rpc-querier.rs b/cw-orch/tests/rpc-querier.rs new file mode 100644 index 000000000..f6ca02d78 --- /dev/null +++ b/cw-orch/tests/rpc-querier.rs @@ -0,0 +1,40 @@ +use base64::{engine::general_purpose, Engine as _}; +use cosmrs::{ + proto::cosmos::bank::v1beta1::{QueryAllBalancesRequest, QueryAllBalancesResponse}, + rpc::{endpoint::abci_query, Client, HttpClient}, + tx::MessageExt, +}; +use hex::encode; +use prost::Message; +use tokio::runtime::Runtime; + +#[test] +fn temp_test() { + // Necessary variable for querying + let rt = Runtime::new().unwrap(); + let client = HttpClient::new("https://rpc.osmosis.zone").unwrap(); + + // Query content + let address = "osmo126pr9qp44aft4juw7x4ev4s2qdtnwe38jzwunec9pxt5cpzaaphqyagqpu".to_string(); + let request = QueryAllBalancesRequest { + address, + pagination: None, + }; + let any = request.to_bytes().unwrap(); + + // Querying + let response = rt + .block_on(client.abci_query( + Some("/cosmos.bank.v1beta1.Query/AllBalances".to_string()), + any, + None, + true, + )) + .unwrap(); + + // Analysing the response + let balance_response = QueryAllBalancesResponse::decode(response.value.as_slice()).unwrap(); + + // Printing the response + panic!("{:?}", response); +} From 4d4405ddc308d64b7153fb0a7d11dd8e517e1347 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Sat, 9 Sep 2023 15:01:19 +0200 Subject: [PATCH 2/8] Added bank querier implementation --- cw-orch-daemon/Cargo.toml | 2 +- cw-orch-daemon/src/daemon_helpers.rs | 173 + cw-orch-daemon/src/error.rs | 4 + cw-orch-daemon/src/lib.rs | 2 +- cw-orch-daemon/src/queriers/bank.rs | 32 +- cw-orch-daemon/src/rpc_queriers/bank.rs | 144 + cw-orch-daemon/src/rpc_queriers/mod.rs | 39 + cw-orch-daemon/src/test.rs | 8364 +++++ cw-orch-daemon/test | 25355 ++++++++++++++++ .../tests/rpc-querier.rs | 2 +- 10 files changed, 34101 insertions(+), 16 deletions(-) create mode 100644 cw-orch-daemon/src/daemon_helpers.rs create mode 100644 cw-orch-daemon/src/rpc_queriers/bank.rs create mode 100644 cw-orch-daemon/src/rpc_queriers/mod.rs create mode 100644 cw-orch-daemon/src/test.rs create mode 100644 cw-orch-daemon/test rename {cw-orch => cw-orch-daemon}/tests/rpc-querier.rs (96%) diff --git a/cw-orch-daemon/Cargo.toml b/cw-orch-daemon/Cargo.toml index 1c65b2b40..71fb3e906 100644 --- a/cw-orch-daemon/Cargo.toml +++ b/cw-orch-daemon/Cargo.toml @@ -55,7 +55,7 @@ hkd32 = { version = "0.7.0", features = ["bip39", "mnemonic", "bech32"] } rand_core = { version = "0.6.4", default-features = false } ed25519-dalek = { version = "2", features = ["serde"] } eyre = { version = "0.6" } -cosmrs = { version = "0.14.0", features = ["dev", "cosmwasm", "grpc"] } +cosmrs = { version = "0.14.0", features = ["dev", "cosmwasm", "grpc" ,"rpc"] } chrono = { version = "0.4" } base16 = { version = "0.2.1" } derive_builder = { version = "0.12.0" } diff --git a/cw-orch-daemon/src/daemon_helpers.rs b/cw-orch-daemon/src/daemon_helpers.rs new file mode 100644 index 000000000..7d5605453 --- /dev/null +++ b/cw-orch-daemon/src/daemon_helpers.rs @@ -0,0 +1,173 @@ +mod common; +#[cfg(feature = "node-tests")] +mod tests { + /* + DaemonAsync contract general tests + */ + + use cw_orch_core::{contract::interface_traits::*, environment::TxHandler}; + use cw_orch_daemon::{ConditionalMigrate, ConditionalUpload, Daemon}; + use mock_contract::{InstantiateMsg, MigrateMsg, QueryMsg}; + + use cosmwasm_std::Addr; + + use speculoos::prelude::*; + + use crate::common::Id; + + #[test] + #[serial_test::serial] + fn helper_traits() { + use cw_orch_networks::networks; + + let runtime = tokio::runtime::Runtime::new().unwrap(); + + let daemon = Daemon::builder() + .chain(networks::LOCAL_JUNO) + .handle(runtime.handle()) + .build() + .unwrap(); + + let sender = daemon.sender(); + + let contract = mock_contract::MockContract::new( + format!("test:mock_contract:{}", Id::new()), + daemon.clone(), + ); + + asserting!("address is not present") + .that(&contract.address()) + .is_err(); + + asserting!("upload_if_needed is ok") + .that(&contract.upload_if_needed()) + .is_ok(); + + asserting!("latest_is_uploaded is true") + .that(&contract.latest_is_uploaded().unwrap()) + .is_true(); + + let init_msg = &InstantiateMsg {}; + + let _ = contract.instantiate(init_msg, Some(&Addr::unchecked(sender)), Some(&[])); + + asserting!("address is present") + .that(&contract.address()) + .is_ok(); + + asserting!("migrate_if_needed is none") + .that( + &contract + .migrate_if_needed(&MigrateMsg { + t: "success".to_string(), + }) + .unwrap(), + ) + .is_none(); + + asserting!("is_running_latest is true") + .that(&contract.is_running_latest().unwrap()) + .is_true(); + + let _ = contract.upload(); + + asserting!("is_running_latest is false") + .that(&contract.is_running_latest().unwrap()) + .is_false(); + + asserting!("migrate_if_needed is some") + .that( + &contract + .migrate_if_needed(&MigrateMsg { + t: "success".to_string(), + }) + .unwrap(), + ) + .is_some(); + + asserting!("code_id is ok") + .that(&contract.code_id()) + .is_ok(); + } + + // #[test] + // #[serial_test::serial] + // fn wrong_min_fee() { + // use cw_orch::prelude::networks; + + // let runtime = tokio::runtime::Runtime::new().unwrap(); + + // let mut chain = networks::UNI_6; + // chain.gas_price = 0.00001; + + // let daemon = Daemon::builder() + // .chain(chain) + // .handle(runtime.handle()) + // .mnemonic("tide genuine angle mass fall promote blind skull swim army maximum add peasant fringe uncle october female crisp voyage blind extend jeans give wrap") + // .build() + // .unwrap(); + + // let contract = mock_contract::MockContract::new( + // format!("test:mock_contract:{}", Id::new()), + // daemon.clone(), + // ); + + // contract.upload().unwrap(); + // } + + #[test] + #[serial_test::serial] + fn cw_orch_interface_traits() { + use cw_orch_networks::networks; + + let runtime = tokio::runtime::Runtime::new().unwrap(); + + let daemon = Daemon::builder() + .chain(networks::LOCAL_JUNO) + .handle(runtime.handle()) + .build() + .unwrap(); + + let sender = daemon.sender(); + + let contract = mock_contract::MockContract::new( + format!("test:mock_contract:{}", Id::new()), + daemon.clone(), + ); + + // upload contract + let upload_res = contract.upload(); + asserting!("upload is successful").that(&upload_res).is_ok(); + + let code_id = upload_res.unwrap().logs[0].events[1].attributes[1] + .value + .clone(); + + log::info!("Using code_id {}", code_id); + + // instantiate contract on chain + let init_res = contract.instantiate(&InstantiateMsg {}, Some(&sender), None); + asserting!("instantiate is successful") + .that(&init_res) + .is_ok(); + + // do a query and validate its successful + let query_res = contract.query::(&QueryMsg::FirstQuery {}); + asserting!("query is successful").that(&query_res).is_ok(); + + // validate migrations are successful + let migrate_res = contract.migrate( + &MigrateMsg { + t: "success".to_string(), + }, + code_id.parse::().unwrap(), + ); + asserting!("migrate is successful") + .that(&migrate_res) + .is_ok(); + + asserting!("that upload_if_needed returns None") + .that(&contract.upload_if_needed().unwrap()) + .is_none(); + } +} diff --git a/cw-orch-daemon/src/error.rs b/cw-orch-daemon/src/error.rs index 7ee789891..74feef155 100644 --- a/cw-orch-daemon/src/error.rs +++ b/cw-orch-daemon/src/error.rs @@ -26,6 +26,10 @@ pub enum DaemonError { #[error(transparent)] TendermintError(#[from] ::cosmrs::tendermint::Error), #[error(transparent)] + TendermintRPCError(#[from] cosmrs::rpc::Error), + #[error(transparent)] + ProseEncoreError(#[from] prost::EncodeError), + #[error(transparent)] CwEnvError(#[from] ::cw_orch_core::CwEnvError), #[error("Bech32 Decode Error")] Bech32DecodeErr, diff --git a/cw-orch-daemon/src/lib.rs b/cw-orch-daemon/src/lib.rs index f7310a325..f1fc4246e 100644 --- a/cw-orch-daemon/src/lib.rs +++ b/cw-orch-daemon/src/lib.rs @@ -1,7 +1,7 @@ //! `Daemon` and `DaemonAsync` execution environments. //! //! The `Daemon` type is a synchronous wrapper around the `DaemonAsync` type and can be used as a contract execution environment. - +pub mod rpc_queriers; pub mod builder; pub mod channel; pub mod core; diff --git a/cw-orch-daemon/src/queriers/bank.rs b/cw-orch-daemon/src/queriers/bank.rs index 087b67e83..4612528b6 100644 --- a/cw-orch-daemon/src/queriers/bank.rs +++ b/cw-orch-daemon/src/queriers/bank.rs @@ -23,25 +23,31 @@ impl Bank { address: impl Into, denom: Option, ) -> Result, DaemonError> { - use cosmos_modules::bank::query_client::QueryClient; match denom { Some(denom) => { - let mut client: QueryClient = QueryClient::new(self.channel.clone()); - let request = cosmos_modules::bank::QueryBalanceRequest { - address: address.into(), - denom, - }; - let resp = client.balance(request).await?.into_inner(); + let resp = cosmos_query!( + self, + bank, + balance, + QueryBalanceRequest { + address: address.into(), + denom: denom, + } + ); let coin = resp.balance.unwrap(); Ok(vec![coin]) } None => { - let mut client: QueryClient = QueryClient::new(self.channel.clone()); - let request = cosmos_modules::bank::QueryAllBalancesRequest { - address: address.into(), - ..Default::default() - }; - let resp = client.all_balances(request).await?.into_inner(); + let resp = cosmos_query!( + self, + bank, + all_balances, + QueryAllBalancesRequest { + address: address.into(), + pagination: None, + } + ); + let coins = resp.balances; Ok(coins.into_iter().collect()) } diff --git a/cw-orch-daemon/src/rpc_queriers/bank.rs b/cw-orch-daemon/src/rpc_queriers/bank.rs new file mode 100644 index 000000000..62fee6947 --- /dev/null +++ b/cw-orch-daemon/src/rpc_queriers/bank.rs @@ -0,0 +1,144 @@ +use crate::{cosmos_modules, error::DaemonError, cosmos_rpc_query}; +use cosmrs::{proto::cosmos::base::{query::v1beta1::PageRequest, v1beta1::Coin}, rpc::HttpClient, tx::MessageExt}; + +use super::RpcQuerier; + +/// Queries for Cosmos Bank Module +pub struct Bank { + client: HttpClient, +} + +impl RpcQuerier for Bank { + fn new(rpc: String) -> Self { + Self { client: HttpClient::new(rpc.as_str()).unwrap() } + } +} + +impl Bank { + /// Query the bank balance of a given address + /// If denom is None, returns all balances + pub async fn balance( + &self, + address: impl Into, + denom: Option, + ) -> Result, DaemonError> { + match denom { + Some(denom) => { + let balance_response = cosmos_rpc_query!( + self, + bank, + "/cosmos.bank.v1beta1.Query/Balance", + QueryBalanceRequest { + address: address.into(), + denom: denom, + }, + QueryBalanceResponse, + ); + let coin = balance_response.balance.unwrap(); + Ok(vec![coin]) + } + None => { + let balance_response = cosmos_rpc_query!( + self, + bank, + "/cosmos.bank.v1beta1.Query/AllBalances", + QueryAllBalancesRequest { + address: address.into(), + pagination: None, + }, + QueryAllBalancesResponse, + ); + + let coins = balance_response.balances; + Ok(coins.into_iter().collect()) + } + } + } + + /// Query spendable balance for address + pub async fn spendable_balances( + &self, + address: impl Into, + ) -> Result, DaemonError> { + let spendable_balances = cosmos_rpc_query!( + self, + bank, + "/cosmos.bank.v1beta1.Query/SpendableBalances", + QuerySpendableBalancesRequest { + address: address.into(), + pagination: None, + }, + QuerySpendableBalancesResponse, + ); + Ok(spendable_balances.balances) + } + + /// Query total supply in the bank + pub async fn total_supply(&self) -> Result, DaemonError> { + let total_supply = cosmos_rpc_query!( + self, + bank, + "/cosmos.bank.v1beta1.Query/TotalSupply", + QueryTotalSupplyRequest { pagination: None }, + QueryTotalSupplyResponse, + ); + Ok(total_supply.supply) + } + + /// Query total supply in the bank for a denom + pub async fn supply_of(&self, denom: impl Into) -> Result { + let supply_of = cosmos_rpc_query!( + self, + bank, + "/cosmos.bank.v1beta1.Query/SupplyOf", + QuerySupplyOfRequest { + denom: denom.into() + }, + QuerySupplyOfResponse, + ); + Ok(supply_of.amount.unwrap()) + } + + /// Query params + pub async fn params(&self) -> Result { + let params = + cosmos_rpc_query!(self, bank, "/cosmos.bank.v1beta1.Query/Params", QueryParamsRequest {}, QueryParamsResponse,); + Ok(params.params.unwrap()) + } + + /// Query denom metadata + pub async fn denom_metadata( + &self, + denom: impl Into, + ) -> Result { + let denom_metadata = cosmos_rpc_query!( + self, + bank, + "/cosmos.bank.v1beta1.Query/DenomMetadata", + QueryDenomMetadataRequest { + denom: denom.into() + }, + QueryDenomMetadataResponse, + ); + Ok(denom_metadata.metadata.unwrap()) + } + + /// Query denoms metadata with pagination + /// + /// see [PageRequest] for pagination + pub async fn denoms_metadata( + &self, + pagination: Option, + ) -> Result, DaemonError> { + let denoms_metadata = cosmos_rpc_query!( + self, + bank, + "/cosmos.bank.v1beta1.Query/DenomsMetadata", + QueryDenomsMetadataRequest { + pagination: pagination + }, + QueryDenomsMetadataResponse, + ); + Ok(denoms_metadata.metadatas) + } +} diff --git a/cw-orch-daemon/src/rpc_queriers/mod.rs b/cw-orch-daemon/src/rpc_queriers/mod.rs new file mode 100644 index 000000000..3027571da --- /dev/null +++ b/cw-orch-daemon/src/rpc_queriers/mod.rs @@ -0,0 +1,39 @@ +pub mod bank; + +/// macro for constructing and performing a query on a CosmosSDK module. +#[macro_export] +macro_rules! cosmos_rpc_query { + ($self:ident, $module:ident, $type_url:literal, $request_type:ident { $($field:ident : $value:expr),* $(,)? }, $request_resp:ident, ) => { + { + use $crate::cosmos_modules::$module::{ + $request_resp, $request_type, + }; + use ::cosmrs::rpc::Client; + use ::prost::Message; + + let request = $request_type { $($field : $value),* }; + let response = $self.client.abci_query( + Some($type_url.to_string()), + request.to_bytes()?, + None, + true + ).await?; + let decoded_response = $request_resp::decode(response.value.as_slice())?; + ::log::trace!( + "cosmos_query: {:?} resulted in: {:?}", + request, + decoded_response + ); + + decoded_response + } +}; +} + + + +/// Constructor for a querier over a given channel +pub trait RpcQuerier { + /// Construct an new querier over a given channel + fn new(rpc: String) -> Self; +} diff --git a/cw-orch-daemon/src/test.rs b/cw-orch-daemon/src/test.rs new file mode 100644 index 000000000..02675d6fb --- /dev/null +++ b/cw-orch-daemon/src/test.rs @@ -0,0 +1,8364 @@ +#![feature(prelude_import)] +//! `Daemon` and `DaemonAsync` execution environments. +//! +//! The `Daemon` type is a synchronous wrapper around the `DaemonAsync` type and can be used as a contract execution environment. +#[prelude_import] +use std::prelude::rust_2021::*; +#[macro_use] +extern crate std; +pub mod builder { + use crate::{DaemonAsync, DaemonBuilder}; + use std::rc::Rc; + use ibc_chain_registry::chain::ChainData; + use super::{error::DaemonError, sender::Sender, state::DaemonState}; + /// The default deployment id if none is provided + pub const DEFAULT_DEPLOYMENT: &str = "default"; + /// Create [`DaemonAsync`] through [`DaemonAsyncBuilder`] + /// ## Example + /// ```no_run + /// # tokio_test::block_on(async { + /// use cw_orch_daemon::{DaemonAsyncBuilder, networks}; + /// let daemon = DaemonAsyncBuilder::default() + /// .chain(networks::LOCAL_JUNO) + /// .deployment_id("v0.1.0") + /// .build() + /// .await.unwrap(); + /// # }) + /// ``` + pub struct DaemonAsyncBuilder { + pub(crate) chain: Option, + pub(crate) deployment_id: Option, + /// Wallet mnemonic + pub(crate) mnemonic: Option, + } + #[automatically_derived] + impl ::core::clone::Clone for DaemonAsyncBuilder { + #[inline] + fn clone(&self) -> DaemonAsyncBuilder { + DaemonAsyncBuilder { + chain: ::core::clone::Clone::clone(&self.chain), + deployment_id: ::core::clone::Clone::clone(&self.deployment_id), + mnemonic: ::core::clone::Clone::clone(&self.mnemonic), + } + } + } + #[automatically_derived] + impl ::core::default::Default for DaemonAsyncBuilder { + #[inline] + fn default() -> DaemonAsyncBuilder { + DaemonAsyncBuilder { + chain: ::core::default::Default::default(), + deployment_id: ::core::default::Default::default(), + mnemonic: ::core::default::Default::default(), + } + } + } + impl DaemonAsyncBuilder { + /// Set the chain the daemon will connect to + pub fn chain(&mut self, chain: impl Into) -> &mut Self { + self.chain = Some(chain.into()); + self + } + /// Set the deployment id to use for the daemon interactions + /// Defaults to `default` + pub fn deployment_id(&mut self, deployment_id: impl Into) -> &mut Self { + self.deployment_id = Some(deployment_id.into()); + self + } + /// Set the mnemonic to use with this chain. + /// Defaults to env variable depending on the environment. + /// + /// Variables: LOCAL_MNEMONIC, TEST_MNEMONIC and MAIN_MNEMONIC + pub fn mnemonic(&mut self, mnemonic: impl ToString) -> &mut Self { + self.mnemonic = Some(mnemonic.to_string()); + self + } + /// Build a daemon + pub async fn build(&self) -> Result { + let chain = self + .chain + .clone() + .ok_or(DaemonError::BuilderMissing("chain information".into()))?; + let deployment_id = self + .deployment_id + .clone() + .unwrap_or(DEFAULT_DEPLOYMENT.to_string()); + let state = Rc::new(DaemonState::new(chain, deployment_id).await?); + let sender = if let Some(mnemonic) = &self.mnemonic { + Sender::from_mnemonic(&state, mnemonic)? + } else { + Sender::new(&state)? + }; + let daemon = DaemonAsync { + state, + sender: Rc::new(sender), + }; + Ok(daemon) + } + } + impl From for DaemonAsyncBuilder { + fn from(value: DaemonBuilder) -> Self { + DaemonAsyncBuilder { + chain: value.chain, + deployment_id: value.deployment_id, + mnemonic: value.mnemonic, + } + } + } +} +pub mod channel { + use cosmrs::proto::cosmos::base::tendermint::v1beta1::{ + service_client::ServiceClient, GetNodeInfoRequest, + }; + use ibc_chain_registry::chain::Grpc; + use ibc_relayer_types::core::ics24_host::identifier::ChainId; + use tonic::transport::{Channel, ClientTlsConfig}; + use super::error::DaemonError; + /// A helper for constructing a gRPC channel + pub struct GrpcChannel {} + impl GrpcChannel { + /// Connect to any of the provided gRPC endpoints + pub async fn connect( + grpc: &[Grpc], + chain_id: &ChainId, + ) -> Result { + let mut successful_connections = ::alloc::vec::Vec::new(); + for Grpc { address, .. } in grpc.iter() { + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Trying to connect to endpoint: {0}", address), + lvl, + &( + "cw_orch_daemon::channel", + "cw_orch_daemon::channel", + "cw-orch-daemon/src/channel.rs", + ), + 19u32, + ::log::__private_api::Option::None, + ); + } + }; + let endpoint = Channel::builder(address.clone().try_into().unwrap()); + let maybe_client = ServiceClient::connect(endpoint.clone()).await; + let mut client = if maybe_client.is_ok() { + maybe_client? + } else { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Cannot connect to gRPC endpoint: {0}, {1:?}", + address, + maybe_client.unwrap_err(), + ), + lvl, + &( + "cw_orch_daemon::channel", + "cw_orch_daemon::channel", + "cw-orch-daemon/src/channel.rs", + ), + 31u32, + ::log::__private_api::Option::None, + ); + } + }; + if !(address.contains("https") || address.contains("443")) { + continue; + } + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Attempting to connect with TLS"), + lvl, + &( + "cw_orch_daemon::channel", + "cw_orch_daemon::channel", + "cw-orch-daemon/src/channel.rs", + ), + 43u32, + ::log::__private_api::Option::None, + ); + } + }; + let endpoint = endpoint.clone().tls_config(ClientTlsConfig::new())?; + let maybe_client = ServiceClient::connect(endpoint.clone()).await; + if maybe_client.is_err() { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "Cannot connect to gRPC endpoint: {0}, {1:?}", + address, + maybe_client.unwrap_err(), + ), + lvl, + &( + "cw_orch_daemon::channel", + "cw_orch_daemon::channel", + "cw-orch-daemon/src/channel.rs", + ), + 51u32, + ::log::__private_api::Option::None, + ); + } + }; + continue; + } + maybe_client? + }; + let node_info = client + .get_node_info(GetNodeInfoRequest {}) + .await? + .into_inner(); + if ChainId::is_epoch_format( + &node_info.default_node_info.as_ref().unwrap().network, + ) { + if node_info.default_node_info.as_ref().unwrap().network + != chain_id.as_str() + { + { + let lvl = ::log::Level::Error; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "Network mismatch: connection:{0} != config:{1}", + node_info.default_node_info.as_ref().unwrap().network, + chain_id.as_str(), + ), + lvl, + &( + "cw_orch_daemon::channel", + "cw_orch_daemon::channel", + "cw-orch-daemon/src/channel.rs", + ), + 72u32, + ::log::__private_api::Option::None, + ); + } + }; + continue; + } + } + successful_connections.push(endpoint.connect().await?) + } + if successful_connections.is_empty() { + return Err(DaemonError::CannotConnectGRPC); + } + Ok(successful_connections.pop().unwrap()) + } + } +} +pub mod core { + use crate::{queriers::CosmWasm, DaemonState}; + use super::{ + builder::DaemonAsyncBuilder, cosmos_modules, error::DaemonError, + queriers::{DaemonQuerier, Node}, + sender::Wallet, tx_resp::CosmTxResponse, + }; + use cosmrs::{ + cosmwasm::{MsgExecuteContract, MsgInstantiateContract, MsgMigrateContract}, + tendermint::Time, AccountId, Denom, + }; + use cosmwasm_std::{Addr, Coin}; + use cw_orch_core::{ + contract::interface_traits::Uploadable, environment::{ChainState, IndexResponse}, + }; + use serde::{de::DeserializeOwned, Serialize}; + use serde_json::from_str; + use std::{ + fmt::Debug, rc::Rc, str::{from_utf8, FromStr}, + time::Duration, + }; + use tonic::transport::Channel; + /** + Represents a blockchain node. + It's constructed using [`DaemonAsyncBuilder`]. + + ## Usage + ```rust,no_run + # tokio_test::block_on(async { + use cw_orch_daemon::{DaemonAsync, networks}; + + let daemon: DaemonAsync = DaemonAsync::builder() + .chain(networks::JUNO_1) + .build() + .await.unwrap(); + # }) + ``` + ## Environment Execution + + The DaemonAsync implements async methods of [`TxHandler`](cw_orch_core::environment::TxHandler) which allows you to perform transactions on the chain. + + ## Querying + + Different Cosmos SDK modules can be queried through the daemon by calling the [`DaemonAsync::query_client`] method with a specific querier. + See [Querier](crate::queriers) for examples. +*/ + pub struct DaemonAsync { + /// Sender to send transactions to the chain + pub sender: Wallet, + /// State of the daemon + pub state: Rc, + } + #[automatically_derived] + impl ::core::clone::Clone for DaemonAsync { + #[inline] + fn clone(&self) -> DaemonAsync { + DaemonAsync { + sender: ::core::clone::Clone::clone(&self.sender), + state: ::core::clone::Clone::clone(&self.state), + } + } + } + impl DaemonAsync { + /// Get the daemon builder + pub fn builder() -> DaemonAsyncBuilder { + DaemonAsyncBuilder::default() + } + /// Perform a query with a given query client. + /// See [Querier](crate::queriers) for examples. + pub fn query_client(&self) -> Querier { + Querier::new(self.sender.channel()) + } + /// Get the channel configured for this DaemonAsync. + pub fn channel(&self) -> Channel { + self.state.grpc_channel.clone() + } + } + impl ChainState for DaemonAsync { + type Out = Rc; + fn state(&self) -> Self::Out { + self.state.clone() + } + } + impl DaemonAsync { + /// Get the sender address + pub fn sender(&self) -> Addr { + self.sender.address().unwrap() + } + /// Execute a message on a contract. + pub async fn execute( + &self, + exec_msg: &E, + coins: &[cosmwasm_std::Coin], + contract_address: &Addr, + ) -> Result { + let exec_msg: MsgExecuteContract = MsgExecuteContract { + sender: self.sender.pub_addr()?, + contract: AccountId::from_str(contract_address.as_str())?, + msg: serde_json::to_vec(&exec_msg)?, + funds: parse_cw_coins(coins)?, + }; + let result = self + .sender + .commit_tx( + <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([exec_msg])), + None, + ) + .await?; + Ok(result) + } + /// Instantiate a contract. + pub async fn instantiate( + &self, + code_id: u64, + init_msg: &I, + label: Option<&str>, + admin: Option<&Addr>, + coins: &[Coin], + ) -> Result { + let sender = &self.sender; + let init_msg = MsgInstantiateContract { + code_id, + label: Some(label.unwrap_or("instantiate_contract").to_string()), + admin: admin.map(|a| FromStr::from_str(a.as_str()).unwrap()), + sender: sender.pub_addr()?, + msg: serde_json::to_vec(&init_msg)?, + funds: parse_cw_coins(coins)?, + }; + let result = sender + .commit_tx( + <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([init_msg])), + None, + ) + .await?; + Ok(result) + } + /// Query a contract. + pub async fn query( + &self, + query_msg: &Q, + contract_address: &Addr, + ) -> Result { + let sender = &self.sender; + let mut client = cosmos_modules::cosmwasm::query_client::QueryClient::new( + sender.channel(), + ); + let resp = client + .smart_contract_state(cosmos_modules::cosmwasm::QuerySmartContractStateRequest { + address: contract_address.to_string(), + query_data: serde_json::to_vec(&query_msg)?, + }) + .await?; + Ok(from_str(from_utf8(&resp.into_inner().data).unwrap())?) + } + /// Migration a contract. + pub async fn migrate( + &self, + migrate_msg: &M, + new_code_id: u64, + contract_address: &Addr, + ) -> Result { + let exec_msg: MsgMigrateContract = MsgMigrateContract { + sender: self.sender.pub_addr()?, + contract: AccountId::from_str(contract_address.as_str())?, + msg: serde_json::to_vec(&migrate_msg)?, + code_id: new_code_id, + }; + let result = self + .sender + .commit_tx( + <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([exec_msg])), + None, + ) + .await?; + Ok(result) + } + /// Wait for a given amount of blocks. + pub async fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { + let mut last_height = self.query_client::().block_height().await?; + let end_height = last_height + amount; + let average_block_speed = self + .query_client::() + .average_block_speed(Some(0.9)) + .await?; + let wait_time = average_block_speed * amount; + tokio::time::sleep(Duration::from_secs(wait_time)).await; + while last_height < end_height { + tokio::time::sleep(Duration::from_secs(average_block_speed)).await; + last_height = self.query_client::().block_height().await?; + } + Ok(()) + } + /// Wait for a given amount of seconds. + pub async fn wait_seconds(&self, secs: u64) -> Result<(), DaemonError> { + tokio::time::sleep(Duration::from_secs(secs)).await; + Ok(()) + } + /// Wait for the next block. + pub async fn next_block(&self) -> Result<(), DaemonError> { + self.wait_blocks(1).await + } + /// Get the current block info. + pub async fn block_info(&self) -> Result { + let block = self.query_client::().latest_block().await?; + let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; + let time = cosmwasm_std::Timestamp::from_nanos( + since_epoch.as_nanos() as u64, + ); + Ok(cosmwasm_std::BlockInfo { + height: block.header.height.value(), + time, + chain_id: block.header.chain_id.to_string(), + }) + } + /// Upload a contract to the chain. + pub async fn upload( + &self, + uploadable: &impl Uploadable, + ) -> Result { + let sender = &self.sender; + let wasm_path = uploadable.wasm(); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Uploading file at {0:?}", wasm_path), + lvl, + &( + "cw_orch_daemon::core", + "cw_orch_daemon::core", + "cw-orch-daemon/src/core.rs", + ), + 233u32, + ::log::__private_api::Option::None, + ); + } + }; + let file_contents = std::fs::read(wasm_path.path())?; + let store_msg = cosmrs::cosmwasm::MsgStoreCode { + sender: sender.pub_addr()?, + wasm_byte_code: file_contents, + instantiate_permission: None, + }; + let result = sender + .commit_tx( + <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([store_msg])), + None, + ) + .await?; + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Uploaded: {0:?}", result.txhash), + lvl, + &( + "cw_orch_daemon::core", + "cw_orch_daemon::core", + "cw-orch-daemon/src/core.rs", + ), + 244u32, + ::log::__private_api::Option::None, + ); + } + }; + let code_id = result.uploaded_code_id().unwrap(); + let wasm = CosmWasm::new(self.channel()); + while wasm.code(code_id).await.is_err() { + self.next_block().await?; + } + Ok(result) + } + /// Set the sender to use with this DaemonAsync to be the given wallet + pub fn set_sender(&mut self, sender: &Wallet) { + self.sender = sender.clone(); + } + } + pub(crate) fn parse_cw_coins( + coins: &[cosmwasm_std::Coin], + ) -> Result, DaemonError> { + coins + .iter() + .map(|cosmwasm_std::Coin { amount, denom }| { + Ok(cosmrs::Coin { + amount: amount.u128(), + denom: Denom::from_str(denom)?, + }) + }) + .collect::, DaemonError>>() + } +} +pub mod error { + #![allow(missing_docs)] + use cw_orch_core::CwEnvError; + use thiserror::Error; + pub enum DaemonError { + #[error("Reqwest HTTP(s) Error")] + ReqwestError(#[from] ::reqwest::Error), + #[error("JSON Conversion Error")] + SerdeJson(#[from] ::serde_json::Error), + #[error(transparent)] + ParseIntError(#[from] std::num::ParseIntError), + #[error(transparent)] + IOErr(#[from] ::std::io::Error), + #[error(transparent)] + Secp256k1(#[from] ::secp256k1::Error), + #[error(transparent)] + VarError(#[from] ::std::env::VarError), + #[error(transparent)] + AnyError(#[from] ::anyhow::Error), + #[error(transparent)] + Status(#[from] ::tonic::Status), + #[error(transparent)] + TransportError(#[from] ::tonic::transport::Error), + #[error(transparent)] + TendermintError(#[from] ::cosmrs::tendermint::Error), + #[error(transparent)] + CwEnvError(#[from] ::cw_orch_core::CwEnvError), + #[error("Bech32 Decode Error")] + Bech32DecodeErr, + #[error( + "Bech32 Decode Error: Key Failed prefix {0} or length {1} Wanted:{2}/{3}" + )] + Bech32DecodeExpanded(String, usize, String, usize), + #[error("Mnemonic - Wrong length, it should be 24 words")] + WrongLength, + #[error("Mnemonic - Bad Phrase")] + Phrasing, + #[error("Mnemonic - Missing Phrase")] + MissingPhrase, + #[error("Bad Implementation. Missing Component")] + Implementation, + #[error("Unable to convert into public key `{key}`")] + Conversion { key: String, source: bitcoin::bech32::Error }, + #[error( + "Can not augment daemon deployment after usage in more than one contract." + )] + SharedDaemonState, + #[error(transparent)] + ErrReport(#[from] ::eyre::ErrReport), + #[error(transparent)] + GRpcDecodeError(#[from] ::prost::DecodeError), + #[error(transparent)] + ED25519(#[from] ::ed25519_dalek::ed25519::Error), + #[error(transparent)] + DecodeError(#[from] ::base64::DecodeError), + #[error(transparent)] + HexError(#[from] ::hex::FromHexError), + #[error(transparent)] + BitCoinBip32(#[from] ::bitcoin::bip32::Error), + #[error("83 length-missing SECP256K1 prefix")] + ConversionSECP256k1, + #[error("82 length-missing ED25519 prefix")] + ConversionED25519, + #[error("Expected Key length of 82 or 83 length was {0}")] + ConversionLength(usize), + #[error("Expected Key length of 40 length was {0}")] + ConversionLengthED25519Hex(usize), + #[error( + "Expected ED25519 key of length 32 with a BECH32 ED25519 prefix of 5 chars - Len {0} - Hex {1}" + )] + ConversionPrefixED25519(usize, String), + #[error("Can't call Transactions without some gas rules")] + NoGasOpts, + #[error("Can't parse `{parse}` into a coin")] + CoinParseErrV { parse: String }, + #[error("Can't parse `{0}` into a coin")] + CoinParseErr(String), + #[error("TX submit returned `{0}` - {1} '{2}'")] + TxResultError(usize, String, String), + #[error("No price found for Gas using denom {0}")] + GasPriceError(String), + #[error( + "Attempting to fetch validator set in parts, and failed Height mismatch {0} {1}" + )] + TendermintValidatorSet(u64, u64), + #[error("Transaction {0} not found after {1} attempts")] + TXNotFound(String, usize), + #[error("unknown API error")] + Unknown, + #[error("Generic Error {0}")] + StdErr(String), + #[error("calling contract with unimplemented action")] + NotImplemented, + #[error("new chain detected, fill out the scaffold at {0}")] + NewChain(String), + #[error("new network detected, fill out the scaffold at {0}")] + NewNetwork(String), + #[error("Can not connect to any grpc endpoint that was provided.")] + CannotConnectGRPC, + #[error("tx failed: {reason} with code {code}")] + TxFailed { code: usize, reason: String }, + #[error("The list of grpc endpoints is empty")] + GRPCListIsEmpty, + #[error("no wasm path provided for contract.")] + MissingWasmPath, + #[error("daemon builder missing {0}")] + BuilderMissing(String), + #[error("ibc error: {0}")] + IbcError(String), + #[error("insufficient fee, check gas price: {0}")] + InsufficientFee(String), + } + #[allow(unused_qualifications)] + impl std::error::Error for DaemonError { + fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> { + use thiserror::__private::AsDynError; + #[allow(deprecated)] + match self { + DaemonError::ReqwestError { 0: source, .. } => { + std::option::Option::Some(source.as_dyn_error()) + } + DaemonError::SerdeJson { 0: source, .. } => { + std::option::Option::Some(source.as_dyn_error()) + } + DaemonError::ParseIntError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::IOErr { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::Secp256k1 { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::VarError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::AnyError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::Status { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::TransportError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::TendermintError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::CwEnvError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::Bech32DecodeErr { .. } => std::option::Option::None, + DaemonError::Bech32DecodeExpanded { .. } => std::option::Option::None, + DaemonError::WrongLength { .. } => std::option::Option::None, + DaemonError::Phrasing { .. } => std::option::Option::None, + DaemonError::MissingPhrase { .. } => std::option::Option::None, + DaemonError::Implementation { .. } => std::option::Option::None, + DaemonError::Conversion { source: source, .. } => { + std::option::Option::Some(source.as_dyn_error()) + } + DaemonError::SharedDaemonState { .. } => std::option::Option::None, + DaemonError::ErrReport { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::GRpcDecodeError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::ED25519 { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::DecodeError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::HexError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::BitCoinBip32 { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::ConversionSECP256k1 { .. } => std::option::Option::None, + DaemonError::ConversionED25519 { .. } => std::option::Option::None, + DaemonError::ConversionLength { .. } => std::option::Option::None, + DaemonError::ConversionLengthED25519Hex { .. } => { + std::option::Option::None + } + DaemonError::ConversionPrefixED25519 { .. } => std::option::Option::None, + DaemonError::NoGasOpts { .. } => std::option::Option::None, + DaemonError::CoinParseErrV { .. } => std::option::Option::None, + DaemonError::CoinParseErr { .. } => std::option::Option::None, + DaemonError::TxResultError { .. } => std::option::Option::None, + DaemonError::GasPriceError { .. } => std::option::Option::None, + DaemonError::TendermintValidatorSet { .. } => std::option::Option::None, + DaemonError::TXNotFound { .. } => std::option::Option::None, + DaemonError::Unknown { .. } => std::option::Option::None, + DaemonError::StdErr { .. } => std::option::Option::None, + DaemonError::NotImplemented { .. } => std::option::Option::None, + DaemonError::NewChain { .. } => std::option::Option::None, + DaemonError::NewNetwork { .. } => std::option::Option::None, + DaemonError::CannotConnectGRPC { .. } => std::option::Option::None, + DaemonError::TxFailed { .. } => std::option::Option::None, + DaemonError::GRPCListIsEmpty { .. } => std::option::Option::None, + DaemonError::MissingWasmPath { .. } => std::option::Option::None, + DaemonError::BuilderMissing { .. } => std::option::Option::None, + DaemonError::IbcError { .. } => std::option::Option::None, + DaemonError::InsufficientFee { .. } => std::option::Option::None, + } + } + } + #[allow(unused_qualifications)] + impl std::fmt::Display for DaemonError { + fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + use thiserror::__private::AsDisplay as _; + #[allow(unused_variables, deprecated, clippy::used_underscore_binding)] + match self { + DaemonError::ReqwestError(_0) => { + __formatter.write_fmt(format_args!("Reqwest HTTP(s) Error")) + } + DaemonError::SerdeJson(_0) => { + __formatter.write_fmt(format_args!("JSON Conversion Error")) + } + DaemonError::ParseIntError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::IOErr(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::Secp256k1(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::VarError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::AnyError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::Status(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::TransportError(_0) => { + std::fmt::Display::fmt(_0, __formatter) + } + DaemonError::TendermintError(_0) => { + std::fmt::Display::fmt(_0, __formatter) + } + DaemonError::CwEnvError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::Bech32DecodeErr {} => { + __formatter.write_fmt(format_args!("Bech32 Decode Error")) + } + DaemonError::Bech32DecodeExpanded(_0, _1, _2, _3) => { + __formatter + .write_fmt( + format_args!( + "Bech32 Decode Error: Key Failed prefix {0} or length {1} Wanted:{2}/{3}", + _0.as_display(), + _1.as_display(), + _2.as_display(), + _3.as_display(), + ), + ) + } + DaemonError::WrongLength {} => { + __formatter + .write_fmt( + format_args!( + "Mnemonic - Wrong length, it should be 24 words", + ), + ) + } + DaemonError::Phrasing {} => { + __formatter.write_fmt(format_args!("Mnemonic - Bad Phrase")) + } + DaemonError::MissingPhrase {} => { + __formatter.write_fmt(format_args!("Mnemonic - Missing Phrase")) + } + DaemonError::Implementation {} => { + __formatter + .write_fmt(format_args!("Bad Implementation. Missing Component")) + } + DaemonError::Conversion { key, source } => { + __formatter + .write_fmt( + format_args!( + "Unable to convert into public key `{0}`", + key.as_display(), + ), + ) + } + DaemonError::SharedDaemonState {} => { + __formatter + .write_fmt( + format_args!( + "Can not augment daemon deployment after usage in more than one contract.", + ), + ) + } + DaemonError::ErrReport(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::GRpcDecodeError(_0) => { + std::fmt::Display::fmt(_0, __formatter) + } + DaemonError::ED25519(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::DecodeError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::HexError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::BitCoinBip32(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::ConversionSECP256k1 {} => { + __formatter + .write_fmt(format_args!("83 length-missing SECP256K1 prefix")) + } + DaemonError::ConversionED25519 {} => { + __formatter + .write_fmt(format_args!("82 length-missing ED25519 prefix")) + } + DaemonError::ConversionLength(_0) => { + __formatter + .write_fmt( + format_args!( + "Expected Key length of 82 or 83 length was {0}", + _0.as_display(), + ), + ) + } + DaemonError::ConversionLengthED25519Hex(_0) => { + __formatter + .write_fmt( + format_args!( + "Expected Key length of 40 length was {0}", + _0.as_display(), + ), + ) + } + DaemonError::ConversionPrefixED25519(_0, _1) => { + __formatter + .write_fmt( + format_args!( + "Expected ED25519 key of length 32 with a BECH32 ED25519 prefix of 5 chars - Len {0} - Hex {1}", + _0.as_display(), + _1.as_display(), + ), + ) + } + DaemonError::NoGasOpts {} => { + __formatter + .write_fmt( + format_args!( + "Can\'t call Transactions without some gas rules", + ), + ) + } + DaemonError::CoinParseErrV { parse } => { + __formatter + .write_fmt( + format_args!( + "Can\'t parse `{0}` into a coin", + parse.as_display(), + ), + ) + } + DaemonError::CoinParseErr(_0) => { + __formatter + .write_fmt( + format_args!( + "Can\'t parse `{0}` into a coin", + _0.as_display(), + ), + ) + } + DaemonError::TxResultError(_0, _1, _2) => { + __formatter + .write_fmt( + format_args!( + "TX submit returned `{0}` - {1} \'{2}\'", + _0.as_display(), + _1.as_display(), + _2.as_display(), + ), + ) + } + DaemonError::GasPriceError(_0) => { + __formatter + .write_fmt( + format_args!( + "No price found for Gas using denom {0}", + _0.as_display(), + ), + ) + } + DaemonError::TendermintValidatorSet(_0, _1) => { + __formatter + .write_fmt( + format_args!( + "Attempting to fetch validator set in parts, and failed Height mismatch {0} {1}", + _0.as_display(), + _1.as_display(), + ), + ) + } + DaemonError::TXNotFound(_0, _1) => { + __formatter + .write_fmt( + format_args!( + "Transaction {0} not found after {1} attempts", + _0.as_display(), + _1.as_display(), + ), + ) + } + DaemonError::Unknown {} => { + __formatter.write_fmt(format_args!("unknown API error")) + } + DaemonError::StdErr(_0) => { + __formatter + .write_fmt(format_args!("Generic Error {0}", _0.as_display())) + } + DaemonError::NotImplemented {} => { + __formatter + .write_fmt( + format_args!("calling contract with unimplemented action"), + ) + } + DaemonError::NewChain(_0) => { + __formatter + .write_fmt( + format_args!( + "new chain detected, fill out the scaffold at {0}", + _0.as_display(), + ), + ) + } + DaemonError::NewNetwork(_0) => { + __formatter + .write_fmt( + format_args!( + "new network detected, fill out the scaffold at {0}", + _0.as_display(), + ), + ) + } + DaemonError::CannotConnectGRPC {} => { + __formatter + .write_fmt( + format_args!( + "Can not connect to any grpc endpoint that was provided.", + ), + ) + } + DaemonError::TxFailed { code, reason } => { + __formatter + .write_fmt( + format_args!( + "tx failed: {0} with code {1}", + reason.as_display(), + code.as_display(), + ), + ) + } + DaemonError::GRPCListIsEmpty {} => { + __formatter + .write_fmt(format_args!("The list of grpc endpoints is empty")) + } + DaemonError::MissingWasmPath {} => { + __formatter + .write_fmt(format_args!("no wasm path provided for contract.")) + } + DaemonError::BuilderMissing(_0) => { + __formatter + .write_fmt( + format_args!("daemon builder missing {0}", _0.as_display()), + ) + } + DaemonError::IbcError(_0) => { + __formatter + .write_fmt(format_args!("ibc error: {0}", _0.as_display())) + } + DaemonError::InsufficientFee(_0) => { + __formatter + .write_fmt( + format_args!( + "insufficient fee, check gas price: {0}", + _0.as_display(), + ), + ) + } + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::reqwest::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::reqwest::Error) -> Self { + DaemonError::ReqwestError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::serde_json::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::serde_json::Error) -> Self { + DaemonError::SerdeJson { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From for DaemonError { + #[allow(deprecated)] + fn from(source: std::num::ParseIntError) -> Self { + DaemonError::ParseIntError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::std::io::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::std::io::Error) -> Self { + DaemonError::IOErr { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::secp256k1::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::secp256k1::Error) -> Self { + DaemonError::Secp256k1 { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::std::env::VarError> for DaemonError { + #[allow(deprecated)] + fn from(source: ::std::env::VarError) -> Self { + DaemonError::VarError { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::anyhow::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::anyhow::Error) -> Self { + DaemonError::AnyError { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::tonic::Status> for DaemonError { + #[allow(deprecated)] + fn from(source: ::tonic::Status) -> Self { + DaemonError::Status { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::tonic::transport::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::tonic::transport::Error) -> Self { + DaemonError::TransportError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::cosmrs::tendermint::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::cosmrs::tendermint::Error) -> Self { + DaemonError::TendermintError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::cw_orch_core::CwEnvError> for DaemonError { + #[allow(deprecated)] + fn from(source: ::cw_orch_core::CwEnvError) -> Self { + DaemonError::CwEnvError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::eyre::ErrReport> for DaemonError { + #[allow(deprecated)] + fn from(source: ::eyre::ErrReport) -> Self { + DaemonError::ErrReport { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::prost::DecodeError> for DaemonError { + #[allow(deprecated)] + fn from(source: ::prost::DecodeError) -> Self { + DaemonError::GRpcDecodeError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::ed25519_dalek::ed25519::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::ed25519_dalek::ed25519::Error) -> Self { + DaemonError::ED25519 { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::base64::DecodeError> for DaemonError { + #[allow(deprecated)] + fn from(source: ::base64::DecodeError) -> Self { + DaemonError::DecodeError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::hex::FromHexError> for DaemonError { + #[allow(deprecated)] + fn from(source: ::hex::FromHexError) -> Self { + DaemonError::HexError { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::bitcoin::bip32::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::bitcoin::bip32::Error) -> Self { + DaemonError::BitCoinBip32 { + 0: source, + } + } + } + #[automatically_derived] + impl ::core::fmt::Debug for DaemonError { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + DaemonError::ReqwestError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ReqwestError", + &__self_0, + ) + } + DaemonError::SerdeJson(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "SerdeJson", + &__self_0, + ) + } + DaemonError::ParseIntError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ParseIntError", + &__self_0, + ) + } + DaemonError::IOErr(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "IOErr", + &__self_0, + ) + } + DaemonError::Secp256k1(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "Secp256k1", + &__self_0, + ) + } + DaemonError::VarError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "VarError", + &__self_0, + ) + } + DaemonError::AnyError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "AnyError", + &__self_0, + ) + } + DaemonError::Status(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "Status", + &__self_0, + ) + } + DaemonError::TransportError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "TransportError", + &__self_0, + ) + } + DaemonError::TendermintError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "TendermintError", + &__self_0, + ) + } + DaemonError::CwEnvError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "CwEnvError", + &__self_0, + ) + } + DaemonError::Bech32DecodeErr => { + ::core::fmt::Formatter::write_str(f, "Bech32DecodeErr") + } + DaemonError::Bech32DecodeExpanded( + __self_0, + __self_1, + __self_2, + __self_3, + ) => { + ::core::fmt::Formatter::debug_tuple_field4_finish( + f, + "Bech32DecodeExpanded", + __self_0, + __self_1, + __self_2, + &__self_3, + ) + } + DaemonError::WrongLength => { + ::core::fmt::Formatter::write_str(f, "WrongLength") + } + DaemonError::Phrasing => ::core::fmt::Formatter::write_str(f, "Phrasing"), + DaemonError::MissingPhrase => { + ::core::fmt::Formatter::write_str(f, "MissingPhrase") + } + DaemonError::Implementation => { + ::core::fmt::Formatter::write_str(f, "Implementation") + } + DaemonError::Conversion { key: __self_0, source: __self_1 } => { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "Conversion", + "key", + __self_0, + "source", + &__self_1, + ) + } + DaemonError::SharedDaemonState => { + ::core::fmt::Formatter::write_str(f, "SharedDaemonState") + } + DaemonError::ErrReport(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ErrReport", + &__self_0, + ) + } + DaemonError::GRpcDecodeError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "GRpcDecodeError", + &__self_0, + ) + } + DaemonError::ED25519(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ED25519", + &__self_0, + ) + } + DaemonError::DecodeError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "DecodeError", + &__self_0, + ) + } + DaemonError::HexError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "HexError", + &__self_0, + ) + } + DaemonError::BitCoinBip32(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "BitCoinBip32", + &__self_0, + ) + } + DaemonError::ConversionSECP256k1 => { + ::core::fmt::Formatter::write_str(f, "ConversionSECP256k1") + } + DaemonError::ConversionED25519 => { + ::core::fmt::Formatter::write_str(f, "ConversionED25519") + } + DaemonError::ConversionLength(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ConversionLength", + &__self_0, + ) + } + DaemonError::ConversionLengthED25519Hex(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ConversionLengthED25519Hex", + &__self_0, + ) + } + DaemonError::ConversionPrefixED25519(__self_0, __self_1) => { + ::core::fmt::Formatter::debug_tuple_field2_finish( + f, + "ConversionPrefixED25519", + __self_0, + &__self_1, + ) + } + DaemonError::NoGasOpts => { + ::core::fmt::Formatter::write_str(f, "NoGasOpts") + } + DaemonError::CoinParseErrV { parse: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "CoinParseErrV", + "parse", + &__self_0, + ) + } + DaemonError::CoinParseErr(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "CoinParseErr", + &__self_0, + ) + } + DaemonError::TxResultError(__self_0, __self_1, __self_2) => { + ::core::fmt::Formatter::debug_tuple_field3_finish( + f, + "TxResultError", + __self_0, + __self_1, + &__self_2, + ) + } + DaemonError::GasPriceError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "GasPriceError", + &__self_0, + ) + } + DaemonError::TendermintValidatorSet(__self_0, __self_1) => { + ::core::fmt::Formatter::debug_tuple_field2_finish( + f, + "TendermintValidatorSet", + __self_0, + &__self_1, + ) + } + DaemonError::TXNotFound(__self_0, __self_1) => { + ::core::fmt::Formatter::debug_tuple_field2_finish( + f, + "TXNotFound", + __self_0, + &__self_1, + ) + } + DaemonError::Unknown => ::core::fmt::Formatter::write_str(f, "Unknown"), + DaemonError::StdErr(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "StdErr", + &__self_0, + ) + } + DaemonError::NotImplemented => { + ::core::fmt::Formatter::write_str(f, "NotImplemented") + } + DaemonError::NewChain(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "NewChain", + &__self_0, + ) + } + DaemonError::NewNetwork(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "NewNetwork", + &__self_0, + ) + } + DaemonError::CannotConnectGRPC => { + ::core::fmt::Formatter::write_str(f, "CannotConnectGRPC") + } + DaemonError::TxFailed { code: __self_0, reason: __self_1 } => { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "TxFailed", + "code", + __self_0, + "reason", + &__self_1, + ) + } + DaemonError::GRPCListIsEmpty => { + ::core::fmt::Formatter::write_str(f, "GRPCListIsEmpty") + } + DaemonError::MissingWasmPath => { + ::core::fmt::Formatter::write_str(f, "MissingWasmPath") + } + DaemonError::BuilderMissing(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "BuilderMissing", + &__self_0, + ) + } + DaemonError::IbcError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "IbcError", + &__self_0, + ) + } + DaemonError::InsufficientFee(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "InsufficientFee", + &__self_0, + ) + } + } + } + } + impl DaemonError { + pub fn ibc_err(msg: impl ToString) -> Self { + Self::IbcError(msg.to_string()) + } + } + impl From for CwEnvError { + fn from(val: DaemonError) -> Self { + CwEnvError::AnyError(val.into()) + } + } +} +pub(crate) mod json_file { + use serde_json::{from_reader, json, Value}; + use std::fs::{File, OpenOptions}; + pub fn write( + filename: &String, + chain_id: &String, + network_id: &String, + deploy_id: &String, + ) { + let file = OpenOptions::new() + .create(true) + .read(true) + .write(true) + .truncate(false) + .open(filename) + .unwrap(); + let mut json: Value = if file.metadata().unwrap().len().eq(&0) { + ::serde_json::Value::Object(::serde_json::Map::new()) + } else { + from_reader(file).unwrap() + }; + if json.get(network_id).is_none() { + json[network_id] = ::serde_json::Value::Object(::serde_json::Map::new()); + } + if json[network_id].get(chain_id).is_none() { + json[network_id][chain_id] = ::serde_json::Value::Object({ + let mut object = ::serde_json::Map::new(); + let _ = object + .insert( + (deploy_id).into(), + ::serde_json::Value::Object(::serde_json::Map::new()), + ); + let _ = object + .insert( + ("code_ids").into(), + ::serde_json::Value::Object(::serde_json::Map::new()), + ); + object + }); + } + serde_json::to_writer_pretty(File::create(filename).unwrap(), &json).unwrap(); + } + pub fn read(filename: &String) -> Value { + let file = File::open(filename) + .unwrap_or_else(|_| { + ::core::panicking::panic_fmt( + format_args!("File should be present at {0}", filename), + ); + }); + let json: serde_json::Value = from_reader(file).unwrap(); + json + } +} +/// Proto types for different blockchains +pub mod proto { + pub mod injective { + #![allow(missing_docs)] + use crate::DaemonError; + use cosmrs::tx::SignDoc; + use cosmrs::{proto::traits::TypeUrl, tx::Raw}; + pub const ETHEREUM_COIN_TYPE: u32 = 60; + pub struct InjectiveEthAccount { + #[prost(message, optional, tag = "1")] + pub base_account: ::core::option::Option< + super::super::cosmos_modules::auth::BaseAccount, + >, + #[prost(bytes, tag = "2")] + pub code_hash: Vec, + } + #[automatically_derived] + impl ::core::clone::Clone for InjectiveEthAccount { + #[inline] + fn clone(&self) -> InjectiveEthAccount { + InjectiveEthAccount { + base_account: ::core::clone::Clone::clone(&self.base_account), + code_hash: ::core::clone::Clone::clone(&self.code_hash), + } + } + } + #[automatically_derived] + impl ::core::marker::StructuralPartialEq for InjectiveEthAccount {} + #[automatically_derived] + impl ::core::cmp::PartialEq for InjectiveEthAccount { + #[inline] + fn eq(&self, other: &InjectiveEthAccount) -> bool { + self.base_account == other.base_account + && self.code_hash == other.code_hash + } + } + impl ::prost::Message for InjectiveEthAccount { + #[allow(unused_variables)] + fn encode_raw(&self, buf: &mut B) + where + B: ::prost::bytes::BufMut, + { + if let Some(ref msg) = self.base_account { + ::prost::encoding::message::encode(1u32, msg, buf); + } + if self.code_hash != b"" as &[u8] { + ::prost::encoding::bytes::encode(2u32, &self.code_hash, buf); + } + } + #[allow(unused_variables)] + fn merge_field( + &mut self, + tag: u32, + wire_type: ::prost::encoding::WireType, + buf: &mut B, + ctx: ::prost::encoding::DecodeContext, + ) -> ::core::result::Result<(), ::prost::DecodeError> + where + B: ::prost::bytes::Buf, + { + const STRUCT_NAME: &'static str = "InjectiveEthAccount"; + match tag { + 1u32 => { + let mut value = &mut self.base_account; + ::prost::encoding::message::merge( + wire_type, + value.get_or_insert_with(::core::default::Default::default), + buf, + ctx, + ) + .map_err(|mut error| { + error.push(STRUCT_NAME, "base_account"); + error + }) + } + 2u32 => { + let mut value = &mut self.code_hash; + ::prost::encoding::bytes::merge(wire_type, value, buf, ctx) + .map_err(|mut error| { + error.push(STRUCT_NAME, "code_hash"); + error + }) + } + _ => ::prost::encoding::skip_field(wire_type, tag, buf, ctx), + } + } + #[inline] + fn encoded_len(&self) -> usize { + 0 + + self + .base_account + .as_ref() + .map_or( + 0, + |msg| ::prost::encoding::message::encoded_len(1u32, msg), + ) + + if self.code_hash != b"" as &[u8] { + ::prost::encoding::bytes::encoded_len(2u32, &self.code_hash) + } else { + 0 + } + } + fn clear(&mut self) { + self.base_account = ::core::option::Option::None; + self.code_hash.clear(); + } + } + impl ::core::default::Default for InjectiveEthAccount { + fn default() -> Self { + InjectiveEthAccount { + base_account: ::core::default::Default::default(), + code_hash: ::core::default::Default::default(), + } + } + } + impl ::core::fmt::Debug for InjectiveEthAccount { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + let mut builder = f.debug_struct("InjectiveEthAccount"); + let builder = { + let wrapper = &self.base_account; + builder.field("base_account", &wrapper) + }; + let builder = { + let wrapper = { + fn ScalarWrapper(v: T) -> T { + v + } + ScalarWrapper(&self.code_hash) + }; + builder.field("code_hash", &wrapper) + }; + builder.finish() + } + } + pub struct InjectivePubKey { + #[prost(bytes, tag = 1)] + pub key: Vec, + } + #[automatically_derived] + impl ::core::clone::Clone for InjectivePubKey { + #[inline] + fn clone(&self) -> InjectivePubKey { + InjectivePubKey { + key: ::core::clone::Clone::clone(&self.key), + } + } + } + #[automatically_derived] + impl ::core::marker::StructuralPartialEq for InjectivePubKey {} + #[automatically_derived] + impl ::core::cmp::PartialEq for InjectivePubKey { + #[inline] + fn eq(&self, other: &InjectivePubKey) -> bool { + self.key == other.key + } + } + impl ::prost::Message for InjectivePubKey { + #[allow(unused_variables)] + fn encode_raw(&self, buf: &mut B) + where + B: ::prost::bytes::BufMut, + { + if self.key != b"" as &[u8] { + ::prost::encoding::bytes::encode(1u32, &self.key, buf); + } + } + #[allow(unused_variables)] + fn merge_field( + &mut self, + tag: u32, + wire_type: ::prost::encoding::WireType, + buf: &mut B, + ctx: ::prost::encoding::DecodeContext, + ) -> ::core::result::Result<(), ::prost::DecodeError> + where + B: ::prost::bytes::Buf, + { + const STRUCT_NAME: &'static str = "InjectivePubKey"; + match tag { + 1u32 => { + let mut value = &mut self.key; + ::prost::encoding::bytes::merge(wire_type, value, buf, ctx) + .map_err(|mut error| { + error.push(STRUCT_NAME, "key"); + error + }) + } + _ => ::prost::encoding::skip_field(wire_type, tag, buf, ctx), + } + } + #[inline] + fn encoded_len(&self) -> usize { + 0 + + if self.key != b"" as &[u8] { + ::prost::encoding::bytes::encoded_len(1u32, &self.key) + } else { + 0 + } + } + fn clear(&mut self) { + self.key.clear(); + } + } + impl ::core::default::Default for InjectivePubKey { + fn default() -> Self { + InjectivePubKey { + key: ::core::default::Default::default(), + } + } + } + impl ::core::fmt::Debug for InjectivePubKey { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + let mut builder = f.debug_struct("InjectivePubKey"); + let builder = { + let wrapper = { + fn ScalarWrapper(v: T) -> T { + v + } + ScalarWrapper(&self.key) + }; + builder.field("key", &wrapper) + }; + builder.finish() + } + } + impl TypeUrl for InjectivePubKey { + const TYPE_URL: &'static str = "/injective.crypto.v1beta1.ethsecp256k1.PubKey"; + } + pub trait InjectiveSigner { + fn sign_injective(&self, sign_doc: SignDoc) -> Result; + } + } +} +pub mod sender { + use crate::{networks::ChainKind, proto::injective::ETHEREUM_COIN_TYPE}; + use super::{ + cosmos_modules::{self, auth::BaseAccount}, + error::DaemonError, queriers::{DaemonQuerier, Node}, + state::DaemonState, tx_builder::TxBuilder, tx_resp::CosmTxResponse, + }; + use crate::proto::injective::InjectiveEthAccount; + use crate::{core::parse_cw_coins, keys::private::PrivateKey}; + use cosmrs::{ + bank::MsgSend, crypto::secp256k1::SigningKey, proto::traits::Message, + tendermint::chain::Id, + tx::{self, ModeInfo, Msg, Raw, SignDoc, SignMode, SignerInfo}, + AccountId, + }; + use cosmwasm_std::Addr; + use secp256k1::{All, Context, Secp256k1, Signing}; + use std::{convert::TryFrom, env, rc::Rc, str::FromStr}; + use cosmos_modules::vesting::PeriodicVestingAccount; + use tonic::transport::Channel; + /// A wallet is a sender of transactions, can be safely cloned and shared within the same thread. + pub type Wallet = Rc>; + /// Signer of the transactions and helper for address derivation + /// This is the main interface for simulating and signing transactions + pub struct Sender { + pub private_key: PrivateKey, + pub secp: Secp256k1, + pub(crate) daemon_state: Rc, + } + impl Sender { + pub fn new(daemon_state: &Rc) -> Result, DaemonError> { + let kind = ChainKind::from(daemon_state.chain_data.network_type.clone()); + let mnemonic = env::var(kind.mnemonic_name()) + .unwrap_or_else(|_| { + { + ::core::panicking::panic_fmt( + format_args!( + "Wallet mnemonic environment variable {0} not set.", + kind.mnemonic_name(), + ), + ); + } + }); + Self::from_mnemonic(daemon_state, &mnemonic) + } + /// Construct a new Sender from a mnemonic + pub fn from_mnemonic( + daemon_state: &Rc, + mnemonic: &str, + ) -> Result, DaemonError> { + let secp = Secp256k1::new(); + let p_key: PrivateKey = PrivateKey::from_words( + &secp, + mnemonic, + 0, + 0, + daemon_state.chain_data.slip44, + )?; + let sender = Sender { + daemon_state: daemon_state.clone(), + private_key: p_key, + secp, + }; + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Interacting with {0} using address: {1}", + daemon_state.chain_data.chain_id, + sender.pub_addr_str()?, + ), + lvl, + &( + "cw_orch_daemon::sender", + "cw_orch_daemon::sender", + "cw-orch-daemon/src/sender.rs", + ), + 71u32, + ::log::__private_api::Option::None, + ); + } + }; + Ok(sender) + } + fn cosmos_private_key(&self) -> SigningKey { + SigningKey::from_slice(&self.private_key.raw_key()).unwrap() + } + pub fn channel(&self) -> Channel { + self.daemon_state.grpc_channel.clone() + } + pub fn pub_addr(&self) -> Result { + Ok( + AccountId::new( + &self.daemon_state.chain_data.bech32_prefix, + &self.private_key.public_key(&self.secp).raw_address.unwrap(), + )?, + ) + } + pub fn address(&self) -> Result { + Ok(Addr::unchecked(self.pub_addr_str()?)) + } + pub fn pub_addr_str(&self) -> Result { + Ok(self.pub_addr()?.to_string()) + } + pub async fn bank_send( + &self, + recipient: &str, + coins: Vec, + ) -> Result { + let msg_send = MsgSend { + from_address: self.pub_addr()?, + to_address: AccountId::from_str(recipient)?, + amount: parse_cw_coins(&coins)?, + }; + self.commit_tx( + <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([msg_send])), + Some("sending tokens"), + ) + .await + } + pub async fn calculate_gas( + &self, + tx_body: &tx::Body, + sequence: u64, + account_number: u64, + ) -> Result { + let fee = TxBuilder::build_fee( + 0u8, + &self.daemon_state.chain_data.fees.fee_tokens[0].denom, + 0, + ); + let auth_info = SignerInfo { + public_key: self.private_key.get_signer_public_key(&self.secp), + mode_info: ModeInfo::single(SignMode::Direct), + sequence, + } + .auth_info(fee); + let sign_doc = SignDoc::new( + tx_body, + &auth_info, + &Id::try_from(self.daemon_state.chain_data.chain_id.to_string())?, + account_number, + )?; + let tx_raw = self.sign(sign_doc)?; + Node::new(self.channel()).simulate_tx(tx_raw.to_bytes()?).await + } + pub async fn commit_tx( + &self, + msgs: Vec, + memo: Option<&str>, + ) -> Result { + let timeout_height = Node::new(self.channel()).block_height().await? + 10u64; + let tx_body = TxBuilder::build_body(msgs, memo, timeout_height); + let mut tx_builder = TxBuilder::new(tx_body); + let tx = tx_builder.build(self).await?; + let mut tx_response = self.broadcast_tx(tx).await?; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("tx broadcast response: {0:?}", tx_response), + lvl, + &( + "cw_orch_daemon::sender", + "cw_orch_daemon::sender", + "cw-orch-daemon/src/sender.rs", + ), + 166u32, + ::log::__private_api::Option::None, + ); + } + }; + if has_insufficient_fee(&tx_response.raw_log) { + let suggested_fee = parse_suggested_fee(&tx_response.raw_log); + let Some(new_fee) = suggested_fee else { + return Err(DaemonError::InsufficientFee(tx_response.raw_log)); + }; + tx_builder.fee_amount(new_fee); + let tx = tx_builder.build(self).await?; + tx_response = self.broadcast_tx(tx).await?; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("tx broadcast response: {0:?}", tx_response), + lvl, + &( + "cw_orch_daemon::sender", + "cw_orch_daemon::sender", + "cw-orch-daemon/src/sender.rs", + ), + 181u32, + ::log::__private_api::Option::None, + ); + } + }; + } + let resp = Node::new(self.channel()).find_tx(tx_response.txhash).await?; + if resp.code == 0 { + Ok(resp) + } else { + Err(DaemonError::TxFailed { + code: resp.code, + reason: resp.raw_log, + }) + } + } + pub fn sign(&self, sign_doc: SignDoc) -> Result { + let tx_raw = if self.private_key.coin_type == ETHEREUM_COIN_TYPE { + { + ::core::panicking::panic_fmt( + format_args!( + "Coin Type {0} not supported without eth feature", + ETHEREUM_COIN_TYPE, + ), + ); + }; + } else { + sign_doc.sign(&self.cosmos_private_key())? + }; + Ok(tx_raw) + } + pub async fn base_account(&self) -> Result { + let addr = self.pub_addr().unwrap().to_string(); + let mut client = cosmos_modules::auth::query_client::QueryClient::new( + self.channel(), + ); + let resp = client + .account(cosmos_modules::auth::QueryAccountRequest { + address: addr, + }) + .await? + .into_inner(); + let account = resp.account.unwrap().value; + let acc = if let Ok(acc) = BaseAccount::decode(account.as_ref()) { + acc + } else if let Ok(acc) = PeriodicVestingAccount::decode(account.as_ref()) { + acc.base_vesting_account.unwrap().base_account.unwrap() + } else if let Ok(acc) = InjectiveEthAccount::decode(account.as_ref()) { + acc.base_account.unwrap() + } else { + return Err( + DaemonError::StdErr( + "Unknown account type returned from QueryAccountRequest".into(), + ), + ); + }; + Ok(acc) + } + async fn broadcast_tx( + &self, + tx: Raw, + ) -> Result< + cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse, + DaemonError, + > { + let mut client = cosmos_modules::tx::service_client::ServiceClient::new( + self.channel(), + ); + let commit = client + .broadcast_tx(cosmos_modules::tx::BroadcastTxRequest { + tx_bytes: tx.to_bytes()?, + mode: cosmos_modules::tx::BroadcastMode::Sync.into(), + }) + .await?; + let commit = commit.into_inner().tx_response.unwrap(); + Ok(commit) + } + } + fn has_insufficient_fee(raw_log: &str) -> bool { + raw_log.contains("insufficient fees") + } + fn parse_suggested_fee(raw_log: &str) -> Option { + let parts: Vec<&str> = raw_log.split("required: ").collect(); + if parts.len() != 2 { + return None; + } + let got_parts: Vec<&str> = parts[0].split_whitespace().collect(); + let paid_fee_with_denom = got_parts.last()?; + let (_, denomination) = paid_fee_with_denom + .split_at(paid_fee_with_denom.find(|c: char| !c.is_numeric())?); + { + ::std::io::_eprint(format_args!("denom: {0}\n", denomination)); + }; + let required_fees: Vec<&str> = parts[1].split(denomination).collect(); + { + ::std::io::_eprint(format_args!("required fees: {0:?}\n", required_fees)); + }; + let (_, suggested_fee) = required_fees[0] + .split_at(required_fees[0].rfind(|c: char| !c.is_numeric())?); + { + ::std::io::_eprint(format_args!("suggested fee: {0}\n", suggested_fee)); + }; + suggested_fee.parse::().ok().or(suggested_fee[1..].parse::().ok()) + } +} +pub mod state { + use super::error::DaemonError; + use crate::{channel::GrpcChannel, networks::ChainKind}; + use cosmwasm_std::Addr; + use cw_orch_core::{ + environment::{DeployDetails, StateInterface}, + CwEnvError, + }; + use ibc_chain_registry::chain::ChainData; + use serde::Serialize; + use serde_json::{json, Value}; + use std::{collections::HashMap, env, fs::File, path::Path}; + use tonic::transport::Channel; + /// Stores the chain information and deployment state. + /// Uses a simple JSON file to store the deployment information locally. + pub struct DaemonState { + /// this is passed via env var STATE_FILE + pub json_file_path: String, + /// Deployment identifier + pub deployment_id: String, + /// gRPC channel + pub grpc_channel: Channel, + /// Information about the chain + pub chain_data: ChainData, + } + #[automatically_derived] + impl ::core::clone::Clone for DaemonState { + #[inline] + fn clone(&self) -> DaemonState { + DaemonState { + json_file_path: ::core::clone::Clone::clone(&self.json_file_path), + deployment_id: ::core::clone::Clone::clone(&self.deployment_id), + grpc_channel: ::core::clone::Clone::clone(&self.grpc_channel), + chain_data: ::core::clone::Clone::clone(&self.chain_data), + } + } + } + #[automatically_derived] + impl ::core::fmt::Debug for DaemonState { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field4_finish( + f, + "DaemonState", + "json_file_path", + &self.json_file_path, + "deployment_id", + &self.deployment_id, + "grpc_channel", + &self.grpc_channel, + "chain_data", + &&self.chain_data, + ) + } + } + impl DaemonState { + /// Creates a new state from the given chain data and deployment id. + /// Attempts to connect to any of the provided gRPC endpoints. + pub async fn new( + mut chain_data: ChainData, + deployment_id: String, + ) -> Result { + if chain_data.apis.grpc.is_empty() { + return Err(DaemonError::GRPCListIsEmpty); + } + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Found {0} gRPC endpoints", + chain_data.apis.grpc.len(), + ), + lvl, + &( + "cw_orch_daemon::state", + "cw_orch_daemon::state", + "cw-orch-daemon/src/state.rs", + ), + 40u32, + ::log::__private_api::Option::None, + ); + } + }; + let grpc_channel = GrpcChannel::connect( + &chain_data.apis.grpc, + &chain_data.chain_id, + ) + .await?; + let mut json_file_path = env::var("STATE_FILE") + .unwrap_or("./state.json".to_string()); + if chain_data.network_type == ChainKind::Local.to_string() { + let name = Path::new(&json_file_path) + .file_stem() + .unwrap() + .to_str() + .unwrap(); + let folder = Path::new(&json_file_path) + .parent() + .unwrap() + .to_str() + .unwrap(); + json_file_path = { + let res = ::alloc::fmt::format( + format_args!("{0}/{1}_local.json", folder, name), + ); + res + }; + } + let shortest_denom_token = chain_data + .fees + .fee_tokens + .iter() + .fold( + chain_data.fees.fee_tokens[0].clone(), + |acc, item| { + if item.denom.len() < acc.denom.len() { + item.clone() + } else { + acc + } + }, + ); + chain_data + .fees + .fee_tokens = <[_]>::into_vec( + #[rustc_box] + ::alloc::boxed::Box::new([shortest_denom_token]), + ); + let state = DaemonState { + json_file_path, + deployment_id, + grpc_channel, + chain_data, + }; + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Writing daemon state JSON file: {0:#?}", + state.json_file_path, + ), + lvl, + &( + "cw_orch_daemon::state", + "cw_orch_daemon::state", + "cw-orch-daemon/src/state.rs", + ), + 87u32, + ::log::__private_api::Option::None, + ); + } + }; + crate::json_file::write( + &state.json_file_path, + &state.chain_data.chain_id.to_string(), + &state.chain_data.chain_name, + &state.deployment_id, + ); + Ok(state) + } + /// Get the state filepath and read it as json + fn read_state(&self) -> serde_json::Value { + crate::json_file::read(&self.json_file_path) + } + /// Retrieve a stateful value using the chainId and networkId + pub fn get(&self, key: &str) -> Value { + let json = self.read_state(); + json[&self.chain_data.chain_name][&self.chain_data.chain_id.to_string()][key] + .clone() + } + /// Set a stateful value using the chainId and networkId + pub fn set(&self, key: &str, contract_id: &str, value: T) { + let mut json = self.read_state(); + json[&self + .chain_data + .chain_name][&self + .chain_data + .chain_id + .to_string()][key][contract_id] = ::serde_json::to_value(&value) + .unwrap(); + serde_json::to_writer_pretty( + File::create(&self.json_file_path).unwrap(), + &json, + ) + .unwrap(); + } + } + impl StateInterface for DaemonState { + /// Read address for contract in deployment id from state file + fn get_address(&self, contract_id: &str) -> Result { + let value = self + .get(&self.deployment_id) + .get(contract_id) + .ok_or_else(|| CwEnvError::AddrNotInStore(contract_id.to_owned()))? + .clone(); + Ok(Addr::unchecked(value.as_str().unwrap())) + } + /// Set address for contract in deployment id in state file + fn set_address(&mut self, contract_id: &str, address: &Addr) { + self.set(&self.deployment_id, contract_id, address.as_str()); + } + /// Get the locally-saved version of the contract's version on this network + fn get_code_id(&self, contract_id: &str) -> Result { + let value = self + .get("code_ids") + .get(contract_id) + .ok_or_else(|| CwEnvError::CodeIdNotInStore(contract_id.to_owned()))? + .clone(); + Ok(value.as_u64().unwrap()) + } + /// Set the locally-saved version of the contract's latest version on this network + fn set_code_id(&mut self, contract_id: &str, code_id: u64) { + self.set("code_ids", contract_id, code_id); + } + /// Get all addresses for deployment id from state file + fn get_all_addresses(&self) -> Result, CwEnvError> { + let mut store = HashMap::new(); + let addresses = self.get(&self.deployment_id); + let value = addresses.as_object().unwrap(); + for (id, addr) in value { + store.insert(id.clone(), Addr::unchecked(addr.as_str().unwrap())); + } + Ok(store) + } + fn get_all_code_ids(&self) -> Result, CwEnvError> { + let mut store = HashMap::new(); + let code_ids = self.get("code_ids"); + let value = code_ids.as_object().unwrap(); + for (id, code_id) in value { + store.insert(id.clone(), code_id.as_u64().unwrap()); + } + Ok(store) + } + fn deploy_details(&self) -> DeployDetails { + DeployDetails { + chain_id: self.chain_data.chain_id.to_string(), + chain_name: self.chain_data.chain_name.clone(), + deployment_id: self.deployment_id.clone(), + } + } + } +} +pub mod sync { + mod builder { + use ibc_chain_registry::chain::ChainData; + use crate::DaemonAsyncBuilder; + use super::{super::error::DaemonError, core::Daemon}; + /// Create [`Daemon`] through [`DaemonBuilder`] + /// ## Example + /// ```no_run + /// use cw_orch_daemon::{networks, DaemonBuilder}; + /// + /// let Daemon = DaemonBuilder::default() + /// .chain(networks::LOCAL_JUNO) + /// .deployment_id("v0.1.0") + /// .build() + /// .unwrap(); + /// ``` + pub struct DaemonBuilder { + pub(crate) chain: Option, + pub(crate) handle: Option, + pub(crate) deployment_id: Option, + /// Wallet mnemonic + pub(crate) mnemonic: Option, + } + #[automatically_derived] + impl ::core::clone::Clone for DaemonBuilder { + #[inline] + fn clone(&self) -> DaemonBuilder { + DaemonBuilder { + chain: ::core::clone::Clone::clone(&self.chain), + handle: ::core::clone::Clone::clone(&self.handle), + deployment_id: ::core::clone::Clone::clone(&self.deployment_id), + mnemonic: ::core::clone::Clone::clone(&self.mnemonic), + } + } + } + #[automatically_derived] + impl ::core::default::Default for DaemonBuilder { + #[inline] + fn default() -> DaemonBuilder { + DaemonBuilder { + chain: ::core::default::Default::default(), + handle: ::core::default::Default::default(), + deployment_id: ::core::default::Default::default(), + mnemonic: ::core::default::Default::default(), + } + } + } + impl DaemonBuilder { + /// Set the chain the Daemon will connect to + pub fn chain(&mut self, chain: impl Into) -> &mut Self { + self.chain = Some(chain.into()); + self + } + /// Set the deployment id to use for the Daemon interactions + /// Defaults to `default` + pub fn deployment_id( + &mut self, + deployment_id: impl Into, + ) -> &mut Self { + self.deployment_id = Some(deployment_id.into()); + self + } + /// Set the tokio runtime handle to use for the Daemon + /// + /// ## Example + /// ```no_run + /// use cw_orch_daemon::Daemon; + /// use tokio::runtime::Runtime; + /// let rt = Runtime::new().unwrap(); + /// let Daemon = Daemon::builder() + /// .handle(rt.handle()) + /// // ... + /// .build() + /// .unwrap(); + /// ``` + pub fn handle(&mut self, handle: &tokio::runtime::Handle) -> &mut Self { + self.handle = Some(handle.clone()); + self + } + /// Set the mnemonic to use with this chain. + pub fn mnemonic(&mut self, mnemonic: impl ToString) -> &mut Self { + self.mnemonic = Some(mnemonic.to_string()); + self + } + /// Build a Daemon + pub fn build(&self) -> Result { + let rt_handle = self + .handle + .clone() + .ok_or(DaemonError::BuilderMissing("runtime handle".into()))?; + let daemon = rt_handle + .block_on(DaemonAsyncBuilder::from(self.clone()).build())?; + Ok(Daemon { rt_handle, daemon }) + } + } + } + mod core { + use std::{fmt::Debug, rc::Rc, time::Duration}; + use super::super::{sender::Wallet, DaemonAsync}; + use crate::{ + queriers::{DaemonQuerier, Node}, + CosmTxResponse, DaemonBuilder, DaemonError, DaemonState, + }; + use cosmrs::tendermint::Time; + use cosmwasm_std::{Addr, Coin}; + use cw_orch_core::{ + contract::{interface_traits::Uploadable, WasmPath}, + environment::{ChainState, TxHandler}, + }; + use serde::{de::DeserializeOwned, Serialize}; + use tokio::runtime::Handle; + use tonic::transport::Channel; + /** + Represents a blockchain node. + Is constructed with the [DaemonBuilder]. + + ## Usage + + ```rust,no_run + use cw_orch_daemon::{Daemon, networks}; + use tokio::runtime::Runtime; + + let rt = Runtime::new().unwrap(); + let daemon: Daemon = Daemon::builder() + .chain(networks::JUNO_1) + .handle(rt.handle()) + .build() + .unwrap(); + ``` + ## Environment Execution + + The Daemon implements [`TxHandler`] which allows you to perform transactions on the chain. + + ## Querying + + Different Cosmos SDK modules can be queried through the daemon by calling the [`Daemon.query_client`] method with a specific querier. + See [Querier](crate::queriers) for examples. +*/ + pub struct Daemon { + pub daemon: DaemonAsync, + /// Runtime handle to execute async tasks + pub rt_handle: Handle, + } + #[automatically_derived] + impl ::core::clone::Clone for Daemon { + #[inline] + fn clone(&self) -> Daemon { + Daemon { + daemon: ::core::clone::Clone::clone(&self.daemon), + rt_handle: ::core::clone::Clone::clone(&self.rt_handle), + } + } + } + impl Daemon { + /// Get the daemon builder + pub fn builder() -> DaemonBuilder { + DaemonBuilder::default() + } + /// Perform a query with a given querier + /// See [Querier](crate::queriers) for examples. + pub fn query_client(&self) -> Querier { + self.daemon.query_client() + } + /// Get the channel configured for this Daemon + pub fn channel(&self) -> Channel { + self.daemon.state.grpc_channel.clone() + } + /// Get the channel configured for this Daemon + pub fn wallet(&self) -> Wallet { + self.daemon.sender.clone() + } + } + impl ChainState for Daemon { + type Out = Rc; + fn state(&self) -> Self::Out { + self.daemon.state.clone() + } + } + impl TxHandler for Daemon { + type Response = CosmTxResponse; + type Error = DaemonError; + type ContractSource = WasmPath; + type Sender = Wallet; + fn sender(&self) -> Addr { + self.daemon.sender.address().unwrap() + } + fn set_sender(&mut self, sender: Self::Sender) { + self.daemon.sender = sender; + } + fn upload( + &self, + uploadable: &impl Uploadable, + ) -> Result { + self.rt_handle.block_on(self.daemon.upload(uploadable)) + } + fn execute( + &self, + exec_msg: &E, + coins: &[cosmwasm_std::Coin], + contract_address: &Addr, + ) -> Result { + self.rt_handle + .block_on(self.daemon.execute(exec_msg, coins, contract_address)) + } + fn instantiate( + &self, + code_id: u64, + init_msg: &I, + label: Option<&str>, + admin: Option<&Addr>, + coins: &[Coin], + ) -> Result { + self.rt_handle + .block_on( + self.daemon.instantiate(code_id, init_msg, label, admin, coins), + ) + } + fn query( + &self, + query_msg: &Q, + contract_address: &Addr, + ) -> Result { + self.rt_handle.block_on(self.daemon.query(query_msg, contract_address)) + } + fn migrate( + &self, + migrate_msg: &M, + new_code_id: u64, + contract_address: &Addr, + ) -> Result { + self.rt_handle + .block_on( + self.daemon.migrate(migrate_msg, new_code_id, contract_address), + ) + } + fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { + let mut last_height = self + .rt_handle + .block_on(self.query_client::().block_height())?; + let end_height = last_height + amount; + while last_height < end_height { + self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(4))); + last_height = self + .rt_handle + .block_on(self.query_client::().block_height())?; + } + Ok(()) + } + fn wait_seconds(&self, secs: u64) -> Result<(), DaemonError> { + self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(secs))); + Ok(()) + } + fn next_block(&self) -> Result<(), DaemonError> { + let mut last_height = self + .rt_handle + .block_on(self.query_client::().block_height())?; + let end_height = last_height + 1; + while last_height < end_height { + self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(4))); + last_height = self + .rt_handle + .block_on(self.query_client::().block_height())?; + } + Ok(()) + } + fn block_info(&self) -> Result { + let block = self + .rt_handle + .block_on(self.query_client::().latest_block())?; + let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; + let time = cosmwasm_std::Timestamp::from_nanos( + since_epoch.as_nanos() as u64, + ); + Ok(cosmwasm_std::BlockInfo { + height: block.header.height.value(), + time, + chain_id: block.header.chain_id.to_string(), + }) + } + } + } + pub use self::{builder::*, core::*}; +} +pub mod tx_resp { + use super::{ + cosmos_modules::{ + abci::{AbciMessageLog, Attribute, StringEvent, TxResponse}, + tendermint_abci::Event, + }, + error::DaemonError, + }; + use chrono::{DateTime, NaiveDateTime, TimeZone, Utc}; + use cosmwasm_std::{to_binary, Binary, StdError, StdResult}; + use cw_orch_core::environment::IndexResponse; + use serde::{Deserialize, Serialize}; + const FORMAT: &str = "%Y-%m-%dT%H:%M:%S%.f"; + const FORMAT_TZ_SUPPLIED: &str = "%Y-%m-%dT%H:%M:%S.%f%:z"; + const FORMAT_SHORT_Z: &str = "%Y-%m-%dT%H:%M:%SZ"; + const FORMAT_SHORT_Z2: &str = "%Y-%m-%dT%H:%M:%S.%fZ"; + /// The response from a transaction performed on a blockchain. + pub struct CosmTxResponse { + /// Height of the block in which the transaction was included. + pub height: u64, + /// Transaction hash. + pub txhash: String, + /// Transaction index within the block. + pub codespace: String, + /// Transaction result code + pub code: usize, + /// Arbitrary data that can be included in a transaction. + pub data: String, + /// Raw log message. + pub raw_log: String, + /// Logs of the transaction. + pub logs: Vec, + /// Transaction info. + pub info: String, + /// Gas limit. + pub gas_wanted: u64, + /// Gas used. + pub gas_used: u64, + /// Timestamp of the block in which the transaction was included. + pub timestamp: DateTime, + /// Transaction events. + pub events: Vec, + } + #[automatically_derived] + impl ::core::fmt::Debug for CosmTxResponse { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + let names: &'static _ = &[ + "height", + "txhash", + "codespace", + "code", + "data", + "raw_log", + "logs", + "info", + "gas_wanted", + "gas_used", + "timestamp", + "events", + ]; + let values: &[&dyn ::core::fmt::Debug] = &[ + &self.height, + &self.txhash, + &self.codespace, + &self.code, + &self.data, + &self.raw_log, + &self.logs, + &self.info, + &self.gas_wanted, + &self.gas_used, + &self.timestamp, + &&self.events, + ]; + ::core::fmt::Formatter::debug_struct_fields_finish( + f, + "CosmTxResponse", + names, + values, + ) + } + } + #[automatically_derived] + impl ::core::default::Default for CosmTxResponse { + #[inline] + fn default() -> CosmTxResponse { + CosmTxResponse { + height: ::core::default::Default::default(), + txhash: ::core::default::Default::default(), + codespace: ::core::default::Default::default(), + code: ::core::default::Default::default(), + data: ::core::default::Default::default(), + raw_log: ::core::default::Default::default(), + logs: ::core::default::Default::default(), + info: ::core::default::Default::default(), + gas_wanted: ::core::default::Default::default(), + gas_used: ::core::default::Default::default(), + timestamp: ::core::default::Default::default(), + events: ::core::default::Default::default(), + } + } + } + impl CosmTxResponse { + /// find a attribute's value from TX logs. + /// returns: msg_index and value + pub fn get_attribute_from_logs( + &self, + event_type: &str, + attribute_key: &str, + ) -> Vec<(usize, String)> { + let mut response: Vec<(usize, String)> = Default::default(); + let logs = &self.logs; + for log_part in logs { + let msg_index = log_part.msg_index.unwrap_or_default(); + let events = &log_part.events; + let events_filtered = events + .iter() + .filter(|event| event.s_type == event_type) + .collect::>(); + if let Some(event) = events_filtered.first() { + let attributes_filtered = event + .attributes + .iter() + .filter(|attr| attr.key == attribute_key) + .map(|f| f.value.clone()) + .collect::>(); + if let Some(attr_key) = attributes_filtered.first() { + response.push((msg_index, attr_key.clone())); + } + } + } + response + } + /// get the list of event types from a TX record + pub fn get_events(&self, event_type: &str) -> Vec { + let mut response: Vec = Default::default(); + for log_part in &self.logs { + let events = &log_part.events; + let events_filtered = events + .iter() + .filter(|event| event.s_type == event_type) + .collect::>(); + for event in events_filtered { + response.push(event.clone()); + } + } + response + } + } + impl From<&serde_json::Value> for TxResultBlockMsg { + fn from(value: &serde_json::Value) -> Self { + serde_json::from_value(value.clone()).unwrap() + } + } + impl From for CosmTxResponse { + fn from(tx: TxResponse) -> Self { + Self { + height: tx.height as u64, + txhash: tx.txhash, + codespace: tx.codespace, + code: tx.code as usize, + data: tx.data, + raw_log: tx.raw_log, + logs: tx.logs.into_iter().map(TxResultBlockMsg::from).collect(), + info: tx.info, + gas_wanted: tx.gas_wanted as u64, + gas_used: tx.gas_used as u64, + timestamp: parse_timestamp(tx.timestamp).unwrap(), + events: tx.events, + } + } + } + impl IndexResponse for CosmTxResponse { + fn events(&self) -> Vec { + let mut parsed_events = ::alloc::vec::Vec::new(); + for event in &self.events { + let mut pattr = ::alloc::vec::Vec::new(); + for attr in &event.attributes { + pattr + .push(cosmwasm_std::Attribute { + key: attr.key.clone(), + value: attr.value.clone(), + }) + } + let pevent = cosmwasm_std::Event::new(event.r#type.clone()) + .add_attributes(pattr); + parsed_events.push(pevent); + } + parsed_events + } + fn data(&self) -> Option { + if self.data.is_empty() { + None + } else { + Some(to_binary(self.data.as_bytes()).unwrap()) + } + } + fn event_attr_value( + &self, + event_type: &str, + attr_key: &str, + ) -> StdResult { + for event in &self.events { + if event.r#type == event_type { + for attr in &event.attributes { + if attr.key == attr_key { + return Ok(attr.value.clone()); + } + } + } + } + Err( + StdError::generic_err({ + let res = ::alloc::fmt::format( + format_args!( + "event of type {0} does not have a value at key {1}", + event_type, + attr_key, + ), + ); + res + }), + ) + } + } + /// The events from a single message in a transaction. + pub struct TxResultBlockMsg { + /// index of the message in the transaction + pub msg_index: Option, + /// Events from this message + pub events: Vec, + } + #[automatically_derived] + impl ::core::clone::Clone for TxResultBlockMsg { + #[inline] + fn clone(&self) -> TxResultBlockMsg { + TxResultBlockMsg { + msg_index: ::core::clone::Clone::clone(&self.msg_index), + events: ::core::clone::Clone::clone(&self.events), + } + } + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl _serde::Serialize for TxResultBlockMsg { + fn serialize<__S>( + &self, + __serializer: __S, + ) -> _serde::__private::Result<__S::Ok, __S::Error> + where + __S: _serde::Serializer, + { + let mut __serde_state = _serde::Serializer::serialize_struct( + __serializer, + "TxResultBlockMsg", + false as usize + 1 + 1, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "msg_index", + &self.msg_index, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "events", + &self.events, + )?; + _serde::ser::SerializeStruct::end(__serde_state) + } + } + }; + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl<'de> _serde::Deserialize<'de> for TxResultBlockMsg { + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + #[allow(non_camel_case_types)] + #[doc(hidden)] + enum __Field { + __field0, + __field1, + __ignore, + } + #[doc(hidden)] + struct __FieldVisitor; + impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { + type Value = __Field; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "field identifier", + ) + } + fn visit_u64<__E>( + self, + __value: u64, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + 0u64 => _serde::__private::Ok(__Field::__field0), + 1u64 => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_str<__E>( + self, + __value: &str, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + "msg_index" => _serde::__private::Ok(__Field::__field0), + "events" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_bytes<__E>( + self, + __value: &[u8], + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + b"msg_index" => _serde::__private::Ok(__Field::__field0), + b"events" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + } + impl<'de> _serde::Deserialize<'de> for __Field { + #[inline] + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + _serde::Deserializer::deserialize_identifier( + __deserializer, + __FieldVisitor, + ) + } + } + #[doc(hidden)] + struct __Visitor<'de> { + marker: _serde::__private::PhantomData, + lifetime: _serde::__private::PhantomData<&'de ()>, + } + impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { + type Value = TxResultBlockMsg; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "struct TxResultBlockMsg", + ) + } + #[inline] + fn visit_seq<__A>( + self, + mut __seq: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::SeqAccess<'de>, + { + let __field0 = match _serde::de::SeqAccess::next_element::< + Option, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 0usize, + &"struct TxResultBlockMsg with 2 elements", + ), + ); + } + }; + let __field1 = match _serde::de::SeqAccess::next_element::< + Vec, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 1usize, + &"struct TxResultBlockMsg with 2 elements", + ), + ); + } + }; + _serde::__private::Ok(TxResultBlockMsg { + msg_index: __field0, + events: __field1, + }) + } + #[inline] + fn visit_map<__A>( + self, + mut __map: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::MapAccess<'de>, + { + let mut __field0: _serde::__private::Option> = _serde::__private::None; + let mut __field1: _serde::__private::Option< + Vec, + > = _serde::__private::None; + while let _serde::__private::Some(__key) + = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { + match __key { + __Field::__field0 => { + if _serde::__private::Option::is_some(&__field0) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field( + "msg_index", + ), + ); + } + __field0 = _serde::__private::Some( + _serde::de::MapAccess::next_value::< + Option, + >(&mut __map)?, + ); + } + __Field::__field1 => { + if _serde::__private::Option::is_some(&__field1) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field("events"), + ); + } + __field1 = _serde::__private::Some( + _serde::de::MapAccess::next_value::< + Vec, + >(&mut __map)?, + ); + } + _ => { + let _ = _serde::de::MapAccess::next_value::< + _serde::de::IgnoredAny, + >(&mut __map)?; + } + } + } + let __field0 = match __field0 { + _serde::__private::Some(__field0) => __field0, + _serde::__private::None => { + _serde::__private::de::missing_field("msg_index")? + } + }; + let __field1 = match __field1 { + _serde::__private::Some(__field1) => __field1, + _serde::__private::None => { + _serde::__private::de::missing_field("events")? + } + }; + _serde::__private::Ok(TxResultBlockMsg { + msg_index: __field0, + events: __field1, + }) + } + } + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &["msg_index", "events"]; + _serde::Deserializer::deserialize_struct( + __deserializer, + "TxResultBlockMsg", + FIELDS, + __Visitor { + marker: _serde::__private::PhantomData::, + lifetime: _serde::__private::PhantomData, + }, + ) + } + } + }; + #[automatically_derived] + impl ::core::fmt::Debug for TxResultBlockMsg { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "TxResultBlockMsg", + "msg_index", + &self.msg_index, + "events", + &&self.events, + ) + } + } + impl From for TxResultBlockMsg { + fn from(msg: AbciMessageLog) -> Self { + Self { + msg_index: Some(msg.msg_index as usize), + events: msg.events.into_iter().map(TxResultBlockEvent::from).collect(), + } + } + } + /// A single event from a transaction and its attributes. + pub struct TxResultBlockEvent { + #[serde(rename = "type")] + /// Type of the event + pub s_type: String, + /// Attributes of the event + pub attributes: Vec, + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl<'de> _serde::Deserialize<'de> for TxResultBlockEvent { + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + #[allow(non_camel_case_types)] + #[doc(hidden)] + enum __Field { + __field0, + __field1, + __ignore, + } + #[doc(hidden)] + struct __FieldVisitor; + impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { + type Value = __Field; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "field identifier", + ) + } + fn visit_u64<__E>( + self, + __value: u64, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + 0u64 => _serde::__private::Ok(__Field::__field0), + 1u64 => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_str<__E>( + self, + __value: &str, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + "type" => _serde::__private::Ok(__Field::__field0), + "attributes" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_bytes<__E>( + self, + __value: &[u8], + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + b"type" => _serde::__private::Ok(__Field::__field0), + b"attributes" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + } + impl<'de> _serde::Deserialize<'de> for __Field { + #[inline] + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + _serde::Deserializer::deserialize_identifier( + __deserializer, + __FieldVisitor, + ) + } + } + #[doc(hidden)] + struct __Visitor<'de> { + marker: _serde::__private::PhantomData, + lifetime: _serde::__private::PhantomData<&'de ()>, + } + impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { + type Value = TxResultBlockEvent; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "struct TxResultBlockEvent", + ) + } + #[inline] + fn visit_seq<__A>( + self, + mut __seq: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::SeqAccess<'de>, + { + let __field0 = match _serde::de::SeqAccess::next_element::< + String, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 0usize, + &"struct TxResultBlockEvent with 2 elements", + ), + ); + } + }; + let __field1 = match _serde::de::SeqAccess::next_element::< + Vec, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 1usize, + &"struct TxResultBlockEvent with 2 elements", + ), + ); + } + }; + _serde::__private::Ok(TxResultBlockEvent { + s_type: __field0, + attributes: __field1, + }) + } + #[inline] + fn visit_map<__A>( + self, + mut __map: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::MapAccess<'de>, + { + let mut __field0: _serde::__private::Option = _serde::__private::None; + let mut __field1: _serde::__private::Option< + Vec, + > = _serde::__private::None; + while let _serde::__private::Some(__key) + = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { + match __key { + __Field::__field0 => { + if _serde::__private::Option::is_some(&__field0) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field("type"), + ); + } + __field0 = _serde::__private::Some( + _serde::de::MapAccess::next_value::(&mut __map)?, + ); + } + __Field::__field1 => { + if _serde::__private::Option::is_some(&__field1) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field( + "attributes", + ), + ); + } + __field1 = _serde::__private::Some( + _serde::de::MapAccess::next_value::< + Vec, + >(&mut __map)?, + ); + } + _ => { + let _ = _serde::de::MapAccess::next_value::< + _serde::de::IgnoredAny, + >(&mut __map)?; + } + } + } + let __field0 = match __field0 { + _serde::__private::Some(__field0) => __field0, + _serde::__private::None => { + _serde::__private::de::missing_field("type")? + } + }; + let __field1 = match __field1 { + _serde::__private::Some(__field1) => __field1, + _serde::__private::None => { + _serde::__private::de::missing_field("attributes")? + } + }; + _serde::__private::Ok(TxResultBlockEvent { + s_type: __field0, + attributes: __field1, + }) + } + } + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &["type", "attributes"]; + _serde::Deserializer::deserialize_struct( + __deserializer, + "TxResultBlockEvent", + FIELDS, + __Visitor { + marker: _serde::__private::PhantomData::, + lifetime: _serde::__private::PhantomData, + }, + ) + } + } + }; + #[automatically_derived] + impl ::core::clone::Clone for TxResultBlockEvent { + #[inline] + fn clone(&self) -> TxResultBlockEvent { + TxResultBlockEvent { + s_type: ::core::clone::Clone::clone(&self.s_type), + attributes: ::core::clone::Clone::clone(&self.attributes), + } + } + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl _serde::Serialize for TxResultBlockEvent { + fn serialize<__S>( + &self, + __serializer: __S, + ) -> _serde::__private::Result<__S::Ok, __S::Error> + where + __S: _serde::Serializer, + { + let mut __serde_state = _serde::Serializer::serialize_struct( + __serializer, + "TxResultBlockEvent", + false as usize + 1 + 1, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "type", + &self.s_type, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "attributes", + &self.attributes, + )?; + _serde::ser::SerializeStruct::end(__serde_state) + } + } + }; + #[automatically_derived] + impl ::core::fmt::Debug for TxResultBlockEvent { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "TxResultBlockEvent", + "s_type", + &self.s_type, + "attributes", + &&self.attributes, + ) + } + } + impl From for TxResultBlockEvent { + fn from(event: StringEvent) -> Self { + Self { + s_type: event.r#type, + attributes: event + .attributes + .into_iter() + .map(TxResultBlockAttribute::from) + .collect(), + } + } + } + impl TxResultBlockEvent { + /// get all key/values from the event that have the key 'key' + pub fn get_attributes(&self, key: &str) -> Vec { + self.attributes.iter().filter(|attr| attr.key == key).cloned().collect() + } + /// return the first value of the first attribute that has the key 'key' + pub fn get_first_attribute_value(&self, key: &str) -> Option { + self.get_attributes(key).first().map(|attr| attr.value.clone()) + } + } + /// A single attribute of an event. + pub struct TxResultBlockAttribute { + /// Key of the attribute + pub key: String, + /// Value of the attribute + pub value: String, + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl<'de> _serde::Deserialize<'de> for TxResultBlockAttribute { + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + #[allow(non_camel_case_types)] + #[doc(hidden)] + enum __Field { + __field0, + __field1, + __ignore, + } + #[doc(hidden)] + struct __FieldVisitor; + impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { + type Value = __Field; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "field identifier", + ) + } + fn visit_u64<__E>( + self, + __value: u64, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + 0u64 => _serde::__private::Ok(__Field::__field0), + 1u64 => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_str<__E>( + self, + __value: &str, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + "key" => _serde::__private::Ok(__Field::__field0), + "value" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_bytes<__E>( + self, + __value: &[u8], + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + b"key" => _serde::__private::Ok(__Field::__field0), + b"value" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + } + impl<'de> _serde::Deserialize<'de> for __Field { + #[inline] + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + _serde::Deserializer::deserialize_identifier( + __deserializer, + __FieldVisitor, + ) + } + } + #[doc(hidden)] + struct __Visitor<'de> { + marker: _serde::__private::PhantomData, + lifetime: _serde::__private::PhantomData<&'de ()>, + } + impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { + type Value = TxResultBlockAttribute; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "struct TxResultBlockAttribute", + ) + } + #[inline] + fn visit_seq<__A>( + self, + mut __seq: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::SeqAccess<'de>, + { + let __field0 = match _serde::de::SeqAccess::next_element::< + String, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 0usize, + &"struct TxResultBlockAttribute with 2 elements", + ), + ); + } + }; + let __field1 = match _serde::de::SeqAccess::next_element::< + String, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 1usize, + &"struct TxResultBlockAttribute with 2 elements", + ), + ); + } + }; + _serde::__private::Ok(TxResultBlockAttribute { + key: __field0, + value: __field1, + }) + } + #[inline] + fn visit_map<__A>( + self, + mut __map: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::MapAccess<'de>, + { + let mut __field0: _serde::__private::Option = _serde::__private::None; + let mut __field1: _serde::__private::Option = _serde::__private::None; + while let _serde::__private::Some(__key) + = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { + match __key { + __Field::__field0 => { + if _serde::__private::Option::is_some(&__field0) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field("key"), + ); + } + __field0 = _serde::__private::Some( + _serde::de::MapAccess::next_value::(&mut __map)?, + ); + } + __Field::__field1 => { + if _serde::__private::Option::is_some(&__field1) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field("value"), + ); + } + __field1 = _serde::__private::Some( + _serde::de::MapAccess::next_value::(&mut __map)?, + ); + } + _ => { + let _ = _serde::de::MapAccess::next_value::< + _serde::de::IgnoredAny, + >(&mut __map)?; + } + } + } + let __field0 = match __field0 { + _serde::__private::Some(__field0) => __field0, + _serde::__private::None => { + _serde::__private::de::missing_field("key")? + } + }; + let __field1 = match __field1 { + _serde::__private::Some(__field1) => __field1, + _serde::__private::None => { + _serde::__private::de::missing_field("value")? + } + }; + _serde::__private::Ok(TxResultBlockAttribute { + key: __field0, + value: __field1, + }) + } + } + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &["key", "value"]; + _serde::Deserializer::deserialize_struct( + __deserializer, + "TxResultBlockAttribute", + FIELDS, + __Visitor { + marker: _serde::__private::PhantomData::, + lifetime: _serde::__private::PhantomData, + }, + ) + } + } + }; + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl _serde::Serialize for TxResultBlockAttribute { + fn serialize<__S>( + &self, + __serializer: __S, + ) -> _serde::__private::Result<__S::Ok, __S::Error> + where + __S: _serde::Serializer, + { + let mut __serde_state = _serde::Serializer::serialize_struct( + __serializer, + "TxResultBlockAttribute", + false as usize + 1 + 1, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "key", + &self.key, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "value", + &self.value, + )?; + _serde::ser::SerializeStruct::end(__serde_state) + } + } + }; + #[automatically_derived] + impl ::core::clone::Clone for TxResultBlockAttribute { + #[inline] + fn clone(&self) -> TxResultBlockAttribute { + TxResultBlockAttribute { + key: ::core::clone::Clone::clone(&self.key), + value: ::core::clone::Clone::clone(&self.value), + } + } + } + #[automatically_derived] + impl ::core::fmt::Debug for TxResultBlockAttribute { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "TxResultBlockAttribute", + "key", + &self.key, + "value", + &&self.value, + ) + } + } + impl From for TxResultBlockAttribute { + fn from(a: Attribute) -> Self { + Self { key: a.key, value: a.value } + } + } + /// Parse a string timestamp into a `DateTime` + pub fn parse_timestamp(s: String) -> Result, DaemonError> { + let len = s.len(); + let slice_len = if s.contains('.') { len.saturating_sub(4) } else { len }; + let sliced = &s[0..slice_len]; + match NaiveDateTime::parse_from_str(sliced, FORMAT) { + Err(_e) => { + match NaiveDateTime::parse_from_str(&s, FORMAT_TZ_SUPPLIED) { + Err(_e2) => { + match NaiveDateTime::parse_from_str(sliced, FORMAT_SHORT_Z) { + Err(_e3) => { + match NaiveDateTime::parse_from_str(&s, FORMAT_SHORT_Z2) { + Err(_e4) => { + { + ::std::io::_eprint( + format_args!("DateTime Fail {0} {1:#?}\n", s, _e4), + ); + }; + Err(DaemonError::StdErr(_e4.to_string())) + } + Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), + } + } + Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), + } + } + Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), + } + } + Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), + } + } +} +pub mod keys { + #![allow(unused)] + pub mod private { + use super::public::PublicKey; + use crate::proto::injective::{InjectivePubKey, ETHEREUM_COIN_TYPE}; + use crate::DaemonError; + use base64::Engine; + use bitcoin::{ + bip32::{ExtendedPrivKey, IntoDerivationPath}, + Network, + }; + use cosmrs::tx::SignerPublicKey; + use hkd32::mnemonic::{Phrase, Seed}; + use rand_core::OsRng; + use secp256k1::Secp256k1; + /// The Private key structure that is used to generate signatures and public keys + /// WARNING: No Security Audit has been performed + pub struct PrivateKey { + #[allow(missing_docs)] + pub account: u32, + #[allow(missing_docs)] + pub index: u32, + #[allow(missing_docs)] + pub coin_type: u32, + /// The 24 words used to generate this private key + mnemonic: Option, + #[allow(dead_code)] + /// This is used for testing + root_private_key: ExtendedPrivKey, + /// The private key + private_key: ExtendedPrivKey, + } + #[automatically_derived] + impl ::core::clone::Clone for PrivateKey { + #[inline] + fn clone(&self) -> PrivateKey { + PrivateKey { + account: ::core::clone::Clone::clone(&self.account), + index: ::core::clone::Clone::clone(&self.index), + coin_type: ::core::clone::Clone::clone(&self.coin_type), + mnemonic: ::core::clone::Clone::clone(&self.mnemonic), + root_private_key: ::core::clone::Clone::clone( + &self.root_private_key, + ), + private_key: ::core::clone::Clone::clone(&self.private_key), + } + } + } + impl PrivateKey { + /// Generate a new private key + pub fn new( + secp: &Secp256k1, + coin_type: u32, + ) -> Result { + let phrase = hkd32::mnemonic::Phrase::random( + OsRng, + hkd32::mnemonic::Language::English, + ); + PrivateKey::gen_private_key_phrase(secp, phrase, 0, 0, coin_type, "") + } + /// generate a new private key with a seed phrase + pub fn new_seed( + secp: &Secp256k1, + seed_phrase: &str, + coin_type: u32, + ) -> Result { + let phrase = hkd32::mnemonic::Phrase::random( + OsRng, + hkd32::mnemonic::Language::English, + ); + PrivateKey::gen_private_key_phrase( + secp, + phrase, + 0, + 0, + coin_type, + seed_phrase, + ) + } + /// for private key recovery. This is also used by wallet routines to re-hydrate the structure + pub fn from_words( + secp: &Secp256k1, + words: &str, + account: u32, + index: u32, + coin_type: u32, + ) -> Result { + if words.split(' ').count() != 24 { + return Err(DaemonError::WrongLength); + } + match hkd32::mnemonic::Phrase::new( + words, + hkd32::mnemonic::Language::English, + ) { + Ok(phrase) => { + PrivateKey::gen_private_key_phrase( + secp, + phrase, + account, + index, + coin_type, + "", + ) + } + Err(_) => Err(DaemonError::Phrasing), + } + } + /// for private key recovery with seed phrase + pub fn from_words_seed( + secp: &Secp256k1, + words: &str, + seed_pass: &str, + coin_type: u32, + ) -> Result { + match hkd32::mnemonic::Phrase::new( + words, + hkd32::mnemonic::Language::English, + ) { + Ok(phrase) => { + PrivateKey::gen_private_key_phrase( + secp, + phrase, + 0, + 0, + coin_type, + seed_pass, + ) + } + Err(_) => Err(DaemonError::Phrasing), + } + } + /// generate the public key for this private key + pub fn public_key( + &self, + secp: &Secp256k1, + ) -> PublicKey { + if self.coin_type == ETHEREUM_COIN_TYPE { + { + ::core::panicking::panic_fmt( + format_args!( + "Coin Type {0} not supported without eth feature", + ETHEREUM_COIN_TYPE, + ), + ); + }; + } + let x = self.private_key.private_key.public_key(secp); + PublicKey::from_bitcoin_public_key(&bitcoin::PublicKey::new(x)) + } + pub fn get_injective_public_key( + &self, + secp: &Secp256k1, + ) -> SignerPublicKey { + use base64::engine::general_purpose; + use cosmrs::tx::MessageExt; + use secp256k1::SecretKey; + let secret_key = SecretKey::from_slice(self.raw_key().as_slice()) + .unwrap(); + let public_key = secp256k1::PublicKey::from_secret_key( + secp, + &secret_key, + ); + let vec_pk = public_key.serialize(); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "{0:?}, public key", + general_purpose::STANDARD.encode(vec_pk), + ), + lvl, + &( + "cw_orch_daemon::keys::private", + "cw_orch_daemon::keys::private", + "cw-orch-daemon/src/keys/private.rs", + ), + 124u32, + ::log::__private_api::Option::None, + ); + } + }; + let inj_key = InjectivePubKey { + key: vec_pk.into(), + }; + inj_key.to_any().unwrap().try_into().unwrap() + } + pub fn get_signer_public_key( + &self, + secp: &Secp256k1, + ) -> Option { + if self.coin_type == ETHEREUM_COIN_TYPE { + { + ::core::panicking::panic_fmt( + format_args!( + "Coin Type {0} not supported without eth feature", + ETHEREUM_COIN_TYPE, + ), + ); + }; + } + Some( + cosmrs::crypto::secp256k1::SigningKey::from_slice( + self.raw_key().as_slice(), + ) + .unwrap() + .public_key() + .into(), + ) + } + pub fn raw_key(&self) -> Vec { + self.private_key.private_key.secret_bytes().to_vec() + } + fn gen_private_key_phrase( + secp: &Secp256k1, + phrase: Phrase, + account: u32, + index: u32, + coin_type: u32, + seed_phrase: &str, + ) -> Result { + let seed = phrase.to_seed(seed_phrase); + let root_private_key = ExtendedPrivKey::new_master( + Network::Bitcoin, + seed.as_bytes(), + ) + .unwrap(); + let path = { + let res = ::alloc::fmt::format( + format_args!( + "m/44\'/{0}\'/{1}\'/0/{2}", + coin_type, + account, + index, + ), + ); + res + }; + let derivation_path = path.into_derivation_path()?; + let private_key = root_private_key.derive_priv(secp, &derivation_path)?; + Ok(PrivateKey { + account, + index, + coin_type, + mnemonic: Some(phrase), + root_private_key, + private_key, + }) + } + /// the words used to generate this private key + pub fn words(&self) -> Option<&str> { + self.mnemonic.as_ref().map(|phrase| phrase.phrase()) + } + /// used for testing + /// could potentially be used to recreate the private key instead of words + #[allow(dead_code)] + pub(crate) fn seed(&self, passwd: &str) -> Option { + self.mnemonic.as_ref().map(|phrase| phrase.to_seed(passwd)) + } + } + } + pub mod public { + use crate::DaemonError; + use bitcoin::bech32::{decode, encode, u5, FromBase32, ToBase32, Variant}; + pub use ed25519_dalek::VerifyingKey as Ed25519; + use ring::digest::{Context, SHA256}; + use ripemd::{Digest as _, Ripemd160}; + use serde::{Deserialize, Serialize}; + static BECH32_PUBKEY_DATA_PREFIX_SECP256K1: [u8; 5] = [ + 0xeb, + 0x5a, + 0xe9, + 0x87, + 0x21, + ]; + static BECH32_PUBKEY_DATA_PREFIX_ED25519: [u8; 5] = [ + 0x16, + 0x24, + 0xde, + 0x64, + 0x20, + ]; + /// The public key we used to generate the cosmos/tendermind/terrad addresses + pub struct PublicKey { + /// This is optional as we can generate non-pub keys without + pub raw_pub_key: Option>, + /// The raw bytes used to generate non-pub keys + pub raw_address: Option>, + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl<'de> _serde::Deserialize<'de> for PublicKey { + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + #[allow(non_camel_case_types)] + #[doc(hidden)] + enum __Field { + __field0, + __field1, + __ignore, + } + #[doc(hidden)] + struct __FieldVisitor; + impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { + type Value = __Field; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "field identifier", + ) + } + fn visit_u64<__E>( + self, + __value: u64, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + 0u64 => _serde::__private::Ok(__Field::__field0), + 1u64 => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_str<__E>( + self, + __value: &str, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + "raw_pub_key" => _serde::__private::Ok(__Field::__field0), + "raw_address" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_bytes<__E>( + self, + __value: &[u8], + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + b"raw_pub_key" => _serde::__private::Ok(__Field::__field0), + b"raw_address" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + } + impl<'de> _serde::Deserialize<'de> for __Field { + #[inline] + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + _serde::Deserializer::deserialize_identifier( + __deserializer, + __FieldVisitor, + ) + } + } + #[doc(hidden)] + struct __Visitor<'de> { + marker: _serde::__private::PhantomData, + lifetime: _serde::__private::PhantomData<&'de ()>, + } + impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { + type Value = PublicKey; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "struct PublicKey", + ) + } + #[inline] + fn visit_seq<__A>( + self, + mut __seq: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::SeqAccess<'de>, + { + let __field0 = match _serde::de::SeqAccess::next_element::< + Option>, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 0usize, + &"struct PublicKey with 2 elements", + ), + ); + } + }; + let __field1 = match _serde::de::SeqAccess::next_element::< + Option>, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 1usize, + &"struct PublicKey with 2 elements", + ), + ); + } + }; + _serde::__private::Ok(PublicKey { + raw_pub_key: __field0, + raw_address: __field1, + }) + } + #[inline] + fn visit_map<__A>( + self, + mut __map: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::MapAccess<'de>, + { + let mut __field0: _serde::__private::Option< + Option>, + > = _serde::__private::None; + let mut __field1: _serde::__private::Option< + Option>, + > = _serde::__private::None; + while let _serde::__private::Some(__key) + = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { + match __key { + __Field::__field0 => { + if _serde::__private::Option::is_some(&__field0) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field( + "raw_pub_key", + ), + ); + } + __field0 = _serde::__private::Some( + _serde::de::MapAccess::next_value::< + Option>, + >(&mut __map)?, + ); + } + __Field::__field1 => { + if _serde::__private::Option::is_some(&__field1) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field( + "raw_address", + ), + ); + } + __field1 = _serde::__private::Some( + _serde::de::MapAccess::next_value::< + Option>, + >(&mut __map)?, + ); + } + _ => { + let _ = _serde::de::MapAccess::next_value::< + _serde::de::IgnoredAny, + >(&mut __map)?; + } + } + } + let __field0 = match __field0 { + _serde::__private::Some(__field0) => __field0, + _serde::__private::None => { + _serde::__private::de::missing_field("raw_pub_key")? + } + }; + let __field1 = match __field1 { + _serde::__private::Some(__field1) => __field1, + _serde::__private::None => { + _serde::__private::de::missing_field("raw_address")? + } + }; + _serde::__private::Ok(PublicKey { + raw_pub_key: __field0, + raw_address: __field1, + }) + } + } + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &[ + "raw_pub_key", + "raw_address", + ]; + _serde::Deserializer::deserialize_struct( + __deserializer, + "PublicKey", + FIELDS, + __Visitor { + marker: _serde::__private::PhantomData::, + lifetime: _serde::__private::PhantomData, + }, + ) + } + } + }; + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl _serde::Serialize for PublicKey { + fn serialize<__S>( + &self, + __serializer: __S, + ) -> _serde::__private::Result<__S::Ok, __S::Error> + where + __S: _serde::Serializer, + { + let mut __serde_state = _serde::Serializer::serialize_struct( + __serializer, + "PublicKey", + false as usize + 1 + 1, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "raw_pub_key", + &self.raw_pub_key, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "raw_address", + &self.raw_address, + )?; + _serde::ser::SerializeStruct::end(__serde_state) + } + } + }; + #[automatically_derived] + impl ::core::fmt::Debug for PublicKey { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "PublicKey", + "raw_pub_key", + &self.raw_pub_key, + "raw_address", + &&self.raw_address, + ) + } + } + #[automatically_derived] + impl ::core::clone::Clone for PublicKey { + #[inline] + fn clone(&self) -> PublicKey { + PublicKey { + raw_pub_key: ::core::clone::Clone::clone(&self.raw_pub_key), + raw_address: ::core::clone::Clone::clone(&self.raw_address), + } + } + } + impl PublicKey { + /// Generate a Cosmos/Tendermint/Terrad Public Key + pub fn from_bitcoin_public_key(bpub: &bitcoin::key::PublicKey) -> PublicKey { + let bpub_bytes = bpub.inner.serialize(); + let raw_pub_key = PublicKey::pubkey_from_public_key(&bpub_bytes); + let raw_address = PublicKey::address_from_public_key(&bpub_bytes); + PublicKey { + raw_pub_key: Some(raw_pub_key), + raw_address: Some(raw_address), + } + } + /// Generate from secp256k1 Cosmos/Terrad Public Key + pub fn from_public_key(bpub: &[u8]) -> PublicKey { + let raw_pub_key = PublicKey::pubkey_from_public_key(bpub); + let raw_address = PublicKey::address_from_public_key(bpub); + PublicKey { + raw_pub_key: Some(raw_pub_key), + raw_address: Some(raw_address), + } + } + /// Generate a Cosmos/Tendermint/Terrad Account + pub fn from_account( + acc_address: &str, + prefix: &str, + ) -> Result { + PublicKey::check_prefix_and_length(prefix, acc_address, 44) + .and_then(|vu5| { + let vu8 = Vec::from_base32(vu5.as_slice()) + .map_err(|source| DaemonError::Conversion { + key: acc_address.into(), + source, + })?; + Ok(PublicKey { + raw_pub_key: None, + raw_address: Some(vu8), + }) + }) + } + /// build a public key from a tendermint public key + pub fn from_tendermint_key( + tendermint_public_key: &str, + ) -> Result { + let len = tendermint_public_key.len(); + if len == 83 { + PublicKey::check_prefix_and_length( + "terravalconspub", + tendermint_public_key, + len, + ) + .and_then(|vu5| { + let vu8 = Vec::from_base32(vu5.as_slice()) + .map_err(|source| { + DaemonError::Conversion { + key: tendermint_public_key.into(), + source, + } + })?; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("{0:#?}", hex::encode(&vu8)), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 74u32, + ::log::__private_api::Option::None, + ); + } + }; + if vu8.starts_with(&BECH32_PUBKEY_DATA_PREFIX_SECP256K1) { + let public_key = PublicKey::public_key_from_pubkey(&vu8)?; + let raw = PublicKey::address_from_public_key(&public_key); + Ok(PublicKey { + raw_pub_key: Some(vu8), + raw_address: Some(raw), + }) + } else { + Err(DaemonError::ConversionSECP256k1) + } + }) + } else if len == 82 { + PublicKey::check_prefix_and_length( + "terravalconspub", + tendermint_public_key, + len, + ) + .and_then(|vu5| { + let vu8 = Vec::from_base32(vu5.as_slice()) + .map_err(|source| { + DaemonError::Conversion { + key: tendermint_public_key.into(), + source, + } + })?; + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("ED25519 public keys are not fully supported"), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 100u32, + ::log::__private_api::Option::None, + ); + } + }; + if vu8.starts_with(&BECH32_PUBKEY_DATA_PREFIX_ED25519) { + let raw = PublicKey::address_from_public_ed25519_key(&vu8)?; + Ok(PublicKey { + raw_pub_key: Some(vu8), + raw_address: Some(raw), + }) + } else { + Err(DaemonError::ConversionED25519) + } + }) + } else { + Err(DaemonError::ConversionLength(len)) + } + } + /// build a terravalcons address from a tendermint hex key + /// the tendermint_hex_address should be a hex code of 40 length + pub fn from_tendermint_address( + tendermint_hex_address: &str, + ) -> Result { + let len = tendermint_hex_address.len(); + if len == 40 { + let raw = hex::decode(tendermint_hex_address)?; + Ok(PublicKey { + raw_pub_key: None, + raw_address: Some(raw), + }) + } else { + Err(DaemonError::ConversionLengthED25519Hex(len)) + } + } + /// Generate a Operator address for this public key (used by the validator) + pub fn from_operator_address( + valoper_address: &str, + ) -> Result { + PublicKey::check_prefix_and_length("terravaloper", valoper_address, 51) + .and_then(|vu5| { + let vu8 = Vec::from_base32(vu5.as_slice()) + .map_err(|source| DaemonError::Conversion { + key: valoper_address.into(), + source, + })?; + Ok(PublicKey { + raw_pub_key: None, + raw_address: Some(vu8), + }) + }) + } + /// Generate Public key from raw address + pub fn from_raw_address( + raw_address: &str, + ) -> Result { + let vec1 = hex::decode(raw_address)?; + Ok(PublicKey { + raw_pub_key: None, + raw_address: Some(vec1), + }) + } + fn check_prefix_and_length( + prefix: &str, + data: &str, + length: usize, + ) -> Result, DaemonError> { + let (hrp, decoded_str, _) = decode(data) + .map_err(|source| DaemonError::Conversion { + key: data.into(), + source, + })?; + if hrp == prefix && data.len() == length { + Ok(decoded_str) + } else { + Err( + DaemonError::Bech32DecodeExpanded( + hrp, + data.len(), + prefix.into(), + length, + ), + ) + } + } + /** + Gets a bech32-words pubkey from a compressed bytes Secp256K1 public key. + + @param publicKey raw public key + */ + pub fn pubkey_from_public_key(public_key: &[u8]) -> Vec { + [BECH32_PUBKEY_DATA_PREFIX_SECP256K1.to_vec(), public_key.to_vec()] + .concat() + } + /** + Gets a bech32-words pubkey from a compressed bytes Ed25519 public key. + + @param publicKey raw public key + */ + pub fn pubkey_from_ed25519_public_key(public_key: &[u8]) -> Vec { + [BECH32_PUBKEY_DATA_PREFIX_ED25519.to_vec(), public_key.to_vec()] + .concat() + } + /// Translate from a BECH32 prefixed key to a standard public key + pub fn public_key_from_pubkey( + pub_key: &[u8], + ) -> Result, DaemonError> { + if pub_key.starts_with(&BECH32_PUBKEY_DATA_PREFIX_SECP256K1) { + let len = BECH32_PUBKEY_DATA_PREFIX_SECP256K1.len(); + let len2 = pub_key.len(); + Ok(Vec::from(&pub_key[len..len2])) + } else if pub_key.starts_with(&BECH32_PUBKEY_DATA_PREFIX_ED25519) { + let len = BECH32_PUBKEY_DATA_PREFIX_ED25519.len(); + let len2 = pub_key.len(); + let vec = &pub_key[len..len2]; + let ed25519_pubkey = Ed25519::from_bytes(vec.try_into().unwrap())?; + Ok(ed25519_pubkey.to_bytes().to_vec()) + } else { + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("pub key does not start with BECH32 PREFIX"), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 228u32, + ::log::__private_api::Option::None, + ); + } + }; + Err(DaemonError::Bech32DecodeErr) + } + } + /** + Gets a raw address from a compressed bytes public key. + + @param publicKey raw public key + */ + pub fn address_from_public_key(public_key: &[u8]) -> Vec { + let mut hasher = Ripemd160::new(); + let sha_result = ring::digest::digest(&SHA256, public_key); + hasher.update(&sha_result.as_ref()[0..32]); + let ripe_result = hasher.finalize(); + let address: Vec = ripe_result[0..20].to_vec(); + address + } + /** + Gets a raw address from a ed25519 public key. + + @param publicKey raw public key + */ + pub fn address_from_public_ed25519_key( + public_key: &[u8], + ) -> Result, DaemonError> { + if public_key.len() != (32 + 5) { + Err( + DaemonError::ConversionPrefixED25519( + public_key.len(), + hex::encode(public_key), + ), + ) + } else { + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "address_from_public_ed25519_key public key - {0}", + hex::encode(public_key), + ), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 262u32, + ::log::__private_api::Option::None, + ); + } + }; + let mut sha_result: [u8; 32] = [0; 32]; + let sha_result = ring::digest::digest(&SHA256, &public_key[5..]); + let address: Vec = sha_result.as_ref()[0..20].to_vec(); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "address_from_public_ed25519_key sha result - {0}", + hex::encode(&address), + ), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 283u32, + ::log::__private_api::Option::None, + ); + } + }; + Ok(address) + } + } + /// The main account used in most things + pub fn account(&self, prefix: &str) -> Result { + match &self.raw_address { + Some(raw) => { + let data = encode(prefix, raw.to_base32(), Variant::Bech32); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => Err(DaemonError::Implementation), + } + } + /// The operator address used for validators + pub fn operator_address(&self, prefix: &str) -> Result { + match &self.raw_address { + Some(raw) => { + let data = encode( + &{ + let res = ::alloc::fmt::format( + format_args!("{0}{1}", prefix, "valoper"), + ); + res + }, + raw.to_base32(), + Variant::Bech32, + ); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => Err(DaemonError::Implementation), + } + } + /// application public key - Application keys are associated with a public key terrapub- and an address terra- + pub fn application_public_key( + &self, + prefix: &str, + ) -> Result { + match &self.raw_pub_key { + Some(raw) => { + let data = encode( + &{ + let res = ::alloc::fmt::format( + format_args!("{0}{1}", prefix, "pub"), + ); + res + }, + raw.to_base32(), + Variant::Bech32, + ); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Missing Public Key. Can\'t continue"), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 335u32, + ::log::__private_api::Option::None, + ); + } + }; + Err(DaemonError::Implementation) + } + } + } + /// The operator address used for validators public key. + pub fn operator_address_public_key( + &self, + prefix: &str, + ) -> Result { + match &self.raw_pub_key { + Some(raw) => { + let data = encode( + &{ + let res = ::alloc::fmt::format( + format_args!("{0}{1}", prefix, "valoperpub"), + ); + res + }, + raw.to_base32(), + Variant::Bech32, + ); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => Err(DaemonError::Implementation), + } + } + /// This is a unique key used to sign block hashes. It is associated with a public key terravalconspub. + pub fn tendermint(&self, prefix: &str) -> Result { + match &self.raw_address { + Some(raw) => { + let data = encode( + &{ + let res = ::alloc::fmt::format( + format_args!("{0}{1}", prefix, "valcons"), + ); + res + }, + raw.to_base32(), + Variant::Bech32, + ); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => Err(DaemonError::Implementation), + } + } + /// This is a unique key used to sign block hashes. It is associated with a public key terravalconspub. + pub fn tendermint_pubkey( + &self, + prefix: &str, + ) -> Result { + match &self.raw_pub_key { + Some(raw) => { + let b32 = raw.to_base32(); + let data = encode( + &{ + let res = ::alloc::fmt::format( + format_args!("{0}{1}", prefix, "valconspub"), + ); + res + }, + b32, + Variant::Bech32, + ); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => Err(DaemonError::Implementation), + } + } + } + } + pub mod signature { + use crate::DaemonError; + use base64::engine::{general_purpose::STANDARD, Engine}; + use ring::digest::SHA256; + use secp256k1::{Message, Secp256k1}; + pub struct Signature {} + impl Signature { + pub fn verify( + secp: &Secp256k1, + pub_key: &str, + signature: &str, + blob: &str, + ) -> Result<(), DaemonError> { + let public = STANDARD.decode(pub_key)?; + let sig = STANDARD.decode(signature)?; + let pk = secp256k1::PublicKey::from_slice(public.as_slice())?; + let sha_result = ring::digest::digest(&SHA256, blob.as_bytes()); + let message: Message = Message::from_slice(&sha_result.as_ref()[0..32])?; + let secp_sig = secp256k1::ecdsa::Signature::from_compact( + sig.as_slice(), + )?; + secp.verify_ecdsa(&message, &secp_sig, &pk)?; + Ok(()) + } + } + } +} +pub mod live_mock { + //! Live mock is a mock that uses a live chain to query for data. + //! It can be used to do chain-backed unit-testing. It can't be used for state-changing operations. + use crate::queriers::Bank; + use crate::queriers::CosmWasm; + use crate::queriers::DaemonQuerier; + use crate::queriers::Staking; + use cosmwasm_std::Addr; + use cosmwasm_std::AllBalanceResponse; + use cosmwasm_std::BalanceResponse; + use cosmwasm_std::Delegation; + use cosmwasm_std::{AllDelegationsResponse, BondedDenomResponse}; + use cosmwasm_std::BankQuery; + use cosmwasm_std::Binary; + use cosmwasm_std::Empty; + use cosmwasm_std::StakingQuery; + use ibc_chain_registry::chain::ChainData; + use tokio::runtime::Runtime; + use tonic::transport::Channel; + use std::marker::PhantomData; + use std::str::FromStr; + use cosmwasm_std::testing::{MockApi, MockStorage}; + use cosmwasm_std::{ + from_slice, to_binary, Coin, ContractResult, OwnedDeps, Querier, QuerierResult, + QueryRequest, SystemError, SystemResult, Uint128, WasmQuery, + }; + use crate::channel::GrpcChannel; + fn to_cosmwasm_coin(c: cosmrs::proto::cosmos::base::v1beta1::Coin) -> Coin { + Coin { + amount: Uint128::from_str(&c.amount).unwrap(), + denom: c.denom, + } + } + const QUERIER_ERROR: &str = "Only Bank balances and Wasm (raw + smart) and Some staking queries are covered for now"; + /// mock_dependencies is a drop-in replacement for cosmwasm_std::testing::mock_dependencies + /// this uses our CustomQuerier. + pub fn mock_dependencies( + chain_info: ChainData, + ) -> OwnedDeps { + let custom_querier: WasmMockQuerier = WasmMockQuerier::new(chain_info); + OwnedDeps { + storage: MockStorage::default(), + api: MockApi::default(), + querier: custom_querier, + custom_query_type: PhantomData, + } + } + /// Querier struct that fetches queries on-chain directly + pub struct WasmMockQuerier { + channel: Channel, + runtime: Runtime, + } + impl Querier for WasmMockQuerier { + fn raw_query(&self, bin_request: &[u8]) -> QuerierResult { + let request: QueryRequest = match from_slice(bin_request) { + Ok(v) => v, + Err(e) => { + return SystemResult::Err(SystemError::InvalidRequest { + error: { + let res = ::alloc::fmt::format( + format_args!("Parsing query request: {0}", e), + ); + res + }, + request: bin_request.into(), + }); + } + }; + self.handle_query(&request) + } + } + impl WasmMockQuerier { + /// Function used to handle a query and customize the query behavior + /// This implements some queries by querying an actual node for the responses + pub fn handle_query(&self, request: &QueryRequest) -> QuerierResult { + match &request { + QueryRequest::Wasm(x) => { + let querier = CosmWasm::new(self.channel.clone()); + match x { + WasmQuery::Smart { contract_addr, msg } => { + let query_result: Result = self + .runtime + .block_on( + querier + .contract_state(contract_addr.to_string(), msg.to_vec()), + ) + .map(|query_result| query_result.into()); + SystemResult::Ok(ContractResult::from(query_result)) + } + WasmQuery::Raw { contract_addr, key } => { + let query_result = self + .runtime + .block_on( + querier + .contract_raw_state(contract_addr.to_string(), key.to_vec()), + ) + .map(|query_result| query_result.data.into()); + SystemResult::Ok(ContractResult::from(query_result)) + } + _ => { + SystemResult::Err(SystemError::InvalidRequest { + error: QUERIER_ERROR.to_string(), + request: to_binary(&request).unwrap(), + }) + } + } + } + QueryRequest::Bank(x) => { + let querier = Bank::new(self.channel.clone()); + match x { + BankQuery::Balance { address, denom } => { + let query_result = self + .runtime + .block_on(querier.balance(address, Some(denom.clone()))) + .map(|result| { + to_binary( + &BalanceResponse { + amount: Coin { + amount: Uint128::from_str(&result[0].amount).unwrap(), + denom: result[0].denom.clone(), + }, + }, + ) + .unwrap() + }); + SystemResult::Ok(ContractResult::from(query_result)) + } + BankQuery::AllBalances { address } => { + let query_result = self + .runtime + .block_on(querier.balance(address, None)) + .map(|result| AllBalanceResponse { + amount: result + .into_iter() + .map(|c| Coin { + amount: Uint128::from_str(&c.amount).unwrap(), + denom: c.denom, + }) + .collect(), + }) + .map(|query_result| to_binary(&query_result)) + .unwrap(); + SystemResult::Ok(ContractResult::from(query_result)) + } + _ => { + SystemResult::Err(SystemError::InvalidRequest { + error: QUERIER_ERROR.to_string(), + request: to_binary(&request).unwrap(), + }) + } + } + } + QueryRequest::Staking(x) => { + let querier = Staking::new(self.channel.clone()); + match x { + StakingQuery::BondedDenom {} => { + let query_result = self + .runtime + .block_on(querier.params()) + .map(|result| BondedDenomResponse { + denom: result.params.unwrap().bond_denom, + }) + .map(|query_result| to_binary(&query_result)) + .unwrap(); + SystemResult::Ok(ContractResult::from(query_result)) + } + StakingQuery::AllDelegations { delegator } => { + let query_result = self + .runtime + .block_on(querier.delegator_delegations(delegator, None)) + .map(|result| AllDelegationsResponse { + delegations: result + .delegation_responses + .into_iter() + .filter_map(|delegation| { + delegation + .delegation + .map(|d| Delegation { + delegator: Addr::unchecked(d.delegator_address), + validator: d.validator_address, + amount: to_cosmwasm_coin(delegation.balance.unwrap()), + }) + }) + .collect(), + }) + .map(|query_result| to_binary(&query_result)) + .unwrap(); + SystemResult::Ok(ContractResult::from(query_result)) + } + _ => ::core::panicking::panic("not yet implemented"), + } + } + _ => { + SystemResult::Err(SystemError::InvalidRequest { + error: QUERIER_ERROR.to_string(), + request: to_binary(&request).unwrap(), + }) + } + } + } + } + impl WasmMockQuerier { + /// Creates a querier from chain information + pub fn new(chain: ChainData) -> Self { + let rt = Runtime::new().unwrap(); + let channel = rt + .block_on(GrpcChannel::connect(&chain.apis.grpc, &chain.chain_id)) + .unwrap(); + WasmMockQuerier { + channel, + runtime: rt, + } + } + } +} +pub mod queriers { + //! # DaemonQuerier + //! + //! DaemonAsync queriers are gRPC query clients for the CosmosSDK modules. They can be used to query the different modules (Bank, Ibc, Authz, ...). + //! + //! ## Usage + //! + //! You will need to acquire a [gRPC channel](Channel) to a running CosmosSDK node to be able to use the queriers. + //! Here is an example of how to acquire one using the DaemonAsync builder. + //! + //! ```no_run + //! // require the querier you want to use, in this case Node + //! use cw_orch_daemon::{queriers::Node, DaemonAsync, networks, queriers::DaemonQuerier}; + //! # tokio_test::block_on(async { + //! // call the builder and configure it as you need + //! let daemon = DaemonAsync::builder() + //! .chain(networks::LOCAL_JUNO) + //! .build() + //! .await.unwrap(); + //! // now you can use the Node querier: + //! let node = Node::new(daemon.channel()); + //! let node_info = node.info(); + //! # }) + //! ``` + mod bank { + use crate::{cosmos_modules, error::DaemonError}; + use cosmrs::proto::cosmos::{ + base::{query::v1beta1::PageRequest, v1beta1::Coin}, + bank::v1beta1::QueryBalanceResponse, + }; + use tonic::transport::Channel; + use super::DaemonQuerier; + /// Queries for Cosmos Bank Module + pub struct Bank { + channel: Channel, + } + impl DaemonQuerier for Bank { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Bank { + /// Query the bank balance of a given address + /// If denom is None, returns all balances + pub async fn balance( + &self, + address: impl Into, + denom: Option, + ) -> Result, DaemonError> { + use cosmos_modules::bank::query_client::QueryClient; + match denom { + Some(denom) => { + let resp: QueryBalanceResponse = (); + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = cosmos_modules::bank::QueryBalanceRequest { + address: address.into(), + denom, + }; + let resp = client.balance(request).await?.into_inner(); + let coin = resp.balance.unwrap(); + Ok( + <[_]>::into_vec( + #[rustc_box] + ::alloc::boxed::Box::new([coin]), + ), + ) + } + None => { + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = cosmos_modules::bank::QueryAllBalancesRequest { + address: address.into(), + ..Default::default() + }; + let resp: QueryBalanceResponse = (); + let denoms_metadata: cosmos_modules::bank::QueryDenomsMetadataResponse = (); + let resp = client.all_balances(request).await?.into_inner(); + let coins = resp.balances; + Ok(coins.into_iter().collect()) + } + } + } + /// Query spendable balance for address + pub async fn spendable_balances( + &self, + address: impl Into, + ) -> Result, DaemonError> { + let spendable_balances: cosmos_modules::bank::QuerySpendableBalancesResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QuerySpendableBalancesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QuerySpendableBalancesRequest { + address: address.into(), + pagination: None, + }; + let response = client + .spendable_balances(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 89u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(spendable_balances.balances) + } + /// Query total supply in the bank + pub async fn total_supply(&self) -> Result, DaemonError> { + let total_supply: cosmos_modules::bank::QueryTotalSupplyResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QueryTotalSupplyRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryTotalSupplyRequest { + pagination: None, + }; + let response = client + .total_supply(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 103u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(total_supply.supply) + } + /// Query total supply in the bank for a denom + pub async fn supply_of( + &self, + denom: impl Into, + ) -> Result { + let supply_of: cosmos_modules::bank::QuerySupplyOfResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QuerySupplyOfRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QuerySupplyOfRequest { + denom: denom.into(), + }; + let response = client.supply_of(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 114u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(supply_of.amount.unwrap()) + } + /// Query params + pub async fn params( + &self, + ) -> Result { + let params: cosmos_modules::bank::QueryParamsResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QueryParamsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryParamsRequest {}; + let response = client.params(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 128u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(params.params.unwrap()) + } + /// Query denom metadata + pub async fn denom_metadata( + &self, + denom: impl Into, + ) -> Result { + let denom_metadata: cosmos_modules::bank::QueryDenomMetadataResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QueryDenomMetadataRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDenomMetadataRequest { + denom: denom.into(), + }; + let response = client + .denom_metadata(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 137u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(denom_metadata.metadata.unwrap()) + } + /// Query denoms metadata with pagination + /// + /// see [PageRequest] for pagination + pub async fn denoms_metadata( + &self, + pagination: Option, + ) -> Result, DaemonError> { + let denoms_metadata: cosmos_modules::bank::QueryDenomsMetadataResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QueryDenomsMetadataRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDenomsMetadataRequest { + pagination: pagination, + }; + let response = client + .denoms_metadata(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 155u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(denoms_metadata.metadatas) + } + } + } + mod cosmwasm { + use crate::{cosmos_modules, error::DaemonError}; + use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; + use tonic::transport::Channel; + use super::DaemonQuerier; + /// Querier for the CosmWasm SDK module + pub struct CosmWasm { + channel: Channel, + } + impl DaemonQuerier for CosmWasm { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl CosmWasm { + /// Query code_id by hash + pub async fn code_id_hash( + &self, + code_id: u64, + ) -> Result { + use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryCodeRequest { code_id }; + let resp = client.code(request).await?.into_inner(); + let contract_hash = resp.code_info.unwrap().data_hash; + let on_chain_hash = base16::encode_lower(&contract_hash); + Ok(on_chain_hash) + } + /// Query contract info + pub async fn contract_info( + &self, + address: impl Into, + ) -> Result { + use cosmos_modules::cosmwasm::{ + query_client::*, QueryContractInfoRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryContractInfoRequest { + address: address.into(), + }; + let resp = client.contract_info(request).await?.into_inner(); + let contract_info = resp.contract_info.unwrap(); + Ok(contract_info) + } + /// Query contract history + pub async fn contract_history( + &self, + address: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::cosmwasm::QueryContractHistoryResponse, + DaemonError, + > { + use cosmos_modules::cosmwasm::{ + query_client::*, QueryContractHistoryRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryContractHistoryRequest { + address: address.into(), + pagination, + }; + Ok(client.contract_history(request).await?.into_inner()) + } + /// Query contract state + pub async fn contract_state( + &self, + address: impl Into, + query_data: Vec, + ) -> Result, DaemonError> { + use cosmos_modules::cosmwasm::{ + query_client::*, QuerySmartContractStateRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QuerySmartContractStateRequest { + address: address.into(), + query_data, + }; + Ok(client.smart_contract_state(request).await?.into_inner().data) + } + /// Query all contract state + pub async fn all_contract_state( + &self, + address: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::cosmwasm::QueryAllContractStateResponse, + DaemonError, + > { + use cosmos_modules::cosmwasm::{ + query_client::*, QueryAllContractStateRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryAllContractStateRequest { + address: address.into(), + pagination, + }; + Ok(client.all_contract_state(request).await?.into_inner()) + } + /// Query code + pub async fn code( + &self, + code_id: u64, + ) -> Result { + use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryCodeRequest { code_id }; + Ok(client.code(request).await?.into_inner().code_info.unwrap()) + } + /// Query code bytes + pub async fn code_data(&self, code_id: u64) -> Result, DaemonError> { + use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryCodeRequest { code_id }; + Ok(client.code(request).await?.into_inner().data) + } + /// Query codes + pub async fn codes( + &self, + pagination: Option, + ) -> Result, DaemonError> { + use cosmos_modules::cosmwasm::{query_client::*, QueryCodesRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryCodesRequest { pagination }; + Ok(client.codes(request).await?.into_inner().code_infos) + } + /// Query pinned codes + pub async fn pinned_codes( + &self, + ) -> Result< + cosmos_modules::cosmwasm::QueryPinnedCodesResponse, + DaemonError, + > { + use cosmos_modules::cosmwasm::{query_client::*, QueryPinnedCodesRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryPinnedCodesRequest { + pagination: None, + }; + Ok(client.pinned_codes(request).await?.into_inner()) + } + /// Query contracts by code + pub async fn contract_by_codes( + &self, + code_id: u64, + ) -> Result< + cosmos_modules::cosmwasm::QueryContractsByCodeResponse, + DaemonError, + > { + use cosmos_modules::cosmwasm::{ + query_client::*, QueryContractsByCodeRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryContractsByCodeRequest { + code_id, + pagination: None, + }; + Ok(client.contracts_by_code(request).await?.into_inner()) + } + /// Query raw contract state + pub async fn contract_raw_state( + &self, + address: impl Into, + query_data: Vec, + ) -> Result< + cosmos_modules::cosmwasm::QueryRawContractStateResponse, + DaemonError, + > { + use cosmos_modules::cosmwasm::{ + query_client::*, QueryRawContractStateRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryRawContractStateRequest { + address: address.into(), + query_data, + }; + Ok(client.raw_contract_state(request).await?.into_inner()) + } + /// Query params + pub async fn params( + &self, + ) -> Result { + use cosmos_modules::cosmwasm::{query_client::*, QueryParamsRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + Ok(client.params(QueryParamsRequest {}).await?.into_inner()) + } + } + } + mod feegrant { + use crate::{cosmos_modules, error::DaemonError}; + use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; + use tonic::transport::Channel; + use super::DaemonQuerier; + /// Querier for the Cosmos Gov module + pub struct Feegrant { + channel: Channel, + } + impl DaemonQuerier for Feegrant { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Feegrant { + /// Query all allowances granted to the grantee address by a granter address + pub async fn allowance( + &self, + granter: impl Into, + grantee: impl Into, + ) -> Result { + let allowance: cosmos_modules::feegrant::QueryAllowanceResponse = { + use crate::cosmos_modules::feegrant::{ + query_client::QueryClient, QueryAllowanceRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryAllowanceRequest { + granter: granter.into(), + grantee: grantee.into(), + }; + let response = client.allowance(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::feegrant", + "cw_orch_daemon::queriers::feegrant", + "cw-orch-daemon/src/queriers/feegrant.rs", + ), + 25u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(allowance.allowance.unwrap()) + } + /// Query allowances for grantee address with a given pagination + /// + /// see [PageRequest] for pagination + pub async fn allowances( + &self, + grantee: impl Into, + pagination: Option, + ) -> Result, DaemonError> { + let allowances: cosmos_modules::feegrant::QueryAllowancesResponse = { + use crate::cosmos_modules::feegrant::{ + query_client::QueryClient, QueryAllowancesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryAllowancesRequest { + grantee: grantee.into(), + pagination: pagination, + }; + let response = client + .allowances(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::feegrant", + "cw_orch_daemon::queriers::feegrant", + "cw-orch-daemon/src/queriers/feegrant.rs", + ), + 45u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(allowances.allowances) + } + } + } + mod gov { + use crate::{cosmos_modules, error::DaemonError}; + use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; + use tonic::transport::Channel; + use super::DaemonQuerier; + /// Querier for the Cosmos Gov module + pub struct Gov { + channel: Channel, + } + impl DaemonQuerier for Gov { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Gov { + /// Query proposal details by proposal id + pub async fn proposal( + &self, + proposal_id: u64, + ) -> Result { + let proposal: cosmos_modules::gov::QueryProposalResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryProposalRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryProposalRequest { + proposal_id: proposal_id, + }; + let response = client.proposal(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 24u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(proposal.proposal.unwrap()) + } + /// Query proposals based on given status + /// + /// see [PageRequest] for pagination + pub async fn proposals( + &self, + proposal_status: GovProposalStatus, + voter: impl Into, + depositor: impl Into, + pagination: Option, + ) -> Result { + let proposals: cosmos_modules::gov::QueryProposalsResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryProposalsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryProposalsRequest { + proposal_status: proposal_status as i32, + voter: voter.into(), + depositor: depositor.into(), + pagination: pagination, + }; + let response = client.proposals(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 45u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(proposals) + } + /// Query voted information based on proposal_id for voter address + pub async fn vote( + &self, + proposal_id: u64, + voter: impl Into, + ) -> Result { + let vote: cosmos_modules::gov::QueryVoteResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryVoteRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryVoteRequest { + proposal_id: proposal_id, + voter: voter.into(), + }; + let response = client.vote(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 65u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(vote.vote.unwrap()) + } + /// Query votes of a given proposal + /// + /// see [PageRequest] for pagination + pub async fn votes( + &self, + proposal_id: impl Into, + pagination: Option, + ) -> Result { + let votes: cosmos_modules::gov::QueryVotesResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryVotesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryVotesRequest { + proposal_id: proposal_id.into(), + pagination: pagination, + }; + let response = client.votes(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 85u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(votes) + } + /// Query all parameters of the gov module + pub async fn params( + &self, + params_type: impl Into, + ) -> Result { + let params: cosmos_modules::gov::QueryParamsResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryParamsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryParamsRequest { + params_type: params_type.into(), + }; + let response = client.params(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 102u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(params) + } + /// Query deposit information using proposal_id and depositor address + pub async fn deposit( + &self, + proposal_id: u64, + depositor: impl Into, + ) -> Result { + let deposit: cosmos_modules::gov::QueryDepositResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryDepositRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDepositRequest { + proposal_id: proposal_id, + depositor: depositor.into(), + }; + let response = client.deposit(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 119u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(deposit.deposit.unwrap()) + } + /// Query deposits of a proposal + /// + /// see [PageRequest] for pagination + pub async fn deposits( + &self, + proposal_id: u64, + pagination: Option, + ) -> Result { + let deposits: cosmos_modules::gov::QueryDepositsResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryDepositsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDepositsRequest { + proposal_id: proposal_id, + pagination: pagination, + }; + let response = client.deposits(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 139u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(deposits) + } + /// TallyResult queries the tally of a proposal vote. + pub async fn tally_result( + &mut self, + proposal_id: u64, + ) -> Result { + let tally_result: cosmos_modules::gov::QueryTallyResultResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryTallyResultRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryTallyResultRequest { + proposal_id: proposal_id, + }; + let response = client + .tally_result(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 156u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(tally_result.tally.unwrap()) + } + } + /// Proposal status + #[allow(missing_docs)] + pub enum GovProposalStatus { + Unspecified = 0, + DepositPeriod = 1, + VotingPeriod = 2, + Passed = 3, + Rejected = 4, + Failed = 5, + } + } + mod ibc { + use super::DaemonQuerier; + use crate::{cosmos_modules, error::DaemonError}; + use cosmos_modules::ibc_channel; + use cosmrs::proto::ibc::{ + applications::transfer::v1::{DenomTrace, QueryDenomTraceResponse}, + core::{ + channel::v1::QueryPacketCommitmentResponse, + client::v1::{IdentifiedClientState, QueryClientStatesResponse}, + connection::v1::{IdentifiedConnection, State}, + }, + lightclients::tendermint::v1::ClientState, + }; + use prost::Message; + use tonic::transport::Channel; + /// Querier for the Cosmos IBC module + pub struct Ibc { + channel: Channel, + } + impl DaemonQuerier for Ibc { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Ibc { + /// Get the trace of a specific denom + pub async fn denom_trace( + &self, + hash: String, + ) -> Result { + let denom_trace: QueryDenomTraceResponse = { + use crate::cosmos_modules::ibc_transfer::{ + query_client::QueryClient, QueryDenomTraceRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDenomTraceRequest { + hash: hash, + }; + let response = client + .denom_trace(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 32u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(denom_trace.denom_trace.unwrap()) + } + /// Get all the IBC clients for this daemon + pub async fn clients( + &self, + ) -> Result, DaemonError> { + let ibc_clients: QueryClientStatesResponse = { + use crate::cosmos_modules::ibc_client::{ + query_client::QueryClient, QueryClientStatesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryClientStatesRequest { + pagination: None, + }; + let response = client + .client_states(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 45u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_clients.client_states) + } + /// Get the state of a specific IBC client + pub async fn client_state( + &self, + client_id: impl ToString, + ) -> Result< + cosmos_modules::ibc_client::QueryClientStateResponse, + DaemonError, + > { + let response: cosmos_modules::ibc_client::QueryClientStateResponse = { + use crate::cosmos_modules::ibc_client::{ + query_client::QueryClient, QueryClientStateRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryClientStateRequest { + client_id: client_id.to_string(), + }; + let response = client + .client_state(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 60u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(response) + } + /// Get the consensus state of a specific IBC client + 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 = { + use crate::cosmos_modules::ibc_client::{ + query_client::QueryClient, QueryConsensusStatesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryConsensusStatesRequest { + client_id: client_id, + pagination: None, + }; + let response = client + .consensus_states(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 77u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(response) + } + /// Get the consensus status of a specific IBC client + pub async fn client_status( + &self, + client_id: impl ToString, + ) -> Result< + cosmos_modules::ibc_client::QueryClientStatusResponse, + DaemonError, + > { + let response: cosmos_modules::ibc_client::QueryClientStatusResponse = { + use crate::cosmos_modules::ibc_client::{ + query_client::QueryClient, QueryClientStatusRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryClientStatusRequest { + client_id: client_id.to_string(), + }; + let response = client + .client_status(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 95u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(response) + } + /// Get the ibc client parameters + pub async fn client_params( + &self, + ) -> Result< + cosmos_modules::ibc_client::QueryClientParamsResponse, + DaemonError, + > { + let response: cosmos_modules::ibc_client::QueryClientParamsResponse = { + use crate::cosmos_modules::ibc_client::{ + query_client::QueryClient, QueryClientParamsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryClientParamsRequest {}; + let response = client + .client_params(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 111u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(response) + } + /// Query the IBC connections for a specific chain + pub async fn connections( + &self, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_connection::QueryConnectionsResponse; + let ibc_connections: QueryConnectionsResponse = { + use crate::cosmos_modules::ibc_connection::{ + query_client::QueryClient, QueryConnectionsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryConnectionsRequest { + pagination: None, + }; + let response = client + .connections(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 121u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_connections.connections) + } + /// Search for open connections with a specific chain. + pub async fn open_connections( + &self, + client_chain_id: impl ToString, + ) -> Result, 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) + } + /// Get all the connections for this client + pub async fn client_connections( + &self, + client_id: impl Into, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_connection::QueryClientConnectionsResponse; + let client_id = client_id.into(); + let ibc_client_connections: QueryClientConnectionsResponse = { + use crate::cosmos_modules::ibc_connection::{ + query_client::QueryClient, QueryClientConnectionsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryClientConnectionsRequest { + client_id: client_id.clone(), + }; + let response = client + .client_connections(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 163u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_client_connections.connection_paths) + } + /// Get the (tendermint) client state for a specific connection + pub async fn connection_client( + &self, + connection_id: impl Into, + ) -> Result { + use cosmos_modules::ibc_connection::QueryConnectionClientStateResponse; + let connection_id = connection_id.into(); + let ibc_connection_client: QueryConnectionClientStateResponse = { + use crate::cosmos_modules::ibc_connection::{ + query_client::QueryClient, QueryConnectionClientStateRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryConnectionClientStateRequest { + connection_id: connection_id.clone(), + }; + let response = client + .connection_client_state(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 183u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + let client_state = ibc_connection_client + .identified_client_state + .ok_or( + DaemonError::ibc_err({ + let res = ::alloc::fmt::format( + format_args!( + "error identifying client for connection {0}", + connection_id, + ), + ); + res + }), + )?; + let client_state = ClientState::decode( + client_state.client_state.unwrap().value.as_slice(), + ) + .map_err(|e| DaemonError::ibc_err({ + let res = ::alloc::fmt::format( + format_args!("error decoding client state: {0}", e), + ); + res + }))?; + Ok(client_state) + } + /// Get the channel for a specific port and channel id + pub async fn channel( + &self, + port_id: impl Into, + channel_id: impl Into, + ) -> Result { + use cosmos_modules::ibc_channel::QueryChannelResponse; + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_channel: QueryChannelResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryChannelRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryChannelRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }; + let response = client.channel(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 218u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + ibc_channel + .channel + .ok_or( + DaemonError::ibc_err({ + let res = ::alloc::fmt::format( + format_args!( + "error fetching channel {0} on port {1}", + channel_id, + port_id, + ), + ); + res + }), + ) + } + /// Get all the channels for a specific connection + pub async fn connection_channels( + &self, + connection_id: impl Into, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_channel::QueryConnectionChannelsResponse; + let connection_id = connection_id.into(); + let ibc_connection_channels: QueryConnectionChannelsResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryConnectionChannelsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryConnectionChannelsRequest { + connection: connection_id.clone(), + pagination: None, + }; + let response = client + .connection_channels(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 242u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_connection_channels.channels) + } + /// Get the client state for a specific channel and port + pub async fn channel_client_state( + &self, + port_id: impl Into, + channel_id: impl Into, + ) -> Result { + use cosmos_modules::ibc_channel::QueryChannelClientStateResponse; + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_channel_client_state: QueryChannelClientStateResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryChannelClientStateRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryChannelClientStateRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }; + let response = client + .channel_client_state(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 265u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + ibc_channel_client_state + .identified_client_state + .ok_or( + DaemonError::ibc_err({ + let res = ::alloc::fmt::format( + format_args!( + "error identifying client for channel {0} on port {1}", + channel_id, + port_id, + ), + ); + res + }), + ) + } + /// Get all the packet commitments for a specific channel and port + pub async fn packet_commitments( + &self, + port_id: impl Into, + channel_id: impl Into, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_channel::QueryPacketCommitmentsResponse; + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_commitments: QueryPacketCommitmentsResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryPacketCommitmentsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPacketCommitmentsRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + pagination: None, + }; + let response = client + .packet_commitments(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 297u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_commitments.commitments) + } + /// Get the packet commitment for a specific channel, port and sequence + pub async fn packet_commitment( + &self, + port_id: impl Into, + channel_id: impl Into, + sequence: u64, + ) -> Result { + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_commitment: QueryPacketCommitmentResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryPacketCommitmentRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPacketCommitmentRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + sequence: sequence, + }; + let response = client + .packet_commitment(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 320u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_commitment) + } + /// Returns if the packet is received on the connected chain. + pub async fn packet_receipt( + &self, + port_id: impl Into, + channel_id: impl Into, + sequence: u64, + ) -> Result { + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_receipt: ibc_channel::QueryPacketReceiptResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryPacketReceiptRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPacketReceiptRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + sequence: sequence, + }; + let response = client + .packet_receipt(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 345u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_receipt.received) + } + /// Get all the packet acknowledgements for a specific channel, port and commitment sequences + pub async fn packet_acknowledgements( + &self, + port_id: impl Into, + channel_id: impl Into, + packet_commitment_sequences: Vec, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_channel::QueryPacketAcknowledgementsResponse; + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_acknowledgements: QueryPacketAcknowledgementsResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryPacketAcknowledgementsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPacketAcknowledgementsRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + packet_commitment_sequences: packet_commitment_sequences, + pagination: None, + }; + let response = client + .packet_acknowledgements(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 372u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_acknowledgements.acknowledgements) + } + /// Get the packet acknowledgement for a specific channel, port and sequence + pub async fn packet_acknowledgement( + &self, + port_id: impl Into, + channel_id: impl Into, + sequence: u64, + ) -> Result, DaemonError> { + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_acknowledgement: ibc_channel::QueryPacketAcknowledgementResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryPacketAcknowledgementRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPacketAcknowledgementRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + sequence: sequence, + }; + let response = client + .packet_acknowledgement(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 396u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_acknowledgement.acknowledgement) + } + /// No acknowledgement exists on receiving chain for the given packet commitment sequence on sending chain. + /// Returns the packet sequences that have not yet been received. + pub async fn unreceived_packets( + &self, + port_id: impl Into, + channel_id: impl Into, + packet_commitment_sequences: Vec, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_channel::QueryUnreceivedPacketsResponse; + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_unreceived: QueryUnreceivedPacketsResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryUnreceivedPacketsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryUnreceivedPacketsRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + packet_commitment_sequences: packet_commitment_sequences, + }; + let response = client + .unreceived_packets(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 422u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_unreceived.sequences) + } + /// Returns the acknowledgement sequences that have not yet been received. + /// Given a list of acknowledgement sequences from counterparty, determine if an ack on the counterparty chain has been received on the executing chain. + /// Returns the list of acknowledgement sequences that have not yet been received. + pub async fn unreceived_acks( + &self, + port_id: impl Into, + channel_id: impl Into, + packet_ack_sequences: Vec, + ) -> Result, DaemonError> { + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_unreceived: ibc_channel::QueryUnreceivedAcksResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryUnreceivedAcksRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryUnreceivedAcksRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + packet_ack_sequences: packet_ack_sequences, + }; + let response = client + .unreceived_acks(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 447u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_unreceived.sequences) + } + /// Returns the acknowledgement sequences that have not yet been received. + /// Given a list of acknowledgement sequences from counterparty, determine if an ack on the counterparty chain has been received on the executing chain. + /// Returns the list of acknowledgement sequences that have not yet been received. + pub async fn next_sequence_receive( + &self, + port_id: impl Into, + channel_id: impl Into, + ) -> Result { + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let next_receive: ibc_channel::QueryNextSequenceReceiveResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryNextSequenceReceiveRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryNextSequenceReceiveRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }; + let response = client + .next_sequence_receive(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 471u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(next_receive.next_sequence_receive) + } + } + } + mod node { + use std::{cmp::min, time::Duration}; + use crate::{cosmos_modules, error::DaemonError, tx_resp::CosmTxResponse}; + use cosmrs::{ + proto::cosmos::{ + base::query::v1beta1::PageRequest, tx::v1beta1::SimulateResponse, + }, + tendermint::{Block, Time}, + }; + use tonic::transport::Channel; + use super::DaemonQuerier; + const MAX_TX_QUERY_RETRIES: usize = 50; + /// Querier for the Tendermint node. + /// Supports queries for block and tx information + pub struct Node { + channel: Channel, + } + impl DaemonQuerier for Node { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Node { + /// Returns node info + pub async fn info( + &self, + ) -> Result { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_node_info(cosmos_modules::tendermint::GetNodeInfoRequest { + }) + .await? + .into_inner(); + Ok(resp) + } + /// Queries node syncing + pub async fn syncing(&self) -> Result { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_syncing(cosmos_modules::tendermint::GetSyncingRequest { + }) + .await? + .into_inner(); + Ok(resp.syncing) + } + /// Returns latests block information + pub async fn latest_block(&self) -> Result { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_latest_block(cosmos_modules::tendermint::GetLatestBlockRequest { + }) + .await? + .into_inner(); + Ok(Block::try_from(resp.block.unwrap())?) + } + /// Returns block information fetched by height + pub async fn block_by_height( + &self, + height: u64, + ) -> Result { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_block_by_height(cosmos_modules::tendermint::GetBlockByHeightRequest { + height: height as i64, + }) + .await? + .into_inner(); + Ok(Block::try_from(resp.block.unwrap())?) + } + /// Return the average block time for the last 50 blocks or since inception + /// This is used to estimate the time when a tx will be included in a block + pub async fn average_block_speed( + &self, + multiplier: Option, + ) -> Result { + let mut latest_block = self.latest_block().await?; + let latest_block_time = latest_block.header.time; + let mut latest_block_height = latest_block.header.height.value(); + while latest_block_height <= 1 { + tokio::time::sleep(Duration::from_secs(1)).await; + latest_block = self.latest_block().await?; + latest_block_height = latest_block.header.height.value(); + } + let avg_period = min(latest_block_height - 1, 50); + let block_avg_period_ago = self + .block_by_height(latest_block_height - avg_period) + .await?; + let block_avg_period_ago_time = block_avg_period_ago.header.time; + let average_block_time = latest_block_time + .duration_since(block_avg_period_ago_time)?; + let average_block_time = average_block_time.as_secs() / avg_period; + let average_block_time = match multiplier { + Some(multiplier) => (average_block_time as f32 * multiplier) as u64, + None => average_block_time, + }; + Ok(std::cmp::max(average_block_time, 1)) + } + /// Returns latests validator set + pub async fn latest_validator_set( + &self, + pagination: Option, + ) -> Result< + cosmos_modules::tendermint::GetLatestValidatorSetResponse, + DaemonError, + > { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_latest_validator_set(cosmos_modules::tendermint::GetLatestValidatorSetRequest { + pagination, + }) + .await? + .into_inner(); + Ok(resp) + } + /// Returns latests validator set fetched by height + pub async fn validator_set_by_height( + &self, + height: i64, + pagination: Option, + ) -> Result< + cosmos_modules::tendermint::GetValidatorSetByHeightResponse, + DaemonError, + > { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_validator_set_by_height(cosmos_modules::tendermint::GetValidatorSetByHeightRequest { + height, + pagination, + }) + .await? + .into_inner(); + Ok(resp) + } + /// Returns current block height + pub async fn block_height(&self) -> Result { + let block = self.latest_block().await?; + Ok(block.header.height.value()) + } + /// Returns the block timestamp (since unix epoch) in nanos + pub async fn block_time(&self) -> Result { + let block = self.latest_block().await?; + Ok(block.header.time.duration_since(Time::unix_epoch())?.as_nanos()) + } + /// Simulate TX + pub async fn simulate_tx( + &self, + tx_bytes: Vec, + ) -> Result { + let mut client = cosmos_modules::tx::service_client::ServiceClient::new( + self.channel.clone(), + ); + #[allow(deprecated)] + let resp: SimulateResponse = client + .simulate(cosmos_modules::tx::SimulateRequest { + tx: None, + tx_bytes, + }) + .await? + .into_inner(); + let gas_used = resp.gas_info.unwrap().gas_used; + Ok(gas_used) + } + /// Returns all the block info + pub async fn block_info( + &self, + ) -> Result { + let block = self.latest_block().await?; + let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; + let time = cosmwasm_std::Timestamp::from_nanos( + since_epoch.as_nanos() as u64, + ); + Ok(cosmwasm_std::BlockInfo { + height: block.header.height.value(), + time, + chain_id: block.header.chain_id.to_string(), + }) + } + /// Find TX by hash + pub async fn find_tx( + &self, + hash: String, + ) -> Result { + self.find_tx_with_retries(hash, MAX_TX_QUERY_RETRIES).await + } + /// Find TX by hash with a given amount of retries + pub async fn find_tx_with_retries( + &self, + hash: String, + retries: usize, + ) -> Result { + let mut client = cosmos_modules::tx::service_client::ServiceClient::new( + self.channel.clone(), + ); + let request = cosmos_modules::tx::GetTxRequest { + hash: hash.clone(), + }; + let mut block_speed = self.average_block_speed(Some(0.7)).await?; + for _ in 0..retries { + match client.get_tx(request.clone()).await { + Ok(tx) => { + let resp = tx.into_inner().tx_response.unwrap(); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("TX found: {0:?}", resp), + lvl, + &( + "cw_orch_daemon::queriers::node", + "cw_orch_daemon::queriers::node", + "cw-orch-daemon/src/queriers/node.rs", + ), + 220u32, + ::log::__private_api::Option::None, + ); + } + }; + return Ok(resp.into()); + } + Err(err) => { + block_speed = (block_speed as f64 * 1.6) as u64; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("TX not found with error: {0:?}", err), + lvl, + &( + "cw_orch_daemon::queriers::node", + "cw_orch_daemon::queriers::node", + "cw-orch-daemon/src/queriers/node.rs", + ), + 226u32, + ::log::__private_api::Option::None, + ); + } + }; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Waiting {0} seconds", block_speed), + lvl, + &( + "cw_orch_daemon::queriers::node", + "cw_orch_daemon::queriers::node", + "cw-orch-daemon/src/queriers/node.rs", + ), + 227u32, + ::log::__private_api::Option::None, + ); + } + }; + tokio::time::sleep(Duration::from_secs(block_speed)).await; + } + } + } + Err(DaemonError::TXNotFound(hash, retries)) + } + } + } + mod staking { + use crate::{cosmos_modules, error::DaemonError}; + use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; + use tonic::transport::Channel; + use super::DaemonQuerier; + /// Querier for the Cosmos Staking module + pub struct Staking { + channel: Channel, + } + impl DaemonQuerier for Staking { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Staking { + /// Queries validator info for given validator address + pub async fn validator( + &self, + validator_addr: impl Into, + ) -> Result { + let validator: cosmos_modules::staking::QueryValidatorResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryValidatorRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryValidatorRequest { + validator_addr: validator_addr.into(), + }; + let response = client.validator(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 24u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(validator.validator.unwrap()) + } + /// Queries all validators that match the given status + /// + /// see [StakingBondStatus] for available statuses + pub async fn validators( + &self, + status: StakingBondStatus, + ) -> Result, DaemonError> { + let validators: cosmos_modules::staking::QueryValidatorsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryValidatorsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryValidatorsRequest { + status: status.to_string(), + pagination: None, + }; + let response = client + .validators(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 42u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(validators.validators) + } + /// Query validator delegations info for given validator + /// + /// see [PageRequest] for pagination + pub async fn delegations( + &self, + validator_addr: impl Into, + pagination: Option, + ) -> Result, DaemonError> { + let validator_delegations: cosmos_modules::staking::QueryValidatorDelegationsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryValidatorDelegationsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryValidatorDelegationsRequest { + validator_addr: validator_addr.into(), + pagination: pagination, + }; + let response = client + .validator_delegations(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 62u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(validator_delegations.delegation_responses) + } + /// Query validator unbonding delegations of a validator + pub async fn unbonding_delegations( + &self, + validator_addr: impl Into, + ) -> Result, DaemonError> { + let validator_unbonding_delegations: cosmos_modules::staking::QueryValidatorUnbondingDelegationsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, + QueryValidatorUnbondingDelegationsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryValidatorUnbondingDelegationsRequest { + validator_addr: validator_addr.into(), + pagination: None, + }; + let response = client + .validator_unbonding_delegations(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 79u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(validator_unbonding_delegations.unbonding_responses) + } + /// Query delegation info for given validator for a delegator + pub async fn delegation( + &self, + validator_addr: impl Into, + delegator_addr: impl Into, + ) -> Result { + let delegation: cosmos_modules::staking::QueryDelegationResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryDelegationRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDelegationRequest { + validator_addr: validator_addr.into(), + delegator_addr: delegator_addr.into(), + }; + let response = client + .delegation(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 97u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(delegation.delegation_response.unwrap()) + } + /// Query unbonding delegation info for given validator delegator + pub async fn unbonding_delegation( + &self, + validator_addr: impl Into, + delegator_addr: impl Into, + ) -> Result { + let unbonding_delegation: cosmos_modules::staking::QueryUnbondingDelegationResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryUnbondingDelegationRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryUnbondingDelegationRequest { + validator_addr: validator_addr.into(), + delegator_addr: delegator_addr.into(), + }; + let response = client + .unbonding_delegation(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 115u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(unbonding_delegation.unbond.unwrap()) + } + /// Query all delegator delegations of a given delegator address + /// + /// see [PageRequest] for pagination + pub async fn delegator_delegations( + &self, + delegator_addr: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::staking::QueryDelegatorDelegationsResponse, + DaemonError, + > { + let delegator_delegations: cosmos_modules::staking::QueryDelegatorDelegationsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryDelegatorDelegationsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDelegatorDelegationsRequest { + delegator_addr: delegator_addr.into(), + pagination: pagination, + }; + let response = client + .delegator_delegations(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 135u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(delegator_delegations) + } + /// Queries all unbonding delegations of a given delegator address. + /// + /// see [PageRequest] for pagination + pub async fn delegator_unbonding_delegations( + &self, + delegator_addr: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::staking::QueryDelegatorUnbondingDelegationsResponse, + DaemonError, + > { + let delegator_unbonding_delegations: cosmos_modules::staking::QueryDelegatorUnbondingDelegationsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, + QueryDelegatorUnbondingDelegationsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDelegatorUnbondingDelegationsRequest { + delegator_addr: delegator_addr.into(), + pagination: pagination, + }; + let response = client + .delegator_unbonding_delegations(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 156u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(delegator_unbonding_delegations) + } + /// Query redelegations of a given address + /// + /// see [PageRequest] for pagination + pub async fn redelegations( + &self, + delegator_addr: impl Into, + src_validator_addr: impl Into, + dst_validator_addr: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::staking::QueryRedelegationsResponse, + DaemonError, + > { + let redelegations: cosmos_modules::staking::QueryRedelegationsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryRedelegationsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryRedelegationsRequest { + delegator_addr: delegator_addr.into(), + src_validator_addr: src_validator_addr.into(), + dst_validator_addr: dst_validator_addr.into(), + pagination: pagination, + }; + let response = client + .redelegations(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 178u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(redelegations) + } + /// Query delegator validators info for given delegator address. + pub async fn delegator_validator( + &self, + validator_addr: impl Into, + delegator_addr: impl Into, + ) -> Result< + cosmos_modules::staking::QueryDelegatorValidatorResponse, + DaemonError, + > { + let delegator_validator: cosmos_modules::staking::QueryDelegatorValidatorResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryDelegatorValidatorRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDelegatorValidatorRequest { + validator_addr: validator_addr.into(), + delegator_addr: delegator_addr.into(), + }; + let response = client + .delegator_validator(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 198u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(delegator_validator) + } + /// Query delegator validators info for given delegator address + /// + /// see [PageRequest] for pagination + pub async fn delegator_validators( + &self, + delegator_addr: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::staking::QueryDelegatorValidatorsResponse, + DaemonError, + > { + let delegator_validators: cosmos_modules::staking::QueryDelegatorValidatorsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryDelegatorValidatorsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDelegatorValidatorsRequest { + delegator_addr: delegator_addr.into(), + pagination: pagination, + }; + let response = client + .delegator_validators(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 218u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(delegator_validators) + } + /// Query historical info info for given height + pub async fn historical_info( + &self, + height: i64, + ) -> Result< + cosmos_modules::staking::QueryHistoricalInfoResponse, + DaemonError, + > { + let historical_info: cosmos_modules::staking::QueryHistoricalInfoResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryHistoricalInfoRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryHistoricalInfoRequest { + height: height, + }; + let response = client + .historical_info(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 236u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(historical_info) + } + /// Query the pool info + pub async fn pool( + &self, + ) -> Result { + let pool: cosmos_modules::staking::QueryPoolResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryPoolRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPoolRequest {}; + let response = client.pool(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 248u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(pool) + } + /// Query staking parameters + pub async fn params( + &self, + ) -> Result { + let params: cosmos_modules::staking::QueryParamsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryParamsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryParamsRequest {}; + let response = client.params(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 257u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(params) + } + } + /// Staking bond statuses + pub enum StakingBondStatus { + /// UNSPECIFIED defines an invalid validator status. + Unspecified = 0, + /// UNBONDED defines a validator that is not bonded. + Unbonded = 1, + /// UNBONDING defines a validator that is unbonding. + Unbonding = 2, + /// BONDED defines a validator that is bonded. + Bonded = 3, + } + impl ToString for StakingBondStatus { + /// Convert to string + fn to_string(&self) -> String { + match self { + StakingBondStatus::Unspecified => { + "BOND_STATUS_UNSPECIFIED".to_string() + } + StakingBondStatus::Unbonded => "BOND_STATUS_UNBONDED".to_string(), + StakingBondStatus::Unbonding => "BOND_STATUS_UNBONDING".to_string(), + StakingBondStatus::Bonded => "BOND_STATUS_BONDED".to_string(), + } + } + } + } + pub use bank::Bank; + pub use cosmwasm::CosmWasm; + pub use feegrant::Feegrant; + pub use ibc::Ibc; + pub use node::Node; + pub use gov::*; + pub use staking::*; + use tonic::transport::Channel; + /// Constructor for a querier over a given channel + pub trait DaemonQuerier { + /// Construct an new querier over a given channel + fn new(channel: Channel) -> Self; + } +} +mod traits { + use cw_orch_core::{ + contract::interface_traits::{CwOrchMigrate, CwOrchUpload}, + environment::TxResponse, + }; + use crate::{queriers::CosmWasm, Daemon, DaemonError}; + /// Helper methods for conditional uploading of a contract. + pub trait ConditionalUpload: CwOrchUpload { + /// Only upload the contract if it is not uploaded yet (checksum does not match) + fn upload_if_needed(&self) -> Result>, DaemonError> { + if self.latest_is_uploaded()? { + Ok(None) + } else { + Some(self.upload()).transpose().map_err(Into::into) + } + } + /// Returns whether the checksum of the WASM file matches the checksum of the latest uploaded code for this contract. + fn latest_is_uploaded(&self) -> Result { + let Some(latest_uploaded_code_id) = self.code_id().ok() else { + return Ok(false); + }; + let chain = self.get_chain(); + let on_chain_hash = chain + .rt_handle + .block_on( + chain + .query_client::() + .code_id_hash(latest_uploaded_code_id), + )?; + let local_hash = self.wasm().checksum()?; + Ok(local_hash == on_chain_hash) + } + /// Returns whether the contract is running the latest uploaded code for it + fn is_running_latest(&self) -> Result { + let Some(latest_uploaded_code_id) = self.code_id().ok() else { + return Ok(false); + }; + let chain = self.get_chain(); + let info = chain + .rt_handle + .block_on( + chain.query_client::().contract_info(self.address()?), + )?; + Ok(latest_uploaded_code_id == info.code_id) + } + } + impl ConditionalUpload for T + where + T: CwOrchUpload, + {} + /// Helper methods for conditional migration of a contract. + pub trait ConditionalMigrate: CwOrchMigrate + ConditionalUpload { + /// Only migrate the contract if it is not on the latest code-id yet + fn migrate_if_needed( + &self, + migrate_msg: &Self::MigrateMsg, + ) -> Result>, DaemonError> { + if self.is_running_latest()? { + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "{0} is already running the latest code", + self.id(), + ), + lvl, + &( + "cw_orch_daemon::traits", + "cw_orch_daemon::traits", + "cw-orch-daemon/src/traits.rs", + ), + 61u32, + ::log::__private_api::Option::None, + ); + } + }; + Ok(None) + } else { + Some(self.migrate(migrate_msg, self.code_id()?)) + .transpose() + .map_err(Into::into) + } + } + /// Uploads the contract if the local contract hash is different from the latest on-chain code hash. + /// Proceeds to migrates the contract if the contract is not running the latest code. + fn upload_and_migrate_if_needed( + &self, + migrate_msg: &Self::MigrateMsg, + ) -> Result>>, DaemonError> { + let mut txs = Vec::with_capacity(2); + if let Some(tx) = self.upload_if_needed()? { + txs.push(tx); + } + if let Some(tx) = self.migrate_if_needed(migrate_msg)? { + txs.push(tx); + } + if txs.is_empty() { Ok(None) } else { Ok(Some(txs)) } + } + } + impl ConditionalMigrate for T + where + T: CwOrchMigrate + CwOrchUpload, + {} +} +pub mod tx_builder { + use cosmrs::tx::{ModeInfo, SignMode}; + use cosmrs::{ + proto::cosmos::auth::v1beta1::BaseAccount, tendermint::chain::Id, + tx::{self, Body, Fee, Msg, Raw, SequenceNumber, SignDoc, SignerInfo}, + Any, Coin, + }; + use secp256k1::All; + use super::{sender::Sender, DaemonError}; + const GAS_BUFFER: f64 = 1.3; + const BUFFER_THRESHOLD: u64 = 200_000; + const SMALL_GAS_BUFFER: f64 = 1.4; + /// Struct used to build a raw transaction and broadcast it with a sender. + pub struct TxBuilder { + pub(crate) body: Body, + pub(crate) fee_amount: Option, + pub(crate) gas_limit: Option, + pub(crate) sequence: Option, + } + #[automatically_derived] + impl ::core::clone::Clone for TxBuilder { + #[inline] + fn clone(&self) -> TxBuilder { + TxBuilder { + body: ::core::clone::Clone::clone(&self.body), + fee_amount: ::core::clone::Clone::clone(&self.fee_amount), + gas_limit: ::core::clone::Clone::clone(&self.gas_limit), + sequence: ::core::clone::Clone::clone(&self.sequence), + } + } + } + #[automatically_derived] + impl ::core::fmt::Debug for TxBuilder { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field4_finish( + f, + "TxBuilder", + "body", + &self.body, + "fee_amount", + &self.fee_amount, + "gas_limit", + &self.gas_limit, + "sequence", + &&self.sequence, + ) + } + } + impl TxBuilder { + /// Create a new TxBuilder with a given body. + pub fn new(body: Body) -> Self { + Self { + body, + fee_amount: None, + gas_limit: None, + sequence: None, + } + } + /// Set a fixed fee amount for the tx + pub fn fee_amount(&mut self, fee_amount: u128) -> &mut Self { + self.fee_amount = Some(fee_amount); + self + } + /// Set a gas limit for the tx + pub fn gas_limit(&mut self, gas_limit: u64) -> &mut Self { + self.gas_limit = Some(gas_limit); + self + } + /// Set a sequence number for the tx + pub fn sequence(&mut self, sequence: u64) -> &mut Self { + self.sequence = Some(sequence); + self + } + /// Builds the body of the tx with a given memo and timeout. + pub fn build_body( + msgs: Vec, + memo: Option<&str>, + timeout: u64, + ) -> tx::Body { + let msgs = msgs + .into_iter() + .map(Msg::into_any) + .collect::, _>>() + .unwrap(); + tx::Body::new(msgs, memo.unwrap_or_default(), timeout as u32) + } + pub(crate) fn build_fee( + amount: impl Into, + denom: &str, + gas_limit: u64, + ) -> Fee { + let fee = Coin::new(amount.into(), denom).unwrap(); + Fee::from_amount_and_gas(fee, gas_limit) + } + /// Builds the raw tx with a given body and fee and signs it. + /// Sets the TxBuilder's gas limit to its simulated amount for later use. + pub async fn build(&mut self, wallet: &Sender) -> Result { + let BaseAccount { account_number, sequence, .. } = wallet + .base_account() + .await?; + let sequence = self.sequence.unwrap_or(sequence); + let (tx_fee, gas_limit) = if let (Some(fee), Some(gas_limit)) + = (self.fee_amount, self.gas_limit) { + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Using pre-defined fee and gas limits: {0}, {1}", + fee, + gas_limit, + ), + lvl, + &( + "cw_orch_daemon::tx_builder", + "cw_orch_daemon::tx_builder", + "cw-orch-daemon/src/tx_builder.rs", + ), + 91u32, + ::log::__private_api::Option::None, + ); + } + }; + (fee, gas_limit) + } else { + let sim_gas_used = wallet + .calculate_gas(&self.body, sequence, account_number) + .await?; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Simulated gas needed {0:?}", sim_gas_used), + lvl, + &( + "cw_orch_daemon::tx_builder", + "cw_orch_daemon::tx_builder", + "cw-orch-daemon/src/tx_builder.rs", + ), + 101u32, + ::log::__private_api::Option::None, + ); + } + }; + let gas_expected = if sim_gas_used < BUFFER_THRESHOLD { + sim_gas_used as f64 * SMALL_GAS_BUFFER + } else { + sim_gas_used as f64 * GAS_BUFFER + }; + let fee_amount = gas_expected + * (wallet + .daemon_state + .chain_data + .fees + .fee_tokens[0] + .fixed_min_gas_price + 0.00001); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Calculated fee needed: {0:?}", fee_amount), + lvl, + &( + "cw_orch_daemon::tx_builder", + "cw_orch_daemon::tx_builder", + "cw-orch-daemon/src/tx_builder.rs", + ), + 111u32, + ::log::__private_api::Option::None, + ); + } + }; + self.gas_limit = Some(gas_expected as u64); + (fee_amount as u128, gas_expected as u64) + }; + let fee = Self::build_fee( + tx_fee, + &wallet.daemon_state.chain_data.fees.fee_tokens[0].denom, + gas_limit, + ); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "submitting tx: \n fee: {0:?}\naccount_nr: {1:?}\nsequence: {2:?}", + fee, + account_number, + sequence, + ), + lvl, + &( + "cw_orch_daemon::tx_builder", + "cw_orch_daemon::tx_builder", + "cw-orch-daemon/src/tx_builder.rs", + ), + 125u32, + ::log::__private_api::Option::None, + ); + } + }; + let auth_info = SignerInfo { + public_key: wallet.private_key.get_signer_public_key(&wallet.secp), + mode_info: ModeInfo::single(SignMode::Direct), + sequence, + } + .auth_info(fee); + let sign_doc = SignDoc::new( + &self.body, + &auth_info, + &Id::try_from(wallet.daemon_state.chain_data.chain_id.to_string())?, + account_number, + )?; + wallet.sign(sign_doc).map_err(Into::into) + } + } +} +pub use self::{ + builder::*, channel::*, core::*, error::*, state::*, sync::*, traits::*, tx_resp::*, +}; +pub use cw_orch_networks::chain_info::*; +pub use cw_orch_networks::networks; +pub use sender::Wallet; +pub use tx_builder::TxBuilder; +pub(crate) mod cosmos_modules { + pub use cosmrs::proto::{ + cosmos::{ + auth::v1beta1 as auth, authz::v1beta1 as authz, bank::v1beta1 as bank, + base::{ + abci::v1beta1 as abci, tendermint::v1beta1 as tendermint, v1beta1 as base, + }, + crisis::v1beta1 as crisis, distribution::v1beta1 as distribution, + evidence::v1beta1 as evidence, feegrant::v1beta1 as feegrant, + gov::v1beta1 as gov, mint::v1beta1 as mint, params::v1beta1 as params, + slashing::v1beta1 as slashing, staking::v1beta1 as staking, + tx::v1beta1 as tx, vesting::v1beta1 as vesting, + }, + cosmwasm::wasm::v1 as cosmwasm, + ibc::{ + applications::transfer::v1 as ibc_transfer, + core::{ + channel::v1 as ibc_channel, client::v1 as ibc_client, + connection::v1 as ibc_connection, + }, + }, + tendermint::abci as tendermint_abci, + }; +} +/// Re-export trait and data required to fetch daemon data from chain-registry +pub use ibc_chain_registry::{ + chain::ChainData as ChainRegistryData, fetchable::Fetchable, +}; diff --git a/cw-orch-daemon/test b/cw-orch-daemon/test new file mode 100644 index 000000000..b08bac50a --- /dev/null +++ b/cw-orch-daemon/test @@ -0,0 +1,25355 @@ +#![feature(prelude_import)] +//! `Daemon` and `DaemonAsync` execution environments. +//! +//! The `Daemon` type is a synchronous wrapper around the `DaemonAsync` type and can be used as a contract execution environment. +#[prelude_import] +use std::prelude::rust_2021::*; +#[macro_use] +extern crate std; +pub mod builder { + use crate::{DaemonAsync, DaemonBuilder}; + use std::rc::Rc; + use ibc_chain_registry::chain::ChainData; + use super::{error::DaemonError, sender::Sender, state::DaemonState}; + /// The default deployment id if none is provided + pub const DEFAULT_DEPLOYMENT: &str = "default"; + /// Create [`DaemonAsync`] through [`DaemonAsyncBuilder`] + /// ## Example + /// ```no_run + /// # tokio_test::block_on(async { + /// use cw_orch_daemon::{DaemonAsyncBuilder, networks}; + /// let daemon = DaemonAsyncBuilder::default() + /// .chain(networks::LOCAL_JUNO) + /// .deployment_id("v0.1.0") + /// .build() + /// .await.unwrap(); + /// # }) + /// ``` + pub struct DaemonAsyncBuilder { + pub(crate) chain: Option, + pub(crate) deployment_id: Option, + /// Wallet mnemonic + pub(crate) mnemonic: Option, + } + #[automatically_derived] + impl ::core::clone::Clone for DaemonAsyncBuilder { + #[inline] + fn clone(&self) -> DaemonAsyncBuilder { + DaemonAsyncBuilder { + chain: ::core::clone::Clone::clone(&self.chain), + deployment_id: ::core::clone::Clone::clone(&self.deployment_id), + mnemonic: ::core::clone::Clone::clone(&self.mnemonic), + } + } + } + #[automatically_derived] + impl ::core::default::Default for DaemonAsyncBuilder { + #[inline] + fn default() -> DaemonAsyncBuilder { + DaemonAsyncBuilder { + chain: ::core::default::Default::default(), + deployment_id: ::core::default::Default::default(), + mnemonic: ::core::default::Default::default(), + } + } + } + impl DaemonAsyncBuilder { + /// Set the chain the daemon will connect to + pub fn chain(&mut self, chain: impl Into) -> &mut Self { + self.chain = Some(chain.into()); + self + } + /// Set the deployment id to use for the daemon interactions + /// Defaults to `default` + pub fn deployment_id(&mut self, deployment_id: impl Into) -> &mut Self { + self.deployment_id = Some(deployment_id.into()); + self + } + /// Set the mnemonic to use with this chain. + /// Defaults to env variable depending on the environment. + /// + /// Variables: LOCAL_MNEMONIC, TEST_MNEMONIC and MAIN_MNEMONIC + pub fn mnemonic(&mut self, mnemonic: impl ToString) -> &mut Self { + self.mnemonic = Some(mnemonic.to_string()); + self + } + /// Build a daemon + pub async fn build(&self) -> Result { + let chain = self + .chain + .clone() + .ok_or(DaemonError::BuilderMissing("chain information".into()))?; + let deployment_id = self + .deployment_id + .clone() + .unwrap_or(DEFAULT_DEPLOYMENT.to_string()); + let state = Rc::new(DaemonState::new(chain, deployment_id).await?); + let sender = if let Some(mnemonic) = &self.mnemonic { + Sender::from_mnemonic(&state, mnemonic)? + } else { + Sender::new(&state)? + }; + let daemon = DaemonAsync { + state, + sender: Rc::new(sender), + }; + Ok(daemon) + } + } + impl From for DaemonAsyncBuilder { + fn from(value: DaemonBuilder) -> Self { + DaemonAsyncBuilder { + chain: value.chain, + deployment_id: value.deployment_id, + mnemonic: value.mnemonic, + } + } + } +} +pub mod channel { + use cosmrs::proto::cosmos::base::tendermint::v1beta1::{ + service_client::ServiceClient, GetNodeInfoRequest, + }; + use ibc_chain_registry::chain::Grpc; + use ibc_relayer_types::core::ics24_host::identifier::ChainId; + use tonic::transport::{Channel, ClientTlsConfig}; + use super::error::DaemonError; + /// A helper for constructing a gRPC channel + pub struct GrpcChannel {} + impl GrpcChannel { + /// Connect to any of the provided gRPC endpoints + pub async fn connect( + grpc: &[Grpc], + chain_id: &ChainId, + ) -> Result { + let mut successful_connections = ::alloc::vec::Vec::new(); + for Grpc { address, .. } in grpc.iter() { + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Trying to connect to endpoint: {0}", address), + lvl, + &( + "cw_orch_daemon::channel", + "cw_orch_daemon::channel", + "cw-orch-daemon/src/channel.rs", + ), + 19u32, + ::log::__private_api::Option::None, + ); + } + }; + let endpoint = Channel::builder(address.clone().try_into().unwrap()); + let maybe_client = ServiceClient::connect(endpoint.clone()).await; + let mut client = if maybe_client.is_ok() { + maybe_client? + } else { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Cannot connect to gRPC endpoint: {0}, {1:?}", + address, + maybe_client.unwrap_err(), + ), + lvl, + &( + "cw_orch_daemon::channel", + "cw_orch_daemon::channel", + "cw-orch-daemon/src/channel.rs", + ), + 31u32, + ::log::__private_api::Option::None, + ); + } + }; + if !(address.contains("https") || address.contains("443")) { + continue; + } + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Attempting to connect with TLS"), + lvl, + &( + "cw_orch_daemon::channel", + "cw_orch_daemon::channel", + "cw-orch-daemon/src/channel.rs", + ), + 43u32, + ::log::__private_api::Option::None, + ); + } + }; + let endpoint = endpoint.clone().tls_config(ClientTlsConfig::new())?; + let maybe_client = ServiceClient::connect(endpoint.clone()).await; + if maybe_client.is_err() { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "Cannot connect to gRPC endpoint: {0}, {1:?}", + address, + maybe_client.unwrap_err(), + ), + lvl, + &( + "cw_orch_daemon::channel", + "cw_orch_daemon::channel", + "cw-orch-daemon/src/channel.rs", + ), + 51u32, + ::log::__private_api::Option::None, + ); + } + }; + continue; + } + maybe_client? + }; + let node_info = client + .get_node_info(GetNodeInfoRequest {}) + .await? + .into_inner(); + if ChainId::is_epoch_format( + &node_info.default_node_info.as_ref().unwrap().network, + ) { + if node_info.default_node_info.as_ref().unwrap().network + != chain_id.as_str() + { + { + let lvl = ::log::Level::Error; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "Network mismatch: connection:{0} != config:{1}", + node_info.default_node_info.as_ref().unwrap().network, + chain_id.as_str(), + ), + lvl, + &( + "cw_orch_daemon::channel", + "cw_orch_daemon::channel", + "cw-orch-daemon/src/channel.rs", + ), + 72u32, + ::log::__private_api::Option::None, + ); + } + }; + continue; + } + } + successful_connections.push(endpoint.connect().await?) + } + if successful_connections.is_empty() { + return Err(DaemonError::CannotConnectGRPC); + } + Ok(successful_connections.pop().unwrap()) + } + } +} +pub mod core { + use crate::{queriers::CosmWasm, DaemonState}; + use super::{ + builder::DaemonAsyncBuilder, cosmos_modules, error::DaemonError, + queriers::{DaemonQuerier, Node}, + sender::Wallet, tx_resp::CosmTxResponse, + }; + use cosmrs::{ + cosmwasm::{MsgExecuteContract, MsgInstantiateContract, MsgMigrateContract}, + tendermint::Time, AccountId, Denom, + }; + use cosmwasm_std::{Addr, Coin}; + use cw_orch_core::{ + contract::interface_traits::Uploadable, environment::{ChainState, IndexResponse}, + }; + use serde::{de::DeserializeOwned, Serialize}; + use serde_json::from_str; + use std::{ + fmt::Debug, rc::Rc, str::{from_utf8, FromStr}, + time::Duration, + }; + use tonic::transport::Channel; + /** + Represents a blockchain node. + It's constructed using [`DaemonAsyncBuilder`]. + + ## Usage + ```rust,no_run + # tokio_test::block_on(async { + use cw_orch_daemon::{DaemonAsync, networks}; + + let daemon: DaemonAsync = DaemonAsync::builder() + .chain(networks::JUNO_1) + .build() + .await.unwrap(); + # }) + ``` + ## Environment Execution + + The DaemonAsync implements async methods of [`TxHandler`](cw_orch_core::environment::TxHandler) which allows you to perform transactions on the chain. + + ## Querying + + Different Cosmos SDK modules can be queried through the daemon by calling the [`DaemonAsync::query_client`] method with a specific querier. + See [Querier](crate::queriers) for examples. +*/ + pub struct DaemonAsync { + /// Sender to send transactions to the chain + pub sender: Wallet, + /// State of the daemon + pub state: Rc, + } + #[automatically_derived] + impl ::core::clone::Clone for DaemonAsync { + #[inline] + fn clone(&self) -> DaemonAsync { + DaemonAsync { + sender: ::core::clone::Clone::clone(&self.sender), + state: ::core::clone::Clone::clone(&self.state), + } + } + } + impl DaemonAsync { + /// Get the daemon builder + pub fn builder() -> DaemonAsyncBuilder { + DaemonAsyncBuilder::default() + } + /// Perform a query with a given query client. + /// See [Querier](crate::queriers) for examples. + pub fn query_client(&self) -> Querier { + Querier::new(self.sender.channel()) + } + /// Get the channel configured for this DaemonAsync. + pub fn channel(&self) -> Channel { + self.state.grpc_channel.clone() + } + } + impl ChainState for DaemonAsync { + type Out = Rc; + fn state(&self) -> Self::Out { + self.state.clone() + } + } + impl DaemonAsync { + /// Get the sender address + pub fn sender(&self) -> Addr { + self.sender.address().unwrap() + } + /// Execute a message on a contract. + pub async fn execute( + &self, + exec_msg: &E, + coins: &[cosmwasm_std::Coin], + contract_address: &Addr, + ) -> Result { + let exec_msg: MsgExecuteContract = MsgExecuteContract { + sender: self.sender.pub_addr()?, + contract: AccountId::from_str(contract_address.as_str())?, + msg: serde_json::to_vec(&exec_msg)?, + funds: parse_cw_coins(coins)?, + }; + let result = self + .sender + .commit_tx( + <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([exec_msg])), + None, + ) + .await?; + Ok(result) + } + /// Instantiate a contract. + pub async fn instantiate( + &self, + code_id: u64, + init_msg: &I, + label: Option<&str>, + admin: Option<&Addr>, + coins: &[Coin], + ) -> Result { + let sender = &self.sender; + let init_msg = MsgInstantiateContract { + code_id, + label: Some(label.unwrap_or("instantiate_contract").to_string()), + admin: admin.map(|a| FromStr::from_str(a.as_str()).unwrap()), + sender: sender.pub_addr()?, + msg: serde_json::to_vec(&init_msg)?, + funds: parse_cw_coins(coins)?, + }; + let result = sender + .commit_tx( + <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([init_msg])), + None, + ) + .await?; + Ok(result) + } + /// Query a contract. + pub async fn query( + &self, + query_msg: &Q, + contract_address: &Addr, + ) -> Result { + let sender = &self.sender; + let mut client = cosmos_modules::cosmwasm::query_client::QueryClient::new( + sender.channel(), + ); + let resp = client + .smart_contract_state(cosmos_modules::cosmwasm::QuerySmartContractStateRequest { + address: contract_address.to_string(), + query_data: serde_json::to_vec(&query_msg)?, + }) + .await?; + Ok(from_str(from_utf8(&resp.into_inner().data).unwrap())?) + } + /// Migration a contract. + pub async fn migrate( + &self, + migrate_msg: &M, + new_code_id: u64, + contract_address: &Addr, + ) -> Result { + let exec_msg: MsgMigrateContract = MsgMigrateContract { + sender: self.sender.pub_addr()?, + contract: AccountId::from_str(contract_address.as_str())?, + msg: serde_json::to_vec(&migrate_msg)?, + code_id: new_code_id, + }; + let result = self + .sender + .commit_tx( + <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([exec_msg])), + None, + ) + .await?; + Ok(result) + } + /// Wait for a given amount of blocks. + pub async fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { + let mut last_height = self.query_client::().block_height().await?; + let end_height = last_height + amount; + let average_block_speed = self + .query_client::() + .average_block_speed(Some(0.9)) + .await?; + let wait_time = average_block_speed * amount; + tokio::time::sleep(Duration::from_secs(wait_time)).await; + while last_height < end_height { + tokio::time::sleep(Duration::from_secs(average_block_speed)).await; + last_height = self.query_client::().block_height().await?; + } + Ok(()) + } + /// Wait for a given amount of seconds. + pub async fn wait_seconds(&self, secs: u64) -> Result<(), DaemonError> { + tokio::time::sleep(Duration::from_secs(secs)).await; + Ok(()) + } + /// Wait for the next block. + pub async fn next_block(&self) -> Result<(), DaemonError> { + self.wait_blocks(1).await + } + /// Get the current block info. + pub async fn block_info(&self) -> Result { + let block = self.query_client::().latest_block().await?; + let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; + let time = cosmwasm_std::Timestamp::from_nanos( + since_epoch.as_nanos() as u64, + ); + Ok(cosmwasm_std::BlockInfo { + height: block.header.height.value(), + time, + chain_id: block.header.chain_id.to_string(), + }) + } + /// Upload a contract to the chain. + pub async fn upload( + &self, + uploadable: &impl Uploadable, + ) -> Result { + let sender = &self.sender; + let wasm_path = uploadable.wasm(); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Uploading file at {0:?}", wasm_path), + lvl, + &( + "cw_orch_daemon::core", + "cw_orch_daemon::core", + "cw-orch-daemon/src/core.rs", + ), + 233u32, + ::log::__private_api::Option::None, + ); + } + }; + let file_contents = std::fs::read(wasm_path.path())?; + let store_msg = cosmrs::cosmwasm::MsgStoreCode { + sender: sender.pub_addr()?, + wasm_byte_code: file_contents, + instantiate_permission: None, + }; + let result = sender + .commit_tx( + <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([store_msg])), + None, + ) + .await?; + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Uploaded: {0:?}", result.txhash), + lvl, + &( + "cw_orch_daemon::core", + "cw_orch_daemon::core", + "cw-orch-daemon/src/core.rs", + ), + 244u32, + ::log::__private_api::Option::None, + ); + } + }; + let code_id = result.uploaded_code_id().unwrap(); + let wasm = CosmWasm::new(self.channel()); + while wasm.code(code_id).await.is_err() { + self.next_block().await?; + } + Ok(result) + } + /// Set the sender to use with this DaemonAsync to be the given wallet + pub fn set_sender(&mut self, sender: &Wallet) { + self.sender = sender.clone(); + } + } + pub(crate) fn parse_cw_coins( + coins: &[cosmwasm_std::Coin], + ) -> Result, DaemonError> { + coins + .iter() + .map(|cosmwasm_std::Coin { amount, denom }| { + Ok(cosmrs::Coin { + amount: amount.u128(), + denom: Denom::from_str(denom)?, + }) + }) + .collect::, DaemonError>>() + } +} +pub mod error { + #![allow(missing_docs)] + use cw_orch_core::CwEnvError; + use thiserror::Error; + pub enum DaemonError { + #[error("Reqwest HTTP(s) Error")] + ReqwestError(#[from] ::reqwest::Error), + #[error("JSON Conversion Error")] + SerdeJson(#[from] ::serde_json::Error), + #[error(transparent)] + ParseIntError(#[from] std::num::ParseIntError), + #[error(transparent)] + IOErr(#[from] ::std::io::Error), + #[error(transparent)] + Secp256k1(#[from] ::secp256k1::Error), + #[error(transparent)] + VarError(#[from] ::std::env::VarError), + #[error(transparent)] + AnyError(#[from] ::anyhow::Error), + #[error(transparent)] + Status(#[from] ::tonic::Status), + #[error(transparent)] + TransportError(#[from] ::tonic::transport::Error), + #[error(transparent)] + TendermintError(#[from] ::cosmrs::tendermint::Error), + #[error(transparent)] + CwEnvError(#[from] ::cw_orch_core::CwEnvError), + #[error("Bech32 Decode Error")] + Bech32DecodeErr, + #[error( + "Bech32 Decode Error: Key Failed prefix {0} or length {1} Wanted:{2}/{3}" + )] + Bech32DecodeExpanded(String, usize, String, usize), + #[error("Mnemonic - Wrong length, it should be 24 words")] + WrongLength, + #[error("Mnemonic - Bad Phrase")] + Phrasing, + #[error("Mnemonic - Missing Phrase")] + MissingPhrase, + #[error("Bad Implementation. Missing Component")] + Implementation, + #[error("Unable to convert into public key `{key}`")] + Conversion { key: String, source: bitcoin::bech32::Error }, + #[error( + "Can not augment daemon deployment after usage in more than one contract." + )] + SharedDaemonState, + #[error(transparent)] + ErrReport(#[from] ::eyre::ErrReport), + #[error(transparent)] + GRpcDecodeError(#[from] ::prost::DecodeError), + #[error(transparent)] + ED25519(#[from] ::ed25519_dalek::ed25519::Error), + #[error(transparent)] + DecodeError(#[from] ::base64::DecodeError), + #[error(transparent)] + HexError(#[from] ::hex::FromHexError), + #[error(transparent)] + BitCoinBip32(#[from] ::bitcoin::bip32::Error), + #[error("83 length-missing SECP256K1 prefix")] + ConversionSECP256k1, + #[error("82 length-missing ED25519 prefix")] + ConversionED25519, + #[error("Expected Key length of 82 or 83 length was {0}")] + ConversionLength(usize), + #[error("Expected Key length of 40 length was {0}")] + ConversionLengthED25519Hex(usize), + #[error( + "Expected ED25519 key of length 32 with a BECH32 ED25519 prefix of 5 chars - Len {0} - Hex {1}" + )] + ConversionPrefixED25519(usize, String), + #[error("Can't call Transactions without some gas rules")] + NoGasOpts, + #[error("Can't parse `{parse}` into a coin")] + CoinParseErrV { parse: String }, + #[error("Can't parse `{0}` into a coin")] + CoinParseErr(String), + #[error("TX submit returned `{0}` - {1} '{2}'")] + TxResultError(usize, String, String), + #[error("No price found for Gas using denom {0}")] + GasPriceError(String), + #[error( + "Attempting to fetch validator set in parts, and failed Height mismatch {0} {1}" + )] + TendermintValidatorSet(u64, u64), + #[error("Transaction {0} not found after {1} attempts")] + TXNotFound(String, usize), + #[error("unknown API error")] + Unknown, + #[error("Generic Error {0}")] + StdErr(String), + #[error("calling contract with unimplemented action")] + NotImplemented, + #[error("new chain detected, fill out the scaffold at {0}")] + NewChain(String), + #[error("new network detected, fill out the scaffold at {0}")] + NewNetwork(String), + #[error("Can not connect to any grpc endpoint that was provided.")] + CannotConnectGRPC, + #[error("tx failed: {reason} with code {code}")] + TxFailed { code: usize, reason: String }, + #[error("The list of grpc endpoints is empty")] + GRPCListIsEmpty, + #[error("no wasm path provided for contract.")] + MissingWasmPath, + #[error("daemon builder missing {0}")] + BuilderMissing(String), + #[error("ibc error: {0}")] + IbcError(String), + #[error("insufficient fee, check gas price: {0}")] + InsufficientFee(String), + } + #[allow(unused_qualifications)] + impl std::error::Error for DaemonError { + fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> { + use thiserror::__private::AsDynError; + #[allow(deprecated)] + match self { + DaemonError::ReqwestError { 0: source, .. } => { + std::option::Option::Some(source.as_dyn_error()) + } + DaemonError::SerdeJson { 0: source, .. } => { + std::option::Option::Some(source.as_dyn_error()) + } + DaemonError::ParseIntError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::IOErr { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::Secp256k1 { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::VarError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::AnyError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::Status { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::TransportError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::TendermintError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::CwEnvError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::Bech32DecodeErr { .. } => std::option::Option::None, + DaemonError::Bech32DecodeExpanded { .. } => std::option::Option::None, + DaemonError::WrongLength { .. } => std::option::Option::None, + DaemonError::Phrasing { .. } => std::option::Option::None, + DaemonError::MissingPhrase { .. } => std::option::Option::None, + DaemonError::Implementation { .. } => std::option::Option::None, + DaemonError::Conversion { source: source, .. } => { + std::option::Option::Some(source.as_dyn_error()) + } + DaemonError::SharedDaemonState { .. } => std::option::Option::None, + DaemonError::ErrReport { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::GRpcDecodeError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::ED25519 { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::DecodeError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::HexError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::BitCoinBip32 { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::ConversionSECP256k1 { .. } => std::option::Option::None, + DaemonError::ConversionED25519 { .. } => std::option::Option::None, + DaemonError::ConversionLength { .. } => std::option::Option::None, + DaemonError::ConversionLengthED25519Hex { .. } => { + std::option::Option::None + } + DaemonError::ConversionPrefixED25519 { .. } => std::option::Option::None, + DaemonError::NoGasOpts { .. } => std::option::Option::None, + DaemonError::CoinParseErrV { .. } => std::option::Option::None, + DaemonError::CoinParseErr { .. } => std::option::Option::None, + DaemonError::TxResultError { .. } => std::option::Option::None, + DaemonError::GasPriceError { .. } => std::option::Option::None, + DaemonError::TendermintValidatorSet { .. } => std::option::Option::None, + DaemonError::TXNotFound { .. } => std::option::Option::None, + DaemonError::Unknown { .. } => std::option::Option::None, + DaemonError::StdErr { .. } => std::option::Option::None, + DaemonError::NotImplemented { .. } => std::option::Option::None, + DaemonError::NewChain { .. } => std::option::Option::None, + DaemonError::NewNetwork { .. } => std::option::Option::None, + DaemonError::CannotConnectGRPC { .. } => std::option::Option::None, + DaemonError::TxFailed { .. } => std::option::Option::None, + DaemonError::GRPCListIsEmpty { .. } => std::option::Option::None, + DaemonError::MissingWasmPath { .. } => std::option::Option::None, + DaemonError::BuilderMissing { .. } => std::option::Option::None, + DaemonError::IbcError { .. } => std::option::Option::None, + DaemonError::InsufficientFee { .. } => std::option::Option::None, + } + } + } + #[allow(unused_qualifications)] + impl std::fmt::Display for DaemonError { + fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + use thiserror::__private::AsDisplay as _; + #[allow(unused_variables, deprecated, clippy::used_underscore_binding)] + match self { + DaemonError::ReqwestError(_0) => { + __formatter.write_fmt(format_args!("Reqwest HTTP(s) Error")) + } + DaemonError::SerdeJson(_0) => { + __formatter.write_fmt(format_args!("JSON Conversion Error")) + } + DaemonError::ParseIntError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::IOErr(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::Secp256k1(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::VarError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::AnyError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::Status(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::TransportError(_0) => { + std::fmt::Display::fmt(_0, __formatter) + } + DaemonError::TendermintError(_0) => { + std::fmt::Display::fmt(_0, __formatter) + } + DaemonError::CwEnvError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::Bech32DecodeErr {} => { + __formatter.write_fmt(format_args!("Bech32 Decode Error")) + } + DaemonError::Bech32DecodeExpanded(_0, _1, _2, _3) => { + __formatter + .write_fmt( + format_args!( + "Bech32 Decode Error: Key Failed prefix {0} or length {1} Wanted:{2}/{3}", + _0.as_display(), + _1.as_display(), + _2.as_display(), + _3.as_display(), + ), + ) + } + DaemonError::WrongLength {} => { + __formatter + .write_fmt( + format_args!( + "Mnemonic - Wrong length, it should be 24 words", + ), + ) + } + DaemonError::Phrasing {} => { + __formatter.write_fmt(format_args!("Mnemonic - Bad Phrase")) + } + DaemonError::MissingPhrase {} => { + __formatter.write_fmt(format_args!("Mnemonic - Missing Phrase")) + } + DaemonError::Implementation {} => { + __formatter + .write_fmt(format_args!("Bad Implementation. Missing Component")) + } + DaemonError::Conversion { key, source } => { + __formatter + .write_fmt( + format_args!( + "Unable to convert into public key `{0}`", + key.as_display(), + ), + ) + } + DaemonError::SharedDaemonState {} => { + __formatter + .write_fmt( + format_args!( + "Can not augment daemon deployment after usage in more than one contract.", + ), + ) + } + DaemonError::ErrReport(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::GRpcDecodeError(_0) => { + std::fmt::Display::fmt(_0, __formatter) + } + DaemonError::ED25519(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::DecodeError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::HexError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::BitCoinBip32(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::ConversionSECP256k1 {} => { + __formatter + .write_fmt(format_args!("83 length-missing SECP256K1 prefix")) + } + DaemonError::ConversionED25519 {} => { + __formatter + .write_fmt(format_args!("82 length-missing ED25519 prefix")) + } + DaemonError::ConversionLength(_0) => { + __formatter + .write_fmt( + format_args!( + "Expected Key length of 82 or 83 length was {0}", + _0.as_display(), + ), + ) + } + DaemonError::ConversionLengthED25519Hex(_0) => { + __formatter + .write_fmt( + format_args!( + "Expected Key length of 40 length was {0}", + _0.as_display(), + ), + ) + } + DaemonError::ConversionPrefixED25519(_0, _1) => { + __formatter + .write_fmt( + format_args!( + "Expected ED25519 key of length 32 with a BECH32 ED25519 prefix of 5 chars - Len {0} - Hex {1}", + _0.as_display(), + _1.as_display(), + ), + ) + } + DaemonError::NoGasOpts {} => { + __formatter + .write_fmt( + format_args!( + "Can\'t call Transactions without some gas rules", + ), + ) + } + DaemonError::CoinParseErrV { parse } => { + __formatter + .write_fmt( + format_args!( + "Can\'t parse `{0}` into a coin", + parse.as_display(), + ), + ) + } + DaemonError::CoinParseErr(_0) => { + __formatter + .write_fmt( + format_args!( + "Can\'t parse `{0}` into a coin", + _0.as_display(), + ), + ) + } + DaemonError::TxResultError(_0, _1, _2) => { + __formatter + .write_fmt( + format_args!( + "TX submit returned `{0}` - {1} \'{2}\'", + _0.as_display(), + _1.as_display(), + _2.as_display(), + ), + ) + } + DaemonError::GasPriceError(_0) => { + __formatter + .write_fmt( + format_args!( + "No price found for Gas using denom {0}", + _0.as_display(), + ), + ) + } + DaemonError::TendermintValidatorSet(_0, _1) => { + __formatter + .write_fmt( + format_args!( + "Attempting to fetch validator set in parts, and failed Height mismatch {0} {1}", + _0.as_display(), + _1.as_display(), + ), + ) + } + DaemonError::TXNotFound(_0, _1) => { + __formatter + .write_fmt( + format_args!( + "Transaction {0} not found after {1} attempts", + _0.as_display(), + _1.as_display(), + ), + ) + } + DaemonError::Unknown {} => { + __formatter.write_fmt(format_args!("unknown API error")) + } + DaemonError::StdErr(_0) => { + __formatter + .write_fmt(format_args!("Generic Error {0}", _0.as_display())) + } + DaemonError::NotImplemented {} => { + __formatter + .write_fmt( + format_args!("calling contract with unimplemented action"), + ) + } + DaemonError::NewChain(_0) => { + __formatter + .write_fmt( + format_args!( + "new chain detected, fill out the scaffold at {0}", + _0.as_display(), + ), + ) + } + DaemonError::NewNetwork(_0) => { + __formatter + .write_fmt( + format_args!( + "new network detected, fill out the scaffold at {0}", + _0.as_display(), + ), + ) + } + DaemonError::CannotConnectGRPC {} => { + __formatter + .write_fmt( + format_args!( + "Can not connect to any grpc endpoint that was provided.", + ), + ) + } + DaemonError::TxFailed { code, reason } => { + __formatter + .write_fmt( + format_args!( + "tx failed: {0} with code {1}", + reason.as_display(), + code.as_display(), + ), + ) + } + DaemonError::GRPCListIsEmpty {} => { + __formatter + .write_fmt(format_args!("The list of grpc endpoints is empty")) + } + DaemonError::MissingWasmPath {} => { + __formatter + .write_fmt(format_args!("no wasm path provided for contract.")) + } + DaemonError::BuilderMissing(_0) => { + __formatter + .write_fmt( + format_args!("daemon builder missing {0}", _0.as_display()), + ) + } + DaemonError::IbcError(_0) => { + __formatter + .write_fmt(format_args!("ibc error: {0}", _0.as_display())) + } + DaemonError::InsufficientFee(_0) => { + __formatter + .write_fmt( + format_args!( + "insufficient fee, check gas price: {0}", + _0.as_display(), + ), + ) + } + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::reqwest::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::reqwest::Error) -> Self { + DaemonError::ReqwestError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::serde_json::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::serde_json::Error) -> Self { + DaemonError::SerdeJson { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From for DaemonError { + #[allow(deprecated)] + fn from(source: std::num::ParseIntError) -> Self { + DaemonError::ParseIntError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::std::io::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::std::io::Error) -> Self { + DaemonError::IOErr { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::secp256k1::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::secp256k1::Error) -> Self { + DaemonError::Secp256k1 { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::std::env::VarError> for DaemonError { + #[allow(deprecated)] + fn from(source: ::std::env::VarError) -> Self { + DaemonError::VarError { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::anyhow::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::anyhow::Error) -> Self { + DaemonError::AnyError { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::tonic::Status> for DaemonError { + #[allow(deprecated)] + fn from(source: ::tonic::Status) -> Self { + DaemonError::Status { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::tonic::transport::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::tonic::transport::Error) -> Self { + DaemonError::TransportError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::cosmrs::tendermint::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::cosmrs::tendermint::Error) -> Self { + DaemonError::TendermintError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::cw_orch_core::CwEnvError> for DaemonError { + #[allow(deprecated)] + fn from(source: ::cw_orch_core::CwEnvError) -> Self { + DaemonError::CwEnvError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::eyre::ErrReport> for DaemonError { + #[allow(deprecated)] + fn from(source: ::eyre::ErrReport) -> Self { + DaemonError::ErrReport { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::prost::DecodeError> for DaemonError { + #[allow(deprecated)] + fn from(source: ::prost::DecodeError) -> Self { + DaemonError::GRpcDecodeError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::ed25519_dalek::ed25519::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::ed25519_dalek::ed25519::Error) -> Self { + DaemonError::ED25519 { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::base64::DecodeError> for DaemonError { + #[allow(deprecated)] + fn from(source: ::base64::DecodeError) -> Self { + DaemonError::DecodeError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::hex::FromHexError> for DaemonError { + #[allow(deprecated)] + fn from(source: ::hex::FromHexError) -> Self { + DaemonError::HexError { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::bitcoin::bip32::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::bitcoin::bip32::Error) -> Self { + DaemonError::BitCoinBip32 { + 0: source, + } + } + } + #[automatically_derived] + impl ::core::fmt::Debug for DaemonError { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + DaemonError::ReqwestError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ReqwestError", + &__self_0, + ) + } + DaemonError::SerdeJson(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "SerdeJson", + &__self_0, + ) + } + DaemonError::ParseIntError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ParseIntError", + &__self_0, + ) + } + DaemonError::IOErr(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "IOErr", + &__self_0, + ) + } + DaemonError::Secp256k1(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "Secp256k1", + &__self_0, + ) + } + DaemonError::VarError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "VarError", + &__self_0, + ) + } + DaemonError::AnyError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "AnyError", + &__self_0, + ) + } + DaemonError::Status(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "Status", + &__self_0, + ) + } + DaemonError::TransportError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "TransportError", + &__self_0, + ) + } + DaemonError::TendermintError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "TendermintError", + &__self_0, + ) + } + DaemonError::CwEnvError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "CwEnvError", + &__self_0, + ) + } + DaemonError::Bech32DecodeErr => { + ::core::fmt::Formatter::write_str(f, "Bech32DecodeErr") + } + DaemonError::Bech32DecodeExpanded( + __self_0, + __self_1, + __self_2, + __self_3, + ) => { + ::core::fmt::Formatter::debug_tuple_field4_finish( + f, + "Bech32DecodeExpanded", + __self_0, + __self_1, + __self_2, + &__self_3, + ) + } + DaemonError::WrongLength => { + ::core::fmt::Formatter::write_str(f, "WrongLength") + } + DaemonError::Phrasing => ::core::fmt::Formatter::write_str(f, "Phrasing"), + DaemonError::MissingPhrase => { + ::core::fmt::Formatter::write_str(f, "MissingPhrase") + } + DaemonError::Implementation => { + ::core::fmt::Formatter::write_str(f, "Implementation") + } + DaemonError::Conversion { key: __self_0, source: __self_1 } => { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "Conversion", + "key", + __self_0, + "source", + &__self_1, + ) + } + DaemonError::SharedDaemonState => { + ::core::fmt::Formatter::write_str(f, "SharedDaemonState") + } + DaemonError::ErrReport(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ErrReport", + &__self_0, + ) + } + DaemonError::GRpcDecodeError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "GRpcDecodeError", + &__self_0, + ) + } + DaemonError::ED25519(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ED25519", + &__self_0, + ) + } + DaemonError::DecodeError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "DecodeError", + &__self_0, + ) + } + DaemonError::HexError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "HexError", + &__self_0, + ) + } + DaemonError::BitCoinBip32(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "BitCoinBip32", + &__self_0, + ) + } + DaemonError::ConversionSECP256k1 => { + ::core::fmt::Formatter::write_str(f, "ConversionSECP256k1") + } + DaemonError::ConversionED25519 => { + ::core::fmt::Formatter::write_str(f, "ConversionED25519") + } + DaemonError::ConversionLength(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ConversionLength", + &__self_0, + ) + } + DaemonError::ConversionLengthED25519Hex(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ConversionLengthED25519Hex", + &__self_0, + ) + } + DaemonError::ConversionPrefixED25519(__self_0, __self_1) => { + ::core::fmt::Formatter::debug_tuple_field2_finish( + f, + "ConversionPrefixED25519", + __self_0, + &__self_1, + ) + } + DaemonError::NoGasOpts => { + ::core::fmt::Formatter::write_str(f, "NoGasOpts") + } + DaemonError::CoinParseErrV { parse: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "CoinParseErrV", + "parse", + &__self_0, + ) + } + DaemonError::CoinParseErr(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "CoinParseErr", + &__self_0, + ) + } + DaemonError::TxResultError(__self_0, __self_1, __self_2) => { + ::core::fmt::Formatter::debug_tuple_field3_finish( + f, + "TxResultError", + __self_0, + __self_1, + &__self_2, + ) + } + DaemonError::GasPriceError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "GasPriceError", + &__self_0, + ) + } + DaemonError::TendermintValidatorSet(__self_0, __self_1) => { + ::core::fmt::Formatter::debug_tuple_field2_finish( + f, + "TendermintValidatorSet", + __self_0, + &__self_1, + ) + } + DaemonError::TXNotFound(__self_0, __self_1) => { + ::core::fmt::Formatter::debug_tuple_field2_finish( + f, + "TXNotFound", + __self_0, + &__self_1, + ) + } + DaemonError::Unknown => ::core::fmt::Formatter::write_str(f, "Unknown"), + DaemonError::StdErr(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "StdErr", + &__self_0, + ) + } + DaemonError::NotImplemented => { + ::core::fmt::Formatter::write_str(f, "NotImplemented") + } + DaemonError::NewChain(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "NewChain", + &__self_0, + ) + } + DaemonError::NewNetwork(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "NewNetwork", + &__self_0, + ) + } + DaemonError::CannotConnectGRPC => { + ::core::fmt::Formatter::write_str(f, "CannotConnectGRPC") + } + DaemonError::TxFailed { code: __self_0, reason: __self_1 } => { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "TxFailed", + "code", + __self_0, + "reason", + &__self_1, + ) + } + DaemonError::GRPCListIsEmpty => { + ::core::fmt::Formatter::write_str(f, "GRPCListIsEmpty") + } + DaemonError::MissingWasmPath => { + ::core::fmt::Formatter::write_str(f, "MissingWasmPath") + } + DaemonError::BuilderMissing(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "BuilderMissing", + &__self_0, + ) + } + DaemonError::IbcError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "IbcError", + &__self_0, + ) + } + DaemonError::InsufficientFee(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "InsufficientFee", + &__self_0, + ) + } + } + } + } + impl DaemonError { + pub fn ibc_err(msg: impl ToString) -> Self { + Self::IbcError(msg.to_string()) + } + } + impl From for CwEnvError { + fn from(val: DaemonError) -> Self { + CwEnvError::AnyError(val.into()) + } + } +} +pub(crate) mod json_file { + use serde_json::{from_reader, json, Value}; + use std::fs::{File, OpenOptions}; + pub fn write( + filename: &String, + chain_id: &String, + network_id: &String, + deploy_id: &String, + ) { + let file = OpenOptions::new() + .create(true) + .read(true) + .write(true) + .truncate(false) + .open(filename) + .unwrap(); + let mut json: Value = if file.metadata().unwrap().len().eq(&0) { + ::serde_json::Value::Object(::serde_json::Map::new()) + } else { + from_reader(file).unwrap() + }; + if json.get(network_id).is_none() { + json[network_id] = ::serde_json::Value::Object(::serde_json::Map::new()); + } + if json[network_id].get(chain_id).is_none() { + json[network_id][chain_id] = ::serde_json::Value::Object({ + let mut object = ::serde_json::Map::new(); + let _ = object + .insert( + (deploy_id).into(), + ::serde_json::Value::Object(::serde_json::Map::new()), + ); + let _ = object + .insert( + ("code_ids").into(), + ::serde_json::Value::Object(::serde_json::Map::new()), + ); + object + }); + } + serde_json::to_writer_pretty(File::create(filename).unwrap(), &json).unwrap(); + } + pub fn read(filename: &String) -> Value { + let file = File::open(filename) + .unwrap_or_else(|_| { + ::core::panicking::panic_fmt( + format_args!("File should be present at {0}", filename), + ); + }); + let json: serde_json::Value = from_reader(file).unwrap(); + json + } +} +/// Proto types for different blockchains +pub mod proto { + pub mod injective { + #![allow(missing_docs)] + use crate::DaemonError; + use cosmrs::tx::SignDoc; + use cosmrs::{proto::traits::TypeUrl, tx::Raw}; + pub const ETHEREUM_COIN_TYPE: u32 = 60; + pub struct InjectiveEthAccount { + #[prost(message, optional, tag = "1")] + pub base_account: ::core::option::Option< + super::super::cosmos_modules::auth::BaseAccount, + >, + #[prost(bytes, tag = "2")] + pub code_hash: Vec, + } + #[automatically_derived] + impl ::core::clone::Clone for InjectiveEthAccount { + #[inline] + fn clone(&self) -> InjectiveEthAccount { + InjectiveEthAccount { + base_account: ::core::clone::Clone::clone(&self.base_account), + code_hash: ::core::clone::Clone::clone(&self.code_hash), + } + } + } + #[automatically_derived] + impl ::core::marker::StructuralPartialEq for InjectiveEthAccount {} + #[automatically_derived] + impl ::core::cmp::PartialEq for InjectiveEthAccount { + #[inline] + fn eq(&self, other: &InjectiveEthAccount) -> bool { + self.base_account == other.base_account + && self.code_hash == other.code_hash + } + } + impl ::prost::Message for InjectiveEthAccount { + #[allow(unused_variables)] + fn encode_raw(&self, buf: &mut B) + where + B: ::prost::bytes::BufMut, + { + if let Some(ref msg) = self.base_account { + ::prost::encoding::message::encode(1u32, msg, buf); + } + if self.code_hash != b"" as &[u8] { + ::prost::encoding::bytes::encode(2u32, &self.code_hash, buf); + } + } + #[allow(unused_variables)] + fn merge_field( + &mut self, + tag: u32, + wire_type: ::prost::encoding::WireType, + buf: &mut B, + ctx: ::prost::encoding::DecodeContext, + ) -> ::core::result::Result<(), ::prost::DecodeError> + where + B: ::prost::bytes::Buf, + { + const STRUCT_NAME: &'static str = "InjectiveEthAccount"; + match tag { + 1u32 => { + let mut value = &mut self.base_account; + ::prost::encoding::message::merge( + wire_type, + value.get_or_insert_with(::core::default::Default::default), + buf, + ctx, + ) + .map_err(|mut error| { + error.push(STRUCT_NAME, "base_account"); + error + }) + } + 2u32 => { + let mut value = &mut self.code_hash; + ::prost::encoding::bytes::merge(wire_type, value, buf, ctx) + .map_err(|mut error| { + error.push(STRUCT_NAME, "code_hash"); + error + }) + } + _ => ::prost::encoding::skip_field(wire_type, tag, buf, ctx), + } + } + #[inline] + fn encoded_len(&self) -> usize { + 0 + + self + .base_account + .as_ref() + .map_or( + 0, + |msg| ::prost::encoding::message::encoded_len(1u32, msg), + ) + + if self.code_hash != b"" as &[u8] { + ::prost::encoding::bytes::encoded_len(2u32, &self.code_hash) + } else { + 0 + } + } + fn clear(&mut self) { + self.base_account = ::core::option::Option::None; + self.code_hash.clear(); + } + } + impl ::core::default::Default for InjectiveEthAccount { + fn default() -> Self { + InjectiveEthAccount { + base_account: ::core::default::Default::default(), + code_hash: ::core::default::Default::default(), + } + } + } + impl ::core::fmt::Debug for InjectiveEthAccount { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + let mut builder = f.debug_struct("InjectiveEthAccount"); + let builder = { + let wrapper = &self.base_account; + builder.field("base_account", &wrapper) + }; + let builder = { + let wrapper = { + fn ScalarWrapper(v: T) -> T { + v + } + ScalarWrapper(&self.code_hash) + }; + builder.field("code_hash", &wrapper) + }; + builder.finish() + } + } + pub struct InjectivePubKey { + #[prost(bytes, tag = 1)] + pub key: Vec, + } + #[automatically_derived] + impl ::core::clone::Clone for InjectivePubKey { + #[inline] + fn clone(&self) -> InjectivePubKey { + InjectivePubKey { + key: ::core::clone::Clone::clone(&self.key), + } + } + } + #[automatically_derived] + impl ::core::marker::StructuralPartialEq for InjectivePubKey {} + #[automatically_derived] + impl ::core::cmp::PartialEq for InjectivePubKey { + #[inline] + fn eq(&self, other: &InjectivePubKey) -> bool { + self.key == other.key + } + } + impl ::prost::Message for InjectivePubKey { + #[allow(unused_variables)] + fn encode_raw(&self, buf: &mut B) + where + B: ::prost::bytes::BufMut, + { + if self.key != b"" as &[u8] { + ::prost::encoding::bytes::encode(1u32, &self.key, buf); + } + } + #[allow(unused_variables)] + fn merge_field( + &mut self, + tag: u32, + wire_type: ::prost::encoding::WireType, + buf: &mut B, + ctx: ::prost::encoding::DecodeContext, + ) -> ::core::result::Result<(), ::prost::DecodeError> + where + B: ::prost::bytes::Buf, + { + const STRUCT_NAME: &'static str = "InjectivePubKey"; + match tag { + 1u32 => { + let mut value = &mut self.key; + ::prost::encoding::bytes::merge(wire_type, value, buf, ctx) + .map_err(|mut error| { + error.push(STRUCT_NAME, "key"); + error + }) + } + _ => ::prost::encoding::skip_field(wire_type, tag, buf, ctx), + } + } + #[inline] + fn encoded_len(&self) -> usize { + 0 + + if self.key != b"" as &[u8] { + ::prost::encoding::bytes::encoded_len(1u32, &self.key) + } else { + 0 + } + } + fn clear(&mut self) { + self.key.clear(); + } + } + impl ::core::default::Default for InjectivePubKey { + fn default() -> Self { + InjectivePubKey { + key: ::core::default::Default::default(), + } + } + } + impl ::core::fmt::Debug for InjectivePubKey { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + let mut builder = f.debug_struct("InjectivePubKey"); + let builder = { + let wrapper = { + fn ScalarWrapper(v: T) -> T { + v + } + ScalarWrapper(&self.key) + }; + builder.field("key", &wrapper) + }; + builder.finish() + } + } + impl TypeUrl for InjectivePubKey { + const TYPE_URL: &'static str = "/injective.crypto.v1beta1.ethsecp256k1.PubKey"; + } + pub trait InjectiveSigner { + fn sign_injective(&self, sign_doc: SignDoc) -> Result; + } + } +} +pub mod sender { + use crate::{networks::ChainKind, proto::injective::ETHEREUM_COIN_TYPE}; + use super::{ + cosmos_modules::{self, auth::BaseAccount}, + error::DaemonError, queriers::{DaemonQuerier, Node}, + state::DaemonState, tx_builder::TxBuilder, tx_resp::CosmTxResponse, + }; + use crate::proto::injective::InjectiveEthAccount; + use crate::{core::parse_cw_coins, keys::private::PrivateKey}; + use cosmrs::{ + bank::MsgSend, crypto::secp256k1::SigningKey, proto::traits::Message, + tendermint::chain::Id, + tx::{self, ModeInfo, Msg, Raw, SignDoc, SignMode, SignerInfo}, + AccountId, + }; + use cosmwasm_std::Addr; + use secp256k1::{All, Context, Secp256k1, Signing}; + use std::{convert::TryFrom, env, rc::Rc, str::FromStr}; + use cosmos_modules::vesting::PeriodicVestingAccount; + use tonic::transport::Channel; + /// A wallet is a sender of transactions, can be safely cloned and shared within the same thread. + pub type Wallet = Rc>; + /// Signer of the transactions and helper for address derivation + /// This is the main interface for simulating and signing transactions + pub struct Sender { + pub private_key: PrivateKey, + pub secp: Secp256k1, + pub(crate) daemon_state: Rc, + } + impl Sender { + pub fn new(daemon_state: &Rc) -> Result, DaemonError> { + let kind = ChainKind::from(daemon_state.chain_data.network_type.clone()); + let mnemonic = env::var(kind.mnemonic_name()) + .unwrap_or_else(|_| { + { + ::core::panicking::panic_fmt( + format_args!( + "Wallet mnemonic environment variable {0} not set.", + kind.mnemonic_name(), + ), + ); + } + }); + Self::from_mnemonic(daemon_state, &mnemonic) + } + /// Construct a new Sender from a mnemonic + pub fn from_mnemonic( + daemon_state: &Rc, + mnemonic: &str, + ) -> Result, DaemonError> { + let secp = Secp256k1::new(); + let p_key: PrivateKey = PrivateKey::from_words( + &secp, + mnemonic, + 0, + 0, + daemon_state.chain_data.slip44, + )?; + let sender = Sender { + daemon_state: daemon_state.clone(), + private_key: p_key, + secp, + }; + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Interacting with {0} using address: {1}", + daemon_state.chain_data.chain_id, + sender.pub_addr_str()?, + ), + lvl, + &( + "cw_orch_daemon::sender", + "cw_orch_daemon::sender", + "cw-orch-daemon/src/sender.rs", + ), + 71u32, + ::log::__private_api::Option::None, + ); + } + }; + Ok(sender) + } + fn cosmos_private_key(&self) -> SigningKey { + SigningKey::from_slice(&self.private_key.raw_key()).unwrap() + } + pub fn channel(&self) -> Channel { + self.daemon_state.grpc_channel.clone() + } + pub fn pub_addr(&self) -> Result { + Ok( + AccountId::new( + &self.daemon_state.chain_data.bech32_prefix, + &self.private_key.public_key(&self.secp).raw_address.unwrap(), + )?, + ) + } + pub fn address(&self) -> Result { + Ok(Addr::unchecked(self.pub_addr_str()?)) + } + pub fn pub_addr_str(&self) -> Result { + Ok(self.pub_addr()?.to_string()) + } + pub async fn bank_send( + &self, + recipient: &str, + coins: Vec, + ) -> Result { + let msg_send = MsgSend { + from_address: self.pub_addr()?, + to_address: AccountId::from_str(recipient)?, + amount: parse_cw_coins(&coins)?, + }; + self.commit_tx( + <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([msg_send])), + Some("sending tokens"), + ) + .await + } + pub async fn calculate_gas( + &self, + tx_body: &tx::Body, + sequence: u64, + account_number: u64, + ) -> Result { + let fee = TxBuilder::build_fee( + 0u8, + &self.daemon_state.chain_data.fees.fee_tokens[0].denom, + 0, + ); + let auth_info = SignerInfo { + public_key: self.private_key.get_signer_public_key(&self.secp), + mode_info: ModeInfo::single(SignMode::Direct), + sequence, + } + .auth_info(fee); + let sign_doc = SignDoc::new( + tx_body, + &auth_info, + &Id::try_from(self.daemon_state.chain_data.chain_id.to_string())?, + account_number, + )?; + let tx_raw = self.sign(sign_doc)?; + Node::new(self.channel()).simulate_tx(tx_raw.to_bytes()?).await + } + pub async fn commit_tx( + &self, + msgs: Vec, + memo: Option<&str>, + ) -> Result { + let timeout_height = Node::new(self.channel()).block_height().await? + 10u64; + let tx_body = TxBuilder::build_body(msgs, memo, timeout_height); + let mut tx_builder = TxBuilder::new(tx_body); + let tx = tx_builder.build(self).await?; + let mut tx_response = self.broadcast_tx(tx).await?; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("tx broadcast response: {0:?}", tx_response), + lvl, + &( + "cw_orch_daemon::sender", + "cw_orch_daemon::sender", + "cw-orch-daemon/src/sender.rs", + ), + 166u32, + ::log::__private_api::Option::None, + ); + } + }; + if has_insufficient_fee(&tx_response.raw_log) { + let suggested_fee = parse_suggested_fee(&tx_response.raw_log); + let Some(new_fee) = suggested_fee else { + return Err(DaemonError::InsufficientFee(tx_response.raw_log)); + }; + tx_builder.fee_amount(new_fee); + let tx = tx_builder.build(self).await?; + tx_response = self.broadcast_tx(tx).await?; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("tx broadcast response: {0:?}", tx_response), + lvl, + &( + "cw_orch_daemon::sender", + "cw_orch_daemon::sender", + "cw-orch-daemon/src/sender.rs", + ), + 181u32, + ::log::__private_api::Option::None, + ); + } + }; + } + let resp = Node::new(self.channel()).find_tx(tx_response.txhash).await?; + if resp.code == 0 { + Ok(resp) + } else { + Err(DaemonError::TxFailed { + code: resp.code, + reason: resp.raw_log, + }) + } + } + pub fn sign(&self, sign_doc: SignDoc) -> Result { + let tx_raw = if self.private_key.coin_type == ETHEREUM_COIN_TYPE { + { + ::core::panicking::panic_fmt( + format_args!( + "Coin Type {0} not supported without eth feature", + ETHEREUM_COIN_TYPE, + ), + ); + }; + } else { + sign_doc.sign(&self.cosmos_private_key())? + }; + Ok(tx_raw) + } + pub async fn base_account(&self) -> Result { + let addr = self.pub_addr().unwrap().to_string(); + let mut client = cosmos_modules::auth::query_client::QueryClient::new( + self.channel(), + ); + let resp = client + .account(cosmos_modules::auth::QueryAccountRequest { + address: addr, + }) + .await? + .into_inner(); + let account = resp.account.unwrap().value; + let acc = if let Ok(acc) = BaseAccount::decode(account.as_ref()) { + acc + } else if let Ok(acc) = PeriodicVestingAccount::decode(account.as_ref()) { + acc.base_vesting_account.unwrap().base_account.unwrap() + } else if let Ok(acc) = InjectiveEthAccount::decode(account.as_ref()) { + acc.base_account.unwrap() + } else { + return Err( + DaemonError::StdErr( + "Unknown account type returned from QueryAccountRequest".into(), + ), + ); + }; + Ok(acc) + } + async fn broadcast_tx( + &self, + tx: Raw, + ) -> Result< + cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse, + DaemonError, + > { + let mut client = cosmos_modules::tx::service_client::ServiceClient::new( + self.channel(), + ); + let commit = client + .broadcast_tx(cosmos_modules::tx::BroadcastTxRequest { + tx_bytes: tx.to_bytes()?, + mode: cosmos_modules::tx::BroadcastMode::Sync.into(), + }) + .await?; + let commit = commit.into_inner().tx_response.unwrap(); + Ok(commit) + } + } + fn has_insufficient_fee(raw_log: &str) -> bool { + raw_log.contains("insufficient fees") + } + fn parse_suggested_fee(raw_log: &str) -> Option { + let parts: Vec<&str> = raw_log.split("required: ").collect(); + if parts.len() != 2 { + return None; + } + let got_parts: Vec<&str> = parts[0].split_whitespace().collect(); + let paid_fee_with_denom = got_parts.last()?; + let (_, denomination) = paid_fee_with_denom + .split_at(paid_fee_with_denom.find(|c: char| !c.is_numeric())?); + { + ::std::io::_eprint(format_args!("denom: {0}\n", denomination)); + }; + let required_fees: Vec<&str> = parts[1].split(denomination).collect(); + { + ::std::io::_eprint(format_args!("required fees: {0:?}\n", required_fees)); + }; + let (_, suggested_fee) = required_fees[0] + .split_at(required_fees[0].rfind(|c: char| !c.is_numeric())?); + { + ::std::io::_eprint(format_args!("suggested fee: {0}\n", suggested_fee)); + }; + suggested_fee.parse::().ok().or(suggested_fee[1..].parse::().ok()) + } +} +pub mod state { + use super::error::DaemonError; + use crate::{channel::GrpcChannel, networks::ChainKind}; + use cosmwasm_std::Addr; + use cw_orch_core::{ + environment::{DeployDetails, StateInterface}, + CwEnvError, + }; + use ibc_chain_registry::chain::ChainData; + use serde::Serialize; + use serde_json::{json, Value}; + use std::{collections::HashMap, env, fs::File, path::Path}; + use tonic::transport::Channel; + /// Stores the chain information and deployment state. + /// Uses a simple JSON file to store the deployment information locally. + pub struct DaemonState { + /// this is passed via env var STATE_FILE + pub json_file_path: String, + /// Deployment identifier + pub deployment_id: String, + /// gRPC channel + pub grpc_channel: Channel, + /// Information about the chain + pub chain_data: ChainData, + } + #[automatically_derived] + impl ::core::clone::Clone for DaemonState { + #[inline] + fn clone(&self) -> DaemonState { + DaemonState { + json_file_path: ::core::clone::Clone::clone(&self.json_file_path), + deployment_id: ::core::clone::Clone::clone(&self.deployment_id), + grpc_channel: ::core::clone::Clone::clone(&self.grpc_channel), + chain_data: ::core::clone::Clone::clone(&self.chain_data), + } + } + } + #[automatically_derived] + impl ::core::fmt::Debug for DaemonState { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field4_finish( + f, + "DaemonState", + "json_file_path", + &self.json_file_path, + "deployment_id", + &self.deployment_id, + "grpc_channel", + &self.grpc_channel, + "chain_data", + &&self.chain_data, + ) + } + } + impl DaemonState { + /// Creates a new state from the given chain data and deployment id. + /// Attempts to connect to any of the provided gRPC endpoints. + pub async fn new( + mut chain_data: ChainData, + deployment_id: String, + ) -> Result { + if chain_data.apis.grpc.is_empty() { + return Err(DaemonError::GRPCListIsEmpty); + } + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Found {0} gRPC endpoints", + chain_data.apis.grpc.len(), + ), + lvl, + &( + "cw_orch_daemon::state", + "cw_orch_daemon::state", + "cw-orch-daemon/src/state.rs", + ), + 40u32, + ::log::__private_api::Option::None, + ); + } + }; + let grpc_channel = GrpcChannel::connect( + &chain_data.apis.grpc, + &chain_data.chain_id, + ) + .await?; + let mut json_file_path = env::var("STATE_FILE") + .unwrap_or("./state.json".to_string()); + if chain_data.network_type == ChainKind::Local.to_string() { + let name = Path::new(&json_file_path) + .file_stem() + .unwrap() + .to_str() + .unwrap(); + let folder = Path::new(&json_file_path) + .parent() + .unwrap() + .to_str() + .unwrap(); + json_file_path = { + let res = ::alloc::fmt::format( + format_args!("{0}/{1}_local.json", folder, name), + ); + res + }; + } + let shortest_denom_token = chain_data + .fees + .fee_tokens + .iter() + .fold( + chain_data.fees.fee_tokens[0].clone(), + |acc, item| { + if item.denom.len() < acc.denom.len() { + item.clone() + } else { + acc + } + }, + ); + chain_data + .fees + .fee_tokens = <[_]>::into_vec( + #[rustc_box] + ::alloc::boxed::Box::new([shortest_denom_token]), + ); + let state = DaemonState { + json_file_path, + deployment_id, + grpc_channel, + chain_data, + }; + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Writing daemon state JSON file: {0:#?}", + state.json_file_path, + ), + lvl, + &( + "cw_orch_daemon::state", + "cw_orch_daemon::state", + "cw-orch-daemon/src/state.rs", + ), + 87u32, + ::log::__private_api::Option::None, + ); + } + }; + crate::json_file::write( + &state.json_file_path, + &state.chain_data.chain_id.to_string(), + &state.chain_data.chain_name, + &state.deployment_id, + ); + Ok(state) + } + /// Get the state filepath and read it as json + fn read_state(&self) -> serde_json::Value { + crate::json_file::read(&self.json_file_path) + } + /// Retrieve a stateful value using the chainId and networkId + pub fn get(&self, key: &str) -> Value { + let json = self.read_state(); + json[&self.chain_data.chain_name][&self.chain_data.chain_id.to_string()][key] + .clone() + } + /// Set a stateful value using the chainId and networkId + pub fn set(&self, key: &str, contract_id: &str, value: T) { + let mut json = self.read_state(); + json[&self + .chain_data + .chain_name][&self + .chain_data + .chain_id + .to_string()][key][contract_id] = ::serde_json::to_value(&value) + .unwrap(); + serde_json::to_writer_pretty( + File::create(&self.json_file_path).unwrap(), + &json, + ) + .unwrap(); + } + } + impl StateInterface for DaemonState { + /// Read address for contract in deployment id from state file + fn get_address(&self, contract_id: &str) -> Result { + let value = self + .get(&self.deployment_id) + .get(contract_id) + .ok_or_else(|| CwEnvError::AddrNotInStore(contract_id.to_owned()))? + .clone(); + Ok(Addr::unchecked(value.as_str().unwrap())) + } + /// Set address for contract in deployment id in state file + fn set_address(&mut self, contract_id: &str, address: &Addr) { + self.set(&self.deployment_id, contract_id, address.as_str()); + } + /// Get the locally-saved version of the contract's version on this network + fn get_code_id(&self, contract_id: &str) -> Result { + let value = self + .get("code_ids") + .get(contract_id) + .ok_or_else(|| CwEnvError::CodeIdNotInStore(contract_id.to_owned()))? + .clone(); + Ok(value.as_u64().unwrap()) + } + /// Set the locally-saved version of the contract's latest version on this network + fn set_code_id(&mut self, contract_id: &str, code_id: u64) { + self.set("code_ids", contract_id, code_id); + } + /// Get all addresses for deployment id from state file + fn get_all_addresses(&self) -> Result, CwEnvError> { + let mut store = HashMap::new(); + let addresses = self.get(&self.deployment_id); + let value = addresses.as_object().unwrap(); + for (id, addr) in value { + store.insert(id.clone(), Addr::unchecked(addr.as_str().unwrap())); + } + Ok(store) + } + fn get_all_code_ids(&self) -> Result, CwEnvError> { + let mut store = HashMap::new(); + let code_ids = self.get("code_ids"); + let value = code_ids.as_object().unwrap(); + for (id, code_id) in value { + store.insert(id.clone(), code_id.as_u64().unwrap()); + } + Ok(store) + } + fn deploy_details(&self) -> DeployDetails { + DeployDetails { + chain_id: self.chain_data.chain_id.to_string(), + chain_name: self.chain_data.chain_name.clone(), + deployment_id: self.deployment_id.clone(), + } + } + } +} +pub mod sync { + mod builder { + use ibc_chain_registry::chain::ChainData; + use crate::DaemonAsyncBuilder; + use super::{super::error::DaemonError, core::Daemon}; + /// Create [`Daemon`] through [`DaemonBuilder`] + /// ## Example + /// ```no_run + /// use cw_orch_daemon::{networks, DaemonBuilder}; + /// + /// let Daemon = DaemonBuilder::default() + /// .chain(networks::LOCAL_JUNO) + /// .deployment_id("v0.1.0") + /// .build() + /// .unwrap(); + /// ``` + pub struct DaemonBuilder { + pub(crate) chain: Option, + pub(crate) handle: Option, + pub(crate) deployment_id: Option, + /// Wallet mnemonic + pub(crate) mnemonic: Option, + } + #[automatically_derived] + impl ::core::clone::Clone for DaemonBuilder { + #[inline] + fn clone(&self) -> DaemonBuilder { + DaemonBuilder { + chain: ::core::clone::Clone::clone(&self.chain), + handle: ::core::clone::Clone::clone(&self.handle), + deployment_id: ::core::clone::Clone::clone(&self.deployment_id), + mnemonic: ::core::clone::Clone::clone(&self.mnemonic), + } + } + } + #[automatically_derived] + impl ::core::default::Default for DaemonBuilder { + #[inline] + fn default() -> DaemonBuilder { + DaemonBuilder { + chain: ::core::default::Default::default(), + handle: ::core::default::Default::default(), + deployment_id: ::core::default::Default::default(), + mnemonic: ::core::default::Default::default(), + } + } + } + impl DaemonBuilder { + /// Set the chain the Daemon will connect to + pub fn chain(&mut self, chain: impl Into) -> &mut Self { + self.chain = Some(chain.into()); + self + } + /// Set the deployment id to use for the Daemon interactions + /// Defaults to `default` + pub fn deployment_id( + &mut self, + deployment_id: impl Into, + ) -> &mut Self { + self.deployment_id = Some(deployment_id.into()); + self + } + /// Set the tokio runtime handle to use for the Daemon + /// + /// ## Example + /// ```no_run + /// use cw_orch_daemon::Daemon; + /// use tokio::runtime::Runtime; + /// let rt = Runtime::new().unwrap(); + /// let Daemon = Daemon::builder() + /// .handle(rt.handle()) + /// // ... + /// .build() + /// .unwrap(); + /// ``` + pub fn handle(&mut self, handle: &tokio::runtime::Handle) -> &mut Self { + self.handle = Some(handle.clone()); + self + } + /// Set the mnemonic to use with this chain. + pub fn mnemonic(&mut self, mnemonic: impl ToString) -> &mut Self { + self.mnemonic = Some(mnemonic.to_string()); + self + } + /// Build a Daemon + pub fn build(&self) -> Result { + let rt_handle = self + .handle + .clone() + .ok_or(DaemonError::BuilderMissing("runtime handle".into()))?; + let daemon = rt_handle + .block_on(DaemonAsyncBuilder::from(self.clone()).build())?; + Ok(Daemon { rt_handle, daemon }) + } + } + } + mod core { + use std::{fmt::Debug, rc::Rc, time::Duration}; + use super::super::{sender::Wallet, DaemonAsync}; + use crate::{ + queriers::{DaemonQuerier, Node}, + CosmTxResponse, DaemonBuilder, DaemonError, DaemonState, + }; + use cosmrs::tendermint::Time; + use cosmwasm_std::{Addr, Coin}; + use cw_orch_core::{ + contract::{interface_traits::Uploadable, WasmPath}, + environment::{ChainState, TxHandler}, + }; + use serde::{de::DeserializeOwned, Serialize}; + use tokio::runtime::Handle; + use tonic::transport::Channel; + /** + Represents a blockchain node. + Is constructed with the [DaemonBuilder]. + + ## Usage + + ```rust,no_run + use cw_orch_daemon::{Daemon, networks}; + use tokio::runtime::Runtime; + + let rt = Runtime::new().unwrap(); + let daemon: Daemon = Daemon::builder() + .chain(networks::JUNO_1) + .handle(rt.handle()) + .build() + .unwrap(); + ``` + ## Environment Execution + + The Daemon implements [`TxHandler`] which allows you to perform transactions on the chain. + + ## Querying + + Different Cosmos SDK modules can be queried through the daemon by calling the [`Daemon.query_client`] method with a specific querier. + See [Querier](crate::queriers) for examples. +*/ + pub struct Daemon { + pub daemon: DaemonAsync, + /// Runtime handle to execute async tasks + pub rt_handle: Handle, + } + #[automatically_derived] + impl ::core::clone::Clone for Daemon { + #[inline] + fn clone(&self) -> Daemon { + Daemon { + daemon: ::core::clone::Clone::clone(&self.daemon), + rt_handle: ::core::clone::Clone::clone(&self.rt_handle), + } + } + } + impl Daemon { + /// Get the daemon builder + pub fn builder() -> DaemonBuilder { + DaemonBuilder::default() + } + /// Perform a query with a given querier + /// See [Querier](crate::queriers) for examples. + pub fn query_client(&self) -> Querier { + self.daemon.query_client() + } + /// Get the channel configured for this Daemon + pub fn channel(&self) -> Channel { + self.daemon.state.grpc_channel.clone() + } + /// Get the channel configured for this Daemon + pub fn wallet(&self) -> Wallet { + self.daemon.sender.clone() + } + } + impl ChainState for Daemon { + type Out = Rc; + fn state(&self) -> Self::Out { + self.daemon.state.clone() + } + } + impl TxHandler for Daemon { + type Response = CosmTxResponse; + type Error = DaemonError; + type ContractSource = WasmPath; + type Sender = Wallet; + fn sender(&self) -> Addr { + self.daemon.sender.address().unwrap() + } + fn set_sender(&mut self, sender: Self::Sender) { + self.daemon.sender = sender; + } + fn upload( + &self, + uploadable: &impl Uploadable, + ) -> Result { + self.rt_handle.block_on(self.daemon.upload(uploadable)) + } + fn execute( + &self, + exec_msg: &E, + coins: &[cosmwasm_std::Coin], + contract_address: &Addr, + ) -> Result { + self.rt_handle + .block_on(self.daemon.execute(exec_msg, coins, contract_address)) + } + fn instantiate( + &self, + code_id: u64, + init_msg: &I, + label: Option<&str>, + admin: Option<&Addr>, + coins: &[Coin], + ) -> Result { + self.rt_handle + .block_on( + self.daemon.instantiate(code_id, init_msg, label, admin, coins), + ) + } + fn query( + &self, + query_msg: &Q, + contract_address: &Addr, + ) -> Result { + self.rt_handle.block_on(self.daemon.query(query_msg, contract_address)) + } + fn migrate( + &self, + migrate_msg: &M, + new_code_id: u64, + contract_address: &Addr, + ) -> Result { + self.rt_handle + .block_on( + self.daemon.migrate(migrate_msg, new_code_id, contract_address), + ) + } + fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { + let mut last_height = self + .rt_handle + .block_on(self.query_client::().block_height())?; + let end_height = last_height + amount; + while last_height < end_height { + self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(4))); + last_height = self + .rt_handle + .block_on(self.query_client::().block_height())?; + } + Ok(()) + } + fn wait_seconds(&self, secs: u64) -> Result<(), DaemonError> { + self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(secs))); + Ok(()) + } + fn next_block(&self) -> Result<(), DaemonError> { + let mut last_height = self + .rt_handle + .block_on(self.query_client::().block_height())?; + let end_height = last_height + 1; + while last_height < end_height { + self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(4))); + last_height = self + .rt_handle + .block_on(self.query_client::().block_height())?; + } + Ok(()) + } + fn block_info(&self) -> Result { + let block = self + .rt_handle + .block_on(self.query_client::().latest_block())?; + let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; + let time = cosmwasm_std::Timestamp::from_nanos( + since_epoch.as_nanos() as u64, + ); + Ok(cosmwasm_std::BlockInfo { + height: block.header.height.value(), + time, + chain_id: block.header.chain_id.to_string(), + }) + } + } + } + pub use self::{builder::*, core::*}; +} +pub mod tx_resp { + use super::{ + cosmos_modules::{ + abci::{AbciMessageLog, Attribute, StringEvent, TxResponse}, + tendermint_abci::Event, + }, + error::DaemonError, + }; + use chrono::{DateTime, NaiveDateTime, TimeZone, Utc}; + use cosmwasm_std::{to_binary, Binary, StdError, StdResult}; + use cw_orch_core::environment::IndexResponse; + use serde::{Deserialize, Serialize}; + const FORMAT: &str = "%Y-%m-%dT%H:%M:%S%.f"; + const FORMAT_TZ_SUPPLIED: &str = "%Y-%m-%dT%H:%M:%S.%f%:z"; + const FORMAT_SHORT_Z: &str = "%Y-%m-%dT%H:%M:%SZ"; + const FORMAT_SHORT_Z2: &str = "%Y-%m-%dT%H:%M:%S.%fZ"; + /// The response from a transaction performed on a blockchain. + pub struct CosmTxResponse { + /// Height of the block in which the transaction was included. + pub height: u64, + /// Transaction hash. + pub txhash: String, + /// Transaction index within the block. + pub codespace: String, + /// Transaction result code + pub code: usize, + /// Arbitrary data that can be included in a transaction. + pub data: String, + /// Raw log message. + pub raw_log: String, + /// Logs of the transaction. + pub logs: Vec, + /// Transaction info. + pub info: String, + /// Gas limit. + pub gas_wanted: u64, + /// Gas used. + pub gas_used: u64, + /// Timestamp of the block in which the transaction was included. + pub timestamp: DateTime, + /// Transaction events. + pub events: Vec, + } + #[automatically_derived] + impl ::core::fmt::Debug for CosmTxResponse { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + let names: &'static _ = &[ + "height", + "txhash", + "codespace", + "code", + "data", + "raw_log", + "logs", + "info", + "gas_wanted", + "gas_used", + "timestamp", + "events", + ]; + let values: &[&dyn ::core::fmt::Debug] = &[ + &self.height, + &self.txhash, + &self.codespace, + &self.code, + &self.data, + &self.raw_log, + &self.logs, + &self.info, + &self.gas_wanted, + &self.gas_used, + &self.timestamp, + &&self.events, + ]; + ::core::fmt::Formatter::debug_struct_fields_finish( + f, + "CosmTxResponse", + names, + values, + ) + } + } + #[automatically_derived] + impl ::core::default::Default for CosmTxResponse { + #[inline] + fn default() -> CosmTxResponse { + CosmTxResponse { + height: ::core::default::Default::default(), + txhash: ::core::default::Default::default(), + codespace: ::core::default::Default::default(), + code: ::core::default::Default::default(), + data: ::core::default::Default::default(), + raw_log: ::core::default::Default::default(), + logs: ::core::default::Default::default(), + info: ::core::default::Default::default(), + gas_wanted: ::core::default::Default::default(), + gas_used: ::core::default::Default::default(), + timestamp: ::core::default::Default::default(), + events: ::core::default::Default::default(), + } + } + } + impl CosmTxResponse { + /// find a attribute's value from TX logs. + /// returns: msg_index and value + pub fn get_attribute_from_logs( + &self, + event_type: &str, + attribute_key: &str, + ) -> Vec<(usize, String)> { + let mut response: Vec<(usize, String)> = Default::default(); + let logs = &self.logs; + for log_part in logs { + let msg_index = log_part.msg_index.unwrap_or_default(); + let events = &log_part.events; + let events_filtered = events + .iter() + .filter(|event| event.s_type == event_type) + .collect::>(); + if let Some(event) = events_filtered.first() { + let attributes_filtered = event + .attributes + .iter() + .filter(|attr| attr.key == attribute_key) + .map(|f| f.value.clone()) + .collect::>(); + if let Some(attr_key) = attributes_filtered.first() { + response.push((msg_index, attr_key.clone())); + } + } + } + response + } + /// get the list of event types from a TX record + pub fn get_events(&self, event_type: &str) -> Vec { + let mut response: Vec = Default::default(); + for log_part in &self.logs { + let events = &log_part.events; + let events_filtered = events + .iter() + .filter(|event| event.s_type == event_type) + .collect::>(); + for event in events_filtered { + response.push(event.clone()); + } + } + response + } + } + impl From<&serde_json::Value> for TxResultBlockMsg { + fn from(value: &serde_json::Value) -> Self { + serde_json::from_value(value.clone()).unwrap() + } + } + impl From for CosmTxResponse { + fn from(tx: TxResponse) -> Self { + Self { + height: tx.height as u64, + txhash: tx.txhash, + codespace: tx.codespace, + code: tx.code as usize, + data: tx.data, + raw_log: tx.raw_log, + logs: tx.logs.into_iter().map(TxResultBlockMsg::from).collect(), + info: tx.info, + gas_wanted: tx.gas_wanted as u64, + gas_used: tx.gas_used as u64, + timestamp: parse_timestamp(tx.timestamp).unwrap(), + events: tx.events, + } + } + } + impl IndexResponse for CosmTxResponse { + fn events(&self) -> Vec { + let mut parsed_events = ::alloc::vec::Vec::new(); + for event in &self.events { + let mut pattr = ::alloc::vec::Vec::new(); + for attr in &event.attributes { + pattr + .push(cosmwasm_std::Attribute { + key: attr.key.clone(), + value: attr.value.clone(), + }) + } + let pevent = cosmwasm_std::Event::new(event.r#type.clone()) + .add_attributes(pattr); + parsed_events.push(pevent); + } + parsed_events + } + fn data(&self) -> Option { + if self.data.is_empty() { + None + } else { + Some(to_binary(self.data.as_bytes()).unwrap()) + } + } + fn event_attr_value( + &self, + event_type: &str, + attr_key: &str, + ) -> StdResult { + for event in &self.events { + if event.r#type == event_type { + for attr in &event.attributes { + if attr.key == attr_key { + return Ok(attr.value.clone()); + } + } + } + } + Err( + StdError::generic_err({ + let res = ::alloc::fmt::format( + format_args!( + "event of type {0} does not have a value at key {1}", + event_type, + attr_key, + ), + ); + res + }), + ) + } + } + /// The events from a single message in a transaction. + pub struct TxResultBlockMsg { + /// index of the message in the transaction + pub msg_index: Option, + /// Events from this message + pub events: Vec, + } + #[automatically_derived] + impl ::core::clone::Clone for TxResultBlockMsg { + #[inline] + fn clone(&self) -> TxResultBlockMsg { + TxResultBlockMsg { + msg_index: ::core::clone::Clone::clone(&self.msg_index), + events: ::core::clone::Clone::clone(&self.events), + } + } + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl _serde::Serialize for TxResultBlockMsg { + fn serialize<__S>( + &self, + __serializer: __S, + ) -> _serde::__private::Result<__S::Ok, __S::Error> + where + __S: _serde::Serializer, + { + let mut __serde_state = _serde::Serializer::serialize_struct( + __serializer, + "TxResultBlockMsg", + false as usize + 1 + 1, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "msg_index", + &self.msg_index, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "events", + &self.events, + )?; + _serde::ser::SerializeStruct::end(__serde_state) + } + } + }; + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl<'de> _serde::Deserialize<'de> for TxResultBlockMsg { + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + #[allow(non_camel_case_types)] + #[doc(hidden)] + enum __Field { + __field0, + __field1, + __ignore, + } + #[doc(hidden)] + struct __FieldVisitor; + impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { + type Value = __Field; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "field identifier", + ) + } + fn visit_u64<__E>( + self, + __value: u64, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + 0u64 => _serde::__private::Ok(__Field::__field0), + 1u64 => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_str<__E>( + self, + __value: &str, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + "msg_index" => _serde::__private::Ok(__Field::__field0), + "events" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_bytes<__E>( + self, + __value: &[u8], + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + b"msg_index" => _serde::__private::Ok(__Field::__field0), + b"events" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + } + impl<'de> _serde::Deserialize<'de> for __Field { + #[inline] + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + _serde::Deserializer::deserialize_identifier( + __deserializer, + __FieldVisitor, + ) + } + } + #[doc(hidden)] + struct __Visitor<'de> { + marker: _serde::__private::PhantomData, + lifetime: _serde::__private::PhantomData<&'de ()>, + } + impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { + type Value = TxResultBlockMsg; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "struct TxResultBlockMsg", + ) + } + #[inline] + fn visit_seq<__A>( + self, + mut __seq: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::SeqAccess<'de>, + { + let __field0 = match _serde::de::SeqAccess::next_element::< + Option, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 0usize, + &"struct TxResultBlockMsg with 2 elements", + ), + ); + } + }; + let __field1 = match _serde::de::SeqAccess::next_element::< + Vec, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 1usize, + &"struct TxResultBlockMsg with 2 elements", + ), + ); + } + }; + _serde::__private::Ok(TxResultBlockMsg { + msg_index: __field0, + events: __field1, + }) + } + #[inline] + fn visit_map<__A>( + self, + mut __map: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::MapAccess<'de>, + { + let mut __field0: _serde::__private::Option> = _serde::__private::None; + let mut __field1: _serde::__private::Option< + Vec, + > = _serde::__private::None; + while let _serde::__private::Some(__key) + = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { + match __key { + __Field::__field0 => { + if _serde::__private::Option::is_some(&__field0) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field( + "msg_index", + ), + ); + } + __field0 = _serde::__private::Some( + _serde::de::MapAccess::next_value::< + Option, + >(&mut __map)?, + ); + } + __Field::__field1 => { + if _serde::__private::Option::is_some(&__field1) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field("events"), + ); + } + __field1 = _serde::__private::Some( + _serde::de::MapAccess::next_value::< + Vec, + >(&mut __map)?, + ); + } + _ => { + let _ = _serde::de::MapAccess::next_value::< + _serde::de::IgnoredAny, + >(&mut __map)?; + } + } + } + let __field0 = match __field0 { + _serde::__private::Some(__field0) => __field0, + _serde::__private::None => { + _serde::__private::de::missing_field("msg_index")? + } + }; + let __field1 = match __field1 { + _serde::__private::Some(__field1) => __field1, + _serde::__private::None => { + _serde::__private::de::missing_field("events")? + } + }; + _serde::__private::Ok(TxResultBlockMsg { + msg_index: __field0, + events: __field1, + }) + } + } + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &["msg_index", "events"]; + _serde::Deserializer::deserialize_struct( + __deserializer, + "TxResultBlockMsg", + FIELDS, + __Visitor { + marker: _serde::__private::PhantomData::, + lifetime: _serde::__private::PhantomData, + }, + ) + } + } + }; + #[automatically_derived] + impl ::core::fmt::Debug for TxResultBlockMsg { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "TxResultBlockMsg", + "msg_index", + &self.msg_index, + "events", + &&self.events, + ) + } + } + impl From for TxResultBlockMsg { + fn from(msg: AbciMessageLog) -> Self { + Self { + msg_index: Some(msg.msg_index as usize), + events: msg.events.into_iter().map(TxResultBlockEvent::from).collect(), + } + } + } + /// A single event from a transaction and its attributes. + pub struct TxResultBlockEvent { + #[serde(rename = "type")] + /// Type of the event + pub s_type: String, + /// Attributes of the event + pub attributes: Vec, + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl<'de> _serde::Deserialize<'de> for TxResultBlockEvent { + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + #[allow(non_camel_case_types)] + #[doc(hidden)] + enum __Field { + __field0, + __field1, + __ignore, + } + #[doc(hidden)] + struct __FieldVisitor; + impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { + type Value = __Field; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "field identifier", + ) + } + fn visit_u64<__E>( + self, + __value: u64, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + 0u64 => _serde::__private::Ok(__Field::__field0), + 1u64 => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_str<__E>( + self, + __value: &str, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + "type" => _serde::__private::Ok(__Field::__field0), + "attributes" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_bytes<__E>( + self, + __value: &[u8], + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + b"type" => _serde::__private::Ok(__Field::__field0), + b"attributes" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + } + impl<'de> _serde::Deserialize<'de> for __Field { + #[inline] + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + _serde::Deserializer::deserialize_identifier( + __deserializer, + __FieldVisitor, + ) + } + } + #[doc(hidden)] + struct __Visitor<'de> { + marker: _serde::__private::PhantomData, + lifetime: _serde::__private::PhantomData<&'de ()>, + } + impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { + type Value = TxResultBlockEvent; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "struct TxResultBlockEvent", + ) + } + #[inline] + fn visit_seq<__A>( + self, + mut __seq: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::SeqAccess<'de>, + { + let __field0 = match _serde::de::SeqAccess::next_element::< + String, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 0usize, + &"struct TxResultBlockEvent with 2 elements", + ), + ); + } + }; + let __field1 = match _serde::de::SeqAccess::next_element::< + Vec, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 1usize, + &"struct TxResultBlockEvent with 2 elements", + ), + ); + } + }; + _serde::__private::Ok(TxResultBlockEvent { + s_type: __field0, + attributes: __field1, + }) + } + #[inline] + fn visit_map<__A>( + self, + mut __map: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::MapAccess<'de>, + { + let mut __field0: _serde::__private::Option = _serde::__private::None; + let mut __field1: _serde::__private::Option< + Vec, + > = _serde::__private::None; + while let _serde::__private::Some(__key) + = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { + match __key { + __Field::__field0 => { + if _serde::__private::Option::is_some(&__field0) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field("type"), + ); + } + __field0 = _serde::__private::Some( + _serde::de::MapAccess::next_value::(&mut __map)?, + ); + } + __Field::__field1 => { + if _serde::__private::Option::is_some(&__field1) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field( + "attributes", + ), + ); + } + __field1 = _serde::__private::Some( + _serde::de::MapAccess::next_value::< + Vec, + >(&mut __map)?, + ); + } + _ => { + let _ = _serde::de::MapAccess::next_value::< + _serde::de::IgnoredAny, + >(&mut __map)?; + } + } + } + let __field0 = match __field0 { + _serde::__private::Some(__field0) => __field0, + _serde::__private::None => { + _serde::__private::de::missing_field("type")? + } + }; + let __field1 = match __field1 { + _serde::__private::Some(__field1) => __field1, + _serde::__private::None => { + _serde::__private::de::missing_field("attributes")? + } + }; + _serde::__private::Ok(TxResultBlockEvent { + s_type: __field0, + attributes: __field1, + }) + } + } + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &["type", "attributes"]; + _serde::Deserializer::deserialize_struct( + __deserializer, + "TxResultBlockEvent", + FIELDS, + __Visitor { + marker: _serde::__private::PhantomData::, + lifetime: _serde::__private::PhantomData, + }, + ) + } + } + }; + #[automatically_derived] + impl ::core::clone::Clone for TxResultBlockEvent { + #[inline] + fn clone(&self) -> TxResultBlockEvent { + TxResultBlockEvent { + s_type: ::core::clone::Clone::clone(&self.s_type), + attributes: ::core::clone::Clone::clone(&self.attributes), + } + } + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl _serde::Serialize for TxResultBlockEvent { + fn serialize<__S>( + &self, + __serializer: __S, + ) -> _serde::__private::Result<__S::Ok, __S::Error> + where + __S: _serde::Serializer, + { + let mut __serde_state = _serde::Serializer::serialize_struct( + __serializer, + "TxResultBlockEvent", + false as usize + 1 + 1, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "type", + &self.s_type, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "attributes", + &self.attributes, + )?; + _serde::ser::SerializeStruct::end(__serde_state) + } + } + }; + #[automatically_derived] + impl ::core::fmt::Debug for TxResultBlockEvent { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "TxResultBlockEvent", + "s_type", + &self.s_type, + "attributes", + &&self.attributes, + ) + } + } + impl From for TxResultBlockEvent { + fn from(event: StringEvent) -> Self { + Self { + s_type: event.r#type, + attributes: event + .attributes + .into_iter() + .map(TxResultBlockAttribute::from) + .collect(), + } + } + } + impl TxResultBlockEvent { + /// get all key/values from the event that have the key 'key' + pub fn get_attributes(&self, key: &str) -> Vec { + self.attributes.iter().filter(|attr| attr.key == key).cloned().collect() + } + /// return the first value of the first attribute that has the key 'key' + pub fn get_first_attribute_value(&self, key: &str) -> Option { + self.get_attributes(key).first().map(|attr| attr.value.clone()) + } + } + /// A single attribute of an event. + pub struct TxResultBlockAttribute { + /// Key of the attribute + pub key: String, + /// Value of the attribute + pub value: String, + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl<'de> _serde::Deserialize<'de> for TxResultBlockAttribute { + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + #[allow(non_camel_case_types)] + #[doc(hidden)] + enum __Field { + __field0, + __field1, + __ignore, + } + #[doc(hidden)] + struct __FieldVisitor; + impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { + type Value = __Field; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "field identifier", + ) + } + fn visit_u64<__E>( + self, + __value: u64, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + 0u64 => _serde::__private::Ok(__Field::__field0), + 1u64 => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_str<__E>( + self, + __value: &str, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + "key" => _serde::__private::Ok(__Field::__field0), + "value" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_bytes<__E>( + self, + __value: &[u8], + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + b"key" => _serde::__private::Ok(__Field::__field0), + b"value" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + } + impl<'de> _serde::Deserialize<'de> for __Field { + #[inline] + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + _serde::Deserializer::deserialize_identifier( + __deserializer, + __FieldVisitor, + ) + } + } + #[doc(hidden)] + struct __Visitor<'de> { + marker: _serde::__private::PhantomData, + lifetime: _serde::__private::PhantomData<&'de ()>, + } + impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { + type Value = TxResultBlockAttribute; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "struct TxResultBlockAttribute", + ) + } + #[inline] + fn visit_seq<__A>( + self, + mut __seq: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::SeqAccess<'de>, + { + let __field0 = match _serde::de::SeqAccess::next_element::< + String, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 0usize, + &"struct TxResultBlockAttribute with 2 elements", + ), + ); + } + }; + let __field1 = match _serde::de::SeqAccess::next_element::< + String, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 1usize, + &"struct TxResultBlockAttribute with 2 elements", + ), + ); + } + }; + _serde::__private::Ok(TxResultBlockAttribute { + key: __field0, + value: __field1, + }) + } + #[inline] + fn visit_map<__A>( + self, + mut __map: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::MapAccess<'de>, + { + let mut __field0: _serde::__private::Option = _serde::__private::None; + let mut __field1: _serde::__private::Option = _serde::__private::None; + while let _serde::__private::Some(__key) + = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { + match __key { + __Field::__field0 => { + if _serde::__private::Option::is_some(&__field0) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field("key"), + ); + } + __field0 = _serde::__private::Some( + _serde::de::MapAccess::next_value::(&mut __map)?, + ); + } + __Field::__field1 => { + if _serde::__private::Option::is_some(&__field1) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field("value"), + ); + } + __field1 = _serde::__private::Some( + _serde::de::MapAccess::next_value::(&mut __map)?, + ); + } + _ => { + let _ = _serde::de::MapAccess::next_value::< + _serde::de::IgnoredAny, + >(&mut __map)?; + } + } + } + let __field0 = match __field0 { + _serde::__private::Some(__field0) => __field0, + _serde::__private::None => { + _serde::__private::de::missing_field("key")? + } + }; + let __field1 = match __field1 { + _serde::__private::Some(__field1) => __field1, + _serde::__private::None => { + _serde::__private::de::missing_field("value")? + } + }; + _serde::__private::Ok(TxResultBlockAttribute { + key: __field0, + value: __field1, + }) + } + } + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &["key", "value"]; + _serde::Deserializer::deserialize_struct( + __deserializer, + "TxResultBlockAttribute", + FIELDS, + __Visitor { + marker: _serde::__private::PhantomData::, + lifetime: _serde::__private::PhantomData, + }, + ) + } + } + }; + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl _serde::Serialize for TxResultBlockAttribute { + fn serialize<__S>( + &self, + __serializer: __S, + ) -> _serde::__private::Result<__S::Ok, __S::Error> + where + __S: _serde::Serializer, + { + let mut __serde_state = _serde::Serializer::serialize_struct( + __serializer, + "TxResultBlockAttribute", + false as usize + 1 + 1, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "key", + &self.key, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "value", + &self.value, + )?; + _serde::ser::SerializeStruct::end(__serde_state) + } + } + }; + #[automatically_derived] + impl ::core::clone::Clone for TxResultBlockAttribute { + #[inline] + fn clone(&self) -> TxResultBlockAttribute { + TxResultBlockAttribute { + key: ::core::clone::Clone::clone(&self.key), + value: ::core::clone::Clone::clone(&self.value), + } + } + } + #[automatically_derived] + impl ::core::fmt::Debug for TxResultBlockAttribute { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "TxResultBlockAttribute", + "key", + &self.key, + "value", + &&self.value, + ) + } + } + impl From for TxResultBlockAttribute { + fn from(a: Attribute) -> Self { + Self { key: a.key, value: a.value } + } + } + /// Parse a string timestamp into a `DateTime` + pub fn parse_timestamp(s: String) -> Result, DaemonError> { + let len = s.len(); + let slice_len = if s.contains('.') { len.saturating_sub(4) } else { len }; + let sliced = &s[0..slice_len]; + match NaiveDateTime::parse_from_str(sliced, FORMAT) { + Err(_e) => { + match NaiveDateTime::parse_from_str(&s, FORMAT_TZ_SUPPLIED) { + Err(_e2) => { + match NaiveDateTime::parse_from_str(sliced, FORMAT_SHORT_Z) { + Err(_e3) => { + match NaiveDateTime::parse_from_str(&s, FORMAT_SHORT_Z2) { + Err(_e4) => { + { + ::std::io::_eprint( + format_args!("DateTime Fail {0} {1:#?}\n", s, _e4), + ); + }; + Err(DaemonError::StdErr(_e4.to_string())) + } + Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), + } + } + Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), + } + } + Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), + } + } + Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), + } + } +} +pub mod keys { + #![allow(unused)] + pub mod private { + use super::public::PublicKey; + use crate::proto::injective::{InjectivePubKey, ETHEREUM_COIN_TYPE}; + use crate::DaemonError; + use base64::Engine; + use bitcoin::{ + bip32::{ExtendedPrivKey, IntoDerivationPath}, + Network, + }; + use cosmrs::tx::SignerPublicKey; + use hkd32::mnemonic::{Phrase, Seed}; + use rand_core::OsRng; + use secp256k1::Secp256k1; + /// The Private key structure that is used to generate signatures and public keys + /// WARNING: No Security Audit has been performed + pub struct PrivateKey { + #[allow(missing_docs)] + pub account: u32, + #[allow(missing_docs)] + pub index: u32, + #[allow(missing_docs)] + pub coin_type: u32, + /// The 24 words used to generate this private key + mnemonic: Option, + #[allow(dead_code)] + /// This is used for testing + root_private_key: ExtendedPrivKey, + /// The private key + private_key: ExtendedPrivKey, + } + #[automatically_derived] + impl ::core::clone::Clone for PrivateKey { + #[inline] + fn clone(&self) -> PrivateKey { + PrivateKey { + account: ::core::clone::Clone::clone(&self.account), + index: ::core::clone::Clone::clone(&self.index), + coin_type: ::core::clone::Clone::clone(&self.coin_type), + mnemonic: ::core::clone::Clone::clone(&self.mnemonic), + root_private_key: ::core::clone::Clone::clone( + &self.root_private_key, + ), + private_key: ::core::clone::Clone::clone(&self.private_key), + } + } + } + impl PrivateKey { + /// Generate a new private key + pub fn new( + secp: &Secp256k1, + coin_type: u32, + ) -> Result { + let phrase = hkd32::mnemonic::Phrase::random( + OsRng, + hkd32::mnemonic::Language::English, + ); + PrivateKey::gen_private_key_phrase(secp, phrase, 0, 0, coin_type, "") + } + /// generate a new private key with a seed phrase + pub fn new_seed( + secp: &Secp256k1, + seed_phrase: &str, + coin_type: u32, + ) -> Result { + let phrase = hkd32::mnemonic::Phrase::random( + OsRng, + hkd32::mnemonic::Language::English, + ); + PrivateKey::gen_private_key_phrase( + secp, + phrase, + 0, + 0, + coin_type, + seed_phrase, + ) + } + /// for private key recovery. This is also used by wallet routines to re-hydrate the structure + pub fn from_words( + secp: &Secp256k1, + words: &str, + account: u32, + index: u32, + coin_type: u32, + ) -> Result { + if words.split(' ').count() != 24 { + return Err(DaemonError::WrongLength); + } + match hkd32::mnemonic::Phrase::new( + words, + hkd32::mnemonic::Language::English, + ) { + Ok(phrase) => { + PrivateKey::gen_private_key_phrase( + secp, + phrase, + account, + index, + coin_type, + "", + ) + } + Err(_) => Err(DaemonError::Phrasing), + } + } + /// for private key recovery with seed phrase + pub fn from_words_seed( + secp: &Secp256k1, + words: &str, + seed_pass: &str, + coin_type: u32, + ) -> Result { + match hkd32::mnemonic::Phrase::new( + words, + hkd32::mnemonic::Language::English, + ) { + Ok(phrase) => { + PrivateKey::gen_private_key_phrase( + secp, + phrase, + 0, + 0, + coin_type, + seed_pass, + ) + } + Err(_) => Err(DaemonError::Phrasing), + } + } + /// generate the public key for this private key + pub fn public_key( + &self, + secp: &Secp256k1, + ) -> PublicKey { + if self.coin_type == ETHEREUM_COIN_TYPE { + { + ::core::panicking::panic_fmt( + format_args!( + "Coin Type {0} not supported without eth feature", + ETHEREUM_COIN_TYPE, + ), + ); + }; + } + let x = self.private_key.private_key.public_key(secp); + PublicKey::from_bitcoin_public_key(&bitcoin::PublicKey::new(x)) + } + pub fn get_injective_public_key( + &self, + secp: &Secp256k1, + ) -> SignerPublicKey { + use base64::engine::general_purpose; + use cosmrs::tx::MessageExt; + use secp256k1::SecretKey; + let secret_key = SecretKey::from_slice(self.raw_key().as_slice()) + .unwrap(); + let public_key = secp256k1::PublicKey::from_secret_key( + secp, + &secret_key, + ); + let vec_pk = public_key.serialize(); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "{0:?}, public key", + general_purpose::STANDARD.encode(vec_pk), + ), + lvl, + &( + "cw_orch_daemon::keys::private", + "cw_orch_daemon::keys::private", + "cw-orch-daemon/src/keys/private.rs", + ), + 124u32, + ::log::__private_api::Option::None, + ); + } + }; + let inj_key = InjectivePubKey { + key: vec_pk.into(), + }; + inj_key.to_any().unwrap().try_into().unwrap() + } + pub fn get_signer_public_key( + &self, + secp: &Secp256k1, + ) -> Option { + if self.coin_type == ETHEREUM_COIN_TYPE { + { + ::core::panicking::panic_fmt( + format_args!( + "Coin Type {0} not supported without eth feature", + ETHEREUM_COIN_TYPE, + ), + ); + }; + } + Some( + cosmrs::crypto::secp256k1::SigningKey::from_slice( + self.raw_key().as_slice(), + ) + .unwrap() + .public_key() + .into(), + ) + } + pub fn raw_key(&self) -> Vec { + self.private_key.private_key.secret_bytes().to_vec() + } + fn gen_private_key_phrase( + secp: &Secp256k1, + phrase: Phrase, + account: u32, + index: u32, + coin_type: u32, + seed_phrase: &str, + ) -> Result { + let seed = phrase.to_seed(seed_phrase); + let root_private_key = ExtendedPrivKey::new_master( + Network::Bitcoin, + seed.as_bytes(), + ) + .unwrap(); + let path = { + let res = ::alloc::fmt::format( + format_args!( + "m/44\'/{0}\'/{1}\'/0/{2}", + coin_type, + account, + index, + ), + ); + res + }; + let derivation_path = path.into_derivation_path()?; + let private_key = root_private_key.derive_priv(secp, &derivation_path)?; + Ok(PrivateKey { + account, + index, + coin_type, + mnemonic: Some(phrase), + root_private_key, + private_key, + }) + } + /// the words used to generate this private key + pub fn words(&self) -> Option<&str> { + self.mnemonic.as_ref().map(|phrase| phrase.phrase()) + } + /// used for testing + /// could potentially be used to recreate the private key instead of words + #[allow(dead_code)] + pub(crate) fn seed(&self, passwd: &str) -> Option { + self.mnemonic.as_ref().map(|phrase| phrase.to_seed(passwd)) + } + } + } + pub mod public { + use crate::DaemonError; + use bitcoin::bech32::{decode, encode, u5, FromBase32, ToBase32, Variant}; + pub use ed25519_dalek::VerifyingKey as Ed25519; + use ring::digest::{Context, SHA256}; + use ripemd::{Digest as _, Ripemd160}; + use serde::{Deserialize, Serialize}; + static BECH32_PUBKEY_DATA_PREFIX_SECP256K1: [u8; 5] = [ + 0xeb, + 0x5a, + 0xe9, + 0x87, + 0x21, + ]; + static BECH32_PUBKEY_DATA_PREFIX_ED25519: [u8; 5] = [ + 0x16, + 0x24, + 0xde, + 0x64, + 0x20, + ]; + /// The public key we used to generate the cosmos/tendermind/terrad addresses + pub struct PublicKey { + /// This is optional as we can generate non-pub keys without + pub raw_pub_key: Option>, + /// The raw bytes used to generate non-pub keys + pub raw_address: Option>, + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl<'de> _serde::Deserialize<'de> for PublicKey { + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + #[allow(non_camel_case_types)] + #[doc(hidden)] + enum __Field { + __field0, + __field1, + __ignore, + } + #[doc(hidden)] + struct __FieldVisitor; + impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { + type Value = __Field; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "field identifier", + ) + } + fn visit_u64<__E>( + self, + __value: u64, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + 0u64 => _serde::__private::Ok(__Field::__field0), + 1u64 => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_str<__E>( + self, + __value: &str, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + "raw_pub_key" => _serde::__private::Ok(__Field::__field0), + "raw_address" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_bytes<__E>( + self, + __value: &[u8], + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + b"raw_pub_key" => _serde::__private::Ok(__Field::__field0), + b"raw_address" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + } + impl<'de> _serde::Deserialize<'de> for __Field { + #[inline] + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + _serde::Deserializer::deserialize_identifier( + __deserializer, + __FieldVisitor, + ) + } + } + #[doc(hidden)] + struct __Visitor<'de> { + marker: _serde::__private::PhantomData, + lifetime: _serde::__private::PhantomData<&'de ()>, + } + impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { + type Value = PublicKey; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "struct PublicKey", + ) + } + #[inline] + fn visit_seq<__A>( + self, + mut __seq: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::SeqAccess<'de>, + { + let __field0 = match _serde::de::SeqAccess::next_element::< + Option>, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 0usize, + &"struct PublicKey with 2 elements", + ), + ); + } + }; + let __field1 = match _serde::de::SeqAccess::next_element::< + Option>, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 1usize, + &"struct PublicKey with 2 elements", + ), + ); + } + }; + _serde::__private::Ok(PublicKey { + raw_pub_key: __field0, + raw_address: __field1, + }) + } + #[inline] + fn visit_map<__A>( + self, + mut __map: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::MapAccess<'de>, + { + let mut __field0: _serde::__private::Option< + Option>, + > = _serde::__private::None; + let mut __field1: _serde::__private::Option< + Option>, + > = _serde::__private::None; + while let _serde::__private::Some(__key) + = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { + match __key { + __Field::__field0 => { + if _serde::__private::Option::is_some(&__field0) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field( + "raw_pub_key", + ), + ); + } + __field0 = _serde::__private::Some( + _serde::de::MapAccess::next_value::< + Option>, + >(&mut __map)?, + ); + } + __Field::__field1 => { + if _serde::__private::Option::is_some(&__field1) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field( + "raw_address", + ), + ); + } + __field1 = _serde::__private::Some( + _serde::de::MapAccess::next_value::< + Option>, + >(&mut __map)?, + ); + } + _ => { + let _ = _serde::de::MapAccess::next_value::< + _serde::de::IgnoredAny, + >(&mut __map)?; + } + } + } + let __field0 = match __field0 { + _serde::__private::Some(__field0) => __field0, + _serde::__private::None => { + _serde::__private::de::missing_field("raw_pub_key")? + } + }; + let __field1 = match __field1 { + _serde::__private::Some(__field1) => __field1, + _serde::__private::None => { + _serde::__private::de::missing_field("raw_address")? + } + }; + _serde::__private::Ok(PublicKey { + raw_pub_key: __field0, + raw_address: __field1, + }) + } + } + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &[ + "raw_pub_key", + "raw_address", + ]; + _serde::Deserializer::deserialize_struct( + __deserializer, + "PublicKey", + FIELDS, + __Visitor { + marker: _serde::__private::PhantomData::, + lifetime: _serde::__private::PhantomData, + }, + ) + } + } + }; + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl _serde::Serialize for PublicKey { + fn serialize<__S>( + &self, + __serializer: __S, + ) -> _serde::__private::Result<__S::Ok, __S::Error> + where + __S: _serde::Serializer, + { + let mut __serde_state = _serde::Serializer::serialize_struct( + __serializer, + "PublicKey", + false as usize + 1 + 1, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "raw_pub_key", + &self.raw_pub_key, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "raw_address", + &self.raw_address, + )?; + _serde::ser::SerializeStruct::end(__serde_state) + } + } + }; + #[automatically_derived] + impl ::core::fmt::Debug for PublicKey { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "PublicKey", + "raw_pub_key", + &self.raw_pub_key, + "raw_address", + &&self.raw_address, + ) + } + } + #[automatically_derived] + impl ::core::clone::Clone for PublicKey { + #[inline] + fn clone(&self) -> PublicKey { + PublicKey { + raw_pub_key: ::core::clone::Clone::clone(&self.raw_pub_key), + raw_address: ::core::clone::Clone::clone(&self.raw_address), + } + } + } + impl PublicKey { + /// Generate a Cosmos/Tendermint/Terrad Public Key + pub fn from_bitcoin_public_key(bpub: &bitcoin::key::PublicKey) -> PublicKey { + let bpub_bytes = bpub.inner.serialize(); + let raw_pub_key = PublicKey::pubkey_from_public_key(&bpub_bytes); + let raw_address = PublicKey::address_from_public_key(&bpub_bytes); + PublicKey { + raw_pub_key: Some(raw_pub_key), + raw_address: Some(raw_address), + } + } + /// Generate from secp256k1 Cosmos/Terrad Public Key + pub fn from_public_key(bpub: &[u8]) -> PublicKey { + let raw_pub_key = PublicKey::pubkey_from_public_key(bpub); + let raw_address = PublicKey::address_from_public_key(bpub); + PublicKey { + raw_pub_key: Some(raw_pub_key), + raw_address: Some(raw_address), + } + } + /// Generate a Cosmos/Tendermint/Terrad Account + pub fn from_account( + acc_address: &str, + prefix: &str, + ) -> Result { + PublicKey::check_prefix_and_length(prefix, acc_address, 44) + .and_then(|vu5| { + let vu8 = Vec::from_base32(vu5.as_slice()) + .map_err(|source| DaemonError::Conversion { + key: acc_address.into(), + source, + })?; + Ok(PublicKey { + raw_pub_key: None, + raw_address: Some(vu8), + }) + }) + } + /// build a public key from a tendermint public key + pub fn from_tendermint_key( + tendermint_public_key: &str, + ) -> Result { + let len = tendermint_public_key.len(); + if len == 83 { + PublicKey::check_prefix_and_length( + "terravalconspub", + tendermint_public_key, + len, + ) + .and_then(|vu5| { + let vu8 = Vec::from_base32(vu5.as_slice()) + .map_err(|source| { + DaemonError::Conversion { + key: tendermint_public_key.into(), + source, + } + })?; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("{0:#?}", hex::encode(&vu8)), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 74u32, + ::log::__private_api::Option::None, + ); + } + }; + if vu8.starts_with(&BECH32_PUBKEY_DATA_PREFIX_SECP256K1) { + let public_key = PublicKey::public_key_from_pubkey(&vu8)?; + let raw = PublicKey::address_from_public_key(&public_key); + Ok(PublicKey { + raw_pub_key: Some(vu8), + raw_address: Some(raw), + }) + } else { + Err(DaemonError::ConversionSECP256k1) + } + }) + } else if len == 82 { + PublicKey::check_prefix_and_length( + "terravalconspub", + tendermint_public_key, + len, + ) + .and_then(|vu5| { + let vu8 = Vec::from_base32(vu5.as_slice()) + .map_err(|source| { + DaemonError::Conversion { + key: tendermint_public_key.into(), + source, + } + })?; + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("ED25519 public keys are not fully supported"), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 100u32, + ::log::__private_api::Option::None, + ); + } + }; + if vu8.starts_with(&BECH32_PUBKEY_DATA_PREFIX_ED25519) { + let raw = PublicKey::address_from_public_ed25519_key(&vu8)?; + Ok(PublicKey { + raw_pub_key: Some(vu8), + raw_address: Some(raw), + }) + } else { + Err(DaemonError::ConversionED25519) + } + }) + } else { + Err(DaemonError::ConversionLength(len)) + } + } + /// build a terravalcons address from a tendermint hex key + /// the tendermint_hex_address should be a hex code of 40 length + pub fn from_tendermint_address( + tendermint_hex_address: &str, + ) -> Result { + let len = tendermint_hex_address.len(); + if len == 40 { + let raw = hex::decode(tendermint_hex_address)?; + Ok(PublicKey { + raw_pub_key: None, + raw_address: Some(raw), + }) + } else { + Err(DaemonError::ConversionLengthED25519Hex(len)) + } + } + /// Generate a Operator address for this public key (used by the validator) + pub fn from_operator_address( + valoper_address: &str, + ) -> Result { + PublicKey::check_prefix_and_length("terravaloper", valoper_address, 51) + .and_then(|vu5| { + let vu8 = Vec::from_base32(vu5.as_slice()) + .map_err(|source| DaemonError::Conversion { + key: valoper_address.into(), + source, + })?; + Ok(PublicKey { + raw_pub_key: None, + raw_address: Some(vu8), + }) + }) + } + /// Generate Public key from raw address + pub fn from_raw_address( + raw_address: &str, + ) -> Result { + let vec1 = hex::decode(raw_address)?; + Ok(PublicKey { + raw_pub_key: None, + raw_address: Some(vec1), + }) + } + fn check_prefix_and_length( + prefix: &str, + data: &str, + length: usize, + ) -> Result, DaemonError> { + let (hrp, decoded_str, _) = decode(data) + .map_err(|source| DaemonError::Conversion { + key: data.into(), + source, + })?; + if hrp == prefix && data.len() == length { + Ok(decoded_str) + } else { + Err( + DaemonError::Bech32DecodeExpanded( + hrp, + data.len(), + prefix.into(), + length, + ), + ) + } + } + /** + Gets a bech32-words pubkey from a compressed bytes Secp256K1 public key. + + @param publicKey raw public key + */ + pub fn pubkey_from_public_key(public_key: &[u8]) -> Vec { + [BECH32_PUBKEY_DATA_PREFIX_SECP256K1.to_vec(), public_key.to_vec()] + .concat() + } + /** + Gets a bech32-words pubkey from a compressed bytes Ed25519 public key. + + @param publicKey raw public key + */ + pub fn pubkey_from_ed25519_public_key(public_key: &[u8]) -> Vec { + [BECH32_PUBKEY_DATA_PREFIX_ED25519.to_vec(), public_key.to_vec()] + .concat() + } + /// Translate from a BECH32 prefixed key to a standard public key + pub fn public_key_from_pubkey( + pub_key: &[u8], + ) -> Result, DaemonError> { + if pub_key.starts_with(&BECH32_PUBKEY_DATA_PREFIX_SECP256K1) { + let len = BECH32_PUBKEY_DATA_PREFIX_SECP256K1.len(); + let len2 = pub_key.len(); + Ok(Vec::from(&pub_key[len..len2])) + } else if pub_key.starts_with(&BECH32_PUBKEY_DATA_PREFIX_ED25519) { + let len = BECH32_PUBKEY_DATA_PREFIX_ED25519.len(); + let len2 = pub_key.len(); + let vec = &pub_key[len..len2]; + let ed25519_pubkey = Ed25519::from_bytes(vec.try_into().unwrap())?; + Ok(ed25519_pubkey.to_bytes().to_vec()) + } else { + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("pub key does not start with BECH32 PREFIX"), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 228u32, + ::log::__private_api::Option::None, + ); + } + }; + Err(DaemonError::Bech32DecodeErr) + } + } + /** + Gets a raw address from a compressed bytes public key. + + @param publicKey raw public key + */ + pub fn address_from_public_key(public_key: &[u8]) -> Vec { + let mut hasher = Ripemd160::new(); + let sha_result = ring::digest::digest(&SHA256, public_key); + hasher.update(&sha_result.as_ref()[0..32]); + let ripe_result = hasher.finalize(); + let address: Vec = ripe_result[0..20].to_vec(); + address + } + /** + Gets a raw address from a ed25519 public key. + + @param publicKey raw public key + */ + pub fn address_from_public_ed25519_key( + public_key: &[u8], + ) -> Result, DaemonError> { + if public_key.len() != (32 + 5) { + Err( + DaemonError::ConversionPrefixED25519( + public_key.len(), + hex::encode(public_key), + ), + ) + } else { + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "address_from_public_ed25519_key public key - {0}", + hex::encode(public_key), + ), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 262u32, + ::log::__private_api::Option::None, + ); + } + }; + let mut sha_result: [u8; 32] = [0; 32]; + let sha_result = ring::digest::digest(&SHA256, &public_key[5..]); + let address: Vec = sha_result.as_ref()[0..20].to_vec(); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "address_from_public_ed25519_key sha result - {0}", + hex::encode(&address), + ), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 283u32, + ::log::__private_api::Option::None, + ); + } + }; + Ok(address) + } + } + /// The main account used in most things + pub fn account(&self, prefix: &str) -> Result { + match &self.raw_address { + Some(raw) => { + let data = encode(prefix, raw.to_base32(), Variant::Bech32); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => Err(DaemonError::Implementation), + } + } + /// The operator address used for validators + pub fn operator_address(&self, prefix: &str) -> Result { + match &self.raw_address { + Some(raw) => { + let data = encode( + &{ + let res = ::alloc::fmt::format( + format_args!("{0}{1}", prefix, "valoper"), + ); + res + }, + raw.to_base32(), + Variant::Bech32, + ); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => Err(DaemonError::Implementation), + } + } + /// application public key - Application keys are associated with a public key terrapub- and an address terra- + pub fn application_public_key( + &self, + prefix: &str, + ) -> Result { + match &self.raw_pub_key { + Some(raw) => { + let data = encode( + &{ + let res = ::alloc::fmt::format( + format_args!("{0}{1}", prefix, "pub"), + ); + res + }, + raw.to_base32(), + Variant::Bech32, + ); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Missing Public Key. Can\'t continue"), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 335u32, + ::log::__private_api::Option::None, + ); + } + }; + Err(DaemonError::Implementation) + } + } + } + /// The operator address used for validators public key. + pub fn operator_address_public_key( + &self, + prefix: &str, + ) -> Result { + match &self.raw_pub_key { + Some(raw) => { + let data = encode( + &{ + let res = ::alloc::fmt::format( + format_args!("{0}{1}", prefix, "valoperpub"), + ); + res + }, + raw.to_base32(), + Variant::Bech32, + ); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => Err(DaemonError::Implementation), + } + } + /// This is a unique key used to sign block hashes. It is associated with a public key terravalconspub. + pub fn tendermint(&self, prefix: &str) -> Result { + match &self.raw_address { + Some(raw) => { + let data = encode( + &{ + let res = ::alloc::fmt::format( + format_args!("{0}{1}", prefix, "valcons"), + ); + res + }, + raw.to_base32(), + Variant::Bech32, + ); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => Err(DaemonError::Implementation), + } + } + /// This is a unique key used to sign block hashes. It is associated with a public key terravalconspub. + pub fn tendermint_pubkey( + &self, + prefix: &str, + ) -> Result { + match &self.raw_pub_key { + Some(raw) => { + let b32 = raw.to_base32(); + let data = encode( + &{ + let res = ::alloc::fmt::format( + format_args!("{0}{1}", prefix, "valconspub"), + ); + res + }, + b32, + Variant::Bech32, + ); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => Err(DaemonError::Implementation), + } + } + } + } + pub mod signature { + use crate::DaemonError; + use base64::engine::{general_purpose::STANDARD, Engine}; + use ring::digest::SHA256; + use secp256k1::{Message, Secp256k1}; + pub struct Signature {} + impl Signature { + pub fn verify( + secp: &Secp256k1, + pub_key: &str, + signature: &str, + blob: &str, + ) -> Result<(), DaemonError> { + let public = STANDARD.decode(pub_key)?; + let sig = STANDARD.decode(signature)?; + let pk = secp256k1::PublicKey::from_slice(public.as_slice())?; + let sha_result = ring::digest::digest(&SHA256, blob.as_bytes()); + let message: Message = Message::from_slice(&sha_result.as_ref()[0..32])?; + let secp_sig = secp256k1::ecdsa::Signature::from_compact( + sig.as_slice(), + )?; + secp.verify_ecdsa(&message, &secp_sig, &pk)?; + Ok(()) + } + } + } +} +pub mod live_mock { + //! Live mock is a mock that uses a live chain to query for data. + //! It can be used to do chain-backed unit-testing. It can't be used for state-changing operations. + use crate::queriers::Bank; + use crate::queriers::CosmWasm; + use crate::queriers::DaemonQuerier; + use crate::queriers::Staking; + use cosmwasm_std::Addr; + use cosmwasm_std::AllBalanceResponse; + use cosmwasm_std::BalanceResponse; + use cosmwasm_std::Delegation; + use cosmwasm_std::{AllDelegationsResponse, BondedDenomResponse}; + use cosmwasm_std::BankQuery; + use cosmwasm_std::Binary; + use cosmwasm_std::Empty; + use cosmwasm_std::StakingQuery; + use ibc_chain_registry::chain::ChainData; + use tokio::runtime::Runtime; + use tonic::transport::Channel; + use std::marker::PhantomData; + use std::str::FromStr; + use cosmwasm_std::testing::{MockApi, MockStorage}; + use cosmwasm_std::{ + from_slice, to_binary, Coin, ContractResult, OwnedDeps, Querier, QuerierResult, + QueryRequest, SystemError, SystemResult, Uint128, WasmQuery, + }; + use crate::channel::GrpcChannel; + fn to_cosmwasm_coin(c: cosmrs::proto::cosmos::base::v1beta1::Coin) -> Coin { + Coin { + amount: Uint128::from_str(&c.amount).unwrap(), + denom: c.denom, + } + } + const QUERIER_ERROR: &str = "Only Bank balances and Wasm (raw + smart) and Some staking queries are covered for now"; + /// mock_dependencies is a drop-in replacement for cosmwasm_std::testing::mock_dependencies + /// this uses our CustomQuerier. + pub fn mock_dependencies( + chain_info: ChainData, + ) -> OwnedDeps { + let custom_querier: WasmMockQuerier = WasmMockQuerier::new(chain_info); + OwnedDeps { + storage: MockStorage::default(), + api: MockApi::default(), + querier: custom_querier, + custom_query_type: PhantomData, + } + } + /// Querier struct that fetches queries on-chain directly + pub struct WasmMockQuerier { + channel: Channel, + runtime: Runtime, + } + impl Querier for WasmMockQuerier { + fn raw_query(&self, bin_request: &[u8]) -> QuerierResult { + let request: QueryRequest = match from_slice(bin_request) { + Ok(v) => v, + Err(e) => { + return SystemResult::Err(SystemError::InvalidRequest { + error: { + let res = ::alloc::fmt::format( + format_args!("Parsing query request: {0}", e), + ); + res + }, + request: bin_request.into(), + }); + } + }; + self.handle_query(&request) + } + } + impl WasmMockQuerier { + /// Function used to handle a query and customize the query behavior + /// This implements some queries by querying an actual node for the responses + pub fn handle_query(&self, request: &QueryRequest) -> QuerierResult { + match &request { + QueryRequest::Wasm(x) => { + let querier = CosmWasm::new(self.channel.clone()); + match x { + WasmQuery::Smart { contract_addr, msg } => { + let query_result: Result = self + .runtime + .block_on( + querier + .contract_state(contract_addr.to_string(), msg.to_vec()), + ) + .map(|query_result| query_result.into()); + SystemResult::Ok(ContractResult::from(query_result)) + } + WasmQuery::Raw { contract_addr, key } => { + let query_result = self + .runtime + .block_on( + querier + .contract_raw_state(contract_addr.to_string(), key.to_vec()), + ) + .map(|query_result| query_result.data.into()); + SystemResult::Ok(ContractResult::from(query_result)) + } + _ => { + SystemResult::Err(SystemError::InvalidRequest { + error: QUERIER_ERROR.to_string(), + request: to_binary(&request).unwrap(), + }) + } + } + } + QueryRequest::Bank(x) => { + let querier = Bank::new(self.channel.clone()); + match x { + BankQuery::Balance { address, denom } => { + let query_result = self + .runtime + .block_on(querier.balance(address, Some(denom.clone()))) + .map(|result| { + to_binary( + &BalanceResponse { + amount: Coin { + amount: Uint128::from_str(&result[0].amount).unwrap(), + denom: result[0].denom.clone(), + }, + }, + ) + .unwrap() + }); + SystemResult::Ok(ContractResult::from(query_result)) + } + BankQuery::AllBalances { address } => { + let query_result = self + .runtime + .block_on(querier.balance(address, None)) + .map(|result| AllBalanceResponse { + amount: result + .into_iter() + .map(|c| Coin { + amount: Uint128::from_str(&c.amount).unwrap(), + denom: c.denom, + }) + .collect(), + }) + .map(|query_result| to_binary(&query_result)) + .unwrap(); + SystemResult::Ok(ContractResult::from(query_result)) + } + _ => { + SystemResult::Err(SystemError::InvalidRequest { + error: QUERIER_ERROR.to_string(), + request: to_binary(&request).unwrap(), + }) + } + } + } + QueryRequest::Staking(x) => { + let querier = Staking::new(self.channel.clone()); + match x { + StakingQuery::BondedDenom {} => { + let query_result = self + .runtime + .block_on(querier.params()) + .map(|result| BondedDenomResponse { + denom: result.params.unwrap().bond_denom, + }) + .map(|query_result| to_binary(&query_result)) + .unwrap(); + SystemResult::Ok(ContractResult::from(query_result)) + } + StakingQuery::AllDelegations { delegator } => { + let query_result = self + .runtime + .block_on(querier.delegator_delegations(delegator, None)) + .map(|result| AllDelegationsResponse { + delegations: result + .delegation_responses + .into_iter() + .filter_map(|delegation| { + delegation + .delegation + .map(|d| Delegation { + delegator: Addr::unchecked(d.delegator_address), + validator: d.validator_address, + amount: to_cosmwasm_coin(delegation.balance.unwrap()), + }) + }) + .collect(), + }) + .map(|query_result| to_binary(&query_result)) + .unwrap(); + SystemResult::Ok(ContractResult::from(query_result)) + } + _ => ::core::panicking::panic("not yet implemented"), + } + } + _ => { + SystemResult::Err(SystemError::InvalidRequest { + error: QUERIER_ERROR.to_string(), + request: to_binary(&request).unwrap(), + }) + } + } + } + } + impl WasmMockQuerier { + /// Creates a querier from chain information + pub fn new(chain: ChainData) -> Self { + let rt = Runtime::new().unwrap(); + let channel = rt + .block_on(GrpcChannel::connect(&chain.apis.grpc, &chain.chain_id)) + .unwrap(); + WasmMockQuerier { + channel, + runtime: rt, + } + } + } +} +pub mod queriers { + //! # DaemonQuerier + //! + //! DaemonAsync queriers are gRPC query clients for the CosmosSDK modules. They can be used to query the different modules (Bank, Ibc, Authz, ...). + //! + //! ## Usage + //! + //! You will need to acquire a [gRPC channel](Channel) to a running CosmosSDK node to be able to use the queriers. + //! Here is an example of how to acquire one using the DaemonAsync builder. + //! + //! ```no_run + //! // require the querier you want to use, in this case Node + //! use cw_orch_daemon::{queriers::Node, DaemonAsync, networks, queriers::DaemonQuerier}; + //! # tokio_test::block_on(async { + //! // call the builder and configure it as you need + //! let daemon = DaemonAsync::builder() + //! .chain(networks::LOCAL_JUNO) + //! .build() + //! .await.unwrap(); + //! // now you can use the Node querier: + //! let node = Node::new(daemon.channel()); + //! let node_info = node.info(); + //! # }) + //! ``` + mod bank { + use crate::{cosmos_modules, error::DaemonError}; + use cosmrs::proto::cosmos::{ + base::{query::v1beta1::PageRequest, v1beta1::Coin}, + bank::v1beta1::QueryBalanceResponse, + }; + use tonic::transport::Channel; + use super::DaemonQuerier; + /// Queries for Cosmos Bank Module + pub struct Bank { + channel: Channel, + } + impl DaemonQuerier for Bank { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Bank { + /// Query the bank balance of a given address + /// If denom is None, returns all balances + pub async fn balance( + &self, + address: impl Into, + denom: Option, + ) -> Result, DaemonError> { + use cosmos_modules::bank::query_client::QueryClient; + use cosmos_modules::bank::QueryBalanceRequest; + match denom { + Some(denom) => { + let resp: QueryBalanceResponse = (); + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = cosmos_modules::bank::QueryBalanceRequest { + address: address.into(), + denom, + }; + let resp = client.balance(request).await?.into_inner(); + let coin = resp.balance.unwrap(); + Ok( + <[_]>::into_vec( + #[rustc_box] + ::alloc::boxed::Box::new([coin]), + ), + ) + } + None => { + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = cosmos_modules::bank::QueryAllBalancesRequest { + address: address.into(), + ..Default::default() + }; + let resp = client.all_balances(request).await?.into_inner(); + let coins = resp.balances; + Ok(coins.into_iter().collect()) + } + } + } + /// Query spendable balance for address + pub async fn spendable_balances( + &self, + address: impl Into, + ) -> Result, DaemonError> { + let spendable_balances: cosmos_modules::bank::QuerySpendableBalancesResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QuerySpendableBalancesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QuerySpendableBalancesRequest { + address: address.into(), + pagination: None, + }; + let response = client + .spendable_balances(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 70u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(spendable_balances.balances) + } + /// Query total supply in the bank + pub async fn total_supply(&self) -> Result, DaemonError> { + let total_supply: cosmos_modules::bank::QueryTotalSupplyResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QueryTotalSupplyRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryTotalSupplyRequest { + pagination: None, + }; + let response = client + .total_supply(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 84u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(total_supply.supply) + } + /// Query total supply in the bank for a denom + pub async fn supply_of( + &self, + denom: impl Into, + ) -> Result { + let supply_of: cosmos_modules::bank::QuerySupplyOfResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QuerySupplyOfRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QuerySupplyOfRequest { + denom: denom.into(), + }; + let response = client.supply_of(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 95u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(supply_of.amount.unwrap()) + } + /// Query params + pub async fn params( + &self, + ) -> Result { + let params: cosmos_modules::bank::QueryParamsResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QueryParamsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryParamsRequest {}; + let response = client.params(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 109u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(params.params.unwrap()) + } + /// Query denom metadata + pub async fn denom_metadata( + &self, + denom: impl Into, + ) -> Result { + let denom_metadata: cosmos_modules::bank::QueryDenomMetadataResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QueryDenomMetadataRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDenomMetadataRequest { + denom: denom.into(), + }; + let response = client + .denom_metadata(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 118u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(denom_metadata.metadata.unwrap()) + } + /// Query denoms metadata with pagination + /// + /// see [PageRequest] for pagination + pub async fn denoms_metadata( + &self, + pagination: Option, + ) -> Result, DaemonError> { + let denoms_metadata: cosmos_modules::bank::QueryDenomsMetadataResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QueryDenomsMetadataRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDenomsMetadataRequest { + pagination: pagination, + }; + let response = client + .denoms_metadata(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 136u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(denoms_metadata.metadatas) + } + } + } + mod cosmwasm { + use crate::{cosmos_modules, error::DaemonError}; + use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; + use tonic::transport::Channel; + use super::DaemonQuerier; + /// Querier for the CosmWasm SDK module + pub struct CosmWasm { + channel: Channel, + } + impl DaemonQuerier for CosmWasm { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl CosmWasm { + /// Query code_id by hash + pub async fn code_id_hash( + &self, + code_id: u64, + ) -> Result { + use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryCodeRequest { code_id }; + let resp = client.code(request).await?.into_inner(); + let contract_hash = resp.code_info.unwrap().data_hash; + let on_chain_hash = base16::encode_lower(&contract_hash); + Ok(on_chain_hash) + } + /// Query contract info + pub async fn contract_info( + &self, + address: impl Into, + ) -> Result { + use cosmos_modules::cosmwasm::{ + query_client::*, QueryContractInfoRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryContractInfoRequest { + address: address.into(), + }; + let resp = client.contract_info(request).await?.into_inner(); + let contract_info = resp.contract_info.unwrap(); + Ok(contract_info) + } + /// Query contract history + pub async fn contract_history( + &self, + address: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::cosmwasm::QueryContractHistoryResponse, + DaemonError, + > { + use cosmos_modules::cosmwasm::{ + query_client::*, QueryContractHistoryRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryContractHistoryRequest { + address: address.into(), + pagination, + }; + Ok(client.contract_history(request).await?.into_inner()) + } + /// Query contract state + pub async fn contract_state( + &self, + address: impl Into, + query_data: Vec, + ) -> Result, DaemonError> { + use cosmos_modules::cosmwasm::{ + query_client::*, QuerySmartContractStateRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QuerySmartContractStateRequest { + address: address.into(), + query_data, + }; + Ok(client.smart_contract_state(request).await?.into_inner().data) + } + /// Query all contract state + pub async fn all_contract_state( + &self, + address: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::cosmwasm::QueryAllContractStateResponse, + DaemonError, + > { + use cosmos_modules::cosmwasm::{ + query_client::*, QueryAllContractStateRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryAllContractStateRequest { + address: address.into(), + pagination, + }; + Ok(client.all_contract_state(request).await?.into_inner()) + } + /// Query code + pub async fn code( + &self, + code_id: u64, + ) -> Result { + use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryCodeRequest { code_id }; + Ok(client.code(request).await?.into_inner().code_info.unwrap()) + } + /// Query code bytes + pub async fn code_data(&self, code_id: u64) -> Result, DaemonError> { + use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryCodeRequest { code_id }; + Ok(client.code(request).await?.into_inner().data) + } + /// Query codes + pub async fn codes( + &self, + pagination: Option, + ) -> Result, DaemonError> { + use cosmos_modules::cosmwasm::{query_client::*, QueryCodesRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryCodesRequest { pagination }; + Ok(client.codes(request).await?.into_inner().code_infos) + } + /// Query pinned codes + pub async fn pinned_codes( + &self, + ) -> Result< + cosmos_modules::cosmwasm::QueryPinnedCodesResponse, + DaemonError, + > { + use cosmos_modules::cosmwasm::{query_client::*, QueryPinnedCodesRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryPinnedCodesRequest { + pagination: None, + }; + Ok(client.pinned_codes(request).await?.into_inner()) + } + /// Query contracts by code + pub async fn contract_by_codes( + &self, + code_id: u64, + ) -> Result< + cosmos_modules::cosmwasm::QueryContractsByCodeResponse, + DaemonError, + > { + use cosmos_modules::cosmwasm::{ + query_client::*, QueryContractsByCodeRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryContractsByCodeRequest { + code_id, + pagination: None, + }; + Ok(client.contracts_by_code(request).await?.into_inner()) + } + /// Query raw contract state + pub async fn contract_raw_state( + &self, + address: impl Into, + query_data: Vec, + ) -> Result< + cosmos_modules::cosmwasm::QueryRawContractStateResponse, + DaemonError, + > { + use cosmos_modules::cosmwasm::{ + query_client::*, QueryRawContractStateRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryRawContractStateRequest { + address: address.into(), + query_data, + }; + Ok(client.raw_contract_state(request).await?.into_inner()) + } + /// Query params + pub async fn params( + &self, + ) -> Result { + use cosmos_modules::cosmwasm::{query_client::*, QueryParamsRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + Ok(client.params(QueryParamsRequest {}).await?.into_inner()) + } + } + } + mod feegrant { + use crate::{cosmos_modules, error::DaemonError}; + use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; + use tonic::transport::Channel; + use super::DaemonQuerier; + /// Querier for the Cosmos Gov module + pub struct Feegrant { + channel: Channel, + } + impl DaemonQuerier for Feegrant { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Feegrant { + /// Query all allowances granted to the grantee address by a granter address + pub async fn allowance( + &self, + granter: impl Into, + grantee: impl Into, + ) -> Result { + let allowance: cosmos_modules::feegrant::QueryAllowanceResponse = { + use crate::cosmos_modules::feegrant::{ + query_client::QueryClient, QueryAllowanceRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryAllowanceRequest { + granter: granter.into(), + grantee: grantee.into(), + }; + let response = client.allowance(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::feegrant", + "cw_orch_daemon::queriers::feegrant", + "cw-orch-daemon/src/queriers/feegrant.rs", + ), + 25u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(allowance.allowance.unwrap()) + } + /// Query allowances for grantee address with a given pagination + /// + /// see [PageRequest] for pagination + pub async fn allowances( + &self, + grantee: impl Into, + pagination: Option, + ) -> Result, DaemonError> { + let allowances: cosmos_modules::feegrant::QueryAllowancesResponse = { + use crate::cosmos_modules::feegrant::{ + query_client::QueryClient, QueryAllowancesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryAllowancesRequest { + grantee: grantee.into(), + pagination: pagination, + }; + let response = client + .allowances(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::feegrant", + "cw_orch_daemon::queriers::feegrant", + "cw-orch-daemon/src/queriers/feegrant.rs", + ), + 45u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(allowances.allowances) + } + } + } + mod gov { + use crate::{cosmos_modules, error::DaemonError}; + use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; + use tonic::transport::Channel; + use super::DaemonQuerier; + /// Querier for the Cosmos Gov module + pub struct Gov { + channel: Channel, + } + impl DaemonQuerier for Gov { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Gov { + /// Query proposal details by proposal id + pub async fn proposal( + &self, + proposal_id: u64, + ) -> Result { + let proposal: cosmos_modules::gov::QueryProposalResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryProposalRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryProposalRequest { + proposal_id: proposal_id, + }; + let response = client.proposal(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 24u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(proposal.proposal.unwrap()) + } + /// Query proposals based on given status + /// + /// see [PageRequest] for pagination + pub async fn proposals( + &self, + proposal_status: GovProposalStatus, + voter: impl Into, + depositor: impl Into, + pagination: Option, + ) -> Result { + let proposals: cosmos_modules::gov::QueryProposalsResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryProposalsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryProposalsRequest { + proposal_status: proposal_status as i32, + voter: voter.into(), + depositor: depositor.into(), + pagination: pagination, + }; + let response = client.proposals(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 45u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(proposals) + } + /// Query voted information based on proposal_id for voter address + pub async fn vote( + &self, + proposal_id: u64, + voter: impl Into, + ) -> Result { + let vote: cosmos_modules::gov::QueryVoteResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryVoteRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryVoteRequest { + proposal_id: proposal_id, + voter: voter.into(), + }; + let response = client.vote(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 65u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(vote.vote.unwrap()) + } + /// Query votes of a given proposal + /// + /// see [PageRequest] for pagination + pub async fn votes( + &self, + proposal_id: impl Into, + pagination: Option, + ) -> Result { + let votes: cosmos_modules::gov::QueryVotesResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryVotesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryVotesRequest { + proposal_id: proposal_id.into(), + pagination: pagination, + }; + let response = client.votes(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 85u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(votes) + } + /// Query all parameters of the gov module + pub async fn params( + &self, + params_type: impl Into, + ) -> Result { + let params: cosmos_modules::gov::QueryParamsResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryParamsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryParamsRequest { + params_type: params_type.into(), + }; + let response = client.params(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 102u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(params) + } + /// Query deposit information using proposal_id and depositor address + pub async fn deposit( + &self, + proposal_id: u64, + depositor: impl Into, + ) -> Result { + let deposit: cosmos_modules::gov::QueryDepositResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryDepositRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDepositRequest { + proposal_id: proposal_id, + depositor: depositor.into(), + }; + let response = client.deposit(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 119u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(deposit.deposit.unwrap()) + } + /// Query deposits of a proposal + /// + /// see [PageRequest] for pagination + pub async fn deposits( + &self, + proposal_id: u64, + pagination: Option, + ) -> Result { + let deposits: cosmos_modules::gov::QueryDepositsResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryDepositsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDepositsRequest { + proposal_id: proposal_id, + pagination: pagination, + }; + let response = client.deposits(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 139u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(deposits) + } + /// TallyResult queries the tally of a proposal vote. + pub async fn tally_result( + &mut self, + proposal_id: u64, + ) -> Result { + let tally_result: cosmos_modules::gov::QueryTallyResultResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryTallyResultRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryTallyResultRequest { + proposal_id: proposal_id, + }; + let response = client + .tally_result(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 156u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(tally_result.tally.unwrap()) + } + } + /// Proposal status + #[allow(missing_docs)] + pub enum GovProposalStatus { + Unspecified = 0, + DepositPeriod = 1, + VotingPeriod = 2, + Passed = 3, + Rejected = 4, + Failed = 5, + } + } + mod ibc { + use super::DaemonQuerier; + use crate::{cosmos_modules, error::DaemonError}; + use cosmos_modules::ibc_channel; + use cosmrs::proto::ibc::{ + applications::transfer::v1::{DenomTrace, QueryDenomTraceResponse}, + core::{ + channel::v1::QueryPacketCommitmentResponse, + client::v1::{IdentifiedClientState, QueryClientStatesResponse}, + connection::v1::{IdentifiedConnection, State}, + }, + lightclients::tendermint::v1::ClientState, + }; + use prost::Message; + use tonic::transport::Channel; + /// Querier for the Cosmos IBC module + pub struct Ibc { + channel: Channel, + } + impl DaemonQuerier for Ibc { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Ibc { + /// Get the trace of a specific denom + pub async fn denom_trace( + &self, + hash: String, + ) -> Result { + let denom_trace: QueryDenomTraceResponse = { + use crate::cosmos_modules::ibc_transfer::{ + query_client::QueryClient, QueryDenomTraceRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDenomTraceRequest { + hash: hash, + }; + let response = client + .denom_trace(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 32u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(denom_trace.denom_trace.unwrap()) + } + /// Get all the IBC clients for this daemon + pub async fn clients( + &self, + ) -> Result, DaemonError> { + let ibc_clients: QueryClientStatesResponse = { + use crate::cosmos_modules::ibc_client::{ + query_client::QueryClient, QueryClientStatesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryClientStatesRequest { + pagination: None, + }; + let response = client + .client_states(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 45u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_clients.client_states) + } + /// Get the state of a specific IBC client + pub async fn client_state( + &self, + client_id: impl ToString, + ) -> Result< + cosmos_modules::ibc_client::QueryClientStateResponse, + DaemonError, + > { + let response: cosmos_modules::ibc_client::QueryClientStateResponse = { + use crate::cosmos_modules::ibc_client::{ + query_client::QueryClient, QueryClientStateRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryClientStateRequest { + client_id: client_id.to_string(), + }; + let response = client + .client_state(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 60u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(response) + } + /// Get the consensus state of a specific IBC client + 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 = { + use crate::cosmos_modules::ibc_client::{ + query_client::QueryClient, QueryConsensusStatesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryConsensusStatesRequest { + client_id: client_id, + pagination: None, + }; + let response = client + .consensus_states(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 77u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(response) + } + /// Get the consensus status of a specific IBC client + pub async fn client_status( + &self, + client_id: impl ToString, + ) -> Result< + cosmos_modules::ibc_client::QueryClientStatusResponse, + DaemonError, + > { + let response: cosmos_modules::ibc_client::QueryClientStatusResponse = { + use crate::cosmos_modules::ibc_client::{ + query_client::QueryClient, QueryClientStatusRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryClientStatusRequest { + client_id: client_id.to_string(), + }; + let response = client + .client_status(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 95u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(response) + } + /// Get the ibc client parameters + pub async fn client_params( + &self, + ) -> Result< + cosmos_modules::ibc_client::QueryClientParamsResponse, + DaemonError, + > { + let response: cosmos_modules::ibc_client::QueryClientParamsResponse = { + use crate::cosmos_modules::ibc_client::{ + query_client::QueryClient, QueryClientParamsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryClientParamsRequest {}; + let response = client + .client_params(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 111u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(response) + } + /// Query the IBC connections for a specific chain + pub async fn connections( + &self, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_connection::QueryConnectionsResponse; + let ibc_connections: QueryConnectionsResponse = { + use crate::cosmos_modules::ibc_connection::{ + query_client::QueryClient, QueryConnectionsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryConnectionsRequest { + pagination: None, + }; + let response = client + .connections(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 121u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_connections.connections) + } + /// Search for open connections with a specific chain. + pub async fn open_connections( + &self, + client_chain_id: impl ToString, + ) -> Result, 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) + } + /// Get all the connections for this client + pub async fn client_connections( + &self, + client_id: impl Into, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_connection::QueryClientConnectionsResponse; + let client_id = client_id.into(); + let ibc_client_connections: QueryClientConnectionsResponse = { + use crate::cosmos_modules::ibc_connection::{ + query_client::QueryClient, QueryClientConnectionsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryClientConnectionsRequest { + client_id: client_id.clone(), + }; + let response = client + .client_connections(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 163u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_client_connections.connection_paths) + } + /// Get the (tendermint) client state for a specific connection + pub async fn connection_client( + &self, + connection_id: impl Into, + ) -> Result { + use cosmos_modules::ibc_connection::QueryConnectionClientStateResponse; + let connection_id = connection_id.into(); + let ibc_connection_client: QueryConnectionClientStateResponse = { + use crate::cosmos_modules::ibc_connection::{ + query_client::QueryClient, QueryConnectionClientStateRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryConnectionClientStateRequest { + connection_id: connection_id.clone(), + }; + let response = client + .connection_client_state(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 183u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + let client_state = ibc_connection_client + .identified_client_state + .ok_or( + DaemonError::ibc_err({ + let res = ::alloc::fmt::format( + format_args!( + "error identifying client for connection {0}", + connection_id, + ), + ); + res + }), + )?; + let client_state = ClientState::decode( + client_state.client_state.unwrap().value.as_slice(), + ) + .map_err(|e| DaemonError::ibc_err({ + let res = ::alloc::fmt::format( + format_args!("error decoding client state: {0}", e), + ); + res + }))?; + Ok(client_state) + } + /// Get the channel for a specific port and channel id + pub async fn channel( + &self, + port_id: impl Into, + channel_id: impl Into, + ) -> Result { + use cosmos_modules::ibc_channel::QueryChannelResponse; + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_channel: QueryChannelResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryChannelRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryChannelRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }; + let response = client.channel(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 218u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + ibc_channel + .channel + .ok_or( + DaemonError::ibc_err({ + let res = ::alloc::fmt::format( + format_args!( + "error fetching channel {0} on port {1}", + channel_id, + port_id, + ), + ); + res + }), + ) + } + /// Get all the channels for a specific connection + pub async fn connection_channels( + &self, + connection_id: impl Into, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_channel::QueryConnectionChannelsResponse; + let connection_id = connection_id.into(); + let ibc_connection_channels: QueryConnectionChannelsResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryConnectionChannelsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryConnectionChannelsRequest { + connection: connection_id.clone(), + pagination: None, + }; + let response = client + .connection_channels(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 242u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_connection_channels.channels) + } + /// Get the client state for a specific channel and port + pub async fn channel_client_state( + &self, + port_id: impl Into, + channel_id: impl Into, + ) -> Result { + use cosmos_modules::ibc_channel::QueryChannelClientStateResponse; + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_channel_client_state: QueryChannelClientStateResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryChannelClientStateRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryChannelClientStateRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }; + let response = client + .channel_client_state(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 265u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + ibc_channel_client_state + .identified_client_state + .ok_or( + DaemonError::ibc_err({ + let res = ::alloc::fmt::format( + format_args!( + "error identifying client for channel {0} on port {1}", + channel_id, + port_id, + ), + ); + res + }), + ) + } + /// Get all the packet commitments for a specific channel and port + pub async fn packet_commitments( + &self, + port_id: impl Into, + channel_id: impl Into, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_channel::QueryPacketCommitmentsResponse; + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_commitments: QueryPacketCommitmentsResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryPacketCommitmentsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPacketCommitmentsRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + pagination: None, + }; + let response = client + .packet_commitments(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 297u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_commitments.commitments) + } + /// Get the packet commitment for a specific channel, port and sequence + pub async fn packet_commitment( + &self, + port_id: impl Into, + channel_id: impl Into, + sequence: u64, + ) -> Result { + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_commitment: QueryPacketCommitmentResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryPacketCommitmentRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPacketCommitmentRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + sequence: sequence, + }; + let response = client + .packet_commitment(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 320u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_commitment) + } + /// Returns if the packet is received on the connected chain. + pub async fn packet_receipt( + &self, + port_id: impl Into, + channel_id: impl Into, + sequence: u64, + ) -> Result { + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_receipt: ibc_channel::QueryPacketReceiptResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryPacketReceiptRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPacketReceiptRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + sequence: sequence, + }; + let response = client + .packet_receipt(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 345u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_receipt.received) + } + /// Get all the packet acknowledgements for a specific channel, port and commitment sequences + pub async fn packet_acknowledgements( + &self, + port_id: impl Into, + channel_id: impl Into, + packet_commitment_sequences: Vec, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_channel::QueryPacketAcknowledgementsResponse; + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_acknowledgements: QueryPacketAcknowledgementsResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryPacketAcknowledgementsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPacketAcknowledgementsRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + packet_commitment_sequences: packet_commitment_sequences, + pagination: None, + }; + let response = client + .packet_acknowledgements(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 372u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_acknowledgements.acknowledgements) + } + /// Get the packet acknowledgement for a specific channel, port and sequence + pub async fn packet_acknowledgement( + &self, + port_id: impl Into, + channel_id: impl Into, + sequence: u64, + ) -> Result, DaemonError> { + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_acknowledgement: ibc_channel::QueryPacketAcknowledgementResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryPacketAcknowledgementRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPacketAcknowledgementRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + sequence: sequence, + }; + let response = client + .packet_acknowledgement(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 396u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_acknowledgement.acknowledgement) + } + /// No acknowledgement exists on receiving chain for the given packet commitment sequence on sending chain. + /// Returns the packet sequences that have not yet been received. + pub async fn unreceived_packets( + &self, + port_id: impl Into, + channel_id: impl Into, + packet_commitment_sequences: Vec, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_channel::QueryUnreceivedPacketsResponse; + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_unreceived: QueryUnreceivedPacketsResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryUnreceivedPacketsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryUnreceivedPacketsRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + packet_commitment_sequences: packet_commitment_sequences, + }; + let response = client + .unreceived_packets(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 422u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_unreceived.sequences) + } + /// Returns the acknowledgement sequences that have not yet been received. + /// Given a list of acknowledgement sequences from counterparty, determine if an ack on the counterparty chain has been received on the executing chain. + /// Returns the list of acknowledgement sequences that have not yet been received. + pub async fn unreceived_acks( + &self, + port_id: impl Into, + channel_id: impl Into, + packet_ack_sequences: Vec, + ) -> Result, DaemonError> { + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_unreceived: ibc_channel::QueryUnreceivedAcksResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryUnreceivedAcksRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryUnreceivedAcksRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + packet_ack_sequences: packet_ack_sequences, + }; + let response = client + .unreceived_acks(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 447u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_unreceived.sequences) + } + /// Returns the acknowledgement sequences that have not yet been received. + /// Given a list of acknowledgement sequences from counterparty, determine if an ack on the counterparty chain has been received on the executing chain. + /// Returns the list of acknowledgement sequences that have not yet been received. + pub async fn next_sequence_receive( + &self, + port_id: impl Into, + channel_id: impl Into, + ) -> Result { + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let next_receive: ibc_channel::QueryNextSequenceReceiveResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryNextSequenceReceiveRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryNextSequenceReceiveRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }; + let response = client + .next_sequence_receive(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 471u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(next_receive.next_sequence_receive) + } + } + } + mod node { + use std::{cmp::min, time::Duration}; + use crate::{cosmos_modules, error::DaemonError, tx_resp::CosmTxResponse}; + use cosmrs::{ + proto::cosmos::{ + base::query::v1beta1::PageRequest, tx::v1beta1::SimulateResponse, + }, + tendermint::{Block, Time}, + }; + use tonic::transport::Channel; + use super::DaemonQuerier; + const MAX_TX_QUERY_RETRIES: usize = 50; + /// Querier for the Tendermint node. + /// Supports queries for block and tx information + pub struct Node { + channel: Channel, + } + impl DaemonQuerier for Node { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Node { + /// Returns node info + pub async fn info( + &self, + ) -> Result { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_node_info(cosmos_modules::tendermint::GetNodeInfoRequest { + }) + .await? + .into_inner(); + Ok(resp) + } + /// Queries node syncing + pub async fn syncing(&self) -> Result { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_syncing(cosmos_modules::tendermint::GetSyncingRequest { + }) + .await? + .into_inner(); + Ok(resp.syncing) + } + /// Returns latests block information + pub async fn latest_block(&self) -> Result { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_latest_block(cosmos_modules::tendermint::GetLatestBlockRequest { + }) + .await? + .into_inner(); + Ok(Block::try_from(resp.block.unwrap())?) + } + /// Returns block information fetched by height + pub async fn block_by_height( + &self, + height: u64, + ) -> Result { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_block_by_height(cosmos_modules::tendermint::GetBlockByHeightRequest { + height: height as i64, + }) + .await? + .into_inner(); + Ok(Block::try_from(resp.block.unwrap())?) + } + /// Return the average block time for the last 50 blocks or since inception + /// This is used to estimate the time when a tx will be included in a block + pub async fn average_block_speed( + &self, + multiplier: Option, + ) -> Result { + let mut latest_block = self.latest_block().await?; + let latest_block_time = latest_block.header.time; + let mut latest_block_height = latest_block.header.height.value(); + while latest_block_height <= 1 { + tokio::time::sleep(Duration::from_secs(1)).await; + latest_block = self.latest_block().await?; + latest_block_height = latest_block.header.height.value(); + } + let avg_period = min(latest_block_height - 1, 50); + let block_avg_period_ago = self + .block_by_height(latest_block_height - avg_period) + .await?; + let block_avg_period_ago_time = block_avg_period_ago.header.time; + let average_block_time = latest_block_time + .duration_since(block_avg_period_ago_time)?; + let average_block_time = average_block_time.as_secs() / avg_period; + let average_block_time = match multiplier { + Some(multiplier) => (average_block_time as f32 * multiplier) as u64, + None => average_block_time, + }; + Ok(std::cmp::max(average_block_time, 1)) + } + /// Returns latests validator set + pub async fn latest_validator_set( + &self, + pagination: Option, + ) -> Result< + cosmos_modules::tendermint::GetLatestValidatorSetResponse, + DaemonError, + > { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_latest_validator_set(cosmos_modules::tendermint::GetLatestValidatorSetRequest { + pagination, + }) + .await? + .into_inner(); + Ok(resp) + } + /// Returns latests validator set fetched by height + pub async fn validator_set_by_height( + &self, + height: i64, + pagination: Option, + ) -> Result< + cosmos_modules::tendermint::GetValidatorSetByHeightResponse, + DaemonError, + > { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_validator_set_by_height(cosmos_modules::tendermint::GetValidatorSetByHeightRequest { + height, + pagination, + }) + .await? + .into_inner(); + Ok(resp) + } + /// Returns current block height + pub async fn block_height(&self) -> Result { + let block = self.latest_block().await?; + Ok(block.header.height.value()) + } + /// Returns the block timestamp (since unix epoch) in nanos + pub async fn block_time(&self) -> Result { + let block = self.latest_block().await?; + Ok(block.header.time.duration_since(Time::unix_epoch())?.as_nanos()) + } + /// Simulate TX + pub async fn simulate_tx( + &self, + tx_bytes: Vec, + ) -> Result { + let mut client = cosmos_modules::tx::service_client::ServiceClient::new( + self.channel.clone(), + ); + #[allow(deprecated)] + let resp: SimulateResponse = client + .simulate(cosmos_modules::tx::SimulateRequest { + tx: None, + tx_bytes, + }) + .await? + .into_inner(); + let gas_used = resp.gas_info.unwrap().gas_used; + Ok(gas_used) + } + /// Returns all the block info + pub async fn block_info( + &self, + ) -> Result { + let block = self.latest_block().await?; + let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; + let time = cosmwasm_std::Timestamp::from_nanos( + since_epoch.as_nanos() as u64, + ); + Ok(cosmwasm_std::BlockInfo { + height: block.header.height.value(), + time, + chain_id: block.header.chain_id.to_string(), + }) + } + /// Find TX by hash + pub async fn find_tx( + &self, + hash: String, + ) -> Result { + self.find_tx_with_retries(hash, MAX_TX_QUERY_RETRIES).await + } + /// Find TX by hash with a given amount of retries + pub async fn find_tx_with_retries( + &self, + hash: String, + retries: usize, + ) -> Result { + let mut client = cosmos_modules::tx::service_client::ServiceClient::new( + self.channel.clone(), + ); + let request = cosmos_modules::tx::GetTxRequest { + hash: hash.clone(), + }; + let mut block_speed = self.average_block_speed(Some(0.7)).await?; + for _ in 0..retries { + match client.get_tx(request.clone()).await { + Ok(tx) => { + let resp = tx.into_inner().tx_response.unwrap(); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("TX found: {0:?}", resp), + lvl, + &( + "cw_orch_daemon::queriers::node", + "cw_orch_daemon::queriers::node", + "cw-orch-daemon/src/queriers/node.rs", + ), + 220u32, + ::log::__private_api::Option::None, + ); + } + }; + return Ok(resp.into()); + } + Err(err) => { + block_speed = (block_speed as f64 * 1.6) as u64; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("TX not found with error: {0:?}", err), + lvl, + &( + "cw_orch_daemon::queriers::node", + "cw_orch_daemon::queriers::node", + "cw-orch-daemon/src/queriers/node.rs", + ), + 226u32, + ::log::__private_api::Option::None, + ); + } + }; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Waiting {0} seconds", block_speed), + lvl, + &( + "cw_orch_daemon::queriers::node", + "cw_orch_daemon::queriers::node", + "cw-orch-daemon/src/queriers/node.rs", + ), + 227u32, + ::log::__private_api::Option::None, + ); + } + }; + tokio::time::sleep(Duration::from_secs(block_speed)).await; + } + } + } + Err(DaemonError::TXNotFound(hash, retries)) + } + } + } + mod staking { + use crate::{cosmos_modules, error::DaemonError}; + use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; + use tonic::transport::Channel; + use super::DaemonQuerier; + /// Querier for the Cosmos Staking module + pub struct Staking { + channel: Channel, + } + impl DaemonQuerier for Staking { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Staking { + /// Queries validator info for given validator address + pub async fn validator( + &self, + validator_addr: impl Into, + ) -> Result { + let validator: cosmos_modules::staking::QueryValidatorResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryValidatorRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryValidatorRequest { + validator_addr: validator_addr.into(), + }; + let response = client.validator(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 24u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(validator.validator.unwrap()) + } + /// Queries all validators that match the given status + /// + /// see [StakingBondStatus] for available statuses + pub async fn validators( + &self, + status: StakingBondStatus, + ) -> Result, DaemonError> { + let validators: cosmos_modules::staking::QueryValidatorsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryValidatorsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryValidatorsRequest { + status: status.to_string(), + pagination: None, + }; + let response = client + .validators(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 42u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(validators.validators) + } + /// Query validator delegations info for given validator + /// + /// see [PageRequest] for pagination + pub async fn delegations( + &self, + validator_addr: impl Into, + pagination: Option, + ) -> Result, DaemonError> { + let validator_delegations: cosmos_modules::staking::QueryValidatorDelegationsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryValidatorDelegationsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryValidatorDelegationsRequest { + validator_addr: validator_addr.into(), + pagination: pagination, + }; + let response = client + .validator_delegations(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 62u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(validator_delegations.delegation_responses) + } + /// Query validator unbonding delegations of a validator + pub async fn unbonding_delegations( + &self, + validator_addr: impl Into, + ) -> Result, DaemonError> { + let validator_unbonding_delegations: cosmos_modules::staking::QueryValidatorUnbondingDelegationsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, + QueryValidatorUnbondingDelegationsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryValidatorUnbondingDelegationsRequest { + validator_addr: validator_addr.into(), + pagination: None, + }; + let response = client + .validator_unbonding_delegations(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 79u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(validator_unbonding_delegations.unbonding_responses) + } + /// Query delegation info for given validator for a delegator + pub async fn delegation( + &self, + validator_addr: impl Into, + delegator_addr: impl Into, + ) -> Result { + let delegation: cosmos_modules::staking::QueryDelegationResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryDelegationRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDelegationRequest { + validator_addr: validator_addr.into(), + delegator_addr: delegator_addr.into(), + }; + let response = client + .delegation(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 97u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(delegation.delegation_response.unwrap()) + } + /// Query unbonding delegation info for given validator delegator + pub async fn unbonding_delegation( + &self, + validator_addr: impl Into, + delegator_addr: impl Into, + ) -> Result { + let unbonding_delegation: cosmos_modules::staking::QueryUnbondingDelegationResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryUnbondingDelegationRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryUnbondingDelegationRequest { + validator_addr: validator_addr.into(), + delegator_addr: delegator_addr.into(), + }; + let response = client + .unbonding_delegation(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 115u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(unbonding_delegation.unbond.unwrap()) + } + /// Query all delegator delegations of a given delegator address + /// + /// see [PageRequest] for pagination + pub async fn delegator_delegations( + &self, + delegator_addr: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::staking::QueryDelegatorDelegationsResponse, + DaemonError, + > { + let delegator_delegations: cosmos_modules::staking::QueryDelegatorDelegationsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryDelegatorDelegationsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDelegatorDelegationsRequest { + delegator_addr: delegator_addr.into(), + pagination: pagination, + }; + let response = client + .delegator_delegations(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 135u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(delegator_delegations) + } + /// Queries all unbonding delegations of a given delegator address. + /// + /// see [PageRequest] for pagination + pub async fn delegator_unbonding_delegations( + &self, + delegator_addr: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::staking::QueryDelegatorUnbondingDelegationsResponse, + DaemonError, + > { + let delegator_unbonding_delegations: cosmos_modules::staking::QueryDelegatorUnbondingDelegationsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, + QueryDelegatorUnbondingDelegationsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDelegatorUnbondingDelegationsRequest { + delegator_addr: delegator_addr.into(), + pagination: pagination, + }; + let response = client + .delegator_unbonding_delegations(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 156u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(delegator_unbonding_delegations) + } + /// Query redelegations of a given address + /// + /// see [PageRequest] for pagination + pub async fn redelegations( + &self, + delegator_addr: impl Into, + src_validator_addr: impl Into, + dst_validator_addr: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::staking::QueryRedelegationsResponse, + DaemonError, + > { + let redelegations: cosmos_modules::staking::QueryRedelegationsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryRedelegationsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryRedelegationsRequest { + delegator_addr: delegator_addr.into(), + src_validator_addr: src_validator_addr.into(), + dst_validator_addr: dst_validator_addr.into(), + pagination: pagination, + }; + let response = client + .redelegations(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 178u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(redelegations) + } + /// Query delegator validators info for given delegator address. + pub async fn delegator_validator( + &self, + validator_addr: impl Into, + delegator_addr: impl Into, + ) -> Result< + cosmos_modules::staking::QueryDelegatorValidatorResponse, + DaemonError, + > { + let delegator_validator: cosmos_modules::staking::QueryDelegatorValidatorResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryDelegatorValidatorRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDelegatorValidatorRequest { + validator_addr: validator_addr.into(), + delegator_addr: delegator_addr.into(), + }; + let response = client + .delegator_validator(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 198u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(delegator_validator) + } + /// Query delegator validators info for given delegator address + /// + /// see [PageRequest] for pagination + pub async fn delegator_validators( + &self, + delegator_addr: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::staking::QueryDelegatorValidatorsResponse, + DaemonError, + > { + let delegator_validators: cosmos_modules::staking::QueryDelegatorValidatorsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryDelegatorValidatorsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDelegatorValidatorsRequest { + delegator_addr: delegator_addr.into(), + pagination: pagination, + }; + let response = client + .delegator_validators(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 218u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(delegator_validators) + } + /// Query historical info info for given height + pub async fn historical_info( + &self, + height: i64, + ) -> Result< + cosmos_modules::staking::QueryHistoricalInfoResponse, + DaemonError, + > { + let historical_info: cosmos_modules::staking::QueryHistoricalInfoResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryHistoricalInfoRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryHistoricalInfoRequest { + height: height, + }; + let response = client + .historical_info(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 236u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(historical_info) + } + /// Query the pool info + pub async fn pool( + &self, + ) -> Result { + let pool: cosmos_modules::staking::QueryPoolResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryPoolRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPoolRequest {}; + let response = client.pool(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 248u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(pool) + } + /// Query staking parameters + pub async fn params( + &self, + ) -> Result { + let params: cosmos_modules::staking::QueryParamsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryParamsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryParamsRequest {}; + let response = client.params(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 257u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(params) + } + } + /// Staking bond statuses + pub enum StakingBondStatus { + /// UNSPECIFIED defines an invalid validator status. + Unspecified = 0, + /// UNBONDED defines a validator that is not bonded. + Unbonded = 1, + /// UNBONDING defines a validator that is unbonding. + Unbonding = 2, + /// BONDED defines a validator that is bonded. + Bonded = 3, + } + impl ToString for StakingBondStatus { + /// Convert to string + fn to_string(&self) -> String { + match self { + StakingBondStatus::Unspecified => { + "BOND_STATUS_UNSPECIFIED".to_string() + } + StakingBondStatus::Unbonded => "BOND_STATUS_UNBONDED".to_string(), + StakingBondStatus::Unbonding => "BOND_STATUS_UNBONDING".to_string(), + StakingBondStatus::Bonded => "BOND_STATUS_BONDED".to_string(), + } + } + } + } + pub use bank::Bank; + pub use cosmwasm::CosmWasm; + pub use feegrant::Feegrant; + pub use ibc::Ibc; + pub use node::Node; + pub use gov::*; + pub use staking::*; + use tonic::transport::Channel; + /// Constructor for a querier over a given channel + pub trait DaemonQuerier { + /// Construct an new querier over a given channel + fn new(channel: Channel) -> Self; + } +} +mod traits { + use cw_orch_core::{ + contract::interface_traits::{CwOrchMigrate, CwOrchUpload}, + environment::TxResponse, + }; + use crate::{queriers::CosmWasm, Daemon, DaemonError}; + /// Helper methods for conditional uploading of a contract. + pub trait ConditionalUpload: CwOrchUpload { + /// Only upload the contract if it is not uploaded yet (checksum does not match) + fn upload_if_needed(&self) -> Result>, DaemonError> { + if self.latest_is_uploaded()? { + Ok(None) + } else { + Some(self.upload()).transpose().map_err(Into::into) + } + } + /// Returns whether the checksum of the WASM file matches the checksum of the latest uploaded code for this contract. + fn latest_is_uploaded(&self) -> Result { + let Some(latest_uploaded_code_id) = self.code_id().ok() else { + return Ok(false); + }; + let chain = self.get_chain(); + let on_chain_hash = chain + .rt_handle + .block_on( + chain + .query_client::() + .code_id_hash(latest_uploaded_code_id), + )?; + let local_hash = self.wasm().checksum()?; + Ok(local_hash == on_chain_hash) + } + /// Returns whether the contract is running the latest uploaded code for it + fn is_running_latest(&self) -> Result { + let Some(latest_uploaded_code_id) = self.code_id().ok() else { + return Ok(false); + }; + let chain = self.get_chain(); + let info = chain + .rt_handle + .block_on( + chain.query_client::().contract_info(self.address()?), + )?; + Ok(latest_uploaded_code_id == info.code_id) + } + } + impl ConditionalUpload for T + where + T: CwOrchUpload, + {} + /// Helper methods for conditional migration of a contract. + pub trait ConditionalMigrate: CwOrchMigrate + ConditionalUpload { + /// Only migrate the contract if it is not on the latest code-id yet + fn migrate_if_needed( + &self, + migrate_msg: &Self::MigrateMsg, + ) -> Result>, DaemonError> { + if self.is_running_latest()? { + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "{0} is already running the latest code", + self.id(), + ), + lvl, + &( + "cw_orch_daemon::traits", + "cw_orch_daemon::traits", + "cw-orch-daemon/src/traits.rs", + ), + 61u32, + ::log::__private_api::Option::None, + ); + } + }; + Ok(None) + } else { + Some(self.migrate(migrate_msg, self.code_id()?)) + .transpose() + .map_err(Into::into) + } + } + /// Uploads the contract if the local contract hash is different from the latest on-chain code hash. + /// Proceeds to migrates the contract if the contract is not running the latest code. + fn upload_and_migrate_if_needed( + &self, + migrate_msg: &Self::MigrateMsg, + ) -> Result>>, DaemonError> { + let mut txs = Vec::with_capacity(2); + if let Some(tx) = self.upload_if_needed()? { + txs.push(tx); + } + if let Some(tx) = self.migrate_if_needed(migrate_msg)? { + txs.push(tx); + } + if txs.is_empty() { Ok(None) } else { Ok(Some(txs)) } + } + } + impl ConditionalMigrate for T + where + T: CwOrchMigrate + CwOrchUpload, + {} +} +pub mod tx_builder { + use cosmrs::tx::{ModeInfo, SignMode}; + use cosmrs::{ + proto::cosmos::auth::v1beta1::BaseAccount, tendermint::chain::Id, + tx::{self, Body, Fee, Msg, Raw, SequenceNumber, SignDoc, SignerInfo}, + Any, Coin, + }; + use secp256k1::All; + use super::{sender::Sender, DaemonError}; + const GAS_BUFFER: f64 = 1.3; + const BUFFER_THRESHOLD: u64 = 200_000; + const SMALL_GAS_BUFFER: f64 = 1.4; + /// Struct used to build a raw transaction and broadcast it with a sender. + pub struct TxBuilder { + pub(crate) body: Body, + pub(crate) fee_amount: Option, + pub(crate) gas_limit: Option, + pub(crate) sequence: Option, + } + #[automatically_derived] + impl ::core::clone::Clone for TxBuilder { + #[inline] + fn clone(&self) -> TxBuilder { + TxBuilder { + body: ::core::clone::Clone::clone(&self.body), + fee_amount: ::core::clone::Clone::clone(&self.fee_amount), + gas_limit: ::core::clone::Clone::clone(&self.gas_limit), + sequence: ::core::clone::Clone::clone(&self.sequence), + } + } + } + #[automatically_derived] + impl ::core::fmt::Debug for TxBuilder { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field4_finish( + f, + "TxBuilder", + "body", + &self.body, + "fee_amount", + &self.fee_amount, + "gas_limit", + &self.gas_limit, + "sequence", + &&self.sequence, + ) + } + } + impl TxBuilder { + /// Create a new TxBuilder with a given body. + pub fn new(body: Body) -> Self { + Self { + body, + fee_amount: None, + gas_limit: None, + sequence: None, + } + } + /// Set a fixed fee amount for the tx + pub fn fee_amount(&mut self, fee_amount: u128) -> &mut Self { + self.fee_amount = Some(fee_amount); + self + } + /// Set a gas limit for the tx + pub fn gas_limit(&mut self, gas_limit: u64) -> &mut Self { + self.gas_limit = Some(gas_limit); + self + } + /// Set a sequence number for the tx + pub fn sequence(&mut self, sequence: u64) -> &mut Self { + self.sequence = Some(sequence); + self + } + /// Builds the body of the tx with a given memo and timeout. + pub fn build_body( + msgs: Vec, + memo: Option<&str>, + timeout: u64, + ) -> tx::Body { + let msgs = msgs + .into_iter() + .map(Msg::into_any) + .collect::, _>>() + .unwrap(); + tx::Body::new(msgs, memo.unwrap_or_default(), timeout as u32) + } + pub(crate) fn build_fee( + amount: impl Into, + denom: &str, + gas_limit: u64, + ) -> Fee { + let fee = Coin::new(amount.into(), denom).unwrap(); + Fee::from_amount_and_gas(fee, gas_limit) + } + /// Builds the raw tx with a given body and fee and signs it. + /// Sets the TxBuilder's gas limit to its simulated amount for later use. + pub async fn build(&mut self, wallet: &Sender) -> Result { + let BaseAccount { account_number, sequence, .. } = wallet + .base_account() + .await?; + let sequence = self.sequence.unwrap_or(sequence); + let (tx_fee, gas_limit) = if let (Some(fee), Some(gas_limit)) + = (self.fee_amount, self.gas_limit) { + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Using pre-defined fee and gas limits: {0}, {1}", + fee, + gas_limit, + ), + lvl, + &( + "cw_orch_daemon::tx_builder", + "cw_orch_daemon::tx_builder", + "cw-orch-daemon/src/tx_builder.rs", + ), + 91u32, + ::log::__private_api::Option::None, + ); + } + }; + (fee, gas_limit) + } else { + let sim_gas_used = wallet + .calculate_gas(&self.body, sequence, account_number) + .await?; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Simulated gas needed {0:?}", sim_gas_used), + lvl, + &( + "cw_orch_daemon::tx_builder", + "cw_orch_daemon::tx_builder", + "cw-orch-daemon/src/tx_builder.rs", + ), + 101u32, + ::log::__private_api::Option::None, + ); + } + }; + let gas_expected = if sim_gas_used < BUFFER_THRESHOLD { + sim_gas_used as f64 * SMALL_GAS_BUFFER + } else { + sim_gas_used as f64 * GAS_BUFFER + }; + let fee_amount = gas_expected + * (wallet + .daemon_state + .chain_data + .fees + .fee_tokens[0] + .fixed_min_gas_price + 0.00001); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Calculated fee needed: {0:?}", fee_amount), + lvl, + &( + "cw_orch_daemon::tx_builder", + "cw_orch_daemon::tx_builder", + "cw-orch-daemon/src/tx_builder.rs", + ), + 111u32, + ::log::__private_api::Option::None, + ); + } + }; + self.gas_limit = Some(gas_expected as u64); + (fee_amount as u128, gas_expected as u64) + }; + let fee = Self::build_fee( + tx_fee, + &wallet.daemon_state.chain_data.fees.fee_tokens[0].denom, + gas_limit, + ); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "submitting tx: \n fee: {0:?}\naccount_nr: {1:?}\nsequence: {2:?}", + fee, + account_number, + sequence, + ), + lvl, + &( + "cw_orch_daemon::tx_builder", + "cw_orch_daemon::tx_builder", + "cw-orch-daemon/src/tx_builder.rs", + ), + 125u32, + ::log::__private_api::Option::None, + ); + } + }; + let auth_info = SignerInfo { + public_key: wallet.private_key.get_signer_public_key(&wallet.secp), + mode_info: ModeInfo::single(SignMode::Direct), + sequence, + } + .auth_info(fee); + let sign_doc = SignDoc::new( + &self.body, + &auth_info, + &Id::try_from(wallet.daemon_state.chain_data.chain_id.to_string())?, + account_number, + )?; + wallet.sign(sign_doc).map_err(Into::into) + } + } +} +pub use self::{ + builder::*, channel::*, core::*, error::*, state::*, sync::*, traits::*, tx_resp::*, +}; +pub use cw_orch_networks::chain_info::*; +pub use cw_orch_networks::networks; +pub use sender::Wallet; +pub use tx_builder::TxBuilder; +pub(crate) mod cosmos_modules { + pub use cosmrs::proto::{ + cosmos::{ + auth::v1beta1 as auth, authz::v1beta1 as authz, bank::v1beta1 as bank, + base::{ + abci::v1beta1 as abci, tendermint::v1beta1 as tendermint, v1beta1 as base, + }, + crisis::v1beta1 as crisis, distribution::v1beta1 as distribution, + evidence::v1beta1 as evidence, feegrant::v1beta1 as feegrant, + gov::v1beta1 as gov, mint::v1beta1 as mint, params::v1beta1 as params, + slashing::v1beta1 as slashing, staking::v1beta1 as staking, + tx::v1beta1 as tx, vesting::v1beta1 as vesting, + }, + cosmwasm::wasm::v1 as cosmwasm, + ibc::{ + applications::transfer::v1 as ibc_transfer, + core::{ + channel::v1 as ibc_channel, client::v1 as ibc_client, + connection::v1 as ibc_connection, + }, + }, + tendermint::abci as tendermint_abci, + }; +} +/// Re-export trait and data required to fetch daemon data from chain-registry +pub use ibc_chain_registry::{ + chain::ChainData as ChainRegistryData, fetchable::Fetchable, +}; +#![feature(prelude_import)] +//! `Daemon` and `DaemonAsync` execution environments. +//! +//! The `Daemon` type is a synchronous wrapper around the `DaemonAsync` type and can be used as a contract execution environment. +#[prelude_import] +use std::prelude::rust_2021::*; +#[macro_use] +extern crate std; +pub mod builder { + use crate::{DaemonAsync, DaemonBuilder}; + use std::rc::Rc; + use ibc_chain_registry::chain::ChainData; + use super::{error::DaemonError, sender::Sender, state::DaemonState}; + /// The default deployment id if none is provided + pub const DEFAULT_DEPLOYMENT: &str = "default"; + /// Create [`DaemonAsync`] through [`DaemonAsyncBuilder`] + /// ## Example + /// ```no_run + /// # tokio_test::block_on(async { + /// use cw_orch_daemon::{DaemonAsyncBuilder, networks}; + /// let daemon = DaemonAsyncBuilder::default() + /// .chain(networks::LOCAL_JUNO) + /// .deployment_id("v0.1.0") + /// .build() + /// .await.unwrap(); + /// # }) + /// ``` + pub struct DaemonAsyncBuilder { + pub(crate) chain: Option, + pub(crate) deployment_id: Option, + /// Wallet mnemonic + pub(crate) mnemonic: Option, + } + #[automatically_derived] + impl ::core::clone::Clone for DaemonAsyncBuilder { + #[inline] + fn clone(&self) -> DaemonAsyncBuilder { + DaemonAsyncBuilder { + chain: ::core::clone::Clone::clone(&self.chain), + deployment_id: ::core::clone::Clone::clone(&self.deployment_id), + mnemonic: ::core::clone::Clone::clone(&self.mnemonic), + } + } + } + #[automatically_derived] + impl ::core::default::Default for DaemonAsyncBuilder { + #[inline] + fn default() -> DaemonAsyncBuilder { + DaemonAsyncBuilder { + chain: ::core::default::Default::default(), + deployment_id: ::core::default::Default::default(), + mnemonic: ::core::default::Default::default(), + } + } + } + impl DaemonAsyncBuilder { + /// Set the chain the daemon will connect to + pub fn chain(&mut self, chain: impl Into) -> &mut Self { + self.chain = Some(chain.into()); + self + } + /// Set the deployment id to use for the daemon interactions + /// Defaults to `default` + pub fn deployment_id(&mut self, deployment_id: impl Into) -> &mut Self { + self.deployment_id = Some(deployment_id.into()); + self + } + /// Set the mnemonic to use with this chain. + /// Defaults to env variable depending on the environment. + /// + /// Variables: LOCAL_MNEMONIC, TEST_MNEMONIC and MAIN_MNEMONIC + pub fn mnemonic(&mut self, mnemonic: impl ToString) -> &mut Self { + self.mnemonic = Some(mnemonic.to_string()); + self + } + /// Build a daemon + pub async fn build(&self) -> Result { + let chain = self + .chain + .clone() + .ok_or(DaemonError::BuilderMissing("chain information".into()))?; + let deployment_id = self + .deployment_id + .clone() + .unwrap_or(DEFAULT_DEPLOYMENT.to_string()); + let state = Rc::new(DaemonState::new(chain, deployment_id).await?); + let sender = if let Some(mnemonic) = &self.mnemonic { + Sender::from_mnemonic(&state, mnemonic)? + } else { + Sender::new(&state)? + }; + let daemon = DaemonAsync { + state, + sender: Rc::new(sender), + }; + Ok(daemon) + } + } + impl From for DaemonAsyncBuilder { + fn from(value: DaemonBuilder) -> Self { + DaemonAsyncBuilder { + chain: value.chain, + deployment_id: value.deployment_id, + mnemonic: value.mnemonic, + } + } + } +} +pub mod channel { + use cosmrs::proto::cosmos::base::tendermint::v1beta1::{ + service_client::ServiceClient, GetNodeInfoRequest, + }; + use ibc_chain_registry::chain::Grpc; + use ibc_relayer_types::core::ics24_host::identifier::ChainId; + use tonic::transport::{Channel, ClientTlsConfig}; + use super::error::DaemonError; + /// A helper for constructing a gRPC channel + pub struct GrpcChannel {} + impl GrpcChannel { + /// Connect to any of the provided gRPC endpoints + pub async fn connect( + grpc: &[Grpc], + chain_id: &ChainId, + ) -> Result { + let mut successful_connections = ::alloc::vec::Vec::new(); + for Grpc { address, .. } in grpc.iter() { + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Trying to connect to endpoint: {0}", address), + lvl, + &( + "cw_orch_daemon::channel", + "cw_orch_daemon::channel", + "cw-orch-daemon/src/channel.rs", + ), + 19u32, + ::log::__private_api::Option::None, + ); + } + }; + let endpoint = Channel::builder(address.clone().try_into().unwrap()); + let maybe_client = ServiceClient::connect(endpoint.clone()).await; + let mut client = if maybe_client.is_ok() { + maybe_client? + } else { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Cannot connect to gRPC endpoint: {0}, {1:?}", + address, + maybe_client.unwrap_err(), + ), + lvl, + &( + "cw_orch_daemon::channel", + "cw_orch_daemon::channel", + "cw-orch-daemon/src/channel.rs", + ), + 31u32, + ::log::__private_api::Option::None, + ); + } + }; + if !(address.contains("https") || address.contains("443")) { + continue; + } + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Attempting to connect with TLS"), + lvl, + &( + "cw_orch_daemon::channel", + "cw_orch_daemon::channel", + "cw-orch-daemon/src/channel.rs", + ), + 43u32, + ::log::__private_api::Option::None, + ); + } + }; + let endpoint = endpoint.clone().tls_config(ClientTlsConfig::new())?; + let maybe_client = ServiceClient::connect(endpoint.clone()).await; + if maybe_client.is_err() { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "Cannot connect to gRPC endpoint: {0}, {1:?}", + address, + maybe_client.unwrap_err(), + ), + lvl, + &( + "cw_orch_daemon::channel", + "cw_orch_daemon::channel", + "cw-orch-daemon/src/channel.rs", + ), + 51u32, + ::log::__private_api::Option::None, + ); + } + }; + continue; + } + maybe_client? + }; + let node_info = client + .get_node_info(GetNodeInfoRequest {}) + .await? + .into_inner(); + if ChainId::is_epoch_format( + &node_info.default_node_info.as_ref().unwrap().network, + ) { + if node_info.default_node_info.as_ref().unwrap().network + != chain_id.as_str() + { + { + let lvl = ::log::Level::Error; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "Network mismatch: connection:{0} != config:{1}", + node_info.default_node_info.as_ref().unwrap().network, + chain_id.as_str(), + ), + lvl, + &( + "cw_orch_daemon::channel", + "cw_orch_daemon::channel", + "cw-orch-daemon/src/channel.rs", + ), + 72u32, + ::log::__private_api::Option::None, + ); + } + }; + continue; + } + } + successful_connections.push(endpoint.connect().await?) + } + if successful_connections.is_empty() { + return Err(DaemonError::CannotConnectGRPC); + } + Ok(successful_connections.pop().unwrap()) + } + } +} +pub mod core { + use crate::{queriers::CosmWasm, DaemonState}; + use super::{ + builder::DaemonAsyncBuilder, cosmos_modules, error::DaemonError, + queriers::{DaemonQuerier, Node}, + sender::Wallet, tx_resp::CosmTxResponse, + }; + use cosmrs::{ + cosmwasm::{MsgExecuteContract, MsgInstantiateContract, MsgMigrateContract}, + tendermint::Time, AccountId, Denom, + }; + use cosmwasm_std::{Addr, Coin}; + use cw_orch_core::{ + contract::interface_traits::Uploadable, environment::{ChainState, IndexResponse}, + }; + use serde::{de::DeserializeOwned, Serialize}; + use serde_json::from_str; + use std::{ + fmt::Debug, rc::Rc, str::{from_utf8, FromStr}, + time::Duration, + }; + use tonic::transport::Channel; + /** + Represents a blockchain node. + It's constructed using [`DaemonAsyncBuilder`]. + + ## Usage + ```rust,no_run + # tokio_test::block_on(async { + use cw_orch_daemon::{DaemonAsync, networks}; + + let daemon: DaemonAsync = DaemonAsync::builder() + .chain(networks::JUNO_1) + .build() + .await.unwrap(); + # }) + ``` + ## Environment Execution + + The DaemonAsync implements async methods of [`TxHandler`](cw_orch_core::environment::TxHandler) which allows you to perform transactions on the chain. + + ## Querying + + Different Cosmos SDK modules can be queried through the daemon by calling the [`DaemonAsync::query_client`] method with a specific querier. + See [Querier](crate::queriers) for examples. +*/ + pub struct DaemonAsync { + /// Sender to send transactions to the chain + pub sender: Wallet, + /// State of the daemon + pub state: Rc, + } + #[automatically_derived] + impl ::core::clone::Clone for DaemonAsync { + #[inline] + fn clone(&self) -> DaemonAsync { + DaemonAsync { + sender: ::core::clone::Clone::clone(&self.sender), + state: ::core::clone::Clone::clone(&self.state), + } + } + } + impl DaemonAsync { + /// Get the daemon builder + pub fn builder() -> DaemonAsyncBuilder { + DaemonAsyncBuilder::default() + } + /// Perform a query with a given query client. + /// See [Querier](crate::queriers) for examples. + pub fn query_client(&self) -> Querier { + Querier::new(self.sender.channel()) + } + /// Get the channel configured for this DaemonAsync. + pub fn channel(&self) -> Channel { + self.state.grpc_channel.clone() + } + } + impl ChainState for DaemonAsync { + type Out = Rc; + fn state(&self) -> Self::Out { + self.state.clone() + } + } + impl DaemonAsync { + /// Get the sender address + pub fn sender(&self) -> Addr { + self.sender.address().unwrap() + } + /// Execute a message on a contract. + pub async fn execute( + &self, + exec_msg: &E, + coins: &[cosmwasm_std::Coin], + contract_address: &Addr, + ) -> Result { + let exec_msg: MsgExecuteContract = MsgExecuteContract { + sender: self.sender.pub_addr()?, + contract: AccountId::from_str(contract_address.as_str())?, + msg: serde_json::to_vec(&exec_msg)?, + funds: parse_cw_coins(coins)?, + }; + let result = self + .sender + .commit_tx( + <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([exec_msg])), + None, + ) + .await?; + Ok(result) + } + /// Instantiate a contract. + pub async fn instantiate( + &self, + code_id: u64, + init_msg: &I, + label: Option<&str>, + admin: Option<&Addr>, + coins: &[Coin], + ) -> Result { + let sender = &self.sender; + let init_msg = MsgInstantiateContract { + code_id, + label: Some(label.unwrap_or("instantiate_contract").to_string()), + admin: admin.map(|a| FromStr::from_str(a.as_str()).unwrap()), + sender: sender.pub_addr()?, + msg: serde_json::to_vec(&init_msg)?, + funds: parse_cw_coins(coins)?, + }; + let result = sender + .commit_tx( + <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([init_msg])), + None, + ) + .await?; + Ok(result) + } + /// Query a contract. + pub async fn query( + &self, + query_msg: &Q, + contract_address: &Addr, + ) -> Result { + let sender = &self.sender; + let mut client = cosmos_modules::cosmwasm::query_client::QueryClient::new( + sender.channel(), + ); + let resp = client + .smart_contract_state(cosmos_modules::cosmwasm::QuerySmartContractStateRequest { + address: contract_address.to_string(), + query_data: serde_json::to_vec(&query_msg)?, + }) + .await?; + Ok(from_str(from_utf8(&resp.into_inner().data).unwrap())?) + } + /// Migration a contract. + pub async fn migrate( + &self, + migrate_msg: &M, + new_code_id: u64, + contract_address: &Addr, + ) -> Result { + let exec_msg: MsgMigrateContract = MsgMigrateContract { + sender: self.sender.pub_addr()?, + contract: AccountId::from_str(contract_address.as_str())?, + msg: serde_json::to_vec(&migrate_msg)?, + code_id: new_code_id, + }; + let result = self + .sender + .commit_tx( + <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([exec_msg])), + None, + ) + .await?; + Ok(result) + } + /// Wait for a given amount of blocks. + pub async fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { + let mut last_height = self.query_client::().block_height().await?; + let end_height = last_height + amount; + let average_block_speed = self + .query_client::() + .average_block_speed(Some(0.9)) + .await?; + let wait_time = average_block_speed * amount; + tokio::time::sleep(Duration::from_secs(wait_time)).await; + while last_height < end_height { + tokio::time::sleep(Duration::from_secs(average_block_speed)).await; + last_height = self.query_client::().block_height().await?; + } + Ok(()) + } + /// Wait for a given amount of seconds. + pub async fn wait_seconds(&self, secs: u64) -> Result<(), DaemonError> { + tokio::time::sleep(Duration::from_secs(secs)).await; + Ok(()) + } + /// Wait for the next block. + pub async fn next_block(&self) -> Result<(), DaemonError> { + self.wait_blocks(1).await + } + /// Get the current block info. + pub async fn block_info(&self) -> Result { + let block = self.query_client::().latest_block().await?; + let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; + let time = cosmwasm_std::Timestamp::from_nanos( + since_epoch.as_nanos() as u64, + ); + Ok(cosmwasm_std::BlockInfo { + height: block.header.height.value(), + time, + chain_id: block.header.chain_id.to_string(), + }) + } + /// Upload a contract to the chain. + pub async fn upload( + &self, + uploadable: &impl Uploadable, + ) -> Result { + let sender = &self.sender; + let wasm_path = uploadable.wasm(); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Uploading file at {0:?}", wasm_path), + lvl, + &( + "cw_orch_daemon::core", + "cw_orch_daemon::core", + "cw-orch-daemon/src/core.rs", + ), + 233u32, + ::log::__private_api::Option::None, + ); + } + }; + let file_contents = std::fs::read(wasm_path.path())?; + let store_msg = cosmrs::cosmwasm::MsgStoreCode { + sender: sender.pub_addr()?, + wasm_byte_code: file_contents, + instantiate_permission: None, + }; + let result = sender + .commit_tx( + <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([store_msg])), + None, + ) + .await?; + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Uploaded: {0:?}", result.txhash), + lvl, + &( + "cw_orch_daemon::core", + "cw_orch_daemon::core", + "cw-orch-daemon/src/core.rs", + ), + 244u32, + ::log::__private_api::Option::None, + ); + } + }; + let code_id = result.uploaded_code_id().unwrap(); + let wasm = CosmWasm::new(self.channel()); + while wasm.code(code_id).await.is_err() { + self.next_block().await?; + } + Ok(result) + } + /// Set the sender to use with this DaemonAsync to be the given wallet + pub fn set_sender(&mut self, sender: &Wallet) { + self.sender = sender.clone(); + } + } + pub(crate) fn parse_cw_coins( + coins: &[cosmwasm_std::Coin], + ) -> Result, DaemonError> { + coins + .iter() + .map(|cosmwasm_std::Coin { amount, denom }| { + Ok(cosmrs::Coin { + amount: amount.u128(), + denom: Denom::from_str(denom)?, + }) + }) + .collect::, DaemonError>>() + } +} +pub mod error { + #![allow(missing_docs)] + use cw_orch_core::CwEnvError; + use thiserror::Error; + pub enum DaemonError { + #[error("Reqwest HTTP(s) Error")] + ReqwestError(#[from] ::reqwest::Error), + #[error("JSON Conversion Error")] + SerdeJson(#[from] ::serde_json::Error), + #[error(transparent)] + ParseIntError(#[from] std::num::ParseIntError), + #[error(transparent)] + IOErr(#[from] ::std::io::Error), + #[error(transparent)] + Secp256k1(#[from] ::secp256k1::Error), + #[error(transparent)] + VarError(#[from] ::std::env::VarError), + #[error(transparent)] + AnyError(#[from] ::anyhow::Error), + #[error(transparent)] + Status(#[from] ::tonic::Status), + #[error(transparent)] + TransportError(#[from] ::tonic::transport::Error), + #[error(transparent)] + TendermintError(#[from] ::cosmrs::tendermint::Error), + #[error(transparent)] + CwEnvError(#[from] ::cw_orch_core::CwEnvError), + #[error("Bech32 Decode Error")] + Bech32DecodeErr, + #[error( + "Bech32 Decode Error: Key Failed prefix {0} or length {1} Wanted:{2}/{3}" + )] + Bech32DecodeExpanded(String, usize, String, usize), + #[error("Mnemonic - Wrong length, it should be 24 words")] + WrongLength, + #[error("Mnemonic - Bad Phrase")] + Phrasing, + #[error("Mnemonic - Missing Phrase")] + MissingPhrase, + #[error("Bad Implementation. Missing Component")] + Implementation, + #[error("Unable to convert into public key `{key}`")] + Conversion { key: String, source: bitcoin::bech32::Error }, + #[error( + "Can not augment daemon deployment after usage in more than one contract." + )] + SharedDaemonState, + #[error(transparent)] + ErrReport(#[from] ::eyre::ErrReport), + #[error(transparent)] + GRpcDecodeError(#[from] ::prost::DecodeError), + #[error(transparent)] + ED25519(#[from] ::ed25519_dalek::ed25519::Error), + #[error(transparent)] + DecodeError(#[from] ::base64::DecodeError), + #[error(transparent)] + HexError(#[from] ::hex::FromHexError), + #[error(transparent)] + BitCoinBip32(#[from] ::bitcoin::bip32::Error), + #[error("83 length-missing SECP256K1 prefix")] + ConversionSECP256k1, + #[error("82 length-missing ED25519 prefix")] + ConversionED25519, + #[error("Expected Key length of 82 or 83 length was {0}")] + ConversionLength(usize), + #[error("Expected Key length of 40 length was {0}")] + ConversionLengthED25519Hex(usize), + #[error( + "Expected ED25519 key of length 32 with a BECH32 ED25519 prefix of 5 chars - Len {0} - Hex {1}" + )] + ConversionPrefixED25519(usize, String), + #[error("Can't call Transactions without some gas rules")] + NoGasOpts, + #[error("Can't parse `{parse}` into a coin")] + CoinParseErrV { parse: String }, + #[error("Can't parse `{0}` into a coin")] + CoinParseErr(String), + #[error("TX submit returned `{0}` - {1} '{2}'")] + TxResultError(usize, String, String), + #[error("No price found for Gas using denom {0}")] + GasPriceError(String), + #[error( + "Attempting to fetch validator set in parts, and failed Height mismatch {0} {1}" + )] + TendermintValidatorSet(u64, u64), + #[error("Transaction {0} not found after {1} attempts")] + TXNotFound(String, usize), + #[error("unknown API error")] + Unknown, + #[error("Generic Error {0}")] + StdErr(String), + #[error("calling contract with unimplemented action")] + NotImplemented, + #[error("new chain detected, fill out the scaffold at {0}")] + NewChain(String), + #[error("new network detected, fill out the scaffold at {0}")] + NewNetwork(String), + #[error("Can not connect to any grpc endpoint that was provided.")] + CannotConnectGRPC, + #[error("tx failed: {reason} with code {code}")] + TxFailed { code: usize, reason: String }, + #[error("The list of grpc endpoints is empty")] + GRPCListIsEmpty, + #[error("no wasm path provided for contract.")] + MissingWasmPath, + #[error("daemon builder missing {0}")] + BuilderMissing(String), + #[error("ibc error: {0}")] + IbcError(String), + #[error("insufficient fee, check gas price: {0}")] + InsufficientFee(String), + } + #[allow(unused_qualifications)] + impl std::error::Error for DaemonError { + fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> { + use thiserror::__private::AsDynError; + #[allow(deprecated)] + match self { + DaemonError::ReqwestError { 0: source, .. } => { + std::option::Option::Some(source.as_dyn_error()) + } + DaemonError::SerdeJson { 0: source, .. } => { + std::option::Option::Some(source.as_dyn_error()) + } + DaemonError::ParseIntError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::IOErr { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::Secp256k1 { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::VarError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::AnyError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::Status { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::TransportError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::TendermintError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::CwEnvError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::Bech32DecodeErr { .. } => std::option::Option::None, + DaemonError::Bech32DecodeExpanded { .. } => std::option::Option::None, + DaemonError::WrongLength { .. } => std::option::Option::None, + DaemonError::Phrasing { .. } => std::option::Option::None, + DaemonError::MissingPhrase { .. } => std::option::Option::None, + DaemonError::Implementation { .. } => std::option::Option::None, + DaemonError::Conversion { source: source, .. } => { + std::option::Option::Some(source.as_dyn_error()) + } + DaemonError::SharedDaemonState { .. } => std::option::Option::None, + DaemonError::ErrReport { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::GRpcDecodeError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::ED25519 { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::DecodeError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::HexError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::BitCoinBip32 { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::ConversionSECP256k1 { .. } => std::option::Option::None, + DaemonError::ConversionED25519 { .. } => std::option::Option::None, + DaemonError::ConversionLength { .. } => std::option::Option::None, + DaemonError::ConversionLengthED25519Hex { .. } => { + std::option::Option::None + } + DaemonError::ConversionPrefixED25519 { .. } => std::option::Option::None, + DaemonError::NoGasOpts { .. } => std::option::Option::None, + DaemonError::CoinParseErrV { .. } => std::option::Option::None, + DaemonError::CoinParseErr { .. } => std::option::Option::None, + DaemonError::TxResultError { .. } => std::option::Option::None, + DaemonError::GasPriceError { .. } => std::option::Option::None, + DaemonError::TendermintValidatorSet { .. } => std::option::Option::None, + DaemonError::TXNotFound { .. } => std::option::Option::None, + DaemonError::Unknown { .. } => std::option::Option::None, + DaemonError::StdErr { .. } => std::option::Option::None, + DaemonError::NotImplemented { .. } => std::option::Option::None, + DaemonError::NewChain { .. } => std::option::Option::None, + DaemonError::NewNetwork { .. } => std::option::Option::None, + DaemonError::CannotConnectGRPC { .. } => std::option::Option::None, + DaemonError::TxFailed { .. } => std::option::Option::None, + DaemonError::GRPCListIsEmpty { .. } => std::option::Option::None, + DaemonError::MissingWasmPath { .. } => std::option::Option::None, + DaemonError::BuilderMissing { .. } => std::option::Option::None, + DaemonError::IbcError { .. } => std::option::Option::None, + DaemonError::InsufficientFee { .. } => std::option::Option::None, + } + } + } + #[allow(unused_qualifications)] + impl std::fmt::Display for DaemonError { + fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + use thiserror::__private::AsDisplay as _; + #[allow(unused_variables, deprecated, clippy::used_underscore_binding)] + match self { + DaemonError::ReqwestError(_0) => { + __formatter.write_fmt(format_args!("Reqwest HTTP(s) Error")) + } + DaemonError::SerdeJson(_0) => { + __formatter.write_fmt(format_args!("JSON Conversion Error")) + } + DaemonError::ParseIntError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::IOErr(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::Secp256k1(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::VarError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::AnyError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::Status(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::TransportError(_0) => { + std::fmt::Display::fmt(_0, __formatter) + } + DaemonError::TendermintError(_0) => { + std::fmt::Display::fmt(_0, __formatter) + } + DaemonError::CwEnvError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::Bech32DecodeErr {} => { + __formatter.write_fmt(format_args!("Bech32 Decode Error")) + } + DaemonError::Bech32DecodeExpanded(_0, _1, _2, _3) => { + __formatter + .write_fmt( + format_args!( + "Bech32 Decode Error: Key Failed prefix {0} or length {1} Wanted:{2}/{3}", + _0.as_display(), + _1.as_display(), + _2.as_display(), + _3.as_display(), + ), + ) + } + DaemonError::WrongLength {} => { + __formatter + .write_fmt( + format_args!( + "Mnemonic - Wrong length, it should be 24 words", + ), + ) + } + DaemonError::Phrasing {} => { + __formatter.write_fmt(format_args!("Mnemonic - Bad Phrase")) + } + DaemonError::MissingPhrase {} => { + __formatter.write_fmt(format_args!("Mnemonic - Missing Phrase")) + } + DaemonError::Implementation {} => { + __formatter + .write_fmt(format_args!("Bad Implementation. Missing Component")) + } + DaemonError::Conversion { key, source } => { + __formatter + .write_fmt( + format_args!( + "Unable to convert into public key `{0}`", + key.as_display(), + ), + ) + } + DaemonError::SharedDaemonState {} => { + __formatter + .write_fmt( + format_args!( + "Can not augment daemon deployment after usage in more than one contract.", + ), + ) + } + DaemonError::ErrReport(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::GRpcDecodeError(_0) => { + std::fmt::Display::fmt(_0, __formatter) + } + DaemonError::ED25519(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::DecodeError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::HexError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::BitCoinBip32(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::ConversionSECP256k1 {} => { + __formatter + .write_fmt(format_args!("83 length-missing SECP256K1 prefix")) + } + DaemonError::ConversionED25519 {} => { + __formatter + .write_fmt(format_args!("82 length-missing ED25519 prefix")) + } + DaemonError::ConversionLength(_0) => { + __formatter + .write_fmt( + format_args!( + "Expected Key length of 82 or 83 length was {0}", + _0.as_display(), + ), + ) + } + DaemonError::ConversionLengthED25519Hex(_0) => { + __formatter + .write_fmt( + format_args!( + "Expected Key length of 40 length was {0}", + _0.as_display(), + ), + ) + } + DaemonError::ConversionPrefixED25519(_0, _1) => { + __formatter + .write_fmt( + format_args!( + "Expected ED25519 key of length 32 with a BECH32 ED25519 prefix of 5 chars - Len {0} - Hex {1}", + _0.as_display(), + _1.as_display(), + ), + ) + } + DaemonError::NoGasOpts {} => { + __formatter + .write_fmt( + format_args!( + "Can\'t call Transactions without some gas rules", + ), + ) + } + DaemonError::CoinParseErrV { parse } => { + __formatter + .write_fmt( + format_args!( + "Can\'t parse `{0}` into a coin", + parse.as_display(), + ), + ) + } + DaemonError::CoinParseErr(_0) => { + __formatter + .write_fmt( + format_args!( + "Can\'t parse `{0}` into a coin", + _0.as_display(), + ), + ) + } + DaemonError::TxResultError(_0, _1, _2) => { + __formatter + .write_fmt( + format_args!( + "TX submit returned `{0}` - {1} \'{2}\'", + _0.as_display(), + _1.as_display(), + _2.as_display(), + ), + ) + } + DaemonError::GasPriceError(_0) => { + __formatter + .write_fmt( + format_args!( + "No price found for Gas using denom {0}", + _0.as_display(), + ), + ) + } + DaemonError::TendermintValidatorSet(_0, _1) => { + __formatter + .write_fmt( + format_args!( + "Attempting to fetch validator set in parts, and failed Height mismatch {0} {1}", + _0.as_display(), + _1.as_display(), + ), + ) + } + DaemonError::TXNotFound(_0, _1) => { + __formatter + .write_fmt( + format_args!( + "Transaction {0} not found after {1} attempts", + _0.as_display(), + _1.as_display(), + ), + ) + } + DaemonError::Unknown {} => { + __formatter.write_fmt(format_args!("unknown API error")) + } + DaemonError::StdErr(_0) => { + __formatter + .write_fmt(format_args!("Generic Error {0}", _0.as_display())) + } + DaemonError::NotImplemented {} => { + __formatter + .write_fmt( + format_args!("calling contract with unimplemented action"), + ) + } + DaemonError::NewChain(_0) => { + __formatter + .write_fmt( + format_args!( + "new chain detected, fill out the scaffold at {0}", + _0.as_display(), + ), + ) + } + DaemonError::NewNetwork(_0) => { + __formatter + .write_fmt( + format_args!( + "new network detected, fill out the scaffold at {0}", + _0.as_display(), + ), + ) + } + DaemonError::CannotConnectGRPC {} => { + __formatter + .write_fmt( + format_args!( + "Can not connect to any grpc endpoint that was provided.", + ), + ) + } + DaemonError::TxFailed { code, reason } => { + __formatter + .write_fmt( + format_args!( + "tx failed: {0} with code {1}", + reason.as_display(), + code.as_display(), + ), + ) + } + DaemonError::GRPCListIsEmpty {} => { + __formatter + .write_fmt(format_args!("The list of grpc endpoints is empty")) + } + DaemonError::MissingWasmPath {} => { + __formatter + .write_fmt(format_args!("no wasm path provided for contract.")) + } + DaemonError::BuilderMissing(_0) => { + __formatter + .write_fmt( + format_args!("daemon builder missing {0}", _0.as_display()), + ) + } + DaemonError::IbcError(_0) => { + __formatter + .write_fmt(format_args!("ibc error: {0}", _0.as_display())) + } + DaemonError::InsufficientFee(_0) => { + __formatter + .write_fmt( + format_args!( + "insufficient fee, check gas price: {0}", + _0.as_display(), + ), + ) + } + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::reqwest::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::reqwest::Error) -> Self { + DaemonError::ReqwestError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::serde_json::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::serde_json::Error) -> Self { + DaemonError::SerdeJson { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From for DaemonError { + #[allow(deprecated)] + fn from(source: std::num::ParseIntError) -> Self { + DaemonError::ParseIntError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::std::io::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::std::io::Error) -> Self { + DaemonError::IOErr { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::secp256k1::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::secp256k1::Error) -> Self { + DaemonError::Secp256k1 { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::std::env::VarError> for DaemonError { + #[allow(deprecated)] + fn from(source: ::std::env::VarError) -> Self { + DaemonError::VarError { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::anyhow::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::anyhow::Error) -> Self { + DaemonError::AnyError { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::tonic::Status> for DaemonError { + #[allow(deprecated)] + fn from(source: ::tonic::Status) -> Self { + DaemonError::Status { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::tonic::transport::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::tonic::transport::Error) -> Self { + DaemonError::TransportError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::cosmrs::tendermint::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::cosmrs::tendermint::Error) -> Self { + DaemonError::TendermintError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::cw_orch_core::CwEnvError> for DaemonError { + #[allow(deprecated)] + fn from(source: ::cw_orch_core::CwEnvError) -> Self { + DaemonError::CwEnvError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::eyre::ErrReport> for DaemonError { + #[allow(deprecated)] + fn from(source: ::eyre::ErrReport) -> Self { + DaemonError::ErrReport { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::prost::DecodeError> for DaemonError { + #[allow(deprecated)] + fn from(source: ::prost::DecodeError) -> Self { + DaemonError::GRpcDecodeError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::ed25519_dalek::ed25519::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::ed25519_dalek::ed25519::Error) -> Self { + DaemonError::ED25519 { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::base64::DecodeError> for DaemonError { + #[allow(deprecated)] + fn from(source: ::base64::DecodeError) -> Self { + DaemonError::DecodeError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::hex::FromHexError> for DaemonError { + #[allow(deprecated)] + fn from(source: ::hex::FromHexError) -> Self { + DaemonError::HexError { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::bitcoin::bip32::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::bitcoin::bip32::Error) -> Self { + DaemonError::BitCoinBip32 { + 0: source, + } + } + } + #[automatically_derived] + impl ::core::fmt::Debug for DaemonError { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + DaemonError::ReqwestError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ReqwestError", + &__self_0, + ) + } + DaemonError::SerdeJson(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "SerdeJson", + &__self_0, + ) + } + DaemonError::ParseIntError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ParseIntError", + &__self_0, + ) + } + DaemonError::IOErr(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "IOErr", + &__self_0, + ) + } + DaemonError::Secp256k1(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "Secp256k1", + &__self_0, + ) + } + DaemonError::VarError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "VarError", + &__self_0, + ) + } + DaemonError::AnyError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "AnyError", + &__self_0, + ) + } + DaemonError::Status(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "Status", + &__self_0, + ) + } + DaemonError::TransportError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "TransportError", + &__self_0, + ) + } + DaemonError::TendermintError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "TendermintError", + &__self_0, + ) + } + DaemonError::CwEnvError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "CwEnvError", + &__self_0, + ) + } + DaemonError::Bech32DecodeErr => { + ::core::fmt::Formatter::write_str(f, "Bech32DecodeErr") + } + DaemonError::Bech32DecodeExpanded( + __self_0, + __self_1, + __self_2, + __self_3, + ) => { + ::core::fmt::Formatter::debug_tuple_field4_finish( + f, + "Bech32DecodeExpanded", + __self_0, + __self_1, + __self_2, + &__self_3, + ) + } + DaemonError::WrongLength => { + ::core::fmt::Formatter::write_str(f, "WrongLength") + } + DaemonError::Phrasing => ::core::fmt::Formatter::write_str(f, "Phrasing"), + DaemonError::MissingPhrase => { + ::core::fmt::Formatter::write_str(f, "MissingPhrase") + } + DaemonError::Implementation => { + ::core::fmt::Formatter::write_str(f, "Implementation") + } + DaemonError::Conversion { key: __self_0, source: __self_1 } => { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "Conversion", + "key", + __self_0, + "source", + &__self_1, + ) + } + DaemonError::SharedDaemonState => { + ::core::fmt::Formatter::write_str(f, "SharedDaemonState") + } + DaemonError::ErrReport(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ErrReport", + &__self_0, + ) + } + DaemonError::GRpcDecodeError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "GRpcDecodeError", + &__self_0, + ) + } + DaemonError::ED25519(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ED25519", + &__self_0, + ) + } + DaemonError::DecodeError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "DecodeError", + &__self_0, + ) + } + DaemonError::HexError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "HexError", + &__self_0, + ) + } + DaemonError::BitCoinBip32(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "BitCoinBip32", + &__self_0, + ) + } + DaemonError::ConversionSECP256k1 => { + ::core::fmt::Formatter::write_str(f, "ConversionSECP256k1") + } + DaemonError::ConversionED25519 => { + ::core::fmt::Formatter::write_str(f, "ConversionED25519") + } + DaemonError::ConversionLength(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ConversionLength", + &__self_0, + ) + } + DaemonError::ConversionLengthED25519Hex(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ConversionLengthED25519Hex", + &__self_0, + ) + } + DaemonError::ConversionPrefixED25519(__self_0, __self_1) => { + ::core::fmt::Formatter::debug_tuple_field2_finish( + f, + "ConversionPrefixED25519", + __self_0, + &__self_1, + ) + } + DaemonError::NoGasOpts => { + ::core::fmt::Formatter::write_str(f, "NoGasOpts") + } + DaemonError::CoinParseErrV { parse: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "CoinParseErrV", + "parse", + &__self_0, + ) + } + DaemonError::CoinParseErr(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "CoinParseErr", + &__self_0, + ) + } + DaemonError::TxResultError(__self_0, __self_1, __self_2) => { + ::core::fmt::Formatter::debug_tuple_field3_finish( + f, + "TxResultError", + __self_0, + __self_1, + &__self_2, + ) + } + DaemonError::GasPriceError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "GasPriceError", + &__self_0, + ) + } + DaemonError::TendermintValidatorSet(__self_0, __self_1) => { + ::core::fmt::Formatter::debug_tuple_field2_finish( + f, + "TendermintValidatorSet", + __self_0, + &__self_1, + ) + } + DaemonError::TXNotFound(__self_0, __self_1) => { + ::core::fmt::Formatter::debug_tuple_field2_finish( + f, + "TXNotFound", + __self_0, + &__self_1, + ) + } + DaemonError::Unknown => ::core::fmt::Formatter::write_str(f, "Unknown"), + DaemonError::StdErr(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "StdErr", + &__self_0, + ) + } + DaemonError::NotImplemented => { + ::core::fmt::Formatter::write_str(f, "NotImplemented") + } + DaemonError::NewChain(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "NewChain", + &__self_0, + ) + } + DaemonError::NewNetwork(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "NewNetwork", + &__self_0, + ) + } + DaemonError::CannotConnectGRPC => { + ::core::fmt::Formatter::write_str(f, "CannotConnectGRPC") + } + DaemonError::TxFailed { code: __self_0, reason: __self_1 } => { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "TxFailed", + "code", + __self_0, + "reason", + &__self_1, + ) + } + DaemonError::GRPCListIsEmpty => { + ::core::fmt::Formatter::write_str(f, "GRPCListIsEmpty") + } + DaemonError::MissingWasmPath => { + ::core::fmt::Formatter::write_str(f, "MissingWasmPath") + } + DaemonError::BuilderMissing(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "BuilderMissing", + &__self_0, + ) + } + DaemonError::IbcError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "IbcError", + &__self_0, + ) + } + DaemonError::InsufficientFee(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "InsufficientFee", + &__self_0, + ) + } + } + } + } + impl DaemonError { + pub fn ibc_err(msg: impl ToString) -> Self { + Self::IbcError(msg.to_string()) + } + } + impl From for CwEnvError { + fn from(val: DaemonError) -> Self { + CwEnvError::AnyError(val.into()) + } + } +} +pub(crate) mod json_file { + use serde_json::{from_reader, json, Value}; + use std::fs::{File, OpenOptions}; + pub fn write( + filename: &String, + chain_id: &String, + network_id: &String, + deploy_id: &String, + ) { + let file = OpenOptions::new() + .create(true) + .read(true) + .write(true) + .truncate(false) + .open(filename) + .unwrap(); + let mut json: Value = if file.metadata().unwrap().len().eq(&0) { + ::serde_json::Value::Object(::serde_json::Map::new()) + } else { + from_reader(file).unwrap() + }; + if json.get(network_id).is_none() { + json[network_id] = ::serde_json::Value::Object(::serde_json::Map::new()); + } + if json[network_id].get(chain_id).is_none() { + json[network_id][chain_id] = ::serde_json::Value::Object({ + let mut object = ::serde_json::Map::new(); + let _ = object + .insert( + (deploy_id).into(), + ::serde_json::Value::Object(::serde_json::Map::new()), + ); + let _ = object + .insert( + ("code_ids").into(), + ::serde_json::Value::Object(::serde_json::Map::new()), + ); + object + }); + } + serde_json::to_writer_pretty(File::create(filename).unwrap(), &json).unwrap(); + } + pub fn read(filename: &String) -> Value { + let file = File::open(filename) + .unwrap_or_else(|_| { + ::core::panicking::panic_fmt( + format_args!("File should be present at {0}", filename), + ); + }); + let json: serde_json::Value = from_reader(file).unwrap(); + json + } +} +/// Proto types for different blockchains +pub mod proto { + pub mod injective { + #![allow(missing_docs)] + use crate::DaemonError; + use cosmrs::tx::SignDoc; + use cosmrs::{proto::traits::TypeUrl, tx::Raw}; + pub const ETHEREUM_COIN_TYPE: u32 = 60; + pub struct InjectiveEthAccount { + #[prost(message, optional, tag = "1")] + pub base_account: ::core::option::Option< + super::super::cosmos_modules::auth::BaseAccount, + >, + #[prost(bytes, tag = "2")] + pub code_hash: Vec, + } + #[automatically_derived] + impl ::core::clone::Clone for InjectiveEthAccount { + #[inline] + fn clone(&self) -> InjectiveEthAccount { + InjectiveEthAccount { + base_account: ::core::clone::Clone::clone(&self.base_account), + code_hash: ::core::clone::Clone::clone(&self.code_hash), + } + } + } + #[automatically_derived] + impl ::core::marker::StructuralPartialEq for InjectiveEthAccount {} + #[automatically_derived] + impl ::core::cmp::PartialEq for InjectiveEthAccount { + #[inline] + fn eq(&self, other: &InjectiveEthAccount) -> bool { + self.base_account == other.base_account + && self.code_hash == other.code_hash + } + } + impl ::prost::Message for InjectiveEthAccount { + #[allow(unused_variables)] + fn encode_raw(&self, buf: &mut B) + where + B: ::prost::bytes::BufMut, + { + if let Some(ref msg) = self.base_account { + ::prost::encoding::message::encode(1u32, msg, buf); + } + if self.code_hash != b"" as &[u8] { + ::prost::encoding::bytes::encode(2u32, &self.code_hash, buf); + } + } + #[allow(unused_variables)] + fn merge_field( + &mut self, + tag: u32, + wire_type: ::prost::encoding::WireType, + buf: &mut B, + ctx: ::prost::encoding::DecodeContext, + ) -> ::core::result::Result<(), ::prost::DecodeError> + where + B: ::prost::bytes::Buf, + { + const STRUCT_NAME: &'static str = "InjectiveEthAccount"; + match tag { + 1u32 => { + let mut value = &mut self.base_account; + ::prost::encoding::message::merge( + wire_type, + value.get_or_insert_with(::core::default::Default::default), + buf, + ctx, + ) + .map_err(|mut error| { + error.push(STRUCT_NAME, "base_account"); + error + }) + } + 2u32 => { + let mut value = &mut self.code_hash; + ::prost::encoding::bytes::merge(wire_type, value, buf, ctx) + .map_err(|mut error| { + error.push(STRUCT_NAME, "code_hash"); + error + }) + } + _ => ::prost::encoding::skip_field(wire_type, tag, buf, ctx), + } + } + #[inline] + fn encoded_len(&self) -> usize { + 0 + + self + .base_account + .as_ref() + .map_or( + 0, + |msg| ::prost::encoding::message::encoded_len(1u32, msg), + ) + + if self.code_hash != b"" as &[u8] { + ::prost::encoding::bytes::encoded_len(2u32, &self.code_hash) + } else { + 0 + } + } + fn clear(&mut self) { + self.base_account = ::core::option::Option::None; + self.code_hash.clear(); + } + } + impl ::core::default::Default for InjectiveEthAccount { + fn default() -> Self { + InjectiveEthAccount { + base_account: ::core::default::Default::default(), + code_hash: ::core::default::Default::default(), + } + } + } + impl ::core::fmt::Debug for InjectiveEthAccount { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + let mut builder = f.debug_struct("InjectiveEthAccount"); + let builder = { + let wrapper = &self.base_account; + builder.field("base_account", &wrapper) + }; + let builder = { + let wrapper = { + fn ScalarWrapper(v: T) -> T { + v + } + ScalarWrapper(&self.code_hash) + }; + builder.field("code_hash", &wrapper) + }; + builder.finish() + } + } + pub struct InjectivePubKey { + #[prost(bytes, tag = 1)] + pub key: Vec, + } + #[automatically_derived] + impl ::core::clone::Clone for InjectivePubKey { + #[inline] + fn clone(&self) -> InjectivePubKey { + InjectivePubKey { + key: ::core::clone::Clone::clone(&self.key), + } + } + } + #[automatically_derived] + impl ::core::marker::StructuralPartialEq for InjectivePubKey {} + #[automatically_derived] + impl ::core::cmp::PartialEq for InjectivePubKey { + #[inline] + fn eq(&self, other: &InjectivePubKey) -> bool { + self.key == other.key + } + } + impl ::prost::Message for InjectivePubKey { + #[allow(unused_variables)] + fn encode_raw(&self, buf: &mut B) + where + B: ::prost::bytes::BufMut, + { + if self.key != b"" as &[u8] { + ::prost::encoding::bytes::encode(1u32, &self.key, buf); + } + } + #[allow(unused_variables)] + fn merge_field( + &mut self, + tag: u32, + wire_type: ::prost::encoding::WireType, + buf: &mut B, + ctx: ::prost::encoding::DecodeContext, + ) -> ::core::result::Result<(), ::prost::DecodeError> + where + B: ::prost::bytes::Buf, + { + const STRUCT_NAME: &'static str = "InjectivePubKey"; + match tag { + 1u32 => { + let mut value = &mut self.key; + ::prost::encoding::bytes::merge(wire_type, value, buf, ctx) + .map_err(|mut error| { + error.push(STRUCT_NAME, "key"); + error + }) + } + _ => ::prost::encoding::skip_field(wire_type, tag, buf, ctx), + } + } + #[inline] + fn encoded_len(&self) -> usize { + 0 + + if self.key != b"" as &[u8] { + ::prost::encoding::bytes::encoded_len(1u32, &self.key) + } else { + 0 + } + } + fn clear(&mut self) { + self.key.clear(); + } + } + impl ::core::default::Default for InjectivePubKey { + fn default() -> Self { + InjectivePubKey { + key: ::core::default::Default::default(), + } + } + } + impl ::core::fmt::Debug for InjectivePubKey { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + let mut builder = f.debug_struct("InjectivePubKey"); + let builder = { + let wrapper = { + fn ScalarWrapper(v: T) -> T { + v + } + ScalarWrapper(&self.key) + }; + builder.field("key", &wrapper) + }; + builder.finish() + } + } + impl TypeUrl for InjectivePubKey { + const TYPE_URL: &'static str = "/injective.crypto.v1beta1.ethsecp256k1.PubKey"; + } + pub trait InjectiveSigner { + fn sign_injective(&self, sign_doc: SignDoc) -> Result; + } + } +} +pub mod sender { + use crate::{networks::ChainKind, proto::injective::ETHEREUM_COIN_TYPE}; + use super::{ + cosmos_modules::{self, auth::BaseAccount}, + error::DaemonError, queriers::{DaemonQuerier, Node}, + state::DaemonState, tx_builder::TxBuilder, tx_resp::CosmTxResponse, + }; + use crate::proto::injective::InjectiveEthAccount; + use crate::{core::parse_cw_coins, keys::private::PrivateKey}; + use cosmrs::{ + bank::MsgSend, crypto::secp256k1::SigningKey, proto::traits::Message, + tendermint::chain::Id, + tx::{self, ModeInfo, Msg, Raw, SignDoc, SignMode, SignerInfo}, + AccountId, + }; + use cosmwasm_std::Addr; + use secp256k1::{All, Context, Secp256k1, Signing}; + use std::{convert::TryFrom, env, rc::Rc, str::FromStr}; + use cosmos_modules::vesting::PeriodicVestingAccount; + use tonic::transport::Channel; + /// A wallet is a sender of transactions, can be safely cloned and shared within the same thread. + pub type Wallet = Rc>; + /// Signer of the transactions and helper for address derivation + /// This is the main interface for simulating and signing transactions + pub struct Sender { + pub private_key: PrivateKey, + pub secp: Secp256k1, + pub(crate) daemon_state: Rc, + } + impl Sender { + pub fn new(daemon_state: &Rc) -> Result, DaemonError> { + let kind = ChainKind::from(daemon_state.chain_data.network_type.clone()); + let mnemonic = env::var(kind.mnemonic_name()) + .unwrap_or_else(|_| { + { + ::core::panicking::panic_fmt( + format_args!( + "Wallet mnemonic environment variable {0} not set.", + kind.mnemonic_name(), + ), + ); + } + }); + Self::from_mnemonic(daemon_state, &mnemonic) + } + /// Construct a new Sender from a mnemonic + pub fn from_mnemonic( + daemon_state: &Rc, + mnemonic: &str, + ) -> Result, DaemonError> { + let secp = Secp256k1::new(); + let p_key: PrivateKey = PrivateKey::from_words( + &secp, + mnemonic, + 0, + 0, + daemon_state.chain_data.slip44, + )?; + let sender = Sender { + daemon_state: daemon_state.clone(), + private_key: p_key, + secp, + }; + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Interacting with {0} using address: {1}", + daemon_state.chain_data.chain_id, + sender.pub_addr_str()?, + ), + lvl, + &( + "cw_orch_daemon::sender", + "cw_orch_daemon::sender", + "cw-orch-daemon/src/sender.rs", + ), + 71u32, + ::log::__private_api::Option::None, + ); + } + }; + Ok(sender) + } + fn cosmos_private_key(&self) -> SigningKey { + SigningKey::from_slice(&self.private_key.raw_key()).unwrap() + } + pub fn channel(&self) -> Channel { + self.daemon_state.grpc_channel.clone() + } + pub fn pub_addr(&self) -> Result { + Ok( + AccountId::new( + &self.daemon_state.chain_data.bech32_prefix, + &self.private_key.public_key(&self.secp).raw_address.unwrap(), + )?, + ) + } + pub fn address(&self) -> Result { + Ok(Addr::unchecked(self.pub_addr_str()?)) + } + pub fn pub_addr_str(&self) -> Result { + Ok(self.pub_addr()?.to_string()) + } + pub async fn bank_send( + &self, + recipient: &str, + coins: Vec, + ) -> Result { + let msg_send = MsgSend { + from_address: self.pub_addr()?, + to_address: AccountId::from_str(recipient)?, + amount: parse_cw_coins(&coins)?, + }; + self.commit_tx( + <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([msg_send])), + Some("sending tokens"), + ) + .await + } + pub async fn calculate_gas( + &self, + tx_body: &tx::Body, + sequence: u64, + account_number: u64, + ) -> Result { + let fee = TxBuilder::build_fee( + 0u8, + &self.daemon_state.chain_data.fees.fee_tokens[0].denom, + 0, + ); + let auth_info = SignerInfo { + public_key: self.private_key.get_signer_public_key(&self.secp), + mode_info: ModeInfo::single(SignMode::Direct), + sequence, + } + .auth_info(fee); + let sign_doc = SignDoc::new( + tx_body, + &auth_info, + &Id::try_from(self.daemon_state.chain_data.chain_id.to_string())?, + account_number, + )?; + let tx_raw = self.sign(sign_doc)?; + Node::new(self.channel()).simulate_tx(tx_raw.to_bytes()?).await + } + pub async fn commit_tx( + &self, + msgs: Vec, + memo: Option<&str>, + ) -> Result { + let timeout_height = Node::new(self.channel()).block_height().await? + 10u64; + let tx_body = TxBuilder::build_body(msgs, memo, timeout_height); + let mut tx_builder = TxBuilder::new(tx_body); + let tx = tx_builder.build(self).await?; + let mut tx_response = self.broadcast_tx(tx).await?; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("tx broadcast response: {0:?}", tx_response), + lvl, + &( + "cw_orch_daemon::sender", + "cw_orch_daemon::sender", + "cw-orch-daemon/src/sender.rs", + ), + 166u32, + ::log::__private_api::Option::None, + ); + } + }; + if has_insufficient_fee(&tx_response.raw_log) { + let suggested_fee = parse_suggested_fee(&tx_response.raw_log); + let Some(new_fee) = suggested_fee else { + return Err(DaemonError::InsufficientFee(tx_response.raw_log)); + }; + tx_builder.fee_amount(new_fee); + let tx = tx_builder.build(self).await?; + tx_response = self.broadcast_tx(tx).await?; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("tx broadcast response: {0:?}", tx_response), + lvl, + &( + "cw_orch_daemon::sender", + "cw_orch_daemon::sender", + "cw-orch-daemon/src/sender.rs", + ), + 181u32, + ::log::__private_api::Option::None, + ); + } + }; + } + let resp = Node::new(self.channel()).find_tx(tx_response.txhash).await?; + if resp.code == 0 { + Ok(resp) + } else { + Err(DaemonError::TxFailed { + code: resp.code, + reason: resp.raw_log, + }) + } + } + pub fn sign(&self, sign_doc: SignDoc) -> Result { + let tx_raw = if self.private_key.coin_type == ETHEREUM_COIN_TYPE { + { + ::core::panicking::panic_fmt( + format_args!( + "Coin Type {0} not supported without eth feature", + ETHEREUM_COIN_TYPE, + ), + ); + }; + } else { + sign_doc.sign(&self.cosmos_private_key())? + }; + Ok(tx_raw) + } + pub async fn base_account(&self) -> Result { + let addr = self.pub_addr().unwrap().to_string(); + let mut client = cosmos_modules::auth::query_client::QueryClient::new( + self.channel(), + ); + let resp = client + .account(cosmos_modules::auth::QueryAccountRequest { + address: addr, + }) + .await? + .into_inner(); + let account = resp.account.unwrap().value; + let acc = if let Ok(acc) = BaseAccount::decode(account.as_ref()) { + acc + } else if let Ok(acc) = PeriodicVestingAccount::decode(account.as_ref()) { + acc.base_vesting_account.unwrap().base_account.unwrap() + } else if let Ok(acc) = InjectiveEthAccount::decode(account.as_ref()) { + acc.base_account.unwrap() + } else { + return Err( + DaemonError::StdErr( + "Unknown account type returned from QueryAccountRequest".into(), + ), + ); + }; + Ok(acc) + } + async fn broadcast_tx( + &self, + tx: Raw, + ) -> Result< + cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse, + DaemonError, + > { + let mut client = cosmos_modules::tx::service_client::ServiceClient::new( + self.channel(), + ); + let commit = client + .broadcast_tx(cosmos_modules::tx::BroadcastTxRequest { + tx_bytes: tx.to_bytes()?, + mode: cosmos_modules::tx::BroadcastMode::Sync.into(), + }) + .await?; + let commit = commit.into_inner().tx_response.unwrap(); + Ok(commit) + } + } + fn has_insufficient_fee(raw_log: &str) -> bool { + raw_log.contains("insufficient fees") + } + fn parse_suggested_fee(raw_log: &str) -> Option { + let parts: Vec<&str> = raw_log.split("required: ").collect(); + if parts.len() != 2 { + return None; + } + let got_parts: Vec<&str> = parts[0].split_whitespace().collect(); + let paid_fee_with_denom = got_parts.last()?; + let (_, denomination) = paid_fee_with_denom + .split_at(paid_fee_with_denom.find(|c: char| !c.is_numeric())?); + { + ::std::io::_eprint(format_args!("denom: {0}\n", denomination)); + }; + let required_fees: Vec<&str> = parts[1].split(denomination).collect(); + { + ::std::io::_eprint(format_args!("required fees: {0:?}\n", required_fees)); + }; + let (_, suggested_fee) = required_fees[0] + .split_at(required_fees[0].rfind(|c: char| !c.is_numeric())?); + { + ::std::io::_eprint(format_args!("suggested fee: {0}\n", suggested_fee)); + }; + suggested_fee.parse::().ok().or(suggested_fee[1..].parse::().ok()) + } +} +pub mod state { + use super::error::DaemonError; + use crate::{channel::GrpcChannel, networks::ChainKind}; + use cosmwasm_std::Addr; + use cw_orch_core::{ + environment::{DeployDetails, StateInterface}, + CwEnvError, + }; + use ibc_chain_registry::chain::ChainData; + use serde::Serialize; + use serde_json::{json, Value}; + use std::{collections::HashMap, env, fs::File, path::Path}; + use tonic::transport::Channel; + /// Stores the chain information and deployment state. + /// Uses a simple JSON file to store the deployment information locally. + pub struct DaemonState { + /// this is passed via env var STATE_FILE + pub json_file_path: String, + /// Deployment identifier + pub deployment_id: String, + /// gRPC channel + pub grpc_channel: Channel, + /// Information about the chain + pub chain_data: ChainData, + } + #[automatically_derived] + impl ::core::clone::Clone for DaemonState { + #[inline] + fn clone(&self) -> DaemonState { + DaemonState { + json_file_path: ::core::clone::Clone::clone(&self.json_file_path), + deployment_id: ::core::clone::Clone::clone(&self.deployment_id), + grpc_channel: ::core::clone::Clone::clone(&self.grpc_channel), + chain_data: ::core::clone::Clone::clone(&self.chain_data), + } + } + } + #[automatically_derived] + impl ::core::fmt::Debug for DaemonState { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field4_finish( + f, + "DaemonState", + "json_file_path", + &self.json_file_path, + "deployment_id", + &self.deployment_id, + "grpc_channel", + &self.grpc_channel, + "chain_data", + &&self.chain_data, + ) + } + } + impl DaemonState { + /// Creates a new state from the given chain data and deployment id. + /// Attempts to connect to any of the provided gRPC endpoints. + pub async fn new( + mut chain_data: ChainData, + deployment_id: String, + ) -> Result { + if chain_data.apis.grpc.is_empty() { + return Err(DaemonError::GRPCListIsEmpty); + } + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Found {0} gRPC endpoints", + chain_data.apis.grpc.len(), + ), + lvl, + &( + "cw_orch_daemon::state", + "cw_orch_daemon::state", + "cw-orch-daemon/src/state.rs", + ), + 40u32, + ::log::__private_api::Option::None, + ); + } + }; + let grpc_channel = GrpcChannel::connect( + &chain_data.apis.grpc, + &chain_data.chain_id, + ) + .await?; + let mut json_file_path = env::var("STATE_FILE") + .unwrap_or("./state.json".to_string()); + if chain_data.network_type == ChainKind::Local.to_string() { + let name = Path::new(&json_file_path) + .file_stem() + .unwrap() + .to_str() + .unwrap(); + let folder = Path::new(&json_file_path) + .parent() + .unwrap() + .to_str() + .unwrap(); + json_file_path = { + let res = ::alloc::fmt::format( + format_args!("{0}/{1}_local.json", folder, name), + ); + res + }; + } + let shortest_denom_token = chain_data + .fees + .fee_tokens + .iter() + .fold( + chain_data.fees.fee_tokens[0].clone(), + |acc, item| { + if item.denom.len() < acc.denom.len() { + item.clone() + } else { + acc + } + }, + ); + chain_data + .fees + .fee_tokens = <[_]>::into_vec( + #[rustc_box] + ::alloc::boxed::Box::new([shortest_denom_token]), + ); + let state = DaemonState { + json_file_path, + deployment_id, + grpc_channel, + chain_data, + }; + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Writing daemon state JSON file: {0:#?}", + state.json_file_path, + ), + lvl, + &( + "cw_orch_daemon::state", + "cw_orch_daemon::state", + "cw-orch-daemon/src/state.rs", + ), + 87u32, + ::log::__private_api::Option::None, + ); + } + }; + crate::json_file::write( + &state.json_file_path, + &state.chain_data.chain_id.to_string(), + &state.chain_data.chain_name, + &state.deployment_id, + ); + Ok(state) + } + /// Get the state filepath and read it as json + fn read_state(&self) -> serde_json::Value { + crate::json_file::read(&self.json_file_path) + } + /// Retrieve a stateful value using the chainId and networkId + pub fn get(&self, key: &str) -> Value { + let json = self.read_state(); + json[&self.chain_data.chain_name][&self.chain_data.chain_id.to_string()][key] + .clone() + } + /// Set a stateful value using the chainId and networkId + pub fn set(&self, key: &str, contract_id: &str, value: T) { + let mut json = self.read_state(); + json[&self + .chain_data + .chain_name][&self + .chain_data + .chain_id + .to_string()][key][contract_id] = ::serde_json::to_value(&value) + .unwrap(); + serde_json::to_writer_pretty( + File::create(&self.json_file_path).unwrap(), + &json, + ) + .unwrap(); + } + } + impl StateInterface for DaemonState { + /// Read address for contract in deployment id from state file + fn get_address(&self, contract_id: &str) -> Result { + let value = self + .get(&self.deployment_id) + .get(contract_id) + .ok_or_else(|| CwEnvError::AddrNotInStore(contract_id.to_owned()))? + .clone(); + Ok(Addr::unchecked(value.as_str().unwrap())) + } + /// Set address for contract in deployment id in state file + fn set_address(&mut self, contract_id: &str, address: &Addr) { + self.set(&self.deployment_id, contract_id, address.as_str()); + } + /// Get the locally-saved version of the contract's version on this network + fn get_code_id(&self, contract_id: &str) -> Result { + let value = self + .get("code_ids") + .get(contract_id) + .ok_or_else(|| CwEnvError::CodeIdNotInStore(contract_id.to_owned()))? + .clone(); + Ok(value.as_u64().unwrap()) + } + /// Set the locally-saved version of the contract's latest version on this network + fn set_code_id(&mut self, contract_id: &str, code_id: u64) { + self.set("code_ids", contract_id, code_id); + } + /// Get all addresses for deployment id from state file + fn get_all_addresses(&self) -> Result, CwEnvError> { + let mut store = HashMap::new(); + let addresses = self.get(&self.deployment_id); + let value = addresses.as_object().unwrap(); + for (id, addr) in value { + store.insert(id.clone(), Addr::unchecked(addr.as_str().unwrap())); + } + Ok(store) + } + fn get_all_code_ids(&self) -> Result, CwEnvError> { + let mut store = HashMap::new(); + let code_ids = self.get("code_ids"); + let value = code_ids.as_object().unwrap(); + for (id, code_id) in value { + store.insert(id.clone(), code_id.as_u64().unwrap()); + } + Ok(store) + } + fn deploy_details(&self) -> DeployDetails { + DeployDetails { + chain_id: self.chain_data.chain_id.to_string(), + chain_name: self.chain_data.chain_name.clone(), + deployment_id: self.deployment_id.clone(), + } + } + } +} +pub mod sync { + mod builder { + use ibc_chain_registry::chain::ChainData; + use crate::DaemonAsyncBuilder; + use super::{super::error::DaemonError, core::Daemon}; + /// Create [`Daemon`] through [`DaemonBuilder`] + /// ## Example + /// ```no_run + /// use cw_orch_daemon::{networks, DaemonBuilder}; + /// + /// let Daemon = DaemonBuilder::default() + /// .chain(networks::LOCAL_JUNO) + /// .deployment_id("v0.1.0") + /// .build() + /// .unwrap(); + /// ``` + pub struct DaemonBuilder { + pub(crate) chain: Option, + pub(crate) handle: Option, + pub(crate) deployment_id: Option, + /// Wallet mnemonic + pub(crate) mnemonic: Option, + } + #[automatically_derived] + impl ::core::clone::Clone for DaemonBuilder { + #[inline] + fn clone(&self) -> DaemonBuilder { + DaemonBuilder { + chain: ::core::clone::Clone::clone(&self.chain), + handle: ::core::clone::Clone::clone(&self.handle), + deployment_id: ::core::clone::Clone::clone(&self.deployment_id), + mnemonic: ::core::clone::Clone::clone(&self.mnemonic), + } + } + } + #[automatically_derived] + impl ::core::default::Default for DaemonBuilder { + #[inline] + fn default() -> DaemonBuilder { + DaemonBuilder { + chain: ::core::default::Default::default(), + handle: ::core::default::Default::default(), + deployment_id: ::core::default::Default::default(), + mnemonic: ::core::default::Default::default(), + } + } + } + impl DaemonBuilder { + /// Set the chain the Daemon will connect to + pub fn chain(&mut self, chain: impl Into) -> &mut Self { + self.chain = Some(chain.into()); + self + } + /// Set the deployment id to use for the Daemon interactions + /// Defaults to `default` + pub fn deployment_id( + &mut self, + deployment_id: impl Into, + ) -> &mut Self { + self.deployment_id = Some(deployment_id.into()); + self + } + /// Set the tokio runtime handle to use for the Daemon + /// + /// ## Example + /// ```no_run + /// use cw_orch_daemon::Daemon; + /// use tokio::runtime::Runtime; + /// let rt = Runtime::new().unwrap(); + /// let Daemon = Daemon::builder() + /// .handle(rt.handle()) + /// // ... + /// .build() + /// .unwrap(); + /// ``` + pub fn handle(&mut self, handle: &tokio::runtime::Handle) -> &mut Self { + self.handle = Some(handle.clone()); + self + } + /// Set the mnemonic to use with this chain. + pub fn mnemonic(&mut self, mnemonic: impl ToString) -> &mut Self { + self.mnemonic = Some(mnemonic.to_string()); + self + } + /// Build a Daemon + pub fn build(&self) -> Result { + let rt_handle = self + .handle + .clone() + .ok_or(DaemonError::BuilderMissing("runtime handle".into()))?; + let daemon = rt_handle + .block_on(DaemonAsyncBuilder::from(self.clone()).build())?; + Ok(Daemon { rt_handle, daemon }) + } + } + } + mod core { + use std::{fmt::Debug, rc::Rc, time::Duration}; + use super::super::{sender::Wallet, DaemonAsync}; + use crate::{ + queriers::{DaemonQuerier, Node}, + CosmTxResponse, DaemonBuilder, DaemonError, DaemonState, + }; + use cosmrs::tendermint::Time; + use cosmwasm_std::{Addr, Coin}; + use cw_orch_core::{ + contract::{interface_traits::Uploadable, WasmPath}, + environment::{ChainState, TxHandler}, + }; + use serde::{de::DeserializeOwned, Serialize}; + use tokio::runtime::Handle; + use tonic::transport::Channel; + /** + Represents a blockchain node. + Is constructed with the [DaemonBuilder]. + + ## Usage + + ```rust,no_run + use cw_orch_daemon::{Daemon, networks}; + use tokio::runtime::Runtime; + + let rt = Runtime::new().unwrap(); + let daemon: Daemon = Daemon::builder() + .chain(networks::JUNO_1) + .handle(rt.handle()) + .build() + .unwrap(); + ``` + ## Environment Execution + + The Daemon implements [`TxHandler`] which allows you to perform transactions on the chain. + + ## Querying + + Different Cosmos SDK modules can be queried through the daemon by calling the [`Daemon.query_client`] method with a specific querier. + See [Querier](crate::queriers) for examples. +*/ + pub struct Daemon { + pub daemon: DaemonAsync, + /// Runtime handle to execute async tasks + pub rt_handle: Handle, + } + #[automatically_derived] + impl ::core::clone::Clone for Daemon { + #[inline] + fn clone(&self) -> Daemon { + Daemon { + daemon: ::core::clone::Clone::clone(&self.daemon), + rt_handle: ::core::clone::Clone::clone(&self.rt_handle), + } + } + } + impl Daemon { + /// Get the daemon builder + pub fn builder() -> DaemonBuilder { + DaemonBuilder::default() + } + /// Perform a query with a given querier + /// See [Querier](crate::queriers) for examples. + pub fn query_client(&self) -> Querier { + self.daemon.query_client() + } + /// Get the channel configured for this Daemon + pub fn channel(&self) -> Channel { + self.daemon.state.grpc_channel.clone() + } + /// Get the channel configured for this Daemon + pub fn wallet(&self) -> Wallet { + self.daemon.sender.clone() + } + } + impl ChainState for Daemon { + type Out = Rc; + fn state(&self) -> Self::Out { + self.daemon.state.clone() + } + } + impl TxHandler for Daemon { + type Response = CosmTxResponse; + type Error = DaemonError; + type ContractSource = WasmPath; + type Sender = Wallet; + fn sender(&self) -> Addr { + self.daemon.sender.address().unwrap() + } + fn set_sender(&mut self, sender: Self::Sender) { + self.daemon.sender = sender; + } + fn upload( + &self, + uploadable: &impl Uploadable, + ) -> Result { + self.rt_handle.block_on(self.daemon.upload(uploadable)) + } + fn execute( + &self, + exec_msg: &E, + coins: &[cosmwasm_std::Coin], + contract_address: &Addr, + ) -> Result { + self.rt_handle + .block_on(self.daemon.execute(exec_msg, coins, contract_address)) + } + fn instantiate( + &self, + code_id: u64, + init_msg: &I, + label: Option<&str>, + admin: Option<&Addr>, + coins: &[Coin], + ) -> Result { + self.rt_handle + .block_on( + self.daemon.instantiate(code_id, init_msg, label, admin, coins), + ) + } + fn query( + &self, + query_msg: &Q, + contract_address: &Addr, + ) -> Result { + self.rt_handle.block_on(self.daemon.query(query_msg, contract_address)) + } + fn migrate( + &self, + migrate_msg: &M, + new_code_id: u64, + contract_address: &Addr, + ) -> Result { + self.rt_handle + .block_on( + self.daemon.migrate(migrate_msg, new_code_id, contract_address), + ) + } + fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { + let mut last_height = self + .rt_handle + .block_on(self.query_client::().block_height())?; + let end_height = last_height + amount; + while last_height < end_height { + self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(4))); + last_height = self + .rt_handle + .block_on(self.query_client::().block_height())?; + } + Ok(()) + } + fn wait_seconds(&self, secs: u64) -> Result<(), DaemonError> { + self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(secs))); + Ok(()) + } + fn next_block(&self) -> Result<(), DaemonError> { + let mut last_height = self + .rt_handle + .block_on(self.query_client::().block_height())?; + let end_height = last_height + 1; + while last_height < end_height { + self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(4))); + last_height = self + .rt_handle + .block_on(self.query_client::().block_height())?; + } + Ok(()) + } + fn block_info(&self) -> Result { + let block = self + .rt_handle + .block_on(self.query_client::().latest_block())?; + let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; + let time = cosmwasm_std::Timestamp::from_nanos( + since_epoch.as_nanos() as u64, + ); + Ok(cosmwasm_std::BlockInfo { + height: block.header.height.value(), + time, + chain_id: block.header.chain_id.to_string(), + }) + } + } + } + pub use self::{builder::*, core::*}; +} +pub mod tx_resp { + use super::{ + cosmos_modules::{ + abci::{AbciMessageLog, Attribute, StringEvent, TxResponse}, + tendermint_abci::Event, + }, + error::DaemonError, + }; + use chrono::{DateTime, NaiveDateTime, TimeZone, Utc}; + use cosmwasm_std::{to_binary, Binary, StdError, StdResult}; + use cw_orch_core::environment::IndexResponse; + use serde::{Deserialize, Serialize}; + const FORMAT: &str = "%Y-%m-%dT%H:%M:%S%.f"; + const FORMAT_TZ_SUPPLIED: &str = "%Y-%m-%dT%H:%M:%S.%f%:z"; + const FORMAT_SHORT_Z: &str = "%Y-%m-%dT%H:%M:%SZ"; + const FORMAT_SHORT_Z2: &str = "%Y-%m-%dT%H:%M:%S.%fZ"; + /// The response from a transaction performed on a blockchain. + pub struct CosmTxResponse { + /// Height of the block in which the transaction was included. + pub height: u64, + /// Transaction hash. + pub txhash: String, + /// Transaction index within the block. + pub codespace: String, + /// Transaction result code + pub code: usize, + /// Arbitrary data that can be included in a transaction. + pub data: String, + /// Raw log message. + pub raw_log: String, + /// Logs of the transaction. + pub logs: Vec, + /// Transaction info. + pub info: String, + /// Gas limit. + pub gas_wanted: u64, + /// Gas used. + pub gas_used: u64, + /// Timestamp of the block in which the transaction was included. + pub timestamp: DateTime, + /// Transaction events. + pub events: Vec, + } + #[automatically_derived] + impl ::core::fmt::Debug for CosmTxResponse { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + let names: &'static _ = &[ + "height", + "txhash", + "codespace", + "code", + "data", + "raw_log", + "logs", + "info", + "gas_wanted", + "gas_used", + "timestamp", + "events", + ]; + let values: &[&dyn ::core::fmt::Debug] = &[ + &self.height, + &self.txhash, + &self.codespace, + &self.code, + &self.data, + &self.raw_log, + &self.logs, + &self.info, + &self.gas_wanted, + &self.gas_used, + &self.timestamp, + &&self.events, + ]; + ::core::fmt::Formatter::debug_struct_fields_finish( + f, + "CosmTxResponse", + names, + values, + ) + } + } + #[automatically_derived] + impl ::core::default::Default for CosmTxResponse { + #[inline] + fn default() -> CosmTxResponse { + CosmTxResponse { + height: ::core::default::Default::default(), + txhash: ::core::default::Default::default(), + codespace: ::core::default::Default::default(), + code: ::core::default::Default::default(), + data: ::core::default::Default::default(), + raw_log: ::core::default::Default::default(), + logs: ::core::default::Default::default(), + info: ::core::default::Default::default(), + gas_wanted: ::core::default::Default::default(), + gas_used: ::core::default::Default::default(), + timestamp: ::core::default::Default::default(), + events: ::core::default::Default::default(), + } + } + } + impl CosmTxResponse { + /// find a attribute's value from TX logs. + /// returns: msg_index and value + pub fn get_attribute_from_logs( + &self, + event_type: &str, + attribute_key: &str, + ) -> Vec<(usize, String)> { + let mut response: Vec<(usize, String)> = Default::default(); + let logs = &self.logs; + for log_part in logs { + let msg_index = log_part.msg_index.unwrap_or_default(); + let events = &log_part.events; + let events_filtered = events + .iter() + .filter(|event| event.s_type == event_type) + .collect::>(); + if let Some(event) = events_filtered.first() { + let attributes_filtered = event + .attributes + .iter() + .filter(|attr| attr.key == attribute_key) + .map(|f| f.value.clone()) + .collect::>(); + if let Some(attr_key) = attributes_filtered.first() { + response.push((msg_index, attr_key.clone())); + } + } + } + response + } + /// get the list of event types from a TX record + pub fn get_events(&self, event_type: &str) -> Vec { + let mut response: Vec = Default::default(); + for log_part in &self.logs { + let events = &log_part.events; + let events_filtered = events + .iter() + .filter(|event| event.s_type == event_type) + .collect::>(); + for event in events_filtered { + response.push(event.clone()); + } + } + response + } + } + impl From<&serde_json::Value> for TxResultBlockMsg { + fn from(value: &serde_json::Value) -> Self { + serde_json::from_value(value.clone()).unwrap() + } + } + impl From for CosmTxResponse { + fn from(tx: TxResponse) -> Self { + Self { + height: tx.height as u64, + txhash: tx.txhash, + codespace: tx.codespace, + code: tx.code as usize, + data: tx.data, + raw_log: tx.raw_log, + logs: tx.logs.into_iter().map(TxResultBlockMsg::from).collect(), + info: tx.info, + gas_wanted: tx.gas_wanted as u64, + gas_used: tx.gas_used as u64, + timestamp: parse_timestamp(tx.timestamp).unwrap(), + events: tx.events, + } + } + } + impl IndexResponse for CosmTxResponse { + fn events(&self) -> Vec { + let mut parsed_events = ::alloc::vec::Vec::new(); + for event in &self.events { + let mut pattr = ::alloc::vec::Vec::new(); + for attr in &event.attributes { + pattr + .push(cosmwasm_std::Attribute { + key: attr.key.clone(), + value: attr.value.clone(), + }) + } + let pevent = cosmwasm_std::Event::new(event.r#type.clone()) + .add_attributes(pattr); + parsed_events.push(pevent); + } + parsed_events + } + fn data(&self) -> Option { + if self.data.is_empty() { + None + } else { + Some(to_binary(self.data.as_bytes()).unwrap()) + } + } + fn event_attr_value( + &self, + event_type: &str, + attr_key: &str, + ) -> StdResult { + for event in &self.events { + if event.r#type == event_type { + for attr in &event.attributes { + if attr.key == attr_key { + return Ok(attr.value.clone()); + } + } + } + } + Err( + StdError::generic_err({ + let res = ::alloc::fmt::format( + format_args!( + "event of type {0} does not have a value at key {1}", + event_type, + attr_key, + ), + ); + res + }), + ) + } + } + /// The events from a single message in a transaction. + pub struct TxResultBlockMsg { + /// index of the message in the transaction + pub msg_index: Option, + /// Events from this message + pub events: Vec, + } + #[automatically_derived] + impl ::core::clone::Clone for TxResultBlockMsg { + #[inline] + fn clone(&self) -> TxResultBlockMsg { + TxResultBlockMsg { + msg_index: ::core::clone::Clone::clone(&self.msg_index), + events: ::core::clone::Clone::clone(&self.events), + } + } + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl _serde::Serialize for TxResultBlockMsg { + fn serialize<__S>( + &self, + __serializer: __S, + ) -> _serde::__private::Result<__S::Ok, __S::Error> + where + __S: _serde::Serializer, + { + let mut __serde_state = _serde::Serializer::serialize_struct( + __serializer, + "TxResultBlockMsg", + false as usize + 1 + 1, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "msg_index", + &self.msg_index, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "events", + &self.events, + )?; + _serde::ser::SerializeStruct::end(__serde_state) + } + } + }; + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl<'de> _serde::Deserialize<'de> for TxResultBlockMsg { + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + #[allow(non_camel_case_types)] + #[doc(hidden)] + enum __Field { + __field0, + __field1, + __ignore, + } + #[doc(hidden)] + struct __FieldVisitor; + impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { + type Value = __Field; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "field identifier", + ) + } + fn visit_u64<__E>( + self, + __value: u64, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + 0u64 => _serde::__private::Ok(__Field::__field0), + 1u64 => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_str<__E>( + self, + __value: &str, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + "msg_index" => _serde::__private::Ok(__Field::__field0), + "events" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_bytes<__E>( + self, + __value: &[u8], + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + b"msg_index" => _serde::__private::Ok(__Field::__field0), + b"events" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + } + impl<'de> _serde::Deserialize<'de> for __Field { + #[inline] + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + _serde::Deserializer::deserialize_identifier( + __deserializer, + __FieldVisitor, + ) + } + } + #[doc(hidden)] + struct __Visitor<'de> { + marker: _serde::__private::PhantomData, + lifetime: _serde::__private::PhantomData<&'de ()>, + } + impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { + type Value = TxResultBlockMsg; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "struct TxResultBlockMsg", + ) + } + #[inline] + fn visit_seq<__A>( + self, + mut __seq: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::SeqAccess<'de>, + { + let __field0 = match _serde::de::SeqAccess::next_element::< + Option, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 0usize, + &"struct TxResultBlockMsg with 2 elements", + ), + ); + } + }; + let __field1 = match _serde::de::SeqAccess::next_element::< + Vec, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 1usize, + &"struct TxResultBlockMsg with 2 elements", + ), + ); + } + }; + _serde::__private::Ok(TxResultBlockMsg { + msg_index: __field0, + events: __field1, + }) + } + #[inline] + fn visit_map<__A>( + self, + mut __map: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::MapAccess<'de>, + { + let mut __field0: _serde::__private::Option> = _serde::__private::None; + let mut __field1: _serde::__private::Option< + Vec, + > = _serde::__private::None; + while let _serde::__private::Some(__key) + = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { + match __key { + __Field::__field0 => { + if _serde::__private::Option::is_some(&__field0) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field( + "msg_index", + ), + ); + } + __field0 = _serde::__private::Some( + _serde::de::MapAccess::next_value::< + Option, + >(&mut __map)?, + ); + } + __Field::__field1 => { + if _serde::__private::Option::is_some(&__field1) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field("events"), + ); + } + __field1 = _serde::__private::Some( + _serde::de::MapAccess::next_value::< + Vec, + >(&mut __map)?, + ); + } + _ => { + let _ = _serde::de::MapAccess::next_value::< + _serde::de::IgnoredAny, + >(&mut __map)?; + } + } + } + let __field0 = match __field0 { + _serde::__private::Some(__field0) => __field0, + _serde::__private::None => { + _serde::__private::de::missing_field("msg_index")? + } + }; + let __field1 = match __field1 { + _serde::__private::Some(__field1) => __field1, + _serde::__private::None => { + _serde::__private::de::missing_field("events")? + } + }; + _serde::__private::Ok(TxResultBlockMsg { + msg_index: __field0, + events: __field1, + }) + } + } + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &["msg_index", "events"]; + _serde::Deserializer::deserialize_struct( + __deserializer, + "TxResultBlockMsg", + FIELDS, + __Visitor { + marker: _serde::__private::PhantomData::, + lifetime: _serde::__private::PhantomData, + }, + ) + } + } + }; + #[automatically_derived] + impl ::core::fmt::Debug for TxResultBlockMsg { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "TxResultBlockMsg", + "msg_index", + &self.msg_index, + "events", + &&self.events, + ) + } + } + impl From for TxResultBlockMsg { + fn from(msg: AbciMessageLog) -> Self { + Self { + msg_index: Some(msg.msg_index as usize), + events: msg.events.into_iter().map(TxResultBlockEvent::from).collect(), + } + } + } + /// A single event from a transaction and its attributes. + pub struct TxResultBlockEvent { + #[serde(rename = "type")] + /// Type of the event + pub s_type: String, + /// Attributes of the event + pub attributes: Vec, + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl<'de> _serde::Deserialize<'de> for TxResultBlockEvent { + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + #[allow(non_camel_case_types)] + #[doc(hidden)] + enum __Field { + __field0, + __field1, + __ignore, + } + #[doc(hidden)] + struct __FieldVisitor; + impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { + type Value = __Field; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "field identifier", + ) + } + fn visit_u64<__E>( + self, + __value: u64, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + 0u64 => _serde::__private::Ok(__Field::__field0), + 1u64 => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_str<__E>( + self, + __value: &str, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + "type" => _serde::__private::Ok(__Field::__field0), + "attributes" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_bytes<__E>( + self, + __value: &[u8], + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + b"type" => _serde::__private::Ok(__Field::__field0), + b"attributes" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + } + impl<'de> _serde::Deserialize<'de> for __Field { + #[inline] + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + _serde::Deserializer::deserialize_identifier( + __deserializer, + __FieldVisitor, + ) + } + } + #[doc(hidden)] + struct __Visitor<'de> { + marker: _serde::__private::PhantomData, + lifetime: _serde::__private::PhantomData<&'de ()>, + } + impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { + type Value = TxResultBlockEvent; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "struct TxResultBlockEvent", + ) + } + #[inline] + fn visit_seq<__A>( + self, + mut __seq: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::SeqAccess<'de>, + { + let __field0 = match _serde::de::SeqAccess::next_element::< + String, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 0usize, + &"struct TxResultBlockEvent with 2 elements", + ), + ); + } + }; + let __field1 = match _serde::de::SeqAccess::next_element::< + Vec, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 1usize, + &"struct TxResultBlockEvent with 2 elements", + ), + ); + } + }; + _serde::__private::Ok(TxResultBlockEvent { + s_type: __field0, + attributes: __field1, + }) + } + #[inline] + fn visit_map<__A>( + self, + mut __map: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::MapAccess<'de>, + { + let mut __field0: _serde::__private::Option = _serde::__private::None; + let mut __field1: _serde::__private::Option< + Vec, + > = _serde::__private::None; + while let _serde::__private::Some(__key) + = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { + match __key { + __Field::__field0 => { + if _serde::__private::Option::is_some(&__field0) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field("type"), + ); + } + __field0 = _serde::__private::Some( + _serde::de::MapAccess::next_value::(&mut __map)?, + ); + } + __Field::__field1 => { + if _serde::__private::Option::is_some(&__field1) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field( + "attributes", + ), + ); + } + __field1 = _serde::__private::Some( + _serde::de::MapAccess::next_value::< + Vec, + >(&mut __map)?, + ); + } + _ => { + let _ = _serde::de::MapAccess::next_value::< + _serde::de::IgnoredAny, + >(&mut __map)?; + } + } + } + let __field0 = match __field0 { + _serde::__private::Some(__field0) => __field0, + _serde::__private::None => { + _serde::__private::de::missing_field("type")? + } + }; + let __field1 = match __field1 { + _serde::__private::Some(__field1) => __field1, + _serde::__private::None => { + _serde::__private::de::missing_field("attributes")? + } + }; + _serde::__private::Ok(TxResultBlockEvent { + s_type: __field0, + attributes: __field1, + }) + } + } + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &["type", "attributes"]; + _serde::Deserializer::deserialize_struct( + __deserializer, + "TxResultBlockEvent", + FIELDS, + __Visitor { + marker: _serde::__private::PhantomData::, + lifetime: _serde::__private::PhantomData, + }, + ) + } + } + }; + #[automatically_derived] + impl ::core::clone::Clone for TxResultBlockEvent { + #[inline] + fn clone(&self) -> TxResultBlockEvent { + TxResultBlockEvent { + s_type: ::core::clone::Clone::clone(&self.s_type), + attributes: ::core::clone::Clone::clone(&self.attributes), + } + } + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl _serde::Serialize for TxResultBlockEvent { + fn serialize<__S>( + &self, + __serializer: __S, + ) -> _serde::__private::Result<__S::Ok, __S::Error> + where + __S: _serde::Serializer, + { + let mut __serde_state = _serde::Serializer::serialize_struct( + __serializer, + "TxResultBlockEvent", + false as usize + 1 + 1, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "type", + &self.s_type, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "attributes", + &self.attributes, + )?; + _serde::ser::SerializeStruct::end(__serde_state) + } + } + }; + #[automatically_derived] + impl ::core::fmt::Debug for TxResultBlockEvent { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "TxResultBlockEvent", + "s_type", + &self.s_type, + "attributes", + &&self.attributes, + ) + } + } + impl From for TxResultBlockEvent { + fn from(event: StringEvent) -> Self { + Self { + s_type: event.r#type, + attributes: event + .attributes + .into_iter() + .map(TxResultBlockAttribute::from) + .collect(), + } + } + } + impl TxResultBlockEvent { + /// get all key/values from the event that have the key 'key' + pub fn get_attributes(&self, key: &str) -> Vec { + self.attributes.iter().filter(|attr| attr.key == key).cloned().collect() + } + /// return the first value of the first attribute that has the key 'key' + pub fn get_first_attribute_value(&self, key: &str) -> Option { + self.get_attributes(key).first().map(|attr| attr.value.clone()) + } + } + /// A single attribute of an event. + pub struct TxResultBlockAttribute { + /// Key of the attribute + pub key: String, + /// Value of the attribute + pub value: String, + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl<'de> _serde::Deserialize<'de> for TxResultBlockAttribute { + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + #[allow(non_camel_case_types)] + #[doc(hidden)] + enum __Field { + __field0, + __field1, + __ignore, + } + #[doc(hidden)] + struct __FieldVisitor; + impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { + type Value = __Field; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "field identifier", + ) + } + fn visit_u64<__E>( + self, + __value: u64, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + 0u64 => _serde::__private::Ok(__Field::__field0), + 1u64 => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_str<__E>( + self, + __value: &str, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + "key" => _serde::__private::Ok(__Field::__field0), + "value" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_bytes<__E>( + self, + __value: &[u8], + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + b"key" => _serde::__private::Ok(__Field::__field0), + b"value" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + } + impl<'de> _serde::Deserialize<'de> for __Field { + #[inline] + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + _serde::Deserializer::deserialize_identifier( + __deserializer, + __FieldVisitor, + ) + } + } + #[doc(hidden)] + struct __Visitor<'de> { + marker: _serde::__private::PhantomData, + lifetime: _serde::__private::PhantomData<&'de ()>, + } + impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { + type Value = TxResultBlockAttribute; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "struct TxResultBlockAttribute", + ) + } + #[inline] + fn visit_seq<__A>( + self, + mut __seq: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::SeqAccess<'de>, + { + let __field0 = match _serde::de::SeqAccess::next_element::< + String, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 0usize, + &"struct TxResultBlockAttribute with 2 elements", + ), + ); + } + }; + let __field1 = match _serde::de::SeqAccess::next_element::< + String, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 1usize, + &"struct TxResultBlockAttribute with 2 elements", + ), + ); + } + }; + _serde::__private::Ok(TxResultBlockAttribute { + key: __field0, + value: __field1, + }) + } + #[inline] + fn visit_map<__A>( + self, + mut __map: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::MapAccess<'de>, + { + let mut __field0: _serde::__private::Option = _serde::__private::None; + let mut __field1: _serde::__private::Option = _serde::__private::None; + while let _serde::__private::Some(__key) + = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { + match __key { + __Field::__field0 => { + if _serde::__private::Option::is_some(&__field0) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field("key"), + ); + } + __field0 = _serde::__private::Some( + _serde::de::MapAccess::next_value::(&mut __map)?, + ); + } + __Field::__field1 => { + if _serde::__private::Option::is_some(&__field1) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field("value"), + ); + } + __field1 = _serde::__private::Some( + _serde::de::MapAccess::next_value::(&mut __map)?, + ); + } + _ => { + let _ = _serde::de::MapAccess::next_value::< + _serde::de::IgnoredAny, + >(&mut __map)?; + } + } + } + let __field0 = match __field0 { + _serde::__private::Some(__field0) => __field0, + _serde::__private::None => { + _serde::__private::de::missing_field("key")? + } + }; + let __field1 = match __field1 { + _serde::__private::Some(__field1) => __field1, + _serde::__private::None => { + _serde::__private::de::missing_field("value")? + } + }; + _serde::__private::Ok(TxResultBlockAttribute { + key: __field0, + value: __field1, + }) + } + } + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &["key", "value"]; + _serde::Deserializer::deserialize_struct( + __deserializer, + "TxResultBlockAttribute", + FIELDS, + __Visitor { + marker: _serde::__private::PhantomData::, + lifetime: _serde::__private::PhantomData, + }, + ) + } + } + }; + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl _serde::Serialize for TxResultBlockAttribute { + fn serialize<__S>( + &self, + __serializer: __S, + ) -> _serde::__private::Result<__S::Ok, __S::Error> + where + __S: _serde::Serializer, + { + let mut __serde_state = _serde::Serializer::serialize_struct( + __serializer, + "TxResultBlockAttribute", + false as usize + 1 + 1, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "key", + &self.key, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "value", + &self.value, + )?; + _serde::ser::SerializeStruct::end(__serde_state) + } + } + }; + #[automatically_derived] + impl ::core::clone::Clone for TxResultBlockAttribute { + #[inline] + fn clone(&self) -> TxResultBlockAttribute { + TxResultBlockAttribute { + key: ::core::clone::Clone::clone(&self.key), + value: ::core::clone::Clone::clone(&self.value), + } + } + } + #[automatically_derived] + impl ::core::fmt::Debug for TxResultBlockAttribute { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "TxResultBlockAttribute", + "key", + &self.key, + "value", + &&self.value, + ) + } + } + impl From for TxResultBlockAttribute { + fn from(a: Attribute) -> Self { + Self { key: a.key, value: a.value } + } + } + /// Parse a string timestamp into a `DateTime` + pub fn parse_timestamp(s: String) -> Result, DaemonError> { + let len = s.len(); + let slice_len = if s.contains('.') { len.saturating_sub(4) } else { len }; + let sliced = &s[0..slice_len]; + match NaiveDateTime::parse_from_str(sliced, FORMAT) { + Err(_e) => { + match NaiveDateTime::parse_from_str(&s, FORMAT_TZ_SUPPLIED) { + Err(_e2) => { + match NaiveDateTime::parse_from_str(sliced, FORMAT_SHORT_Z) { + Err(_e3) => { + match NaiveDateTime::parse_from_str(&s, FORMAT_SHORT_Z2) { + Err(_e4) => { + { + ::std::io::_eprint( + format_args!("DateTime Fail {0} {1:#?}\n", s, _e4), + ); + }; + Err(DaemonError::StdErr(_e4.to_string())) + } + Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), + } + } + Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), + } + } + Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), + } + } + Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), + } + } +} +pub mod keys { + #![allow(unused)] + pub mod private { + use super::public::PublicKey; + use crate::proto::injective::{InjectivePubKey, ETHEREUM_COIN_TYPE}; + use crate::DaemonError; + use base64::Engine; + use bitcoin::{ + bip32::{ExtendedPrivKey, IntoDerivationPath}, + Network, + }; + use cosmrs::tx::SignerPublicKey; + use hkd32::mnemonic::{Phrase, Seed}; + use rand_core::OsRng; + use secp256k1::Secp256k1; + /// The Private key structure that is used to generate signatures and public keys + /// WARNING: No Security Audit has been performed + pub struct PrivateKey { + #[allow(missing_docs)] + pub account: u32, + #[allow(missing_docs)] + pub index: u32, + #[allow(missing_docs)] + pub coin_type: u32, + /// The 24 words used to generate this private key + mnemonic: Option, + #[allow(dead_code)] + /// This is used for testing + root_private_key: ExtendedPrivKey, + /// The private key + private_key: ExtendedPrivKey, + } + #[automatically_derived] + impl ::core::clone::Clone for PrivateKey { + #[inline] + fn clone(&self) -> PrivateKey { + PrivateKey { + account: ::core::clone::Clone::clone(&self.account), + index: ::core::clone::Clone::clone(&self.index), + coin_type: ::core::clone::Clone::clone(&self.coin_type), + mnemonic: ::core::clone::Clone::clone(&self.mnemonic), + root_private_key: ::core::clone::Clone::clone( + &self.root_private_key, + ), + private_key: ::core::clone::Clone::clone(&self.private_key), + } + } + } + impl PrivateKey { + /// Generate a new private key + pub fn new( + secp: &Secp256k1, + coin_type: u32, + ) -> Result { + let phrase = hkd32::mnemonic::Phrase::random( + OsRng, + hkd32::mnemonic::Language::English, + ); + PrivateKey::gen_private_key_phrase(secp, phrase, 0, 0, coin_type, "") + } + /// generate a new private key with a seed phrase + pub fn new_seed( + secp: &Secp256k1, + seed_phrase: &str, + coin_type: u32, + ) -> Result { + let phrase = hkd32::mnemonic::Phrase::random( + OsRng, + hkd32::mnemonic::Language::English, + ); + PrivateKey::gen_private_key_phrase( + secp, + phrase, + 0, + 0, + coin_type, + seed_phrase, + ) + } + /// for private key recovery. This is also used by wallet routines to re-hydrate the structure + pub fn from_words( + secp: &Secp256k1, + words: &str, + account: u32, + index: u32, + coin_type: u32, + ) -> Result { + if words.split(' ').count() != 24 { + return Err(DaemonError::WrongLength); + } + match hkd32::mnemonic::Phrase::new( + words, + hkd32::mnemonic::Language::English, + ) { + Ok(phrase) => { + PrivateKey::gen_private_key_phrase( + secp, + phrase, + account, + index, + coin_type, + "", + ) + } + Err(_) => Err(DaemonError::Phrasing), + } + } + /// for private key recovery with seed phrase + pub fn from_words_seed( + secp: &Secp256k1, + words: &str, + seed_pass: &str, + coin_type: u32, + ) -> Result { + match hkd32::mnemonic::Phrase::new( + words, + hkd32::mnemonic::Language::English, + ) { + Ok(phrase) => { + PrivateKey::gen_private_key_phrase( + secp, + phrase, + 0, + 0, + coin_type, + seed_pass, + ) + } + Err(_) => Err(DaemonError::Phrasing), + } + } + /// generate the public key for this private key + pub fn public_key( + &self, + secp: &Secp256k1, + ) -> PublicKey { + if self.coin_type == ETHEREUM_COIN_TYPE { + { + ::core::panicking::panic_fmt( + format_args!( + "Coin Type {0} not supported without eth feature", + ETHEREUM_COIN_TYPE, + ), + ); + }; + } + let x = self.private_key.private_key.public_key(secp); + PublicKey::from_bitcoin_public_key(&bitcoin::PublicKey::new(x)) + } + pub fn get_injective_public_key( + &self, + secp: &Secp256k1, + ) -> SignerPublicKey { + use base64::engine::general_purpose; + use cosmrs::tx::MessageExt; + use secp256k1::SecretKey; + let secret_key = SecretKey::from_slice(self.raw_key().as_slice()) + .unwrap(); + let public_key = secp256k1::PublicKey::from_secret_key( + secp, + &secret_key, + ); + let vec_pk = public_key.serialize(); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "{0:?}, public key", + general_purpose::STANDARD.encode(vec_pk), + ), + lvl, + &( + "cw_orch_daemon::keys::private", + "cw_orch_daemon::keys::private", + "cw-orch-daemon/src/keys/private.rs", + ), + 124u32, + ::log::__private_api::Option::None, + ); + } + }; + let inj_key = InjectivePubKey { + key: vec_pk.into(), + }; + inj_key.to_any().unwrap().try_into().unwrap() + } + pub fn get_signer_public_key( + &self, + secp: &Secp256k1, + ) -> Option { + if self.coin_type == ETHEREUM_COIN_TYPE { + { + ::core::panicking::panic_fmt( + format_args!( + "Coin Type {0} not supported without eth feature", + ETHEREUM_COIN_TYPE, + ), + ); + }; + } + Some( + cosmrs::crypto::secp256k1::SigningKey::from_slice( + self.raw_key().as_slice(), + ) + .unwrap() + .public_key() + .into(), + ) + } + pub fn raw_key(&self) -> Vec { + self.private_key.private_key.secret_bytes().to_vec() + } + fn gen_private_key_phrase( + secp: &Secp256k1, + phrase: Phrase, + account: u32, + index: u32, + coin_type: u32, + seed_phrase: &str, + ) -> Result { + let seed = phrase.to_seed(seed_phrase); + let root_private_key = ExtendedPrivKey::new_master( + Network::Bitcoin, + seed.as_bytes(), + ) + .unwrap(); + let path = { + let res = ::alloc::fmt::format( + format_args!( + "m/44\'/{0}\'/{1}\'/0/{2}", + coin_type, + account, + index, + ), + ); + res + }; + let derivation_path = path.into_derivation_path()?; + let private_key = root_private_key.derive_priv(secp, &derivation_path)?; + Ok(PrivateKey { + account, + index, + coin_type, + mnemonic: Some(phrase), + root_private_key, + private_key, + }) + } + /// the words used to generate this private key + pub fn words(&self) -> Option<&str> { + self.mnemonic.as_ref().map(|phrase| phrase.phrase()) + } + /// used for testing + /// could potentially be used to recreate the private key instead of words + #[allow(dead_code)] + pub(crate) fn seed(&self, passwd: &str) -> Option { + self.mnemonic.as_ref().map(|phrase| phrase.to_seed(passwd)) + } + } + } + pub mod public { + use crate::DaemonError; + use bitcoin::bech32::{decode, encode, u5, FromBase32, ToBase32, Variant}; + pub use ed25519_dalek::VerifyingKey as Ed25519; + use ring::digest::{Context, SHA256}; + use ripemd::{Digest as _, Ripemd160}; + use serde::{Deserialize, Serialize}; + static BECH32_PUBKEY_DATA_PREFIX_SECP256K1: [u8; 5] = [ + 0xeb, + 0x5a, + 0xe9, + 0x87, + 0x21, + ]; + static BECH32_PUBKEY_DATA_PREFIX_ED25519: [u8; 5] = [ + 0x16, + 0x24, + 0xde, + 0x64, + 0x20, + ]; + /// The public key we used to generate the cosmos/tendermind/terrad addresses + pub struct PublicKey { + /// This is optional as we can generate non-pub keys without + pub raw_pub_key: Option>, + /// The raw bytes used to generate non-pub keys + pub raw_address: Option>, + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl<'de> _serde::Deserialize<'de> for PublicKey { + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + #[allow(non_camel_case_types)] + #[doc(hidden)] + enum __Field { + __field0, + __field1, + __ignore, + } + #[doc(hidden)] + struct __FieldVisitor; + impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { + type Value = __Field; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "field identifier", + ) + } + fn visit_u64<__E>( + self, + __value: u64, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + 0u64 => _serde::__private::Ok(__Field::__field0), + 1u64 => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_str<__E>( + self, + __value: &str, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + "raw_pub_key" => _serde::__private::Ok(__Field::__field0), + "raw_address" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_bytes<__E>( + self, + __value: &[u8], + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + b"raw_pub_key" => _serde::__private::Ok(__Field::__field0), + b"raw_address" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + } + impl<'de> _serde::Deserialize<'de> for __Field { + #[inline] + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + _serde::Deserializer::deserialize_identifier( + __deserializer, + __FieldVisitor, + ) + } + } + #[doc(hidden)] + struct __Visitor<'de> { + marker: _serde::__private::PhantomData, + lifetime: _serde::__private::PhantomData<&'de ()>, + } + impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { + type Value = PublicKey; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "struct PublicKey", + ) + } + #[inline] + fn visit_seq<__A>( + self, + mut __seq: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::SeqAccess<'de>, + { + let __field0 = match _serde::de::SeqAccess::next_element::< + Option>, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 0usize, + &"struct PublicKey with 2 elements", + ), + ); + } + }; + let __field1 = match _serde::de::SeqAccess::next_element::< + Option>, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 1usize, + &"struct PublicKey with 2 elements", + ), + ); + } + }; + _serde::__private::Ok(PublicKey { + raw_pub_key: __field0, + raw_address: __field1, + }) + } + #[inline] + fn visit_map<__A>( + self, + mut __map: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::MapAccess<'de>, + { + let mut __field0: _serde::__private::Option< + Option>, + > = _serde::__private::None; + let mut __field1: _serde::__private::Option< + Option>, + > = _serde::__private::None; + while let _serde::__private::Some(__key) + = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { + match __key { + __Field::__field0 => { + if _serde::__private::Option::is_some(&__field0) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field( + "raw_pub_key", + ), + ); + } + __field0 = _serde::__private::Some( + _serde::de::MapAccess::next_value::< + Option>, + >(&mut __map)?, + ); + } + __Field::__field1 => { + if _serde::__private::Option::is_some(&__field1) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field( + "raw_address", + ), + ); + } + __field1 = _serde::__private::Some( + _serde::de::MapAccess::next_value::< + Option>, + >(&mut __map)?, + ); + } + _ => { + let _ = _serde::de::MapAccess::next_value::< + _serde::de::IgnoredAny, + >(&mut __map)?; + } + } + } + let __field0 = match __field0 { + _serde::__private::Some(__field0) => __field0, + _serde::__private::None => { + _serde::__private::de::missing_field("raw_pub_key")? + } + }; + let __field1 = match __field1 { + _serde::__private::Some(__field1) => __field1, + _serde::__private::None => { + _serde::__private::de::missing_field("raw_address")? + } + }; + _serde::__private::Ok(PublicKey { + raw_pub_key: __field0, + raw_address: __field1, + }) + } + } + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &[ + "raw_pub_key", + "raw_address", + ]; + _serde::Deserializer::deserialize_struct( + __deserializer, + "PublicKey", + FIELDS, + __Visitor { + marker: _serde::__private::PhantomData::, + lifetime: _serde::__private::PhantomData, + }, + ) + } + } + }; + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl _serde::Serialize for PublicKey { + fn serialize<__S>( + &self, + __serializer: __S, + ) -> _serde::__private::Result<__S::Ok, __S::Error> + where + __S: _serde::Serializer, + { + let mut __serde_state = _serde::Serializer::serialize_struct( + __serializer, + "PublicKey", + false as usize + 1 + 1, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "raw_pub_key", + &self.raw_pub_key, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "raw_address", + &self.raw_address, + )?; + _serde::ser::SerializeStruct::end(__serde_state) + } + } + }; + #[automatically_derived] + impl ::core::fmt::Debug for PublicKey { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "PublicKey", + "raw_pub_key", + &self.raw_pub_key, + "raw_address", + &&self.raw_address, + ) + } + } + #[automatically_derived] + impl ::core::clone::Clone for PublicKey { + #[inline] + fn clone(&self) -> PublicKey { + PublicKey { + raw_pub_key: ::core::clone::Clone::clone(&self.raw_pub_key), + raw_address: ::core::clone::Clone::clone(&self.raw_address), + } + } + } + impl PublicKey { + /// Generate a Cosmos/Tendermint/Terrad Public Key + pub fn from_bitcoin_public_key(bpub: &bitcoin::key::PublicKey) -> PublicKey { + let bpub_bytes = bpub.inner.serialize(); + let raw_pub_key = PublicKey::pubkey_from_public_key(&bpub_bytes); + let raw_address = PublicKey::address_from_public_key(&bpub_bytes); + PublicKey { + raw_pub_key: Some(raw_pub_key), + raw_address: Some(raw_address), + } + } + /// Generate from secp256k1 Cosmos/Terrad Public Key + pub fn from_public_key(bpub: &[u8]) -> PublicKey { + let raw_pub_key = PublicKey::pubkey_from_public_key(bpub); + let raw_address = PublicKey::address_from_public_key(bpub); + PublicKey { + raw_pub_key: Some(raw_pub_key), + raw_address: Some(raw_address), + } + } + /// Generate a Cosmos/Tendermint/Terrad Account + pub fn from_account( + acc_address: &str, + prefix: &str, + ) -> Result { + PublicKey::check_prefix_and_length(prefix, acc_address, 44) + .and_then(|vu5| { + let vu8 = Vec::from_base32(vu5.as_slice()) + .map_err(|source| DaemonError::Conversion { + key: acc_address.into(), + source, + })?; + Ok(PublicKey { + raw_pub_key: None, + raw_address: Some(vu8), + }) + }) + } + /// build a public key from a tendermint public key + pub fn from_tendermint_key( + tendermint_public_key: &str, + ) -> Result { + let len = tendermint_public_key.len(); + if len == 83 { + PublicKey::check_prefix_and_length( + "terravalconspub", + tendermint_public_key, + len, + ) + .and_then(|vu5| { + let vu8 = Vec::from_base32(vu5.as_slice()) + .map_err(|source| { + DaemonError::Conversion { + key: tendermint_public_key.into(), + source, + } + })?; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("{0:#?}", hex::encode(&vu8)), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 74u32, + ::log::__private_api::Option::None, + ); + } + }; + if vu8.starts_with(&BECH32_PUBKEY_DATA_PREFIX_SECP256K1) { + let public_key = PublicKey::public_key_from_pubkey(&vu8)?; + let raw = PublicKey::address_from_public_key(&public_key); + Ok(PublicKey { + raw_pub_key: Some(vu8), + raw_address: Some(raw), + }) + } else { + Err(DaemonError::ConversionSECP256k1) + } + }) + } else if len == 82 { + PublicKey::check_prefix_and_length( + "terravalconspub", + tendermint_public_key, + len, + ) + .and_then(|vu5| { + let vu8 = Vec::from_base32(vu5.as_slice()) + .map_err(|source| { + DaemonError::Conversion { + key: tendermint_public_key.into(), + source, + } + })?; + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("ED25519 public keys are not fully supported"), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 100u32, + ::log::__private_api::Option::None, + ); + } + }; + if vu8.starts_with(&BECH32_PUBKEY_DATA_PREFIX_ED25519) { + let raw = PublicKey::address_from_public_ed25519_key(&vu8)?; + Ok(PublicKey { + raw_pub_key: Some(vu8), + raw_address: Some(raw), + }) + } else { + Err(DaemonError::ConversionED25519) + } + }) + } else { + Err(DaemonError::ConversionLength(len)) + } + } + /// build a terravalcons address from a tendermint hex key + /// the tendermint_hex_address should be a hex code of 40 length + pub fn from_tendermint_address( + tendermint_hex_address: &str, + ) -> Result { + let len = tendermint_hex_address.len(); + if len == 40 { + let raw = hex::decode(tendermint_hex_address)?; + Ok(PublicKey { + raw_pub_key: None, + raw_address: Some(raw), + }) + } else { + Err(DaemonError::ConversionLengthED25519Hex(len)) + } + } + /// Generate a Operator address for this public key (used by the validator) + pub fn from_operator_address( + valoper_address: &str, + ) -> Result { + PublicKey::check_prefix_and_length("terravaloper", valoper_address, 51) + .and_then(|vu5| { + let vu8 = Vec::from_base32(vu5.as_slice()) + .map_err(|source| DaemonError::Conversion { + key: valoper_address.into(), + source, + })?; + Ok(PublicKey { + raw_pub_key: None, + raw_address: Some(vu8), + }) + }) + } + /// Generate Public key from raw address + pub fn from_raw_address( + raw_address: &str, + ) -> Result { + let vec1 = hex::decode(raw_address)?; + Ok(PublicKey { + raw_pub_key: None, + raw_address: Some(vec1), + }) + } + fn check_prefix_and_length( + prefix: &str, + data: &str, + length: usize, + ) -> Result, DaemonError> { + let (hrp, decoded_str, _) = decode(data) + .map_err(|source| DaemonError::Conversion { + key: data.into(), + source, + })?; + if hrp == prefix && data.len() == length { + Ok(decoded_str) + } else { + Err( + DaemonError::Bech32DecodeExpanded( + hrp, + data.len(), + prefix.into(), + length, + ), + ) + } + } + /** + Gets a bech32-words pubkey from a compressed bytes Secp256K1 public key. + + @param publicKey raw public key + */ + pub fn pubkey_from_public_key(public_key: &[u8]) -> Vec { + [BECH32_PUBKEY_DATA_PREFIX_SECP256K1.to_vec(), public_key.to_vec()] + .concat() + } + /** + Gets a bech32-words pubkey from a compressed bytes Ed25519 public key. + + @param publicKey raw public key + */ + pub fn pubkey_from_ed25519_public_key(public_key: &[u8]) -> Vec { + [BECH32_PUBKEY_DATA_PREFIX_ED25519.to_vec(), public_key.to_vec()] + .concat() + } + /// Translate from a BECH32 prefixed key to a standard public key + pub fn public_key_from_pubkey( + pub_key: &[u8], + ) -> Result, DaemonError> { + if pub_key.starts_with(&BECH32_PUBKEY_DATA_PREFIX_SECP256K1) { + let len = BECH32_PUBKEY_DATA_PREFIX_SECP256K1.len(); + let len2 = pub_key.len(); + Ok(Vec::from(&pub_key[len..len2])) + } else if pub_key.starts_with(&BECH32_PUBKEY_DATA_PREFIX_ED25519) { + let len = BECH32_PUBKEY_DATA_PREFIX_ED25519.len(); + let len2 = pub_key.len(); + let vec = &pub_key[len..len2]; + let ed25519_pubkey = Ed25519::from_bytes(vec.try_into().unwrap())?; + Ok(ed25519_pubkey.to_bytes().to_vec()) + } else { + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("pub key does not start with BECH32 PREFIX"), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 228u32, + ::log::__private_api::Option::None, + ); + } + }; + Err(DaemonError::Bech32DecodeErr) + } + } + /** + Gets a raw address from a compressed bytes public key. + + @param publicKey raw public key + */ + pub fn address_from_public_key(public_key: &[u8]) -> Vec { + let mut hasher = Ripemd160::new(); + let sha_result = ring::digest::digest(&SHA256, public_key); + hasher.update(&sha_result.as_ref()[0..32]); + let ripe_result = hasher.finalize(); + let address: Vec = ripe_result[0..20].to_vec(); + address + } + /** + Gets a raw address from a ed25519 public key. + + @param publicKey raw public key + */ + pub fn address_from_public_ed25519_key( + public_key: &[u8], + ) -> Result, DaemonError> { + if public_key.len() != (32 + 5) { + Err( + DaemonError::ConversionPrefixED25519( + public_key.len(), + hex::encode(public_key), + ), + ) + } else { + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "address_from_public_ed25519_key public key - {0}", + hex::encode(public_key), + ), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 262u32, + ::log::__private_api::Option::None, + ); + } + }; + let mut sha_result: [u8; 32] = [0; 32]; + let sha_result = ring::digest::digest(&SHA256, &public_key[5..]); + let address: Vec = sha_result.as_ref()[0..20].to_vec(); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "address_from_public_ed25519_key sha result - {0}", + hex::encode(&address), + ), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 283u32, + ::log::__private_api::Option::None, + ); + } + }; + Ok(address) + } + } + /// The main account used in most things + pub fn account(&self, prefix: &str) -> Result { + match &self.raw_address { + Some(raw) => { + let data = encode(prefix, raw.to_base32(), Variant::Bech32); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => Err(DaemonError::Implementation), + } + } + /// The operator address used for validators + pub fn operator_address(&self, prefix: &str) -> Result { + match &self.raw_address { + Some(raw) => { + let data = encode( + &{ + let res = ::alloc::fmt::format( + format_args!("{0}{1}", prefix, "valoper"), + ); + res + }, + raw.to_base32(), + Variant::Bech32, + ); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => Err(DaemonError::Implementation), + } + } + /// application public key - Application keys are associated with a public key terrapub- and an address terra- + pub fn application_public_key( + &self, + prefix: &str, + ) -> Result { + match &self.raw_pub_key { + Some(raw) => { + let data = encode( + &{ + let res = ::alloc::fmt::format( + format_args!("{0}{1}", prefix, "pub"), + ); + res + }, + raw.to_base32(), + Variant::Bech32, + ); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Missing Public Key. Can\'t continue"), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 335u32, + ::log::__private_api::Option::None, + ); + } + }; + Err(DaemonError::Implementation) + } + } + } + /// The operator address used for validators public key. + pub fn operator_address_public_key( + &self, + prefix: &str, + ) -> Result { + match &self.raw_pub_key { + Some(raw) => { + let data = encode( + &{ + let res = ::alloc::fmt::format( + format_args!("{0}{1}", prefix, "valoperpub"), + ); + res + }, + raw.to_base32(), + Variant::Bech32, + ); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => Err(DaemonError::Implementation), + } + } + /// This is a unique key used to sign block hashes. It is associated with a public key terravalconspub. + pub fn tendermint(&self, prefix: &str) -> Result { + match &self.raw_address { + Some(raw) => { + let data = encode( + &{ + let res = ::alloc::fmt::format( + format_args!("{0}{1}", prefix, "valcons"), + ); + res + }, + raw.to_base32(), + Variant::Bech32, + ); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => Err(DaemonError::Implementation), + } + } + /// This is a unique key used to sign block hashes. It is associated with a public key terravalconspub. + pub fn tendermint_pubkey( + &self, + prefix: &str, + ) -> Result { + match &self.raw_pub_key { + Some(raw) => { + let b32 = raw.to_base32(); + let data = encode( + &{ + let res = ::alloc::fmt::format( + format_args!("{0}{1}", prefix, "valconspub"), + ); + res + }, + b32, + Variant::Bech32, + ); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => Err(DaemonError::Implementation), + } + } + } + } + pub mod signature { + use crate::DaemonError; + use base64::engine::{general_purpose::STANDARD, Engine}; + use ring::digest::SHA256; + use secp256k1::{Message, Secp256k1}; + pub struct Signature {} + impl Signature { + pub fn verify( + secp: &Secp256k1, + pub_key: &str, + signature: &str, + blob: &str, + ) -> Result<(), DaemonError> { + let public = STANDARD.decode(pub_key)?; + let sig = STANDARD.decode(signature)?; + let pk = secp256k1::PublicKey::from_slice(public.as_slice())?; + let sha_result = ring::digest::digest(&SHA256, blob.as_bytes()); + let message: Message = Message::from_slice(&sha_result.as_ref()[0..32])?; + let secp_sig = secp256k1::ecdsa::Signature::from_compact( + sig.as_slice(), + )?; + secp.verify_ecdsa(&message, &secp_sig, &pk)?; + Ok(()) + } + } + } +} +pub mod live_mock { + //! Live mock is a mock that uses a live chain to query for data. + //! It can be used to do chain-backed unit-testing. It can't be used for state-changing operations. + use crate::queriers::Bank; + use crate::queriers::CosmWasm; + use crate::queriers::DaemonQuerier; + use crate::queriers::Staking; + use cosmwasm_std::Addr; + use cosmwasm_std::AllBalanceResponse; + use cosmwasm_std::BalanceResponse; + use cosmwasm_std::Delegation; + use cosmwasm_std::{AllDelegationsResponse, BondedDenomResponse}; + use cosmwasm_std::BankQuery; + use cosmwasm_std::Binary; + use cosmwasm_std::Empty; + use cosmwasm_std::StakingQuery; + use ibc_chain_registry::chain::ChainData; + use tokio::runtime::Runtime; + use tonic::transport::Channel; + use std::marker::PhantomData; + use std::str::FromStr; + use cosmwasm_std::testing::{MockApi, MockStorage}; + use cosmwasm_std::{ + from_slice, to_binary, Coin, ContractResult, OwnedDeps, Querier, QuerierResult, + QueryRequest, SystemError, SystemResult, Uint128, WasmQuery, + }; + use crate::channel::GrpcChannel; + fn to_cosmwasm_coin(c: cosmrs::proto::cosmos::base::v1beta1::Coin) -> Coin { + Coin { + amount: Uint128::from_str(&c.amount).unwrap(), + denom: c.denom, + } + } + const QUERIER_ERROR: &str = "Only Bank balances and Wasm (raw + smart) and Some staking queries are covered for now"; + /// mock_dependencies is a drop-in replacement for cosmwasm_std::testing::mock_dependencies + /// this uses our CustomQuerier. + pub fn mock_dependencies( + chain_info: ChainData, + ) -> OwnedDeps { + let custom_querier: WasmMockQuerier = WasmMockQuerier::new(chain_info); + OwnedDeps { + storage: MockStorage::default(), + api: MockApi::default(), + querier: custom_querier, + custom_query_type: PhantomData, + } + } + /// Querier struct that fetches queries on-chain directly + pub struct WasmMockQuerier { + channel: Channel, + runtime: Runtime, + } + impl Querier for WasmMockQuerier { + fn raw_query(&self, bin_request: &[u8]) -> QuerierResult { + let request: QueryRequest = match from_slice(bin_request) { + Ok(v) => v, + Err(e) => { + return SystemResult::Err(SystemError::InvalidRequest { + error: { + let res = ::alloc::fmt::format( + format_args!("Parsing query request: {0}", e), + ); + res + }, + request: bin_request.into(), + }); + } + }; + self.handle_query(&request) + } + } + impl WasmMockQuerier { + /// Function used to handle a query and customize the query behavior + /// This implements some queries by querying an actual node for the responses + pub fn handle_query(&self, request: &QueryRequest) -> QuerierResult { + match &request { + QueryRequest::Wasm(x) => { + let querier = CosmWasm::new(self.channel.clone()); + match x { + WasmQuery::Smart { contract_addr, msg } => { + let query_result: Result = self + .runtime + .block_on( + querier + .contract_state(contract_addr.to_string(), msg.to_vec()), + ) + .map(|query_result| query_result.into()); + SystemResult::Ok(ContractResult::from(query_result)) + } + WasmQuery::Raw { contract_addr, key } => { + let query_result = self + .runtime + .block_on( + querier + .contract_raw_state(contract_addr.to_string(), key.to_vec()), + ) + .map(|query_result| query_result.data.into()); + SystemResult::Ok(ContractResult::from(query_result)) + } + _ => { + SystemResult::Err(SystemError::InvalidRequest { + error: QUERIER_ERROR.to_string(), + request: to_binary(&request).unwrap(), + }) + } + } + } + QueryRequest::Bank(x) => { + let querier = Bank::new(self.channel.clone()); + match x { + BankQuery::Balance { address, denom } => { + let query_result = self + .runtime + .block_on(querier.balance(address, Some(denom.clone()))) + .map(|result| { + to_binary( + &BalanceResponse { + amount: Coin { + amount: Uint128::from_str(&result[0].amount).unwrap(), + denom: result[0].denom.clone(), + }, + }, + ) + .unwrap() + }); + SystemResult::Ok(ContractResult::from(query_result)) + } + BankQuery::AllBalances { address } => { + let query_result = self + .runtime + .block_on(querier.balance(address, None)) + .map(|result| AllBalanceResponse { + amount: result + .into_iter() + .map(|c| Coin { + amount: Uint128::from_str(&c.amount).unwrap(), + denom: c.denom, + }) + .collect(), + }) + .map(|query_result| to_binary(&query_result)) + .unwrap(); + SystemResult::Ok(ContractResult::from(query_result)) + } + _ => { + SystemResult::Err(SystemError::InvalidRequest { + error: QUERIER_ERROR.to_string(), + request: to_binary(&request).unwrap(), + }) + } + } + } + QueryRequest::Staking(x) => { + let querier = Staking::new(self.channel.clone()); + match x { + StakingQuery::BondedDenom {} => { + let query_result = self + .runtime + .block_on(querier.params()) + .map(|result| BondedDenomResponse { + denom: result.params.unwrap().bond_denom, + }) + .map(|query_result| to_binary(&query_result)) + .unwrap(); + SystemResult::Ok(ContractResult::from(query_result)) + } + StakingQuery::AllDelegations { delegator } => { + let query_result = self + .runtime + .block_on(querier.delegator_delegations(delegator, None)) + .map(|result| AllDelegationsResponse { + delegations: result + .delegation_responses + .into_iter() + .filter_map(|delegation| { + delegation + .delegation + .map(|d| Delegation { + delegator: Addr::unchecked(d.delegator_address), + validator: d.validator_address, + amount: to_cosmwasm_coin(delegation.balance.unwrap()), + }) + }) + .collect(), + }) + .map(|query_result| to_binary(&query_result)) + .unwrap(); + SystemResult::Ok(ContractResult::from(query_result)) + } + _ => ::core::panicking::panic("not yet implemented"), + } + } + _ => { + SystemResult::Err(SystemError::InvalidRequest { + error: QUERIER_ERROR.to_string(), + request: to_binary(&request).unwrap(), + }) + } + } + } + } + impl WasmMockQuerier { + /// Creates a querier from chain information + pub fn new(chain: ChainData) -> Self { + let rt = Runtime::new().unwrap(); + let channel = rt + .block_on(GrpcChannel::connect(&chain.apis.grpc, &chain.chain_id)) + .unwrap(); + WasmMockQuerier { + channel, + runtime: rt, + } + } + } +} +pub mod queriers { + //! # DaemonQuerier + //! + //! DaemonAsync queriers are gRPC query clients for the CosmosSDK modules. They can be used to query the different modules (Bank, Ibc, Authz, ...). + //! + //! ## Usage + //! + //! You will need to acquire a [gRPC channel](Channel) to a running CosmosSDK node to be able to use the queriers. + //! Here is an example of how to acquire one using the DaemonAsync builder. + //! + //! ```no_run + //! // require the querier you want to use, in this case Node + //! use cw_orch_daemon::{queriers::Node, DaemonAsync, networks, queriers::DaemonQuerier}; + //! # tokio_test::block_on(async { + //! // call the builder and configure it as you need + //! let daemon = DaemonAsync::builder() + //! .chain(networks::LOCAL_JUNO) + //! .build() + //! .await.unwrap(); + //! // now you can use the Node querier: + //! let node = Node::new(daemon.channel()); + //! let node_info = node.info(); + //! # }) + //! ``` + mod bank { + use crate::{cosmos_modules, error::DaemonError}; + use cosmrs::proto::cosmos::{ + base::{query::v1beta1::PageRequest, v1beta1::Coin}, + bank::v1beta1::QueryBalanceResponse, + }; + use tonic::transport::Channel; + use super::DaemonQuerier; + /// Queries for Cosmos Bank Module + pub struct Bank { + channel: Channel, + } + impl DaemonQuerier for Bank { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Bank { + /// Query the bank balance of a given address + /// If denom is None, returns all balances + pub async fn balance( + &self, + address: impl Into, + denom: Option, + ) -> Result, DaemonError> { + use cosmos_modules::bank::query_client::QueryClient; + use cosmos_modules::bank::QueryBalanceRequest; + match denom { + Some(denom) => { + let resp = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QueryBalanceRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryBalanceRequest { + address: address.into(), + denom: denom, + }; + let response = client + .balance(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 30u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + let coin = resp.balance.unwrap(); + Ok( + <[_]>::into_vec( + #[rustc_box] + ::alloc::boxed::Box::new([coin]), + ), + ) + } + None => { + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = cosmos_modules::bank::QueryAllBalancesRequest { + address: address.into(), + ..Default::default() + }; + let resp = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QueryAllBalancesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryAllBalancesRequest { + address: address.into(), + pagination: None, + }; + let response = client + .balance(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 49u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + let resp = client.all_balances(request).await?.into_inner(); + let coins = resp.balances; + Ok(coins.into_iter().collect()) + } + } + } + /// Query spendable balance for address + pub async fn spendable_balances( + &self, + address: impl Into, + ) -> Result, DaemonError> { + let spendable_balances: cosmos_modules::bank::QuerySpendableBalancesResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QuerySpendableBalancesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QuerySpendableBalancesRequest { + address: address.into(), + pagination: None, + }; + let response = client + .spendable_balances(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 71u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(spendable_balances.balances) + } + /// Query total supply in the bank + pub async fn total_supply(&self) -> Result, DaemonError> { + let total_supply: cosmos_modules::bank::QueryTotalSupplyResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QueryTotalSupplyRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryTotalSupplyRequest { + pagination: None, + }; + let response = client + .total_supply(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 85u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(total_supply.supply) + } + /// Query total supply in the bank for a denom + pub async fn supply_of( + &self, + denom: impl Into, + ) -> Result { + let supply_of: cosmos_modules::bank::QuerySupplyOfResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QuerySupplyOfRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QuerySupplyOfRequest { + denom: denom.into(), + }; + let response = client.supply_of(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 96u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(supply_of.amount.unwrap()) + } + /// Query params + pub async fn params( + &self, + ) -> Result { + let params: cosmos_modules::bank::QueryParamsResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QueryParamsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryParamsRequest {}; + let response = client.params(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 110u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(params.params.unwrap()) + } + /// Query denom metadata + pub async fn denom_metadata( + &self, + denom: impl Into, + ) -> Result { + let denom_metadata: cosmos_modules::bank::QueryDenomMetadataResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QueryDenomMetadataRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDenomMetadataRequest { + denom: denom.into(), + }; + let response = client + .denom_metadata(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 119u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(denom_metadata.metadata.unwrap()) + } + /// Query denoms metadata with pagination + /// + /// see [PageRequest] for pagination + pub async fn denoms_metadata( + &self, + pagination: Option, + ) -> Result, DaemonError> { + let denoms_metadata: cosmos_modules::bank::QueryDenomsMetadataResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QueryDenomsMetadataRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDenomsMetadataRequest { + pagination: pagination, + }; + let response = client + .denoms_metadata(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 137u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(denoms_metadata.metadatas) + } + } + } + mod cosmwasm { + use crate::{cosmos_modules, error::DaemonError}; + use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; + use tonic::transport::Channel; + use super::DaemonQuerier; + /// Querier for the CosmWasm SDK module + pub struct CosmWasm { + channel: Channel, + } + impl DaemonQuerier for CosmWasm { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl CosmWasm { + /// Query code_id by hash + pub async fn code_id_hash( + &self, + code_id: u64, + ) -> Result { + use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryCodeRequest { code_id }; + let resp = client.code(request).await?.into_inner(); + let contract_hash = resp.code_info.unwrap().data_hash; + let on_chain_hash = base16::encode_lower(&contract_hash); + Ok(on_chain_hash) + } + /// Query contract info + pub async fn contract_info( + &self, + address: impl Into, + ) -> Result { + use cosmos_modules::cosmwasm::{ + query_client::*, QueryContractInfoRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryContractInfoRequest { + address: address.into(), + }; + let resp = client.contract_info(request).await?.into_inner(); + let contract_info = resp.contract_info.unwrap(); + Ok(contract_info) + } + /// Query contract history + pub async fn contract_history( + &self, + address: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::cosmwasm::QueryContractHistoryResponse, + DaemonError, + > { + use cosmos_modules::cosmwasm::{ + query_client::*, QueryContractHistoryRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryContractHistoryRequest { + address: address.into(), + pagination, + }; + Ok(client.contract_history(request).await?.into_inner()) + } + /// Query contract state + pub async fn contract_state( + &self, + address: impl Into, + query_data: Vec, + ) -> Result, DaemonError> { + use cosmos_modules::cosmwasm::{ + query_client::*, QuerySmartContractStateRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QuerySmartContractStateRequest { + address: address.into(), + query_data, + }; + Ok(client.smart_contract_state(request).await?.into_inner().data) + } + /// Query all contract state + pub async fn all_contract_state( + &self, + address: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::cosmwasm::QueryAllContractStateResponse, + DaemonError, + > { + use cosmos_modules::cosmwasm::{ + query_client::*, QueryAllContractStateRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryAllContractStateRequest { + address: address.into(), + pagination, + }; + Ok(client.all_contract_state(request).await?.into_inner()) + } + /// Query code + pub async fn code( + &self, + code_id: u64, + ) -> Result { + use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryCodeRequest { code_id }; + Ok(client.code(request).await?.into_inner().code_info.unwrap()) + } + /// Query code bytes + pub async fn code_data(&self, code_id: u64) -> Result, DaemonError> { + use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryCodeRequest { code_id }; + Ok(client.code(request).await?.into_inner().data) + } + /// Query codes + pub async fn codes( + &self, + pagination: Option, + ) -> Result, DaemonError> { + use cosmos_modules::cosmwasm::{query_client::*, QueryCodesRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryCodesRequest { pagination }; + Ok(client.codes(request).await?.into_inner().code_infos) + } + /// Query pinned codes + pub async fn pinned_codes( + &self, + ) -> Result< + cosmos_modules::cosmwasm::QueryPinnedCodesResponse, + DaemonError, + > { + use cosmos_modules::cosmwasm::{query_client::*, QueryPinnedCodesRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryPinnedCodesRequest { + pagination: None, + }; + Ok(client.pinned_codes(request).await?.into_inner()) + } + /// Query contracts by code + pub async fn contract_by_codes( + &self, + code_id: u64, + ) -> Result< + cosmos_modules::cosmwasm::QueryContractsByCodeResponse, + DaemonError, + > { + use cosmos_modules::cosmwasm::{ + query_client::*, QueryContractsByCodeRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryContractsByCodeRequest { + code_id, + pagination: None, + }; + Ok(client.contracts_by_code(request).await?.into_inner()) + } + /// Query raw contract state + pub async fn contract_raw_state( + &self, + address: impl Into, + query_data: Vec, + ) -> Result< + cosmos_modules::cosmwasm::QueryRawContractStateResponse, + DaemonError, + > { + use cosmos_modules::cosmwasm::{ + query_client::*, QueryRawContractStateRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryRawContractStateRequest { + address: address.into(), + query_data, + }; + Ok(client.raw_contract_state(request).await?.into_inner()) + } + /// Query params + pub async fn params( + &self, + ) -> Result { + use cosmos_modules::cosmwasm::{query_client::*, QueryParamsRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + Ok(client.params(QueryParamsRequest {}).await?.into_inner()) + } + } + } + mod feegrant { + use crate::{cosmos_modules, error::DaemonError}; + use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; + use tonic::transport::Channel; + use super::DaemonQuerier; + /// Querier for the Cosmos Gov module + pub struct Feegrant { + channel: Channel, + } + impl DaemonQuerier for Feegrant { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Feegrant { + /// Query all allowances granted to the grantee address by a granter address + pub async fn allowance( + &self, + granter: impl Into, + grantee: impl Into, + ) -> Result { + let allowance: cosmos_modules::feegrant::QueryAllowanceResponse = { + use crate::cosmos_modules::feegrant::{ + query_client::QueryClient, QueryAllowanceRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryAllowanceRequest { + granter: granter.into(), + grantee: grantee.into(), + }; + let response = client.allowance(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::feegrant", + "cw_orch_daemon::queriers::feegrant", + "cw-orch-daemon/src/queriers/feegrant.rs", + ), + 25u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(allowance.allowance.unwrap()) + } + /// Query allowances for grantee address with a given pagination + /// + /// see [PageRequest] for pagination + pub async fn allowances( + &self, + grantee: impl Into, + pagination: Option, + ) -> Result, DaemonError> { + let allowances: cosmos_modules::feegrant::QueryAllowancesResponse = { + use crate::cosmos_modules::feegrant::{ + query_client::QueryClient, QueryAllowancesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryAllowancesRequest { + grantee: grantee.into(), + pagination: pagination, + }; + let response = client + .allowances(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::feegrant", + "cw_orch_daemon::queriers::feegrant", + "cw-orch-daemon/src/queriers/feegrant.rs", + ), + 45u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(allowances.allowances) + } + } + } + mod gov { + use crate::{cosmos_modules, error::DaemonError}; + use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; + use tonic::transport::Channel; + use super::DaemonQuerier; + /// Querier for the Cosmos Gov module + pub struct Gov { + channel: Channel, + } + impl DaemonQuerier for Gov { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Gov { + /// Query proposal details by proposal id + pub async fn proposal( + &self, + proposal_id: u64, + ) -> Result { + let proposal: cosmos_modules::gov::QueryProposalResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryProposalRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryProposalRequest { + proposal_id: proposal_id, + }; + let response = client.proposal(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 24u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(proposal.proposal.unwrap()) + } + /// Query proposals based on given status + /// + /// see [PageRequest] for pagination + pub async fn proposals( + &self, + proposal_status: GovProposalStatus, + voter: impl Into, + depositor: impl Into, + pagination: Option, + ) -> Result { + let proposals: cosmos_modules::gov::QueryProposalsResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryProposalsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryProposalsRequest { + proposal_status: proposal_status as i32, + voter: voter.into(), + depositor: depositor.into(), + pagination: pagination, + }; + let response = client.proposals(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 45u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(proposals) + } + /// Query voted information based on proposal_id for voter address + pub async fn vote( + &self, + proposal_id: u64, + voter: impl Into, + ) -> Result { + let vote: cosmos_modules::gov::QueryVoteResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryVoteRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryVoteRequest { + proposal_id: proposal_id, + voter: voter.into(), + }; + let response = client.vote(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 65u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(vote.vote.unwrap()) + } + /// Query votes of a given proposal + /// + /// see [PageRequest] for pagination + pub async fn votes( + &self, + proposal_id: impl Into, + pagination: Option, + ) -> Result { + let votes: cosmos_modules::gov::QueryVotesResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryVotesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryVotesRequest { + proposal_id: proposal_id.into(), + pagination: pagination, + }; + let response = client.votes(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 85u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(votes) + } + /// Query all parameters of the gov module + pub async fn params( + &self, + params_type: impl Into, + ) -> Result { + let params: cosmos_modules::gov::QueryParamsResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryParamsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryParamsRequest { + params_type: params_type.into(), + }; + let response = client.params(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 102u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(params) + } + /// Query deposit information using proposal_id and depositor address + pub async fn deposit( + &self, + proposal_id: u64, + depositor: impl Into, + ) -> Result { + let deposit: cosmos_modules::gov::QueryDepositResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryDepositRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDepositRequest { + proposal_id: proposal_id, + depositor: depositor.into(), + }; + let response = client.deposit(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 119u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(deposit.deposit.unwrap()) + } + /// Query deposits of a proposal + /// + /// see [PageRequest] for pagination + pub async fn deposits( + &self, + proposal_id: u64, + pagination: Option, + ) -> Result { + let deposits: cosmos_modules::gov::QueryDepositsResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryDepositsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDepositsRequest { + proposal_id: proposal_id, + pagination: pagination, + }; + let response = client.deposits(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 139u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(deposits) + } + /// TallyResult queries the tally of a proposal vote. + pub async fn tally_result( + &mut self, + proposal_id: u64, + ) -> Result { + let tally_result: cosmos_modules::gov::QueryTallyResultResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryTallyResultRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryTallyResultRequest { + proposal_id: proposal_id, + }; + let response = client + .tally_result(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 156u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(tally_result.tally.unwrap()) + } + } + /// Proposal status + #[allow(missing_docs)] + pub enum GovProposalStatus { + Unspecified = 0, + DepositPeriod = 1, + VotingPeriod = 2, + Passed = 3, + Rejected = 4, + Failed = 5, + } + } + mod ibc { + use super::DaemonQuerier; + use crate::{cosmos_modules, error::DaemonError}; + use cosmos_modules::ibc_channel; + use cosmrs::proto::ibc::{ + applications::transfer::v1::{DenomTrace, QueryDenomTraceResponse}, + core::{ + channel::v1::QueryPacketCommitmentResponse, + client::v1::{IdentifiedClientState, QueryClientStatesResponse}, + connection::v1::{IdentifiedConnection, State}, + }, + lightclients::tendermint::v1::ClientState, + }; + use prost::Message; + use tonic::transport::Channel; + /// Querier for the Cosmos IBC module + pub struct Ibc { + channel: Channel, + } + impl DaemonQuerier for Ibc { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Ibc { + /// Get the trace of a specific denom + pub async fn denom_trace( + &self, + hash: String, + ) -> Result { + let denom_trace: QueryDenomTraceResponse = { + use crate::cosmos_modules::ibc_transfer::{ + query_client::QueryClient, QueryDenomTraceRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDenomTraceRequest { + hash: hash, + }; + let response = client + .denom_trace(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 32u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(denom_trace.denom_trace.unwrap()) + } + /// Get all the IBC clients for this daemon + pub async fn clients( + &self, + ) -> Result, DaemonError> { + let ibc_clients: QueryClientStatesResponse = { + use crate::cosmos_modules::ibc_client::{ + query_client::QueryClient, QueryClientStatesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryClientStatesRequest { + pagination: None, + }; + let response = client + .client_states(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 45u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_clients.client_states) + } + /// Get the state of a specific IBC client + pub async fn client_state( + &self, + client_id: impl ToString, + ) -> Result< + cosmos_modules::ibc_client::QueryClientStateResponse, + DaemonError, + > { + let response: cosmos_modules::ibc_client::QueryClientStateResponse = { + use crate::cosmos_modules::ibc_client::{ + query_client::QueryClient, QueryClientStateRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryClientStateRequest { + client_id: client_id.to_string(), + }; + let response = client + .client_state(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 60u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(response) + } + /// Get the consensus state of a specific IBC client + 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 = { + use crate::cosmos_modules::ibc_client::{ + query_client::QueryClient, QueryConsensusStatesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryConsensusStatesRequest { + client_id: client_id, + pagination: None, + }; + let response = client + .consensus_states(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 77u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(response) + } + /// Get the consensus status of a specific IBC client + pub async fn client_status( + &self, + client_id: impl ToString, + ) -> Result< + cosmos_modules::ibc_client::QueryClientStatusResponse, + DaemonError, + > { + let response: cosmos_modules::ibc_client::QueryClientStatusResponse = { + use crate::cosmos_modules::ibc_client::{ + query_client::QueryClient, QueryClientStatusRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryClientStatusRequest { + client_id: client_id.to_string(), + }; + let response = client + .client_status(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 95u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(response) + } + /// Get the ibc client parameters + pub async fn client_params( + &self, + ) -> Result< + cosmos_modules::ibc_client::QueryClientParamsResponse, + DaemonError, + > { + let response: cosmos_modules::ibc_client::QueryClientParamsResponse = { + use crate::cosmos_modules::ibc_client::{ + query_client::QueryClient, QueryClientParamsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryClientParamsRequest {}; + let response = client + .client_params(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 111u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(response) + } + /// Query the IBC connections for a specific chain + pub async fn connections( + &self, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_connection::QueryConnectionsResponse; + let ibc_connections: QueryConnectionsResponse = { + use crate::cosmos_modules::ibc_connection::{ + query_client::QueryClient, QueryConnectionsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryConnectionsRequest { + pagination: None, + }; + let response = client + .connections(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 121u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_connections.connections) + } + /// Search for open connections with a specific chain. + pub async fn open_connections( + &self, + client_chain_id: impl ToString, + ) -> Result, 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) + } + /// Get all the connections for this client + pub async fn client_connections( + &self, + client_id: impl Into, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_connection::QueryClientConnectionsResponse; + let client_id = client_id.into(); + let ibc_client_connections: QueryClientConnectionsResponse = { + use crate::cosmos_modules::ibc_connection::{ + query_client::QueryClient, QueryClientConnectionsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryClientConnectionsRequest { + client_id: client_id.clone(), + }; + let response = client + .client_connections(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 163u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_client_connections.connection_paths) + } + /// Get the (tendermint) client state for a specific connection + pub async fn connection_client( + &self, + connection_id: impl Into, + ) -> Result { + use cosmos_modules::ibc_connection::QueryConnectionClientStateResponse; + let connection_id = connection_id.into(); + let ibc_connection_client: QueryConnectionClientStateResponse = { + use crate::cosmos_modules::ibc_connection::{ + query_client::QueryClient, QueryConnectionClientStateRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryConnectionClientStateRequest { + connection_id: connection_id.clone(), + }; + let response = client + .connection_client_state(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 183u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + let client_state = ibc_connection_client + .identified_client_state + .ok_or( + DaemonError::ibc_err({ + let res = ::alloc::fmt::format( + format_args!( + "error identifying client for connection {0}", + connection_id, + ), + ); + res + }), + )?; + let client_state = ClientState::decode( + client_state.client_state.unwrap().value.as_slice(), + ) + .map_err(|e| DaemonError::ibc_err({ + let res = ::alloc::fmt::format( + format_args!("error decoding client state: {0}", e), + ); + res + }))?; + Ok(client_state) + } + /// Get the channel for a specific port and channel id + pub async fn channel( + &self, + port_id: impl Into, + channel_id: impl Into, + ) -> Result { + use cosmos_modules::ibc_channel::QueryChannelResponse; + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_channel: QueryChannelResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryChannelRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryChannelRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }; + let response = client.channel(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 218u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + ibc_channel + .channel + .ok_or( + DaemonError::ibc_err({ + let res = ::alloc::fmt::format( + format_args!( + "error fetching channel {0} on port {1}", + channel_id, + port_id, + ), + ); + res + }), + ) + } + /// Get all the channels for a specific connection + pub async fn connection_channels( + &self, + connection_id: impl Into, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_channel::QueryConnectionChannelsResponse; + let connection_id = connection_id.into(); + let ibc_connection_channels: QueryConnectionChannelsResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryConnectionChannelsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryConnectionChannelsRequest { + connection: connection_id.clone(), + pagination: None, + }; + let response = client + .connection_channels(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 242u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_connection_channels.channels) + } + /// Get the client state for a specific channel and port + pub async fn channel_client_state( + &self, + port_id: impl Into, + channel_id: impl Into, + ) -> Result { + use cosmos_modules::ibc_channel::QueryChannelClientStateResponse; + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_channel_client_state: QueryChannelClientStateResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryChannelClientStateRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryChannelClientStateRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }; + let response = client + .channel_client_state(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 265u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + ibc_channel_client_state + .identified_client_state + .ok_or( + DaemonError::ibc_err({ + let res = ::alloc::fmt::format( + format_args!( + "error identifying client for channel {0} on port {1}", + channel_id, + port_id, + ), + ); + res + }), + ) + } + /// Get all the packet commitments for a specific channel and port + pub async fn packet_commitments( + &self, + port_id: impl Into, + channel_id: impl Into, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_channel::QueryPacketCommitmentsResponse; + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_commitments: QueryPacketCommitmentsResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryPacketCommitmentsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPacketCommitmentsRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + pagination: None, + }; + let response = client + .packet_commitments(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 297u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_commitments.commitments) + } + /// Get the packet commitment for a specific channel, port and sequence + pub async fn packet_commitment( + &self, + port_id: impl Into, + channel_id: impl Into, + sequence: u64, + ) -> Result { + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_commitment: QueryPacketCommitmentResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryPacketCommitmentRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPacketCommitmentRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + sequence: sequence, + }; + let response = client + .packet_commitment(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 320u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_commitment) + } + /// Returns if the packet is received on the connected chain. + pub async fn packet_receipt( + &self, + port_id: impl Into, + channel_id: impl Into, + sequence: u64, + ) -> Result { + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_receipt: ibc_channel::QueryPacketReceiptResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryPacketReceiptRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPacketReceiptRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + sequence: sequence, + }; + let response = client + .packet_receipt(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 345u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_receipt.received) + } + /// Get all the packet acknowledgements for a specific channel, port and commitment sequences + pub async fn packet_acknowledgements( + &self, + port_id: impl Into, + channel_id: impl Into, + packet_commitment_sequences: Vec, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_channel::QueryPacketAcknowledgementsResponse; + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_acknowledgements: QueryPacketAcknowledgementsResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryPacketAcknowledgementsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPacketAcknowledgementsRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + packet_commitment_sequences: packet_commitment_sequences, + pagination: None, + }; + let response = client + .packet_acknowledgements(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 372u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_acknowledgements.acknowledgements) + } + /// Get the packet acknowledgement for a specific channel, port and sequence + pub async fn packet_acknowledgement( + &self, + port_id: impl Into, + channel_id: impl Into, + sequence: u64, + ) -> Result, DaemonError> { + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_acknowledgement: ibc_channel::QueryPacketAcknowledgementResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryPacketAcknowledgementRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPacketAcknowledgementRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + sequence: sequence, + }; + let response = client + .packet_acknowledgement(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 396u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_acknowledgement.acknowledgement) + } + /// No acknowledgement exists on receiving chain for the given packet commitment sequence on sending chain. + /// Returns the packet sequences that have not yet been received. + pub async fn unreceived_packets( + &self, + port_id: impl Into, + channel_id: impl Into, + packet_commitment_sequences: Vec, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_channel::QueryUnreceivedPacketsResponse; + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_unreceived: QueryUnreceivedPacketsResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryUnreceivedPacketsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryUnreceivedPacketsRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + packet_commitment_sequences: packet_commitment_sequences, + }; + let response = client + .unreceived_packets(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 422u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_unreceived.sequences) + } + /// Returns the acknowledgement sequences that have not yet been received. + /// Given a list of acknowledgement sequences from counterparty, determine if an ack on the counterparty chain has been received on the executing chain. + /// Returns the list of acknowledgement sequences that have not yet been received. + pub async fn unreceived_acks( + &self, + port_id: impl Into, + channel_id: impl Into, + packet_ack_sequences: Vec, + ) -> Result, DaemonError> { + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_unreceived: ibc_channel::QueryUnreceivedAcksResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryUnreceivedAcksRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryUnreceivedAcksRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + packet_ack_sequences: packet_ack_sequences, + }; + let response = client + .unreceived_acks(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 447u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_unreceived.sequences) + } + /// Returns the acknowledgement sequences that have not yet been received. + /// Given a list of acknowledgement sequences from counterparty, determine if an ack on the counterparty chain has been received on the executing chain. + /// Returns the list of acknowledgement sequences that have not yet been received. + pub async fn next_sequence_receive( + &self, + port_id: impl Into, + channel_id: impl Into, + ) -> Result { + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let next_receive: ibc_channel::QueryNextSequenceReceiveResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryNextSequenceReceiveRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryNextSequenceReceiveRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }; + let response = client + .next_sequence_receive(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 471u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(next_receive.next_sequence_receive) + } + } + } + mod node { + use std::{cmp::min, time::Duration}; + use crate::{cosmos_modules, error::DaemonError, tx_resp::CosmTxResponse}; + use cosmrs::{ + proto::cosmos::{ + base::query::v1beta1::PageRequest, tx::v1beta1::SimulateResponse, + }, + tendermint::{Block, Time}, + }; + use tonic::transport::Channel; + use super::DaemonQuerier; + const MAX_TX_QUERY_RETRIES: usize = 50; + /// Querier for the Tendermint node. + /// Supports queries for block and tx information + pub struct Node { + channel: Channel, + } + impl DaemonQuerier for Node { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Node { + /// Returns node info + pub async fn info( + &self, + ) -> Result { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_node_info(cosmos_modules::tendermint::GetNodeInfoRequest { + }) + .await? + .into_inner(); + Ok(resp) + } + /// Queries node syncing + pub async fn syncing(&self) -> Result { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_syncing(cosmos_modules::tendermint::GetSyncingRequest { + }) + .await? + .into_inner(); + Ok(resp.syncing) + } + /// Returns latests block information + pub async fn latest_block(&self) -> Result { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_latest_block(cosmos_modules::tendermint::GetLatestBlockRequest { + }) + .await? + .into_inner(); + Ok(Block::try_from(resp.block.unwrap())?) + } + /// Returns block information fetched by height + pub async fn block_by_height( + &self, + height: u64, + ) -> Result { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_block_by_height(cosmos_modules::tendermint::GetBlockByHeightRequest { + height: height as i64, + }) + .await? + .into_inner(); + Ok(Block::try_from(resp.block.unwrap())?) + } + /// Return the average block time for the last 50 blocks or since inception + /// This is used to estimate the time when a tx will be included in a block + pub async fn average_block_speed( + &self, + multiplier: Option, + ) -> Result { + let mut latest_block = self.latest_block().await?; + let latest_block_time = latest_block.header.time; + let mut latest_block_height = latest_block.header.height.value(); + while latest_block_height <= 1 { + tokio::time::sleep(Duration::from_secs(1)).await; + latest_block = self.latest_block().await?; + latest_block_height = latest_block.header.height.value(); + } + let avg_period = min(latest_block_height - 1, 50); + let block_avg_period_ago = self + .block_by_height(latest_block_height - avg_period) + .await?; + let block_avg_period_ago_time = block_avg_period_ago.header.time; + let average_block_time = latest_block_time + .duration_since(block_avg_period_ago_time)?; + let average_block_time = average_block_time.as_secs() / avg_period; + let average_block_time = match multiplier { + Some(multiplier) => (average_block_time as f32 * multiplier) as u64, + None => average_block_time, + }; + Ok(std::cmp::max(average_block_time, 1)) + } + /// Returns latests validator set + pub async fn latest_validator_set( + &self, + pagination: Option, + ) -> Result< + cosmos_modules::tendermint::GetLatestValidatorSetResponse, + DaemonError, + > { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_latest_validator_set(cosmos_modules::tendermint::GetLatestValidatorSetRequest { + pagination, + }) + .await? + .into_inner(); + Ok(resp) + } + /// Returns latests validator set fetched by height + pub async fn validator_set_by_height( + &self, + height: i64, + pagination: Option, + ) -> Result< + cosmos_modules::tendermint::GetValidatorSetByHeightResponse, + DaemonError, + > { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_validator_set_by_height(cosmos_modules::tendermint::GetValidatorSetByHeightRequest { + height, + pagination, + }) + .await? + .into_inner(); + Ok(resp) + } + /// Returns current block height + pub async fn block_height(&self) -> Result { + let block = self.latest_block().await?; + Ok(block.header.height.value()) + } + /// Returns the block timestamp (since unix epoch) in nanos + pub async fn block_time(&self) -> Result { + let block = self.latest_block().await?; + Ok(block.header.time.duration_since(Time::unix_epoch())?.as_nanos()) + } + /// Simulate TX + pub async fn simulate_tx( + &self, + tx_bytes: Vec, + ) -> Result { + let mut client = cosmos_modules::tx::service_client::ServiceClient::new( + self.channel.clone(), + ); + #[allow(deprecated)] + let resp: SimulateResponse = client + .simulate(cosmos_modules::tx::SimulateRequest { + tx: None, + tx_bytes, + }) + .await? + .into_inner(); + let gas_used = resp.gas_info.unwrap().gas_used; + Ok(gas_used) + } + /// Returns all the block info + pub async fn block_info( + &self, + ) -> Result { + let block = self.latest_block().await?; + let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; + let time = cosmwasm_std::Timestamp::from_nanos( + since_epoch.as_nanos() as u64, + ); + Ok(cosmwasm_std::BlockInfo { + height: block.header.height.value(), + time, + chain_id: block.header.chain_id.to_string(), + }) + } + /// Find TX by hash + pub async fn find_tx( + &self, + hash: String, + ) -> Result { + self.find_tx_with_retries(hash, MAX_TX_QUERY_RETRIES).await + } + /// Find TX by hash with a given amount of retries + pub async fn find_tx_with_retries( + &self, + hash: String, + retries: usize, + ) -> Result { + let mut client = cosmos_modules::tx::service_client::ServiceClient::new( + self.channel.clone(), + ); + let request = cosmos_modules::tx::GetTxRequest { + hash: hash.clone(), + }; + let mut block_speed = self.average_block_speed(Some(0.7)).await?; + for _ in 0..retries { + match client.get_tx(request.clone()).await { + Ok(tx) => { + let resp = tx.into_inner().tx_response.unwrap(); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("TX found: {0:?}", resp), + lvl, + &( + "cw_orch_daemon::queriers::node", + "cw_orch_daemon::queriers::node", + "cw-orch-daemon/src/queriers/node.rs", + ), + 220u32, + ::log::__private_api::Option::None, + ); + } + }; + return Ok(resp.into()); + } + Err(err) => { + block_speed = (block_speed as f64 * 1.6) as u64; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("TX not found with error: {0:?}", err), + lvl, + &( + "cw_orch_daemon::queriers::node", + "cw_orch_daemon::queriers::node", + "cw-orch-daemon/src/queriers/node.rs", + ), + 226u32, + ::log::__private_api::Option::None, + ); + } + }; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Waiting {0} seconds", block_speed), + lvl, + &( + "cw_orch_daemon::queriers::node", + "cw_orch_daemon::queriers::node", + "cw-orch-daemon/src/queriers/node.rs", + ), + 227u32, + ::log::__private_api::Option::None, + ); + } + }; + tokio::time::sleep(Duration::from_secs(block_speed)).await; + } + } + } + Err(DaemonError::TXNotFound(hash, retries)) + } + } + } + mod staking { + use crate::{cosmos_modules, error::DaemonError}; + use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; + use tonic::transport::Channel; + use super::DaemonQuerier; + /// Querier for the Cosmos Staking module + pub struct Staking { + channel: Channel, + } + impl DaemonQuerier for Staking { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Staking { + /// Queries validator info for given validator address + pub async fn validator( + &self, + validator_addr: impl Into, + ) -> Result { + let validator: cosmos_modules::staking::QueryValidatorResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryValidatorRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryValidatorRequest { + validator_addr: validator_addr.into(), + }; + let response = client.validator(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 24u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(validator.validator.unwrap()) + } + /// Queries all validators that match the given status + /// + /// see [StakingBondStatus] for available statuses + pub async fn validators( + &self, + status: StakingBondStatus, + ) -> Result, DaemonError> { + let validators: cosmos_modules::staking::QueryValidatorsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryValidatorsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryValidatorsRequest { + status: status.to_string(), + pagination: None, + }; + let response = client + .validators(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 42u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(validators.validators) + } + /// Query validator delegations info for given validator + /// + /// see [PageRequest] for pagination + pub async fn delegations( + &self, + validator_addr: impl Into, + pagination: Option, + ) -> Result, DaemonError> { + let validator_delegations: cosmos_modules::staking::QueryValidatorDelegationsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryValidatorDelegationsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryValidatorDelegationsRequest { + validator_addr: validator_addr.into(), + pagination: pagination, + }; + let response = client + .validator_delegations(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 62u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(validator_delegations.delegation_responses) + } + /// Query validator unbonding delegations of a validator + pub async fn unbonding_delegations( + &self, + validator_addr: impl Into, + ) -> Result, DaemonError> { + let validator_unbonding_delegations: cosmos_modules::staking::QueryValidatorUnbondingDelegationsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, + QueryValidatorUnbondingDelegationsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryValidatorUnbondingDelegationsRequest { + validator_addr: validator_addr.into(), + pagination: None, + }; + let response = client + .validator_unbonding_delegations(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 79u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(validator_unbonding_delegations.unbonding_responses) + } + /// Query delegation info for given validator for a delegator + pub async fn delegation( + &self, + validator_addr: impl Into, + delegator_addr: impl Into, + ) -> Result { + let delegation: cosmos_modules::staking::QueryDelegationResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryDelegationRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDelegationRequest { + validator_addr: validator_addr.into(), + delegator_addr: delegator_addr.into(), + }; + let response = client + .delegation(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 97u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(delegation.delegation_response.unwrap()) + } + /// Query unbonding delegation info for given validator delegator + pub async fn unbonding_delegation( + &self, + validator_addr: impl Into, + delegator_addr: impl Into, + ) -> Result { + let unbonding_delegation: cosmos_modules::staking::QueryUnbondingDelegationResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryUnbondingDelegationRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryUnbondingDelegationRequest { + validator_addr: validator_addr.into(), + delegator_addr: delegator_addr.into(), + }; + let response = client + .unbonding_delegation(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 115u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(unbonding_delegation.unbond.unwrap()) + } + /// Query all delegator delegations of a given delegator address + /// + /// see [PageRequest] for pagination + pub async fn delegator_delegations( + &self, + delegator_addr: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::staking::QueryDelegatorDelegationsResponse, + DaemonError, + > { + let delegator_delegations: cosmos_modules::staking::QueryDelegatorDelegationsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryDelegatorDelegationsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDelegatorDelegationsRequest { + delegator_addr: delegator_addr.into(), + pagination: pagination, + }; + let response = client + .delegator_delegations(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 135u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(delegator_delegations) + } + /// Queries all unbonding delegations of a given delegator address. + /// + /// see [PageRequest] for pagination + pub async fn delegator_unbonding_delegations( + &self, + delegator_addr: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::staking::QueryDelegatorUnbondingDelegationsResponse, + DaemonError, + > { + let delegator_unbonding_delegations: cosmos_modules::staking::QueryDelegatorUnbondingDelegationsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, + QueryDelegatorUnbondingDelegationsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDelegatorUnbondingDelegationsRequest { + delegator_addr: delegator_addr.into(), + pagination: pagination, + }; + let response = client + .delegator_unbonding_delegations(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 156u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(delegator_unbonding_delegations) + } + /// Query redelegations of a given address + /// + /// see [PageRequest] for pagination + pub async fn redelegations( + &self, + delegator_addr: impl Into, + src_validator_addr: impl Into, + dst_validator_addr: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::staking::QueryRedelegationsResponse, + DaemonError, + > { + let redelegations: cosmos_modules::staking::QueryRedelegationsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryRedelegationsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryRedelegationsRequest { + delegator_addr: delegator_addr.into(), + src_validator_addr: src_validator_addr.into(), + dst_validator_addr: dst_validator_addr.into(), + pagination: pagination, + }; + let response = client + .redelegations(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 178u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(redelegations) + } + /// Query delegator validators info for given delegator address. + pub async fn delegator_validator( + &self, + validator_addr: impl Into, + delegator_addr: impl Into, + ) -> Result< + cosmos_modules::staking::QueryDelegatorValidatorResponse, + DaemonError, + > { + let delegator_validator: cosmos_modules::staking::QueryDelegatorValidatorResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryDelegatorValidatorRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDelegatorValidatorRequest { + validator_addr: validator_addr.into(), + delegator_addr: delegator_addr.into(), + }; + let response = client + .delegator_validator(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 198u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(delegator_validator) + } + /// Query delegator validators info for given delegator address + /// + /// see [PageRequest] for pagination + pub async fn delegator_validators( + &self, + delegator_addr: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::staking::QueryDelegatorValidatorsResponse, + DaemonError, + > { + let delegator_validators: cosmos_modules::staking::QueryDelegatorValidatorsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryDelegatorValidatorsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDelegatorValidatorsRequest { + delegator_addr: delegator_addr.into(), + pagination: pagination, + }; + let response = client + .delegator_validators(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 218u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(delegator_validators) + } + /// Query historical info info for given height + pub async fn historical_info( + &self, + height: i64, + ) -> Result< + cosmos_modules::staking::QueryHistoricalInfoResponse, + DaemonError, + > { + let historical_info: cosmos_modules::staking::QueryHistoricalInfoResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryHistoricalInfoRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryHistoricalInfoRequest { + height: height, + }; + let response = client + .historical_info(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 236u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(historical_info) + } + /// Query the pool info + pub async fn pool( + &self, + ) -> Result { + let pool: cosmos_modules::staking::QueryPoolResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryPoolRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPoolRequest {}; + let response = client.pool(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 248u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(pool) + } + /// Query staking parameters + pub async fn params( + &self, + ) -> Result { + let params: cosmos_modules::staking::QueryParamsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryParamsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryParamsRequest {}; + let response = client.params(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 257u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(params) + } + } + /// Staking bond statuses + pub enum StakingBondStatus { + /// UNSPECIFIED defines an invalid validator status. + Unspecified = 0, + /// UNBONDED defines a validator that is not bonded. + Unbonded = 1, + /// UNBONDING defines a validator that is unbonding. + Unbonding = 2, + /// BONDED defines a validator that is bonded. + Bonded = 3, + } + impl ToString for StakingBondStatus { + /// Convert to string + fn to_string(&self) -> String { + match self { + StakingBondStatus::Unspecified => { + "BOND_STATUS_UNSPECIFIED".to_string() + } + StakingBondStatus::Unbonded => "BOND_STATUS_UNBONDED".to_string(), + StakingBondStatus::Unbonding => "BOND_STATUS_UNBONDING".to_string(), + StakingBondStatus::Bonded => "BOND_STATUS_BONDED".to_string(), + } + } + } + } + pub use bank::Bank; + pub use cosmwasm::CosmWasm; + pub use feegrant::Feegrant; + pub use ibc::Ibc; + pub use node::Node; + pub use gov::*; + pub use staking::*; + use tonic::transport::Channel; + /// Constructor for a querier over a given channel + pub trait DaemonQuerier { + /// Construct an new querier over a given channel + fn new(channel: Channel) -> Self; + } +} +mod traits { + use cw_orch_core::{ + contract::interface_traits::{CwOrchMigrate, CwOrchUpload}, + environment::TxResponse, + }; + use crate::{queriers::CosmWasm, Daemon, DaemonError}; + /// Helper methods for conditional uploading of a contract. + pub trait ConditionalUpload: CwOrchUpload { + /// Only upload the contract if it is not uploaded yet (checksum does not match) + fn upload_if_needed(&self) -> Result>, DaemonError> { + if self.latest_is_uploaded()? { + Ok(None) + } else { + Some(self.upload()).transpose().map_err(Into::into) + } + } + /// Returns whether the checksum of the WASM file matches the checksum of the latest uploaded code for this contract. + fn latest_is_uploaded(&self) -> Result { + let Some(latest_uploaded_code_id) = self.code_id().ok() else { + return Ok(false); + }; + let chain = self.get_chain(); + let on_chain_hash = chain + .rt_handle + .block_on( + chain + .query_client::() + .code_id_hash(latest_uploaded_code_id), + )?; + let local_hash = self.wasm().checksum()?; + Ok(local_hash == on_chain_hash) + } + /// Returns whether the contract is running the latest uploaded code for it + fn is_running_latest(&self) -> Result { + let Some(latest_uploaded_code_id) = self.code_id().ok() else { + return Ok(false); + }; + let chain = self.get_chain(); + let info = chain + .rt_handle + .block_on( + chain.query_client::().contract_info(self.address()?), + )?; + Ok(latest_uploaded_code_id == info.code_id) + } + } + impl ConditionalUpload for T + where + T: CwOrchUpload, + {} + /// Helper methods for conditional migration of a contract. + pub trait ConditionalMigrate: CwOrchMigrate + ConditionalUpload { + /// Only migrate the contract if it is not on the latest code-id yet + fn migrate_if_needed( + &self, + migrate_msg: &Self::MigrateMsg, + ) -> Result>, DaemonError> { + if self.is_running_latest()? { + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "{0} is already running the latest code", + self.id(), + ), + lvl, + &( + "cw_orch_daemon::traits", + "cw_orch_daemon::traits", + "cw-orch-daemon/src/traits.rs", + ), + 61u32, + ::log::__private_api::Option::None, + ); + } + }; + Ok(None) + } else { + Some(self.migrate(migrate_msg, self.code_id()?)) + .transpose() + .map_err(Into::into) + } + } + /// Uploads the contract if the local contract hash is different from the latest on-chain code hash. + /// Proceeds to migrates the contract if the contract is not running the latest code. + fn upload_and_migrate_if_needed( + &self, + migrate_msg: &Self::MigrateMsg, + ) -> Result>>, DaemonError> { + let mut txs = Vec::with_capacity(2); + if let Some(tx) = self.upload_if_needed()? { + txs.push(tx); + } + if let Some(tx) = self.migrate_if_needed(migrate_msg)? { + txs.push(tx); + } + if txs.is_empty() { Ok(None) } else { Ok(Some(txs)) } + } + } + impl ConditionalMigrate for T + where + T: CwOrchMigrate + CwOrchUpload, + {} +} +pub mod tx_builder { + use cosmrs::tx::{ModeInfo, SignMode}; + use cosmrs::{ + proto::cosmos::auth::v1beta1::BaseAccount, tendermint::chain::Id, + tx::{self, Body, Fee, Msg, Raw, SequenceNumber, SignDoc, SignerInfo}, + Any, Coin, + }; + use secp256k1::All; + use super::{sender::Sender, DaemonError}; + const GAS_BUFFER: f64 = 1.3; + const BUFFER_THRESHOLD: u64 = 200_000; + const SMALL_GAS_BUFFER: f64 = 1.4; + /// Struct used to build a raw transaction and broadcast it with a sender. + pub struct TxBuilder { + pub(crate) body: Body, + pub(crate) fee_amount: Option, + pub(crate) gas_limit: Option, + pub(crate) sequence: Option, + } + #[automatically_derived] + impl ::core::clone::Clone for TxBuilder { + #[inline] + fn clone(&self) -> TxBuilder { + TxBuilder { + body: ::core::clone::Clone::clone(&self.body), + fee_amount: ::core::clone::Clone::clone(&self.fee_amount), + gas_limit: ::core::clone::Clone::clone(&self.gas_limit), + sequence: ::core::clone::Clone::clone(&self.sequence), + } + } + } + #[automatically_derived] + impl ::core::fmt::Debug for TxBuilder { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field4_finish( + f, + "TxBuilder", + "body", + &self.body, + "fee_amount", + &self.fee_amount, + "gas_limit", + &self.gas_limit, + "sequence", + &&self.sequence, + ) + } + } + impl TxBuilder { + /// Create a new TxBuilder with a given body. + pub fn new(body: Body) -> Self { + Self { + body, + fee_amount: None, + gas_limit: None, + sequence: None, + } + } + /// Set a fixed fee amount for the tx + pub fn fee_amount(&mut self, fee_amount: u128) -> &mut Self { + self.fee_amount = Some(fee_amount); + self + } + /// Set a gas limit for the tx + pub fn gas_limit(&mut self, gas_limit: u64) -> &mut Self { + self.gas_limit = Some(gas_limit); + self + } + /// Set a sequence number for the tx + pub fn sequence(&mut self, sequence: u64) -> &mut Self { + self.sequence = Some(sequence); + self + } + /// Builds the body of the tx with a given memo and timeout. + pub fn build_body( + msgs: Vec, + memo: Option<&str>, + timeout: u64, + ) -> tx::Body { + let msgs = msgs + .into_iter() + .map(Msg::into_any) + .collect::, _>>() + .unwrap(); + tx::Body::new(msgs, memo.unwrap_or_default(), timeout as u32) + } + pub(crate) fn build_fee( + amount: impl Into, + denom: &str, + gas_limit: u64, + ) -> Fee { + let fee = Coin::new(amount.into(), denom).unwrap(); + Fee::from_amount_and_gas(fee, gas_limit) + } + /// Builds the raw tx with a given body and fee and signs it. + /// Sets the TxBuilder's gas limit to its simulated amount for later use. + pub async fn build(&mut self, wallet: &Sender) -> Result { + let BaseAccount { account_number, sequence, .. } = wallet + .base_account() + .await?; + let sequence = self.sequence.unwrap_or(sequence); + let (tx_fee, gas_limit) = if let (Some(fee), Some(gas_limit)) + = (self.fee_amount, self.gas_limit) { + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Using pre-defined fee and gas limits: {0}, {1}", + fee, + gas_limit, + ), + lvl, + &( + "cw_orch_daemon::tx_builder", + "cw_orch_daemon::tx_builder", + "cw-orch-daemon/src/tx_builder.rs", + ), + 91u32, + ::log::__private_api::Option::None, + ); + } + }; + (fee, gas_limit) + } else { + let sim_gas_used = wallet + .calculate_gas(&self.body, sequence, account_number) + .await?; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Simulated gas needed {0:?}", sim_gas_used), + lvl, + &( + "cw_orch_daemon::tx_builder", + "cw_orch_daemon::tx_builder", + "cw-orch-daemon/src/tx_builder.rs", + ), + 101u32, + ::log::__private_api::Option::None, + ); + } + }; + let gas_expected = if sim_gas_used < BUFFER_THRESHOLD { + sim_gas_used as f64 * SMALL_GAS_BUFFER + } else { + sim_gas_used as f64 * GAS_BUFFER + }; + let fee_amount = gas_expected + * (wallet + .daemon_state + .chain_data + .fees + .fee_tokens[0] + .fixed_min_gas_price + 0.00001); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Calculated fee needed: {0:?}", fee_amount), + lvl, + &( + "cw_orch_daemon::tx_builder", + "cw_orch_daemon::tx_builder", + "cw-orch-daemon/src/tx_builder.rs", + ), + 111u32, + ::log::__private_api::Option::None, + ); + } + }; + self.gas_limit = Some(gas_expected as u64); + (fee_amount as u128, gas_expected as u64) + }; + let fee = Self::build_fee( + tx_fee, + &wallet.daemon_state.chain_data.fees.fee_tokens[0].denom, + gas_limit, + ); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "submitting tx: \n fee: {0:?}\naccount_nr: {1:?}\nsequence: {2:?}", + fee, + account_number, + sequence, + ), + lvl, + &( + "cw_orch_daemon::tx_builder", + "cw_orch_daemon::tx_builder", + "cw-orch-daemon/src/tx_builder.rs", + ), + 125u32, + ::log::__private_api::Option::None, + ); + } + }; + let auth_info = SignerInfo { + public_key: wallet.private_key.get_signer_public_key(&wallet.secp), + mode_info: ModeInfo::single(SignMode::Direct), + sequence, + } + .auth_info(fee); + let sign_doc = SignDoc::new( + &self.body, + &auth_info, + &Id::try_from(wallet.daemon_state.chain_data.chain_id.to_string())?, + account_number, + )?; + wallet.sign(sign_doc).map_err(Into::into) + } + } +} +pub use self::{ + builder::*, channel::*, core::*, error::*, state::*, sync::*, traits::*, tx_resp::*, +}; +pub use cw_orch_networks::chain_info::*; +pub use cw_orch_networks::networks; +pub use sender::Wallet; +pub use tx_builder::TxBuilder; +pub(crate) mod cosmos_modules { + pub use cosmrs::proto::{ + cosmos::{ + auth::v1beta1 as auth, authz::v1beta1 as authz, bank::v1beta1 as bank, + base::{ + abci::v1beta1 as abci, tendermint::v1beta1 as tendermint, v1beta1 as base, + }, + crisis::v1beta1 as crisis, distribution::v1beta1 as distribution, + evidence::v1beta1 as evidence, feegrant::v1beta1 as feegrant, + gov::v1beta1 as gov, mint::v1beta1 as mint, params::v1beta1 as params, + slashing::v1beta1 as slashing, staking::v1beta1 as staking, + tx::v1beta1 as tx, vesting::v1beta1 as vesting, + }, + cosmwasm::wasm::v1 as cosmwasm, + ibc::{ + applications::transfer::v1 as ibc_transfer, + core::{ + channel::v1 as ibc_channel, client::v1 as ibc_client, + connection::v1 as ibc_connection, + }, + }, + tendermint::abci as tendermint_abci, + }; +} +/// Re-export trait and data required to fetch daemon data from chain-registry +pub use ibc_chain_registry::{ + chain::ChainData as ChainRegistryData, fetchable::Fetchable, +}; +#![feature(prelude_import)] +//! `Daemon` and `DaemonAsync` execution environments. +//! +//! The `Daemon` type is a synchronous wrapper around the `DaemonAsync` type and can be used as a contract execution environment. +#[prelude_import] +use std::prelude::rust_2021::*; +#[macro_use] +extern crate std; +mod rpc_queriers { + pub mod bank { + use crate::{cosmos_modules, error::DaemonError}; + use cosmrs::{ + proto::cosmos::base::{query::v1beta1::PageRequest, v1beta1::Coin}, + rpc::{HttpClient, Client}, + tx::MessageExt, + }; + use prost::Message; + use super::RpcQuerier; + /// Queries for Cosmos Bank Module + pub struct Bank { + client: HttpClient, + } + impl RpcQuerier for Bank { + fn new(rpc: String) -> Self { + Self { + client: HttpClient::new("https://rpc.osmosis.zone").unwrap(), + } + } + } + impl Bank { + /// Query the bank balance of a given address + /// If denom is None, returns all balances + pub async fn balance( + &self, + address: impl Into, + denom: Option, + ) -> Result, DaemonError> { + match denom { + Some(denom) => { + let resp = (); + let coin = resp.balance.unwrap(); + Ok( + <[_]>::into_vec( + #[rustc_box] + ::alloc::boxed::Box::new([coin]), + ), + ) + } + None => { + use cosmos_modules::bank::QueryAllBalancesResponse; + use cosmos_modules::bank::QueryAllBalancesRequest; + let request = QueryAllBalancesRequest { + address: address.into(), + pagination: None, + }; + let response = self + .client + .abci_query( + Some("/cosmos.bank.v1beta1.Query/AllBalances".to_string()), + request.to_bytes()?, + None, + true, + ) + .await?; + let balance_response = QueryAllBalancesResponse::decode( + response.value.as_slice(), + ) + .unwrap(); + let any = request.to_bytes()?; + let resp = (); + let coins = resp.balances; + Ok(coins.into_iter().collect()) + } + } + } + /// Query spendable balance for address + pub async fn spendable_balances( + &self, + address: impl Into, + ) -> Result, DaemonError> { + let spendable_balances: cosmos_modules::bank::QuerySpendableBalancesResponse = (); + Ok(spendable_balances.balances) + } + /// Query total supply in the bank + pub async fn total_supply(&self) -> Result, DaemonError> { + let total_supply: cosmos_modules::bank::QueryTotalSupplyResponse = (); + Ok(total_supply.supply) + } + /// Query total supply in the bank for a denom + pub async fn supply_of( + &self, + denom: impl Into, + ) -> Result { + let supply_of: cosmos_modules::bank::QuerySupplyOfResponse = (); + Ok(supply_of.amount.unwrap()) + } + /// Query params + pub async fn params( + &self, + ) -> Result { + let params: cosmos_modules::bank::QueryParamsResponse = (); + Ok(params.params.unwrap()) + } + /// Query denom metadata + pub async fn denom_metadata( + &self, + denom: impl Into, + ) -> Result { + let denom_metadata: cosmos_modules::bank::QueryDenomMetadataResponse = (); + Ok(denom_metadata.metadata.unwrap()) + } + /// Query denoms metadata with pagination + /// + /// see [PageRequest] for pagination + pub async fn denoms_metadata( + &self, + pagination: Option, + ) -> Result, DaemonError> { + let denoms_metadata: cosmos_modules::bank::QueryDenomsMetadataResponse = (); + Ok(denoms_metadata.metadatas) + } + } + } + /// Constructor for a querier over a given channel + pub trait RpcQuerier { + /// Construct an new querier over a given channel + fn new(rpc: String) -> Self; + } +} +pub mod builder { + use crate::{DaemonAsync, DaemonBuilder}; + use std::rc::Rc; + use ibc_chain_registry::chain::ChainData; + use super::{error::DaemonError, sender::Sender, state::DaemonState}; + /// The default deployment id if none is provided + pub const DEFAULT_DEPLOYMENT: &str = "default"; + /// Create [`DaemonAsync`] through [`DaemonAsyncBuilder`] + /// ## Example + /// ```no_run + /// # tokio_test::block_on(async { + /// use cw_orch_daemon::{DaemonAsyncBuilder, networks}; + /// let daemon = DaemonAsyncBuilder::default() + /// .chain(networks::LOCAL_JUNO) + /// .deployment_id("v0.1.0") + /// .build() + /// .await.unwrap(); + /// # }) + /// ``` + pub struct DaemonAsyncBuilder { + pub(crate) chain: Option, + pub(crate) deployment_id: Option, + /// Wallet mnemonic + pub(crate) mnemonic: Option, + } + #[automatically_derived] + impl ::core::clone::Clone for DaemonAsyncBuilder { + #[inline] + fn clone(&self) -> DaemonAsyncBuilder { + DaemonAsyncBuilder { + chain: ::core::clone::Clone::clone(&self.chain), + deployment_id: ::core::clone::Clone::clone(&self.deployment_id), + mnemonic: ::core::clone::Clone::clone(&self.mnemonic), + } + } + } + #[automatically_derived] + impl ::core::default::Default for DaemonAsyncBuilder { + #[inline] + fn default() -> DaemonAsyncBuilder { + DaemonAsyncBuilder { + chain: ::core::default::Default::default(), + deployment_id: ::core::default::Default::default(), + mnemonic: ::core::default::Default::default(), + } + } + } + impl DaemonAsyncBuilder { + /// Set the chain the daemon will connect to + pub fn chain(&mut self, chain: impl Into) -> &mut Self { + self.chain = Some(chain.into()); + self + } + /// Set the deployment id to use for the daemon interactions + /// Defaults to `default` + pub fn deployment_id(&mut self, deployment_id: impl Into) -> &mut Self { + self.deployment_id = Some(deployment_id.into()); + self + } + /// Set the mnemonic to use with this chain. + /// Defaults to env variable depending on the environment. + /// + /// Variables: LOCAL_MNEMONIC, TEST_MNEMONIC and MAIN_MNEMONIC + pub fn mnemonic(&mut self, mnemonic: impl ToString) -> &mut Self { + self.mnemonic = Some(mnemonic.to_string()); + self + } + /// Build a daemon + pub async fn build(&self) -> Result { + let chain = self + .chain + .clone() + .ok_or(DaemonError::BuilderMissing("chain information".into()))?; + let deployment_id = self + .deployment_id + .clone() + .unwrap_or(DEFAULT_DEPLOYMENT.to_string()); + let state = Rc::new(DaemonState::new(chain, deployment_id).await?); + let sender = if let Some(mnemonic) = &self.mnemonic { + Sender::from_mnemonic(&state, mnemonic)? + } else { + Sender::new(&state)? + }; + let daemon = DaemonAsync { + state, + sender: Rc::new(sender), + }; + Ok(daemon) + } + } + impl From for DaemonAsyncBuilder { + fn from(value: DaemonBuilder) -> Self { + DaemonAsyncBuilder { + chain: value.chain, + deployment_id: value.deployment_id, + mnemonic: value.mnemonic, + } + } + } +} +pub mod channel { + use cosmrs::proto::cosmos::base::tendermint::v1beta1::{ + service_client::ServiceClient, GetNodeInfoRequest, + }; + use ibc_chain_registry::chain::Grpc; + use ibc_relayer_types::core::ics24_host::identifier::ChainId; + use tonic::transport::{Channel, ClientTlsConfig}; + use super::error::DaemonError; + /// A helper for constructing a gRPC channel + pub struct GrpcChannel {} + impl GrpcChannel { + /// Connect to any of the provided gRPC endpoints + pub async fn connect( + grpc: &[Grpc], + chain_id: &ChainId, + ) -> Result { + let mut successful_connections = ::alloc::vec::Vec::new(); + for Grpc { address, .. } in grpc.iter() { + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Trying to connect to endpoint: {0}", address), + lvl, + &( + "cw_orch_daemon::channel", + "cw_orch_daemon::channel", + "cw-orch-daemon/src/channel.rs", + ), + 19u32, + ::log::__private_api::Option::None, + ); + } + }; + let endpoint = Channel::builder(address.clone().try_into().unwrap()); + let maybe_client = ServiceClient::connect(endpoint.clone()).await; + let mut client = if maybe_client.is_ok() { + maybe_client? + } else { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Cannot connect to gRPC endpoint: {0}, {1:?}", + address, + maybe_client.unwrap_err(), + ), + lvl, + &( + "cw_orch_daemon::channel", + "cw_orch_daemon::channel", + "cw-orch-daemon/src/channel.rs", + ), + 31u32, + ::log::__private_api::Option::None, + ); + } + }; + if !(address.contains("https") || address.contains("443")) { + continue; + } + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Attempting to connect with TLS"), + lvl, + &( + "cw_orch_daemon::channel", + "cw_orch_daemon::channel", + "cw-orch-daemon/src/channel.rs", + ), + 43u32, + ::log::__private_api::Option::None, + ); + } + }; + let endpoint = endpoint.clone().tls_config(ClientTlsConfig::new())?; + let maybe_client = ServiceClient::connect(endpoint.clone()).await; + if maybe_client.is_err() { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "Cannot connect to gRPC endpoint: {0}, {1:?}", + address, + maybe_client.unwrap_err(), + ), + lvl, + &( + "cw_orch_daemon::channel", + "cw_orch_daemon::channel", + "cw-orch-daemon/src/channel.rs", + ), + 51u32, + ::log::__private_api::Option::None, + ); + } + }; + continue; + } + maybe_client? + }; + let node_info = client + .get_node_info(GetNodeInfoRequest {}) + .await? + .into_inner(); + if ChainId::is_epoch_format( + &node_info.default_node_info.as_ref().unwrap().network, + ) { + if node_info.default_node_info.as_ref().unwrap().network + != chain_id.as_str() + { + { + let lvl = ::log::Level::Error; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "Network mismatch: connection:{0} != config:{1}", + node_info.default_node_info.as_ref().unwrap().network, + chain_id.as_str(), + ), + lvl, + &( + "cw_orch_daemon::channel", + "cw_orch_daemon::channel", + "cw-orch-daemon/src/channel.rs", + ), + 72u32, + ::log::__private_api::Option::None, + ); + } + }; + continue; + } + } + successful_connections.push(endpoint.connect().await?) + } + if successful_connections.is_empty() { + return Err(DaemonError::CannotConnectGRPC); + } + Ok(successful_connections.pop().unwrap()) + } + } +} +pub mod core { + use crate::{queriers::CosmWasm, DaemonState}; + use super::{ + builder::DaemonAsyncBuilder, cosmos_modules, error::DaemonError, + queriers::{DaemonQuerier, Node}, + sender::Wallet, tx_resp::CosmTxResponse, + }; + use cosmrs::{ + cosmwasm::{MsgExecuteContract, MsgInstantiateContract, MsgMigrateContract}, + tendermint::Time, AccountId, Denom, + }; + use cosmwasm_std::{Addr, Coin}; + use cw_orch_core::{ + contract::interface_traits::Uploadable, environment::{ChainState, IndexResponse}, + }; + use serde::{de::DeserializeOwned, Serialize}; + use serde_json::from_str; + use std::{ + fmt::Debug, rc::Rc, str::{from_utf8, FromStr}, + time::Duration, + }; + use tonic::transport::Channel; + /** + Represents a blockchain node. + It's constructed using [`DaemonAsyncBuilder`]. + + ## Usage + ```rust,no_run + # tokio_test::block_on(async { + use cw_orch_daemon::{DaemonAsync, networks}; + + let daemon: DaemonAsync = DaemonAsync::builder() + .chain(networks::JUNO_1) + .build() + .await.unwrap(); + # }) + ``` + ## Environment Execution + + The DaemonAsync implements async methods of [`TxHandler`](cw_orch_core::environment::TxHandler) which allows you to perform transactions on the chain. + + ## Querying + + Different Cosmos SDK modules can be queried through the daemon by calling the [`DaemonAsync::query_client`] method with a specific querier. + See [Querier](crate::queriers) for examples. +*/ + pub struct DaemonAsync { + /// Sender to send transactions to the chain + pub sender: Wallet, + /// State of the daemon + pub state: Rc, + } + #[automatically_derived] + impl ::core::clone::Clone for DaemonAsync { + #[inline] + fn clone(&self) -> DaemonAsync { + DaemonAsync { + sender: ::core::clone::Clone::clone(&self.sender), + state: ::core::clone::Clone::clone(&self.state), + } + } + } + impl DaemonAsync { + /// Get the daemon builder + pub fn builder() -> DaemonAsyncBuilder { + DaemonAsyncBuilder::default() + } + /// Perform a query with a given query client. + /// See [Querier](crate::queriers) for examples. + pub fn query_client(&self) -> Querier { + Querier::new(self.sender.channel()) + } + /// Get the channel configured for this DaemonAsync. + pub fn channel(&self) -> Channel { + self.state.grpc_channel.clone() + } + } + impl ChainState for DaemonAsync { + type Out = Rc; + fn state(&self) -> Self::Out { + self.state.clone() + } + } + impl DaemonAsync { + /// Get the sender address + pub fn sender(&self) -> Addr { + self.sender.address().unwrap() + } + /// Execute a message on a contract. + pub async fn execute( + &self, + exec_msg: &E, + coins: &[cosmwasm_std::Coin], + contract_address: &Addr, + ) -> Result { + let exec_msg: MsgExecuteContract = MsgExecuteContract { + sender: self.sender.pub_addr()?, + contract: AccountId::from_str(contract_address.as_str())?, + msg: serde_json::to_vec(&exec_msg)?, + funds: parse_cw_coins(coins)?, + }; + let result = self + .sender + .commit_tx( + <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([exec_msg])), + None, + ) + .await?; + Ok(result) + } + /// Instantiate a contract. + pub async fn instantiate( + &self, + code_id: u64, + init_msg: &I, + label: Option<&str>, + admin: Option<&Addr>, + coins: &[Coin], + ) -> Result { + let sender = &self.sender; + let init_msg = MsgInstantiateContract { + code_id, + label: Some(label.unwrap_or("instantiate_contract").to_string()), + admin: admin.map(|a| FromStr::from_str(a.as_str()).unwrap()), + sender: sender.pub_addr()?, + msg: serde_json::to_vec(&init_msg)?, + funds: parse_cw_coins(coins)?, + }; + let result = sender + .commit_tx( + <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([init_msg])), + None, + ) + .await?; + Ok(result) + } + /// Query a contract. + pub async fn query( + &self, + query_msg: &Q, + contract_address: &Addr, + ) -> Result { + let sender = &self.sender; + let mut client = cosmos_modules::cosmwasm::query_client::QueryClient::new( + sender.channel(), + ); + let resp = client + .smart_contract_state(cosmos_modules::cosmwasm::QuerySmartContractStateRequest { + address: contract_address.to_string(), + query_data: serde_json::to_vec(&query_msg)?, + }) + .await?; + Ok(from_str(from_utf8(&resp.into_inner().data).unwrap())?) + } + /// Migration a contract. + pub async fn migrate( + &self, + migrate_msg: &M, + new_code_id: u64, + contract_address: &Addr, + ) -> Result { + let exec_msg: MsgMigrateContract = MsgMigrateContract { + sender: self.sender.pub_addr()?, + contract: AccountId::from_str(contract_address.as_str())?, + msg: serde_json::to_vec(&migrate_msg)?, + code_id: new_code_id, + }; + let result = self + .sender + .commit_tx( + <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([exec_msg])), + None, + ) + .await?; + Ok(result) + } + /// Wait for a given amount of blocks. + pub async fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { + let mut last_height = self.query_client::().block_height().await?; + let end_height = last_height + amount; + let average_block_speed = self + .query_client::() + .average_block_speed(Some(0.9)) + .await?; + let wait_time = average_block_speed * amount; + tokio::time::sleep(Duration::from_secs(wait_time)).await; + while last_height < end_height { + tokio::time::sleep(Duration::from_secs(average_block_speed)).await; + last_height = self.query_client::().block_height().await?; + } + Ok(()) + } + /// Wait for a given amount of seconds. + pub async fn wait_seconds(&self, secs: u64) -> Result<(), DaemonError> { + tokio::time::sleep(Duration::from_secs(secs)).await; + Ok(()) + } + /// Wait for the next block. + pub async fn next_block(&self) -> Result<(), DaemonError> { + self.wait_blocks(1).await + } + /// Get the current block info. + pub async fn block_info(&self) -> Result { + let block = self.query_client::().latest_block().await?; + let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; + let time = cosmwasm_std::Timestamp::from_nanos( + since_epoch.as_nanos() as u64, + ); + Ok(cosmwasm_std::BlockInfo { + height: block.header.height.value(), + time, + chain_id: block.header.chain_id.to_string(), + }) + } + /// Upload a contract to the chain. + pub async fn upload( + &self, + uploadable: &impl Uploadable, + ) -> Result { + let sender = &self.sender; + let wasm_path = uploadable.wasm(); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Uploading file at {0:?}", wasm_path), + lvl, + &( + "cw_orch_daemon::core", + "cw_orch_daemon::core", + "cw-orch-daemon/src/core.rs", + ), + 233u32, + ::log::__private_api::Option::None, + ); + } + }; + let file_contents = std::fs::read(wasm_path.path())?; + let store_msg = cosmrs::cosmwasm::MsgStoreCode { + sender: sender.pub_addr()?, + wasm_byte_code: file_contents, + instantiate_permission: None, + }; + let result = sender + .commit_tx( + <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([store_msg])), + None, + ) + .await?; + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Uploaded: {0:?}", result.txhash), + lvl, + &( + "cw_orch_daemon::core", + "cw_orch_daemon::core", + "cw-orch-daemon/src/core.rs", + ), + 244u32, + ::log::__private_api::Option::None, + ); + } + }; + let code_id = result.uploaded_code_id().unwrap(); + let wasm = CosmWasm::new(self.channel()); + while wasm.code(code_id).await.is_err() { + self.next_block().await?; + } + Ok(result) + } + /// Set the sender to use with this DaemonAsync to be the given wallet + pub fn set_sender(&mut self, sender: &Wallet) { + self.sender = sender.clone(); + } + } + pub(crate) fn parse_cw_coins( + coins: &[cosmwasm_std::Coin], + ) -> Result, DaemonError> { + coins + .iter() + .map(|cosmwasm_std::Coin { amount, denom }| { + Ok(cosmrs::Coin { + amount: amount.u128(), + denom: Denom::from_str(denom)?, + }) + }) + .collect::, DaemonError>>() + } +} +pub mod error { + #![allow(missing_docs)] + use cw_orch_core::CwEnvError; + use thiserror::Error; + pub enum DaemonError { + #[error("Reqwest HTTP(s) Error")] + ReqwestError(#[from] ::reqwest::Error), + #[error("JSON Conversion Error")] + SerdeJson(#[from] ::serde_json::Error), + #[error(transparent)] + ParseIntError(#[from] std::num::ParseIntError), + #[error(transparent)] + IOErr(#[from] ::std::io::Error), + #[error(transparent)] + Secp256k1(#[from] ::secp256k1::Error), + #[error(transparent)] + VarError(#[from] ::std::env::VarError), + #[error(transparent)] + AnyError(#[from] ::anyhow::Error), + #[error(transparent)] + Status(#[from] ::tonic::Status), + #[error(transparent)] + TransportError(#[from] ::tonic::transport::Error), + #[error(transparent)] + TendermintError(#[from] ::cosmrs::tendermint::Error), + #[error(transparent)] + TendermintRPCError(#[from] cosmrs::tendermint_rpc::Error), + #[error(transparent)] + CwEnvError(#[from] ::cw_orch_core::CwEnvError), + #[error("Bech32 Decode Error")] + Bech32DecodeErr, + #[error( + "Bech32 Decode Error: Key Failed prefix {0} or length {1} Wanted:{2}/{3}" + )] + Bech32DecodeExpanded(String, usize, String, usize), + #[error("Mnemonic - Wrong length, it should be 24 words")] + WrongLength, + #[error("Mnemonic - Bad Phrase")] + Phrasing, + #[error("Mnemonic - Missing Phrase")] + MissingPhrase, + #[error("Bad Implementation. Missing Component")] + Implementation, + #[error("Unable to convert into public key `{key}`")] + Conversion { key: String, source: bitcoin::bech32::Error }, + #[error( + "Can not augment daemon deployment after usage in more than one contract." + )] + SharedDaemonState, + #[error(transparent)] + ErrReport(#[from] ::eyre::ErrReport), + #[error(transparent)] + GRpcDecodeError(#[from] ::prost::DecodeError), + #[error(transparent)] + ED25519(#[from] ::ed25519_dalek::ed25519::Error), + #[error(transparent)] + DecodeError(#[from] ::base64::DecodeError), + #[error(transparent)] + HexError(#[from] ::hex::FromHexError), + #[error(transparent)] + BitCoinBip32(#[from] ::bitcoin::bip32::Error), + #[error("83 length-missing SECP256K1 prefix")] + ConversionSECP256k1, + #[error("82 length-missing ED25519 prefix")] + ConversionED25519, + #[error("Expected Key length of 82 or 83 length was {0}")] + ConversionLength(usize), + #[error("Expected Key length of 40 length was {0}")] + ConversionLengthED25519Hex(usize), + #[error( + "Expected ED25519 key of length 32 with a BECH32 ED25519 prefix of 5 chars - Len {0} - Hex {1}" + )] + ConversionPrefixED25519(usize, String), + #[error("Can't call Transactions without some gas rules")] + NoGasOpts, + #[error("Can't parse `{parse}` into a coin")] + CoinParseErrV { parse: String }, + #[error("Can't parse `{0}` into a coin")] + CoinParseErr(String), + #[error("TX submit returned `{0}` - {1} '{2}'")] + TxResultError(usize, String, String), + #[error("No price found for Gas using denom {0}")] + GasPriceError(String), + #[error( + "Attempting to fetch validator set in parts, and failed Height mismatch {0} {1}" + )] + TendermintValidatorSet(u64, u64), + #[error("Transaction {0} not found after {1} attempts")] + TXNotFound(String, usize), + #[error("unknown API error")] + Unknown, + #[error("Generic Error {0}")] + StdErr(String), + #[error("calling contract with unimplemented action")] + NotImplemented, + #[error("new chain detected, fill out the scaffold at {0}")] + NewChain(String), + #[error("new network detected, fill out the scaffold at {0}")] + NewNetwork(String), + #[error("Can not connect to any grpc endpoint that was provided.")] + CannotConnectGRPC, + #[error("tx failed: {reason} with code {code}")] + TxFailed { code: usize, reason: String }, + #[error("The list of grpc endpoints is empty")] + GRPCListIsEmpty, + #[error("no wasm path provided for contract.")] + MissingWasmPath, + #[error("daemon builder missing {0}")] + BuilderMissing(String), + #[error("ibc error: {0}")] + IbcError(String), + #[error("insufficient fee, check gas price: {0}")] + InsufficientFee(String), + } + #[allow(unused_qualifications)] + impl std::error::Error for DaemonError { + fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> { + use thiserror::__private::AsDynError; + #[allow(deprecated)] + match self { + DaemonError::ReqwestError { 0: source, .. } => { + std::option::Option::Some(source.as_dyn_error()) + } + DaemonError::SerdeJson { 0: source, .. } => { + std::option::Option::Some(source.as_dyn_error()) + } + DaemonError::ParseIntError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::IOErr { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::Secp256k1 { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::VarError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::AnyError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::Status { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::TransportError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::TendermintError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::TendermintRPCError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::CwEnvError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::Bech32DecodeErr { .. } => std::option::Option::None, + DaemonError::Bech32DecodeExpanded { .. } => std::option::Option::None, + DaemonError::WrongLength { .. } => std::option::Option::None, + DaemonError::Phrasing { .. } => std::option::Option::None, + DaemonError::MissingPhrase { .. } => std::option::Option::None, + DaemonError::Implementation { .. } => std::option::Option::None, + DaemonError::Conversion { source: source, .. } => { + std::option::Option::Some(source.as_dyn_error()) + } + DaemonError::SharedDaemonState { .. } => std::option::Option::None, + DaemonError::ErrReport { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::GRpcDecodeError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::ED25519 { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::DecodeError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::HexError { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::BitCoinBip32 { 0: transparent } => { + std::error::Error::source(transparent.as_dyn_error()) + } + DaemonError::ConversionSECP256k1 { .. } => std::option::Option::None, + DaemonError::ConversionED25519 { .. } => std::option::Option::None, + DaemonError::ConversionLength { .. } => std::option::Option::None, + DaemonError::ConversionLengthED25519Hex { .. } => { + std::option::Option::None + } + DaemonError::ConversionPrefixED25519 { .. } => std::option::Option::None, + DaemonError::NoGasOpts { .. } => std::option::Option::None, + DaemonError::CoinParseErrV { .. } => std::option::Option::None, + DaemonError::CoinParseErr { .. } => std::option::Option::None, + DaemonError::TxResultError { .. } => std::option::Option::None, + DaemonError::GasPriceError { .. } => std::option::Option::None, + DaemonError::TendermintValidatorSet { .. } => std::option::Option::None, + DaemonError::TXNotFound { .. } => std::option::Option::None, + DaemonError::Unknown { .. } => std::option::Option::None, + DaemonError::StdErr { .. } => std::option::Option::None, + DaemonError::NotImplemented { .. } => std::option::Option::None, + DaemonError::NewChain { .. } => std::option::Option::None, + DaemonError::NewNetwork { .. } => std::option::Option::None, + DaemonError::CannotConnectGRPC { .. } => std::option::Option::None, + DaemonError::TxFailed { .. } => std::option::Option::None, + DaemonError::GRPCListIsEmpty { .. } => std::option::Option::None, + DaemonError::MissingWasmPath { .. } => std::option::Option::None, + DaemonError::BuilderMissing { .. } => std::option::Option::None, + DaemonError::IbcError { .. } => std::option::Option::None, + DaemonError::InsufficientFee { .. } => std::option::Option::None, + } + } + } + #[allow(unused_qualifications)] + impl std::fmt::Display for DaemonError { + fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + use thiserror::__private::AsDisplay as _; + #[allow(unused_variables, deprecated, clippy::used_underscore_binding)] + match self { + DaemonError::ReqwestError(_0) => { + __formatter.write_fmt(format_args!("Reqwest HTTP(s) Error")) + } + DaemonError::SerdeJson(_0) => { + __formatter.write_fmt(format_args!("JSON Conversion Error")) + } + DaemonError::ParseIntError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::IOErr(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::Secp256k1(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::VarError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::AnyError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::Status(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::TransportError(_0) => { + std::fmt::Display::fmt(_0, __formatter) + } + DaemonError::TendermintError(_0) => { + std::fmt::Display::fmt(_0, __formatter) + } + DaemonError::TendermintRPCError(_0) => { + std::fmt::Display::fmt(_0, __formatter) + } + DaemonError::CwEnvError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::Bech32DecodeErr {} => { + __formatter.write_fmt(format_args!("Bech32 Decode Error")) + } + DaemonError::Bech32DecodeExpanded(_0, _1, _2, _3) => { + __formatter + .write_fmt( + format_args!( + "Bech32 Decode Error: Key Failed prefix {0} or length {1} Wanted:{2}/{3}", + _0.as_display(), + _1.as_display(), + _2.as_display(), + _3.as_display(), + ), + ) + } + DaemonError::WrongLength {} => { + __formatter + .write_fmt( + format_args!( + "Mnemonic - Wrong length, it should be 24 words", + ), + ) + } + DaemonError::Phrasing {} => { + __formatter.write_fmt(format_args!("Mnemonic - Bad Phrase")) + } + DaemonError::MissingPhrase {} => { + __formatter.write_fmt(format_args!("Mnemonic - Missing Phrase")) + } + DaemonError::Implementation {} => { + __formatter + .write_fmt(format_args!("Bad Implementation. Missing Component")) + } + DaemonError::Conversion { key, source } => { + __formatter + .write_fmt( + format_args!( + "Unable to convert into public key `{0}`", + key.as_display(), + ), + ) + } + DaemonError::SharedDaemonState {} => { + __formatter + .write_fmt( + format_args!( + "Can not augment daemon deployment after usage in more than one contract.", + ), + ) + } + DaemonError::ErrReport(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::GRpcDecodeError(_0) => { + std::fmt::Display::fmt(_0, __formatter) + } + DaemonError::ED25519(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::DecodeError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::HexError(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::BitCoinBip32(_0) => std::fmt::Display::fmt(_0, __formatter), + DaemonError::ConversionSECP256k1 {} => { + __formatter + .write_fmt(format_args!("83 length-missing SECP256K1 prefix")) + } + DaemonError::ConversionED25519 {} => { + __formatter + .write_fmt(format_args!("82 length-missing ED25519 prefix")) + } + DaemonError::ConversionLength(_0) => { + __formatter + .write_fmt( + format_args!( + "Expected Key length of 82 or 83 length was {0}", + _0.as_display(), + ), + ) + } + DaemonError::ConversionLengthED25519Hex(_0) => { + __formatter + .write_fmt( + format_args!( + "Expected Key length of 40 length was {0}", + _0.as_display(), + ), + ) + } + DaemonError::ConversionPrefixED25519(_0, _1) => { + __formatter + .write_fmt( + format_args!( + "Expected ED25519 key of length 32 with a BECH32 ED25519 prefix of 5 chars - Len {0} - Hex {1}", + _0.as_display(), + _1.as_display(), + ), + ) + } + DaemonError::NoGasOpts {} => { + __formatter + .write_fmt( + format_args!( + "Can\'t call Transactions without some gas rules", + ), + ) + } + DaemonError::CoinParseErrV { parse } => { + __formatter + .write_fmt( + format_args!( + "Can\'t parse `{0}` into a coin", + parse.as_display(), + ), + ) + } + DaemonError::CoinParseErr(_0) => { + __formatter + .write_fmt( + format_args!( + "Can\'t parse `{0}` into a coin", + _0.as_display(), + ), + ) + } + DaemonError::TxResultError(_0, _1, _2) => { + __formatter + .write_fmt( + format_args!( + "TX submit returned `{0}` - {1} \'{2}\'", + _0.as_display(), + _1.as_display(), + _2.as_display(), + ), + ) + } + DaemonError::GasPriceError(_0) => { + __formatter + .write_fmt( + format_args!( + "No price found for Gas using denom {0}", + _0.as_display(), + ), + ) + } + DaemonError::TendermintValidatorSet(_0, _1) => { + __formatter + .write_fmt( + format_args!( + "Attempting to fetch validator set in parts, and failed Height mismatch {0} {1}", + _0.as_display(), + _1.as_display(), + ), + ) + } + DaemonError::TXNotFound(_0, _1) => { + __formatter + .write_fmt( + format_args!( + "Transaction {0} not found after {1} attempts", + _0.as_display(), + _1.as_display(), + ), + ) + } + DaemonError::Unknown {} => { + __formatter.write_fmt(format_args!("unknown API error")) + } + DaemonError::StdErr(_0) => { + __formatter + .write_fmt(format_args!("Generic Error {0}", _0.as_display())) + } + DaemonError::NotImplemented {} => { + __formatter + .write_fmt( + format_args!("calling contract with unimplemented action"), + ) + } + DaemonError::NewChain(_0) => { + __formatter + .write_fmt( + format_args!( + "new chain detected, fill out the scaffold at {0}", + _0.as_display(), + ), + ) + } + DaemonError::NewNetwork(_0) => { + __formatter + .write_fmt( + format_args!( + "new network detected, fill out the scaffold at {0}", + _0.as_display(), + ), + ) + } + DaemonError::CannotConnectGRPC {} => { + __formatter + .write_fmt( + format_args!( + "Can not connect to any grpc endpoint that was provided.", + ), + ) + } + DaemonError::TxFailed { code, reason } => { + __formatter + .write_fmt( + format_args!( + "tx failed: {0} with code {1}", + reason.as_display(), + code.as_display(), + ), + ) + } + DaemonError::GRPCListIsEmpty {} => { + __formatter + .write_fmt(format_args!("The list of grpc endpoints is empty")) + } + DaemonError::MissingWasmPath {} => { + __formatter + .write_fmt(format_args!("no wasm path provided for contract.")) + } + DaemonError::BuilderMissing(_0) => { + __formatter + .write_fmt( + format_args!("daemon builder missing {0}", _0.as_display()), + ) + } + DaemonError::IbcError(_0) => { + __formatter + .write_fmt(format_args!("ibc error: {0}", _0.as_display())) + } + DaemonError::InsufficientFee(_0) => { + __formatter + .write_fmt( + format_args!( + "insufficient fee, check gas price: {0}", + _0.as_display(), + ), + ) + } + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::reqwest::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::reqwest::Error) -> Self { + DaemonError::ReqwestError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::serde_json::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::serde_json::Error) -> Self { + DaemonError::SerdeJson { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From for DaemonError { + #[allow(deprecated)] + fn from(source: std::num::ParseIntError) -> Self { + DaemonError::ParseIntError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::std::io::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::std::io::Error) -> Self { + DaemonError::IOErr { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::secp256k1::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::secp256k1::Error) -> Self { + DaemonError::Secp256k1 { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::std::env::VarError> for DaemonError { + #[allow(deprecated)] + fn from(source: ::std::env::VarError) -> Self { + DaemonError::VarError { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::anyhow::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::anyhow::Error) -> Self { + DaemonError::AnyError { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::tonic::Status> for DaemonError { + #[allow(deprecated)] + fn from(source: ::tonic::Status) -> Self { + DaemonError::Status { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::tonic::transport::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::tonic::transport::Error) -> Self { + DaemonError::TransportError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::cosmrs::tendermint::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::cosmrs::tendermint::Error) -> Self { + DaemonError::TendermintError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From for DaemonError { + #[allow(deprecated)] + fn from(source: cosmrs::tendermint_rpc::Error) -> Self { + DaemonError::TendermintRPCError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::cw_orch_core::CwEnvError> for DaemonError { + #[allow(deprecated)] + fn from(source: ::cw_orch_core::CwEnvError) -> Self { + DaemonError::CwEnvError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::eyre::ErrReport> for DaemonError { + #[allow(deprecated)] + fn from(source: ::eyre::ErrReport) -> Self { + DaemonError::ErrReport { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::prost::DecodeError> for DaemonError { + #[allow(deprecated)] + fn from(source: ::prost::DecodeError) -> Self { + DaemonError::GRpcDecodeError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::ed25519_dalek::ed25519::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::ed25519_dalek::ed25519::Error) -> Self { + DaemonError::ED25519 { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::base64::DecodeError> for DaemonError { + #[allow(deprecated)] + fn from(source: ::base64::DecodeError) -> Self { + DaemonError::DecodeError { + 0: source, + } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::hex::FromHexError> for DaemonError { + #[allow(deprecated)] + fn from(source: ::hex::FromHexError) -> Self { + DaemonError::HexError { 0: source } + } + } + #[allow(unused_qualifications)] + impl std::convert::From<::bitcoin::bip32::Error> for DaemonError { + #[allow(deprecated)] + fn from(source: ::bitcoin::bip32::Error) -> Self { + DaemonError::BitCoinBip32 { + 0: source, + } + } + } + #[automatically_derived] + impl ::core::fmt::Debug for DaemonError { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + DaemonError::ReqwestError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ReqwestError", + &__self_0, + ) + } + DaemonError::SerdeJson(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "SerdeJson", + &__self_0, + ) + } + DaemonError::ParseIntError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ParseIntError", + &__self_0, + ) + } + DaemonError::IOErr(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "IOErr", + &__self_0, + ) + } + DaemonError::Secp256k1(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "Secp256k1", + &__self_0, + ) + } + DaemonError::VarError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "VarError", + &__self_0, + ) + } + DaemonError::AnyError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "AnyError", + &__self_0, + ) + } + DaemonError::Status(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "Status", + &__self_0, + ) + } + DaemonError::TransportError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "TransportError", + &__self_0, + ) + } + DaemonError::TendermintError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "TendermintError", + &__self_0, + ) + } + DaemonError::TendermintRPCError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "TendermintRPCError", + &__self_0, + ) + } + DaemonError::CwEnvError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "CwEnvError", + &__self_0, + ) + } + DaemonError::Bech32DecodeErr => { + ::core::fmt::Formatter::write_str(f, "Bech32DecodeErr") + } + DaemonError::Bech32DecodeExpanded( + __self_0, + __self_1, + __self_2, + __self_3, + ) => { + ::core::fmt::Formatter::debug_tuple_field4_finish( + f, + "Bech32DecodeExpanded", + __self_0, + __self_1, + __self_2, + &__self_3, + ) + } + DaemonError::WrongLength => { + ::core::fmt::Formatter::write_str(f, "WrongLength") + } + DaemonError::Phrasing => ::core::fmt::Formatter::write_str(f, "Phrasing"), + DaemonError::MissingPhrase => { + ::core::fmt::Formatter::write_str(f, "MissingPhrase") + } + DaemonError::Implementation => { + ::core::fmt::Formatter::write_str(f, "Implementation") + } + DaemonError::Conversion { key: __self_0, source: __self_1 } => { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "Conversion", + "key", + __self_0, + "source", + &__self_1, + ) + } + DaemonError::SharedDaemonState => { + ::core::fmt::Formatter::write_str(f, "SharedDaemonState") + } + DaemonError::ErrReport(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ErrReport", + &__self_0, + ) + } + DaemonError::GRpcDecodeError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "GRpcDecodeError", + &__self_0, + ) + } + DaemonError::ED25519(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ED25519", + &__self_0, + ) + } + DaemonError::DecodeError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "DecodeError", + &__self_0, + ) + } + DaemonError::HexError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "HexError", + &__self_0, + ) + } + DaemonError::BitCoinBip32(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "BitCoinBip32", + &__self_0, + ) + } + DaemonError::ConversionSECP256k1 => { + ::core::fmt::Formatter::write_str(f, "ConversionSECP256k1") + } + DaemonError::ConversionED25519 => { + ::core::fmt::Formatter::write_str(f, "ConversionED25519") + } + DaemonError::ConversionLength(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ConversionLength", + &__self_0, + ) + } + DaemonError::ConversionLengthED25519Hex(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "ConversionLengthED25519Hex", + &__self_0, + ) + } + DaemonError::ConversionPrefixED25519(__self_0, __self_1) => { + ::core::fmt::Formatter::debug_tuple_field2_finish( + f, + "ConversionPrefixED25519", + __self_0, + &__self_1, + ) + } + DaemonError::NoGasOpts => { + ::core::fmt::Formatter::write_str(f, "NoGasOpts") + } + DaemonError::CoinParseErrV { parse: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "CoinParseErrV", + "parse", + &__self_0, + ) + } + DaemonError::CoinParseErr(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "CoinParseErr", + &__self_0, + ) + } + DaemonError::TxResultError(__self_0, __self_1, __self_2) => { + ::core::fmt::Formatter::debug_tuple_field3_finish( + f, + "TxResultError", + __self_0, + __self_1, + &__self_2, + ) + } + DaemonError::GasPriceError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "GasPriceError", + &__self_0, + ) + } + DaemonError::TendermintValidatorSet(__self_0, __self_1) => { + ::core::fmt::Formatter::debug_tuple_field2_finish( + f, + "TendermintValidatorSet", + __self_0, + &__self_1, + ) + } + DaemonError::TXNotFound(__self_0, __self_1) => { + ::core::fmt::Formatter::debug_tuple_field2_finish( + f, + "TXNotFound", + __self_0, + &__self_1, + ) + } + DaemonError::Unknown => ::core::fmt::Formatter::write_str(f, "Unknown"), + DaemonError::StdErr(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "StdErr", + &__self_0, + ) + } + DaemonError::NotImplemented => { + ::core::fmt::Formatter::write_str(f, "NotImplemented") + } + DaemonError::NewChain(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "NewChain", + &__self_0, + ) + } + DaemonError::NewNetwork(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "NewNetwork", + &__self_0, + ) + } + DaemonError::CannotConnectGRPC => { + ::core::fmt::Formatter::write_str(f, "CannotConnectGRPC") + } + DaemonError::TxFailed { code: __self_0, reason: __self_1 } => { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "TxFailed", + "code", + __self_0, + "reason", + &__self_1, + ) + } + DaemonError::GRPCListIsEmpty => { + ::core::fmt::Formatter::write_str(f, "GRPCListIsEmpty") + } + DaemonError::MissingWasmPath => { + ::core::fmt::Formatter::write_str(f, "MissingWasmPath") + } + DaemonError::BuilderMissing(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "BuilderMissing", + &__self_0, + ) + } + DaemonError::IbcError(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "IbcError", + &__self_0, + ) + } + DaemonError::InsufficientFee(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "InsufficientFee", + &__self_0, + ) + } + } + } + } + impl DaemonError { + pub fn ibc_err(msg: impl ToString) -> Self { + Self::IbcError(msg.to_string()) + } + } + impl From for CwEnvError { + fn from(val: DaemonError) -> Self { + CwEnvError::AnyError(val.into()) + } + } +} +pub(crate) mod json_file { + use serde_json::{from_reader, json, Value}; + use std::fs::{File, OpenOptions}; + pub fn write( + filename: &String, + chain_id: &String, + network_id: &String, + deploy_id: &String, + ) { + let file = OpenOptions::new() + .create(true) + .read(true) + .write(true) + .truncate(false) + .open(filename) + .unwrap(); + let mut json: Value = if file.metadata().unwrap().len().eq(&0) { + ::serde_json::Value::Object(::serde_json::Map::new()) + } else { + from_reader(file).unwrap() + }; + if json.get(network_id).is_none() { + json[network_id] = ::serde_json::Value::Object(::serde_json::Map::new()); + } + if json[network_id].get(chain_id).is_none() { + json[network_id][chain_id] = ::serde_json::Value::Object({ + let mut object = ::serde_json::Map::new(); + let _ = object + .insert( + (deploy_id).into(), + ::serde_json::Value::Object(::serde_json::Map::new()), + ); + let _ = object + .insert( + ("code_ids").into(), + ::serde_json::Value::Object(::serde_json::Map::new()), + ); + object + }); + } + serde_json::to_writer_pretty(File::create(filename).unwrap(), &json).unwrap(); + } + pub fn read(filename: &String) -> Value { + let file = File::open(filename) + .unwrap_or_else(|_| { + ::core::panicking::panic_fmt( + format_args!("File should be present at {0}", filename), + ); + }); + let json: serde_json::Value = from_reader(file).unwrap(); + json + } +} +/// Proto types for different blockchains +pub mod proto { + pub mod injective { + #![allow(missing_docs)] + use crate::DaemonError; + use cosmrs::tx::SignDoc; + use cosmrs::{proto::traits::TypeUrl, tx::Raw}; + pub const ETHEREUM_COIN_TYPE: u32 = 60; + pub struct InjectiveEthAccount { + #[prost(message, optional, tag = "1")] + pub base_account: ::core::option::Option< + super::super::cosmos_modules::auth::BaseAccount, + >, + #[prost(bytes, tag = "2")] + pub code_hash: Vec, + } + #[automatically_derived] + impl ::core::clone::Clone for InjectiveEthAccount { + #[inline] + fn clone(&self) -> InjectiveEthAccount { + InjectiveEthAccount { + base_account: ::core::clone::Clone::clone(&self.base_account), + code_hash: ::core::clone::Clone::clone(&self.code_hash), + } + } + } + #[automatically_derived] + impl ::core::marker::StructuralPartialEq for InjectiveEthAccount {} + #[automatically_derived] + impl ::core::cmp::PartialEq for InjectiveEthAccount { + #[inline] + fn eq(&self, other: &InjectiveEthAccount) -> bool { + self.base_account == other.base_account + && self.code_hash == other.code_hash + } + } + impl ::prost::Message for InjectiveEthAccount { + #[allow(unused_variables)] + fn encode_raw(&self, buf: &mut B) + where + B: ::prost::bytes::BufMut, + { + if let Some(ref msg) = self.base_account { + ::prost::encoding::message::encode(1u32, msg, buf); + } + if self.code_hash != b"" as &[u8] { + ::prost::encoding::bytes::encode(2u32, &self.code_hash, buf); + } + } + #[allow(unused_variables)] + fn merge_field( + &mut self, + tag: u32, + wire_type: ::prost::encoding::WireType, + buf: &mut B, + ctx: ::prost::encoding::DecodeContext, + ) -> ::core::result::Result<(), ::prost::DecodeError> + where + B: ::prost::bytes::Buf, + { + const STRUCT_NAME: &'static str = "InjectiveEthAccount"; + match tag { + 1u32 => { + let mut value = &mut self.base_account; + ::prost::encoding::message::merge( + wire_type, + value.get_or_insert_with(::core::default::Default::default), + buf, + ctx, + ) + .map_err(|mut error| { + error.push(STRUCT_NAME, "base_account"); + error + }) + } + 2u32 => { + let mut value = &mut self.code_hash; + ::prost::encoding::bytes::merge(wire_type, value, buf, ctx) + .map_err(|mut error| { + error.push(STRUCT_NAME, "code_hash"); + error + }) + } + _ => ::prost::encoding::skip_field(wire_type, tag, buf, ctx), + } + } + #[inline] + fn encoded_len(&self) -> usize { + 0 + + self + .base_account + .as_ref() + .map_or( + 0, + |msg| ::prost::encoding::message::encoded_len(1u32, msg), + ) + + if self.code_hash != b"" as &[u8] { + ::prost::encoding::bytes::encoded_len(2u32, &self.code_hash) + } else { + 0 + } + } + fn clear(&mut self) { + self.base_account = ::core::option::Option::None; + self.code_hash.clear(); + } + } + impl ::core::default::Default for InjectiveEthAccount { + fn default() -> Self { + InjectiveEthAccount { + base_account: ::core::default::Default::default(), + code_hash: ::core::default::Default::default(), + } + } + } + impl ::core::fmt::Debug for InjectiveEthAccount { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + let mut builder = f.debug_struct("InjectiveEthAccount"); + let builder = { + let wrapper = &self.base_account; + builder.field("base_account", &wrapper) + }; + let builder = { + let wrapper = { + fn ScalarWrapper(v: T) -> T { + v + } + ScalarWrapper(&self.code_hash) + }; + builder.field("code_hash", &wrapper) + }; + builder.finish() + } + } + pub struct InjectivePubKey { + #[prost(bytes, tag = 1)] + pub key: Vec, + } + #[automatically_derived] + impl ::core::clone::Clone for InjectivePubKey { + #[inline] + fn clone(&self) -> InjectivePubKey { + InjectivePubKey { + key: ::core::clone::Clone::clone(&self.key), + } + } + } + #[automatically_derived] + impl ::core::marker::StructuralPartialEq for InjectivePubKey {} + #[automatically_derived] + impl ::core::cmp::PartialEq for InjectivePubKey { + #[inline] + fn eq(&self, other: &InjectivePubKey) -> bool { + self.key == other.key + } + } + impl ::prost::Message for InjectivePubKey { + #[allow(unused_variables)] + fn encode_raw(&self, buf: &mut B) + where + B: ::prost::bytes::BufMut, + { + if self.key != b"" as &[u8] { + ::prost::encoding::bytes::encode(1u32, &self.key, buf); + } + } + #[allow(unused_variables)] + fn merge_field( + &mut self, + tag: u32, + wire_type: ::prost::encoding::WireType, + buf: &mut B, + ctx: ::prost::encoding::DecodeContext, + ) -> ::core::result::Result<(), ::prost::DecodeError> + where + B: ::prost::bytes::Buf, + { + const STRUCT_NAME: &'static str = "InjectivePubKey"; + match tag { + 1u32 => { + let mut value = &mut self.key; + ::prost::encoding::bytes::merge(wire_type, value, buf, ctx) + .map_err(|mut error| { + error.push(STRUCT_NAME, "key"); + error + }) + } + _ => ::prost::encoding::skip_field(wire_type, tag, buf, ctx), + } + } + #[inline] + fn encoded_len(&self) -> usize { + 0 + + if self.key != b"" as &[u8] { + ::prost::encoding::bytes::encoded_len(1u32, &self.key) + } else { + 0 + } + } + fn clear(&mut self) { + self.key.clear(); + } + } + impl ::core::default::Default for InjectivePubKey { + fn default() -> Self { + InjectivePubKey { + key: ::core::default::Default::default(), + } + } + } + impl ::core::fmt::Debug for InjectivePubKey { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + let mut builder = f.debug_struct("InjectivePubKey"); + let builder = { + let wrapper = { + fn ScalarWrapper(v: T) -> T { + v + } + ScalarWrapper(&self.key) + }; + builder.field("key", &wrapper) + }; + builder.finish() + } + } + impl TypeUrl for InjectivePubKey { + const TYPE_URL: &'static str = "/injective.crypto.v1beta1.ethsecp256k1.PubKey"; + } + pub trait InjectiveSigner { + fn sign_injective(&self, sign_doc: SignDoc) -> Result; + } + } +} +pub mod sender { + use crate::{networks::ChainKind, proto::injective::ETHEREUM_COIN_TYPE}; + use super::{ + cosmos_modules::{self, auth::BaseAccount}, + error::DaemonError, queriers::{DaemonQuerier, Node}, + state::DaemonState, tx_builder::TxBuilder, tx_resp::CosmTxResponse, + }; + use crate::proto::injective::InjectiveEthAccount; + use crate::{core::parse_cw_coins, keys::private::PrivateKey}; + use cosmrs::{ + bank::MsgSend, crypto::secp256k1::SigningKey, proto::traits::Message, + tendermint::chain::Id, + tx::{self, ModeInfo, Msg, Raw, SignDoc, SignMode, SignerInfo}, + AccountId, + }; + use cosmwasm_std::Addr; + use secp256k1::{All, Context, Secp256k1, Signing}; + use std::{convert::TryFrom, env, rc::Rc, str::FromStr}; + use cosmos_modules::vesting::PeriodicVestingAccount; + use tonic::transport::Channel; + /// A wallet is a sender of transactions, can be safely cloned and shared within the same thread. + pub type Wallet = Rc>; + /// Signer of the transactions and helper for address derivation + /// This is the main interface for simulating and signing transactions + pub struct Sender { + pub private_key: PrivateKey, + pub secp: Secp256k1, + pub(crate) daemon_state: Rc, + } + impl Sender { + pub fn new(daemon_state: &Rc) -> Result, DaemonError> { + let kind = ChainKind::from(daemon_state.chain_data.network_type.clone()); + let mnemonic = env::var(kind.mnemonic_name()) + .unwrap_or_else(|_| { + { + ::core::panicking::panic_fmt( + format_args!( + "Wallet mnemonic environment variable {0} not set.", + kind.mnemonic_name(), + ), + ); + } + }); + Self::from_mnemonic(daemon_state, &mnemonic) + } + /// Construct a new Sender from a mnemonic + pub fn from_mnemonic( + daemon_state: &Rc, + mnemonic: &str, + ) -> Result, DaemonError> { + let secp = Secp256k1::new(); + let p_key: PrivateKey = PrivateKey::from_words( + &secp, + mnemonic, + 0, + 0, + daemon_state.chain_data.slip44, + )?; + let sender = Sender { + daemon_state: daemon_state.clone(), + private_key: p_key, + secp, + }; + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Interacting with {0} using address: {1}", + daemon_state.chain_data.chain_id, + sender.pub_addr_str()?, + ), + lvl, + &( + "cw_orch_daemon::sender", + "cw_orch_daemon::sender", + "cw-orch-daemon/src/sender.rs", + ), + 71u32, + ::log::__private_api::Option::None, + ); + } + }; + Ok(sender) + } + fn cosmos_private_key(&self) -> SigningKey { + SigningKey::from_slice(&self.private_key.raw_key()).unwrap() + } + pub fn channel(&self) -> Channel { + self.daemon_state.grpc_channel.clone() + } + pub fn pub_addr(&self) -> Result { + Ok( + AccountId::new( + &self.daemon_state.chain_data.bech32_prefix, + &self.private_key.public_key(&self.secp).raw_address.unwrap(), + )?, + ) + } + pub fn address(&self) -> Result { + Ok(Addr::unchecked(self.pub_addr_str()?)) + } + pub fn pub_addr_str(&self) -> Result { + Ok(self.pub_addr()?.to_string()) + } + pub async fn bank_send( + &self, + recipient: &str, + coins: Vec, + ) -> Result { + let msg_send = MsgSend { + from_address: self.pub_addr()?, + to_address: AccountId::from_str(recipient)?, + amount: parse_cw_coins(&coins)?, + }; + self.commit_tx( + <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([msg_send])), + Some("sending tokens"), + ) + .await + } + pub async fn calculate_gas( + &self, + tx_body: &tx::Body, + sequence: u64, + account_number: u64, + ) -> Result { + let fee = TxBuilder::build_fee( + 0u8, + &self.daemon_state.chain_data.fees.fee_tokens[0].denom, + 0, + ); + let auth_info = SignerInfo { + public_key: self.private_key.get_signer_public_key(&self.secp), + mode_info: ModeInfo::single(SignMode::Direct), + sequence, + } + .auth_info(fee); + let sign_doc = SignDoc::new( + tx_body, + &auth_info, + &Id::try_from(self.daemon_state.chain_data.chain_id.to_string())?, + account_number, + )?; + let tx_raw = self.sign(sign_doc)?; + Node::new(self.channel()).simulate_tx(tx_raw.to_bytes()?).await + } + pub async fn commit_tx( + &self, + msgs: Vec, + memo: Option<&str>, + ) -> Result { + let timeout_height = Node::new(self.channel()).block_height().await? + 10u64; + let tx_body = TxBuilder::build_body(msgs, memo, timeout_height); + let mut tx_builder = TxBuilder::new(tx_body); + let tx = tx_builder.build(self).await?; + let mut tx_response = self.broadcast_tx(tx).await?; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("tx broadcast response: {0:?}", tx_response), + lvl, + &( + "cw_orch_daemon::sender", + "cw_orch_daemon::sender", + "cw-orch-daemon/src/sender.rs", + ), + 166u32, + ::log::__private_api::Option::None, + ); + } + }; + if has_insufficient_fee(&tx_response.raw_log) { + let suggested_fee = parse_suggested_fee(&tx_response.raw_log); + let Some(new_fee) = suggested_fee else { + return Err(DaemonError::InsufficientFee(tx_response.raw_log)); + }; + tx_builder.fee_amount(new_fee); + let tx = tx_builder.build(self).await?; + tx_response = self.broadcast_tx(tx).await?; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("tx broadcast response: {0:?}", tx_response), + lvl, + &( + "cw_orch_daemon::sender", + "cw_orch_daemon::sender", + "cw-orch-daemon/src/sender.rs", + ), + 181u32, + ::log::__private_api::Option::None, + ); + } + }; + } + let resp = Node::new(self.channel()).find_tx(tx_response.txhash).await?; + if resp.code == 0 { + Ok(resp) + } else { + Err(DaemonError::TxFailed { + code: resp.code, + reason: resp.raw_log, + }) + } + } + pub fn sign(&self, sign_doc: SignDoc) -> Result { + let tx_raw = if self.private_key.coin_type == ETHEREUM_COIN_TYPE { + { + ::core::panicking::panic_fmt( + format_args!( + "Coin Type {0} not supported without eth feature", + ETHEREUM_COIN_TYPE, + ), + ); + }; + } else { + sign_doc.sign(&self.cosmos_private_key())? + }; + Ok(tx_raw) + } + pub async fn base_account(&self) -> Result { + let addr = self.pub_addr().unwrap().to_string(); + let mut client = cosmos_modules::auth::query_client::QueryClient::new( + self.channel(), + ); + let resp = client + .account(cosmos_modules::auth::QueryAccountRequest { + address: addr, + }) + .await? + .into_inner(); + let account = resp.account.unwrap().value; + let acc = if let Ok(acc) = BaseAccount::decode(account.as_ref()) { + acc + } else if let Ok(acc) = PeriodicVestingAccount::decode(account.as_ref()) { + acc.base_vesting_account.unwrap().base_account.unwrap() + } else if let Ok(acc) = InjectiveEthAccount::decode(account.as_ref()) { + acc.base_account.unwrap() + } else { + return Err( + DaemonError::StdErr( + "Unknown account type returned from QueryAccountRequest".into(), + ), + ); + }; + Ok(acc) + } + async fn broadcast_tx( + &self, + tx: Raw, + ) -> Result< + cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse, + DaemonError, + > { + let mut client = cosmos_modules::tx::service_client::ServiceClient::new( + self.channel(), + ); + let commit = client + .broadcast_tx(cosmos_modules::tx::BroadcastTxRequest { + tx_bytes: tx.to_bytes()?, + mode: cosmos_modules::tx::BroadcastMode::Sync.into(), + }) + .await?; + let commit = commit.into_inner().tx_response.unwrap(); + Ok(commit) + } + } + fn has_insufficient_fee(raw_log: &str) -> bool { + raw_log.contains("insufficient fees") + } + fn parse_suggested_fee(raw_log: &str) -> Option { + let parts: Vec<&str> = raw_log.split("required: ").collect(); + if parts.len() != 2 { + return None; + } + let got_parts: Vec<&str> = parts[0].split_whitespace().collect(); + let paid_fee_with_denom = got_parts.last()?; + let (_, denomination) = paid_fee_with_denom + .split_at(paid_fee_with_denom.find(|c: char| !c.is_numeric())?); + { + ::std::io::_eprint(format_args!("denom: {0}\n", denomination)); + }; + let required_fees: Vec<&str> = parts[1].split(denomination).collect(); + { + ::std::io::_eprint(format_args!("required fees: {0:?}\n", required_fees)); + }; + let (_, suggested_fee) = required_fees[0] + .split_at(required_fees[0].rfind(|c: char| !c.is_numeric())?); + { + ::std::io::_eprint(format_args!("suggested fee: {0}\n", suggested_fee)); + }; + suggested_fee.parse::().ok().or(suggested_fee[1..].parse::().ok()) + } +} +pub mod state { + use super::error::DaemonError; + use crate::{channel::GrpcChannel, networks::ChainKind}; + use cosmwasm_std::Addr; + use cw_orch_core::{ + environment::{DeployDetails, StateInterface}, + CwEnvError, + }; + use ibc_chain_registry::chain::ChainData; + use serde::Serialize; + use serde_json::{json, Value}; + use std::{collections::HashMap, env, fs::File, path::Path}; + use tonic::transport::Channel; + /// Stores the chain information and deployment state. + /// Uses a simple JSON file to store the deployment information locally. + pub struct DaemonState { + /// this is passed via env var STATE_FILE + pub json_file_path: String, + /// Deployment identifier + pub deployment_id: String, + /// gRPC channel + pub grpc_channel: Channel, + /// Information about the chain + pub chain_data: ChainData, + } + #[automatically_derived] + impl ::core::clone::Clone for DaemonState { + #[inline] + fn clone(&self) -> DaemonState { + DaemonState { + json_file_path: ::core::clone::Clone::clone(&self.json_file_path), + deployment_id: ::core::clone::Clone::clone(&self.deployment_id), + grpc_channel: ::core::clone::Clone::clone(&self.grpc_channel), + chain_data: ::core::clone::Clone::clone(&self.chain_data), + } + } + } + #[automatically_derived] + impl ::core::fmt::Debug for DaemonState { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field4_finish( + f, + "DaemonState", + "json_file_path", + &self.json_file_path, + "deployment_id", + &self.deployment_id, + "grpc_channel", + &self.grpc_channel, + "chain_data", + &&self.chain_data, + ) + } + } + impl DaemonState { + /// Creates a new state from the given chain data and deployment id. + /// Attempts to connect to any of the provided gRPC endpoints. + pub async fn new( + mut chain_data: ChainData, + deployment_id: String, + ) -> Result { + if chain_data.apis.grpc.is_empty() { + return Err(DaemonError::GRPCListIsEmpty); + } + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Found {0} gRPC endpoints", + chain_data.apis.grpc.len(), + ), + lvl, + &( + "cw_orch_daemon::state", + "cw_orch_daemon::state", + "cw-orch-daemon/src/state.rs", + ), + 40u32, + ::log::__private_api::Option::None, + ); + } + }; + let grpc_channel = GrpcChannel::connect( + &chain_data.apis.grpc, + &chain_data.chain_id, + ) + .await?; + let mut json_file_path = env::var("STATE_FILE") + .unwrap_or("./state.json".to_string()); + if chain_data.network_type == ChainKind::Local.to_string() { + let name = Path::new(&json_file_path) + .file_stem() + .unwrap() + .to_str() + .unwrap(); + let folder = Path::new(&json_file_path) + .parent() + .unwrap() + .to_str() + .unwrap(); + json_file_path = { + let res = ::alloc::fmt::format( + format_args!("{0}/{1}_local.json", folder, name), + ); + res + }; + } + let shortest_denom_token = chain_data + .fees + .fee_tokens + .iter() + .fold( + chain_data.fees.fee_tokens[0].clone(), + |acc, item| { + if item.denom.len() < acc.denom.len() { + item.clone() + } else { + acc + } + }, + ); + chain_data + .fees + .fee_tokens = <[_]>::into_vec( + #[rustc_box] + ::alloc::boxed::Box::new([shortest_denom_token]), + ); + let state = DaemonState { + json_file_path, + deployment_id, + grpc_channel, + chain_data, + }; + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Writing daemon state JSON file: {0:#?}", + state.json_file_path, + ), + lvl, + &( + "cw_orch_daemon::state", + "cw_orch_daemon::state", + "cw-orch-daemon/src/state.rs", + ), + 87u32, + ::log::__private_api::Option::None, + ); + } + }; + crate::json_file::write( + &state.json_file_path, + &state.chain_data.chain_id.to_string(), + &state.chain_data.chain_name, + &state.deployment_id, + ); + Ok(state) + } + /// Get the state filepath and read it as json + fn read_state(&self) -> serde_json::Value { + crate::json_file::read(&self.json_file_path) + } + /// Retrieve a stateful value using the chainId and networkId + pub fn get(&self, key: &str) -> Value { + let json = self.read_state(); + json[&self.chain_data.chain_name][&self.chain_data.chain_id.to_string()][key] + .clone() + } + /// Set a stateful value using the chainId and networkId + pub fn set(&self, key: &str, contract_id: &str, value: T) { + let mut json = self.read_state(); + json[&self + .chain_data + .chain_name][&self + .chain_data + .chain_id + .to_string()][key][contract_id] = ::serde_json::to_value(&value) + .unwrap(); + serde_json::to_writer_pretty( + File::create(&self.json_file_path).unwrap(), + &json, + ) + .unwrap(); + } + } + impl StateInterface for DaemonState { + /// Read address for contract in deployment id from state file + fn get_address(&self, contract_id: &str) -> Result { + let value = self + .get(&self.deployment_id) + .get(contract_id) + .ok_or_else(|| CwEnvError::AddrNotInStore(contract_id.to_owned()))? + .clone(); + Ok(Addr::unchecked(value.as_str().unwrap())) + } + /// Set address for contract in deployment id in state file + fn set_address(&mut self, contract_id: &str, address: &Addr) { + self.set(&self.deployment_id, contract_id, address.as_str()); + } + /// Get the locally-saved version of the contract's version on this network + fn get_code_id(&self, contract_id: &str) -> Result { + let value = self + .get("code_ids") + .get(contract_id) + .ok_or_else(|| CwEnvError::CodeIdNotInStore(contract_id.to_owned()))? + .clone(); + Ok(value.as_u64().unwrap()) + } + /// Set the locally-saved version of the contract's latest version on this network + fn set_code_id(&mut self, contract_id: &str, code_id: u64) { + self.set("code_ids", contract_id, code_id); + } + /// Get all addresses for deployment id from state file + fn get_all_addresses(&self) -> Result, CwEnvError> { + let mut store = HashMap::new(); + let addresses = self.get(&self.deployment_id); + let value = addresses.as_object().unwrap(); + for (id, addr) in value { + store.insert(id.clone(), Addr::unchecked(addr.as_str().unwrap())); + } + Ok(store) + } + fn get_all_code_ids(&self) -> Result, CwEnvError> { + let mut store = HashMap::new(); + let code_ids = self.get("code_ids"); + let value = code_ids.as_object().unwrap(); + for (id, code_id) in value { + store.insert(id.clone(), code_id.as_u64().unwrap()); + } + Ok(store) + } + fn deploy_details(&self) -> DeployDetails { + DeployDetails { + chain_id: self.chain_data.chain_id.to_string(), + chain_name: self.chain_data.chain_name.clone(), + deployment_id: self.deployment_id.clone(), + } + } + } +} +pub mod sync { + mod builder { + use ibc_chain_registry::chain::ChainData; + use crate::DaemonAsyncBuilder; + use super::{super::error::DaemonError, core::Daemon}; + /// Create [`Daemon`] through [`DaemonBuilder`] + /// ## Example + /// ```no_run + /// use cw_orch_daemon::{networks, DaemonBuilder}; + /// + /// let Daemon = DaemonBuilder::default() + /// .chain(networks::LOCAL_JUNO) + /// .deployment_id("v0.1.0") + /// .build() + /// .unwrap(); + /// ``` + pub struct DaemonBuilder { + pub(crate) chain: Option, + pub(crate) handle: Option, + pub(crate) deployment_id: Option, + /// Wallet mnemonic + pub(crate) mnemonic: Option, + } + #[automatically_derived] + impl ::core::clone::Clone for DaemonBuilder { + #[inline] + fn clone(&self) -> DaemonBuilder { + DaemonBuilder { + chain: ::core::clone::Clone::clone(&self.chain), + handle: ::core::clone::Clone::clone(&self.handle), + deployment_id: ::core::clone::Clone::clone(&self.deployment_id), + mnemonic: ::core::clone::Clone::clone(&self.mnemonic), + } + } + } + #[automatically_derived] + impl ::core::default::Default for DaemonBuilder { + #[inline] + fn default() -> DaemonBuilder { + DaemonBuilder { + chain: ::core::default::Default::default(), + handle: ::core::default::Default::default(), + deployment_id: ::core::default::Default::default(), + mnemonic: ::core::default::Default::default(), + } + } + } + impl DaemonBuilder { + /// Set the chain the Daemon will connect to + pub fn chain(&mut self, chain: impl Into) -> &mut Self { + self.chain = Some(chain.into()); + self + } + /// Set the deployment id to use for the Daemon interactions + /// Defaults to `default` + pub fn deployment_id( + &mut self, + deployment_id: impl Into, + ) -> &mut Self { + self.deployment_id = Some(deployment_id.into()); + self + } + /// Set the tokio runtime handle to use for the Daemon + /// + /// ## Example + /// ```no_run + /// use cw_orch_daemon::Daemon; + /// use tokio::runtime::Runtime; + /// let rt = Runtime::new().unwrap(); + /// let Daemon = Daemon::builder() + /// .handle(rt.handle()) + /// // ... + /// .build() + /// .unwrap(); + /// ``` + pub fn handle(&mut self, handle: &tokio::runtime::Handle) -> &mut Self { + self.handle = Some(handle.clone()); + self + } + /// Set the mnemonic to use with this chain. + pub fn mnemonic(&mut self, mnemonic: impl ToString) -> &mut Self { + self.mnemonic = Some(mnemonic.to_string()); + self + } + /// Build a Daemon + pub fn build(&self) -> Result { + let rt_handle = self + .handle + .clone() + .ok_or(DaemonError::BuilderMissing("runtime handle".into()))?; + let daemon = rt_handle + .block_on(DaemonAsyncBuilder::from(self.clone()).build())?; + Ok(Daemon { rt_handle, daemon }) + } + } + } + mod core { + use std::{fmt::Debug, rc::Rc, time::Duration}; + use super::super::{sender::Wallet, DaemonAsync}; + use crate::{ + queriers::{DaemonQuerier, Node}, + CosmTxResponse, DaemonBuilder, DaemonError, DaemonState, + }; + use cosmrs::tendermint::Time; + use cosmwasm_std::{Addr, Coin}; + use cw_orch_core::{ + contract::{interface_traits::Uploadable, WasmPath}, + environment::{ChainState, TxHandler}, + }; + use serde::{de::DeserializeOwned, Serialize}; + use tokio::runtime::Handle; + use tonic::transport::Channel; + /** + Represents a blockchain node. + Is constructed with the [DaemonBuilder]. + + ## Usage + + ```rust,no_run + use cw_orch_daemon::{Daemon, networks}; + use tokio::runtime::Runtime; + + let rt = Runtime::new().unwrap(); + let daemon: Daemon = Daemon::builder() + .chain(networks::JUNO_1) + .handle(rt.handle()) + .build() + .unwrap(); + ``` + ## Environment Execution + + The Daemon implements [`TxHandler`] which allows you to perform transactions on the chain. + + ## Querying + + Different Cosmos SDK modules can be queried through the daemon by calling the [`Daemon.query_client`] method with a specific querier. + See [Querier](crate::queriers) for examples. +*/ + pub struct Daemon { + pub daemon: DaemonAsync, + /// Runtime handle to execute async tasks + pub rt_handle: Handle, + } + #[automatically_derived] + impl ::core::clone::Clone for Daemon { + #[inline] + fn clone(&self) -> Daemon { + Daemon { + daemon: ::core::clone::Clone::clone(&self.daemon), + rt_handle: ::core::clone::Clone::clone(&self.rt_handle), + } + } + } + impl Daemon { + /// Get the daemon builder + pub fn builder() -> DaemonBuilder { + DaemonBuilder::default() + } + /// Perform a query with a given querier + /// See [Querier](crate::queriers) for examples. + pub fn query_client(&self) -> Querier { + self.daemon.query_client() + } + /// Get the channel configured for this Daemon + pub fn channel(&self) -> Channel { + self.daemon.state.grpc_channel.clone() + } + /// Get the channel configured for this Daemon + pub fn wallet(&self) -> Wallet { + self.daemon.sender.clone() + } + } + impl ChainState for Daemon { + type Out = Rc; + fn state(&self) -> Self::Out { + self.daemon.state.clone() + } + } + impl TxHandler for Daemon { + type Response = CosmTxResponse; + type Error = DaemonError; + type ContractSource = WasmPath; + type Sender = Wallet; + fn sender(&self) -> Addr { + self.daemon.sender.address().unwrap() + } + fn set_sender(&mut self, sender: Self::Sender) { + self.daemon.sender = sender; + } + fn upload( + &self, + uploadable: &impl Uploadable, + ) -> Result { + self.rt_handle.block_on(self.daemon.upload(uploadable)) + } + fn execute( + &self, + exec_msg: &E, + coins: &[cosmwasm_std::Coin], + contract_address: &Addr, + ) -> Result { + self.rt_handle + .block_on(self.daemon.execute(exec_msg, coins, contract_address)) + } + fn instantiate( + &self, + code_id: u64, + init_msg: &I, + label: Option<&str>, + admin: Option<&Addr>, + coins: &[Coin], + ) -> Result { + self.rt_handle + .block_on( + self.daemon.instantiate(code_id, init_msg, label, admin, coins), + ) + } + fn query( + &self, + query_msg: &Q, + contract_address: &Addr, + ) -> Result { + self.rt_handle.block_on(self.daemon.query(query_msg, contract_address)) + } + fn migrate( + &self, + migrate_msg: &M, + new_code_id: u64, + contract_address: &Addr, + ) -> Result { + self.rt_handle + .block_on( + self.daemon.migrate(migrate_msg, new_code_id, contract_address), + ) + } + fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { + let mut last_height = self + .rt_handle + .block_on(self.query_client::().block_height())?; + let end_height = last_height + amount; + while last_height < end_height { + self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(4))); + last_height = self + .rt_handle + .block_on(self.query_client::().block_height())?; + } + Ok(()) + } + fn wait_seconds(&self, secs: u64) -> Result<(), DaemonError> { + self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(secs))); + Ok(()) + } + fn next_block(&self) -> Result<(), DaemonError> { + let mut last_height = self + .rt_handle + .block_on(self.query_client::().block_height())?; + let end_height = last_height + 1; + while last_height < end_height { + self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(4))); + last_height = self + .rt_handle + .block_on(self.query_client::().block_height())?; + } + Ok(()) + } + fn block_info(&self) -> Result { + let block = self + .rt_handle + .block_on(self.query_client::().latest_block())?; + let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; + let time = cosmwasm_std::Timestamp::from_nanos( + since_epoch.as_nanos() as u64, + ); + Ok(cosmwasm_std::BlockInfo { + height: block.header.height.value(), + time, + chain_id: block.header.chain_id.to_string(), + }) + } + } + } + pub use self::{builder::*, core::*}; +} +pub mod tx_resp { + use super::{ + cosmos_modules::{ + abci::{AbciMessageLog, Attribute, StringEvent, TxResponse}, + tendermint_abci::Event, + }, + error::DaemonError, + }; + use chrono::{DateTime, NaiveDateTime, TimeZone, Utc}; + use cosmwasm_std::{to_binary, Binary, StdError, StdResult}; + use cw_orch_core::environment::IndexResponse; + use serde::{Deserialize, Serialize}; + const FORMAT: &str = "%Y-%m-%dT%H:%M:%S%.f"; + const FORMAT_TZ_SUPPLIED: &str = "%Y-%m-%dT%H:%M:%S.%f%:z"; + const FORMAT_SHORT_Z: &str = "%Y-%m-%dT%H:%M:%SZ"; + const FORMAT_SHORT_Z2: &str = "%Y-%m-%dT%H:%M:%S.%fZ"; + /// The response from a transaction performed on a blockchain. + pub struct CosmTxResponse { + /// Height of the block in which the transaction was included. + pub height: u64, + /// Transaction hash. + pub txhash: String, + /// Transaction index within the block. + pub codespace: String, + /// Transaction result code + pub code: usize, + /// Arbitrary data that can be included in a transaction. + pub data: String, + /// Raw log message. + pub raw_log: String, + /// Logs of the transaction. + pub logs: Vec, + /// Transaction info. + pub info: String, + /// Gas limit. + pub gas_wanted: u64, + /// Gas used. + pub gas_used: u64, + /// Timestamp of the block in which the transaction was included. + pub timestamp: DateTime, + /// Transaction events. + pub events: Vec, + } + #[automatically_derived] + impl ::core::fmt::Debug for CosmTxResponse { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + let names: &'static _ = &[ + "height", + "txhash", + "codespace", + "code", + "data", + "raw_log", + "logs", + "info", + "gas_wanted", + "gas_used", + "timestamp", + "events", + ]; + let values: &[&dyn ::core::fmt::Debug] = &[ + &self.height, + &self.txhash, + &self.codespace, + &self.code, + &self.data, + &self.raw_log, + &self.logs, + &self.info, + &self.gas_wanted, + &self.gas_used, + &self.timestamp, + &&self.events, + ]; + ::core::fmt::Formatter::debug_struct_fields_finish( + f, + "CosmTxResponse", + names, + values, + ) + } + } + #[automatically_derived] + impl ::core::default::Default for CosmTxResponse { + #[inline] + fn default() -> CosmTxResponse { + CosmTxResponse { + height: ::core::default::Default::default(), + txhash: ::core::default::Default::default(), + codespace: ::core::default::Default::default(), + code: ::core::default::Default::default(), + data: ::core::default::Default::default(), + raw_log: ::core::default::Default::default(), + logs: ::core::default::Default::default(), + info: ::core::default::Default::default(), + gas_wanted: ::core::default::Default::default(), + gas_used: ::core::default::Default::default(), + timestamp: ::core::default::Default::default(), + events: ::core::default::Default::default(), + } + } + } + impl CosmTxResponse { + /// find a attribute's value from TX logs. + /// returns: msg_index and value + pub fn get_attribute_from_logs( + &self, + event_type: &str, + attribute_key: &str, + ) -> Vec<(usize, String)> { + let mut response: Vec<(usize, String)> = Default::default(); + let logs = &self.logs; + for log_part in logs { + let msg_index = log_part.msg_index.unwrap_or_default(); + let events = &log_part.events; + let events_filtered = events + .iter() + .filter(|event| event.s_type == event_type) + .collect::>(); + if let Some(event) = events_filtered.first() { + let attributes_filtered = event + .attributes + .iter() + .filter(|attr| attr.key == attribute_key) + .map(|f| f.value.clone()) + .collect::>(); + if let Some(attr_key) = attributes_filtered.first() { + response.push((msg_index, attr_key.clone())); + } + } + } + response + } + /// get the list of event types from a TX record + pub fn get_events(&self, event_type: &str) -> Vec { + let mut response: Vec = Default::default(); + for log_part in &self.logs { + let events = &log_part.events; + let events_filtered = events + .iter() + .filter(|event| event.s_type == event_type) + .collect::>(); + for event in events_filtered { + response.push(event.clone()); + } + } + response + } + } + impl From<&serde_json::Value> for TxResultBlockMsg { + fn from(value: &serde_json::Value) -> Self { + serde_json::from_value(value.clone()).unwrap() + } + } + impl From for CosmTxResponse { + fn from(tx: TxResponse) -> Self { + Self { + height: tx.height as u64, + txhash: tx.txhash, + codespace: tx.codespace, + code: tx.code as usize, + data: tx.data, + raw_log: tx.raw_log, + logs: tx.logs.into_iter().map(TxResultBlockMsg::from).collect(), + info: tx.info, + gas_wanted: tx.gas_wanted as u64, + gas_used: tx.gas_used as u64, + timestamp: parse_timestamp(tx.timestamp).unwrap(), + events: tx.events, + } + } + } + impl IndexResponse for CosmTxResponse { + fn events(&self) -> Vec { + let mut parsed_events = ::alloc::vec::Vec::new(); + for event in &self.events { + let mut pattr = ::alloc::vec::Vec::new(); + for attr in &event.attributes { + pattr + .push(cosmwasm_std::Attribute { + key: attr.key.clone(), + value: attr.value.clone(), + }) + } + let pevent = cosmwasm_std::Event::new(event.r#type.clone()) + .add_attributes(pattr); + parsed_events.push(pevent); + } + parsed_events + } + fn data(&self) -> Option { + if self.data.is_empty() { + None + } else { + Some(to_binary(self.data.as_bytes()).unwrap()) + } + } + fn event_attr_value( + &self, + event_type: &str, + attr_key: &str, + ) -> StdResult { + for event in &self.events { + if event.r#type == event_type { + for attr in &event.attributes { + if attr.key == attr_key { + return Ok(attr.value.clone()); + } + } + } + } + Err( + StdError::generic_err({ + let res = ::alloc::fmt::format( + format_args!( + "event of type {0} does not have a value at key {1}", + event_type, + attr_key, + ), + ); + res + }), + ) + } + } + /// The events from a single message in a transaction. + pub struct TxResultBlockMsg { + /// index of the message in the transaction + pub msg_index: Option, + /// Events from this message + pub events: Vec, + } + #[automatically_derived] + impl ::core::clone::Clone for TxResultBlockMsg { + #[inline] + fn clone(&self) -> TxResultBlockMsg { + TxResultBlockMsg { + msg_index: ::core::clone::Clone::clone(&self.msg_index), + events: ::core::clone::Clone::clone(&self.events), + } + } + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl _serde::Serialize for TxResultBlockMsg { + fn serialize<__S>( + &self, + __serializer: __S, + ) -> _serde::__private::Result<__S::Ok, __S::Error> + where + __S: _serde::Serializer, + { + let mut __serde_state = _serde::Serializer::serialize_struct( + __serializer, + "TxResultBlockMsg", + false as usize + 1 + 1, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "msg_index", + &self.msg_index, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "events", + &self.events, + )?; + _serde::ser::SerializeStruct::end(__serde_state) + } + } + }; + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl<'de> _serde::Deserialize<'de> for TxResultBlockMsg { + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + #[allow(non_camel_case_types)] + #[doc(hidden)] + enum __Field { + __field0, + __field1, + __ignore, + } + #[doc(hidden)] + struct __FieldVisitor; + impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { + type Value = __Field; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "field identifier", + ) + } + fn visit_u64<__E>( + self, + __value: u64, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + 0u64 => _serde::__private::Ok(__Field::__field0), + 1u64 => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_str<__E>( + self, + __value: &str, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + "msg_index" => _serde::__private::Ok(__Field::__field0), + "events" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_bytes<__E>( + self, + __value: &[u8], + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + b"msg_index" => _serde::__private::Ok(__Field::__field0), + b"events" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + } + impl<'de> _serde::Deserialize<'de> for __Field { + #[inline] + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + _serde::Deserializer::deserialize_identifier( + __deserializer, + __FieldVisitor, + ) + } + } + #[doc(hidden)] + struct __Visitor<'de> { + marker: _serde::__private::PhantomData, + lifetime: _serde::__private::PhantomData<&'de ()>, + } + impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { + type Value = TxResultBlockMsg; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "struct TxResultBlockMsg", + ) + } + #[inline] + fn visit_seq<__A>( + self, + mut __seq: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::SeqAccess<'de>, + { + let __field0 = match _serde::de::SeqAccess::next_element::< + Option, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 0usize, + &"struct TxResultBlockMsg with 2 elements", + ), + ); + } + }; + let __field1 = match _serde::de::SeqAccess::next_element::< + Vec, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 1usize, + &"struct TxResultBlockMsg with 2 elements", + ), + ); + } + }; + _serde::__private::Ok(TxResultBlockMsg { + msg_index: __field0, + events: __field1, + }) + } + #[inline] + fn visit_map<__A>( + self, + mut __map: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::MapAccess<'de>, + { + let mut __field0: _serde::__private::Option> = _serde::__private::None; + let mut __field1: _serde::__private::Option< + Vec, + > = _serde::__private::None; + while let _serde::__private::Some(__key) + = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { + match __key { + __Field::__field0 => { + if _serde::__private::Option::is_some(&__field0) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field( + "msg_index", + ), + ); + } + __field0 = _serde::__private::Some( + _serde::de::MapAccess::next_value::< + Option, + >(&mut __map)?, + ); + } + __Field::__field1 => { + if _serde::__private::Option::is_some(&__field1) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field("events"), + ); + } + __field1 = _serde::__private::Some( + _serde::de::MapAccess::next_value::< + Vec, + >(&mut __map)?, + ); + } + _ => { + let _ = _serde::de::MapAccess::next_value::< + _serde::de::IgnoredAny, + >(&mut __map)?; + } + } + } + let __field0 = match __field0 { + _serde::__private::Some(__field0) => __field0, + _serde::__private::None => { + _serde::__private::de::missing_field("msg_index")? + } + }; + let __field1 = match __field1 { + _serde::__private::Some(__field1) => __field1, + _serde::__private::None => { + _serde::__private::de::missing_field("events")? + } + }; + _serde::__private::Ok(TxResultBlockMsg { + msg_index: __field0, + events: __field1, + }) + } + } + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &["msg_index", "events"]; + _serde::Deserializer::deserialize_struct( + __deserializer, + "TxResultBlockMsg", + FIELDS, + __Visitor { + marker: _serde::__private::PhantomData::, + lifetime: _serde::__private::PhantomData, + }, + ) + } + } + }; + #[automatically_derived] + impl ::core::fmt::Debug for TxResultBlockMsg { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "TxResultBlockMsg", + "msg_index", + &self.msg_index, + "events", + &&self.events, + ) + } + } + impl From for TxResultBlockMsg { + fn from(msg: AbciMessageLog) -> Self { + Self { + msg_index: Some(msg.msg_index as usize), + events: msg.events.into_iter().map(TxResultBlockEvent::from).collect(), + } + } + } + /// A single event from a transaction and its attributes. + pub struct TxResultBlockEvent { + #[serde(rename = "type")] + /// Type of the event + pub s_type: String, + /// Attributes of the event + pub attributes: Vec, + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl<'de> _serde::Deserialize<'de> for TxResultBlockEvent { + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + #[allow(non_camel_case_types)] + #[doc(hidden)] + enum __Field { + __field0, + __field1, + __ignore, + } + #[doc(hidden)] + struct __FieldVisitor; + impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { + type Value = __Field; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "field identifier", + ) + } + fn visit_u64<__E>( + self, + __value: u64, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + 0u64 => _serde::__private::Ok(__Field::__field0), + 1u64 => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_str<__E>( + self, + __value: &str, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + "type" => _serde::__private::Ok(__Field::__field0), + "attributes" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_bytes<__E>( + self, + __value: &[u8], + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + b"type" => _serde::__private::Ok(__Field::__field0), + b"attributes" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + } + impl<'de> _serde::Deserialize<'de> for __Field { + #[inline] + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + _serde::Deserializer::deserialize_identifier( + __deserializer, + __FieldVisitor, + ) + } + } + #[doc(hidden)] + struct __Visitor<'de> { + marker: _serde::__private::PhantomData, + lifetime: _serde::__private::PhantomData<&'de ()>, + } + impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { + type Value = TxResultBlockEvent; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "struct TxResultBlockEvent", + ) + } + #[inline] + fn visit_seq<__A>( + self, + mut __seq: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::SeqAccess<'de>, + { + let __field0 = match _serde::de::SeqAccess::next_element::< + String, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 0usize, + &"struct TxResultBlockEvent with 2 elements", + ), + ); + } + }; + let __field1 = match _serde::de::SeqAccess::next_element::< + Vec, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 1usize, + &"struct TxResultBlockEvent with 2 elements", + ), + ); + } + }; + _serde::__private::Ok(TxResultBlockEvent { + s_type: __field0, + attributes: __field1, + }) + } + #[inline] + fn visit_map<__A>( + self, + mut __map: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::MapAccess<'de>, + { + let mut __field0: _serde::__private::Option = _serde::__private::None; + let mut __field1: _serde::__private::Option< + Vec, + > = _serde::__private::None; + while let _serde::__private::Some(__key) + = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { + match __key { + __Field::__field0 => { + if _serde::__private::Option::is_some(&__field0) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field("type"), + ); + } + __field0 = _serde::__private::Some( + _serde::de::MapAccess::next_value::(&mut __map)?, + ); + } + __Field::__field1 => { + if _serde::__private::Option::is_some(&__field1) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field( + "attributes", + ), + ); + } + __field1 = _serde::__private::Some( + _serde::de::MapAccess::next_value::< + Vec, + >(&mut __map)?, + ); + } + _ => { + let _ = _serde::de::MapAccess::next_value::< + _serde::de::IgnoredAny, + >(&mut __map)?; + } + } + } + let __field0 = match __field0 { + _serde::__private::Some(__field0) => __field0, + _serde::__private::None => { + _serde::__private::de::missing_field("type")? + } + }; + let __field1 = match __field1 { + _serde::__private::Some(__field1) => __field1, + _serde::__private::None => { + _serde::__private::de::missing_field("attributes")? + } + }; + _serde::__private::Ok(TxResultBlockEvent { + s_type: __field0, + attributes: __field1, + }) + } + } + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &["type", "attributes"]; + _serde::Deserializer::deserialize_struct( + __deserializer, + "TxResultBlockEvent", + FIELDS, + __Visitor { + marker: _serde::__private::PhantomData::, + lifetime: _serde::__private::PhantomData, + }, + ) + } + } + }; + #[automatically_derived] + impl ::core::clone::Clone for TxResultBlockEvent { + #[inline] + fn clone(&self) -> TxResultBlockEvent { + TxResultBlockEvent { + s_type: ::core::clone::Clone::clone(&self.s_type), + attributes: ::core::clone::Clone::clone(&self.attributes), + } + } + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl _serde::Serialize for TxResultBlockEvent { + fn serialize<__S>( + &self, + __serializer: __S, + ) -> _serde::__private::Result<__S::Ok, __S::Error> + where + __S: _serde::Serializer, + { + let mut __serde_state = _serde::Serializer::serialize_struct( + __serializer, + "TxResultBlockEvent", + false as usize + 1 + 1, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "type", + &self.s_type, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "attributes", + &self.attributes, + )?; + _serde::ser::SerializeStruct::end(__serde_state) + } + } + }; + #[automatically_derived] + impl ::core::fmt::Debug for TxResultBlockEvent { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "TxResultBlockEvent", + "s_type", + &self.s_type, + "attributes", + &&self.attributes, + ) + } + } + impl From for TxResultBlockEvent { + fn from(event: StringEvent) -> Self { + Self { + s_type: event.r#type, + attributes: event + .attributes + .into_iter() + .map(TxResultBlockAttribute::from) + .collect(), + } + } + } + impl TxResultBlockEvent { + /// get all key/values from the event that have the key 'key' + pub fn get_attributes(&self, key: &str) -> Vec { + self.attributes.iter().filter(|attr| attr.key == key).cloned().collect() + } + /// return the first value of the first attribute that has the key 'key' + pub fn get_first_attribute_value(&self, key: &str) -> Option { + self.get_attributes(key).first().map(|attr| attr.value.clone()) + } + } + /// A single attribute of an event. + pub struct TxResultBlockAttribute { + /// Key of the attribute + pub key: String, + /// Value of the attribute + pub value: String, + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl<'de> _serde::Deserialize<'de> for TxResultBlockAttribute { + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + #[allow(non_camel_case_types)] + #[doc(hidden)] + enum __Field { + __field0, + __field1, + __ignore, + } + #[doc(hidden)] + struct __FieldVisitor; + impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { + type Value = __Field; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "field identifier", + ) + } + fn visit_u64<__E>( + self, + __value: u64, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + 0u64 => _serde::__private::Ok(__Field::__field0), + 1u64 => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_str<__E>( + self, + __value: &str, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + "key" => _serde::__private::Ok(__Field::__field0), + "value" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_bytes<__E>( + self, + __value: &[u8], + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + b"key" => _serde::__private::Ok(__Field::__field0), + b"value" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + } + impl<'de> _serde::Deserialize<'de> for __Field { + #[inline] + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + _serde::Deserializer::deserialize_identifier( + __deserializer, + __FieldVisitor, + ) + } + } + #[doc(hidden)] + struct __Visitor<'de> { + marker: _serde::__private::PhantomData, + lifetime: _serde::__private::PhantomData<&'de ()>, + } + impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { + type Value = TxResultBlockAttribute; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "struct TxResultBlockAttribute", + ) + } + #[inline] + fn visit_seq<__A>( + self, + mut __seq: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::SeqAccess<'de>, + { + let __field0 = match _serde::de::SeqAccess::next_element::< + String, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 0usize, + &"struct TxResultBlockAttribute with 2 elements", + ), + ); + } + }; + let __field1 = match _serde::de::SeqAccess::next_element::< + String, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 1usize, + &"struct TxResultBlockAttribute with 2 elements", + ), + ); + } + }; + _serde::__private::Ok(TxResultBlockAttribute { + key: __field0, + value: __field1, + }) + } + #[inline] + fn visit_map<__A>( + self, + mut __map: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::MapAccess<'de>, + { + let mut __field0: _serde::__private::Option = _serde::__private::None; + let mut __field1: _serde::__private::Option = _serde::__private::None; + while let _serde::__private::Some(__key) + = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { + match __key { + __Field::__field0 => { + if _serde::__private::Option::is_some(&__field0) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field("key"), + ); + } + __field0 = _serde::__private::Some( + _serde::de::MapAccess::next_value::(&mut __map)?, + ); + } + __Field::__field1 => { + if _serde::__private::Option::is_some(&__field1) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field("value"), + ); + } + __field1 = _serde::__private::Some( + _serde::de::MapAccess::next_value::(&mut __map)?, + ); + } + _ => { + let _ = _serde::de::MapAccess::next_value::< + _serde::de::IgnoredAny, + >(&mut __map)?; + } + } + } + let __field0 = match __field0 { + _serde::__private::Some(__field0) => __field0, + _serde::__private::None => { + _serde::__private::de::missing_field("key")? + } + }; + let __field1 = match __field1 { + _serde::__private::Some(__field1) => __field1, + _serde::__private::None => { + _serde::__private::de::missing_field("value")? + } + }; + _serde::__private::Ok(TxResultBlockAttribute { + key: __field0, + value: __field1, + }) + } + } + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &["key", "value"]; + _serde::Deserializer::deserialize_struct( + __deserializer, + "TxResultBlockAttribute", + FIELDS, + __Visitor { + marker: _serde::__private::PhantomData::, + lifetime: _serde::__private::PhantomData, + }, + ) + } + } + }; + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl _serde::Serialize for TxResultBlockAttribute { + fn serialize<__S>( + &self, + __serializer: __S, + ) -> _serde::__private::Result<__S::Ok, __S::Error> + where + __S: _serde::Serializer, + { + let mut __serde_state = _serde::Serializer::serialize_struct( + __serializer, + "TxResultBlockAttribute", + false as usize + 1 + 1, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "key", + &self.key, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "value", + &self.value, + )?; + _serde::ser::SerializeStruct::end(__serde_state) + } + } + }; + #[automatically_derived] + impl ::core::clone::Clone for TxResultBlockAttribute { + #[inline] + fn clone(&self) -> TxResultBlockAttribute { + TxResultBlockAttribute { + key: ::core::clone::Clone::clone(&self.key), + value: ::core::clone::Clone::clone(&self.value), + } + } + } + #[automatically_derived] + impl ::core::fmt::Debug for TxResultBlockAttribute { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "TxResultBlockAttribute", + "key", + &self.key, + "value", + &&self.value, + ) + } + } + impl From for TxResultBlockAttribute { + fn from(a: Attribute) -> Self { + Self { key: a.key, value: a.value } + } + } + /// Parse a string timestamp into a `DateTime` + pub fn parse_timestamp(s: String) -> Result, DaemonError> { + let len = s.len(); + let slice_len = if s.contains('.') { len.saturating_sub(4) } else { len }; + let sliced = &s[0..slice_len]; + match NaiveDateTime::parse_from_str(sliced, FORMAT) { + Err(_e) => { + match NaiveDateTime::parse_from_str(&s, FORMAT_TZ_SUPPLIED) { + Err(_e2) => { + match NaiveDateTime::parse_from_str(sliced, FORMAT_SHORT_Z) { + Err(_e3) => { + match NaiveDateTime::parse_from_str(&s, FORMAT_SHORT_Z2) { + Err(_e4) => { + { + ::std::io::_eprint( + format_args!("DateTime Fail {0} {1:#?}\n", s, _e4), + ); + }; + Err(DaemonError::StdErr(_e4.to_string())) + } + Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), + } + } + Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), + } + } + Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), + } + } + Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), + } + } +} +pub mod keys { + #![allow(unused)] + pub mod private { + use super::public::PublicKey; + use crate::proto::injective::{InjectivePubKey, ETHEREUM_COIN_TYPE}; + use crate::DaemonError; + use base64::Engine; + use bitcoin::{ + bip32::{ExtendedPrivKey, IntoDerivationPath}, + Network, + }; + use cosmrs::tx::SignerPublicKey; + use hkd32::mnemonic::{Phrase, Seed}; + use rand_core::OsRng; + use secp256k1::Secp256k1; + /// The Private key structure that is used to generate signatures and public keys + /// WARNING: No Security Audit has been performed + pub struct PrivateKey { + #[allow(missing_docs)] + pub account: u32, + #[allow(missing_docs)] + pub index: u32, + #[allow(missing_docs)] + pub coin_type: u32, + /// The 24 words used to generate this private key + mnemonic: Option, + #[allow(dead_code)] + /// This is used for testing + root_private_key: ExtendedPrivKey, + /// The private key + private_key: ExtendedPrivKey, + } + #[automatically_derived] + impl ::core::clone::Clone for PrivateKey { + #[inline] + fn clone(&self) -> PrivateKey { + PrivateKey { + account: ::core::clone::Clone::clone(&self.account), + index: ::core::clone::Clone::clone(&self.index), + coin_type: ::core::clone::Clone::clone(&self.coin_type), + mnemonic: ::core::clone::Clone::clone(&self.mnemonic), + root_private_key: ::core::clone::Clone::clone( + &self.root_private_key, + ), + private_key: ::core::clone::Clone::clone(&self.private_key), + } + } + } + impl PrivateKey { + /// Generate a new private key + pub fn new( + secp: &Secp256k1, + coin_type: u32, + ) -> Result { + let phrase = hkd32::mnemonic::Phrase::random( + OsRng, + hkd32::mnemonic::Language::English, + ); + PrivateKey::gen_private_key_phrase(secp, phrase, 0, 0, coin_type, "") + } + /// generate a new private key with a seed phrase + pub fn new_seed( + secp: &Secp256k1, + seed_phrase: &str, + coin_type: u32, + ) -> Result { + let phrase = hkd32::mnemonic::Phrase::random( + OsRng, + hkd32::mnemonic::Language::English, + ); + PrivateKey::gen_private_key_phrase( + secp, + phrase, + 0, + 0, + coin_type, + seed_phrase, + ) + } + /// for private key recovery. This is also used by wallet routines to re-hydrate the structure + pub fn from_words( + secp: &Secp256k1, + words: &str, + account: u32, + index: u32, + coin_type: u32, + ) -> Result { + if words.split(' ').count() != 24 { + return Err(DaemonError::WrongLength); + } + match hkd32::mnemonic::Phrase::new( + words, + hkd32::mnemonic::Language::English, + ) { + Ok(phrase) => { + PrivateKey::gen_private_key_phrase( + secp, + phrase, + account, + index, + coin_type, + "", + ) + } + Err(_) => Err(DaemonError::Phrasing), + } + } + /// for private key recovery with seed phrase + pub fn from_words_seed( + secp: &Secp256k1, + words: &str, + seed_pass: &str, + coin_type: u32, + ) -> Result { + match hkd32::mnemonic::Phrase::new( + words, + hkd32::mnemonic::Language::English, + ) { + Ok(phrase) => { + PrivateKey::gen_private_key_phrase( + secp, + phrase, + 0, + 0, + coin_type, + seed_pass, + ) + } + Err(_) => Err(DaemonError::Phrasing), + } + } + /// generate the public key for this private key + pub fn public_key( + &self, + secp: &Secp256k1, + ) -> PublicKey { + if self.coin_type == ETHEREUM_COIN_TYPE { + { + ::core::panicking::panic_fmt( + format_args!( + "Coin Type {0} not supported without eth feature", + ETHEREUM_COIN_TYPE, + ), + ); + }; + } + let x = self.private_key.private_key.public_key(secp); + PublicKey::from_bitcoin_public_key(&bitcoin::PublicKey::new(x)) + } + pub fn get_injective_public_key( + &self, + secp: &Secp256k1, + ) -> SignerPublicKey { + use base64::engine::general_purpose; + use cosmrs::tx::MessageExt; + use secp256k1::SecretKey; + let secret_key = SecretKey::from_slice(self.raw_key().as_slice()) + .unwrap(); + let public_key = secp256k1::PublicKey::from_secret_key( + secp, + &secret_key, + ); + let vec_pk = public_key.serialize(); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "{0:?}, public key", + general_purpose::STANDARD.encode(vec_pk), + ), + lvl, + &( + "cw_orch_daemon::keys::private", + "cw_orch_daemon::keys::private", + "cw-orch-daemon/src/keys/private.rs", + ), + 124u32, + ::log::__private_api::Option::None, + ); + } + }; + let inj_key = InjectivePubKey { + key: vec_pk.into(), + }; + inj_key.to_any().unwrap().try_into().unwrap() + } + pub fn get_signer_public_key( + &self, + secp: &Secp256k1, + ) -> Option { + if self.coin_type == ETHEREUM_COIN_TYPE { + { + ::core::panicking::panic_fmt( + format_args!( + "Coin Type {0} not supported without eth feature", + ETHEREUM_COIN_TYPE, + ), + ); + }; + } + Some( + cosmrs::crypto::secp256k1::SigningKey::from_slice( + self.raw_key().as_slice(), + ) + .unwrap() + .public_key() + .into(), + ) + } + pub fn raw_key(&self) -> Vec { + self.private_key.private_key.secret_bytes().to_vec() + } + fn gen_private_key_phrase( + secp: &Secp256k1, + phrase: Phrase, + account: u32, + index: u32, + coin_type: u32, + seed_phrase: &str, + ) -> Result { + let seed = phrase.to_seed(seed_phrase); + let root_private_key = ExtendedPrivKey::new_master( + Network::Bitcoin, + seed.as_bytes(), + ) + .unwrap(); + let path = { + let res = ::alloc::fmt::format( + format_args!( + "m/44\'/{0}\'/{1}\'/0/{2}", + coin_type, + account, + index, + ), + ); + res + }; + let derivation_path = path.into_derivation_path()?; + let private_key = root_private_key.derive_priv(secp, &derivation_path)?; + Ok(PrivateKey { + account, + index, + coin_type, + mnemonic: Some(phrase), + root_private_key, + private_key, + }) + } + /// the words used to generate this private key + pub fn words(&self) -> Option<&str> { + self.mnemonic.as_ref().map(|phrase| phrase.phrase()) + } + /// used for testing + /// could potentially be used to recreate the private key instead of words + #[allow(dead_code)] + pub(crate) fn seed(&self, passwd: &str) -> Option { + self.mnemonic.as_ref().map(|phrase| phrase.to_seed(passwd)) + } + } + } + pub mod public { + use crate::DaemonError; + use bitcoin::bech32::{decode, encode, u5, FromBase32, ToBase32, Variant}; + pub use ed25519_dalek::VerifyingKey as Ed25519; + use ring::digest::{Context, SHA256}; + use ripemd::{Digest as _, Ripemd160}; + use serde::{Deserialize, Serialize}; + static BECH32_PUBKEY_DATA_PREFIX_SECP256K1: [u8; 5] = [ + 0xeb, + 0x5a, + 0xe9, + 0x87, + 0x21, + ]; + static BECH32_PUBKEY_DATA_PREFIX_ED25519: [u8; 5] = [ + 0x16, + 0x24, + 0xde, + 0x64, + 0x20, + ]; + /// The public key we used to generate the cosmos/tendermind/terrad addresses + pub struct PublicKey { + /// This is optional as we can generate non-pub keys without + pub raw_pub_key: Option>, + /// The raw bytes used to generate non-pub keys + pub raw_address: Option>, + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl<'de> _serde::Deserialize<'de> for PublicKey { + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + #[allow(non_camel_case_types)] + #[doc(hidden)] + enum __Field { + __field0, + __field1, + __ignore, + } + #[doc(hidden)] + struct __FieldVisitor; + impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { + type Value = __Field; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "field identifier", + ) + } + fn visit_u64<__E>( + self, + __value: u64, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + 0u64 => _serde::__private::Ok(__Field::__field0), + 1u64 => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_str<__E>( + self, + __value: &str, + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + "raw_pub_key" => _serde::__private::Ok(__Field::__field0), + "raw_address" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + fn visit_bytes<__E>( + self, + __value: &[u8], + ) -> _serde::__private::Result + where + __E: _serde::de::Error, + { + match __value { + b"raw_pub_key" => _serde::__private::Ok(__Field::__field0), + b"raw_address" => _serde::__private::Ok(__Field::__field1), + _ => _serde::__private::Ok(__Field::__ignore), + } + } + } + impl<'de> _serde::Deserialize<'de> for __Field { + #[inline] + fn deserialize<__D>( + __deserializer: __D, + ) -> _serde::__private::Result + where + __D: _serde::Deserializer<'de>, + { + _serde::Deserializer::deserialize_identifier( + __deserializer, + __FieldVisitor, + ) + } + } + #[doc(hidden)] + struct __Visitor<'de> { + marker: _serde::__private::PhantomData, + lifetime: _serde::__private::PhantomData<&'de ()>, + } + impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { + type Value = PublicKey; + fn expecting( + &self, + __formatter: &mut _serde::__private::Formatter, + ) -> _serde::__private::fmt::Result { + _serde::__private::Formatter::write_str( + __formatter, + "struct PublicKey", + ) + } + #[inline] + fn visit_seq<__A>( + self, + mut __seq: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::SeqAccess<'de>, + { + let __field0 = match _serde::de::SeqAccess::next_element::< + Option>, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 0usize, + &"struct PublicKey with 2 elements", + ), + ); + } + }; + let __field1 = match _serde::de::SeqAccess::next_element::< + Option>, + >(&mut __seq)? { + _serde::__private::Some(__value) => __value, + _serde::__private::None => { + return _serde::__private::Err( + _serde::de::Error::invalid_length( + 1usize, + &"struct PublicKey with 2 elements", + ), + ); + } + }; + _serde::__private::Ok(PublicKey { + raw_pub_key: __field0, + raw_address: __field1, + }) + } + #[inline] + fn visit_map<__A>( + self, + mut __map: __A, + ) -> _serde::__private::Result + where + __A: _serde::de::MapAccess<'de>, + { + let mut __field0: _serde::__private::Option< + Option>, + > = _serde::__private::None; + let mut __field1: _serde::__private::Option< + Option>, + > = _serde::__private::None; + while let _serde::__private::Some(__key) + = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { + match __key { + __Field::__field0 => { + if _serde::__private::Option::is_some(&__field0) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field( + "raw_pub_key", + ), + ); + } + __field0 = _serde::__private::Some( + _serde::de::MapAccess::next_value::< + Option>, + >(&mut __map)?, + ); + } + __Field::__field1 => { + if _serde::__private::Option::is_some(&__field1) { + return _serde::__private::Err( + <__A::Error as _serde::de::Error>::duplicate_field( + "raw_address", + ), + ); + } + __field1 = _serde::__private::Some( + _serde::de::MapAccess::next_value::< + Option>, + >(&mut __map)?, + ); + } + _ => { + let _ = _serde::de::MapAccess::next_value::< + _serde::de::IgnoredAny, + >(&mut __map)?; + } + } + } + let __field0 = match __field0 { + _serde::__private::Some(__field0) => __field0, + _serde::__private::None => { + _serde::__private::de::missing_field("raw_pub_key")? + } + }; + let __field1 = match __field1 { + _serde::__private::Some(__field1) => __field1, + _serde::__private::None => { + _serde::__private::de::missing_field("raw_address")? + } + }; + _serde::__private::Ok(PublicKey { + raw_pub_key: __field0, + raw_address: __field1, + }) + } + } + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &[ + "raw_pub_key", + "raw_address", + ]; + _serde::Deserializer::deserialize_struct( + __deserializer, + "PublicKey", + FIELDS, + __Visitor { + marker: _serde::__private::PhantomData::, + lifetime: _serde::__private::PhantomData, + }, + ) + } + } + }; + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _: () = { + #[allow(unused_extern_crates, clippy::useless_attribute)] + extern crate serde as _serde; + #[automatically_derived] + impl _serde::Serialize for PublicKey { + fn serialize<__S>( + &self, + __serializer: __S, + ) -> _serde::__private::Result<__S::Ok, __S::Error> + where + __S: _serde::Serializer, + { + let mut __serde_state = _serde::Serializer::serialize_struct( + __serializer, + "PublicKey", + false as usize + 1 + 1, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "raw_pub_key", + &self.raw_pub_key, + )?; + _serde::ser::SerializeStruct::serialize_field( + &mut __serde_state, + "raw_address", + &self.raw_address, + )?; + _serde::ser::SerializeStruct::end(__serde_state) + } + } + }; + #[automatically_derived] + impl ::core::fmt::Debug for PublicKey { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "PublicKey", + "raw_pub_key", + &self.raw_pub_key, + "raw_address", + &&self.raw_address, + ) + } + } + #[automatically_derived] + impl ::core::clone::Clone for PublicKey { + #[inline] + fn clone(&self) -> PublicKey { + PublicKey { + raw_pub_key: ::core::clone::Clone::clone(&self.raw_pub_key), + raw_address: ::core::clone::Clone::clone(&self.raw_address), + } + } + } + impl PublicKey { + /// Generate a Cosmos/Tendermint/Terrad Public Key + pub fn from_bitcoin_public_key(bpub: &bitcoin::key::PublicKey) -> PublicKey { + let bpub_bytes = bpub.inner.serialize(); + let raw_pub_key = PublicKey::pubkey_from_public_key(&bpub_bytes); + let raw_address = PublicKey::address_from_public_key(&bpub_bytes); + PublicKey { + raw_pub_key: Some(raw_pub_key), + raw_address: Some(raw_address), + } + } + /// Generate from secp256k1 Cosmos/Terrad Public Key + pub fn from_public_key(bpub: &[u8]) -> PublicKey { + let raw_pub_key = PublicKey::pubkey_from_public_key(bpub); + let raw_address = PublicKey::address_from_public_key(bpub); + PublicKey { + raw_pub_key: Some(raw_pub_key), + raw_address: Some(raw_address), + } + } + /// Generate a Cosmos/Tendermint/Terrad Account + pub fn from_account( + acc_address: &str, + prefix: &str, + ) -> Result { + PublicKey::check_prefix_and_length(prefix, acc_address, 44) + .and_then(|vu5| { + let vu8 = Vec::from_base32(vu5.as_slice()) + .map_err(|source| DaemonError::Conversion { + key: acc_address.into(), + source, + })?; + Ok(PublicKey { + raw_pub_key: None, + raw_address: Some(vu8), + }) + }) + } + /// build a public key from a tendermint public key + pub fn from_tendermint_key( + tendermint_public_key: &str, + ) -> Result { + let len = tendermint_public_key.len(); + if len == 83 { + PublicKey::check_prefix_and_length( + "terravalconspub", + tendermint_public_key, + len, + ) + .and_then(|vu5| { + let vu8 = Vec::from_base32(vu5.as_slice()) + .map_err(|source| { + DaemonError::Conversion { + key: tendermint_public_key.into(), + source, + } + })?; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("{0:#?}", hex::encode(&vu8)), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 74u32, + ::log::__private_api::Option::None, + ); + } + }; + if vu8.starts_with(&BECH32_PUBKEY_DATA_PREFIX_SECP256K1) { + let public_key = PublicKey::public_key_from_pubkey(&vu8)?; + let raw = PublicKey::address_from_public_key(&public_key); + Ok(PublicKey { + raw_pub_key: Some(vu8), + raw_address: Some(raw), + }) + } else { + Err(DaemonError::ConversionSECP256k1) + } + }) + } else if len == 82 { + PublicKey::check_prefix_and_length( + "terravalconspub", + tendermint_public_key, + len, + ) + .and_then(|vu5| { + let vu8 = Vec::from_base32(vu5.as_slice()) + .map_err(|source| { + DaemonError::Conversion { + key: tendermint_public_key.into(), + source, + } + })?; + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("ED25519 public keys are not fully supported"), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 100u32, + ::log::__private_api::Option::None, + ); + } + }; + if vu8.starts_with(&BECH32_PUBKEY_DATA_PREFIX_ED25519) { + let raw = PublicKey::address_from_public_ed25519_key(&vu8)?; + Ok(PublicKey { + raw_pub_key: Some(vu8), + raw_address: Some(raw), + }) + } else { + Err(DaemonError::ConversionED25519) + } + }) + } else { + Err(DaemonError::ConversionLength(len)) + } + } + /// build a terravalcons address from a tendermint hex key + /// the tendermint_hex_address should be a hex code of 40 length + pub fn from_tendermint_address( + tendermint_hex_address: &str, + ) -> Result { + let len = tendermint_hex_address.len(); + if len == 40 { + let raw = hex::decode(tendermint_hex_address)?; + Ok(PublicKey { + raw_pub_key: None, + raw_address: Some(raw), + }) + } else { + Err(DaemonError::ConversionLengthED25519Hex(len)) + } + } + /// Generate a Operator address for this public key (used by the validator) + pub fn from_operator_address( + valoper_address: &str, + ) -> Result { + PublicKey::check_prefix_and_length("terravaloper", valoper_address, 51) + .and_then(|vu5| { + let vu8 = Vec::from_base32(vu5.as_slice()) + .map_err(|source| DaemonError::Conversion { + key: valoper_address.into(), + source, + })?; + Ok(PublicKey { + raw_pub_key: None, + raw_address: Some(vu8), + }) + }) + } + /// Generate Public key from raw address + pub fn from_raw_address( + raw_address: &str, + ) -> Result { + let vec1 = hex::decode(raw_address)?; + Ok(PublicKey { + raw_pub_key: None, + raw_address: Some(vec1), + }) + } + fn check_prefix_and_length( + prefix: &str, + data: &str, + length: usize, + ) -> Result, DaemonError> { + let (hrp, decoded_str, _) = decode(data) + .map_err(|source| DaemonError::Conversion { + key: data.into(), + source, + })?; + if hrp == prefix && data.len() == length { + Ok(decoded_str) + } else { + Err( + DaemonError::Bech32DecodeExpanded( + hrp, + data.len(), + prefix.into(), + length, + ), + ) + } + } + /** + Gets a bech32-words pubkey from a compressed bytes Secp256K1 public key. + + @param publicKey raw public key + */ + pub fn pubkey_from_public_key(public_key: &[u8]) -> Vec { + [BECH32_PUBKEY_DATA_PREFIX_SECP256K1.to_vec(), public_key.to_vec()] + .concat() + } + /** + Gets a bech32-words pubkey from a compressed bytes Ed25519 public key. + + @param publicKey raw public key + */ + pub fn pubkey_from_ed25519_public_key(public_key: &[u8]) -> Vec { + [BECH32_PUBKEY_DATA_PREFIX_ED25519.to_vec(), public_key.to_vec()] + .concat() + } + /// Translate from a BECH32 prefixed key to a standard public key + pub fn public_key_from_pubkey( + pub_key: &[u8], + ) -> Result, DaemonError> { + if pub_key.starts_with(&BECH32_PUBKEY_DATA_PREFIX_SECP256K1) { + let len = BECH32_PUBKEY_DATA_PREFIX_SECP256K1.len(); + let len2 = pub_key.len(); + Ok(Vec::from(&pub_key[len..len2])) + } else if pub_key.starts_with(&BECH32_PUBKEY_DATA_PREFIX_ED25519) { + let len = BECH32_PUBKEY_DATA_PREFIX_ED25519.len(); + let len2 = pub_key.len(); + let vec = &pub_key[len..len2]; + let ed25519_pubkey = Ed25519::from_bytes(vec.try_into().unwrap())?; + Ok(ed25519_pubkey.to_bytes().to_vec()) + } else { + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("pub key does not start with BECH32 PREFIX"), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 228u32, + ::log::__private_api::Option::None, + ); + } + }; + Err(DaemonError::Bech32DecodeErr) + } + } + /** + Gets a raw address from a compressed bytes public key. + + @param publicKey raw public key + */ + pub fn address_from_public_key(public_key: &[u8]) -> Vec { + let mut hasher = Ripemd160::new(); + let sha_result = ring::digest::digest(&SHA256, public_key); + hasher.update(&sha_result.as_ref()[0..32]); + let ripe_result = hasher.finalize(); + let address: Vec = ripe_result[0..20].to_vec(); + address + } + /** + Gets a raw address from a ed25519 public key. + + @param publicKey raw public key + */ + pub fn address_from_public_ed25519_key( + public_key: &[u8], + ) -> Result, DaemonError> { + if public_key.len() != (32 + 5) { + Err( + DaemonError::ConversionPrefixED25519( + public_key.len(), + hex::encode(public_key), + ), + ) + } else { + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "address_from_public_ed25519_key public key - {0}", + hex::encode(public_key), + ), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 262u32, + ::log::__private_api::Option::None, + ); + } + }; + let mut sha_result: [u8; 32] = [0; 32]; + let sha_result = ring::digest::digest(&SHA256, &public_key[5..]); + let address: Vec = sha_result.as_ref()[0..20].to_vec(); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "address_from_public_ed25519_key sha result - {0}", + hex::encode(&address), + ), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 283u32, + ::log::__private_api::Option::None, + ); + } + }; + Ok(address) + } + } + /// The main account used in most things + pub fn account(&self, prefix: &str) -> Result { + match &self.raw_address { + Some(raw) => { + let data = encode(prefix, raw.to_base32(), Variant::Bech32); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => Err(DaemonError::Implementation), + } + } + /// The operator address used for validators + pub fn operator_address(&self, prefix: &str) -> Result { + match &self.raw_address { + Some(raw) => { + let data = encode( + &{ + let res = ::alloc::fmt::format( + format_args!("{0}{1}", prefix, "valoper"), + ); + res + }, + raw.to_base32(), + Variant::Bech32, + ); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => Err(DaemonError::Implementation), + } + } + /// application public key - Application keys are associated with a public key terrapub- and an address terra- + pub fn application_public_key( + &self, + prefix: &str, + ) -> Result { + match &self.raw_pub_key { + Some(raw) => { + let data = encode( + &{ + let res = ::alloc::fmt::format( + format_args!("{0}{1}", prefix, "pub"), + ); + res + }, + raw.to_base32(), + Variant::Bech32, + ); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Missing Public Key. Can\'t continue"), + lvl, + &( + "cw_orch_daemon::keys::public", + "cw_orch_daemon::keys::public", + "cw-orch-daemon/src/keys/public.rs", + ), + 335u32, + ::log::__private_api::Option::None, + ); + } + }; + Err(DaemonError::Implementation) + } + } + } + /// The operator address used for validators public key. + pub fn operator_address_public_key( + &self, + prefix: &str, + ) -> Result { + match &self.raw_pub_key { + Some(raw) => { + let data = encode( + &{ + let res = ::alloc::fmt::format( + format_args!("{0}{1}", prefix, "valoperpub"), + ); + res + }, + raw.to_base32(), + Variant::Bech32, + ); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => Err(DaemonError::Implementation), + } + } + /// This is a unique key used to sign block hashes. It is associated with a public key terravalconspub. + pub fn tendermint(&self, prefix: &str) -> Result { + match &self.raw_address { + Some(raw) => { + let data = encode( + &{ + let res = ::alloc::fmt::format( + format_args!("{0}{1}", prefix, "valcons"), + ); + res + }, + raw.to_base32(), + Variant::Bech32, + ); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => Err(DaemonError::Implementation), + } + } + /// This is a unique key used to sign block hashes. It is associated with a public key terravalconspub. + pub fn tendermint_pubkey( + &self, + prefix: &str, + ) -> Result { + match &self.raw_pub_key { + Some(raw) => { + let b32 = raw.to_base32(); + let data = encode( + &{ + let res = ::alloc::fmt::format( + format_args!("{0}{1}", prefix, "valconspub"), + ); + res + }, + b32, + Variant::Bech32, + ); + match data { + Ok(acc) => Ok(acc), + Err(_) => Err(DaemonError::Bech32DecodeErr), + } + } + None => Err(DaemonError::Implementation), + } + } + } + } + pub mod signature { + use crate::DaemonError; + use base64::engine::{general_purpose::STANDARD, Engine}; + use ring::digest::SHA256; + use secp256k1::{Message, Secp256k1}; + pub struct Signature {} + impl Signature { + pub fn verify( + secp: &Secp256k1, + pub_key: &str, + signature: &str, + blob: &str, + ) -> Result<(), DaemonError> { + let public = STANDARD.decode(pub_key)?; + let sig = STANDARD.decode(signature)?; + let pk = secp256k1::PublicKey::from_slice(public.as_slice())?; + let sha_result = ring::digest::digest(&SHA256, blob.as_bytes()); + let message: Message = Message::from_slice(&sha_result.as_ref()[0..32])?; + let secp_sig = secp256k1::ecdsa::Signature::from_compact( + sig.as_slice(), + )?; + secp.verify_ecdsa(&message, &secp_sig, &pk)?; + Ok(()) + } + } + } +} +pub mod live_mock { + //! Live mock is a mock that uses a live chain to query for data. + //! It can be used to do chain-backed unit-testing. It can't be used for state-changing operations. + use crate::queriers::Bank; + use crate::queriers::CosmWasm; + use crate::queriers::DaemonQuerier; + use crate::queriers::Staking; + use cosmwasm_std::Addr; + use cosmwasm_std::AllBalanceResponse; + use cosmwasm_std::BalanceResponse; + use cosmwasm_std::Delegation; + use cosmwasm_std::{AllDelegationsResponse, BondedDenomResponse}; + use cosmwasm_std::BankQuery; + use cosmwasm_std::Binary; + use cosmwasm_std::Empty; + use cosmwasm_std::StakingQuery; + use ibc_chain_registry::chain::ChainData; + use tokio::runtime::Runtime; + use tonic::transport::Channel; + use std::marker::PhantomData; + use std::str::FromStr; + use cosmwasm_std::testing::{MockApi, MockStorage}; + use cosmwasm_std::{ + from_slice, to_binary, Coin, ContractResult, OwnedDeps, Querier, QuerierResult, + QueryRequest, SystemError, SystemResult, Uint128, WasmQuery, + }; + use crate::channel::GrpcChannel; + fn to_cosmwasm_coin(c: cosmrs::proto::cosmos::base::v1beta1::Coin) -> Coin { + Coin { + amount: Uint128::from_str(&c.amount).unwrap(), + denom: c.denom, + } + } + const QUERIER_ERROR: &str = "Only Bank balances and Wasm (raw + smart) and Some staking queries are covered for now"; + /// mock_dependencies is a drop-in replacement for cosmwasm_std::testing::mock_dependencies + /// this uses our CustomQuerier. + pub fn mock_dependencies( + chain_info: ChainData, + ) -> OwnedDeps { + let custom_querier: WasmMockQuerier = WasmMockQuerier::new(chain_info); + OwnedDeps { + storage: MockStorage::default(), + api: MockApi::default(), + querier: custom_querier, + custom_query_type: PhantomData, + } + } + /// Querier struct that fetches queries on-chain directly + pub struct WasmMockQuerier { + channel: Channel, + runtime: Runtime, + } + impl Querier for WasmMockQuerier { + fn raw_query(&self, bin_request: &[u8]) -> QuerierResult { + let request: QueryRequest = match from_slice(bin_request) { + Ok(v) => v, + Err(e) => { + return SystemResult::Err(SystemError::InvalidRequest { + error: { + let res = ::alloc::fmt::format( + format_args!("Parsing query request: {0}", e), + ); + res + }, + request: bin_request.into(), + }); + } + }; + self.handle_query(&request) + } + } + impl WasmMockQuerier { + /// Function used to handle a query and customize the query behavior + /// This implements some queries by querying an actual node for the responses + pub fn handle_query(&self, request: &QueryRequest) -> QuerierResult { + match &request { + QueryRequest::Wasm(x) => { + let querier = CosmWasm::new(self.channel.clone()); + match x { + WasmQuery::Smart { contract_addr, msg } => { + let query_result: Result = self + .runtime + .block_on( + querier + .contract_state(contract_addr.to_string(), msg.to_vec()), + ) + .map(|query_result| query_result.into()); + SystemResult::Ok(ContractResult::from(query_result)) + } + WasmQuery::Raw { contract_addr, key } => { + let query_result = self + .runtime + .block_on( + querier + .contract_raw_state(contract_addr.to_string(), key.to_vec()), + ) + .map(|query_result| query_result.data.into()); + SystemResult::Ok(ContractResult::from(query_result)) + } + _ => { + SystemResult::Err(SystemError::InvalidRequest { + error: QUERIER_ERROR.to_string(), + request: to_binary(&request).unwrap(), + }) + } + } + } + QueryRequest::Bank(x) => { + let querier = Bank::new(self.channel.clone()); + match x { + BankQuery::Balance { address, denom } => { + let query_result = self + .runtime + .block_on(querier.balance(address, Some(denom.clone()))) + .map(|result| { + to_binary( + &BalanceResponse { + amount: Coin { + amount: Uint128::from_str(&result[0].amount).unwrap(), + denom: result[0].denom.clone(), + }, + }, + ) + .unwrap() + }); + SystemResult::Ok(ContractResult::from(query_result)) + } + BankQuery::AllBalances { address } => { + let query_result = self + .runtime + .block_on(querier.balance(address, None)) + .map(|result| AllBalanceResponse { + amount: result + .into_iter() + .map(|c| Coin { + amount: Uint128::from_str(&c.amount).unwrap(), + denom: c.denom, + }) + .collect(), + }) + .map(|query_result| to_binary(&query_result)) + .unwrap(); + SystemResult::Ok(ContractResult::from(query_result)) + } + _ => { + SystemResult::Err(SystemError::InvalidRequest { + error: QUERIER_ERROR.to_string(), + request: to_binary(&request).unwrap(), + }) + } + } + } + QueryRequest::Staking(x) => { + let querier = Staking::new(self.channel.clone()); + match x { + StakingQuery::BondedDenom {} => { + let query_result = self + .runtime + .block_on(querier.params()) + .map(|result| BondedDenomResponse { + denom: result.params.unwrap().bond_denom, + }) + .map(|query_result| to_binary(&query_result)) + .unwrap(); + SystemResult::Ok(ContractResult::from(query_result)) + } + StakingQuery::AllDelegations { delegator } => { + let query_result = self + .runtime + .block_on(querier.delegator_delegations(delegator, None)) + .map(|result| AllDelegationsResponse { + delegations: result + .delegation_responses + .into_iter() + .filter_map(|delegation| { + delegation + .delegation + .map(|d| Delegation { + delegator: Addr::unchecked(d.delegator_address), + validator: d.validator_address, + amount: to_cosmwasm_coin(delegation.balance.unwrap()), + }) + }) + .collect(), + }) + .map(|query_result| to_binary(&query_result)) + .unwrap(); + SystemResult::Ok(ContractResult::from(query_result)) + } + _ => ::core::panicking::panic("not yet implemented"), + } + } + _ => { + SystemResult::Err(SystemError::InvalidRequest { + error: QUERIER_ERROR.to_string(), + request: to_binary(&request).unwrap(), + }) + } + } + } + } + impl WasmMockQuerier { + /// Creates a querier from chain information + pub fn new(chain: ChainData) -> Self { + let rt = Runtime::new().unwrap(); + let channel = rt + .block_on(GrpcChannel::connect(&chain.apis.grpc, &chain.chain_id)) + .unwrap(); + WasmMockQuerier { + channel, + runtime: rt, + } + } + } +} +pub mod queriers { + //! # DaemonQuerier + //! + //! DaemonAsync queriers are gRPC query clients for the CosmosSDK modules. They can be used to query the different modules (Bank, Ibc, Authz, ...). + //! + //! ## Usage + //! + //! You will need to acquire a [gRPC channel](Channel) to a running CosmosSDK node to be able to use the queriers. + //! Here is an example of how to acquire one using the DaemonAsync builder. + //! + //! ```no_run + //! // require the querier you want to use, in this case Node + //! use cw_orch_daemon::{queriers::Node, DaemonAsync, networks, queriers::DaemonQuerier}; + //! # tokio_test::block_on(async { + //! // call the builder and configure it as you need + //! let daemon = DaemonAsync::builder() + //! .chain(networks::LOCAL_JUNO) + //! .build() + //! .await.unwrap(); + //! // now you can use the Node querier: + //! let node = Node::new(daemon.channel()); + //! let node_info = node.info(); + //! # }) + //! ``` + mod bank { + use crate::{cosmos_modules, error::DaemonError}; + use cosmrs::proto::cosmos::base::{query::v1beta1::PageRequest, v1beta1::Coin}; + use tonic::transport::Channel; + use super::DaemonQuerier; + /// Queries for Cosmos Bank Module + pub struct Bank { + channel: Channel, + } + impl DaemonQuerier for Bank { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Bank { + /// Query the bank balance of a given address + /// If denom is None, returns all balances + pub async fn balance( + &self, + address: impl Into, + denom: Option, + ) -> Result, DaemonError> { + match denom { + Some(denom) => { + let resp = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QueryBalanceRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryBalanceRequest { + address: address.into(), + denom: denom, + }; + let response = client + .balance(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 28u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + let coin = resp.balance.unwrap(); + Ok( + <[_]>::into_vec( + #[rustc_box] + ::alloc::boxed::Box::new([coin]), + ), + ) + } + None => { + let resp = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QueryAllBalancesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryAllBalancesRequest { + address: address.into(), + pagination: None, + }; + let response = client + .all_balances(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 41u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + let coins = resp.balances; + Ok(coins.into_iter().collect()) + } + } + } + /// Query spendable balance for address + pub async fn spendable_balances( + &self, + address: impl Into, + ) -> Result, DaemonError> { + let spendable_balances: cosmos_modules::bank::QuerySpendableBalancesResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QuerySpendableBalancesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QuerySpendableBalancesRequest { + address: address.into(), + pagination: None, + }; + let response = client + .spendable_balances(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 62u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(spendable_balances.balances) + } + /// Query total supply in the bank + pub async fn total_supply(&self) -> Result, DaemonError> { + let total_supply: cosmos_modules::bank::QueryTotalSupplyResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QueryTotalSupplyRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryTotalSupplyRequest { + pagination: None, + }; + let response = client + .total_supply(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 76u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(total_supply.supply) + } + /// Query total supply in the bank for a denom + pub async fn supply_of( + &self, + denom: impl Into, + ) -> Result { + let supply_of: cosmos_modules::bank::QuerySupplyOfResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QuerySupplyOfRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QuerySupplyOfRequest { + denom: denom.into(), + }; + let response = client.supply_of(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 87u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(supply_of.amount.unwrap()) + } + /// Query params + pub async fn params( + &self, + ) -> Result { + let params: cosmos_modules::bank::QueryParamsResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QueryParamsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryParamsRequest {}; + let response = client.params(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 101u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(params.params.unwrap()) + } + /// Query denom metadata + pub async fn denom_metadata( + &self, + denom: impl Into, + ) -> Result { + let denom_metadata: cosmos_modules::bank::QueryDenomMetadataResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QueryDenomMetadataRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDenomMetadataRequest { + denom: denom.into(), + }; + let response = client + .denom_metadata(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 110u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(denom_metadata.metadata.unwrap()) + } + /// Query denoms metadata with pagination + /// + /// see [PageRequest] for pagination + pub async fn denoms_metadata( + &self, + pagination: Option, + ) -> Result, DaemonError> { + let denoms_metadata: cosmos_modules::bank::QueryDenomsMetadataResponse = { + use crate::cosmos_modules::bank::{ + query_client::QueryClient, QueryDenomsMetadataRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDenomsMetadataRequest { + pagination: pagination, + }; + let response = client + .denoms_metadata(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::bank", + "cw_orch_daemon::queriers::bank", + "cw-orch-daemon/src/queriers/bank.rs", + ), + 128u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(denoms_metadata.metadatas) + } + } + } + mod cosmwasm { + use crate::{cosmos_modules, error::DaemonError}; + use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; + use tonic::transport::Channel; + use super::DaemonQuerier; + /// Querier for the CosmWasm SDK module + pub struct CosmWasm { + channel: Channel, + } + impl DaemonQuerier for CosmWasm { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl CosmWasm { + /// Query code_id by hash + pub async fn code_id_hash( + &self, + code_id: u64, + ) -> Result { + use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryCodeRequest { code_id }; + let resp = client.code(request).await?.into_inner(); + let contract_hash = resp.code_info.unwrap().data_hash; + let on_chain_hash = base16::encode_lower(&contract_hash); + Ok(on_chain_hash) + } + /// Query contract info + pub async fn contract_info( + &self, + address: impl Into, + ) -> Result { + use cosmos_modules::cosmwasm::{ + query_client::*, QueryContractInfoRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryContractInfoRequest { + address: address.into(), + }; + let resp = client.contract_info(request).await?.into_inner(); + let contract_info = resp.contract_info.unwrap(); + Ok(contract_info) + } + /// Query contract history + pub async fn contract_history( + &self, + address: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::cosmwasm::QueryContractHistoryResponse, + DaemonError, + > { + use cosmos_modules::cosmwasm::{ + query_client::*, QueryContractHistoryRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryContractHistoryRequest { + address: address.into(), + pagination, + }; + Ok(client.contract_history(request).await?.into_inner()) + } + /// Query contract state + pub async fn contract_state( + &self, + address: impl Into, + query_data: Vec, + ) -> Result, DaemonError> { + use cosmos_modules::cosmwasm::{ + query_client::*, QuerySmartContractStateRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QuerySmartContractStateRequest { + address: address.into(), + query_data, + }; + Ok(client.smart_contract_state(request).await?.into_inner().data) + } + /// Query all contract state + pub async fn all_contract_state( + &self, + address: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::cosmwasm::QueryAllContractStateResponse, + DaemonError, + > { + use cosmos_modules::cosmwasm::{ + query_client::*, QueryAllContractStateRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryAllContractStateRequest { + address: address.into(), + pagination, + }; + Ok(client.all_contract_state(request).await?.into_inner()) + } + /// Query code + pub async fn code( + &self, + code_id: u64, + ) -> Result { + use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryCodeRequest { code_id }; + Ok(client.code(request).await?.into_inner().code_info.unwrap()) + } + /// Query code bytes + pub async fn code_data(&self, code_id: u64) -> Result, DaemonError> { + use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryCodeRequest { code_id }; + Ok(client.code(request).await?.into_inner().data) + } + /// Query codes + pub async fn codes( + &self, + pagination: Option, + ) -> Result, DaemonError> { + use cosmos_modules::cosmwasm::{query_client::*, QueryCodesRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryCodesRequest { pagination }; + Ok(client.codes(request).await?.into_inner().code_infos) + } + /// Query pinned codes + pub async fn pinned_codes( + &self, + ) -> Result< + cosmos_modules::cosmwasm::QueryPinnedCodesResponse, + DaemonError, + > { + use cosmos_modules::cosmwasm::{query_client::*, QueryPinnedCodesRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryPinnedCodesRequest { + pagination: None, + }; + Ok(client.pinned_codes(request).await?.into_inner()) + } + /// Query contracts by code + pub async fn contract_by_codes( + &self, + code_id: u64, + ) -> Result< + cosmos_modules::cosmwasm::QueryContractsByCodeResponse, + DaemonError, + > { + use cosmos_modules::cosmwasm::{ + query_client::*, QueryContractsByCodeRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryContractsByCodeRequest { + code_id, + pagination: None, + }; + Ok(client.contracts_by_code(request).await?.into_inner()) + } + /// Query raw contract state + pub async fn contract_raw_state( + &self, + address: impl Into, + query_data: Vec, + ) -> Result< + cosmos_modules::cosmwasm::QueryRawContractStateResponse, + DaemonError, + > { + use cosmos_modules::cosmwasm::{ + query_client::*, QueryRawContractStateRequest, + }; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + let request = QueryRawContractStateRequest { + address: address.into(), + query_data, + }; + Ok(client.raw_contract_state(request).await?.into_inner()) + } + /// Query params + pub async fn params( + &self, + ) -> Result { + use cosmos_modules::cosmwasm::{query_client::*, QueryParamsRequest}; + let mut client: QueryClient = QueryClient::new( + self.channel.clone(), + ); + Ok(client.params(QueryParamsRequest {}).await?.into_inner()) + } + } + } + mod feegrant { + use crate::{cosmos_modules, error::DaemonError}; + use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; + use tonic::transport::Channel; + use super::DaemonQuerier; + /// Querier for the Cosmos Gov module + pub struct Feegrant { + channel: Channel, + } + impl DaemonQuerier for Feegrant { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Feegrant { + /// Query all allowances granted to the grantee address by a granter address + pub async fn allowance( + &self, + granter: impl Into, + grantee: impl Into, + ) -> Result { + let allowance: cosmos_modules::feegrant::QueryAllowanceResponse = { + use crate::cosmos_modules::feegrant::{ + query_client::QueryClient, QueryAllowanceRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryAllowanceRequest { + granter: granter.into(), + grantee: grantee.into(), + }; + let response = client.allowance(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::feegrant", + "cw_orch_daemon::queriers::feegrant", + "cw-orch-daemon/src/queriers/feegrant.rs", + ), + 25u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(allowance.allowance.unwrap()) + } + /// Query allowances for grantee address with a given pagination + /// + /// see [PageRequest] for pagination + pub async fn allowances( + &self, + grantee: impl Into, + pagination: Option, + ) -> Result, DaemonError> { + let allowances: cosmos_modules::feegrant::QueryAllowancesResponse = { + use crate::cosmos_modules::feegrant::{ + query_client::QueryClient, QueryAllowancesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryAllowancesRequest { + grantee: grantee.into(), + pagination: pagination, + }; + let response = client + .allowances(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::feegrant", + "cw_orch_daemon::queriers::feegrant", + "cw-orch-daemon/src/queriers/feegrant.rs", + ), + 45u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(allowances.allowances) + } + } + } + mod gov { + use crate::{cosmos_modules, error::DaemonError}; + use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; + use tonic::transport::Channel; + use super::DaemonQuerier; + /// Querier for the Cosmos Gov module + pub struct Gov { + channel: Channel, + } + impl DaemonQuerier for Gov { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Gov { + /// Query proposal details by proposal id + pub async fn proposal( + &self, + proposal_id: u64, + ) -> Result { + let proposal: cosmos_modules::gov::QueryProposalResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryProposalRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryProposalRequest { + proposal_id: proposal_id, + }; + let response = client.proposal(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 24u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(proposal.proposal.unwrap()) + } + /// Query proposals based on given status + /// + /// see [PageRequest] for pagination + pub async fn proposals( + &self, + proposal_status: GovProposalStatus, + voter: impl Into, + depositor: impl Into, + pagination: Option, + ) -> Result { + let proposals: cosmos_modules::gov::QueryProposalsResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryProposalsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryProposalsRequest { + proposal_status: proposal_status as i32, + voter: voter.into(), + depositor: depositor.into(), + pagination: pagination, + }; + let response = client.proposals(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 45u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(proposals) + } + /// Query voted information based on proposal_id for voter address + pub async fn vote( + &self, + proposal_id: u64, + voter: impl Into, + ) -> Result { + let vote: cosmos_modules::gov::QueryVoteResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryVoteRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryVoteRequest { + proposal_id: proposal_id, + voter: voter.into(), + }; + let response = client.vote(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 65u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(vote.vote.unwrap()) + } + /// Query votes of a given proposal + /// + /// see [PageRequest] for pagination + pub async fn votes( + &self, + proposal_id: impl Into, + pagination: Option, + ) -> Result { + let votes: cosmos_modules::gov::QueryVotesResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryVotesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryVotesRequest { + proposal_id: proposal_id.into(), + pagination: pagination, + }; + let response = client.votes(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 85u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(votes) + } + /// Query all parameters of the gov module + pub async fn params( + &self, + params_type: impl Into, + ) -> Result { + let params: cosmos_modules::gov::QueryParamsResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryParamsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryParamsRequest { + params_type: params_type.into(), + }; + let response = client.params(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 102u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(params) + } + /// Query deposit information using proposal_id and depositor address + pub async fn deposit( + &self, + proposal_id: u64, + depositor: impl Into, + ) -> Result { + let deposit: cosmos_modules::gov::QueryDepositResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryDepositRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDepositRequest { + proposal_id: proposal_id, + depositor: depositor.into(), + }; + let response = client.deposit(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 119u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(deposit.deposit.unwrap()) + } + /// Query deposits of a proposal + /// + /// see [PageRequest] for pagination + pub async fn deposits( + &self, + proposal_id: u64, + pagination: Option, + ) -> Result { + let deposits: cosmos_modules::gov::QueryDepositsResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryDepositsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDepositsRequest { + proposal_id: proposal_id, + pagination: pagination, + }; + let response = client.deposits(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 139u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(deposits) + } + /// TallyResult queries the tally of a proposal vote. + pub async fn tally_result( + &mut self, + proposal_id: u64, + ) -> Result { + let tally_result: cosmos_modules::gov::QueryTallyResultResponse = { + use crate::cosmos_modules::gov::{ + query_client::QueryClient, QueryTallyResultRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryTallyResultRequest { + proposal_id: proposal_id, + }; + let response = client + .tally_result(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::gov", + "cw_orch_daemon::queriers::gov", + "cw-orch-daemon/src/queriers/gov.rs", + ), + 156u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(tally_result.tally.unwrap()) + } + } + /// Proposal status + #[allow(missing_docs)] + pub enum GovProposalStatus { + Unspecified = 0, + DepositPeriod = 1, + VotingPeriod = 2, + Passed = 3, + Rejected = 4, + Failed = 5, + } + } + mod ibc { + use super::DaemonQuerier; + use crate::{cosmos_modules, error::DaemonError}; + use cosmos_modules::ibc_channel; + use cosmrs::proto::ibc::{ + applications::transfer::v1::{DenomTrace, QueryDenomTraceResponse}, + core::{ + channel::v1::QueryPacketCommitmentResponse, + client::v1::{IdentifiedClientState, QueryClientStatesResponse}, + connection::v1::{IdentifiedConnection, State}, + }, + lightclients::tendermint::v1::ClientState, + }; + use prost::Message; + use tonic::transport::Channel; + /// Querier for the Cosmos IBC module + pub struct Ibc { + channel: Channel, + } + impl DaemonQuerier for Ibc { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Ibc { + /// Get the trace of a specific denom + pub async fn denom_trace( + &self, + hash: String, + ) -> Result { + let denom_trace: QueryDenomTraceResponse = { + use crate::cosmos_modules::ibc_transfer::{ + query_client::QueryClient, QueryDenomTraceRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDenomTraceRequest { + hash: hash, + }; + let response = client + .denom_trace(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 32u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(denom_trace.denom_trace.unwrap()) + } + /// Get all the IBC clients for this daemon + pub async fn clients( + &self, + ) -> Result, DaemonError> { + let ibc_clients: QueryClientStatesResponse = { + use crate::cosmos_modules::ibc_client::{ + query_client::QueryClient, QueryClientStatesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryClientStatesRequest { + pagination: None, + }; + let response = client + .client_states(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 45u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_clients.client_states) + } + /// Get the state of a specific IBC client + pub async fn client_state( + &self, + client_id: impl ToString, + ) -> Result< + cosmos_modules::ibc_client::QueryClientStateResponse, + DaemonError, + > { + let response: cosmos_modules::ibc_client::QueryClientStateResponse = { + use crate::cosmos_modules::ibc_client::{ + query_client::QueryClient, QueryClientStateRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryClientStateRequest { + client_id: client_id.to_string(), + }; + let response = client + .client_state(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 60u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(response) + } + /// Get the consensus state of a specific IBC client + 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 = { + use crate::cosmos_modules::ibc_client::{ + query_client::QueryClient, QueryConsensusStatesRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryConsensusStatesRequest { + client_id: client_id, + pagination: None, + }; + let response = client + .consensus_states(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 77u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(response) + } + /// Get the consensus status of a specific IBC client + pub async fn client_status( + &self, + client_id: impl ToString, + ) -> Result< + cosmos_modules::ibc_client::QueryClientStatusResponse, + DaemonError, + > { + let response: cosmos_modules::ibc_client::QueryClientStatusResponse = { + use crate::cosmos_modules::ibc_client::{ + query_client::QueryClient, QueryClientStatusRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryClientStatusRequest { + client_id: client_id.to_string(), + }; + let response = client + .client_status(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 95u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(response) + } + /// Get the ibc client parameters + pub async fn client_params( + &self, + ) -> Result< + cosmos_modules::ibc_client::QueryClientParamsResponse, + DaemonError, + > { + let response: cosmos_modules::ibc_client::QueryClientParamsResponse = { + use crate::cosmos_modules::ibc_client::{ + query_client::QueryClient, QueryClientParamsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryClientParamsRequest {}; + let response = client + .client_params(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 111u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(response) + } + /// Query the IBC connections for a specific chain + pub async fn connections( + &self, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_connection::QueryConnectionsResponse; + let ibc_connections: QueryConnectionsResponse = { + use crate::cosmos_modules::ibc_connection::{ + query_client::QueryClient, QueryConnectionsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryConnectionsRequest { + pagination: None, + }; + let response = client + .connections(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 121u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_connections.connections) + } + /// Search for open connections with a specific chain. + pub async fn open_connections( + &self, + client_chain_id: impl ToString, + ) -> Result, 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) + } + /// Get all the connections for this client + pub async fn client_connections( + &self, + client_id: impl Into, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_connection::QueryClientConnectionsResponse; + let client_id = client_id.into(); + let ibc_client_connections: QueryClientConnectionsResponse = { + use crate::cosmos_modules::ibc_connection::{ + query_client::QueryClient, QueryClientConnectionsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryClientConnectionsRequest { + client_id: client_id.clone(), + }; + let response = client + .client_connections(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 163u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_client_connections.connection_paths) + } + /// Get the (tendermint) client state for a specific connection + pub async fn connection_client( + &self, + connection_id: impl Into, + ) -> Result { + use cosmos_modules::ibc_connection::QueryConnectionClientStateResponse; + let connection_id = connection_id.into(); + let ibc_connection_client: QueryConnectionClientStateResponse = { + use crate::cosmos_modules::ibc_connection::{ + query_client::QueryClient, QueryConnectionClientStateRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryConnectionClientStateRequest { + connection_id: connection_id.clone(), + }; + let response = client + .connection_client_state(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 183u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + let client_state = ibc_connection_client + .identified_client_state + .ok_or( + DaemonError::ibc_err({ + let res = ::alloc::fmt::format( + format_args!( + "error identifying client for connection {0}", + connection_id, + ), + ); + res + }), + )?; + let client_state = ClientState::decode( + client_state.client_state.unwrap().value.as_slice(), + ) + .map_err(|e| DaemonError::ibc_err({ + let res = ::alloc::fmt::format( + format_args!("error decoding client state: {0}", e), + ); + res + }))?; + Ok(client_state) + } + /// Get the channel for a specific port and channel id + pub async fn channel( + &self, + port_id: impl Into, + channel_id: impl Into, + ) -> Result { + use cosmos_modules::ibc_channel::QueryChannelResponse; + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_channel: QueryChannelResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryChannelRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryChannelRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }; + let response = client.channel(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 218u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + ibc_channel + .channel + .ok_or( + DaemonError::ibc_err({ + let res = ::alloc::fmt::format( + format_args!( + "error fetching channel {0} on port {1}", + channel_id, + port_id, + ), + ); + res + }), + ) + } + /// Get all the channels for a specific connection + pub async fn connection_channels( + &self, + connection_id: impl Into, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_channel::QueryConnectionChannelsResponse; + let connection_id = connection_id.into(); + let ibc_connection_channels: QueryConnectionChannelsResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryConnectionChannelsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryConnectionChannelsRequest { + connection: connection_id.clone(), + pagination: None, + }; + let response = client + .connection_channels(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 242u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_connection_channels.channels) + } + /// Get the client state for a specific channel and port + pub async fn channel_client_state( + &self, + port_id: impl Into, + channel_id: impl Into, + ) -> Result { + use cosmos_modules::ibc_channel::QueryChannelClientStateResponse; + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_channel_client_state: QueryChannelClientStateResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryChannelClientStateRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryChannelClientStateRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }; + let response = client + .channel_client_state(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 265u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + ibc_channel_client_state + .identified_client_state + .ok_or( + DaemonError::ibc_err({ + let res = ::alloc::fmt::format( + format_args!( + "error identifying client for channel {0} on port {1}", + channel_id, + port_id, + ), + ); + res + }), + ) + } + /// Get all the packet commitments for a specific channel and port + pub async fn packet_commitments( + &self, + port_id: impl Into, + channel_id: impl Into, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_channel::QueryPacketCommitmentsResponse; + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_commitments: QueryPacketCommitmentsResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryPacketCommitmentsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPacketCommitmentsRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + pagination: None, + }; + let response = client + .packet_commitments(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 297u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_commitments.commitments) + } + /// Get the packet commitment for a specific channel, port and sequence + pub async fn packet_commitment( + &self, + port_id: impl Into, + channel_id: impl Into, + sequence: u64, + ) -> Result { + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_commitment: QueryPacketCommitmentResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryPacketCommitmentRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPacketCommitmentRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + sequence: sequence, + }; + let response = client + .packet_commitment(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 320u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_commitment) + } + /// Returns if the packet is received on the connected chain. + pub async fn packet_receipt( + &self, + port_id: impl Into, + channel_id: impl Into, + sequence: u64, + ) -> Result { + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_receipt: ibc_channel::QueryPacketReceiptResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryPacketReceiptRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPacketReceiptRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + sequence: sequence, + }; + let response = client + .packet_receipt(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 345u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_receipt.received) + } + /// Get all the packet acknowledgements for a specific channel, port and commitment sequences + pub async fn packet_acknowledgements( + &self, + port_id: impl Into, + channel_id: impl Into, + packet_commitment_sequences: Vec, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_channel::QueryPacketAcknowledgementsResponse; + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_acknowledgements: QueryPacketAcknowledgementsResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryPacketAcknowledgementsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPacketAcknowledgementsRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + packet_commitment_sequences: packet_commitment_sequences, + pagination: None, + }; + let response = client + .packet_acknowledgements(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 372u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_acknowledgements.acknowledgements) + } + /// Get the packet acknowledgement for a specific channel, port and sequence + pub async fn packet_acknowledgement( + &self, + port_id: impl Into, + channel_id: impl Into, + sequence: u64, + ) -> Result, DaemonError> { + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_acknowledgement: ibc_channel::QueryPacketAcknowledgementResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryPacketAcknowledgementRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPacketAcknowledgementRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + sequence: sequence, + }; + let response = client + .packet_acknowledgement(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 396u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_acknowledgement.acknowledgement) + } + /// No acknowledgement exists on receiving chain for the given packet commitment sequence on sending chain. + /// Returns the packet sequences that have not yet been received. + pub async fn unreceived_packets( + &self, + port_id: impl Into, + channel_id: impl Into, + packet_commitment_sequences: Vec, + ) -> Result, DaemonError> { + use cosmos_modules::ibc_channel::QueryUnreceivedPacketsResponse; + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_unreceived: QueryUnreceivedPacketsResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryUnreceivedPacketsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryUnreceivedPacketsRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + packet_commitment_sequences: packet_commitment_sequences, + }; + let response = client + .unreceived_packets(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 422u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_unreceived.sequences) + } + /// Returns the acknowledgement sequences that have not yet been received. + /// Given a list of acknowledgement sequences from counterparty, determine if an ack on the counterparty chain has been received on the executing chain. + /// Returns the list of acknowledgement sequences that have not yet been received. + pub async fn unreceived_acks( + &self, + port_id: impl Into, + channel_id: impl Into, + packet_ack_sequences: Vec, + ) -> Result, DaemonError> { + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let ibc_packet_unreceived: ibc_channel::QueryUnreceivedAcksResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryUnreceivedAcksRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryUnreceivedAcksRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + packet_ack_sequences: packet_ack_sequences, + }; + let response = client + .unreceived_acks(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 447u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(ibc_packet_unreceived.sequences) + } + /// Returns the acknowledgement sequences that have not yet been received. + /// Given a list of acknowledgement sequences from counterparty, determine if an ack on the counterparty chain has been received on the executing chain. + /// Returns the list of acknowledgement sequences that have not yet been received. + pub async fn next_sequence_receive( + &self, + port_id: impl Into, + channel_id: impl Into, + ) -> Result { + let port_id = port_id.into(); + let channel_id = channel_id.into(); + let next_receive: ibc_channel::QueryNextSequenceReceiveResponse = { + use crate::cosmos_modules::ibc_channel::{ + query_client::QueryClient, QueryNextSequenceReceiveRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryNextSequenceReceiveRequest { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }; + let response = client + .next_sequence_receive(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::ibc", + "cw_orch_daemon::queriers::ibc", + "cw-orch-daemon/src/queriers/ibc.rs", + ), + 471u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(next_receive.next_sequence_receive) + } + } + } + mod node { + use std::{cmp::min, time::Duration}; + use crate::{cosmos_modules, error::DaemonError, tx_resp::CosmTxResponse}; + use cosmrs::{ + proto::cosmos::{ + base::query::v1beta1::PageRequest, tx::v1beta1::SimulateResponse, + }, + tendermint::{Block, Time}, + }; + use tonic::transport::Channel; + use super::DaemonQuerier; + const MAX_TX_QUERY_RETRIES: usize = 50; + /// Querier for the Tendermint node. + /// Supports queries for block and tx information + pub struct Node { + channel: Channel, + } + impl DaemonQuerier for Node { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Node { + /// Returns node info + pub async fn info( + &self, + ) -> Result { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_node_info(cosmos_modules::tendermint::GetNodeInfoRequest { + }) + .await? + .into_inner(); + Ok(resp) + } + /// Queries node syncing + pub async fn syncing(&self) -> Result { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_syncing(cosmos_modules::tendermint::GetSyncingRequest { + }) + .await? + .into_inner(); + Ok(resp.syncing) + } + /// Returns latests block information + pub async fn latest_block(&self) -> Result { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_latest_block(cosmos_modules::tendermint::GetLatestBlockRequest { + }) + .await? + .into_inner(); + Ok(Block::try_from(resp.block.unwrap())?) + } + /// Returns block information fetched by height + pub async fn block_by_height( + &self, + height: u64, + ) -> Result { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_block_by_height(cosmos_modules::tendermint::GetBlockByHeightRequest { + height: height as i64, + }) + .await? + .into_inner(); + Ok(Block::try_from(resp.block.unwrap())?) + } + /// Return the average block time for the last 50 blocks or since inception + /// This is used to estimate the time when a tx will be included in a block + pub async fn average_block_speed( + &self, + multiplier: Option, + ) -> Result { + let mut latest_block = self.latest_block().await?; + let latest_block_time = latest_block.header.time; + let mut latest_block_height = latest_block.header.height.value(); + while latest_block_height <= 1 { + tokio::time::sleep(Duration::from_secs(1)).await; + latest_block = self.latest_block().await?; + latest_block_height = latest_block.header.height.value(); + } + let avg_period = min(latest_block_height - 1, 50); + let block_avg_period_ago = self + .block_by_height(latest_block_height - avg_period) + .await?; + let block_avg_period_ago_time = block_avg_period_ago.header.time; + let average_block_time = latest_block_time + .duration_since(block_avg_period_ago_time)?; + let average_block_time = average_block_time.as_secs() / avg_period; + let average_block_time = match multiplier { + Some(multiplier) => (average_block_time as f32 * multiplier) as u64, + None => average_block_time, + }; + Ok(std::cmp::max(average_block_time, 1)) + } + /// Returns latests validator set + pub async fn latest_validator_set( + &self, + pagination: Option, + ) -> Result< + cosmos_modules::tendermint::GetLatestValidatorSetResponse, + DaemonError, + > { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_latest_validator_set(cosmos_modules::tendermint::GetLatestValidatorSetRequest { + pagination, + }) + .await? + .into_inner(); + Ok(resp) + } + /// Returns latests validator set fetched by height + pub async fn validator_set_by_height( + &self, + height: i64, + pagination: Option, + ) -> Result< + cosmos_modules::tendermint::GetValidatorSetByHeightResponse, + DaemonError, + > { + let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( + self.channel.clone(), + ); + let resp = client + .get_validator_set_by_height(cosmos_modules::tendermint::GetValidatorSetByHeightRequest { + height, + pagination, + }) + .await? + .into_inner(); + Ok(resp) + } + /// Returns current block height + pub async fn block_height(&self) -> Result { + let block = self.latest_block().await?; + Ok(block.header.height.value()) + } + /// Returns the block timestamp (since unix epoch) in nanos + pub async fn block_time(&self) -> Result { + let block = self.latest_block().await?; + Ok(block.header.time.duration_since(Time::unix_epoch())?.as_nanos()) + } + /// Simulate TX + pub async fn simulate_tx( + &self, + tx_bytes: Vec, + ) -> Result { + let mut client = cosmos_modules::tx::service_client::ServiceClient::new( + self.channel.clone(), + ); + #[allow(deprecated)] + let resp: SimulateResponse = client + .simulate(cosmos_modules::tx::SimulateRequest { + tx: None, + tx_bytes, + }) + .await? + .into_inner(); + let gas_used = resp.gas_info.unwrap().gas_used; + Ok(gas_used) + } + /// Returns all the block info + pub async fn block_info( + &self, + ) -> Result { + let block = self.latest_block().await?; + let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; + let time = cosmwasm_std::Timestamp::from_nanos( + since_epoch.as_nanos() as u64, + ); + Ok(cosmwasm_std::BlockInfo { + height: block.header.height.value(), + time, + chain_id: block.header.chain_id.to_string(), + }) + } + /// Find TX by hash + pub async fn find_tx( + &self, + hash: String, + ) -> Result { + self.find_tx_with_retries(hash, MAX_TX_QUERY_RETRIES).await + } + /// Find TX by hash with a given amount of retries + pub async fn find_tx_with_retries( + &self, + hash: String, + retries: usize, + ) -> Result { + let mut client = cosmos_modules::tx::service_client::ServiceClient::new( + self.channel.clone(), + ); + let request = cosmos_modules::tx::GetTxRequest { + hash: hash.clone(), + }; + let mut block_speed = self.average_block_speed(Some(0.7)).await?; + for _ in 0..retries { + match client.get_tx(request.clone()).await { + Ok(tx) => { + let resp = tx.into_inner().tx_response.unwrap(); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("TX found: {0:?}", resp), + lvl, + &( + "cw_orch_daemon::queriers::node", + "cw_orch_daemon::queriers::node", + "cw-orch-daemon/src/queriers/node.rs", + ), + 220u32, + ::log::__private_api::Option::None, + ); + } + }; + return Ok(resp.into()); + } + Err(err) => { + block_speed = (block_speed as f64 * 1.6) as u64; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("TX not found with error: {0:?}", err), + lvl, + &( + "cw_orch_daemon::queriers::node", + "cw_orch_daemon::queriers::node", + "cw-orch-daemon/src/queriers/node.rs", + ), + 226u32, + ::log::__private_api::Option::None, + ); + } + }; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Waiting {0} seconds", block_speed), + lvl, + &( + "cw_orch_daemon::queriers::node", + "cw_orch_daemon::queriers::node", + "cw-orch-daemon/src/queriers/node.rs", + ), + 227u32, + ::log::__private_api::Option::None, + ); + } + }; + tokio::time::sleep(Duration::from_secs(block_speed)).await; + } + } + } + Err(DaemonError::TXNotFound(hash, retries)) + } + } + } + mod staking { + use crate::{cosmos_modules, error::DaemonError}; + use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; + use tonic::transport::Channel; + use super::DaemonQuerier; + /// Querier for the Cosmos Staking module + pub struct Staking { + channel: Channel, + } + impl DaemonQuerier for Staking { + fn new(channel: Channel) -> Self { + Self { channel } + } + } + impl Staking { + /// Queries validator info for given validator address + pub async fn validator( + &self, + validator_addr: impl Into, + ) -> Result { + let validator: cosmos_modules::staking::QueryValidatorResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryValidatorRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryValidatorRequest { + validator_addr: validator_addr.into(), + }; + let response = client.validator(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 24u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(validator.validator.unwrap()) + } + /// Queries all validators that match the given status + /// + /// see [StakingBondStatus] for available statuses + pub async fn validators( + &self, + status: StakingBondStatus, + ) -> Result, DaemonError> { + let validators: cosmos_modules::staking::QueryValidatorsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryValidatorsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryValidatorsRequest { + status: status.to_string(), + pagination: None, + }; + let response = client + .validators(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 42u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(validators.validators) + } + /// Query validator delegations info for given validator + /// + /// see [PageRequest] for pagination + pub async fn delegations( + &self, + validator_addr: impl Into, + pagination: Option, + ) -> Result, DaemonError> { + let validator_delegations: cosmos_modules::staking::QueryValidatorDelegationsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryValidatorDelegationsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryValidatorDelegationsRequest { + validator_addr: validator_addr.into(), + pagination: pagination, + }; + let response = client + .validator_delegations(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 62u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(validator_delegations.delegation_responses) + } + /// Query validator unbonding delegations of a validator + pub async fn unbonding_delegations( + &self, + validator_addr: impl Into, + ) -> Result, DaemonError> { + let validator_unbonding_delegations: cosmos_modules::staking::QueryValidatorUnbondingDelegationsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, + QueryValidatorUnbondingDelegationsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryValidatorUnbondingDelegationsRequest { + validator_addr: validator_addr.into(), + pagination: None, + }; + let response = client + .validator_unbonding_delegations(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 79u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(validator_unbonding_delegations.unbonding_responses) + } + /// Query delegation info for given validator for a delegator + pub async fn delegation( + &self, + validator_addr: impl Into, + delegator_addr: impl Into, + ) -> Result { + let delegation: cosmos_modules::staking::QueryDelegationResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryDelegationRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDelegationRequest { + validator_addr: validator_addr.into(), + delegator_addr: delegator_addr.into(), + }; + let response = client + .delegation(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 97u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(delegation.delegation_response.unwrap()) + } + /// Query unbonding delegation info for given validator delegator + pub async fn unbonding_delegation( + &self, + validator_addr: impl Into, + delegator_addr: impl Into, + ) -> Result { + let unbonding_delegation: cosmos_modules::staking::QueryUnbondingDelegationResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryUnbondingDelegationRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryUnbondingDelegationRequest { + validator_addr: validator_addr.into(), + delegator_addr: delegator_addr.into(), + }; + let response = client + .unbonding_delegation(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 115u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(unbonding_delegation.unbond.unwrap()) + } + /// Query all delegator delegations of a given delegator address + /// + /// see [PageRequest] for pagination + pub async fn delegator_delegations( + &self, + delegator_addr: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::staking::QueryDelegatorDelegationsResponse, + DaemonError, + > { + let delegator_delegations: cosmos_modules::staking::QueryDelegatorDelegationsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryDelegatorDelegationsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDelegatorDelegationsRequest { + delegator_addr: delegator_addr.into(), + pagination: pagination, + }; + let response = client + .delegator_delegations(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 135u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(delegator_delegations) + } + /// Queries all unbonding delegations of a given delegator address. + /// + /// see [PageRequest] for pagination + pub async fn delegator_unbonding_delegations( + &self, + delegator_addr: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::staking::QueryDelegatorUnbondingDelegationsResponse, + DaemonError, + > { + let delegator_unbonding_delegations: cosmos_modules::staking::QueryDelegatorUnbondingDelegationsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, + QueryDelegatorUnbondingDelegationsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDelegatorUnbondingDelegationsRequest { + delegator_addr: delegator_addr.into(), + pagination: pagination, + }; + let response = client + .delegator_unbonding_delegations(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 156u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(delegator_unbonding_delegations) + } + /// Query redelegations of a given address + /// + /// see [PageRequest] for pagination + pub async fn redelegations( + &self, + delegator_addr: impl Into, + src_validator_addr: impl Into, + dst_validator_addr: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::staking::QueryRedelegationsResponse, + DaemonError, + > { + let redelegations: cosmos_modules::staking::QueryRedelegationsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryRedelegationsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryRedelegationsRequest { + delegator_addr: delegator_addr.into(), + src_validator_addr: src_validator_addr.into(), + dst_validator_addr: dst_validator_addr.into(), + pagination: pagination, + }; + let response = client + .redelegations(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 178u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(redelegations) + } + /// Query delegator validators info for given delegator address. + pub async fn delegator_validator( + &self, + validator_addr: impl Into, + delegator_addr: impl Into, + ) -> Result< + cosmos_modules::staking::QueryDelegatorValidatorResponse, + DaemonError, + > { + let delegator_validator: cosmos_modules::staking::QueryDelegatorValidatorResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryDelegatorValidatorRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDelegatorValidatorRequest { + validator_addr: validator_addr.into(), + delegator_addr: delegator_addr.into(), + }; + let response = client + .delegator_validator(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 198u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(delegator_validator) + } + /// Query delegator validators info for given delegator address + /// + /// see [PageRequest] for pagination + pub async fn delegator_validators( + &self, + delegator_addr: impl Into, + pagination: Option, + ) -> Result< + cosmos_modules::staking::QueryDelegatorValidatorsResponse, + DaemonError, + > { + let delegator_validators: cosmos_modules::staking::QueryDelegatorValidatorsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryDelegatorValidatorsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryDelegatorValidatorsRequest { + delegator_addr: delegator_addr.into(), + pagination: pagination, + }; + let response = client + .delegator_validators(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 218u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(delegator_validators) + } + /// Query historical info info for given height + pub async fn historical_info( + &self, + height: i64, + ) -> Result< + cosmos_modules::staking::QueryHistoricalInfoResponse, + DaemonError, + > { + let historical_info: cosmos_modules::staking::QueryHistoricalInfoResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryHistoricalInfoRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryHistoricalInfoRequest { + height: height, + }; + let response = client + .historical_info(request.clone()) + .await? + .into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 236u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(historical_info) + } + /// Query the pool info + pub async fn pool( + &self, + ) -> Result { + let pool: cosmos_modules::staking::QueryPoolResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryPoolRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryPoolRequest {}; + let response = client.pool(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 248u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(pool) + } + /// Query staking parameters + pub async fn params( + &self, + ) -> Result { + let params: cosmos_modules::staking::QueryParamsResponse = { + use crate::cosmos_modules::staking::{ + query_client::QueryClient, QueryParamsRequest, + }; + let mut client = QueryClient::new(self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = QueryParamsRequest {}; + let response = client.params(request.clone()).await?.into_inner(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "cosmos_query: {0:?} resulted in: {1:?}", + request, + response, + ), + lvl, + &( + "cw_orch_daemon::queriers::staking", + "cw_orch_daemon::queriers::staking", + "cw-orch-daemon/src/queriers/staking.rs", + ), + 257u32, + ::log::__private_api::Option::None, + ); + } + }; + response + }; + Ok(params) + } + } + /// Staking bond statuses + pub enum StakingBondStatus { + /// UNSPECIFIED defines an invalid validator status. + Unspecified = 0, + /// UNBONDED defines a validator that is not bonded. + Unbonded = 1, + /// UNBONDING defines a validator that is unbonding. + Unbonding = 2, + /// BONDED defines a validator that is bonded. + Bonded = 3, + } + impl ToString for StakingBondStatus { + /// Convert to string + fn to_string(&self) -> String { + match self { + StakingBondStatus::Unspecified => { + "BOND_STATUS_UNSPECIFIED".to_string() + } + StakingBondStatus::Unbonded => "BOND_STATUS_UNBONDED".to_string(), + StakingBondStatus::Unbonding => "BOND_STATUS_UNBONDING".to_string(), + StakingBondStatus::Bonded => "BOND_STATUS_BONDED".to_string(), + } + } + } + } + pub use bank::Bank; + pub use cosmwasm::CosmWasm; + pub use feegrant::Feegrant; + pub use ibc::Ibc; + pub use node::Node; + pub use gov::*; + pub use staking::*; + use tonic::transport::Channel; + /// Constructor for a querier over a given channel + pub trait DaemonQuerier { + /// Construct an new querier over a given channel + fn new(channel: Channel) -> Self; + } +} +mod traits { + use cw_orch_core::{ + contract::interface_traits::{CwOrchMigrate, CwOrchUpload}, + environment::TxResponse, + }; + use crate::{queriers::CosmWasm, Daemon, DaemonError}; + /// Helper methods for conditional uploading of a contract. + pub trait ConditionalUpload: CwOrchUpload { + /// Only upload the contract if it is not uploaded yet (checksum does not match) + fn upload_if_needed(&self) -> Result>, DaemonError> { + if self.latest_is_uploaded()? { + Ok(None) + } else { + Some(self.upload()).transpose().map_err(Into::into) + } + } + /// Returns whether the checksum of the WASM file matches the checksum of the latest uploaded code for this contract. + fn latest_is_uploaded(&self) -> Result { + let Some(latest_uploaded_code_id) = self.code_id().ok() else { + return Ok(false); + }; + let chain = self.get_chain(); + let on_chain_hash = chain + .rt_handle + .block_on( + chain + .query_client::() + .code_id_hash(latest_uploaded_code_id), + )?; + let local_hash = self.wasm().checksum()?; + Ok(local_hash == on_chain_hash) + } + /// Returns whether the contract is running the latest uploaded code for it + fn is_running_latest(&self) -> Result { + let Some(latest_uploaded_code_id) = self.code_id().ok() else { + return Ok(false); + }; + let chain = self.get_chain(); + let info = chain + .rt_handle + .block_on( + chain.query_client::().contract_info(self.address()?), + )?; + Ok(latest_uploaded_code_id == info.code_id) + } + } + impl ConditionalUpload for T + where + T: CwOrchUpload, + {} + /// Helper methods for conditional migration of a contract. + pub trait ConditionalMigrate: CwOrchMigrate + ConditionalUpload { + /// Only migrate the contract if it is not on the latest code-id yet + fn migrate_if_needed( + &self, + migrate_msg: &Self::MigrateMsg, + ) -> Result>, DaemonError> { + if self.is_running_latest()? { + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "{0} is already running the latest code", + self.id(), + ), + lvl, + &( + "cw_orch_daemon::traits", + "cw_orch_daemon::traits", + "cw-orch-daemon/src/traits.rs", + ), + 61u32, + ::log::__private_api::Option::None, + ); + } + }; + Ok(None) + } else { + Some(self.migrate(migrate_msg, self.code_id()?)) + .transpose() + .map_err(Into::into) + } + } + /// Uploads the contract if the local contract hash is different from the latest on-chain code hash. + /// Proceeds to migrates the contract if the contract is not running the latest code. + fn upload_and_migrate_if_needed( + &self, + migrate_msg: &Self::MigrateMsg, + ) -> Result>>, DaemonError> { + let mut txs = Vec::with_capacity(2); + if let Some(tx) = self.upload_if_needed()? { + txs.push(tx); + } + if let Some(tx) = self.migrate_if_needed(migrate_msg)? { + txs.push(tx); + } + if txs.is_empty() { Ok(None) } else { Ok(Some(txs)) } + } + } + impl ConditionalMigrate for T + where + T: CwOrchMigrate + CwOrchUpload, + {} +} +pub mod tx_builder { + use cosmrs::tx::{ModeInfo, SignMode}; + use cosmrs::{ + proto::cosmos::auth::v1beta1::BaseAccount, tendermint::chain::Id, + tx::{self, Body, Fee, Msg, Raw, SequenceNumber, SignDoc, SignerInfo}, + Any, Coin, + }; + use secp256k1::All; + use super::{sender::Sender, DaemonError}; + const GAS_BUFFER: f64 = 1.3; + const BUFFER_THRESHOLD: u64 = 200_000; + const SMALL_GAS_BUFFER: f64 = 1.4; + /// Struct used to build a raw transaction and broadcast it with a sender. + pub struct TxBuilder { + pub(crate) body: Body, + pub(crate) fee_amount: Option, + pub(crate) gas_limit: Option, + pub(crate) sequence: Option, + } + #[automatically_derived] + impl ::core::clone::Clone for TxBuilder { + #[inline] + fn clone(&self) -> TxBuilder { + TxBuilder { + body: ::core::clone::Clone::clone(&self.body), + fee_amount: ::core::clone::Clone::clone(&self.fee_amount), + gas_limit: ::core::clone::Clone::clone(&self.gas_limit), + sequence: ::core::clone::Clone::clone(&self.sequence), + } + } + } + #[automatically_derived] + impl ::core::fmt::Debug for TxBuilder { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field4_finish( + f, + "TxBuilder", + "body", + &self.body, + "fee_amount", + &self.fee_amount, + "gas_limit", + &self.gas_limit, + "sequence", + &&self.sequence, + ) + } + } + impl TxBuilder { + /// Create a new TxBuilder with a given body. + pub fn new(body: Body) -> Self { + Self { + body, + fee_amount: None, + gas_limit: None, + sequence: None, + } + } + /// Set a fixed fee amount for the tx + pub fn fee_amount(&mut self, fee_amount: u128) -> &mut Self { + self.fee_amount = Some(fee_amount); + self + } + /// Set a gas limit for the tx + pub fn gas_limit(&mut self, gas_limit: u64) -> &mut Self { + self.gas_limit = Some(gas_limit); + self + } + /// Set a sequence number for the tx + pub fn sequence(&mut self, sequence: u64) -> &mut Self { + self.sequence = Some(sequence); + self + } + /// Builds the body of the tx with a given memo and timeout. + pub fn build_body( + msgs: Vec, + memo: Option<&str>, + timeout: u64, + ) -> tx::Body { + let msgs = msgs + .into_iter() + .map(Msg::into_any) + .collect::, _>>() + .unwrap(); + tx::Body::new(msgs, memo.unwrap_or_default(), timeout as u32) + } + pub(crate) fn build_fee( + amount: impl Into, + denom: &str, + gas_limit: u64, + ) -> Fee { + let fee = Coin::new(amount.into(), denom).unwrap(); + Fee::from_amount_and_gas(fee, gas_limit) + } + /// Builds the raw tx with a given body and fee and signs it. + /// Sets the TxBuilder's gas limit to its simulated amount for later use. + pub async fn build(&mut self, wallet: &Sender) -> Result { + let BaseAccount { account_number, sequence, .. } = wallet + .base_account() + .await?; + let sequence = self.sequence.unwrap_or(sequence); + let (tx_fee, gas_limit) = if let (Some(fee), Some(gas_limit)) + = (self.fee_amount, self.gas_limit) { + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Using pre-defined fee and gas limits: {0}, {1}", + fee, + gas_limit, + ), + lvl, + &( + "cw_orch_daemon::tx_builder", + "cw_orch_daemon::tx_builder", + "cw-orch-daemon/src/tx_builder.rs", + ), + 91u32, + ::log::__private_api::Option::None, + ); + } + }; + (fee, gas_limit) + } else { + let sim_gas_used = wallet + .calculate_gas(&self.body, sequence, account_number) + .await?; + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Simulated gas needed {0:?}", sim_gas_used), + lvl, + &( + "cw_orch_daemon::tx_builder", + "cw_orch_daemon::tx_builder", + "cw-orch-daemon/src/tx_builder.rs", + ), + 101u32, + ::log::__private_api::Option::None, + ); + } + }; + let gas_expected = if sim_gas_used < BUFFER_THRESHOLD { + sim_gas_used as f64 * SMALL_GAS_BUFFER + } else { + sim_gas_used as f64 * GAS_BUFFER + }; + let fee_amount = gas_expected + * (wallet + .daemon_state + .chain_data + .fees + .fee_tokens[0] + .fixed_min_gas_price + 0.00001); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!("Calculated fee needed: {0:?}", fee_amount), + lvl, + &( + "cw_orch_daemon::tx_builder", + "cw_orch_daemon::tx_builder", + "cw-orch-daemon/src/tx_builder.rs", + ), + 111u32, + ::log::__private_api::Option::None, + ); + } + }; + self.gas_limit = Some(gas_expected as u64); + (fee_amount as u128, gas_expected as u64) + }; + let fee = Self::build_fee( + tx_fee, + &wallet.daemon_state.chain_data.fees.fee_tokens[0].denom, + gas_limit, + ); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "submitting tx: \n fee: {0:?}\naccount_nr: {1:?}\nsequence: {2:?}", + fee, + account_number, + sequence, + ), + lvl, + &( + "cw_orch_daemon::tx_builder", + "cw_orch_daemon::tx_builder", + "cw-orch-daemon/src/tx_builder.rs", + ), + 125u32, + ::log::__private_api::Option::None, + ); + } + }; + let auth_info = SignerInfo { + public_key: wallet.private_key.get_signer_public_key(&wallet.secp), + mode_info: ModeInfo::single(SignMode::Direct), + sequence, + } + .auth_info(fee); + let sign_doc = SignDoc::new( + &self.body, + &auth_info, + &Id::try_from(wallet.daemon_state.chain_data.chain_id.to_string())?, + account_number, + )?; + wallet.sign(sign_doc).map_err(Into::into) + } + } +} +pub use self::{ + builder::*, channel::*, core::*, error::*, state::*, sync::*, traits::*, tx_resp::*, +}; +pub use cw_orch_networks::chain_info::*; +pub use cw_orch_networks::networks; +pub use sender::Wallet; +pub use tx_builder::TxBuilder; +pub(crate) mod cosmos_modules { + pub use cosmrs::proto::{ + cosmos::{ + auth::v1beta1 as auth, authz::v1beta1 as authz, bank::v1beta1 as bank, + base::{ + abci::v1beta1 as abci, tendermint::v1beta1 as tendermint, v1beta1 as base, + }, + crisis::v1beta1 as crisis, distribution::v1beta1 as distribution, + evidence::v1beta1 as evidence, feegrant::v1beta1 as feegrant, + gov::v1beta1 as gov, mint::v1beta1 as mint, params::v1beta1 as params, + slashing::v1beta1 as slashing, staking::v1beta1 as staking, + tx::v1beta1 as tx, vesting::v1beta1 as vesting, + }, + cosmwasm::wasm::v1 as cosmwasm, + ibc::{ + applications::transfer::v1 as ibc_transfer, + core::{ + channel::v1 as ibc_channel, client::v1 as ibc_client, + connection::v1 as ibc_connection, + }, + }, + tendermint::abci as tendermint_abci, + }; +} +/// Re-export trait and data required to fetch daemon data from chain-registry +pub use ibc_chain_registry::{ + chain::ChainData as ChainRegistryData, fetchable::Fetchable, +}; diff --git a/cw-orch/tests/rpc-querier.rs b/cw-orch-daemon/tests/rpc-querier.rs similarity index 96% rename from cw-orch/tests/rpc-querier.rs rename to cw-orch-daemon/tests/rpc-querier.rs index f6ca02d78..963e47677 100644 --- a/cw-orch/tests/rpc-querier.rs +++ b/cw-orch-daemon/tests/rpc-querier.rs @@ -36,5 +36,5 @@ fn temp_test() { let balance_response = QueryAllBalancesResponse::decode(response.value.as_slice()).unwrap(); // Printing the response - panic!("{:?}", response); + panic!("{:?}", balance_response); } From f48f4fad925408a00a7caba7575cb42cc8438e2e Mon Sep 17 00:00:00 2001 From: Kayanski Date: Sat, 9 Sep 2023 16:47:39 +0200 Subject: [PATCH 3/8] Implemented every with rpc/grpc --- cw-orch-daemon/Cargo.toml | 11 +- cw-orch-daemon/src/core.rs | 32 +- cw-orch-daemon/src/error.rs | 5 + .../src/{channel.rs => grpc_channel.rs} | 0 cw-orch-daemon/src/lib.rs | 17 +- cw-orch-daemon/src/live_mock.rs | 16 +- cw-orch-daemon/src/queriers.rs | 76 +- cw-orch-daemon/src/queriers/grpc/auth.rs | 33 + .../src/queriers/{ => grpc}/bank.rs | 2 +- .../src/queriers/{ => grpc}/cosmwasm.rs | 2 +- .../src/queriers/{ => grpc}/feegrant.rs | 2 +- cw-orch-daemon/src/queriers/{ => grpc}/gov.rs | 2 +- cw-orch-daemon/src/queriers/{ => grpc}/ibc.rs | 2 +- cw-orch-daemon/src/queriers/grpc/mod.rs | 68 + .../src/queriers/{ => grpc}/node.rs | 7 +- .../src/queriers/{ => grpc}/staking.rs | 2 +- cw-orch-daemon/src/queriers/grpc/tx.rs | 42 + cw-orch-daemon/src/queriers/rpc/auth.rs | 35 + .../{rpc_queriers => queriers/rpc}/bank.rs | 11 +- cw-orch-daemon/src/queriers/rpc/cosmwasm.rs | 229 + .../src/{rpc_queriers => queriers/rpc}/mod.rs | 27 +- cw-orch-daemon/src/queriers/rpc/node.rs | 244 + cw-orch-daemon/src/queriers/rpc/staking.rs | 295 + cw-orch-daemon/src/queriers/rpc/tx.rs | 38 + cw-orch-daemon/src/rpc_channel.rs | 103 + cw-orch-daemon/src/sender.rs | 35 +- cw-orch-daemon/src/state.rs | 32 +- cw-orch-daemon/src/sync/core.rs | 10 +- cw-orch-daemon/src/test.rs | 8364 ----- cw-orch-daemon/test | 25355 ---------------- 30 files changed, 1226 insertions(+), 33871 deletions(-) rename cw-orch-daemon/src/{channel.rs => grpc_channel.rs} (100%) create mode 100644 cw-orch-daemon/src/queriers/grpc/auth.rs rename cw-orch-daemon/src/queriers/{ => grpc}/bank.rs (99%) rename cw-orch-daemon/src/queriers/{ => grpc}/cosmwasm.rs (99%) rename cw-orch-daemon/src/queriers/{ => grpc}/feegrant.rs (97%) rename cw-orch-daemon/src/queriers/{ => grpc}/gov.rs (99%) rename cw-orch-daemon/src/queriers/{ => grpc}/ibc.rs (99%) create mode 100644 cw-orch-daemon/src/queriers/grpc/mod.rs rename cw-orch-daemon/src/queriers/{ => grpc}/node.rs (98%) rename cw-orch-daemon/src/queriers/{ => grpc}/staking.rs (99%) create mode 100644 cw-orch-daemon/src/queriers/grpc/tx.rs create mode 100644 cw-orch-daemon/src/queriers/rpc/auth.rs rename cw-orch-daemon/src/{rpc_queriers => queriers/rpc}/bank.rs (95%) create mode 100644 cw-orch-daemon/src/queriers/rpc/cosmwasm.rs rename cw-orch-daemon/src/{rpc_queriers => queriers/rpc}/mod.rs (76%) create mode 100644 cw-orch-daemon/src/queriers/rpc/node.rs create mode 100644 cw-orch-daemon/src/queriers/rpc/staking.rs create mode 100644 cw-orch-daemon/src/queriers/rpc/tx.rs create mode 100644 cw-orch-daemon/src/rpc_channel.rs delete mode 100644 cw-orch-daemon/src/test.rs delete mode 100644 cw-orch-daemon/test diff --git a/cw-orch-daemon/Cargo.toml b/cw-orch-daemon/Cargo.toml index 71fb3e906..154bb5a85 100644 --- a/cw-orch-daemon/Cargo.toml +++ b/cw-orch-daemon/Cargo.toml @@ -17,11 +17,16 @@ exclude = [".env"] all-features = true [features] -default = ["node-tests"] +default = ["node-tests", "grpc"] # enable node-backed tests (ensure Docker is running) # run with `cargo test --jobs 1 --features node-tests` node-tests = [] eth = ["dep:ethers-signers", "dep:ethers-core"] +rpc = ["cosmrs/rpc"] +grpc = ["cosmrs/grpc", "dep:tonic"] + + + [dependencies] # Default deps cw-orch-contract-derive = { workspace = true } @@ -47,7 +52,7 @@ hex = { version = "0.4.3" } ripemd = { version = "0.1.3" } ibc-chain-registry = { workspace = true } tokio = { version = "1.4", features = ["full"] } -tonic = { version = "0.9.2", features = ["tls", "tls-roots"] } +tonic = { version = "0.9.2", features = ["tls", "tls-roots"], optional = true } secp256k1 = { version = "0.27.0", default-features = false } reqwest = { version = "0.11.9" } base64 = { version = "0.21.0" } @@ -55,7 +60,7 @@ hkd32 = { version = "0.7.0", features = ["bip39", "mnemonic", "bech32"] } rand_core = { version = "0.6.4", default-features = false } ed25519-dalek = { version = "2", features = ["serde"] } eyre = { version = "0.6" } -cosmrs = { version = "0.14.0", features = ["dev", "cosmwasm", "grpc" ,"rpc"] } +cosmrs = { version = "0.14.0", features = ["dev", "cosmwasm"] } chrono = { version = "0.4" } base16 = { version = "0.2.1" } derive_builder = { version = "0.12.0" } diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index a9502b413..0afe802bf 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -1,10 +1,9 @@ -use crate::{queriers::CosmWasm, DaemonState}; +use crate::DaemonState; use super::{ builder::DaemonAsyncBuilder, - cosmos_modules, error::DaemonError, - queriers::{DaemonQuerier, Node}, + queriers::{DaemonQuerier, Node, CosmWasm}, sender::Wallet, tx_resp::CosmTxResponse, }; @@ -28,7 +27,6 @@ use std::{ time::Duration, }; -use tonic::transport::Channel; #[derive(Clone)] /** @@ -75,8 +73,13 @@ impl DaemonAsync { } /// Get the channel configured for this DaemonAsync. - pub fn channel(&self) -> Channel { - self.state.grpc_channel.clone() + #[cfg(feature="grpc")] + pub fn channel(&self) -> tonic::transport::Channel { + self.state.transport_channel.clone() + } + #[cfg(feature="rpc")] + pub fn channel(&self) -> cosmrs::rpc::HttpClient { + self.state.transport_channel.clone() } } @@ -138,21 +141,16 @@ impl DaemonAsync { } /// Query a contract. - pub async fn query( + pub async fn query( &self, query_msg: &Q, contract_address: &Addr, - ) -> Result { - let sender = &self.sender; - let mut client = cosmos_modules::cosmwasm::query_client::QueryClient::new(sender.channel()); - let resp = client - .smart_contract_state(cosmos_modules::cosmwasm::QuerySmartContractStateRequest { - address: contract_address.to_string(), - query_data: serde_json::to_vec(&query_msg)?, - }) - .await?; + ) -> Result { + let querier = CosmWasm::new(self.channel()); + + let resp: Vec = querier.contract_state(contract_address, serde_json::to_vec(&query_msg)?).await?; - Ok(from_str(from_utf8(&resp.into_inner().data).unwrap())?) + Ok(from_str(from_utf8(&resp).unwrap())?) } /// Migration a contract. diff --git a/cw-orch-daemon/src/error.rs b/cw-orch-daemon/src/error.rs index 74feef155..756320a24 100644 --- a/cw-orch-daemon/src/error.rs +++ b/cw-orch-daemon/src/error.rs @@ -19,10 +19,15 @@ pub enum DaemonError { VarError(#[from] ::std::env::VarError), #[error(transparent)] AnyError(#[from] ::anyhow::Error), + + #[cfg(feature="grpc")] #[error(transparent)] Status(#[from] ::tonic::Status), + + #[cfg(feature="grpc")] #[error(transparent)] TransportError(#[from] ::tonic::transport::Error), + #[error(transparent)] TendermintError(#[from] ::cosmrs::tendermint::Error), #[error(transparent)] diff --git a/cw-orch-daemon/src/channel.rs b/cw-orch-daemon/src/grpc_channel.rs similarity index 100% rename from cw-orch-daemon/src/channel.rs rename to cw-orch-daemon/src/grpc_channel.rs diff --git a/cw-orch-daemon/src/lib.rs b/cw-orch-daemon/src/lib.rs index f1fc4246e..c46e637eb 100644 --- a/cw-orch-daemon/src/lib.rs +++ b/cw-orch-daemon/src/lib.rs @@ -1,9 +1,7 @@ //! `Daemon` and `DaemonAsync` execution environments. //! //! The `Daemon` type is a synchronous wrapper around the `DaemonAsync` type and can be used as a contract execution environment. -pub mod rpc_queriers; pub mod builder; -pub mod channel; pub mod core; pub mod error; pub(crate) mod json_file; @@ -16,12 +14,25 @@ pub mod tx_resp; // expose these as mods as they can grow pub mod keys; pub mod live_mock; + pub mod queriers; + +#[cfg(feature="rpc")] +pub mod rpc_channel; +#[cfg(feature="rpc")] +pub use self::rpc_channel::*; + +#[cfg(feature="grpc")] +pub mod grpc_channel; +#[cfg(feature="grpc")] +pub use self::grpc_channel::*; + + mod traits; pub mod tx_builder; pub use self::{ - builder::*, channel::*, core::*, error::*, state::*, sync::*, traits::*, tx_resp::*, + builder::*, core::*, error::*, state::*, sync::*, traits::*, tx_resp::*, }; pub use cw_orch_networks::chain_info::*; pub use cw_orch_networks::networks; diff --git a/cw-orch-daemon/src/live_mock.rs b/cw-orch-daemon/src/live_mock.rs index ff0b564ba..9632d3526 100644 --- a/cw-orch-daemon/src/live_mock.rs +++ b/cw-orch-daemon/src/live_mock.rs @@ -1,10 +1,8 @@ //! Live mock is a mock that uses a live chain to query for data. //! It can be used to do chain-backed unit-testing. It can't be used for state-changing operations. -use crate::queriers::Bank; -use crate::queriers::CosmWasm; -use crate::queriers::DaemonQuerier; -use crate::queriers::Staking; +use crate::create_transport_channel; +use crate::queriers::{DaemonQuerier, Staking, Bank, CosmWasm}; use cosmwasm_std::Addr; use cosmwasm_std::AllBalanceResponse; use cosmwasm_std::BalanceResponse; @@ -17,7 +15,6 @@ use cosmwasm_std::Empty; use cosmwasm_std::StakingQuery; use ibc_chain_registry::chain::ChainData; use tokio::runtime::Runtime; -use tonic::transport::Channel; use std::marker::PhantomData; use std::str::FromStr; @@ -28,8 +25,6 @@ use cosmwasm_std::{ SystemError, SystemResult, Uint128, WasmQuery, }; -use crate::channel::GrpcChannel; - fn to_cosmwasm_coin(c: cosmrs::proto::cosmos::base::v1beta1::Coin) -> Coin { Coin { amount: Uint128::from_str(&c.amount).unwrap(), @@ -57,7 +52,10 @@ pub fn mock_dependencies( /// Querier struct that fetches queries on-chain directly pub struct WasmMockQuerier { - channel: Channel, + #[cfg(feature="grpc")] + channel: tonic::transport::Channel, + #[cfg(feature="rpc")] + channel: cosmrs::rpc::HttpClient, runtime: Runtime, } @@ -208,7 +206,7 @@ impl WasmMockQuerier { let rt = Runtime::new().unwrap(); let channel = rt - .block_on(GrpcChannel::connect(&chain.apis.grpc, &chain.chain_id)) + .block_on(create_transport_channel(&chain)) .unwrap(); WasmMockQuerier { diff --git a/cw-orch-daemon/src/queriers.rs b/cw-orch-daemon/src/queriers.rs index 1ad770119..3c69ae828 100644 --- a/cw-orch-daemon/src/queriers.rs +++ b/cw-orch-daemon/src/queriers.rs @@ -1,71 +1,23 @@ -//! # DaemonQuerier -//! -//! DaemonAsync queriers are gRPC query clients for the CosmosSDK modules. They can be used to query the different modules (Bank, Ibc, Authz, ...). -//! -//! ## Usage -//! -//! You will need to acquire a [gRPC channel](Channel) to a running CosmosSDK node to be able to use the queriers. -//! Here is an example of how to acquire one using the DaemonAsync builder. -//! -//! ```no_run -//! // require the querier you want to use, in this case Node -//! use cw_orch_daemon::{queriers::Node, DaemonAsync, networks, queriers::DaemonQuerier}; -//! # tokio_test::block_on(async { -//! // call the builder and configure it as you need -//! let daemon = DaemonAsync::builder() -//! .chain(networks::LOCAL_JUNO) -//! .build() -//! .await.unwrap(); -//! // now you can use the Node querier: -//! let node = Node::new(daemon.channel()); -//! let node_info = node.info(); -//! # }) -//! ``` -/// macro for constructing and performing a query on a CosmosSDK module. -#[macro_export] -macro_rules! cosmos_query { - ($self:ident, $module:ident, $func_name:ident, $request_type:ident { $($field:ident : $value:expr),* $(,)? }) => { - { - use $crate::cosmos_modules::$module::{ - query_client::QueryClient, $request_type, - }; - let mut client = QueryClient::new($self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = $request_type { $($field : $value),* }; - let response = client.$func_name(request.clone()).await?.into_inner(); - ::log::trace!( - "cosmos_query: {:?} resulted in: {:?}", - request, - response - ); - response - } -}; -} -mod bank; -mod cosmwasm; -mod feegrant; -mod gov; -mod ibc; -mod node; -mod staking; +pub const MAX_TX_QUERY_RETRIES: usize = 50; -pub use bank::Bank; -pub use cosmwasm::CosmWasm; -pub use feegrant::Feegrant; -pub use ibc::Ibc; -pub use node::Node; +#[cfg(feature="rpc")] +pub mod rpc; +#[cfg(feature="rpc")] +pub use rpc::*; +#[cfg(feature="grpc")] +pub mod grpc; +#[cfg(feature="grpc")] +pub use grpc::*; -// this two containt structs that are helpers for the queries -pub use gov::*; -pub use staking::*; -use tonic::transport::Channel; /// Constructor for a querier over a given channel pub trait DaemonQuerier { /// Construct an new querier over a given channel - fn new(channel: Channel) -> Self; -} + #[cfg(feature="rpc")] + fn new(client: cosmrs::rpc::HttpClient) -> Self; + #[cfg(feature="grpc")] + fn new(channel: tonic::transport::Channel) -> Self; +} \ No newline at end of file diff --git a/cw-orch-daemon/src/queriers/grpc/auth.rs b/cw-orch-daemon/src/queriers/grpc/auth.rs new file mode 100644 index 000000000..b830f7f3b --- /dev/null +++ b/cw-orch-daemon/src/queriers/grpc/auth.rs @@ -0,0 +1,33 @@ +// Only a simple implementation to not overload the tx builder +use tonic::transport::Channel; + +use crate::{queriers::DaemonQuerier, cosmos_query, DaemonError}; + +/// Queries for Cosmos Bank Module +pub struct Auth { + channel: Channel, +} + +impl DaemonQuerier for Auth { + fn new(channel: Channel) -> Self { + Self { channel } + } +} + + +impl Auth{ + + /// Query spendable balance for address + pub async fn account( + &self, + address: impl Into, + ) -> Result, DaemonError> { + let resp = cosmos_query!( + self, + auth, + account, + QueryAccountRequest { address: address.into() } + ); + Ok(resp.account.unwrap().value) + } +} \ No newline at end of file diff --git a/cw-orch-daemon/src/queriers/bank.rs b/cw-orch-daemon/src/queriers/grpc/bank.rs similarity index 99% rename from cw-orch-daemon/src/queriers/bank.rs rename to cw-orch-daemon/src/queriers/grpc/bank.rs index 4612528b6..c6134033c 100644 --- a/cw-orch-daemon/src/queriers/bank.rs +++ b/cw-orch-daemon/src/queriers/grpc/bank.rs @@ -2,7 +2,7 @@ use crate::{cosmos_modules, error::DaemonError}; use cosmrs::proto::cosmos::base::{query::v1beta1::PageRequest, v1beta1::Coin}; use tonic::transport::Channel; -use super::DaemonQuerier; +use crate::queriers::DaemonQuerier; /// Queries for Cosmos Bank Module pub struct Bank { diff --git a/cw-orch-daemon/src/queriers/cosmwasm.rs b/cw-orch-daemon/src/queriers/grpc/cosmwasm.rs similarity index 99% rename from cw-orch-daemon/src/queriers/cosmwasm.rs rename to cw-orch-daemon/src/queriers/grpc/cosmwasm.rs index 34d649d0f..4ffeee9bb 100644 --- a/cw-orch-daemon/src/queriers/cosmwasm.rs +++ b/cw-orch-daemon/src/queriers/grpc/cosmwasm.rs @@ -2,7 +2,7 @@ use crate::{cosmos_modules, error::DaemonError}; use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; use tonic::transport::Channel; -use super::DaemonQuerier; +use crate::queriers::DaemonQuerier; /// Querier for the CosmWasm SDK module pub struct CosmWasm { diff --git a/cw-orch-daemon/src/queriers/feegrant.rs b/cw-orch-daemon/src/queriers/grpc/feegrant.rs similarity index 97% rename from cw-orch-daemon/src/queriers/feegrant.rs rename to cw-orch-daemon/src/queriers/grpc/feegrant.rs index 7c4d3d281..cd897fc4d 100644 --- a/cw-orch-daemon/src/queriers/feegrant.rs +++ b/cw-orch-daemon/src/queriers/grpc/feegrant.rs @@ -2,7 +2,7 @@ use crate::{cosmos_modules, error::DaemonError}; use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; use tonic::transport::Channel; -use super::DaemonQuerier; +use crate::queriers::DaemonQuerier; /// Querier for the Cosmos Gov module pub struct Feegrant { diff --git a/cw-orch-daemon/src/queriers/gov.rs b/cw-orch-daemon/src/queriers/grpc/gov.rs similarity index 99% rename from cw-orch-daemon/src/queriers/gov.rs rename to cw-orch-daemon/src/queriers/grpc/gov.rs index 3ecc9fb92..d0e70d139 100644 --- a/cw-orch-daemon/src/queriers/gov.rs +++ b/cw-orch-daemon/src/queriers/grpc/gov.rs @@ -2,7 +2,7 @@ use crate::{cosmos_modules, error::DaemonError}; use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; use tonic::transport::Channel; -use super::DaemonQuerier; +use crate::queriers::DaemonQuerier; /// Querier for the Cosmos Gov module pub struct Gov { diff --git a/cw-orch-daemon/src/queriers/ibc.rs b/cw-orch-daemon/src/queriers/grpc/ibc.rs similarity index 99% rename from cw-orch-daemon/src/queriers/ibc.rs rename to cw-orch-daemon/src/queriers/grpc/ibc.rs index 4e6b4aaf6..6873069b8 100644 --- a/cw-orch-daemon/src/queriers/ibc.rs +++ b/cw-orch-daemon/src/queriers/grpc/ibc.rs @@ -1,4 +1,4 @@ -use super::DaemonQuerier; +use crate::queriers::DaemonQuerier; use crate::{cosmos_modules, error::DaemonError}; use cosmos_modules::ibc_channel; use cosmrs::proto::ibc::{ diff --git a/cw-orch-daemon/src/queriers/grpc/mod.rs b/cw-orch-daemon/src/queriers/grpc/mod.rs new file mode 100644 index 000000000..95b06bd07 --- /dev/null +++ b/cw-orch-daemon/src/queriers/grpc/mod.rs @@ -0,0 +1,68 @@ +//! # DaemonQuerier +//! +//! DaemonAsync queriers are gRPC query clients for the CosmosSDK modules. They can be used to query the different modules (Bank, Ibc, Authz, ...). +//! +//! ## Usage +//! +//! You will need to acquire a [gRPC channel](Channel) to a running CosmosSDK node to be able to use the queriers. +//! Here is an example of how to acquire one using the DaemonAsync builder. +//! +//! ```no_run +//! // require the querier you want to use, in this case Node +//! use cw_orch_daemon::{queriers::Node, DaemonAsync, networks, queriers::DaemonQuerier}; +//! # tokio_test::block_on(async { +//! // call the builder and configure it as you need +//! let daemon = DaemonAsync::builder() +//! .chain(networks::LOCAL_JUNO) +//! .build() +//! .await.unwrap(); +//! // now you can use the Node querier: +//! let node = Node::new(daemon.channel()); +//! let node_info = node.info(); +//! # }) +//! ``` + +/// macro for constructing and performing a query on a CosmosSDK module. +#[macro_export] +macro_rules! cosmos_query { + ($self:ident, $module:ident, $func_name:ident, $request_type:ident { $($field:ident : $value:expr),* $(,)? }) => { + { + use $crate::cosmos_modules::$module::{ + query_client::QueryClient, $request_type, + }; + let mut client = QueryClient::new($self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = $request_type { $($field : $value),* }; + let response = client.$func_name(request.clone()).await?.into_inner(); + ::log::trace!( + "cosmos_query: {:?} resulted in: {:?}", + request, + response + ); + response + } +}; +} + +mod bank; +mod cosmwasm; +mod feegrant; +mod gov; +mod ibc; +mod node; +mod staking; +mod auth; +mod tx; + +pub use bank::Bank; +pub use cosmwasm::CosmWasm; +pub use feegrant::Feegrant; +pub use ibc::Ibc; +pub use node::Node; +pub use auth::Auth; +pub use tx::Tx; + +// this two containt structs that are helpers for the queries +pub use gov::*; +pub use staking::*; + diff --git a/cw-orch-daemon/src/queriers/node.rs b/cw-orch-daemon/src/queriers/grpc/node.rs similarity index 98% rename from cw-orch-daemon/src/queriers/node.rs rename to cw-orch-daemon/src/queriers/grpc/node.rs index 9ed84e904..bc04ce3ca 100644 --- a/cw-orch-daemon/src/queriers/node.rs +++ b/cw-orch-daemon/src/queriers/grpc/node.rs @@ -1,6 +1,6 @@ use std::{cmp::min, time::Duration}; -use crate::{cosmos_modules, error::DaemonError, tx_resp::CosmTxResponse}; +use crate::{cosmos_modules, error::DaemonError, tx_resp::CosmTxResponse, queriers::MAX_TX_QUERY_RETRIES}; use cosmrs::{ proto::cosmos::{base::query::v1beta1::PageRequest, tx::v1beta1::SimulateResponse}, @@ -8,9 +8,7 @@ use cosmrs::{ }; use tonic::transport::Channel; -use super::DaemonQuerier; - -const MAX_TX_QUERY_RETRIES: usize = 50; +use crate::queriers::DaemonQuerier; /// Querier for the Tendermint node. /// Supports queries for block and tx information @@ -172,6 +170,7 @@ impl Node { } /// Simulate TX + /// TODO, should be transferred over to the tx namespace pub async fn simulate_tx(&self, tx_bytes: Vec) -> Result { let mut client = cosmos_modules::tx::service_client::ServiceClient::new(self.channel.clone()); diff --git a/cw-orch-daemon/src/queriers/staking.rs b/cw-orch-daemon/src/queriers/grpc/staking.rs similarity index 99% rename from cw-orch-daemon/src/queriers/staking.rs rename to cw-orch-daemon/src/queriers/grpc/staking.rs index 973faccce..d1750ebdd 100644 --- a/cw-orch-daemon/src/queriers/staking.rs +++ b/cw-orch-daemon/src/queriers/grpc/staking.rs @@ -2,7 +2,7 @@ use crate::{cosmos_modules, error::DaemonError}; use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; use tonic::transport::Channel; -use super::DaemonQuerier; +use crate::queriers::DaemonQuerier; /// Querier for the Cosmos Staking module pub struct Staking { diff --git a/cw-orch-daemon/src/queriers/grpc/tx.rs b/cw-orch-daemon/src/queriers/grpc/tx.rs new file mode 100644 index 000000000..6c9ead9e8 --- /dev/null +++ b/cw-orch-daemon/src/queriers/grpc/tx.rs @@ -0,0 +1,42 @@ +// Only a simple implementation to not overload the tx builder + +use cosmrs::{tx::Raw, proto::cosmos::base::abci::v1beta1::TxResponse}; +use tonic::transport::Channel; + +use crate::{queriers::DaemonQuerier, DaemonError, cosmos_modules}; + + +/// Queries for Cosmos Bank Module +pub struct Tx { + channel: Channel, +} + +impl DaemonQuerier for Tx { + fn new(channel: Channel) -> Self { + Self { channel } + } +} + +impl Tx{ + + /// Query spendable balance for address + pub async fn broadcast( + &self, + tx: Raw, + ) -> Result { + + let mut client = + cosmos_modules::tx::service_client::ServiceClient::new(self.channel.clone()); + + let resp = client + .broadcast_tx(cosmos_modules::tx::BroadcastTxRequest { + tx_bytes: tx.to_bytes()?, + mode: cosmos_modules::tx::BroadcastMode::Sync.into(), + }) + .await? + .into_inner(); + + + Ok(resp.tx_response.unwrap()) + } +} \ No newline at end of file diff --git a/cw-orch-daemon/src/queriers/rpc/auth.rs b/cw-orch-daemon/src/queriers/rpc/auth.rs new file mode 100644 index 000000000..4e53890ab --- /dev/null +++ b/cw-orch-daemon/src/queriers/rpc/auth.rs @@ -0,0 +1,35 @@ +// Only a simple implementation to not overload the tx builder + +use cosmrs::rpc::HttpClient; + +use crate::{queriers::DaemonQuerier, cosmos_rpc_query, DaemonError}; + + +/// Queries for Cosmos Bank Module +pub struct Auth { + client: HttpClient, +} + +impl DaemonQuerier for Auth { + fn new(client: HttpClient) -> Self { + Self { client } + } +} + +impl Auth{ + + /// Query spendable balance for address + pub async fn account( + &self, + address: impl Into, + ) -> Result, DaemonError> { + let resp = cosmos_rpc_query!( + self, + auth, + "/cosmos.auth.v1beta1.Query/Account", + QueryAccountRequest { address: address.into() }, + QueryAccountResponse, + ); + Ok(resp.account.unwrap().value) + } +} \ No newline at end of file diff --git a/cw-orch-daemon/src/rpc_queriers/bank.rs b/cw-orch-daemon/src/queriers/rpc/bank.rs similarity index 95% rename from cw-orch-daemon/src/rpc_queriers/bank.rs rename to cw-orch-daemon/src/queriers/rpc/bank.rs index 62fee6947..daf13c534 100644 --- a/cw-orch-daemon/src/rpc_queriers/bank.rs +++ b/cw-orch-daemon/src/queriers/rpc/bank.rs @@ -1,16 +1,15 @@ -use crate::{cosmos_modules, error::DaemonError, cosmos_rpc_query}; -use cosmrs::{proto::cosmos::base::{query::v1beta1::PageRequest, v1beta1::Coin}, rpc::HttpClient, tx::MessageExt}; +use crate::{cosmos_modules, error::DaemonError, cosmos_rpc_query, queriers::DaemonQuerier}; +use cosmrs::{proto::cosmos::base::{query::v1beta1::PageRequest, v1beta1::Coin}, rpc::HttpClient}; -use super::RpcQuerier; /// Queries for Cosmos Bank Module pub struct Bank { client: HttpClient, } -impl RpcQuerier for Bank { - fn new(rpc: String) -> Self { - Self { client: HttpClient::new(rpc.as_str()).unwrap() } +impl DaemonQuerier for Bank { + fn new(client: HttpClient) -> Self { + Self { client } } } diff --git a/cw-orch-daemon/src/queriers/rpc/cosmwasm.rs b/cw-orch-daemon/src/queriers/rpc/cosmwasm.rs new file mode 100644 index 000000000..19949a201 --- /dev/null +++ b/cw-orch-daemon/src/queriers/rpc/cosmwasm.rs @@ -0,0 +1,229 @@ +use crate::{cosmos_modules, error::DaemonError, cosmos_rpc_query, queriers::DaemonQuerier}; +use cosmrs::{proto::cosmos::base::query::v1beta1::PageRequest, rpc::HttpClient}; + + +/// Querier for the CosmWasm SDK module +pub struct CosmWasm { + client: HttpClient, +} + +impl DaemonQuerier for CosmWasm { + fn new(client: HttpClient) -> Self { + Self { client } + } +} + +impl CosmWasm { + /// Query code_id by hash + pub async fn code_id_hash(&self, code_id: u64) -> Result { + + let resp = cosmos_rpc_query!( + self, + cosmwasm, + "/cosmwasm.wasm.v1.Query/Code", + QueryCodeRequest { code_id: code_id }, + QueryCodeResponse, + ); + + let contract_hash = resp.code_info.unwrap().data_hash; + let on_chain_hash = base16::encode_lower(&contract_hash); + Ok(on_chain_hash) + } + + /// Query contract info + pub async fn contract_info( + &self, + address: impl Into, + ) -> Result { + + let resp = cosmos_rpc_query!( + self, + cosmwasm, + "/cosmwasm.wasm.v1.Query/ContractInfo", + QueryContractInfoRequest { + address: address.into(), + }, + QueryContractInfoResponse, + ); + + let contract_info = resp.contract_info.unwrap(); + Ok(contract_info) + } + + /// Query contract history + pub async fn contract_history( + &self, + address: impl Into, + pagination: Option, + ) -> Result { + + let resp = cosmos_rpc_query!( + self, + cosmwasm, + "/cosmwasm.wasm.v1.Query/ContractHistory", + QueryContractHistoryRequest { + address: address.into(), + pagination: pagination, + }, + QueryContractHistoryResponse, + ); + + Ok(resp) + } + + /// Query contract state + pub async fn contract_state( + &self, + address: impl Into, + query_data: Vec, + ) -> Result, DaemonError> { + + let resp = cosmos_rpc_query!( + self, + cosmwasm, + "/cosmwasm.wasm.v1.Query/SmartContractState", + QuerySmartContractStateRequest { + address: address.into(), + query_data: query_data, + }, + QuerySmartContractStateResponse, + ); + + Ok(resp.data) + } + + /// Query all contract state + pub async fn all_contract_state( + &self, + address: impl Into, + pagination: Option, + ) -> Result { + let resp = cosmos_rpc_query!( + self, + cosmwasm, + "/cosmwasm.wasm.v1.Query/AllContractState", + QueryAllContractStateRequest { + address: address.into(), + pagination: pagination, + }, + QueryAllContractStateResponse, + ); + Ok(resp) + } + + /// Query code + pub async fn code( + &self, + code_id: u64, + ) -> Result { + let resp = cosmos_rpc_query!( + self, + cosmwasm, + "/cosmwasm.wasm.v1.Query/Code", + QueryCodeRequest { code_id: code_id }, + QueryCodeResponse, + ); + + Ok(resp.code_info.unwrap()) + } + + /// Query code bytes + pub async fn code_data(&self, code_id: u64) -> Result, DaemonError> { + + let resp = cosmos_rpc_query!( + self, + cosmwasm, + "/cosmwasm.wasm.v1.Query/Code", + QueryCodeRequest { code_id: code_id }, + QueryCodeResponse, + ); + + Ok(resp.data) + } + + /// Query codes + pub async fn codes( + &self, + pagination: Option, + ) -> Result, DaemonError> { + + let resp = cosmos_rpc_query!( + self, + cosmwasm, + "/cosmwasm.wasm.v1.Query/Codes", + QueryCodesRequest { pagination: pagination }, + QueryCodesResponse, + ); + + Ok(resp.code_infos) + } + + /// Query pinned codes + pub async fn pinned_codes( + &self, + ) -> Result { + let resp = cosmos_rpc_query!( + self, + cosmwasm, + "/cosmwasm.wasm.v1.Query/PinnedCodes", + QueryPinnedCodesRequest { pagination: None }, + QueryPinnedCodesResponse, + ); + Ok(resp) + } + + /// Query contracts by code + pub async fn contract_by_codes( + &self, + code_id: u64, + ) -> Result { + let resp = cosmos_rpc_query!( + self, + cosmwasm, + "/cosmwasm.wasm.v1.Query/ContractsByCode", + QueryContractsByCodeRequest { + code_id: code_id, + pagination: None, + }, + QueryContractsByCodeResponse, + ); + + Ok(resp) + } + + /// Query raw contract state + pub async fn contract_raw_state( + &self, + address: impl Into, + query_data: Vec, + ) -> Result { + + let resp = cosmos_rpc_query!( + self, + cosmwasm, + "/cosmwasm.wasm.v1.Query/RawContractState", + QueryRawContractStateRequest { + address: address.into(), + query_data: query_data, + }, + QueryRawContractStateResponse, + ); + + Ok(resp) + } + + /// Query params + pub async fn params( + &self, + ) -> Result { + let resp = cosmos_rpc_query!( + self, + cosmwasm, + "/cosmwasm.wasm.v1.Query/Params", + QueryParamsRequest {}, + QueryParamsResponse, + ); + + Ok(resp) + } +} diff --git a/cw-orch-daemon/src/rpc_queriers/mod.rs b/cw-orch-daemon/src/queriers/rpc/mod.rs similarity index 76% rename from cw-orch-daemon/src/rpc_queriers/mod.rs rename to cw-orch-daemon/src/queriers/rpc/mod.rs index 3027571da..834900bd5 100644 --- a/cw-orch-daemon/src/rpc_queriers/mod.rs +++ b/cw-orch-daemon/src/queriers/rpc/mod.rs @@ -1,4 +1,18 @@ -pub mod bank; +mod bank; +mod cosmwasm; +mod node; +mod staking; +mod auth; +mod tx; + +pub use bank::Bank; +pub use cosmwasm::CosmWasm; +pub use node::Node; +pub use staking::Staking; +pub use auth::Auth; +pub use tx::Tx; +// pub use feegrant::Feegrant; +// pub use ibc::Ibc; /// macro for constructing and performing a query on a CosmosSDK module. #[macro_export] @@ -9,6 +23,7 @@ macro_rules! cosmos_rpc_query { $request_resp, $request_type, }; use ::cosmrs::rpc::Client; + use ::cosmrs::tx::MessageExt; use ::prost::Message; let request = $request_type { $($field : $value),* }; @@ -28,12 +43,4 @@ macro_rules! cosmos_rpc_query { decoded_response } }; -} - - - -/// Constructor for a querier over a given channel -pub trait RpcQuerier { - /// Construct an new querier over a given channel - fn new(rpc: String) -> Self; -} +} \ No newline at end of file diff --git a/cw-orch-daemon/src/queriers/rpc/node.rs b/cw-orch-daemon/src/queriers/rpc/node.rs new file mode 100644 index 000000000..dc61d8c80 --- /dev/null +++ b/cw-orch-daemon/src/queriers/rpc/node.rs @@ -0,0 +1,244 @@ +use std::{cmp::min, time::Duration}; + +use crate::{cosmos_modules, error::DaemonError, tx_resp::CosmTxResponse, queriers::{MAX_TX_QUERY_RETRIES, DaemonQuerier}, cosmos_rpc_query}; + +use cosmrs::{ + proto::cosmos::base::{query::v1beta1::PageRequest, abci::v1beta1::TxResponse}, + tendermint::{Block, Time}, rpc::{HttpClient, Client}, tx::MessageExt, +}; +use prost::Message; + + +/// Querier for the Tendermint node. +/// Supports queries for block and tx information +pub struct Node { + client: HttpClient, +} + +impl DaemonQuerier for Node { + fn new(client: HttpClient) -> Self { + Self { client } + } +} + + +impl Node { + /// Returns node info + pub async fn info( + &self, + ) -> Result { + + let resp = cosmos_rpc_query!( + self, + tendermint, + "/cosmos.base.tendermint.v1beta1.Service/GetNodeInfo", + GetNodeInfoRequest {}, + GetNodeInfoResponse, + ); + + Ok(resp) + } + + /// Queries node syncing + pub async fn syncing(&self) -> Result { + + let resp = cosmos_rpc_query!( + self, + tendermint, + "/cosmos.base.tendermint.v1beta1.Service/GetSyncing", + GetSyncingRequest {}, + GetSyncingResponse, + ); + + Ok(resp.syncing) + } + + /// Returns latests block information + pub async fn latest_block(&self) -> Result { + + let resp = cosmos_rpc_query!( + self, + tendermint, + "/cosmos.base.tendermint.v1beta1.Service/GetLatestBlock", + GetLatestBlockRequest {}, + GetLatestBlockResponse, + ); + + Ok(Block::try_from(resp.block.unwrap())?) + } + + /// Returns block information fetched by height + pub async fn block_by_height(&self, height: u64) -> Result { + let resp = cosmos_rpc_query!( + self, + tendermint, + "/cosmos.base.tendermint.v1beta1.Service/GetBlockByHeight", + GetBlockByHeightRequest { + height: height as i64, + }, + GetBlockByHeightResponse, + ); + + Ok(Block::try_from(resp.block.unwrap())?) + } + + /// Return the average block time for the last 50 blocks or since inception + /// This is used to estimate the time when a tx will be included in a block + pub async fn average_block_speed(&self, multiplier: Option) -> Result { + // get latest block time and height + let mut latest_block = self.latest_block().await?; + let latest_block_time = latest_block.header.time; + let mut latest_block_height = latest_block.header.height.value(); + + while latest_block_height <= 1 { + // wait to get some blocks + tokio::time::sleep(Duration::from_secs(1)).await; + latest_block = self.latest_block().await?; + latest_block_height = latest_block.header.height.value(); + } + + // let avg period + let avg_period = min(latest_block_height - 1, 50); + + // get block time for block avg_period blocks ago + let block_avg_period_ago = self + .block_by_height(latest_block_height - avg_period) + .await?; + let block_avg_period_ago_time = block_avg_period_ago.header.time; + + // calculate average block time + let average_block_time = latest_block_time.duration_since(block_avg_period_ago_time)?; + let average_block_time = average_block_time.as_secs() / avg_period; + + // multiply by multiplier if provided + let average_block_time = match multiplier { + Some(multiplier) => (average_block_time as f32 * multiplier) as u64, + None => average_block_time, + }; + + Ok(std::cmp::max(average_block_time, 1)) + } + + /// Returns latests validator set + pub async fn latest_validator_set( + &self, + pagination: Option, + ) -> Result { + + let resp = cosmos_rpc_query!( + self, + tendermint, + "/cosmos.base.tendermint.v1beta1.Service/GetLatestValidatorSet", + GetLatestValidatorSetRequest { + pagination: pagination, + }, + GetLatestValidatorSetResponse, + ); + + Ok(resp) + } + + /// Returns latests validator set fetched by height + pub async fn validator_set_by_height( + &self, + height: i64, + pagination: Option, + ) -> Result { + + let resp = cosmos_rpc_query!( + self, + tendermint, + "/cosmos.base.tendermint.v1beta1.Service/GetValidatorSetByHeight", + GetValidatorSetByHeightRequest { height: height, pagination: pagination }, + GetValidatorSetByHeightResponse, + ); + + Ok(resp) + } + + /// Returns current block height + pub async fn block_height(&self) -> Result { + let block = self.latest_block().await?; + Ok(block.header.height.value()) + } + + /// Returns the block timestamp (since unix epoch) in nanos + pub async fn block_time(&self) -> Result { + let block = self.latest_block().await?; + Ok(block + .header + .time + .duration_since(Time::unix_epoch())? + .as_nanos()) + } + + /// Simulate TX + pub async fn simulate_tx(&self, tx_bytes: Vec) -> Result { + + // We use this allow deprecated for the tx field of the simulate request (but we set it to None, so that's ok) + #[allow(deprecated)] + let resp = cosmos_rpc_query!( + self, + tx, + "/cosmos.tx.v1beta1.Service/Simulate", + SimulateRequest { tx: None, tx_bytes: tx_bytes }, + SimulateResponse, + ); + + let gas_used = resp.gas_info.unwrap().gas_used; + Ok(gas_used) + } + + /// Returns all the block info + pub async fn block_info(&self) -> Result { + let block = self.latest_block().await?; + let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; + let time = cosmwasm_std::Timestamp::from_nanos(since_epoch.as_nanos() as u64); + Ok(cosmwasm_std::BlockInfo { + height: block.header.height.value(), + time, + chain_id: block.header.chain_id.to_string(), + }) + } + + /// Find TX by hash + pub async fn find_tx(&self, hash: String) -> Result { + self.find_tx_with_retries(hash, MAX_TX_QUERY_RETRIES).await + } + + /// Find TX by hash with a given amount of retries + pub async fn find_tx_with_retries( + &self, + hash: String, + retries: usize, + ) -> Result { + + let request = cosmos_modules::tx::GetTxRequest { hash: hash.clone() }; + let mut block_speed = self.average_block_speed(Some(0.7)).await?; + + for _ in 0..retries { + let tx_response = self.client.abci_query( + Some("/cosmos.tx.v1beta1.Service/GetTx".to_string()), + request.to_bytes()?, + None, + true, + ).await?; + match TxResponse::decode(tx_response.value.as_slice()) { + Ok(tx) => { + log::debug!("TX found: {:?}", tx); + return Ok(tx.into()); + } + Err(err) => { + // increase wait time + block_speed = (block_speed as f64 * 1.6) as u64; + log::debug!("TX not found with error: {:?}", err); + log::debug!("Waiting {block_speed} seconds"); + tokio::time::sleep(Duration::from_secs(block_speed)).await; + } + } + } + + // return error if tx not found by now + Err(DaemonError::TXNotFound(hash, retries)) + } +} diff --git a/cw-orch-daemon/src/queriers/rpc/staking.rs b/cw-orch-daemon/src/queriers/rpc/staking.rs new file mode 100644 index 000000000..6e34365e3 --- /dev/null +++ b/cw-orch-daemon/src/queriers/rpc/staking.rs @@ -0,0 +1,295 @@ +use crate::{cosmos_modules, error::DaemonError, cosmos_rpc_query}; +use cosmrs::{proto::cosmos::base::query::v1beta1::PageRequest, rpc::HttpClient}; + +use crate::queriers::DaemonQuerier; + +/// Querier for the Cosmos Staking module +pub struct Staking { + client: HttpClient, +} + +impl DaemonQuerier for Staking { + fn new(client: HttpClient) -> Self { + Self { client } + } +} + +impl Staking { + /// Queries validator info for given validator address + pub async fn validator( + &self, + validator_addr: impl Into, + ) -> Result { + let validator = cosmos_rpc_query!( + self, + staking, + "/cosmos.staking.v1beta1.Query/Validator", + QueryValidatorRequest { + validator_addr: validator_addr.into() + }, + QueryValidatorResponse, + ); + Ok(validator.validator.unwrap()) + } + + /// Queries all validators that match the given status + /// + /// see [StakingBondStatus] for available statuses + pub async fn validators( + &self, + status: StakingBondStatus, + ) -> Result, DaemonError> { + let validators = cosmos_rpc_query!( + self, + staking, + "/cosmos.staking.v1beta1.Query/Validators", + QueryValidatorsRequest { + status: status.to_string(), + pagination: None, + }, + QueryValidatorsResponse, + ); + Ok(validators.validators) + } + + /// Query validator delegations info for given validator + /// + /// see [PageRequest] for pagination + pub async fn delegations( + &self, + validator_addr: impl Into, + pagination: Option, + ) -> Result, DaemonError> { + let validator_delegations = cosmos_rpc_query!( + self, + staking, + "/cosmos.staking.v1beta1.Query/ValidatorDelegations", + QueryValidatorDelegationsRequest { + validator_addr: validator_addr.into(), + pagination: pagination + }, + QueryValidatorDelegationsResponse, + ); + Ok(validator_delegations.delegation_responses) + } + + /// Query validator unbonding delegations of a validator + pub async fn unbonding_delegations( + &self, + validator_addr: impl Into, + ) -> Result, DaemonError> { + let validator_unbonding_delegations = cosmos_rpc_query!( + self, + staking, + "/cosmos.staking.v1beta1.Query/ValidatorUnbondingDelegations", + QueryValidatorUnbondingDelegationsRequest { + validator_addr: validator_addr.into(), + pagination: None + }, + QueryValidatorUnbondingDelegationsResponse, + ); + Ok(validator_unbonding_delegations.unbonding_responses) + } + + /// Query delegation info for given validator for a delegator + pub async fn delegation( + &self, + validator_addr: impl Into, + delegator_addr: impl Into, + ) -> Result { + let delegation = cosmos_rpc_query!( + self, + staking, + "/cosmos.staking.v1beta1.Query/Delegation", + QueryDelegationRequest { + validator_addr: validator_addr.into(), + delegator_addr: delegator_addr.into() + }, + QueryDelegationResponse, + ); + Ok(delegation.delegation_response.unwrap()) + } + + /// Query unbonding delegation info for given validator delegator + pub async fn unbonding_delegation( + &self, + validator_addr: impl Into, + delegator_addr: impl Into, + ) -> Result { + let unbonding_delegation = cosmos_rpc_query!( + self, + staking, + "/cosmos.staking.v1beta1.Query/UnbondingDelegation", + QueryUnbondingDelegationRequest { + validator_addr: validator_addr.into(), + delegator_addr: delegator_addr.into() + }, + QueryUnbondingDelegationResponse, + ); + Ok(unbonding_delegation.unbond.unwrap()) + } + + /// Query all delegator delegations of a given delegator address + /// + /// see [PageRequest] for pagination + pub async fn delegator_delegations( + &self, + delegator_addr: impl Into, + pagination: Option, + ) -> Result { + let delegator_delegations = cosmos_rpc_query!( + self, + staking, + "/cosmos.staking.v1beta1.Query/DelegatorDelegations", + QueryDelegatorDelegationsRequest { + delegator_addr: delegator_addr.into(), + pagination: pagination + }, + QueryDelegatorDelegationsResponse, + ); + Ok(delegator_delegations) + } + + /// Queries all unbonding delegations of a given delegator address. + /// + /// see [PageRequest] for pagination + pub async fn delegator_unbonding_delegations( + &self, + delegator_addr: impl Into, + pagination: Option, + ) -> Result + { + let delegator_unbonding_delegations = cosmos_rpc_query!( + self, + staking, + "/cosmos.staking.v1beta1.Query/DelegatorUnbondingDelegations", + QueryDelegatorUnbondingDelegationsRequest { + delegator_addr: delegator_addr.into(), + pagination: pagination + }, + QueryDelegatorUnbondingDelegationsResponse, + ); + Ok(delegator_unbonding_delegations) + } + + /// Query redelegations of a given address + /// + /// see [PageRequest] for pagination + pub async fn redelegations( + &self, + delegator_addr: impl Into, + src_validator_addr: impl Into, + dst_validator_addr: impl Into, + pagination: Option, + ) -> Result { + let redelegations = cosmos_rpc_query!( + self, + staking, + "/cosmos.staking.v1beta1.Query/Redelegations", + QueryRedelegationsRequest { + delegator_addr: delegator_addr.into(), + src_validator_addr: src_validator_addr.into(), + dst_validator_addr: dst_validator_addr.into(), + pagination: pagination + }, + QueryRedelegationsResponse, + ); + Ok(redelegations) + } + + /// Query delegator validators info for given delegator address. + pub async fn delegator_validator( + &self, + validator_addr: impl Into, + delegator_addr: impl Into, + ) -> Result { + let delegator_validator = cosmos_rpc_query!( + self, + staking, + "/cosmos.staking.v1beta1.Query/DelegatorValidator", + QueryDelegatorValidatorRequest { + validator_addr: validator_addr.into(), + delegator_addr: delegator_addr.into(), + }, + QueryDelegatorValidatorResponse, + ); + Ok(delegator_validator) + } + + /// Query delegator validators info for given delegator address + /// + /// see [PageRequest] for pagination + pub async fn delegator_validators( + &self, + delegator_addr: impl Into, + pagination: Option, + ) -> Result { + let delegator_validators = cosmos_rpc_query!( + self, + staking, + "/cosmos.staking.v1beta1.Query/DelegatorValidators", + QueryDelegatorValidatorsRequest { + delegator_addr: delegator_addr.into(), + pagination: pagination + }, + QueryDelegatorValidatorsResponse, + ); + + Ok(delegator_validators) + } + + /// Query historical info info for given height + pub async fn historical_info( + &self, + height: i64, + ) -> Result { + let historical_info = cosmos_rpc_query!( + self, + staking, + "/cosmos.staking.v1beta1.Query/HistoricalInfo", + QueryHistoricalInfoRequest { height: height }, + QueryHistoricalInfoResponse, + ); + Ok(historical_info) + } + + /// Query the pool info + pub async fn pool(&self) -> Result { + let pool = + cosmos_rpc_query!(self, staking, "/cosmos.staking.v1beta1.Query/Pool", QueryPoolRequest {}, QueryPoolResponse,); + Ok(pool) + } + + /// Query staking parameters + pub async fn params( + &self, + ) -> Result { + let params = + cosmos_rpc_query!(self, staking, "/cosmos.staking.v1beta1.Query/Params", QueryParamsRequest {}, QueryParamsResponse,); + Ok(params) + } +} + +/// Staking bond statuses +pub enum StakingBondStatus { + /// UNSPECIFIED defines an invalid validator status. + Unspecified = 0, + /// UNBONDED defines a validator that is not bonded. + Unbonded = 1, + /// UNBONDING defines a validator that is unbonding. + Unbonding = 2, + /// BONDED defines a validator that is bonded. + Bonded = 3, +} + +impl ToString for StakingBondStatus { + /// Convert to string + fn to_string(&self) -> String { + match self { + StakingBondStatus::Unspecified => "BOND_STATUS_UNSPECIFIED".to_string(), + StakingBondStatus::Unbonded => "BOND_STATUS_UNBONDED".to_string(), + StakingBondStatus::Unbonding => "BOND_STATUS_UNBONDING".to_string(), + StakingBondStatus::Bonded => "BOND_STATUS_BONDED".to_string(), + } + } +} diff --git a/cw-orch-daemon/src/queriers/rpc/tx.rs b/cw-orch-daemon/src/queriers/rpc/tx.rs new file mode 100644 index 000000000..3f3797b77 --- /dev/null +++ b/cw-orch-daemon/src/queriers/rpc/tx.rs @@ -0,0 +1,38 @@ +// Only a simple implementation to not overload the tx builder + +use cosmrs::{rpc::HttpClient, tx::Raw, proto::cosmos::base::abci::v1beta1::TxResponse}; + +use crate::{queriers::DaemonQuerier, cosmos_rpc_query, DaemonError, cosmos_modules}; + + +/// Queries for Cosmos Bank Module +pub struct Tx { + client: HttpClient, +} + +impl DaemonQuerier for Tx { + fn new(client: HttpClient) -> Self { + Self { client } + } +} + +impl Tx{ + + /// Query spendable balance for address + pub async fn broadcast( + &self, + tx: Raw, + ) -> Result { + let resp = cosmos_rpc_query!( + self, + tx, + "/cosmos.tx.v1beta1.Service/BroadcastTx", + BroadcastTxRequest { + tx_bytes: tx.to_bytes()?, + mode: cosmos_modules::tx::BroadcastMode::Sync.into(), + }, + BroadcastTxResponse, + ); + Ok(resp.tx_response.unwrap()) + } +} \ No newline at end of file diff --git a/cw-orch-daemon/src/rpc_channel.rs b/cw-orch-daemon/src/rpc_channel.rs new file mode 100644 index 000000000..a026afa55 --- /dev/null +++ b/cw-orch-daemon/src/rpc_channel.rs @@ -0,0 +1,103 @@ +use cosmrs::rpc::{HttpClient, Client}; +use ibc_chain_registry::chain::Rpc; +use ibc_relayer_types::core::ics24_host::identifier::ChainId; + +use super::error::DaemonError; + +/// A helper for constructing a gRPC channel +pub struct RpcChannel {} + +impl RpcChannel { + /// Connect to any of the provided gRPC endpoints + pub async fn connect(rpc: &[Rpc], chain_id: &ChainId) -> Result { + let mut successful_connections = vec![]; + + for Rpc { address, .. } in rpc.iter() { + log::info!("Trying to connect to endpoint: {}", address); + + // try to connect to rpc endpoint + let maybe_client = HttpClient::new(address.as_str()); + + // connection succeeded or try the next rpc endpoint + let client = if maybe_client.is_ok() { + maybe_client? + } else { + continue + }; + + // get client information for verification down below + let node_info = client + .status().await? + .node_info; + + // local juno does not return a proper ChainId with epoch format + if ChainId::is_epoch_format(node_info.network.as_str()) { + // verify we are connected to the spected network + if node_info.network.as_str().ne(chain_id.as_str()) { + log::error!( + "Network mismatch: connection:{} != config:{}", + node_info.network, + chain_id.as_str() + ); + continue; + } + } + + // add endpoint to succesful connections + successful_connections.push(client) + } + + // we could not get any succesful connections + if successful_connections.is_empty() { + return Err(DaemonError::CannotConnectGRPC); + } + + Ok(successful_connections.pop().unwrap()) + } +} + +#[cfg(test)] +mod tests { + /* + This test asserts breaking issues around the GRPC connection + */ + + use crate::DaemonAsync; + use speculoos::prelude::*; + + #[tokio::test] + async fn no_connection() { + let mut chain = cw_orch_daemon::networks::LOCAL_JUNO; + let grpcs = &vec!["https://127.0.0.1:99999"]; + chain.grpc_urls = grpcs; + + let build_res = DaemonAsync::builder() + .chain(chain) + .deployment_id("v0.1.0") + .build() + .await; + + asserting!("there is no GRPC connection") + .that(&build_res.err().unwrap().to_string()) + .is_equal_to(String::from( + "Can not connect to any grpc endpoint that was provided.", + )) + } + + #[tokio::test] + async fn network_grpcs_list_is_empty() { + let mut chain = cw_orch_daemon::networks::LOCAL_JUNO; + let grpcs: &Vec<&str> = &vec![]; + chain.grpc_urls = grpcs; + + let build_res = DaemonAsync::builder() + .chain(chain) + .deployment_id("v0.1.0") + .build() + .await; + + asserting!("GRPC list is empty") + .that(&build_res.err().unwrap().to_string()) + .is_equal_to(String::from("The list of grpc endpoints is empty")) + } +} diff --git a/cw-orch-daemon/src/sender.rs b/cw-orch-daemon/src/sender.rs index 12fb19f83..8fe40cf55 100644 --- a/cw-orch-daemon/src/sender.rs +++ b/cw-orch-daemon/src/sender.rs @@ -1,4 +1,4 @@ -use crate::{networks::ChainKind, proto::injective::ETHEREUM_COIN_TYPE}; +use crate::{networks::ChainKind, proto::injective::ETHEREUM_COIN_TYPE, queriers::{Auth, Tx}}; use super::{ cosmos_modules::{self, auth::BaseAccount}, @@ -27,7 +27,6 @@ use secp256k1::{All, Context, Secp256k1, Signing}; use std::{convert::TryFrom, env, rc::Rc, str::FromStr}; use cosmos_modules::vesting::PeriodicVestingAccount; -use tonic::transport::Channel; /// A wallet is a sender of transactions, can be safely cloned and shared within the same thread. pub type Wallet = Rc>; @@ -80,8 +79,13 @@ impl Sender { SigningKey::from_slice(&self.private_key.raw_key()).unwrap() } - pub fn channel(&self) -> Channel { - self.daemon_state.grpc_channel.clone() + #[cfg(feature="grpc")] + pub fn channel(&self) -> tonic::transport::Channel { + self.daemon_state.transport_channel.clone() + } + #[cfg(feature="rpc")] + pub fn channel(&self) -> cosmrs::rpc::HttpClient { + self.daemon_state.transport_channel.clone() } pub fn pub_addr(&self) -> Result { @@ -146,9 +150,9 @@ impl Sender { .await } - pub async fn commit_tx( + pub async fn commit_tx( &self, - msgs: Vec, + msgs: Vec, memo: Option<&str>, ) -> Result { let timeout_height = Node::new(self.channel()).block_height().await? + 10u64; @@ -215,14 +219,9 @@ impl Sender { pub async fn base_account(&self) -> Result { let addr = self.pub_addr().unwrap().to_string(); - let mut client = cosmos_modules::auth::query_client::QueryClient::new(self.channel()); - - let resp = client - .account(cosmos_modules::auth::QueryAccountRequest { address: addr }) - .await? - .into_inner(); + let querier = Auth::new(self.channel().clone()); - let account = resp.account.unwrap().value; + let account = querier.account(addr).await?; let acc = if let Ok(acc) = BaseAccount::decode(account.as_ref()) { acc @@ -244,15 +243,9 @@ impl Sender { &self, tx: Raw, ) -> Result { - let mut client = cosmos_modules::tx::service_client::ServiceClient::new(self.channel()); - let commit = client - .broadcast_tx(cosmos_modules::tx::BroadcastTxRequest { - tx_bytes: tx.to_bytes()?, - mode: cosmos_modules::tx::BroadcastMode::Sync.into(), - }) - .await?; + let broadcaster = Tx::new(self.channel().clone()); + let commit = broadcaster.broadcast(tx).await?; - let commit = commit.into_inner().tx_response.unwrap(); Ok(commit) } } diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index 8e9f9c9b6..bd00f271a 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -1,5 +1,10 @@ use super::error::DaemonError; -use crate::{channel::GrpcChannel, networks::ChainKind}; +use crate::networks::ChainKind; + +#[cfg(feature="grpc")] +use crate::grpc_channel::GrpcChannel; +#[cfg(feature="rpc")] +use crate::rpc_channel::RpcChannel; use cosmwasm_std::Addr; use cw_orch_core::{ @@ -10,7 +15,6 @@ use ibc_chain_registry::chain::ChainData; use serde::Serialize; use serde_json::{json, Value}; use std::{collections::HashMap, env, fs::File, path::Path}; -use tonic::transport::Channel; /// Stores the chain information and deployment state. /// Uses a simple JSON file to store the deployment information locally. @@ -20,12 +24,25 @@ pub struct DaemonState { pub json_file_path: String, /// Deployment identifier pub deployment_id: String, - /// gRPC channel - pub grpc_channel: Channel, + /// Transport channel + #[cfg(feature="grpc")] + pub transport_channel: tonic::transport::Channel, + #[cfg(feature="rpc")] + pub transport_channel: cosmrs::rpc::HttpClient, /// Information about the chain pub chain_data: ChainData, } +#[cfg(feature="grpc")] +pub async fn create_transport_channel(chain_data: &ChainData) -> Result{ + GrpcChannel::connect(&chain_data.apis.grpc, &chain_data.chain_id).await +} + +#[cfg(feature="rpc")] +pub async fn create_transport_channel(chain_data: &ChainData) -> Result{ + RpcChannel::connect(&chain_data.apis.rpc, &chain_data.chain_id).await +} + impl DaemonState { /// Creates a new state from the given chain data and deployment id. /// Attempts to connect to any of the provided gRPC endpoints. @@ -40,8 +57,7 @@ impl DaemonState { log::info!("Found {} gRPC endpoints", chain_data.apis.grpc.len()); // find working grpc channel - let grpc_channel = - GrpcChannel::connect(&chain_data.apis.grpc, &chain_data.chain_id).await?; + let transport_channel = create_transport_channel(&chain_data).await?; // check if STATE_FILE en var is configured, default to state.json let mut json_file_path = env::var("STATE_FILE").unwrap_or("./state.json".to_string()); @@ -80,7 +96,7 @@ impl DaemonState { let state = DaemonState { json_file_path, deployment_id, - grpc_channel, + transport_channel, chain_data, }; @@ -113,7 +129,7 @@ impl DaemonState { } /// Set a stateful value using the chainId and networkId - pub fn set(&self, key: &str, contract_id: &str, value: T) { + pub fn set(&self, key: &str, contract_id: &str, value: V) { let mut json = self.read_state(); json[&self.chain_data.chain_name][&self.chain_data.chain_id.to_string()][key] diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index 22e9d8a37..4b5a6e10f 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -14,7 +14,6 @@ use cw_orch_core::{ }; use serde::{de::DeserializeOwned, Serialize}; use tokio::runtime::Handle; -use tonic::transport::Channel; #[derive(Clone)] /** @@ -62,8 +61,13 @@ impl Daemon { } /// Get the channel configured for this Daemon - pub fn channel(&self) -> Channel { - self.daemon.state.grpc_channel.clone() + #[cfg(feature="grpc")] + pub fn channel(&self) -> tonic::transport::Channel { + self.daemon.state.transport_channel.clone() + } + #[cfg(feature="rpc")] + pub fn channel(&self) -> cosmrs::rpc::HttpClient { + self.daemon.state.transport_channel.clone() } /// Get the channel configured for this Daemon diff --git a/cw-orch-daemon/src/test.rs b/cw-orch-daemon/src/test.rs deleted file mode 100644 index 02675d6fb..000000000 --- a/cw-orch-daemon/src/test.rs +++ /dev/null @@ -1,8364 +0,0 @@ -#![feature(prelude_import)] -//! `Daemon` and `DaemonAsync` execution environments. -//! -//! The `Daemon` type is a synchronous wrapper around the `DaemonAsync` type and can be used as a contract execution environment. -#[prelude_import] -use std::prelude::rust_2021::*; -#[macro_use] -extern crate std; -pub mod builder { - use crate::{DaemonAsync, DaemonBuilder}; - use std::rc::Rc; - use ibc_chain_registry::chain::ChainData; - use super::{error::DaemonError, sender::Sender, state::DaemonState}; - /// The default deployment id if none is provided - pub const DEFAULT_DEPLOYMENT: &str = "default"; - /// Create [`DaemonAsync`] through [`DaemonAsyncBuilder`] - /// ## Example - /// ```no_run - /// # tokio_test::block_on(async { - /// use cw_orch_daemon::{DaemonAsyncBuilder, networks}; - /// let daemon = DaemonAsyncBuilder::default() - /// .chain(networks::LOCAL_JUNO) - /// .deployment_id("v0.1.0") - /// .build() - /// .await.unwrap(); - /// # }) - /// ``` - pub struct DaemonAsyncBuilder { - pub(crate) chain: Option, - pub(crate) deployment_id: Option, - /// Wallet mnemonic - pub(crate) mnemonic: Option, - } - #[automatically_derived] - impl ::core::clone::Clone for DaemonAsyncBuilder { - #[inline] - fn clone(&self) -> DaemonAsyncBuilder { - DaemonAsyncBuilder { - chain: ::core::clone::Clone::clone(&self.chain), - deployment_id: ::core::clone::Clone::clone(&self.deployment_id), - mnemonic: ::core::clone::Clone::clone(&self.mnemonic), - } - } - } - #[automatically_derived] - impl ::core::default::Default for DaemonAsyncBuilder { - #[inline] - fn default() -> DaemonAsyncBuilder { - DaemonAsyncBuilder { - chain: ::core::default::Default::default(), - deployment_id: ::core::default::Default::default(), - mnemonic: ::core::default::Default::default(), - } - } - } - impl DaemonAsyncBuilder { - /// Set the chain the daemon will connect to - pub fn chain(&mut self, chain: impl Into) -> &mut Self { - self.chain = Some(chain.into()); - self - } - /// Set the deployment id to use for the daemon interactions - /// Defaults to `default` - pub fn deployment_id(&mut self, deployment_id: impl Into) -> &mut Self { - self.deployment_id = Some(deployment_id.into()); - self - } - /// Set the mnemonic to use with this chain. - /// Defaults to env variable depending on the environment. - /// - /// Variables: LOCAL_MNEMONIC, TEST_MNEMONIC and MAIN_MNEMONIC - pub fn mnemonic(&mut self, mnemonic: impl ToString) -> &mut Self { - self.mnemonic = Some(mnemonic.to_string()); - self - } - /// Build a daemon - pub async fn build(&self) -> Result { - let chain = self - .chain - .clone() - .ok_or(DaemonError::BuilderMissing("chain information".into()))?; - let deployment_id = self - .deployment_id - .clone() - .unwrap_or(DEFAULT_DEPLOYMENT.to_string()); - let state = Rc::new(DaemonState::new(chain, deployment_id).await?); - let sender = if let Some(mnemonic) = &self.mnemonic { - Sender::from_mnemonic(&state, mnemonic)? - } else { - Sender::new(&state)? - }; - let daemon = DaemonAsync { - state, - sender: Rc::new(sender), - }; - Ok(daemon) - } - } - impl From for DaemonAsyncBuilder { - fn from(value: DaemonBuilder) -> Self { - DaemonAsyncBuilder { - chain: value.chain, - deployment_id: value.deployment_id, - mnemonic: value.mnemonic, - } - } - } -} -pub mod channel { - use cosmrs::proto::cosmos::base::tendermint::v1beta1::{ - service_client::ServiceClient, GetNodeInfoRequest, - }; - use ibc_chain_registry::chain::Grpc; - use ibc_relayer_types::core::ics24_host::identifier::ChainId; - use tonic::transport::{Channel, ClientTlsConfig}; - use super::error::DaemonError; - /// A helper for constructing a gRPC channel - pub struct GrpcChannel {} - impl GrpcChannel { - /// Connect to any of the provided gRPC endpoints - pub async fn connect( - grpc: &[Grpc], - chain_id: &ChainId, - ) -> Result { - let mut successful_connections = ::alloc::vec::Vec::new(); - for Grpc { address, .. } in grpc.iter() { - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Trying to connect to endpoint: {0}", address), - lvl, - &( - "cw_orch_daemon::channel", - "cw_orch_daemon::channel", - "cw-orch-daemon/src/channel.rs", - ), - 19u32, - ::log::__private_api::Option::None, - ); - } - }; - let endpoint = Channel::builder(address.clone().try_into().unwrap()); - let maybe_client = ServiceClient::connect(endpoint.clone()).await; - let mut client = if maybe_client.is_ok() { - maybe_client? - } else { - { - let lvl = ::log::Level::Warn; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Cannot connect to gRPC endpoint: {0}, {1:?}", - address, - maybe_client.unwrap_err(), - ), - lvl, - &( - "cw_orch_daemon::channel", - "cw_orch_daemon::channel", - "cw-orch-daemon/src/channel.rs", - ), - 31u32, - ::log::__private_api::Option::None, - ); - } - }; - if !(address.contains("https") || address.contains("443")) { - continue; - } - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Attempting to connect with TLS"), - lvl, - &( - "cw_orch_daemon::channel", - "cw_orch_daemon::channel", - "cw-orch-daemon/src/channel.rs", - ), - 43u32, - ::log::__private_api::Option::None, - ); - } - }; - let endpoint = endpoint.clone().tls_config(ClientTlsConfig::new())?; - let maybe_client = ServiceClient::connect(endpoint.clone()).await; - if maybe_client.is_err() { - { - let lvl = ::log::Level::Warn; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!( - "Cannot connect to gRPC endpoint: {0}, {1:?}", - address, - maybe_client.unwrap_err(), - ), - lvl, - &( - "cw_orch_daemon::channel", - "cw_orch_daemon::channel", - "cw-orch-daemon/src/channel.rs", - ), - 51u32, - ::log::__private_api::Option::None, - ); - } - }; - continue; - } - maybe_client? - }; - let node_info = client - .get_node_info(GetNodeInfoRequest {}) - .await? - .into_inner(); - if ChainId::is_epoch_format( - &node_info.default_node_info.as_ref().unwrap().network, - ) { - if node_info.default_node_info.as_ref().unwrap().network - != chain_id.as_str() - { - { - let lvl = ::log::Level::Error; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!( - "Network mismatch: connection:{0} != config:{1}", - node_info.default_node_info.as_ref().unwrap().network, - chain_id.as_str(), - ), - lvl, - &( - "cw_orch_daemon::channel", - "cw_orch_daemon::channel", - "cw-orch-daemon/src/channel.rs", - ), - 72u32, - ::log::__private_api::Option::None, - ); - } - }; - continue; - } - } - successful_connections.push(endpoint.connect().await?) - } - if successful_connections.is_empty() { - return Err(DaemonError::CannotConnectGRPC); - } - Ok(successful_connections.pop().unwrap()) - } - } -} -pub mod core { - use crate::{queriers::CosmWasm, DaemonState}; - use super::{ - builder::DaemonAsyncBuilder, cosmos_modules, error::DaemonError, - queriers::{DaemonQuerier, Node}, - sender::Wallet, tx_resp::CosmTxResponse, - }; - use cosmrs::{ - cosmwasm::{MsgExecuteContract, MsgInstantiateContract, MsgMigrateContract}, - tendermint::Time, AccountId, Denom, - }; - use cosmwasm_std::{Addr, Coin}; - use cw_orch_core::{ - contract::interface_traits::Uploadable, environment::{ChainState, IndexResponse}, - }; - use serde::{de::DeserializeOwned, Serialize}; - use serde_json::from_str; - use std::{ - fmt::Debug, rc::Rc, str::{from_utf8, FromStr}, - time::Duration, - }; - use tonic::transport::Channel; - /** - Represents a blockchain node. - It's constructed using [`DaemonAsyncBuilder`]. - - ## Usage - ```rust,no_run - # tokio_test::block_on(async { - use cw_orch_daemon::{DaemonAsync, networks}; - - let daemon: DaemonAsync = DaemonAsync::builder() - .chain(networks::JUNO_1) - .build() - .await.unwrap(); - # }) - ``` - ## Environment Execution - - The DaemonAsync implements async methods of [`TxHandler`](cw_orch_core::environment::TxHandler) which allows you to perform transactions on the chain. - - ## Querying - - Different Cosmos SDK modules can be queried through the daemon by calling the [`DaemonAsync::query_client`] method with a specific querier. - See [Querier](crate::queriers) for examples. -*/ - pub struct DaemonAsync { - /// Sender to send transactions to the chain - pub sender: Wallet, - /// State of the daemon - pub state: Rc, - } - #[automatically_derived] - impl ::core::clone::Clone for DaemonAsync { - #[inline] - fn clone(&self) -> DaemonAsync { - DaemonAsync { - sender: ::core::clone::Clone::clone(&self.sender), - state: ::core::clone::Clone::clone(&self.state), - } - } - } - impl DaemonAsync { - /// Get the daemon builder - pub fn builder() -> DaemonAsyncBuilder { - DaemonAsyncBuilder::default() - } - /// Perform a query with a given query client. - /// See [Querier](crate::queriers) for examples. - pub fn query_client(&self) -> Querier { - Querier::new(self.sender.channel()) - } - /// Get the channel configured for this DaemonAsync. - pub fn channel(&self) -> Channel { - self.state.grpc_channel.clone() - } - } - impl ChainState for DaemonAsync { - type Out = Rc; - fn state(&self) -> Self::Out { - self.state.clone() - } - } - impl DaemonAsync { - /// Get the sender address - pub fn sender(&self) -> Addr { - self.sender.address().unwrap() - } - /// Execute a message on a contract. - pub async fn execute( - &self, - exec_msg: &E, - coins: &[cosmwasm_std::Coin], - contract_address: &Addr, - ) -> Result { - let exec_msg: MsgExecuteContract = MsgExecuteContract { - sender: self.sender.pub_addr()?, - contract: AccountId::from_str(contract_address.as_str())?, - msg: serde_json::to_vec(&exec_msg)?, - funds: parse_cw_coins(coins)?, - }; - let result = self - .sender - .commit_tx( - <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([exec_msg])), - None, - ) - .await?; - Ok(result) - } - /// Instantiate a contract. - pub async fn instantiate( - &self, - code_id: u64, - init_msg: &I, - label: Option<&str>, - admin: Option<&Addr>, - coins: &[Coin], - ) -> Result { - let sender = &self.sender; - let init_msg = MsgInstantiateContract { - code_id, - label: Some(label.unwrap_or("instantiate_contract").to_string()), - admin: admin.map(|a| FromStr::from_str(a.as_str()).unwrap()), - sender: sender.pub_addr()?, - msg: serde_json::to_vec(&init_msg)?, - funds: parse_cw_coins(coins)?, - }; - let result = sender - .commit_tx( - <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([init_msg])), - None, - ) - .await?; - Ok(result) - } - /// Query a contract. - pub async fn query( - &self, - query_msg: &Q, - contract_address: &Addr, - ) -> Result { - let sender = &self.sender; - let mut client = cosmos_modules::cosmwasm::query_client::QueryClient::new( - sender.channel(), - ); - let resp = client - .smart_contract_state(cosmos_modules::cosmwasm::QuerySmartContractStateRequest { - address: contract_address.to_string(), - query_data: serde_json::to_vec(&query_msg)?, - }) - .await?; - Ok(from_str(from_utf8(&resp.into_inner().data).unwrap())?) - } - /// Migration a contract. - pub async fn migrate( - &self, - migrate_msg: &M, - new_code_id: u64, - contract_address: &Addr, - ) -> Result { - let exec_msg: MsgMigrateContract = MsgMigrateContract { - sender: self.sender.pub_addr()?, - contract: AccountId::from_str(contract_address.as_str())?, - msg: serde_json::to_vec(&migrate_msg)?, - code_id: new_code_id, - }; - let result = self - .sender - .commit_tx( - <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([exec_msg])), - None, - ) - .await?; - Ok(result) - } - /// Wait for a given amount of blocks. - pub async fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { - let mut last_height = self.query_client::().block_height().await?; - let end_height = last_height + amount; - let average_block_speed = self - .query_client::() - .average_block_speed(Some(0.9)) - .await?; - let wait_time = average_block_speed * amount; - tokio::time::sleep(Duration::from_secs(wait_time)).await; - while last_height < end_height { - tokio::time::sleep(Duration::from_secs(average_block_speed)).await; - last_height = self.query_client::().block_height().await?; - } - Ok(()) - } - /// Wait for a given amount of seconds. - pub async fn wait_seconds(&self, secs: u64) -> Result<(), DaemonError> { - tokio::time::sleep(Duration::from_secs(secs)).await; - Ok(()) - } - /// Wait for the next block. - pub async fn next_block(&self) -> Result<(), DaemonError> { - self.wait_blocks(1).await - } - /// Get the current block info. - pub async fn block_info(&self) -> Result { - let block = self.query_client::().latest_block().await?; - let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; - let time = cosmwasm_std::Timestamp::from_nanos( - since_epoch.as_nanos() as u64, - ); - Ok(cosmwasm_std::BlockInfo { - height: block.header.height.value(), - time, - chain_id: block.header.chain_id.to_string(), - }) - } - /// Upload a contract to the chain. - pub async fn upload( - &self, - uploadable: &impl Uploadable, - ) -> Result { - let sender = &self.sender; - let wasm_path = uploadable.wasm(); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Uploading file at {0:?}", wasm_path), - lvl, - &( - "cw_orch_daemon::core", - "cw_orch_daemon::core", - "cw-orch-daemon/src/core.rs", - ), - 233u32, - ::log::__private_api::Option::None, - ); - } - }; - let file_contents = std::fs::read(wasm_path.path())?; - let store_msg = cosmrs::cosmwasm::MsgStoreCode { - sender: sender.pub_addr()?, - wasm_byte_code: file_contents, - instantiate_permission: None, - }; - let result = sender - .commit_tx( - <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([store_msg])), - None, - ) - .await?; - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Uploaded: {0:?}", result.txhash), - lvl, - &( - "cw_orch_daemon::core", - "cw_orch_daemon::core", - "cw-orch-daemon/src/core.rs", - ), - 244u32, - ::log::__private_api::Option::None, - ); - } - }; - let code_id = result.uploaded_code_id().unwrap(); - let wasm = CosmWasm::new(self.channel()); - while wasm.code(code_id).await.is_err() { - self.next_block().await?; - } - Ok(result) - } - /// Set the sender to use with this DaemonAsync to be the given wallet - pub fn set_sender(&mut self, sender: &Wallet) { - self.sender = sender.clone(); - } - } - pub(crate) fn parse_cw_coins( - coins: &[cosmwasm_std::Coin], - ) -> Result, DaemonError> { - coins - .iter() - .map(|cosmwasm_std::Coin { amount, denom }| { - Ok(cosmrs::Coin { - amount: amount.u128(), - denom: Denom::from_str(denom)?, - }) - }) - .collect::, DaemonError>>() - } -} -pub mod error { - #![allow(missing_docs)] - use cw_orch_core::CwEnvError; - use thiserror::Error; - pub enum DaemonError { - #[error("Reqwest HTTP(s) Error")] - ReqwestError(#[from] ::reqwest::Error), - #[error("JSON Conversion Error")] - SerdeJson(#[from] ::serde_json::Error), - #[error(transparent)] - ParseIntError(#[from] std::num::ParseIntError), - #[error(transparent)] - IOErr(#[from] ::std::io::Error), - #[error(transparent)] - Secp256k1(#[from] ::secp256k1::Error), - #[error(transparent)] - VarError(#[from] ::std::env::VarError), - #[error(transparent)] - AnyError(#[from] ::anyhow::Error), - #[error(transparent)] - Status(#[from] ::tonic::Status), - #[error(transparent)] - TransportError(#[from] ::tonic::transport::Error), - #[error(transparent)] - TendermintError(#[from] ::cosmrs::tendermint::Error), - #[error(transparent)] - CwEnvError(#[from] ::cw_orch_core::CwEnvError), - #[error("Bech32 Decode Error")] - Bech32DecodeErr, - #[error( - "Bech32 Decode Error: Key Failed prefix {0} or length {1} Wanted:{2}/{3}" - )] - Bech32DecodeExpanded(String, usize, String, usize), - #[error("Mnemonic - Wrong length, it should be 24 words")] - WrongLength, - #[error("Mnemonic - Bad Phrase")] - Phrasing, - #[error("Mnemonic - Missing Phrase")] - MissingPhrase, - #[error("Bad Implementation. Missing Component")] - Implementation, - #[error("Unable to convert into public key `{key}`")] - Conversion { key: String, source: bitcoin::bech32::Error }, - #[error( - "Can not augment daemon deployment after usage in more than one contract." - )] - SharedDaemonState, - #[error(transparent)] - ErrReport(#[from] ::eyre::ErrReport), - #[error(transparent)] - GRpcDecodeError(#[from] ::prost::DecodeError), - #[error(transparent)] - ED25519(#[from] ::ed25519_dalek::ed25519::Error), - #[error(transparent)] - DecodeError(#[from] ::base64::DecodeError), - #[error(transparent)] - HexError(#[from] ::hex::FromHexError), - #[error(transparent)] - BitCoinBip32(#[from] ::bitcoin::bip32::Error), - #[error("83 length-missing SECP256K1 prefix")] - ConversionSECP256k1, - #[error("82 length-missing ED25519 prefix")] - ConversionED25519, - #[error("Expected Key length of 82 or 83 length was {0}")] - ConversionLength(usize), - #[error("Expected Key length of 40 length was {0}")] - ConversionLengthED25519Hex(usize), - #[error( - "Expected ED25519 key of length 32 with a BECH32 ED25519 prefix of 5 chars - Len {0} - Hex {1}" - )] - ConversionPrefixED25519(usize, String), - #[error("Can't call Transactions without some gas rules")] - NoGasOpts, - #[error("Can't parse `{parse}` into a coin")] - CoinParseErrV { parse: String }, - #[error("Can't parse `{0}` into a coin")] - CoinParseErr(String), - #[error("TX submit returned `{0}` - {1} '{2}'")] - TxResultError(usize, String, String), - #[error("No price found for Gas using denom {0}")] - GasPriceError(String), - #[error( - "Attempting to fetch validator set in parts, and failed Height mismatch {0} {1}" - )] - TendermintValidatorSet(u64, u64), - #[error("Transaction {0} not found after {1} attempts")] - TXNotFound(String, usize), - #[error("unknown API error")] - Unknown, - #[error("Generic Error {0}")] - StdErr(String), - #[error("calling contract with unimplemented action")] - NotImplemented, - #[error("new chain detected, fill out the scaffold at {0}")] - NewChain(String), - #[error("new network detected, fill out the scaffold at {0}")] - NewNetwork(String), - #[error("Can not connect to any grpc endpoint that was provided.")] - CannotConnectGRPC, - #[error("tx failed: {reason} with code {code}")] - TxFailed { code: usize, reason: String }, - #[error("The list of grpc endpoints is empty")] - GRPCListIsEmpty, - #[error("no wasm path provided for contract.")] - MissingWasmPath, - #[error("daemon builder missing {0}")] - BuilderMissing(String), - #[error("ibc error: {0}")] - IbcError(String), - #[error("insufficient fee, check gas price: {0}")] - InsufficientFee(String), - } - #[allow(unused_qualifications)] - impl std::error::Error for DaemonError { - fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> { - use thiserror::__private::AsDynError; - #[allow(deprecated)] - match self { - DaemonError::ReqwestError { 0: source, .. } => { - std::option::Option::Some(source.as_dyn_error()) - } - DaemonError::SerdeJson { 0: source, .. } => { - std::option::Option::Some(source.as_dyn_error()) - } - DaemonError::ParseIntError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::IOErr { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::Secp256k1 { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::VarError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::AnyError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::Status { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::TransportError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::TendermintError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::CwEnvError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::Bech32DecodeErr { .. } => std::option::Option::None, - DaemonError::Bech32DecodeExpanded { .. } => std::option::Option::None, - DaemonError::WrongLength { .. } => std::option::Option::None, - DaemonError::Phrasing { .. } => std::option::Option::None, - DaemonError::MissingPhrase { .. } => std::option::Option::None, - DaemonError::Implementation { .. } => std::option::Option::None, - DaemonError::Conversion { source: source, .. } => { - std::option::Option::Some(source.as_dyn_error()) - } - DaemonError::SharedDaemonState { .. } => std::option::Option::None, - DaemonError::ErrReport { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::GRpcDecodeError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::ED25519 { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::DecodeError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::HexError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::BitCoinBip32 { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::ConversionSECP256k1 { .. } => std::option::Option::None, - DaemonError::ConversionED25519 { .. } => std::option::Option::None, - DaemonError::ConversionLength { .. } => std::option::Option::None, - DaemonError::ConversionLengthED25519Hex { .. } => { - std::option::Option::None - } - DaemonError::ConversionPrefixED25519 { .. } => std::option::Option::None, - DaemonError::NoGasOpts { .. } => std::option::Option::None, - DaemonError::CoinParseErrV { .. } => std::option::Option::None, - DaemonError::CoinParseErr { .. } => std::option::Option::None, - DaemonError::TxResultError { .. } => std::option::Option::None, - DaemonError::GasPriceError { .. } => std::option::Option::None, - DaemonError::TendermintValidatorSet { .. } => std::option::Option::None, - DaemonError::TXNotFound { .. } => std::option::Option::None, - DaemonError::Unknown { .. } => std::option::Option::None, - DaemonError::StdErr { .. } => std::option::Option::None, - DaemonError::NotImplemented { .. } => std::option::Option::None, - DaemonError::NewChain { .. } => std::option::Option::None, - DaemonError::NewNetwork { .. } => std::option::Option::None, - DaemonError::CannotConnectGRPC { .. } => std::option::Option::None, - DaemonError::TxFailed { .. } => std::option::Option::None, - DaemonError::GRPCListIsEmpty { .. } => std::option::Option::None, - DaemonError::MissingWasmPath { .. } => std::option::Option::None, - DaemonError::BuilderMissing { .. } => std::option::Option::None, - DaemonError::IbcError { .. } => std::option::Option::None, - DaemonError::InsufficientFee { .. } => std::option::Option::None, - } - } - } - #[allow(unused_qualifications)] - impl std::fmt::Display for DaemonError { - fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - use thiserror::__private::AsDisplay as _; - #[allow(unused_variables, deprecated, clippy::used_underscore_binding)] - match self { - DaemonError::ReqwestError(_0) => { - __formatter.write_fmt(format_args!("Reqwest HTTP(s) Error")) - } - DaemonError::SerdeJson(_0) => { - __formatter.write_fmt(format_args!("JSON Conversion Error")) - } - DaemonError::ParseIntError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::IOErr(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::Secp256k1(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::VarError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::AnyError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::Status(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::TransportError(_0) => { - std::fmt::Display::fmt(_0, __formatter) - } - DaemonError::TendermintError(_0) => { - std::fmt::Display::fmt(_0, __formatter) - } - DaemonError::CwEnvError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::Bech32DecodeErr {} => { - __formatter.write_fmt(format_args!("Bech32 Decode Error")) - } - DaemonError::Bech32DecodeExpanded(_0, _1, _2, _3) => { - __formatter - .write_fmt( - format_args!( - "Bech32 Decode Error: Key Failed prefix {0} or length {1} Wanted:{2}/{3}", - _0.as_display(), - _1.as_display(), - _2.as_display(), - _3.as_display(), - ), - ) - } - DaemonError::WrongLength {} => { - __formatter - .write_fmt( - format_args!( - "Mnemonic - Wrong length, it should be 24 words", - ), - ) - } - DaemonError::Phrasing {} => { - __formatter.write_fmt(format_args!("Mnemonic - Bad Phrase")) - } - DaemonError::MissingPhrase {} => { - __formatter.write_fmt(format_args!("Mnemonic - Missing Phrase")) - } - DaemonError::Implementation {} => { - __formatter - .write_fmt(format_args!("Bad Implementation. Missing Component")) - } - DaemonError::Conversion { key, source } => { - __formatter - .write_fmt( - format_args!( - "Unable to convert into public key `{0}`", - key.as_display(), - ), - ) - } - DaemonError::SharedDaemonState {} => { - __formatter - .write_fmt( - format_args!( - "Can not augment daemon deployment after usage in more than one contract.", - ), - ) - } - DaemonError::ErrReport(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::GRpcDecodeError(_0) => { - std::fmt::Display::fmt(_0, __formatter) - } - DaemonError::ED25519(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::DecodeError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::HexError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::BitCoinBip32(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::ConversionSECP256k1 {} => { - __formatter - .write_fmt(format_args!("83 length-missing SECP256K1 prefix")) - } - DaemonError::ConversionED25519 {} => { - __formatter - .write_fmt(format_args!("82 length-missing ED25519 prefix")) - } - DaemonError::ConversionLength(_0) => { - __formatter - .write_fmt( - format_args!( - "Expected Key length of 82 or 83 length was {0}", - _0.as_display(), - ), - ) - } - DaemonError::ConversionLengthED25519Hex(_0) => { - __formatter - .write_fmt( - format_args!( - "Expected Key length of 40 length was {0}", - _0.as_display(), - ), - ) - } - DaemonError::ConversionPrefixED25519(_0, _1) => { - __formatter - .write_fmt( - format_args!( - "Expected ED25519 key of length 32 with a BECH32 ED25519 prefix of 5 chars - Len {0} - Hex {1}", - _0.as_display(), - _1.as_display(), - ), - ) - } - DaemonError::NoGasOpts {} => { - __formatter - .write_fmt( - format_args!( - "Can\'t call Transactions without some gas rules", - ), - ) - } - DaemonError::CoinParseErrV { parse } => { - __formatter - .write_fmt( - format_args!( - "Can\'t parse `{0}` into a coin", - parse.as_display(), - ), - ) - } - DaemonError::CoinParseErr(_0) => { - __formatter - .write_fmt( - format_args!( - "Can\'t parse `{0}` into a coin", - _0.as_display(), - ), - ) - } - DaemonError::TxResultError(_0, _1, _2) => { - __formatter - .write_fmt( - format_args!( - "TX submit returned `{0}` - {1} \'{2}\'", - _0.as_display(), - _1.as_display(), - _2.as_display(), - ), - ) - } - DaemonError::GasPriceError(_0) => { - __formatter - .write_fmt( - format_args!( - "No price found for Gas using denom {0}", - _0.as_display(), - ), - ) - } - DaemonError::TendermintValidatorSet(_0, _1) => { - __formatter - .write_fmt( - format_args!( - "Attempting to fetch validator set in parts, and failed Height mismatch {0} {1}", - _0.as_display(), - _1.as_display(), - ), - ) - } - DaemonError::TXNotFound(_0, _1) => { - __formatter - .write_fmt( - format_args!( - "Transaction {0} not found after {1} attempts", - _0.as_display(), - _1.as_display(), - ), - ) - } - DaemonError::Unknown {} => { - __formatter.write_fmt(format_args!("unknown API error")) - } - DaemonError::StdErr(_0) => { - __formatter - .write_fmt(format_args!("Generic Error {0}", _0.as_display())) - } - DaemonError::NotImplemented {} => { - __formatter - .write_fmt( - format_args!("calling contract with unimplemented action"), - ) - } - DaemonError::NewChain(_0) => { - __formatter - .write_fmt( - format_args!( - "new chain detected, fill out the scaffold at {0}", - _0.as_display(), - ), - ) - } - DaemonError::NewNetwork(_0) => { - __formatter - .write_fmt( - format_args!( - "new network detected, fill out the scaffold at {0}", - _0.as_display(), - ), - ) - } - DaemonError::CannotConnectGRPC {} => { - __formatter - .write_fmt( - format_args!( - "Can not connect to any grpc endpoint that was provided.", - ), - ) - } - DaemonError::TxFailed { code, reason } => { - __formatter - .write_fmt( - format_args!( - "tx failed: {0} with code {1}", - reason.as_display(), - code.as_display(), - ), - ) - } - DaemonError::GRPCListIsEmpty {} => { - __formatter - .write_fmt(format_args!("The list of grpc endpoints is empty")) - } - DaemonError::MissingWasmPath {} => { - __formatter - .write_fmt(format_args!("no wasm path provided for contract.")) - } - DaemonError::BuilderMissing(_0) => { - __formatter - .write_fmt( - format_args!("daemon builder missing {0}", _0.as_display()), - ) - } - DaemonError::IbcError(_0) => { - __formatter - .write_fmt(format_args!("ibc error: {0}", _0.as_display())) - } - DaemonError::InsufficientFee(_0) => { - __formatter - .write_fmt( - format_args!( - "insufficient fee, check gas price: {0}", - _0.as_display(), - ), - ) - } - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::reqwest::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::reqwest::Error) -> Self { - DaemonError::ReqwestError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::serde_json::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::serde_json::Error) -> Self { - DaemonError::SerdeJson { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From for DaemonError { - #[allow(deprecated)] - fn from(source: std::num::ParseIntError) -> Self { - DaemonError::ParseIntError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::std::io::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::std::io::Error) -> Self { - DaemonError::IOErr { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::secp256k1::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::secp256k1::Error) -> Self { - DaemonError::Secp256k1 { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::std::env::VarError> for DaemonError { - #[allow(deprecated)] - fn from(source: ::std::env::VarError) -> Self { - DaemonError::VarError { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::anyhow::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::anyhow::Error) -> Self { - DaemonError::AnyError { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::tonic::Status> for DaemonError { - #[allow(deprecated)] - fn from(source: ::tonic::Status) -> Self { - DaemonError::Status { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::tonic::transport::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::tonic::transport::Error) -> Self { - DaemonError::TransportError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::cosmrs::tendermint::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::cosmrs::tendermint::Error) -> Self { - DaemonError::TendermintError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::cw_orch_core::CwEnvError> for DaemonError { - #[allow(deprecated)] - fn from(source: ::cw_orch_core::CwEnvError) -> Self { - DaemonError::CwEnvError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::eyre::ErrReport> for DaemonError { - #[allow(deprecated)] - fn from(source: ::eyre::ErrReport) -> Self { - DaemonError::ErrReport { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::prost::DecodeError> for DaemonError { - #[allow(deprecated)] - fn from(source: ::prost::DecodeError) -> Self { - DaemonError::GRpcDecodeError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::ed25519_dalek::ed25519::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::ed25519_dalek::ed25519::Error) -> Self { - DaemonError::ED25519 { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::base64::DecodeError> for DaemonError { - #[allow(deprecated)] - fn from(source: ::base64::DecodeError) -> Self { - DaemonError::DecodeError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::hex::FromHexError> for DaemonError { - #[allow(deprecated)] - fn from(source: ::hex::FromHexError) -> Self { - DaemonError::HexError { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::bitcoin::bip32::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::bitcoin::bip32::Error) -> Self { - DaemonError::BitCoinBip32 { - 0: source, - } - } - } - #[automatically_derived] - impl ::core::fmt::Debug for DaemonError { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match self { - DaemonError::ReqwestError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ReqwestError", - &__self_0, - ) - } - DaemonError::SerdeJson(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "SerdeJson", - &__self_0, - ) - } - DaemonError::ParseIntError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ParseIntError", - &__self_0, - ) - } - DaemonError::IOErr(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "IOErr", - &__self_0, - ) - } - DaemonError::Secp256k1(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "Secp256k1", - &__self_0, - ) - } - DaemonError::VarError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "VarError", - &__self_0, - ) - } - DaemonError::AnyError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "AnyError", - &__self_0, - ) - } - DaemonError::Status(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "Status", - &__self_0, - ) - } - DaemonError::TransportError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "TransportError", - &__self_0, - ) - } - DaemonError::TendermintError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "TendermintError", - &__self_0, - ) - } - DaemonError::CwEnvError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "CwEnvError", - &__self_0, - ) - } - DaemonError::Bech32DecodeErr => { - ::core::fmt::Formatter::write_str(f, "Bech32DecodeErr") - } - DaemonError::Bech32DecodeExpanded( - __self_0, - __self_1, - __self_2, - __self_3, - ) => { - ::core::fmt::Formatter::debug_tuple_field4_finish( - f, - "Bech32DecodeExpanded", - __self_0, - __self_1, - __self_2, - &__self_3, - ) - } - DaemonError::WrongLength => { - ::core::fmt::Formatter::write_str(f, "WrongLength") - } - DaemonError::Phrasing => ::core::fmt::Formatter::write_str(f, "Phrasing"), - DaemonError::MissingPhrase => { - ::core::fmt::Formatter::write_str(f, "MissingPhrase") - } - DaemonError::Implementation => { - ::core::fmt::Formatter::write_str(f, "Implementation") - } - DaemonError::Conversion { key: __self_0, source: __self_1 } => { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "Conversion", - "key", - __self_0, - "source", - &__self_1, - ) - } - DaemonError::SharedDaemonState => { - ::core::fmt::Formatter::write_str(f, "SharedDaemonState") - } - DaemonError::ErrReport(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ErrReport", - &__self_0, - ) - } - DaemonError::GRpcDecodeError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "GRpcDecodeError", - &__self_0, - ) - } - DaemonError::ED25519(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ED25519", - &__self_0, - ) - } - DaemonError::DecodeError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "DecodeError", - &__self_0, - ) - } - DaemonError::HexError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "HexError", - &__self_0, - ) - } - DaemonError::BitCoinBip32(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "BitCoinBip32", - &__self_0, - ) - } - DaemonError::ConversionSECP256k1 => { - ::core::fmt::Formatter::write_str(f, "ConversionSECP256k1") - } - DaemonError::ConversionED25519 => { - ::core::fmt::Formatter::write_str(f, "ConversionED25519") - } - DaemonError::ConversionLength(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ConversionLength", - &__self_0, - ) - } - DaemonError::ConversionLengthED25519Hex(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ConversionLengthED25519Hex", - &__self_0, - ) - } - DaemonError::ConversionPrefixED25519(__self_0, __self_1) => { - ::core::fmt::Formatter::debug_tuple_field2_finish( - f, - "ConversionPrefixED25519", - __self_0, - &__self_1, - ) - } - DaemonError::NoGasOpts => { - ::core::fmt::Formatter::write_str(f, "NoGasOpts") - } - DaemonError::CoinParseErrV { parse: __self_0 } => { - ::core::fmt::Formatter::debug_struct_field1_finish( - f, - "CoinParseErrV", - "parse", - &__self_0, - ) - } - DaemonError::CoinParseErr(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "CoinParseErr", - &__self_0, - ) - } - DaemonError::TxResultError(__self_0, __self_1, __self_2) => { - ::core::fmt::Formatter::debug_tuple_field3_finish( - f, - "TxResultError", - __self_0, - __self_1, - &__self_2, - ) - } - DaemonError::GasPriceError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "GasPriceError", - &__self_0, - ) - } - DaemonError::TendermintValidatorSet(__self_0, __self_1) => { - ::core::fmt::Formatter::debug_tuple_field2_finish( - f, - "TendermintValidatorSet", - __self_0, - &__self_1, - ) - } - DaemonError::TXNotFound(__self_0, __self_1) => { - ::core::fmt::Formatter::debug_tuple_field2_finish( - f, - "TXNotFound", - __self_0, - &__self_1, - ) - } - DaemonError::Unknown => ::core::fmt::Formatter::write_str(f, "Unknown"), - DaemonError::StdErr(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "StdErr", - &__self_0, - ) - } - DaemonError::NotImplemented => { - ::core::fmt::Formatter::write_str(f, "NotImplemented") - } - DaemonError::NewChain(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "NewChain", - &__self_0, - ) - } - DaemonError::NewNetwork(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "NewNetwork", - &__self_0, - ) - } - DaemonError::CannotConnectGRPC => { - ::core::fmt::Formatter::write_str(f, "CannotConnectGRPC") - } - DaemonError::TxFailed { code: __self_0, reason: __self_1 } => { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "TxFailed", - "code", - __self_0, - "reason", - &__self_1, - ) - } - DaemonError::GRPCListIsEmpty => { - ::core::fmt::Formatter::write_str(f, "GRPCListIsEmpty") - } - DaemonError::MissingWasmPath => { - ::core::fmt::Formatter::write_str(f, "MissingWasmPath") - } - DaemonError::BuilderMissing(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "BuilderMissing", - &__self_0, - ) - } - DaemonError::IbcError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "IbcError", - &__self_0, - ) - } - DaemonError::InsufficientFee(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "InsufficientFee", - &__self_0, - ) - } - } - } - } - impl DaemonError { - pub fn ibc_err(msg: impl ToString) -> Self { - Self::IbcError(msg.to_string()) - } - } - impl From for CwEnvError { - fn from(val: DaemonError) -> Self { - CwEnvError::AnyError(val.into()) - } - } -} -pub(crate) mod json_file { - use serde_json::{from_reader, json, Value}; - use std::fs::{File, OpenOptions}; - pub fn write( - filename: &String, - chain_id: &String, - network_id: &String, - deploy_id: &String, - ) { - let file = OpenOptions::new() - .create(true) - .read(true) - .write(true) - .truncate(false) - .open(filename) - .unwrap(); - let mut json: Value = if file.metadata().unwrap().len().eq(&0) { - ::serde_json::Value::Object(::serde_json::Map::new()) - } else { - from_reader(file).unwrap() - }; - if json.get(network_id).is_none() { - json[network_id] = ::serde_json::Value::Object(::serde_json::Map::new()); - } - if json[network_id].get(chain_id).is_none() { - json[network_id][chain_id] = ::serde_json::Value::Object({ - let mut object = ::serde_json::Map::new(); - let _ = object - .insert( - (deploy_id).into(), - ::serde_json::Value::Object(::serde_json::Map::new()), - ); - let _ = object - .insert( - ("code_ids").into(), - ::serde_json::Value::Object(::serde_json::Map::new()), - ); - object - }); - } - serde_json::to_writer_pretty(File::create(filename).unwrap(), &json).unwrap(); - } - pub fn read(filename: &String) -> Value { - let file = File::open(filename) - .unwrap_or_else(|_| { - ::core::panicking::panic_fmt( - format_args!("File should be present at {0}", filename), - ); - }); - let json: serde_json::Value = from_reader(file).unwrap(); - json - } -} -/// Proto types for different blockchains -pub mod proto { - pub mod injective { - #![allow(missing_docs)] - use crate::DaemonError; - use cosmrs::tx::SignDoc; - use cosmrs::{proto::traits::TypeUrl, tx::Raw}; - pub const ETHEREUM_COIN_TYPE: u32 = 60; - pub struct InjectiveEthAccount { - #[prost(message, optional, tag = "1")] - pub base_account: ::core::option::Option< - super::super::cosmos_modules::auth::BaseAccount, - >, - #[prost(bytes, tag = "2")] - pub code_hash: Vec, - } - #[automatically_derived] - impl ::core::clone::Clone for InjectiveEthAccount { - #[inline] - fn clone(&self) -> InjectiveEthAccount { - InjectiveEthAccount { - base_account: ::core::clone::Clone::clone(&self.base_account), - code_hash: ::core::clone::Clone::clone(&self.code_hash), - } - } - } - #[automatically_derived] - impl ::core::marker::StructuralPartialEq for InjectiveEthAccount {} - #[automatically_derived] - impl ::core::cmp::PartialEq for InjectiveEthAccount { - #[inline] - fn eq(&self, other: &InjectiveEthAccount) -> bool { - self.base_account == other.base_account - && self.code_hash == other.code_hash - } - } - impl ::prost::Message for InjectiveEthAccount { - #[allow(unused_variables)] - fn encode_raw(&self, buf: &mut B) - where - B: ::prost::bytes::BufMut, - { - if let Some(ref msg) = self.base_account { - ::prost::encoding::message::encode(1u32, msg, buf); - } - if self.code_hash != b"" as &[u8] { - ::prost::encoding::bytes::encode(2u32, &self.code_hash, buf); - } - } - #[allow(unused_variables)] - fn merge_field( - &mut self, - tag: u32, - wire_type: ::prost::encoding::WireType, - buf: &mut B, - ctx: ::prost::encoding::DecodeContext, - ) -> ::core::result::Result<(), ::prost::DecodeError> - where - B: ::prost::bytes::Buf, - { - const STRUCT_NAME: &'static str = "InjectiveEthAccount"; - match tag { - 1u32 => { - let mut value = &mut self.base_account; - ::prost::encoding::message::merge( - wire_type, - value.get_or_insert_with(::core::default::Default::default), - buf, - ctx, - ) - .map_err(|mut error| { - error.push(STRUCT_NAME, "base_account"); - error - }) - } - 2u32 => { - let mut value = &mut self.code_hash; - ::prost::encoding::bytes::merge(wire_type, value, buf, ctx) - .map_err(|mut error| { - error.push(STRUCT_NAME, "code_hash"); - error - }) - } - _ => ::prost::encoding::skip_field(wire_type, tag, buf, ctx), - } - } - #[inline] - fn encoded_len(&self) -> usize { - 0 - + self - .base_account - .as_ref() - .map_or( - 0, - |msg| ::prost::encoding::message::encoded_len(1u32, msg), - ) - + if self.code_hash != b"" as &[u8] { - ::prost::encoding::bytes::encoded_len(2u32, &self.code_hash) - } else { - 0 - } - } - fn clear(&mut self) { - self.base_account = ::core::option::Option::None; - self.code_hash.clear(); - } - } - impl ::core::default::Default for InjectiveEthAccount { - fn default() -> Self { - InjectiveEthAccount { - base_account: ::core::default::Default::default(), - code_hash: ::core::default::Default::default(), - } - } - } - impl ::core::fmt::Debug for InjectiveEthAccount { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - let mut builder = f.debug_struct("InjectiveEthAccount"); - let builder = { - let wrapper = &self.base_account; - builder.field("base_account", &wrapper) - }; - let builder = { - let wrapper = { - fn ScalarWrapper(v: T) -> T { - v - } - ScalarWrapper(&self.code_hash) - }; - builder.field("code_hash", &wrapper) - }; - builder.finish() - } - } - pub struct InjectivePubKey { - #[prost(bytes, tag = 1)] - pub key: Vec, - } - #[automatically_derived] - impl ::core::clone::Clone for InjectivePubKey { - #[inline] - fn clone(&self) -> InjectivePubKey { - InjectivePubKey { - key: ::core::clone::Clone::clone(&self.key), - } - } - } - #[automatically_derived] - impl ::core::marker::StructuralPartialEq for InjectivePubKey {} - #[automatically_derived] - impl ::core::cmp::PartialEq for InjectivePubKey { - #[inline] - fn eq(&self, other: &InjectivePubKey) -> bool { - self.key == other.key - } - } - impl ::prost::Message for InjectivePubKey { - #[allow(unused_variables)] - fn encode_raw(&self, buf: &mut B) - where - B: ::prost::bytes::BufMut, - { - if self.key != b"" as &[u8] { - ::prost::encoding::bytes::encode(1u32, &self.key, buf); - } - } - #[allow(unused_variables)] - fn merge_field( - &mut self, - tag: u32, - wire_type: ::prost::encoding::WireType, - buf: &mut B, - ctx: ::prost::encoding::DecodeContext, - ) -> ::core::result::Result<(), ::prost::DecodeError> - where - B: ::prost::bytes::Buf, - { - const STRUCT_NAME: &'static str = "InjectivePubKey"; - match tag { - 1u32 => { - let mut value = &mut self.key; - ::prost::encoding::bytes::merge(wire_type, value, buf, ctx) - .map_err(|mut error| { - error.push(STRUCT_NAME, "key"); - error - }) - } - _ => ::prost::encoding::skip_field(wire_type, tag, buf, ctx), - } - } - #[inline] - fn encoded_len(&self) -> usize { - 0 - + if self.key != b"" as &[u8] { - ::prost::encoding::bytes::encoded_len(1u32, &self.key) - } else { - 0 - } - } - fn clear(&mut self) { - self.key.clear(); - } - } - impl ::core::default::Default for InjectivePubKey { - fn default() -> Self { - InjectivePubKey { - key: ::core::default::Default::default(), - } - } - } - impl ::core::fmt::Debug for InjectivePubKey { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - let mut builder = f.debug_struct("InjectivePubKey"); - let builder = { - let wrapper = { - fn ScalarWrapper(v: T) -> T { - v - } - ScalarWrapper(&self.key) - }; - builder.field("key", &wrapper) - }; - builder.finish() - } - } - impl TypeUrl for InjectivePubKey { - const TYPE_URL: &'static str = "/injective.crypto.v1beta1.ethsecp256k1.PubKey"; - } - pub trait InjectiveSigner { - fn sign_injective(&self, sign_doc: SignDoc) -> Result; - } - } -} -pub mod sender { - use crate::{networks::ChainKind, proto::injective::ETHEREUM_COIN_TYPE}; - use super::{ - cosmos_modules::{self, auth::BaseAccount}, - error::DaemonError, queriers::{DaemonQuerier, Node}, - state::DaemonState, tx_builder::TxBuilder, tx_resp::CosmTxResponse, - }; - use crate::proto::injective::InjectiveEthAccount; - use crate::{core::parse_cw_coins, keys::private::PrivateKey}; - use cosmrs::{ - bank::MsgSend, crypto::secp256k1::SigningKey, proto::traits::Message, - tendermint::chain::Id, - tx::{self, ModeInfo, Msg, Raw, SignDoc, SignMode, SignerInfo}, - AccountId, - }; - use cosmwasm_std::Addr; - use secp256k1::{All, Context, Secp256k1, Signing}; - use std::{convert::TryFrom, env, rc::Rc, str::FromStr}; - use cosmos_modules::vesting::PeriodicVestingAccount; - use tonic::transport::Channel; - /// A wallet is a sender of transactions, can be safely cloned and shared within the same thread. - pub type Wallet = Rc>; - /// Signer of the transactions and helper for address derivation - /// This is the main interface for simulating and signing transactions - pub struct Sender { - pub private_key: PrivateKey, - pub secp: Secp256k1, - pub(crate) daemon_state: Rc, - } - impl Sender { - pub fn new(daemon_state: &Rc) -> Result, DaemonError> { - let kind = ChainKind::from(daemon_state.chain_data.network_type.clone()); - let mnemonic = env::var(kind.mnemonic_name()) - .unwrap_or_else(|_| { - { - ::core::panicking::panic_fmt( - format_args!( - "Wallet mnemonic environment variable {0} not set.", - kind.mnemonic_name(), - ), - ); - } - }); - Self::from_mnemonic(daemon_state, &mnemonic) - } - /// Construct a new Sender from a mnemonic - pub fn from_mnemonic( - daemon_state: &Rc, - mnemonic: &str, - ) -> Result, DaemonError> { - let secp = Secp256k1::new(); - let p_key: PrivateKey = PrivateKey::from_words( - &secp, - mnemonic, - 0, - 0, - daemon_state.chain_data.slip44, - )?; - let sender = Sender { - daemon_state: daemon_state.clone(), - private_key: p_key, - secp, - }; - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Interacting with {0} using address: {1}", - daemon_state.chain_data.chain_id, - sender.pub_addr_str()?, - ), - lvl, - &( - "cw_orch_daemon::sender", - "cw_orch_daemon::sender", - "cw-orch-daemon/src/sender.rs", - ), - 71u32, - ::log::__private_api::Option::None, - ); - } - }; - Ok(sender) - } - fn cosmos_private_key(&self) -> SigningKey { - SigningKey::from_slice(&self.private_key.raw_key()).unwrap() - } - pub fn channel(&self) -> Channel { - self.daemon_state.grpc_channel.clone() - } - pub fn pub_addr(&self) -> Result { - Ok( - AccountId::new( - &self.daemon_state.chain_data.bech32_prefix, - &self.private_key.public_key(&self.secp).raw_address.unwrap(), - )?, - ) - } - pub fn address(&self) -> Result { - Ok(Addr::unchecked(self.pub_addr_str()?)) - } - pub fn pub_addr_str(&self) -> Result { - Ok(self.pub_addr()?.to_string()) - } - pub async fn bank_send( - &self, - recipient: &str, - coins: Vec, - ) -> Result { - let msg_send = MsgSend { - from_address: self.pub_addr()?, - to_address: AccountId::from_str(recipient)?, - amount: parse_cw_coins(&coins)?, - }; - self.commit_tx( - <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([msg_send])), - Some("sending tokens"), - ) - .await - } - pub async fn calculate_gas( - &self, - tx_body: &tx::Body, - sequence: u64, - account_number: u64, - ) -> Result { - let fee = TxBuilder::build_fee( - 0u8, - &self.daemon_state.chain_data.fees.fee_tokens[0].denom, - 0, - ); - let auth_info = SignerInfo { - public_key: self.private_key.get_signer_public_key(&self.secp), - mode_info: ModeInfo::single(SignMode::Direct), - sequence, - } - .auth_info(fee); - let sign_doc = SignDoc::new( - tx_body, - &auth_info, - &Id::try_from(self.daemon_state.chain_data.chain_id.to_string())?, - account_number, - )?; - let tx_raw = self.sign(sign_doc)?; - Node::new(self.channel()).simulate_tx(tx_raw.to_bytes()?).await - } - pub async fn commit_tx( - &self, - msgs: Vec, - memo: Option<&str>, - ) -> Result { - let timeout_height = Node::new(self.channel()).block_height().await? + 10u64; - let tx_body = TxBuilder::build_body(msgs, memo, timeout_height); - let mut tx_builder = TxBuilder::new(tx_body); - let tx = tx_builder.build(self).await?; - let mut tx_response = self.broadcast_tx(tx).await?; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("tx broadcast response: {0:?}", tx_response), - lvl, - &( - "cw_orch_daemon::sender", - "cw_orch_daemon::sender", - "cw-orch-daemon/src/sender.rs", - ), - 166u32, - ::log::__private_api::Option::None, - ); - } - }; - if has_insufficient_fee(&tx_response.raw_log) { - let suggested_fee = parse_suggested_fee(&tx_response.raw_log); - let Some(new_fee) = suggested_fee else { - return Err(DaemonError::InsufficientFee(tx_response.raw_log)); - }; - tx_builder.fee_amount(new_fee); - let tx = tx_builder.build(self).await?; - tx_response = self.broadcast_tx(tx).await?; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("tx broadcast response: {0:?}", tx_response), - lvl, - &( - "cw_orch_daemon::sender", - "cw_orch_daemon::sender", - "cw-orch-daemon/src/sender.rs", - ), - 181u32, - ::log::__private_api::Option::None, - ); - } - }; - } - let resp = Node::new(self.channel()).find_tx(tx_response.txhash).await?; - if resp.code == 0 { - Ok(resp) - } else { - Err(DaemonError::TxFailed { - code: resp.code, - reason: resp.raw_log, - }) - } - } - pub fn sign(&self, sign_doc: SignDoc) -> Result { - let tx_raw = if self.private_key.coin_type == ETHEREUM_COIN_TYPE { - { - ::core::panicking::panic_fmt( - format_args!( - "Coin Type {0} not supported without eth feature", - ETHEREUM_COIN_TYPE, - ), - ); - }; - } else { - sign_doc.sign(&self.cosmos_private_key())? - }; - Ok(tx_raw) - } - pub async fn base_account(&self) -> Result { - let addr = self.pub_addr().unwrap().to_string(); - let mut client = cosmos_modules::auth::query_client::QueryClient::new( - self.channel(), - ); - let resp = client - .account(cosmos_modules::auth::QueryAccountRequest { - address: addr, - }) - .await? - .into_inner(); - let account = resp.account.unwrap().value; - let acc = if let Ok(acc) = BaseAccount::decode(account.as_ref()) { - acc - } else if let Ok(acc) = PeriodicVestingAccount::decode(account.as_ref()) { - acc.base_vesting_account.unwrap().base_account.unwrap() - } else if let Ok(acc) = InjectiveEthAccount::decode(account.as_ref()) { - acc.base_account.unwrap() - } else { - return Err( - DaemonError::StdErr( - "Unknown account type returned from QueryAccountRequest".into(), - ), - ); - }; - Ok(acc) - } - async fn broadcast_tx( - &self, - tx: Raw, - ) -> Result< - cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse, - DaemonError, - > { - let mut client = cosmos_modules::tx::service_client::ServiceClient::new( - self.channel(), - ); - let commit = client - .broadcast_tx(cosmos_modules::tx::BroadcastTxRequest { - tx_bytes: tx.to_bytes()?, - mode: cosmos_modules::tx::BroadcastMode::Sync.into(), - }) - .await?; - let commit = commit.into_inner().tx_response.unwrap(); - Ok(commit) - } - } - fn has_insufficient_fee(raw_log: &str) -> bool { - raw_log.contains("insufficient fees") - } - fn parse_suggested_fee(raw_log: &str) -> Option { - let parts: Vec<&str> = raw_log.split("required: ").collect(); - if parts.len() != 2 { - return None; - } - let got_parts: Vec<&str> = parts[0].split_whitespace().collect(); - let paid_fee_with_denom = got_parts.last()?; - let (_, denomination) = paid_fee_with_denom - .split_at(paid_fee_with_denom.find(|c: char| !c.is_numeric())?); - { - ::std::io::_eprint(format_args!("denom: {0}\n", denomination)); - }; - let required_fees: Vec<&str> = parts[1].split(denomination).collect(); - { - ::std::io::_eprint(format_args!("required fees: {0:?}\n", required_fees)); - }; - let (_, suggested_fee) = required_fees[0] - .split_at(required_fees[0].rfind(|c: char| !c.is_numeric())?); - { - ::std::io::_eprint(format_args!("suggested fee: {0}\n", suggested_fee)); - }; - suggested_fee.parse::().ok().or(suggested_fee[1..].parse::().ok()) - } -} -pub mod state { - use super::error::DaemonError; - use crate::{channel::GrpcChannel, networks::ChainKind}; - use cosmwasm_std::Addr; - use cw_orch_core::{ - environment::{DeployDetails, StateInterface}, - CwEnvError, - }; - use ibc_chain_registry::chain::ChainData; - use serde::Serialize; - use serde_json::{json, Value}; - use std::{collections::HashMap, env, fs::File, path::Path}; - use tonic::transport::Channel; - /// Stores the chain information and deployment state. - /// Uses a simple JSON file to store the deployment information locally. - pub struct DaemonState { - /// this is passed via env var STATE_FILE - pub json_file_path: String, - /// Deployment identifier - pub deployment_id: String, - /// gRPC channel - pub grpc_channel: Channel, - /// Information about the chain - pub chain_data: ChainData, - } - #[automatically_derived] - impl ::core::clone::Clone for DaemonState { - #[inline] - fn clone(&self) -> DaemonState { - DaemonState { - json_file_path: ::core::clone::Clone::clone(&self.json_file_path), - deployment_id: ::core::clone::Clone::clone(&self.deployment_id), - grpc_channel: ::core::clone::Clone::clone(&self.grpc_channel), - chain_data: ::core::clone::Clone::clone(&self.chain_data), - } - } - } - #[automatically_derived] - impl ::core::fmt::Debug for DaemonState { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field4_finish( - f, - "DaemonState", - "json_file_path", - &self.json_file_path, - "deployment_id", - &self.deployment_id, - "grpc_channel", - &self.grpc_channel, - "chain_data", - &&self.chain_data, - ) - } - } - impl DaemonState { - /// Creates a new state from the given chain data and deployment id. - /// Attempts to connect to any of the provided gRPC endpoints. - pub async fn new( - mut chain_data: ChainData, - deployment_id: String, - ) -> Result { - if chain_data.apis.grpc.is_empty() { - return Err(DaemonError::GRPCListIsEmpty); - } - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Found {0} gRPC endpoints", - chain_data.apis.grpc.len(), - ), - lvl, - &( - "cw_orch_daemon::state", - "cw_orch_daemon::state", - "cw-orch-daemon/src/state.rs", - ), - 40u32, - ::log::__private_api::Option::None, - ); - } - }; - let grpc_channel = GrpcChannel::connect( - &chain_data.apis.grpc, - &chain_data.chain_id, - ) - .await?; - let mut json_file_path = env::var("STATE_FILE") - .unwrap_or("./state.json".to_string()); - if chain_data.network_type == ChainKind::Local.to_string() { - let name = Path::new(&json_file_path) - .file_stem() - .unwrap() - .to_str() - .unwrap(); - let folder = Path::new(&json_file_path) - .parent() - .unwrap() - .to_str() - .unwrap(); - json_file_path = { - let res = ::alloc::fmt::format( - format_args!("{0}/{1}_local.json", folder, name), - ); - res - }; - } - let shortest_denom_token = chain_data - .fees - .fee_tokens - .iter() - .fold( - chain_data.fees.fee_tokens[0].clone(), - |acc, item| { - if item.denom.len() < acc.denom.len() { - item.clone() - } else { - acc - } - }, - ); - chain_data - .fees - .fee_tokens = <[_]>::into_vec( - #[rustc_box] - ::alloc::boxed::Box::new([shortest_denom_token]), - ); - let state = DaemonState { - json_file_path, - deployment_id, - grpc_channel, - chain_data, - }; - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Writing daemon state JSON file: {0:#?}", - state.json_file_path, - ), - lvl, - &( - "cw_orch_daemon::state", - "cw_orch_daemon::state", - "cw-orch-daemon/src/state.rs", - ), - 87u32, - ::log::__private_api::Option::None, - ); - } - }; - crate::json_file::write( - &state.json_file_path, - &state.chain_data.chain_id.to_string(), - &state.chain_data.chain_name, - &state.deployment_id, - ); - Ok(state) - } - /// Get the state filepath and read it as json - fn read_state(&self) -> serde_json::Value { - crate::json_file::read(&self.json_file_path) - } - /// Retrieve a stateful value using the chainId and networkId - pub fn get(&self, key: &str) -> Value { - let json = self.read_state(); - json[&self.chain_data.chain_name][&self.chain_data.chain_id.to_string()][key] - .clone() - } - /// Set a stateful value using the chainId and networkId - pub fn set(&self, key: &str, contract_id: &str, value: T) { - let mut json = self.read_state(); - json[&self - .chain_data - .chain_name][&self - .chain_data - .chain_id - .to_string()][key][contract_id] = ::serde_json::to_value(&value) - .unwrap(); - serde_json::to_writer_pretty( - File::create(&self.json_file_path).unwrap(), - &json, - ) - .unwrap(); - } - } - impl StateInterface for DaemonState { - /// Read address for contract in deployment id from state file - fn get_address(&self, contract_id: &str) -> Result { - let value = self - .get(&self.deployment_id) - .get(contract_id) - .ok_or_else(|| CwEnvError::AddrNotInStore(contract_id.to_owned()))? - .clone(); - Ok(Addr::unchecked(value.as_str().unwrap())) - } - /// Set address for contract in deployment id in state file - fn set_address(&mut self, contract_id: &str, address: &Addr) { - self.set(&self.deployment_id, contract_id, address.as_str()); - } - /// Get the locally-saved version of the contract's version on this network - fn get_code_id(&self, contract_id: &str) -> Result { - let value = self - .get("code_ids") - .get(contract_id) - .ok_or_else(|| CwEnvError::CodeIdNotInStore(contract_id.to_owned()))? - .clone(); - Ok(value.as_u64().unwrap()) - } - /// Set the locally-saved version of the contract's latest version on this network - fn set_code_id(&mut self, contract_id: &str, code_id: u64) { - self.set("code_ids", contract_id, code_id); - } - /// Get all addresses for deployment id from state file - fn get_all_addresses(&self) -> Result, CwEnvError> { - let mut store = HashMap::new(); - let addresses = self.get(&self.deployment_id); - let value = addresses.as_object().unwrap(); - for (id, addr) in value { - store.insert(id.clone(), Addr::unchecked(addr.as_str().unwrap())); - } - Ok(store) - } - fn get_all_code_ids(&self) -> Result, CwEnvError> { - let mut store = HashMap::new(); - let code_ids = self.get("code_ids"); - let value = code_ids.as_object().unwrap(); - for (id, code_id) in value { - store.insert(id.clone(), code_id.as_u64().unwrap()); - } - Ok(store) - } - fn deploy_details(&self) -> DeployDetails { - DeployDetails { - chain_id: self.chain_data.chain_id.to_string(), - chain_name: self.chain_data.chain_name.clone(), - deployment_id: self.deployment_id.clone(), - } - } - } -} -pub mod sync { - mod builder { - use ibc_chain_registry::chain::ChainData; - use crate::DaemonAsyncBuilder; - use super::{super::error::DaemonError, core::Daemon}; - /// Create [`Daemon`] through [`DaemonBuilder`] - /// ## Example - /// ```no_run - /// use cw_orch_daemon::{networks, DaemonBuilder}; - /// - /// let Daemon = DaemonBuilder::default() - /// .chain(networks::LOCAL_JUNO) - /// .deployment_id("v0.1.0") - /// .build() - /// .unwrap(); - /// ``` - pub struct DaemonBuilder { - pub(crate) chain: Option, - pub(crate) handle: Option, - pub(crate) deployment_id: Option, - /// Wallet mnemonic - pub(crate) mnemonic: Option, - } - #[automatically_derived] - impl ::core::clone::Clone for DaemonBuilder { - #[inline] - fn clone(&self) -> DaemonBuilder { - DaemonBuilder { - chain: ::core::clone::Clone::clone(&self.chain), - handle: ::core::clone::Clone::clone(&self.handle), - deployment_id: ::core::clone::Clone::clone(&self.deployment_id), - mnemonic: ::core::clone::Clone::clone(&self.mnemonic), - } - } - } - #[automatically_derived] - impl ::core::default::Default for DaemonBuilder { - #[inline] - fn default() -> DaemonBuilder { - DaemonBuilder { - chain: ::core::default::Default::default(), - handle: ::core::default::Default::default(), - deployment_id: ::core::default::Default::default(), - mnemonic: ::core::default::Default::default(), - } - } - } - impl DaemonBuilder { - /// Set the chain the Daemon will connect to - pub fn chain(&mut self, chain: impl Into) -> &mut Self { - self.chain = Some(chain.into()); - self - } - /// Set the deployment id to use for the Daemon interactions - /// Defaults to `default` - pub fn deployment_id( - &mut self, - deployment_id: impl Into, - ) -> &mut Self { - self.deployment_id = Some(deployment_id.into()); - self - } - /// Set the tokio runtime handle to use for the Daemon - /// - /// ## Example - /// ```no_run - /// use cw_orch_daemon::Daemon; - /// use tokio::runtime::Runtime; - /// let rt = Runtime::new().unwrap(); - /// let Daemon = Daemon::builder() - /// .handle(rt.handle()) - /// // ... - /// .build() - /// .unwrap(); - /// ``` - pub fn handle(&mut self, handle: &tokio::runtime::Handle) -> &mut Self { - self.handle = Some(handle.clone()); - self - } - /// Set the mnemonic to use with this chain. - pub fn mnemonic(&mut self, mnemonic: impl ToString) -> &mut Self { - self.mnemonic = Some(mnemonic.to_string()); - self - } - /// Build a Daemon - pub fn build(&self) -> Result { - let rt_handle = self - .handle - .clone() - .ok_or(DaemonError::BuilderMissing("runtime handle".into()))?; - let daemon = rt_handle - .block_on(DaemonAsyncBuilder::from(self.clone()).build())?; - Ok(Daemon { rt_handle, daemon }) - } - } - } - mod core { - use std::{fmt::Debug, rc::Rc, time::Duration}; - use super::super::{sender::Wallet, DaemonAsync}; - use crate::{ - queriers::{DaemonQuerier, Node}, - CosmTxResponse, DaemonBuilder, DaemonError, DaemonState, - }; - use cosmrs::tendermint::Time; - use cosmwasm_std::{Addr, Coin}; - use cw_orch_core::{ - contract::{interface_traits::Uploadable, WasmPath}, - environment::{ChainState, TxHandler}, - }; - use serde::{de::DeserializeOwned, Serialize}; - use tokio::runtime::Handle; - use tonic::transport::Channel; - /** - Represents a blockchain node. - Is constructed with the [DaemonBuilder]. - - ## Usage - - ```rust,no_run - use cw_orch_daemon::{Daemon, networks}; - use tokio::runtime::Runtime; - - let rt = Runtime::new().unwrap(); - let daemon: Daemon = Daemon::builder() - .chain(networks::JUNO_1) - .handle(rt.handle()) - .build() - .unwrap(); - ``` - ## Environment Execution - - The Daemon implements [`TxHandler`] which allows you to perform transactions on the chain. - - ## Querying - - Different Cosmos SDK modules can be queried through the daemon by calling the [`Daemon.query_client`] method with a specific querier. - See [Querier](crate::queriers) for examples. -*/ - pub struct Daemon { - pub daemon: DaemonAsync, - /// Runtime handle to execute async tasks - pub rt_handle: Handle, - } - #[automatically_derived] - impl ::core::clone::Clone for Daemon { - #[inline] - fn clone(&self) -> Daemon { - Daemon { - daemon: ::core::clone::Clone::clone(&self.daemon), - rt_handle: ::core::clone::Clone::clone(&self.rt_handle), - } - } - } - impl Daemon { - /// Get the daemon builder - pub fn builder() -> DaemonBuilder { - DaemonBuilder::default() - } - /// Perform a query with a given querier - /// See [Querier](crate::queriers) for examples. - pub fn query_client(&self) -> Querier { - self.daemon.query_client() - } - /// Get the channel configured for this Daemon - pub fn channel(&self) -> Channel { - self.daemon.state.grpc_channel.clone() - } - /// Get the channel configured for this Daemon - pub fn wallet(&self) -> Wallet { - self.daemon.sender.clone() - } - } - impl ChainState for Daemon { - type Out = Rc; - fn state(&self) -> Self::Out { - self.daemon.state.clone() - } - } - impl TxHandler for Daemon { - type Response = CosmTxResponse; - type Error = DaemonError; - type ContractSource = WasmPath; - type Sender = Wallet; - fn sender(&self) -> Addr { - self.daemon.sender.address().unwrap() - } - fn set_sender(&mut self, sender: Self::Sender) { - self.daemon.sender = sender; - } - fn upload( - &self, - uploadable: &impl Uploadable, - ) -> Result { - self.rt_handle.block_on(self.daemon.upload(uploadable)) - } - fn execute( - &self, - exec_msg: &E, - coins: &[cosmwasm_std::Coin], - contract_address: &Addr, - ) -> Result { - self.rt_handle - .block_on(self.daemon.execute(exec_msg, coins, contract_address)) - } - fn instantiate( - &self, - code_id: u64, - init_msg: &I, - label: Option<&str>, - admin: Option<&Addr>, - coins: &[Coin], - ) -> Result { - self.rt_handle - .block_on( - self.daemon.instantiate(code_id, init_msg, label, admin, coins), - ) - } - fn query( - &self, - query_msg: &Q, - contract_address: &Addr, - ) -> Result { - self.rt_handle.block_on(self.daemon.query(query_msg, contract_address)) - } - fn migrate( - &self, - migrate_msg: &M, - new_code_id: u64, - contract_address: &Addr, - ) -> Result { - self.rt_handle - .block_on( - self.daemon.migrate(migrate_msg, new_code_id, contract_address), - ) - } - fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { - let mut last_height = self - .rt_handle - .block_on(self.query_client::().block_height())?; - let end_height = last_height + amount; - while last_height < end_height { - self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(4))); - last_height = self - .rt_handle - .block_on(self.query_client::().block_height())?; - } - Ok(()) - } - fn wait_seconds(&self, secs: u64) -> Result<(), DaemonError> { - self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(secs))); - Ok(()) - } - fn next_block(&self) -> Result<(), DaemonError> { - let mut last_height = self - .rt_handle - .block_on(self.query_client::().block_height())?; - let end_height = last_height + 1; - while last_height < end_height { - self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(4))); - last_height = self - .rt_handle - .block_on(self.query_client::().block_height())?; - } - Ok(()) - } - fn block_info(&self) -> Result { - let block = self - .rt_handle - .block_on(self.query_client::().latest_block())?; - let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; - let time = cosmwasm_std::Timestamp::from_nanos( - since_epoch.as_nanos() as u64, - ); - Ok(cosmwasm_std::BlockInfo { - height: block.header.height.value(), - time, - chain_id: block.header.chain_id.to_string(), - }) - } - } - } - pub use self::{builder::*, core::*}; -} -pub mod tx_resp { - use super::{ - cosmos_modules::{ - abci::{AbciMessageLog, Attribute, StringEvent, TxResponse}, - tendermint_abci::Event, - }, - error::DaemonError, - }; - use chrono::{DateTime, NaiveDateTime, TimeZone, Utc}; - use cosmwasm_std::{to_binary, Binary, StdError, StdResult}; - use cw_orch_core::environment::IndexResponse; - use serde::{Deserialize, Serialize}; - const FORMAT: &str = "%Y-%m-%dT%H:%M:%S%.f"; - const FORMAT_TZ_SUPPLIED: &str = "%Y-%m-%dT%H:%M:%S.%f%:z"; - const FORMAT_SHORT_Z: &str = "%Y-%m-%dT%H:%M:%SZ"; - const FORMAT_SHORT_Z2: &str = "%Y-%m-%dT%H:%M:%S.%fZ"; - /// The response from a transaction performed on a blockchain. - pub struct CosmTxResponse { - /// Height of the block in which the transaction was included. - pub height: u64, - /// Transaction hash. - pub txhash: String, - /// Transaction index within the block. - pub codespace: String, - /// Transaction result code - pub code: usize, - /// Arbitrary data that can be included in a transaction. - pub data: String, - /// Raw log message. - pub raw_log: String, - /// Logs of the transaction. - pub logs: Vec, - /// Transaction info. - pub info: String, - /// Gas limit. - pub gas_wanted: u64, - /// Gas used. - pub gas_used: u64, - /// Timestamp of the block in which the transaction was included. - pub timestamp: DateTime, - /// Transaction events. - pub events: Vec, - } - #[automatically_derived] - impl ::core::fmt::Debug for CosmTxResponse { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - let names: &'static _ = &[ - "height", - "txhash", - "codespace", - "code", - "data", - "raw_log", - "logs", - "info", - "gas_wanted", - "gas_used", - "timestamp", - "events", - ]; - let values: &[&dyn ::core::fmt::Debug] = &[ - &self.height, - &self.txhash, - &self.codespace, - &self.code, - &self.data, - &self.raw_log, - &self.logs, - &self.info, - &self.gas_wanted, - &self.gas_used, - &self.timestamp, - &&self.events, - ]; - ::core::fmt::Formatter::debug_struct_fields_finish( - f, - "CosmTxResponse", - names, - values, - ) - } - } - #[automatically_derived] - impl ::core::default::Default for CosmTxResponse { - #[inline] - fn default() -> CosmTxResponse { - CosmTxResponse { - height: ::core::default::Default::default(), - txhash: ::core::default::Default::default(), - codespace: ::core::default::Default::default(), - code: ::core::default::Default::default(), - data: ::core::default::Default::default(), - raw_log: ::core::default::Default::default(), - logs: ::core::default::Default::default(), - info: ::core::default::Default::default(), - gas_wanted: ::core::default::Default::default(), - gas_used: ::core::default::Default::default(), - timestamp: ::core::default::Default::default(), - events: ::core::default::Default::default(), - } - } - } - impl CosmTxResponse { - /// find a attribute's value from TX logs. - /// returns: msg_index and value - pub fn get_attribute_from_logs( - &self, - event_type: &str, - attribute_key: &str, - ) -> Vec<(usize, String)> { - let mut response: Vec<(usize, String)> = Default::default(); - let logs = &self.logs; - for log_part in logs { - let msg_index = log_part.msg_index.unwrap_or_default(); - let events = &log_part.events; - let events_filtered = events - .iter() - .filter(|event| event.s_type == event_type) - .collect::>(); - if let Some(event) = events_filtered.first() { - let attributes_filtered = event - .attributes - .iter() - .filter(|attr| attr.key == attribute_key) - .map(|f| f.value.clone()) - .collect::>(); - if let Some(attr_key) = attributes_filtered.first() { - response.push((msg_index, attr_key.clone())); - } - } - } - response - } - /// get the list of event types from a TX record - pub fn get_events(&self, event_type: &str) -> Vec { - let mut response: Vec = Default::default(); - for log_part in &self.logs { - let events = &log_part.events; - let events_filtered = events - .iter() - .filter(|event| event.s_type == event_type) - .collect::>(); - for event in events_filtered { - response.push(event.clone()); - } - } - response - } - } - impl From<&serde_json::Value> for TxResultBlockMsg { - fn from(value: &serde_json::Value) -> Self { - serde_json::from_value(value.clone()).unwrap() - } - } - impl From for CosmTxResponse { - fn from(tx: TxResponse) -> Self { - Self { - height: tx.height as u64, - txhash: tx.txhash, - codespace: tx.codespace, - code: tx.code as usize, - data: tx.data, - raw_log: tx.raw_log, - logs: tx.logs.into_iter().map(TxResultBlockMsg::from).collect(), - info: tx.info, - gas_wanted: tx.gas_wanted as u64, - gas_used: tx.gas_used as u64, - timestamp: parse_timestamp(tx.timestamp).unwrap(), - events: tx.events, - } - } - } - impl IndexResponse for CosmTxResponse { - fn events(&self) -> Vec { - let mut parsed_events = ::alloc::vec::Vec::new(); - for event in &self.events { - let mut pattr = ::alloc::vec::Vec::new(); - for attr in &event.attributes { - pattr - .push(cosmwasm_std::Attribute { - key: attr.key.clone(), - value: attr.value.clone(), - }) - } - let pevent = cosmwasm_std::Event::new(event.r#type.clone()) - .add_attributes(pattr); - parsed_events.push(pevent); - } - parsed_events - } - fn data(&self) -> Option { - if self.data.is_empty() { - None - } else { - Some(to_binary(self.data.as_bytes()).unwrap()) - } - } - fn event_attr_value( - &self, - event_type: &str, - attr_key: &str, - ) -> StdResult { - for event in &self.events { - if event.r#type == event_type { - for attr in &event.attributes { - if attr.key == attr_key { - return Ok(attr.value.clone()); - } - } - } - } - Err( - StdError::generic_err({ - let res = ::alloc::fmt::format( - format_args!( - "event of type {0} does not have a value at key {1}", - event_type, - attr_key, - ), - ); - res - }), - ) - } - } - /// The events from a single message in a transaction. - pub struct TxResultBlockMsg { - /// index of the message in the transaction - pub msg_index: Option, - /// Events from this message - pub events: Vec, - } - #[automatically_derived] - impl ::core::clone::Clone for TxResultBlockMsg { - #[inline] - fn clone(&self) -> TxResultBlockMsg { - TxResultBlockMsg { - msg_index: ::core::clone::Clone::clone(&self.msg_index), - events: ::core::clone::Clone::clone(&self.events), - } - } - } - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl _serde::Serialize for TxResultBlockMsg { - fn serialize<__S>( - &self, - __serializer: __S, - ) -> _serde::__private::Result<__S::Ok, __S::Error> - where - __S: _serde::Serializer, - { - let mut __serde_state = _serde::Serializer::serialize_struct( - __serializer, - "TxResultBlockMsg", - false as usize + 1 + 1, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "msg_index", - &self.msg_index, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "events", - &self.events, - )?; - _serde::ser::SerializeStruct::end(__serde_state) - } - } - }; - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl<'de> _serde::Deserialize<'de> for TxResultBlockMsg { - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - #[allow(non_camel_case_types)] - #[doc(hidden)] - enum __Field { - __field0, - __field1, - __ignore, - } - #[doc(hidden)] - struct __FieldVisitor; - impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { - type Value = __Field; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "field identifier", - ) - } - fn visit_u64<__E>( - self, - __value: u64, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - 0u64 => _serde::__private::Ok(__Field::__field0), - 1u64 => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_str<__E>( - self, - __value: &str, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - "msg_index" => _serde::__private::Ok(__Field::__field0), - "events" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_bytes<__E>( - self, - __value: &[u8], - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - b"msg_index" => _serde::__private::Ok(__Field::__field0), - b"events" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - } - impl<'de> _serde::Deserialize<'de> for __Field { - #[inline] - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - _serde::Deserializer::deserialize_identifier( - __deserializer, - __FieldVisitor, - ) - } - } - #[doc(hidden)] - struct __Visitor<'de> { - marker: _serde::__private::PhantomData, - lifetime: _serde::__private::PhantomData<&'de ()>, - } - impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { - type Value = TxResultBlockMsg; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "struct TxResultBlockMsg", - ) - } - #[inline] - fn visit_seq<__A>( - self, - mut __seq: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<'de>, - { - let __field0 = match _serde::de::SeqAccess::next_element::< - Option, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 0usize, - &"struct TxResultBlockMsg with 2 elements", - ), - ); - } - }; - let __field1 = match _serde::de::SeqAccess::next_element::< - Vec, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 1usize, - &"struct TxResultBlockMsg with 2 elements", - ), - ); - } - }; - _serde::__private::Ok(TxResultBlockMsg { - msg_index: __field0, - events: __field1, - }) - } - #[inline] - fn visit_map<__A>( - self, - mut __map: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::MapAccess<'de>, - { - let mut __field0: _serde::__private::Option> = _serde::__private::None; - let mut __field1: _serde::__private::Option< - Vec, - > = _serde::__private::None; - while let _serde::__private::Some(__key) - = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { - match __key { - __Field::__field0 => { - if _serde::__private::Option::is_some(&__field0) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field( - "msg_index", - ), - ); - } - __field0 = _serde::__private::Some( - _serde::de::MapAccess::next_value::< - Option, - >(&mut __map)?, - ); - } - __Field::__field1 => { - if _serde::__private::Option::is_some(&__field1) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field("events"), - ); - } - __field1 = _serde::__private::Some( - _serde::de::MapAccess::next_value::< - Vec, - >(&mut __map)?, - ); - } - _ => { - let _ = _serde::de::MapAccess::next_value::< - _serde::de::IgnoredAny, - >(&mut __map)?; - } - } - } - let __field0 = match __field0 { - _serde::__private::Some(__field0) => __field0, - _serde::__private::None => { - _serde::__private::de::missing_field("msg_index")? - } - }; - let __field1 = match __field1 { - _serde::__private::Some(__field1) => __field1, - _serde::__private::None => { - _serde::__private::de::missing_field("events")? - } - }; - _serde::__private::Ok(TxResultBlockMsg { - msg_index: __field0, - events: __field1, - }) - } - } - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &["msg_index", "events"]; - _serde::Deserializer::deserialize_struct( - __deserializer, - "TxResultBlockMsg", - FIELDS, - __Visitor { - marker: _serde::__private::PhantomData::, - lifetime: _serde::__private::PhantomData, - }, - ) - } - } - }; - #[automatically_derived] - impl ::core::fmt::Debug for TxResultBlockMsg { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "TxResultBlockMsg", - "msg_index", - &self.msg_index, - "events", - &&self.events, - ) - } - } - impl From for TxResultBlockMsg { - fn from(msg: AbciMessageLog) -> Self { - Self { - msg_index: Some(msg.msg_index as usize), - events: msg.events.into_iter().map(TxResultBlockEvent::from).collect(), - } - } - } - /// A single event from a transaction and its attributes. - pub struct TxResultBlockEvent { - #[serde(rename = "type")] - /// Type of the event - pub s_type: String, - /// Attributes of the event - pub attributes: Vec, - } - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl<'de> _serde::Deserialize<'de> for TxResultBlockEvent { - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - #[allow(non_camel_case_types)] - #[doc(hidden)] - enum __Field { - __field0, - __field1, - __ignore, - } - #[doc(hidden)] - struct __FieldVisitor; - impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { - type Value = __Field; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "field identifier", - ) - } - fn visit_u64<__E>( - self, - __value: u64, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - 0u64 => _serde::__private::Ok(__Field::__field0), - 1u64 => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_str<__E>( - self, - __value: &str, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - "type" => _serde::__private::Ok(__Field::__field0), - "attributes" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_bytes<__E>( - self, - __value: &[u8], - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - b"type" => _serde::__private::Ok(__Field::__field0), - b"attributes" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - } - impl<'de> _serde::Deserialize<'de> for __Field { - #[inline] - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - _serde::Deserializer::deserialize_identifier( - __deserializer, - __FieldVisitor, - ) - } - } - #[doc(hidden)] - struct __Visitor<'de> { - marker: _serde::__private::PhantomData, - lifetime: _serde::__private::PhantomData<&'de ()>, - } - impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { - type Value = TxResultBlockEvent; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "struct TxResultBlockEvent", - ) - } - #[inline] - fn visit_seq<__A>( - self, - mut __seq: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<'de>, - { - let __field0 = match _serde::de::SeqAccess::next_element::< - String, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 0usize, - &"struct TxResultBlockEvent with 2 elements", - ), - ); - } - }; - let __field1 = match _serde::de::SeqAccess::next_element::< - Vec, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 1usize, - &"struct TxResultBlockEvent with 2 elements", - ), - ); - } - }; - _serde::__private::Ok(TxResultBlockEvent { - s_type: __field0, - attributes: __field1, - }) - } - #[inline] - fn visit_map<__A>( - self, - mut __map: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::MapAccess<'de>, - { - let mut __field0: _serde::__private::Option = _serde::__private::None; - let mut __field1: _serde::__private::Option< - Vec, - > = _serde::__private::None; - while let _serde::__private::Some(__key) - = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { - match __key { - __Field::__field0 => { - if _serde::__private::Option::is_some(&__field0) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field("type"), - ); - } - __field0 = _serde::__private::Some( - _serde::de::MapAccess::next_value::(&mut __map)?, - ); - } - __Field::__field1 => { - if _serde::__private::Option::is_some(&__field1) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field( - "attributes", - ), - ); - } - __field1 = _serde::__private::Some( - _serde::de::MapAccess::next_value::< - Vec, - >(&mut __map)?, - ); - } - _ => { - let _ = _serde::de::MapAccess::next_value::< - _serde::de::IgnoredAny, - >(&mut __map)?; - } - } - } - let __field0 = match __field0 { - _serde::__private::Some(__field0) => __field0, - _serde::__private::None => { - _serde::__private::de::missing_field("type")? - } - }; - let __field1 = match __field1 { - _serde::__private::Some(__field1) => __field1, - _serde::__private::None => { - _serde::__private::de::missing_field("attributes")? - } - }; - _serde::__private::Ok(TxResultBlockEvent { - s_type: __field0, - attributes: __field1, - }) - } - } - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &["type", "attributes"]; - _serde::Deserializer::deserialize_struct( - __deserializer, - "TxResultBlockEvent", - FIELDS, - __Visitor { - marker: _serde::__private::PhantomData::, - lifetime: _serde::__private::PhantomData, - }, - ) - } - } - }; - #[automatically_derived] - impl ::core::clone::Clone for TxResultBlockEvent { - #[inline] - fn clone(&self) -> TxResultBlockEvent { - TxResultBlockEvent { - s_type: ::core::clone::Clone::clone(&self.s_type), - attributes: ::core::clone::Clone::clone(&self.attributes), - } - } - } - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl _serde::Serialize for TxResultBlockEvent { - fn serialize<__S>( - &self, - __serializer: __S, - ) -> _serde::__private::Result<__S::Ok, __S::Error> - where - __S: _serde::Serializer, - { - let mut __serde_state = _serde::Serializer::serialize_struct( - __serializer, - "TxResultBlockEvent", - false as usize + 1 + 1, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "type", - &self.s_type, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "attributes", - &self.attributes, - )?; - _serde::ser::SerializeStruct::end(__serde_state) - } - } - }; - #[automatically_derived] - impl ::core::fmt::Debug for TxResultBlockEvent { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "TxResultBlockEvent", - "s_type", - &self.s_type, - "attributes", - &&self.attributes, - ) - } - } - impl From for TxResultBlockEvent { - fn from(event: StringEvent) -> Self { - Self { - s_type: event.r#type, - attributes: event - .attributes - .into_iter() - .map(TxResultBlockAttribute::from) - .collect(), - } - } - } - impl TxResultBlockEvent { - /// get all key/values from the event that have the key 'key' - pub fn get_attributes(&self, key: &str) -> Vec { - self.attributes.iter().filter(|attr| attr.key == key).cloned().collect() - } - /// return the first value of the first attribute that has the key 'key' - pub fn get_first_attribute_value(&self, key: &str) -> Option { - self.get_attributes(key).first().map(|attr| attr.value.clone()) - } - } - /// A single attribute of an event. - pub struct TxResultBlockAttribute { - /// Key of the attribute - pub key: String, - /// Value of the attribute - pub value: String, - } - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl<'de> _serde::Deserialize<'de> for TxResultBlockAttribute { - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - #[allow(non_camel_case_types)] - #[doc(hidden)] - enum __Field { - __field0, - __field1, - __ignore, - } - #[doc(hidden)] - struct __FieldVisitor; - impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { - type Value = __Field; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "field identifier", - ) - } - fn visit_u64<__E>( - self, - __value: u64, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - 0u64 => _serde::__private::Ok(__Field::__field0), - 1u64 => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_str<__E>( - self, - __value: &str, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - "key" => _serde::__private::Ok(__Field::__field0), - "value" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_bytes<__E>( - self, - __value: &[u8], - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - b"key" => _serde::__private::Ok(__Field::__field0), - b"value" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - } - impl<'de> _serde::Deserialize<'de> for __Field { - #[inline] - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - _serde::Deserializer::deserialize_identifier( - __deserializer, - __FieldVisitor, - ) - } - } - #[doc(hidden)] - struct __Visitor<'de> { - marker: _serde::__private::PhantomData, - lifetime: _serde::__private::PhantomData<&'de ()>, - } - impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { - type Value = TxResultBlockAttribute; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "struct TxResultBlockAttribute", - ) - } - #[inline] - fn visit_seq<__A>( - self, - mut __seq: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<'de>, - { - let __field0 = match _serde::de::SeqAccess::next_element::< - String, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 0usize, - &"struct TxResultBlockAttribute with 2 elements", - ), - ); - } - }; - let __field1 = match _serde::de::SeqAccess::next_element::< - String, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 1usize, - &"struct TxResultBlockAttribute with 2 elements", - ), - ); - } - }; - _serde::__private::Ok(TxResultBlockAttribute { - key: __field0, - value: __field1, - }) - } - #[inline] - fn visit_map<__A>( - self, - mut __map: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::MapAccess<'de>, - { - let mut __field0: _serde::__private::Option = _serde::__private::None; - let mut __field1: _serde::__private::Option = _serde::__private::None; - while let _serde::__private::Some(__key) - = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { - match __key { - __Field::__field0 => { - if _serde::__private::Option::is_some(&__field0) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field("key"), - ); - } - __field0 = _serde::__private::Some( - _serde::de::MapAccess::next_value::(&mut __map)?, - ); - } - __Field::__field1 => { - if _serde::__private::Option::is_some(&__field1) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field("value"), - ); - } - __field1 = _serde::__private::Some( - _serde::de::MapAccess::next_value::(&mut __map)?, - ); - } - _ => { - let _ = _serde::de::MapAccess::next_value::< - _serde::de::IgnoredAny, - >(&mut __map)?; - } - } - } - let __field0 = match __field0 { - _serde::__private::Some(__field0) => __field0, - _serde::__private::None => { - _serde::__private::de::missing_field("key")? - } - }; - let __field1 = match __field1 { - _serde::__private::Some(__field1) => __field1, - _serde::__private::None => { - _serde::__private::de::missing_field("value")? - } - }; - _serde::__private::Ok(TxResultBlockAttribute { - key: __field0, - value: __field1, - }) - } - } - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &["key", "value"]; - _serde::Deserializer::deserialize_struct( - __deserializer, - "TxResultBlockAttribute", - FIELDS, - __Visitor { - marker: _serde::__private::PhantomData::, - lifetime: _serde::__private::PhantomData, - }, - ) - } - } - }; - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl _serde::Serialize for TxResultBlockAttribute { - fn serialize<__S>( - &self, - __serializer: __S, - ) -> _serde::__private::Result<__S::Ok, __S::Error> - where - __S: _serde::Serializer, - { - let mut __serde_state = _serde::Serializer::serialize_struct( - __serializer, - "TxResultBlockAttribute", - false as usize + 1 + 1, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "key", - &self.key, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "value", - &self.value, - )?; - _serde::ser::SerializeStruct::end(__serde_state) - } - } - }; - #[automatically_derived] - impl ::core::clone::Clone for TxResultBlockAttribute { - #[inline] - fn clone(&self) -> TxResultBlockAttribute { - TxResultBlockAttribute { - key: ::core::clone::Clone::clone(&self.key), - value: ::core::clone::Clone::clone(&self.value), - } - } - } - #[automatically_derived] - impl ::core::fmt::Debug for TxResultBlockAttribute { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "TxResultBlockAttribute", - "key", - &self.key, - "value", - &&self.value, - ) - } - } - impl From for TxResultBlockAttribute { - fn from(a: Attribute) -> Self { - Self { key: a.key, value: a.value } - } - } - /// Parse a string timestamp into a `DateTime` - pub fn parse_timestamp(s: String) -> Result, DaemonError> { - let len = s.len(); - let slice_len = if s.contains('.') { len.saturating_sub(4) } else { len }; - let sliced = &s[0..slice_len]; - match NaiveDateTime::parse_from_str(sliced, FORMAT) { - Err(_e) => { - match NaiveDateTime::parse_from_str(&s, FORMAT_TZ_SUPPLIED) { - Err(_e2) => { - match NaiveDateTime::parse_from_str(sliced, FORMAT_SHORT_Z) { - Err(_e3) => { - match NaiveDateTime::parse_from_str(&s, FORMAT_SHORT_Z2) { - Err(_e4) => { - { - ::std::io::_eprint( - format_args!("DateTime Fail {0} {1:#?}\n", s, _e4), - ); - }; - Err(DaemonError::StdErr(_e4.to_string())) - } - Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), - } - } - Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), - } - } - Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), - } - } - Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), - } - } -} -pub mod keys { - #![allow(unused)] - pub mod private { - use super::public::PublicKey; - use crate::proto::injective::{InjectivePubKey, ETHEREUM_COIN_TYPE}; - use crate::DaemonError; - use base64::Engine; - use bitcoin::{ - bip32::{ExtendedPrivKey, IntoDerivationPath}, - Network, - }; - use cosmrs::tx::SignerPublicKey; - use hkd32::mnemonic::{Phrase, Seed}; - use rand_core::OsRng; - use secp256k1::Secp256k1; - /// The Private key structure that is used to generate signatures and public keys - /// WARNING: No Security Audit has been performed - pub struct PrivateKey { - #[allow(missing_docs)] - pub account: u32, - #[allow(missing_docs)] - pub index: u32, - #[allow(missing_docs)] - pub coin_type: u32, - /// The 24 words used to generate this private key - mnemonic: Option, - #[allow(dead_code)] - /// This is used for testing - root_private_key: ExtendedPrivKey, - /// The private key - private_key: ExtendedPrivKey, - } - #[automatically_derived] - impl ::core::clone::Clone for PrivateKey { - #[inline] - fn clone(&self) -> PrivateKey { - PrivateKey { - account: ::core::clone::Clone::clone(&self.account), - index: ::core::clone::Clone::clone(&self.index), - coin_type: ::core::clone::Clone::clone(&self.coin_type), - mnemonic: ::core::clone::Clone::clone(&self.mnemonic), - root_private_key: ::core::clone::Clone::clone( - &self.root_private_key, - ), - private_key: ::core::clone::Clone::clone(&self.private_key), - } - } - } - impl PrivateKey { - /// Generate a new private key - pub fn new( - secp: &Secp256k1, - coin_type: u32, - ) -> Result { - let phrase = hkd32::mnemonic::Phrase::random( - OsRng, - hkd32::mnemonic::Language::English, - ); - PrivateKey::gen_private_key_phrase(secp, phrase, 0, 0, coin_type, "") - } - /// generate a new private key with a seed phrase - pub fn new_seed( - secp: &Secp256k1, - seed_phrase: &str, - coin_type: u32, - ) -> Result { - let phrase = hkd32::mnemonic::Phrase::random( - OsRng, - hkd32::mnemonic::Language::English, - ); - PrivateKey::gen_private_key_phrase( - secp, - phrase, - 0, - 0, - coin_type, - seed_phrase, - ) - } - /// for private key recovery. This is also used by wallet routines to re-hydrate the structure - pub fn from_words( - secp: &Secp256k1, - words: &str, - account: u32, - index: u32, - coin_type: u32, - ) -> Result { - if words.split(' ').count() != 24 { - return Err(DaemonError::WrongLength); - } - match hkd32::mnemonic::Phrase::new( - words, - hkd32::mnemonic::Language::English, - ) { - Ok(phrase) => { - PrivateKey::gen_private_key_phrase( - secp, - phrase, - account, - index, - coin_type, - "", - ) - } - Err(_) => Err(DaemonError::Phrasing), - } - } - /// for private key recovery with seed phrase - pub fn from_words_seed( - secp: &Secp256k1, - words: &str, - seed_pass: &str, - coin_type: u32, - ) -> Result { - match hkd32::mnemonic::Phrase::new( - words, - hkd32::mnemonic::Language::English, - ) { - Ok(phrase) => { - PrivateKey::gen_private_key_phrase( - secp, - phrase, - 0, - 0, - coin_type, - seed_pass, - ) - } - Err(_) => Err(DaemonError::Phrasing), - } - } - /// generate the public key for this private key - pub fn public_key( - &self, - secp: &Secp256k1, - ) -> PublicKey { - if self.coin_type == ETHEREUM_COIN_TYPE { - { - ::core::panicking::panic_fmt( - format_args!( - "Coin Type {0} not supported without eth feature", - ETHEREUM_COIN_TYPE, - ), - ); - }; - } - let x = self.private_key.private_key.public_key(secp); - PublicKey::from_bitcoin_public_key(&bitcoin::PublicKey::new(x)) - } - pub fn get_injective_public_key( - &self, - secp: &Secp256k1, - ) -> SignerPublicKey { - use base64::engine::general_purpose; - use cosmrs::tx::MessageExt; - use secp256k1::SecretKey; - let secret_key = SecretKey::from_slice(self.raw_key().as_slice()) - .unwrap(); - let public_key = secp256k1::PublicKey::from_secret_key( - secp, - &secret_key, - ); - let vec_pk = public_key.serialize(); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "{0:?}, public key", - general_purpose::STANDARD.encode(vec_pk), - ), - lvl, - &( - "cw_orch_daemon::keys::private", - "cw_orch_daemon::keys::private", - "cw-orch-daemon/src/keys/private.rs", - ), - 124u32, - ::log::__private_api::Option::None, - ); - } - }; - let inj_key = InjectivePubKey { - key: vec_pk.into(), - }; - inj_key.to_any().unwrap().try_into().unwrap() - } - pub fn get_signer_public_key( - &self, - secp: &Secp256k1, - ) -> Option { - if self.coin_type == ETHEREUM_COIN_TYPE { - { - ::core::panicking::panic_fmt( - format_args!( - "Coin Type {0} not supported without eth feature", - ETHEREUM_COIN_TYPE, - ), - ); - }; - } - Some( - cosmrs::crypto::secp256k1::SigningKey::from_slice( - self.raw_key().as_slice(), - ) - .unwrap() - .public_key() - .into(), - ) - } - pub fn raw_key(&self) -> Vec { - self.private_key.private_key.secret_bytes().to_vec() - } - fn gen_private_key_phrase( - secp: &Secp256k1, - phrase: Phrase, - account: u32, - index: u32, - coin_type: u32, - seed_phrase: &str, - ) -> Result { - let seed = phrase.to_seed(seed_phrase); - let root_private_key = ExtendedPrivKey::new_master( - Network::Bitcoin, - seed.as_bytes(), - ) - .unwrap(); - let path = { - let res = ::alloc::fmt::format( - format_args!( - "m/44\'/{0}\'/{1}\'/0/{2}", - coin_type, - account, - index, - ), - ); - res - }; - let derivation_path = path.into_derivation_path()?; - let private_key = root_private_key.derive_priv(secp, &derivation_path)?; - Ok(PrivateKey { - account, - index, - coin_type, - mnemonic: Some(phrase), - root_private_key, - private_key, - }) - } - /// the words used to generate this private key - pub fn words(&self) -> Option<&str> { - self.mnemonic.as_ref().map(|phrase| phrase.phrase()) - } - /// used for testing - /// could potentially be used to recreate the private key instead of words - #[allow(dead_code)] - pub(crate) fn seed(&self, passwd: &str) -> Option { - self.mnemonic.as_ref().map(|phrase| phrase.to_seed(passwd)) - } - } - } - pub mod public { - use crate::DaemonError; - use bitcoin::bech32::{decode, encode, u5, FromBase32, ToBase32, Variant}; - pub use ed25519_dalek::VerifyingKey as Ed25519; - use ring::digest::{Context, SHA256}; - use ripemd::{Digest as _, Ripemd160}; - use serde::{Deserialize, Serialize}; - static BECH32_PUBKEY_DATA_PREFIX_SECP256K1: [u8; 5] = [ - 0xeb, - 0x5a, - 0xe9, - 0x87, - 0x21, - ]; - static BECH32_PUBKEY_DATA_PREFIX_ED25519: [u8; 5] = [ - 0x16, - 0x24, - 0xde, - 0x64, - 0x20, - ]; - /// The public key we used to generate the cosmos/tendermind/terrad addresses - pub struct PublicKey { - /// This is optional as we can generate non-pub keys without - pub raw_pub_key: Option>, - /// The raw bytes used to generate non-pub keys - pub raw_address: Option>, - } - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl<'de> _serde::Deserialize<'de> for PublicKey { - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - #[allow(non_camel_case_types)] - #[doc(hidden)] - enum __Field { - __field0, - __field1, - __ignore, - } - #[doc(hidden)] - struct __FieldVisitor; - impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { - type Value = __Field; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "field identifier", - ) - } - fn visit_u64<__E>( - self, - __value: u64, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - 0u64 => _serde::__private::Ok(__Field::__field0), - 1u64 => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_str<__E>( - self, - __value: &str, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - "raw_pub_key" => _serde::__private::Ok(__Field::__field0), - "raw_address" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_bytes<__E>( - self, - __value: &[u8], - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - b"raw_pub_key" => _serde::__private::Ok(__Field::__field0), - b"raw_address" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - } - impl<'de> _serde::Deserialize<'de> for __Field { - #[inline] - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - _serde::Deserializer::deserialize_identifier( - __deserializer, - __FieldVisitor, - ) - } - } - #[doc(hidden)] - struct __Visitor<'de> { - marker: _serde::__private::PhantomData, - lifetime: _serde::__private::PhantomData<&'de ()>, - } - impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { - type Value = PublicKey; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "struct PublicKey", - ) - } - #[inline] - fn visit_seq<__A>( - self, - mut __seq: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<'de>, - { - let __field0 = match _serde::de::SeqAccess::next_element::< - Option>, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 0usize, - &"struct PublicKey with 2 elements", - ), - ); - } - }; - let __field1 = match _serde::de::SeqAccess::next_element::< - Option>, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 1usize, - &"struct PublicKey with 2 elements", - ), - ); - } - }; - _serde::__private::Ok(PublicKey { - raw_pub_key: __field0, - raw_address: __field1, - }) - } - #[inline] - fn visit_map<__A>( - self, - mut __map: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::MapAccess<'de>, - { - let mut __field0: _serde::__private::Option< - Option>, - > = _serde::__private::None; - let mut __field1: _serde::__private::Option< - Option>, - > = _serde::__private::None; - while let _serde::__private::Some(__key) - = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { - match __key { - __Field::__field0 => { - if _serde::__private::Option::is_some(&__field0) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field( - "raw_pub_key", - ), - ); - } - __field0 = _serde::__private::Some( - _serde::de::MapAccess::next_value::< - Option>, - >(&mut __map)?, - ); - } - __Field::__field1 => { - if _serde::__private::Option::is_some(&__field1) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field( - "raw_address", - ), - ); - } - __field1 = _serde::__private::Some( - _serde::de::MapAccess::next_value::< - Option>, - >(&mut __map)?, - ); - } - _ => { - let _ = _serde::de::MapAccess::next_value::< - _serde::de::IgnoredAny, - >(&mut __map)?; - } - } - } - let __field0 = match __field0 { - _serde::__private::Some(__field0) => __field0, - _serde::__private::None => { - _serde::__private::de::missing_field("raw_pub_key")? - } - }; - let __field1 = match __field1 { - _serde::__private::Some(__field1) => __field1, - _serde::__private::None => { - _serde::__private::de::missing_field("raw_address")? - } - }; - _serde::__private::Ok(PublicKey { - raw_pub_key: __field0, - raw_address: __field1, - }) - } - } - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &[ - "raw_pub_key", - "raw_address", - ]; - _serde::Deserializer::deserialize_struct( - __deserializer, - "PublicKey", - FIELDS, - __Visitor { - marker: _serde::__private::PhantomData::, - lifetime: _serde::__private::PhantomData, - }, - ) - } - } - }; - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl _serde::Serialize for PublicKey { - fn serialize<__S>( - &self, - __serializer: __S, - ) -> _serde::__private::Result<__S::Ok, __S::Error> - where - __S: _serde::Serializer, - { - let mut __serde_state = _serde::Serializer::serialize_struct( - __serializer, - "PublicKey", - false as usize + 1 + 1, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "raw_pub_key", - &self.raw_pub_key, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "raw_address", - &self.raw_address, - )?; - _serde::ser::SerializeStruct::end(__serde_state) - } - } - }; - #[automatically_derived] - impl ::core::fmt::Debug for PublicKey { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "PublicKey", - "raw_pub_key", - &self.raw_pub_key, - "raw_address", - &&self.raw_address, - ) - } - } - #[automatically_derived] - impl ::core::clone::Clone for PublicKey { - #[inline] - fn clone(&self) -> PublicKey { - PublicKey { - raw_pub_key: ::core::clone::Clone::clone(&self.raw_pub_key), - raw_address: ::core::clone::Clone::clone(&self.raw_address), - } - } - } - impl PublicKey { - /// Generate a Cosmos/Tendermint/Terrad Public Key - pub fn from_bitcoin_public_key(bpub: &bitcoin::key::PublicKey) -> PublicKey { - let bpub_bytes = bpub.inner.serialize(); - let raw_pub_key = PublicKey::pubkey_from_public_key(&bpub_bytes); - let raw_address = PublicKey::address_from_public_key(&bpub_bytes); - PublicKey { - raw_pub_key: Some(raw_pub_key), - raw_address: Some(raw_address), - } - } - /// Generate from secp256k1 Cosmos/Terrad Public Key - pub fn from_public_key(bpub: &[u8]) -> PublicKey { - let raw_pub_key = PublicKey::pubkey_from_public_key(bpub); - let raw_address = PublicKey::address_from_public_key(bpub); - PublicKey { - raw_pub_key: Some(raw_pub_key), - raw_address: Some(raw_address), - } - } - /// Generate a Cosmos/Tendermint/Terrad Account - pub fn from_account( - acc_address: &str, - prefix: &str, - ) -> Result { - PublicKey::check_prefix_and_length(prefix, acc_address, 44) - .and_then(|vu5| { - let vu8 = Vec::from_base32(vu5.as_slice()) - .map_err(|source| DaemonError::Conversion { - key: acc_address.into(), - source, - })?; - Ok(PublicKey { - raw_pub_key: None, - raw_address: Some(vu8), - }) - }) - } - /// build a public key from a tendermint public key - pub fn from_tendermint_key( - tendermint_public_key: &str, - ) -> Result { - let len = tendermint_public_key.len(); - if len == 83 { - PublicKey::check_prefix_and_length( - "terravalconspub", - tendermint_public_key, - len, - ) - .and_then(|vu5| { - let vu8 = Vec::from_base32(vu5.as_slice()) - .map_err(|source| { - DaemonError::Conversion { - key: tendermint_public_key.into(), - source, - } - })?; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("{0:#?}", hex::encode(&vu8)), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 74u32, - ::log::__private_api::Option::None, - ); - } - }; - if vu8.starts_with(&BECH32_PUBKEY_DATA_PREFIX_SECP256K1) { - let public_key = PublicKey::public_key_from_pubkey(&vu8)?; - let raw = PublicKey::address_from_public_key(&public_key); - Ok(PublicKey { - raw_pub_key: Some(vu8), - raw_address: Some(raw), - }) - } else { - Err(DaemonError::ConversionSECP256k1) - } - }) - } else if len == 82 { - PublicKey::check_prefix_and_length( - "terravalconspub", - tendermint_public_key, - len, - ) - .and_then(|vu5| { - let vu8 = Vec::from_base32(vu5.as_slice()) - .map_err(|source| { - DaemonError::Conversion { - key: tendermint_public_key.into(), - source, - } - })?; - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("ED25519 public keys are not fully supported"), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 100u32, - ::log::__private_api::Option::None, - ); - } - }; - if vu8.starts_with(&BECH32_PUBKEY_DATA_PREFIX_ED25519) { - let raw = PublicKey::address_from_public_ed25519_key(&vu8)?; - Ok(PublicKey { - raw_pub_key: Some(vu8), - raw_address: Some(raw), - }) - } else { - Err(DaemonError::ConversionED25519) - } - }) - } else { - Err(DaemonError::ConversionLength(len)) - } - } - /// build a terravalcons address from a tendermint hex key - /// the tendermint_hex_address should be a hex code of 40 length - pub fn from_tendermint_address( - tendermint_hex_address: &str, - ) -> Result { - let len = tendermint_hex_address.len(); - if len == 40 { - let raw = hex::decode(tendermint_hex_address)?; - Ok(PublicKey { - raw_pub_key: None, - raw_address: Some(raw), - }) - } else { - Err(DaemonError::ConversionLengthED25519Hex(len)) - } - } - /// Generate a Operator address for this public key (used by the validator) - pub fn from_operator_address( - valoper_address: &str, - ) -> Result { - PublicKey::check_prefix_and_length("terravaloper", valoper_address, 51) - .and_then(|vu5| { - let vu8 = Vec::from_base32(vu5.as_slice()) - .map_err(|source| DaemonError::Conversion { - key: valoper_address.into(), - source, - })?; - Ok(PublicKey { - raw_pub_key: None, - raw_address: Some(vu8), - }) - }) - } - /// Generate Public key from raw address - pub fn from_raw_address( - raw_address: &str, - ) -> Result { - let vec1 = hex::decode(raw_address)?; - Ok(PublicKey { - raw_pub_key: None, - raw_address: Some(vec1), - }) - } - fn check_prefix_and_length( - prefix: &str, - data: &str, - length: usize, - ) -> Result, DaemonError> { - let (hrp, decoded_str, _) = decode(data) - .map_err(|source| DaemonError::Conversion { - key: data.into(), - source, - })?; - if hrp == prefix && data.len() == length { - Ok(decoded_str) - } else { - Err( - DaemonError::Bech32DecodeExpanded( - hrp, - data.len(), - prefix.into(), - length, - ), - ) - } - } - /** - Gets a bech32-words pubkey from a compressed bytes Secp256K1 public key. - - @param publicKey raw public key - */ - pub fn pubkey_from_public_key(public_key: &[u8]) -> Vec { - [BECH32_PUBKEY_DATA_PREFIX_SECP256K1.to_vec(), public_key.to_vec()] - .concat() - } - /** - Gets a bech32-words pubkey from a compressed bytes Ed25519 public key. - - @param publicKey raw public key - */ - pub fn pubkey_from_ed25519_public_key(public_key: &[u8]) -> Vec { - [BECH32_PUBKEY_DATA_PREFIX_ED25519.to_vec(), public_key.to_vec()] - .concat() - } - /// Translate from a BECH32 prefixed key to a standard public key - pub fn public_key_from_pubkey( - pub_key: &[u8], - ) -> Result, DaemonError> { - if pub_key.starts_with(&BECH32_PUBKEY_DATA_PREFIX_SECP256K1) { - let len = BECH32_PUBKEY_DATA_PREFIX_SECP256K1.len(); - let len2 = pub_key.len(); - Ok(Vec::from(&pub_key[len..len2])) - } else if pub_key.starts_with(&BECH32_PUBKEY_DATA_PREFIX_ED25519) { - let len = BECH32_PUBKEY_DATA_PREFIX_ED25519.len(); - let len2 = pub_key.len(); - let vec = &pub_key[len..len2]; - let ed25519_pubkey = Ed25519::from_bytes(vec.try_into().unwrap())?; - Ok(ed25519_pubkey.to_bytes().to_vec()) - } else { - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("pub key does not start with BECH32 PREFIX"), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 228u32, - ::log::__private_api::Option::None, - ); - } - }; - Err(DaemonError::Bech32DecodeErr) - } - } - /** - Gets a raw address from a compressed bytes public key. - - @param publicKey raw public key - */ - pub fn address_from_public_key(public_key: &[u8]) -> Vec { - let mut hasher = Ripemd160::new(); - let sha_result = ring::digest::digest(&SHA256, public_key); - hasher.update(&sha_result.as_ref()[0..32]); - let ripe_result = hasher.finalize(); - let address: Vec = ripe_result[0..20].to_vec(); - address - } - /** - Gets a raw address from a ed25519 public key. - - @param publicKey raw public key - */ - pub fn address_from_public_ed25519_key( - public_key: &[u8], - ) -> Result, DaemonError> { - if public_key.len() != (32 + 5) { - Err( - DaemonError::ConversionPrefixED25519( - public_key.len(), - hex::encode(public_key), - ), - ) - } else { - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "address_from_public_ed25519_key public key - {0}", - hex::encode(public_key), - ), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 262u32, - ::log::__private_api::Option::None, - ); - } - }; - let mut sha_result: [u8; 32] = [0; 32]; - let sha_result = ring::digest::digest(&SHA256, &public_key[5..]); - let address: Vec = sha_result.as_ref()[0..20].to_vec(); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "address_from_public_ed25519_key sha result - {0}", - hex::encode(&address), - ), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 283u32, - ::log::__private_api::Option::None, - ); - } - }; - Ok(address) - } - } - /// The main account used in most things - pub fn account(&self, prefix: &str) -> Result { - match &self.raw_address { - Some(raw) => { - let data = encode(prefix, raw.to_base32(), Variant::Bech32); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => Err(DaemonError::Implementation), - } - } - /// The operator address used for validators - pub fn operator_address(&self, prefix: &str) -> Result { - match &self.raw_address { - Some(raw) => { - let data = encode( - &{ - let res = ::alloc::fmt::format( - format_args!("{0}{1}", prefix, "valoper"), - ); - res - }, - raw.to_base32(), - Variant::Bech32, - ); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => Err(DaemonError::Implementation), - } - } - /// application public key - Application keys are associated with a public key terrapub- and an address terra- - pub fn application_public_key( - &self, - prefix: &str, - ) -> Result { - match &self.raw_pub_key { - Some(raw) => { - let data = encode( - &{ - let res = ::alloc::fmt::format( - format_args!("{0}{1}", prefix, "pub"), - ); - res - }, - raw.to_base32(), - Variant::Bech32, - ); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => { - { - let lvl = ::log::Level::Warn; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("Missing Public Key. Can\'t continue"), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 335u32, - ::log::__private_api::Option::None, - ); - } - }; - Err(DaemonError::Implementation) - } - } - } - /// The operator address used for validators public key. - pub fn operator_address_public_key( - &self, - prefix: &str, - ) -> Result { - match &self.raw_pub_key { - Some(raw) => { - let data = encode( - &{ - let res = ::alloc::fmt::format( - format_args!("{0}{1}", prefix, "valoperpub"), - ); - res - }, - raw.to_base32(), - Variant::Bech32, - ); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => Err(DaemonError::Implementation), - } - } - /// This is a unique key used to sign block hashes. It is associated with a public key terravalconspub. - pub fn tendermint(&self, prefix: &str) -> Result { - match &self.raw_address { - Some(raw) => { - let data = encode( - &{ - let res = ::alloc::fmt::format( - format_args!("{0}{1}", prefix, "valcons"), - ); - res - }, - raw.to_base32(), - Variant::Bech32, - ); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => Err(DaemonError::Implementation), - } - } - /// This is a unique key used to sign block hashes. It is associated with a public key terravalconspub. - pub fn tendermint_pubkey( - &self, - prefix: &str, - ) -> Result { - match &self.raw_pub_key { - Some(raw) => { - let b32 = raw.to_base32(); - let data = encode( - &{ - let res = ::alloc::fmt::format( - format_args!("{0}{1}", prefix, "valconspub"), - ); - res - }, - b32, - Variant::Bech32, - ); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => Err(DaemonError::Implementation), - } - } - } - } - pub mod signature { - use crate::DaemonError; - use base64::engine::{general_purpose::STANDARD, Engine}; - use ring::digest::SHA256; - use secp256k1::{Message, Secp256k1}; - pub struct Signature {} - impl Signature { - pub fn verify( - secp: &Secp256k1, - pub_key: &str, - signature: &str, - blob: &str, - ) -> Result<(), DaemonError> { - let public = STANDARD.decode(pub_key)?; - let sig = STANDARD.decode(signature)?; - let pk = secp256k1::PublicKey::from_slice(public.as_slice())?; - let sha_result = ring::digest::digest(&SHA256, blob.as_bytes()); - let message: Message = Message::from_slice(&sha_result.as_ref()[0..32])?; - let secp_sig = secp256k1::ecdsa::Signature::from_compact( - sig.as_slice(), - )?; - secp.verify_ecdsa(&message, &secp_sig, &pk)?; - Ok(()) - } - } - } -} -pub mod live_mock { - //! Live mock is a mock that uses a live chain to query for data. - //! It can be used to do chain-backed unit-testing. It can't be used for state-changing operations. - use crate::queriers::Bank; - use crate::queriers::CosmWasm; - use crate::queriers::DaemonQuerier; - use crate::queriers::Staking; - use cosmwasm_std::Addr; - use cosmwasm_std::AllBalanceResponse; - use cosmwasm_std::BalanceResponse; - use cosmwasm_std::Delegation; - use cosmwasm_std::{AllDelegationsResponse, BondedDenomResponse}; - use cosmwasm_std::BankQuery; - use cosmwasm_std::Binary; - use cosmwasm_std::Empty; - use cosmwasm_std::StakingQuery; - use ibc_chain_registry::chain::ChainData; - use tokio::runtime::Runtime; - use tonic::transport::Channel; - use std::marker::PhantomData; - use std::str::FromStr; - use cosmwasm_std::testing::{MockApi, MockStorage}; - use cosmwasm_std::{ - from_slice, to_binary, Coin, ContractResult, OwnedDeps, Querier, QuerierResult, - QueryRequest, SystemError, SystemResult, Uint128, WasmQuery, - }; - use crate::channel::GrpcChannel; - fn to_cosmwasm_coin(c: cosmrs::proto::cosmos::base::v1beta1::Coin) -> Coin { - Coin { - amount: Uint128::from_str(&c.amount).unwrap(), - denom: c.denom, - } - } - const QUERIER_ERROR: &str = "Only Bank balances and Wasm (raw + smart) and Some staking queries are covered for now"; - /// mock_dependencies is a drop-in replacement for cosmwasm_std::testing::mock_dependencies - /// this uses our CustomQuerier. - pub fn mock_dependencies( - chain_info: ChainData, - ) -> OwnedDeps { - let custom_querier: WasmMockQuerier = WasmMockQuerier::new(chain_info); - OwnedDeps { - storage: MockStorage::default(), - api: MockApi::default(), - querier: custom_querier, - custom_query_type: PhantomData, - } - } - /// Querier struct that fetches queries on-chain directly - pub struct WasmMockQuerier { - channel: Channel, - runtime: Runtime, - } - impl Querier for WasmMockQuerier { - fn raw_query(&self, bin_request: &[u8]) -> QuerierResult { - let request: QueryRequest = match from_slice(bin_request) { - Ok(v) => v, - Err(e) => { - return SystemResult::Err(SystemError::InvalidRequest { - error: { - let res = ::alloc::fmt::format( - format_args!("Parsing query request: {0}", e), - ); - res - }, - request: bin_request.into(), - }); - } - }; - self.handle_query(&request) - } - } - impl WasmMockQuerier { - /// Function used to handle a query and customize the query behavior - /// This implements some queries by querying an actual node for the responses - pub fn handle_query(&self, request: &QueryRequest) -> QuerierResult { - match &request { - QueryRequest::Wasm(x) => { - let querier = CosmWasm::new(self.channel.clone()); - match x { - WasmQuery::Smart { contract_addr, msg } => { - let query_result: Result = self - .runtime - .block_on( - querier - .contract_state(contract_addr.to_string(), msg.to_vec()), - ) - .map(|query_result| query_result.into()); - SystemResult::Ok(ContractResult::from(query_result)) - } - WasmQuery::Raw { contract_addr, key } => { - let query_result = self - .runtime - .block_on( - querier - .contract_raw_state(contract_addr.to_string(), key.to_vec()), - ) - .map(|query_result| query_result.data.into()); - SystemResult::Ok(ContractResult::from(query_result)) - } - _ => { - SystemResult::Err(SystemError::InvalidRequest { - error: QUERIER_ERROR.to_string(), - request: to_binary(&request).unwrap(), - }) - } - } - } - QueryRequest::Bank(x) => { - let querier = Bank::new(self.channel.clone()); - match x { - BankQuery::Balance { address, denom } => { - let query_result = self - .runtime - .block_on(querier.balance(address, Some(denom.clone()))) - .map(|result| { - to_binary( - &BalanceResponse { - amount: Coin { - amount: Uint128::from_str(&result[0].amount).unwrap(), - denom: result[0].denom.clone(), - }, - }, - ) - .unwrap() - }); - SystemResult::Ok(ContractResult::from(query_result)) - } - BankQuery::AllBalances { address } => { - let query_result = self - .runtime - .block_on(querier.balance(address, None)) - .map(|result| AllBalanceResponse { - amount: result - .into_iter() - .map(|c| Coin { - amount: Uint128::from_str(&c.amount).unwrap(), - denom: c.denom, - }) - .collect(), - }) - .map(|query_result| to_binary(&query_result)) - .unwrap(); - SystemResult::Ok(ContractResult::from(query_result)) - } - _ => { - SystemResult::Err(SystemError::InvalidRequest { - error: QUERIER_ERROR.to_string(), - request: to_binary(&request).unwrap(), - }) - } - } - } - QueryRequest::Staking(x) => { - let querier = Staking::new(self.channel.clone()); - match x { - StakingQuery::BondedDenom {} => { - let query_result = self - .runtime - .block_on(querier.params()) - .map(|result| BondedDenomResponse { - denom: result.params.unwrap().bond_denom, - }) - .map(|query_result| to_binary(&query_result)) - .unwrap(); - SystemResult::Ok(ContractResult::from(query_result)) - } - StakingQuery::AllDelegations { delegator } => { - let query_result = self - .runtime - .block_on(querier.delegator_delegations(delegator, None)) - .map(|result| AllDelegationsResponse { - delegations: result - .delegation_responses - .into_iter() - .filter_map(|delegation| { - delegation - .delegation - .map(|d| Delegation { - delegator: Addr::unchecked(d.delegator_address), - validator: d.validator_address, - amount: to_cosmwasm_coin(delegation.balance.unwrap()), - }) - }) - .collect(), - }) - .map(|query_result| to_binary(&query_result)) - .unwrap(); - SystemResult::Ok(ContractResult::from(query_result)) - } - _ => ::core::panicking::panic("not yet implemented"), - } - } - _ => { - SystemResult::Err(SystemError::InvalidRequest { - error: QUERIER_ERROR.to_string(), - request: to_binary(&request).unwrap(), - }) - } - } - } - } - impl WasmMockQuerier { - /// Creates a querier from chain information - pub fn new(chain: ChainData) -> Self { - let rt = Runtime::new().unwrap(); - let channel = rt - .block_on(GrpcChannel::connect(&chain.apis.grpc, &chain.chain_id)) - .unwrap(); - WasmMockQuerier { - channel, - runtime: rt, - } - } - } -} -pub mod queriers { - //! # DaemonQuerier - //! - //! DaemonAsync queriers are gRPC query clients for the CosmosSDK modules. They can be used to query the different modules (Bank, Ibc, Authz, ...). - //! - //! ## Usage - //! - //! You will need to acquire a [gRPC channel](Channel) to a running CosmosSDK node to be able to use the queriers. - //! Here is an example of how to acquire one using the DaemonAsync builder. - //! - //! ```no_run - //! // require the querier you want to use, in this case Node - //! use cw_orch_daemon::{queriers::Node, DaemonAsync, networks, queriers::DaemonQuerier}; - //! # tokio_test::block_on(async { - //! // call the builder and configure it as you need - //! let daemon = DaemonAsync::builder() - //! .chain(networks::LOCAL_JUNO) - //! .build() - //! .await.unwrap(); - //! // now you can use the Node querier: - //! let node = Node::new(daemon.channel()); - //! let node_info = node.info(); - //! # }) - //! ``` - mod bank { - use crate::{cosmos_modules, error::DaemonError}; - use cosmrs::proto::cosmos::{ - base::{query::v1beta1::PageRequest, v1beta1::Coin}, - bank::v1beta1::QueryBalanceResponse, - }; - use tonic::transport::Channel; - use super::DaemonQuerier; - /// Queries for Cosmos Bank Module - pub struct Bank { - channel: Channel, - } - impl DaemonQuerier for Bank { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Bank { - /// Query the bank balance of a given address - /// If denom is None, returns all balances - pub async fn balance( - &self, - address: impl Into, - denom: Option, - ) -> Result, DaemonError> { - use cosmos_modules::bank::query_client::QueryClient; - match denom { - Some(denom) => { - let resp: QueryBalanceResponse = (); - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = cosmos_modules::bank::QueryBalanceRequest { - address: address.into(), - denom, - }; - let resp = client.balance(request).await?.into_inner(); - let coin = resp.balance.unwrap(); - Ok( - <[_]>::into_vec( - #[rustc_box] - ::alloc::boxed::Box::new([coin]), - ), - ) - } - None => { - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = cosmos_modules::bank::QueryAllBalancesRequest { - address: address.into(), - ..Default::default() - }; - let resp: QueryBalanceResponse = (); - let denoms_metadata: cosmos_modules::bank::QueryDenomsMetadataResponse = (); - let resp = client.all_balances(request).await?.into_inner(); - let coins = resp.balances; - Ok(coins.into_iter().collect()) - } - } - } - /// Query spendable balance for address - pub async fn spendable_balances( - &self, - address: impl Into, - ) -> Result, DaemonError> { - let spendable_balances: cosmos_modules::bank::QuerySpendableBalancesResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QuerySpendableBalancesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QuerySpendableBalancesRequest { - address: address.into(), - pagination: None, - }; - let response = client - .spendable_balances(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 89u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(spendable_balances.balances) - } - /// Query total supply in the bank - pub async fn total_supply(&self) -> Result, DaemonError> { - let total_supply: cosmos_modules::bank::QueryTotalSupplyResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QueryTotalSupplyRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryTotalSupplyRequest { - pagination: None, - }; - let response = client - .total_supply(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 103u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(total_supply.supply) - } - /// Query total supply in the bank for a denom - pub async fn supply_of( - &self, - denom: impl Into, - ) -> Result { - let supply_of: cosmos_modules::bank::QuerySupplyOfResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QuerySupplyOfRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QuerySupplyOfRequest { - denom: denom.into(), - }; - let response = client.supply_of(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 114u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(supply_of.amount.unwrap()) - } - /// Query params - pub async fn params( - &self, - ) -> Result { - let params: cosmos_modules::bank::QueryParamsResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QueryParamsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryParamsRequest {}; - let response = client.params(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 128u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(params.params.unwrap()) - } - /// Query denom metadata - pub async fn denom_metadata( - &self, - denom: impl Into, - ) -> Result { - let denom_metadata: cosmos_modules::bank::QueryDenomMetadataResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QueryDenomMetadataRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDenomMetadataRequest { - denom: denom.into(), - }; - let response = client - .denom_metadata(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 137u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(denom_metadata.metadata.unwrap()) - } - /// Query denoms metadata with pagination - /// - /// see [PageRequest] for pagination - pub async fn denoms_metadata( - &self, - pagination: Option, - ) -> Result, DaemonError> { - let denoms_metadata: cosmos_modules::bank::QueryDenomsMetadataResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QueryDenomsMetadataRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDenomsMetadataRequest { - pagination: pagination, - }; - let response = client - .denoms_metadata(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 155u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(denoms_metadata.metadatas) - } - } - } - mod cosmwasm { - use crate::{cosmos_modules, error::DaemonError}; - use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; - use tonic::transport::Channel; - use super::DaemonQuerier; - /// Querier for the CosmWasm SDK module - pub struct CosmWasm { - channel: Channel, - } - impl DaemonQuerier for CosmWasm { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl CosmWasm { - /// Query code_id by hash - pub async fn code_id_hash( - &self, - code_id: u64, - ) -> Result { - use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryCodeRequest { code_id }; - let resp = client.code(request).await?.into_inner(); - let contract_hash = resp.code_info.unwrap().data_hash; - let on_chain_hash = base16::encode_lower(&contract_hash); - Ok(on_chain_hash) - } - /// Query contract info - pub async fn contract_info( - &self, - address: impl Into, - ) -> Result { - use cosmos_modules::cosmwasm::{ - query_client::*, QueryContractInfoRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryContractInfoRequest { - address: address.into(), - }; - let resp = client.contract_info(request).await?.into_inner(); - let contract_info = resp.contract_info.unwrap(); - Ok(contract_info) - } - /// Query contract history - pub async fn contract_history( - &self, - address: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::cosmwasm::QueryContractHistoryResponse, - DaemonError, - > { - use cosmos_modules::cosmwasm::{ - query_client::*, QueryContractHistoryRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryContractHistoryRequest { - address: address.into(), - pagination, - }; - Ok(client.contract_history(request).await?.into_inner()) - } - /// Query contract state - pub async fn contract_state( - &self, - address: impl Into, - query_data: Vec, - ) -> Result, DaemonError> { - use cosmos_modules::cosmwasm::{ - query_client::*, QuerySmartContractStateRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QuerySmartContractStateRequest { - address: address.into(), - query_data, - }; - Ok(client.smart_contract_state(request).await?.into_inner().data) - } - /// Query all contract state - pub async fn all_contract_state( - &self, - address: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::cosmwasm::QueryAllContractStateResponse, - DaemonError, - > { - use cosmos_modules::cosmwasm::{ - query_client::*, QueryAllContractStateRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryAllContractStateRequest { - address: address.into(), - pagination, - }; - Ok(client.all_contract_state(request).await?.into_inner()) - } - /// Query code - pub async fn code( - &self, - code_id: u64, - ) -> Result { - use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryCodeRequest { code_id }; - Ok(client.code(request).await?.into_inner().code_info.unwrap()) - } - /// Query code bytes - pub async fn code_data(&self, code_id: u64) -> Result, DaemonError> { - use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryCodeRequest { code_id }; - Ok(client.code(request).await?.into_inner().data) - } - /// Query codes - pub async fn codes( - &self, - pagination: Option, - ) -> Result, DaemonError> { - use cosmos_modules::cosmwasm::{query_client::*, QueryCodesRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryCodesRequest { pagination }; - Ok(client.codes(request).await?.into_inner().code_infos) - } - /// Query pinned codes - pub async fn pinned_codes( - &self, - ) -> Result< - cosmos_modules::cosmwasm::QueryPinnedCodesResponse, - DaemonError, - > { - use cosmos_modules::cosmwasm::{query_client::*, QueryPinnedCodesRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryPinnedCodesRequest { - pagination: None, - }; - Ok(client.pinned_codes(request).await?.into_inner()) - } - /// Query contracts by code - pub async fn contract_by_codes( - &self, - code_id: u64, - ) -> Result< - cosmos_modules::cosmwasm::QueryContractsByCodeResponse, - DaemonError, - > { - use cosmos_modules::cosmwasm::{ - query_client::*, QueryContractsByCodeRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryContractsByCodeRequest { - code_id, - pagination: None, - }; - Ok(client.contracts_by_code(request).await?.into_inner()) - } - /// Query raw contract state - pub async fn contract_raw_state( - &self, - address: impl Into, - query_data: Vec, - ) -> Result< - cosmos_modules::cosmwasm::QueryRawContractStateResponse, - DaemonError, - > { - use cosmos_modules::cosmwasm::{ - query_client::*, QueryRawContractStateRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryRawContractStateRequest { - address: address.into(), - query_data, - }; - Ok(client.raw_contract_state(request).await?.into_inner()) - } - /// Query params - pub async fn params( - &self, - ) -> Result { - use cosmos_modules::cosmwasm::{query_client::*, QueryParamsRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - Ok(client.params(QueryParamsRequest {}).await?.into_inner()) - } - } - } - mod feegrant { - use crate::{cosmos_modules, error::DaemonError}; - use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; - use tonic::transport::Channel; - use super::DaemonQuerier; - /// Querier for the Cosmos Gov module - pub struct Feegrant { - channel: Channel, - } - impl DaemonQuerier for Feegrant { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Feegrant { - /// Query all allowances granted to the grantee address by a granter address - pub async fn allowance( - &self, - granter: impl Into, - grantee: impl Into, - ) -> Result { - let allowance: cosmos_modules::feegrant::QueryAllowanceResponse = { - use crate::cosmos_modules::feegrant::{ - query_client::QueryClient, QueryAllowanceRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryAllowanceRequest { - granter: granter.into(), - grantee: grantee.into(), - }; - let response = client.allowance(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::feegrant", - "cw_orch_daemon::queriers::feegrant", - "cw-orch-daemon/src/queriers/feegrant.rs", - ), - 25u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(allowance.allowance.unwrap()) - } - /// Query allowances for grantee address with a given pagination - /// - /// see [PageRequest] for pagination - pub async fn allowances( - &self, - grantee: impl Into, - pagination: Option, - ) -> Result, DaemonError> { - let allowances: cosmos_modules::feegrant::QueryAllowancesResponse = { - use crate::cosmos_modules::feegrant::{ - query_client::QueryClient, QueryAllowancesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryAllowancesRequest { - grantee: grantee.into(), - pagination: pagination, - }; - let response = client - .allowances(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::feegrant", - "cw_orch_daemon::queriers::feegrant", - "cw-orch-daemon/src/queriers/feegrant.rs", - ), - 45u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(allowances.allowances) - } - } - } - mod gov { - use crate::{cosmos_modules, error::DaemonError}; - use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; - use tonic::transport::Channel; - use super::DaemonQuerier; - /// Querier for the Cosmos Gov module - pub struct Gov { - channel: Channel, - } - impl DaemonQuerier for Gov { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Gov { - /// Query proposal details by proposal id - pub async fn proposal( - &self, - proposal_id: u64, - ) -> Result { - let proposal: cosmos_modules::gov::QueryProposalResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryProposalRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryProposalRequest { - proposal_id: proposal_id, - }; - let response = client.proposal(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 24u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(proposal.proposal.unwrap()) - } - /// Query proposals based on given status - /// - /// see [PageRequest] for pagination - pub async fn proposals( - &self, - proposal_status: GovProposalStatus, - voter: impl Into, - depositor: impl Into, - pagination: Option, - ) -> Result { - let proposals: cosmos_modules::gov::QueryProposalsResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryProposalsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryProposalsRequest { - proposal_status: proposal_status as i32, - voter: voter.into(), - depositor: depositor.into(), - pagination: pagination, - }; - let response = client.proposals(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 45u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(proposals) - } - /// Query voted information based on proposal_id for voter address - pub async fn vote( - &self, - proposal_id: u64, - voter: impl Into, - ) -> Result { - let vote: cosmos_modules::gov::QueryVoteResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryVoteRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryVoteRequest { - proposal_id: proposal_id, - voter: voter.into(), - }; - let response = client.vote(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 65u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(vote.vote.unwrap()) - } - /// Query votes of a given proposal - /// - /// see [PageRequest] for pagination - pub async fn votes( - &self, - proposal_id: impl Into, - pagination: Option, - ) -> Result { - let votes: cosmos_modules::gov::QueryVotesResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryVotesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryVotesRequest { - proposal_id: proposal_id.into(), - pagination: pagination, - }; - let response = client.votes(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 85u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(votes) - } - /// Query all parameters of the gov module - pub async fn params( - &self, - params_type: impl Into, - ) -> Result { - let params: cosmos_modules::gov::QueryParamsResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryParamsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryParamsRequest { - params_type: params_type.into(), - }; - let response = client.params(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 102u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(params) - } - /// Query deposit information using proposal_id and depositor address - pub async fn deposit( - &self, - proposal_id: u64, - depositor: impl Into, - ) -> Result { - let deposit: cosmos_modules::gov::QueryDepositResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryDepositRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDepositRequest { - proposal_id: proposal_id, - depositor: depositor.into(), - }; - let response = client.deposit(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 119u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(deposit.deposit.unwrap()) - } - /// Query deposits of a proposal - /// - /// see [PageRequest] for pagination - pub async fn deposits( - &self, - proposal_id: u64, - pagination: Option, - ) -> Result { - let deposits: cosmos_modules::gov::QueryDepositsResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryDepositsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDepositsRequest { - proposal_id: proposal_id, - pagination: pagination, - }; - let response = client.deposits(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 139u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(deposits) - } - /// TallyResult queries the tally of a proposal vote. - pub async fn tally_result( - &mut self, - proposal_id: u64, - ) -> Result { - let tally_result: cosmos_modules::gov::QueryTallyResultResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryTallyResultRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryTallyResultRequest { - proposal_id: proposal_id, - }; - let response = client - .tally_result(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 156u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(tally_result.tally.unwrap()) - } - } - /// Proposal status - #[allow(missing_docs)] - pub enum GovProposalStatus { - Unspecified = 0, - DepositPeriod = 1, - VotingPeriod = 2, - Passed = 3, - Rejected = 4, - Failed = 5, - } - } - mod ibc { - use super::DaemonQuerier; - use crate::{cosmos_modules, error::DaemonError}; - use cosmos_modules::ibc_channel; - use cosmrs::proto::ibc::{ - applications::transfer::v1::{DenomTrace, QueryDenomTraceResponse}, - core::{ - channel::v1::QueryPacketCommitmentResponse, - client::v1::{IdentifiedClientState, QueryClientStatesResponse}, - connection::v1::{IdentifiedConnection, State}, - }, - lightclients::tendermint::v1::ClientState, - }; - use prost::Message; - use tonic::transport::Channel; - /// Querier for the Cosmos IBC module - pub struct Ibc { - channel: Channel, - } - impl DaemonQuerier for Ibc { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Ibc { - /// Get the trace of a specific denom - pub async fn denom_trace( - &self, - hash: String, - ) -> Result { - let denom_trace: QueryDenomTraceResponse = { - use crate::cosmos_modules::ibc_transfer::{ - query_client::QueryClient, QueryDenomTraceRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDenomTraceRequest { - hash: hash, - }; - let response = client - .denom_trace(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 32u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(denom_trace.denom_trace.unwrap()) - } - /// Get all the IBC clients for this daemon - pub async fn clients( - &self, - ) -> Result, DaemonError> { - let ibc_clients: QueryClientStatesResponse = { - use crate::cosmos_modules::ibc_client::{ - query_client::QueryClient, QueryClientStatesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryClientStatesRequest { - pagination: None, - }; - let response = client - .client_states(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 45u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_clients.client_states) - } - /// Get the state of a specific IBC client - pub async fn client_state( - &self, - client_id: impl ToString, - ) -> Result< - cosmos_modules::ibc_client::QueryClientStateResponse, - DaemonError, - > { - let response: cosmos_modules::ibc_client::QueryClientStateResponse = { - use crate::cosmos_modules::ibc_client::{ - query_client::QueryClient, QueryClientStateRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryClientStateRequest { - client_id: client_id.to_string(), - }; - let response = client - .client_state(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 60u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(response) - } - /// Get the consensus state of a specific IBC client - 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 = { - use crate::cosmos_modules::ibc_client::{ - query_client::QueryClient, QueryConsensusStatesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryConsensusStatesRequest { - client_id: client_id, - pagination: None, - }; - let response = client - .consensus_states(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 77u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(response) - } - /// Get the consensus status of a specific IBC client - pub async fn client_status( - &self, - client_id: impl ToString, - ) -> Result< - cosmos_modules::ibc_client::QueryClientStatusResponse, - DaemonError, - > { - let response: cosmos_modules::ibc_client::QueryClientStatusResponse = { - use crate::cosmos_modules::ibc_client::{ - query_client::QueryClient, QueryClientStatusRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryClientStatusRequest { - client_id: client_id.to_string(), - }; - let response = client - .client_status(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 95u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(response) - } - /// Get the ibc client parameters - pub async fn client_params( - &self, - ) -> Result< - cosmos_modules::ibc_client::QueryClientParamsResponse, - DaemonError, - > { - let response: cosmos_modules::ibc_client::QueryClientParamsResponse = { - use crate::cosmos_modules::ibc_client::{ - query_client::QueryClient, QueryClientParamsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryClientParamsRequest {}; - let response = client - .client_params(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 111u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(response) - } - /// Query the IBC connections for a specific chain - pub async fn connections( - &self, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_connection::QueryConnectionsResponse; - let ibc_connections: QueryConnectionsResponse = { - use crate::cosmos_modules::ibc_connection::{ - query_client::QueryClient, QueryConnectionsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryConnectionsRequest { - pagination: None, - }; - let response = client - .connections(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 121u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_connections.connections) - } - /// Search for open connections with a specific chain. - pub async fn open_connections( - &self, - client_chain_id: impl ToString, - ) -> Result, 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) - } - /// Get all the connections for this client - pub async fn client_connections( - &self, - client_id: impl Into, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_connection::QueryClientConnectionsResponse; - let client_id = client_id.into(); - let ibc_client_connections: QueryClientConnectionsResponse = { - use crate::cosmos_modules::ibc_connection::{ - query_client::QueryClient, QueryClientConnectionsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryClientConnectionsRequest { - client_id: client_id.clone(), - }; - let response = client - .client_connections(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 163u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_client_connections.connection_paths) - } - /// Get the (tendermint) client state for a specific connection - pub async fn connection_client( - &self, - connection_id: impl Into, - ) -> Result { - use cosmos_modules::ibc_connection::QueryConnectionClientStateResponse; - let connection_id = connection_id.into(); - let ibc_connection_client: QueryConnectionClientStateResponse = { - use crate::cosmos_modules::ibc_connection::{ - query_client::QueryClient, QueryConnectionClientStateRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryConnectionClientStateRequest { - connection_id: connection_id.clone(), - }; - let response = client - .connection_client_state(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 183u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - let client_state = ibc_connection_client - .identified_client_state - .ok_or( - DaemonError::ibc_err({ - let res = ::alloc::fmt::format( - format_args!( - "error identifying client for connection {0}", - connection_id, - ), - ); - res - }), - )?; - let client_state = ClientState::decode( - client_state.client_state.unwrap().value.as_slice(), - ) - .map_err(|e| DaemonError::ibc_err({ - let res = ::alloc::fmt::format( - format_args!("error decoding client state: {0}", e), - ); - res - }))?; - Ok(client_state) - } - /// Get the channel for a specific port and channel id - pub async fn channel( - &self, - port_id: impl Into, - channel_id: impl Into, - ) -> Result { - use cosmos_modules::ibc_channel::QueryChannelResponse; - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_channel: QueryChannelResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryChannelRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryChannelRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }; - let response = client.channel(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 218u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - ibc_channel - .channel - .ok_or( - DaemonError::ibc_err({ - let res = ::alloc::fmt::format( - format_args!( - "error fetching channel {0} on port {1}", - channel_id, - port_id, - ), - ); - res - }), - ) - } - /// Get all the channels for a specific connection - pub async fn connection_channels( - &self, - connection_id: impl Into, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_channel::QueryConnectionChannelsResponse; - let connection_id = connection_id.into(); - let ibc_connection_channels: QueryConnectionChannelsResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryConnectionChannelsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryConnectionChannelsRequest { - connection: connection_id.clone(), - pagination: None, - }; - let response = client - .connection_channels(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 242u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_connection_channels.channels) - } - /// Get the client state for a specific channel and port - pub async fn channel_client_state( - &self, - port_id: impl Into, - channel_id: impl Into, - ) -> Result { - use cosmos_modules::ibc_channel::QueryChannelClientStateResponse; - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_channel_client_state: QueryChannelClientStateResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryChannelClientStateRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryChannelClientStateRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }; - let response = client - .channel_client_state(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 265u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - ibc_channel_client_state - .identified_client_state - .ok_or( - DaemonError::ibc_err({ - let res = ::alloc::fmt::format( - format_args!( - "error identifying client for channel {0} on port {1}", - channel_id, - port_id, - ), - ); - res - }), - ) - } - /// Get all the packet commitments for a specific channel and port - pub async fn packet_commitments( - &self, - port_id: impl Into, - channel_id: impl Into, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_channel::QueryPacketCommitmentsResponse; - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_commitments: QueryPacketCommitmentsResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryPacketCommitmentsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPacketCommitmentsRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - pagination: None, - }; - let response = client - .packet_commitments(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 297u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_commitments.commitments) - } - /// Get the packet commitment for a specific channel, port and sequence - pub async fn packet_commitment( - &self, - port_id: impl Into, - channel_id: impl Into, - sequence: u64, - ) -> Result { - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_commitment: QueryPacketCommitmentResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryPacketCommitmentRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPacketCommitmentRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - sequence: sequence, - }; - let response = client - .packet_commitment(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 320u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_commitment) - } - /// Returns if the packet is received on the connected chain. - pub async fn packet_receipt( - &self, - port_id: impl Into, - channel_id: impl Into, - sequence: u64, - ) -> Result { - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_receipt: ibc_channel::QueryPacketReceiptResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryPacketReceiptRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPacketReceiptRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - sequence: sequence, - }; - let response = client - .packet_receipt(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 345u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_receipt.received) - } - /// Get all the packet acknowledgements for a specific channel, port and commitment sequences - pub async fn packet_acknowledgements( - &self, - port_id: impl Into, - channel_id: impl Into, - packet_commitment_sequences: Vec, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_channel::QueryPacketAcknowledgementsResponse; - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_acknowledgements: QueryPacketAcknowledgementsResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryPacketAcknowledgementsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPacketAcknowledgementsRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - packet_commitment_sequences: packet_commitment_sequences, - pagination: None, - }; - let response = client - .packet_acknowledgements(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 372u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_acknowledgements.acknowledgements) - } - /// Get the packet acknowledgement for a specific channel, port and sequence - pub async fn packet_acknowledgement( - &self, - port_id: impl Into, - channel_id: impl Into, - sequence: u64, - ) -> Result, DaemonError> { - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_acknowledgement: ibc_channel::QueryPacketAcknowledgementResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryPacketAcknowledgementRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPacketAcknowledgementRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - sequence: sequence, - }; - let response = client - .packet_acknowledgement(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 396u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_acknowledgement.acknowledgement) - } - /// No acknowledgement exists on receiving chain for the given packet commitment sequence on sending chain. - /// Returns the packet sequences that have not yet been received. - pub async fn unreceived_packets( - &self, - port_id: impl Into, - channel_id: impl Into, - packet_commitment_sequences: Vec, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_channel::QueryUnreceivedPacketsResponse; - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_unreceived: QueryUnreceivedPacketsResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryUnreceivedPacketsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryUnreceivedPacketsRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - packet_commitment_sequences: packet_commitment_sequences, - }; - let response = client - .unreceived_packets(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 422u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_unreceived.sequences) - } - /// Returns the acknowledgement sequences that have not yet been received. - /// Given a list of acknowledgement sequences from counterparty, determine if an ack on the counterparty chain has been received on the executing chain. - /// Returns the list of acknowledgement sequences that have not yet been received. - pub async fn unreceived_acks( - &self, - port_id: impl Into, - channel_id: impl Into, - packet_ack_sequences: Vec, - ) -> Result, DaemonError> { - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_unreceived: ibc_channel::QueryUnreceivedAcksResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryUnreceivedAcksRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryUnreceivedAcksRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - packet_ack_sequences: packet_ack_sequences, - }; - let response = client - .unreceived_acks(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 447u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_unreceived.sequences) - } - /// Returns the acknowledgement sequences that have not yet been received. - /// Given a list of acknowledgement sequences from counterparty, determine if an ack on the counterparty chain has been received on the executing chain. - /// Returns the list of acknowledgement sequences that have not yet been received. - pub async fn next_sequence_receive( - &self, - port_id: impl Into, - channel_id: impl Into, - ) -> Result { - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let next_receive: ibc_channel::QueryNextSequenceReceiveResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryNextSequenceReceiveRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryNextSequenceReceiveRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }; - let response = client - .next_sequence_receive(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 471u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(next_receive.next_sequence_receive) - } - } - } - mod node { - use std::{cmp::min, time::Duration}; - use crate::{cosmos_modules, error::DaemonError, tx_resp::CosmTxResponse}; - use cosmrs::{ - proto::cosmos::{ - base::query::v1beta1::PageRequest, tx::v1beta1::SimulateResponse, - }, - tendermint::{Block, Time}, - }; - use tonic::transport::Channel; - use super::DaemonQuerier; - const MAX_TX_QUERY_RETRIES: usize = 50; - /// Querier for the Tendermint node. - /// Supports queries for block and tx information - pub struct Node { - channel: Channel, - } - impl DaemonQuerier for Node { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Node { - /// Returns node info - pub async fn info( - &self, - ) -> Result { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_node_info(cosmos_modules::tendermint::GetNodeInfoRequest { - }) - .await? - .into_inner(); - Ok(resp) - } - /// Queries node syncing - pub async fn syncing(&self) -> Result { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_syncing(cosmos_modules::tendermint::GetSyncingRequest { - }) - .await? - .into_inner(); - Ok(resp.syncing) - } - /// Returns latests block information - pub async fn latest_block(&self) -> Result { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_latest_block(cosmos_modules::tendermint::GetLatestBlockRequest { - }) - .await? - .into_inner(); - Ok(Block::try_from(resp.block.unwrap())?) - } - /// Returns block information fetched by height - pub async fn block_by_height( - &self, - height: u64, - ) -> Result { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_block_by_height(cosmos_modules::tendermint::GetBlockByHeightRequest { - height: height as i64, - }) - .await? - .into_inner(); - Ok(Block::try_from(resp.block.unwrap())?) - } - /// Return the average block time for the last 50 blocks or since inception - /// This is used to estimate the time when a tx will be included in a block - pub async fn average_block_speed( - &self, - multiplier: Option, - ) -> Result { - let mut latest_block = self.latest_block().await?; - let latest_block_time = latest_block.header.time; - let mut latest_block_height = latest_block.header.height.value(); - while latest_block_height <= 1 { - tokio::time::sleep(Duration::from_secs(1)).await; - latest_block = self.latest_block().await?; - latest_block_height = latest_block.header.height.value(); - } - let avg_period = min(latest_block_height - 1, 50); - let block_avg_period_ago = self - .block_by_height(latest_block_height - avg_period) - .await?; - let block_avg_period_ago_time = block_avg_period_ago.header.time; - let average_block_time = latest_block_time - .duration_since(block_avg_period_ago_time)?; - let average_block_time = average_block_time.as_secs() / avg_period; - let average_block_time = match multiplier { - Some(multiplier) => (average_block_time as f32 * multiplier) as u64, - None => average_block_time, - }; - Ok(std::cmp::max(average_block_time, 1)) - } - /// Returns latests validator set - pub async fn latest_validator_set( - &self, - pagination: Option, - ) -> Result< - cosmos_modules::tendermint::GetLatestValidatorSetResponse, - DaemonError, - > { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_latest_validator_set(cosmos_modules::tendermint::GetLatestValidatorSetRequest { - pagination, - }) - .await? - .into_inner(); - Ok(resp) - } - /// Returns latests validator set fetched by height - pub async fn validator_set_by_height( - &self, - height: i64, - pagination: Option, - ) -> Result< - cosmos_modules::tendermint::GetValidatorSetByHeightResponse, - DaemonError, - > { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_validator_set_by_height(cosmos_modules::tendermint::GetValidatorSetByHeightRequest { - height, - pagination, - }) - .await? - .into_inner(); - Ok(resp) - } - /// Returns current block height - pub async fn block_height(&self) -> Result { - let block = self.latest_block().await?; - Ok(block.header.height.value()) - } - /// Returns the block timestamp (since unix epoch) in nanos - pub async fn block_time(&self) -> Result { - let block = self.latest_block().await?; - Ok(block.header.time.duration_since(Time::unix_epoch())?.as_nanos()) - } - /// Simulate TX - pub async fn simulate_tx( - &self, - tx_bytes: Vec, - ) -> Result { - let mut client = cosmos_modules::tx::service_client::ServiceClient::new( - self.channel.clone(), - ); - #[allow(deprecated)] - let resp: SimulateResponse = client - .simulate(cosmos_modules::tx::SimulateRequest { - tx: None, - tx_bytes, - }) - .await? - .into_inner(); - let gas_used = resp.gas_info.unwrap().gas_used; - Ok(gas_used) - } - /// Returns all the block info - pub async fn block_info( - &self, - ) -> Result { - let block = self.latest_block().await?; - let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; - let time = cosmwasm_std::Timestamp::from_nanos( - since_epoch.as_nanos() as u64, - ); - Ok(cosmwasm_std::BlockInfo { - height: block.header.height.value(), - time, - chain_id: block.header.chain_id.to_string(), - }) - } - /// Find TX by hash - pub async fn find_tx( - &self, - hash: String, - ) -> Result { - self.find_tx_with_retries(hash, MAX_TX_QUERY_RETRIES).await - } - /// Find TX by hash with a given amount of retries - pub async fn find_tx_with_retries( - &self, - hash: String, - retries: usize, - ) -> Result { - let mut client = cosmos_modules::tx::service_client::ServiceClient::new( - self.channel.clone(), - ); - let request = cosmos_modules::tx::GetTxRequest { - hash: hash.clone(), - }; - let mut block_speed = self.average_block_speed(Some(0.7)).await?; - for _ in 0..retries { - match client.get_tx(request.clone()).await { - Ok(tx) => { - let resp = tx.into_inner().tx_response.unwrap(); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("TX found: {0:?}", resp), - lvl, - &( - "cw_orch_daemon::queriers::node", - "cw_orch_daemon::queriers::node", - "cw-orch-daemon/src/queriers/node.rs", - ), - 220u32, - ::log::__private_api::Option::None, - ); - } - }; - return Ok(resp.into()); - } - Err(err) => { - block_speed = (block_speed as f64 * 1.6) as u64; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("TX not found with error: {0:?}", err), - lvl, - &( - "cw_orch_daemon::queriers::node", - "cw_orch_daemon::queriers::node", - "cw-orch-daemon/src/queriers/node.rs", - ), - 226u32, - ::log::__private_api::Option::None, - ); - } - }; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("Waiting {0} seconds", block_speed), - lvl, - &( - "cw_orch_daemon::queriers::node", - "cw_orch_daemon::queriers::node", - "cw-orch-daemon/src/queriers/node.rs", - ), - 227u32, - ::log::__private_api::Option::None, - ); - } - }; - tokio::time::sleep(Duration::from_secs(block_speed)).await; - } - } - } - Err(DaemonError::TXNotFound(hash, retries)) - } - } - } - mod staking { - use crate::{cosmos_modules, error::DaemonError}; - use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; - use tonic::transport::Channel; - use super::DaemonQuerier; - /// Querier for the Cosmos Staking module - pub struct Staking { - channel: Channel, - } - impl DaemonQuerier for Staking { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Staking { - /// Queries validator info for given validator address - pub async fn validator( - &self, - validator_addr: impl Into, - ) -> Result { - let validator: cosmos_modules::staking::QueryValidatorResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryValidatorRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryValidatorRequest { - validator_addr: validator_addr.into(), - }; - let response = client.validator(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 24u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(validator.validator.unwrap()) - } - /// Queries all validators that match the given status - /// - /// see [StakingBondStatus] for available statuses - pub async fn validators( - &self, - status: StakingBondStatus, - ) -> Result, DaemonError> { - let validators: cosmos_modules::staking::QueryValidatorsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryValidatorsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryValidatorsRequest { - status: status.to_string(), - pagination: None, - }; - let response = client - .validators(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 42u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(validators.validators) - } - /// Query validator delegations info for given validator - /// - /// see [PageRequest] for pagination - pub async fn delegations( - &self, - validator_addr: impl Into, - pagination: Option, - ) -> Result, DaemonError> { - let validator_delegations: cosmos_modules::staking::QueryValidatorDelegationsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryValidatorDelegationsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryValidatorDelegationsRequest { - validator_addr: validator_addr.into(), - pagination: pagination, - }; - let response = client - .validator_delegations(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 62u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(validator_delegations.delegation_responses) - } - /// Query validator unbonding delegations of a validator - pub async fn unbonding_delegations( - &self, - validator_addr: impl Into, - ) -> Result, DaemonError> { - let validator_unbonding_delegations: cosmos_modules::staking::QueryValidatorUnbondingDelegationsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, - QueryValidatorUnbondingDelegationsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryValidatorUnbondingDelegationsRequest { - validator_addr: validator_addr.into(), - pagination: None, - }; - let response = client - .validator_unbonding_delegations(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 79u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(validator_unbonding_delegations.unbonding_responses) - } - /// Query delegation info for given validator for a delegator - pub async fn delegation( - &self, - validator_addr: impl Into, - delegator_addr: impl Into, - ) -> Result { - let delegation: cosmos_modules::staking::QueryDelegationResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryDelegationRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDelegationRequest { - validator_addr: validator_addr.into(), - delegator_addr: delegator_addr.into(), - }; - let response = client - .delegation(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 97u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(delegation.delegation_response.unwrap()) - } - /// Query unbonding delegation info for given validator delegator - pub async fn unbonding_delegation( - &self, - validator_addr: impl Into, - delegator_addr: impl Into, - ) -> Result { - let unbonding_delegation: cosmos_modules::staking::QueryUnbondingDelegationResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryUnbondingDelegationRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryUnbondingDelegationRequest { - validator_addr: validator_addr.into(), - delegator_addr: delegator_addr.into(), - }; - let response = client - .unbonding_delegation(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 115u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(unbonding_delegation.unbond.unwrap()) - } - /// Query all delegator delegations of a given delegator address - /// - /// see [PageRequest] for pagination - pub async fn delegator_delegations( - &self, - delegator_addr: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::staking::QueryDelegatorDelegationsResponse, - DaemonError, - > { - let delegator_delegations: cosmos_modules::staking::QueryDelegatorDelegationsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryDelegatorDelegationsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDelegatorDelegationsRequest { - delegator_addr: delegator_addr.into(), - pagination: pagination, - }; - let response = client - .delegator_delegations(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 135u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(delegator_delegations) - } - /// Queries all unbonding delegations of a given delegator address. - /// - /// see [PageRequest] for pagination - pub async fn delegator_unbonding_delegations( - &self, - delegator_addr: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::staking::QueryDelegatorUnbondingDelegationsResponse, - DaemonError, - > { - let delegator_unbonding_delegations: cosmos_modules::staking::QueryDelegatorUnbondingDelegationsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, - QueryDelegatorUnbondingDelegationsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDelegatorUnbondingDelegationsRequest { - delegator_addr: delegator_addr.into(), - pagination: pagination, - }; - let response = client - .delegator_unbonding_delegations(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 156u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(delegator_unbonding_delegations) - } - /// Query redelegations of a given address - /// - /// see [PageRequest] for pagination - pub async fn redelegations( - &self, - delegator_addr: impl Into, - src_validator_addr: impl Into, - dst_validator_addr: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::staking::QueryRedelegationsResponse, - DaemonError, - > { - let redelegations: cosmos_modules::staking::QueryRedelegationsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryRedelegationsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryRedelegationsRequest { - delegator_addr: delegator_addr.into(), - src_validator_addr: src_validator_addr.into(), - dst_validator_addr: dst_validator_addr.into(), - pagination: pagination, - }; - let response = client - .redelegations(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 178u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(redelegations) - } - /// Query delegator validators info for given delegator address. - pub async fn delegator_validator( - &self, - validator_addr: impl Into, - delegator_addr: impl Into, - ) -> Result< - cosmos_modules::staking::QueryDelegatorValidatorResponse, - DaemonError, - > { - let delegator_validator: cosmos_modules::staking::QueryDelegatorValidatorResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryDelegatorValidatorRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDelegatorValidatorRequest { - validator_addr: validator_addr.into(), - delegator_addr: delegator_addr.into(), - }; - let response = client - .delegator_validator(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 198u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(delegator_validator) - } - /// Query delegator validators info for given delegator address - /// - /// see [PageRequest] for pagination - pub async fn delegator_validators( - &self, - delegator_addr: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::staking::QueryDelegatorValidatorsResponse, - DaemonError, - > { - let delegator_validators: cosmos_modules::staking::QueryDelegatorValidatorsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryDelegatorValidatorsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDelegatorValidatorsRequest { - delegator_addr: delegator_addr.into(), - pagination: pagination, - }; - let response = client - .delegator_validators(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 218u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(delegator_validators) - } - /// Query historical info info for given height - pub async fn historical_info( - &self, - height: i64, - ) -> Result< - cosmos_modules::staking::QueryHistoricalInfoResponse, - DaemonError, - > { - let historical_info: cosmos_modules::staking::QueryHistoricalInfoResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryHistoricalInfoRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryHistoricalInfoRequest { - height: height, - }; - let response = client - .historical_info(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 236u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(historical_info) - } - /// Query the pool info - pub async fn pool( - &self, - ) -> Result { - let pool: cosmos_modules::staking::QueryPoolResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryPoolRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPoolRequest {}; - let response = client.pool(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 248u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(pool) - } - /// Query staking parameters - pub async fn params( - &self, - ) -> Result { - let params: cosmos_modules::staking::QueryParamsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryParamsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryParamsRequest {}; - let response = client.params(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 257u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(params) - } - } - /// Staking bond statuses - pub enum StakingBondStatus { - /// UNSPECIFIED defines an invalid validator status. - Unspecified = 0, - /// UNBONDED defines a validator that is not bonded. - Unbonded = 1, - /// UNBONDING defines a validator that is unbonding. - Unbonding = 2, - /// BONDED defines a validator that is bonded. - Bonded = 3, - } - impl ToString for StakingBondStatus { - /// Convert to string - fn to_string(&self) -> String { - match self { - StakingBondStatus::Unspecified => { - "BOND_STATUS_UNSPECIFIED".to_string() - } - StakingBondStatus::Unbonded => "BOND_STATUS_UNBONDED".to_string(), - StakingBondStatus::Unbonding => "BOND_STATUS_UNBONDING".to_string(), - StakingBondStatus::Bonded => "BOND_STATUS_BONDED".to_string(), - } - } - } - } - pub use bank::Bank; - pub use cosmwasm::CosmWasm; - pub use feegrant::Feegrant; - pub use ibc::Ibc; - pub use node::Node; - pub use gov::*; - pub use staking::*; - use tonic::transport::Channel; - /// Constructor for a querier over a given channel - pub trait DaemonQuerier { - /// Construct an new querier over a given channel - fn new(channel: Channel) -> Self; - } -} -mod traits { - use cw_orch_core::{ - contract::interface_traits::{CwOrchMigrate, CwOrchUpload}, - environment::TxResponse, - }; - use crate::{queriers::CosmWasm, Daemon, DaemonError}; - /// Helper methods for conditional uploading of a contract. - pub trait ConditionalUpload: CwOrchUpload { - /// Only upload the contract if it is not uploaded yet (checksum does not match) - fn upload_if_needed(&self) -> Result>, DaemonError> { - if self.latest_is_uploaded()? { - Ok(None) - } else { - Some(self.upload()).transpose().map_err(Into::into) - } - } - /// Returns whether the checksum of the WASM file matches the checksum of the latest uploaded code for this contract. - fn latest_is_uploaded(&self) -> Result { - let Some(latest_uploaded_code_id) = self.code_id().ok() else { - return Ok(false); - }; - let chain = self.get_chain(); - let on_chain_hash = chain - .rt_handle - .block_on( - chain - .query_client::() - .code_id_hash(latest_uploaded_code_id), - )?; - let local_hash = self.wasm().checksum()?; - Ok(local_hash == on_chain_hash) - } - /// Returns whether the contract is running the latest uploaded code for it - fn is_running_latest(&self) -> Result { - let Some(latest_uploaded_code_id) = self.code_id().ok() else { - return Ok(false); - }; - let chain = self.get_chain(); - let info = chain - .rt_handle - .block_on( - chain.query_client::().contract_info(self.address()?), - )?; - Ok(latest_uploaded_code_id == info.code_id) - } - } - impl ConditionalUpload for T - where - T: CwOrchUpload, - {} - /// Helper methods for conditional migration of a contract. - pub trait ConditionalMigrate: CwOrchMigrate + ConditionalUpload { - /// Only migrate the contract if it is not on the latest code-id yet - fn migrate_if_needed( - &self, - migrate_msg: &Self::MigrateMsg, - ) -> Result>, DaemonError> { - if self.is_running_latest()? { - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "{0} is already running the latest code", - self.id(), - ), - lvl, - &( - "cw_orch_daemon::traits", - "cw_orch_daemon::traits", - "cw-orch-daemon/src/traits.rs", - ), - 61u32, - ::log::__private_api::Option::None, - ); - } - }; - Ok(None) - } else { - Some(self.migrate(migrate_msg, self.code_id()?)) - .transpose() - .map_err(Into::into) - } - } - /// Uploads the contract if the local contract hash is different from the latest on-chain code hash. - /// Proceeds to migrates the contract if the contract is not running the latest code. - fn upload_and_migrate_if_needed( - &self, - migrate_msg: &Self::MigrateMsg, - ) -> Result>>, DaemonError> { - let mut txs = Vec::with_capacity(2); - if let Some(tx) = self.upload_if_needed()? { - txs.push(tx); - } - if let Some(tx) = self.migrate_if_needed(migrate_msg)? { - txs.push(tx); - } - if txs.is_empty() { Ok(None) } else { Ok(Some(txs)) } - } - } - impl ConditionalMigrate for T - where - T: CwOrchMigrate + CwOrchUpload, - {} -} -pub mod tx_builder { - use cosmrs::tx::{ModeInfo, SignMode}; - use cosmrs::{ - proto::cosmos::auth::v1beta1::BaseAccount, tendermint::chain::Id, - tx::{self, Body, Fee, Msg, Raw, SequenceNumber, SignDoc, SignerInfo}, - Any, Coin, - }; - use secp256k1::All; - use super::{sender::Sender, DaemonError}; - const GAS_BUFFER: f64 = 1.3; - const BUFFER_THRESHOLD: u64 = 200_000; - const SMALL_GAS_BUFFER: f64 = 1.4; - /// Struct used to build a raw transaction and broadcast it with a sender. - pub struct TxBuilder { - pub(crate) body: Body, - pub(crate) fee_amount: Option, - pub(crate) gas_limit: Option, - pub(crate) sequence: Option, - } - #[automatically_derived] - impl ::core::clone::Clone for TxBuilder { - #[inline] - fn clone(&self) -> TxBuilder { - TxBuilder { - body: ::core::clone::Clone::clone(&self.body), - fee_amount: ::core::clone::Clone::clone(&self.fee_amount), - gas_limit: ::core::clone::Clone::clone(&self.gas_limit), - sequence: ::core::clone::Clone::clone(&self.sequence), - } - } - } - #[automatically_derived] - impl ::core::fmt::Debug for TxBuilder { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field4_finish( - f, - "TxBuilder", - "body", - &self.body, - "fee_amount", - &self.fee_amount, - "gas_limit", - &self.gas_limit, - "sequence", - &&self.sequence, - ) - } - } - impl TxBuilder { - /// Create a new TxBuilder with a given body. - pub fn new(body: Body) -> Self { - Self { - body, - fee_amount: None, - gas_limit: None, - sequence: None, - } - } - /// Set a fixed fee amount for the tx - pub fn fee_amount(&mut self, fee_amount: u128) -> &mut Self { - self.fee_amount = Some(fee_amount); - self - } - /// Set a gas limit for the tx - pub fn gas_limit(&mut self, gas_limit: u64) -> &mut Self { - self.gas_limit = Some(gas_limit); - self - } - /// Set a sequence number for the tx - pub fn sequence(&mut self, sequence: u64) -> &mut Self { - self.sequence = Some(sequence); - self - } - /// Builds the body of the tx with a given memo and timeout. - pub fn build_body( - msgs: Vec, - memo: Option<&str>, - timeout: u64, - ) -> tx::Body { - let msgs = msgs - .into_iter() - .map(Msg::into_any) - .collect::, _>>() - .unwrap(); - tx::Body::new(msgs, memo.unwrap_or_default(), timeout as u32) - } - pub(crate) fn build_fee( - amount: impl Into, - denom: &str, - gas_limit: u64, - ) -> Fee { - let fee = Coin::new(amount.into(), denom).unwrap(); - Fee::from_amount_and_gas(fee, gas_limit) - } - /// Builds the raw tx with a given body and fee and signs it. - /// Sets the TxBuilder's gas limit to its simulated amount for later use. - pub async fn build(&mut self, wallet: &Sender) -> Result { - let BaseAccount { account_number, sequence, .. } = wallet - .base_account() - .await?; - let sequence = self.sequence.unwrap_or(sequence); - let (tx_fee, gas_limit) = if let (Some(fee), Some(gas_limit)) - = (self.fee_amount, self.gas_limit) { - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Using pre-defined fee and gas limits: {0}, {1}", - fee, - gas_limit, - ), - lvl, - &( - "cw_orch_daemon::tx_builder", - "cw_orch_daemon::tx_builder", - "cw-orch-daemon/src/tx_builder.rs", - ), - 91u32, - ::log::__private_api::Option::None, - ); - } - }; - (fee, gas_limit) - } else { - let sim_gas_used = wallet - .calculate_gas(&self.body, sequence, account_number) - .await?; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Simulated gas needed {0:?}", sim_gas_used), - lvl, - &( - "cw_orch_daemon::tx_builder", - "cw_orch_daemon::tx_builder", - "cw-orch-daemon/src/tx_builder.rs", - ), - 101u32, - ::log::__private_api::Option::None, - ); - } - }; - let gas_expected = if sim_gas_used < BUFFER_THRESHOLD { - sim_gas_used as f64 * SMALL_GAS_BUFFER - } else { - sim_gas_used as f64 * GAS_BUFFER - }; - let fee_amount = gas_expected - * (wallet - .daemon_state - .chain_data - .fees - .fee_tokens[0] - .fixed_min_gas_price + 0.00001); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Calculated fee needed: {0:?}", fee_amount), - lvl, - &( - "cw_orch_daemon::tx_builder", - "cw_orch_daemon::tx_builder", - "cw-orch-daemon/src/tx_builder.rs", - ), - 111u32, - ::log::__private_api::Option::None, - ); - } - }; - self.gas_limit = Some(gas_expected as u64); - (fee_amount as u128, gas_expected as u64) - }; - let fee = Self::build_fee( - tx_fee, - &wallet.daemon_state.chain_data.fees.fee_tokens[0].denom, - gas_limit, - ); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "submitting tx: \n fee: {0:?}\naccount_nr: {1:?}\nsequence: {2:?}", - fee, - account_number, - sequence, - ), - lvl, - &( - "cw_orch_daemon::tx_builder", - "cw_orch_daemon::tx_builder", - "cw-orch-daemon/src/tx_builder.rs", - ), - 125u32, - ::log::__private_api::Option::None, - ); - } - }; - let auth_info = SignerInfo { - public_key: wallet.private_key.get_signer_public_key(&wallet.secp), - mode_info: ModeInfo::single(SignMode::Direct), - sequence, - } - .auth_info(fee); - let sign_doc = SignDoc::new( - &self.body, - &auth_info, - &Id::try_from(wallet.daemon_state.chain_data.chain_id.to_string())?, - account_number, - )?; - wallet.sign(sign_doc).map_err(Into::into) - } - } -} -pub use self::{ - builder::*, channel::*, core::*, error::*, state::*, sync::*, traits::*, tx_resp::*, -}; -pub use cw_orch_networks::chain_info::*; -pub use cw_orch_networks::networks; -pub use sender::Wallet; -pub use tx_builder::TxBuilder; -pub(crate) mod cosmos_modules { - pub use cosmrs::proto::{ - cosmos::{ - auth::v1beta1 as auth, authz::v1beta1 as authz, bank::v1beta1 as bank, - base::{ - abci::v1beta1 as abci, tendermint::v1beta1 as tendermint, v1beta1 as base, - }, - crisis::v1beta1 as crisis, distribution::v1beta1 as distribution, - evidence::v1beta1 as evidence, feegrant::v1beta1 as feegrant, - gov::v1beta1 as gov, mint::v1beta1 as mint, params::v1beta1 as params, - slashing::v1beta1 as slashing, staking::v1beta1 as staking, - tx::v1beta1 as tx, vesting::v1beta1 as vesting, - }, - cosmwasm::wasm::v1 as cosmwasm, - ibc::{ - applications::transfer::v1 as ibc_transfer, - core::{ - channel::v1 as ibc_channel, client::v1 as ibc_client, - connection::v1 as ibc_connection, - }, - }, - tendermint::abci as tendermint_abci, - }; -} -/// Re-export trait and data required to fetch daemon data from chain-registry -pub use ibc_chain_registry::{ - chain::ChainData as ChainRegistryData, fetchable::Fetchable, -}; diff --git a/cw-orch-daemon/test b/cw-orch-daemon/test deleted file mode 100644 index b08bac50a..000000000 --- a/cw-orch-daemon/test +++ /dev/null @@ -1,25355 +0,0 @@ -#![feature(prelude_import)] -//! `Daemon` and `DaemonAsync` execution environments. -//! -//! The `Daemon` type is a synchronous wrapper around the `DaemonAsync` type and can be used as a contract execution environment. -#[prelude_import] -use std::prelude::rust_2021::*; -#[macro_use] -extern crate std; -pub mod builder { - use crate::{DaemonAsync, DaemonBuilder}; - use std::rc::Rc; - use ibc_chain_registry::chain::ChainData; - use super::{error::DaemonError, sender::Sender, state::DaemonState}; - /// The default deployment id if none is provided - pub const DEFAULT_DEPLOYMENT: &str = "default"; - /// Create [`DaemonAsync`] through [`DaemonAsyncBuilder`] - /// ## Example - /// ```no_run - /// # tokio_test::block_on(async { - /// use cw_orch_daemon::{DaemonAsyncBuilder, networks}; - /// let daemon = DaemonAsyncBuilder::default() - /// .chain(networks::LOCAL_JUNO) - /// .deployment_id("v0.1.0") - /// .build() - /// .await.unwrap(); - /// # }) - /// ``` - pub struct DaemonAsyncBuilder { - pub(crate) chain: Option, - pub(crate) deployment_id: Option, - /// Wallet mnemonic - pub(crate) mnemonic: Option, - } - #[automatically_derived] - impl ::core::clone::Clone for DaemonAsyncBuilder { - #[inline] - fn clone(&self) -> DaemonAsyncBuilder { - DaemonAsyncBuilder { - chain: ::core::clone::Clone::clone(&self.chain), - deployment_id: ::core::clone::Clone::clone(&self.deployment_id), - mnemonic: ::core::clone::Clone::clone(&self.mnemonic), - } - } - } - #[automatically_derived] - impl ::core::default::Default for DaemonAsyncBuilder { - #[inline] - fn default() -> DaemonAsyncBuilder { - DaemonAsyncBuilder { - chain: ::core::default::Default::default(), - deployment_id: ::core::default::Default::default(), - mnemonic: ::core::default::Default::default(), - } - } - } - impl DaemonAsyncBuilder { - /// Set the chain the daemon will connect to - pub fn chain(&mut self, chain: impl Into) -> &mut Self { - self.chain = Some(chain.into()); - self - } - /// Set the deployment id to use for the daemon interactions - /// Defaults to `default` - pub fn deployment_id(&mut self, deployment_id: impl Into) -> &mut Self { - self.deployment_id = Some(deployment_id.into()); - self - } - /// Set the mnemonic to use with this chain. - /// Defaults to env variable depending on the environment. - /// - /// Variables: LOCAL_MNEMONIC, TEST_MNEMONIC and MAIN_MNEMONIC - pub fn mnemonic(&mut self, mnemonic: impl ToString) -> &mut Self { - self.mnemonic = Some(mnemonic.to_string()); - self - } - /// Build a daemon - pub async fn build(&self) -> Result { - let chain = self - .chain - .clone() - .ok_or(DaemonError::BuilderMissing("chain information".into()))?; - let deployment_id = self - .deployment_id - .clone() - .unwrap_or(DEFAULT_DEPLOYMENT.to_string()); - let state = Rc::new(DaemonState::new(chain, deployment_id).await?); - let sender = if let Some(mnemonic) = &self.mnemonic { - Sender::from_mnemonic(&state, mnemonic)? - } else { - Sender::new(&state)? - }; - let daemon = DaemonAsync { - state, - sender: Rc::new(sender), - }; - Ok(daemon) - } - } - impl From for DaemonAsyncBuilder { - fn from(value: DaemonBuilder) -> Self { - DaemonAsyncBuilder { - chain: value.chain, - deployment_id: value.deployment_id, - mnemonic: value.mnemonic, - } - } - } -} -pub mod channel { - use cosmrs::proto::cosmos::base::tendermint::v1beta1::{ - service_client::ServiceClient, GetNodeInfoRequest, - }; - use ibc_chain_registry::chain::Grpc; - use ibc_relayer_types::core::ics24_host::identifier::ChainId; - use tonic::transport::{Channel, ClientTlsConfig}; - use super::error::DaemonError; - /// A helper for constructing a gRPC channel - pub struct GrpcChannel {} - impl GrpcChannel { - /// Connect to any of the provided gRPC endpoints - pub async fn connect( - grpc: &[Grpc], - chain_id: &ChainId, - ) -> Result { - let mut successful_connections = ::alloc::vec::Vec::new(); - for Grpc { address, .. } in grpc.iter() { - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Trying to connect to endpoint: {0}", address), - lvl, - &( - "cw_orch_daemon::channel", - "cw_orch_daemon::channel", - "cw-orch-daemon/src/channel.rs", - ), - 19u32, - ::log::__private_api::Option::None, - ); - } - }; - let endpoint = Channel::builder(address.clone().try_into().unwrap()); - let maybe_client = ServiceClient::connect(endpoint.clone()).await; - let mut client = if maybe_client.is_ok() { - maybe_client? - } else { - { - let lvl = ::log::Level::Warn; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Cannot connect to gRPC endpoint: {0}, {1:?}", - address, - maybe_client.unwrap_err(), - ), - lvl, - &( - "cw_orch_daemon::channel", - "cw_orch_daemon::channel", - "cw-orch-daemon/src/channel.rs", - ), - 31u32, - ::log::__private_api::Option::None, - ); - } - }; - if !(address.contains("https") || address.contains("443")) { - continue; - } - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Attempting to connect with TLS"), - lvl, - &( - "cw_orch_daemon::channel", - "cw_orch_daemon::channel", - "cw-orch-daemon/src/channel.rs", - ), - 43u32, - ::log::__private_api::Option::None, - ); - } - }; - let endpoint = endpoint.clone().tls_config(ClientTlsConfig::new())?; - let maybe_client = ServiceClient::connect(endpoint.clone()).await; - if maybe_client.is_err() { - { - let lvl = ::log::Level::Warn; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!( - "Cannot connect to gRPC endpoint: {0}, {1:?}", - address, - maybe_client.unwrap_err(), - ), - lvl, - &( - "cw_orch_daemon::channel", - "cw_orch_daemon::channel", - "cw-orch-daemon/src/channel.rs", - ), - 51u32, - ::log::__private_api::Option::None, - ); - } - }; - continue; - } - maybe_client? - }; - let node_info = client - .get_node_info(GetNodeInfoRequest {}) - .await? - .into_inner(); - if ChainId::is_epoch_format( - &node_info.default_node_info.as_ref().unwrap().network, - ) { - if node_info.default_node_info.as_ref().unwrap().network - != chain_id.as_str() - { - { - let lvl = ::log::Level::Error; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!( - "Network mismatch: connection:{0} != config:{1}", - node_info.default_node_info.as_ref().unwrap().network, - chain_id.as_str(), - ), - lvl, - &( - "cw_orch_daemon::channel", - "cw_orch_daemon::channel", - "cw-orch-daemon/src/channel.rs", - ), - 72u32, - ::log::__private_api::Option::None, - ); - } - }; - continue; - } - } - successful_connections.push(endpoint.connect().await?) - } - if successful_connections.is_empty() { - return Err(DaemonError::CannotConnectGRPC); - } - Ok(successful_connections.pop().unwrap()) - } - } -} -pub mod core { - use crate::{queriers::CosmWasm, DaemonState}; - use super::{ - builder::DaemonAsyncBuilder, cosmos_modules, error::DaemonError, - queriers::{DaemonQuerier, Node}, - sender::Wallet, tx_resp::CosmTxResponse, - }; - use cosmrs::{ - cosmwasm::{MsgExecuteContract, MsgInstantiateContract, MsgMigrateContract}, - tendermint::Time, AccountId, Denom, - }; - use cosmwasm_std::{Addr, Coin}; - use cw_orch_core::{ - contract::interface_traits::Uploadable, environment::{ChainState, IndexResponse}, - }; - use serde::{de::DeserializeOwned, Serialize}; - use serde_json::from_str; - use std::{ - fmt::Debug, rc::Rc, str::{from_utf8, FromStr}, - time::Duration, - }; - use tonic::transport::Channel; - /** - Represents a blockchain node. - It's constructed using [`DaemonAsyncBuilder`]. - - ## Usage - ```rust,no_run - # tokio_test::block_on(async { - use cw_orch_daemon::{DaemonAsync, networks}; - - let daemon: DaemonAsync = DaemonAsync::builder() - .chain(networks::JUNO_1) - .build() - .await.unwrap(); - # }) - ``` - ## Environment Execution - - The DaemonAsync implements async methods of [`TxHandler`](cw_orch_core::environment::TxHandler) which allows you to perform transactions on the chain. - - ## Querying - - Different Cosmos SDK modules can be queried through the daemon by calling the [`DaemonAsync::query_client`] method with a specific querier. - See [Querier](crate::queriers) for examples. -*/ - pub struct DaemonAsync { - /// Sender to send transactions to the chain - pub sender: Wallet, - /// State of the daemon - pub state: Rc, - } - #[automatically_derived] - impl ::core::clone::Clone for DaemonAsync { - #[inline] - fn clone(&self) -> DaemonAsync { - DaemonAsync { - sender: ::core::clone::Clone::clone(&self.sender), - state: ::core::clone::Clone::clone(&self.state), - } - } - } - impl DaemonAsync { - /// Get the daemon builder - pub fn builder() -> DaemonAsyncBuilder { - DaemonAsyncBuilder::default() - } - /// Perform a query with a given query client. - /// See [Querier](crate::queriers) for examples. - pub fn query_client(&self) -> Querier { - Querier::new(self.sender.channel()) - } - /// Get the channel configured for this DaemonAsync. - pub fn channel(&self) -> Channel { - self.state.grpc_channel.clone() - } - } - impl ChainState for DaemonAsync { - type Out = Rc; - fn state(&self) -> Self::Out { - self.state.clone() - } - } - impl DaemonAsync { - /// Get the sender address - pub fn sender(&self) -> Addr { - self.sender.address().unwrap() - } - /// Execute a message on a contract. - pub async fn execute( - &self, - exec_msg: &E, - coins: &[cosmwasm_std::Coin], - contract_address: &Addr, - ) -> Result { - let exec_msg: MsgExecuteContract = MsgExecuteContract { - sender: self.sender.pub_addr()?, - contract: AccountId::from_str(contract_address.as_str())?, - msg: serde_json::to_vec(&exec_msg)?, - funds: parse_cw_coins(coins)?, - }; - let result = self - .sender - .commit_tx( - <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([exec_msg])), - None, - ) - .await?; - Ok(result) - } - /// Instantiate a contract. - pub async fn instantiate( - &self, - code_id: u64, - init_msg: &I, - label: Option<&str>, - admin: Option<&Addr>, - coins: &[Coin], - ) -> Result { - let sender = &self.sender; - let init_msg = MsgInstantiateContract { - code_id, - label: Some(label.unwrap_or("instantiate_contract").to_string()), - admin: admin.map(|a| FromStr::from_str(a.as_str()).unwrap()), - sender: sender.pub_addr()?, - msg: serde_json::to_vec(&init_msg)?, - funds: parse_cw_coins(coins)?, - }; - let result = sender - .commit_tx( - <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([init_msg])), - None, - ) - .await?; - Ok(result) - } - /// Query a contract. - pub async fn query( - &self, - query_msg: &Q, - contract_address: &Addr, - ) -> Result { - let sender = &self.sender; - let mut client = cosmos_modules::cosmwasm::query_client::QueryClient::new( - sender.channel(), - ); - let resp = client - .smart_contract_state(cosmos_modules::cosmwasm::QuerySmartContractStateRequest { - address: contract_address.to_string(), - query_data: serde_json::to_vec(&query_msg)?, - }) - .await?; - Ok(from_str(from_utf8(&resp.into_inner().data).unwrap())?) - } - /// Migration a contract. - pub async fn migrate( - &self, - migrate_msg: &M, - new_code_id: u64, - contract_address: &Addr, - ) -> Result { - let exec_msg: MsgMigrateContract = MsgMigrateContract { - sender: self.sender.pub_addr()?, - contract: AccountId::from_str(contract_address.as_str())?, - msg: serde_json::to_vec(&migrate_msg)?, - code_id: new_code_id, - }; - let result = self - .sender - .commit_tx( - <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([exec_msg])), - None, - ) - .await?; - Ok(result) - } - /// Wait for a given amount of blocks. - pub async fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { - let mut last_height = self.query_client::().block_height().await?; - let end_height = last_height + amount; - let average_block_speed = self - .query_client::() - .average_block_speed(Some(0.9)) - .await?; - let wait_time = average_block_speed * amount; - tokio::time::sleep(Duration::from_secs(wait_time)).await; - while last_height < end_height { - tokio::time::sleep(Duration::from_secs(average_block_speed)).await; - last_height = self.query_client::().block_height().await?; - } - Ok(()) - } - /// Wait for a given amount of seconds. - pub async fn wait_seconds(&self, secs: u64) -> Result<(), DaemonError> { - tokio::time::sleep(Duration::from_secs(secs)).await; - Ok(()) - } - /// Wait for the next block. - pub async fn next_block(&self) -> Result<(), DaemonError> { - self.wait_blocks(1).await - } - /// Get the current block info. - pub async fn block_info(&self) -> Result { - let block = self.query_client::().latest_block().await?; - let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; - let time = cosmwasm_std::Timestamp::from_nanos( - since_epoch.as_nanos() as u64, - ); - Ok(cosmwasm_std::BlockInfo { - height: block.header.height.value(), - time, - chain_id: block.header.chain_id.to_string(), - }) - } - /// Upload a contract to the chain. - pub async fn upload( - &self, - uploadable: &impl Uploadable, - ) -> Result { - let sender = &self.sender; - let wasm_path = uploadable.wasm(); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Uploading file at {0:?}", wasm_path), - lvl, - &( - "cw_orch_daemon::core", - "cw_orch_daemon::core", - "cw-orch-daemon/src/core.rs", - ), - 233u32, - ::log::__private_api::Option::None, - ); - } - }; - let file_contents = std::fs::read(wasm_path.path())?; - let store_msg = cosmrs::cosmwasm::MsgStoreCode { - sender: sender.pub_addr()?, - wasm_byte_code: file_contents, - instantiate_permission: None, - }; - let result = sender - .commit_tx( - <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([store_msg])), - None, - ) - .await?; - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Uploaded: {0:?}", result.txhash), - lvl, - &( - "cw_orch_daemon::core", - "cw_orch_daemon::core", - "cw-orch-daemon/src/core.rs", - ), - 244u32, - ::log::__private_api::Option::None, - ); - } - }; - let code_id = result.uploaded_code_id().unwrap(); - let wasm = CosmWasm::new(self.channel()); - while wasm.code(code_id).await.is_err() { - self.next_block().await?; - } - Ok(result) - } - /// Set the sender to use with this DaemonAsync to be the given wallet - pub fn set_sender(&mut self, sender: &Wallet) { - self.sender = sender.clone(); - } - } - pub(crate) fn parse_cw_coins( - coins: &[cosmwasm_std::Coin], - ) -> Result, DaemonError> { - coins - .iter() - .map(|cosmwasm_std::Coin { amount, denom }| { - Ok(cosmrs::Coin { - amount: amount.u128(), - denom: Denom::from_str(denom)?, - }) - }) - .collect::, DaemonError>>() - } -} -pub mod error { - #![allow(missing_docs)] - use cw_orch_core::CwEnvError; - use thiserror::Error; - pub enum DaemonError { - #[error("Reqwest HTTP(s) Error")] - ReqwestError(#[from] ::reqwest::Error), - #[error("JSON Conversion Error")] - SerdeJson(#[from] ::serde_json::Error), - #[error(transparent)] - ParseIntError(#[from] std::num::ParseIntError), - #[error(transparent)] - IOErr(#[from] ::std::io::Error), - #[error(transparent)] - Secp256k1(#[from] ::secp256k1::Error), - #[error(transparent)] - VarError(#[from] ::std::env::VarError), - #[error(transparent)] - AnyError(#[from] ::anyhow::Error), - #[error(transparent)] - Status(#[from] ::tonic::Status), - #[error(transparent)] - TransportError(#[from] ::tonic::transport::Error), - #[error(transparent)] - TendermintError(#[from] ::cosmrs::tendermint::Error), - #[error(transparent)] - CwEnvError(#[from] ::cw_orch_core::CwEnvError), - #[error("Bech32 Decode Error")] - Bech32DecodeErr, - #[error( - "Bech32 Decode Error: Key Failed prefix {0} or length {1} Wanted:{2}/{3}" - )] - Bech32DecodeExpanded(String, usize, String, usize), - #[error("Mnemonic - Wrong length, it should be 24 words")] - WrongLength, - #[error("Mnemonic - Bad Phrase")] - Phrasing, - #[error("Mnemonic - Missing Phrase")] - MissingPhrase, - #[error("Bad Implementation. Missing Component")] - Implementation, - #[error("Unable to convert into public key `{key}`")] - Conversion { key: String, source: bitcoin::bech32::Error }, - #[error( - "Can not augment daemon deployment after usage in more than one contract." - )] - SharedDaemonState, - #[error(transparent)] - ErrReport(#[from] ::eyre::ErrReport), - #[error(transparent)] - GRpcDecodeError(#[from] ::prost::DecodeError), - #[error(transparent)] - ED25519(#[from] ::ed25519_dalek::ed25519::Error), - #[error(transparent)] - DecodeError(#[from] ::base64::DecodeError), - #[error(transparent)] - HexError(#[from] ::hex::FromHexError), - #[error(transparent)] - BitCoinBip32(#[from] ::bitcoin::bip32::Error), - #[error("83 length-missing SECP256K1 prefix")] - ConversionSECP256k1, - #[error("82 length-missing ED25519 prefix")] - ConversionED25519, - #[error("Expected Key length of 82 or 83 length was {0}")] - ConversionLength(usize), - #[error("Expected Key length of 40 length was {0}")] - ConversionLengthED25519Hex(usize), - #[error( - "Expected ED25519 key of length 32 with a BECH32 ED25519 prefix of 5 chars - Len {0} - Hex {1}" - )] - ConversionPrefixED25519(usize, String), - #[error("Can't call Transactions without some gas rules")] - NoGasOpts, - #[error("Can't parse `{parse}` into a coin")] - CoinParseErrV { parse: String }, - #[error("Can't parse `{0}` into a coin")] - CoinParseErr(String), - #[error("TX submit returned `{0}` - {1} '{2}'")] - TxResultError(usize, String, String), - #[error("No price found for Gas using denom {0}")] - GasPriceError(String), - #[error( - "Attempting to fetch validator set in parts, and failed Height mismatch {0} {1}" - )] - TendermintValidatorSet(u64, u64), - #[error("Transaction {0} not found after {1} attempts")] - TXNotFound(String, usize), - #[error("unknown API error")] - Unknown, - #[error("Generic Error {0}")] - StdErr(String), - #[error("calling contract with unimplemented action")] - NotImplemented, - #[error("new chain detected, fill out the scaffold at {0}")] - NewChain(String), - #[error("new network detected, fill out the scaffold at {0}")] - NewNetwork(String), - #[error("Can not connect to any grpc endpoint that was provided.")] - CannotConnectGRPC, - #[error("tx failed: {reason} with code {code}")] - TxFailed { code: usize, reason: String }, - #[error("The list of grpc endpoints is empty")] - GRPCListIsEmpty, - #[error("no wasm path provided for contract.")] - MissingWasmPath, - #[error("daemon builder missing {0}")] - BuilderMissing(String), - #[error("ibc error: {0}")] - IbcError(String), - #[error("insufficient fee, check gas price: {0}")] - InsufficientFee(String), - } - #[allow(unused_qualifications)] - impl std::error::Error for DaemonError { - fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> { - use thiserror::__private::AsDynError; - #[allow(deprecated)] - match self { - DaemonError::ReqwestError { 0: source, .. } => { - std::option::Option::Some(source.as_dyn_error()) - } - DaemonError::SerdeJson { 0: source, .. } => { - std::option::Option::Some(source.as_dyn_error()) - } - DaemonError::ParseIntError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::IOErr { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::Secp256k1 { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::VarError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::AnyError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::Status { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::TransportError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::TendermintError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::CwEnvError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::Bech32DecodeErr { .. } => std::option::Option::None, - DaemonError::Bech32DecodeExpanded { .. } => std::option::Option::None, - DaemonError::WrongLength { .. } => std::option::Option::None, - DaemonError::Phrasing { .. } => std::option::Option::None, - DaemonError::MissingPhrase { .. } => std::option::Option::None, - DaemonError::Implementation { .. } => std::option::Option::None, - DaemonError::Conversion { source: source, .. } => { - std::option::Option::Some(source.as_dyn_error()) - } - DaemonError::SharedDaemonState { .. } => std::option::Option::None, - DaemonError::ErrReport { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::GRpcDecodeError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::ED25519 { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::DecodeError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::HexError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::BitCoinBip32 { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::ConversionSECP256k1 { .. } => std::option::Option::None, - DaemonError::ConversionED25519 { .. } => std::option::Option::None, - DaemonError::ConversionLength { .. } => std::option::Option::None, - DaemonError::ConversionLengthED25519Hex { .. } => { - std::option::Option::None - } - DaemonError::ConversionPrefixED25519 { .. } => std::option::Option::None, - DaemonError::NoGasOpts { .. } => std::option::Option::None, - DaemonError::CoinParseErrV { .. } => std::option::Option::None, - DaemonError::CoinParseErr { .. } => std::option::Option::None, - DaemonError::TxResultError { .. } => std::option::Option::None, - DaemonError::GasPriceError { .. } => std::option::Option::None, - DaemonError::TendermintValidatorSet { .. } => std::option::Option::None, - DaemonError::TXNotFound { .. } => std::option::Option::None, - DaemonError::Unknown { .. } => std::option::Option::None, - DaemonError::StdErr { .. } => std::option::Option::None, - DaemonError::NotImplemented { .. } => std::option::Option::None, - DaemonError::NewChain { .. } => std::option::Option::None, - DaemonError::NewNetwork { .. } => std::option::Option::None, - DaemonError::CannotConnectGRPC { .. } => std::option::Option::None, - DaemonError::TxFailed { .. } => std::option::Option::None, - DaemonError::GRPCListIsEmpty { .. } => std::option::Option::None, - DaemonError::MissingWasmPath { .. } => std::option::Option::None, - DaemonError::BuilderMissing { .. } => std::option::Option::None, - DaemonError::IbcError { .. } => std::option::Option::None, - DaemonError::InsufficientFee { .. } => std::option::Option::None, - } - } - } - #[allow(unused_qualifications)] - impl std::fmt::Display for DaemonError { - fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - use thiserror::__private::AsDisplay as _; - #[allow(unused_variables, deprecated, clippy::used_underscore_binding)] - match self { - DaemonError::ReqwestError(_0) => { - __formatter.write_fmt(format_args!("Reqwest HTTP(s) Error")) - } - DaemonError::SerdeJson(_0) => { - __formatter.write_fmt(format_args!("JSON Conversion Error")) - } - DaemonError::ParseIntError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::IOErr(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::Secp256k1(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::VarError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::AnyError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::Status(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::TransportError(_0) => { - std::fmt::Display::fmt(_0, __formatter) - } - DaemonError::TendermintError(_0) => { - std::fmt::Display::fmt(_0, __formatter) - } - DaemonError::CwEnvError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::Bech32DecodeErr {} => { - __formatter.write_fmt(format_args!("Bech32 Decode Error")) - } - DaemonError::Bech32DecodeExpanded(_0, _1, _2, _3) => { - __formatter - .write_fmt( - format_args!( - "Bech32 Decode Error: Key Failed prefix {0} or length {1} Wanted:{2}/{3}", - _0.as_display(), - _1.as_display(), - _2.as_display(), - _3.as_display(), - ), - ) - } - DaemonError::WrongLength {} => { - __formatter - .write_fmt( - format_args!( - "Mnemonic - Wrong length, it should be 24 words", - ), - ) - } - DaemonError::Phrasing {} => { - __formatter.write_fmt(format_args!("Mnemonic - Bad Phrase")) - } - DaemonError::MissingPhrase {} => { - __formatter.write_fmt(format_args!("Mnemonic - Missing Phrase")) - } - DaemonError::Implementation {} => { - __formatter - .write_fmt(format_args!("Bad Implementation. Missing Component")) - } - DaemonError::Conversion { key, source } => { - __formatter - .write_fmt( - format_args!( - "Unable to convert into public key `{0}`", - key.as_display(), - ), - ) - } - DaemonError::SharedDaemonState {} => { - __formatter - .write_fmt( - format_args!( - "Can not augment daemon deployment after usage in more than one contract.", - ), - ) - } - DaemonError::ErrReport(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::GRpcDecodeError(_0) => { - std::fmt::Display::fmt(_0, __formatter) - } - DaemonError::ED25519(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::DecodeError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::HexError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::BitCoinBip32(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::ConversionSECP256k1 {} => { - __formatter - .write_fmt(format_args!("83 length-missing SECP256K1 prefix")) - } - DaemonError::ConversionED25519 {} => { - __formatter - .write_fmt(format_args!("82 length-missing ED25519 prefix")) - } - DaemonError::ConversionLength(_0) => { - __formatter - .write_fmt( - format_args!( - "Expected Key length of 82 or 83 length was {0}", - _0.as_display(), - ), - ) - } - DaemonError::ConversionLengthED25519Hex(_0) => { - __formatter - .write_fmt( - format_args!( - "Expected Key length of 40 length was {0}", - _0.as_display(), - ), - ) - } - DaemonError::ConversionPrefixED25519(_0, _1) => { - __formatter - .write_fmt( - format_args!( - "Expected ED25519 key of length 32 with a BECH32 ED25519 prefix of 5 chars - Len {0} - Hex {1}", - _0.as_display(), - _1.as_display(), - ), - ) - } - DaemonError::NoGasOpts {} => { - __formatter - .write_fmt( - format_args!( - "Can\'t call Transactions without some gas rules", - ), - ) - } - DaemonError::CoinParseErrV { parse } => { - __formatter - .write_fmt( - format_args!( - "Can\'t parse `{0}` into a coin", - parse.as_display(), - ), - ) - } - DaemonError::CoinParseErr(_0) => { - __formatter - .write_fmt( - format_args!( - "Can\'t parse `{0}` into a coin", - _0.as_display(), - ), - ) - } - DaemonError::TxResultError(_0, _1, _2) => { - __formatter - .write_fmt( - format_args!( - "TX submit returned `{0}` - {1} \'{2}\'", - _0.as_display(), - _1.as_display(), - _2.as_display(), - ), - ) - } - DaemonError::GasPriceError(_0) => { - __formatter - .write_fmt( - format_args!( - "No price found for Gas using denom {0}", - _0.as_display(), - ), - ) - } - DaemonError::TendermintValidatorSet(_0, _1) => { - __formatter - .write_fmt( - format_args!( - "Attempting to fetch validator set in parts, and failed Height mismatch {0} {1}", - _0.as_display(), - _1.as_display(), - ), - ) - } - DaemonError::TXNotFound(_0, _1) => { - __formatter - .write_fmt( - format_args!( - "Transaction {0} not found after {1} attempts", - _0.as_display(), - _1.as_display(), - ), - ) - } - DaemonError::Unknown {} => { - __formatter.write_fmt(format_args!("unknown API error")) - } - DaemonError::StdErr(_0) => { - __formatter - .write_fmt(format_args!("Generic Error {0}", _0.as_display())) - } - DaemonError::NotImplemented {} => { - __formatter - .write_fmt( - format_args!("calling contract with unimplemented action"), - ) - } - DaemonError::NewChain(_0) => { - __formatter - .write_fmt( - format_args!( - "new chain detected, fill out the scaffold at {0}", - _0.as_display(), - ), - ) - } - DaemonError::NewNetwork(_0) => { - __formatter - .write_fmt( - format_args!( - "new network detected, fill out the scaffold at {0}", - _0.as_display(), - ), - ) - } - DaemonError::CannotConnectGRPC {} => { - __formatter - .write_fmt( - format_args!( - "Can not connect to any grpc endpoint that was provided.", - ), - ) - } - DaemonError::TxFailed { code, reason } => { - __formatter - .write_fmt( - format_args!( - "tx failed: {0} with code {1}", - reason.as_display(), - code.as_display(), - ), - ) - } - DaemonError::GRPCListIsEmpty {} => { - __formatter - .write_fmt(format_args!("The list of grpc endpoints is empty")) - } - DaemonError::MissingWasmPath {} => { - __formatter - .write_fmt(format_args!("no wasm path provided for contract.")) - } - DaemonError::BuilderMissing(_0) => { - __formatter - .write_fmt( - format_args!("daemon builder missing {0}", _0.as_display()), - ) - } - DaemonError::IbcError(_0) => { - __formatter - .write_fmt(format_args!("ibc error: {0}", _0.as_display())) - } - DaemonError::InsufficientFee(_0) => { - __formatter - .write_fmt( - format_args!( - "insufficient fee, check gas price: {0}", - _0.as_display(), - ), - ) - } - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::reqwest::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::reqwest::Error) -> Self { - DaemonError::ReqwestError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::serde_json::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::serde_json::Error) -> Self { - DaemonError::SerdeJson { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From for DaemonError { - #[allow(deprecated)] - fn from(source: std::num::ParseIntError) -> Self { - DaemonError::ParseIntError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::std::io::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::std::io::Error) -> Self { - DaemonError::IOErr { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::secp256k1::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::secp256k1::Error) -> Self { - DaemonError::Secp256k1 { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::std::env::VarError> for DaemonError { - #[allow(deprecated)] - fn from(source: ::std::env::VarError) -> Self { - DaemonError::VarError { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::anyhow::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::anyhow::Error) -> Self { - DaemonError::AnyError { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::tonic::Status> for DaemonError { - #[allow(deprecated)] - fn from(source: ::tonic::Status) -> Self { - DaemonError::Status { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::tonic::transport::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::tonic::transport::Error) -> Self { - DaemonError::TransportError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::cosmrs::tendermint::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::cosmrs::tendermint::Error) -> Self { - DaemonError::TendermintError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::cw_orch_core::CwEnvError> for DaemonError { - #[allow(deprecated)] - fn from(source: ::cw_orch_core::CwEnvError) -> Self { - DaemonError::CwEnvError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::eyre::ErrReport> for DaemonError { - #[allow(deprecated)] - fn from(source: ::eyre::ErrReport) -> Self { - DaemonError::ErrReport { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::prost::DecodeError> for DaemonError { - #[allow(deprecated)] - fn from(source: ::prost::DecodeError) -> Self { - DaemonError::GRpcDecodeError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::ed25519_dalek::ed25519::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::ed25519_dalek::ed25519::Error) -> Self { - DaemonError::ED25519 { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::base64::DecodeError> for DaemonError { - #[allow(deprecated)] - fn from(source: ::base64::DecodeError) -> Self { - DaemonError::DecodeError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::hex::FromHexError> for DaemonError { - #[allow(deprecated)] - fn from(source: ::hex::FromHexError) -> Self { - DaemonError::HexError { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::bitcoin::bip32::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::bitcoin::bip32::Error) -> Self { - DaemonError::BitCoinBip32 { - 0: source, - } - } - } - #[automatically_derived] - impl ::core::fmt::Debug for DaemonError { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match self { - DaemonError::ReqwestError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ReqwestError", - &__self_0, - ) - } - DaemonError::SerdeJson(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "SerdeJson", - &__self_0, - ) - } - DaemonError::ParseIntError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ParseIntError", - &__self_0, - ) - } - DaemonError::IOErr(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "IOErr", - &__self_0, - ) - } - DaemonError::Secp256k1(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "Secp256k1", - &__self_0, - ) - } - DaemonError::VarError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "VarError", - &__self_0, - ) - } - DaemonError::AnyError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "AnyError", - &__self_0, - ) - } - DaemonError::Status(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "Status", - &__self_0, - ) - } - DaemonError::TransportError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "TransportError", - &__self_0, - ) - } - DaemonError::TendermintError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "TendermintError", - &__self_0, - ) - } - DaemonError::CwEnvError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "CwEnvError", - &__self_0, - ) - } - DaemonError::Bech32DecodeErr => { - ::core::fmt::Formatter::write_str(f, "Bech32DecodeErr") - } - DaemonError::Bech32DecodeExpanded( - __self_0, - __self_1, - __self_2, - __self_3, - ) => { - ::core::fmt::Formatter::debug_tuple_field4_finish( - f, - "Bech32DecodeExpanded", - __self_0, - __self_1, - __self_2, - &__self_3, - ) - } - DaemonError::WrongLength => { - ::core::fmt::Formatter::write_str(f, "WrongLength") - } - DaemonError::Phrasing => ::core::fmt::Formatter::write_str(f, "Phrasing"), - DaemonError::MissingPhrase => { - ::core::fmt::Formatter::write_str(f, "MissingPhrase") - } - DaemonError::Implementation => { - ::core::fmt::Formatter::write_str(f, "Implementation") - } - DaemonError::Conversion { key: __self_0, source: __self_1 } => { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "Conversion", - "key", - __self_0, - "source", - &__self_1, - ) - } - DaemonError::SharedDaemonState => { - ::core::fmt::Formatter::write_str(f, "SharedDaemonState") - } - DaemonError::ErrReport(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ErrReport", - &__self_0, - ) - } - DaemonError::GRpcDecodeError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "GRpcDecodeError", - &__self_0, - ) - } - DaemonError::ED25519(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ED25519", - &__self_0, - ) - } - DaemonError::DecodeError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "DecodeError", - &__self_0, - ) - } - DaemonError::HexError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "HexError", - &__self_0, - ) - } - DaemonError::BitCoinBip32(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "BitCoinBip32", - &__self_0, - ) - } - DaemonError::ConversionSECP256k1 => { - ::core::fmt::Formatter::write_str(f, "ConversionSECP256k1") - } - DaemonError::ConversionED25519 => { - ::core::fmt::Formatter::write_str(f, "ConversionED25519") - } - DaemonError::ConversionLength(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ConversionLength", - &__self_0, - ) - } - DaemonError::ConversionLengthED25519Hex(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ConversionLengthED25519Hex", - &__self_0, - ) - } - DaemonError::ConversionPrefixED25519(__self_0, __self_1) => { - ::core::fmt::Formatter::debug_tuple_field2_finish( - f, - "ConversionPrefixED25519", - __self_0, - &__self_1, - ) - } - DaemonError::NoGasOpts => { - ::core::fmt::Formatter::write_str(f, "NoGasOpts") - } - DaemonError::CoinParseErrV { parse: __self_0 } => { - ::core::fmt::Formatter::debug_struct_field1_finish( - f, - "CoinParseErrV", - "parse", - &__self_0, - ) - } - DaemonError::CoinParseErr(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "CoinParseErr", - &__self_0, - ) - } - DaemonError::TxResultError(__self_0, __self_1, __self_2) => { - ::core::fmt::Formatter::debug_tuple_field3_finish( - f, - "TxResultError", - __self_0, - __self_1, - &__self_2, - ) - } - DaemonError::GasPriceError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "GasPriceError", - &__self_0, - ) - } - DaemonError::TendermintValidatorSet(__self_0, __self_1) => { - ::core::fmt::Formatter::debug_tuple_field2_finish( - f, - "TendermintValidatorSet", - __self_0, - &__self_1, - ) - } - DaemonError::TXNotFound(__self_0, __self_1) => { - ::core::fmt::Formatter::debug_tuple_field2_finish( - f, - "TXNotFound", - __self_0, - &__self_1, - ) - } - DaemonError::Unknown => ::core::fmt::Formatter::write_str(f, "Unknown"), - DaemonError::StdErr(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "StdErr", - &__self_0, - ) - } - DaemonError::NotImplemented => { - ::core::fmt::Formatter::write_str(f, "NotImplemented") - } - DaemonError::NewChain(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "NewChain", - &__self_0, - ) - } - DaemonError::NewNetwork(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "NewNetwork", - &__self_0, - ) - } - DaemonError::CannotConnectGRPC => { - ::core::fmt::Formatter::write_str(f, "CannotConnectGRPC") - } - DaemonError::TxFailed { code: __self_0, reason: __self_1 } => { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "TxFailed", - "code", - __self_0, - "reason", - &__self_1, - ) - } - DaemonError::GRPCListIsEmpty => { - ::core::fmt::Formatter::write_str(f, "GRPCListIsEmpty") - } - DaemonError::MissingWasmPath => { - ::core::fmt::Formatter::write_str(f, "MissingWasmPath") - } - DaemonError::BuilderMissing(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "BuilderMissing", - &__self_0, - ) - } - DaemonError::IbcError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "IbcError", - &__self_0, - ) - } - DaemonError::InsufficientFee(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "InsufficientFee", - &__self_0, - ) - } - } - } - } - impl DaemonError { - pub fn ibc_err(msg: impl ToString) -> Self { - Self::IbcError(msg.to_string()) - } - } - impl From for CwEnvError { - fn from(val: DaemonError) -> Self { - CwEnvError::AnyError(val.into()) - } - } -} -pub(crate) mod json_file { - use serde_json::{from_reader, json, Value}; - use std::fs::{File, OpenOptions}; - pub fn write( - filename: &String, - chain_id: &String, - network_id: &String, - deploy_id: &String, - ) { - let file = OpenOptions::new() - .create(true) - .read(true) - .write(true) - .truncate(false) - .open(filename) - .unwrap(); - let mut json: Value = if file.metadata().unwrap().len().eq(&0) { - ::serde_json::Value::Object(::serde_json::Map::new()) - } else { - from_reader(file).unwrap() - }; - if json.get(network_id).is_none() { - json[network_id] = ::serde_json::Value::Object(::serde_json::Map::new()); - } - if json[network_id].get(chain_id).is_none() { - json[network_id][chain_id] = ::serde_json::Value::Object({ - let mut object = ::serde_json::Map::new(); - let _ = object - .insert( - (deploy_id).into(), - ::serde_json::Value::Object(::serde_json::Map::new()), - ); - let _ = object - .insert( - ("code_ids").into(), - ::serde_json::Value::Object(::serde_json::Map::new()), - ); - object - }); - } - serde_json::to_writer_pretty(File::create(filename).unwrap(), &json).unwrap(); - } - pub fn read(filename: &String) -> Value { - let file = File::open(filename) - .unwrap_or_else(|_| { - ::core::panicking::panic_fmt( - format_args!("File should be present at {0}", filename), - ); - }); - let json: serde_json::Value = from_reader(file).unwrap(); - json - } -} -/// Proto types for different blockchains -pub mod proto { - pub mod injective { - #![allow(missing_docs)] - use crate::DaemonError; - use cosmrs::tx::SignDoc; - use cosmrs::{proto::traits::TypeUrl, tx::Raw}; - pub const ETHEREUM_COIN_TYPE: u32 = 60; - pub struct InjectiveEthAccount { - #[prost(message, optional, tag = "1")] - pub base_account: ::core::option::Option< - super::super::cosmos_modules::auth::BaseAccount, - >, - #[prost(bytes, tag = "2")] - pub code_hash: Vec, - } - #[automatically_derived] - impl ::core::clone::Clone for InjectiveEthAccount { - #[inline] - fn clone(&self) -> InjectiveEthAccount { - InjectiveEthAccount { - base_account: ::core::clone::Clone::clone(&self.base_account), - code_hash: ::core::clone::Clone::clone(&self.code_hash), - } - } - } - #[automatically_derived] - impl ::core::marker::StructuralPartialEq for InjectiveEthAccount {} - #[automatically_derived] - impl ::core::cmp::PartialEq for InjectiveEthAccount { - #[inline] - fn eq(&self, other: &InjectiveEthAccount) -> bool { - self.base_account == other.base_account - && self.code_hash == other.code_hash - } - } - impl ::prost::Message for InjectiveEthAccount { - #[allow(unused_variables)] - fn encode_raw(&self, buf: &mut B) - where - B: ::prost::bytes::BufMut, - { - if let Some(ref msg) = self.base_account { - ::prost::encoding::message::encode(1u32, msg, buf); - } - if self.code_hash != b"" as &[u8] { - ::prost::encoding::bytes::encode(2u32, &self.code_hash, buf); - } - } - #[allow(unused_variables)] - fn merge_field( - &mut self, - tag: u32, - wire_type: ::prost::encoding::WireType, - buf: &mut B, - ctx: ::prost::encoding::DecodeContext, - ) -> ::core::result::Result<(), ::prost::DecodeError> - where - B: ::prost::bytes::Buf, - { - const STRUCT_NAME: &'static str = "InjectiveEthAccount"; - match tag { - 1u32 => { - let mut value = &mut self.base_account; - ::prost::encoding::message::merge( - wire_type, - value.get_or_insert_with(::core::default::Default::default), - buf, - ctx, - ) - .map_err(|mut error| { - error.push(STRUCT_NAME, "base_account"); - error - }) - } - 2u32 => { - let mut value = &mut self.code_hash; - ::prost::encoding::bytes::merge(wire_type, value, buf, ctx) - .map_err(|mut error| { - error.push(STRUCT_NAME, "code_hash"); - error - }) - } - _ => ::prost::encoding::skip_field(wire_type, tag, buf, ctx), - } - } - #[inline] - fn encoded_len(&self) -> usize { - 0 - + self - .base_account - .as_ref() - .map_or( - 0, - |msg| ::prost::encoding::message::encoded_len(1u32, msg), - ) - + if self.code_hash != b"" as &[u8] { - ::prost::encoding::bytes::encoded_len(2u32, &self.code_hash) - } else { - 0 - } - } - fn clear(&mut self) { - self.base_account = ::core::option::Option::None; - self.code_hash.clear(); - } - } - impl ::core::default::Default for InjectiveEthAccount { - fn default() -> Self { - InjectiveEthAccount { - base_account: ::core::default::Default::default(), - code_hash: ::core::default::Default::default(), - } - } - } - impl ::core::fmt::Debug for InjectiveEthAccount { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - let mut builder = f.debug_struct("InjectiveEthAccount"); - let builder = { - let wrapper = &self.base_account; - builder.field("base_account", &wrapper) - }; - let builder = { - let wrapper = { - fn ScalarWrapper(v: T) -> T { - v - } - ScalarWrapper(&self.code_hash) - }; - builder.field("code_hash", &wrapper) - }; - builder.finish() - } - } - pub struct InjectivePubKey { - #[prost(bytes, tag = 1)] - pub key: Vec, - } - #[automatically_derived] - impl ::core::clone::Clone for InjectivePubKey { - #[inline] - fn clone(&self) -> InjectivePubKey { - InjectivePubKey { - key: ::core::clone::Clone::clone(&self.key), - } - } - } - #[automatically_derived] - impl ::core::marker::StructuralPartialEq for InjectivePubKey {} - #[automatically_derived] - impl ::core::cmp::PartialEq for InjectivePubKey { - #[inline] - fn eq(&self, other: &InjectivePubKey) -> bool { - self.key == other.key - } - } - impl ::prost::Message for InjectivePubKey { - #[allow(unused_variables)] - fn encode_raw(&self, buf: &mut B) - where - B: ::prost::bytes::BufMut, - { - if self.key != b"" as &[u8] { - ::prost::encoding::bytes::encode(1u32, &self.key, buf); - } - } - #[allow(unused_variables)] - fn merge_field( - &mut self, - tag: u32, - wire_type: ::prost::encoding::WireType, - buf: &mut B, - ctx: ::prost::encoding::DecodeContext, - ) -> ::core::result::Result<(), ::prost::DecodeError> - where - B: ::prost::bytes::Buf, - { - const STRUCT_NAME: &'static str = "InjectivePubKey"; - match tag { - 1u32 => { - let mut value = &mut self.key; - ::prost::encoding::bytes::merge(wire_type, value, buf, ctx) - .map_err(|mut error| { - error.push(STRUCT_NAME, "key"); - error - }) - } - _ => ::prost::encoding::skip_field(wire_type, tag, buf, ctx), - } - } - #[inline] - fn encoded_len(&self) -> usize { - 0 - + if self.key != b"" as &[u8] { - ::prost::encoding::bytes::encoded_len(1u32, &self.key) - } else { - 0 - } - } - fn clear(&mut self) { - self.key.clear(); - } - } - impl ::core::default::Default for InjectivePubKey { - fn default() -> Self { - InjectivePubKey { - key: ::core::default::Default::default(), - } - } - } - impl ::core::fmt::Debug for InjectivePubKey { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - let mut builder = f.debug_struct("InjectivePubKey"); - let builder = { - let wrapper = { - fn ScalarWrapper(v: T) -> T { - v - } - ScalarWrapper(&self.key) - }; - builder.field("key", &wrapper) - }; - builder.finish() - } - } - impl TypeUrl for InjectivePubKey { - const TYPE_URL: &'static str = "/injective.crypto.v1beta1.ethsecp256k1.PubKey"; - } - pub trait InjectiveSigner { - fn sign_injective(&self, sign_doc: SignDoc) -> Result; - } - } -} -pub mod sender { - use crate::{networks::ChainKind, proto::injective::ETHEREUM_COIN_TYPE}; - use super::{ - cosmos_modules::{self, auth::BaseAccount}, - error::DaemonError, queriers::{DaemonQuerier, Node}, - state::DaemonState, tx_builder::TxBuilder, tx_resp::CosmTxResponse, - }; - use crate::proto::injective::InjectiveEthAccount; - use crate::{core::parse_cw_coins, keys::private::PrivateKey}; - use cosmrs::{ - bank::MsgSend, crypto::secp256k1::SigningKey, proto::traits::Message, - tendermint::chain::Id, - tx::{self, ModeInfo, Msg, Raw, SignDoc, SignMode, SignerInfo}, - AccountId, - }; - use cosmwasm_std::Addr; - use secp256k1::{All, Context, Secp256k1, Signing}; - use std::{convert::TryFrom, env, rc::Rc, str::FromStr}; - use cosmos_modules::vesting::PeriodicVestingAccount; - use tonic::transport::Channel; - /// A wallet is a sender of transactions, can be safely cloned and shared within the same thread. - pub type Wallet = Rc>; - /// Signer of the transactions and helper for address derivation - /// This is the main interface for simulating and signing transactions - pub struct Sender { - pub private_key: PrivateKey, - pub secp: Secp256k1, - pub(crate) daemon_state: Rc, - } - impl Sender { - pub fn new(daemon_state: &Rc) -> Result, DaemonError> { - let kind = ChainKind::from(daemon_state.chain_data.network_type.clone()); - let mnemonic = env::var(kind.mnemonic_name()) - .unwrap_or_else(|_| { - { - ::core::panicking::panic_fmt( - format_args!( - "Wallet mnemonic environment variable {0} not set.", - kind.mnemonic_name(), - ), - ); - } - }); - Self::from_mnemonic(daemon_state, &mnemonic) - } - /// Construct a new Sender from a mnemonic - pub fn from_mnemonic( - daemon_state: &Rc, - mnemonic: &str, - ) -> Result, DaemonError> { - let secp = Secp256k1::new(); - let p_key: PrivateKey = PrivateKey::from_words( - &secp, - mnemonic, - 0, - 0, - daemon_state.chain_data.slip44, - )?; - let sender = Sender { - daemon_state: daemon_state.clone(), - private_key: p_key, - secp, - }; - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Interacting with {0} using address: {1}", - daemon_state.chain_data.chain_id, - sender.pub_addr_str()?, - ), - lvl, - &( - "cw_orch_daemon::sender", - "cw_orch_daemon::sender", - "cw-orch-daemon/src/sender.rs", - ), - 71u32, - ::log::__private_api::Option::None, - ); - } - }; - Ok(sender) - } - fn cosmos_private_key(&self) -> SigningKey { - SigningKey::from_slice(&self.private_key.raw_key()).unwrap() - } - pub fn channel(&self) -> Channel { - self.daemon_state.grpc_channel.clone() - } - pub fn pub_addr(&self) -> Result { - Ok( - AccountId::new( - &self.daemon_state.chain_data.bech32_prefix, - &self.private_key.public_key(&self.secp).raw_address.unwrap(), - )?, - ) - } - pub fn address(&self) -> Result { - Ok(Addr::unchecked(self.pub_addr_str()?)) - } - pub fn pub_addr_str(&self) -> Result { - Ok(self.pub_addr()?.to_string()) - } - pub async fn bank_send( - &self, - recipient: &str, - coins: Vec, - ) -> Result { - let msg_send = MsgSend { - from_address: self.pub_addr()?, - to_address: AccountId::from_str(recipient)?, - amount: parse_cw_coins(&coins)?, - }; - self.commit_tx( - <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([msg_send])), - Some("sending tokens"), - ) - .await - } - pub async fn calculate_gas( - &self, - tx_body: &tx::Body, - sequence: u64, - account_number: u64, - ) -> Result { - let fee = TxBuilder::build_fee( - 0u8, - &self.daemon_state.chain_data.fees.fee_tokens[0].denom, - 0, - ); - let auth_info = SignerInfo { - public_key: self.private_key.get_signer_public_key(&self.secp), - mode_info: ModeInfo::single(SignMode::Direct), - sequence, - } - .auth_info(fee); - let sign_doc = SignDoc::new( - tx_body, - &auth_info, - &Id::try_from(self.daemon_state.chain_data.chain_id.to_string())?, - account_number, - )?; - let tx_raw = self.sign(sign_doc)?; - Node::new(self.channel()).simulate_tx(tx_raw.to_bytes()?).await - } - pub async fn commit_tx( - &self, - msgs: Vec, - memo: Option<&str>, - ) -> Result { - let timeout_height = Node::new(self.channel()).block_height().await? + 10u64; - let tx_body = TxBuilder::build_body(msgs, memo, timeout_height); - let mut tx_builder = TxBuilder::new(tx_body); - let tx = tx_builder.build(self).await?; - let mut tx_response = self.broadcast_tx(tx).await?; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("tx broadcast response: {0:?}", tx_response), - lvl, - &( - "cw_orch_daemon::sender", - "cw_orch_daemon::sender", - "cw-orch-daemon/src/sender.rs", - ), - 166u32, - ::log::__private_api::Option::None, - ); - } - }; - if has_insufficient_fee(&tx_response.raw_log) { - let suggested_fee = parse_suggested_fee(&tx_response.raw_log); - let Some(new_fee) = suggested_fee else { - return Err(DaemonError::InsufficientFee(tx_response.raw_log)); - }; - tx_builder.fee_amount(new_fee); - let tx = tx_builder.build(self).await?; - tx_response = self.broadcast_tx(tx).await?; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("tx broadcast response: {0:?}", tx_response), - lvl, - &( - "cw_orch_daemon::sender", - "cw_orch_daemon::sender", - "cw-orch-daemon/src/sender.rs", - ), - 181u32, - ::log::__private_api::Option::None, - ); - } - }; - } - let resp = Node::new(self.channel()).find_tx(tx_response.txhash).await?; - if resp.code == 0 { - Ok(resp) - } else { - Err(DaemonError::TxFailed { - code: resp.code, - reason: resp.raw_log, - }) - } - } - pub fn sign(&self, sign_doc: SignDoc) -> Result { - let tx_raw = if self.private_key.coin_type == ETHEREUM_COIN_TYPE { - { - ::core::panicking::panic_fmt( - format_args!( - "Coin Type {0} not supported without eth feature", - ETHEREUM_COIN_TYPE, - ), - ); - }; - } else { - sign_doc.sign(&self.cosmos_private_key())? - }; - Ok(tx_raw) - } - pub async fn base_account(&self) -> Result { - let addr = self.pub_addr().unwrap().to_string(); - let mut client = cosmos_modules::auth::query_client::QueryClient::new( - self.channel(), - ); - let resp = client - .account(cosmos_modules::auth::QueryAccountRequest { - address: addr, - }) - .await? - .into_inner(); - let account = resp.account.unwrap().value; - let acc = if let Ok(acc) = BaseAccount::decode(account.as_ref()) { - acc - } else if let Ok(acc) = PeriodicVestingAccount::decode(account.as_ref()) { - acc.base_vesting_account.unwrap().base_account.unwrap() - } else if let Ok(acc) = InjectiveEthAccount::decode(account.as_ref()) { - acc.base_account.unwrap() - } else { - return Err( - DaemonError::StdErr( - "Unknown account type returned from QueryAccountRequest".into(), - ), - ); - }; - Ok(acc) - } - async fn broadcast_tx( - &self, - tx: Raw, - ) -> Result< - cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse, - DaemonError, - > { - let mut client = cosmos_modules::tx::service_client::ServiceClient::new( - self.channel(), - ); - let commit = client - .broadcast_tx(cosmos_modules::tx::BroadcastTxRequest { - tx_bytes: tx.to_bytes()?, - mode: cosmos_modules::tx::BroadcastMode::Sync.into(), - }) - .await?; - let commit = commit.into_inner().tx_response.unwrap(); - Ok(commit) - } - } - fn has_insufficient_fee(raw_log: &str) -> bool { - raw_log.contains("insufficient fees") - } - fn parse_suggested_fee(raw_log: &str) -> Option { - let parts: Vec<&str> = raw_log.split("required: ").collect(); - if parts.len() != 2 { - return None; - } - let got_parts: Vec<&str> = parts[0].split_whitespace().collect(); - let paid_fee_with_denom = got_parts.last()?; - let (_, denomination) = paid_fee_with_denom - .split_at(paid_fee_with_denom.find(|c: char| !c.is_numeric())?); - { - ::std::io::_eprint(format_args!("denom: {0}\n", denomination)); - }; - let required_fees: Vec<&str> = parts[1].split(denomination).collect(); - { - ::std::io::_eprint(format_args!("required fees: {0:?}\n", required_fees)); - }; - let (_, suggested_fee) = required_fees[0] - .split_at(required_fees[0].rfind(|c: char| !c.is_numeric())?); - { - ::std::io::_eprint(format_args!("suggested fee: {0}\n", suggested_fee)); - }; - suggested_fee.parse::().ok().or(suggested_fee[1..].parse::().ok()) - } -} -pub mod state { - use super::error::DaemonError; - use crate::{channel::GrpcChannel, networks::ChainKind}; - use cosmwasm_std::Addr; - use cw_orch_core::{ - environment::{DeployDetails, StateInterface}, - CwEnvError, - }; - use ibc_chain_registry::chain::ChainData; - use serde::Serialize; - use serde_json::{json, Value}; - use std::{collections::HashMap, env, fs::File, path::Path}; - use tonic::transport::Channel; - /// Stores the chain information and deployment state. - /// Uses a simple JSON file to store the deployment information locally. - pub struct DaemonState { - /// this is passed via env var STATE_FILE - pub json_file_path: String, - /// Deployment identifier - pub deployment_id: String, - /// gRPC channel - pub grpc_channel: Channel, - /// Information about the chain - pub chain_data: ChainData, - } - #[automatically_derived] - impl ::core::clone::Clone for DaemonState { - #[inline] - fn clone(&self) -> DaemonState { - DaemonState { - json_file_path: ::core::clone::Clone::clone(&self.json_file_path), - deployment_id: ::core::clone::Clone::clone(&self.deployment_id), - grpc_channel: ::core::clone::Clone::clone(&self.grpc_channel), - chain_data: ::core::clone::Clone::clone(&self.chain_data), - } - } - } - #[automatically_derived] - impl ::core::fmt::Debug for DaemonState { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field4_finish( - f, - "DaemonState", - "json_file_path", - &self.json_file_path, - "deployment_id", - &self.deployment_id, - "grpc_channel", - &self.grpc_channel, - "chain_data", - &&self.chain_data, - ) - } - } - impl DaemonState { - /// Creates a new state from the given chain data and deployment id. - /// Attempts to connect to any of the provided gRPC endpoints. - pub async fn new( - mut chain_data: ChainData, - deployment_id: String, - ) -> Result { - if chain_data.apis.grpc.is_empty() { - return Err(DaemonError::GRPCListIsEmpty); - } - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Found {0} gRPC endpoints", - chain_data.apis.grpc.len(), - ), - lvl, - &( - "cw_orch_daemon::state", - "cw_orch_daemon::state", - "cw-orch-daemon/src/state.rs", - ), - 40u32, - ::log::__private_api::Option::None, - ); - } - }; - let grpc_channel = GrpcChannel::connect( - &chain_data.apis.grpc, - &chain_data.chain_id, - ) - .await?; - let mut json_file_path = env::var("STATE_FILE") - .unwrap_or("./state.json".to_string()); - if chain_data.network_type == ChainKind::Local.to_string() { - let name = Path::new(&json_file_path) - .file_stem() - .unwrap() - .to_str() - .unwrap(); - let folder = Path::new(&json_file_path) - .parent() - .unwrap() - .to_str() - .unwrap(); - json_file_path = { - let res = ::alloc::fmt::format( - format_args!("{0}/{1}_local.json", folder, name), - ); - res - }; - } - let shortest_denom_token = chain_data - .fees - .fee_tokens - .iter() - .fold( - chain_data.fees.fee_tokens[0].clone(), - |acc, item| { - if item.denom.len() < acc.denom.len() { - item.clone() - } else { - acc - } - }, - ); - chain_data - .fees - .fee_tokens = <[_]>::into_vec( - #[rustc_box] - ::alloc::boxed::Box::new([shortest_denom_token]), - ); - let state = DaemonState { - json_file_path, - deployment_id, - grpc_channel, - chain_data, - }; - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Writing daemon state JSON file: {0:#?}", - state.json_file_path, - ), - lvl, - &( - "cw_orch_daemon::state", - "cw_orch_daemon::state", - "cw-orch-daemon/src/state.rs", - ), - 87u32, - ::log::__private_api::Option::None, - ); - } - }; - crate::json_file::write( - &state.json_file_path, - &state.chain_data.chain_id.to_string(), - &state.chain_data.chain_name, - &state.deployment_id, - ); - Ok(state) - } - /// Get the state filepath and read it as json - fn read_state(&self) -> serde_json::Value { - crate::json_file::read(&self.json_file_path) - } - /// Retrieve a stateful value using the chainId and networkId - pub fn get(&self, key: &str) -> Value { - let json = self.read_state(); - json[&self.chain_data.chain_name][&self.chain_data.chain_id.to_string()][key] - .clone() - } - /// Set a stateful value using the chainId and networkId - pub fn set(&self, key: &str, contract_id: &str, value: T) { - let mut json = self.read_state(); - json[&self - .chain_data - .chain_name][&self - .chain_data - .chain_id - .to_string()][key][contract_id] = ::serde_json::to_value(&value) - .unwrap(); - serde_json::to_writer_pretty( - File::create(&self.json_file_path).unwrap(), - &json, - ) - .unwrap(); - } - } - impl StateInterface for DaemonState { - /// Read address for contract in deployment id from state file - fn get_address(&self, contract_id: &str) -> Result { - let value = self - .get(&self.deployment_id) - .get(contract_id) - .ok_or_else(|| CwEnvError::AddrNotInStore(contract_id.to_owned()))? - .clone(); - Ok(Addr::unchecked(value.as_str().unwrap())) - } - /// Set address for contract in deployment id in state file - fn set_address(&mut self, contract_id: &str, address: &Addr) { - self.set(&self.deployment_id, contract_id, address.as_str()); - } - /// Get the locally-saved version of the contract's version on this network - fn get_code_id(&self, contract_id: &str) -> Result { - let value = self - .get("code_ids") - .get(contract_id) - .ok_or_else(|| CwEnvError::CodeIdNotInStore(contract_id.to_owned()))? - .clone(); - Ok(value.as_u64().unwrap()) - } - /// Set the locally-saved version of the contract's latest version on this network - fn set_code_id(&mut self, contract_id: &str, code_id: u64) { - self.set("code_ids", contract_id, code_id); - } - /// Get all addresses for deployment id from state file - fn get_all_addresses(&self) -> Result, CwEnvError> { - let mut store = HashMap::new(); - let addresses = self.get(&self.deployment_id); - let value = addresses.as_object().unwrap(); - for (id, addr) in value { - store.insert(id.clone(), Addr::unchecked(addr.as_str().unwrap())); - } - Ok(store) - } - fn get_all_code_ids(&self) -> Result, CwEnvError> { - let mut store = HashMap::new(); - let code_ids = self.get("code_ids"); - let value = code_ids.as_object().unwrap(); - for (id, code_id) in value { - store.insert(id.clone(), code_id.as_u64().unwrap()); - } - Ok(store) - } - fn deploy_details(&self) -> DeployDetails { - DeployDetails { - chain_id: self.chain_data.chain_id.to_string(), - chain_name: self.chain_data.chain_name.clone(), - deployment_id: self.deployment_id.clone(), - } - } - } -} -pub mod sync { - mod builder { - use ibc_chain_registry::chain::ChainData; - use crate::DaemonAsyncBuilder; - use super::{super::error::DaemonError, core::Daemon}; - /// Create [`Daemon`] through [`DaemonBuilder`] - /// ## Example - /// ```no_run - /// use cw_orch_daemon::{networks, DaemonBuilder}; - /// - /// let Daemon = DaemonBuilder::default() - /// .chain(networks::LOCAL_JUNO) - /// .deployment_id("v0.1.0") - /// .build() - /// .unwrap(); - /// ``` - pub struct DaemonBuilder { - pub(crate) chain: Option, - pub(crate) handle: Option, - pub(crate) deployment_id: Option, - /// Wallet mnemonic - pub(crate) mnemonic: Option, - } - #[automatically_derived] - impl ::core::clone::Clone for DaemonBuilder { - #[inline] - fn clone(&self) -> DaemonBuilder { - DaemonBuilder { - chain: ::core::clone::Clone::clone(&self.chain), - handle: ::core::clone::Clone::clone(&self.handle), - deployment_id: ::core::clone::Clone::clone(&self.deployment_id), - mnemonic: ::core::clone::Clone::clone(&self.mnemonic), - } - } - } - #[automatically_derived] - impl ::core::default::Default for DaemonBuilder { - #[inline] - fn default() -> DaemonBuilder { - DaemonBuilder { - chain: ::core::default::Default::default(), - handle: ::core::default::Default::default(), - deployment_id: ::core::default::Default::default(), - mnemonic: ::core::default::Default::default(), - } - } - } - impl DaemonBuilder { - /// Set the chain the Daemon will connect to - pub fn chain(&mut self, chain: impl Into) -> &mut Self { - self.chain = Some(chain.into()); - self - } - /// Set the deployment id to use for the Daemon interactions - /// Defaults to `default` - pub fn deployment_id( - &mut self, - deployment_id: impl Into, - ) -> &mut Self { - self.deployment_id = Some(deployment_id.into()); - self - } - /// Set the tokio runtime handle to use for the Daemon - /// - /// ## Example - /// ```no_run - /// use cw_orch_daemon::Daemon; - /// use tokio::runtime::Runtime; - /// let rt = Runtime::new().unwrap(); - /// let Daemon = Daemon::builder() - /// .handle(rt.handle()) - /// // ... - /// .build() - /// .unwrap(); - /// ``` - pub fn handle(&mut self, handle: &tokio::runtime::Handle) -> &mut Self { - self.handle = Some(handle.clone()); - self - } - /// Set the mnemonic to use with this chain. - pub fn mnemonic(&mut self, mnemonic: impl ToString) -> &mut Self { - self.mnemonic = Some(mnemonic.to_string()); - self - } - /// Build a Daemon - pub fn build(&self) -> Result { - let rt_handle = self - .handle - .clone() - .ok_or(DaemonError::BuilderMissing("runtime handle".into()))?; - let daemon = rt_handle - .block_on(DaemonAsyncBuilder::from(self.clone()).build())?; - Ok(Daemon { rt_handle, daemon }) - } - } - } - mod core { - use std::{fmt::Debug, rc::Rc, time::Duration}; - use super::super::{sender::Wallet, DaemonAsync}; - use crate::{ - queriers::{DaemonQuerier, Node}, - CosmTxResponse, DaemonBuilder, DaemonError, DaemonState, - }; - use cosmrs::tendermint::Time; - use cosmwasm_std::{Addr, Coin}; - use cw_orch_core::{ - contract::{interface_traits::Uploadable, WasmPath}, - environment::{ChainState, TxHandler}, - }; - use serde::{de::DeserializeOwned, Serialize}; - use tokio::runtime::Handle; - use tonic::transport::Channel; - /** - Represents a blockchain node. - Is constructed with the [DaemonBuilder]. - - ## Usage - - ```rust,no_run - use cw_orch_daemon::{Daemon, networks}; - use tokio::runtime::Runtime; - - let rt = Runtime::new().unwrap(); - let daemon: Daemon = Daemon::builder() - .chain(networks::JUNO_1) - .handle(rt.handle()) - .build() - .unwrap(); - ``` - ## Environment Execution - - The Daemon implements [`TxHandler`] which allows you to perform transactions on the chain. - - ## Querying - - Different Cosmos SDK modules can be queried through the daemon by calling the [`Daemon.query_client`] method with a specific querier. - See [Querier](crate::queriers) for examples. -*/ - pub struct Daemon { - pub daemon: DaemonAsync, - /// Runtime handle to execute async tasks - pub rt_handle: Handle, - } - #[automatically_derived] - impl ::core::clone::Clone for Daemon { - #[inline] - fn clone(&self) -> Daemon { - Daemon { - daemon: ::core::clone::Clone::clone(&self.daemon), - rt_handle: ::core::clone::Clone::clone(&self.rt_handle), - } - } - } - impl Daemon { - /// Get the daemon builder - pub fn builder() -> DaemonBuilder { - DaemonBuilder::default() - } - /// Perform a query with a given querier - /// See [Querier](crate::queriers) for examples. - pub fn query_client(&self) -> Querier { - self.daemon.query_client() - } - /// Get the channel configured for this Daemon - pub fn channel(&self) -> Channel { - self.daemon.state.grpc_channel.clone() - } - /// Get the channel configured for this Daemon - pub fn wallet(&self) -> Wallet { - self.daemon.sender.clone() - } - } - impl ChainState for Daemon { - type Out = Rc; - fn state(&self) -> Self::Out { - self.daemon.state.clone() - } - } - impl TxHandler for Daemon { - type Response = CosmTxResponse; - type Error = DaemonError; - type ContractSource = WasmPath; - type Sender = Wallet; - fn sender(&self) -> Addr { - self.daemon.sender.address().unwrap() - } - fn set_sender(&mut self, sender: Self::Sender) { - self.daemon.sender = sender; - } - fn upload( - &self, - uploadable: &impl Uploadable, - ) -> Result { - self.rt_handle.block_on(self.daemon.upload(uploadable)) - } - fn execute( - &self, - exec_msg: &E, - coins: &[cosmwasm_std::Coin], - contract_address: &Addr, - ) -> Result { - self.rt_handle - .block_on(self.daemon.execute(exec_msg, coins, contract_address)) - } - fn instantiate( - &self, - code_id: u64, - init_msg: &I, - label: Option<&str>, - admin: Option<&Addr>, - coins: &[Coin], - ) -> Result { - self.rt_handle - .block_on( - self.daemon.instantiate(code_id, init_msg, label, admin, coins), - ) - } - fn query( - &self, - query_msg: &Q, - contract_address: &Addr, - ) -> Result { - self.rt_handle.block_on(self.daemon.query(query_msg, contract_address)) - } - fn migrate( - &self, - migrate_msg: &M, - new_code_id: u64, - contract_address: &Addr, - ) -> Result { - self.rt_handle - .block_on( - self.daemon.migrate(migrate_msg, new_code_id, contract_address), - ) - } - fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { - let mut last_height = self - .rt_handle - .block_on(self.query_client::().block_height())?; - let end_height = last_height + amount; - while last_height < end_height { - self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(4))); - last_height = self - .rt_handle - .block_on(self.query_client::().block_height())?; - } - Ok(()) - } - fn wait_seconds(&self, secs: u64) -> Result<(), DaemonError> { - self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(secs))); - Ok(()) - } - fn next_block(&self) -> Result<(), DaemonError> { - let mut last_height = self - .rt_handle - .block_on(self.query_client::().block_height())?; - let end_height = last_height + 1; - while last_height < end_height { - self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(4))); - last_height = self - .rt_handle - .block_on(self.query_client::().block_height())?; - } - Ok(()) - } - fn block_info(&self) -> Result { - let block = self - .rt_handle - .block_on(self.query_client::().latest_block())?; - let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; - let time = cosmwasm_std::Timestamp::from_nanos( - since_epoch.as_nanos() as u64, - ); - Ok(cosmwasm_std::BlockInfo { - height: block.header.height.value(), - time, - chain_id: block.header.chain_id.to_string(), - }) - } - } - } - pub use self::{builder::*, core::*}; -} -pub mod tx_resp { - use super::{ - cosmos_modules::{ - abci::{AbciMessageLog, Attribute, StringEvent, TxResponse}, - tendermint_abci::Event, - }, - error::DaemonError, - }; - use chrono::{DateTime, NaiveDateTime, TimeZone, Utc}; - use cosmwasm_std::{to_binary, Binary, StdError, StdResult}; - use cw_orch_core::environment::IndexResponse; - use serde::{Deserialize, Serialize}; - const FORMAT: &str = "%Y-%m-%dT%H:%M:%S%.f"; - const FORMAT_TZ_SUPPLIED: &str = "%Y-%m-%dT%H:%M:%S.%f%:z"; - const FORMAT_SHORT_Z: &str = "%Y-%m-%dT%H:%M:%SZ"; - const FORMAT_SHORT_Z2: &str = "%Y-%m-%dT%H:%M:%S.%fZ"; - /// The response from a transaction performed on a blockchain. - pub struct CosmTxResponse { - /// Height of the block in which the transaction was included. - pub height: u64, - /// Transaction hash. - pub txhash: String, - /// Transaction index within the block. - pub codespace: String, - /// Transaction result code - pub code: usize, - /// Arbitrary data that can be included in a transaction. - pub data: String, - /// Raw log message. - pub raw_log: String, - /// Logs of the transaction. - pub logs: Vec, - /// Transaction info. - pub info: String, - /// Gas limit. - pub gas_wanted: u64, - /// Gas used. - pub gas_used: u64, - /// Timestamp of the block in which the transaction was included. - pub timestamp: DateTime, - /// Transaction events. - pub events: Vec, - } - #[automatically_derived] - impl ::core::fmt::Debug for CosmTxResponse { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - let names: &'static _ = &[ - "height", - "txhash", - "codespace", - "code", - "data", - "raw_log", - "logs", - "info", - "gas_wanted", - "gas_used", - "timestamp", - "events", - ]; - let values: &[&dyn ::core::fmt::Debug] = &[ - &self.height, - &self.txhash, - &self.codespace, - &self.code, - &self.data, - &self.raw_log, - &self.logs, - &self.info, - &self.gas_wanted, - &self.gas_used, - &self.timestamp, - &&self.events, - ]; - ::core::fmt::Formatter::debug_struct_fields_finish( - f, - "CosmTxResponse", - names, - values, - ) - } - } - #[automatically_derived] - impl ::core::default::Default for CosmTxResponse { - #[inline] - fn default() -> CosmTxResponse { - CosmTxResponse { - height: ::core::default::Default::default(), - txhash: ::core::default::Default::default(), - codespace: ::core::default::Default::default(), - code: ::core::default::Default::default(), - data: ::core::default::Default::default(), - raw_log: ::core::default::Default::default(), - logs: ::core::default::Default::default(), - info: ::core::default::Default::default(), - gas_wanted: ::core::default::Default::default(), - gas_used: ::core::default::Default::default(), - timestamp: ::core::default::Default::default(), - events: ::core::default::Default::default(), - } - } - } - impl CosmTxResponse { - /// find a attribute's value from TX logs. - /// returns: msg_index and value - pub fn get_attribute_from_logs( - &self, - event_type: &str, - attribute_key: &str, - ) -> Vec<(usize, String)> { - let mut response: Vec<(usize, String)> = Default::default(); - let logs = &self.logs; - for log_part in logs { - let msg_index = log_part.msg_index.unwrap_or_default(); - let events = &log_part.events; - let events_filtered = events - .iter() - .filter(|event| event.s_type == event_type) - .collect::>(); - if let Some(event) = events_filtered.first() { - let attributes_filtered = event - .attributes - .iter() - .filter(|attr| attr.key == attribute_key) - .map(|f| f.value.clone()) - .collect::>(); - if let Some(attr_key) = attributes_filtered.first() { - response.push((msg_index, attr_key.clone())); - } - } - } - response - } - /// get the list of event types from a TX record - pub fn get_events(&self, event_type: &str) -> Vec { - let mut response: Vec = Default::default(); - for log_part in &self.logs { - let events = &log_part.events; - let events_filtered = events - .iter() - .filter(|event| event.s_type == event_type) - .collect::>(); - for event in events_filtered { - response.push(event.clone()); - } - } - response - } - } - impl From<&serde_json::Value> for TxResultBlockMsg { - fn from(value: &serde_json::Value) -> Self { - serde_json::from_value(value.clone()).unwrap() - } - } - impl From for CosmTxResponse { - fn from(tx: TxResponse) -> Self { - Self { - height: tx.height as u64, - txhash: tx.txhash, - codespace: tx.codespace, - code: tx.code as usize, - data: tx.data, - raw_log: tx.raw_log, - logs: tx.logs.into_iter().map(TxResultBlockMsg::from).collect(), - info: tx.info, - gas_wanted: tx.gas_wanted as u64, - gas_used: tx.gas_used as u64, - timestamp: parse_timestamp(tx.timestamp).unwrap(), - events: tx.events, - } - } - } - impl IndexResponse for CosmTxResponse { - fn events(&self) -> Vec { - let mut parsed_events = ::alloc::vec::Vec::new(); - for event in &self.events { - let mut pattr = ::alloc::vec::Vec::new(); - for attr in &event.attributes { - pattr - .push(cosmwasm_std::Attribute { - key: attr.key.clone(), - value: attr.value.clone(), - }) - } - let pevent = cosmwasm_std::Event::new(event.r#type.clone()) - .add_attributes(pattr); - parsed_events.push(pevent); - } - parsed_events - } - fn data(&self) -> Option { - if self.data.is_empty() { - None - } else { - Some(to_binary(self.data.as_bytes()).unwrap()) - } - } - fn event_attr_value( - &self, - event_type: &str, - attr_key: &str, - ) -> StdResult { - for event in &self.events { - if event.r#type == event_type { - for attr in &event.attributes { - if attr.key == attr_key { - return Ok(attr.value.clone()); - } - } - } - } - Err( - StdError::generic_err({ - let res = ::alloc::fmt::format( - format_args!( - "event of type {0} does not have a value at key {1}", - event_type, - attr_key, - ), - ); - res - }), - ) - } - } - /// The events from a single message in a transaction. - pub struct TxResultBlockMsg { - /// index of the message in the transaction - pub msg_index: Option, - /// Events from this message - pub events: Vec, - } - #[automatically_derived] - impl ::core::clone::Clone for TxResultBlockMsg { - #[inline] - fn clone(&self) -> TxResultBlockMsg { - TxResultBlockMsg { - msg_index: ::core::clone::Clone::clone(&self.msg_index), - events: ::core::clone::Clone::clone(&self.events), - } - } - } - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl _serde::Serialize for TxResultBlockMsg { - fn serialize<__S>( - &self, - __serializer: __S, - ) -> _serde::__private::Result<__S::Ok, __S::Error> - where - __S: _serde::Serializer, - { - let mut __serde_state = _serde::Serializer::serialize_struct( - __serializer, - "TxResultBlockMsg", - false as usize + 1 + 1, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "msg_index", - &self.msg_index, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "events", - &self.events, - )?; - _serde::ser::SerializeStruct::end(__serde_state) - } - } - }; - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl<'de> _serde::Deserialize<'de> for TxResultBlockMsg { - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - #[allow(non_camel_case_types)] - #[doc(hidden)] - enum __Field { - __field0, - __field1, - __ignore, - } - #[doc(hidden)] - struct __FieldVisitor; - impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { - type Value = __Field; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "field identifier", - ) - } - fn visit_u64<__E>( - self, - __value: u64, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - 0u64 => _serde::__private::Ok(__Field::__field0), - 1u64 => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_str<__E>( - self, - __value: &str, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - "msg_index" => _serde::__private::Ok(__Field::__field0), - "events" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_bytes<__E>( - self, - __value: &[u8], - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - b"msg_index" => _serde::__private::Ok(__Field::__field0), - b"events" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - } - impl<'de> _serde::Deserialize<'de> for __Field { - #[inline] - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - _serde::Deserializer::deserialize_identifier( - __deserializer, - __FieldVisitor, - ) - } - } - #[doc(hidden)] - struct __Visitor<'de> { - marker: _serde::__private::PhantomData, - lifetime: _serde::__private::PhantomData<&'de ()>, - } - impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { - type Value = TxResultBlockMsg; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "struct TxResultBlockMsg", - ) - } - #[inline] - fn visit_seq<__A>( - self, - mut __seq: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<'de>, - { - let __field0 = match _serde::de::SeqAccess::next_element::< - Option, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 0usize, - &"struct TxResultBlockMsg with 2 elements", - ), - ); - } - }; - let __field1 = match _serde::de::SeqAccess::next_element::< - Vec, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 1usize, - &"struct TxResultBlockMsg with 2 elements", - ), - ); - } - }; - _serde::__private::Ok(TxResultBlockMsg { - msg_index: __field0, - events: __field1, - }) - } - #[inline] - fn visit_map<__A>( - self, - mut __map: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::MapAccess<'de>, - { - let mut __field0: _serde::__private::Option> = _serde::__private::None; - let mut __field1: _serde::__private::Option< - Vec, - > = _serde::__private::None; - while let _serde::__private::Some(__key) - = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { - match __key { - __Field::__field0 => { - if _serde::__private::Option::is_some(&__field0) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field( - "msg_index", - ), - ); - } - __field0 = _serde::__private::Some( - _serde::de::MapAccess::next_value::< - Option, - >(&mut __map)?, - ); - } - __Field::__field1 => { - if _serde::__private::Option::is_some(&__field1) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field("events"), - ); - } - __field1 = _serde::__private::Some( - _serde::de::MapAccess::next_value::< - Vec, - >(&mut __map)?, - ); - } - _ => { - let _ = _serde::de::MapAccess::next_value::< - _serde::de::IgnoredAny, - >(&mut __map)?; - } - } - } - let __field0 = match __field0 { - _serde::__private::Some(__field0) => __field0, - _serde::__private::None => { - _serde::__private::de::missing_field("msg_index")? - } - }; - let __field1 = match __field1 { - _serde::__private::Some(__field1) => __field1, - _serde::__private::None => { - _serde::__private::de::missing_field("events")? - } - }; - _serde::__private::Ok(TxResultBlockMsg { - msg_index: __field0, - events: __field1, - }) - } - } - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &["msg_index", "events"]; - _serde::Deserializer::deserialize_struct( - __deserializer, - "TxResultBlockMsg", - FIELDS, - __Visitor { - marker: _serde::__private::PhantomData::, - lifetime: _serde::__private::PhantomData, - }, - ) - } - } - }; - #[automatically_derived] - impl ::core::fmt::Debug for TxResultBlockMsg { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "TxResultBlockMsg", - "msg_index", - &self.msg_index, - "events", - &&self.events, - ) - } - } - impl From for TxResultBlockMsg { - fn from(msg: AbciMessageLog) -> Self { - Self { - msg_index: Some(msg.msg_index as usize), - events: msg.events.into_iter().map(TxResultBlockEvent::from).collect(), - } - } - } - /// A single event from a transaction and its attributes. - pub struct TxResultBlockEvent { - #[serde(rename = "type")] - /// Type of the event - pub s_type: String, - /// Attributes of the event - pub attributes: Vec, - } - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl<'de> _serde::Deserialize<'de> for TxResultBlockEvent { - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - #[allow(non_camel_case_types)] - #[doc(hidden)] - enum __Field { - __field0, - __field1, - __ignore, - } - #[doc(hidden)] - struct __FieldVisitor; - impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { - type Value = __Field; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "field identifier", - ) - } - fn visit_u64<__E>( - self, - __value: u64, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - 0u64 => _serde::__private::Ok(__Field::__field0), - 1u64 => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_str<__E>( - self, - __value: &str, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - "type" => _serde::__private::Ok(__Field::__field0), - "attributes" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_bytes<__E>( - self, - __value: &[u8], - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - b"type" => _serde::__private::Ok(__Field::__field0), - b"attributes" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - } - impl<'de> _serde::Deserialize<'de> for __Field { - #[inline] - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - _serde::Deserializer::deserialize_identifier( - __deserializer, - __FieldVisitor, - ) - } - } - #[doc(hidden)] - struct __Visitor<'de> { - marker: _serde::__private::PhantomData, - lifetime: _serde::__private::PhantomData<&'de ()>, - } - impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { - type Value = TxResultBlockEvent; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "struct TxResultBlockEvent", - ) - } - #[inline] - fn visit_seq<__A>( - self, - mut __seq: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<'de>, - { - let __field0 = match _serde::de::SeqAccess::next_element::< - String, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 0usize, - &"struct TxResultBlockEvent with 2 elements", - ), - ); - } - }; - let __field1 = match _serde::de::SeqAccess::next_element::< - Vec, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 1usize, - &"struct TxResultBlockEvent with 2 elements", - ), - ); - } - }; - _serde::__private::Ok(TxResultBlockEvent { - s_type: __field0, - attributes: __field1, - }) - } - #[inline] - fn visit_map<__A>( - self, - mut __map: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::MapAccess<'de>, - { - let mut __field0: _serde::__private::Option = _serde::__private::None; - let mut __field1: _serde::__private::Option< - Vec, - > = _serde::__private::None; - while let _serde::__private::Some(__key) - = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { - match __key { - __Field::__field0 => { - if _serde::__private::Option::is_some(&__field0) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field("type"), - ); - } - __field0 = _serde::__private::Some( - _serde::de::MapAccess::next_value::(&mut __map)?, - ); - } - __Field::__field1 => { - if _serde::__private::Option::is_some(&__field1) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field( - "attributes", - ), - ); - } - __field1 = _serde::__private::Some( - _serde::de::MapAccess::next_value::< - Vec, - >(&mut __map)?, - ); - } - _ => { - let _ = _serde::de::MapAccess::next_value::< - _serde::de::IgnoredAny, - >(&mut __map)?; - } - } - } - let __field0 = match __field0 { - _serde::__private::Some(__field0) => __field0, - _serde::__private::None => { - _serde::__private::de::missing_field("type")? - } - }; - let __field1 = match __field1 { - _serde::__private::Some(__field1) => __field1, - _serde::__private::None => { - _serde::__private::de::missing_field("attributes")? - } - }; - _serde::__private::Ok(TxResultBlockEvent { - s_type: __field0, - attributes: __field1, - }) - } - } - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &["type", "attributes"]; - _serde::Deserializer::deserialize_struct( - __deserializer, - "TxResultBlockEvent", - FIELDS, - __Visitor { - marker: _serde::__private::PhantomData::, - lifetime: _serde::__private::PhantomData, - }, - ) - } - } - }; - #[automatically_derived] - impl ::core::clone::Clone for TxResultBlockEvent { - #[inline] - fn clone(&self) -> TxResultBlockEvent { - TxResultBlockEvent { - s_type: ::core::clone::Clone::clone(&self.s_type), - attributes: ::core::clone::Clone::clone(&self.attributes), - } - } - } - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl _serde::Serialize for TxResultBlockEvent { - fn serialize<__S>( - &self, - __serializer: __S, - ) -> _serde::__private::Result<__S::Ok, __S::Error> - where - __S: _serde::Serializer, - { - let mut __serde_state = _serde::Serializer::serialize_struct( - __serializer, - "TxResultBlockEvent", - false as usize + 1 + 1, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "type", - &self.s_type, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "attributes", - &self.attributes, - )?; - _serde::ser::SerializeStruct::end(__serde_state) - } - } - }; - #[automatically_derived] - impl ::core::fmt::Debug for TxResultBlockEvent { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "TxResultBlockEvent", - "s_type", - &self.s_type, - "attributes", - &&self.attributes, - ) - } - } - impl From for TxResultBlockEvent { - fn from(event: StringEvent) -> Self { - Self { - s_type: event.r#type, - attributes: event - .attributes - .into_iter() - .map(TxResultBlockAttribute::from) - .collect(), - } - } - } - impl TxResultBlockEvent { - /// get all key/values from the event that have the key 'key' - pub fn get_attributes(&self, key: &str) -> Vec { - self.attributes.iter().filter(|attr| attr.key == key).cloned().collect() - } - /// return the first value of the first attribute that has the key 'key' - pub fn get_first_attribute_value(&self, key: &str) -> Option { - self.get_attributes(key).first().map(|attr| attr.value.clone()) - } - } - /// A single attribute of an event. - pub struct TxResultBlockAttribute { - /// Key of the attribute - pub key: String, - /// Value of the attribute - pub value: String, - } - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl<'de> _serde::Deserialize<'de> for TxResultBlockAttribute { - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - #[allow(non_camel_case_types)] - #[doc(hidden)] - enum __Field { - __field0, - __field1, - __ignore, - } - #[doc(hidden)] - struct __FieldVisitor; - impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { - type Value = __Field; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "field identifier", - ) - } - fn visit_u64<__E>( - self, - __value: u64, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - 0u64 => _serde::__private::Ok(__Field::__field0), - 1u64 => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_str<__E>( - self, - __value: &str, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - "key" => _serde::__private::Ok(__Field::__field0), - "value" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_bytes<__E>( - self, - __value: &[u8], - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - b"key" => _serde::__private::Ok(__Field::__field0), - b"value" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - } - impl<'de> _serde::Deserialize<'de> for __Field { - #[inline] - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - _serde::Deserializer::deserialize_identifier( - __deserializer, - __FieldVisitor, - ) - } - } - #[doc(hidden)] - struct __Visitor<'de> { - marker: _serde::__private::PhantomData, - lifetime: _serde::__private::PhantomData<&'de ()>, - } - impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { - type Value = TxResultBlockAttribute; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "struct TxResultBlockAttribute", - ) - } - #[inline] - fn visit_seq<__A>( - self, - mut __seq: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<'de>, - { - let __field0 = match _serde::de::SeqAccess::next_element::< - String, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 0usize, - &"struct TxResultBlockAttribute with 2 elements", - ), - ); - } - }; - let __field1 = match _serde::de::SeqAccess::next_element::< - String, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 1usize, - &"struct TxResultBlockAttribute with 2 elements", - ), - ); - } - }; - _serde::__private::Ok(TxResultBlockAttribute { - key: __field0, - value: __field1, - }) - } - #[inline] - fn visit_map<__A>( - self, - mut __map: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::MapAccess<'de>, - { - let mut __field0: _serde::__private::Option = _serde::__private::None; - let mut __field1: _serde::__private::Option = _serde::__private::None; - while let _serde::__private::Some(__key) - = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { - match __key { - __Field::__field0 => { - if _serde::__private::Option::is_some(&__field0) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field("key"), - ); - } - __field0 = _serde::__private::Some( - _serde::de::MapAccess::next_value::(&mut __map)?, - ); - } - __Field::__field1 => { - if _serde::__private::Option::is_some(&__field1) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field("value"), - ); - } - __field1 = _serde::__private::Some( - _serde::de::MapAccess::next_value::(&mut __map)?, - ); - } - _ => { - let _ = _serde::de::MapAccess::next_value::< - _serde::de::IgnoredAny, - >(&mut __map)?; - } - } - } - let __field0 = match __field0 { - _serde::__private::Some(__field0) => __field0, - _serde::__private::None => { - _serde::__private::de::missing_field("key")? - } - }; - let __field1 = match __field1 { - _serde::__private::Some(__field1) => __field1, - _serde::__private::None => { - _serde::__private::de::missing_field("value")? - } - }; - _serde::__private::Ok(TxResultBlockAttribute { - key: __field0, - value: __field1, - }) - } - } - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &["key", "value"]; - _serde::Deserializer::deserialize_struct( - __deserializer, - "TxResultBlockAttribute", - FIELDS, - __Visitor { - marker: _serde::__private::PhantomData::, - lifetime: _serde::__private::PhantomData, - }, - ) - } - } - }; - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl _serde::Serialize for TxResultBlockAttribute { - fn serialize<__S>( - &self, - __serializer: __S, - ) -> _serde::__private::Result<__S::Ok, __S::Error> - where - __S: _serde::Serializer, - { - let mut __serde_state = _serde::Serializer::serialize_struct( - __serializer, - "TxResultBlockAttribute", - false as usize + 1 + 1, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "key", - &self.key, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "value", - &self.value, - )?; - _serde::ser::SerializeStruct::end(__serde_state) - } - } - }; - #[automatically_derived] - impl ::core::clone::Clone for TxResultBlockAttribute { - #[inline] - fn clone(&self) -> TxResultBlockAttribute { - TxResultBlockAttribute { - key: ::core::clone::Clone::clone(&self.key), - value: ::core::clone::Clone::clone(&self.value), - } - } - } - #[automatically_derived] - impl ::core::fmt::Debug for TxResultBlockAttribute { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "TxResultBlockAttribute", - "key", - &self.key, - "value", - &&self.value, - ) - } - } - impl From for TxResultBlockAttribute { - fn from(a: Attribute) -> Self { - Self { key: a.key, value: a.value } - } - } - /// Parse a string timestamp into a `DateTime` - pub fn parse_timestamp(s: String) -> Result, DaemonError> { - let len = s.len(); - let slice_len = if s.contains('.') { len.saturating_sub(4) } else { len }; - let sliced = &s[0..slice_len]; - match NaiveDateTime::parse_from_str(sliced, FORMAT) { - Err(_e) => { - match NaiveDateTime::parse_from_str(&s, FORMAT_TZ_SUPPLIED) { - Err(_e2) => { - match NaiveDateTime::parse_from_str(sliced, FORMAT_SHORT_Z) { - Err(_e3) => { - match NaiveDateTime::parse_from_str(&s, FORMAT_SHORT_Z2) { - Err(_e4) => { - { - ::std::io::_eprint( - format_args!("DateTime Fail {0} {1:#?}\n", s, _e4), - ); - }; - Err(DaemonError::StdErr(_e4.to_string())) - } - Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), - } - } - Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), - } - } - Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), - } - } - Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), - } - } -} -pub mod keys { - #![allow(unused)] - pub mod private { - use super::public::PublicKey; - use crate::proto::injective::{InjectivePubKey, ETHEREUM_COIN_TYPE}; - use crate::DaemonError; - use base64::Engine; - use bitcoin::{ - bip32::{ExtendedPrivKey, IntoDerivationPath}, - Network, - }; - use cosmrs::tx::SignerPublicKey; - use hkd32::mnemonic::{Phrase, Seed}; - use rand_core::OsRng; - use secp256k1::Secp256k1; - /// The Private key structure that is used to generate signatures and public keys - /// WARNING: No Security Audit has been performed - pub struct PrivateKey { - #[allow(missing_docs)] - pub account: u32, - #[allow(missing_docs)] - pub index: u32, - #[allow(missing_docs)] - pub coin_type: u32, - /// The 24 words used to generate this private key - mnemonic: Option, - #[allow(dead_code)] - /// This is used for testing - root_private_key: ExtendedPrivKey, - /// The private key - private_key: ExtendedPrivKey, - } - #[automatically_derived] - impl ::core::clone::Clone for PrivateKey { - #[inline] - fn clone(&self) -> PrivateKey { - PrivateKey { - account: ::core::clone::Clone::clone(&self.account), - index: ::core::clone::Clone::clone(&self.index), - coin_type: ::core::clone::Clone::clone(&self.coin_type), - mnemonic: ::core::clone::Clone::clone(&self.mnemonic), - root_private_key: ::core::clone::Clone::clone( - &self.root_private_key, - ), - private_key: ::core::clone::Clone::clone(&self.private_key), - } - } - } - impl PrivateKey { - /// Generate a new private key - pub fn new( - secp: &Secp256k1, - coin_type: u32, - ) -> Result { - let phrase = hkd32::mnemonic::Phrase::random( - OsRng, - hkd32::mnemonic::Language::English, - ); - PrivateKey::gen_private_key_phrase(secp, phrase, 0, 0, coin_type, "") - } - /// generate a new private key with a seed phrase - pub fn new_seed( - secp: &Secp256k1, - seed_phrase: &str, - coin_type: u32, - ) -> Result { - let phrase = hkd32::mnemonic::Phrase::random( - OsRng, - hkd32::mnemonic::Language::English, - ); - PrivateKey::gen_private_key_phrase( - secp, - phrase, - 0, - 0, - coin_type, - seed_phrase, - ) - } - /// for private key recovery. This is also used by wallet routines to re-hydrate the structure - pub fn from_words( - secp: &Secp256k1, - words: &str, - account: u32, - index: u32, - coin_type: u32, - ) -> Result { - if words.split(' ').count() != 24 { - return Err(DaemonError::WrongLength); - } - match hkd32::mnemonic::Phrase::new( - words, - hkd32::mnemonic::Language::English, - ) { - Ok(phrase) => { - PrivateKey::gen_private_key_phrase( - secp, - phrase, - account, - index, - coin_type, - "", - ) - } - Err(_) => Err(DaemonError::Phrasing), - } - } - /// for private key recovery with seed phrase - pub fn from_words_seed( - secp: &Secp256k1, - words: &str, - seed_pass: &str, - coin_type: u32, - ) -> Result { - match hkd32::mnemonic::Phrase::new( - words, - hkd32::mnemonic::Language::English, - ) { - Ok(phrase) => { - PrivateKey::gen_private_key_phrase( - secp, - phrase, - 0, - 0, - coin_type, - seed_pass, - ) - } - Err(_) => Err(DaemonError::Phrasing), - } - } - /// generate the public key for this private key - pub fn public_key( - &self, - secp: &Secp256k1, - ) -> PublicKey { - if self.coin_type == ETHEREUM_COIN_TYPE { - { - ::core::panicking::panic_fmt( - format_args!( - "Coin Type {0} not supported without eth feature", - ETHEREUM_COIN_TYPE, - ), - ); - }; - } - let x = self.private_key.private_key.public_key(secp); - PublicKey::from_bitcoin_public_key(&bitcoin::PublicKey::new(x)) - } - pub fn get_injective_public_key( - &self, - secp: &Secp256k1, - ) -> SignerPublicKey { - use base64::engine::general_purpose; - use cosmrs::tx::MessageExt; - use secp256k1::SecretKey; - let secret_key = SecretKey::from_slice(self.raw_key().as_slice()) - .unwrap(); - let public_key = secp256k1::PublicKey::from_secret_key( - secp, - &secret_key, - ); - let vec_pk = public_key.serialize(); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "{0:?}, public key", - general_purpose::STANDARD.encode(vec_pk), - ), - lvl, - &( - "cw_orch_daemon::keys::private", - "cw_orch_daemon::keys::private", - "cw-orch-daemon/src/keys/private.rs", - ), - 124u32, - ::log::__private_api::Option::None, - ); - } - }; - let inj_key = InjectivePubKey { - key: vec_pk.into(), - }; - inj_key.to_any().unwrap().try_into().unwrap() - } - pub fn get_signer_public_key( - &self, - secp: &Secp256k1, - ) -> Option { - if self.coin_type == ETHEREUM_COIN_TYPE { - { - ::core::panicking::panic_fmt( - format_args!( - "Coin Type {0} not supported without eth feature", - ETHEREUM_COIN_TYPE, - ), - ); - }; - } - Some( - cosmrs::crypto::secp256k1::SigningKey::from_slice( - self.raw_key().as_slice(), - ) - .unwrap() - .public_key() - .into(), - ) - } - pub fn raw_key(&self) -> Vec { - self.private_key.private_key.secret_bytes().to_vec() - } - fn gen_private_key_phrase( - secp: &Secp256k1, - phrase: Phrase, - account: u32, - index: u32, - coin_type: u32, - seed_phrase: &str, - ) -> Result { - let seed = phrase.to_seed(seed_phrase); - let root_private_key = ExtendedPrivKey::new_master( - Network::Bitcoin, - seed.as_bytes(), - ) - .unwrap(); - let path = { - let res = ::alloc::fmt::format( - format_args!( - "m/44\'/{0}\'/{1}\'/0/{2}", - coin_type, - account, - index, - ), - ); - res - }; - let derivation_path = path.into_derivation_path()?; - let private_key = root_private_key.derive_priv(secp, &derivation_path)?; - Ok(PrivateKey { - account, - index, - coin_type, - mnemonic: Some(phrase), - root_private_key, - private_key, - }) - } - /// the words used to generate this private key - pub fn words(&self) -> Option<&str> { - self.mnemonic.as_ref().map(|phrase| phrase.phrase()) - } - /// used for testing - /// could potentially be used to recreate the private key instead of words - #[allow(dead_code)] - pub(crate) fn seed(&self, passwd: &str) -> Option { - self.mnemonic.as_ref().map(|phrase| phrase.to_seed(passwd)) - } - } - } - pub mod public { - use crate::DaemonError; - use bitcoin::bech32::{decode, encode, u5, FromBase32, ToBase32, Variant}; - pub use ed25519_dalek::VerifyingKey as Ed25519; - use ring::digest::{Context, SHA256}; - use ripemd::{Digest as _, Ripemd160}; - use serde::{Deserialize, Serialize}; - static BECH32_PUBKEY_DATA_PREFIX_SECP256K1: [u8; 5] = [ - 0xeb, - 0x5a, - 0xe9, - 0x87, - 0x21, - ]; - static BECH32_PUBKEY_DATA_PREFIX_ED25519: [u8; 5] = [ - 0x16, - 0x24, - 0xde, - 0x64, - 0x20, - ]; - /// The public key we used to generate the cosmos/tendermind/terrad addresses - pub struct PublicKey { - /// This is optional as we can generate non-pub keys without - pub raw_pub_key: Option>, - /// The raw bytes used to generate non-pub keys - pub raw_address: Option>, - } - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl<'de> _serde::Deserialize<'de> for PublicKey { - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - #[allow(non_camel_case_types)] - #[doc(hidden)] - enum __Field { - __field0, - __field1, - __ignore, - } - #[doc(hidden)] - struct __FieldVisitor; - impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { - type Value = __Field; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "field identifier", - ) - } - fn visit_u64<__E>( - self, - __value: u64, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - 0u64 => _serde::__private::Ok(__Field::__field0), - 1u64 => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_str<__E>( - self, - __value: &str, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - "raw_pub_key" => _serde::__private::Ok(__Field::__field0), - "raw_address" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_bytes<__E>( - self, - __value: &[u8], - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - b"raw_pub_key" => _serde::__private::Ok(__Field::__field0), - b"raw_address" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - } - impl<'de> _serde::Deserialize<'de> for __Field { - #[inline] - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - _serde::Deserializer::deserialize_identifier( - __deserializer, - __FieldVisitor, - ) - } - } - #[doc(hidden)] - struct __Visitor<'de> { - marker: _serde::__private::PhantomData, - lifetime: _serde::__private::PhantomData<&'de ()>, - } - impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { - type Value = PublicKey; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "struct PublicKey", - ) - } - #[inline] - fn visit_seq<__A>( - self, - mut __seq: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<'de>, - { - let __field0 = match _serde::de::SeqAccess::next_element::< - Option>, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 0usize, - &"struct PublicKey with 2 elements", - ), - ); - } - }; - let __field1 = match _serde::de::SeqAccess::next_element::< - Option>, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 1usize, - &"struct PublicKey with 2 elements", - ), - ); - } - }; - _serde::__private::Ok(PublicKey { - raw_pub_key: __field0, - raw_address: __field1, - }) - } - #[inline] - fn visit_map<__A>( - self, - mut __map: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::MapAccess<'de>, - { - let mut __field0: _serde::__private::Option< - Option>, - > = _serde::__private::None; - let mut __field1: _serde::__private::Option< - Option>, - > = _serde::__private::None; - while let _serde::__private::Some(__key) - = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { - match __key { - __Field::__field0 => { - if _serde::__private::Option::is_some(&__field0) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field( - "raw_pub_key", - ), - ); - } - __field0 = _serde::__private::Some( - _serde::de::MapAccess::next_value::< - Option>, - >(&mut __map)?, - ); - } - __Field::__field1 => { - if _serde::__private::Option::is_some(&__field1) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field( - "raw_address", - ), - ); - } - __field1 = _serde::__private::Some( - _serde::de::MapAccess::next_value::< - Option>, - >(&mut __map)?, - ); - } - _ => { - let _ = _serde::de::MapAccess::next_value::< - _serde::de::IgnoredAny, - >(&mut __map)?; - } - } - } - let __field0 = match __field0 { - _serde::__private::Some(__field0) => __field0, - _serde::__private::None => { - _serde::__private::de::missing_field("raw_pub_key")? - } - }; - let __field1 = match __field1 { - _serde::__private::Some(__field1) => __field1, - _serde::__private::None => { - _serde::__private::de::missing_field("raw_address")? - } - }; - _serde::__private::Ok(PublicKey { - raw_pub_key: __field0, - raw_address: __field1, - }) - } - } - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &[ - "raw_pub_key", - "raw_address", - ]; - _serde::Deserializer::deserialize_struct( - __deserializer, - "PublicKey", - FIELDS, - __Visitor { - marker: _serde::__private::PhantomData::, - lifetime: _serde::__private::PhantomData, - }, - ) - } - } - }; - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl _serde::Serialize for PublicKey { - fn serialize<__S>( - &self, - __serializer: __S, - ) -> _serde::__private::Result<__S::Ok, __S::Error> - where - __S: _serde::Serializer, - { - let mut __serde_state = _serde::Serializer::serialize_struct( - __serializer, - "PublicKey", - false as usize + 1 + 1, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "raw_pub_key", - &self.raw_pub_key, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "raw_address", - &self.raw_address, - )?; - _serde::ser::SerializeStruct::end(__serde_state) - } - } - }; - #[automatically_derived] - impl ::core::fmt::Debug for PublicKey { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "PublicKey", - "raw_pub_key", - &self.raw_pub_key, - "raw_address", - &&self.raw_address, - ) - } - } - #[automatically_derived] - impl ::core::clone::Clone for PublicKey { - #[inline] - fn clone(&self) -> PublicKey { - PublicKey { - raw_pub_key: ::core::clone::Clone::clone(&self.raw_pub_key), - raw_address: ::core::clone::Clone::clone(&self.raw_address), - } - } - } - impl PublicKey { - /// Generate a Cosmos/Tendermint/Terrad Public Key - pub fn from_bitcoin_public_key(bpub: &bitcoin::key::PublicKey) -> PublicKey { - let bpub_bytes = bpub.inner.serialize(); - let raw_pub_key = PublicKey::pubkey_from_public_key(&bpub_bytes); - let raw_address = PublicKey::address_from_public_key(&bpub_bytes); - PublicKey { - raw_pub_key: Some(raw_pub_key), - raw_address: Some(raw_address), - } - } - /// Generate from secp256k1 Cosmos/Terrad Public Key - pub fn from_public_key(bpub: &[u8]) -> PublicKey { - let raw_pub_key = PublicKey::pubkey_from_public_key(bpub); - let raw_address = PublicKey::address_from_public_key(bpub); - PublicKey { - raw_pub_key: Some(raw_pub_key), - raw_address: Some(raw_address), - } - } - /// Generate a Cosmos/Tendermint/Terrad Account - pub fn from_account( - acc_address: &str, - prefix: &str, - ) -> Result { - PublicKey::check_prefix_and_length(prefix, acc_address, 44) - .and_then(|vu5| { - let vu8 = Vec::from_base32(vu5.as_slice()) - .map_err(|source| DaemonError::Conversion { - key: acc_address.into(), - source, - })?; - Ok(PublicKey { - raw_pub_key: None, - raw_address: Some(vu8), - }) - }) - } - /// build a public key from a tendermint public key - pub fn from_tendermint_key( - tendermint_public_key: &str, - ) -> Result { - let len = tendermint_public_key.len(); - if len == 83 { - PublicKey::check_prefix_and_length( - "terravalconspub", - tendermint_public_key, - len, - ) - .and_then(|vu5| { - let vu8 = Vec::from_base32(vu5.as_slice()) - .map_err(|source| { - DaemonError::Conversion { - key: tendermint_public_key.into(), - source, - } - })?; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("{0:#?}", hex::encode(&vu8)), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 74u32, - ::log::__private_api::Option::None, - ); - } - }; - if vu8.starts_with(&BECH32_PUBKEY_DATA_PREFIX_SECP256K1) { - let public_key = PublicKey::public_key_from_pubkey(&vu8)?; - let raw = PublicKey::address_from_public_key(&public_key); - Ok(PublicKey { - raw_pub_key: Some(vu8), - raw_address: Some(raw), - }) - } else { - Err(DaemonError::ConversionSECP256k1) - } - }) - } else if len == 82 { - PublicKey::check_prefix_and_length( - "terravalconspub", - tendermint_public_key, - len, - ) - .and_then(|vu5| { - let vu8 = Vec::from_base32(vu5.as_slice()) - .map_err(|source| { - DaemonError::Conversion { - key: tendermint_public_key.into(), - source, - } - })?; - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("ED25519 public keys are not fully supported"), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 100u32, - ::log::__private_api::Option::None, - ); - } - }; - if vu8.starts_with(&BECH32_PUBKEY_DATA_PREFIX_ED25519) { - let raw = PublicKey::address_from_public_ed25519_key(&vu8)?; - Ok(PublicKey { - raw_pub_key: Some(vu8), - raw_address: Some(raw), - }) - } else { - Err(DaemonError::ConversionED25519) - } - }) - } else { - Err(DaemonError::ConversionLength(len)) - } - } - /// build a terravalcons address from a tendermint hex key - /// the tendermint_hex_address should be a hex code of 40 length - pub fn from_tendermint_address( - tendermint_hex_address: &str, - ) -> Result { - let len = tendermint_hex_address.len(); - if len == 40 { - let raw = hex::decode(tendermint_hex_address)?; - Ok(PublicKey { - raw_pub_key: None, - raw_address: Some(raw), - }) - } else { - Err(DaemonError::ConversionLengthED25519Hex(len)) - } - } - /// Generate a Operator address for this public key (used by the validator) - pub fn from_operator_address( - valoper_address: &str, - ) -> Result { - PublicKey::check_prefix_and_length("terravaloper", valoper_address, 51) - .and_then(|vu5| { - let vu8 = Vec::from_base32(vu5.as_slice()) - .map_err(|source| DaemonError::Conversion { - key: valoper_address.into(), - source, - })?; - Ok(PublicKey { - raw_pub_key: None, - raw_address: Some(vu8), - }) - }) - } - /// Generate Public key from raw address - pub fn from_raw_address( - raw_address: &str, - ) -> Result { - let vec1 = hex::decode(raw_address)?; - Ok(PublicKey { - raw_pub_key: None, - raw_address: Some(vec1), - }) - } - fn check_prefix_and_length( - prefix: &str, - data: &str, - length: usize, - ) -> Result, DaemonError> { - let (hrp, decoded_str, _) = decode(data) - .map_err(|source| DaemonError::Conversion { - key: data.into(), - source, - })?; - if hrp == prefix && data.len() == length { - Ok(decoded_str) - } else { - Err( - DaemonError::Bech32DecodeExpanded( - hrp, - data.len(), - prefix.into(), - length, - ), - ) - } - } - /** - Gets a bech32-words pubkey from a compressed bytes Secp256K1 public key. - - @param publicKey raw public key - */ - pub fn pubkey_from_public_key(public_key: &[u8]) -> Vec { - [BECH32_PUBKEY_DATA_PREFIX_SECP256K1.to_vec(), public_key.to_vec()] - .concat() - } - /** - Gets a bech32-words pubkey from a compressed bytes Ed25519 public key. - - @param publicKey raw public key - */ - pub fn pubkey_from_ed25519_public_key(public_key: &[u8]) -> Vec { - [BECH32_PUBKEY_DATA_PREFIX_ED25519.to_vec(), public_key.to_vec()] - .concat() - } - /// Translate from a BECH32 prefixed key to a standard public key - pub fn public_key_from_pubkey( - pub_key: &[u8], - ) -> Result, DaemonError> { - if pub_key.starts_with(&BECH32_PUBKEY_DATA_PREFIX_SECP256K1) { - let len = BECH32_PUBKEY_DATA_PREFIX_SECP256K1.len(); - let len2 = pub_key.len(); - Ok(Vec::from(&pub_key[len..len2])) - } else if pub_key.starts_with(&BECH32_PUBKEY_DATA_PREFIX_ED25519) { - let len = BECH32_PUBKEY_DATA_PREFIX_ED25519.len(); - let len2 = pub_key.len(); - let vec = &pub_key[len..len2]; - let ed25519_pubkey = Ed25519::from_bytes(vec.try_into().unwrap())?; - Ok(ed25519_pubkey.to_bytes().to_vec()) - } else { - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("pub key does not start with BECH32 PREFIX"), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 228u32, - ::log::__private_api::Option::None, - ); - } - }; - Err(DaemonError::Bech32DecodeErr) - } - } - /** - Gets a raw address from a compressed bytes public key. - - @param publicKey raw public key - */ - pub fn address_from_public_key(public_key: &[u8]) -> Vec { - let mut hasher = Ripemd160::new(); - let sha_result = ring::digest::digest(&SHA256, public_key); - hasher.update(&sha_result.as_ref()[0..32]); - let ripe_result = hasher.finalize(); - let address: Vec = ripe_result[0..20].to_vec(); - address - } - /** - Gets a raw address from a ed25519 public key. - - @param publicKey raw public key - */ - pub fn address_from_public_ed25519_key( - public_key: &[u8], - ) -> Result, DaemonError> { - if public_key.len() != (32 + 5) { - Err( - DaemonError::ConversionPrefixED25519( - public_key.len(), - hex::encode(public_key), - ), - ) - } else { - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "address_from_public_ed25519_key public key - {0}", - hex::encode(public_key), - ), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 262u32, - ::log::__private_api::Option::None, - ); - } - }; - let mut sha_result: [u8; 32] = [0; 32]; - let sha_result = ring::digest::digest(&SHA256, &public_key[5..]); - let address: Vec = sha_result.as_ref()[0..20].to_vec(); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "address_from_public_ed25519_key sha result - {0}", - hex::encode(&address), - ), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 283u32, - ::log::__private_api::Option::None, - ); - } - }; - Ok(address) - } - } - /// The main account used in most things - pub fn account(&self, prefix: &str) -> Result { - match &self.raw_address { - Some(raw) => { - let data = encode(prefix, raw.to_base32(), Variant::Bech32); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => Err(DaemonError::Implementation), - } - } - /// The operator address used for validators - pub fn operator_address(&self, prefix: &str) -> Result { - match &self.raw_address { - Some(raw) => { - let data = encode( - &{ - let res = ::alloc::fmt::format( - format_args!("{0}{1}", prefix, "valoper"), - ); - res - }, - raw.to_base32(), - Variant::Bech32, - ); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => Err(DaemonError::Implementation), - } - } - /// application public key - Application keys are associated with a public key terrapub- and an address terra- - pub fn application_public_key( - &self, - prefix: &str, - ) -> Result { - match &self.raw_pub_key { - Some(raw) => { - let data = encode( - &{ - let res = ::alloc::fmt::format( - format_args!("{0}{1}", prefix, "pub"), - ); - res - }, - raw.to_base32(), - Variant::Bech32, - ); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => { - { - let lvl = ::log::Level::Warn; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("Missing Public Key. Can\'t continue"), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 335u32, - ::log::__private_api::Option::None, - ); - } - }; - Err(DaemonError::Implementation) - } - } - } - /// The operator address used for validators public key. - pub fn operator_address_public_key( - &self, - prefix: &str, - ) -> Result { - match &self.raw_pub_key { - Some(raw) => { - let data = encode( - &{ - let res = ::alloc::fmt::format( - format_args!("{0}{1}", prefix, "valoperpub"), - ); - res - }, - raw.to_base32(), - Variant::Bech32, - ); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => Err(DaemonError::Implementation), - } - } - /// This is a unique key used to sign block hashes. It is associated with a public key terravalconspub. - pub fn tendermint(&self, prefix: &str) -> Result { - match &self.raw_address { - Some(raw) => { - let data = encode( - &{ - let res = ::alloc::fmt::format( - format_args!("{0}{1}", prefix, "valcons"), - ); - res - }, - raw.to_base32(), - Variant::Bech32, - ); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => Err(DaemonError::Implementation), - } - } - /// This is a unique key used to sign block hashes. It is associated with a public key terravalconspub. - pub fn tendermint_pubkey( - &self, - prefix: &str, - ) -> Result { - match &self.raw_pub_key { - Some(raw) => { - let b32 = raw.to_base32(); - let data = encode( - &{ - let res = ::alloc::fmt::format( - format_args!("{0}{1}", prefix, "valconspub"), - ); - res - }, - b32, - Variant::Bech32, - ); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => Err(DaemonError::Implementation), - } - } - } - } - pub mod signature { - use crate::DaemonError; - use base64::engine::{general_purpose::STANDARD, Engine}; - use ring::digest::SHA256; - use secp256k1::{Message, Secp256k1}; - pub struct Signature {} - impl Signature { - pub fn verify( - secp: &Secp256k1, - pub_key: &str, - signature: &str, - blob: &str, - ) -> Result<(), DaemonError> { - let public = STANDARD.decode(pub_key)?; - let sig = STANDARD.decode(signature)?; - let pk = secp256k1::PublicKey::from_slice(public.as_slice())?; - let sha_result = ring::digest::digest(&SHA256, blob.as_bytes()); - let message: Message = Message::from_slice(&sha_result.as_ref()[0..32])?; - let secp_sig = secp256k1::ecdsa::Signature::from_compact( - sig.as_slice(), - )?; - secp.verify_ecdsa(&message, &secp_sig, &pk)?; - Ok(()) - } - } - } -} -pub mod live_mock { - //! Live mock is a mock that uses a live chain to query for data. - //! It can be used to do chain-backed unit-testing. It can't be used for state-changing operations. - use crate::queriers::Bank; - use crate::queriers::CosmWasm; - use crate::queriers::DaemonQuerier; - use crate::queriers::Staking; - use cosmwasm_std::Addr; - use cosmwasm_std::AllBalanceResponse; - use cosmwasm_std::BalanceResponse; - use cosmwasm_std::Delegation; - use cosmwasm_std::{AllDelegationsResponse, BondedDenomResponse}; - use cosmwasm_std::BankQuery; - use cosmwasm_std::Binary; - use cosmwasm_std::Empty; - use cosmwasm_std::StakingQuery; - use ibc_chain_registry::chain::ChainData; - use tokio::runtime::Runtime; - use tonic::transport::Channel; - use std::marker::PhantomData; - use std::str::FromStr; - use cosmwasm_std::testing::{MockApi, MockStorage}; - use cosmwasm_std::{ - from_slice, to_binary, Coin, ContractResult, OwnedDeps, Querier, QuerierResult, - QueryRequest, SystemError, SystemResult, Uint128, WasmQuery, - }; - use crate::channel::GrpcChannel; - fn to_cosmwasm_coin(c: cosmrs::proto::cosmos::base::v1beta1::Coin) -> Coin { - Coin { - amount: Uint128::from_str(&c.amount).unwrap(), - denom: c.denom, - } - } - const QUERIER_ERROR: &str = "Only Bank balances and Wasm (raw + smart) and Some staking queries are covered for now"; - /// mock_dependencies is a drop-in replacement for cosmwasm_std::testing::mock_dependencies - /// this uses our CustomQuerier. - pub fn mock_dependencies( - chain_info: ChainData, - ) -> OwnedDeps { - let custom_querier: WasmMockQuerier = WasmMockQuerier::new(chain_info); - OwnedDeps { - storage: MockStorage::default(), - api: MockApi::default(), - querier: custom_querier, - custom_query_type: PhantomData, - } - } - /// Querier struct that fetches queries on-chain directly - pub struct WasmMockQuerier { - channel: Channel, - runtime: Runtime, - } - impl Querier for WasmMockQuerier { - fn raw_query(&self, bin_request: &[u8]) -> QuerierResult { - let request: QueryRequest = match from_slice(bin_request) { - Ok(v) => v, - Err(e) => { - return SystemResult::Err(SystemError::InvalidRequest { - error: { - let res = ::alloc::fmt::format( - format_args!("Parsing query request: {0}", e), - ); - res - }, - request: bin_request.into(), - }); - } - }; - self.handle_query(&request) - } - } - impl WasmMockQuerier { - /// Function used to handle a query and customize the query behavior - /// This implements some queries by querying an actual node for the responses - pub fn handle_query(&self, request: &QueryRequest) -> QuerierResult { - match &request { - QueryRequest::Wasm(x) => { - let querier = CosmWasm::new(self.channel.clone()); - match x { - WasmQuery::Smart { contract_addr, msg } => { - let query_result: Result = self - .runtime - .block_on( - querier - .contract_state(contract_addr.to_string(), msg.to_vec()), - ) - .map(|query_result| query_result.into()); - SystemResult::Ok(ContractResult::from(query_result)) - } - WasmQuery::Raw { contract_addr, key } => { - let query_result = self - .runtime - .block_on( - querier - .contract_raw_state(contract_addr.to_string(), key.to_vec()), - ) - .map(|query_result| query_result.data.into()); - SystemResult::Ok(ContractResult::from(query_result)) - } - _ => { - SystemResult::Err(SystemError::InvalidRequest { - error: QUERIER_ERROR.to_string(), - request: to_binary(&request).unwrap(), - }) - } - } - } - QueryRequest::Bank(x) => { - let querier = Bank::new(self.channel.clone()); - match x { - BankQuery::Balance { address, denom } => { - let query_result = self - .runtime - .block_on(querier.balance(address, Some(denom.clone()))) - .map(|result| { - to_binary( - &BalanceResponse { - amount: Coin { - amount: Uint128::from_str(&result[0].amount).unwrap(), - denom: result[0].denom.clone(), - }, - }, - ) - .unwrap() - }); - SystemResult::Ok(ContractResult::from(query_result)) - } - BankQuery::AllBalances { address } => { - let query_result = self - .runtime - .block_on(querier.balance(address, None)) - .map(|result| AllBalanceResponse { - amount: result - .into_iter() - .map(|c| Coin { - amount: Uint128::from_str(&c.amount).unwrap(), - denom: c.denom, - }) - .collect(), - }) - .map(|query_result| to_binary(&query_result)) - .unwrap(); - SystemResult::Ok(ContractResult::from(query_result)) - } - _ => { - SystemResult::Err(SystemError::InvalidRequest { - error: QUERIER_ERROR.to_string(), - request: to_binary(&request).unwrap(), - }) - } - } - } - QueryRequest::Staking(x) => { - let querier = Staking::new(self.channel.clone()); - match x { - StakingQuery::BondedDenom {} => { - let query_result = self - .runtime - .block_on(querier.params()) - .map(|result| BondedDenomResponse { - denom: result.params.unwrap().bond_denom, - }) - .map(|query_result| to_binary(&query_result)) - .unwrap(); - SystemResult::Ok(ContractResult::from(query_result)) - } - StakingQuery::AllDelegations { delegator } => { - let query_result = self - .runtime - .block_on(querier.delegator_delegations(delegator, None)) - .map(|result| AllDelegationsResponse { - delegations: result - .delegation_responses - .into_iter() - .filter_map(|delegation| { - delegation - .delegation - .map(|d| Delegation { - delegator: Addr::unchecked(d.delegator_address), - validator: d.validator_address, - amount: to_cosmwasm_coin(delegation.balance.unwrap()), - }) - }) - .collect(), - }) - .map(|query_result| to_binary(&query_result)) - .unwrap(); - SystemResult::Ok(ContractResult::from(query_result)) - } - _ => ::core::panicking::panic("not yet implemented"), - } - } - _ => { - SystemResult::Err(SystemError::InvalidRequest { - error: QUERIER_ERROR.to_string(), - request: to_binary(&request).unwrap(), - }) - } - } - } - } - impl WasmMockQuerier { - /// Creates a querier from chain information - pub fn new(chain: ChainData) -> Self { - let rt = Runtime::new().unwrap(); - let channel = rt - .block_on(GrpcChannel::connect(&chain.apis.grpc, &chain.chain_id)) - .unwrap(); - WasmMockQuerier { - channel, - runtime: rt, - } - } - } -} -pub mod queriers { - //! # DaemonQuerier - //! - //! DaemonAsync queriers are gRPC query clients for the CosmosSDK modules. They can be used to query the different modules (Bank, Ibc, Authz, ...). - //! - //! ## Usage - //! - //! You will need to acquire a [gRPC channel](Channel) to a running CosmosSDK node to be able to use the queriers. - //! Here is an example of how to acquire one using the DaemonAsync builder. - //! - //! ```no_run - //! // require the querier you want to use, in this case Node - //! use cw_orch_daemon::{queriers::Node, DaemonAsync, networks, queriers::DaemonQuerier}; - //! # tokio_test::block_on(async { - //! // call the builder and configure it as you need - //! let daemon = DaemonAsync::builder() - //! .chain(networks::LOCAL_JUNO) - //! .build() - //! .await.unwrap(); - //! // now you can use the Node querier: - //! let node = Node::new(daemon.channel()); - //! let node_info = node.info(); - //! # }) - //! ``` - mod bank { - use crate::{cosmos_modules, error::DaemonError}; - use cosmrs::proto::cosmos::{ - base::{query::v1beta1::PageRequest, v1beta1::Coin}, - bank::v1beta1::QueryBalanceResponse, - }; - use tonic::transport::Channel; - use super::DaemonQuerier; - /// Queries for Cosmos Bank Module - pub struct Bank { - channel: Channel, - } - impl DaemonQuerier for Bank { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Bank { - /// Query the bank balance of a given address - /// If denom is None, returns all balances - pub async fn balance( - &self, - address: impl Into, - denom: Option, - ) -> Result, DaemonError> { - use cosmos_modules::bank::query_client::QueryClient; - use cosmos_modules::bank::QueryBalanceRequest; - match denom { - Some(denom) => { - let resp: QueryBalanceResponse = (); - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = cosmos_modules::bank::QueryBalanceRequest { - address: address.into(), - denom, - }; - let resp = client.balance(request).await?.into_inner(); - let coin = resp.balance.unwrap(); - Ok( - <[_]>::into_vec( - #[rustc_box] - ::alloc::boxed::Box::new([coin]), - ), - ) - } - None => { - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = cosmos_modules::bank::QueryAllBalancesRequest { - address: address.into(), - ..Default::default() - }; - let resp = client.all_balances(request).await?.into_inner(); - let coins = resp.balances; - Ok(coins.into_iter().collect()) - } - } - } - /// Query spendable balance for address - pub async fn spendable_balances( - &self, - address: impl Into, - ) -> Result, DaemonError> { - let spendable_balances: cosmos_modules::bank::QuerySpendableBalancesResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QuerySpendableBalancesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QuerySpendableBalancesRequest { - address: address.into(), - pagination: None, - }; - let response = client - .spendable_balances(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 70u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(spendable_balances.balances) - } - /// Query total supply in the bank - pub async fn total_supply(&self) -> Result, DaemonError> { - let total_supply: cosmos_modules::bank::QueryTotalSupplyResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QueryTotalSupplyRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryTotalSupplyRequest { - pagination: None, - }; - let response = client - .total_supply(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 84u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(total_supply.supply) - } - /// Query total supply in the bank for a denom - pub async fn supply_of( - &self, - denom: impl Into, - ) -> Result { - let supply_of: cosmos_modules::bank::QuerySupplyOfResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QuerySupplyOfRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QuerySupplyOfRequest { - denom: denom.into(), - }; - let response = client.supply_of(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 95u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(supply_of.amount.unwrap()) - } - /// Query params - pub async fn params( - &self, - ) -> Result { - let params: cosmos_modules::bank::QueryParamsResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QueryParamsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryParamsRequest {}; - let response = client.params(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 109u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(params.params.unwrap()) - } - /// Query denom metadata - pub async fn denom_metadata( - &self, - denom: impl Into, - ) -> Result { - let denom_metadata: cosmos_modules::bank::QueryDenomMetadataResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QueryDenomMetadataRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDenomMetadataRequest { - denom: denom.into(), - }; - let response = client - .denom_metadata(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 118u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(denom_metadata.metadata.unwrap()) - } - /// Query denoms metadata with pagination - /// - /// see [PageRequest] for pagination - pub async fn denoms_metadata( - &self, - pagination: Option, - ) -> Result, DaemonError> { - let denoms_metadata: cosmos_modules::bank::QueryDenomsMetadataResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QueryDenomsMetadataRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDenomsMetadataRequest { - pagination: pagination, - }; - let response = client - .denoms_metadata(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 136u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(denoms_metadata.metadatas) - } - } - } - mod cosmwasm { - use crate::{cosmos_modules, error::DaemonError}; - use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; - use tonic::transport::Channel; - use super::DaemonQuerier; - /// Querier for the CosmWasm SDK module - pub struct CosmWasm { - channel: Channel, - } - impl DaemonQuerier for CosmWasm { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl CosmWasm { - /// Query code_id by hash - pub async fn code_id_hash( - &self, - code_id: u64, - ) -> Result { - use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryCodeRequest { code_id }; - let resp = client.code(request).await?.into_inner(); - let contract_hash = resp.code_info.unwrap().data_hash; - let on_chain_hash = base16::encode_lower(&contract_hash); - Ok(on_chain_hash) - } - /// Query contract info - pub async fn contract_info( - &self, - address: impl Into, - ) -> Result { - use cosmos_modules::cosmwasm::{ - query_client::*, QueryContractInfoRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryContractInfoRequest { - address: address.into(), - }; - let resp = client.contract_info(request).await?.into_inner(); - let contract_info = resp.contract_info.unwrap(); - Ok(contract_info) - } - /// Query contract history - pub async fn contract_history( - &self, - address: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::cosmwasm::QueryContractHistoryResponse, - DaemonError, - > { - use cosmos_modules::cosmwasm::{ - query_client::*, QueryContractHistoryRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryContractHistoryRequest { - address: address.into(), - pagination, - }; - Ok(client.contract_history(request).await?.into_inner()) - } - /// Query contract state - pub async fn contract_state( - &self, - address: impl Into, - query_data: Vec, - ) -> Result, DaemonError> { - use cosmos_modules::cosmwasm::{ - query_client::*, QuerySmartContractStateRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QuerySmartContractStateRequest { - address: address.into(), - query_data, - }; - Ok(client.smart_contract_state(request).await?.into_inner().data) - } - /// Query all contract state - pub async fn all_contract_state( - &self, - address: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::cosmwasm::QueryAllContractStateResponse, - DaemonError, - > { - use cosmos_modules::cosmwasm::{ - query_client::*, QueryAllContractStateRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryAllContractStateRequest { - address: address.into(), - pagination, - }; - Ok(client.all_contract_state(request).await?.into_inner()) - } - /// Query code - pub async fn code( - &self, - code_id: u64, - ) -> Result { - use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryCodeRequest { code_id }; - Ok(client.code(request).await?.into_inner().code_info.unwrap()) - } - /// Query code bytes - pub async fn code_data(&self, code_id: u64) -> Result, DaemonError> { - use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryCodeRequest { code_id }; - Ok(client.code(request).await?.into_inner().data) - } - /// Query codes - pub async fn codes( - &self, - pagination: Option, - ) -> Result, DaemonError> { - use cosmos_modules::cosmwasm::{query_client::*, QueryCodesRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryCodesRequest { pagination }; - Ok(client.codes(request).await?.into_inner().code_infos) - } - /// Query pinned codes - pub async fn pinned_codes( - &self, - ) -> Result< - cosmos_modules::cosmwasm::QueryPinnedCodesResponse, - DaemonError, - > { - use cosmos_modules::cosmwasm::{query_client::*, QueryPinnedCodesRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryPinnedCodesRequest { - pagination: None, - }; - Ok(client.pinned_codes(request).await?.into_inner()) - } - /// Query contracts by code - pub async fn contract_by_codes( - &self, - code_id: u64, - ) -> Result< - cosmos_modules::cosmwasm::QueryContractsByCodeResponse, - DaemonError, - > { - use cosmos_modules::cosmwasm::{ - query_client::*, QueryContractsByCodeRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryContractsByCodeRequest { - code_id, - pagination: None, - }; - Ok(client.contracts_by_code(request).await?.into_inner()) - } - /// Query raw contract state - pub async fn contract_raw_state( - &self, - address: impl Into, - query_data: Vec, - ) -> Result< - cosmos_modules::cosmwasm::QueryRawContractStateResponse, - DaemonError, - > { - use cosmos_modules::cosmwasm::{ - query_client::*, QueryRawContractStateRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryRawContractStateRequest { - address: address.into(), - query_data, - }; - Ok(client.raw_contract_state(request).await?.into_inner()) - } - /// Query params - pub async fn params( - &self, - ) -> Result { - use cosmos_modules::cosmwasm::{query_client::*, QueryParamsRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - Ok(client.params(QueryParamsRequest {}).await?.into_inner()) - } - } - } - mod feegrant { - use crate::{cosmos_modules, error::DaemonError}; - use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; - use tonic::transport::Channel; - use super::DaemonQuerier; - /// Querier for the Cosmos Gov module - pub struct Feegrant { - channel: Channel, - } - impl DaemonQuerier for Feegrant { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Feegrant { - /// Query all allowances granted to the grantee address by a granter address - pub async fn allowance( - &self, - granter: impl Into, - grantee: impl Into, - ) -> Result { - let allowance: cosmos_modules::feegrant::QueryAllowanceResponse = { - use crate::cosmos_modules::feegrant::{ - query_client::QueryClient, QueryAllowanceRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryAllowanceRequest { - granter: granter.into(), - grantee: grantee.into(), - }; - let response = client.allowance(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::feegrant", - "cw_orch_daemon::queriers::feegrant", - "cw-orch-daemon/src/queriers/feegrant.rs", - ), - 25u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(allowance.allowance.unwrap()) - } - /// Query allowances for grantee address with a given pagination - /// - /// see [PageRequest] for pagination - pub async fn allowances( - &self, - grantee: impl Into, - pagination: Option, - ) -> Result, DaemonError> { - let allowances: cosmos_modules::feegrant::QueryAllowancesResponse = { - use crate::cosmos_modules::feegrant::{ - query_client::QueryClient, QueryAllowancesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryAllowancesRequest { - grantee: grantee.into(), - pagination: pagination, - }; - let response = client - .allowances(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::feegrant", - "cw_orch_daemon::queriers::feegrant", - "cw-orch-daemon/src/queriers/feegrant.rs", - ), - 45u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(allowances.allowances) - } - } - } - mod gov { - use crate::{cosmos_modules, error::DaemonError}; - use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; - use tonic::transport::Channel; - use super::DaemonQuerier; - /// Querier for the Cosmos Gov module - pub struct Gov { - channel: Channel, - } - impl DaemonQuerier for Gov { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Gov { - /// Query proposal details by proposal id - pub async fn proposal( - &self, - proposal_id: u64, - ) -> Result { - let proposal: cosmos_modules::gov::QueryProposalResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryProposalRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryProposalRequest { - proposal_id: proposal_id, - }; - let response = client.proposal(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 24u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(proposal.proposal.unwrap()) - } - /// Query proposals based on given status - /// - /// see [PageRequest] for pagination - pub async fn proposals( - &self, - proposal_status: GovProposalStatus, - voter: impl Into, - depositor: impl Into, - pagination: Option, - ) -> Result { - let proposals: cosmos_modules::gov::QueryProposalsResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryProposalsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryProposalsRequest { - proposal_status: proposal_status as i32, - voter: voter.into(), - depositor: depositor.into(), - pagination: pagination, - }; - let response = client.proposals(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 45u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(proposals) - } - /// Query voted information based on proposal_id for voter address - pub async fn vote( - &self, - proposal_id: u64, - voter: impl Into, - ) -> Result { - let vote: cosmos_modules::gov::QueryVoteResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryVoteRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryVoteRequest { - proposal_id: proposal_id, - voter: voter.into(), - }; - let response = client.vote(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 65u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(vote.vote.unwrap()) - } - /// Query votes of a given proposal - /// - /// see [PageRequest] for pagination - pub async fn votes( - &self, - proposal_id: impl Into, - pagination: Option, - ) -> Result { - let votes: cosmos_modules::gov::QueryVotesResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryVotesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryVotesRequest { - proposal_id: proposal_id.into(), - pagination: pagination, - }; - let response = client.votes(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 85u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(votes) - } - /// Query all parameters of the gov module - pub async fn params( - &self, - params_type: impl Into, - ) -> Result { - let params: cosmos_modules::gov::QueryParamsResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryParamsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryParamsRequest { - params_type: params_type.into(), - }; - let response = client.params(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 102u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(params) - } - /// Query deposit information using proposal_id and depositor address - pub async fn deposit( - &self, - proposal_id: u64, - depositor: impl Into, - ) -> Result { - let deposit: cosmos_modules::gov::QueryDepositResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryDepositRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDepositRequest { - proposal_id: proposal_id, - depositor: depositor.into(), - }; - let response = client.deposit(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 119u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(deposit.deposit.unwrap()) - } - /// Query deposits of a proposal - /// - /// see [PageRequest] for pagination - pub async fn deposits( - &self, - proposal_id: u64, - pagination: Option, - ) -> Result { - let deposits: cosmos_modules::gov::QueryDepositsResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryDepositsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDepositsRequest { - proposal_id: proposal_id, - pagination: pagination, - }; - let response = client.deposits(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 139u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(deposits) - } - /// TallyResult queries the tally of a proposal vote. - pub async fn tally_result( - &mut self, - proposal_id: u64, - ) -> Result { - let tally_result: cosmos_modules::gov::QueryTallyResultResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryTallyResultRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryTallyResultRequest { - proposal_id: proposal_id, - }; - let response = client - .tally_result(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 156u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(tally_result.tally.unwrap()) - } - } - /// Proposal status - #[allow(missing_docs)] - pub enum GovProposalStatus { - Unspecified = 0, - DepositPeriod = 1, - VotingPeriod = 2, - Passed = 3, - Rejected = 4, - Failed = 5, - } - } - mod ibc { - use super::DaemonQuerier; - use crate::{cosmos_modules, error::DaemonError}; - use cosmos_modules::ibc_channel; - use cosmrs::proto::ibc::{ - applications::transfer::v1::{DenomTrace, QueryDenomTraceResponse}, - core::{ - channel::v1::QueryPacketCommitmentResponse, - client::v1::{IdentifiedClientState, QueryClientStatesResponse}, - connection::v1::{IdentifiedConnection, State}, - }, - lightclients::tendermint::v1::ClientState, - }; - use prost::Message; - use tonic::transport::Channel; - /// Querier for the Cosmos IBC module - pub struct Ibc { - channel: Channel, - } - impl DaemonQuerier for Ibc { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Ibc { - /// Get the trace of a specific denom - pub async fn denom_trace( - &self, - hash: String, - ) -> Result { - let denom_trace: QueryDenomTraceResponse = { - use crate::cosmos_modules::ibc_transfer::{ - query_client::QueryClient, QueryDenomTraceRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDenomTraceRequest { - hash: hash, - }; - let response = client - .denom_trace(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 32u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(denom_trace.denom_trace.unwrap()) - } - /// Get all the IBC clients for this daemon - pub async fn clients( - &self, - ) -> Result, DaemonError> { - let ibc_clients: QueryClientStatesResponse = { - use crate::cosmos_modules::ibc_client::{ - query_client::QueryClient, QueryClientStatesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryClientStatesRequest { - pagination: None, - }; - let response = client - .client_states(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 45u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_clients.client_states) - } - /// Get the state of a specific IBC client - pub async fn client_state( - &self, - client_id: impl ToString, - ) -> Result< - cosmos_modules::ibc_client::QueryClientStateResponse, - DaemonError, - > { - let response: cosmos_modules::ibc_client::QueryClientStateResponse = { - use crate::cosmos_modules::ibc_client::{ - query_client::QueryClient, QueryClientStateRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryClientStateRequest { - client_id: client_id.to_string(), - }; - let response = client - .client_state(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 60u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(response) - } - /// Get the consensus state of a specific IBC client - 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 = { - use crate::cosmos_modules::ibc_client::{ - query_client::QueryClient, QueryConsensusStatesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryConsensusStatesRequest { - client_id: client_id, - pagination: None, - }; - let response = client - .consensus_states(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 77u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(response) - } - /// Get the consensus status of a specific IBC client - pub async fn client_status( - &self, - client_id: impl ToString, - ) -> Result< - cosmos_modules::ibc_client::QueryClientStatusResponse, - DaemonError, - > { - let response: cosmos_modules::ibc_client::QueryClientStatusResponse = { - use crate::cosmos_modules::ibc_client::{ - query_client::QueryClient, QueryClientStatusRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryClientStatusRequest { - client_id: client_id.to_string(), - }; - let response = client - .client_status(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 95u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(response) - } - /// Get the ibc client parameters - pub async fn client_params( - &self, - ) -> Result< - cosmos_modules::ibc_client::QueryClientParamsResponse, - DaemonError, - > { - let response: cosmos_modules::ibc_client::QueryClientParamsResponse = { - use crate::cosmos_modules::ibc_client::{ - query_client::QueryClient, QueryClientParamsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryClientParamsRequest {}; - let response = client - .client_params(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 111u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(response) - } - /// Query the IBC connections for a specific chain - pub async fn connections( - &self, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_connection::QueryConnectionsResponse; - let ibc_connections: QueryConnectionsResponse = { - use crate::cosmos_modules::ibc_connection::{ - query_client::QueryClient, QueryConnectionsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryConnectionsRequest { - pagination: None, - }; - let response = client - .connections(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 121u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_connections.connections) - } - /// Search for open connections with a specific chain. - pub async fn open_connections( - &self, - client_chain_id: impl ToString, - ) -> Result, 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) - } - /// Get all the connections for this client - pub async fn client_connections( - &self, - client_id: impl Into, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_connection::QueryClientConnectionsResponse; - let client_id = client_id.into(); - let ibc_client_connections: QueryClientConnectionsResponse = { - use crate::cosmos_modules::ibc_connection::{ - query_client::QueryClient, QueryClientConnectionsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryClientConnectionsRequest { - client_id: client_id.clone(), - }; - let response = client - .client_connections(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 163u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_client_connections.connection_paths) - } - /// Get the (tendermint) client state for a specific connection - pub async fn connection_client( - &self, - connection_id: impl Into, - ) -> Result { - use cosmos_modules::ibc_connection::QueryConnectionClientStateResponse; - let connection_id = connection_id.into(); - let ibc_connection_client: QueryConnectionClientStateResponse = { - use crate::cosmos_modules::ibc_connection::{ - query_client::QueryClient, QueryConnectionClientStateRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryConnectionClientStateRequest { - connection_id: connection_id.clone(), - }; - let response = client - .connection_client_state(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 183u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - let client_state = ibc_connection_client - .identified_client_state - .ok_or( - DaemonError::ibc_err({ - let res = ::alloc::fmt::format( - format_args!( - "error identifying client for connection {0}", - connection_id, - ), - ); - res - }), - )?; - let client_state = ClientState::decode( - client_state.client_state.unwrap().value.as_slice(), - ) - .map_err(|e| DaemonError::ibc_err({ - let res = ::alloc::fmt::format( - format_args!("error decoding client state: {0}", e), - ); - res - }))?; - Ok(client_state) - } - /// Get the channel for a specific port and channel id - pub async fn channel( - &self, - port_id: impl Into, - channel_id: impl Into, - ) -> Result { - use cosmos_modules::ibc_channel::QueryChannelResponse; - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_channel: QueryChannelResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryChannelRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryChannelRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }; - let response = client.channel(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 218u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - ibc_channel - .channel - .ok_or( - DaemonError::ibc_err({ - let res = ::alloc::fmt::format( - format_args!( - "error fetching channel {0} on port {1}", - channel_id, - port_id, - ), - ); - res - }), - ) - } - /// Get all the channels for a specific connection - pub async fn connection_channels( - &self, - connection_id: impl Into, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_channel::QueryConnectionChannelsResponse; - let connection_id = connection_id.into(); - let ibc_connection_channels: QueryConnectionChannelsResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryConnectionChannelsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryConnectionChannelsRequest { - connection: connection_id.clone(), - pagination: None, - }; - let response = client - .connection_channels(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 242u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_connection_channels.channels) - } - /// Get the client state for a specific channel and port - pub async fn channel_client_state( - &self, - port_id: impl Into, - channel_id: impl Into, - ) -> Result { - use cosmos_modules::ibc_channel::QueryChannelClientStateResponse; - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_channel_client_state: QueryChannelClientStateResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryChannelClientStateRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryChannelClientStateRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }; - let response = client - .channel_client_state(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 265u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - ibc_channel_client_state - .identified_client_state - .ok_or( - DaemonError::ibc_err({ - let res = ::alloc::fmt::format( - format_args!( - "error identifying client for channel {0} on port {1}", - channel_id, - port_id, - ), - ); - res - }), - ) - } - /// Get all the packet commitments for a specific channel and port - pub async fn packet_commitments( - &self, - port_id: impl Into, - channel_id: impl Into, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_channel::QueryPacketCommitmentsResponse; - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_commitments: QueryPacketCommitmentsResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryPacketCommitmentsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPacketCommitmentsRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - pagination: None, - }; - let response = client - .packet_commitments(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 297u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_commitments.commitments) - } - /// Get the packet commitment for a specific channel, port and sequence - pub async fn packet_commitment( - &self, - port_id: impl Into, - channel_id: impl Into, - sequence: u64, - ) -> Result { - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_commitment: QueryPacketCommitmentResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryPacketCommitmentRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPacketCommitmentRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - sequence: sequence, - }; - let response = client - .packet_commitment(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 320u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_commitment) - } - /// Returns if the packet is received on the connected chain. - pub async fn packet_receipt( - &self, - port_id: impl Into, - channel_id: impl Into, - sequence: u64, - ) -> Result { - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_receipt: ibc_channel::QueryPacketReceiptResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryPacketReceiptRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPacketReceiptRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - sequence: sequence, - }; - let response = client - .packet_receipt(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 345u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_receipt.received) - } - /// Get all the packet acknowledgements for a specific channel, port and commitment sequences - pub async fn packet_acknowledgements( - &self, - port_id: impl Into, - channel_id: impl Into, - packet_commitment_sequences: Vec, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_channel::QueryPacketAcknowledgementsResponse; - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_acknowledgements: QueryPacketAcknowledgementsResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryPacketAcknowledgementsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPacketAcknowledgementsRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - packet_commitment_sequences: packet_commitment_sequences, - pagination: None, - }; - let response = client - .packet_acknowledgements(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 372u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_acknowledgements.acknowledgements) - } - /// Get the packet acknowledgement for a specific channel, port and sequence - pub async fn packet_acknowledgement( - &self, - port_id: impl Into, - channel_id: impl Into, - sequence: u64, - ) -> Result, DaemonError> { - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_acknowledgement: ibc_channel::QueryPacketAcknowledgementResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryPacketAcknowledgementRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPacketAcknowledgementRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - sequence: sequence, - }; - let response = client - .packet_acknowledgement(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 396u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_acknowledgement.acknowledgement) - } - /// No acknowledgement exists on receiving chain for the given packet commitment sequence on sending chain. - /// Returns the packet sequences that have not yet been received. - pub async fn unreceived_packets( - &self, - port_id: impl Into, - channel_id: impl Into, - packet_commitment_sequences: Vec, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_channel::QueryUnreceivedPacketsResponse; - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_unreceived: QueryUnreceivedPacketsResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryUnreceivedPacketsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryUnreceivedPacketsRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - packet_commitment_sequences: packet_commitment_sequences, - }; - let response = client - .unreceived_packets(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 422u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_unreceived.sequences) - } - /// Returns the acknowledgement sequences that have not yet been received. - /// Given a list of acknowledgement sequences from counterparty, determine if an ack on the counterparty chain has been received on the executing chain. - /// Returns the list of acknowledgement sequences that have not yet been received. - pub async fn unreceived_acks( - &self, - port_id: impl Into, - channel_id: impl Into, - packet_ack_sequences: Vec, - ) -> Result, DaemonError> { - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_unreceived: ibc_channel::QueryUnreceivedAcksResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryUnreceivedAcksRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryUnreceivedAcksRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - packet_ack_sequences: packet_ack_sequences, - }; - let response = client - .unreceived_acks(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 447u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_unreceived.sequences) - } - /// Returns the acknowledgement sequences that have not yet been received. - /// Given a list of acknowledgement sequences from counterparty, determine if an ack on the counterparty chain has been received on the executing chain. - /// Returns the list of acknowledgement sequences that have not yet been received. - pub async fn next_sequence_receive( - &self, - port_id: impl Into, - channel_id: impl Into, - ) -> Result { - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let next_receive: ibc_channel::QueryNextSequenceReceiveResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryNextSequenceReceiveRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryNextSequenceReceiveRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }; - let response = client - .next_sequence_receive(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 471u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(next_receive.next_sequence_receive) - } - } - } - mod node { - use std::{cmp::min, time::Duration}; - use crate::{cosmos_modules, error::DaemonError, tx_resp::CosmTxResponse}; - use cosmrs::{ - proto::cosmos::{ - base::query::v1beta1::PageRequest, tx::v1beta1::SimulateResponse, - }, - tendermint::{Block, Time}, - }; - use tonic::transport::Channel; - use super::DaemonQuerier; - const MAX_TX_QUERY_RETRIES: usize = 50; - /// Querier for the Tendermint node. - /// Supports queries for block and tx information - pub struct Node { - channel: Channel, - } - impl DaemonQuerier for Node { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Node { - /// Returns node info - pub async fn info( - &self, - ) -> Result { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_node_info(cosmos_modules::tendermint::GetNodeInfoRequest { - }) - .await? - .into_inner(); - Ok(resp) - } - /// Queries node syncing - pub async fn syncing(&self) -> Result { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_syncing(cosmos_modules::tendermint::GetSyncingRequest { - }) - .await? - .into_inner(); - Ok(resp.syncing) - } - /// Returns latests block information - pub async fn latest_block(&self) -> Result { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_latest_block(cosmos_modules::tendermint::GetLatestBlockRequest { - }) - .await? - .into_inner(); - Ok(Block::try_from(resp.block.unwrap())?) - } - /// Returns block information fetched by height - pub async fn block_by_height( - &self, - height: u64, - ) -> Result { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_block_by_height(cosmos_modules::tendermint::GetBlockByHeightRequest { - height: height as i64, - }) - .await? - .into_inner(); - Ok(Block::try_from(resp.block.unwrap())?) - } - /// Return the average block time for the last 50 blocks or since inception - /// This is used to estimate the time when a tx will be included in a block - pub async fn average_block_speed( - &self, - multiplier: Option, - ) -> Result { - let mut latest_block = self.latest_block().await?; - let latest_block_time = latest_block.header.time; - let mut latest_block_height = latest_block.header.height.value(); - while latest_block_height <= 1 { - tokio::time::sleep(Duration::from_secs(1)).await; - latest_block = self.latest_block().await?; - latest_block_height = latest_block.header.height.value(); - } - let avg_period = min(latest_block_height - 1, 50); - let block_avg_period_ago = self - .block_by_height(latest_block_height - avg_period) - .await?; - let block_avg_period_ago_time = block_avg_period_ago.header.time; - let average_block_time = latest_block_time - .duration_since(block_avg_period_ago_time)?; - let average_block_time = average_block_time.as_secs() / avg_period; - let average_block_time = match multiplier { - Some(multiplier) => (average_block_time as f32 * multiplier) as u64, - None => average_block_time, - }; - Ok(std::cmp::max(average_block_time, 1)) - } - /// Returns latests validator set - pub async fn latest_validator_set( - &self, - pagination: Option, - ) -> Result< - cosmos_modules::tendermint::GetLatestValidatorSetResponse, - DaemonError, - > { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_latest_validator_set(cosmos_modules::tendermint::GetLatestValidatorSetRequest { - pagination, - }) - .await? - .into_inner(); - Ok(resp) - } - /// Returns latests validator set fetched by height - pub async fn validator_set_by_height( - &self, - height: i64, - pagination: Option, - ) -> Result< - cosmos_modules::tendermint::GetValidatorSetByHeightResponse, - DaemonError, - > { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_validator_set_by_height(cosmos_modules::tendermint::GetValidatorSetByHeightRequest { - height, - pagination, - }) - .await? - .into_inner(); - Ok(resp) - } - /// Returns current block height - pub async fn block_height(&self) -> Result { - let block = self.latest_block().await?; - Ok(block.header.height.value()) - } - /// Returns the block timestamp (since unix epoch) in nanos - pub async fn block_time(&self) -> Result { - let block = self.latest_block().await?; - Ok(block.header.time.duration_since(Time::unix_epoch())?.as_nanos()) - } - /// Simulate TX - pub async fn simulate_tx( - &self, - tx_bytes: Vec, - ) -> Result { - let mut client = cosmos_modules::tx::service_client::ServiceClient::new( - self.channel.clone(), - ); - #[allow(deprecated)] - let resp: SimulateResponse = client - .simulate(cosmos_modules::tx::SimulateRequest { - tx: None, - tx_bytes, - }) - .await? - .into_inner(); - let gas_used = resp.gas_info.unwrap().gas_used; - Ok(gas_used) - } - /// Returns all the block info - pub async fn block_info( - &self, - ) -> Result { - let block = self.latest_block().await?; - let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; - let time = cosmwasm_std::Timestamp::from_nanos( - since_epoch.as_nanos() as u64, - ); - Ok(cosmwasm_std::BlockInfo { - height: block.header.height.value(), - time, - chain_id: block.header.chain_id.to_string(), - }) - } - /// Find TX by hash - pub async fn find_tx( - &self, - hash: String, - ) -> Result { - self.find_tx_with_retries(hash, MAX_TX_QUERY_RETRIES).await - } - /// Find TX by hash with a given amount of retries - pub async fn find_tx_with_retries( - &self, - hash: String, - retries: usize, - ) -> Result { - let mut client = cosmos_modules::tx::service_client::ServiceClient::new( - self.channel.clone(), - ); - let request = cosmos_modules::tx::GetTxRequest { - hash: hash.clone(), - }; - let mut block_speed = self.average_block_speed(Some(0.7)).await?; - for _ in 0..retries { - match client.get_tx(request.clone()).await { - Ok(tx) => { - let resp = tx.into_inner().tx_response.unwrap(); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("TX found: {0:?}", resp), - lvl, - &( - "cw_orch_daemon::queriers::node", - "cw_orch_daemon::queriers::node", - "cw-orch-daemon/src/queriers/node.rs", - ), - 220u32, - ::log::__private_api::Option::None, - ); - } - }; - return Ok(resp.into()); - } - Err(err) => { - block_speed = (block_speed as f64 * 1.6) as u64; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("TX not found with error: {0:?}", err), - lvl, - &( - "cw_orch_daemon::queriers::node", - "cw_orch_daemon::queriers::node", - "cw-orch-daemon/src/queriers/node.rs", - ), - 226u32, - ::log::__private_api::Option::None, - ); - } - }; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("Waiting {0} seconds", block_speed), - lvl, - &( - "cw_orch_daemon::queriers::node", - "cw_orch_daemon::queriers::node", - "cw-orch-daemon/src/queriers/node.rs", - ), - 227u32, - ::log::__private_api::Option::None, - ); - } - }; - tokio::time::sleep(Duration::from_secs(block_speed)).await; - } - } - } - Err(DaemonError::TXNotFound(hash, retries)) - } - } - } - mod staking { - use crate::{cosmos_modules, error::DaemonError}; - use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; - use tonic::transport::Channel; - use super::DaemonQuerier; - /// Querier for the Cosmos Staking module - pub struct Staking { - channel: Channel, - } - impl DaemonQuerier for Staking { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Staking { - /// Queries validator info for given validator address - pub async fn validator( - &self, - validator_addr: impl Into, - ) -> Result { - let validator: cosmos_modules::staking::QueryValidatorResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryValidatorRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryValidatorRequest { - validator_addr: validator_addr.into(), - }; - let response = client.validator(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 24u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(validator.validator.unwrap()) - } - /// Queries all validators that match the given status - /// - /// see [StakingBondStatus] for available statuses - pub async fn validators( - &self, - status: StakingBondStatus, - ) -> Result, DaemonError> { - let validators: cosmos_modules::staking::QueryValidatorsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryValidatorsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryValidatorsRequest { - status: status.to_string(), - pagination: None, - }; - let response = client - .validators(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 42u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(validators.validators) - } - /// Query validator delegations info for given validator - /// - /// see [PageRequest] for pagination - pub async fn delegations( - &self, - validator_addr: impl Into, - pagination: Option, - ) -> Result, DaemonError> { - let validator_delegations: cosmos_modules::staking::QueryValidatorDelegationsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryValidatorDelegationsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryValidatorDelegationsRequest { - validator_addr: validator_addr.into(), - pagination: pagination, - }; - let response = client - .validator_delegations(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 62u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(validator_delegations.delegation_responses) - } - /// Query validator unbonding delegations of a validator - pub async fn unbonding_delegations( - &self, - validator_addr: impl Into, - ) -> Result, DaemonError> { - let validator_unbonding_delegations: cosmos_modules::staking::QueryValidatorUnbondingDelegationsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, - QueryValidatorUnbondingDelegationsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryValidatorUnbondingDelegationsRequest { - validator_addr: validator_addr.into(), - pagination: None, - }; - let response = client - .validator_unbonding_delegations(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 79u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(validator_unbonding_delegations.unbonding_responses) - } - /// Query delegation info for given validator for a delegator - pub async fn delegation( - &self, - validator_addr: impl Into, - delegator_addr: impl Into, - ) -> Result { - let delegation: cosmos_modules::staking::QueryDelegationResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryDelegationRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDelegationRequest { - validator_addr: validator_addr.into(), - delegator_addr: delegator_addr.into(), - }; - let response = client - .delegation(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 97u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(delegation.delegation_response.unwrap()) - } - /// Query unbonding delegation info for given validator delegator - pub async fn unbonding_delegation( - &self, - validator_addr: impl Into, - delegator_addr: impl Into, - ) -> Result { - let unbonding_delegation: cosmos_modules::staking::QueryUnbondingDelegationResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryUnbondingDelegationRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryUnbondingDelegationRequest { - validator_addr: validator_addr.into(), - delegator_addr: delegator_addr.into(), - }; - let response = client - .unbonding_delegation(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 115u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(unbonding_delegation.unbond.unwrap()) - } - /// Query all delegator delegations of a given delegator address - /// - /// see [PageRequest] for pagination - pub async fn delegator_delegations( - &self, - delegator_addr: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::staking::QueryDelegatorDelegationsResponse, - DaemonError, - > { - let delegator_delegations: cosmos_modules::staking::QueryDelegatorDelegationsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryDelegatorDelegationsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDelegatorDelegationsRequest { - delegator_addr: delegator_addr.into(), - pagination: pagination, - }; - let response = client - .delegator_delegations(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 135u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(delegator_delegations) - } - /// Queries all unbonding delegations of a given delegator address. - /// - /// see [PageRequest] for pagination - pub async fn delegator_unbonding_delegations( - &self, - delegator_addr: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::staking::QueryDelegatorUnbondingDelegationsResponse, - DaemonError, - > { - let delegator_unbonding_delegations: cosmos_modules::staking::QueryDelegatorUnbondingDelegationsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, - QueryDelegatorUnbondingDelegationsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDelegatorUnbondingDelegationsRequest { - delegator_addr: delegator_addr.into(), - pagination: pagination, - }; - let response = client - .delegator_unbonding_delegations(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 156u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(delegator_unbonding_delegations) - } - /// Query redelegations of a given address - /// - /// see [PageRequest] for pagination - pub async fn redelegations( - &self, - delegator_addr: impl Into, - src_validator_addr: impl Into, - dst_validator_addr: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::staking::QueryRedelegationsResponse, - DaemonError, - > { - let redelegations: cosmos_modules::staking::QueryRedelegationsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryRedelegationsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryRedelegationsRequest { - delegator_addr: delegator_addr.into(), - src_validator_addr: src_validator_addr.into(), - dst_validator_addr: dst_validator_addr.into(), - pagination: pagination, - }; - let response = client - .redelegations(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 178u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(redelegations) - } - /// Query delegator validators info for given delegator address. - pub async fn delegator_validator( - &self, - validator_addr: impl Into, - delegator_addr: impl Into, - ) -> Result< - cosmos_modules::staking::QueryDelegatorValidatorResponse, - DaemonError, - > { - let delegator_validator: cosmos_modules::staking::QueryDelegatorValidatorResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryDelegatorValidatorRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDelegatorValidatorRequest { - validator_addr: validator_addr.into(), - delegator_addr: delegator_addr.into(), - }; - let response = client - .delegator_validator(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 198u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(delegator_validator) - } - /// Query delegator validators info for given delegator address - /// - /// see [PageRequest] for pagination - pub async fn delegator_validators( - &self, - delegator_addr: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::staking::QueryDelegatorValidatorsResponse, - DaemonError, - > { - let delegator_validators: cosmos_modules::staking::QueryDelegatorValidatorsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryDelegatorValidatorsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDelegatorValidatorsRequest { - delegator_addr: delegator_addr.into(), - pagination: pagination, - }; - let response = client - .delegator_validators(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 218u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(delegator_validators) - } - /// Query historical info info for given height - pub async fn historical_info( - &self, - height: i64, - ) -> Result< - cosmos_modules::staking::QueryHistoricalInfoResponse, - DaemonError, - > { - let historical_info: cosmos_modules::staking::QueryHistoricalInfoResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryHistoricalInfoRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryHistoricalInfoRequest { - height: height, - }; - let response = client - .historical_info(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 236u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(historical_info) - } - /// Query the pool info - pub async fn pool( - &self, - ) -> Result { - let pool: cosmos_modules::staking::QueryPoolResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryPoolRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPoolRequest {}; - let response = client.pool(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 248u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(pool) - } - /// Query staking parameters - pub async fn params( - &self, - ) -> Result { - let params: cosmos_modules::staking::QueryParamsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryParamsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryParamsRequest {}; - let response = client.params(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 257u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(params) - } - } - /// Staking bond statuses - pub enum StakingBondStatus { - /// UNSPECIFIED defines an invalid validator status. - Unspecified = 0, - /// UNBONDED defines a validator that is not bonded. - Unbonded = 1, - /// UNBONDING defines a validator that is unbonding. - Unbonding = 2, - /// BONDED defines a validator that is bonded. - Bonded = 3, - } - impl ToString for StakingBondStatus { - /// Convert to string - fn to_string(&self) -> String { - match self { - StakingBondStatus::Unspecified => { - "BOND_STATUS_UNSPECIFIED".to_string() - } - StakingBondStatus::Unbonded => "BOND_STATUS_UNBONDED".to_string(), - StakingBondStatus::Unbonding => "BOND_STATUS_UNBONDING".to_string(), - StakingBondStatus::Bonded => "BOND_STATUS_BONDED".to_string(), - } - } - } - } - pub use bank::Bank; - pub use cosmwasm::CosmWasm; - pub use feegrant::Feegrant; - pub use ibc::Ibc; - pub use node::Node; - pub use gov::*; - pub use staking::*; - use tonic::transport::Channel; - /// Constructor for a querier over a given channel - pub trait DaemonQuerier { - /// Construct an new querier over a given channel - fn new(channel: Channel) -> Self; - } -} -mod traits { - use cw_orch_core::{ - contract::interface_traits::{CwOrchMigrate, CwOrchUpload}, - environment::TxResponse, - }; - use crate::{queriers::CosmWasm, Daemon, DaemonError}; - /// Helper methods for conditional uploading of a contract. - pub trait ConditionalUpload: CwOrchUpload { - /// Only upload the contract if it is not uploaded yet (checksum does not match) - fn upload_if_needed(&self) -> Result>, DaemonError> { - if self.latest_is_uploaded()? { - Ok(None) - } else { - Some(self.upload()).transpose().map_err(Into::into) - } - } - /// Returns whether the checksum of the WASM file matches the checksum of the latest uploaded code for this contract. - fn latest_is_uploaded(&self) -> Result { - let Some(latest_uploaded_code_id) = self.code_id().ok() else { - return Ok(false); - }; - let chain = self.get_chain(); - let on_chain_hash = chain - .rt_handle - .block_on( - chain - .query_client::() - .code_id_hash(latest_uploaded_code_id), - )?; - let local_hash = self.wasm().checksum()?; - Ok(local_hash == on_chain_hash) - } - /// Returns whether the contract is running the latest uploaded code for it - fn is_running_latest(&self) -> Result { - let Some(latest_uploaded_code_id) = self.code_id().ok() else { - return Ok(false); - }; - let chain = self.get_chain(); - let info = chain - .rt_handle - .block_on( - chain.query_client::().contract_info(self.address()?), - )?; - Ok(latest_uploaded_code_id == info.code_id) - } - } - impl ConditionalUpload for T - where - T: CwOrchUpload, - {} - /// Helper methods for conditional migration of a contract. - pub trait ConditionalMigrate: CwOrchMigrate + ConditionalUpload { - /// Only migrate the contract if it is not on the latest code-id yet - fn migrate_if_needed( - &self, - migrate_msg: &Self::MigrateMsg, - ) -> Result>, DaemonError> { - if self.is_running_latest()? { - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "{0} is already running the latest code", - self.id(), - ), - lvl, - &( - "cw_orch_daemon::traits", - "cw_orch_daemon::traits", - "cw-orch-daemon/src/traits.rs", - ), - 61u32, - ::log::__private_api::Option::None, - ); - } - }; - Ok(None) - } else { - Some(self.migrate(migrate_msg, self.code_id()?)) - .transpose() - .map_err(Into::into) - } - } - /// Uploads the contract if the local contract hash is different from the latest on-chain code hash. - /// Proceeds to migrates the contract if the contract is not running the latest code. - fn upload_and_migrate_if_needed( - &self, - migrate_msg: &Self::MigrateMsg, - ) -> Result>>, DaemonError> { - let mut txs = Vec::with_capacity(2); - if let Some(tx) = self.upload_if_needed()? { - txs.push(tx); - } - if let Some(tx) = self.migrate_if_needed(migrate_msg)? { - txs.push(tx); - } - if txs.is_empty() { Ok(None) } else { Ok(Some(txs)) } - } - } - impl ConditionalMigrate for T - where - T: CwOrchMigrate + CwOrchUpload, - {} -} -pub mod tx_builder { - use cosmrs::tx::{ModeInfo, SignMode}; - use cosmrs::{ - proto::cosmos::auth::v1beta1::BaseAccount, tendermint::chain::Id, - tx::{self, Body, Fee, Msg, Raw, SequenceNumber, SignDoc, SignerInfo}, - Any, Coin, - }; - use secp256k1::All; - use super::{sender::Sender, DaemonError}; - const GAS_BUFFER: f64 = 1.3; - const BUFFER_THRESHOLD: u64 = 200_000; - const SMALL_GAS_BUFFER: f64 = 1.4; - /// Struct used to build a raw transaction and broadcast it with a sender. - pub struct TxBuilder { - pub(crate) body: Body, - pub(crate) fee_amount: Option, - pub(crate) gas_limit: Option, - pub(crate) sequence: Option, - } - #[automatically_derived] - impl ::core::clone::Clone for TxBuilder { - #[inline] - fn clone(&self) -> TxBuilder { - TxBuilder { - body: ::core::clone::Clone::clone(&self.body), - fee_amount: ::core::clone::Clone::clone(&self.fee_amount), - gas_limit: ::core::clone::Clone::clone(&self.gas_limit), - sequence: ::core::clone::Clone::clone(&self.sequence), - } - } - } - #[automatically_derived] - impl ::core::fmt::Debug for TxBuilder { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field4_finish( - f, - "TxBuilder", - "body", - &self.body, - "fee_amount", - &self.fee_amount, - "gas_limit", - &self.gas_limit, - "sequence", - &&self.sequence, - ) - } - } - impl TxBuilder { - /// Create a new TxBuilder with a given body. - pub fn new(body: Body) -> Self { - Self { - body, - fee_amount: None, - gas_limit: None, - sequence: None, - } - } - /// Set a fixed fee amount for the tx - pub fn fee_amount(&mut self, fee_amount: u128) -> &mut Self { - self.fee_amount = Some(fee_amount); - self - } - /// Set a gas limit for the tx - pub fn gas_limit(&mut self, gas_limit: u64) -> &mut Self { - self.gas_limit = Some(gas_limit); - self - } - /// Set a sequence number for the tx - pub fn sequence(&mut self, sequence: u64) -> &mut Self { - self.sequence = Some(sequence); - self - } - /// Builds the body of the tx with a given memo and timeout. - pub fn build_body( - msgs: Vec, - memo: Option<&str>, - timeout: u64, - ) -> tx::Body { - let msgs = msgs - .into_iter() - .map(Msg::into_any) - .collect::, _>>() - .unwrap(); - tx::Body::new(msgs, memo.unwrap_or_default(), timeout as u32) - } - pub(crate) fn build_fee( - amount: impl Into, - denom: &str, - gas_limit: u64, - ) -> Fee { - let fee = Coin::new(amount.into(), denom).unwrap(); - Fee::from_amount_and_gas(fee, gas_limit) - } - /// Builds the raw tx with a given body and fee and signs it. - /// Sets the TxBuilder's gas limit to its simulated amount for later use. - pub async fn build(&mut self, wallet: &Sender) -> Result { - let BaseAccount { account_number, sequence, .. } = wallet - .base_account() - .await?; - let sequence = self.sequence.unwrap_or(sequence); - let (tx_fee, gas_limit) = if let (Some(fee), Some(gas_limit)) - = (self.fee_amount, self.gas_limit) { - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Using pre-defined fee and gas limits: {0}, {1}", - fee, - gas_limit, - ), - lvl, - &( - "cw_orch_daemon::tx_builder", - "cw_orch_daemon::tx_builder", - "cw-orch-daemon/src/tx_builder.rs", - ), - 91u32, - ::log::__private_api::Option::None, - ); - } - }; - (fee, gas_limit) - } else { - let sim_gas_used = wallet - .calculate_gas(&self.body, sequence, account_number) - .await?; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Simulated gas needed {0:?}", sim_gas_used), - lvl, - &( - "cw_orch_daemon::tx_builder", - "cw_orch_daemon::tx_builder", - "cw-orch-daemon/src/tx_builder.rs", - ), - 101u32, - ::log::__private_api::Option::None, - ); - } - }; - let gas_expected = if sim_gas_used < BUFFER_THRESHOLD { - sim_gas_used as f64 * SMALL_GAS_BUFFER - } else { - sim_gas_used as f64 * GAS_BUFFER - }; - let fee_amount = gas_expected - * (wallet - .daemon_state - .chain_data - .fees - .fee_tokens[0] - .fixed_min_gas_price + 0.00001); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Calculated fee needed: {0:?}", fee_amount), - lvl, - &( - "cw_orch_daemon::tx_builder", - "cw_orch_daemon::tx_builder", - "cw-orch-daemon/src/tx_builder.rs", - ), - 111u32, - ::log::__private_api::Option::None, - ); - } - }; - self.gas_limit = Some(gas_expected as u64); - (fee_amount as u128, gas_expected as u64) - }; - let fee = Self::build_fee( - tx_fee, - &wallet.daemon_state.chain_data.fees.fee_tokens[0].denom, - gas_limit, - ); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "submitting tx: \n fee: {0:?}\naccount_nr: {1:?}\nsequence: {2:?}", - fee, - account_number, - sequence, - ), - lvl, - &( - "cw_orch_daemon::tx_builder", - "cw_orch_daemon::tx_builder", - "cw-orch-daemon/src/tx_builder.rs", - ), - 125u32, - ::log::__private_api::Option::None, - ); - } - }; - let auth_info = SignerInfo { - public_key: wallet.private_key.get_signer_public_key(&wallet.secp), - mode_info: ModeInfo::single(SignMode::Direct), - sequence, - } - .auth_info(fee); - let sign_doc = SignDoc::new( - &self.body, - &auth_info, - &Id::try_from(wallet.daemon_state.chain_data.chain_id.to_string())?, - account_number, - )?; - wallet.sign(sign_doc).map_err(Into::into) - } - } -} -pub use self::{ - builder::*, channel::*, core::*, error::*, state::*, sync::*, traits::*, tx_resp::*, -}; -pub use cw_orch_networks::chain_info::*; -pub use cw_orch_networks::networks; -pub use sender::Wallet; -pub use tx_builder::TxBuilder; -pub(crate) mod cosmos_modules { - pub use cosmrs::proto::{ - cosmos::{ - auth::v1beta1 as auth, authz::v1beta1 as authz, bank::v1beta1 as bank, - base::{ - abci::v1beta1 as abci, tendermint::v1beta1 as tendermint, v1beta1 as base, - }, - crisis::v1beta1 as crisis, distribution::v1beta1 as distribution, - evidence::v1beta1 as evidence, feegrant::v1beta1 as feegrant, - gov::v1beta1 as gov, mint::v1beta1 as mint, params::v1beta1 as params, - slashing::v1beta1 as slashing, staking::v1beta1 as staking, - tx::v1beta1 as tx, vesting::v1beta1 as vesting, - }, - cosmwasm::wasm::v1 as cosmwasm, - ibc::{ - applications::transfer::v1 as ibc_transfer, - core::{ - channel::v1 as ibc_channel, client::v1 as ibc_client, - connection::v1 as ibc_connection, - }, - }, - tendermint::abci as tendermint_abci, - }; -} -/// Re-export trait and data required to fetch daemon data from chain-registry -pub use ibc_chain_registry::{ - chain::ChainData as ChainRegistryData, fetchable::Fetchable, -}; -#![feature(prelude_import)] -//! `Daemon` and `DaemonAsync` execution environments. -//! -//! The `Daemon` type is a synchronous wrapper around the `DaemonAsync` type and can be used as a contract execution environment. -#[prelude_import] -use std::prelude::rust_2021::*; -#[macro_use] -extern crate std; -pub mod builder { - use crate::{DaemonAsync, DaemonBuilder}; - use std::rc::Rc; - use ibc_chain_registry::chain::ChainData; - use super::{error::DaemonError, sender::Sender, state::DaemonState}; - /// The default deployment id if none is provided - pub const DEFAULT_DEPLOYMENT: &str = "default"; - /// Create [`DaemonAsync`] through [`DaemonAsyncBuilder`] - /// ## Example - /// ```no_run - /// # tokio_test::block_on(async { - /// use cw_orch_daemon::{DaemonAsyncBuilder, networks}; - /// let daemon = DaemonAsyncBuilder::default() - /// .chain(networks::LOCAL_JUNO) - /// .deployment_id("v0.1.0") - /// .build() - /// .await.unwrap(); - /// # }) - /// ``` - pub struct DaemonAsyncBuilder { - pub(crate) chain: Option, - pub(crate) deployment_id: Option, - /// Wallet mnemonic - pub(crate) mnemonic: Option, - } - #[automatically_derived] - impl ::core::clone::Clone for DaemonAsyncBuilder { - #[inline] - fn clone(&self) -> DaemonAsyncBuilder { - DaemonAsyncBuilder { - chain: ::core::clone::Clone::clone(&self.chain), - deployment_id: ::core::clone::Clone::clone(&self.deployment_id), - mnemonic: ::core::clone::Clone::clone(&self.mnemonic), - } - } - } - #[automatically_derived] - impl ::core::default::Default for DaemonAsyncBuilder { - #[inline] - fn default() -> DaemonAsyncBuilder { - DaemonAsyncBuilder { - chain: ::core::default::Default::default(), - deployment_id: ::core::default::Default::default(), - mnemonic: ::core::default::Default::default(), - } - } - } - impl DaemonAsyncBuilder { - /// Set the chain the daemon will connect to - pub fn chain(&mut self, chain: impl Into) -> &mut Self { - self.chain = Some(chain.into()); - self - } - /// Set the deployment id to use for the daemon interactions - /// Defaults to `default` - pub fn deployment_id(&mut self, deployment_id: impl Into) -> &mut Self { - self.deployment_id = Some(deployment_id.into()); - self - } - /// Set the mnemonic to use with this chain. - /// Defaults to env variable depending on the environment. - /// - /// Variables: LOCAL_MNEMONIC, TEST_MNEMONIC and MAIN_MNEMONIC - pub fn mnemonic(&mut self, mnemonic: impl ToString) -> &mut Self { - self.mnemonic = Some(mnemonic.to_string()); - self - } - /// Build a daemon - pub async fn build(&self) -> Result { - let chain = self - .chain - .clone() - .ok_or(DaemonError::BuilderMissing("chain information".into()))?; - let deployment_id = self - .deployment_id - .clone() - .unwrap_or(DEFAULT_DEPLOYMENT.to_string()); - let state = Rc::new(DaemonState::new(chain, deployment_id).await?); - let sender = if let Some(mnemonic) = &self.mnemonic { - Sender::from_mnemonic(&state, mnemonic)? - } else { - Sender::new(&state)? - }; - let daemon = DaemonAsync { - state, - sender: Rc::new(sender), - }; - Ok(daemon) - } - } - impl From for DaemonAsyncBuilder { - fn from(value: DaemonBuilder) -> Self { - DaemonAsyncBuilder { - chain: value.chain, - deployment_id: value.deployment_id, - mnemonic: value.mnemonic, - } - } - } -} -pub mod channel { - use cosmrs::proto::cosmos::base::tendermint::v1beta1::{ - service_client::ServiceClient, GetNodeInfoRequest, - }; - use ibc_chain_registry::chain::Grpc; - use ibc_relayer_types::core::ics24_host::identifier::ChainId; - use tonic::transport::{Channel, ClientTlsConfig}; - use super::error::DaemonError; - /// A helper for constructing a gRPC channel - pub struct GrpcChannel {} - impl GrpcChannel { - /// Connect to any of the provided gRPC endpoints - pub async fn connect( - grpc: &[Grpc], - chain_id: &ChainId, - ) -> Result { - let mut successful_connections = ::alloc::vec::Vec::new(); - for Grpc { address, .. } in grpc.iter() { - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Trying to connect to endpoint: {0}", address), - lvl, - &( - "cw_orch_daemon::channel", - "cw_orch_daemon::channel", - "cw-orch-daemon/src/channel.rs", - ), - 19u32, - ::log::__private_api::Option::None, - ); - } - }; - let endpoint = Channel::builder(address.clone().try_into().unwrap()); - let maybe_client = ServiceClient::connect(endpoint.clone()).await; - let mut client = if maybe_client.is_ok() { - maybe_client? - } else { - { - let lvl = ::log::Level::Warn; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Cannot connect to gRPC endpoint: {0}, {1:?}", - address, - maybe_client.unwrap_err(), - ), - lvl, - &( - "cw_orch_daemon::channel", - "cw_orch_daemon::channel", - "cw-orch-daemon/src/channel.rs", - ), - 31u32, - ::log::__private_api::Option::None, - ); - } - }; - if !(address.contains("https") || address.contains("443")) { - continue; - } - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Attempting to connect with TLS"), - lvl, - &( - "cw_orch_daemon::channel", - "cw_orch_daemon::channel", - "cw-orch-daemon/src/channel.rs", - ), - 43u32, - ::log::__private_api::Option::None, - ); - } - }; - let endpoint = endpoint.clone().tls_config(ClientTlsConfig::new())?; - let maybe_client = ServiceClient::connect(endpoint.clone()).await; - if maybe_client.is_err() { - { - let lvl = ::log::Level::Warn; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!( - "Cannot connect to gRPC endpoint: {0}, {1:?}", - address, - maybe_client.unwrap_err(), - ), - lvl, - &( - "cw_orch_daemon::channel", - "cw_orch_daemon::channel", - "cw-orch-daemon/src/channel.rs", - ), - 51u32, - ::log::__private_api::Option::None, - ); - } - }; - continue; - } - maybe_client? - }; - let node_info = client - .get_node_info(GetNodeInfoRequest {}) - .await? - .into_inner(); - if ChainId::is_epoch_format( - &node_info.default_node_info.as_ref().unwrap().network, - ) { - if node_info.default_node_info.as_ref().unwrap().network - != chain_id.as_str() - { - { - let lvl = ::log::Level::Error; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!( - "Network mismatch: connection:{0} != config:{1}", - node_info.default_node_info.as_ref().unwrap().network, - chain_id.as_str(), - ), - lvl, - &( - "cw_orch_daemon::channel", - "cw_orch_daemon::channel", - "cw-orch-daemon/src/channel.rs", - ), - 72u32, - ::log::__private_api::Option::None, - ); - } - }; - continue; - } - } - successful_connections.push(endpoint.connect().await?) - } - if successful_connections.is_empty() { - return Err(DaemonError::CannotConnectGRPC); - } - Ok(successful_connections.pop().unwrap()) - } - } -} -pub mod core { - use crate::{queriers::CosmWasm, DaemonState}; - use super::{ - builder::DaemonAsyncBuilder, cosmos_modules, error::DaemonError, - queriers::{DaemonQuerier, Node}, - sender::Wallet, tx_resp::CosmTxResponse, - }; - use cosmrs::{ - cosmwasm::{MsgExecuteContract, MsgInstantiateContract, MsgMigrateContract}, - tendermint::Time, AccountId, Denom, - }; - use cosmwasm_std::{Addr, Coin}; - use cw_orch_core::{ - contract::interface_traits::Uploadable, environment::{ChainState, IndexResponse}, - }; - use serde::{de::DeserializeOwned, Serialize}; - use serde_json::from_str; - use std::{ - fmt::Debug, rc::Rc, str::{from_utf8, FromStr}, - time::Duration, - }; - use tonic::transport::Channel; - /** - Represents a blockchain node. - It's constructed using [`DaemonAsyncBuilder`]. - - ## Usage - ```rust,no_run - # tokio_test::block_on(async { - use cw_orch_daemon::{DaemonAsync, networks}; - - let daemon: DaemonAsync = DaemonAsync::builder() - .chain(networks::JUNO_1) - .build() - .await.unwrap(); - # }) - ``` - ## Environment Execution - - The DaemonAsync implements async methods of [`TxHandler`](cw_orch_core::environment::TxHandler) which allows you to perform transactions on the chain. - - ## Querying - - Different Cosmos SDK modules can be queried through the daemon by calling the [`DaemonAsync::query_client`] method with a specific querier. - See [Querier](crate::queriers) for examples. -*/ - pub struct DaemonAsync { - /// Sender to send transactions to the chain - pub sender: Wallet, - /// State of the daemon - pub state: Rc, - } - #[automatically_derived] - impl ::core::clone::Clone for DaemonAsync { - #[inline] - fn clone(&self) -> DaemonAsync { - DaemonAsync { - sender: ::core::clone::Clone::clone(&self.sender), - state: ::core::clone::Clone::clone(&self.state), - } - } - } - impl DaemonAsync { - /// Get the daemon builder - pub fn builder() -> DaemonAsyncBuilder { - DaemonAsyncBuilder::default() - } - /// Perform a query with a given query client. - /// See [Querier](crate::queriers) for examples. - pub fn query_client(&self) -> Querier { - Querier::new(self.sender.channel()) - } - /// Get the channel configured for this DaemonAsync. - pub fn channel(&self) -> Channel { - self.state.grpc_channel.clone() - } - } - impl ChainState for DaemonAsync { - type Out = Rc; - fn state(&self) -> Self::Out { - self.state.clone() - } - } - impl DaemonAsync { - /// Get the sender address - pub fn sender(&self) -> Addr { - self.sender.address().unwrap() - } - /// Execute a message on a contract. - pub async fn execute( - &self, - exec_msg: &E, - coins: &[cosmwasm_std::Coin], - contract_address: &Addr, - ) -> Result { - let exec_msg: MsgExecuteContract = MsgExecuteContract { - sender: self.sender.pub_addr()?, - contract: AccountId::from_str(contract_address.as_str())?, - msg: serde_json::to_vec(&exec_msg)?, - funds: parse_cw_coins(coins)?, - }; - let result = self - .sender - .commit_tx( - <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([exec_msg])), - None, - ) - .await?; - Ok(result) - } - /// Instantiate a contract. - pub async fn instantiate( - &self, - code_id: u64, - init_msg: &I, - label: Option<&str>, - admin: Option<&Addr>, - coins: &[Coin], - ) -> Result { - let sender = &self.sender; - let init_msg = MsgInstantiateContract { - code_id, - label: Some(label.unwrap_or("instantiate_contract").to_string()), - admin: admin.map(|a| FromStr::from_str(a.as_str()).unwrap()), - sender: sender.pub_addr()?, - msg: serde_json::to_vec(&init_msg)?, - funds: parse_cw_coins(coins)?, - }; - let result = sender - .commit_tx( - <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([init_msg])), - None, - ) - .await?; - Ok(result) - } - /// Query a contract. - pub async fn query( - &self, - query_msg: &Q, - contract_address: &Addr, - ) -> Result { - let sender = &self.sender; - let mut client = cosmos_modules::cosmwasm::query_client::QueryClient::new( - sender.channel(), - ); - let resp = client - .smart_contract_state(cosmos_modules::cosmwasm::QuerySmartContractStateRequest { - address: contract_address.to_string(), - query_data: serde_json::to_vec(&query_msg)?, - }) - .await?; - Ok(from_str(from_utf8(&resp.into_inner().data).unwrap())?) - } - /// Migration a contract. - pub async fn migrate( - &self, - migrate_msg: &M, - new_code_id: u64, - contract_address: &Addr, - ) -> Result { - let exec_msg: MsgMigrateContract = MsgMigrateContract { - sender: self.sender.pub_addr()?, - contract: AccountId::from_str(contract_address.as_str())?, - msg: serde_json::to_vec(&migrate_msg)?, - code_id: new_code_id, - }; - let result = self - .sender - .commit_tx( - <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([exec_msg])), - None, - ) - .await?; - Ok(result) - } - /// Wait for a given amount of blocks. - pub async fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { - let mut last_height = self.query_client::().block_height().await?; - let end_height = last_height + amount; - let average_block_speed = self - .query_client::() - .average_block_speed(Some(0.9)) - .await?; - let wait_time = average_block_speed * amount; - tokio::time::sleep(Duration::from_secs(wait_time)).await; - while last_height < end_height { - tokio::time::sleep(Duration::from_secs(average_block_speed)).await; - last_height = self.query_client::().block_height().await?; - } - Ok(()) - } - /// Wait for a given amount of seconds. - pub async fn wait_seconds(&self, secs: u64) -> Result<(), DaemonError> { - tokio::time::sleep(Duration::from_secs(secs)).await; - Ok(()) - } - /// Wait for the next block. - pub async fn next_block(&self) -> Result<(), DaemonError> { - self.wait_blocks(1).await - } - /// Get the current block info. - pub async fn block_info(&self) -> Result { - let block = self.query_client::().latest_block().await?; - let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; - let time = cosmwasm_std::Timestamp::from_nanos( - since_epoch.as_nanos() as u64, - ); - Ok(cosmwasm_std::BlockInfo { - height: block.header.height.value(), - time, - chain_id: block.header.chain_id.to_string(), - }) - } - /// Upload a contract to the chain. - pub async fn upload( - &self, - uploadable: &impl Uploadable, - ) -> Result { - let sender = &self.sender; - let wasm_path = uploadable.wasm(); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Uploading file at {0:?}", wasm_path), - lvl, - &( - "cw_orch_daemon::core", - "cw_orch_daemon::core", - "cw-orch-daemon/src/core.rs", - ), - 233u32, - ::log::__private_api::Option::None, - ); - } - }; - let file_contents = std::fs::read(wasm_path.path())?; - let store_msg = cosmrs::cosmwasm::MsgStoreCode { - sender: sender.pub_addr()?, - wasm_byte_code: file_contents, - instantiate_permission: None, - }; - let result = sender - .commit_tx( - <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([store_msg])), - None, - ) - .await?; - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Uploaded: {0:?}", result.txhash), - lvl, - &( - "cw_orch_daemon::core", - "cw_orch_daemon::core", - "cw-orch-daemon/src/core.rs", - ), - 244u32, - ::log::__private_api::Option::None, - ); - } - }; - let code_id = result.uploaded_code_id().unwrap(); - let wasm = CosmWasm::new(self.channel()); - while wasm.code(code_id).await.is_err() { - self.next_block().await?; - } - Ok(result) - } - /// Set the sender to use with this DaemonAsync to be the given wallet - pub fn set_sender(&mut self, sender: &Wallet) { - self.sender = sender.clone(); - } - } - pub(crate) fn parse_cw_coins( - coins: &[cosmwasm_std::Coin], - ) -> Result, DaemonError> { - coins - .iter() - .map(|cosmwasm_std::Coin { amount, denom }| { - Ok(cosmrs::Coin { - amount: amount.u128(), - denom: Denom::from_str(denom)?, - }) - }) - .collect::, DaemonError>>() - } -} -pub mod error { - #![allow(missing_docs)] - use cw_orch_core::CwEnvError; - use thiserror::Error; - pub enum DaemonError { - #[error("Reqwest HTTP(s) Error")] - ReqwestError(#[from] ::reqwest::Error), - #[error("JSON Conversion Error")] - SerdeJson(#[from] ::serde_json::Error), - #[error(transparent)] - ParseIntError(#[from] std::num::ParseIntError), - #[error(transparent)] - IOErr(#[from] ::std::io::Error), - #[error(transparent)] - Secp256k1(#[from] ::secp256k1::Error), - #[error(transparent)] - VarError(#[from] ::std::env::VarError), - #[error(transparent)] - AnyError(#[from] ::anyhow::Error), - #[error(transparent)] - Status(#[from] ::tonic::Status), - #[error(transparent)] - TransportError(#[from] ::tonic::transport::Error), - #[error(transparent)] - TendermintError(#[from] ::cosmrs::tendermint::Error), - #[error(transparent)] - CwEnvError(#[from] ::cw_orch_core::CwEnvError), - #[error("Bech32 Decode Error")] - Bech32DecodeErr, - #[error( - "Bech32 Decode Error: Key Failed prefix {0} or length {1} Wanted:{2}/{3}" - )] - Bech32DecodeExpanded(String, usize, String, usize), - #[error("Mnemonic - Wrong length, it should be 24 words")] - WrongLength, - #[error("Mnemonic - Bad Phrase")] - Phrasing, - #[error("Mnemonic - Missing Phrase")] - MissingPhrase, - #[error("Bad Implementation. Missing Component")] - Implementation, - #[error("Unable to convert into public key `{key}`")] - Conversion { key: String, source: bitcoin::bech32::Error }, - #[error( - "Can not augment daemon deployment after usage in more than one contract." - )] - SharedDaemonState, - #[error(transparent)] - ErrReport(#[from] ::eyre::ErrReport), - #[error(transparent)] - GRpcDecodeError(#[from] ::prost::DecodeError), - #[error(transparent)] - ED25519(#[from] ::ed25519_dalek::ed25519::Error), - #[error(transparent)] - DecodeError(#[from] ::base64::DecodeError), - #[error(transparent)] - HexError(#[from] ::hex::FromHexError), - #[error(transparent)] - BitCoinBip32(#[from] ::bitcoin::bip32::Error), - #[error("83 length-missing SECP256K1 prefix")] - ConversionSECP256k1, - #[error("82 length-missing ED25519 prefix")] - ConversionED25519, - #[error("Expected Key length of 82 or 83 length was {0}")] - ConversionLength(usize), - #[error("Expected Key length of 40 length was {0}")] - ConversionLengthED25519Hex(usize), - #[error( - "Expected ED25519 key of length 32 with a BECH32 ED25519 prefix of 5 chars - Len {0} - Hex {1}" - )] - ConversionPrefixED25519(usize, String), - #[error("Can't call Transactions without some gas rules")] - NoGasOpts, - #[error("Can't parse `{parse}` into a coin")] - CoinParseErrV { parse: String }, - #[error("Can't parse `{0}` into a coin")] - CoinParseErr(String), - #[error("TX submit returned `{0}` - {1} '{2}'")] - TxResultError(usize, String, String), - #[error("No price found for Gas using denom {0}")] - GasPriceError(String), - #[error( - "Attempting to fetch validator set in parts, and failed Height mismatch {0} {1}" - )] - TendermintValidatorSet(u64, u64), - #[error("Transaction {0} not found after {1} attempts")] - TXNotFound(String, usize), - #[error("unknown API error")] - Unknown, - #[error("Generic Error {0}")] - StdErr(String), - #[error("calling contract with unimplemented action")] - NotImplemented, - #[error("new chain detected, fill out the scaffold at {0}")] - NewChain(String), - #[error("new network detected, fill out the scaffold at {0}")] - NewNetwork(String), - #[error("Can not connect to any grpc endpoint that was provided.")] - CannotConnectGRPC, - #[error("tx failed: {reason} with code {code}")] - TxFailed { code: usize, reason: String }, - #[error("The list of grpc endpoints is empty")] - GRPCListIsEmpty, - #[error("no wasm path provided for contract.")] - MissingWasmPath, - #[error("daemon builder missing {0}")] - BuilderMissing(String), - #[error("ibc error: {0}")] - IbcError(String), - #[error("insufficient fee, check gas price: {0}")] - InsufficientFee(String), - } - #[allow(unused_qualifications)] - impl std::error::Error for DaemonError { - fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> { - use thiserror::__private::AsDynError; - #[allow(deprecated)] - match self { - DaemonError::ReqwestError { 0: source, .. } => { - std::option::Option::Some(source.as_dyn_error()) - } - DaemonError::SerdeJson { 0: source, .. } => { - std::option::Option::Some(source.as_dyn_error()) - } - DaemonError::ParseIntError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::IOErr { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::Secp256k1 { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::VarError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::AnyError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::Status { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::TransportError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::TendermintError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::CwEnvError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::Bech32DecodeErr { .. } => std::option::Option::None, - DaemonError::Bech32DecodeExpanded { .. } => std::option::Option::None, - DaemonError::WrongLength { .. } => std::option::Option::None, - DaemonError::Phrasing { .. } => std::option::Option::None, - DaemonError::MissingPhrase { .. } => std::option::Option::None, - DaemonError::Implementation { .. } => std::option::Option::None, - DaemonError::Conversion { source: source, .. } => { - std::option::Option::Some(source.as_dyn_error()) - } - DaemonError::SharedDaemonState { .. } => std::option::Option::None, - DaemonError::ErrReport { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::GRpcDecodeError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::ED25519 { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::DecodeError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::HexError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::BitCoinBip32 { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::ConversionSECP256k1 { .. } => std::option::Option::None, - DaemonError::ConversionED25519 { .. } => std::option::Option::None, - DaemonError::ConversionLength { .. } => std::option::Option::None, - DaemonError::ConversionLengthED25519Hex { .. } => { - std::option::Option::None - } - DaemonError::ConversionPrefixED25519 { .. } => std::option::Option::None, - DaemonError::NoGasOpts { .. } => std::option::Option::None, - DaemonError::CoinParseErrV { .. } => std::option::Option::None, - DaemonError::CoinParseErr { .. } => std::option::Option::None, - DaemonError::TxResultError { .. } => std::option::Option::None, - DaemonError::GasPriceError { .. } => std::option::Option::None, - DaemonError::TendermintValidatorSet { .. } => std::option::Option::None, - DaemonError::TXNotFound { .. } => std::option::Option::None, - DaemonError::Unknown { .. } => std::option::Option::None, - DaemonError::StdErr { .. } => std::option::Option::None, - DaemonError::NotImplemented { .. } => std::option::Option::None, - DaemonError::NewChain { .. } => std::option::Option::None, - DaemonError::NewNetwork { .. } => std::option::Option::None, - DaemonError::CannotConnectGRPC { .. } => std::option::Option::None, - DaemonError::TxFailed { .. } => std::option::Option::None, - DaemonError::GRPCListIsEmpty { .. } => std::option::Option::None, - DaemonError::MissingWasmPath { .. } => std::option::Option::None, - DaemonError::BuilderMissing { .. } => std::option::Option::None, - DaemonError::IbcError { .. } => std::option::Option::None, - DaemonError::InsufficientFee { .. } => std::option::Option::None, - } - } - } - #[allow(unused_qualifications)] - impl std::fmt::Display for DaemonError { - fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - use thiserror::__private::AsDisplay as _; - #[allow(unused_variables, deprecated, clippy::used_underscore_binding)] - match self { - DaemonError::ReqwestError(_0) => { - __formatter.write_fmt(format_args!("Reqwest HTTP(s) Error")) - } - DaemonError::SerdeJson(_0) => { - __formatter.write_fmt(format_args!("JSON Conversion Error")) - } - DaemonError::ParseIntError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::IOErr(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::Secp256k1(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::VarError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::AnyError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::Status(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::TransportError(_0) => { - std::fmt::Display::fmt(_0, __formatter) - } - DaemonError::TendermintError(_0) => { - std::fmt::Display::fmt(_0, __formatter) - } - DaemonError::CwEnvError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::Bech32DecodeErr {} => { - __formatter.write_fmt(format_args!("Bech32 Decode Error")) - } - DaemonError::Bech32DecodeExpanded(_0, _1, _2, _3) => { - __formatter - .write_fmt( - format_args!( - "Bech32 Decode Error: Key Failed prefix {0} or length {1} Wanted:{2}/{3}", - _0.as_display(), - _1.as_display(), - _2.as_display(), - _3.as_display(), - ), - ) - } - DaemonError::WrongLength {} => { - __formatter - .write_fmt( - format_args!( - "Mnemonic - Wrong length, it should be 24 words", - ), - ) - } - DaemonError::Phrasing {} => { - __formatter.write_fmt(format_args!("Mnemonic - Bad Phrase")) - } - DaemonError::MissingPhrase {} => { - __formatter.write_fmt(format_args!("Mnemonic - Missing Phrase")) - } - DaemonError::Implementation {} => { - __formatter - .write_fmt(format_args!("Bad Implementation. Missing Component")) - } - DaemonError::Conversion { key, source } => { - __formatter - .write_fmt( - format_args!( - "Unable to convert into public key `{0}`", - key.as_display(), - ), - ) - } - DaemonError::SharedDaemonState {} => { - __formatter - .write_fmt( - format_args!( - "Can not augment daemon deployment after usage in more than one contract.", - ), - ) - } - DaemonError::ErrReport(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::GRpcDecodeError(_0) => { - std::fmt::Display::fmt(_0, __formatter) - } - DaemonError::ED25519(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::DecodeError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::HexError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::BitCoinBip32(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::ConversionSECP256k1 {} => { - __formatter - .write_fmt(format_args!("83 length-missing SECP256K1 prefix")) - } - DaemonError::ConversionED25519 {} => { - __formatter - .write_fmt(format_args!("82 length-missing ED25519 prefix")) - } - DaemonError::ConversionLength(_0) => { - __formatter - .write_fmt( - format_args!( - "Expected Key length of 82 or 83 length was {0}", - _0.as_display(), - ), - ) - } - DaemonError::ConversionLengthED25519Hex(_0) => { - __formatter - .write_fmt( - format_args!( - "Expected Key length of 40 length was {0}", - _0.as_display(), - ), - ) - } - DaemonError::ConversionPrefixED25519(_0, _1) => { - __formatter - .write_fmt( - format_args!( - "Expected ED25519 key of length 32 with a BECH32 ED25519 prefix of 5 chars - Len {0} - Hex {1}", - _0.as_display(), - _1.as_display(), - ), - ) - } - DaemonError::NoGasOpts {} => { - __formatter - .write_fmt( - format_args!( - "Can\'t call Transactions without some gas rules", - ), - ) - } - DaemonError::CoinParseErrV { parse } => { - __formatter - .write_fmt( - format_args!( - "Can\'t parse `{0}` into a coin", - parse.as_display(), - ), - ) - } - DaemonError::CoinParseErr(_0) => { - __formatter - .write_fmt( - format_args!( - "Can\'t parse `{0}` into a coin", - _0.as_display(), - ), - ) - } - DaemonError::TxResultError(_0, _1, _2) => { - __formatter - .write_fmt( - format_args!( - "TX submit returned `{0}` - {1} \'{2}\'", - _0.as_display(), - _1.as_display(), - _2.as_display(), - ), - ) - } - DaemonError::GasPriceError(_0) => { - __formatter - .write_fmt( - format_args!( - "No price found for Gas using denom {0}", - _0.as_display(), - ), - ) - } - DaemonError::TendermintValidatorSet(_0, _1) => { - __formatter - .write_fmt( - format_args!( - "Attempting to fetch validator set in parts, and failed Height mismatch {0} {1}", - _0.as_display(), - _1.as_display(), - ), - ) - } - DaemonError::TXNotFound(_0, _1) => { - __formatter - .write_fmt( - format_args!( - "Transaction {0} not found after {1} attempts", - _0.as_display(), - _1.as_display(), - ), - ) - } - DaemonError::Unknown {} => { - __formatter.write_fmt(format_args!("unknown API error")) - } - DaemonError::StdErr(_0) => { - __formatter - .write_fmt(format_args!("Generic Error {0}", _0.as_display())) - } - DaemonError::NotImplemented {} => { - __formatter - .write_fmt( - format_args!("calling contract with unimplemented action"), - ) - } - DaemonError::NewChain(_0) => { - __formatter - .write_fmt( - format_args!( - "new chain detected, fill out the scaffold at {0}", - _0.as_display(), - ), - ) - } - DaemonError::NewNetwork(_0) => { - __formatter - .write_fmt( - format_args!( - "new network detected, fill out the scaffold at {0}", - _0.as_display(), - ), - ) - } - DaemonError::CannotConnectGRPC {} => { - __formatter - .write_fmt( - format_args!( - "Can not connect to any grpc endpoint that was provided.", - ), - ) - } - DaemonError::TxFailed { code, reason } => { - __formatter - .write_fmt( - format_args!( - "tx failed: {0} with code {1}", - reason.as_display(), - code.as_display(), - ), - ) - } - DaemonError::GRPCListIsEmpty {} => { - __formatter - .write_fmt(format_args!("The list of grpc endpoints is empty")) - } - DaemonError::MissingWasmPath {} => { - __formatter - .write_fmt(format_args!("no wasm path provided for contract.")) - } - DaemonError::BuilderMissing(_0) => { - __formatter - .write_fmt( - format_args!("daemon builder missing {0}", _0.as_display()), - ) - } - DaemonError::IbcError(_0) => { - __formatter - .write_fmt(format_args!("ibc error: {0}", _0.as_display())) - } - DaemonError::InsufficientFee(_0) => { - __formatter - .write_fmt( - format_args!( - "insufficient fee, check gas price: {0}", - _0.as_display(), - ), - ) - } - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::reqwest::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::reqwest::Error) -> Self { - DaemonError::ReqwestError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::serde_json::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::serde_json::Error) -> Self { - DaemonError::SerdeJson { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From for DaemonError { - #[allow(deprecated)] - fn from(source: std::num::ParseIntError) -> Self { - DaemonError::ParseIntError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::std::io::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::std::io::Error) -> Self { - DaemonError::IOErr { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::secp256k1::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::secp256k1::Error) -> Self { - DaemonError::Secp256k1 { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::std::env::VarError> for DaemonError { - #[allow(deprecated)] - fn from(source: ::std::env::VarError) -> Self { - DaemonError::VarError { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::anyhow::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::anyhow::Error) -> Self { - DaemonError::AnyError { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::tonic::Status> for DaemonError { - #[allow(deprecated)] - fn from(source: ::tonic::Status) -> Self { - DaemonError::Status { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::tonic::transport::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::tonic::transport::Error) -> Self { - DaemonError::TransportError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::cosmrs::tendermint::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::cosmrs::tendermint::Error) -> Self { - DaemonError::TendermintError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::cw_orch_core::CwEnvError> for DaemonError { - #[allow(deprecated)] - fn from(source: ::cw_orch_core::CwEnvError) -> Self { - DaemonError::CwEnvError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::eyre::ErrReport> for DaemonError { - #[allow(deprecated)] - fn from(source: ::eyre::ErrReport) -> Self { - DaemonError::ErrReport { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::prost::DecodeError> for DaemonError { - #[allow(deprecated)] - fn from(source: ::prost::DecodeError) -> Self { - DaemonError::GRpcDecodeError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::ed25519_dalek::ed25519::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::ed25519_dalek::ed25519::Error) -> Self { - DaemonError::ED25519 { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::base64::DecodeError> for DaemonError { - #[allow(deprecated)] - fn from(source: ::base64::DecodeError) -> Self { - DaemonError::DecodeError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::hex::FromHexError> for DaemonError { - #[allow(deprecated)] - fn from(source: ::hex::FromHexError) -> Self { - DaemonError::HexError { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::bitcoin::bip32::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::bitcoin::bip32::Error) -> Self { - DaemonError::BitCoinBip32 { - 0: source, - } - } - } - #[automatically_derived] - impl ::core::fmt::Debug for DaemonError { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match self { - DaemonError::ReqwestError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ReqwestError", - &__self_0, - ) - } - DaemonError::SerdeJson(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "SerdeJson", - &__self_0, - ) - } - DaemonError::ParseIntError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ParseIntError", - &__self_0, - ) - } - DaemonError::IOErr(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "IOErr", - &__self_0, - ) - } - DaemonError::Secp256k1(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "Secp256k1", - &__self_0, - ) - } - DaemonError::VarError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "VarError", - &__self_0, - ) - } - DaemonError::AnyError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "AnyError", - &__self_0, - ) - } - DaemonError::Status(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "Status", - &__self_0, - ) - } - DaemonError::TransportError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "TransportError", - &__self_0, - ) - } - DaemonError::TendermintError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "TendermintError", - &__self_0, - ) - } - DaemonError::CwEnvError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "CwEnvError", - &__self_0, - ) - } - DaemonError::Bech32DecodeErr => { - ::core::fmt::Formatter::write_str(f, "Bech32DecodeErr") - } - DaemonError::Bech32DecodeExpanded( - __self_0, - __self_1, - __self_2, - __self_3, - ) => { - ::core::fmt::Formatter::debug_tuple_field4_finish( - f, - "Bech32DecodeExpanded", - __self_0, - __self_1, - __self_2, - &__self_3, - ) - } - DaemonError::WrongLength => { - ::core::fmt::Formatter::write_str(f, "WrongLength") - } - DaemonError::Phrasing => ::core::fmt::Formatter::write_str(f, "Phrasing"), - DaemonError::MissingPhrase => { - ::core::fmt::Formatter::write_str(f, "MissingPhrase") - } - DaemonError::Implementation => { - ::core::fmt::Formatter::write_str(f, "Implementation") - } - DaemonError::Conversion { key: __self_0, source: __self_1 } => { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "Conversion", - "key", - __self_0, - "source", - &__self_1, - ) - } - DaemonError::SharedDaemonState => { - ::core::fmt::Formatter::write_str(f, "SharedDaemonState") - } - DaemonError::ErrReport(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ErrReport", - &__self_0, - ) - } - DaemonError::GRpcDecodeError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "GRpcDecodeError", - &__self_0, - ) - } - DaemonError::ED25519(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ED25519", - &__self_0, - ) - } - DaemonError::DecodeError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "DecodeError", - &__self_0, - ) - } - DaemonError::HexError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "HexError", - &__self_0, - ) - } - DaemonError::BitCoinBip32(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "BitCoinBip32", - &__self_0, - ) - } - DaemonError::ConversionSECP256k1 => { - ::core::fmt::Formatter::write_str(f, "ConversionSECP256k1") - } - DaemonError::ConversionED25519 => { - ::core::fmt::Formatter::write_str(f, "ConversionED25519") - } - DaemonError::ConversionLength(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ConversionLength", - &__self_0, - ) - } - DaemonError::ConversionLengthED25519Hex(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ConversionLengthED25519Hex", - &__self_0, - ) - } - DaemonError::ConversionPrefixED25519(__self_0, __self_1) => { - ::core::fmt::Formatter::debug_tuple_field2_finish( - f, - "ConversionPrefixED25519", - __self_0, - &__self_1, - ) - } - DaemonError::NoGasOpts => { - ::core::fmt::Formatter::write_str(f, "NoGasOpts") - } - DaemonError::CoinParseErrV { parse: __self_0 } => { - ::core::fmt::Formatter::debug_struct_field1_finish( - f, - "CoinParseErrV", - "parse", - &__self_0, - ) - } - DaemonError::CoinParseErr(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "CoinParseErr", - &__self_0, - ) - } - DaemonError::TxResultError(__self_0, __self_1, __self_2) => { - ::core::fmt::Formatter::debug_tuple_field3_finish( - f, - "TxResultError", - __self_0, - __self_1, - &__self_2, - ) - } - DaemonError::GasPriceError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "GasPriceError", - &__self_0, - ) - } - DaemonError::TendermintValidatorSet(__self_0, __self_1) => { - ::core::fmt::Formatter::debug_tuple_field2_finish( - f, - "TendermintValidatorSet", - __self_0, - &__self_1, - ) - } - DaemonError::TXNotFound(__self_0, __self_1) => { - ::core::fmt::Formatter::debug_tuple_field2_finish( - f, - "TXNotFound", - __self_0, - &__self_1, - ) - } - DaemonError::Unknown => ::core::fmt::Formatter::write_str(f, "Unknown"), - DaemonError::StdErr(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "StdErr", - &__self_0, - ) - } - DaemonError::NotImplemented => { - ::core::fmt::Formatter::write_str(f, "NotImplemented") - } - DaemonError::NewChain(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "NewChain", - &__self_0, - ) - } - DaemonError::NewNetwork(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "NewNetwork", - &__self_0, - ) - } - DaemonError::CannotConnectGRPC => { - ::core::fmt::Formatter::write_str(f, "CannotConnectGRPC") - } - DaemonError::TxFailed { code: __self_0, reason: __self_1 } => { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "TxFailed", - "code", - __self_0, - "reason", - &__self_1, - ) - } - DaemonError::GRPCListIsEmpty => { - ::core::fmt::Formatter::write_str(f, "GRPCListIsEmpty") - } - DaemonError::MissingWasmPath => { - ::core::fmt::Formatter::write_str(f, "MissingWasmPath") - } - DaemonError::BuilderMissing(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "BuilderMissing", - &__self_0, - ) - } - DaemonError::IbcError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "IbcError", - &__self_0, - ) - } - DaemonError::InsufficientFee(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "InsufficientFee", - &__self_0, - ) - } - } - } - } - impl DaemonError { - pub fn ibc_err(msg: impl ToString) -> Self { - Self::IbcError(msg.to_string()) - } - } - impl From for CwEnvError { - fn from(val: DaemonError) -> Self { - CwEnvError::AnyError(val.into()) - } - } -} -pub(crate) mod json_file { - use serde_json::{from_reader, json, Value}; - use std::fs::{File, OpenOptions}; - pub fn write( - filename: &String, - chain_id: &String, - network_id: &String, - deploy_id: &String, - ) { - let file = OpenOptions::new() - .create(true) - .read(true) - .write(true) - .truncate(false) - .open(filename) - .unwrap(); - let mut json: Value = if file.metadata().unwrap().len().eq(&0) { - ::serde_json::Value::Object(::serde_json::Map::new()) - } else { - from_reader(file).unwrap() - }; - if json.get(network_id).is_none() { - json[network_id] = ::serde_json::Value::Object(::serde_json::Map::new()); - } - if json[network_id].get(chain_id).is_none() { - json[network_id][chain_id] = ::serde_json::Value::Object({ - let mut object = ::serde_json::Map::new(); - let _ = object - .insert( - (deploy_id).into(), - ::serde_json::Value::Object(::serde_json::Map::new()), - ); - let _ = object - .insert( - ("code_ids").into(), - ::serde_json::Value::Object(::serde_json::Map::new()), - ); - object - }); - } - serde_json::to_writer_pretty(File::create(filename).unwrap(), &json).unwrap(); - } - pub fn read(filename: &String) -> Value { - let file = File::open(filename) - .unwrap_or_else(|_| { - ::core::panicking::panic_fmt( - format_args!("File should be present at {0}", filename), - ); - }); - let json: serde_json::Value = from_reader(file).unwrap(); - json - } -} -/// Proto types for different blockchains -pub mod proto { - pub mod injective { - #![allow(missing_docs)] - use crate::DaemonError; - use cosmrs::tx::SignDoc; - use cosmrs::{proto::traits::TypeUrl, tx::Raw}; - pub const ETHEREUM_COIN_TYPE: u32 = 60; - pub struct InjectiveEthAccount { - #[prost(message, optional, tag = "1")] - pub base_account: ::core::option::Option< - super::super::cosmos_modules::auth::BaseAccount, - >, - #[prost(bytes, tag = "2")] - pub code_hash: Vec, - } - #[automatically_derived] - impl ::core::clone::Clone for InjectiveEthAccount { - #[inline] - fn clone(&self) -> InjectiveEthAccount { - InjectiveEthAccount { - base_account: ::core::clone::Clone::clone(&self.base_account), - code_hash: ::core::clone::Clone::clone(&self.code_hash), - } - } - } - #[automatically_derived] - impl ::core::marker::StructuralPartialEq for InjectiveEthAccount {} - #[automatically_derived] - impl ::core::cmp::PartialEq for InjectiveEthAccount { - #[inline] - fn eq(&self, other: &InjectiveEthAccount) -> bool { - self.base_account == other.base_account - && self.code_hash == other.code_hash - } - } - impl ::prost::Message for InjectiveEthAccount { - #[allow(unused_variables)] - fn encode_raw(&self, buf: &mut B) - where - B: ::prost::bytes::BufMut, - { - if let Some(ref msg) = self.base_account { - ::prost::encoding::message::encode(1u32, msg, buf); - } - if self.code_hash != b"" as &[u8] { - ::prost::encoding::bytes::encode(2u32, &self.code_hash, buf); - } - } - #[allow(unused_variables)] - fn merge_field( - &mut self, - tag: u32, - wire_type: ::prost::encoding::WireType, - buf: &mut B, - ctx: ::prost::encoding::DecodeContext, - ) -> ::core::result::Result<(), ::prost::DecodeError> - where - B: ::prost::bytes::Buf, - { - const STRUCT_NAME: &'static str = "InjectiveEthAccount"; - match tag { - 1u32 => { - let mut value = &mut self.base_account; - ::prost::encoding::message::merge( - wire_type, - value.get_or_insert_with(::core::default::Default::default), - buf, - ctx, - ) - .map_err(|mut error| { - error.push(STRUCT_NAME, "base_account"); - error - }) - } - 2u32 => { - let mut value = &mut self.code_hash; - ::prost::encoding::bytes::merge(wire_type, value, buf, ctx) - .map_err(|mut error| { - error.push(STRUCT_NAME, "code_hash"); - error - }) - } - _ => ::prost::encoding::skip_field(wire_type, tag, buf, ctx), - } - } - #[inline] - fn encoded_len(&self) -> usize { - 0 - + self - .base_account - .as_ref() - .map_or( - 0, - |msg| ::prost::encoding::message::encoded_len(1u32, msg), - ) - + if self.code_hash != b"" as &[u8] { - ::prost::encoding::bytes::encoded_len(2u32, &self.code_hash) - } else { - 0 - } - } - fn clear(&mut self) { - self.base_account = ::core::option::Option::None; - self.code_hash.clear(); - } - } - impl ::core::default::Default for InjectiveEthAccount { - fn default() -> Self { - InjectiveEthAccount { - base_account: ::core::default::Default::default(), - code_hash: ::core::default::Default::default(), - } - } - } - impl ::core::fmt::Debug for InjectiveEthAccount { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - let mut builder = f.debug_struct("InjectiveEthAccount"); - let builder = { - let wrapper = &self.base_account; - builder.field("base_account", &wrapper) - }; - let builder = { - let wrapper = { - fn ScalarWrapper(v: T) -> T { - v - } - ScalarWrapper(&self.code_hash) - }; - builder.field("code_hash", &wrapper) - }; - builder.finish() - } - } - pub struct InjectivePubKey { - #[prost(bytes, tag = 1)] - pub key: Vec, - } - #[automatically_derived] - impl ::core::clone::Clone for InjectivePubKey { - #[inline] - fn clone(&self) -> InjectivePubKey { - InjectivePubKey { - key: ::core::clone::Clone::clone(&self.key), - } - } - } - #[automatically_derived] - impl ::core::marker::StructuralPartialEq for InjectivePubKey {} - #[automatically_derived] - impl ::core::cmp::PartialEq for InjectivePubKey { - #[inline] - fn eq(&self, other: &InjectivePubKey) -> bool { - self.key == other.key - } - } - impl ::prost::Message for InjectivePubKey { - #[allow(unused_variables)] - fn encode_raw(&self, buf: &mut B) - where - B: ::prost::bytes::BufMut, - { - if self.key != b"" as &[u8] { - ::prost::encoding::bytes::encode(1u32, &self.key, buf); - } - } - #[allow(unused_variables)] - fn merge_field( - &mut self, - tag: u32, - wire_type: ::prost::encoding::WireType, - buf: &mut B, - ctx: ::prost::encoding::DecodeContext, - ) -> ::core::result::Result<(), ::prost::DecodeError> - where - B: ::prost::bytes::Buf, - { - const STRUCT_NAME: &'static str = "InjectivePubKey"; - match tag { - 1u32 => { - let mut value = &mut self.key; - ::prost::encoding::bytes::merge(wire_type, value, buf, ctx) - .map_err(|mut error| { - error.push(STRUCT_NAME, "key"); - error - }) - } - _ => ::prost::encoding::skip_field(wire_type, tag, buf, ctx), - } - } - #[inline] - fn encoded_len(&self) -> usize { - 0 - + if self.key != b"" as &[u8] { - ::prost::encoding::bytes::encoded_len(1u32, &self.key) - } else { - 0 - } - } - fn clear(&mut self) { - self.key.clear(); - } - } - impl ::core::default::Default for InjectivePubKey { - fn default() -> Self { - InjectivePubKey { - key: ::core::default::Default::default(), - } - } - } - impl ::core::fmt::Debug for InjectivePubKey { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - let mut builder = f.debug_struct("InjectivePubKey"); - let builder = { - let wrapper = { - fn ScalarWrapper(v: T) -> T { - v - } - ScalarWrapper(&self.key) - }; - builder.field("key", &wrapper) - }; - builder.finish() - } - } - impl TypeUrl for InjectivePubKey { - const TYPE_URL: &'static str = "/injective.crypto.v1beta1.ethsecp256k1.PubKey"; - } - pub trait InjectiveSigner { - fn sign_injective(&self, sign_doc: SignDoc) -> Result; - } - } -} -pub mod sender { - use crate::{networks::ChainKind, proto::injective::ETHEREUM_COIN_TYPE}; - use super::{ - cosmos_modules::{self, auth::BaseAccount}, - error::DaemonError, queriers::{DaemonQuerier, Node}, - state::DaemonState, tx_builder::TxBuilder, tx_resp::CosmTxResponse, - }; - use crate::proto::injective::InjectiveEthAccount; - use crate::{core::parse_cw_coins, keys::private::PrivateKey}; - use cosmrs::{ - bank::MsgSend, crypto::secp256k1::SigningKey, proto::traits::Message, - tendermint::chain::Id, - tx::{self, ModeInfo, Msg, Raw, SignDoc, SignMode, SignerInfo}, - AccountId, - }; - use cosmwasm_std::Addr; - use secp256k1::{All, Context, Secp256k1, Signing}; - use std::{convert::TryFrom, env, rc::Rc, str::FromStr}; - use cosmos_modules::vesting::PeriodicVestingAccount; - use tonic::transport::Channel; - /// A wallet is a sender of transactions, can be safely cloned and shared within the same thread. - pub type Wallet = Rc>; - /// Signer of the transactions and helper for address derivation - /// This is the main interface for simulating and signing transactions - pub struct Sender { - pub private_key: PrivateKey, - pub secp: Secp256k1, - pub(crate) daemon_state: Rc, - } - impl Sender { - pub fn new(daemon_state: &Rc) -> Result, DaemonError> { - let kind = ChainKind::from(daemon_state.chain_data.network_type.clone()); - let mnemonic = env::var(kind.mnemonic_name()) - .unwrap_or_else(|_| { - { - ::core::panicking::panic_fmt( - format_args!( - "Wallet mnemonic environment variable {0} not set.", - kind.mnemonic_name(), - ), - ); - } - }); - Self::from_mnemonic(daemon_state, &mnemonic) - } - /// Construct a new Sender from a mnemonic - pub fn from_mnemonic( - daemon_state: &Rc, - mnemonic: &str, - ) -> Result, DaemonError> { - let secp = Secp256k1::new(); - let p_key: PrivateKey = PrivateKey::from_words( - &secp, - mnemonic, - 0, - 0, - daemon_state.chain_data.slip44, - )?; - let sender = Sender { - daemon_state: daemon_state.clone(), - private_key: p_key, - secp, - }; - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Interacting with {0} using address: {1}", - daemon_state.chain_data.chain_id, - sender.pub_addr_str()?, - ), - lvl, - &( - "cw_orch_daemon::sender", - "cw_orch_daemon::sender", - "cw-orch-daemon/src/sender.rs", - ), - 71u32, - ::log::__private_api::Option::None, - ); - } - }; - Ok(sender) - } - fn cosmos_private_key(&self) -> SigningKey { - SigningKey::from_slice(&self.private_key.raw_key()).unwrap() - } - pub fn channel(&self) -> Channel { - self.daemon_state.grpc_channel.clone() - } - pub fn pub_addr(&self) -> Result { - Ok( - AccountId::new( - &self.daemon_state.chain_data.bech32_prefix, - &self.private_key.public_key(&self.secp).raw_address.unwrap(), - )?, - ) - } - pub fn address(&self) -> Result { - Ok(Addr::unchecked(self.pub_addr_str()?)) - } - pub fn pub_addr_str(&self) -> Result { - Ok(self.pub_addr()?.to_string()) - } - pub async fn bank_send( - &self, - recipient: &str, - coins: Vec, - ) -> Result { - let msg_send = MsgSend { - from_address: self.pub_addr()?, - to_address: AccountId::from_str(recipient)?, - amount: parse_cw_coins(&coins)?, - }; - self.commit_tx( - <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([msg_send])), - Some("sending tokens"), - ) - .await - } - pub async fn calculate_gas( - &self, - tx_body: &tx::Body, - sequence: u64, - account_number: u64, - ) -> Result { - let fee = TxBuilder::build_fee( - 0u8, - &self.daemon_state.chain_data.fees.fee_tokens[0].denom, - 0, - ); - let auth_info = SignerInfo { - public_key: self.private_key.get_signer_public_key(&self.secp), - mode_info: ModeInfo::single(SignMode::Direct), - sequence, - } - .auth_info(fee); - let sign_doc = SignDoc::new( - tx_body, - &auth_info, - &Id::try_from(self.daemon_state.chain_data.chain_id.to_string())?, - account_number, - )?; - let tx_raw = self.sign(sign_doc)?; - Node::new(self.channel()).simulate_tx(tx_raw.to_bytes()?).await - } - pub async fn commit_tx( - &self, - msgs: Vec, - memo: Option<&str>, - ) -> Result { - let timeout_height = Node::new(self.channel()).block_height().await? + 10u64; - let tx_body = TxBuilder::build_body(msgs, memo, timeout_height); - let mut tx_builder = TxBuilder::new(tx_body); - let tx = tx_builder.build(self).await?; - let mut tx_response = self.broadcast_tx(tx).await?; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("tx broadcast response: {0:?}", tx_response), - lvl, - &( - "cw_orch_daemon::sender", - "cw_orch_daemon::sender", - "cw-orch-daemon/src/sender.rs", - ), - 166u32, - ::log::__private_api::Option::None, - ); - } - }; - if has_insufficient_fee(&tx_response.raw_log) { - let suggested_fee = parse_suggested_fee(&tx_response.raw_log); - let Some(new_fee) = suggested_fee else { - return Err(DaemonError::InsufficientFee(tx_response.raw_log)); - }; - tx_builder.fee_amount(new_fee); - let tx = tx_builder.build(self).await?; - tx_response = self.broadcast_tx(tx).await?; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("tx broadcast response: {0:?}", tx_response), - lvl, - &( - "cw_orch_daemon::sender", - "cw_orch_daemon::sender", - "cw-orch-daemon/src/sender.rs", - ), - 181u32, - ::log::__private_api::Option::None, - ); - } - }; - } - let resp = Node::new(self.channel()).find_tx(tx_response.txhash).await?; - if resp.code == 0 { - Ok(resp) - } else { - Err(DaemonError::TxFailed { - code: resp.code, - reason: resp.raw_log, - }) - } - } - pub fn sign(&self, sign_doc: SignDoc) -> Result { - let tx_raw = if self.private_key.coin_type == ETHEREUM_COIN_TYPE { - { - ::core::panicking::panic_fmt( - format_args!( - "Coin Type {0} not supported without eth feature", - ETHEREUM_COIN_TYPE, - ), - ); - }; - } else { - sign_doc.sign(&self.cosmos_private_key())? - }; - Ok(tx_raw) - } - pub async fn base_account(&self) -> Result { - let addr = self.pub_addr().unwrap().to_string(); - let mut client = cosmos_modules::auth::query_client::QueryClient::new( - self.channel(), - ); - let resp = client - .account(cosmos_modules::auth::QueryAccountRequest { - address: addr, - }) - .await? - .into_inner(); - let account = resp.account.unwrap().value; - let acc = if let Ok(acc) = BaseAccount::decode(account.as_ref()) { - acc - } else if let Ok(acc) = PeriodicVestingAccount::decode(account.as_ref()) { - acc.base_vesting_account.unwrap().base_account.unwrap() - } else if let Ok(acc) = InjectiveEthAccount::decode(account.as_ref()) { - acc.base_account.unwrap() - } else { - return Err( - DaemonError::StdErr( - "Unknown account type returned from QueryAccountRequest".into(), - ), - ); - }; - Ok(acc) - } - async fn broadcast_tx( - &self, - tx: Raw, - ) -> Result< - cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse, - DaemonError, - > { - let mut client = cosmos_modules::tx::service_client::ServiceClient::new( - self.channel(), - ); - let commit = client - .broadcast_tx(cosmos_modules::tx::BroadcastTxRequest { - tx_bytes: tx.to_bytes()?, - mode: cosmos_modules::tx::BroadcastMode::Sync.into(), - }) - .await?; - let commit = commit.into_inner().tx_response.unwrap(); - Ok(commit) - } - } - fn has_insufficient_fee(raw_log: &str) -> bool { - raw_log.contains("insufficient fees") - } - fn parse_suggested_fee(raw_log: &str) -> Option { - let parts: Vec<&str> = raw_log.split("required: ").collect(); - if parts.len() != 2 { - return None; - } - let got_parts: Vec<&str> = parts[0].split_whitespace().collect(); - let paid_fee_with_denom = got_parts.last()?; - let (_, denomination) = paid_fee_with_denom - .split_at(paid_fee_with_denom.find(|c: char| !c.is_numeric())?); - { - ::std::io::_eprint(format_args!("denom: {0}\n", denomination)); - }; - let required_fees: Vec<&str> = parts[1].split(denomination).collect(); - { - ::std::io::_eprint(format_args!("required fees: {0:?}\n", required_fees)); - }; - let (_, suggested_fee) = required_fees[0] - .split_at(required_fees[0].rfind(|c: char| !c.is_numeric())?); - { - ::std::io::_eprint(format_args!("suggested fee: {0}\n", suggested_fee)); - }; - suggested_fee.parse::().ok().or(suggested_fee[1..].parse::().ok()) - } -} -pub mod state { - use super::error::DaemonError; - use crate::{channel::GrpcChannel, networks::ChainKind}; - use cosmwasm_std::Addr; - use cw_orch_core::{ - environment::{DeployDetails, StateInterface}, - CwEnvError, - }; - use ibc_chain_registry::chain::ChainData; - use serde::Serialize; - use serde_json::{json, Value}; - use std::{collections::HashMap, env, fs::File, path::Path}; - use tonic::transport::Channel; - /// Stores the chain information and deployment state. - /// Uses a simple JSON file to store the deployment information locally. - pub struct DaemonState { - /// this is passed via env var STATE_FILE - pub json_file_path: String, - /// Deployment identifier - pub deployment_id: String, - /// gRPC channel - pub grpc_channel: Channel, - /// Information about the chain - pub chain_data: ChainData, - } - #[automatically_derived] - impl ::core::clone::Clone for DaemonState { - #[inline] - fn clone(&self) -> DaemonState { - DaemonState { - json_file_path: ::core::clone::Clone::clone(&self.json_file_path), - deployment_id: ::core::clone::Clone::clone(&self.deployment_id), - grpc_channel: ::core::clone::Clone::clone(&self.grpc_channel), - chain_data: ::core::clone::Clone::clone(&self.chain_data), - } - } - } - #[automatically_derived] - impl ::core::fmt::Debug for DaemonState { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field4_finish( - f, - "DaemonState", - "json_file_path", - &self.json_file_path, - "deployment_id", - &self.deployment_id, - "grpc_channel", - &self.grpc_channel, - "chain_data", - &&self.chain_data, - ) - } - } - impl DaemonState { - /// Creates a new state from the given chain data and deployment id. - /// Attempts to connect to any of the provided gRPC endpoints. - pub async fn new( - mut chain_data: ChainData, - deployment_id: String, - ) -> Result { - if chain_data.apis.grpc.is_empty() { - return Err(DaemonError::GRPCListIsEmpty); - } - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Found {0} gRPC endpoints", - chain_data.apis.grpc.len(), - ), - lvl, - &( - "cw_orch_daemon::state", - "cw_orch_daemon::state", - "cw-orch-daemon/src/state.rs", - ), - 40u32, - ::log::__private_api::Option::None, - ); - } - }; - let grpc_channel = GrpcChannel::connect( - &chain_data.apis.grpc, - &chain_data.chain_id, - ) - .await?; - let mut json_file_path = env::var("STATE_FILE") - .unwrap_or("./state.json".to_string()); - if chain_data.network_type == ChainKind::Local.to_string() { - let name = Path::new(&json_file_path) - .file_stem() - .unwrap() - .to_str() - .unwrap(); - let folder = Path::new(&json_file_path) - .parent() - .unwrap() - .to_str() - .unwrap(); - json_file_path = { - let res = ::alloc::fmt::format( - format_args!("{0}/{1}_local.json", folder, name), - ); - res - }; - } - let shortest_denom_token = chain_data - .fees - .fee_tokens - .iter() - .fold( - chain_data.fees.fee_tokens[0].clone(), - |acc, item| { - if item.denom.len() < acc.denom.len() { - item.clone() - } else { - acc - } - }, - ); - chain_data - .fees - .fee_tokens = <[_]>::into_vec( - #[rustc_box] - ::alloc::boxed::Box::new([shortest_denom_token]), - ); - let state = DaemonState { - json_file_path, - deployment_id, - grpc_channel, - chain_data, - }; - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Writing daemon state JSON file: {0:#?}", - state.json_file_path, - ), - lvl, - &( - "cw_orch_daemon::state", - "cw_orch_daemon::state", - "cw-orch-daemon/src/state.rs", - ), - 87u32, - ::log::__private_api::Option::None, - ); - } - }; - crate::json_file::write( - &state.json_file_path, - &state.chain_data.chain_id.to_string(), - &state.chain_data.chain_name, - &state.deployment_id, - ); - Ok(state) - } - /// Get the state filepath and read it as json - fn read_state(&self) -> serde_json::Value { - crate::json_file::read(&self.json_file_path) - } - /// Retrieve a stateful value using the chainId and networkId - pub fn get(&self, key: &str) -> Value { - let json = self.read_state(); - json[&self.chain_data.chain_name][&self.chain_data.chain_id.to_string()][key] - .clone() - } - /// Set a stateful value using the chainId and networkId - pub fn set(&self, key: &str, contract_id: &str, value: T) { - let mut json = self.read_state(); - json[&self - .chain_data - .chain_name][&self - .chain_data - .chain_id - .to_string()][key][contract_id] = ::serde_json::to_value(&value) - .unwrap(); - serde_json::to_writer_pretty( - File::create(&self.json_file_path).unwrap(), - &json, - ) - .unwrap(); - } - } - impl StateInterface for DaemonState { - /// Read address for contract in deployment id from state file - fn get_address(&self, contract_id: &str) -> Result { - let value = self - .get(&self.deployment_id) - .get(contract_id) - .ok_or_else(|| CwEnvError::AddrNotInStore(contract_id.to_owned()))? - .clone(); - Ok(Addr::unchecked(value.as_str().unwrap())) - } - /// Set address for contract in deployment id in state file - fn set_address(&mut self, contract_id: &str, address: &Addr) { - self.set(&self.deployment_id, contract_id, address.as_str()); - } - /// Get the locally-saved version of the contract's version on this network - fn get_code_id(&self, contract_id: &str) -> Result { - let value = self - .get("code_ids") - .get(contract_id) - .ok_or_else(|| CwEnvError::CodeIdNotInStore(contract_id.to_owned()))? - .clone(); - Ok(value.as_u64().unwrap()) - } - /// Set the locally-saved version of the contract's latest version on this network - fn set_code_id(&mut self, contract_id: &str, code_id: u64) { - self.set("code_ids", contract_id, code_id); - } - /// Get all addresses for deployment id from state file - fn get_all_addresses(&self) -> Result, CwEnvError> { - let mut store = HashMap::new(); - let addresses = self.get(&self.deployment_id); - let value = addresses.as_object().unwrap(); - for (id, addr) in value { - store.insert(id.clone(), Addr::unchecked(addr.as_str().unwrap())); - } - Ok(store) - } - fn get_all_code_ids(&self) -> Result, CwEnvError> { - let mut store = HashMap::new(); - let code_ids = self.get("code_ids"); - let value = code_ids.as_object().unwrap(); - for (id, code_id) in value { - store.insert(id.clone(), code_id.as_u64().unwrap()); - } - Ok(store) - } - fn deploy_details(&self) -> DeployDetails { - DeployDetails { - chain_id: self.chain_data.chain_id.to_string(), - chain_name: self.chain_data.chain_name.clone(), - deployment_id: self.deployment_id.clone(), - } - } - } -} -pub mod sync { - mod builder { - use ibc_chain_registry::chain::ChainData; - use crate::DaemonAsyncBuilder; - use super::{super::error::DaemonError, core::Daemon}; - /// Create [`Daemon`] through [`DaemonBuilder`] - /// ## Example - /// ```no_run - /// use cw_orch_daemon::{networks, DaemonBuilder}; - /// - /// let Daemon = DaemonBuilder::default() - /// .chain(networks::LOCAL_JUNO) - /// .deployment_id("v0.1.0") - /// .build() - /// .unwrap(); - /// ``` - pub struct DaemonBuilder { - pub(crate) chain: Option, - pub(crate) handle: Option, - pub(crate) deployment_id: Option, - /// Wallet mnemonic - pub(crate) mnemonic: Option, - } - #[automatically_derived] - impl ::core::clone::Clone for DaemonBuilder { - #[inline] - fn clone(&self) -> DaemonBuilder { - DaemonBuilder { - chain: ::core::clone::Clone::clone(&self.chain), - handle: ::core::clone::Clone::clone(&self.handle), - deployment_id: ::core::clone::Clone::clone(&self.deployment_id), - mnemonic: ::core::clone::Clone::clone(&self.mnemonic), - } - } - } - #[automatically_derived] - impl ::core::default::Default for DaemonBuilder { - #[inline] - fn default() -> DaemonBuilder { - DaemonBuilder { - chain: ::core::default::Default::default(), - handle: ::core::default::Default::default(), - deployment_id: ::core::default::Default::default(), - mnemonic: ::core::default::Default::default(), - } - } - } - impl DaemonBuilder { - /// Set the chain the Daemon will connect to - pub fn chain(&mut self, chain: impl Into) -> &mut Self { - self.chain = Some(chain.into()); - self - } - /// Set the deployment id to use for the Daemon interactions - /// Defaults to `default` - pub fn deployment_id( - &mut self, - deployment_id: impl Into, - ) -> &mut Self { - self.deployment_id = Some(deployment_id.into()); - self - } - /// Set the tokio runtime handle to use for the Daemon - /// - /// ## Example - /// ```no_run - /// use cw_orch_daemon::Daemon; - /// use tokio::runtime::Runtime; - /// let rt = Runtime::new().unwrap(); - /// let Daemon = Daemon::builder() - /// .handle(rt.handle()) - /// // ... - /// .build() - /// .unwrap(); - /// ``` - pub fn handle(&mut self, handle: &tokio::runtime::Handle) -> &mut Self { - self.handle = Some(handle.clone()); - self - } - /// Set the mnemonic to use with this chain. - pub fn mnemonic(&mut self, mnemonic: impl ToString) -> &mut Self { - self.mnemonic = Some(mnemonic.to_string()); - self - } - /// Build a Daemon - pub fn build(&self) -> Result { - let rt_handle = self - .handle - .clone() - .ok_or(DaemonError::BuilderMissing("runtime handle".into()))?; - let daemon = rt_handle - .block_on(DaemonAsyncBuilder::from(self.clone()).build())?; - Ok(Daemon { rt_handle, daemon }) - } - } - } - mod core { - use std::{fmt::Debug, rc::Rc, time::Duration}; - use super::super::{sender::Wallet, DaemonAsync}; - use crate::{ - queriers::{DaemonQuerier, Node}, - CosmTxResponse, DaemonBuilder, DaemonError, DaemonState, - }; - use cosmrs::tendermint::Time; - use cosmwasm_std::{Addr, Coin}; - use cw_orch_core::{ - contract::{interface_traits::Uploadable, WasmPath}, - environment::{ChainState, TxHandler}, - }; - use serde::{de::DeserializeOwned, Serialize}; - use tokio::runtime::Handle; - use tonic::transport::Channel; - /** - Represents a blockchain node. - Is constructed with the [DaemonBuilder]. - - ## Usage - - ```rust,no_run - use cw_orch_daemon::{Daemon, networks}; - use tokio::runtime::Runtime; - - let rt = Runtime::new().unwrap(); - let daemon: Daemon = Daemon::builder() - .chain(networks::JUNO_1) - .handle(rt.handle()) - .build() - .unwrap(); - ``` - ## Environment Execution - - The Daemon implements [`TxHandler`] which allows you to perform transactions on the chain. - - ## Querying - - Different Cosmos SDK modules can be queried through the daemon by calling the [`Daemon.query_client`] method with a specific querier. - See [Querier](crate::queriers) for examples. -*/ - pub struct Daemon { - pub daemon: DaemonAsync, - /// Runtime handle to execute async tasks - pub rt_handle: Handle, - } - #[automatically_derived] - impl ::core::clone::Clone for Daemon { - #[inline] - fn clone(&self) -> Daemon { - Daemon { - daemon: ::core::clone::Clone::clone(&self.daemon), - rt_handle: ::core::clone::Clone::clone(&self.rt_handle), - } - } - } - impl Daemon { - /// Get the daemon builder - pub fn builder() -> DaemonBuilder { - DaemonBuilder::default() - } - /// Perform a query with a given querier - /// See [Querier](crate::queriers) for examples. - pub fn query_client(&self) -> Querier { - self.daemon.query_client() - } - /// Get the channel configured for this Daemon - pub fn channel(&self) -> Channel { - self.daemon.state.grpc_channel.clone() - } - /// Get the channel configured for this Daemon - pub fn wallet(&self) -> Wallet { - self.daemon.sender.clone() - } - } - impl ChainState for Daemon { - type Out = Rc; - fn state(&self) -> Self::Out { - self.daemon.state.clone() - } - } - impl TxHandler for Daemon { - type Response = CosmTxResponse; - type Error = DaemonError; - type ContractSource = WasmPath; - type Sender = Wallet; - fn sender(&self) -> Addr { - self.daemon.sender.address().unwrap() - } - fn set_sender(&mut self, sender: Self::Sender) { - self.daemon.sender = sender; - } - fn upload( - &self, - uploadable: &impl Uploadable, - ) -> Result { - self.rt_handle.block_on(self.daemon.upload(uploadable)) - } - fn execute( - &self, - exec_msg: &E, - coins: &[cosmwasm_std::Coin], - contract_address: &Addr, - ) -> Result { - self.rt_handle - .block_on(self.daemon.execute(exec_msg, coins, contract_address)) - } - fn instantiate( - &self, - code_id: u64, - init_msg: &I, - label: Option<&str>, - admin: Option<&Addr>, - coins: &[Coin], - ) -> Result { - self.rt_handle - .block_on( - self.daemon.instantiate(code_id, init_msg, label, admin, coins), - ) - } - fn query( - &self, - query_msg: &Q, - contract_address: &Addr, - ) -> Result { - self.rt_handle.block_on(self.daemon.query(query_msg, contract_address)) - } - fn migrate( - &self, - migrate_msg: &M, - new_code_id: u64, - contract_address: &Addr, - ) -> Result { - self.rt_handle - .block_on( - self.daemon.migrate(migrate_msg, new_code_id, contract_address), - ) - } - fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { - let mut last_height = self - .rt_handle - .block_on(self.query_client::().block_height())?; - let end_height = last_height + amount; - while last_height < end_height { - self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(4))); - last_height = self - .rt_handle - .block_on(self.query_client::().block_height())?; - } - Ok(()) - } - fn wait_seconds(&self, secs: u64) -> Result<(), DaemonError> { - self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(secs))); - Ok(()) - } - fn next_block(&self) -> Result<(), DaemonError> { - let mut last_height = self - .rt_handle - .block_on(self.query_client::().block_height())?; - let end_height = last_height + 1; - while last_height < end_height { - self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(4))); - last_height = self - .rt_handle - .block_on(self.query_client::().block_height())?; - } - Ok(()) - } - fn block_info(&self) -> Result { - let block = self - .rt_handle - .block_on(self.query_client::().latest_block())?; - let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; - let time = cosmwasm_std::Timestamp::from_nanos( - since_epoch.as_nanos() as u64, - ); - Ok(cosmwasm_std::BlockInfo { - height: block.header.height.value(), - time, - chain_id: block.header.chain_id.to_string(), - }) - } - } - } - pub use self::{builder::*, core::*}; -} -pub mod tx_resp { - use super::{ - cosmos_modules::{ - abci::{AbciMessageLog, Attribute, StringEvent, TxResponse}, - tendermint_abci::Event, - }, - error::DaemonError, - }; - use chrono::{DateTime, NaiveDateTime, TimeZone, Utc}; - use cosmwasm_std::{to_binary, Binary, StdError, StdResult}; - use cw_orch_core::environment::IndexResponse; - use serde::{Deserialize, Serialize}; - const FORMAT: &str = "%Y-%m-%dT%H:%M:%S%.f"; - const FORMAT_TZ_SUPPLIED: &str = "%Y-%m-%dT%H:%M:%S.%f%:z"; - const FORMAT_SHORT_Z: &str = "%Y-%m-%dT%H:%M:%SZ"; - const FORMAT_SHORT_Z2: &str = "%Y-%m-%dT%H:%M:%S.%fZ"; - /// The response from a transaction performed on a blockchain. - pub struct CosmTxResponse { - /// Height of the block in which the transaction was included. - pub height: u64, - /// Transaction hash. - pub txhash: String, - /// Transaction index within the block. - pub codespace: String, - /// Transaction result code - pub code: usize, - /// Arbitrary data that can be included in a transaction. - pub data: String, - /// Raw log message. - pub raw_log: String, - /// Logs of the transaction. - pub logs: Vec, - /// Transaction info. - pub info: String, - /// Gas limit. - pub gas_wanted: u64, - /// Gas used. - pub gas_used: u64, - /// Timestamp of the block in which the transaction was included. - pub timestamp: DateTime, - /// Transaction events. - pub events: Vec, - } - #[automatically_derived] - impl ::core::fmt::Debug for CosmTxResponse { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - let names: &'static _ = &[ - "height", - "txhash", - "codespace", - "code", - "data", - "raw_log", - "logs", - "info", - "gas_wanted", - "gas_used", - "timestamp", - "events", - ]; - let values: &[&dyn ::core::fmt::Debug] = &[ - &self.height, - &self.txhash, - &self.codespace, - &self.code, - &self.data, - &self.raw_log, - &self.logs, - &self.info, - &self.gas_wanted, - &self.gas_used, - &self.timestamp, - &&self.events, - ]; - ::core::fmt::Formatter::debug_struct_fields_finish( - f, - "CosmTxResponse", - names, - values, - ) - } - } - #[automatically_derived] - impl ::core::default::Default for CosmTxResponse { - #[inline] - fn default() -> CosmTxResponse { - CosmTxResponse { - height: ::core::default::Default::default(), - txhash: ::core::default::Default::default(), - codespace: ::core::default::Default::default(), - code: ::core::default::Default::default(), - data: ::core::default::Default::default(), - raw_log: ::core::default::Default::default(), - logs: ::core::default::Default::default(), - info: ::core::default::Default::default(), - gas_wanted: ::core::default::Default::default(), - gas_used: ::core::default::Default::default(), - timestamp: ::core::default::Default::default(), - events: ::core::default::Default::default(), - } - } - } - impl CosmTxResponse { - /// find a attribute's value from TX logs. - /// returns: msg_index and value - pub fn get_attribute_from_logs( - &self, - event_type: &str, - attribute_key: &str, - ) -> Vec<(usize, String)> { - let mut response: Vec<(usize, String)> = Default::default(); - let logs = &self.logs; - for log_part in logs { - let msg_index = log_part.msg_index.unwrap_or_default(); - let events = &log_part.events; - let events_filtered = events - .iter() - .filter(|event| event.s_type == event_type) - .collect::>(); - if let Some(event) = events_filtered.first() { - let attributes_filtered = event - .attributes - .iter() - .filter(|attr| attr.key == attribute_key) - .map(|f| f.value.clone()) - .collect::>(); - if let Some(attr_key) = attributes_filtered.first() { - response.push((msg_index, attr_key.clone())); - } - } - } - response - } - /// get the list of event types from a TX record - pub fn get_events(&self, event_type: &str) -> Vec { - let mut response: Vec = Default::default(); - for log_part in &self.logs { - let events = &log_part.events; - let events_filtered = events - .iter() - .filter(|event| event.s_type == event_type) - .collect::>(); - for event in events_filtered { - response.push(event.clone()); - } - } - response - } - } - impl From<&serde_json::Value> for TxResultBlockMsg { - fn from(value: &serde_json::Value) -> Self { - serde_json::from_value(value.clone()).unwrap() - } - } - impl From for CosmTxResponse { - fn from(tx: TxResponse) -> Self { - Self { - height: tx.height as u64, - txhash: tx.txhash, - codespace: tx.codespace, - code: tx.code as usize, - data: tx.data, - raw_log: tx.raw_log, - logs: tx.logs.into_iter().map(TxResultBlockMsg::from).collect(), - info: tx.info, - gas_wanted: tx.gas_wanted as u64, - gas_used: tx.gas_used as u64, - timestamp: parse_timestamp(tx.timestamp).unwrap(), - events: tx.events, - } - } - } - impl IndexResponse for CosmTxResponse { - fn events(&self) -> Vec { - let mut parsed_events = ::alloc::vec::Vec::new(); - for event in &self.events { - let mut pattr = ::alloc::vec::Vec::new(); - for attr in &event.attributes { - pattr - .push(cosmwasm_std::Attribute { - key: attr.key.clone(), - value: attr.value.clone(), - }) - } - let pevent = cosmwasm_std::Event::new(event.r#type.clone()) - .add_attributes(pattr); - parsed_events.push(pevent); - } - parsed_events - } - fn data(&self) -> Option { - if self.data.is_empty() { - None - } else { - Some(to_binary(self.data.as_bytes()).unwrap()) - } - } - fn event_attr_value( - &self, - event_type: &str, - attr_key: &str, - ) -> StdResult { - for event in &self.events { - if event.r#type == event_type { - for attr in &event.attributes { - if attr.key == attr_key { - return Ok(attr.value.clone()); - } - } - } - } - Err( - StdError::generic_err({ - let res = ::alloc::fmt::format( - format_args!( - "event of type {0} does not have a value at key {1}", - event_type, - attr_key, - ), - ); - res - }), - ) - } - } - /// The events from a single message in a transaction. - pub struct TxResultBlockMsg { - /// index of the message in the transaction - pub msg_index: Option, - /// Events from this message - pub events: Vec, - } - #[automatically_derived] - impl ::core::clone::Clone for TxResultBlockMsg { - #[inline] - fn clone(&self) -> TxResultBlockMsg { - TxResultBlockMsg { - msg_index: ::core::clone::Clone::clone(&self.msg_index), - events: ::core::clone::Clone::clone(&self.events), - } - } - } - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl _serde::Serialize for TxResultBlockMsg { - fn serialize<__S>( - &self, - __serializer: __S, - ) -> _serde::__private::Result<__S::Ok, __S::Error> - where - __S: _serde::Serializer, - { - let mut __serde_state = _serde::Serializer::serialize_struct( - __serializer, - "TxResultBlockMsg", - false as usize + 1 + 1, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "msg_index", - &self.msg_index, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "events", - &self.events, - )?; - _serde::ser::SerializeStruct::end(__serde_state) - } - } - }; - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl<'de> _serde::Deserialize<'de> for TxResultBlockMsg { - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - #[allow(non_camel_case_types)] - #[doc(hidden)] - enum __Field { - __field0, - __field1, - __ignore, - } - #[doc(hidden)] - struct __FieldVisitor; - impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { - type Value = __Field; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "field identifier", - ) - } - fn visit_u64<__E>( - self, - __value: u64, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - 0u64 => _serde::__private::Ok(__Field::__field0), - 1u64 => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_str<__E>( - self, - __value: &str, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - "msg_index" => _serde::__private::Ok(__Field::__field0), - "events" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_bytes<__E>( - self, - __value: &[u8], - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - b"msg_index" => _serde::__private::Ok(__Field::__field0), - b"events" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - } - impl<'de> _serde::Deserialize<'de> for __Field { - #[inline] - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - _serde::Deserializer::deserialize_identifier( - __deserializer, - __FieldVisitor, - ) - } - } - #[doc(hidden)] - struct __Visitor<'de> { - marker: _serde::__private::PhantomData, - lifetime: _serde::__private::PhantomData<&'de ()>, - } - impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { - type Value = TxResultBlockMsg; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "struct TxResultBlockMsg", - ) - } - #[inline] - fn visit_seq<__A>( - self, - mut __seq: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<'de>, - { - let __field0 = match _serde::de::SeqAccess::next_element::< - Option, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 0usize, - &"struct TxResultBlockMsg with 2 elements", - ), - ); - } - }; - let __field1 = match _serde::de::SeqAccess::next_element::< - Vec, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 1usize, - &"struct TxResultBlockMsg with 2 elements", - ), - ); - } - }; - _serde::__private::Ok(TxResultBlockMsg { - msg_index: __field0, - events: __field1, - }) - } - #[inline] - fn visit_map<__A>( - self, - mut __map: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::MapAccess<'de>, - { - let mut __field0: _serde::__private::Option> = _serde::__private::None; - let mut __field1: _serde::__private::Option< - Vec, - > = _serde::__private::None; - while let _serde::__private::Some(__key) - = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { - match __key { - __Field::__field0 => { - if _serde::__private::Option::is_some(&__field0) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field( - "msg_index", - ), - ); - } - __field0 = _serde::__private::Some( - _serde::de::MapAccess::next_value::< - Option, - >(&mut __map)?, - ); - } - __Field::__field1 => { - if _serde::__private::Option::is_some(&__field1) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field("events"), - ); - } - __field1 = _serde::__private::Some( - _serde::de::MapAccess::next_value::< - Vec, - >(&mut __map)?, - ); - } - _ => { - let _ = _serde::de::MapAccess::next_value::< - _serde::de::IgnoredAny, - >(&mut __map)?; - } - } - } - let __field0 = match __field0 { - _serde::__private::Some(__field0) => __field0, - _serde::__private::None => { - _serde::__private::de::missing_field("msg_index")? - } - }; - let __field1 = match __field1 { - _serde::__private::Some(__field1) => __field1, - _serde::__private::None => { - _serde::__private::de::missing_field("events")? - } - }; - _serde::__private::Ok(TxResultBlockMsg { - msg_index: __field0, - events: __field1, - }) - } - } - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &["msg_index", "events"]; - _serde::Deserializer::deserialize_struct( - __deserializer, - "TxResultBlockMsg", - FIELDS, - __Visitor { - marker: _serde::__private::PhantomData::, - lifetime: _serde::__private::PhantomData, - }, - ) - } - } - }; - #[automatically_derived] - impl ::core::fmt::Debug for TxResultBlockMsg { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "TxResultBlockMsg", - "msg_index", - &self.msg_index, - "events", - &&self.events, - ) - } - } - impl From for TxResultBlockMsg { - fn from(msg: AbciMessageLog) -> Self { - Self { - msg_index: Some(msg.msg_index as usize), - events: msg.events.into_iter().map(TxResultBlockEvent::from).collect(), - } - } - } - /// A single event from a transaction and its attributes. - pub struct TxResultBlockEvent { - #[serde(rename = "type")] - /// Type of the event - pub s_type: String, - /// Attributes of the event - pub attributes: Vec, - } - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl<'de> _serde::Deserialize<'de> for TxResultBlockEvent { - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - #[allow(non_camel_case_types)] - #[doc(hidden)] - enum __Field { - __field0, - __field1, - __ignore, - } - #[doc(hidden)] - struct __FieldVisitor; - impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { - type Value = __Field; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "field identifier", - ) - } - fn visit_u64<__E>( - self, - __value: u64, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - 0u64 => _serde::__private::Ok(__Field::__field0), - 1u64 => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_str<__E>( - self, - __value: &str, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - "type" => _serde::__private::Ok(__Field::__field0), - "attributes" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_bytes<__E>( - self, - __value: &[u8], - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - b"type" => _serde::__private::Ok(__Field::__field0), - b"attributes" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - } - impl<'de> _serde::Deserialize<'de> for __Field { - #[inline] - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - _serde::Deserializer::deserialize_identifier( - __deserializer, - __FieldVisitor, - ) - } - } - #[doc(hidden)] - struct __Visitor<'de> { - marker: _serde::__private::PhantomData, - lifetime: _serde::__private::PhantomData<&'de ()>, - } - impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { - type Value = TxResultBlockEvent; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "struct TxResultBlockEvent", - ) - } - #[inline] - fn visit_seq<__A>( - self, - mut __seq: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<'de>, - { - let __field0 = match _serde::de::SeqAccess::next_element::< - String, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 0usize, - &"struct TxResultBlockEvent with 2 elements", - ), - ); - } - }; - let __field1 = match _serde::de::SeqAccess::next_element::< - Vec, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 1usize, - &"struct TxResultBlockEvent with 2 elements", - ), - ); - } - }; - _serde::__private::Ok(TxResultBlockEvent { - s_type: __field0, - attributes: __field1, - }) - } - #[inline] - fn visit_map<__A>( - self, - mut __map: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::MapAccess<'de>, - { - let mut __field0: _serde::__private::Option = _serde::__private::None; - let mut __field1: _serde::__private::Option< - Vec, - > = _serde::__private::None; - while let _serde::__private::Some(__key) - = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { - match __key { - __Field::__field0 => { - if _serde::__private::Option::is_some(&__field0) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field("type"), - ); - } - __field0 = _serde::__private::Some( - _serde::de::MapAccess::next_value::(&mut __map)?, - ); - } - __Field::__field1 => { - if _serde::__private::Option::is_some(&__field1) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field( - "attributes", - ), - ); - } - __field1 = _serde::__private::Some( - _serde::de::MapAccess::next_value::< - Vec, - >(&mut __map)?, - ); - } - _ => { - let _ = _serde::de::MapAccess::next_value::< - _serde::de::IgnoredAny, - >(&mut __map)?; - } - } - } - let __field0 = match __field0 { - _serde::__private::Some(__field0) => __field0, - _serde::__private::None => { - _serde::__private::de::missing_field("type")? - } - }; - let __field1 = match __field1 { - _serde::__private::Some(__field1) => __field1, - _serde::__private::None => { - _serde::__private::de::missing_field("attributes")? - } - }; - _serde::__private::Ok(TxResultBlockEvent { - s_type: __field0, - attributes: __field1, - }) - } - } - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &["type", "attributes"]; - _serde::Deserializer::deserialize_struct( - __deserializer, - "TxResultBlockEvent", - FIELDS, - __Visitor { - marker: _serde::__private::PhantomData::, - lifetime: _serde::__private::PhantomData, - }, - ) - } - } - }; - #[automatically_derived] - impl ::core::clone::Clone for TxResultBlockEvent { - #[inline] - fn clone(&self) -> TxResultBlockEvent { - TxResultBlockEvent { - s_type: ::core::clone::Clone::clone(&self.s_type), - attributes: ::core::clone::Clone::clone(&self.attributes), - } - } - } - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl _serde::Serialize for TxResultBlockEvent { - fn serialize<__S>( - &self, - __serializer: __S, - ) -> _serde::__private::Result<__S::Ok, __S::Error> - where - __S: _serde::Serializer, - { - let mut __serde_state = _serde::Serializer::serialize_struct( - __serializer, - "TxResultBlockEvent", - false as usize + 1 + 1, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "type", - &self.s_type, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "attributes", - &self.attributes, - )?; - _serde::ser::SerializeStruct::end(__serde_state) - } - } - }; - #[automatically_derived] - impl ::core::fmt::Debug for TxResultBlockEvent { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "TxResultBlockEvent", - "s_type", - &self.s_type, - "attributes", - &&self.attributes, - ) - } - } - impl From for TxResultBlockEvent { - fn from(event: StringEvent) -> Self { - Self { - s_type: event.r#type, - attributes: event - .attributes - .into_iter() - .map(TxResultBlockAttribute::from) - .collect(), - } - } - } - impl TxResultBlockEvent { - /// get all key/values from the event that have the key 'key' - pub fn get_attributes(&self, key: &str) -> Vec { - self.attributes.iter().filter(|attr| attr.key == key).cloned().collect() - } - /// return the first value of the first attribute that has the key 'key' - pub fn get_first_attribute_value(&self, key: &str) -> Option { - self.get_attributes(key).first().map(|attr| attr.value.clone()) - } - } - /// A single attribute of an event. - pub struct TxResultBlockAttribute { - /// Key of the attribute - pub key: String, - /// Value of the attribute - pub value: String, - } - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl<'de> _serde::Deserialize<'de> for TxResultBlockAttribute { - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - #[allow(non_camel_case_types)] - #[doc(hidden)] - enum __Field { - __field0, - __field1, - __ignore, - } - #[doc(hidden)] - struct __FieldVisitor; - impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { - type Value = __Field; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "field identifier", - ) - } - fn visit_u64<__E>( - self, - __value: u64, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - 0u64 => _serde::__private::Ok(__Field::__field0), - 1u64 => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_str<__E>( - self, - __value: &str, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - "key" => _serde::__private::Ok(__Field::__field0), - "value" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_bytes<__E>( - self, - __value: &[u8], - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - b"key" => _serde::__private::Ok(__Field::__field0), - b"value" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - } - impl<'de> _serde::Deserialize<'de> for __Field { - #[inline] - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - _serde::Deserializer::deserialize_identifier( - __deserializer, - __FieldVisitor, - ) - } - } - #[doc(hidden)] - struct __Visitor<'de> { - marker: _serde::__private::PhantomData, - lifetime: _serde::__private::PhantomData<&'de ()>, - } - impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { - type Value = TxResultBlockAttribute; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "struct TxResultBlockAttribute", - ) - } - #[inline] - fn visit_seq<__A>( - self, - mut __seq: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<'de>, - { - let __field0 = match _serde::de::SeqAccess::next_element::< - String, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 0usize, - &"struct TxResultBlockAttribute with 2 elements", - ), - ); - } - }; - let __field1 = match _serde::de::SeqAccess::next_element::< - String, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 1usize, - &"struct TxResultBlockAttribute with 2 elements", - ), - ); - } - }; - _serde::__private::Ok(TxResultBlockAttribute { - key: __field0, - value: __field1, - }) - } - #[inline] - fn visit_map<__A>( - self, - mut __map: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::MapAccess<'de>, - { - let mut __field0: _serde::__private::Option = _serde::__private::None; - let mut __field1: _serde::__private::Option = _serde::__private::None; - while let _serde::__private::Some(__key) - = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { - match __key { - __Field::__field0 => { - if _serde::__private::Option::is_some(&__field0) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field("key"), - ); - } - __field0 = _serde::__private::Some( - _serde::de::MapAccess::next_value::(&mut __map)?, - ); - } - __Field::__field1 => { - if _serde::__private::Option::is_some(&__field1) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field("value"), - ); - } - __field1 = _serde::__private::Some( - _serde::de::MapAccess::next_value::(&mut __map)?, - ); - } - _ => { - let _ = _serde::de::MapAccess::next_value::< - _serde::de::IgnoredAny, - >(&mut __map)?; - } - } - } - let __field0 = match __field0 { - _serde::__private::Some(__field0) => __field0, - _serde::__private::None => { - _serde::__private::de::missing_field("key")? - } - }; - let __field1 = match __field1 { - _serde::__private::Some(__field1) => __field1, - _serde::__private::None => { - _serde::__private::de::missing_field("value")? - } - }; - _serde::__private::Ok(TxResultBlockAttribute { - key: __field0, - value: __field1, - }) - } - } - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &["key", "value"]; - _serde::Deserializer::deserialize_struct( - __deserializer, - "TxResultBlockAttribute", - FIELDS, - __Visitor { - marker: _serde::__private::PhantomData::, - lifetime: _serde::__private::PhantomData, - }, - ) - } - } - }; - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl _serde::Serialize for TxResultBlockAttribute { - fn serialize<__S>( - &self, - __serializer: __S, - ) -> _serde::__private::Result<__S::Ok, __S::Error> - where - __S: _serde::Serializer, - { - let mut __serde_state = _serde::Serializer::serialize_struct( - __serializer, - "TxResultBlockAttribute", - false as usize + 1 + 1, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "key", - &self.key, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "value", - &self.value, - )?; - _serde::ser::SerializeStruct::end(__serde_state) - } - } - }; - #[automatically_derived] - impl ::core::clone::Clone for TxResultBlockAttribute { - #[inline] - fn clone(&self) -> TxResultBlockAttribute { - TxResultBlockAttribute { - key: ::core::clone::Clone::clone(&self.key), - value: ::core::clone::Clone::clone(&self.value), - } - } - } - #[automatically_derived] - impl ::core::fmt::Debug for TxResultBlockAttribute { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "TxResultBlockAttribute", - "key", - &self.key, - "value", - &&self.value, - ) - } - } - impl From for TxResultBlockAttribute { - fn from(a: Attribute) -> Self { - Self { key: a.key, value: a.value } - } - } - /// Parse a string timestamp into a `DateTime` - pub fn parse_timestamp(s: String) -> Result, DaemonError> { - let len = s.len(); - let slice_len = if s.contains('.') { len.saturating_sub(4) } else { len }; - let sliced = &s[0..slice_len]; - match NaiveDateTime::parse_from_str(sliced, FORMAT) { - Err(_e) => { - match NaiveDateTime::parse_from_str(&s, FORMAT_TZ_SUPPLIED) { - Err(_e2) => { - match NaiveDateTime::parse_from_str(sliced, FORMAT_SHORT_Z) { - Err(_e3) => { - match NaiveDateTime::parse_from_str(&s, FORMAT_SHORT_Z2) { - Err(_e4) => { - { - ::std::io::_eprint( - format_args!("DateTime Fail {0} {1:#?}\n", s, _e4), - ); - }; - Err(DaemonError::StdErr(_e4.to_string())) - } - Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), - } - } - Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), - } - } - Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), - } - } - Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), - } - } -} -pub mod keys { - #![allow(unused)] - pub mod private { - use super::public::PublicKey; - use crate::proto::injective::{InjectivePubKey, ETHEREUM_COIN_TYPE}; - use crate::DaemonError; - use base64::Engine; - use bitcoin::{ - bip32::{ExtendedPrivKey, IntoDerivationPath}, - Network, - }; - use cosmrs::tx::SignerPublicKey; - use hkd32::mnemonic::{Phrase, Seed}; - use rand_core::OsRng; - use secp256k1::Secp256k1; - /// The Private key structure that is used to generate signatures and public keys - /// WARNING: No Security Audit has been performed - pub struct PrivateKey { - #[allow(missing_docs)] - pub account: u32, - #[allow(missing_docs)] - pub index: u32, - #[allow(missing_docs)] - pub coin_type: u32, - /// The 24 words used to generate this private key - mnemonic: Option, - #[allow(dead_code)] - /// This is used for testing - root_private_key: ExtendedPrivKey, - /// The private key - private_key: ExtendedPrivKey, - } - #[automatically_derived] - impl ::core::clone::Clone for PrivateKey { - #[inline] - fn clone(&self) -> PrivateKey { - PrivateKey { - account: ::core::clone::Clone::clone(&self.account), - index: ::core::clone::Clone::clone(&self.index), - coin_type: ::core::clone::Clone::clone(&self.coin_type), - mnemonic: ::core::clone::Clone::clone(&self.mnemonic), - root_private_key: ::core::clone::Clone::clone( - &self.root_private_key, - ), - private_key: ::core::clone::Clone::clone(&self.private_key), - } - } - } - impl PrivateKey { - /// Generate a new private key - pub fn new( - secp: &Secp256k1, - coin_type: u32, - ) -> Result { - let phrase = hkd32::mnemonic::Phrase::random( - OsRng, - hkd32::mnemonic::Language::English, - ); - PrivateKey::gen_private_key_phrase(secp, phrase, 0, 0, coin_type, "") - } - /// generate a new private key with a seed phrase - pub fn new_seed( - secp: &Secp256k1, - seed_phrase: &str, - coin_type: u32, - ) -> Result { - let phrase = hkd32::mnemonic::Phrase::random( - OsRng, - hkd32::mnemonic::Language::English, - ); - PrivateKey::gen_private_key_phrase( - secp, - phrase, - 0, - 0, - coin_type, - seed_phrase, - ) - } - /// for private key recovery. This is also used by wallet routines to re-hydrate the structure - pub fn from_words( - secp: &Secp256k1, - words: &str, - account: u32, - index: u32, - coin_type: u32, - ) -> Result { - if words.split(' ').count() != 24 { - return Err(DaemonError::WrongLength); - } - match hkd32::mnemonic::Phrase::new( - words, - hkd32::mnemonic::Language::English, - ) { - Ok(phrase) => { - PrivateKey::gen_private_key_phrase( - secp, - phrase, - account, - index, - coin_type, - "", - ) - } - Err(_) => Err(DaemonError::Phrasing), - } - } - /// for private key recovery with seed phrase - pub fn from_words_seed( - secp: &Secp256k1, - words: &str, - seed_pass: &str, - coin_type: u32, - ) -> Result { - match hkd32::mnemonic::Phrase::new( - words, - hkd32::mnemonic::Language::English, - ) { - Ok(phrase) => { - PrivateKey::gen_private_key_phrase( - secp, - phrase, - 0, - 0, - coin_type, - seed_pass, - ) - } - Err(_) => Err(DaemonError::Phrasing), - } - } - /// generate the public key for this private key - pub fn public_key( - &self, - secp: &Secp256k1, - ) -> PublicKey { - if self.coin_type == ETHEREUM_COIN_TYPE { - { - ::core::panicking::panic_fmt( - format_args!( - "Coin Type {0} not supported without eth feature", - ETHEREUM_COIN_TYPE, - ), - ); - }; - } - let x = self.private_key.private_key.public_key(secp); - PublicKey::from_bitcoin_public_key(&bitcoin::PublicKey::new(x)) - } - pub fn get_injective_public_key( - &self, - secp: &Secp256k1, - ) -> SignerPublicKey { - use base64::engine::general_purpose; - use cosmrs::tx::MessageExt; - use secp256k1::SecretKey; - let secret_key = SecretKey::from_slice(self.raw_key().as_slice()) - .unwrap(); - let public_key = secp256k1::PublicKey::from_secret_key( - secp, - &secret_key, - ); - let vec_pk = public_key.serialize(); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "{0:?}, public key", - general_purpose::STANDARD.encode(vec_pk), - ), - lvl, - &( - "cw_orch_daemon::keys::private", - "cw_orch_daemon::keys::private", - "cw-orch-daemon/src/keys/private.rs", - ), - 124u32, - ::log::__private_api::Option::None, - ); - } - }; - let inj_key = InjectivePubKey { - key: vec_pk.into(), - }; - inj_key.to_any().unwrap().try_into().unwrap() - } - pub fn get_signer_public_key( - &self, - secp: &Secp256k1, - ) -> Option { - if self.coin_type == ETHEREUM_COIN_TYPE { - { - ::core::panicking::panic_fmt( - format_args!( - "Coin Type {0} not supported without eth feature", - ETHEREUM_COIN_TYPE, - ), - ); - }; - } - Some( - cosmrs::crypto::secp256k1::SigningKey::from_slice( - self.raw_key().as_slice(), - ) - .unwrap() - .public_key() - .into(), - ) - } - pub fn raw_key(&self) -> Vec { - self.private_key.private_key.secret_bytes().to_vec() - } - fn gen_private_key_phrase( - secp: &Secp256k1, - phrase: Phrase, - account: u32, - index: u32, - coin_type: u32, - seed_phrase: &str, - ) -> Result { - let seed = phrase.to_seed(seed_phrase); - let root_private_key = ExtendedPrivKey::new_master( - Network::Bitcoin, - seed.as_bytes(), - ) - .unwrap(); - let path = { - let res = ::alloc::fmt::format( - format_args!( - "m/44\'/{0}\'/{1}\'/0/{2}", - coin_type, - account, - index, - ), - ); - res - }; - let derivation_path = path.into_derivation_path()?; - let private_key = root_private_key.derive_priv(secp, &derivation_path)?; - Ok(PrivateKey { - account, - index, - coin_type, - mnemonic: Some(phrase), - root_private_key, - private_key, - }) - } - /// the words used to generate this private key - pub fn words(&self) -> Option<&str> { - self.mnemonic.as_ref().map(|phrase| phrase.phrase()) - } - /// used for testing - /// could potentially be used to recreate the private key instead of words - #[allow(dead_code)] - pub(crate) fn seed(&self, passwd: &str) -> Option { - self.mnemonic.as_ref().map(|phrase| phrase.to_seed(passwd)) - } - } - } - pub mod public { - use crate::DaemonError; - use bitcoin::bech32::{decode, encode, u5, FromBase32, ToBase32, Variant}; - pub use ed25519_dalek::VerifyingKey as Ed25519; - use ring::digest::{Context, SHA256}; - use ripemd::{Digest as _, Ripemd160}; - use serde::{Deserialize, Serialize}; - static BECH32_PUBKEY_DATA_PREFIX_SECP256K1: [u8; 5] = [ - 0xeb, - 0x5a, - 0xe9, - 0x87, - 0x21, - ]; - static BECH32_PUBKEY_DATA_PREFIX_ED25519: [u8; 5] = [ - 0x16, - 0x24, - 0xde, - 0x64, - 0x20, - ]; - /// The public key we used to generate the cosmos/tendermind/terrad addresses - pub struct PublicKey { - /// This is optional as we can generate non-pub keys without - pub raw_pub_key: Option>, - /// The raw bytes used to generate non-pub keys - pub raw_address: Option>, - } - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl<'de> _serde::Deserialize<'de> for PublicKey { - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - #[allow(non_camel_case_types)] - #[doc(hidden)] - enum __Field { - __field0, - __field1, - __ignore, - } - #[doc(hidden)] - struct __FieldVisitor; - impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { - type Value = __Field; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "field identifier", - ) - } - fn visit_u64<__E>( - self, - __value: u64, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - 0u64 => _serde::__private::Ok(__Field::__field0), - 1u64 => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_str<__E>( - self, - __value: &str, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - "raw_pub_key" => _serde::__private::Ok(__Field::__field0), - "raw_address" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_bytes<__E>( - self, - __value: &[u8], - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - b"raw_pub_key" => _serde::__private::Ok(__Field::__field0), - b"raw_address" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - } - impl<'de> _serde::Deserialize<'de> for __Field { - #[inline] - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - _serde::Deserializer::deserialize_identifier( - __deserializer, - __FieldVisitor, - ) - } - } - #[doc(hidden)] - struct __Visitor<'de> { - marker: _serde::__private::PhantomData, - lifetime: _serde::__private::PhantomData<&'de ()>, - } - impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { - type Value = PublicKey; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "struct PublicKey", - ) - } - #[inline] - fn visit_seq<__A>( - self, - mut __seq: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<'de>, - { - let __field0 = match _serde::de::SeqAccess::next_element::< - Option>, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 0usize, - &"struct PublicKey with 2 elements", - ), - ); - } - }; - let __field1 = match _serde::de::SeqAccess::next_element::< - Option>, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 1usize, - &"struct PublicKey with 2 elements", - ), - ); - } - }; - _serde::__private::Ok(PublicKey { - raw_pub_key: __field0, - raw_address: __field1, - }) - } - #[inline] - fn visit_map<__A>( - self, - mut __map: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::MapAccess<'de>, - { - let mut __field0: _serde::__private::Option< - Option>, - > = _serde::__private::None; - let mut __field1: _serde::__private::Option< - Option>, - > = _serde::__private::None; - while let _serde::__private::Some(__key) - = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { - match __key { - __Field::__field0 => { - if _serde::__private::Option::is_some(&__field0) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field( - "raw_pub_key", - ), - ); - } - __field0 = _serde::__private::Some( - _serde::de::MapAccess::next_value::< - Option>, - >(&mut __map)?, - ); - } - __Field::__field1 => { - if _serde::__private::Option::is_some(&__field1) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field( - "raw_address", - ), - ); - } - __field1 = _serde::__private::Some( - _serde::de::MapAccess::next_value::< - Option>, - >(&mut __map)?, - ); - } - _ => { - let _ = _serde::de::MapAccess::next_value::< - _serde::de::IgnoredAny, - >(&mut __map)?; - } - } - } - let __field0 = match __field0 { - _serde::__private::Some(__field0) => __field0, - _serde::__private::None => { - _serde::__private::de::missing_field("raw_pub_key")? - } - }; - let __field1 = match __field1 { - _serde::__private::Some(__field1) => __field1, - _serde::__private::None => { - _serde::__private::de::missing_field("raw_address")? - } - }; - _serde::__private::Ok(PublicKey { - raw_pub_key: __field0, - raw_address: __field1, - }) - } - } - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &[ - "raw_pub_key", - "raw_address", - ]; - _serde::Deserializer::deserialize_struct( - __deserializer, - "PublicKey", - FIELDS, - __Visitor { - marker: _serde::__private::PhantomData::, - lifetime: _serde::__private::PhantomData, - }, - ) - } - } - }; - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl _serde::Serialize for PublicKey { - fn serialize<__S>( - &self, - __serializer: __S, - ) -> _serde::__private::Result<__S::Ok, __S::Error> - where - __S: _serde::Serializer, - { - let mut __serde_state = _serde::Serializer::serialize_struct( - __serializer, - "PublicKey", - false as usize + 1 + 1, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "raw_pub_key", - &self.raw_pub_key, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "raw_address", - &self.raw_address, - )?; - _serde::ser::SerializeStruct::end(__serde_state) - } - } - }; - #[automatically_derived] - impl ::core::fmt::Debug for PublicKey { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "PublicKey", - "raw_pub_key", - &self.raw_pub_key, - "raw_address", - &&self.raw_address, - ) - } - } - #[automatically_derived] - impl ::core::clone::Clone for PublicKey { - #[inline] - fn clone(&self) -> PublicKey { - PublicKey { - raw_pub_key: ::core::clone::Clone::clone(&self.raw_pub_key), - raw_address: ::core::clone::Clone::clone(&self.raw_address), - } - } - } - impl PublicKey { - /// Generate a Cosmos/Tendermint/Terrad Public Key - pub fn from_bitcoin_public_key(bpub: &bitcoin::key::PublicKey) -> PublicKey { - let bpub_bytes = bpub.inner.serialize(); - let raw_pub_key = PublicKey::pubkey_from_public_key(&bpub_bytes); - let raw_address = PublicKey::address_from_public_key(&bpub_bytes); - PublicKey { - raw_pub_key: Some(raw_pub_key), - raw_address: Some(raw_address), - } - } - /// Generate from secp256k1 Cosmos/Terrad Public Key - pub fn from_public_key(bpub: &[u8]) -> PublicKey { - let raw_pub_key = PublicKey::pubkey_from_public_key(bpub); - let raw_address = PublicKey::address_from_public_key(bpub); - PublicKey { - raw_pub_key: Some(raw_pub_key), - raw_address: Some(raw_address), - } - } - /// Generate a Cosmos/Tendermint/Terrad Account - pub fn from_account( - acc_address: &str, - prefix: &str, - ) -> Result { - PublicKey::check_prefix_and_length(prefix, acc_address, 44) - .and_then(|vu5| { - let vu8 = Vec::from_base32(vu5.as_slice()) - .map_err(|source| DaemonError::Conversion { - key: acc_address.into(), - source, - })?; - Ok(PublicKey { - raw_pub_key: None, - raw_address: Some(vu8), - }) - }) - } - /// build a public key from a tendermint public key - pub fn from_tendermint_key( - tendermint_public_key: &str, - ) -> Result { - let len = tendermint_public_key.len(); - if len == 83 { - PublicKey::check_prefix_and_length( - "terravalconspub", - tendermint_public_key, - len, - ) - .and_then(|vu5| { - let vu8 = Vec::from_base32(vu5.as_slice()) - .map_err(|source| { - DaemonError::Conversion { - key: tendermint_public_key.into(), - source, - } - })?; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("{0:#?}", hex::encode(&vu8)), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 74u32, - ::log::__private_api::Option::None, - ); - } - }; - if vu8.starts_with(&BECH32_PUBKEY_DATA_PREFIX_SECP256K1) { - let public_key = PublicKey::public_key_from_pubkey(&vu8)?; - let raw = PublicKey::address_from_public_key(&public_key); - Ok(PublicKey { - raw_pub_key: Some(vu8), - raw_address: Some(raw), - }) - } else { - Err(DaemonError::ConversionSECP256k1) - } - }) - } else if len == 82 { - PublicKey::check_prefix_and_length( - "terravalconspub", - tendermint_public_key, - len, - ) - .and_then(|vu5| { - let vu8 = Vec::from_base32(vu5.as_slice()) - .map_err(|source| { - DaemonError::Conversion { - key: tendermint_public_key.into(), - source, - } - })?; - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("ED25519 public keys are not fully supported"), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 100u32, - ::log::__private_api::Option::None, - ); - } - }; - if vu8.starts_with(&BECH32_PUBKEY_DATA_PREFIX_ED25519) { - let raw = PublicKey::address_from_public_ed25519_key(&vu8)?; - Ok(PublicKey { - raw_pub_key: Some(vu8), - raw_address: Some(raw), - }) - } else { - Err(DaemonError::ConversionED25519) - } - }) - } else { - Err(DaemonError::ConversionLength(len)) - } - } - /// build a terravalcons address from a tendermint hex key - /// the tendermint_hex_address should be a hex code of 40 length - pub fn from_tendermint_address( - tendermint_hex_address: &str, - ) -> Result { - let len = tendermint_hex_address.len(); - if len == 40 { - let raw = hex::decode(tendermint_hex_address)?; - Ok(PublicKey { - raw_pub_key: None, - raw_address: Some(raw), - }) - } else { - Err(DaemonError::ConversionLengthED25519Hex(len)) - } - } - /// Generate a Operator address for this public key (used by the validator) - pub fn from_operator_address( - valoper_address: &str, - ) -> Result { - PublicKey::check_prefix_and_length("terravaloper", valoper_address, 51) - .and_then(|vu5| { - let vu8 = Vec::from_base32(vu5.as_slice()) - .map_err(|source| DaemonError::Conversion { - key: valoper_address.into(), - source, - })?; - Ok(PublicKey { - raw_pub_key: None, - raw_address: Some(vu8), - }) - }) - } - /// Generate Public key from raw address - pub fn from_raw_address( - raw_address: &str, - ) -> Result { - let vec1 = hex::decode(raw_address)?; - Ok(PublicKey { - raw_pub_key: None, - raw_address: Some(vec1), - }) - } - fn check_prefix_and_length( - prefix: &str, - data: &str, - length: usize, - ) -> Result, DaemonError> { - let (hrp, decoded_str, _) = decode(data) - .map_err(|source| DaemonError::Conversion { - key: data.into(), - source, - })?; - if hrp == prefix && data.len() == length { - Ok(decoded_str) - } else { - Err( - DaemonError::Bech32DecodeExpanded( - hrp, - data.len(), - prefix.into(), - length, - ), - ) - } - } - /** - Gets a bech32-words pubkey from a compressed bytes Secp256K1 public key. - - @param publicKey raw public key - */ - pub fn pubkey_from_public_key(public_key: &[u8]) -> Vec { - [BECH32_PUBKEY_DATA_PREFIX_SECP256K1.to_vec(), public_key.to_vec()] - .concat() - } - /** - Gets a bech32-words pubkey from a compressed bytes Ed25519 public key. - - @param publicKey raw public key - */ - pub fn pubkey_from_ed25519_public_key(public_key: &[u8]) -> Vec { - [BECH32_PUBKEY_DATA_PREFIX_ED25519.to_vec(), public_key.to_vec()] - .concat() - } - /// Translate from a BECH32 prefixed key to a standard public key - pub fn public_key_from_pubkey( - pub_key: &[u8], - ) -> Result, DaemonError> { - if pub_key.starts_with(&BECH32_PUBKEY_DATA_PREFIX_SECP256K1) { - let len = BECH32_PUBKEY_DATA_PREFIX_SECP256K1.len(); - let len2 = pub_key.len(); - Ok(Vec::from(&pub_key[len..len2])) - } else if pub_key.starts_with(&BECH32_PUBKEY_DATA_PREFIX_ED25519) { - let len = BECH32_PUBKEY_DATA_PREFIX_ED25519.len(); - let len2 = pub_key.len(); - let vec = &pub_key[len..len2]; - let ed25519_pubkey = Ed25519::from_bytes(vec.try_into().unwrap())?; - Ok(ed25519_pubkey.to_bytes().to_vec()) - } else { - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("pub key does not start with BECH32 PREFIX"), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 228u32, - ::log::__private_api::Option::None, - ); - } - }; - Err(DaemonError::Bech32DecodeErr) - } - } - /** - Gets a raw address from a compressed bytes public key. - - @param publicKey raw public key - */ - pub fn address_from_public_key(public_key: &[u8]) -> Vec { - let mut hasher = Ripemd160::new(); - let sha_result = ring::digest::digest(&SHA256, public_key); - hasher.update(&sha_result.as_ref()[0..32]); - let ripe_result = hasher.finalize(); - let address: Vec = ripe_result[0..20].to_vec(); - address - } - /** - Gets a raw address from a ed25519 public key. - - @param publicKey raw public key - */ - pub fn address_from_public_ed25519_key( - public_key: &[u8], - ) -> Result, DaemonError> { - if public_key.len() != (32 + 5) { - Err( - DaemonError::ConversionPrefixED25519( - public_key.len(), - hex::encode(public_key), - ), - ) - } else { - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "address_from_public_ed25519_key public key - {0}", - hex::encode(public_key), - ), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 262u32, - ::log::__private_api::Option::None, - ); - } - }; - let mut sha_result: [u8; 32] = [0; 32]; - let sha_result = ring::digest::digest(&SHA256, &public_key[5..]); - let address: Vec = sha_result.as_ref()[0..20].to_vec(); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "address_from_public_ed25519_key sha result - {0}", - hex::encode(&address), - ), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 283u32, - ::log::__private_api::Option::None, - ); - } - }; - Ok(address) - } - } - /// The main account used in most things - pub fn account(&self, prefix: &str) -> Result { - match &self.raw_address { - Some(raw) => { - let data = encode(prefix, raw.to_base32(), Variant::Bech32); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => Err(DaemonError::Implementation), - } - } - /// The operator address used for validators - pub fn operator_address(&self, prefix: &str) -> Result { - match &self.raw_address { - Some(raw) => { - let data = encode( - &{ - let res = ::alloc::fmt::format( - format_args!("{0}{1}", prefix, "valoper"), - ); - res - }, - raw.to_base32(), - Variant::Bech32, - ); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => Err(DaemonError::Implementation), - } - } - /// application public key - Application keys are associated with a public key terrapub- and an address terra- - pub fn application_public_key( - &self, - prefix: &str, - ) -> Result { - match &self.raw_pub_key { - Some(raw) => { - let data = encode( - &{ - let res = ::alloc::fmt::format( - format_args!("{0}{1}", prefix, "pub"), - ); - res - }, - raw.to_base32(), - Variant::Bech32, - ); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => { - { - let lvl = ::log::Level::Warn; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("Missing Public Key. Can\'t continue"), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 335u32, - ::log::__private_api::Option::None, - ); - } - }; - Err(DaemonError::Implementation) - } - } - } - /// The operator address used for validators public key. - pub fn operator_address_public_key( - &self, - prefix: &str, - ) -> Result { - match &self.raw_pub_key { - Some(raw) => { - let data = encode( - &{ - let res = ::alloc::fmt::format( - format_args!("{0}{1}", prefix, "valoperpub"), - ); - res - }, - raw.to_base32(), - Variant::Bech32, - ); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => Err(DaemonError::Implementation), - } - } - /// This is a unique key used to sign block hashes. It is associated with a public key terravalconspub. - pub fn tendermint(&self, prefix: &str) -> Result { - match &self.raw_address { - Some(raw) => { - let data = encode( - &{ - let res = ::alloc::fmt::format( - format_args!("{0}{1}", prefix, "valcons"), - ); - res - }, - raw.to_base32(), - Variant::Bech32, - ); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => Err(DaemonError::Implementation), - } - } - /// This is a unique key used to sign block hashes. It is associated with a public key terravalconspub. - pub fn tendermint_pubkey( - &self, - prefix: &str, - ) -> Result { - match &self.raw_pub_key { - Some(raw) => { - let b32 = raw.to_base32(); - let data = encode( - &{ - let res = ::alloc::fmt::format( - format_args!("{0}{1}", prefix, "valconspub"), - ); - res - }, - b32, - Variant::Bech32, - ); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => Err(DaemonError::Implementation), - } - } - } - } - pub mod signature { - use crate::DaemonError; - use base64::engine::{general_purpose::STANDARD, Engine}; - use ring::digest::SHA256; - use secp256k1::{Message, Secp256k1}; - pub struct Signature {} - impl Signature { - pub fn verify( - secp: &Secp256k1, - pub_key: &str, - signature: &str, - blob: &str, - ) -> Result<(), DaemonError> { - let public = STANDARD.decode(pub_key)?; - let sig = STANDARD.decode(signature)?; - let pk = secp256k1::PublicKey::from_slice(public.as_slice())?; - let sha_result = ring::digest::digest(&SHA256, blob.as_bytes()); - let message: Message = Message::from_slice(&sha_result.as_ref()[0..32])?; - let secp_sig = secp256k1::ecdsa::Signature::from_compact( - sig.as_slice(), - )?; - secp.verify_ecdsa(&message, &secp_sig, &pk)?; - Ok(()) - } - } - } -} -pub mod live_mock { - //! Live mock is a mock that uses a live chain to query for data. - //! It can be used to do chain-backed unit-testing. It can't be used for state-changing operations. - use crate::queriers::Bank; - use crate::queriers::CosmWasm; - use crate::queriers::DaemonQuerier; - use crate::queriers::Staking; - use cosmwasm_std::Addr; - use cosmwasm_std::AllBalanceResponse; - use cosmwasm_std::BalanceResponse; - use cosmwasm_std::Delegation; - use cosmwasm_std::{AllDelegationsResponse, BondedDenomResponse}; - use cosmwasm_std::BankQuery; - use cosmwasm_std::Binary; - use cosmwasm_std::Empty; - use cosmwasm_std::StakingQuery; - use ibc_chain_registry::chain::ChainData; - use tokio::runtime::Runtime; - use tonic::transport::Channel; - use std::marker::PhantomData; - use std::str::FromStr; - use cosmwasm_std::testing::{MockApi, MockStorage}; - use cosmwasm_std::{ - from_slice, to_binary, Coin, ContractResult, OwnedDeps, Querier, QuerierResult, - QueryRequest, SystemError, SystemResult, Uint128, WasmQuery, - }; - use crate::channel::GrpcChannel; - fn to_cosmwasm_coin(c: cosmrs::proto::cosmos::base::v1beta1::Coin) -> Coin { - Coin { - amount: Uint128::from_str(&c.amount).unwrap(), - denom: c.denom, - } - } - const QUERIER_ERROR: &str = "Only Bank balances and Wasm (raw + smart) and Some staking queries are covered for now"; - /// mock_dependencies is a drop-in replacement for cosmwasm_std::testing::mock_dependencies - /// this uses our CustomQuerier. - pub fn mock_dependencies( - chain_info: ChainData, - ) -> OwnedDeps { - let custom_querier: WasmMockQuerier = WasmMockQuerier::new(chain_info); - OwnedDeps { - storage: MockStorage::default(), - api: MockApi::default(), - querier: custom_querier, - custom_query_type: PhantomData, - } - } - /// Querier struct that fetches queries on-chain directly - pub struct WasmMockQuerier { - channel: Channel, - runtime: Runtime, - } - impl Querier for WasmMockQuerier { - fn raw_query(&self, bin_request: &[u8]) -> QuerierResult { - let request: QueryRequest = match from_slice(bin_request) { - Ok(v) => v, - Err(e) => { - return SystemResult::Err(SystemError::InvalidRequest { - error: { - let res = ::alloc::fmt::format( - format_args!("Parsing query request: {0}", e), - ); - res - }, - request: bin_request.into(), - }); - } - }; - self.handle_query(&request) - } - } - impl WasmMockQuerier { - /// Function used to handle a query and customize the query behavior - /// This implements some queries by querying an actual node for the responses - pub fn handle_query(&self, request: &QueryRequest) -> QuerierResult { - match &request { - QueryRequest::Wasm(x) => { - let querier = CosmWasm::new(self.channel.clone()); - match x { - WasmQuery::Smart { contract_addr, msg } => { - let query_result: Result = self - .runtime - .block_on( - querier - .contract_state(contract_addr.to_string(), msg.to_vec()), - ) - .map(|query_result| query_result.into()); - SystemResult::Ok(ContractResult::from(query_result)) - } - WasmQuery::Raw { contract_addr, key } => { - let query_result = self - .runtime - .block_on( - querier - .contract_raw_state(contract_addr.to_string(), key.to_vec()), - ) - .map(|query_result| query_result.data.into()); - SystemResult::Ok(ContractResult::from(query_result)) - } - _ => { - SystemResult::Err(SystemError::InvalidRequest { - error: QUERIER_ERROR.to_string(), - request: to_binary(&request).unwrap(), - }) - } - } - } - QueryRequest::Bank(x) => { - let querier = Bank::new(self.channel.clone()); - match x { - BankQuery::Balance { address, denom } => { - let query_result = self - .runtime - .block_on(querier.balance(address, Some(denom.clone()))) - .map(|result| { - to_binary( - &BalanceResponse { - amount: Coin { - amount: Uint128::from_str(&result[0].amount).unwrap(), - denom: result[0].denom.clone(), - }, - }, - ) - .unwrap() - }); - SystemResult::Ok(ContractResult::from(query_result)) - } - BankQuery::AllBalances { address } => { - let query_result = self - .runtime - .block_on(querier.balance(address, None)) - .map(|result| AllBalanceResponse { - amount: result - .into_iter() - .map(|c| Coin { - amount: Uint128::from_str(&c.amount).unwrap(), - denom: c.denom, - }) - .collect(), - }) - .map(|query_result| to_binary(&query_result)) - .unwrap(); - SystemResult::Ok(ContractResult::from(query_result)) - } - _ => { - SystemResult::Err(SystemError::InvalidRequest { - error: QUERIER_ERROR.to_string(), - request: to_binary(&request).unwrap(), - }) - } - } - } - QueryRequest::Staking(x) => { - let querier = Staking::new(self.channel.clone()); - match x { - StakingQuery::BondedDenom {} => { - let query_result = self - .runtime - .block_on(querier.params()) - .map(|result| BondedDenomResponse { - denom: result.params.unwrap().bond_denom, - }) - .map(|query_result| to_binary(&query_result)) - .unwrap(); - SystemResult::Ok(ContractResult::from(query_result)) - } - StakingQuery::AllDelegations { delegator } => { - let query_result = self - .runtime - .block_on(querier.delegator_delegations(delegator, None)) - .map(|result| AllDelegationsResponse { - delegations: result - .delegation_responses - .into_iter() - .filter_map(|delegation| { - delegation - .delegation - .map(|d| Delegation { - delegator: Addr::unchecked(d.delegator_address), - validator: d.validator_address, - amount: to_cosmwasm_coin(delegation.balance.unwrap()), - }) - }) - .collect(), - }) - .map(|query_result| to_binary(&query_result)) - .unwrap(); - SystemResult::Ok(ContractResult::from(query_result)) - } - _ => ::core::panicking::panic("not yet implemented"), - } - } - _ => { - SystemResult::Err(SystemError::InvalidRequest { - error: QUERIER_ERROR.to_string(), - request: to_binary(&request).unwrap(), - }) - } - } - } - } - impl WasmMockQuerier { - /// Creates a querier from chain information - pub fn new(chain: ChainData) -> Self { - let rt = Runtime::new().unwrap(); - let channel = rt - .block_on(GrpcChannel::connect(&chain.apis.grpc, &chain.chain_id)) - .unwrap(); - WasmMockQuerier { - channel, - runtime: rt, - } - } - } -} -pub mod queriers { - //! # DaemonQuerier - //! - //! DaemonAsync queriers are gRPC query clients for the CosmosSDK modules. They can be used to query the different modules (Bank, Ibc, Authz, ...). - //! - //! ## Usage - //! - //! You will need to acquire a [gRPC channel](Channel) to a running CosmosSDK node to be able to use the queriers. - //! Here is an example of how to acquire one using the DaemonAsync builder. - //! - //! ```no_run - //! // require the querier you want to use, in this case Node - //! use cw_orch_daemon::{queriers::Node, DaemonAsync, networks, queriers::DaemonQuerier}; - //! # tokio_test::block_on(async { - //! // call the builder and configure it as you need - //! let daemon = DaemonAsync::builder() - //! .chain(networks::LOCAL_JUNO) - //! .build() - //! .await.unwrap(); - //! // now you can use the Node querier: - //! let node = Node::new(daemon.channel()); - //! let node_info = node.info(); - //! # }) - //! ``` - mod bank { - use crate::{cosmos_modules, error::DaemonError}; - use cosmrs::proto::cosmos::{ - base::{query::v1beta1::PageRequest, v1beta1::Coin}, - bank::v1beta1::QueryBalanceResponse, - }; - use tonic::transport::Channel; - use super::DaemonQuerier; - /// Queries for Cosmos Bank Module - pub struct Bank { - channel: Channel, - } - impl DaemonQuerier for Bank { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Bank { - /// Query the bank balance of a given address - /// If denom is None, returns all balances - pub async fn balance( - &self, - address: impl Into, - denom: Option, - ) -> Result, DaemonError> { - use cosmos_modules::bank::query_client::QueryClient; - use cosmos_modules::bank::QueryBalanceRequest; - match denom { - Some(denom) => { - let resp = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QueryBalanceRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryBalanceRequest { - address: address.into(), - denom: denom, - }; - let response = client - .balance(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 30u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - let coin = resp.balance.unwrap(); - Ok( - <[_]>::into_vec( - #[rustc_box] - ::alloc::boxed::Box::new([coin]), - ), - ) - } - None => { - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = cosmos_modules::bank::QueryAllBalancesRequest { - address: address.into(), - ..Default::default() - }; - let resp = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QueryAllBalancesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryAllBalancesRequest { - address: address.into(), - pagination: None, - }; - let response = client - .balance(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 49u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - let resp = client.all_balances(request).await?.into_inner(); - let coins = resp.balances; - Ok(coins.into_iter().collect()) - } - } - } - /// Query spendable balance for address - pub async fn spendable_balances( - &self, - address: impl Into, - ) -> Result, DaemonError> { - let spendable_balances: cosmos_modules::bank::QuerySpendableBalancesResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QuerySpendableBalancesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QuerySpendableBalancesRequest { - address: address.into(), - pagination: None, - }; - let response = client - .spendable_balances(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 71u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(spendable_balances.balances) - } - /// Query total supply in the bank - pub async fn total_supply(&self) -> Result, DaemonError> { - let total_supply: cosmos_modules::bank::QueryTotalSupplyResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QueryTotalSupplyRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryTotalSupplyRequest { - pagination: None, - }; - let response = client - .total_supply(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 85u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(total_supply.supply) - } - /// Query total supply in the bank for a denom - pub async fn supply_of( - &self, - denom: impl Into, - ) -> Result { - let supply_of: cosmos_modules::bank::QuerySupplyOfResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QuerySupplyOfRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QuerySupplyOfRequest { - denom: denom.into(), - }; - let response = client.supply_of(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 96u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(supply_of.amount.unwrap()) - } - /// Query params - pub async fn params( - &self, - ) -> Result { - let params: cosmos_modules::bank::QueryParamsResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QueryParamsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryParamsRequest {}; - let response = client.params(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 110u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(params.params.unwrap()) - } - /// Query denom metadata - pub async fn denom_metadata( - &self, - denom: impl Into, - ) -> Result { - let denom_metadata: cosmos_modules::bank::QueryDenomMetadataResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QueryDenomMetadataRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDenomMetadataRequest { - denom: denom.into(), - }; - let response = client - .denom_metadata(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 119u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(denom_metadata.metadata.unwrap()) - } - /// Query denoms metadata with pagination - /// - /// see [PageRequest] for pagination - pub async fn denoms_metadata( - &self, - pagination: Option, - ) -> Result, DaemonError> { - let denoms_metadata: cosmos_modules::bank::QueryDenomsMetadataResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QueryDenomsMetadataRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDenomsMetadataRequest { - pagination: pagination, - }; - let response = client - .denoms_metadata(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 137u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(denoms_metadata.metadatas) - } - } - } - mod cosmwasm { - use crate::{cosmos_modules, error::DaemonError}; - use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; - use tonic::transport::Channel; - use super::DaemonQuerier; - /// Querier for the CosmWasm SDK module - pub struct CosmWasm { - channel: Channel, - } - impl DaemonQuerier for CosmWasm { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl CosmWasm { - /// Query code_id by hash - pub async fn code_id_hash( - &self, - code_id: u64, - ) -> Result { - use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryCodeRequest { code_id }; - let resp = client.code(request).await?.into_inner(); - let contract_hash = resp.code_info.unwrap().data_hash; - let on_chain_hash = base16::encode_lower(&contract_hash); - Ok(on_chain_hash) - } - /// Query contract info - pub async fn contract_info( - &self, - address: impl Into, - ) -> Result { - use cosmos_modules::cosmwasm::{ - query_client::*, QueryContractInfoRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryContractInfoRequest { - address: address.into(), - }; - let resp = client.contract_info(request).await?.into_inner(); - let contract_info = resp.contract_info.unwrap(); - Ok(contract_info) - } - /// Query contract history - pub async fn contract_history( - &self, - address: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::cosmwasm::QueryContractHistoryResponse, - DaemonError, - > { - use cosmos_modules::cosmwasm::{ - query_client::*, QueryContractHistoryRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryContractHistoryRequest { - address: address.into(), - pagination, - }; - Ok(client.contract_history(request).await?.into_inner()) - } - /// Query contract state - pub async fn contract_state( - &self, - address: impl Into, - query_data: Vec, - ) -> Result, DaemonError> { - use cosmos_modules::cosmwasm::{ - query_client::*, QuerySmartContractStateRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QuerySmartContractStateRequest { - address: address.into(), - query_data, - }; - Ok(client.smart_contract_state(request).await?.into_inner().data) - } - /// Query all contract state - pub async fn all_contract_state( - &self, - address: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::cosmwasm::QueryAllContractStateResponse, - DaemonError, - > { - use cosmos_modules::cosmwasm::{ - query_client::*, QueryAllContractStateRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryAllContractStateRequest { - address: address.into(), - pagination, - }; - Ok(client.all_contract_state(request).await?.into_inner()) - } - /// Query code - pub async fn code( - &self, - code_id: u64, - ) -> Result { - use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryCodeRequest { code_id }; - Ok(client.code(request).await?.into_inner().code_info.unwrap()) - } - /// Query code bytes - pub async fn code_data(&self, code_id: u64) -> Result, DaemonError> { - use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryCodeRequest { code_id }; - Ok(client.code(request).await?.into_inner().data) - } - /// Query codes - pub async fn codes( - &self, - pagination: Option, - ) -> Result, DaemonError> { - use cosmos_modules::cosmwasm::{query_client::*, QueryCodesRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryCodesRequest { pagination }; - Ok(client.codes(request).await?.into_inner().code_infos) - } - /// Query pinned codes - pub async fn pinned_codes( - &self, - ) -> Result< - cosmos_modules::cosmwasm::QueryPinnedCodesResponse, - DaemonError, - > { - use cosmos_modules::cosmwasm::{query_client::*, QueryPinnedCodesRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryPinnedCodesRequest { - pagination: None, - }; - Ok(client.pinned_codes(request).await?.into_inner()) - } - /// Query contracts by code - pub async fn contract_by_codes( - &self, - code_id: u64, - ) -> Result< - cosmos_modules::cosmwasm::QueryContractsByCodeResponse, - DaemonError, - > { - use cosmos_modules::cosmwasm::{ - query_client::*, QueryContractsByCodeRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryContractsByCodeRequest { - code_id, - pagination: None, - }; - Ok(client.contracts_by_code(request).await?.into_inner()) - } - /// Query raw contract state - pub async fn contract_raw_state( - &self, - address: impl Into, - query_data: Vec, - ) -> Result< - cosmos_modules::cosmwasm::QueryRawContractStateResponse, - DaemonError, - > { - use cosmos_modules::cosmwasm::{ - query_client::*, QueryRawContractStateRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryRawContractStateRequest { - address: address.into(), - query_data, - }; - Ok(client.raw_contract_state(request).await?.into_inner()) - } - /// Query params - pub async fn params( - &self, - ) -> Result { - use cosmos_modules::cosmwasm::{query_client::*, QueryParamsRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - Ok(client.params(QueryParamsRequest {}).await?.into_inner()) - } - } - } - mod feegrant { - use crate::{cosmos_modules, error::DaemonError}; - use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; - use tonic::transport::Channel; - use super::DaemonQuerier; - /// Querier for the Cosmos Gov module - pub struct Feegrant { - channel: Channel, - } - impl DaemonQuerier for Feegrant { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Feegrant { - /// Query all allowances granted to the grantee address by a granter address - pub async fn allowance( - &self, - granter: impl Into, - grantee: impl Into, - ) -> Result { - let allowance: cosmos_modules::feegrant::QueryAllowanceResponse = { - use crate::cosmos_modules::feegrant::{ - query_client::QueryClient, QueryAllowanceRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryAllowanceRequest { - granter: granter.into(), - grantee: grantee.into(), - }; - let response = client.allowance(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::feegrant", - "cw_orch_daemon::queriers::feegrant", - "cw-orch-daemon/src/queriers/feegrant.rs", - ), - 25u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(allowance.allowance.unwrap()) - } - /// Query allowances for grantee address with a given pagination - /// - /// see [PageRequest] for pagination - pub async fn allowances( - &self, - grantee: impl Into, - pagination: Option, - ) -> Result, DaemonError> { - let allowances: cosmos_modules::feegrant::QueryAllowancesResponse = { - use crate::cosmos_modules::feegrant::{ - query_client::QueryClient, QueryAllowancesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryAllowancesRequest { - grantee: grantee.into(), - pagination: pagination, - }; - let response = client - .allowances(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::feegrant", - "cw_orch_daemon::queriers::feegrant", - "cw-orch-daemon/src/queriers/feegrant.rs", - ), - 45u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(allowances.allowances) - } - } - } - mod gov { - use crate::{cosmos_modules, error::DaemonError}; - use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; - use tonic::transport::Channel; - use super::DaemonQuerier; - /// Querier for the Cosmos Gov module - pub struct Gov { - channel: Channel, - } - impl DaemonQuerier for Gov { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Gov { - /// Query proposal details by proposal id - pub async fn proposal( - &self, - proposal_id: u64, - ) -> Result { - let proposal: cosmos_modules::gov::QueryProposalResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryProposalRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryProposalRequest { - proposal_id: proposal_id, - }; - let response = client.proposal(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 24u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(proposal.proposal.unwrap()) - } - /// Query proposals based on given status - /// - /// see [PageRequest] for pagination - pub async fn proposals( - &self, - proposal_status: GovProposalStatus, - voter: impl Into, - depositor: impl Into, - pagination: Option, - ) -> Result { - let proposals: cosmos_modules::gov::QueryProposalsResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryProposalsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryProposalsRequest { - proposal_status: proposal_status as i32, - voter: voter.into(), - depositor: depositor.into(), - pagination: pagination, - }; - let response = client.proposals(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 45u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(proposals) - } - /// Query voted information based on proposal_id for voter address - pub async fn vote( - &self, - proposal_id: u64, - voter: impl Into, - ) -> Result { - let vote: cosmos_modules::gov::QueryVoteResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryVoteRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryVoteRequest { - proposal_id: proposal_id, - voter: voter.into(), - }; - let response = client.vote(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 65u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(vote.vote.unwrap()) - } - /// Query votes of a given proposal - /// - /// see [PageRequest] for pagination - pub async fn votes( - &self, - proposal_id: impl Into, - pagination: Option, - ) -> Result { - let votes: cosmos_modules::gov::QueryVotesResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryVotesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryVotesRequest { - proposal_id: proposal_id.into(), - pagination: pagination, - }; - let response = client.votes(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 85u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(votes) - } - /// Query all parameters of the gov module - pub async fn params( - &self, - params_type: impl Into, - ) -> Result { - let params: cosmos_modules::gov::QueryParamsResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryParamsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryParamsRequest { - params_type: params_type.into(), - }; - let response = client.params(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 102u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(params) - } - /// Query deposit information using proposal_id and depositor address - pub async fn deposit( - &self, - proposal_id: u64, - depositor: impl Into, - ) -> Result { - let deposit: cosmos_modules::gov::QueryDepositResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryDepositRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDepositRequest { - proposal_id: proposal_id, - depositor: depositor.into(), - }; - let response = client.deposit(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 119u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(deposit.deposit.unwrap()) - } - /// Query deposits of a proposal - /// - /// see [PageRequest] for pagination - pub async fn deposits( - &self, - proposal_id: u64, - pagination: Option, - ) -> Result { - let deposits: cosmos_modules::gov::QueryDepositsResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryDepositsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDepositsRequest { - proposal_id: proposal_id, - pagination: pagination, - }; - let response = client.deposits(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 139u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(deposits) - } - /// TallyResult queries the tally of a proposal vote. - pub async fn tally_result( - &mut self, - proposal_id: u64, - ) -> Result { - let tally_result: cosmos_modules::gov::QueryTallyResultResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryTallyResultRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryTallyResultRequest { - proposal_id: proposal_id, - }; - let response = client - .tally_result(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 156u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(tally_result.tally.unwrap()) - } - } - /// Proposal status - #[allow(missing_docs)] - pub enum GovProposalStatus { - Unspecified = 0, - DepositPeriod = 1, - VotingPeriod = 2, - Passed = 3, - Rejected = 4, - Failed = 5, - } - } - mod ibc { - use super::DaemonQuerier; - use crate::{cosmos_modules, error::DaemonError}; - use cosmos_modules::ibc_channel; - use cosmrs::proto::ibc::{ - applications::transfer::v1::{DenomTrace, QueryDenomTraceResponse}, - core::{ - channel::v1::QueryPacketCommitmentResponse, - client::v1::{IdentifiedClientState, QueryClientStatesResponse}, - connection::v1::{IdentifiedConnection, State}, - }, - lightclients::tendermint::v1::ClientState, - }; - use prost::Message; - use tonic::transport::Channel; - /// Querier for the Cosmos IBC module - pub struct Ibc { - channel: Channel, - } - impl DaemonQuerier for Ibc { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Ibc { - /// Get the trace of a specific denom - pub async fn denom_trace( - &self, - hash: String, - ) -> Result { - let denom_trace: QueryDenomTraceResponse = { - use crate::cosmos_modules::ibc_transfer::{ - query_client::QueryClient, QueryDenomTraceRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDenomTraceRequest { - hash: hash, - }; - let response = client - .denom_trace(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 32u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(denom_trace.denom_trace.unwrap()) - } - /// Get all the IBC clients for this daemon - pub async fn clients( - &self, - ) -> Result, DaemonError> { - let ibc_clients: QueryClientStatesResponse = { - use crate::cosmos_modules::ibc_client::{ - query_client::QueryClient, QueryClientStatesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryClientStatesRequest { - pagination: None, - }; - let response = client - .client_states(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 45u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_clients.client_states) - } - /// Get the state of a specific IBC client - pub async fn client_state( - &self, - client_id: impl ToString, - ) -> Result< - cosmos_modules::ibc_client::QueryClientStateResponse, - DaemonError, - > { - let response: cosmos_modules::ibc_client::QueryClientStateResponse = { - use crate::cosmos_modules::ibc_client::{ - query_client::QueryClient, QueryClientStateRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryClientStateRequest { - client_id: client_id.to_string(), - }; - let response = client - .client_state(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 60u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(response) - } - /// Get the consensus state of a specific IBC client - 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 = { - use crate::cosmos_modules::ibc_client::{ - query_client::QueryClient, QueryConsensusStatesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryConsensusStatesRequest { - client_id: client_id, - pagination: None, - }; - let response = client - .consensus_states(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 77u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(response) - } - /// Get the consensus status of a specific IBC client - pub async fn client_status( - &self, - client_id: impl ToString, - ) -> Result< - cosmos_modules::ibc_client::QueryClientStatusResponse, - DaemonError, - > { - let response: cosmos_modules::ibc_client::QueryClientStatusResponse = { - use crate::cosmos_modules::ibc_client::{ - query_client::QueryClient, QueryClientStatusRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryClientStatusRequest { - client_id: client_id.to_string(), - }; - let response = client - .client_status(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 95u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(response) - } - /// Get the ibc client parameters - pub async fn client_params( - &self, - ) -> Result< - cosmos_modules::ibc_client::QueryClientParamsResponse, - DaemonError, - > { - let response: cosmos_modules::ibc_client::QueryClientParamsResponse = { - use crate::cosmos_modules::ibc_client::{ - query_client::QueryClient, QueryClientParamsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryClientParamsRequest {}; - let response = client - .client_params(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 111u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(response) - } - /// Query the IBC connections for a specific chain - pub async fn connections( - &self, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_connection::QueryConnectionsResponse; - let ibc_connections: QueryConnectionsResponse = { - use crate::cosmos_modules::ibc_connection::{ - query_client::QueryClient, QueryConnectionsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryConnectionsRequest { - pagination: None, - }; - let response = client - .connections(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 121u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_connections.connections) - } - /// Search for open connections with a specific chain. - pub async fn open_connections( - &self, - client_chain_id: impl ToString, - ) -> Result, 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) - } - /// Get all the connections for this client - pub async fn client_connections( - &self, - client_id: impl Into, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_connection::QueryClientConnectionsResponse; - let client_id = client_id.into(); - let ibc_client_connections: QueryClientConnectionsResponse = { - use crate::cosmos_modules::ibc_connection::{ - query_client::QueryClient, QueryClientConnectionsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryClientConnectionsRequest { - client_id: client_id.clone(), - }; - let response = client - .client_connections(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 163u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_client_connections.connection_paths) - } - /// Get the (tendermint) client state for a specific connection - pub async fn connection_client( - &self, - connection_id: impl Into, - ) -> Result { - use cosmos_modules::ibc_connection::QueryConnectionClientStateResponse; - let connection_id = connection_id.into(); - let ibc_connection_client: QueryConnectionClientStateResponse = { - use crate::cosmos_modules::ibc_connection::{ - query_client::QueryClient, QueryConnectionClientStateRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryConnectionClientStateRequest { - connection_id: connection_id.clone(), - }; - let response = client - .connection_client_state(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 183u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - let client_state = ibc_connection_client - .identified_client_state - .ok_or( - DaemonError::ibc_err({ - let res = ::alloc::fmt::format( - format_args!( - "error identifying client for connection {0}", - connection_id, - ), - ); - res - }), - )?; - let client_state = ClientState::decode( - client_state.client_state.unwrap().value.as_slice(), - ) - .map_err(|e| DaemonError::ibc_err({ - let res = ::alloc::fmt::format( - format_args!("error decoding client state: {0}", e), - ); - res - }))?; - Ok(client_state) - } - /// Get the channel for a specific port and channel id - pub async fn channel( - &self, - port_id: impl Into, - channel_id: impl Into, - ) -> Result { - use cosmos_modules::ibc_channel::QueryChannelResponse; - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_channel: QueryChannelResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryChannelRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryChannelRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }; - let response = client.channel(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 218u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - ibc_channel - .channel - .ok_or( - DaemonError::ibc_err({ - let res = ::alloc::fmt::format( - format_args!( - "error fetching channel {0} on port {1}", - channel_id, - port_id, - ), - ); - res - }), - ) - } - /// Get all the channels for a specific connection - pub async fn connection_channels( - &self, - connection_id: impl Into, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_channel::QueryConnectionChannelsResponse; - let connection_id = connection_id.into(); - let ibc_connection_channels: QueryConnectionChannelsResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryConnectionChannelsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryConnectionChannelsRequest { - connection: connection_id.clone(), - pagination: None, - }; - let response = client - .connection_channels(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 242u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_connection_channels.channels) - } - /// Get the client state for a specific channel and port - pub async fn channel_client_state( - &self, - port_id: impl Into, - channel_id: impl Into, - ) -> Result { - use cosmos_modules::ibc_channel::QueryChannelClientStateResponse; - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_channel_client_state: QueryChannelClientStateResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryChannelClientStateRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryChannelClientStateRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }; - let response = client - .channel_client_state(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 265u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - ibc_channel_client_state - .identified_client_state - .ok_or( - DaemonError::ibc_err({ - let res = ::alloc::fmt::format( - format_args!( - "error identifying client for channel {0} on port {1}", - channel_id, - port_id, - ), - ); - res - }), - ) - } - /// Get all the packet commitments for a specific channel and port - pub async fn packet_commitments( - &self, - port_id: impl Into, - channel_id: impl Into, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_channel::QueryPacketCommitmentsResponse; - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_commitments: QueryPacketCommitmentsResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryPacketCommitmentsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPacketCommitmentsRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - pagination: None, - }; - let response = client - .packet_commitments(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 297u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_commitments.commitments) - } - /// Get the packet commitment for a specific channel, port and sequence - pub async fn packet_commitment( - &self, - port_id: impl Into, - channel_id: impl Into, - sequence: u64, - ) -> Result { - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_commitment: QueryPacketCommitmentResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryPacketCommitmentRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPacketCommitmentRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - sequence: sequence, - }; - let response = client - .packet_commitment(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 320u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_commitment) - } - /// Returns if the packet is received on the connected chain. - pub async fn packet_receipt( - &self, - port_id: impl Into, - channel_id: impl Into, - sequence: u64, - ) -> Result { - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_receipt: ibc_channel::QueryPacketReceiptResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryPacketReceiptRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPacketReceiptRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - sequence: sequence, - }; - let response = client - .packet_receipt(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 345u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_receipt.received) - } - /// Get all the packet acknowledgements for a specific channel, port and commitment sequences - pub async fn packet_acknowledgements( - &self, - port_id: impl Into, - channel_id: impl Into, - packet_commitment_sequences: Vec, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_channel::QueryPacketAcknowledgementsResponse; - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_acknowledgements: QueryPacketAcknowledgementsResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryPacketAcknowledgementsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPacketAcknowledgementsRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - packet_commitment_sequences: packet_commitment_sequences, - pagination: None, - }; - let response = client - .packet_acknowledgements(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 372u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_acknowledgements.acknowledgements) - } - /// Get the packet acknowledgement for a specific channel, port and sequence - pub async fn packet_acknowledgement( - &self, - port_id: impl Into, - channel_id: impl Into, - sequence: u64, - ) -> Result, DaemonError> { - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_acknowledgement: ibc_channel::QueryPacketAcknowledgementResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryPacketAcknowledgementRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPacketAcknowledgementRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - sequence: sequence, - }; - let response = client - .packet_acknowledgement(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 396u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_acknowledgement.acknowledgement) - } - /// No acknowledgement exists on receiving chain for the given packet commitment sequence on sending chain. - /// Returns the packet sequences that have not yet been received. - pub async fn unreceived_packets( - &self, - port_id: impl Into, - channel_id: impl Into, - packet_commitment_sequences: Vec, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_channel::QueryUnreceivedPacketsResponse; - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_unreceived: QueryUnreceivedPacketsResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryUnreceivedPacketsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryUnreceivedPacketsRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - packet_commitment_sequences: packet_commitment_sequences, - }; - let response = client - .unreceived_packets(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 422u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_unreceived.sequences) - } - /// Returns the acknowledgement sequences that have not yet been received. - /// Given a list of acknowledgement sequences from counterparty, determine if an ack on the counterparty chain has been received on the executing chain. - /// Returns the list of acknowledgement sequences that have not yet been received. - pub async fn unreceived_acks( - &self, - port_id: impl Into, - channel_id: impl Into, - packet_ack_sequences: Vec, - ) -> Result, DaemonError> { - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_unreceived: ibc_channel::QueryUnreceivedAcksResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryUnreceivedAcksRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryUnreceivedAcksRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - packet_ack_sequences: packet_ack_sequences, - }; - let response = client - .unreceived_acks(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 447u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_unreceived.sequences) - } - /// Returns the acknowledgement sequences that have not yet been received. - /// Given a list of acknowledgement sequences from counterparty, determine if an ack on the counterparty chain has been received on the executing chain. - /// Returns the list of acknowledgement sequences that have not yet been received. - pub async fn next_sequence_receive( - &self, - port_id: impl Into, - channel_id: impl Into, - ) -> Result { - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let next_receive: ibc_channel::QueryNextSequenceReceiveResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryNextSequenceReceiveRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryNextSequenceReceiveRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }; - let response = client - .next_sequence_receive(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 471u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(next_receive.next_sequence_receive) - } - } - } - mod node { - use std::{cmp::min, time::Duration}; - use crate::{cosmos_modules, error::DaemonError, tx_resp::CosmTxResponse}; - use cosmrs::{ - proto::cosmos::{ - base::query::v1beta1::PageRequest, tx::v1beta1::SimulateResponse, - }, - tendermint::{Block, Time}, - }; - use tonic::transport::Channel; - use super::DaemonQuerier; - const MAX_TX_QUERY_RETRIES: usize = 50; - /// Querier for the Tendermint node. - /// Supports queries for block and tx information - pub struct Node { - channel: Channel, - } - impl DaemonQuerier for Node { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Node { - /// Returns node info - pub async fn info( - &self, - ) -> Result { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_node_info(cosmos_modules::tendermint::GetNodeInfoRequest { - }) - .await? - .into_inner(); - Ok(resp) - } - /// Queries node syncing - pub async fn syncing(&self) -> Result { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_syncing(cosmos_modules::tendermint::GetSyncingRequest { - }) - .await? - .into_inner(); - Ok(resp.syncing) - } - /// Returns latests block information - pub async fn latest_block(&self) -> Result { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_latest_block(cosmos_modules::tendermint::GetLatestBlockRequest { - }) - .await? - .into_inner(); - Ok(Block::try_from(resp.block.unwrap())?) - } - /// Returns block information fetched by height - pub async fn block_by_height( - &self, - height: u64, - ) -> Result { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_block_by_height(cosmos_modules::tendermint::GetBlockByHeightRequest { - height: height as i64, - }) - .await? - .into_inner(); - Ok(Block::try_from(resp.block.unwrap())?) - } - /// Return the average block time for the last 50 blocks or since inception - /// This is used to estimate the time when a tx will be included in a block - pub async fn average_block_speed( - &self, - multiplier: Option, - ) -> Result { - let mut latest_block = self.latest_block().await?; - let latest_block_time = latest_block.header.time; - let mut latest_block_height = latest_block.header.height.value(); - while latest_block_height <= 1 { - tokio::time::sleep(Duration::from_secs(1)).await; - latest_block = self.latest_block().await?; - latest_block_height = latest_block.header.height.value(); - } - let avg_period = min(latest_block_height - 1, 50); - let block_avg_period_ago = self - .block_by_height(latest_block_height - avg_period) - .await?; - let block_avg_period_ago_time = block_avg_period_ago.header.time; - let average_block_time = latest_block_time - .duration_since(block_avg_period_ago_time)?; - let average_block_time = average_block_time.as_secs() / avg_period; - let average_block_time = match multiplier { - Some(multiplier) => (average_block_time as f32 * multiplier) as u64, - None => average_block_time, - }; - Ok(std::cmp::max(average_block_time, 1)) - } - /// Returns latests validator set - pub async fn latest_validator_set( - &self, - pagination: Option, - ) -> Result< - cosmos_modules::tendermint::GetLatestValidatorSetResponse, - DaemonError, - > { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_latest_validator_set(cosmos_modules::tendermint::GetLatestValidatorSetRequest { - pagination, - }) - .await? - .into_inner(); - Ok(resp) - } - /// Returns latests validator set fetched by height - pub async fn validator_set_by_height( - &self, - height: i64, - pagination: Option, - ) -> Result< - cosmos_modules::tendermint::GetValidatorSetByHeightResponse, - DaemonError, - > { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_validator_set_by_height(cosmos_modules::tendermint::GetValidatorSetByHeightRequest { - height, - pagination, - }) - .await? - .into_inner(); - Ok(resp) - } - /// Returns current block height - pub async fn block_height(&self) -> Result { - let block = self.latest_block().await?; - Ok(block.header.height.value()) - } - /// Returns the block timestamp (since unix epoch) in nanos - pub async fn block_time(&self) -> Result { - let block = self.latest_block().await?; - Ok(block.header.time.duration_since(Time::unix_epoch())?.as_nanos()) - } - /// Simulate TX - pub async fn simulate_tx( - &self, - tx_bytes: Vec, - ) -> Result { - let mut client = cosmos_modules::tx::service_client::ServiceClient::new( - self.channel.clone(), - ); - #[allow(deprecated)] - let resp: SimulateResponse = client - .simulate(cosmos_modules::tx::SimulateRequest { - tx: None, - tx_bytes, - }) - .await? - .into_inner(); - let gas_used = resp.gas_info.unwrap().gas_used; - Ok(gas_used) - } - /// Returns all the block info - pub async fn block_info( - &self, - ) -> Result { - let block = self.latest_block().await?; - let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; - let time = cosmwasm_std::Timestamp::from_nanos( - since_epoch.as_nanos() as u64, - ); - Ok(cosmwasm_std::BlockInfo { - height: block.header.height.value(), - time, - chain_id: block.header.chain_id.to_string(), - }) - } - /// Find TX by hash - pub async fn find_tx( - &self, - hash: String, - ) -> Result { - self.find_tx_with_retries(hash, MAX_TX_QUERY_RETRIES).await - } - /// Find TX by hash with a given amount of retries - pub async fn find_tx_with_retries( - &self, - hash: String, - retries: usize, - ) -> Result { - let mut client = cosmos_modules::tx::service_client::ServiceClient::new( - self.channel.clone(), - ); - let request = cosmos_modules::tx::GetTxRequest { - hash: hash.clone(), - }; - let mut block_speed = self.average_block_speed(Some(0.7)).await?; - for _ in 0..retries { - match client.get_tx(request.clone()).await { - Ok(tx) => { - let resp = tx.into_inner().tx_response.unwrap(); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("TX found: {0:?}", resp), - lvl, - &( - "cw_orch_daemon::queriers::node", - "cw_orch_daemon::queriers::node", - "cw-orch-daemon/src/queriers/node.rs", - ), - 220u32, - ::log::__private_api::Option::None, - ); - } - }; - return Ok(resp.into()); - } - Err(err) => { - block_speed = (block_speed as f64 * 1.6) as u64; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("TX not found with error: {0:?}", err), - lvl, - &( - "cw_orch_daemon::queriers::node", - "cw_orch_daemon::queriers::node", - "cw-orch-daemon/src/queriers/node.rs", - ), - 226u32, - ::log::__private_api::Option::None, - ); - } - }; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("Waiting {0} seconds", block_speed), - lvl, - &( - "cw_orch_daemon::queriers::node", - "cw_orch_daemon::queriers::node", - "cw-orch-daemon/src/queriers/node.rs", - ), - 227u32, - ::log::__private_api::Option::None, - ); - } - }; - tokio::time::sleep(Duration::from_secs(block_speed)).await; - } - } - } - Err(DaemonError::TXNotFound(hash, retries)) - } - } - } - mod staking { - use crate::{cosmos_modules, error::DaemonError}; - use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; - use tonic::transport::Channel; - use super::DaemonQuerier; - /// Querier for the Cosmos Staking module - pub struct Staking { - channel: Channel, - } - impl DaemonQuerier for Staking { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Staking { - /// Queries validator info for given validator address - pub async fn validator( - &self, - validator_addr: impl Into, - ) -> Result { - let validator: cosmos_modules::staking::QueryValidatorResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryValidatorRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryValidatorRequest { - validator_addr: validator_addr.into(), - }; - let response = client.validator(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 24u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(validator.validator.unwrap()) - } - /// Queries all validators that match the given status - /// - /// see [StakingBondStatus] for available statuses - pub async fn validators( - &self, - status: StakingBondStatus, - ) -> Result, DaemonError> { - let validators: cosmos_modules::staking::QueryValidatorsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryValidatorsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryValidatorsRequest { - status: status.to_string(), - pagination: None, - }; - let response = client - .validators(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 42u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(validators.validators) - } - /// Query validator delegations info for given validator - /// - /// see [PageRequest] for pagination - pub async fn delegations( - &self, - validator_addr: impl Into, - pagination: Option, - ) -> Result, DaemonError> { - let validator_delegations: cosmos_modules::staking::QueryValidatorDelegationsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryValidatorDelegationsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryValidatorDelegationsRequest { - validator_addr: validator_addr.into(), - pagination: pagination, - }; - let response = client - .validator_delegations(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 62u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(validator_delegations.delegation_responses) - } - /// Query validator unbonding delegations of a validator - pub async fn unbonding_delegations( - &self, - validator_addr: impl Into, - ) -> Result, DaemonError> { - let validator_unbonding_delegations: cosmos_modules::staking::QueryValidatorUnbondingDelegationsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, - QueryValidatorUnbondingDelegationsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryValidatorUnbondingDelegationsRequest { - validator_addr: validator_addr.into(), - pagination: None, - }; - let response = client - .validator_unbonding_delegations(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 79u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(validator_unbonding_delegations.unbonding_responses) - } - /// Query delegation info for given validator for a delegator - pub async fn delegation( - &self, - validator_addr: impl Into, - delegator_addr: impl Into, - ) -> Result { - let delegation: cosmos_modules::staking::QueryDelegationResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryDelegationRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDelegationRequest { - validator_addr: validator_addr.into(), - delegator_addr: delegator_addr.into(), - }; - let response = client - .delegation(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 97u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(delegation.delegation_response.unwrap()) - } - /// Query unbonding delegation info for given validator delegator - pub async fn unbonding_delegation( - &self, - validator_addr: impl Into, - delegator_addr: impl Into, - ) -> Result { - let unbonding_delegation: cosmos_modules::staking::QueryUnbondingDelegationResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryUnbondingDelegationRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryUnbondingDelegationRequest { - validator_addr: validator_addr.into(), - delegator_addr: delegator_addr.into(), - }; - let response = client - .unbonding_delegation(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 115u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(unbonding_delegation.unbond.unwrap()) - } - /// Query all delegator delegations of a given delegator address - /// - /// see [PageRequest] for pagination - pub async fn delegator_delegations( - &self, - delegator_addr: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::staking::QueryDelegatorDelegationsResponse, - DaemonError, - > { - let delegator_delegations: cosmos_modules::staking::QueryDelegatorDelegationsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryDelegatorDelegationsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDelegatorDelegationsRequest { - delegator_addr: delegator_addr.into(), - pagination: pagination, - }; - let response = client - .delegator_delegations(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 135u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(delegator_delegations) - } - /// Queries all unbonding delegations of a given delegator address. - /// - /// see [PageRequest] for pagination - pub async fn delegator_unbonding_delegations( - &self, - delegator_addr: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::staking::QueryDelegatorUnbondingDelegationsResponse, - DaemonError, - > { - let delegator_unbonding_delegations: cosmos_modules::staking::QueryDelegatorUnbondingDelegationsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, - QueryDelegatorUnbondingDelegationsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDelegatorUnbondingDelegationsRequest { - delegator_addr: delegator_addr.into(), - pagination: pagination, - }; - let response = client - .delegator_unbonding_delegations(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 156u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(delegator_unbonding_delegations) - } - /// Query redelegations of a given address - /// - /// see [PageRequest] for pagination - pub async fn redelegations( - &self, - delegator_addr: impl Into, - src_validator_addr: impl Into, - dst_validator_addr: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::staking::QueryRedelegationsResponse, - DaemonError, - > { - let redelegations: cosmos_modules::staking::QueryRedelegationsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryRedelegationsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryRedelegationsRequest { - delegator_addr: delegator_addr.into(), - src_validator_addr: src_validator_addr.into(), - dst_validator_addr: dst_validator_addr.into(), - pagination: pagination, - }; - let response = client - .redelegations(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 178u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(redelegations) - } - /// Query delegator validators info for given delegator address. - pub async fn delegator_validator( - &self, - validator_addr: impl Into, - delegator_addr: impl Into, - ) -> Result< - cosmos_modules::staking::QueryDelegatorValidatorResponse, - DaemonError, - > { - let delegator_validator: cosmos_modules::staking::QueryDelegatorValidatorResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryDelegatorValidatorRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDelegatorValidatorRequest { - validator_addr: validator_addr.into(), - delegator_addr: delegator_addr.into(), - }; - let response = client - .delegator_validator(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 198u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(delegator_validator) - } - /// Query delegator validators info for given delegator address - /// - /// see [PageRequest] for pagination - pub async fn delegator_validators( - &self, - delegator_addr: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::staking::QueryDelegatorValidatorsResponse, - DaemonError, - > { - let delegator_validators: cosmos_modules::staking::QueryDelegatorValidatorsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryDelegatorValidatorsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDelegatorValidatorsRequest { - delegator_addr: delegator_addr.into(), - pagination: pagination, - }; - let response = client - .delegator_validators(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 218u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(delegator_validators) - } - /// Query historical info info for given height - pub async fn historical_info( - &self, - height: i64, - ) -> Result< - cosmos_modules::staking::QueryHistoricalInfoResponse, - DaemonError, - > { - let historical_info: cosmos_modules::staking::QueryHistoricalInfoResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryHistoricalInfoRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryHistoricalInfoRequest { - height: height, - }; - let response = client - .historical_info(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 236u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(historical_info) - } - /// Query the pool info - pub async fn pool( - &self, - ) -> Result { - let pool: cosmos_modules::staking::QueryPoolResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryPoolRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPoolRequest {}; - let response = client.pool(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 248u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(pool) - } - /// Query staking parameters - pub async fn params( - &self, - ) -> Result { - let params: cosmos_modules::staking::QueryParamsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryParamsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryParamsRequest {}; - let response = client.params(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 257u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(params) - } - } - /// Staking bond statuses - pub enum StakingBondStatus { - /// UNSPECIFIED defines an invalid validator status. - Unspecified = 0, - /// UNBONDED defines a validator that is not bonded. - Unbonded = 1, - /// UNBONDING defines a validator that is unbonding. - Unbonding = 2, - /// BONDED defines a validator that is bonded. - Bonded = 3, - } - impl ToString for StakingBondStatus { - /// Convert to string - fn to_string(&self) -> String { - match self { - StakingBondStatus::Unspecified => { - "BOND_STATUS_UNSPECIFIED".to_string() - } - StakingBondStatus::Unbonded => "BOND_STATUS_UNBONDED".to_string(), - StakingBondStatus::Unbonding => "BOND_STATUS_UNBONDING".to_string(), - StakingBondStatus::Bonded => "BOND_STATUS_BONDED".to_string(), - } - } - } - } - pub use bank::Bank; - pub use cosmwasm::CosmWasm; - pub use feegrant::Feegrant; - pub use ibc::Ibc; - pub use node::Node; - pub use gov::*; - pub use staking::*; - use tonic::transport::Channel; - /// Constructor for a querier over a given channel - pub trait DaemonQuerier { - /// Construct an new querier over a given channel - fn new(channel: Channel) -> Self; - } -} -mod traits { - use cw_orch_core::{ - contract::interface_traits::{CwOrchMigrate, CwOrchUpload}, - environment::TxResponse, - }; - use crate::{queriers::CosmWasm, Daemon, DaemonError}; - /// Helper methods for conditional uploading of a contract. - pub trait ConditionalUpload: CwOrchUpload { - /// Only upload the contract if it is not uploaded yet (checksum does not match) - fn upload_if_needed(&self) -> Result>, DaemonError> { - if self.latest_is_uploaded()? { - Ok(None) - } else { - Some(self.upload()).transpose().map_err(Into::into) - } - } - /// Returns whether the checksum of the WASM file matches the checksum of the latest uploaded code for this contract. - fn latest_is_uploaded(&self) -> Result { - let Some(latest_uploaded_code_id) = self.code_id().ok() else { - return Ok(false); - }; - let chain = self.get_chain(); - let on_chain_hash = chain - .rt_handle - .block_on( - chain - .query_client::() - .code_id_hash(latest_uploaded_code_id), - )?; - let local_hash = self.wasm().checksum()?; - Ok(local_hash == on_chain_hash) - } - /// Returns whether the contract is running the latest uploaded code for it - fn is_running_latest(&self) -> Result { - let Some(latest_uploaded_code_id) = self.code_id().ok() else { - return Ok(false); - }; - let chain = self.get_chain(); - let info = chain - .rt_handle - .block_on( - chain.query_client::().contract_info(self.address()?), - )?; - Ok(latest_uploaded_code_id == info.code_id) - } - } - impl ConditionalUpload for T - where - T: CwOrchUpload, - {} - /// Helper methods for conditional migration of a contract. - pub trait ConditionalMigrate: CwOrchMigrate + ConditionalUpload { - /// Only migrate the contract if it is not on the latest code-id yet - fn migrate_if_needed( - &self, - migrate_msg: &Self::MigrateMsg, - ) -> Result>, DaemonError> { - if self.is_running_latest()? { - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "{0} is already running the latest code", - self.id(), - ), - lvl, - &( - "cw_orch_daemon::traits", - "cw_orch_daemon::traits", - "cw-orch-daemon/src/traits.rs", - ), - 61u32, - ::log::__private_api::Option::None, - ); - } - }; - Ok(None) - } else { - Some(self.migrate(migrate_msg, self.code_id()?)) - .transpose() - .map_err(Into::into) - } - } - /// Uploads the contract if the local contract hash is different from the latest on-chain code hash. - /// Proceeds to migrates the contract if the contract is not running the latest code. - fn upload_and_migrate_if_needed( - &self, - migrate_msg: &Self::MigrateMsg, - ) -> Result>>, DaemonError> { - let mut txs = Vec::with_capacity(2); - if let Some(tx) = self.upload_if_needed()? { - txs.push(tx); - } - if let Some(tx) = self.migrate_if_needed(migrate_msg)? { - txs.push(tx); - } - if txs.is_empty() { Ok(None) } else { Ok(Some(txs)) } - } - } - impl ConditionalMigrate for T - where - T: CwOrchMigrate + CwOrchUpload, - {} -} -pub mod tx_builder { - use cosmrs::tx::{ModeInfo, SignMode}; - use cosmrs::{ - proto::cosmos::auth::v1beta1::BaseAccount, tendermint::chain::Id, - tx::{self, Body, Fee, Msg, Raw, SequenceNumber, SignDoc, SignerInfo}, - Any, Coin, - }; - use secp256k1::All; - use super::{sender::Sender, DaemonError}; - const GAS_BUFFER: f64 = 1.3; - const BUFFER_THRESHOLD: u64 = 200_000; - const SMALL_GAS_BUFFER: f64 = 1.4; - /// Struct used to build a raw transaction and broadcast it with a sender. - pub struct TxBuilder { - pub(crate) body: Body, - pub(crate) fee_amount: Option, - pub(crate) gas_limit: Option, - pub(crate) sequence: Option, - } - #[automatically_derived] - impl ::core::clone::Clone for TxBuilder { - #[inline] - fn clone(&self) -> TxBuilder { - TxBuilder { - body: ::core::clone::Clone::clone(&self.body), - fee_amount: ::core::clone::Clone::clone(&self.fee_amount), - gas_limit: ::core::clone::Clone::clone(&self.gas_limit), - sequence: ::core::clone::Clone::clone(&self.sequence), - } - } - } - #[automatically_derived] - impl ::core::fmt::Debug for TxBuilder { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field4_finish( - f, - "TxBuilder", - "body", - &self.body, - "fee_amount", - &self.fee_amount, - "gas_limit", - &self.gas_limit, - "sequence", - &&self.sequence, - ) - } - } - impl TxBuilder { - /// Create a new TxBuilder with a given body. - pub fn new(body: Body) -> Self { - Self { - body, - fee_amount: None, - gas_limit: None, - sequence: None, - } - } - /// Set a fixed fee amount for the tx - pub fn fee_amount(&mut self, fee_amount: u128) -> &mut Self { - self.fee_amount = Some(fee_amount); - self - } - /// Set a gas limit for the tx - pub fn gas_limit(&mut self, gas_limit: u64) -> &mut Self { - self.gas_limit = Some(gas_limit); - self - } - /// Set a sequence number for the tx - pub fn sequence(&mut self, sequence: u64) -> &mut Self { - self.sequence = Some(sequence); - self - } - /// Builds the body of the tx with a given memo and timeout. - pub fn build_body( - msgs: Vec, - memo: Option<&str>, - timeout: u64, - ) -> tx::Body { - let msgs = msgs - .into_iter() - .map(Msg::into_any) - .collect::, _>>() - .unwrap(); - tx::Body::new(msgs, memo.unwrap_or_default(), timeout as u32) - } - pub(crate) fn build_fee( - amount: impl Into, - denom: &str, - gas_limit: u64, - ) -> Fee { - let fee = Coin::new(amount.into(), denom).unwrap(); - Fee::from_amount_and_gas(fee, gas_limit) - } - /// Builds the raw tx with a given body and fee and signs it. - /// Sets the TxBuilder's gas limit to its simulated amount for later use. - pub async fn build(&mut self, wallet: &Sender) -> Result { - let BaseAccount { account_number, sequence, .. } = wallet - .base_account() - .await?; - let sequence = self.sequence.unwrap_or(sequence); - let (tx_fee, gas_limit) = if let (Some(fee), Some(gas_limit)) - = (self.fee_amount, self.gas_limit) { - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Using pre-defined fee and gas limits: {0}, {1}", - fee, - gas_limit, - ), - lvl, - &( - "cw_orch_daemon::tx_builder", - "cw_orch_daemon::tx_builder", - "cw-orch-daemon/src/tx_builder.rs", - ), - 91u32, - ::log::__private_api::Option::None, - ); - } - }; - (fee, gas_limit) - } else { - let sim_gas_used = wallet - .calculate_gas(&self.body, sequence, account_number) - .await?; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Simulated gas needed {0:?}", sim_gas_used), - lvl, - &( - "cw_orch_daemon::tx_builder", - "cw_orch_daemon::tx_builder", - "cw-orch-daemon/src/tx_builder.rs", - ), - 101u32, - ::log::__private_api::Option::None, - ); - } - }; - let gas_expected = if sim_gas_used < BUFFER_THRESHOLD { - sim_gas_used as f64 * SMALL_GAS_BUFFER - } else { - sim_gas_used as f64 * GAS_BUFFER - }; - let fee_amount = gas_expected - * (wallet - .daemon_state - .chain_data - .fees - .fee_tokens[0] - .fixed_min_gas_price + 0.00001); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Calculated fee needed: {0:?}", fee_amount), - lvl, - &( - "cw_orch_daemon::tx_builder", - "cw_orch_daemon::tx_builder", - "cw-orch-daemon/src/tx_builder.rs", - ), - 111u32, - ::log::__private_api::Option::None, - ); - } - }; - self.gas_limit = Some(gas_expected as u64); - (fee_amount as u128, gas_expected as u64) - }; - let fee = Self::build_fee( - tx_fee, - &wallet.daemon_state.chain_data.fees.fee_tokens[0].denom, - gas_limit, - ); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "submitting tx: \n fee: {0:?}\naccount_nr: {1:?}\nsequence: {2:?}", - fee, - account_number, - sequence, - ), - lvl, - &( - "cw_orch_daemon::tx_builder", - "cw_orch_daemon::tx_builder", - "cw-orch-daemon/src/tx_builder.rs", - ), - 125u32, - ::log::__private_api::Option::None, - ); - } - }; - let auth_info = SignerInfo { - public_key: wallet.private_key.get_signer_public_key(&wallet.secp), - mode_info: ModeInfo::single(SignMode::Direct), - sequence, - } - .auth_info(fee); - let sign_doc = SignDoc::new( - &self.body, - &auth_info, - &Id::try_from(wallet.daemon_state.chain_data.chain_id.to_string())?, - account_number, - )?; - wallet.sign(sign_doc).map_err(Into::into) - } - } -} -pub use self::{ - builder::*, channel::*, core::*, error::*, state::*, sync::*, traits::*, tx_resp::*, -}; -pub use cw_orch_networks::chain_info::*; -pub use cw_orch_networks::networks; -pub use sender::Wallet; -pub use tx_builder::TxBuilder; -pub(crate) mod cosmos_modules { - pub use cosmrs::proto::{ - cosmos::{ - auth::v1beta1 as auth, authz::v1beta1 as authz, bank::v1beta1 as bank, - base::{ - abci::v1beta1 as abci, tendermint::v1beta1 as tendermint, v1beta1 as base, - }, - crisis::v1beta1 as crisis, distribution::v1beta1 as distribution, - evidence::v1beta1 as evidence, feegrant::v1beta1 as feegrant, - gov::v1beta1 as gov, mint::v1beta1 as mint, params::v1beta1 as params, - slashing::v1beta1 as slashing, staking::v1beta1 as staking, - tx::v1beta1 as tx, vesting::v1beta1 as vesting, - }, - cosmwasm::wasm::v1 as cosmwasm, - ibc::{ - applications::transfer::v1 as ibc_transfer, - core::{ - channel::v1 as ibc_channel, client::v1 as ibc_client, - connection::v1 as ibc_connection, - }, - }, - tendermint::abci as tendermint_abci, - }; -} -/// Re-export trait and data required to fetch daemon data from chain-registry -pub use ibc_chain_registry::{ - chain::ChainData as ChainRegistryData, fetchable::Fetchable, -}; -#![feature(prelude_import)] -//! `Daemon` and `DaemonAsync` execution environments. -//! -//! The `Daemon` type is a synchronous wrapper around the `DaemonAsync` type and can be used as a contract execution environment. -#[prelude_import] -use std::prelude::rust_2021::*; -#[macro_use] -extern crate std; -mod rpc_queriers { - pub mod bank { - use crate::{cosmos_modules, error::DaemonError}; - use cosmrs::{ - proto::cosmos::base::{query::v1beta1::PageRequest, v1beta1::Coin}, - rpc::{HttpClient, Client}, - tx::MessageExt, - }; - use prost::Message; - use super::RpcQuerier; - /// Queries for Cosmos Bank Module - pub struct Bank { - client: HttpClient, - } - impl RpcQuerier for Bank { - fn new(rpc: String) -> Self { - Self { - client: HttpClient::new("https://rpc.osmosis.zone").unwrap(), - } - } - } - impl Bank { - /// Query the bank balance of a given address - /// If denom is None, returns all balances - pub async fn balance( - &self, - address: impl Into, - denom: Option, - ) -> Result, DaemonError> { - match denom { - Some(denom) => { - let resp = (); - let coin = resp.balance.unwrap(); - Ok( - <[_]>::into_vec( - #[rustc_box] - ::alloc::boxed::Box::new([coin]), - ), - ) - } - None => { - use cosmos_modules::bank::QueryAllBalancesResponse; - use cosmos_modules::bank::QueryAllBalancesRequest; - let request = QueryAllBalancesRequest { - address: address.into(), - pagination: None, - }; - let response = self - .client - .abci_query( - Some("/cosmos.bank.v1beta1.Query/AllBalances".to_string()), - request.to_bytes()?, - None, - true, - ) - .await?; - let balance_response = QueryAllBalancesResponse::decode( - response.value.as_slice(), - ) - .unwrap(); - let any = request.to_bytes()?; - let resp = (); - let coins = resp.balances; - Ok(coins.into_iter().collect()) - } - } - } - /// Query spendable balance for address - pub async fn spendable_balances( - &self, - address: impl Into, - ) -> Result, DaemonError> { - let spendable_balances: cosmos_modules::bank::QuerySpendableBalancesResponse = (); - Ok(spendable_balances.balances) - } - /// Query total supply in the bank - pub async fn total_supply(&self) -> Result, DaemonError> { - let total_supply: cosmos_modules::bank::QueryTotalSupplyResponse = (); - Ok(total_supply.supply) - } - /// Query total supply in the bank for a denom - pub async fn supply_of( - &self, - denom: impl Into, - ) -> Result { - let supply_of: cosmos_modules::bank::QuerySupplyOfResponse = (); - Ok(supply_of.amount.unwrap()) - } - /// Query params - pub async fn params( - &self, - ) -> Result { - let params: cosmos_modules::bank::QueryParamsResponse = (); - Ok(params.params.unwrap()) - } - /// Query denom metadata - pub async fn denom_metadata( - &self, - denom: impl Into, - ) -> Result { - let denom_metadata: cosmos_modules::bank::QueryDenomMetadataResponse = (); - Ok(denom_metadata.metadata.unwrap()) - } - /// Query denoms metadata with pagination - /// - /// see [PageRequest] for pagination - pub async fn denoms_metadata( - &self, - pagination: Option, - ) -> Result, DaemonError> { - let denoms_metadata: cosmos_modules::bank::QueryDenomsMetadataResponse = (); - Ok(denoms_metadata.metadatas) - } - } - } - /// Constructor for a querier over a given channel - pub trait RpcQuerier { - /// Construct an new querier over a given channel - fn new(rpc: String) -> Self; - } -} -pub mod builder { - use crate::{DaemonAsync, DaemonBuilder}; - use std::rc::Rc; - use ibc_chain_registry::chain::ChainData; - use super::{error::DaemonError, sender::Sender, state::DaemonState}; - /// The default deployment id if none is provided - pub const DEFAULT_DEPLOYMENT: &str = "default"; - /// Create [`DaemonAsync`] through [`DaemonAsyncBuilder`] - /// ## Example - /// ```no_run - /// # tokio_test::block_on(async { - /// use cw_orch_daemon::{DaemonAsyncBuilder, networks}; - /// let daemon = DaemonAsyncBuilder::default() - /// .chain(networks::LOCAL_JUNO) - /// .deployment_id("v0.1.0") - /// .build() - /// .await.unwrap(); - /// # }) - /// ``` - pub struct DaemonAsyncBuilder { - pub(crate) chain: Option, - pub(crate) deployment_id: Option, - /// Wallet mnemonic - pub(crate) mnemonic: Option, - } - #[automatically_derived] - impl ::core::clone::Clone for DaemonAsyncBuilder { - #[inline] - fn clone(&self) -> DaemonAsyncBuilder { - DaemonAsyncBuilder { - chain: ::core::clone::Clone::clone(&self.chain), - deployment_id: ::core::clone::Clone::clone(&self.deployment_id), - mnemonic: ::core::clone::Clone::clone(&self.mnemonic), - } - } - } - #[automatically_derived] - impl ::core::default::Default for DaemonAsyncBuilder { - #[inline] - fn default() -> DaemonAsyncBuilder { - DaemonAsyncBuilder { - chain: ::core::default::Default::default(), - deployment_id: ::core::default::Default::default(), - mnemonic: ::core::default::Default::default(), - } - } - } - impl DaemonAsyncBuilder { - /// Set the chain the daemon will connect to - pub fn chain(&mut self, chain: impl Into) -> &mut Self { - self.chain = Some(chain.into()); - self - } - /// Set the deployment id to use for the daemon interactions - /// Defaults to `default` - pub fn deployment_id(&mut self, deployment_id: impl Into) -> &mut Self { - self.deployment_id = Some(deployment_id.into()); - self - } - /// Set the mnemonic to use with this chain. - /// Defaults to env variable depending on the environment. - /// - /// Variables: LOCAL_MNEMONIC, TEST_MNEMONIC and MAIN_MNEMONIC - pub fn mnemonic(&mut self, mnemonic: impl ToString) -> &mut Self { - self.mnemonic = Some(mnemonic.to_string()); - self - } - /// Build a daemon - pub async fn build(&self) -> Result { - let chain = self - .chain - .clone() - .ok_or(DaemonError::BuilderMissing("chain information".into()))?; - let deployment_id = self - .deployment_id - .clone() - .unwrap_or(DEFAULT_DEPLOYMENT.to_string()); - let state = Rc::new(DaemonState::new(chain, deployment_id).await?); - let sender = if let Some(mnemonic) = &self.mnemonic { - Sender::from_mnemonic(&state, mnemonic)? - } else { - Sender::new(&state)? - }; - let daemon = DaemonAsync { - state, - sender: Rc::new(sender), - }; - Ok(daemon) - } - } - impl From for DaemonAsyncBuilder { - fn from(value: DaemonBuilder) -> Self { - DaemonAsyncBuilder { - chain: value.chain, - deployment_id: value.deployment_id, - mnemonic: value.mnemonic, - } - } - } -} -pub mod channel { - use cosmrs::proto::cosmos::base::tendermint::v1beta1::{ - service_client::ServiceClient, GetNodeInfoRequest, - }; - use ibc_chain_registry::chain::Grpc; - use ibc_relayer_types::core::ics24_host::identifier::ChainId; - use tonic::transport::{Channel, ClientTlsConfig}; - use super::error::DaemonError; - /// A helper for constructing a gRPC channel - pub struct GrpcChannel {} - impl GrpcChannel { - /// Connect to any of the provided gRPC endpoints - pub async fn connect( - grpc: &[Grpc], - chain_id: &ChainId, - ) -> Result { - let mut successful_connections = ::alloc::vec::Vec::new(); - for Grpc { address, .. } in grpc.iter() { - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Trying to connect to endpoint: {0}", address), - lvl, - &( - "cw_orch_daemon::channel", - "cw_orch_daemon::channel", - "cw-orch-daemon/src/channel.rs", - ), - 19u32, - ::log::__private_api::Option::None, - ); - } - }; - let endpoint = Channel::builder(address.clone().try_into().unwrap()); - let maybe_client = ServiceClient::connect(endpoint.clone()).await; - let mut client = if maybe_client.is_ok() { - maybe_client? - } else { - { - let lvl = ::log::Level::Warn; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Cannot connect to gRPC endpoint: {0}, {1:?}", - address, - maybe_client.unwrap_err(), - ), - lvl, - &( - "cw_orch_daemon::channel", - "cw_orch_daemon::channel", - "cw-orch-daemon/src/channel.rs", - ), - 31u32, - ::log::__private_api::Option::None, - ); - } - }; - if !(address.contains("https") || address.contains("443")) { - continue; - } - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Attempting to connect with TLS"), - lvl, - &( - "cw_orch_daemon::channel", - "cw_orch_daemon::channel", - "cw-orch-daemon/src/channel.rs", - ), - 43u32, - ::log::__private_api::Option::None, - ); - } - }; - let endpoint = endpoint.clone().tls_config(ClientTlsConfig::new())?; - let maybe_client = ServiceClient::connect(endpoint.clone()).await; - if maybe_client.is_err() { - { - let lvl = ::log::Level::Warn; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!( - "Cannot connect to gRPC endpoint: {0}, {1:?}", - address, - maybe_client.unwrap_err(), - ), - lvl, - &( - "cw_orch_daemon::channel", - "cw_orch_daemon::channel", - "cw-orch-daemon/src/channel.rs", - ), - 51u32, - ::log::__private_api::Option::None, - ); - } - }; - continue; - } - maybe_client? - }; - let node_info = client - .get_node_info(GetNodeInfoRequest {}) - .await? - .into_inner(); - if ChainId::is_epoch_format( - &node_info.default_node_info.as_ref().unwrap().network, - ) { - if node_info.default_node_info.as_ref().unwrap().network - != chain_id.as_str() - { - { - let lvl = ::log::Level::Error; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!( - "Network mismatch: connection:{0} != config:{1}", - node_info.default_node_info.as_ref().unwrap().network, - chain_id.as_str(), - ), - lvl, - &( - "cw_orch_daemon::channel", - "cw_orch_daemon::channel", - "cw-orch-daemon/src/channel.rs", - ), - 72u32, - ::log::__private_api::Option::None, - ); - } - }; - continue; - } - } - successful_connections.push(endpoint.connect().await?) - } - if successful_connections.is_empty() { - return Err(DaemonError::CannotConnectGRPC); - } - Ok(successful_connections.pop().unwrap()) - } - } -} -pub mod core { - use crate::{queriers::CosmWasm, DaemonState}; - use super::{ - builder::DaemonAsyncBuilder, cosmos_modules, error::DaemonError, - queriers::{DaemonQuerier, Node}, - sender::Wallet, tx_resp::CosmTxResponse, - }; - use cosmrs::{ - cosmwasm::{MsgExecuteContract, MsgInstantiateContract, MsgMigrateContract}, - tendermint::Time, AccountId, Denom, - }; - use cosmwasm_std::{Addr, Coin}; - use cw_orch_core::{ - contract::interface_traits::Uploadable, environment::{ChainState, IndexResponse}, - }; - use serde::{de::DeserializeOwned, Serialize}; - use serde_json::from_str; - use std::{ - fmt::Debug, rc::Rc, str::{from_utf8, FromStr}, - time::Duration, - }; - use tonic::transport::Channel; - /** - Represents a blockchain node. - It's constructed using [`DaemonAsyncBuilder`]. - - ## Usage - ```rust,no_run - # tokio_test::block_on(async { - use cw_orch_daemon::{DaemonAsync, networks}; - - let daemon: DaemonAsync = DaemonAsync::builder() - .chain(networks::JUNO_1) - .build() - .await.unwrap(); - # }) - ``` - ## Environment Execution - - The DaemonAsync implements async methods of [`TxHandler`](cw_orch_core::environment::TxHandler) which allows you to perform transactions on the chain. - - ## Querying - - Different Cosmos SDK modules can be queried through the daemon by calling the [`DaemonAsync::query_client`] method with a specific querier. - See [Querier](crate::queriers) for examples. -*/ - pub struct DaemonAsync { - /// Sender to send transactions to the chain - pub sender: Wallet, - /// State of the daemon - pub state: Rc, - } - #[automatically_derived] - impl ::core::clone::Clone for DaemonAsync { - #[inline] - fn clone(&self) -> DaemonAsync { - DaemonAsync { - sender: ::core::clone::Clone::clone(&self.sender), - state: ::core::clone::Clone::clone(&self.state), - } - } - } - impl DaemonAsync { - /// Get the daemon builder - pub fn builder() -> DaemonAsyncBuilder { - DaemonAsyncBuilder::default() - } - /// Perform a query with a given query client. - /// See [Querier](crate::queriers) for examples. - pub fn query_client(&self) -> Querier { - Querier::new(self.sender.channel()) - } - /// Get the channel configured for this DaemonAsync. - pub fn channel(&self) -> Channel { - self.state.grpc_channel.clone() - } - } - impl ChainState for DaemonAsync { - type Out = Rc; - fn state(&self) -> Self::Out { - self.state.clone() - } - } - impl DaemonAsync { - /// Get the sender address - pub fn sender(&self) -> Addr { - self.sender.address().unwrap() - } - /// Execute a message on a contract. - pub async fn execute( - &self, - exec_msg: &E, - coins: &[cosmwasm_std::Coin], - contract_address: &Addr, - ) -> Result { - let exec_msg: MsgExecuteContract = MsgExecuteContract { - sender: self.sender.pub_addr()?, - contract: AccountId::from_str(contract_address.as_str())?, - msg: serde_json::to_vec(&exec_msg)?, - funds: parse_cw_coins(coins)?, - }; - let result = self - .sender - .commit_tx( - <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([exec_msg])), - None, - ) - .await?; - Ok(result) - } - /// Instantiate a contract. - pub async fn instantiate( - &self, - code_id: u64, - init_msg: &I, - label: Option<&str>, - admin: Option<&Addr>, - coins: &[Coin], - ) -> Result { - let sender = &self.sender; - let init_msg = MsgInstantiateContract { - code_id, - label: Some(label.unwrap_or("instantiate_contract").to_string()), - admin: admin.map(|a| FromStr::from_str(a.as_str()).unwrap()), - sender: sender.pub_addr()?, - msg: serde_json::to_vec(&init_msg)?, - funds: parse_cw_coins(coins)?, - }; - let result = sender - .commit_tx( - <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([init_msg])), - None, - ) - .await?; - Ok(result) - } - /// Query a contract. - pub async fn query( - &self, - query_msg: &Q, - contract_address: &Addr, - ) -> Result { - let sender = &self.sender; - let mut client = cosmos_modules::cosmwasm::query_client::QueryClient::new( - sender.channel(), - ); - let resp = client - .smart_contract_state(cosmos_modules::cosmwasm::QuerySmartContractStateRequest { - address: contract_address.to_string(), - query_data: serde_json::to_vec(&query_msg)?, - }) - .await?; - Ok(from_str(from_utf8(&resp.into_inner().data).unwrap())?) - } - /// Migration a contract. - pub async fn migrate( - &self, - migrate_msg: &M, - new_code_id: u64, - contract_address: &Addr, - ) -> Result { - let exec_msg: MsgMigrateContract = MsgMigrateContract { - sender: self.sender.pub_addr()?, - contract: AccountId::from_str(contract_address.as_str())?, - msg: serde_json::to_vec(&migrate_msg)?, - code_id: new_code_id, - }; - let result = self - .sender - .commit_tx( - <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([exec_msg])), - None, - ) - .await?; - Ok(result) - } - /// Wait for a given amount of blocks. - pub async fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { - let mut last_height = self.query_client::().block_height().await?; - let end_height = last_height + amount; - let average_block_speed = self - .query_client::() - .average_block_speed(Some(0.9)) - .await?; - let wait_time = average_block_speed * amount; - tokio::time::sleep(Duration::from_secs(wait_time)).await; - while last_height < end_height { - tokio::time::sleep(Duration::from_secs(average_block_speed)).await; - last_height = self.query_client::().block_height().await?; - } - Ok(()) - } - /// Wait for a given amount of seconds. - pub async fn wait_seconds(&self, secs: u64) -> Result<(), DaemonError> { - tokio::time::sleep(Duration::from_secs(secs)).await; - Ok(()) - } - /// Wait for the next block. - pub async fn next_block(&self) -> Result<(), DaemonError> { - self.wait_blocks(1).await - } - /// Get the current block info. - pub async fn block_info(&self) -> Result { - let block = self.query_client::().latest_block().await?; - let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; - let time = cosmwasm_std::Timestamp::from_nanos( - since_epoch.as_nanos() as u64, - ); - Ok(cosmwasm_std::BlockInfo { - height: block.header.height.value(), - time, - chain_id: block.header.chain_id.to_string(), - }) - } - /// Upload a contract to the chain. - pub async fn upload( - &self, - uploadable: &impl Uploadable, - ) -> Result { - let sender = &self.sender; - let wasm_path = uploadable.wasm(); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Uploading file at {0:?}", wasm_path), - lvl, - &( - "cw_orch_daemon::core", - "cw_orch_daemon::core", - "cw-orch-daemon/src/core.rs", - ), - 233u32, - ::log::__private_api::Option::None, - ); - } - }; - let file_contents = std::fs::read(wasm_path.path())?; - let store_msg = cosmrs::cosmwasm::MsgStoreCode { - sender: sender.pub_addr()?, - wasm_byte_code: file_contents, - instantiate_permission: None, - }; - let result = sender - .commit_tx( - <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([store_msg])), - None, - ) - .await?; - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Uploaded: {0:?}", result.txhash), - lvl, - &( - "cw_orch_daemon::core", - "cw_orch_daemon::core", - "cw-orch-daemon/src/core.rs", - ), - 244u32, - ::log::__private_api::Option::None, - ); - } - }; - let code_id = result.uploaded_code_id().unwrap(); - let wasm = CosmWasm::new(self.channel()); - while wasm.code(code_id).await.is_err() { - self.next_block().await?; - } - Ok(result) - } - /// Set the sender to use with this DaemonAsync to be the given wallet - pub fn set_sender(&mut self, sender: &Wallet) { - self.sender = sender.clone(); - } - } - pub(crate) fn parse_cw_coins( - coins: &[cosmwasm_std::Coin], - ) -> Result, DaemonError> { - coins - .iter() - .map(|cosmwasm_std::Coin { amount, denom }| { - Ok(cosmrs::Coin { - amount: amount.u128(), - denom: Denom::from_str(denom)?, - }) - }) - .collect::, DaemonError>>() - } -} -pub mod error { - #![allow(missing_docs)] - use cw_orch_core::CwEnvError; - use thiserror::Error; - pub enum DaemonError { - #[error("Reqwest HTTP(s) Error")] - ReqwestError(#[from] ::reqwest::Error), - #[error("JSON Conversion Error")] - SerdeJson(#[from] ::serde_json::Error), - #[error(transparent)] - ParseIntError(#[from] std::num::ParseIntError), - #[error(transparent)] - IOErr(#[from] ::std::io::Error), - #[error(transparent)] - Secp256k1(#[from] ::secp256k1::Error), - #[error(transparent)] - VarError(#[from] ::std::env::VarError), - #[error(transparent)] - AnyError(#[from] ::anyhow::Error), - #[error(transparent)] - Status(#[from] ::tonic::Status), - #[error(transparent)] - TransportError(#[from] ::tonic::transport::Error), - #[error(transparent)] - TendermintError(#[from] ::cosmrs::tendermint::Error), - #[error(transparent)] - TendermintRPCError(#[from] cosmrs::tendermint_rpc::Error), - #[error(transparent)] - CwEnvError(#[from] ::cw_orch_core::CwEnvError), - #[error("Bech32 Decode Error")] - Bech32DecodeErr, - #[error( - "Bech32 Decode Error: Key Failed prefix {0} or length {1} Wanted:{2}/{3}" - )] - Bech32DecodeExpanded(String, usize, String, usize), - #[error("Mnemonic - Wrong length, it should be 24 words")] - WrongLength, - #[error("Mnemonic - Bad Phrase")] - Phrasing, - #[error("Mnemonic - Missing Phrase")] - MissingPhrase, - #[error("Bad Implementation. Missing Component")] - Implementation, - #[error("Unable to convert into public key `{key}`")] - Conversion { key: String, source: bitcoin::bech32::Error }, - #[error( - "Can not augment daemon deployment after usage in more than one contract." - )] - SharedDaemonState, - #[error(transparent)] - ErrReport(#[from] ::eyre::ErrReport), - #[error(transparent)] - GRpcDecodeError(#[from] ::prost::DecodeError), - #[error(transparent)] - ED25519(#[from] ::ed25519_dalek::ed25519::Error), - #[error(transparent)] - DecodeError(#[from] ::base64::DecodeError), - #[error(transparent)] - HexError(#[from] ::hex::FromHexError), - #[error(transparent)] - BitCoinBip32(#[from] ::bitcoin::bip32::Error), - #[error("83 length-missing SECP256K1 prefix")] - ConversionSECP256k1, - #[error("82 length-missing ED25519 prefix")] - ConversionED25519, - #[error("Expected Key length of 82 or 83 length was {0}")] - ConversionLength(usize), - #[error("Expected Key length of 40 length was {0}")] - ConversionLengthED25519Hex(usize), - #[error( - "Expected ED25519 key of length 32 with a BECH32 ED25519 prefix of 5 chars - Len {0} - Hex {1}" - )] - ConversionPrefixED25519(usize, String), - #[error("Can't call Transactions without some gas rules")] - NoGasOpts, - #[error("Can't parse `{parse}` into a coin")] - CoinParseErrV { parse: String }, - #[error("Can't parse `{0}` into a coin")] - CoinParseErr(String), - #[error("TX submit returned `{0}` - {1} '{2}'")] - TxResultError(usize, String, String), - #[error("No price found for Gas using denom {0}")] - GasPriceError(String), - #[error( - "Attempting to fetch validator set in parts, and failed Height mismatch {0} {1}" - )] - TendermintValidatorSet(u64, u64), - #[error("Transaction {0} not found after {1} attempts")] - TXNotFound(String, usize), - #[error("unknown API error")] - Unknown, - #[error("Generic Error {0}")] - StdErr(String), - #[error("calling contract with unimplemented action")] - NotImplemented, - #[error("new chain detected, fill out the scaffold at {0}")] - NewChain(String), - #[error("new network detected, fill out the scaffold at {0}")] - NewNetwork(String), - #[error("Can not connect to any grpc endpoint that was provided.")] - CannotConnectGRPC, - #[error("tx failed: {reason} with code {code}")] - TxFailed { code: usize, reason: String }, - #[error("The list of grpc endpoints is empty")] - GRPCListIsEmpty, - #[error("no wasm path provided for contract.")] - MissingWasmPath, - #[error("daemon builder missing {0}")] - BuilderMissing(String), - #[error("ibc error: {0}")] - IbcError(String), - #[error("insufficient fee, check gas price: {0}")] - InsufficientFee(String), - } - #[allow(unused_qualifications)] - impl std::error::Error for DaemonError { - fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> { - use thiserror::__private::AsDynError; - #[allow(deprecated)] - match self { - DaemonError::ReqwestError { 0: source, .. } => { - std::option::Option::Some(source.as_dyn_error()) - } - DaemonError::SerdeJson { 0: source, .. } => { - std::option::Option::Some(source.as_dyn_error()) - } - DaemonError::ParseIntError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::IOErr { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::Secp256k1 { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::VarError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::AnyError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::Status { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::TransportError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::TendermintError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::TendermintRPCError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::CwEnvError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::Bech32DecodeErr { .. } => std::option::Option::None, - DaemonError::Bech32DecodeExpanded { .. } => std::option::Option::None, - DaemonError::WrongLength { .. } => std::option::Option::None, - DaemonError::Phrasing { .. } => std::option::Option::None, - DaemonError::MissingPhrase { .. } => std::option::Option::None, - DaemonError::Implementation { .. } => std::option::Option::None, - DaemonError::Conversion { source: source, .. } => { - std::option::Option::Some(source.as_dyn_error()) - } - DaemonError::SharedDaemonState { .. } => std::option::Option::None, - DaemonError::ErrReport { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::GRpcDecodeError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::ED25519 { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::DecodeError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::HexError { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::BitCoinBip32 { 0: transparent } => { - std::error::Error::source(transparent.as_dyn_error()) - } - DaemonError::ConversionSECP256k1 { .. } => std::option::Option::None, - DaemonError::ConversionED25519 { .. } => std::option::Option::None, - DaemonError::ConversionLength { .. } => std::option::Option::None, - DaemonError::ConversionLengthED25519Hex { .. } => { - std::option::Option::None - } - DaemonError::ConversionPrefixED25519 { .. } => std::option::Option::None, - DaemonError::NoGasOpts { .. } => std::option::Option::None, - DaemonError::CoinParseErrV { .. } => std::option::Option::None, - DaemonError::CoinParseErr { .. } => std::option::Option::None, - DaemonError::TxResultError { .. } => std::option::Option::None, - DaemonError::GasPriceError { .. } => std::option::Option::None, - DaemonError::TendermintValidatorSet { .. } => std::option::Option::None, - DaemonError::TXNotFound { .. } => std::option::Option::None, - DaemonError::Unknown { .. } => std::option::Option::None, - DaemonError::StdErr { .. } => std::option::Option::None, - DaemonError::NotImplemented { .. } => std::option::Option::None, - DaemonError::NewChain { .. } => std::option::Option::None, - DaemonError::NewNetwork { .. } => std::option::Option::None, - DaemonError::CannotConnectGRPC { .. } => std::option::Option::None, - DaemonError::TxFailed { .. } => std::option::Option::None, - DaemonError::GRPCListIsEmpty { .. } => std::option::Option::None, - DaemonError::MissingWasmPath { .. } => std::option::Option::None, - DaemonError::BuilderMissing { .. } => std::option::Option::None, - DaemonError::IbcError { .. } => std::option::Option::None, - DaemonError::InsufficientFee { .. } => std::option::Option::None, - } - } - } - #[allow(unused_qualifications)] - impl std::fmt::Display for DaemonError { - fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - use thiserror::__private::AsDisplay as _; - #[allow(unused_variables, deprecated, clippy::used_underscore_binding)] - match self { - DaemonError::ReqwestError(_0) => { - __formatter.write_fmt(format_args!("Reqwest HTTP(s) Error")) - } - DaemonError::SerdeJson(_0) => { - __formatter.write_fmt(format_args!("JSON Conversion Error")) - } - DaemonError::ParseIntError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::IOErr(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::Secp256k1(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::VarError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::AnyError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::Status(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::TransportError(_0) => { - std::fmt::Display::fmt(_0, __formatter) - } - DaemonError::TendermintError(_0) => { - std::fmt::Display::fmt(_0, __formatter) - } - DaemonError::TendermintRPCError(_0) => { - std::fmt::Display::fmt(_0, __formatter) - } - DaemonError::CwEnvError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::Bech32DecodeErr {} => { - __formatter.write_fmt(format_args!("Bech32 Decode Error")) - } - DaemonError::Bech32DecodeExpanded(_0, _1, _2, _3) => { - __formatter - .write_fmt( - format_args!( - "Bech32 Decode Error: Key Failed prefix {0} or length {1} Wanted:{2}/{3}", - _0.as_display(), - _1.as_display(), - _2.as_display(), - _3.as_display(), - ), - ) - } - DaemonError::WrongLength {} => { - __formatter - .write_fmt( - format_args!( - "Mnemonic - Wrong length, it should be 24 words", - ), - ) - } - DaemonError::Phrasing {} => { - __formatter.write_fmt(format_args!("Mnemonic - Bad Phrase")) - } - DaemonError::MissingPhrase {} => { - __formatter.write_fmt(format_args!("Mnemonic - Missing Phrase")) - } - DaemonError::Implementation {} => { - __formatter - .write_fmt(format_args!("Bad Implementation. Missing Component")) - } - DaemonError::Conversion { key, source } => { - __formatter - .write_fmt( - format_args!( - "Unable to convert into public key `{0}`", - key.as_display(), - ), - ) - } - DaemonError::SharedDaemonState {} => { - __formatter - .write_fmt( - format_args!( - "Can not augment daemon deployment after usage in more than one contract.", - ), - ) - } - DaemonError::ErrReport(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::GRpcDecodeError(_0) => { - std::fmt::Display::fmt(_0, __formatter) - } - DaemonError::ED25519(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::DecodeError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::HexError(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::BitCoinBip32(_0) => std::fmt::Display::fmt(_0, __formatter), - DaemonError::ConversionSECP256k1 {} => { - __formatter - .write_fmt(format_args!("83 length-missing SECP256K1 prefix")) - } - DaemonError::ConversionED25519 {} => { - __formatter - .write_fmt(format_args!("82 length-missing ED25519 prefix")) - } - DaemonError::ConversionLength(_0) => { - __formatter - .write_fmt( - format_args!( - "Expected Key length of 82 or 83 length was {0}", - _0.as_display(), - ), - ) - } - DaemonError::ConversionLengthED25519Hex(_0) => { - __formatter - .write_fmt( - format_args!( - "Expected Key length of 40 length was {0}", - _0.as_display(), - ), - ) - } - DaemonError::ConversionPrefixED25519(_0, _1) => { - __formatter - .write_fmt( - format_args!( - "Expected ED25519 key of length 32 with a BECH32 ED25519 prefix of 5 chars - Len {0} - Hex {1}", - _0.as_display(), - _1.as_display(), - ), - ) - } - DaemonError::NoGasOpts {} => { - __formatter - .write_fmt( - format_args!( - "Can\'t call Transactions without some gas rules", - ), - ) - } - DaemonError::CoinParseErrV { parse } => { - __formatter - .write_fmt( - format_args!( - "Can\'t parse `{0}` into a coin", - parse.as_display(), - ), - ) - } - DaemonError::CoinParseErr(_0) => { - __formatter - .write_fmt( - format_args!( - "Can\'t parse `{0}` into a coin", - _0.as_display(), - ), - ) - } - DaemonError::TxResultError(_0, _1, _2) => { - __formatter - .write_fmt( - format_args!( - "TX submit returned `{0}` - {1} \'{2}\'", - _0.as_display(), - _1.as_display(), - _2.as_display(), - ), - ) - } - DaemonError::GasPriceError(_0) => { - __formatter - .write_fmt( - format_args!( - "No price found for Gas using denom {0}", - _0.as_display(), - ), - ) - } - DaemonError::TendermintValidatorSet(_0, _1) => { - __formatter - .write_fmt( - format_args!( - "Attempting to fetch validator set in parts, and failed Height mismatch {0} {1}", - _0.as_display(), - _1.as_display(), - ), - ) - } - DaemonError::TXNotFound(_0, _1) => { - __formatter - .write_fmt( - format_args!( - "Transaction {0} not found after {1} attempts", - _0.as_display(), - _1.as_display(), - ), - ) - } - DaemonError::Unknown {} => { - __formatter.write_fmt(format_args!("unknown API error")) - } - DaemonError::StdErr(_0) => { - __formatter - .write_fmt(format_args!("Generic Error {0}", _0.as_display())) - } - DaemonError::NotImplemented {} => { - __formatter - .write_fmt( - format_args!("calling contract with unimplemented action"), - ) - } - DaemonError::NewChain(_0) => { - __formatter - .write_fmt( - format_args!( - "new chain detected, fill out the scaffold at {0}", - _0.as_display(), - ), - ) - } - DaemonError::NewNetwork(_0) => { - __formatter - .write_fmt( - format_args!( - "new network detected, fill out the scaffold at {0}", - _0.as_display(), - ), - ) - } - DaemonError::CannotConnectGRPC {} => { - __formatter - .write_fmt( - format_args!( - "Can not connect to any grpc endpoint that was provided.", - ), - ) - } - DaemonError::TxFailed { code, reason } => { - __formatter - .write_fmt( - format_args!( - "tx failed: {0} with code {1}", - reason.as_display(), - code.as_display(), - ), - ) - } - DaemonError::GRPCListIsEmpty {} => { - __formatter - .write_fmt(format_args!("The list of grpc endpoints is empty")) - } - DaemonError::MissingWasmPath {} => { - __formatter - .write_fmt(format_args!("no wasm path provided for contract.")) - } - DaemonError::BuilderMissing(_0) => { - __formatter - .write_fmt( - format_args!("daemon builder missing {0}", _0.as_display()), - ) - } - DaemonError::IbcError(_0) => { - __formatter - .write_fmt(format_args!("ibc error: {0}", _0.as_display())) - } - DaemonError::InsufficientFee(_0) => { - __formatter - .write_fmt( - format_args!( - "insufficient fee, check gas price: {0}", - _0.as_display(), - ), - ) - } - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::reqwest::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::reqwest::Error) -> Self { - DaemonError::ReqwestError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::serde_json::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::serde_json::Error) -> Self { - DaemonError::SerdeJson { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From for DaemonError { - #[allow(deprecated)] - fn from(source: std::num::ParseIntError) -> Self { - DaemonError::ParseIntError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::std::io::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::std::io::Error) -> Self { - DaemonError::IOErr { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::secp256k1::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::secp256k1::Error) -> Self { - DaemonError::Secp256k1 { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::std::env::VarError> for DaemonError { - #[allow(deprecated)] - fn from(source: ::std::env::VarError) -> Self { - DaemonError::VarError { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::anyhow::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::anyhow::Error) -> Self { - DaemonError::AnyError { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::tonic::Status> for DaemonError { - #[allow(deprecated)] - fn from(source: ::tonic::Status) -> Self { - DaemonError::Status { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::tonic::transport::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::tonic::transport::Error) -> Self { - DaemonError::TransportError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::cosmrs::tendermint::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::cosmrs::tendermint::Error) -> Self { - DaemonError::TendermintError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From for DaemonError { - #[allow(deprecated)] - fn from(source: cosmrs::tendermint_rpc::Error) -> Self { - DaemonError::TendermintRPCError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::cw_orch_core::CwEnvError> for DaemonError { - #[allow(deprecated)] - fn from(source: ::cw_orch_core::CwEnvError) -> Self { - DaemonError::CwEnvError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::eyre::ErrReport> for DaemonError { - #[allow(deprecated)] - fn from(source: ::eyre::ErrReport) -> Self { - DaemonError::ErrReport { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::prost::DecodeError> for DaemonError { - #[allow(deprecated)] - fn from(source: ::prost::DecodeError) -> Self { - DaemonError::GRpcDecodeError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::ed25519_dalek::ed25519::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::ed25519_dalek::ed25519::Error) -> Self { - DaemonError::ED25519 { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::base64::DecodeError> for DaemonError { - #[allow(deprecated)] - fn from(source: ::base64::DecodeError) -> Self { - DaemonError::DecodeError { - 0: source, - } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::hex::FromHexError> for DaemonError { - #[allow(deprecated)] - fn from(source: ::hex::FromHexError) -> Self { - DaemonError::HexError { 0: source } - } - } - #[allow(unused_qualifications)] - impl std::convert::From<::bitcoin::bip32::Error> for DaemonError { - #[allow(deprecated)] - fn from(source: ::bitcoin::bip32::Error) -> Self { - DaemonError::BitCoinBip32 { - 0: source, - } - } - } - #[automatically_derived] - impl ::core::fmt::Debug for DaemonError { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match self { - DaemonError::ReqwestError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ReqwestError", - &__self_0, - ) - } - DaemonError::SerdeJson(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "SerdeJson", - &__self_0, - ) - } - DaemonError::ParseIntError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ParseIntError", - &__self_0, - ) - } - DaemonError::IOErr(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "IOErr", - &__self_0, - ) - } - DaemonError::Secp256k1(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "Secp256k1", - &__self_0, - ) - } - DaemonError::VarError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "VarError", - &__self_0, - ) - } - DaemonError::AnyError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "AnyError", - &__self_0, - ) - } - DaemonError::Status(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "Status", - &__self_0, - ) - } - DaemonError::TransportError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "TransportError", - &__self_0, - ) - } - DaemonError::TendermintError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "TendermintError", - &__self_0, - ) - } - DaemonError::TendermintRPCError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "TendermintRPCError", - &__self_0, - ) - } - DaemonError::CwEnvError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "CwEnvError", - &__self_0, - ) - } - DaemonError::Bech32DecodeErr => { - ::core::fmt::Formatter::write_str(f, "Bech32DecodeErr") - } - DaemonError::Bech32DecodeExpanded( - __self_0, - __self_1, - __self_2, - __self_3, - ) => { - ::core::fmt::Formatter::debug_tuple_field4_finish( - f, - "Bech32DecodeExpanded", - __self_0, - __self_1, - __self_2, - &__self_3, - ) - } - DaemonError::WrongLength => { - ::core::fmt::Formatter::write_str(f, "WrongLength") - } - DaemonError::Phrasing => ::core::fmt::Formatter::write_str(f, "Phrasing"), - DaemonError::MissingPhrase => { - ::core::fmt::Formatter::write_str(f, "MissingPhrase") - } - DaemonError::Implementation => { - ::core::fmt::Formatter::write_str(f, "Implementation") - } - DaemonError::Conversion { key: __self_0, source: __self_1 } => { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "Conversion", - "key", - __self_0, - "source", - &__self_1, - ) - } - DaemonError::SharedDaemonState => { - ::core::fmt::Formatter::write_str(f, "SharedDaemonState") - } - DaemonError::ErrReport(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ErrReport", - &__self_0, - ) - } - DaemonError::GRpcDecodeError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "GRpcDecodeError", - &__self_0, - ) - } - DaemonError::ED25519(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ED25519", - &__self_0, - ) - } - DaemonError::DecodeError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "DecodeError", - &__self_0, - ) - } - DaemonError::HexError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "HexError", - &__self_0, - ) - } - DaemonError::BitCoinBip32(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "BitCoinBip32", - &__self_0, - ) - } - DaemonError::ConversionSECP256k1 => { - ::core::fmt::Formatter::write_str(f, "ConversionSECP256k1") - } - DaemonError::ConversionED25519 => { - ::core::fmt::Formatter::write_str(f, "ConversionED25519") - } - DaemonError::ConversionLength(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ConversionLength", - &__self_0, - ) - } - DaemonError::ConversionLengthED25519Hex(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "ConversionLengthED25519Hex", - &__self_0, - ) - } - DaemonError::ConversionPrefixED25519(__self_0, __self_1) => { - ::core::fmt::Formatter::debug_tuple_field2_finish( - f, - "ConversionPrefixED25519", - __self_0, - &__self_1, - ) - } - DaemonError::NoGasOpts => { - ::core::fmt::Formatter::write_str(f, "NoGasOpts") - } - DaemonError::CoinParseErrV { parse: __self_0 } => { - ::core::fmt::Formatter::debug_struct_field1_finish( - f, - "CoinParseErrV", - "parse", - &__self_0, - ) - } - DaemonError::CoinParseErr(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "CoinParseErr", - &__self_0, - ) - } - DaemonError::TxResultError(__self_0, __self_1, __self_2) => { - ::core::fmt::Formatter::debug_tuple_field3_finish( - f, - "TxResultError", - __self_0, - __self_1, - &__self_2, - ) - } - DaemonError::GasPriceError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "GasPriceError", - &__self_0, - ) - } - DaemonError::TendermintValidatorSet(__self_0, __self_1) => { - ::core::fmt::Formatter::debug_tuple_field2_finish( - f, - "TendermintValidatorSet", - __self_0, - &__self_1, - ) - } - DaemonError::TXNotFound(__self_0, __self_1) => { - ::core::fmt::Formatter::debug_tuple_field2_finish( - f, - "TXNotFound", - __self_0, - &__self_1, - ) - } - DaemonError::Unknown => ::core::fmt::Formatter::write_str(f, "Unknown"), - DaemonError::StdErr(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "StdErr", - &__self_0, - ) - } - DaemonError::NotImplemented => { - ::core::fmt::Formatter::write_str(f, "NotImplemented") - } - DaemonError::NewChain(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "NewChain", - &__self_0, - ) - } - DaemonError::NewNetwork(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "NewNetwork", - &__self_0, - ) - } - DaemonError::CannotConnectGRPC => { - ::core::fmt::Formatter::write_str(f, "CannotConnectGRPC") - } - DaemonError::TxFailed { code: __self_0, reason: __self_1 } => { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "TxFailed", - "code", - __self_0, - "reason", - &__self_1, - ) - } - DaemonError::GRPCListIsEmpty => { - ::core::fmt::Formatter::write_str(f, "GRPCListIsEmpty") - } - DaemonError::MissingWasmPath => { - ::core::fmt::Formatter::write_str(f, "MissingWasmPath") - } - DaemonError::BuilderMissing(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "BuilderMissing", - &__self_0, - ) - } - DaemonError::IbcError(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "IbcError", - &__self_0, - ) - } - DaemonError::InsufficientFee(__self_0) => { - ::core::fmt::Formatter::debug_tuple_field1_finish( - f, - "InsufficientFee", - &__self_0, - ) - } - } - } - } - impl DaemonError { - pub fn ibc_err(msg: impl ToString) -> Self { - Self::IbcError(msg.to_string()) - } - } - impl From for CwEnvError { - fn from(val: DaemonError) -> Self { - CwEnvError::AnyError(val.into()) - } - } -} -pub(crate) mod json_file { - use serde_json::{from_reader, json, Value}; - use std::fs::{File, OpenOptions}; - pub fn write( - filename: &String, - chain_id: &String, - network_id: &String, - deploy_id: &String, - ) { - let file = OpenOptions::new() - .create(true) - .read(true) - .write(true) - .truncate(false) - .open(filename) - .unwrap(); - let mut json: Value = if file.metadata().unwrap().len().eq(&0) { - ::serde_json::Value::Object(::serde_json::Map::new()) - } else { - from_reader(file).unwrap() - }; - if json.get(network_id).is_none() { - json[network_id] = ::serde_json::Value::Object(::serde_json::Map::new()); - } - if json[network_id].get(chain_id).is_none() { - json[network_id][chain_id] = ::serde_json::Value::Object({ - let mut object = ::serde_json::Map::new(); - let _ = object - .insert( - (deploy_id).into(), - ::serde_json::Value::Object(::serde_json::Map::new()), - ); - let _ = object - .insert( - ("code_ids").into(), - ::serde_json::Value::Object(::serde_json::Map::new()), - ); - object - }); - } - serde_json::to_writer_pretty(File::create(filename).unwrap(), &json).unwrap(); - } - pub fn read(filename: &String) -> Value { - let file = File::open(filename) - .unwrap_or_else(|_| { - ::core::panicking::panic_fmt( - format_args!("File should be present at {0}", filename), - ); - }); - let json: serde_json::Value = from_reader(file).unwrap(); - json - } -} -/// Proto types for different blockchains -pub mod proto { - pub mod injective { - #![allow(missing_docs)] - use crate::DaemonError; - use cosmrs::tx::SignDoc; - use cosmrs::{proto::traits::TypeUrl, tx::Raw}; - pub const ETHEREUM_COIN_TYPE: u32 = 60; - pub struct InjectiveEthAccount { - #[prost(message, optional, tag = "1")] - pub base_account: ::core::option::Option< - super::super::cosmos_modules::auth::BaseAccount, - >, - #[prost(bytes, tag = "2")] - pub code_hash: Vec, - } - #[automatically_derived] - impl ::core::clone::Clone for InjectiveEthAccount { - #[inline] - fn clone(&self) -> InjectiveEthAccount { - InjectiveEthAccount { - base_account: ::core::clone::Clone::clone(&self.base_account), - code_hash: ::core::clone::Clone::clone(&self.code_hash), - } - } - } - #[automatically_derived] - impl ::core::marker::StructuralPartialEq for InjectiveEthAccount {} - #[automatically_derived] - impl ::core::cmp::PartialEq for InjectiveEthAccount { - #[inline] - fn eq(&self, other: &InjectiveEthAccount) -> bool { - self.base_account == other.base_account - && self.code_hash == other.code_hash - } - } - impl ::prost::Message for InjectiveEthAccount { - #[allow(unused_variables)] - fn encode_raw(&self, buf: &mut B) - where - B: ::prost::bytes::BufMut, - { - if let Some(ref msg) = self.base_account { - ::prost::encoding::message::encode(1u32, msg, buf); - } - if self.code_hash != b"" as &[u8] { - ::prost::encoding::bytes::encode(2u32, &self.code_hash, buf); - } - } - #[allow(unused_variables)] - fn merge_field( - &mut self, - tag: u32, - wire_type: ::prost::encoding::WireType, - buf: &mut B, - ctx: ::prost::encoding::DecodeContext, - ) -> ::core::result::Result<(), ::prost::DecodeError> - where - B: ::prost::bytes::Buf, - { - const STRUCT_NAME: &'static str = "InjectiveEthAccount"; - match tag { - 1u32 => { - let mut value = &mut self.base_account; - ::prost::encoding::message::merge( - wire_type, - value.get_or_insert_with(::core::default::Default::default), - buf, - ctx, - ) - .map_err(|mut error| { - error.push(STRUCT_NAME, "base_account"); - error - }) - } - 2u32 => { - let mut value = &mut self.code_hash; - ::prost::encoding::bytes::merge(wire_type, value, buf, ctx) - .map_err(|mut error| { - error.push(STRUCT_NAME, "code_hash"); - error - }) - } - _ => ::prost::encoding::skip_field(wire_type, tag, buf, ctx), - } - } - #[inline] - fn encoded_len(&self) -> usize { - 0 - + self - .base_account - .as_ref() - .map_or( - 0, - |msg| ::prost::encoding::message::encoded_len(1u32, msg), - ) - + if self.code_hash != b"" as &[u8] { - ::prost::encoding::bytes::encoded_len(2u32, &self.code_hash) - } else { - 0 - } - } - fn clear(&mut self) { - self.base_account = ::core::option::Option::None; - self.code_hash.clear(); - } - } - impl ::core::default::Default for InjectiveEthAccount { - fn default() -> Self { - InjectiveEthAccount { - base_account: ::core::default::Default::default(), - code_hash: ::core::default::Default::default(), - } - } - } - impl ::core::fmt::Debug for InjectiveEthAccount { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - let mut builder = f.debug_struct("InjectiveEthAccount"); - let builder = { - let wrapper = &self.base_account; - builder.field("base_account", &wrapper) - }; - let builder = { - let wrapper = { - fn ScalarWrapper(v: T) -> T { - v - } - ScalarWrapper(&self.code_hash) - }; - builder.field("code_hash", &wrapper) - }; - builder.finish() - } - } - pub struct InjectivePubKey { - #[prost(bytes, tag = 1)] - pub key: Vec, - } - #[automatically_derived] - impl ::core::clone::Clone for InjectivePubKey { - #[inline] - fn clone(&self) -> InjectivePubKey { - InjectivePubKey { - key: ::core::clone::Clone::clone(&self.key), - } - } - } - #[automatically_derived] - impl ::core::marker::StructuralPartialEq for InjectivePubKey {} - #[automatically_derived] - impl ::core::cmp::PartialEq for InjectivePubKey { - #[inline] - fn eq(&self, other: &InjectivePubKey) -> bool { - self.key == other.key - } - } - impl ::prost::Message for InjectivePubKey { - #[allow(unused_variables)] - fn encode_raw(&self, buf: &mut B) - where - B: ::prost::bytes::BufMut, - { - if self.key != b"" as &[u8] { - ::prost::encoding::bytes::encode(1u32, &self.key, buf); - } - } - #[allow(unused_variables)] - fn merge_field( - &mut self, - tag: u32, - wire_type: ::prost::encoding::WireType, - buf: &mut B, - ctx: ::prost::encoding::DecodeContext, - ) -> ::core::result::Result<(), ::prost::DecodeError> - where - B: ::prost::bytes::Buf, - { - const STRUCT_NAME: &'static str = "InjectivePubKey"; - match tag { - 1u32 => { - let mut value = &mut self.key; - ::prost::encoding::bytes::merge(wire_type, value, buf, ctx) - .map_err(|mut error| { - error.push(STRUCT_NAME, "key"); - error - }) - } - _ => ::prost::encoding::skip_field(wire_type, tag, buf, ctx), - } - } - #[inline] - fn encoded_len(&self) -> usize { - 0 - + if self.key != b"" as &[u8] { - ::prost::encoding::bytes::encoded_len(1u32, &self.key) - } else { - 0 - } - } - fn clear(&mut self) { - self.key.clear(); - } - } - impl ::core::default::Default for InjectivePubKey { - fn default() -> Self { - InjectivePubKey { - key: ::core::default::Default::default(), - } - } - } - impl ::core::fmt::Debug for InjectivePubKey { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - let mut builder = f.debug_struct("InjectivePubKey"); - let builder = { - let wrapper = { - fn ScalarWrapper(v: T) -> T { - v - } - ScalarWrapper(&self.key) - }; - builder.field("key", &wrapper) - }; - builder.finish() - } - } - impl TypeUrl for InjectivePubKey { - const TYPE_URL: &'static str = "/injective.crypto.v1beta1.ethsecp256k1.PubKey"; - } - pub trait InjectiveSigner { - fn sign_injective(&self, sign_doc: SignDoc) -> Result; - } - } -} -pub mod sender { - use crate::{networks::ChainKind, proto::injective::ETHEREUM_COIN_TYPE}; - use super::{ - cosmos_modules::{self, auth::BaseAccount}, - error::DaemonError, queriers::{DaemonQuerier, Node}, - state::DaemonState, tx_builder::TxBuilder, tx_resp::CosmTxResponse, - }; - use crate::proto::injective::InjectiveEthAccount; - use crate::{core::parse_cw_coins, keys::private::PrivateKey}; - use cosmrs::{ - bank::MsgSend, crypto::secp256k1::SigningKey, proto::traits::Message, - tendermint::chain::Id, - tx::{self, ModeInfo, Msg, Raw, SignDoc, SignMode, SignerInfo}, - AccountId, - }; - use cosmwasm_std::Addr; - use secp256k1::{All, Context, Secp256k1, Signing}; - use std::{convert::TryFrom, env, rc::Rc, str::FromStr}; - use cosmos_modules::vesting::PeriodicVestingAccount; - use tonic::transport::Channel; - /// A wallet is a sender of transactions, can be safely cloned and shared within the same thread. - pub type Wallet = Rc>; - /// Signer of the transactions and helper for address derivation - /// This is the main interface for simulating and signing transactions - pub struct Sender { - pub private_key: PrivateKey, - pub secp: Secp256k1, - pub(crate) daemon_state: Rc, - } - impl Sender { - pub fn new(daemon_state: &Rc) -> Result, DaemonError> { - let kind = ChainKind::from(daemon_state.chain_data.network_type.clone()); - let mnemonic = env::var(kind.mnemonic_name()) - .unwrap_or_else(|_| { - { - ::core::panicking::panic_fmt( - format_args!( - "Wallet mnemonic environment variable {0} not set.", - kind.mnemonic_name(), - ), - ); - } - }); - Self::from_mnemonic(daemon_state, &mnemonic) - } - /// Construct a new Sender from a mnemonic - pub fn from_mnemonic( - daemon_state: &Rc, - mnemonic: &str, - ) -> Result, DaemonError> { - let secp = Secp256k1::new(); - let p_key: PrivateKey = PrivateKey::from_words( - &secp, - mnemonic, - 0, - 0, - daemon_state.chain_data.slip44, - )?; - let sender = Sender { - daemon_state: daemon_state.clone(), - private_key: p_key, - secp, - }; - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Interacting with {0} using address: {1}", - daemon_state.chain_data.chain_id, - sender.pub_addr_str()?, - ), - lvl, - &( - "cw_orch_daemon::sender", - "cw_orch_daemon::sender", - "cw-orch-daemon/src/sender.rs", - ), - 71u32, - ::log::__private_api::Option::None, - ); - } - }; - Ok(sender) - } - fn cosmos_private_key(&self) -> SigningKey { - SigningKey::from_slice(&self.private_key.raw_key()).unwrap() - } - pub fn channel(&self) -> Channel { - self.daemon_state.grpc_channel.clone() - } - pub fn pub_addr(&self) -> Result { - Ok( - AccountId::new( - &self.daemon_state.chain_data.bech32_prefix, - &self.private_key.public_key(&self.secp).raw_address.unwrap(), - )?, - ) - } - pub fn address(&self) -> Result { - Ok(Addr::unchecked(self.pub_addr_str()?)) - } - pub fn pub_addr_str(&self) -> Result { - Ok(self.pub_addr()?.to_string()) - } - pub async fn bank_send( - &self, - recipient: &str, - coins: Vec, - ) -> Result { - let msg_send = MsgSend { - from_address: self.pub_addr()?, - to_address: AccountId::from_str(recipient)?, - amount: parse_cw_coins(&coins)?, - }; - self.commit_tx( - <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([msg_send])), - Some("sending tokens"), - ) - .await - } - pub async fn calculate_gas( - &self, - tx_body: &tx::Body, - sequence: u64, - account_number: u64, - ) -> Result { - let fee = TxBuilder::build_fee( - 0u8, - &self.daemon_state.chain_data.fees.fee_tokens[0].denom, - 0, - ); - let auth_info = SignerInfo { - public_key: self.private_key.get_signer_public_key(&self.secp), - mode_info: ModeInfo::single(SignMode::Direct), - sequence, - } - .auth_info(fee); - let sign_doc = SignDoc::new( - tx_body, - &auth_info, - &Id::try_from(self.daemon_state.chain_data.chain_id.to_string())?, - account_number, - )?; - let tx_raw = self.sign(sign_doc)?; - Node::new(self.channel()).simulate_tx(tx_raw.to_bytes()?).await - } - pub async fn commit_tx( - &self, - msgs: Vec, - memo: Option<&str>, - ) -> Result { - let timeout_height = Node::new(self.channel()).block_height().await? + 10u64; - let tx_body = TxBuilder::build_body(msgs, memo, timeout_height); - let mut tx_builder = TxBuilder::new(tx_body); - let tx = tx_builder.build(self).await?; - let mut tx_response = self.broadcast_tx(tx).await?; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("tx broadcast response: {0:?}", tx_response), - lvl, - &( - "cw_orch_daemon::sender", - "cw_orch_daemon::sender", - "cw-orch-daemon/src/sender.rs", - ), - 166u32, - ::log::__private_api::Option::None, - ); - } - }; - if has_insufficient_fee(&tx_response.raw_log) { - let suggested_fee = parse_suggested_fee(&tx_response.raw_log); - let Some(new_fee) = suggested_fee else { - return Err(DaemonError::InsufficientFee(tx_response.raw_log)); - }; - tx_builder.fee_amount(new_fee); - let tx = tx_builder.build(self).await?; - tx_response = self.broadcast_tx(tx).await?; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("tx broadcast response: {0:?}", tx_response), - lvl, - &( - "cw_orch_daemon::sender", - "cw_orch_daemon::sender", - "cw-orch-daemon/src/sender.rs", - ), - 181u32, - ::log::__private_api::Option::None, - ); - } - }; - } - let resp = Node::new(self.channel()).find_tx(tx_response.txhash).await?; - if resp.code == 0 { - Ok(resp) - } else { - Err(DaemonError::TxFailed { - code: resp.code, - reason: resp.raw_log, - }) - } - } - pub fn sign(&self, sign_doc: SignDoc) -> Result { - let tx_raw = if self.private_key.coin_type == ETHEREUM_COIN_TYPE { - { - ::core::panicking::panic_fmt( - format_args!( - "Coin Type {0} not supported without eth feature", - ETHEREUM_COIN_TYPE, - ), - ); - }; - } else { - sign_doc.sign(&self.cosmos_private_key())? - }; - Ok(tx_raw) - } - pub async fn base_account(&self) -> Result { - let addr = self.pub_addr().unwrap().to_string(); - let mut client = cosmos_modules::auth::query_client::QueryClient::new( - self.channel(), - ); - let resp = client - .account(cosmos_modules::auth::QueryAccountRequest { - address: addr, - }) - .await? - .into_inner(); - let account = resp.account.unwrap().value; - let acc = if let Ok(acc) = BaseAccount::decode(account.as_ref()) { - acc - } else if let Ok(acc) = PeriodicVestingAccount::decode(account.as_ref()) { - acc.base_vesting_account.unwrap().base_account.unwrap() - } else if let Ok(acc) = InjectiveEthAccount::decode(account.as_ref()) { - acc.base_account.unwrap() - } else { - return Err( - DaemonError::StdErr( - "Unknown account type returned from QueryAccountRequest".into(), - ), - ); - }; - Ok(acc) - } - async fn broadcast_tx( - &self, - tx: Raw, - ) -> Result< - cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse, - DaemonError, - > { - let mut client = cosmos_modules::tx::service_client::ServiceClient::new( - self.channel(), - ); - let commit = client - .broadcast_tx(cosmos_modules::tx::BroadcastTxRequest { - tx_bytes: tx.to_bytes()?, - mode: cosmos_modules::tx::BroadcastMode::Sync.into(), - }) - .await?; - let commit = commit.into_inner().tx_response.unwrap(); - Ok(commit) - } - } - fn has_insufficient_fee(raw_log: &str) -> bool { - raw_log.contains("insufficient fees") - } - fn parse_suggested_fee(raw_log: &str) -> Option { - let parts: Vec<&str> = raw_log.split("required: ").collect(); - if parts.len() != 2 { - return None; - } - let got_parts: Vec<&str> = parts[0].split_whitespace().collect(); - let paid_fee_with_denom = got_parts.last()?; - let (_, denomination) = paid_fee_with_denom - .split_at(paid_fee_with_denom.find(|c: char| !c.is_numeric())?); - { - ::std::io::_eprint(format_args!("denom: {0}\n", denomination)); - }; - let required_fees: Vec<&str> = parts[1].split(denomination).collect(); - { - ::std::io::_eprint(format_args!("required fees: {0:?}\n", required_fees)); - }; - let (_, suggested_fee) = required_fees[0] - .split_at(required_fees[0].rfind(|c: char| !c.is_numeric())?); - { - ::std::io::_eprint(format_args!("suggested fee: {0}\n", suggested_fee)); - }; - suggested_fee.parse::().ok().or(suggested_fee[1..].parse::().ok()) - } -} -pub mod state { - use super::error::DaemonError; - use crate::{channel::GrpcChannel, networks::ChainKind}; - use cosmwasm_std::Addr; - use cw_orch_core::{ - environment::{DeployDetails, StateInterface}, - CwEnvError, - }; - use ibc_chain_registry::chain::ChainData; - use serde::Serialize; - use serde_json::{json, Value}; - use std::{collections::HashMap, env, fs::File, path::Path}; - use tonic::transport::Channel; - /// Stores the chain information and deployment state. - /// Uses a simple JSON file to store the deployment information locally. - pub struct DaemonState { - /// this is passed via env var STATE_FILE - pub json_file_path: String, - /// Deployment identifier - pub deployment_id: String, - /// gRPC channel - pub grpc_channel: Channel, - /// Information about the chain - pub chain_data: ChainData, - } - #[automatically_derived] - impl ::core::clone::Clone for DaemonState { - #[inline] - fn clone(&self) -> DaemonState { - DaemonState { - json_file_path: ::core::clone::Clone::clone(&self.json_file_path), - deployment_id: ::core::clone::Clone::clone(&self.deployment_id), - grpc_channel: ::core::clone::Clone::clone(&self.grpc_channel), - chain_data: ::core::clone::Clone::clone(&self.chain_data), - } - } - } - #[automatically_derived] - impl ::core::fmt::Debug for DaemonState { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field4_finish( - f, - "DaemonState", - "json_file_path", - &self.json_file_path, - "deployment_id", - &self.deployment_id, - "grpc_channel", - &self.grpc_channel, - "chain_data", - &&self.chain_data, - ) - } - } - impl DaemonState { - /// Creates a new state from the given chain data and deployment id. - /// Attempts to connect to any of the provided gRPC endpoints. - pub async fn new( - mut chain_data: ChainData, - deployment_id: String, - ) -> Result { - if chain_data.apis.grpc.is_empty() { - return Err(DaemonError::GRPCListIsEmpty); - } - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Found {0} gRPC endpoints", - chain_data.apis.grpc.len(), - ), - lvl, - &( - "cw_orch_daemon::state", - "cw_orch_daemon::state", - "cw-orch-daemon/src/state.rs", - ), - 40u32, - ::log::__private_api::Option::None, - ); - } - }; - let grpc_channel = GrpcChannel::connect( - &chain_data.apis.grpc, - &chain_data.chain_id, - ) - .await?; - let mut json_file_path = env::var("STATE_FILE") - .unwrap_or("./state.json".to_string()); - if chain_data.network_type == ChainKind::Local.to_string() { - let name = Path::new(&json_file_path) - .file_stem() - .unwrap() - .to_str() - .unwrap(); - let folder = Path::new(&json_file_path) - .parent() - .unwrap() - .to_str() - .unwrap(); - json_file_path = { - let res = ::alloc::fmt::format( - format_args!("{0}/{1}_local.json", folder, name), - ); - res - }; - } - let shortest_denom_token = chain_data - .fees - .fee_tokens - .iter() - .fold( - chain_data.fees.fee_tokens[0].clone(), - |acc, item| { - if item.denom.len() < acc.denom.len() { - item.clone() - } else { - acc - } - }, - ); - chain_data - .fees - .fee_tokens = <[_]>::into_vec( - #[rustc_box] - ::alloc::boxed::Box::new([shortest_denom_token]), - ); - let state = DaemonState { - json_file_path, - deployment_id, - grpc_channel, - chain_data, - }; - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Writing daemon state JSON file: {0:#?}", - state.json_file_path, - ), - lvl, - &( - "cw_orch_daemon::state", - "cw_orch_daemon::state", - "cw-orch-daemon/src/state.rs", - ), - 87u32, - ::log::__private_api::Option::None, - ); - } - }; - crate::json_file::write( - &state.json_file_path, - &state.chain_data.chain_id.to_string(), - &state.chain_data.chain_name, - &state.deployment_id, - ); - Ok(state) - } - /// Get the state filepath and read it as json - fn read_state(&self) -> serde_json::Value { - crate::json_file::read(&self.json_file_path) - } - /// Retrieve a stateful value using the chainId and networkId - pub fn get(&self, key: &str) -> Value { - let json = self.read_state(); - json[&self.chain_data.chain_name][&self.chain_data.chain_id.to_string()][key] - .clone() - } - /// Set a stateful value using the chainId and networkId - pub fn set(&self, key: &str, contract_id: &str, value: T) { - let mut json = self.read_state(); - json[&self - .chain_data - .chain_name][&self - .chain_data - .chain_id - .to_string()][key][contract_id] = ::serde_json::to_value(&value) - .unwrap(); - serde_json::to_writer_pretty( - File::create(&self.json_file_path).unwrap(), - &json, - ) - .unwrap(); - } - } - impl StateInterface for DaemonState { - /// Read address for contract in deployment id from state file - fn get_address(&self, contract_id: &str) -> Result { - let value = self - .get(&self.deployment_id) - .get(contract_id) - .ok_or_else(|| CwEnvError::AddrNotInStore(contract_id.to_owned()))? - .clone(); - Ok(Addr::unchecked(value.as_str().unwrap())) - } - /// Set address for contract in deployment id in state file - fn set_address(&mut self, contract_id: &str, address: &Addr) { - self.set(&self.deployment_id, contract_id, address.as_str()); - } - /// Get the locally-saved version of the contract's version on this network - fn get_code_id(&self, contract_id: &str) -> Result { - let value = self - .get("code_ids") - .get(contract_id) - .ok_or_else(|| CwEnvError::CodeIdNotInStore(contract_id.to_owned()))? - .clone(); - Ok(value.as_u64().unwrap()) - } - /// Set the locally-saved version of the contract's latest version on this network - fn set_code_id(&mut self, contract_id: &str, code_id: u64) { - self.set("code_ids", contract_id, code_id); - } - /// Get all addresses for deployment id from state file - fn get_all_addresses(&self) -> Result, CwEnvError> { - let mut store = HashMap::new(); - let addresses = self.get(&self.deployment_id); - let value = addresses.as_object().unwrap(); - for (id, addr) in value { - store.insert(id.clone(), Addr::unchecked(addr.as_str().unwrap())); - } - Ok(store) - } - fn get_all_code_ids(&self) -> Result, CwEnvError> { - let mut store = HashMap::new(); - let code_ids = self.get("code_ids"); - let value = code_ids.as_object().unwrap(); - for (id, code_id) in value { - store.insert(id.clone(), code_id.as_u64().unwrap()); - } - Ok(store) - } - fn deploy_details(&self) -> DeployDetails { - DeployDetails { - chain_id: self.chain_data.chain_id.to_string(), - chain_name: self.chain_data.chain_name.clone(), - deployment_id: self.deployment_id.clone(), - } - } - } -} -pub mod sync { - mod builder { - use ibc_chain_registry::chain::ChainData; - use crate::DaemonAsyncBuilder; - use super::{super::error::DaemonError, core::Daemon}; - /// Create [`Daemon`] through [`DaemonBuilder`] - /// ## Example - /// ```no_run - /// use cw_orch_daemon::{networks, DaemonBuilder}; - /// - /// let Daemon = DaemonBuilder::default() - /// .chain(networks::LOCAL_JUNO) - /// .deployment_id("v0.1.0") - /// .build() - /// .unwrap(); - /// ``` - pub struct DaemonBuilder { - pub(crate) chain: Option, - pub(crate) handle: Option, - pub(crate) deployment_id: Option, - /// Wallet mnemonic - pub(crate) mnemonic: Option, - } - #[automatically_derived] - impl ::core::clone::Clone for DaemonBuilder { - #[inline] - fn clone(&self) -> DaemonBuilder { - DaemonBuilder { - chain: ::core::clone::Clone::clone(&self.chain), - handle: ::core::clone::Clone::clone(&self.handle), - deployment_id: ::core::clone::Clone::clone(&self.deployment_id), - mnemonic: ::core::clone::Clone::clone(&self.mnemonic), - } - } - } - #[automatically_derived] - impl ::core::default::Default for DaemonBuilder { - #[inline] - fn default() -> DaemonBuilder { - DaemonBuilder { - chain: ::core::default::Default::default(), - handle: ::core::default::Default::default(), - deployment_id: ::core::default::Default::default(), - mnemonic: ::core::default::Default::default(), - } - } - } - impl DaemonBuilder { - /// Set the chain the Daemon will connect to - pub fn chain(&mut self, chain: impl Into) -> &mut Self { - self.chain = Some(chain.into()); - self - } - /// Set the deployment id to use for the Daemon interactions - /// Defaults to `default` - pub fn deployment_id( - &mut self, - deployment_id: impl Into, - ) -> &mut Self { - self.deployment_id = Some(deployment_id.into()); - self - } - /// Set the tokio runtime handle to use for the Daemon - /// - /// ## Example - /// ```no_run - /// use cw_orch_daemon::Daemon; - /// use tokio::runtime::Runtime; - /// let rt = Runtime::new().unwrap(); - /// let Daemon = Daemon::builder() - /// .handle(rt.handle()) - /// // ... - /// .build() - /// .unwrap(); - /// ``` - pub fn handle(&mut self, handle: &tokio::runtime::Handle) -> &mut Self { - self.handle = Some(handle.clone()); - self - } - /// Set the mnemonic to use with this chain. - pub fn mnemonic(&mut self, mnemonic: impl ToString) -> &mut Self { - self.mnemonic = Some(mnemonic.to_string()); - self - } - /// Build a Daemon - pub fn build(&self) -> Result { - let rt_handle = self - .handle - .clone() - .ok_or(DaemonError::BuilderMissing("runtime handle".into()))?; - let daemon = rt_handle - .block_on(DaemonAsyncBuilder::from(self.clone()).build())?; - Ok(Daemon { rt_handle, daemon }) - } - } - } - mod core { - use std::{fmt::Debug, rc::Rc, time::Duration}; - use super::super::{sender::Wallet, DaemonAsync}; - use crate::{ - queriers::{DaemonQuerier, Node}, - CosmTxResponse, DaemonBuilder, DaemonError, DaemonState, - }; - use cosmrs::tendermint::Time; - use cosmwasm_std::{Addr, Coin}; - use cw_orch_core::{ - contract::{interface_traits::Uploadable, WasmPath}, - environment::{ChainState, TxHandler}, - }; - use serde::{de::DeserializeOwned, Serialize}; - use tokio::runtime::Handle; - use tonic::transport::Channel; - /** - Represents a blockchain node. - Is constructed with the [DaemonBuilder]. - - ## Usage - - ```rust,no_run - use cw_orch_daemon::{Daemon, networks}; - use tokio::runtime::Runtime; - - let rt = Runtime::new().unwrap(); - let daemon: Daemon = Daemon::builder() - .chain(networks::JUNO_1) - .handle(rt.handle()) - .build() - .unwrap(); - ``` - ## Environment Execution - - The Daemon implements [`TxHandler`] which allows you to perform transactions on the chain. - - ## Querying - - Different Cosmos SDK modules can be queried through the daemon by calling the [`Daemon.query_client`] method with a specific querier. - See [Querier](crate::queriers) for examples. -*/ - pub struct Daemon { - pub daemon: DaemonAsync, - /// Runtime handle to execute async tasks - pub rt_handle: Handle, - } - #[automatically_derived] - impl ::core::clone::Clone for Daemon { - #[inline] - fn clone(&self) -> Daemon { - Daemon { - daemon: ::core::clone::Clone::clone(&self.daemon), - rt_handle: ::core::clone::Clone::clone(&self.rt_handle), - } - } - } - impl Daemon { - /// Get the daemon builder - pub fn builder() -> DaemonBuilder { - DaemonBuilder::default() - } - /// Perform a query with a given querier - /// See [Querier](crate::queriers) for examples. - pub fn query_client(&self) -> Querier { - self.daemon.query_client() - } - /// Get the channel configured for this Daemon - pub fn channel(&self) -> Channel { - self.daemon.state.grpc_channel.clone() - } - /// Get the channel configured for this Daemon - pub fn wallet(&self) -> Wallet { - self.daemon.sender.clone() - } - } - impl ChainState for Daemon { - type Out = Rc; - fn state(&self) -> Self::Out { - self.daemon.state.clone() - } - } - impl TxHandler for Daemon { - type Response = CosmTxResponse; - type Error = DaemonError; - type ContractSource = WasmPath; - type Sender = Wallet; - fn sender(&self) -> Addr { - self.daemon.sender.address().unwrap() - } - fn set_sender(&mut self, sender: Self::Sender) { - self.daemon.sender = sender; - } - fn upload( - &self, - uploadable: &impl Uploadable, - ) -> Result { - self.rt_handle.block_on(self.daemon.upload(uploadable)) - } - fn execute( - &self, - exec_msg: &E, - coins: &[cosmwasm_std::Coin], - contract_address: &Addr, - ) -> Result { - self.rt_handle - .block_on(self.daemon.execute(exec_msg, coins, contract_address)) - } - fn instantiate( - &self, - code_id: u64, - init_msg: &I, - label: Option<&str>, - admin: Option<&Addr>, - coins: &[Coin], - ) -> Result { - self.rt_handle - .block_on( - self.daemon.instantiate(code_id, init_msg, label, admin, coins), - ) - } - fn query( - &self, - query_msg: &Q, - contract_address: &Addr, - ) -> Result { - self.rt_handle.block_on(self.daemon.query(query_msg, contract_address)) - } - fn migrate( - &self, - migrate_msg: &M, - new_code_id: u64, - contract_address: &Addr, - ) -> Result { - self.rt_handle - .block_on( - self.daemon.migrate(migrate_msg, new_code_id, contract_address), - ) - } - fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { - let mut last_height = self - .rt_handle - .block_on(self.query_client::().block_height())?; - let end_height = last_height + amount; - while last_height < end_height { - self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(4))); - last_height = self - .rt_handle - .block_on(self.query_client::().block_height())?; - } - Ok(()) - } - fn wait_seconds(&self, secs: u64) -> Result<(), DaemonError> { - self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(secs))); - Ok(()) - } - fn next_block(&self) -> Result<(), DaemonError> { - let mut last_height = self - .rt_handle - .block_on(self.query_client::().block_height())?; - let end_height = last_height + 1; - while last_height < end_height { - self.rt_handle.block_on(tokio::time::sleep(Duration::from_secs(4))); - last_height = self - .rt_handle - .block_on(self.query_client::().block_height())?; - } - Ok(()) - } - fn block_info(&self) -> Result { - let block = self - .rt_handle - .block_on(self.query_client::().latest_block())?; - let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; - let time = cosmwasm_std::Timestamp::from_nanos( - since_epoch.as_nanos() as u64, - ); - Ok(cosmwasm_std::BlockInfo { - height: block.header.height.value(), - time, - chain_id: block.header.chain_id.to_string(), - }) - } - } - } - pub use self::{builder::*, core::*}; -} -pub mod tx_resp { - use super::{ - cosmos_modules::{ - abci::{AbciMessageLog, Attribute, StringEvent, TxResponse}, - tendermint_abci::Event, - }, - error::DaemonError, - }; - use chrono::{DateTime, NaiveDateTime, TimeZone, Utc}; - use cosmwasm_std::{to_binary, Binary, StdError, StdResult}; - use cw_orch_core::environment::IndexResponse; - use serde::{Deserialize, Serialize}; - const FORMAT: &str = "%Y-%m-%dT%H:%M:%S%.f"; - const FORMAT_TZ_SUPPLIED: &str = "%Y-%m-%dT%H:%M:%S.%f%:z"; - const FORMAT_SHORT_Z: &str = "%Y-%m-%dT%H:%M:%SZ"; - const FORMAT_SHORT_Z2: &str = "%Y-%m-%dT%H:%M:%S.%fZ"; - /// The response from a transaction performed on a blockchain. - pub struct CosmTxResponse { - /// Height of the block in which the transaction was included. - pub height: u64, - /// Transaction hash. - pub txhash: String, - /// Transaction index within the block. - pub codespace: String, - /// Transaction result code - pub code: usize, - /// Arbitrary data that can be included in a transaction. - pub data: String, - /// Raw log message. - pub raw_log: String, - /// Logs of the transaction. - pub logs: Vec, - /// Transaction info. - pub info: String, - /// Gas limit. - pub gas_wanted: u64, - /// Gas used. - pub gas_used: u64, - /// Timestamp of the block in which the transaction was included. - pub timestamp: DateTime, - /// Transaction events. - pub events: Vec, - } - #[automatically_derived] - impl ::core::fmt::Debug for CosmTxResponse { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - let names: &'static _ = &[ - "height", - "txhash", - "codespace", - "code", - "data", - "raw_log", - "logs", - "info", - "gas_wanted", - "gas_used", - "timestamp", - "events", - ]; - let values: &[&dyn ::core::fmt::Debug] = &[ - &self.height, - &self.txhash, - &self.codespace, - &self.code, - &self.data, - &self.raw_log, - &self.logs, - &self.info, - &self.gas_wanted, - &self.gas_used, - &self.timestamp, - &&self.events, - ]; - ::core::fmt::Formatter::debug_struct_fields_finish( - f, - "CosmTxResponse", - names, - values, - ) - } - } - #[automatically_derived] - impl ::core::default::Default for CosmTxResponse { - #[inline] - fn default() -> CosmTxResponse { - CosmTxResponse { - height: ::core::default::Default::default(), - txhash: ::core::default::Default::default(), - codespace: ::core::default::Default::default(), - code: ::core::default::Default::default(), - data: ::core::default::Default::default(), - raw_log: ::core::default::Default::default(), - logs: ::core::default::Default::default(), - info: ::core::default::Default::default(), - gas_wanted: ::core::default::Default::default(), - gas_used: ::core::default::Default::default(), - timestamp: ::core::default::Default::default(), - events: ::core::default::Default::default(), - } - } - } - impl CosmTxResponse { - /// find a attribute's value from TX logs. - /// returns: msg_index and value - pub fn get_attribute_from_logs( - &self, - event_type: &str, - attribute_key: &str, - ) -> Vec<(usize, String)> { - let mut response: Vec<(usize, String)> = Default::default(); - let logs = &self.logs; - for log_part in logs { - let msg_index = log_part.msg_index.unwrap_or_default(); - let events = &log_part.events; - let events_filtered = events - .iter() - .filter(|event| event.s_type == event_type) - .collect::>(); - if let Some(event) = events_filtered.first() { - let attributes_filtered = event - .attributes - .iter() - .filter(|attr| attr.key == attribute_key) - .map(|f| f.value.clone()) - .collect::>(); - if let Some(attr_key) = attributes_filtered.first() { - response.push((msg_index, attr_key.clone())); - } - } - } - response - } - /// get the list of event types from a TX record - pub fn get_events(&self, event_type: &str) -> Vec { - let mut response: Vec = Default::default(); - for log_part in &self.logs { - let events = &log_part.events; - let events_filtered = events - .iter() - .filter(|event| event.s_type == event_type) - .collect::>(); - for event in events_filtered { - response.push(event.clone()); - } - } - response - } - } - impl From<&serde_json::Value> for TxResultBlockMsg { - fn from(value: &serde_json::Value) -> Self { - serde_json::from_value(value.clone()).unwrap() - } - } - impl From for CosmTxResponse { - fn from(tx: TxResponse) -> Self { - Self { - height: tx.height as u64, - txhash: tx.txhash, - codespace: tx.codespace, - code: tx.code as usize, - data: tx.data, - raw_log: tx.raw_log, - logs: tx.logs.into_iter().map(TxResultBlockMsg::from).collect(), - info: tx.info, - gas_wanted: tx.gas_wanted as u64, - gas_used: tx.gas_used as u64, - timestamp: parse_timestamp(tx.timestamp).unwrap(), - events: tx.events, - } - } - } - impl IndexResponse for CosmTxResponse { - fn events(&self) -> Vec { - let mut parsed_events = ::alloc::vec::Vec::new(); - for event in &self.events { - let mut pattr = ::alloc::vec::Vec::new(); - for attr in &event.attributes { - pattr - .push(cosmwasm_std::Attribute { - key: attr.key.clone(), - value: attr.value.clone(), - }) - } - let pevent = cosmwasm_std::Event::new(event.r#type.clone()) - .add_attributes(pattr); - parsed_events.push(pevent); - } - parsed_events - } - fn data(&self) -> Option { - if self.data.is_empty() { - None - } else { - Some(to_binary(self.data.as_bytes()).unwrap()) - } - } - fn event_attr_value( - &self, - event_type: &str, - attr_key: &str, - ) -> StdResult { - for event in &self.events { - if event.r#type == event_type { - for attr in &event.attributes { - if attr.key == attr_key { - return Ok(attr.value.clone()); - } - } - } - } - Err( - StdError::generic_err({ - let res = ::alloc::fmt::format( - format_args!( - "event of type {0} does not have a value at key {1}", - event_type, - attr_key, - ), - ); - res - }), - ) - } - } - /// The events from a single message in a transaction. - pub struct TxResultBlockMsg { - /// index of the message in the transaction - pub msg_index: Option, - /// Events from this message - pub events: Vec, - } - #[automatically_derived] - impl ::core::clone::Clone for TxResultBlockMsg { - #[inline] - fn clone(&self) -> TxResultBlockMsg { - TxResultBlockMsg { - msg_index: ::core::clone::Clone::clone(&self.msg_index), - events: ::core::clone::Clone::clone(&self.events), - } - } - } - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl _serde::Serialize for TxResultBlockMsg { - fn serialize<__S>( - &self, - __serializer: __S, - ) -> _serde::__private::Result<__S::Ok, __S::Error> - where - __S: _serde::Serializer, - { - let mut __serde_state = _serde::Serializer::serialize_struct( - __serializer, - "TxResultBlockMsg", - false as usize + 1 + 1, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "msg_index", - &self.msg_index, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "events", - &self.events, - )?; - _serde::ser::SerializeStruct::end(__serde_state) - } - } - }; - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl<'de> _serde::Deserialize<'de> for TxResultBlockMsg { - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - #[allow(non_camel_case_types)] - #[doc(hidden)] - enum __Field { - __field0, - __field1, - __ignore, - } - #[doc(hidden)] - struct __FieldVisitor; - impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { - type Value = __Field; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "field identifier", - ) - } - fn visit_u64<__E>( - self, - __value: u64, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - 0u64 => _serde::__private::Ok(__Field::__field0), - 1u64 => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_str<__E>( - self, - __value: &str, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - "msg_index" => _serde::__private::Ok(__Field::__field0), - "events" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_bytes<__E>( - self, - __value: &[u8], - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - b"msg_index" => _serde::__private::Ok(__Field::__field0), - b"events" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - } - impl<'de> _serde::Deserialize<'de> for __Field { - #[inline] - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - _serde::Deserializer::deserialize_identifier( - __deserializer, - __FieldVisitor, - ) - } - } - #[doc(hidden)] - struct __Visitor<'de> { - marker: _serde::__private::PhantomData, - lifetime: _serde::__private::PhantomData<&'de ()>, - } - impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { - type Value = TxResultBlockMsg; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "struct TxResultBlockMsg", - ) - } - #[inline] - fn visit_seq<__A>( - self, - mut __seq: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<'de>, - { - let __field0 = match _serde::de::SeqAccess::next_element::< - Option, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 0usize, - &"struct TxResultBlockMsg with 2 elements", - ), - ); - } - }; - let __field1 = match _serde::de::SeqAccess::next_element::< - Vec, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 1usize, - &"struct TxResultBlockMsg with 2 elements", - ), - ); - } - }; - _serde::__private::Ok(TxResultBlockMsg { - msg_index: __field0, - events: __field1, - }) - } - #[inline] - fn visit_map<__A>( - self, - mut __map: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::MapAccess<'de>, - { - let mut __field0: _serde::__private::Option> = _serde::__private::None; - let mut __field1: _serde::__private::Option< - Vec, - > = _serde::__private::None; - while let _serde::__private::Some(__key) - = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { - match __key { - __Field::__field0 => { - if _serde::__private::Option::is_some(&__field0) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field( - "msg_index", - ), - ); - } - __field0 = _serde::__private::Some( - _serde::de::MapAccess::next_value::< - Option, - >(&mut __map)?, - ); - } - __Field::__field1 => { - if _serde::__private::Option::is_some(&__field1) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field("events"), - ); - } - __field1 = _serde::__private::Some( - _serde::de::MapAccess::next_value::< - Vec, - >(&mut __map)?, - ); - } - _ => { - let _ = _serde::de::MapAccess::next_value::< - _serde::de::IgnoredAny, - >(&mut __map)?; - } - } - } - let __field0 = match __field0 { - _serde::__private::Some(__field0) => __field0, - _serde::__private::None => { - _serde::__private::de::missing_field("msg_index")? - } - }; - let __field1 = match __field1 { - _serde::__private::Some(__field1) => __field1, - _serde::__private::None => { - _serde::__private::de::missing_field("events")? - } - }; - _serde::__private::Ok(TxResultBlockMsg { - msg_index: __field0, - events: __field1, - }) - } - } - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &["msg_index", "events"]; - _serde::Deserializer::deserialize_struct( - __deserializer, - "TxResultBlockMsg", - FIELDS, - __Visitor { - marker: _serde::__private::PhantomData::, - lifetime: _serde::__private::PhantomData, - }, - ) - } - } - }; - #[automatically_derived] - impl ::core::fmt::Debug for TxResultBlockMsg { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "TxResultBlockMsg", - "msg_index", - &self.msg_index, - "events", - &&self.events, - ) - } - } - impl From for TxResultBlockMsg { - fn from(msg: AbciMessageLog) -> Self { - Self { - msg_index: Some(msg.msg_index as usize), - events: msg.events.into_iter().map(TxResultBlockEvent::from).collect(), - } - } - } - /// A single event from a transaction and its attributes. - pub struct TxResultBlockEvent { - #[serde(rename = "type")] - /// Type of the event - pub s_type: String, - /// Attributes of the event - pub attributes: Vec, - } - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl<'de> _serde::Deserialize<'de> for TxResultBlockEvent { - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - #[allow(non_camel_case_types)] - #[doc(hidden)] - enum __Field { - __field0, - __field1, - __ignore, - } - #[doc(hidden)] - struct __FieldVisitor; - impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { - type Value = __Field; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "field identifier", - ) - } - fn visit_u64<__E>( - self, - __value: u64, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - 0u64 => _serde::__private::Ok(__Field::__field0), - 1u64 => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_str<__E>( - self, - __value: &str, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - "type" => _serde::__private::Ok(__Field::__field0), - "attributes" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_bytes<__E>( - self, - __value: &[u8], - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - b"type" => _serde::__private::Ok(__Field::__field0), - b"attributes" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - } - impl<'de> _serde::Deserialize<'de> for __Field { - #[inline] - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - _serde::Deserializer::deserialize_identifier( - __deserializer, - __FieldVisitor, - ) - } - } - #[doc(hidden)] - struct __Visitor<'de> { - marker: _serde::__private::PhantomData, - lifetime: _serde::__private::PhantomData<&'de ()>, - } - impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { - type Value = TxResultBlockEvent; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "struct TxResultBlockEvent", - ) - } - #[inline] - fn visit_seq<__A>( - self, - mut __seq: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<'de>, - { - let __field0 = match _serde::de::SeqAccess::next_element::< - String, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 0usize, - &"struct TxResultBlockEvent with 2 elements", - ), - ); - } - }; - let __field1 = match _serde::de::SeqAccess::next_element::< - Vec, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 1usize, - &"struct TxResultBlockEvent with 2 elements", - ), - ); - } - }; - _serde::__private::Ok(TxResultBlockEvent { - s_type: __field0, - attributes: __field1, - }) - } - #[inline] - fn visit_map<__A>( - self, - mut __map: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::MapAccess<'de>, - { - let mut __field0: _serde::__private::Option = _serde::__private::None; - let mut __field1: _serde::__private::Option< - Vec, - > = _serde::__private::None; - while let _serde::__private::Some(__key) - = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { - match __key { - __Field::__field0 => { - if _serde::__private::Option::is_some(&__field0) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field("type"), - ); - } - __field0 = _serde::__private::Some( - _serde::de::MapAccess::next_value::(&mut __map)?, - ); - } - __Field::__field1 => { - if _serde::__private::Option::is_some(&__field1) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field( - "attributes", - ), - ); - } - __field1 = _serde::__private::Some( - _serde::de::MapAccess::next_value::< - Vec, - >(&mut __map)?, - ); - } - _ => { - let _ = _serde::de::MapAccess::next_value::< - _serde::de::IgnoredAny, - >(&mut __map)?; - } - } - } - let __field0 = match __field0 { - _serde::__private::Some(__field0) => __field0, - _serde::__private::None => { - _serde::__private::de::missing_field("type")? - } - }; - let __field1 = match __field1 { - _serde::__private::Some(__field1) => __field1, - _serde::__private::None => { - _serde::__private::de::missing_field("attributes")? - } - }; - _serde::__private::Ok(TxResultBlockEvent { - s_type: __field0, - attributes: __field1, - }) - } - } - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &["type", "attributes"]; - _serde::Deserializer::deserialize_struct( - __deserializer, - "TxResultBlockEvent", - FIELDS, - __Visitor { - marker: _serde::__private::PhantomData::, - lifetime: _serde::__private::PhantomData, - }, - ) - } - } - }; - #[automatically_derived] - impl ::core::clone::Clone for TxResultBlockEvent { - #[inline] - fn clone(&self) -> TxResultBlockEvent { - TxResultBlockEvent { - s_type: ::core::clone::Clone::clone(&self.s_type), - attributes: ::core::clone::Clone::clone(&self.attributes), - } - } - } - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl _serde::Serialize for TxResultBlockEvent { - fn serialize<__S>( - &self, - __serializer: __S, - ) -> _serde::__private::Result<__S::Ok, __S::Error> - where - __S: _serde::Serializer, - { - let mut __serde_state = _serde::Serializer::serialize_struct( - __serializer, - "TxResultBlockEvent", - false as usize + 1 + 1, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "type", - &self.s_type, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "attributes", - &self.attributes, - )?; - _serde::ser::SerializeStruct::end(__serde_state) - } - } - }; - #[automatically_derived] - impl ::core::fmt::Debug for TxResultBlockEvent { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "TxResultBlockEvent", - "s_type", - &self.s_type, - "attributes", - &&self.attributes, - ) - } - } - impl From for TxResultBlockEvent { - fn from(event: StringEvent) -> Self { - Self { - s_type: event.r#type, - attributes: event - .attributes - .into_iter() - .map(TxResultBlockAttribute::from) - .collect(), - } - } - } - impl TxResultBlockEvent { - /// get all key/values from the event that have the key 'key' - pub fn get_attributes(&self, key: &str) -> Vec { - self.attributes.iter().filter(|attr| attr.key == key).cloned().collect() - } - /// return the first value of the first attribute that has the key 'key' - pub fn get_first_attribute_value(&self, key: &str) -> Option { - self.get_attributes(key).first().map(|attr| attr.value.clone()) - } - } - /// A single attribute of an event. - pub struct TxResultBlockAttribute { - /// Key of the attribute - pub key: String, - /// Value of the attribute - pub value: String, - } - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl<'de> _serde::Deserialize<'de> for TxResultBlockAttribute { - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - #[allow(non_camel_case_types)] - #[doc(hidden)] - enum __Field { - __field0, - __field1, - __ignore, - } - #[doc(hidden)] - struct __FieldVisitor; - impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { - type Value = __Field; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "field identifier", - ) - } - fn visit_u64<__E>( - self, - __value: u64, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - 0u64 => _serde::__private::Ok(__Field::__field0), - 1u64 => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_str<__E>( - self, - __value: &str, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - "key" => _serde::__private::Ok(__Field::__field0), - "value" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_bytes<__E>( - self, - __value: &[u8], - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - b"key" => _serde::__private::Ok(__Field::__field0), - b"value" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - } - impl<'de> _serde::Deserialize<'de> for __Field { - #[inline] - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - _serde::Deserializer::deserialize_identifier( - __deserializer, - __FieldVisitor, - ) - } - } - #[doc(hidden)] - struct __Visitor<'de> { - marker: _serde::__private::PhantomData, - lifetime: _serde::__private::PhantomData<&'de ()>, - } - impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { - type Value = TxResultBlockAttribute; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "struct TxResultBlockAttribute", - ) - } - #[inline] - fn visit_seq<__A>( - self, - mut __seq: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<'de>, - { - let __field0 = match _serde::de::SeqAccess::next_element::< - String, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 0usize, - &"struct TxResultBlockAttribute with 2 elements", - ), - ); - } - }; - let __field1 = match _serde::de::SeqAccess::next_element::< - String, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 1usize, - &"struct TxResultBlockAttribute with 2 elements", - ), - ); - } - }; - _serde::__private::Ok(TxResultBlockAttribute { - key: __field0, - value: __field1, - }) - } - #[inline] - fn visit_map<__A>( - self, - mut __map: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::MapAccess<'de>, - { - let mut __field0: _serde::__private::Option = _serde::__private::None; - let mut __field1: _serde::__private::Option = _serde::__private::None; - while let _serde::__private::Some(__key) - = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { - match __key { - __Field::__field0 => { - if _serde::__private::Option::is_some(&__field0) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field("key"), - ); - } - __field0 = _serde::__private::Some( - _serde::de::MapAccess::next_value::(&mut __map)?, - ); - } - __Field::__field1 => { - if _serde::__private::Option::is_some(&__field1) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field("value"), - ); - } - __field1 = _serde::__private::Some( - _serde::de::MapAccess::next_value::(&mut __map)?, - ); - } - _ => { - let _ = _serde::de::MapAccess::next_value::< - _serde::de::IgnoredAny, - >(&mut __map)?; - } - } - } - let __field0 = match __field0 { - _serde::__private::Some(__field0) => __field0, - _serde::__private::None => { - _serde::__private::de::missing_field("key")? - } - }; - let __field1 = match __field1 { - _serde::__private::Some(__field1) => __field1, - _serde::__private::None => { - _serde::__private::de::missing_field("value")? - } - }; - _serde::__private::Ok(TxResultBlockAttribute { - key: __field0, - value: __field1, - }) - } - } - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &["key", "value"]; - _serde::Deserializer::deserialize_struct( - __deserializer, - "TxResultBlockAttribute", - FIELDS, - __Visitor { - marker: _serde::__private::PhantomData::, - lifetime: _serde::__private::PhantomData, - }, - ) - } - } - }; - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl _serde::Serialize for TxResultBlockAttribute { - fn serialize<__S>( - &self, - __serializer: __S, - ) -> _serde::__private::Result<__S::Ok, __S::Error> - where - __S: _serde::Serializer, - { - let mut __serde_state = _serde::Serializer::serialize_struct( - __serializer, - "TxResultBlockAttribute", - false as usize + 1 + 1, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "key", - &self.key, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "value", - &self.value, - )?; - _serde::ser::SerializeStruct::end(__serde_state) - } - } - }; - #[automatically_derived] - impl ::core::clone::Clone for TxResultBlockAttribute { - #[inline] - fn clone(&self) -> TxResultBlockAttribute { - TxResultBlockAttribute { - key: ::core::clone::Clone::clone(&self.key), - value: ::core::clone::Clone::clone(&self.value), - } - } - } - #[automatically_derived] - impl ::core::fmt::Debug for TxResultBlockAttribute { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "TxResultBlockAttribute", - "key", - &self.key, - "value", - &&self.value, - ) - } - } - impl From for TxResultBlockAttribute { - fn from(a: Attribute) -> Self { - Self { key: a.key, value: a.value } - } - } - /// Parse a string timestamp into a `DateTime` - pub fn parse_timestamp(s: String) -> Result, DaemonError> { - let len = s.len(); - let slice_len = if s.contains('.') { len.saturating_sub(4) } else { len }; - let sliced = &s[0..slice_len]; - match NaiveDateTime::parse_from_str(sliced, FORMAT) { - Err(_e) => { - match NaiveDateTime::parse_from_str(&s, FORMAT_TZ_SUPPLIED) { - Err(_e2) => { - match NaiveDateTime::parse_from_str(sliced, FORMAT_SHORT_Z) { - Err(_e3) => { - match NaiveDateTime::parse_from_str(&s, FORMAT_SHORT_Z2) { - Err(_e4) => { - { - ::std::io::_eprint( - format_args!("DateTime Fail {0} {1:#?}\n", s, _e4), - ); - }; - Err(DaemonError::StdErr(_e4.to_string())) - } - Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), - } - } - Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), - } - } - Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), - } - } - Ok(dt) => Ok(Utc.from_utc_datetime(&dt)), - } - } -} -pub mod keys { - #![allow(unused)] - pub mod private { - use super::public::PublicKey; - use crate::proto::injective::{InjectivePubKey, ETHEREUM_COIN_TYPE}; - use crate::DaemonError; - use base64::Engine; - use bitcoin::{ - bip32::{ExtendedPrivKey, IntoDerivationPath}, - Network, - }; - use cosmrs::tx::SignerPublicKey; - use hkd32::mnemonic::{Phrase, Seed}; - use rand_core::OsRng; - use secp256k1::Secp256k1; - /// The Private key structure that is used to generate signatures and public keys - /// WARNING: No Security Audit has been performed - pub struct PrivateKey { - #[allow(missing_docs)] - pub account: u32, - #[allow(missing_docs)] - pub index: u32, - #[allow(missing_docs)] - pub coin_type: u32, - /// The 24 words used to generate this private key - mnemonic: Option, - #[allow(dead_code)] - /// This is used for testing - root_private_key: ExtendedPrivKey, - /// The private key - private_key: ExtendedPrivKey, - } - #[automatically_derived] - impl ::core::clone::Clone for PrivateKey { - #[inline] - fn clone(&self) -> PrivateKey { - PrivateKey { - account: ::core::clone::Clone::clone(&self.account), - index: ::core::clone::Clone::clone(&self.index), - coin_type: ::core::clone::Clone::clone(&self.coin_type), - mnemonic: ::core::clone::Clone::clone(&self.mnemonic), - root_private_key: ::core::clone::Clone::clone( - &self.root_private_key, - ), - private_key: ::core::clone::Clone::clone(&self.private_key), - } - } - } - impl PrivateKey { - /// Generate a new private key - pub fn new( - secp: &Secp256k1, - coin_type: u32, - ) -> Result { - let phrase = hkd32::mnemonic::Phrase::random( - OsRng, - hkd32::mnemonic::Language::English, - ); - PrivateKey::gen_private_key_phrase(secp, phrase, 0, 0, coin_type, "") - } - /// generate a new private key with a seed phrase - pub fn new_seed( - secp: &Secp256k1, - seed_phrase: &str, - coin_type: u32, - ) -> Result { - let phrase = hkd32::mnemonic::Phrase::random( - OsRng, - hkd32::mnemonic::Language::English, - ); - PrivateKey::gen_private_key_phrase( - secp, - phrase, - 0, - 0, - coin_type, - seed_phrase, - ) - } - /// for private key recovery. This is also used by wallet routines to re-hydrate the structure - pub fn from_words( - secp: &Secp256k1, - words: &str, - account: u32, - index: u32, - coin_type: u32, - ) -> Result { - if words.split(' ').count() != 24 { - return Err(DaemonError::WrongLength); - } - match hkd32::mnemonic::Phrase::new( - words, - hkd32::mnemonic::Language::English, - ) { - Ok(phrase) => { - PrivateKey::gen_private_key_phrase( - secp, - phrase, - account, - index, - coin_type, - "", - ) - } - Err(_) => Err(DaemonError::Phrasing), - } - } - /// for private key recovery with seed phrase - pub fn from_words_seed( - secp: &Secp256k1, - words: &str, - seed_pass: &str, - coin_type: u32, - ) -> Result { - match hkd32::mnemonic::Phrase::new( - words, - hkd32::mnemonic::Language::English, - ) { - Ok(phrase) => { - PrivateKey::gen_private_key_phrase( - secp, - phrase, - 0, - 0, - coin_type, - seed_pass, - ) - } - Err(_) => Err(DaemonError::Phrasing), - } - } - /// generate the public key for this private key - pub fn public_key( - &self, - secp: &Secp256k1, - ) -> PublicKey { - if self.coin_type == ETHEREUM_COIN_TYPE { - { - ::core::panicking::panic_fmt( - format_args!( - "Coin Type {0} not supported without eth feature", - ETHEREUM_COIN_TYPE, - ), - ); - }; - } - let x = self.private_key.private_key.public_key(secp); - PublicKey::from_bitcoin_public_key(&bitcoin::PublicKey::new(x)) - } - pub fn get_injective_public_key( - &self, - secp: &Secp256k1, - ) -> SignerPublicKey { - use base64::engine::general_purpose; - use cosmrs::tx::MessageExt; - use secp256k1::SecretKey; - let secret_key = SecretKey::from_slice(self.raw_key().as_slice()) - .unwrap(); - let public_key = secp256k1::PublicKey::from_secret_key( - secp, - &secret_key, - ); - let vec_pk = public_key.serialize(); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "{0:?}, public key", - general_purpose::STANDARD.encode(vec_pk), - ), - lvl, - &( - "cw_orch_daemon::keys::private", - "cw_orch_daemon::keys::private", - "cw-orch-daemon/src/keys/private.rs", - ), - 124u32, - ::log::__private_api::Option::None, - ); - } - }; - let inj_key = InjectivePubKey { - key: vec_pk.into(), - }; - inj_key.to_any().unwrap().try_into().unwrap() - } - pub fn get_signer_public_key( - &self, - secp: &Secp256k1, - ) -> Option { - if self.coin_type == ETHEREUM_COIN_TYPE { - { - ::core::panicking::panic_fmt( - format_args!( - "Coin Type {0} not supported without eth feature", - ETHEREUM_COIN_TYPE, - ), - ); - }; - } - Some( - cosmrs::crypto::secp256k1::SigningKey::from_slice( - self.raw_key().as_slice(), - ) - .unwrap() - .public_key() - .into(), - ) - } - pub fn raw_key(&self) -> Vec { - self.private_key.private_key.secret_bytes().to_vec() - } - fn gen_private_key_phrase( - secp: &Secp256k1, - phrase: Phrase, - account: u32, - index: u32, - coin_type: u32, - seed_phrase: &str, - ) -> Result { - let seed = phrase.to_seed(seed_phrase); - let root_private_key = ExtendedPrivKey::new_master( - Network::Bitcoin, - seed.as_bytes(), - ) - .unwrap(); - let path = { - let res = ::alloc::fmt::format( - format_args!( - "m/44\'/{0}\'/{1}\'/0/{2}", - coin_type, - account, - index, - ), - ); - res - }; - let derivation_path = path.into_derivation_path()?; - let private_key = root_private_key.derive_priv(secp, &derivation_path)?; - Ok(PrivateKey { - account, - index, - coin_type, - mnemonic: Some(phrase), - root_private_key, - private_key, - }) - } - /// the words used to generate this private key - pub fn words(&self) -> Option<&str> { - self.mnemonic.as_ref().map(|phrase| phrase.phrase()) - } - /// used for testing - /// could potentially be used to recreate the private key instead of words - #[allow(dead_code)] - pub(crate) fn seed(&self, passwd: &str) -> Option { - self.mnemonic.as_ref().map(|phrase| phrase.to_seed(passwd)) - } - } - } - pub mod public { - use crate::DaemonError; - use bitcoin::bech32::{decode, encode, u5, FromBase32, ToBase32, Variant}; - pub use ed25519_dalek::VerifyingKey as Ed25519; - use ring::digest::{Context, SHA256}; - use ripemd::{Digest as _, Ripemd160}; - use serde::{Deserialize, Serialize}; - static BECH32_PUBKEY_DATA_PREFIX_SECP256K1: [u8; 5] = [ - 0xeb, - 0x5a, - 0xe9, - 0x87, - 0x21, - ]; - static BECH32_PUBKEY_DATA_PREFIX_ED25519: [u8; 5] = [ - 0x16, - 0x24, - 0xde, - 0x64, - 0x20, - ]; - /// The public key we used to generate the cosmos/tendermind/terrad addresses - pub struct PublicKey { - /// This is optional as we can generate non-pub keys without - pub raw_pub_key: Option>, - /// The raw bytes used to generate non-pub keys - pub raw_address: Option>, - } - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl<'de> _serde::Deserialize<'de> for PublicKey { - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - #[allow(non_camel_case_types)] - #[doc(hidden)] - enum __Field { - __field0, - __field1, - __ignore, - } - #[doc(hidden)] - struct __FieldVisitor; - impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { - type Value = __Field; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "field identifier", - ) - } - fn visit_u64<__E>( - self, - __value: u64, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - 0u64 => _serde::__private::Ok(__Field::__field0), - 1u64 => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_str<__E>( - self, - __value: &str, - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - "raw_pub_key" => _serde::__private::Ok(__Field::__field0), - "raw_address" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - fn visit_bytes<__E>( - self, - __value: &[u8], - ) -> _serde::__private::Result - where - __E: _serde::de::Error, - { - match __value { - b"raw_pub_key" => _serde::__private::Ok(__Field::__field0), - b"raw_address" => _serde::__private::Ok(__Field::__field1), - _ => _serde::__private::Ok(__Field::__ignore), - } - } - } - impl<'de> _serde::Deserialize<'de> for __Field { - #[inline] - fn deserialize<__D>( - __deserializer: __D, - ) -> _serde::__private::Result - where - __D: _serde::Deserializer<'de>, - { - _serde::Deserializer::deserialize_identifier( - __deserializer, - __FieldVisitor, - ) - } - } - #[doc(hidden)] - struct __Visitor<'de> { - marker: _serde::__private::PhantomData, - lifetime: _serde::__private::PhantomData<&'de ()>, - } - impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> { - type Value = PublicKey; - fn expecting( - &self, - __formatter: &mut _serde::__private::Formatter, - ) -> _serde::__private::fmt::Result { - _serde::__private::Formatter::write_str( - __formatter, - "struct PublicKey", - ) - } - #[inline] - fn visit_seq<__A>( - self, - mut __seq: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<'de>, - { - let __field0 = match _serde::de::SeqAccess::next_element::< - Option>, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 0usize, - &"struct PublicKey with 2 elements", - ), - ); - } - }; - let __field1 = match _serde::de::SeqAccess::next_element::< - Option>, - >(&mut __seq)? { - _serde::__private::Some(__value) => __value, - _serde::__private::None => { - return _serde::__private::Err( - _serde::de::Error::invalid_length( - 1usize, - &"struct PublicKey with 2 elements", - ), - ); - } - }; - _serde::__private::Ok(PublicKey { - raw_pub_key: __field0, - raw_address: __field1, - }) - } - #[inline] - fn visit_map<__A>( - self, - mut __map: __A, - ) -> _serde::__private::Result - where - __A: _serde::de::MapAccess<'de>, - { - let mut __field0: _serde::__private::Option< - Option>, - > = _serde::__private::None; - let mut __field1: _serde::__private::Option< - Option>, - > = _serde::__private::None; - while let _serde::__private::Some(__key) - = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { - match __key { - __Field::__field0 => { - if _serde::__private::Option::is_some(&__field0) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field( - "raw_pub_key", - ), - ); - } - __field0 = _serde::__private::Some( - _serde::de::MapAccess::next_value::< - Option>, - >(&mut __map)?, - ); - } - __Field::__field1 => { - if _serde::__private::Option::is_some(&__field1) { - return _serde::__private::Err( - <__A::Error as _serde::de::Error>::duplicate_field( - "raw_address", - ), - ); - } - __field1 = _serde::__private::Some( - _serde::de::MapAccess::next_value::< - Option>, - >(&mut __map)?, - ); - } - _ => { - let _ = _serde::de::MapAccess::next_value::< - _serde::de::IgnoredAny, - >(&mut __map)?; - } - } - } - let __field0 = match __field0 { - _serde::__private::Some(__field0) => __field0, - _serde::__private::None => { - _serde::__private::de::missing_field("raw_pub_key")? - } - }; - let __field1 = match __field1 { - _serde::__private::Some(__field1) => __field1, - _serde::__private::None => { - _serde::__private::de::missing_field("raw_address")? - } - }; - _serde::__private::Ok(PublicKey { - raw_pub_key: __field0, - raw_address: __field1, - }) - } - } - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &[ - "raw_pub_key", - "raw_address", - ]; - _serde::Deserializer::deserialize_struct( - __deserializer, - "PublicKey", - FIELDS, - __Visitor { - marker: _serde::__private::PhantomData::, - lifetime: _serde::__private::PhantomData, - }, - ) - } - } - }; - #[doc(hidden)] - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _: () = { - #[allow(unused_extern_crates, clippy::useless_attribute)] - extern crate serde as _serde; - #[automatically_derived] - impl _serde::Serialize for PublicKey { - fn serialize<__S>( - &self, - __serializer: __S, - ) -> _serde::__private::Result<__S::Ok, __S::Error> - where - __S: _serde::Serializer, - { - let mut __serde_state = _serde::Serializer::serialize_struct( - __serializer, - "PublicKey", - false as usize + 1 + 1, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "raw_pub_key", - &self.raw_pub_key, - )?; - _serde::ser::SerializeStruct::serialize_field( - &mut __serde_state, - "raw_address", - &self.raw_address, - )?; - _serde::ser::SerializeStruct::end(__serde_state) - } - } - }; - #[automatically_derived] - impl ::core::fmt::Debug for PublicKey { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "PublicKey", - "raw_pub_key", - &self.raw_pub_key, - "raw_address", - &&self.raw_address, - ) - } - } - #[automatically_derived] - impl ::core::clone::Clone for PublicKey { - #[inline] - fn clone(&self) -> PublicKey { - PublicKey { - raw_pub_key: ::core::clone::Clone::clone(&self.raw_pub_key), - raw_address: ::core::clone::Clone::clone(&self.raw_address), - } - } - } - impl PublicKey { - /// Generate a Cosmos/Tendermint/Terrad Public Key - pub fn from_bitcoin_public_key(bpub: &bitcoin::key::PublicKey) -> PublicKey { - let bpub_bytes = bpub.inner.serialize(); - let raw_pub_key = PublicKey::pubkey_from_public_key(&bpub_bytes); - let raw_address = PublicKey::address_from_public_key(&bpub_bytes); - PublicKey { - raw_pub_key: Some(raw_pub_key), - raw_address: Some(raw_address), - } - } - /// Generate from secp256k1 Cosmos/Terrad Public Key - pub fn from_public_key(bpub: &[u8]) -> PublicKey { - let raw_pub_key = PublicKey::pubkey_from_public_key(bpub); - let raw_address = PublicKey::address_from_public_key(bpub); - PublicKey { - raw_pub_key: Some(raw_pub_key), - raw_address: Some(raw_address), - } - } - /// Generate a Cosmos/Tendermint/Terrad Account - pub fn from_account( - acc_address: &str, - prefix: &str, - ) -> Result { - PublicKey::check_prefix_and_length(prefix, acc_address, 44) - .and_then(|vu5| { - let vu8 = Vec::from_base32(vu5.as_slice()) - .map_err(|source| DaemonError::Conversion { - key: acc_address.into(), - source, - })?; - Ok(PublicKey { - raw_pub_key: None, - raw_address: Some(vu8), - }) - }) - } - /// build a public key from a tendermint public key - pub fn from_tendermint_key( - tendermint_public_key: &str, - ) -> Result { - let len = tendermint_public_key.len(); - if len == 83 { - PublicKey::check_prefix_and_length( - "terravalconspub", - tendermint_public_key, - len, - ) - .and_then(|vu5| { - let vu8 = Vec::from_base32(vu5.as_slice()) - .map_err(|source| { - DaemonError::Conversion { - key: tendermint_public_key.into(), - source, - } - })?; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("{0:#?}", hex::encode(&vu8)), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 74u32, - ::log::__private_api::Option::None, - ); - } - }; - if vu8.starts_with(&BECH32_PUBKEY_DATA_PREFIX_SECP256K1) { - let public_key = PublicKey::public_key_from_pubkey(&vu8)?; - let raw = PublicKey::address_from_public_key(&public_key); - Ok(PublicKey { - raw_pub_key: Some(vu8), - raw_address: Some(raw), - }) - } else { - Err(DaemonError::ConversionSECP256k1) - } - }) - } else if len == 82 { - PublicKey::check_prefix_and_length( - "terravalconspub", - tendermint_public_key, - len, - ) - .and_then(|vu5| { - let vu8 = Vec::from_base32(vu5.as_slice()) - .map_err(|source| { - DaemonError::Conversion { - key: tendermint_public_key.into(), - source, - } - })?; - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("ED25519 public keys are not fully supported"), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 100u32, - ::log::__private_api::Option::None, - ); - } - }; - if vu8.starts_with(&BECH32_PUBKEY_DATA_PREFIX_ED25519) { - let raw = PublicKey::address_from_public_ed25519_key(&vu8)?; - Ok(PublicKey { - raw_pub_key: Some(vu8), - raw_address: Some(raw), - }) - } else { - Err(DaemonError::ConversionED25519) - } - }) - } else { - Err(DaemonError::ConversionLength(len)) - } - } - /// build a terravalcons address from a tendermint hex key - /// the tendermint_hex_address should be a hex code of 40 length - pub fn from_tendermint_address( - tendermint_hex_address: &str, - ) -> Result { - let len = tendermint_hex_address.len(); - if len == 40 { - let raw = hex::decode(tendermint_hex_address)?; - Ok(PublicKey { - raw_pub_key: None, - raw_address: Some(raw), - }) - } else { - Err(DaemonError::ConversionLengthED25519Hex(len)) - } - } - /// Generate a Operator address for this public key (used by the validator) - pub fn from_operator_address( - valoper_address: &str, - ) -> Result { - PublicKey::check_prefix_and_length("terravaloper", valoper_address, 51) - .and_then(|vu5| { - let vu8 = Vec::from_base32(vu5.as_slice()) - .map_err(|source| DaemonError::Conversion { - key: valoper_address.into(), - source, - })?; - Ok(PublicKey { - raw_pub_key: None, - raw_address: Some(vu8), - }) - }) - } - /// Generate Public key from raw address - pub fn from_raw_address( - raw_address: &str, - ) -> Result { - let vec1 = hex::decode(raw_address)?; - Ok(PublicKey { - raw_pub_key: None, - raw_address: Some(vec1), - }) - } - fn check_prefix_and_length( - prefix: &str, - data: &str, - length: usize, - ) -> Result, DaemonError> { - let (hrp, decoded_str, _) = decode(data) - .map_err(|source| DaemonError::Conversion { - key: data.into(), - source, - })?; - if hrp == prefix && data.len() == length { - Ok(decoded_str) - } else { - Err( - DaemonError::Bech32DecodeExpanded( - hrp, - data.len(), - prefix.into(), - length, - ), - ) - } - } - /** - Gets a bech32-words pubkey from a compressed bytes Secp256K1 public key. - - @param publicKey raw public key - */ - pub fn pubkey_from_public_key(public_key: &[u8]) -> Vec { - [BECH32_PUBKEY_DATA_PREFIX_SECP256K1.to_vec(), public_key.to_vec()] - .concat() - } - /** - Gets a bech32-words pubkey from a compressed bytes Ed25519 public key. - - @param publicKey raw public key - */ - pub fn pubkey_from_ed25519_public_key(public_key: &[u8]) -> Vec { - [BECH32_PUBKEY_DATA_PREFIX_ED25519.to_vec(), public_key.to_vec()] - .concat() - } - /// Translate from a BECH32 prefixed key to a standard public key - pub fn public_key_from_pubkey( - pub_key: &[u8], - ) -> Result, DaemonError> { - if pub_key.starts_with(&BECH32_PUBKEY_DATA_PREFIX_SECP256K1) { - let len = BECH32_PUBKEY_DATA_PREFIX_SECP256K1.len(); - let len2 = pub_key.len(); - Ok(Vec::from(&pub_key[len..len2])) - } else if pub_key.starts_with(&BECH32_PUBKEY_DATA_PREFIX_ED25519) { - let len = BECH32_PUBKEY_DATA_PREFIX_ED25519.len(); - let len2 = pub_key.len(); - let vec = &pub_key[len..len2]; - let ed25519_pubkey = Ed25519::from_bytes(vec.try_into().unwrap())?; - Ok(ed25519_pubkey.to_bytes().to_vec()) - } else { - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("pub key does not start with BECH32 PREFIX"), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 228u32, - ::log::__private_api::Option::None, - ); - } - }; - Err(DaemonError::Bech32DecodeErr) - } - } - /** - Gets a raw address from a compressed bytes public key. - - @param publicKey raw public key - */ - pub fn address_from_public_key(public_key: &[u8]) -> Vec { - let mut hasher = Ripemd160::new(); - let sha_result = ring::digest::digest(&SHA256, public_key); - hasher.update(&sha_result.as_ref()[0..32]); - let ripe_result = hasher.finalize(); - let address: Vec = ripe_result[0..20].to_vec(); - address - } - /** - Gets a raw address from a ed25519 public key. - - @param publicKey raw public key - */ - pub fn address_from_public_ed25519_key( - public_key: &[u8], - ) -> Result, DaemonError> { - if public_key.len() != (32 + 5) { - Err( - DaemonError::ConversionPrefixED25519( - public_key.len(), - hex::encode(public_key), - ), - ) - } else { - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "address_from_public_ed25519_key public key - {0}", - hex::encode(public_key), - ), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 262u32, - ::log::__private_api::Option::None, - ); - } - }; - let mut sha_result: [u8; 32] = [0; 32]; - let sha_result = ring::digest::digest(&SHA256, &public_key[5..]); - let address: Vec = sha_result.as_ref()[0..20].to_vec(); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "address_from_public_ed25519_key sha result - {0}", - hex::encode(&address), - ), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 283u32, - ::log::__private_api::Option::None, - ); - } - }; - Ok(address) - } - } - /// The main account used in most things - pub fn account(&self, prefix: &str) -> Result { - match &self.raw_address { - Some(raw) => { - let data = encode(prefix, raw.to_base32(), Variant::Bech32); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => Err(DaemonError::Implementation), - } - } - /// The operator address used for validators - pub fn operator_address(&self, prefix: &str) -> Result { - match &self.raw_address { - Some(raw) => { - let data = encode( - &{ - let res = ::alloc::fmt::format( - format_args!("{0}{1}", prefix, "valoper"), - ); - res - }, - raw.to_base32(), - Variant::Bech32, - ); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => Err(DaemonError::Implementation), - } - } - /// application public key - Application keys are associated with a public key terrapub- and an address terra- - pub fn application_public_key( - &self, - prefix: &str, - ) -> Result { - match &self.raw_pub_key { - Some(raw) => { - let data = encode( - &{ - let res = ::alloc::fmt::format( - format_args!("{0}{1}", prefix, "pub"), - ); - res - }, - raw.to_base32(), - Variant::Bech32, - ); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => { - { - let lvl = ::log::Level::Warn; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("Missing Public Key. Can\'t continue"), - lvl, - &( - "cw_orch_daemon::keys::public", - "cw_orch_daemon::keys::public", - "cw-orch-daemon/src/keys/public.rs", - ), - 335u32, - ::log::__private_api::Option::None, - ); - } - }; - Err(DaemonError::Implementation) - } - } - } - /// The operator address used for validators public key. - pub fn operator_address_public_key( - &self, - prefix: &str, - ) -> Result { - match &self.raw_pub_key { - Some(raw) => { - let data = encode( - &{ - let res = ::alloc::fmt::format( - format_args!("{0}{1}", prefix, "valoperpub"), - ); - res - }, - raw.to_base32(), - Variant::Bech32, - ); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => Err(DaemonError::Implementation), - } - } - /// This is a unique key used to sign block hashes. It is associated with a public key terravalconspub. - pub fn tendermint(&self, prefix: &str) -> Result { - match &self.raw_address { - Some(raw) => { - let data = encode( - &{ - let res = ::alloc::fmt::format( - format_args!("{0}{1}", prefix, "valcons"), - ); - res - }, - raw.to_base32(), - Variant::Bech32, - ); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => Err(DaemonError::Implementation), - } - } - /// This is a unique key used to sign block hashes. It is associated with a public key terravalconspub. - pub fn tendermint_pubkey( - &self, - prefix: &str, - ) -> Result { - match &self.raw_pub_key { - Some(raw) => { - let b32 = raw.to_base32(); - let data = encode( - &{ - let res = ::alloc::fmt::format( - format_args!("{0}{1}", prefix, "valconspub"), - ); - res - }, - b32, - Variant::Bech32, - ); - match data { - Ok(acc) => Ok(acc), - Err(_) => Err(DaemonError::Bech32DecodeErr), - } - } - None => Err(DaemonError::Implementation), - } - } - } - } - pub mod signature { - use crate::DaemonError; - use base64::engine::{general_purpose::STANDARD, Engine}; - use ring::digest::SHA256; - use secp256k1::{Message, Secp256k1}; - pub struct Signature {} - impl Signature { - pub fn verify( - secp: &Secp256k1, - pub_key: &str, - signature: &str, - blob: &str, - ) -> Result<(), DaemonError> { - let public = STANDARD.decode(pub_key)?; - let sig = STANDARD.decode(signature)?; - let pk = secp256k1::PublicKey::from_slice(public.as_slice())?; - let sha_result = ring::digest::digest(&SHA256, blob.as_bytes()); - let message: Message = Message::from_slice(&sha_result.as_ref()[0..32])?; - let secp_sig = secp256k1::ecdsa::Signature::from_compact( - sig.as_slice(), - )?; - secp.verify_ecdsa(&message, &secp_sig, &pk)?; - Ok(()) - } - } - } -} -pub mod live_mock { - //! Live mock is a mock that uses a live chain to query for data. - //! It can be used to do chain-backed unit-testing. It can't be used for state-changing operations. - use crate::queriers::Bank; - use crate::queriers::CosmWasm; - use crate::queriers::DaemonQuerier; - use crate::queriers::Staking; - use cosmwasm_std::Addr; - use cosmwasm_std::AllBalanceResponse; - use cosmwasm_std::BalanceResponse; - use cosmwasm_std::Delegation; - use cosmwasm_std::{AllDelegationsResponse, BondedDenomResponse}; - use cosmwasm_std::BankQuery; - use cosmwasm_std::Binary; - use cosmwasm_std::Empty; - use cosmwasm_std::StakingQuery; - use ibc_chain_registry::chain::ChainData; - use tokio::runtime::Runtime; - use tonic::transport::Channel; - use std::marker::PhantomData; - use std::str::FromStr; - use cosmwasm_std::testing::{MockApi, MockStorage}; - use cosmwasm_std::{ - from_slice, to_binary, Coin, ContractResult, OwnedDeps, Querier, QuerierResult, - QueryRequest, SystemError, SystemResult, Uint128, WasmQuery, - }; - use crate::channel::GrpcChannel; - fn to_cosmwasm_coin(c: cosmrs::proto::cosmos::base::v1beta1::Coin) -> Coin { - Coin { - amount: Uint128::from_str(&c.amount).unwrap(), - denom: c.denom, - } - } - const QUERIER_ERROR: &str = "Only Bank balances and Wasm (raw + smart) and Some staking queries are covered for now"; - /// mock_dependencies is a drop-in replacement for cosmwasm_std::testing::mock_dependencies - /// this uses our CustomQuerier. - pub fn mock_dependencies( - chain_info: ChainData, - ) -> OwnedDeps { - let custom_querier: WasmMockQuerier = WasmMockQuerier::new(chain_info); - OwnedDeps { - storage: MockStorage::default(), - api: MockApi::default(), - querier: custom_querier, - custom_query_type: PhantomData, - } - } - /// Querier struct that fetches queries on-chain directly - pub struct WasmMockQuerier { - channel: Channel, - runtime: Runtime, - } - impl Querier for WasmMockQuerier { - fn raw_query(&self, bin_request: &[u8]) -> QuerierResult { - let request: QueryRequest = match from_slice(bin_request) { - Ok(v) => v, - Err(e) => { - return SystemResult::Err(SystemError::InvalidRequest { - error: { - let res = ::alloc::fmt::format( - format_args!("Parsing query request: {0}", e), - ); - res - }, - request: bin_request.into(), - }); - } - }; - self.handle_query(&request) - } - } - impl WasmMockQuerier { - /// Function used to handle a query and customize the query behavior - /// This implements some queries by querying an actual node for the responses - pub fn handle_query(&self, request: &QueryRequest) -> QuerierResult { - match &request { - QueryRequest::Wasm(x) => { - let querier = CosmWasm::new(self.channel.clone()); - match x { - WasmQuery::Smart { contract_addr, msg } => { - let query_result: Result = self - .runtime - .block_on( - querier - .contract_state(contract_addr.to_string(), msg.to_vec()), - ) - .map(|query_result| query_result.into()); - SystemResult::Ok(ContractResult::from(query_result)) - } - WasmQuery::Raw { contract_addr, key } => { - let query_result = self - .runtime - .block_on( - querier - .contract_raw_state(contract_addr.to_string(), key.to_vec()), - ) - .map(|query_result| query_result.data.into()); - SystemResult::Ok(ContractResult::from(query_result)) - } - _ => { - SystemResult::Err(SystemError::InvalidRequest { - error: QUERIER_ERROR.to_string(), - request: to_binary(&request).unwrap(), - }) - } - } - } - QueryRequest::Bank(x) => { - let querier = Bank::new(self.channel.clone()); - match x { - BankQuery::Balance { address, denom } => { - let query_result = self - .runtime - .block_on(querier.balance(address, Some(denom.clone()))) - .map(|result| { - to_binary( - &BalanceResponse { - amount: Coin { - amount: Uint128::from_str(&result[0].amount).unwrap(), - denom: result[0].denom.clone(), - }, - }, - ) - .unwrap() - }); - SystemResult::Ok(ContractResult::from(query_result)) - } - BankQuery::AllBalances { address } => { - let query_result = self - .runtime - .block_on(querier.balance(address, None)) - .map(|result| AllBalanceResponse { - amount: result - .into_iter() - .map(|c| Coin { - amount: Uint128::from_str(&c.amount).unwrap(), - denom: c.denom, - }) - .collect(), - }) - .map(|query_result| to_binary(&query_result)) - .unwrap(); - SystemResult::Ok(ContractResult::from(query_result)) - } - _ => { - SystemResult::Err(SystemError::InvalidRequest { - error: QUERIER_ERROR.to_string(), - request: to_binary(&request).unwrap(), - }) - } - } - } - QueryRequest::Staking(x) => { - let querier = Staking::new(self.channel.clone()); - match x { - StakingQuery::BondedDenom {} => { - let query_result = self - .runtime - .block_on(querier.params()) - .map(|result| BondedDenomResponse { - denom: result.params.unwrap().bond_denom, - }) - .map(|query_result| to_binary(&query_result)) - .unwrap(); - SystemResult::Ok(ContractResult::from(query_result)) - } - StakingQuery::AllDelegations { delegator } => { - let query_result = self - .runtime - .block_on(querier.delegator_delegations(delegator, None)) - .map(|result| AllDelegationsResponse { - delegations: result - .delegation_responses - .into_iter() - .filter_map(|delegation| { - delegation - .delegation - .map(|d| Delegation { - delegator: Addr::unchecked(d.delegator_address), - validator: d.validator_address, - amount: to_cosmwasm_coin(delegation.balance.unwrap()), - }) - }) - .collect(), - }) - .map(|query_result| to_binary(&query_result)) - .unwrap(); - SystemResult::Ok(ContractResult::from(query_result)) - } - _ => ::core::panicking::panic("not yet implemented"), - } - } - _ => { - SystemResult::Err(SystemError::InvalidRequest { - error: QUERIER_ERROR.to_string(), - request: to_binary(&request).unwrap(), - }) - } - } - } - } - impl WasmMockQuerier { - /// Creates a querier from chain information - pub fn new(chain: ChainData) -> Self { - let rt = Runtime::new().unwrap(); - let channel = rt - .block_on(GrpcChannel::connect(&chain.apis.grpc, &chain.chain_id)) - .unwrap(); - WasmMockQuerier { - channel, - runtime: rt, - } - } - } -} -pub mod queriers { - //! # DaemonQuerier - //! - //! DaemonAsync queriers are gRPC query clients for the CosmosSDK modules. They can be used to query the different modules (Bank, Ibc, Authz, ...). - //! - //! ## Usage - //! - //! You will need to acquire a [gRPC channel](Channel) to a running CosmosSDK node to be able to use the queriers. - //! Here is an example of how to acquire one using the DaemonAsync builder. - //! - //! ```no_run - //! // require the querier you want to use, in this case Node - //! use cw_orch_daemon::{queriers::Node, DaemonAsync, networks, queriers::DaemonQuerier}; - //! # tokio_test::block_on(async { - //! // call the builder and configure it as you need - //! let daemon = DaemonAsync::builder() - //! .chain(networks::LOCAL_JUNO) - //! .build() - //! .await.unwrap(); - //! // now you can use the Node querier: - //! let node = Node::new(daemon.channel()); - //! let node_info = node.info(); - //! # }) - //! ``` - mod bank { - use crate::{cosmos_modules, error::DaemonError}; - use cosmrs::proto::cosmos::base::{query::v1beta1::PageRequest, v1beta1::Coin}; - use tonic::transport::Channel; - use super::DaemonQuerier; - /// Queries for Cosmos Bank Module - pub struct Bank { - channel: Channel, - } - impl DaemonQuerier for Bank { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Bank { - /// Query the bank balance of a given address - /// If denom is None, returns all balances - pub async fn balance( - &self, - address: impl Into, - denom: Option, - ) -> Result, DaemonError> { - match denom { - Some(denom) => { - let resp = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QueryBalanceRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryBalanceRequest { - address: address.into(), - denom: denom, - }; - let response = client - .balance(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 28u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - let coin = resp.balance.unwrap(); - Ok( - <[_]>::into_vec( - #[rustc_box] - ::alloc::boxed::Box::new([coin]), - ), - ) - } - None => { - let resp = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QueryAllBalancesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryAllBalancesRequest { - address: address.into(), - pagination: None, - }; - let response = client - .all_balances(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 41u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - let coins = resp.balances; - Ok(coins.into_iter().collect()) - } - } - } - /// Query spendable balance for address - pub async fn spendable_balances( - &self, - address: impl Into, - ) -> Result, DaemonError> { - let spendable_balances: cosmos_modules::bank::QuerySpendableBalancesResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QuerySpendableBalancesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QuerySpendableBalancesRequest { - address: address.into(), - pagination: None, - }; - let response = client - .spendable_balances(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 62u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(spendable_balances.balances) - } - /// Query total supply in the bank - pub async fn total_supply(&self) -> Result, DaemonError> { - let total_supply: cosmos_modules::bank::QueryTotalSupplyResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QueryTotalSupplyRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryTotalSupplyRequest { - pagination: None, - }; - let response = client - .total_supply(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 76u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(total_supply.supply) - } - /// Query total supply in the bank for a denom - pub async fn supply_of( - &self, - denom: impl Into, - ) -> Result { - let supply_of: cosmos_modules::bank::QuerySupplyOfResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QuerySupplyOfRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QuerySupplyOfRequest { - denom: denom.into(), - }; - let response = client.supply_of(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 87u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(supply_of.amount.unwrap()) - } - /// Query params - pub async fn params( - &self, - ) -> Result { - let params: cosmos_modules::bank::QueryParamsResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QueryParamsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryParamsRequest {}; - let response = client.params(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 101u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(params.params.unwrap()) - } - /// Query denom metadata - pub async fn denom_metadata( - &self, - denom: impl Into, - ) -> Result { - let denom_metadata: cosmos_modules::bank::QueryDenomMetadataResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QueryDenomMetadataRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDenomMetadataRequest { - denom: denom.into(), - }; - let response = client - .denom_metadata(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 110u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(denom_metadata.metadata.unwrap()) - } - /// Query denoms metadata with pagination - /// - /// see [PageRequest] for pagination - pub async fn denoms_metadata( - &self, - pagination: Option, - ) -> Result, DaemonError> { - let denoms_metadata: cosmos_modules::bank::QueryDenomsMetadataResponse = { - use crate::cosmos_modules::bank::{ - query_client::QueryClient, QueryDenomsMetadataRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDenomsMetadataRequest { - pagination: pagination, - }; - let response = client - .denoms_metadata(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::bank", - "cw_orch_daemon::queriers::bank", - "cw-orch-daemon/src/queriers/bank.rs", - ), - 128u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(denoms_metadata.metadatas) - } - } - } - mod cosmwasm { - use crate::{cosmos_modules, error::DaemonError}; - use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; - use tonic::transport::Channel; - use super::DaemonQuerier; - /// Querier for the CosmWasm SDK module - pub struct CosmWasm { - channel: Channel, - } - impl DaemonQuerier for CosmWasm { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl CosmWasm { - /// Query code_id by hash - pub async fn code_id_hash( - &self, - code_id: u64, - ) -> Result { - use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryCodeRequest { code_id }; - let resp = client.code(request).await?.into_inner(); - let contract_hash = resp.code_info.unwrap().data_hash; - let on_chain_hash = base16::encode_lower(&contract_hash); - Ok(on_chain_hash) - } - /// Query contract info - pub async fn contract_info( - &self, - address: impl Into, - ) -> Result { - use cosmos_modules::cosmwasm::{ - query_client::*, QueryContractInfoRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryContractInfoRequest { - address: address.into(), - }; - let resp = client.contract_info(request).await?.into_inner(); - let contract_info = resp.contract_info.unwrap(); - Ok(contract_info) - } - /// Query contract history - pub async fn contract_history( - &self, - address: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::cosmwasm::QueryContractHistoryResponse, - DaemonError, - > { - use cosmos_modules::cosmwasm::{ - query_client::*, QueryContractHistoryRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryContractHistoryRequest { - address: address.into(), - pagination, - }; - Ok(client.contract_history(request).await?.into_inner()) - } - /// Query contract state - pub async fn contract_state( - &self, - address: impl Into, - query_data: Vec, - ) -> Result, DaemonError> { - use cosmos_modules::cosmwasm::{ - query_client::*, QuerySmartContractStateRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QuerySmartContractStateRequest { - address: address.into(), - query_data, - }; - Ok(client.smart_contract_state(request).await?.into_inner().data) - } - /// Query all contract state - pub async fn all_contract_state( - &self, - address: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::cosmwasm::QueryAllContractStateResponse, - DaemonError, - > { - use cosmos_modules::cosmwasm::{ - query_client::*, QueryAllContractStateRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryAllContractStateRequest { - address: address.into(), - pagination, - }; - Ok(client.all_contract_state(request).await?.into_inner()) - } - /// Query code - pub async fn code( - &self, - code_id: u64, - ) -> Result { - use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryCodeRequest { code_id }; - Ok(client.code(request).await?.into_inner().code_info.unwrap()) - } - /// Query code bytes - pub async fn code_data(&self, code_id: u64) -> Result, DaemonError> { - use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryCodeRequest { code_id }; - Ok(client.code(request).await?.into_inner().data) - } - /// Query codes - pub async fn codes( - &self, - pagination: Option, - ) -> Result, DaemonError> { - use cosmos_modules::cosmwasm::{query_client::*, QueryCodesRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryCodesRequest { pagination }; - Ok(client.codes(request).await?.into_inner().code_infos) - } - /// Query pinned codes - pub async fn pinned_codes( - &self, - ) -> Result< - cosmos_modules::cosmwasm::QueryPinnedCodesResponse, - DaemonError, - > { - use cosmos_modules::cosmwasm::{query_client::*, QueryPinnedCodesRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryPinnedCodesRequest { - pagination: None, - }; - Ok(client.pinned_codes(request).await?.into_inner()) - } - /// Query contracts by code - pub async fn contract_by_codes( - &self, - code_id: u64, - ) -> Result< - cosmos_modules::cosmwasm::QueryContractsByCodeResponse, - DaemonError, - > { - use cosmos_modules::cosmwasm::{ - query_client::*, QueryContractsByCodeRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryContractsByCodeRequest { - code_id, - pagination: None, - }; - Ok(client.contracts_by_code(request).await?.into_inner()) - } - /// Query raw contract state - pub async fn contract_raw_state( - &self, - address: impl Into, - query_data: Vec, - ) -> Result< - cosmos_modules::cosmwasm::QueryRawContractStateResponse, - DaemonError, - > { - use cosmos_modules::cosmwasm::{ - query_client::*, QueryRawContractStateRequest, - }; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - let request = QueryRawContractStateRequest { - address: address.into(), - query_data, - }; - Ok(client.raw_contract_state(request).await?.into_inner()) - } - /// Query params - pub async fn params( - &self, - ) -> Result { - use cosmos_modules::cosmwasm::{query_client::*, QueryParamsRequest}; - let mut client: QueryClient = QueryClient::new( - self.channel.clone(), - ); - Ok(client.params(QueryParamsRequest {}).await?.into_inner()) - } - } - } - mod feegrant { - use crate::{cosmos_modules, error::DaemonError}; - use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; - use tonic::transport::Channel; - use super::DaemonQuerier; - /// Querier for the Cosmos Gov module - pub struct Feegrant { - channel: Channel, - } - impl DaemonQuerier for Feegrant { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Feegrant { - /// Query all allowances granted to the grantee address by a granter address - pub async fn allowance( - &self, - granter: impl Into, - grantee: impl Into, - ) -> Result { - let allowance: cosmos_modules::feegrant::QueryAllowanceResponse = { - use crate::cosmos_modules::feegrant::{ - query_client::QueryClient, QueryAllowanceRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryAllowanceRequest { - granter: granter.into(), - grantee: grantee.into(), - }; - let response = client.allowance(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::feegrant", - "cw_orch_daemon::queriers::feegrant", - "cw-orch-daemon/src/queriers/feegrant.rs", - ), - 25u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(allowance.allowance.unwrap()) - } - /// Query allowances for grantee address with a given pagination - /// - /// see [PageRequest] for pagination - pub async fn allowances( - &self, - grantee: impl Into, - pagination: Option, - ) -> Result, DaemonError> { - let allowances: cosmos_modules::feegrant::QueryAllowancesResponse = { - use crate::cosmos_modules::feegrant::{ - query_client::QueryClient, QueryAllowancesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryAllowancesRequest { - grantee: grantee.into(), - pagination: pagination, - }; - let response = client - .allowances(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::feegrant", - "cw_orch_daemon::queriers::feegrant", - "cw-orch-daemon/src/queriers/feegrant.rs", - ), - 45u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(allowances.allowances) - } - } - } - mod gov { - use crate::{cosmos_modules, error::DaemonError}; - use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; - use tonic::transport::Channel; - use super::DaemonQuerier; - /// Querier for the Cosmos Gov module - pub struct Gov { - channel: Channel, - } - impl DaemonQuerier for Gov { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Gov { - /// Query proposal details by proposal id - pub async fn proposal( - &self, - proposal_id: u64, - ) -> Result { - let proposal: cosmos_modules::gov::QueryProposalResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryProposalRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryProposalRequest { - proposal_id: proposal_id, - }; - let response = client.proposal(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 24u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(proposal.proposal.unwrap()) - } - /// Query proposals based on given status - /// - /// see [PageRequest] for pagination - pub async fn proposals( - &self, - proposal_status: GovProposalStatus, - voter: impl Into, - depositor: impl Into, - pagination: Option, - ) -> Result { - let proposals: cosmos_modules::gov::QueryProposalsResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryProposalsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryProposalsRequest { - proposal_status: proposal_status as i32, - voter: voter.into(), - depositor: depositor.into(), - pagination: pagination, - }; - let response = client.proposals(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 45u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(proposals) - } - /// Query voted information based on proposal_id for voter address - pub async fn vote( - &self, - proposal_id: u64, - voter: impl Into, - ) -> Result { - let vote: cosmos_modules::gov::QueryVoteResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryVoteRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryVoteRequest { - proposal_id: proposal_id, - voter: voter.into(), - }; - let response = client.vote(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 65u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(vote.vote.unwrap()) - } - /// Query votes of a given proposal - /// - /// see [PageRequest] for pagination - pub async fn votes( - &self, - proposal_id: impl Into, - pagination: Option, - ) -> Result { - let votes: cosmos_modules::gov::QueryVotesResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryVotesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryVotesRequest { - proposal_id: proposal_id.into(), - pagination: pagination, - }; - let response = client.votes(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 85u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(votes) - } - /// Query all parameters of the gov module - pub async fn params( - &self, - params_type: impl Into, - ) -> Result { - let params: cosmos_modules::gov::QueryParamsResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryParamsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryParamsRequest { - params_type: params_type.into(), - }; - let response = client.params(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 102u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(params) - } - /// Query deposit information using proposal_id and depositor address - pub async fn deposit( - &self, - proposal_id: u64, - depositor: impl Into, - ) -> Result { - let deposit: cosmos_modules::gov::QueryDepositResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryDepositRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDepositRequest { - proposal_id: proposal_id, - depositor: depositor.into(), - }; - let response = client.deposit(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 119u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(deposit.deposit.unwrap()) - } - /// Query deposits of a proposal - /// - /// see [PageRequest] for pagination - pub async fn deposits( - &self, - proposal_id: u64, - pagination: Option, - ) -> Result { - let deposits: cosmos_modules::gov::QueryDepositsResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryDepositsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDepositsRequest { - proposal_id: proposal_id, - pagination: pagination, - }; - let response = client.deposits(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 139u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(deposits) - } - /// TallyResult queries the tally of a proposal vote. - pub async fn tally_result( - &mut self, - proposal_id: u64, - ) -> Result { - let tally_result: cosmos_modules::gov::QueryTallyResultResponse = { - use crate::cosmos_modules::gov::{ - query_client::QueryClient, QueryTallyResultRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryTallyResultRequest { - proposal_id: proposal_id, - }; - let response = client - .tally_result(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::gov", - "cw_orch_daemon::queriers::gov", - "cw-orch-daemon/src/queriers/gov.rs", - ), - 156u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(tally_result.tally.unwrap()) - } - } - /// Proposal status - #[allow(missing_docs)] - pub enum GovProposalStatus { - Unspecified = 0, - DepositPeriod = 1, - VotingPeriod = 2, - Passed = 3, - Rejected = 4, - Failed = 5, - } - } - mod ibc { - use super::DaemonQuerier; - use crate::{cosmos_modules, error::DaemonError}; - use cosmos_modules::ibc_channel; - use cosmrs::proto::ibc::{ - applications::transfer::v1::{DenomTrace, QueryDenomTraceResponse}, - core::{ - channel::v1::QueryPacketCommitmentResponse, - client::v1::{IdentifiedClientState, QueryClientStatesResponse}, - connection::v1::{IdentifiedConnection, State}, - }, - lightclients::tendermint::v1::ClientState, - }; - use prost::Message; - use tonic::transport::Channel; - /// Querier for the Cosmos IBC module - pub struct Ibc { - channel: Channel, - } - impl DaemonQuerier for Ibc { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Ibc { - /// Get the trace of a specific denom - pub async fn denom_trace( - &self, - hash: String, - ) -> Result { - let denom_trace: QueryDenomTraceResponse = { - use crate::cosmos_modules::ibc_transfer::{ - query_client::QueryClient, QueryDenomTraceRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDenomTraceRequest { - hash: hash, - }; - let response = client - .denom_trace(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 32u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(denom_trace.denom_trace.unwrap()) - } - /// Get all the IBC clients for this daemon - pub async fn clients( - &self, - ) -> Result, DaemonError> { - let ibc_clients: QueryClientStatesResponse = { - use crate::cosmos_modules::ibc_client::{ - query_client::QueryClient, QueryClientStatesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryClientStatesRequest { - pagination: None, - }; - let response = client - .client_states(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 45u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_clients.client_states) - } - /// Get the state of a specific IBC client - pub async fn client_state( - &self, - client_id: impl ToString, - ) -> Result< - cosmos_modules::ibc_client::QueryClientStateResponse, - DaemonError, - > { - let response: cosmos_modules::ibc_client::QueryClientStateResponse = { - use crate::cosmos_modules::ibc_client::{ - query_client::QueryClient, QueryClientStateRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryClientStateRequest { - client_id: client_id.to_string(), - }; - let response = client - .client_state(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 60u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(response) - } - /// Get the consensus state of a specific IBC client - 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 = { - use crate::cosmos_modules::ibc_client::{ - query_client::QueryClient, QueryConsensusStatesRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryConsensusStatesRequest { - client_id: client_id, - pagination: None, - }; - let response = client - .consensus_states(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 77u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(response) - } - /// Get the consensus status of a specific IBC client - pub async fn client_status( - &self, - client_id: impl ToString, - ) -> Result< - cosmos_modules::ibc_client::QueryClientStatusResponse, - DaemonError, - > { - let response: cosmos_modules::ibc_client::QueryClientStatusResponse = { - use crate::cosmos_modules::ibc_client::{ - query_client::QueryClient, QueryClientStatusRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryClientStatusRequest { - client_id: client_id.to_string(), - }; - let response = client - .client_status(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 95u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(response) - } - /// Get the ibc client parameters - pub async fn client_params( - &self, - ) -> Result< - cosmos_modules::ibc_client::QueryClientParamsResponse, - DaemonError, - > { - let response: cosmos_modules::ibc_client::QueryClientParamsResponse = { - use crate::cosmos_modules::ibc_client::{ - query_client::QueryClient, QueryClientParamsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryClientParamsRequest {}; - let response = client - .client_params(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 111u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(response) - } - /// Query the IBC connections for a specific chain - pub async fn connections( - &self, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_connection::QueryConnectionsResponse; - let ibc_connections: QueryConnectionsResponse = { - use crate::cosmos_modules::ibc_connection::{ - query_client::QueryClient, QueryConnectionsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryConnectionsRequest { - pagination: None, - }; - let response = client - .connections(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 121u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_connections.connections) - } - /// Search for open connections with a specific chain. - pub async fn open_connections( - &self, - client_chain_id: impl ToString, - ) -> Result, 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) - } - /// Get all the connections for this client - pub async fn client_connections( - &self, - client_id: impl Into, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_connection::QueryClientConnectionsResponse; - let client_id = client_id.into(); - let ibc_client_connections: QueryClientConnectionsResponse = { - use crate::cosmos_modules::ibc_connection::{ - query_client::QueryClient, QueryClientConnectionsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryClientConnectionsRequest { - client_id: client_id.clone(), - }; - let response = client - .client_connections(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 163u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_client_connections.connection_paths) - } - /// Get the (tendermint) client state for a specific connection - pub async fn connection_client( - &self, - connection_id: impl Into, - ) -> Result { - use cosmos_modules::ibc_connection::QueryConnectionClientStateResponse; - let connection_id = connection_id.into(); - let ibc_connection_client: QueryConnectionClientStateResponse = { - use crate::cosmos_modules::ibc_connection::{ - query_client::QueryClient, QueryConnectionClientStateRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryConnectionClientStateRequest { - connection_id: connection_id.clone(), - }; - let response = client - .connection_client_state(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 183u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - let client_state = ibc_connection_client - .identified_client_state - .ok_or( - DaemonError::ibc_err({ - let res = ::alloc::fmt::format( - format_args!( - "error identifying client for connection {0}", - connection_id, - ), - ); - res - }), - )?; - let client_state = ClientState::decode( - client_state.client_state.unwrap().value.as_slice(), - ) - .map_err(|e| DaemonError::ibc_err({ - let res = ::alloc::fmt::format( - format_args!("error decoding client state: {0}", e), - ); - res - }))?; - Ok(client_state) - } - /// Get the channel for a specific port and channel id - pub async fn channel( - &self, - port_id: impl Into, - channel_id: impl Into, - ) -> Result { - use cosmos_modules::ibc_channel::QueryChannelResponse; - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_channel: QueryChannelResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryChannelRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryChannelRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }; - let response = client.channel(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 218u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - ibc_channel - .channel - .ok_or( - DaemonError::ibc_err({ - let res = ::alloc::fmt::format( - format_args!( - "error fetching channel {0} on port {1}", - channel_id, - port_id, - ), - ); - res - }), - ) - } - /// Get all the channels for a specific connection - pub async fn connection_channels( - &self, - connection_id: impl Into, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_channel::QueryConnectionChannelsResponse; - let connection_id = connection_id.into(); - let ibc_connection_channels: QueryConnectionChannelsResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryConnectionChannelsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryConnectionChannelsRequest { - connection: connection_id.clone(), - pagination: None, - }; - let response = client - .connection_channels(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 242u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_connection_channels.channels) - } - /// Get the client state for a specific channel and port - pub async fn channel_client_state( - &self, - port_id: impl Into, - channel_id: impl Into, - ) -> Result { - use cosmos_modules::ibc_channel::QueryChannelClientStateResponse; - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_channel_client_state: QueryChannelClientStateResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryChannelClientStateRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryChannelClientStateRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }; - let response = client - .channel_client_state(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 265u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - ibc_channel_client_state - .identified_client_state - .ok_or( - DaemonError::ibc_err({ - let res = ::alloc::fmt::format( - format_args!( - "error identifying client for channel {0} on port {1}", - channel_id, - port_id, - ), - ); - res - }), - ) - } - /// Get all the packet commitments for a specific channel and port - pub async fn packet_commitments( - &self, - port_id: impl Into, - channel_id: impl Into, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_channel::QueryPacketCommitmentsResponse; - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_commitments: QueryPacketCommitmentsResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryPacketCommitmentsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPacketCommitmentsRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - pagination: None, - }; - let response = client - .packet_commitments(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 297u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_commitments.commitments) - } - /// Get the packet commitment for a specific channel, port and sequence - pub async fn packet_commitment( - &self, - port_id: impl Into, - channel_id: impl Into, - sequence: u64, - ) -> Result { - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_commitment: QueryPacketCommitmentResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryPacketCommitmentRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPacketCommitmentRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - sequence: sequence, - }; - let response = client - .packet_commitment(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 320u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_commitment) - } - /// Returns if the packet is received on the connected chain. - pub async fn packet_receipt( - &self, - port_id: impl Into, - channel_id: impl Into, - sequence: u64, - ) -> Result { - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_receipt: ibc_channel::QueryPacketReceiptResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryPacketReceiptRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPacketReceiptRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - sequence: sequence, - }; - let response = client - .packet_receipt(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 345u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_receipt.received) - } - /// Get all the packet acknowledgements for a specific channel, port and commitment sequences - pub async fn packet_acknowledgements( - &self, - port_id: impl Into, - channel_id: impl Into, - packet_commitment_sequences: Vec, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_channel::QueryPacketAcknowledgementsResponse; - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_acknowledgements: QueryPacketAcknowledgementsResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryPacketAcknowledgementsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPacketAcknowledgementsRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - packet_commitment_sequences: packet_commitment_sequences, - pagination: None, - }; - let response = client - .packet_acknowledgements(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 372u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_acknowledgements.acknowledgements) - } - /// Get the packet acknowledgement for a specific channel, port and sequence - pub async fn packet_acknowledgement( - &self, - port_id: impl Into, - channel_id: impl Into, - sequence: u64, - ) -> Result, DaemonError> { - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_acknowledgement: ibc_channel::QueryPacketAcknowledgementResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryPacketAcknowledgementRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPacketAcknowledgementRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - sequence: sequence, - }; - let response = client - .packet_acknowledgement(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 396u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_acknowledgement.acknowledgement) - } - /// No acknowledgement exists on receiving chain for the given packet commitment sequence on sending chain. - /// Returns the packet sequences that have not yet been received. - pub async fn unreceived_packets( - &self, - port_id: impl Into, - channel_id: impl Into, - packet_commitment_sequences: Vec, - ) -> Result, DaemonError> { - use cosmos_modules::ibc_channel::QueryUnreceivedPacketsResponse; - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_unreceived: QueryUnreceivedPacketsResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryUnreceivedPacketsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryUnreceivedPacketsRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - packet_commitment_sequences: packet_commitment_sequences, - }; - let response = client - .unreceived_packets(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 422u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_unreceived.sequences) - } - /// Returns the acknowledgement sequences that have not yet been received. - /// Given a list of acknowledgement sequences from counterparty, determine if an ack on the counterparty chain has been received on the executing chain. - /// Returns the list of acknowledgement sequences that have not yet been received. - pub async fn unreceived_acks( - &self, - port_id: impl Into, - channel_id: impl Into, - packet_ack_sequences: Vec, - ) -> Result, DaemonError> { - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let ibc_packet_unreceived: ibc_channel::QueryUnreceivedAcksResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryUnreceivedAcksRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryUnreceivedAcksRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - packet_ack_sequences: packet_ack_sequences, - }; - let response = client - .unreceived_acks(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 447u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(ibc_packet_unreceived.sequences) - } - /// Returns the acknowledgement sequences that have not yet been received. - /// Given a list of acknowledgement sequences from counterparty, determine if an ack on the counterparty chain has been received on the executing chain. - /// Returns the list of acknowledgement sequences that have not yet been received. - pub async fn next_sequence_receive( - &self, - port_id: impl Into, - channel_id: impl Into, - ) -> Result { - let port_id = port_id.into(); - let channel_id = channel_id.into(); - let next_receive: ibc_channel::QueryNextSequenceReceiveResponse = { - use crate::cosmos_modules::ibc_channel::{ - query_client::QueryClient, QueryNextSequenceReceiveRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryNextSequenceReceiveRequest { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }; - let response = client - .next_sequence_receive(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::ibc", - "cw_orch_daemon::queriers::ibc", - "cw-orch-daemon/src/queriers/ibc.rs", - ), - 471u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(next_receive.next_sequence_receive) - } - } - } - mod node { - use std::{cmp::min, time::Duration}; - use crate::{cosmos_modules, error::DaemonError, tx_resp::CosmTxResponse}; - use cosmrs::{ - proto::cosmos::{ - base::query::v1beta1::PageRequest, tx::v1beta1::SimulateResponse, - }, - tendermint::{Block, Time}, - }; - use tonic::transport::Channel; - use super::DaemonQuerier; - const MAX_TX_QUERY_RETRIES: usize = 50; - /// Querier for the Tendermint node. - /// Supports queries for block and tx information - pub struct Node { - channel: Channel, - } - impl DaemonQuerier for Node { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Node { - /// Returns node info - pub async fn info( - &self, - ) -> Result { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_node_info(cosmos_modules::tendermint::GetNodeInfoRequest { - }) - .await? - .into_inner(); - Ok(resp) - } - /// Queries node syncing - pub async fn syncing(&self) -> Result { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_syncing(cosmos_modules::tendermint::GetSyncingRequest { - }) - .await? - .into_inner(); - Ok(resp.syncing) - } - /// Returns latests block information - pub async fn latest_block(&self) -> Result { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_latest_block(cosmos_modules::tendermint::GetLatestBlockRequest { - }) - .await? - .into_inner(); - Ok(Block::try_from(resp.block.unwrap())?) - } - /// Returns block information fetched by height - pub async fn block_by_height( - &self, - height: u64, - ) -> Result { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_block_by_height(cosmos_modules::tendermint::GetBlockByHeightRequest { - height: height as i64, - }) - .await? - .into_inner(); - Ok(Block::try_from(resp.block.unwrap())?) - } - /// Return the average block time for the last 50 blocks or since inception - /// This is used to estimate the time when a tx will be included in a block - pub async fn average_block_speed( - &self, - multiplier: Option, - ) -> Result { - let mut latest_block = self.latest_block().await?; - let latest_block_time = latest_block.header.time; - let mut latest_block_height = latest_block.header.height.value(); - while latest_block_height <= 1 { - tokio::time::sleep(Duration::from_secs(1)).await; - latest_block = self.latest_block().await?; - latest_block_height = latest_block.header.height.value(); - } - let avg_period = min(latest_block_height - 1, 50); - let block_avg_period_ago = self - .block_by_height(latest_block_height - avg_period) - .await?; - let block_avg_period_ago_time = block_avg_period_ago.header.time; - let average_block_time = latest_block_time - .duration_since(block_avg_period_ago_time)?; - let average_block_time = average_block_time.as_secs() / avg_period; - let average_block_time = match multiplier { - Some(multiplier) => (average_block_time as f32 * multiplier) as u64, - None => average_block_time, - }; - Ok(std::cmp::max(average_block_time, 1)) - } - /// Returns latests validator set - pub async fn latest_validator_set( - &self, - pagination: Option, - ) -> Result< - cosmos_modules::tendermint::GetLatestValidatorSetResponse, - DaemonError, - > { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_latest_validator_set(cosmos_modules::tendermint::GetLatestValidatorSetRequest { - pagination, - }) - .await? - .into_inner(); - Ok(resp) - } - /// Returns latests validator set fetched by height - pub async fn validator_set_by_height( - &self, - height: i64, - pagination: Option, - ) -> Result< - cosmos_modules::tendermint::GetValidatorSetByHeightResponse, - DaemonError, - > { - let mut client = cosmos_modules::tendermint::service_client::ServiceClient::new( - self.channel.clone(), - ); - let resp = client - .get_validator_set_by_height(cosmos_modules::tendermint::GetValidatorSetByHeightRequest { - height, - pagination, - }) - .await? - .into_inner(); - Ok(resp) - } - /// Returns current block height - pub async fn block_height(&self) -> Result { - let block = self.latest_block().await?; - Ok(block.header.height.value()) - } - /// Returns the block timestamp (since unix epoch) in nanos - pub async fn block_time(&self) -> Result { - let block = self.latest_block().await?; - Ok(block.header.time.duration_since(Time::unix_epoch())?.as_nanos()) - } - /// Simulate TX - pub async fn simulate_tx( - &self, - tx_bytes: Vec, - ) -> Result { - let mut client = cosmos_modules::tx::service_client::ServiceClient::new( - self.channel.clone(), - ); - #[allow(deprecated)] - let resp: SimulateResponse = client - .simulate(cosmos_modules::tx::SimulateRequest { - tx: None, - tx_bytes, - }) - .await? - .into_inner(); - let gas_used = resp.gas_info.unwrap().gas_used; - Ok(gas_used) - } - /// Returns all the block info - pub async fn block_info( - &self, - ) -> Result { - let block = self.latest_block().await?; - let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; - let time = cosmwasm_std::Timestamp::from_nanos( - since_epoch.as_nanos() as u64, - ); - Ok(cosmwasm_std::BlockInfo { - height: block.header.height.value(), - time, - chain_id: block.header.chain_id.to_string(), - }) - } - /// Find TX by hash - pub async fn find_tx( - &self, - hash: String, - ) -> Result { - self.find_tx_with_retries(hash, MAX_TX_QUERY_RETRIES).await - } - /// Find TX by hash with a given amount of retries - pub async fn find_tx_with_retries( - &self, - hash: String, - retries: usize, - ) -> Result { - let mut client = cosmos_modules::tx::service_client::ServiceClient::new( - self.channel.clone(), - ); - let request = cosmos_modules::tx::GetTxRequest { - hash: hash.clone(), - }; - let mut block_speed = self.average_block_speed(Some(0.7)).await?; - for _ in 0..retries { - match client.get_tx(request.clone()).await { - Ok(tx) => { - let resp = tx.into_inner().tx_response.unwrap(); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("TX found: {0:?}", resp), - lvl, - &( - "cw_orch_daemon::queriers::node", - "cw_orch_daemon::queriers::node", - "cw-orch-daemon/src/queriers/node.rs", - ), - 220u32, - ::log::__private_api::Option::None, - ); - } - }; - return Ok(resp.into()); - } - Err(err) => { - block_speed = (block_speed as f64 * 1.6) as u64; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("TX not found with error: {0:?}", err), - lvl, - &( - "cw_orch_daemon::queriers::node", - "cw_orch_daemon::queriers::node", - "cw-orch-daemon/src/queriers/node.rs", - ), - 226u32, - ::log::__private_api::Option::None, - ); - } - }; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("Waiting {0} seconds", block_speed), - lvl, - &( - "cw_orch_daemon::queriers::node", - "cw_orch_daemon::queriers::node", - "cw-orch-daemon/src/queriers/node.rs", - ), - 227u32, - ::log::__private_api::Option::None, - ); - } - }; - tokio::time::sleep(Duration::from_secs(block_speed)).await; - } - } - } - Err(DaemonError::TXNotFound(hash, retries)) - } - } - } - mod staking { - use crate::{cosmos_modules, error::DaemonError}; - use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; - use tonic::transport::Channel; - use super::DaemonQuerier; - /// Querier for the Cosmos Staking module - pub struct Staking { - channel: Channel, - } - impl DaemonQuerier for Staking { - fn new(channel: Channel) -> Self { - Self { channel } - } - } - impl Staking { - /// Queries validator info for given validator address - pub async fn validator( - &self, - validator_addr: impl Into, - ) -> Result { - let validator: cosmos_modules::staking::QueryValidatorResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryValidatorRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryValidatorRequest { - validator_addr: validator_addr.into(), - }; - let response = client.validator(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 24u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(validator.validator.unwrap()) - } - /// Queries all validators that match the given status - /// - /// see [StakingBondStatus] for available statuses - pub async fn validators( - &self, - status: StakingBondStatus, - ) -> Result, DaemonError> { - let validators: cosmos_modules::staking::QueryValidatorsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryValidatorsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryValidatorsRequest { - status: status.to_string(), - pagination: None, - }; - let response = client - .validators(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 42u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(validators.validators) - } - /// Query validator delegations info for given validator - /// - /// see [PageRequest] for pagination - pub async fn delegations( - &self, - validator_addr: impl Into, - pagination: Option, - ) -> Result, DaemonError> { - let validator_delegations: cosmos_modules::staking::QueryValidatorDelegationsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryValidatorDelegationsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryValidatorDelegationsRequest { - validator_addr: validator_addr.into(), - pagination: pagination, - }; - let response = client - .validator_delegations(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 62u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(validator_delegations.delegation_responses) - } - /// Query validator unbonding delegations of a validator - pub async fn unbonding_delegations( - &self, - validator_addr: impl Into, - ) -> Result, DaemonError> { - let validator_unbonding_delegations: cosmos_modules::staking::QueryValidatorUnbondingDelegationsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, - QueryValidatorUnbondingDelegationsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryValidatorUnbondingDelegationsRequest { - validator_addr: validator_addr.into(), - pagination: None, - }; - let response = client - .validator_unbonding_delegations(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 79u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(validator_unbonding_delegations.unbonding_responses) - } - /// Query delegation info for given validator for a delegator - pub async fn delegation( - &self, - validator_addr: impl Into, - delegator_addr: impl Into, - ) -> Result { - let delegation: cosmos_modules::staking::QueryDelegationResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryDelegationRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDelegationRequest { - validator_addr: validator_addr.into(), - delegator_addr: delegator_addr.into(), - }; - let response = client - .delegation(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 97u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(delegation.delegation_response.unwrap()) - } - /// Query unbonding delegation info for given validator delegator - pub async fn unbonding_delegation( - &self, - validator_addr: impl Into, - delegator_addr: impl Into, - ) -> Result { - let unbonding_delegation: cosmos_modules::staking::QueryUnbondingDelegationResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryUnbondingDelegationRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryUnbondingDelegationRequest { - validator_addr: validator_addr.into(), - delegator_addr: delegator_addr.into(), - }; - let response = client - .unbonding_delegation(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 115u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(unbonding_delegation.unbond.unwrap()) - } - /// Query all delegator delegations of a given delegator address - /// - /// see [PageRequest] for pagination - pub async fn delegator_delegations( - &self, - delegator_addr: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::staking::QueryDelegatorDelegationsResponse, - DaemonError, - > { - let delegator_delegations: cosmos_modules::staking::QueryDelegatorDelegationsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryDelegatorDelegationsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDelegatorDelegationsRequest { - delegator_addr: delegator_addr.into(), - pagination: pagination, - }; - let response = client - .delegator_delegations(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 135u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(delegator_delegations) - } - /// Queries all unbonding delegations of a given delegator address. - /// - /// see [PageRequest] for pagination - pub async fn delegator_unbonding_delegations( - &self, - delegator_addr: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::staking::QueryDelegatorUnbondingDelegationsResponse, - DaemonError, - > { - let delegator_unbonding_delegations: cosmos_modules::staking::QueryDelegatorUnbondingDelegationsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, - QueryDelegatorUnbondingDelegationsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDelegatorUnbondingDelegationsRequest { - delegator_addr: delegator_addr.into(), - pagination: pagination, - }; - let response = client - .delegator_unbonding_delegations(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 156u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(delegator_unbonding_delegations) - } - /// Query redelegations of a given address - /// - /// see [PageRequest] for pagination - pub async fn redelegations( - &self, - delegator_addr: impl Into, - src_validator_addr: impl Into, - dst_validator_addr: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::staking::QueryRedelegationsResponse, - DaemonError, - > { - let redelegations: cosmos_modules::staking::QueryRedelegationsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryRedelegationsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryRedelegationsRequest { - delegator_addr: delegator_addr.into(), - src_validator_addr: src_validator_addr.into(), - dst_validator_addr: dst_validator_addr.into(), - pagination: pagination, - }; - let response = client - .redelegations(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 178u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(redelegations) - } - /// Query delegator validators info for given delegator address. - pub async fn delegator_validator( - &self, - validator_addr: impl Into, - delegator_addr: impl Into, - ) -> Result< - cosmos_modules::staking::QueryDelegatorValidatorResponse, - DaemonError, - > { - let delegator_validator: cosmos_modules::staking::QueryDelegatorValidatorResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryDelegatorValidatorRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDelegatorValidatorRequest { - validator_addr: validator_addr.into(), - delegator_addr: delegator_addr.into(), - }; - let response = client - .delegator_validator(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 198u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(delegator_validator) - } - /// Query delegator validators info for given delegator address - /// - /// see [PageRequest] for pagination - pub async fn delegator_validators( - &self, - delegator_addr: impl Into, - pagination: Option, - ) -> Result< - cosmos_modules::staking::QueryDelegatorValidatorsResponse, - DaemonError, - > { - let delegator_validators: cosmos_modules::staking::QueryDelegatorValidatorsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryDelegatorValidatorsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryDelegatorValidatorsRequest { - delegator_addr: delegator_addr.into(), - pagination: pagination, - }; - let response = client - .delegator_validators(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 218u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(delegator_validators) - } - /// Query historical info info for given height - pub async fn historical_info( - &self, - height: i64, - ) -> Result< - cosmos_modules::staking::QueryHistoricalInfoResponse, - DaemonError, - > { - let historical_info: cosmos_modules::staking::QueryHistoricalInfoResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryHistoricalInfoRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryHistoricalInfoRequest { - height: height, - }; - let response = client - .historical_info(request.clone()) - .await? - .into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 236u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(historical_info) - } - /// Query the pool info - pub async fn pool( - &self, - ) -> Result { - let pool: cosmos_modules::staking::QueryPoolResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryPoolRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryPoolRequest {}; - let response = client.pool(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 248u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(pool) - } - /// Query staking parameters - pub async fn params( - &self, - ) -> Result { - let params: cosmos_modules::staking::QueryParamsResponse = { - use crate::cosmos_modules::staking::{ - query_client::QueryClient, QueryParamsRequest, - }; - let mut client = QueryClient::new(self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = QueryParamsRequest {}; - let response = client.params(request.clone()).await?.into_inner(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "cosmos_query: {0:?} resulted in: {1:?}", - request, - response, - ), - lvl, - &( - "cw_orch_daemon::queriers::staking", - "cw_orch_daemon::queriers::staking", - "cw-orch-daemon/src/queriers/staking.rs", - ), - 257u32, - ::log::__private_api::Option::None, - ); - } - }; - response - }; - Ok(params) - } - } - /// Staking bond statuses - pub enum StakingBondStatus { - /// UNSPECIFIED defines an invalid validator status. - Unspecified = 0, - /// UNBONDED defines a validator that is not bonded. - Unbonded = 1, - /// UNBONDING defines a validator that is unbonding. - Unbonding = 2, - /// BONDED defines a validator that is bonded. - Bonded = 3, - } - impl ToString for StakingBondStatus { - /// Convert to string - fn to_string(&self) -> String { - match self { - StakingBondStatus::Unspecified => { - "BOND_STATUS_UNSPECIFIED".to_string() - } - StakingBondStatus::Unbonded => "BOND_STATUS_UNBONDED".to_string(), - StakingBondStatus::Unbonding => "BOND_STATUS_UNBONDING".to_string(), - StakingBondStatus::Bonded => "BOND_STATUS_BONDED".to_string(), - } - } - } - } - pub use bank::Bank; - pub use cosmwasm::CosmWasm; - pub use feegrant::Feegrant; - pub use ibc::Ibc; - pub use node::Node; - pub use gov::*; - pub use staking::*; - use tonic::transport::Channel; - /// Constructor for a querier over a given channel - pub trait DaemonQuerier { - /// Construct an new querier over a given channel - fn new(channel: Channel) -> Self; - } -} -mod traits { - use cw_orch_core::{ - contract::interface_traits::{CwOrchMigrate, CwOrchUpload}, - environment::TxResponse, - }; - use crate::{queriers::CosmWasm, Daemon, DaemonError}; - /// Helper methods for conditional uploading of a contract. - pub trait ConditionalUpload: CwOrchUpload { - /// Only upload the contract if it is not uploaded yet (checksum does not match) - fn upload_if_needed(&self) -> Result>, DaemonError> { - if self.latest_is_uploaded()? { - Ok(None) - } else { - Some(self.upload()).transpose().map_err(Into::into) - } - } - /// Returns whether the checksum of the WASM file matches the checksum of the latest uploaded code for this contract. - fn latest_is_uploaded(&self) -> Result { - let Some(latest_uploaded_code_id) = self.code_id().ok() else { - return Ok(false); - }; - let chain = self.get_chain(); - let on_chain_hash = chain - .rt_handle - .block_on( - chain - .query_client::() - .code_id_hash(latest_uploaded_code_id), - )?; - let local_hash = self.wasm().checksum()?; - Ok(local_hash == on_chain_hash) - } - /// Returns whether the contract is running the latest uploaded code for it - fn is_running_latest(&self) -> Result { - let Some(latest_uploaded_code_id) = self.code_id().ok() else { - return Ok(false); - }; - let chain = self.get_chain(); - let info = chain - .rt_handle - .block_on( - chain.query_client::().contract_info(self.address()?), - )?; - Ok(latest_uploaded_code_id == info.code_id) - } - } - impl ConditionalUpload for T - where - T: CwOrchUpload, - {} - /// Helper methods for conditional migration of a contract. - pub trait ConditionalMigrate: CwOrchMigrate + ConditionalUpload { - /// Only migrate the contract if it is not on the latest code-id yet - fn migrate_if_needed( - &self, - migrate_msg: &Self::MigrateMsg, - ) -> Result>, DaemonError> { - if self.is_running_latest()? { - { - let lvl = ::log::Level::Info; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "{0} is already running the latest code", - self.id(), - ), - lvl, - &( - "cw_orch_daemon::traits", - "cw_orch_daemon::traits", - "cw-orch-daemon/src/traits.rs", - ), - 61u32, - ::log::__private_api::Option::None, - ); - } - }; - Ok(None) - } else { - Some(self.migrate(migrate_msg, self.code_id()?)) - .transpose() - .map_err(Into::into) - } - } - /// Uploads the contract if the local contract hash is different from the latest on-chain code hash. - /// Proceeds to migrates the contract if the contract is not running the latest code. - fn upload_and_migrate_if_needed( - &self, - migrate_msg: &Self::MigrateMsg, - ) -> Result>>, DaemonError> { - let mut txs = Vec::with_capacity(2); - if let Some(tx) = self.upload_if_needed()? { - txs.push(tx); - } - if let Some(tx) = self.migrate_if_needed(migrate_msg)? { - txs.push(tx); - } - if txs.is_empty() { Ok(None) } else { Ok(Some(txs)) } - } - } - impl ConditionalMigrate for T - where - T: CwOrchMigrate + CwOrchUpload, - {} -} -pub mod tx_builder { - use cosmrs::tx::{ModeInfo, SignMode}; - use cosmrs::{ - proto::cosmos::auth::v1beta1::BaseAccount, tendermint::chain::Id, - tx::{self, Body, Fee, Msg, Raw, SequenceNumber, SignDoc, SignerInfo}, - Any, Coin, - }; - use secp256k1::All; - use super::{sender::Sender, DaemonError}; - const GAS_BUFFER: f64 = 1.3; - const BUFFER_THRESHOLD: u64 = 200_000; - const SMALL_GAS_BUFFER: f64 = 1.4; - /// Struct used to build a raw transaction and broadcast it with a sender. - pub struct TxBuilder { - pub(crate) body: Body, - pub(crate) fee_amount: Option, - pub(crate) gas_limit: Option, - pub(crate) sequence: Option, - } - #[automatically_derived] - impl ::core::clone::Clone for TxBuilder { - #[inline] - fn clone(&self) -> TxBuilder { - TxBuilder { - body: ::core::clone::Clone::clone(&self.body), - fee_amount: ::core::clone::Clone::clone(&self.fee_amount), - gas_limit: ::core::clone::Clone::clone(&self.gas_limit), - sequence: ::core::clone::Clone::clone(&self.sequence), - } - } - } - #[automatically_derived] - impl ::core::fmt::Debug for TxBuilder { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field4_finish( - f, - "TxBuilder", - "body", - &self.body, - "fee_amount", - &self.fee_amount, - "gas_limit", - &self.gas_limit, - "sequence", - &&self.sequence, - ) - } - } - impl TxBuilder { - /// Create a new TxBuilder with a given body. - pub fn new(body: Body) -> Self { - Self { - body, - fee_amount: None, - gas_limit: None, - sequence: None, - } - } - /// Set a fixed fee amount for the tx - pub fn fee_amount(&mut self, fee_amount: u128) -> &mut Self { - self.fee_amount = Some(fee_amount); - self - } - /// Set a gas limit for the tx - pub fn gas_limit(&mut self, gas_limit: u64) -> &mut Self { - self.gas_limit = Some(gas_limit); - self - } - /// Set a sequence number for the tx - pub fn sequence(&mut self, sequence: u64) -> &mut Self { - self.sequence = Some(sequence); - self - } - /// Builds the body of the tx with a given memo and timeout. - pub fn build_body( - msgs: Vec, - memo: Option<&str>, - timeout: u64, - ) -> tx::Body { - let msgs = msgs - .into_iter() - .map(Msg::into_any) - .collect::, _>>() - .unwrap(); - tx::Body::new(msgs, memo.unwrap_or_default(), timeout as u32) - } - pub(crate) fn build_fee( - amount: impl Into, - denom: &str, - gas_limit: u64, - ) -> Fee { - let fee = Coin::new(amount.into(), denom).unwrap(); - Fee::from_amount_and_gas(fee, gas_limit) - } - /// Builds the raw tx with a given body and fee and signs it. - /// Sets the TxBuilder's gas limit to its simulated amount for later use. - pub async fn build(&mut self, wallet: &Sender) -> Result { - let BaseAccount { account_number, sequence, .. } = wallet - .base_account() - .await?; - let sequence = self.sequence.unwrap_or(sequence); - let (tx_fee, gas_limit) = if let (Some(fee), Some(gas_limit)) - = (self.fee_amount, self.gas_limit) { - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Using pre-defined fee and gas limits: {0}, {1}", - fee, - gas_limit, - ), - lvl, - &( - "cw_orch_daemon::tx_builder", - "cw_orch_daemon::tx_builder", - "cw-orch-daemon/src/tx_builder.rs", - ), - 91u32, - ::log::__private_api::Option::None, - ); - } - }; - (fee, gas_limit) - } else { - let sim_gas_used = wallet - .calculate_gas(&self.body, sequence, account_number) - .await?; - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Simulated gas needed {0:?}", sim_gas_used), - lvl, - &( - "cw_orch_daemon::tx_builder", - "cw_orch_daemon::tx_builder", - "cw-orch-daemon/src/tx_builder.rs", - ), - 101u32, - ::log::__private_api::Option::None, - ); - } - }; - let gas_expected = if sim_gas_used < BUFFER_THRESHOLD { - sim_gas_used as f64 * SMALL_GAS_BUFFER - } else { - sim_gas_used as f64 * GAS_BUFFER - }; - let fee_amount = gas_expected - * (wallet - .daemon_state - .chain_data - .fees - .fee_tokens[0] - .fixed_min_gas_price + 0.00001); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!("Calculated fee needed: {0:?}", fee_amount), - lvl, - &( - "cw_orch_daemon::tx_builder", - "cw_orch_daemon::tx_builder", - "cw-orch-daemon/src/tx_builder.rs", - ), - 111u32, - ::log::__private_api::Option::None, - ); - } - }; - self.gas_limit = Some(gas_expected as u64); - (fee_amount as u128, gas_expected as u64) - }; - let fee = Self::build_fee( - tx_fee, - &wallet.daemon_state.chain_data.fees.fee_tokens[0].denom, - gas_limit, - ); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "submitting tx: \n fee: {0:?}\naccount_nr: {1:?}\nsequence: {2:?}", - fee, - account_number, - sequence, - ), - lvl, - &( - "cw_orch_daemon::tx_builder", - "cw_orch_daemon::tx_builder", - "cw-orch-daemon/src/tx_builder.rs", - ), - 125u32, - ::log::__private_api::Option::None, - ); - } - }; - let auth_info = SignerInfo { - public_key: wallet.private_key.get_signer_public_key(&wallet.secp), - mode_info: ModeInfo::single(SignMode::Direct), - sequence, - } - .auth_info(fee); - let sign_doc = SignDoc::new( - &self.body, - &auth_info, - &Id::try_from(wallet.daemon_state.chain_data.chain_id.to_string())?, - account_number, - )?; - wallet.sign(sign_doc).map_err(Into::into) - } - } -} -pub use self::{ - builder::*, channel::*, core::*, error::*, state::*, sync::*, traits::*, tx_resp::*, -}; -pub use cw_orch_networks::chain_info::*; -pub use cw_orch_networks::networks; -pub use sender::Wallet; -pub use tx_builder::TxBuilder; -pub(crate) mod cosmos_modules { - pub use cosmrs::proto::{ - cosmos::{ - auth::v1beta1 as auth, authz::v1beta1 as authz, bank::v1beta1 as bank, - base::{ - abci::v1beta1 as abci, tendermint::v1beta1 as tendermint, v1beta1 as base, - }, - crisis::v1beta1 as crisis, distribution::v1beta1 as distribution, - evidence::v1beta1 as evidence, feegrant::v1beta1 as feegrant, - gov::v1beta1 as gov, mint::v1beta1 as mint, params::v1beta1 as params, - slashing::v1beta1 as slashing, staking::v1beta1 as staking, - tx::v1beta1 as tx, vesting::v1beta1 as vesting, - }, - cosmwasm::wasm::v1 as cosmwasm, - ibc::{ - applications::transfer::v1 as ibc_transfer, - core::{ - channel::v1 as ibc_channel, client::v1 as ibc_client, - connection::v1 as ibc_connection, - }, - }, - tendermint::abci as tendermint_abci, - }; -} -/// Re-export trait and data required to fetch daemon data from chain-registry -pub use ibc_chain_registry::{ - chain::ChainData as ChainRegistryData, fetchable::Fetchable, -}; From a3204737557e2650f6a5b0b5839e3f923b36cbd0 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Sun, 10 Sep 2023 01:22:26 +0200 Subject: [PATCH 4/8] Added rpc url parameter + tested rpc tx submit --- cw-orch-daemon/Cargo.toml | 2 +- cw-orch-daemon/src/lib.rs | 8 +++++ cw-orch-daemon/src/queriers/rpc/node.rs | 7 ++-- cw-orch-daemon/src/queriers/rpc/tx.rs | 34 +++++++++++-------- cw-orch-daemon/tests/querier.rs | 30 ++++++++++++++-- packages/cw-orch-networks/src/chain_info.rs | 12 ++++++- .../cw-orch-networks/src/networks/archway.rs | 3 ++ .../src/networks/injective.rs | 2 ++ .../cw-orch-networks/src/networks/juno.rs | 4 +++ .../cw-orch-networks/src/networks/kujira.rs | 1 + .../cw-orch-networks/src/networks/migaloo.rs | 3 ++ .../cw-orch-networks/src/networks/neutron.rs | 3 ++ .../cw-orch-networks/src/networks/osmosis.rs | 3 ++ packages/cw-orch-networks/src/networks/sei.rs | 3 ++ .../cw-orch-networks/src/networks/terra.rs | 3 ++ 15 files changed, 96 insertions(+), 22 deletions(-) diff --git a/cw-orch-daemon/Cargo.toml b/cw-orch-daemon/Cargo.toml index 154bb5a85..4329f4886 100644 --- a/cw-orch-daemon/Cargo.toml +++ b/cw-orch-daemon/Cargo.toml @@ -17,7 +17,7 @@ exclude = [".env"] all-features = true [features] -default = ["node-tests", "grpc"] +default = ["node-tests", "rpc"] # enable node-backed tests (ensure Docker is running) # run with `cargo test --jobs 1 --features node-tests` node-tests = [] diff --git a/cw-orch-daemon/src/lib.rs b/cw-orch-daemon/src/lib.rs index c46e637eb..a4055a864 100644 --- a/cw-orch-daemon/src/lib.rs +++ b/cw-orch-daemon/src/lib.rs @@ -1,6 +1,14 @@ + //! `Daemon` and `DaemonAsync` execution environments. //! //! The `Daemon` type is a synchronous wrapper around the `DaemonAsync` type and can be used as a contract execution environment. +//! +#[cfg(all(feature = "rpc", feature = "grpc"))] +compile_error!("feature \"rpc\" and feature \"grpc\" cannot be enabled at the same time"); + +#[cfg(not(any(feature = "rpc", feature = "grpc")))] +compile_error!("At least one of feature \"rpc\" and feature \"grpc\" needs to be enabled"); + pub mod builder; pub mod core; pub mod error; diff --git a/cw-orch-daemon/src/queriers/rpc/node.rs b/cw-orch-daemon/src/queriers/rpc/node.rs index dc61d8c80..59b725d34 100644 --- a/cw-orch-daemon/src/queriers/rpc/node.rs +++ b/cw-orch-daemon/src/queriers/rpc/node.rs @@ -3,7 +3,7 @@ use std::{cmp::min, time::Duration}; use crate::{cosmos_modules, error::DaemonError, tx_resp::CosmTxResponse, queriers::{MAX_TX_QUERY_RETRIES, DaemonQuerier}, cosmos_rpc_query}; use cosmrs::{ - proto::cosmos::base::{query::v1beta1::PageRequest, abci::v1beta1::TxResponse}, + proto::cosmos::{base::query::v1beta1::PageRequest, tx::v1beta1::GetTxResponse}, tendermint::{Block, Time}, rpc::{HttpClient, Client}, tx::MessageExt, }; use prost::Message; @@ -223,10 +223,11 @@ impl Node { None, true, ).await?; - match TxResponse::decode(tx_response.value.as_slice()) { + + match GetTxResponse::decode(tx_response.value.as_slice()) { Ok(tx) => { log::debug!("TX found: {:?}", tx); - return Ok(tx.into()); + return Ok(tx.tx_response.unwrap().into()); } Err(err) => { // increase wait time diff --git a/cw-orch-daemon/src/queriers/rpc/tx.rs b/cw-orch-daemon/src/queriers/rpc/tx.rs index 3f3797b77..a08b22ce1 100644 --- a/cw-orch-daemon/src/queriers/rpc/tx.rs +++ b/cw-orch-daemon/src/queriers/rpc/tx.rs @@ -2,8 +2,8 @@ use cosmrs::{rpc::HttpClient, tx::Raw, proto::cosmos::base::abci::v1beta1::TxResponse}; -use crate::{queriers::DaemonQuerier, cosmos_rpc_query, DaemonError, cosmos_modules}; - +use crate::{queriers::DaemonQuerier, DaemonError}; + use cosmrs::rpc::Client; /// Queries for Cosmos Bank Module pub struct Tx { @@ -17,22 +17,28 @@ impl DaemonQuerier for Tx { } impl Tx{ - /// Query spendable balance for address pub async fn broadcast( &self, tx: Raw, ) -> Result { - let resp = cosmos_rpc_query!( - self, - tx, - "/cosmos.tx.v1beta1.Service/BroadcastTx", - BroadcastTxRequest { - tx_bytes: tx.to_bytes()?, - mode: cosmos_modules::tx::BroadcastMode::Sync.into(), - }, - BroadcastTxResponse, - ); - Ok(resp.tx_response.unwrap()) + let resp = self.client.broadcast_tx_commit(tx.to_bytes()?).await?; + + let check = resp.check_tx; + Ok(TxResponse { + height: resp.height.into(), + txhash: resp.hash.to_string(), + codespace: check.codespace, + code: check.code.into(), + data: "".to_string(), + raw_log: check.log, + logs: vec![], + info: check.info, + gas_wanted: check.gas_wanted, + gas_used: check.gas_used, + tx: None, + timestamp: "".to_string(), + events: vec![] + }) } } \ No newline at end of file diff --git a/cw-orch-daemon/tests/querier.rs b/cw-orch-daemon/tests/querier.rs index 9d6edee3f..54c511747 100644 --- a/cw-orch-daemon/tests/querier.rs +++ b/cw-orch-daemon/tests/querier.rs @@ -5,9 +5,7 @@ mod queriers { use cw_orch_core::contract::interface_traits::*; use cw_orch_core::environment::TxHandler; - use cw_orch_daemon::GrpcChannel; use cw_orch_networks::networks; - use ibc_chain_registry::chain::Grpc; use ibc_relayer_types::core::ics24_host::identifier::ChainId; use mock_contract::InstantiateMsg; use speculoos::{asserting, result::ResultAssertions}; @@ -26,7 +24,11 @@ mod queriers { AccountId, Denom, }; + + #[cfg(feature="grpc")] pub async fn build_channel() -> tonic::transport::Channel { + use ibc_chain_registry::chain::Grpc; + let network = networks::LOCAL_JUNO; let grpcs: Vec = vec![Grpc { @@ -36,7 +38,29 @@ mod queriers { let chain: ChainId = ChainId::new(network.chain_id.to_owned(), 1); - let channel = GrpcChannel::connect(&grpcs, &chain).await; + let channel = cw_orch_daemon::GrpcChannel::connect(&grpcs, &chain).await; + + asserting!("channel connection is succesful") + .that(&channel) + .is_ok(); + + channel.unwrap() + } + + #[cfg(feature="rpc")] + pub async fn build_channel() -> cosmrs::rpc::HttpClient { + use ibc_chain_registry::chain::Rpc; + + let network = networks::LOCAL_JUNO; + + let rpcs: Vec = vec![Rpc { + address: network.rpc_urls[0].into(), + provider: None, + }]; + + let chain: ChainId = ChainId::new(network.chain_id.to_owned(), 1); + + let channel = cw_orch_daemon::RpcChannel::connect(&rpcs, &chain).await; asserting!("channel connection is succesful") .that(&channel) diff --git a/packages/cw-orch-networks/src/chain_info.rs b/packages/cw-orch-networks/src/chain_info.rs index b842be4fc..de60fb964 100644 --- a/packages/cw-orch-networks/src/chain_info.rs +++ b/packages/cw-orch-networks/src/chain_info.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use ibc_chain_registry::chain::{Apis, ChainData as RegistryChainInfo, FeeToken, FeeTokens, Grpc}; +use ibc_chain_registry::chain::{Apis, ChainData as RegistryChainInfo, FeeToken, FeeTokens, Grpc, Rpc}; #[allow(clippy::from_over_into)] impl Into for ChainInfo<'_> { @@ -26,6 +26,14 @@ impl Into for ChainInfo<'_> { ..Default::default() }) .collect(), + rpc: self + .rpc_urls + .iter() + .map(|url| Rpc { + address: url.to_string(), + ..Default::default() + }) + .collect(), ..Default::default() }, slip44: self.network_info.coin_type, @@ -47,6 +55,8 @@ pub struct ChainInfo<'a> { pub gas_price: f64, /// gRPC urls, used to attempt connection pub grpc_urls: &'a [&'a str], + /// gRPC urls, used to attempt connection + pub rpc_urls: &'a [&'a str], /// Optional urls for custom functionality pub lcd_url: Option<&'a str>, /// Optional urls for custom functionality diff --git a/packages/cw-orch-networks/src/networks/archway.rs b/packages/cw-orch-networks/src/networks/archway.rs index aa766eaf8..25b426f85 100644 --- a/packages/cw-orch-networks/src/networks/archway.rs +++ b/packages/cw-orch-networks/src/networks/archway.rs @@ -15,6 +15,7 @@ pub const CONSTANTINE_3: ChainInfo = ChainInfo { gas_denom: "aconst", gas_price: 1000000000000.0, grpc_urls: &["https://grpc.constantine.archway.tech:443"], + rpc_urls: &[], network_info: ARCHWAY_NETWORK, lcd_url: Some("https://api.constantine.archway.tech"), fcd_url: None, @@ -28,6 +29,7 @@ pub const ARCHWAY_1: ChainInfo = ChainInfo { gas_denom: "aarch", gas_price: 1000000000000.0, grpc_urls: &["https://grpc.mainnet.archway.io:443"], + rpc_urls: &["https://rpc.mainnet.archway.io"], network_info: ARCHWAY_NETWORK, lcd_url: Some("https://api.mainnet.archway.io"), fcd_url: None, @@ -44,6 +46,7 @@ pub const CONSTANTINE_1: ChainInfo = ChainInfo { gas_denom: "uconst", gas_price: 0.025, grpc_urls: &["https://grpc.constantine-1.archway.tech:443"], + rpc_urls: &[], network_info: ARCHWAY_NETWORK, lcd_url: Some("https://api.constantine-1.archway.tech"), fcd_url: None, diff --git a/packages/cw-orch-networks/src/networks/injective.rs b/packages/cw-orch-networks/src/networks/injective.rs index 2fa0ba835..e3a8df6b3 100644 --- a/packages/cw-orch-networks/src/networks/injective.rs +++ b/packages/cw-orch-networks/src/networks/injective.rs @@ -16,6 +16,7 @@ pub const INJECTIVE_1: ChainInfo = ChainInfo { gas_denom: "inj", gas_price: 500_000_000.0, grpc_urls: &["https://k8s.global.mainnet.chain.grpc.injective.network:443"], + rpc_urls: &[], network_info: INJECTIVE_NETWORK, lcd_url: None, fcd_url: None, @@ -29,6 +30,7 @@ pub const INJECTIVE_888: ChainInfo = ChainInfo { gas_denom: "inj", gas_price: 500_000_000.0, grpc_urls: &["https://k8s.testnet.chain.grpc.injective.network:443"], + rpc_urls: &[], network_info: INJECTIVE_NETWORK, lcd_url: None, fcd_url: None, diff --git a/packages/cw-orch-networks/src/networks/juno.rs b/packages/cw-orch-networks/src/networks/juno.rs index 478bf1277..50a62d5e9 100644 --- a/packages/cw-orch-networks/src/networks/juno.rs +++ b/packages/cw-orch-networks/src/networks/juno.rs @@ -15,6 +15,7 @@ pub const UNI_6: ChainInfo = ChainInfo { gas_denom: "ujunox", gas_price: 0.025, grpc_urls: &["http://juno-testnet-grpc.polkachu.com:12690"], + rpc_urls: &[], network_info: JUNO_NETWORK, lcd_url: None, fcd_url: None, @@ -29,6 +30,7 @@ pub const JUNO_1: ChainInfo = ChainInfo { "https://grpc-juno-ia.cosmosia.notional.ventures", "http://juno-grpc.polkachu.com:12690", ], + rpc_urls: &[], network_info: JUNO_NETWORK, lcd_url: None, fcd_url: None, @@ -40,6 +42,7 @@ pub const LOCAL_JUNO: ChainInfo = ChainInfo { gas_denom: "ujunox", gas_price: 0.0, grpc_urls: &["http://localhost:9090"], + rpc_urls: &["http://localhost:26657"], network_info: JUNO_NETWORK, lcd_url: None, fcd_url: None, @@ -56,6 +59,7 @@ pub const UNI_5: ChainInfo = ChainInfo { gas_denom: "ujunox", gas_price: 0.025, grpc_urls: &["https://juno-testnet-grpc.polkachu.com:12690"], + rpc_urls: &[], network_info: JUNO_NETWORK, lcd_url: None, fcd_url: None, diff --git a/packages/cw-orch-networks/src/networks/kujira.rs b/packages/cw-orch-networks/src/networks/kujira.rs index 1547d74c2..c2629ee22 100644 --- a/packages/cw-orch-networks/src/networks/kujira.rs +++ b/packages/cw-orch-networks/src/networks/kujira.rs @@ -13,6 +13,7 @@ pub const HARPOON_4: ChainInfo = ChainInfo { gas_denom: "ukuji", gas_price: 0.025, grpc_urls: &["http://kujira-testnet-grpc.polkachu.com:11890"], + rpc_urls: &[], network_info: KUJIRA_NETWORK, lcd_url: None, fcd_url: None, diff --git a/packages/cw-orch-networks/src/networks/migaloo.rs b/packages/cw-orch-networks/src/networks/migaloo.rs index e076ecf60..78352feb4 100644 --- a/packages/cw-orch-networks/src/networks/migaloo.rs +++ b/packages/cw-orch-networks/src/networks/migaloo.rs @@ -13,6 +13,7 @@ pub const LOCAL_MIGALOO: ChainInfo = ChainInfo { gas_denom: "uwhale", gas_price: 0.1, grpc_urls: &["http://localhost:9090"], + rpc_urls: &[], network_info: MIGALOO_NETWORK, lcd_url: None, fcd_url: None, @@ -25,6 +26,7 @@ pub const NARWHAL_1: ChainInfo = ChainInfo { gas_denom: "uwhale", gas_price: 0.1, grpc_urls: &["migaloo-testnet-grpc.polkachu.com:20790"], + rpc_urls: &[], network_info: MIGALOO_NETWORK, lcd_url: None, fcd_url: None, @@ -37,6 +39,7 @@ pub const MIGALOO_1: ChainInfo = ChainInfo { gas_denom: "uwhale", gas_price: 0.1, grpc_urls: &["migaloo-grpc.polkachu.com:20790"], + rpc_urls: &[], network_info: MIGALOO_NETWORK, lcd_url: None, fcd_url: None, diff --git a/packages/cw-orch-networks/src/networks/neutron.rs b/packages/cw-orch-networks/src/networks/neutron.rs index c7645da9e..112ff33dc 100644 --- a/packages/cw-orch-networks/src/networks/neutron.rs +++ b/packages/cw-orch-networks/src/networks/neutron.rs @@ -14,6 +14,7 @@ pub const PION_1: ChainInfo = ChainInfo { gas_denom: "untrn", gas_price: 0.001, grpc_urls: &["http://grpc-palvus.pion-1.ntrn.tech:80"], + rpc_urls: &[], network_info: NEUTRON_NETWORK, lcd_url: Some("https://rest-palvus.pion-1.ntrn.tech"), fcd_url: None, @@ -26,6 +27,7 @@ pub const NEUTRON_1: ChainInfo = ChainInfo { gas_denom: "untrn", gas_price: 0.01, grpc_urls: &["http://grpc-kralum.neutron-1.neutron.org:80"], + rpc_urls: &[], network_info: NEUTRON_NETWORK, lcd_url: Some("https://rest-kralum.neutron-1.neutron.org"), fcd_url: None, @@ -37,6 +39,7 @@ pub const LOCAL_NEUTRON: ChainInfo = ChainInfo { gas_denom: "untrn", gas_price: 0.0025, grpc_urls: &["http://localhost:8090"], + rpc_urls: &[], network_info: NEUTRON_NETWORK, lcd_url: None, fcd_url: None, diff --git a/packages/cw-orch-networks/src/networks/osmosis.rs b/packages/cw-orch-networks/src/networks/osmosis.rs index 584f260c2..df4040e98 100644 --- a/packages/cw-orch-networks/src/networks/osmosis.rs +++ b/packages/cw-orch-networks/src/networks/osmosis.rs @@ -13,6 +13,7 @@ pub const OSMOSIS_1: ChainInfo = ChainInfo { gas_denom: "uosmo", gas_price: 0.025, grpc_urls: &["http://grpc.osmosis.zone:9090"], + rpc_urls: &[], network_info: OSMO_NETWORK, lcd_url: None, fcd_url: None, @@ -24,6 +25,7 @@ pub const OSMO_5: ChainInfo = ChainInfo { gas_denom: "uosmo", gas_price: 0.025, grpc_urls: &["https://grpc.osmotest5.osmosis.zone:443"], + rpc_urls: &[], network_info: OSMO_NETWORK, lcd_url: None, fcd_url: None, @@ -35,6 +37,7 @@ pub const LOCAL_OSMO: ChainInfo = ChainInfo { gas_denom: "uosmo", gas_price: 0.0026, grpc_urls: &["http://65.108.235.46:9094"], + rpc_urls: &[], network_info: OSMO_NETWORK, lcd_url: None, fcd_url: None, diff --git a/packages/cw-orch-networks/src/networks/sei.rs b/packages/cw-orch-networks/src/networks/sei.rs index 43124f66e..ff2599a5a 100644 --- a/packages/cw-orch-networks/src/networks/sei.rs +++ b/packages/cw-orch-networks/src/networks/sei.rs @@ -13,6 +13,7 @@ pub const LOCAL_SEI: ChainInfo = ChainInfo { gas_denom: "usei", gas_price: 0.1, grpc_urls: &["http://localhost:9090"], + rpc_urls: &[], network_info: SEI_NETWORK, lcd_url: None, fcd_url: None, @@ -24,6 +25,7 @@ pub const SEI_DEVNET_3: ChainInfo = ChainInfo { gas_denom: "usei", gas_price: 0.1, grpc_urls: &["http://sei_devnet-testnet-grpc.polkachu.com:11990"], + rpc_urls: &[], network_info: SEI_NETWORK, lcd_url: None, fcd_url: None, @@ -35,6 +37,7 @@ pub const ATLANTIC_2: ChainInfo = ChainInfo { gas_denom: "usei", gas_price: 0.1, grpc_urls: &["http://sei-testnet-grpc.polkachu.com:11990"], + rpc_urls: &[], network_info: SEI_NETWORK, lcd_url: None, fcd_url: None, diff --git a/packages/cw-orch-networks/src/networks/terra.rs b/packages/cw-orch-networks/src/networks/terra.rs index 6ddafb17c..955628e5d 100644 --- a/packages/cw-orch-networks/src/networks/terra.rs +++ b/packages/cw-orch-networks/src/networks/terra.rs @@ -15,6 +15,7 @@ pub const PISCO_1: ChainInfo = ChainInfo { gas_denom: "uluna", gas_price: 0.15, grpc_urls: &["http://terra-testnet-grpc.polkachu.com:11790"], + rpc_urls: &[], network_info: TERRA_NETWORK, lcd_url: None, fcd_url: None, @@ -28,6 +29,7 @@ pub const PHOENIX_1: ChainInfo = ChainInfo { gas_denom: "uluna", gas_price: 0.15, grpc_urls: &["http://terra-grpc.polkachu.com:11790"], + rpc_urls: &[], network_info: TERRA_NETWORK, lcd_url: None, fcd_url: None, @@ -41,6 +43,7 @@ pub const LOCAL_TERRA: ChainInfo = ChainInfo { gas_denom: "uluna", gas_price: 0.15, grpc_urls: &["http://65.108.235.46:9090"], + rpc_urls: &[], network_info: TERRA_NETWORK, lcd_url: None, fcd_url: None, From eac137807f4811add8e67ee77ccb18da22980ca3 Mon Sep 17 00:00:00 2001 From: Interchain Adair <32375605+adairrr@users.noreply.github.com> Date: Thu, 27 Jun 2024 17:05:26 +0200 Subject: [PATCH 5/8] Update RPC Tendermint to 38 (#273) * Add local wasm network * Update support of tendermint rpc to 0.38 * Remove unused tendermint-rpc dep * Minor bug fixes * Return proper rpc error * Update wasmd network names * Format --- cw-orch-daemon/Cargo.toml | 5 +- cw-orch-daemon/src/core.rs | 11 ++- cw-orch-daemon/src/error.rs | 8 +- cw-orch-daemon/src/lib.rs | 16 ++-- cw-orch-daemon/src/live_mock.rs | 10 +- cw-orch-daemon/src/proto/injective.rs | 10 +- cw-orch-daemon/src/queriers.rs | 18 ++-- cw-orch-daemon/src/queriers/grpc/auth.rs | 17 ++-- cw-orch-daemon/src/queriers/grpc/mod.rs | 5 +- cw-orch-daemon/src/queriers/grpc/node.rs | 4 +- cw-orch-daemon/src/queriers/grpc/tx.rs | 19 ++-- cw-orch-daemon/src/queriers/rpc/auth.rs | 21 ++--- cw-orch-daemon/src/queriers/rpc/bank.rs | 19 ++-- cw-orch-daemon/src/queriers/rpc/cosmwasm.rs | 22 ++--- cw-orch-daemon/src/queriers/rpc/mod.rs | 9 +- cw-orch-daemon/src/queriers/rpc/node.rs | 91 ++++++++----------- cw-orch-daemon/src/queriers/rpc/staking.rs | 20 +++- cw-orch-daemon/src/queriers/rpc/tx.rs | 37 ++++---- cw-orch-daemon/src/rpc_channel.rs | 10 +- cw-orch-daemon/src/sender.rs | 12 ++- cw-orch-daemon/src/state.rs | 20 ++-- cw-orch-daemon/src/sync/core.rs | 4 +- cw-orch-daemon/src/tx_builder.rs | 16 +++- cw-orch-daemon/src/tx_resp.rs | 74 +++++++++++++-- cw-orch-daemon/tests/querier.rs | 5 +- packages/cw-orch-networks/src/chain_info.rs | 4 +- packages/cw-orch-networks/src/networks/mod.rs | 2 + .../cw-orch-networks/src/networks/wasm.rs | 22 +++++ 28 files changed, 296 insertions(+), 215 deletions(-) create mode 100644 packages/cw-orch-networks/src/networks/wasm.rs diff --git a/cw-orch-daemon/Cargo.toml b/cw-orch-daemon/Cargo.toml index 4329f4886..7a06f7bcc 100644 --- a/cw-orch-daemon/Cargo.toml +++ b/cw-orch-daemon/Cargo.toml @@ -26,7 +26,6 @@ rpc = ["cosmrs/rpc"] grpc = ["cosmrs/grpc", "dep:tonic"] - [dependencies] # Default deps cw-orch-contract-derive = { workspace = true } @@ -46,7 +45,7 @@ thiserror = { workspace = true } # Daemon deps sha256 = { workspace = true } ibc-relayer-types = { workspace = true } -prost = { version = "0.11" } +prost = { version = "0.12" } bitcoin = { version = "0.30.0" } hex = { version = "0.4.3" } ripemd = { version = "0.1.3" } @@ -60,7 +59,7 @@ hkd32 = { version = "0.7.0", features = ["bip39", "mnemonic", "bech32"] } rand_core = { version = "0.6.4", default-features = false } ed25519-dalek = { version = "2", features = ["serde"] } eyre = { version = "0.6" } -cosmrs = { version = "0.14.0", features = ["dev", "cosmwasm"] } +cosmrs = { version = "0.15.0", features = ["dev", "cosmwasm"] } chrono = { version = "0.4" } base16 = { version = "0.2.1" } derive_builder = { version = "0.12.0" } diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index 0afe802bf..9281797b7 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -3,7 +3,7 @@ use crate::DaemonState; use super::{ builder::DaemonAsyncBuilder, error::DaemonError, - queriers::{DaemonQuerier, Node, CosmWasm}, + queriers::{CosmWasm, DaemonQuerier, Node}, sender::Wallet, tx_resp::CosmTxResponse, }; @@ -27,7 +27,6 @@ use std::{ time::Duration, }; - #[derive(Clone)] /** Represents a blockchain node. @@ -73,11 +72,11 @@ impl DaemonAsync { } /// Get the channel configured for this DaemonAsync. - #[cfg(feature="grpc")] + #[cfg(feature = "grpc")] pub fn channel(&self) -> tonic::transport::Channel { self.state.transport_channel.clone() } - #[cfg(feature="rpc")] + #[cfg(feature = "rpc")] pub fn channel(&self) -> cosmrs::rpc::HttpClient { self.state.transport_channel.clone() } @@ -148,7 +147,9 @@ impl DaemonAsync { ) -> Result { let querier = CosmWasm::new(self.channel()); - let resp: Vec = querier.contract_state(contract_address, serde_json::to_vec(&query_msg)?).await?; + let resp: Vec = querier + .contract_state(contract_address, serde_json::to_vec(&query_msg)?) + .await?; Ok(from_str(from_utf8(&resp).unwrap())?) } diff --git a/cw-orch-daemon/src/error.rs b/cw-orch-daemon/src/error.rs index 756320a24..8f2170d1e 100644 --- a/cw-orch-daemon/src/error.rs +++ b/cw-orch-daemon/src/error.rs @@ -19,12 +19,12 @@ pub enum DaemonError { VarError(#[from] ::std::env::VarError), #[error(transparent)] AnyError(#[from] ::anyhow::Error), - - #[cfg(feature="grpc")] + + #[cfg(feature = "grpc")] #[error(transparent)] Status(#[from] ::tonic::Status), - #[cfg(feature="grpc")] + #[cfg(feature = "grpc")] #[error(transparent)] TransportError(#[from] ::tonic::transport::Error), @@ -104,6 +104,8 @@ pub enum DaemonError { NewNetwork(String), #[error("Can not connect to any grpc endpoint that was provided.")] CannotConnectGRPC, + #[error("Can not connect to any rpc endpoint that was provided.")] + CannotConnectRPC, #[error("tx failed: {reason} with code {code}")] TxFailed { code: usize, reason: String }, #[error("The list of grpc endpoints is empty")] diff --git a/cw-orch-daemon/src/lib.rs b/cw-orch-daemon/src/lib.rs index a4055a864..c663a1760 100644 --- a/cw-orch-daemon/src/lib.rs +++ b/cw-orch-daemon/src/lib.rs @@ -1,8 +1,7 @@ - //! `Daemon` and `DaemonAsync` execution environments. //! //! The `Daemon` type is a synchronous wrapper around the `DaemonAsync` type and can be used as a contract execution environment. -//! +//! #[cfg(all(feature = "rpc", feature = "grpc"))] compile_error!("feature \"rpc\" and feature \"grpc\" cannot be enabled at the same time"); @@ -25,23 +24,20 @@ pub mod live_mock; pub mod queriers; -#[cfg(feature="rpc")] +#[cfg(feature = "rpc")] pub mod rpc_channel; -#[cfg(feature="rpc")] +#[cfg(feature = "rpc")] pub use self::rpc_channel::*; -#[cfg(feature="grpc")] +#[cfg(feature = "grpc")] pub mod grpc_channel; -#[cfg(feature="grpc")] +#[cfg(feature = "grpc")] pub use self::grpc_channel::*; - mod traits; pub mod tx_builder; -pub use self::{ - builder::*, core::*, error::*, state::*, sync::*, traits::*, tx_resp::*, -}; +pub use self::{builder::*, core::*, error::*, state::*, sync::*, traits::*, tx_resp::*}; pub use cw_orch_networks::chain_info::*; pub use cw_orch_networks::networks; pub use sender::Wallet; diff --git a/cw-orch-daemon/src/live_mock.rs b/cw-orch-daemon/src/live_mock.rs index 9632d3526..3230663ab 100644 --- a/cw-orch-daemon/src/live_mock.rs +++ b/cw-orch-daemon/src/live_mock.rs @@ -2,7 +2,7 @@ //! It can be used to do chain-backed unit-testing. It can't be used for state-changing operations. use crate::create_transport_channel; -use crate::queriers::{DaemonQuerier, Staking, Bank, CosmWasm}; +use crate::queriers::{Bank, CosmWasm, DaemonQuerier, Staking}; use cosmwasm_std::Addr; use cosmwasm_std::AllBalanceResponse; use cosmwasm_std::BalanceResponse; @@ -52,9 +52,9 @@ pub fn mock_dependencies( /// Querier struct that fetches queries on-chain directly pub struct WasmMockQuerier { - #[cfg(feature="grpc")] + #[cfg(feature = "grpc")] channel: tonic::transport::Channel, - #[cfg(feature="rpc")] + #[cfg(feature = "rpc")] channel: cosmrs::rpc::HttpClient, runtime: Runtime, } @@ -205,9 +205,7 @@ impl WasmMockQuerier { pub fn new(chain: ChainData) -> Self { let rt = Runtime::new().unwrap(); - let channel = rt - .block_on(create_transport_channel(&chain)) - .unwrap(); + let channel = rt.block_on(create_transport_channel(&chain)).unwrap(); WasmMockQuerier { channel, diff --git a/cw-orch-daemon/src/proto/injective.rs b/cw-orch-daemon/src/proto/injective.rs index 60d60f2a8..98ac6b993 100644 --- a/cw-orch-daemon/src/proto/injective.rs +++ b/cw-orch-daemon/src/proto/injective.rs @@ -2,7 +2,10 @@ use crate::DaemonError; use cosmrs::tx::SignDoc; -use cosmrs::{proto::traits::TypeUrl, tx::Raw}; +use cosmrs::{ + proto::traits::{Message, Name}, + tx::Raw, +}; #[cfg(feature = "eth")] use crate::keys::private::PrivateKey; @@ -26,8 +29,9 @@ pub struct InjectivePubKey { pub key: Vec, } -impl TypeUrl for InjectivePubKey { - const TYPE_URL: &'static str = "/injective.crypto.v1beta1.ethsecp256k1.PubKey"; +impl Name for InjectivePubKey { + const NAME: &'static str = "PubKey"; + const PACKAGE: &'static str = "/injective.crypto.v1beta1.ethsecp256k1"; } pub trait InjectiveSigner { diff --git a/cw-orch-daemon/src/queriers.rs b/cw-orch-daemon/src/queriers.rs index 3c69ae828..f532b83b9 100644 --- a/cw-orch-daemon/src/queriers.rs +++ b/cw-orch-daemon/src/queriers.rs @@ -1,23 +1,19 @@ - - pub const MAX_TX_QUERY_RETRIES: usize = 50; -#[cfg(feature="rpc")] +#[cfg(feature = "rpc")] pub mod rpc; -#[cfg(feature="rpc")] +#[cfg(feature = "rpc")] pub use rpc::*; -#[cfg(feature="grpc")] +#[cfg(feature = "grpc")] pub mod grpc; -#[cfg(feature="grpc")] +#[cfg(feature = "grpc")] pub use grpc::*; - - /// Constructor for a querier over a given channel pub trait DaemonQuerier { /// Construct an new querier over a given channel - #[cfg(feature="rpc")] + #[cfg(feature = "rpc")] fn new(client: cosmrs::rpc::HttpClient) -> Self; - #[cfg(feature="grpc")] + #[cfg(feature = "grpc")] fn new(channel: tonic::transport::Channel) -> Self; -} \ No newline at end of file +} diff --git a/cw-orch-daemon/src/queriers/grpc/auth.rs b/cw-orch-daemon/src/queriers/grpc/auth.rs index b830f7f3b..25f33d70b 100644 --- a/cw-orch-daemon/src/queriers/grpc/auth.rs +++ b/cw-orch-daemon/src/queriers/grpc/auth.rs @@ -1,7 +1,7 @@ // Only a simple implementation to not overload the tx builder use tonic::transport::Channel; -use crate::{queriers::DaemonQuerier, cosmos_query, DaemonError}; +use crate::{cosmos_query, queriers::DaemonQuerier, DaemonError}; /// Queries for Cosmos Bank Module pub struct Auth { @@ -14,20 +14,17 @@ impl DaemonQuerier for Auth { } } - -impl Auth{ - +impl Auth { /// Query spendable balance for address - pub async fn account( - &self, - address: impl Into, - ) -> Result, DaemonError> { + pub async fn account(&self, address: impl Into) -> Result, DaemonError> { let resp = cosmos_query!( self, auth, account, - QueryAccountRequest { address: address.into() } + QueryAccountRequest { + address: address.into() + } ); Ok(resp.account.unwrap().value) } -} \ No newline at end of file +} diff --git a/cw-orch-daemon/src/queriers/grpc/mod.rs b/cw-orch-daemon/src/queriers/grpc/mod.rs index 95b06bd07..8b6931052 100644 --- a/cw-orch-daemon/src/queriers/grpc/mod.rs +++ b/cw-orch-daemon/src/queriers/grpc/mod.rs @@ -44,6 +44,7 @@ macro_rules! cosmos_query { }; } +mod auth; mod bank; mod cosmwasm; mod feegrant; @@ -51,18 +52,16 @@ mod gov; mod ibc; mod node; mod staking; -mod auth; mod tx; +pub use auth::Auth; pub use bank::Bank; pub use cosmwasm::CosmWasm; pub use feegrant::Feegrant; pub use ibc::Ibc; pub use node::Node; -pub use auth::Auth; pub use tx::Tx; // this two containt structs that are helpers for the queries pub use gov::*; pub use staking::*; - diff --git a/cw-orch-daemon/src/queriers/grpc/node.rs b/cw-orch-daemon/src/queriers/grpc/node.rs index bc04ce3ca..a6cc3c142 100644 --- a/cw-orch-daemon/src/queriers/grpc/node.rs +++ b/cw-orch-daemon/src/queriers/grpc/node.rs @@ -1,6 +1,8 @@ use std::{cmp::min, time::Duration}; -use crate::{cosmos_modules, error::DaemonError, tx_resp::CosmTxResponse, queriers::MAX_TX_QUERY_RETRIES}; +use crate::{ + cosmos_modules, error::DaemonError, queriers::MAX_TX_QUERY_RETRIES, tx_resp::CosmTxResponse, +}; use cosmrs::{ proto::cosmos::{base::query::v1beta1::PageRequest, tx::v1beta1::SimulateResponse}, diff --git a/cw-orch-daemon/src/queriers/grpc/tx.rs b/cw-orch-daemon/src/queriers/grpc/tx.rs index 6c9ead9e8..9853a16b3 100644 --- a/cw-orch-daemon/src/queriers/grpc/tx.rs +++ b/cw-orch-daemon/src/queriers/grpc/tx.rs @@ -1,10 +1,9 @@ // Only a simple implementation to not overload the tx builder -use cosmrs::{tx::Raw, proto::cosmos::base::abci::v1beta1::TxResponse}; +use cosmrs::{proto::cosmos::base::abci::v1beta1::TxResponse, tx::Raw}; use tonic::transport::Channel; -use crate::{queriers::DaemonQuerier, DaemonError, cosmos_modules}; - +use crate::{cosmos_modules, queriers::DaemonQuerier, DaemonError}; /// Queries for Cosmos Bank Module pub struct Tx { @@ -17,16 +16,11 @@ impl DaemonQuerier for Tx { } } -impl Tx{ - +impl Tx { /// Query spendable balance for address - pub async fn broadcast( - &self, - tx: Raw, - ) -> Result { - + pub async fn broadcast(&self, tx: Raw) -> Result { let mut client = - cosmos_modules::tx::service_client::ServiceClient::new(self.channel.clone()); + cosmos_modules::tx::service_client::ServiceClient::new(self.channel.clone()); let resp = client .broadcast_tx(cosmos_modules::tx::BroadcastTxRequest { @@ -36,7 +30,6 @@ impl Tx{ .await? .into_inner(); - Ok(resp.tx_response.unwrap()) } -} \ No newline at end of file +} diff --git a/cw-orch-daemon/src/queriers/rpc/auth.rs b/cw-orch-daemon/src/queriers/rpc/auth.rs index 4e53890ab..c09543449 100644 --- a/cw-orch-daemon/src/queriers/rpc/auth.rs +++ b/cw-orch-daemon/src/queriers/rpc/auth.rs @@ -2,10 +2,9 @@ use cosmrs::rpc::HttpClient; -use crate::{queriers::DaemonQuerier, cosmos_rpc_query, DaemonError}; +use crate::{cosmos_rpc_query, queriers::DaemonQuerier, DaemonError}; - -/// Queries for Cosmos Bank Module +/// Queries for Cosmos Auth Module pub struct Auth { client: HttpClient, } @@ -16,20 +15,18 @@ impl DaemonQuerier for Auth { } } -impl Auth{ - - /// Query spendable balance for address - pub async fn account( - &self, - address: impl Into, - ) -> Result, DaemonError> { +impl Auth { + /// Query the account + pub async fn account(&self, address: impl Into) -> Result, DaemonError> { let resp = cosmos_rpc_query!( self, auth, "/cosmos.auth.v1beta1.Query/Account", - QueryAccountRequest { address: address.into() }, + QueryAccountRequest { + address: address.into() + }, QueryAccountResponse, ); Ok(resp.account.unwrap().value) } -} \ No newline at end of file +} diff --git a/cw-orch-daemon/src/queriers/rpc/bank.rs b/cw-orch-daemon/src/queriers/rpc/bank.rs index daf13c534..d5251546a 100644 --- a/cw-orch-daemon/src/queriers/rpc/bank.rs +++ b/cw-orch-daemon/src/queriers/rpc/bank.rs @@ -1,6 +1,8 @@ -use crate::{cosmos_modules, error::DaemonError, cosmos_rpc_query, queriers::DaemonQuerier}; -use cosmrs::{proto::cosmos::base::{query::v1beta1::PageRequest, v1beta1::Coin}, rpc::HttpClient}; - +use crate::{cosmos_modules, cosmos_rpc_query, error::DaemonError, queriers::DaemonQuerier}; +use cosmrs::{ + proto::cosmos::base::{query::v1beta1::PageRequest, v1beta1::Coin}, + rpc::HttpClient, +}; /// Queries for Cosmos Bank Module pub struct Bank { @@ -100,8 +102,13 @@ impl Bank { /// Query params pub async fn params(&self) -> Result { - let params = - cosmos_rpc_query!(self, bank, "/cosmos.bank.v1beta1.Query/Params", QueryParamsRequest {}, QueryParamsResponse,); + let params = cosmos_rpc_query!( + self, + bank, + "/cosmos.bank.v1beta1.Query/Params", + QueryParamsRequest {}, + QueryParamsResponse, + ); Ok(params.params.unwrap()) } @@ -136,7 +143,7 @@ impl Bank { QueryDenomsMetadataRequest { pagination: pagination }, - QueryDenomsMetadataResponse, + QueryDenomsMetadataResponse, ); Ok(denoms_metadata.metadatas) } diff --git a/cw-orch-daemon/src/queriers/rpc/cosmwasm.rs b/cw-orch-daemon/src/queriers/rpc/cosmwasm.rs index 19949a201..6a4474717 100644 --- a/cw-orch-daemon/src/queriers/rpc/cosmwasm.rs +++ b/cw-orch-daemon/src/queriers/rpc/cosmwasm.rs @@ -1,7 +1,6 @@ -use crate::{cosmos_modules, error::DaemonError, cosmos_rpc_query, queriers::DaemonQuerier}; +use crate::{cosmos_modules, cosmos_rpc_query, error::DaemonError, queriers::DaemonQuerier}; use cosmrs::{proto::cosmos::base::query::v1beta1::PageRequest, rpc::HttpClient}; - /// Querier for the CosmWasm SDK module pub struct CosmWasm { client: HttpClient, @@ -16,7 +15,6 @@ impl DaemonQuerier for CosmWasm { impl CosmWasm { /// Query code_id by hash pub async fn code_id_hash(&self, code_id: u64) -> Result { - let resp = cosmos_rpc_query!( self, cosmwasm, @@ -35,7 +33,6 @@ impl CosmWasm { &self, address: impl Into, ) -> Result { - let resp = cosmos_rpc_query!( self, cosmwasm, @@ -56,7 +53,6 @@ impl CosmWasm { address: impl Into, pagination: Option, ) -> Result { - let resp = cosmos_rpc_query!( self, cosmwasm, @@ -77,7 +73,6 @@ impl CosmWasm { address: impl Into, query_data: Vec, ) -> Result, DaemonError> { - let resp = cosmos_rpc_query!( self, cosmwasm, @@ -88,7 +83,7 @@ impl CosmWasm { }, QuerySmartContractStateResponse, ); - + Ok(resp.data) } @@ -123,13 +118,12 @@ impl CosmWasm { QueryCodeRequest { code_id: code_id }, QueryCodeResponse, ); - + Ok(resp.code_info.unwrap()) } /// Query code bytes pub async fn code_data(&self, code_id: u64) -> Result, DaemonError> { - let resp = cosmos_rpc_query!( self, cosmwasm, @@ -137,7 +131,7 @@ impl CosmWasm { QueryCodeRequest { code_id: code_id }, QueryCodeResponse, ); - + Ok(resp.data) } @@ -146,12 +140,13 @@ impl CosmWasm { &self, pagination: Option, ) -> Result, DaemonError> { - let resp = cosmos_rpc_query!( self, cosmwasm, "/cosmwasm.wasm.v1.Query/Codes", - QueryCodesRequest { pagination: pagination }, + QueryCodesRequest { + pagination: pagination + }, QueryCodesResponse, ); @@ -197,7 +192,6 @@ impl CosmWasm { address: impl Into, query_data: Vec, ) -> Result { - let resp = cosmos_rpc_query!( self, cosmwasm, @@ -208,7 +202,7 @@ impl CosmWasm { }, QueryRawContractStateResponse, ); - + Ok(resp) } diff --git a/cw-orch-daemon/src/queriers/rpc/mod.rs b/cw-orch-daemon/src/queriers/rpc/mod.rs index 834900bd5..eaef0cd43 100644 --- a/cw-orch-daemon/src/queriers/rpc/mod.rs +++ b/cw-orch-daemon/src/queriers/rpc/mod.rs @@ -1,15 +1,15 @@ +mod auth; mod bank; mod cosmwasm; mod node; mod staking; -mod auth; mod tx; +pub use auth::Auth; pub use bank::Bank; pub use cosmwasm::CosmWasm; pub use node::Node; pub use staking::Staking; -pub use auth::Auth; pub use tx::Tx; // pub use feegrant::Feegrant; // pub use ibc::Ibc; @@ -31,7 +31,8 @@ macro_rules! cosmos_rpc_query { Some($type_url.to_string()), request.to_bytes()?, None, - true + // Don't request proof, we don't need it + false, ).await?; let decoded_response = $request_resp::decode(response.value.as_slice())?; ::log::trace!( @@ -43,4 +44,4 @@ macro_rules! cosmos_rpc_query { decoded_response } }; -} \ No newline at end of file +} diff --git a/cw-orch-daemon/src/queriers/rpc/node.rs b/cw-orch-daemon/src/queriers/rpc/node.rs index 59b725d34..54d40bbfa 100644 --- a/cw-orch-daemon/src/queriers/rpc/node.rs +++ b/cw-orch-daemon/src/queriers/rpc/node.rs @@ -1,16 +1,23 @@ use std::{cmp::min, time::Duration}; -use crate::{cosmos_modules, error::DaemonError, tx_resp::CosmTxResponse, queriers::{MAX_TX_QUERY_RETRIES, DaemonQuerier}, cosmos_rpc_query}; - +use cosmrs::tendermint::block::Height; +use cosmrs::tendermint::node; use cosmrs::{ - proto::cosmos::{base::query::v1beta1::PageRequest, tx::v1beta1::GetTxResponse}, - tendermint::{Block, Time}, rpc::{HttpClient, Client}, tx::MessageExt, + proto::cosmos::base::query::v1beta1::PageRequest, + rpc::{Client, HttpClient}, + tendermint::{Block, Time}, }; -use prost::Message; +use crate::{ + cosmos_modules, cosmos_rpc_query, + error::DaemonError, + queriers::{DaemonQuerier, MAX_TX_QUERY_RETRIES}, + tx_resp::CosmTxResponse, +}; /// Querier for the Tendermint node. /// Supports queries for block and tx information +/// @TODO: all tendermint queries should use the tendermint-rpc explicitly instead of hitting the tendermint node with the typed queries. pub struct Node { client: HttpClient, } @@ -21,27 +28,16 @@ impl DaemonQuerier for Node { } } - impl Node { /// Returns node info - pub async fn info( - &self, - ) -> Result { + pub async fn info(&self) -> Result { + let stats = self.client.status().await?; - let resp = cosmos_rpc_query!( - self, - tendermint, - "/cosmos.base.tendermint.v1beta1.Service/GetNodeInfo", - GetNodeInfoRequest {}, - GetNodeInfoResponse, - ); - - Ok(resp) + Ok(stats.node_info) } /// Queries node syncing pub async fn syncing(&self) -> Result { - let resp = cosmos_rpc_query!( self, tendermint, @@ -53,33 +49,18 @@ impl Node { Ok(resp.syncing) } - /// Returns latests block information + /// Returns latest block information pub async fn latest_block(&self) -> Result { + let resp = self.client.latest_block().await?; - let resp = cosmos_rpc_query!( - self, - tendermint, - "/cosmos.base.tendermint.v1beta1.Service/GetLatestBlock", - GetLatestBlockRequest {}, - GetLatestBlockResponse, - ); - - Ok(Block::try_from(resp.block.unwrap())?) + Ok(resp.block) } /// Returns block information fetched by height pub async fn block_by_height(&self, height: u64) -> Result { - let resp = cosmos_rpc_query!( - self, - tendermint, - "/cosmos.base.tendermint.v1beta1.Service/GetBlockByHeight", - GetBlockByHeightRequest { - height: height as i64, - }, - GetBlockByHeightResponse, - ); + let resp = self.client.block(Height::try_from(height).unwrap()).await?; - Ok(Block::try_from(resp.block.unwrap())?) + Ok(resp.block) } /// Return the average block time for the last 50 blocks or since inception @@ -124,7 +105,6 @@ impl Node { &self, pagination: Option, ) -> Result { - let resp = cosmos_rpc_query!( self, tendermint, @@ -144,12 +124,14 @@ impl Node { height: i64, pagination: Option, ) -> Result { - let resp = cosmos_rpc_query!( self, tendermint, "/cosmos.base.tendermint.v1beta1.Service/GetValidatorSetByHeight", - GetValidatorSetByHeightRequest { height: height, pagination: pagination }, + GetValidatorSetByHeightRequest { + height: height, + pagination: pagination + }, GetValidatorSetByHeightResponse, ); @@ -174,6 +156,7 @@ impl Node { /// Simulate TX pub async fn simulate_tx(&self, tx_bytes: Vec) -> Result { + log::debug!("Simulating transaction"); // We use this allow deprecated for the tx field of the simulate request (but we set it to None, so that's ok) #[allow(deprecated)] @@ -181,11 +164,16 @@ impl Node { self, tx, "/cosmos.tx.v1beta1.Service/Simulate", - SimulateRequest { tx: None, tx_bytes: tx_bytes }, + SimulateRequest { + tx: None, + tx_bytes: tx_bytes + }, SimulateResponse, ); let gas_used = resp.gas_info.unwrap().gas_used; + + log::debug!("Gas used in simulation: {:?}", gas_used); Ok(gas_used) } @@ -212,22 +200,17 @@ impl Node { hash: String, retries: usize, ) -> Result { - - let request = cosmos_modules::tx::GetTxRequest { hash: hash.clone() }; let mut block_speed = self.average_block_speed(Some(0.7)).await?; + let hexed_hash = hex::decode(hash.clone())?.try_into().unwrap(); + for _ in 0..retries { - let tx_response = self.client.abci_query( - Some("/cosmos.tx.v1beta1.Service/GetTx".to_string()), - request.to_bytes()?, - None, - true, - ).await?; - - match GetTxResponse::decode(tx_response.value.as_slice()) { + let tx_r = self.client.tx(hexed_hash, false).await; + + match tx_r { Ok(tx) => { log::debug!("TX found: {:?}", tx); - return Ok(tx.tx_response.unwrap().into()); + return Ok(tx.into()); } Err(err) => { // increase wait time diff --git a/cw-orch-daemon/src/queriers/rpc/staking.rs b/cw-orch-daemon/src/queriers/rpc/staking.rs index 6e34365e3..6bda5611d 100644 --- a/cw-orch-daemon/src/queriers/rpc/staking.rs +++ b/cw-orch-daemon/src/queriers/rpc/staking.rs @@ -1,4 +1,4 @@ -use crate::{cosmos_modules, error::DaemonError, cosmos_rpc_query}; +use crate::{cosmos_modules, cosmos_rpc_query, error::DaemonError}; use cosmrs::{proto::cosmos::base::query::v1beta1::PageRequest, rpc::HttpClient}; use crate::queriers::DaemonQuerier; @@ -255,8 +255,13 @@ impl Staking { /// Query the pool info pub async fn pool(&self) -> Result { - let pool = - cosmos_rpc_query!(self, staking, "/cosmos.staking.v1beta1.Query/Pool", QueryPoolRequest {}, QueryPoolResponse,); + let pool = cosmos_rpc_query!( + self, + staking, + "/cosmos.staking.v1beta1.Query/Pool", + QueryPoolRequest {}, + QueryPoolResponse, + ); Ok(pool) } @@ -264,8 +269,13 @@ impl Staking { pub async fn params( &self, ) -> Result { - let params = - cosmos_rpc_query!(self, staking, "/cosmos.staking.v1beta1.Query/Params", QueryParamsRequest {}, QueryParamsResponse,); + let params = cosmos_rpc_query!( + self, + staking, + "/cosmos.staking.v1beta1.Query/Params", + QueryParamsRequest {}, + QueryParamsResponse, + ); Ok(params) } } diff --git a/cw-orch-daemon/src/queriers/rpc/tx.rs b/cw-orch-daemon/src/queriers/rpc/tx.rs index a08b22ce1..aa4e68fad 100644 --- a/cw-orch-daemon/src/queriers/rpc/tx.rs +++ b/cw-orch-daemon/src/queriers/rpc/tx.rs @@ -1,9 +1,9 @@ // Only a simple implementation to not overload the tx builder -use cosmrs::{rpc::HttpClient, tx::Raw, proto::cosmos::base::abci::v1beta1::TxResponse}; +use cosmrs::{proto::cosmos::base::abci::v1beta1::TxResponse, rpc::HttpClient, tx::Raw}; use crate::{queriers::DaemonQuerier, DaemonError}; - use cosmrs::rpc::Client; +use cosmrs::rpc::Client; /// Queries for Cosmos Bank Module pub struct Tx { @@ -16,29 +16,26 @@ impl DaemonQuerier for Tx { } } -impl Tx{ +impl Tx { /// Query spendable balance for address - pub async fn broadcast( - &self, - tx: Raw, - ) -> Result { + pub async fn broadcast(&self, tx: Raw) -> Result { let resp = self.client.broadcast_tx_commit(tx.to_bytes()?).await?; let check = resp.check_tx; - Ok(TxResponse { - height: resp.height.into(), - txhash: resp.hash.to_string(), - codespace: check.codespace, - code: check.code.into(), - data: "".to_string(), - raw_log: check.log, - logs: vec![], - info: check.info, - gas_wanted: check.gas_wanted, - gas_used: check.gas_used, + Ok(TxResponse { + height: resp.height.into(), + txhash: resp.hash.to_string(), + codespace: check.codespace, + code: check.code.into(), + data: "".to_string(), + raw_log: check.log, + logs: vec![], + info: check.info, + gas_wanted: check.gas_wanted, + gas_used: check.gas_used, tx: None, timestamp: "".to_string(), - events: vec![] + events: vec![], }) } -} \ No newline at end of file +} diff --git a/cw-orch-daemon/src/rpc_channel.rs b/cw-orch-daemon/src/rpc_channel.rs index a026afa55..f84705c33 100644 --- a/cw-orch-daemon/src/rpc_channel.rs +++ b/cw-orch-daemon/src/rpc_channel.rs @@ -1,4 +1,4 @@ -use cosmrs::rpc::{HttpClient, Client}; +use cosmrs::rpc::{Client, HttpClient}; use ibc_chain_registry::chain::Rpc; use ibc_relayer_types::core::ics24_host::identifier::ChainId; @@ -22,13 +22,11 @@ impl RpcChannel { let client = if maybe_client.is_ok() { maybe_client? } else { - continue + continue; }; // get client information for verification down below - let node_info = client - .status().await? - .node_info; + let node_info = client.status().await?.node_info; // local juno does not return a proper ChainId with epoch format if ChainId::is_epoch_format(node_info.network.as_str()) { @@ -49,7 +47,7 @@ impl RpcChannel { // we could not get any succesful connections if successful_connections.is_empty() { - return Err(DaemonError::CannotConnectGRPC); + return Err(DaemonError::CannotConnectRPC); } Ok(successful_connections.pop().unwrap()) diff --git a/cw-orch-daemon/src/sender.rs b/cw-orch-daemon/src/sender.rs index 8fe40cf55..217d06c40 100644 --- a/cw-orch-daemon/src/sender.rs +++ b/cw-orch-daemon/src/sender.rs @@ -1,4 +1,8 @@ -use crate::{networks::ChainKind, proto::injective::ETHEREUM_COIN_TYPE, queriers::{Auth, Tx}}; +use crate::{ + networks::ChainKind, + proto::injective::ETHEREUM_COIN_TYPE, + queriers::{Auth, Tx}, +}; use super::{ cosmos_modules::{self, auth::BaseAccount}, @@ -79,11 +83,11 @@ impl Sender { SigningKey::from_slice(&self.private_key.raw_key()).unwrap() } - #[cfg(feature="grpc")] + #[cfg(feature = "grpc")] pub fn channel(&self) -> tonic::transport::Channel { self.daemon_state.transport_channel.clone() } - #[cfg(feature="rpc")] + #[cfg(feature = "rpc")] pub fn channel(&self) -> cosmrs::rpc::HttpClient { self.daemon_state.transport_channel.clone() } @@ -165,6 +169,8 @@ impl Sender { let tx = tx_builder.build(self).await?; + log::trace!("raw tx: {:?}", tx); + let mut tx_response = self.broadcast_tx(tx).await?; log::debug!("tx broadcast response: {:?}", tx_response); diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index bd00f271a..ad7302130 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -1,9 +1,9 @@ use super::error::DaemonError; use crate::networks::ChainKind; -#[cfg(feature="grpc")] +#[cfg(feature = "grpc")] use crate::grpc_channel::GrpcChannel; -#[cfg(feature="rpc")] +#[cfg(feature = "rpc")] use crate::rpc_channel::RpcChannel; use cosmwasm_std::Addr; @@ -25,21 +25,25 @@ pub struct DaemonState { /// Deployment identifier pub deployment_id: String, /// Transport channel - #[cfg(feature="grpc")] + #[cfg(feature = "grpc")] pub transport_channel: tonic::transport::Channel, - #[cfg(feature="rpc")] + #[cfg(feature = "rpc")] pub transport_channel: cosmrs::rpc::HttpClient, /// Information about the chain pub chain_data: ChainData, } -#[cfg(feature="grpc")] -pub async fn create_transport_channel(chain_data: &ChainData) -> Result{ +#[cfg(feature = "grpc")] +pub async fn create_transport_channel( + chain_data: &ChainData, +) -> Result { GrpcChannel::connect(&chain_data.apis.grpc, &chain_data.chain_id).await } -#[cfg(feature="rpc")] -pub async fn create_transport_channel(chain_data: &ChainData) -> Result{ +#[cfg(feature = "rpc")] +pub async fn create_transport_channel( + chain_data: &ChainData, +) -> Result { RpcChannel::connect(&chain_data.apis.rpc, &chain_data.chain_id).await } diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index 4b5a6e10f..3c6edd682 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -61,11 +61,11 @@ impl Daemon { } /// Get the channel configured for this Daemon - #[cfg(feature="grpc")] + #[cfg(feature = "grpc")] pub fn channel(&self) -> tonic::transport::Channel { self.daemon.state.transport_channel.clone() } - #[cfg(feature="rpc")] + #[cfg(feature = "rpc")] pub fn channel(&self) -> cosmrs::rpc::HttpClient { self.daemon.state.transport_channel.clone() } diff --git a/cw-orch-daemon/src/tx_builder.rs b/cw-orch-daemon/src/tx_builder.rs index 5d1cb0a19..7a471e39c 100644 --- a/cw-orch-daemon/src/tx_builder.rs +++ b/cw-orch-daemon/src/tx_builder.rs @@ -67,8 +67,17 @@ impl TxBuilder { } pub(crate) fn build_fee(amount: impl Into, denom: &str, gas_limit: u64) -> Fee { - let fee = Coin::new(amount.into(), denom).unwrap(); - Fee::from_amount_and_gas(fee, gas_limit) + // Ensure that the fee is not 0, which can be invalid + let fee = match amount.into() { + 0 => vec![], + x => vec![Coin::new(x, denom).unwrap()], + }; + Fee { + gas_limit, + amount: fee, + payer: None, + granter: None, + } } /// Builds the raw tx with a given body and fee and signs it. @@ -81,6 +90,8 @@ impl TxBuilder { .. } = wallet.base_account().await?; + log::trace!("Retrieved base account"); + // overwrite sequence if set (can be used for concurrent txs) let sequence = self.sequence.unwrap_or(sequence); @@ -95,6 +106,7 @@ impl TxBuilder { ); (fee, gas_limit) } else { + log::debug!("Calculating new fee and gas limits"); let sim_gas_used = wallet .calculate_gas(&self.body, sequence, account_number) .await?; diff --git a/cw-orch-daemon/src/tx_resp.rs b/cw-orch-daemon/src/tx_resp.rs index 9a52ef6d9..0142a6bbf 100644 --- a/cw-orch-daemon/src/tx_resp.rs +++ b/cw-orch-daemon/src/tx_resp.rs @@ -1,15 +1,19 @@ +use chrono::{DateTime, NaiveDateTime, TimeZone, Utc}; +use cosmrs::rpc::endpoint; +use cosmrs::tendermint::abci::Code; +use cosmrs::tendermint::Hash; +use cosmwasm_std::{to_binary, Binary, StdError, StdResult}; +use serde::{Deserialize, Serialize}; + +use cw_orch_core::environment::IndexResponse; + use super::{ cosmos_modules::{ abci::{AbciMessageLog, Attribute, StringEvent, TxResponse}, - tendermint_abci::Event, + tendermint_abci::{Event, EventAttribute}, }, error::DaemonError, }; -use chrono::{DateTime, NaiveDateTime, TimeZone, Utc}; - -use cosmwasm_std::{to_binary, Binary, StdError, StdResult}; -use cw_orch_core::environment::IndexResponse; -use serde::{Deserialize, Serialize}; const FORMAT: &str = "%Y-%m-%dT%H:%M:%S%.f"; const FORMAT_TZ_SUPPLIED: &str = "%Y-%m-%dT%H:%M:%S.%f%:z"; @@ -124,7 +128,63 @@ impl From for CosmTxResponse { gas_wanted: tx.gas_wanted as u64, gas_used: tx.gas_used as u64, timestamp: parse_timestamp(tx.timestamp).unwrap(), - events: tx.events, + events: tx + .events + .into_iter() + .map(|e| Event { + r#type: e.r#type, + attributes: e + .attributes + .into_iter() + .map(|a| EventAttribute { + key: String::from_utf8(a.key.clone().to_vec()).unwrap(), + value: String::from_utf8(a.value.clone().to_vec()).unwrap(), + index: false, + }) + .collect(), + }) + .collect(), + } + } +} + +impl From for CosmTxResponse { + fn from(tx: endpoint::tx::Response) -> Self { + Self { + height: tx.height.value(), + txhash: match tx.hash { + Hash::None => "".to_string(), + Hash::Sha256(x) => hex::encode(x), + }, + codespace: tx.tx_result.codespace, + code: match tx.tx_result.code { + Code::Ok => 0, + Code::Err(code) => code.get() as usize, + }, + data: String::from_utf8(tx.tx_result.data.clone().to_vec()).unwrap(), + raw_log: tx.tx_result.log, + logs: vec![], + info: tx.tx_result.info, + gas_wanted: tx.tx_result.gas_wanted as u64, + gas_used: tx.tx_result.gas_used as u64, + timestamp: DateTime::default(), + events: tx + .tx_result + .events + .into_iter() + .map(|e| Event { + r#type: e.kind, + attributes: e + .attributes + .into_iter() + .map(|a| EventAttribute { + key: a.key, + value: a.value, + index: false, + }) + .collect(), + }) + .collect(), } } } diff --git a/cw-orch-daemon/tests/querier.rs b/cw-orch-daemon/tests/querier.rs index 54c511747..0cf615cdd 100644 --- a/cw-orch-daemon/tests/querier.rs +++ b/cw-orch-daemon/tests/querier.rs @@ -24,8 +24,7 @@ mod queriers { AccountId, Denom, }; - - #[cfg(feature="grpc")] + #[cfg(feature = "grpc")] pub async fn build_channel() -> tonic::transport::Channel { use ibc_chain_registry::chain::Grpc; @@ -47,7 +46,7 @@ mod queriers { channel.unwrap() } - #[cfg(feature="rpc")] + #[cfg(feature = "rpc")] pub async fn build_channel() -> cosmrs::rpc::HttpClient { use ibc_chain_registry::chain::Rpc; diff --git a/packages/cw-orch-networks/src/chain_info.rs b/packages/cw-orch-networks/src/chain_info.rs index de60fb964..26e867cab 100644 --- a/packages/cw-orch-networks/src/chain_info.rs +++ b/packages/cw-orch-networks/src/chain_info.rs @@ -1,6 +1,8 @@ use serde::{Deserialize, Serialize}; -use ibc_chain_registry::chain::{Apis, ChainData as RegistryChainInfo, FeeToken, FeeTokens, Grpc, Rpc}; +use ibc_chain_registry::chain::{ + Apis, ChainData as RegistryChainInfo, FeeToken, FeeTokens, Grpc, Rpc, +}; #[allow(clippy::from_over_into)] impl Into for ChainInfo<'_> { diff --git a/packages/cw-orch-networks/src/networks/mod.rs b/packages/cw-orch-networks/src/networks/mod.rs index 91e4e278f..42641c434 100644 --- a/packages/cw-orch-networks/src/networks/mod.rs +++ b/packages/cw-orch-networks/src/networks/mod.rs @@ -11,6 +11,7 @@ pub mod neutron; pub mod osmosis; pub mod sei; pub mod terra; +pub mod wasm; pub use crate::chain_info::{ChainInfo, ChainKind, NetworkInfo}; pub use archway::{ARCHWAY_1, CONSTANTINE_3}; @@ -22,6 +23,7 @@ pub use neutron::{LOCAL_NEUTRON, NEUTRON_1, PION_1}; pub use osmosis::{LOCAL_OSMO, OSMO_5}; pub use sei::{ATLANTIC_2, LOCAL_SEI, SEI_DEVNET_3}; pub use terra::{LOCAL_TERRA, PHOENIX_1, PISCO_1}; +pub use wasm::LOCAL_WASMD; /// A helper function to retrieve a [`ChainInfo`] struct for a given chain-id. /// diff --git a/packages/cw-orch-networks/src/networks/wasm.rs b/packages/cw-orch-networks/src/networks/wasm.rs new file mode 100644 index 000000000..9a9fa1b32 --- /dev/null +++ b/packages/cw-orch-networks/src/networks/wasm.rs @@ -0,0 +1,22 @@ +use crate::chain_info::{ChainInfo, ChainKind, NetworkInfo}; + +// ANCHOR: wasmd +/// Wasmd local network +pub const WASMD_NETWORK: NetworkInfo = NetworkInfo { + id: "wasm", + pub_address_prefix: "wasm", + coin_type: 118u32, +}; + +pub const LOCAL_WASMD: ChainInfo = ChainInfo { + kind: ChainKind::Local, + chain_id: "testing", + gas_denom: "ucosm", + gas_price: 0.1, + grpc_urls: &["http://localhost:9090"], + rpc_urls: &["http://localhost:26657"], + network_info: WASMD_NETWORK, + lcd_url: None, + fcd_url: None, +}; +// ANCHOR_END: wasmd From 5c7f603b67c21b1b64513b3c17caa3e73d667972 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Mon, 15 Jul 2024 18:21:04 +0200 Subject: [PATCH 6/8] add rpc url to chaininfobase --- packages/cw-orch-core/src/environment/chain_info.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/cw-orch-core/src/environment/chain_info.rs b/packages/cw-orch-core/src/environment/chain_info.rs index 5b86c75ec..ccf422fbf 100644 --- a/packages/cw-orch-core/src/environment/chain_info.rs +++ b/packages/cw-orch-core/src/environment/chain_info.rs @@ -21,6 +21,8 @@ pub struct ChainInfoBase, StringArrayType: AsRef<[Strin pub gas_price: f64, /// gRPC urls, used to attempt connection pub grpc_urls: StringArrayType, + /// RPC urls, used to attempt connection + pub rpc_urls: StringArrayType, /// Optional urls for custom functionality pub lcd_url: Option, /// Optional urls for custom functionality @@ -49,6 +51,7 @@ impl From for ChainInfoOwned { gas_denom: value.gas_denom.to_string(), gas_price: value.gas_price, grpc_urls: value.grpc_urls.iter().map(|url| url.to_string()).collect(), + rpc_urls: value.rpc_urls.iter().map(|url| url.to_string()).collect(), lcd_url: value.lcd_url.map(ToString::to_string), fcd_url: value.fcd_url.map(ToString::to_string), network_info: value.network_info.into(), From 1c06b2e1f93b8c7a196b144659acd90476f8dff3 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Tue, 16 Jul 2024 13:43:43 +0200 Subject: [PATCH 7/8] move around implementations --- cw-orch-daemon/src/core.rs | 6 +- cw-orch-daemon/src/grpc_channel.rs | 144 ----- cw-orch-daemon/src/lib.rs | 13 +- cw-orch-daemon/src/live_mock.rs | 3 +- cw-orch-daemon/src/proto/injective.rs | 1 - cw-orch-daemon/src/queriers.rs | 26 +- cw-orch-daemon/src/queriers/clients/auth.rs | 32 ++ .../src/queriers/{grpc => clients}/bank.rs | 0 .../queriers/{grpc => clients}/cosmwasm.rs | 0 .../queriers/{grpc => clients}/feegrant.rs | 0 .../src/queriers/{grpc => clients}/gov.rs | 0 .../src/queriers/{grpc => clients}/ibc.rs | 0 .../src/queriers/{grpc => clients}/mod.rs | 2 +- .../src/queriers/{grpc => clients}/node.rs | 0 .../src/queriers/{grpc => clients}/staking.rs | 0 .../src/queriers/{grpc => clients}/tx.rs | 0 cw-orch-daemon/src/queriers/grpc.rs | 21 + .../src/queriers/{grpc => grpc_old}/auth.rs | 0 cw-orch-daemon/src/queriers/grpc_old/bank.rs | 196 +++++++ .../src/queriers/grpc_old/cosmwasm.rs | 325 +++++++++++ .../src/queriers/grpc_old/feegrant.rs | 78 +++ cw-orch-daemon/src/queriers/grpc_old/gov.rs | 199 +++++++ cw-orch-daemon/src/queriers/grpc_old/ibc.rs | 537 ++++++++++++++++++ cw-orch-daemon/src/queriers/grpc_old/mod.rs | 45 ++ cw-orch-daemon/src/queriers/grpc_old/node.rs | 427 ++++++++++++++ .../src/queriers/grpc_old/staking.rs | 347 +++++++++++ cw-orch-daemon/src/queriers/grpc_old/tx.rs | 35 ++ .../src/queriers/{rpc/mod.rs => rpc.rs} | 15 - .../src/queriers/{rpc => rpc_old}/auth.rs | 0 .../src/queriers/{rpc => rpc_old}/bank.rs | 0 .../src/queriers/{rpc => rpc_old}/cosmwasm.rs | 0 cw-orch-daemon/src/queriers/rpc_old/mod.rs | 15 + .../src/queriers/{rpc => rpc_old}/node.rs | 0 .../src/queriers/{rpc => rpc_old}/staking.rs | 0 .../src/queriers/{rpc => rpc_old}/tx.rs | 0 cw-orch-daemon/src/rpc_channel.rs | 101 ---- cw-orch-daemon/src/senders/client.rs | 294 ++++++++++ cw-orch-daemon/src/senders/cosmos.rs | 11 +- cw-orch-daemon/src/senders/mod.rs | 2 + cw-orch-daemon/src/senders/query_only.rs | 10 +- cw-orch-daemon/src/state.rs | 4 +- packages/cw-orch-networks/src/lib.rs | 22 + .../cw-orch-networks/src/networks/archway.rs | 2 +- .../cw-orch-networks/src/networks/doravota.rs | 2 + .../src/networks/injective.rs | 2 +- .../cw-orch-networks/src/networks/kujira.rs | 2 +- .../src/networks/landslide.rs | 1 + .../cw-orch-networks/src/networks/migaloo.rs | 2 +- packages/cw-orch-networks/src/networks/mod.rs | 20 +- .../cw-orch-networks/src/networks/neutron.rs | 2 +- .../cw-orch-networks/src/networks/nibiru.rs | 1 + .../cw-orch-networks/src/networks/osmosis.rs | 2 +- .../cw-orch-networks/src/networks/rollkit.rs | 2 + packages/cw-orch-networks/src/networks/sei.rs | 3 +- .../cw-orch-networks/src/networks/wasm.rs | 4 +- .../cw-orch-networks/src/networks/xion.rs | 1 + 56 files changed, 2626 insertions(+), 331 deletions(-) delete mode 100644 cw-orch-daemon/src/grpc_channel.rs create mode 100644 cw-orch-daemon/src/queriers/clients/auth.rs rename cw-orch-daemon/src/queriers/{grpc => clients}/bank.rs (100%) rename cw-orch-daemon/src/queriers/{grpc => clients}/cosmwasm.rs (100%) rename cw-orch-daemon/src/queriers/{grpc => clients}/feegrant.rs (100%) rename cw-orch-daemon/src/queriers/{grpc => clients}/gov.rs (100%) rename cw-orch-daemon/src/queriers/{grpc => clients}/ibc.rs (100%) rename cw-orch-daemon/src/queriers/{grpc => clients}/mod.rs (97%) rename cw-orch-daemon/src/queriers/{grpc => clients}/node.rs (100%) rename cw-orch-daemon/src/queriers/{grpc => clients}/staking.rs (100%) rename cw-orch-daemon/src/queriers/{grpc => clients}/tx.rs (100%) create mode 100644 cw-orch-daemon/src/queriers/grpc.rs rename cw-orch-daemon/src/queriers/{grpc => grpc_old}/auth.rs (100%) create mode 100644 cw-orch-daemon/src/queriers/grpc_old/bank.rs create mode 100644 cw-orch-daemon/src/queriers/grpc_old/cosmwasm.rs create mode 100644 cw-orch-daemon/src/queriers/grpc_old/feegrant.rs create mode 100644 cw-orch-daemon/src/queriers/grpc_old/gov.rs create mode 100644 cw-orch-daemon/src/queriers/grpc_old/ibc.rs create mode 100644 cw-orch-daemon/src/queriers/grpc_old/mod.rs create mode 100644 cw-orch-daemon/src/queriers/grpc_old/node.rs create mode 100644 cw-orch-daemon/src/queriers/grpc_old/staking.rs create mode 100644 cw-orch-daemon/src/queriers/grpc_old/tx.rs rename cw-orch-daemon/src/queriers/{rpc/mod.rs => rpc.rs} (80%) rename cw-orch-daemon/src/queriers/{rpc => rpc_old}/auth.rs (100%) rename cw-orch-daemon/src/queriers/{rpc => rpc_old}/bank.rs (100%) rename cw-orch-daemon/src/queriers/{rpc => rpc_old}/cosmwasm.rs (100%) create mode 100644 cw-orch-daemon/src/queriers/rpc_old/mod.rs rename cw-orch-daemon/src/queriers/{rpc => rpc_old}/node.rs (100%) rename cw-orch-daemon/src/queriers/{rpc => rpc_old}/staking.rs (100%) rename cw-orch-daemon/src/queriers/{rpc => rpc_old}/tx.rs (100%) delete mode 100644 cw-orch-daemon/src/rpc_channel.rs create mode 100644 cw-orch-daemon/src/senders/client.rs diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index 0bb0d22e6..a9576e55c 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -1,5 +1,5 @@ use crate::{ - queriers::CosmWasm, + queriers::{CosmWasm, CosmWasmBase}, senders::{builder::SenderBuilder, query::QuerySender}, DaemonAsyncBuilder, DaemonState, }; @@ -149,10 +149,10 @@ impl DaemonAsyncBase { query_msg: &Q, contract_address: &Addr, ) -> Result { - let querier = CosmWasm::new(self.channel()); + let querier = CosmWasmBase::::new_async(self.channel()); let resp: Vec = querier - .contract_state(contract_address, serde_json::to_vec(&query_msg)?) + ._contract_state(contract_address, serde_json::to_vec(&query_msg)?) .await?; Ok(from_str(from_utf8(&resp).unwrap())?) diff --git a/cw-orch-daemon/src/grpc_channel.rs b/cw-orch-daemon/src/grpc_channel.rs deleted file mode 100644 index 5a3837ff0..000000000 --- a/cw-orch-daemon/src/grpc_channel.rs +++ /dev/null @@ -1,144 +0,0 @@ -use cosmrs::proto::cosmos::base::tendermint::v1beta1::{ - service_client::ServiceClient, GetNodeInfoRequest, -}; -use cw_orch_core::{environment::ChainInfoOwned, log::connectivity_target}; -use tonic::transport::{Channel, ClientTlsConfig}; - -use super::error::DaemonError; - -/// A helper for constructing a gRPC channel -pub struct GrpcChannel {} - -impl GrpcChannel { - /// Connect to any of the provided gRPC endpoints - pub async fn connect(grpc: &[String], chain_id: &str) -> Result { - if grpc.is_empty() { - return Err(DaemonError::GRPCListIsEmpty); - } - - let mut successful_connections = vec![]; - - for address in grpc.iter() { - log::debug!(target: &connectivity_target(), "Trying to connect to endpoint: {}", address); - - // get grpc endpoint - let endpoint = Channel::builder(address.clone().try_into().unwrap()); - - // try to connect to grpc endpoint - let maybe_client = ServiceClient::connect(endpoint.clone()).await; - - // connection succeeded - let mut client = if maybe_client.is_ok() { - maybe_client? - } else { - log::warn!( - "Cannot connect to gRPC endpoint: {}, {:?}", - address, - maybe_client.unwrap_err() - ); - - // try HTTPS approach - // https://github.com/hyperium/tonic/issues/363#issuecomment-638545965 - if !(address.contains("https") || address.contains("443")) { - continue; - }; - - log::debug!(target: &connectivity_target(), "Attempting to connect with TLS"); - - // re attempt to connect - let endpoint = endpoint.clone().tls_config(ClientTlsConfig::new())?; - let maybe_client = ServiceClient::connect(endpoint.clone()).await; - - // connection still fails - if maybe_client.is_err() { - log::warn!( - "Cannot connect to gRPC endpoint: {}, {:?}", - address, - maybe_client.unwrap_err() - ); - continue; - }; - - maybe_client? - }; - - // get client information for verification down below - let node_info = client - .get_node_info(GetNodeInfoRequest {}) - .await? - .into_inner(); - - // local juno does not return a proper ChainId with epoch format - // verify we are connected to the expected network - if node_info.default_node_info.as_ref().unwrap().network != chain_id { - log::error!( - "Network mismatch: connection:{} != config:{}", - node_info.default_node_info.as_ref().unwrap().network, - chain_id - ); - continue; - } - - // add endpoint to succesful connections - successful_connections.push(endpoint.connect().await?) - } - - // we could not get any succesful connections - if successful_connections.is_empty() { - return Err(DaemonError::CannotConnectGRPC); - } - - Ok(successful_connections.pop().unwrap()) - } - - /// Create a gRPC channel from the chain info - pub async fn from_chain_info(chain_info: &ChainInfoOwned) -> Result { - GrpcChannel::connect(&chain_info.grpc_urls, &chain_info.chain_id).await - } -} - -#[cfg(test)] -mod tests { - /* - This test asserts breaking issues around the GRPC connection - */ - - use crate::DaemonAsync; - use speculoos::prelude::*; - - #[tokio::test] - #[serial_test::serial] - async fn no_connection() { - let mut chain = cw_orch_daemon::networks::LOCAL_JUNO; - let grpcs = &["https://127.0.0.1:99999"]; - chain.grpc_urls = grpcs; - - let build_res = DaemonAsync::builder(chain) - .deployment_id("v0.1.0") - .build_sender(()) - .await; - - asserting!("there is no GRPC connection") - .that(&build_res.err().unwrap().to_string()) - .is_equal_to(String::from( - "Can not connect to any grpc endpoint that was provided.", - )) - } - - #[tokio::test] - #[serial_test::serial] - async fn network_grpcs_list_is_empty() { - let mut chain = cw_orch_daemon::networks::LOCAL_JUNO; - let grpcs = &[]; - chain.grpc_urls = grpcs; - - let build_res = DaemonAsync::builder(chain) - .deployment_id("v0.1.0") - .build_sender(()) - .await; - - asserting!("GRPC list is empty") - .that(&build_res.err().unwrap().to_string()) - .is_equal_to(String::from("The list of grpc endpoints is empty")) - } -} diff --git a/cw-orch-daemon/src/lib.rs b/cw-orch-daemon/src/lib.rs index c824afa56..9b05dae9b 100644 --- a/cw-orch-daemon/src/lib.rs +++ b/cw-orch-daemon/src/lib.rs @@ -24,18 +24,7 @@ mod state; mod sync; mod tx_resp; -#[cfg(feature = "rpc")] -pub mod rpc_channel; -#[cfg(feature = "rpc")] -pub use self::rpc_channel::*; - -#[cfg(feature = "grpc")] -pub mod grpc_channel; -#[cfg(feature = "grpc")] -pub use self::grpc_channel::*; - - -pub use self::{builder::*, channel::*, core::*, error::*, state::*, sync::*, tx_resp::*}; +pub use self::{builder::*, core::*, error::*, state::*, sync::*, tx_resp::*}; pub use cw_orch_networks::networks; pub use senders::{query::QuerySender, tx::TxSender, CosmosOptions, Wallet}; pub use tx_builder::TxBuilder; diff --git a/cw-orch-daemon/src/live_mock.rs b/cw-orch-daemon/src/live_mock.rs index 5fffc935d..f424541b3 100644 --- a/cw-orch-daemon/src/live_mock.rs +++ b/cw-orch-daemon/src/live_mock.rs @@ -13,7 +13,6 @@ use cosmwasm_std::Binary; use cosmwasm_std::Delegation; use cosmwasm_std::Empty; use cosmwasm_std::StakingQuery; -use ibc_chain_registry::chain::ChainData; use tokio::runtime::Runtime; use cosmwasm_std::{ @@ -190,7 +189,7 @@ impl WasmMockQuerier { /// Creates a querier from chain information pub fn new(chain: ChainInfoOwned) -> Self { let channel = RUNTIME - .block_on(GrpcChannel::connect( + .block_on(CosmosClient::connect( &chain.grpc_urls, chain.chain_id.as_str(), )) diff --git a/cw-orch-daemon/src/proto/injective.rs b/cw-orch-daemon/src/proto/injective.rs index 82738af23..b2dd1e2bf 100644 --- a/cw-orch-daemon/src/proto/injective.rs +++ b/cw-orch-daemon/src/proto/injective.rs @@ -5,7 +5,6 @@ use cosmrs::tx::Raw; use cosmrs::tx::SignDoc; use cosmrs::{ proto::traits::{Message, Name}, - tx::Raw, }; #[cfg(feature = "eth")] diff --git a/cw-orch-daemon/src/queriers.rs b/cw-orch-daemon/src/queriers.rs index 8257427e2..b5c436bd6 100644 --- a/cw-orch-daemon/src/queriers.rs +++ b/cw-orch-daemon/src/queriers.rs @@ -32,6 +32,8 @@ pub mod grpc; #[cfg(feature = "grpc")] pub use grpc::*; +pub mod clients; + /// Constructor for a querier over a given channel pub trait DaemonQuerier { /// Construct an new querier over a given channel @@ -64,21 +66,21 @@ macro_rules! cosmos_query { } mod authz; -mod bank; -mod cosmwasm; mod env; -mod feegrant; -mod gov; -mod ibc; -mod node; -mod staking; +// mod bank; +// mod cosmwasm; +// mod feegrant; +// mod gov; +// mod ibc; +// mod node; +// mod staking; pub use authz::Authz; -pub use bank::{cosmrs_to_cosmwasm_coins, Bank}; -pub use cosmwasm::{CosmWasm, CosmWasmBase}; -pub use feegrant::FeeGrant; -pub use ibc::Ibc; -pub use node::Node; +// pub use bank::{cosmrs_to_cosmwasm_coins, Bank}; +// pub use cosmwasm::{CosmWasm, CosmWasmBase}; +// pub use feegrant::FeeGrant; +// pub use ibc::Ibc; +// pub use node::Node; // this two containt structs that are helpers for the queries pub use gov::*; diff --git a/cw-orch-daemon/src/queriers/clients/auth.rs b/cw-orch-daemon/src/queriers/clients/auth.rs new file mode 100644 index 000000000..e9915a204 --- /dev/null +++ b/cw-orch-daemon/src/queriers/clients/auth.rs @@ -0,0 +1,32 @@ +// Only a simple implementation to not overload the tx builder +use tonic::transport::Channel; + +use crate::{cosmos_query, queriers::DaemonQuerier, DaemonError}; + +/// Queries for Cosmos Bank Module +pub struct Auth { + channel: Channel, +} + +impl DaemonQuerier for Auth { + fn new(channel: Channel) -> Self { + Self { channel } + } +} + +impl Auth { + /// Query spendable balance for address + pub async fn account(&self, address: impl Into) -> Result, DaemonError> { + + + let resp = cosmos_query!( + self, + auth, + account, + QueryAccountRequest { + address: address.into() + } + ); + Ok(resp.account.unwrap().value) + } +} diff --git a/cw-orch-daemon/src/queriers/grpc/bank.rs b/cw-orch-daemon/src/queriers/clients/bank.rs similarity index 100% rename from cw-orch-daemon/src/queriers/grpc/bank.rs rename to cw-orch-daemon/src/queriers/clients/bank.rs diff --git a/cw-orch-daemon/src/queriers/grpc/cosmwasm.rs b/cw-orch-daemon/src/queriers/clients/cosmwasm.rs similarity index 100% rename from cw-orch-daemon/src/queriers/grpc/cosmwasm.rs rename to cw-orch-daemon/src/queriers/clients/cosmwasm.rs diff --git a/cw-orch-daemon/src/queriers/grpc/feegrant.rs b/cw-orch-daemon/src/queriers/clients/feegrant.rs similarity index 100% rename from cw-orch-daemon/src/queriers/grpc/feegrant.rs rename to cw-orch-daemon/src/queriers/clients/feegrant.rs diff --git a/cw-orch-daemon/src/queriers/grpc/gov.rs b/cw-orch-daemon/src/queriers/clients/gov.rs similarity index 100% rename from cw-orch-daemon/src/queriers/grpc/gov.rs rename to cw-orch-daemon/src/queriers/clients/gov.rs diff --git a/cw-orch-daemon/src/queriers/grpc/ibc.rs b/cw-orch-daemon/src/queriers/clients/ibc.rs similarity index 100% rename from cw-orch-daemon/src/queriers/grpc/ibc.rs rename to cw-orch-daemon/src/queriers/clients/ibc.rs diff --git a/cw-orch-daemon/src/queriers/grpc/mod.rs b/cw-orch-daemon/src/queriers/clients/mod.rs similarity index 97% rename from cw-orch-daemon/src/queriers/grpc/mod.rs rename to cw-orch-daemon/src/queriers/clients/mod.rs index 8b6931052..b888ff429 100644 --- a/cw-orch-daemon/src/queriers/grpc/mod.rs +++ b/cw-orch-daemon/src/queriers/clients/mod.rs @@ -56,7 +56,7 @@ mod tx; pub use auth::Auth; pub use bank::Bank; -pub use cosmwasm::CosmWasm; +pub use cosmwasm::{CosmWasm, CosmWasmBase}; pub use feegrant::Feegrant; pub use ibc::Ibc; pub use node::Node; diff --git a/cw-orch-daemon/src/queriers/grpc/node.rs b/cw-orch-daemon/src/queriers/clients/node.rs similarity index 100% rename from cw-orch-daemon/src/queriers/grpc/node.rs rename to cw-orch-daemon/src/queriers/clients/node.rs diff --git a/cw-orch-daemon/src/queriers/grpc/staking.rs b/cw-orch-daemon/src/queriers/clients/staking.rs similarity index 100% rename from cw-orch-daemon/src/queriers/grpc/staking.rs rename to cw-orch-daemon/src/queriers/clients/staking.rs diff --git a/cw-orch-daemon/src/queriers/grpc/tx.rs b/cw-orch-daemon/src/queriers/clients/tx.rs similarity index 100% rename from cw-orch-daemon/src/queriers/grpc/tx.rs rename to cw-orch-daemon/src/queriers/clients/tx.rs diff --git a/cw-orch-daemon/src/queriers/grpc.rs b/cw-orch-daemon/src/queriers/grpc.rs new file mode 100644 index 000000000..dc420403e --- /dev/null +++ b/cw-orch-daemon/src/queriers/grpc.rs @@ -0,0 +1,21 @@ +/// macro for constructing and performing a query on a CosmosSDK module. +#[macro_export] +macro_rules! cosmos_query { + ($self:ident, $module:ident, $func_name:ident, $request_type:ident { $($field:ident : $value:expr),* $(,)? }) => { + { + use $crate::cosmos_modules::$module::{ + query_client::QueryClient, $request_type, + }; + let mut client = QueryClient::new($self.channel.clone()); + #[allow(clippy::redundant_field_names)] + let request = $request_type { $($field : $value),* }; + let response = client.$func_name(request.clone()).await?.into_inner(); + ::log::trace!( + "cosmos_query: {:?} resulted in: {:?}", + request, + response + ); + response + } +}; +} \ No newline at end of file diff --git a/cw-orch-daemon/src/queriers/grpc/auth.rs b/cw-orch-daemon/src/queriers/grpc_old/auth.rs similarity index 100% rename from cw-orch-daemon/src/queriers/grpc/auth.rs rename to cw-orch-daemon/src/queriers/grpc_old/auth.rs diff --git a/cw-orch-daemon/src/queriers/grpc_old/bank.rs b/cw-orch-daemon/src/queriers/grpc_old/bank.rs new file mode 100644 index 000000000..9bc53e0bc --- /dev/null +++ b/cw-orch-daemon/src/queriers/grpc_old/bank.rs @@ -0,0 +1,196 @@ +use crate::{cosmos_modules, error::DaemonError, senders::query::QuerySender, DaemonBase}; +use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; +use cosmwasm_std::{Coin, StdError}; +use cw_orch_core::environment::{BankQuerier, Querier, QuerierGetter}; +use tokio::runtime::Handle; +use tonic::transport::Channel; + +/// Queries for Cosmos Bank Module +/// All the async function are prefixed with `_` +pub struct Bank { + pub channel: Channel, + pub rt_handle: Option, +} + +impl Bank { + pub fn new(daemon: &DaemonBase) -> 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 Bank { + type Error = DaemonError; +} + +impl QuerierGetter for DaemonBase { + fn querier(&self) -> Bank { + Bank::new(self) + } +} + +impl Bank { + /// Query the bank balance of a given address + /// If denom is None, returns all balances + pub async fn _balance( + &self, + address: impl Into, + denom: Option, + ) -> Result, DaemonError> { + match denom { + Some(denom) => { + let resp = cosmos_query!( + self, + bank, + balance, + QueryBalanceRequest { + address: address.into(), + denom: denom, + } + ); + let coin = resp.balance.unwrap(); + Ok(vec![cosmrs_to_cosmwasm_coin(coin)?]) + } + None => { + let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let request = cosmos_modules::bank::QueryAllBalancesRequest { + address: address.into(), + ..Default::default() + }; + let resp = client.all_balances(request).await?.into_inner(); + Ok(cosmrs_to_cosmwasm_coins(resp.balances)?) + } + } + } + + /// Query spendable balance for address + pub async fn _spendable_balances( + &self, + address: impl Into, + ) -> Result, DaemonError> { + let spendable_balances: cosmos_modules::bank::QuerySpendableBalancesResponse = cosmos_query!( + self, + bank, + spendable_balances, + QuerySpendableBalancesRequest { + address: address.into(), + pagination: None, + } + ); + Ok(cosmrs_to_cosmwasm_coins(spendable_balances.balances)?) + } + + /// Query total supply in the bank + pub async fn _total_supply(&self) -> Result, DaemonError> { + let total_supply: cosmos_modules::bank::QueryTotalSupplyResponse = cosmos_query!( + self, + bank, + total_supply, + QueryTotalSupplyRequest { pagination: None } + ); + Ok(cosmrs_to_cosmwasm_coins(total_supply.supply)?) + } + + /// Query total supply in the bank for a denom + pub async fn _supply_of(&self, denom: impl Into) -> Result { + let supply_of: cosmos_modules::bank::QuerySupplyOfResponse = cosmos_query!( + self, + bank, + supply_of, + QuerySupplyOfRequest { + denom: denom.into() + } + ); + Ok(cosmrs_to_cosmwasm_coin(supply_of.amount.unwrap())?) + } + + /// Query params + pub async fn _params(&self) -> Result { + let params: cosmos_modules::bank::QueryParamsResponse = + cosmos_query!(self, bank, params, QueryParamsRequest {}); + Ok(params.params.unwrap()) + } + + /// Query denom metadata + pub async fn _denom_metadata( + &self, + denom: impl Into, + ) -> Result { + let denom_metadata: cosmos_modules::bank::QueryDenomMetadataResponse = cosmos_query!( + self, + bank, + denom_metadata, + QueryDenomMetadataRequest { + denom: denom.into() + } + ); + Ok(denom_metadata.metadata.unwrap()) + } + + /// Query denoms metadata with pagination + /// + /// see [PageRequest] for pagination + pub async fn _denoms_metadata( + &self, + pagination: Option, + ) -> Result, DaemonError> { + let denoms_metadata: cosmos_modules::bank::QueryDenomsMetadataResponse = cosmos_query!( + self, + bank, + denoms_metadata, + QueryDenomsMetadataRequest { + pagination: pagination + } + ); + Ok(denoms_metadata.metadatas) + } +} + +pub fn cosmrs_to_cosmwasm_coin( + c: cosmrs::proto::cosmos::base::v1beta1::Coin, +) -> Result { + Ok(Coin { + amount: c.amount.parse()?, + denom: c.denom, + }) +} + +pub fn cosmrs_to_cosmwasm_coins( + c: Vec, +) -> Result, StdError> { + c.into_iter().map(cosmrs_to_cosmwasm_coin).collect() +} +impl BankQuerier for Bank { + fn balance( + &self, + address: impl Into, + denom: Option, + ) -> Result, Self::Error> { + self.rt_handle + .as_ref() + .ok_or(DaemonError::QuerierNeedRuntime)? + .block_on(self._balance(address, denom)) + } + + fn total_supply(&self) -> Result, Self::Error> { + self.rt_handle + .as_ref() + .ok_or(DaemonError::QuerierNeedRuntime)? + .block_on(self._total_supply()) + } + + fn supply_of(&self, denom: impl Into) -> Result { + self.rt_handle + .as_ref() + .ok_or(DaemonError::QuerierNeedRuntime)? + .block_on(self._supply_of(denom)) + } +} diff --git a/cw-orch-daemon/src/queriers/grpc_old/cosmwasm.rs b/cw-orch-daemon/src/queriers/grpc_old/cosmwasm.rs new file mode 100644 index 000000000..5dd15329b --- /dev/null +++ b/cw-orch-daemon/src/queriers/grpc_old/cosmwasm.rs @@ -0,0 +1,325 @@ +use std::{marker::PhantomData, str::FromStr}; + +use crate::senders::query::QuerySender; +use crate::senders::QueryOnlySender; +use crate::{cosmos_modules, error::DaemonError, DaemonBase}; +use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; +use cosmrs::AccountId; +use cosmwasm_std::{ + from_json, instantiate2_address, to_json_binary, CanonicalAddr, CodeInfoResponse, + ContractInfoResponse, HexBinary, +}; +use cw_orch_core::environment::Environment; +use cw_orch_core::{ + contract::interface_traits::Uploadable, + environment::{Querier, QuerierGetter, WasmQuerier}, +}; +use tokio::runtime::Handle; +use tonic::transport::Channel; + +/// Querier for the CosmWasm SDK module +/// All the async function are prefixed with `_` +pub struct CosmWasmBase { + pub channel: Channel, + pub rt_handle: Option, + _sender: PhantomData, +} + +pub type CosmWasm = CosmWasmBase; + +impl CosmWasmBase { + pub fn new(daemon: &DaemonBase) -> Self { + Self { + channel: daemon.channel(), + rt_handle: Some(daemon.rt_handle.clone()), + _sender: PhantomData, + } + } + pub fn new_async(channel: Channel) -> Self { + Self { + channel, + rt_handle: None, + _sender: PhantomData, + } + } + pub fn new_sync(channel: Channel, handle: &Handle) -> Self { + Self { + channel, + rt_handle: Some(handle.clone()), + _sender: PhantomData, + } + } +} + +impl QuerierGetter> for DaemonBase { + fn querier(&self) -> CosmWasmBase { + CosmWasmBase::new(self) + } +} + +impl Querier for CosmWasmBase { + type Error = DaemonError; +} + +impl CosmWasmBase { + /// Query code_id by hash + pub async fn _code_id_hash(&self, code_id: u64) -> Result { + use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; + let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let request = QueryCodeRequest { code_id }; + let resp = client.code(request).await?.into_inner(); + let contract_hash = resp.code_info.unwrap().data_hash; + Ok(contract_hash.into()) + } + + /// Query contract info + pub async fn _contract_info( + &self, + address: impl Into, + ) -> Result { + use cosmos_modules::cosmwasm::{query_client::*, QueryContractInfoRequest}; + let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let request = QueryContractInfoRequest { + address: address.into(), + }; + let resp = client.contract_info(request).await?.into_inner(); + let contract_info = resp.contract_info.unwrap(); + + let mut c = ContractInfoResponse::default(); + c.code_id = contract_info.code_id; + c.creator = contract_info.creator; + c.admin = if contract_info.admin.is_empty() { + None + } else { + Some(contract_info.admin) + }; + c.ibc_port = if contract_info.ibc_port_id.is_empty() { + None + } else { + Some(contract_info.ibc_port_id) + }; + Ok(c) + } + + /// Query contract history + pub async fn _contract_history( + &self, + address: impl Into, + pagination: Option, + ) -> Result { + use cosmos_modules::cosmwasm::{query_client::*, QueryContractHistoryRequest}; + let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let request = QueryContractHistoryRequest { + address: address.into(), + pagination, + }; + Ok(client.contract_history(request).await?.into_inner()) + } + + /// Query contract state + pub async fn _contract_state( + &self, + address: impl Into, + query_data: Vec, + ) -> Result, DaemonError> { + use cosmos_modules::cosmwasm::{query_client::*, QuerySmartContractStateRequest}; + let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let request = QuerySmartContractStateRequest { + address: address.into(), + query_data, + }; + Ok(client + .smart_contract_state(request) + .await? + .into_inner() + .data) + } + + /// Query all contract state + pub async fn _all_contract_state( + &self, + address: impl Into, + pagination: Option, + ) -> Result { + use cosmos_modules::cosmwasm::{query_client::*, QueryAllContractStateRequest}; + let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let request = QueryAllContractStateRequest { + address: address.into(), + pagination, + }; + Ok(client.all_contract_state(request).await?.into_inner()) + } + + /// Query code + pub async fn _code(&self, code_id: u64) -> Result { + use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; + let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let request = QueryCodeRequest { code_id }; + let response = client.code(request).await?.into_inner().code_info.unwrap(); + + Ok(cosmrs_to_cosmwasm_code_info(response)) + } + + /// Query code bytes + pub async fn _code_data(&self, code_id: u64) -> Result, DaemonError> { + use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; + let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let request = QueryCodeRequest { code_id }; + Ok(client.code(request).await?.into_inner().data) + } + + /// Query codes + pub async fn _codes( + &self, + pagination: Option, + ) -> Result, DaemonError> { + use cosmos_modules::cosmwasm::{query_client::*, QueryCodesRequest}; + let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let request = QueryCodesRequest { pagination }; + let response = client.codes(request).await?.into_inner().code_infos; + + Ok(response + .into_iter() + .map(cosmrs_to_cosmwasm_code_info) + .collect()) + } + + /// Query pinned codes + pub async fn _pinned_codes( + &self, + ) -> Result { + use cosmos_modules::cosmwasm::{query_client::*, QueryPinnedCodesRequest}; + let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let request = QueryPinnedCodesRequest { pagination: None }; + Ok(client.pinned_codes(request).await?.into_inner()) + } + + /// Query contracts by code + pub async fn _contract_by_codes( + &self, + code_id: u64, + ) -> Result { + use cosmos_modules::cosmwasm::{query_client::*, QueryContractsByCodeRequest}; + let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let request = QueryContractsByCodeRequest { + code_id, + pagination: None, + }; + Ok(client.contracts_by_code(request).await?.into_inner()) + } + + /// Query raw contract state + pub async fn _contract_raw_state( + &self, + address: impl Into, + query_data: Vec, + ) -> Result { + use cosmos_modules::cosmwasm::{query_client::*, QueryRawContractStateRequest}; + let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let request = QueryRawContractStateRequest { + address: address.into(), + query_data, + }; + Ok(client.raw_contract_state(request).await?.into_inner()) + } + + /// Query params + pub async fn _params( + &self, + ) -> Result { + use cosmos_modules::cosmwasm::{query_client::*, QueryParamsRequest}; + let mut client: QueryClient = QueryClient::new(self.channel.clone()); + Ok(client.params(QueryParamsRequest {}).await?.into_inner()) + } +} + +impl WasmQuerier for CosmWasmBase { + type Chain = DaemonBase; + fn code_id_hash(&self, code_id: u64) -> Result { + self.rt_handle + .as_ref() + .ok_or(DaemonError::QuerierNeedRuntime)? + .block_on(self._code_id_hash(code_id)) + } + + fn contract_info( + &self, + address: impl Into, + ) -> Result { + self.rt_handle + .as_ref() + .ok_or(DaemonError::QuerierNeedRuntime)? + .block_on(self._contract_info(address)) + } + + fn raw_query( + &self, + address: impl Into, + query_data: Vec, + ) -> Result, Self::Error> { + let response = self + .rt_handle + .as_ref() + .ok_or(DaemonError::QuerierNeedRuntime)? + .block_on(self._contract_raw_state(address, query_data))?; + + Ok(response.data) + } + + fn smart_query( + &self, + address: impl Into, + query_data: &Q, + ) -> Result { + let response = self + .rt_handle + .as_ref() + .ok_or(DaemonError::QuerierNeedRuntime)? + .block_on(self._contract_state(address, to_json_binary(&query_data)?.to_vec()))?; + + Ok(from_json(response)?) + } + + fn code(&self, code_id: u64) -> Result { + self.rt_handle + .as_ref() + .ok_or(DaemonError::QuerierNeedRuntime)? + .block_on(self._code(code_id)) + } + + fn instantiate2_addr( + &self, + code_id: u64, + creator: impl Into, + salt: cosmwasm_std::Binary, + ) -> Result { + let creator_str = creator.into(); + let account_id = AccountId::from_str(&creator_str)?; + let prefix = account_id.prefix(); + let canon = account_id.to_bytes(); + let checksum = self.code_id_hash(code_id)?; + let addr = instantiate2_address(checksum.as_slice(), &CanonicalAddr(canon.into()), &salt)?; + + Ok(AccountId::new(prefix, &addr.0)?.to_string()) + } + + fn local_hash< + T: cw_orch_core::contract::interface_traits::Uploadable + + cw_orch_core::contract::interface_traits::ContractInstance>, + >( + &self, + contract: &T, + ) -> Result { + ::wasm(contract.environment().daemon.chain_info()).checksum() + } +} + +pub fn cosmrs_to_cosmwasm_code_info( + code_info: cosmrs::proto::cosmwasm::wasm::v1::CodeInfoResponse, +) -> CodeInfoResponse { + let mut c = CodeInfoResponse::default(); + c.code_id = code_info.code_id; + c.creator = code_info.creator; + c.checksum = code_info.data_hash.into(); + c +} diff --git a/cw-orch-daemon/src/queriers/grpc_old/feegrant.rs b/cw-orch-daemon/src/queriers/grpc_old/feegrant.rs new file mode 100644 index 000000000..5937075d4 --- /dev/null +++ b/cw-orch-daemon/src/queriers/grpc_old/feegrant.rs @@ -0,0 +1,78 @@ +use crate::{cosmos_modules, error::DaemonError, Daemon}; +use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; +use cw_orch_core::environment::{Querier, QuerierGetter}; +use tokio::runtime::Handle; +use tonic::transport::Channel; + +/// Querier for the Cosmos Gov module +/// All the async function are prefixed with `_` +pub struct FeeGrant { + pub channel: Channel, + pub rt_handle: Option, +} + +impl FeeGrant { + 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 FeeGrant { + type Error = DaemonError; +} + +impl QuerierGetter for Daemon { + fn querier(&self) -> FeeGrant { + FeeGrant::new(self) + } +} + +impl FeeGrant { + /// Query all allowances granted to the grantee address by a granter address + pub async fn _allowance( + &self, + granter: impl Into, + grantee: impl Into, + ) -> Result { + let allowance: cosmos_modules::feegrant::QueryAllowanceResponse = cosmos_query!( + self, + feegrant, + allowance, + QueryAllowanceRequest { + granter: granter.into(), + grantee: grantee.into(), + } + ); + Ok(allowance.allowance.unwrap()) + } + + /// Query allowances for grantee address with a given pagination + /// + /// see [PageRequest] for pagination + pub async fn _allowances( + &self, + grantee: impl Into, + pagination: Option, + ) -> Result, DaemonError> { + let allowances: cosmos_modules::feegrant::QueryAllowancesResponse = cosmos_query!( + self, + feegrant, + allowances, + QueryAllowancesRequest { + grantee: grantee.into(), + pagination: pagination + } + ); + Ok(allowances.allowances) + } +} diff --git a/cw-orch-daemon/src/queriers/grpc_old/gov.rs b/cw-orch-daemon/src/queriers/grpc_old/gov.rs new file mode 100644 index 000000000..1fd4a5197 --- /dev/null +++ b/cw-orch-daemon/src/queriers/grpc_old/gov.rs @@ -0,0 +1,199 @@ +use crate::{cosmos_modules, error::DaemonError, Daemon}; +use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; +use cw_orch_core::environment::{Querier, QuerierGetter}; +use tokio::runtime::Handle; +use tonic::transport::Channel; + +/// Querier for the Cosmos Gov module +/// All the async function are prefixed with `_` +pub struct Gov { + pub channel: Channel, + pub rt_handle: Option, +} + +impl Gov { + 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 Gov { + type Error = DaemonError; +} + +impl QuerierGetter for Daemon { + fn querier(&self) -> Gov { + Gov::new(self) + } +} + +impl Gov { + /// Query proposal details by proposal id + pub async fn _proposal( + &self, + proposal_id: u64, + ) -> Result { + let proposal: cosmos_modules::gov::QueryProposalResponse = cosmos_query!( + self, + gov, + proposal, + QueryProposalRequest { + proposal_id: proposal_id, + } + ); + Ok(proposal.proposal.unwrap()) + } + + /// Query proposals based on given status + /// + /// see [PageRequest] for pagination + pub async fn _proposals( + &self, + proposal_status: GovProposalStatus, + voter: impl Into, + depositor: impl Into, + pagination: Option, + ) -> Result { + let proposals: cosmos_modules::gov::QueryProposalsResponse = cosmos_query!( + self, + gov, + proposals, + QueryProposalsRequest { + proposal_status: proposal_status as i32, + voter: voter.into(), + depositor: depositor.into(), + pagination: pagination + } + ); + Ok(proposals) + } + + /// Query voted information based on proposal_id for voter address + pub async fn _vote( + &self, + proposal_id: u64, + voter: impl Into, + ) -> Result { + let vote: cosmos_modules::gov::QueryVoteResponse = cosmos_query!( + self, + gov, + vote, + QueryVoteRequest { + proposal_id: proposal_id, + voter: voter.into() + } + ); + Ok(vote.vote.unwrap()) + } + + /// Query votes of a given proposal + /// + /// see [PageRequest] for pagination + pub async fn _votes( + &self, + proposal_id: impl Into, + pagination: Option, + ) -> Result { + let votes: cosmos_modules::gov::QueryVotesResponse = cosmos_query!( + self, + gov, + votes, + QueryVotesRequest { + proposal_id: proposal_id.into(), + pagination: pagination + } + ); + Ok(votes) + } + + /// Query all parameters of the gov module + pub async fn _params( + &self, + params_type: impl Into, + ) -> Result { + let params: cosmos_modules::gov::QueryParamsResponse = cosmos_query!( + self, + gov, + params, + QueryParamsRequest { + params_type: params_type.into() + } + ); + Ok(params) + } + + /// Query deposit information using proposal_id and depositor address + pub async fn _deposit( + &self, + proposal_id: u64, + depositor: impl Into, + ) -> Result { + let deposit: cosmos_modules::gov::QueryDepositResponse = cosmos_query!( + self, + gov, + deposit, + QueryDepositRequest { + proposal_id: proposal_id, + depositor: depositor.into() + } + ); + Ok(deposit.deposit.unwrap()) + } + + /// Query deposits of a proposal + /// + /// see [PageRequest] for pagination + pub async fn _deposits( + &self, + proposal_id: u64, + pagination: Option, + ) -> Result { + let deposits: cosmos_modules::gov::QueryDepositsResponse = cosmos_query!( + self, + gov, + deposits, + QueryDepositsRequest { + proposal_id: proposal_id, + pagination: pagination + } + ); + Ok(deposits) + } + + /// TallyResult queries the tally of a proposal vote. + pub async fn _tally_result( + &mut self, + proposal_id: u64, + ) -> Result { + let tally_result: cosmos_modules::gov::QueryTallyResultResponse = cosmos_query!( + self, + gov, + tally_result, + QueryTallyResultRequest { + proposal_id: proposal_id, + } + ); + Ok(tally_result.tally.unwrap()) + } +} + +/// Proposal status +#[allow(missing_docs)] +pub enum GovProposalStatus { + Unspecified = 0, + DepositPeriod = 1, + VotingPeriod = 2, + Passed = 3, + Rejected = 4, + Failed = 5, +} diff --git a/cw-orch-daemon/src/queriers/grpc_old/ibc.rs b/cw-orch-daemon/src/queriers/grpc_old/ibc.rs new file mode 100644 index 000000000..986ee5ff7 --- /dev/null +++ b/cw-orch-daemon/src/queriers/grpc_old/ibc.rs @@ -0,0 +1,537 @@ +use crate::{cosmos_modules, error::DaemonError, Daemon}; +use cosmos_modules::ibc_channel; +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; + +/// Querier for the Cosmos IBC module +/// All the async function are prefixed with `_` +pub struct Ibc { + pub channel: Channel, + pub rt_handle: Option, +} + +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 for Daemon { + fn querier(&self) -> Ibc { + Ibc::new(self) + } +} + +impl Ibc { + // ### Transfer queries ### // + + /// Get the trace of a specific denom + pub async fn _denom_trace(&self, hash: String) -> Result { + let denom_trace: QueryDenomTraceResponse = cosmos_query!( + self, + ibc_transfer, + denom_trace, + QueryDenomTraceRequest { hash: hash } + ); + Ok(denom_trace.denom_trace.unwrap()) + } + + /// Get the hash of a specific denom from its trace + pub async fn _denom_hash(&self, trace: String) -> Result { + let denom_hash: QueryDenomHashResponse = cosmos_query!( + self, + ibc_transfer, + denom_hash, + QueryDenomHashRequest { trace: trace } + ); + Ok(denom_hash.hash) + } + + // ### Client queries ### + + /// Get all the IBC clients for this daemon + pub async fn _clients(&self) -> Result, DaemonError> { + let ibc_clients: QueryClientStatesResponse = cosmos_query!( + self, + ibc_client, + client_states, + QueryClientStatesRequest { pagination: None } + ); + Ok(ibc_clients.client_states) + } + + /// Get the state of a specific IBC client + pub async fn _client_state( + &self, + client_id: impl ToString, + // Add the necessary parameters here + ) -> Result { + let response: cosmos_modules::ibc_client::QueryClientStateResponse = cosmos_query!( + self, + ibc_client, + client_state, + QueryClientStateRequest { + client_id: client_id.to_string(), + } + ); + Ok(response) + } + + /// Get the consensus state of a specific IBC client + pub async fn _consensus_states( + &self, + client_id: impl ToString, + ) -> Result { + 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) + } + + /// Get the consensus status of a specific IBC client + pub async fn _client_status( + &self, + client_id: impl ToString, + // Add the necessary parameters here + ) -> Result { + let response: cosmos_modules::ibc_client::QueryClientStatusResponse = cosmos_query!( + self, + ibc_client, + client_status, + QueryClientStatusRequest { + client_id: client_id.to_string(), + } + ); + Ok(response) + } + + /// Get the ibc client parameters + pub async fn _client_params( + &self, + ) -> Result { + let response: cosmos_modules::ibc_client::QueryClientParamsResponse = + cosmos_query!(self, ibc_client, client_params, QueryClientParamsRequest {}); + Ok(response) + } + + // ### Connection queries ### + + /// Query the IBC connections for a specific chain + pub async fn _connections(&self) -> Result, DaemonError> { + use cosmos_modules::ibc_connection::QueryConnectionsResponse; + + let ibc_connections: QueryConnectionsResponse = cosmos_query!( + self, + ibc_connection, + connections, + QueryConnectionsRequest { pagination: None } + ); + Ok(ibc_connections.connections) + } + + /// Search for open connections with a specific chain. + pub async fn _open_connections( + &self, + client_chain_id: impl ToString, + ) -> Result, 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); + } + } + + // now search for the connections that use a client with the correct chain ids + 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) + } + + // Get the information about a specific connection + pub async fn _connection_end( + &self, + connection_id: impl Into, + ) -> Result, 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) + } + + /// Get all the connections for this client + pub async fn _client_connections( + &self, + client_id: impl Into, + ) -> Result, 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) + } + + /// Get the (tendermint) client state for a specific connection + pub async fn _connection_client( + &self, + connection_id: impl Into, + ) -> Result { + 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) + } + + // ### Channel queries ### + + /// Get the channel for a specific port and channel id + pub async fn _channel( + &self, + port_id: impl Into, + channel_id: impl Into, + ) -> Result { + 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 + ))) + } + + /// Get all the channels for a specific connection + pub async fn _connection_channels( + &self, + connection_id: impl Into, + ) -> Result, 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) + } + + /// Get the client state for a specific channel and port + pub async fn _channel_client_state( + &self, + port_id: impl Into, + channel_id: impl Into, + ) -> Result { + 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 + ))) + } + + // ### Packet queries ### + + // Commitment + + /// Get all the packet commitments for a specific channel and port + pub async fn _packet_commitments( + &self, + port_id: impl Into, + channel_id: impl Into, + ) -> Result, 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) + } + + /// Get the packet commitment for a specific channel, port and sequence + pub async fn _packet_commitment( + &self, + port_id: impl Into, + channel_id: impl Into, + sequence: u64, + ) -> Result { + 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) + } + + // Receipt + + /// Returns if the packet is received on the connected chain. + pub async fn _packet_receipt( + &self, + port_id: impl Into, + channel_id: impl Into, + sequence: u64, + ) -> Result { + 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) + } + + // Acknowledgement + + /// Get all the packet acknowledgements for a specific channel, port and commitment sequences + pub async fn _packet_acknowledgements( + &self, + port_id: impl Into, + channel_id: impl Into, + packet_commitment_sequences: Vec, + ) -> Result, 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) + } + + /// Get the packet acknowledgement for a specific channel, port and sequence + pub async fn _packet_acknowledgement( + &self, + port_id: impl Into, + channel_id: impl Into, + sequence: u64, + ) -> Result, 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) + } + + /// No acknowledgement exists on receiving chain for the given packet commitment sequence on sending chain. + /// Returns the packet sequences that have not yet been received. + pub async fn _unreceived_packets( + &self, + port_id: impl Into, + channel_id: impl Into, + packet_commitment_sequences: Vec, + ) -> Result, 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) + } + + /// Returns the acknowledgement sequences that have not yet been received. + /// Given a list of acknowledgement sequences from counterparty, determine if an ack on the counterparty chain has been received on the executing chain. + /// Returns the list of acknowledgement sequences that have not yet been received. + pub async fn _unreceived_acks( + &self, + port_id: impl Into, + channel_id: impl Into, + packet_ack_sequences: Vec, + ) -> Result, 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) + } + + /// Returns the acknowledgement sequences that have not yet been received. + /// Given a list of acknowledgement sequences from counterparty, determine if an ack on the counterparty chain has been received on the executing chain. + /// Returns the list of acknowledgement sequences that have not yet been received. + pub async fn _next_sequence_receive( + &self, + port_id: impl Into, + channel_id: impl Into, + ) -> Result { + 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) + } +} diff --git a/cw-orch-daemon/src/queriers/grpc_old/mod.rs b/cw-orch-daemon/src/queriers/grpc_old/mod.rs new file mode 100644 index 000000000..85b5586e8 --- /dev/null +++ b/cw-orch-daemon/src/queriers/grpc_old/mod.rs @@ -0,0 +1,45 @@ +//! # DaemonQuerier +//! +//! DaemonAsync queriers are gRPC query clients for the CosmosSDK modules. They can be used to query the different modules (Bank, Ibc, Authz, ...). +//! +//! ## Usage +//! +//! You will need to acquire a [gRPC channel](Channel) to a running CosmosSDK node to be able to use the queriers. +//! Here is an example of how to acquire one using the DaemonAsync builder. +//! +//! ```no_run +//! // require the querier you want to use, in this case Node +//! use cw_orch_daemon::{queriers::Node, DaemonAsync, networks, queriers::DaemonQuerier}; +//! # tokio_test::block_on(async { +//! // call the builder and configure it as you need +//! let daemon = DaemonAsync::builder() +//! .chain(networks::LOCAL_JUNO) +//! .build() +//! .await.unwrap(); +//! // now you can use the Node querier: +//! let node = Node::new(daemon.channel()); +//! let node_info = node.info(); +//! # }) +//! ``` + +mod auth; +mod bank; +mod cosmwasm; +mod feegrant; +mod gov; +mod ibc; +mod node; +mod staking; +mod tx; + +pub use auth::Auth; +pub use bank::Bank; +pub use cosmwasm::{CosmWasm, CosmWasmBase}; +pub use feegrant::Feegrant; +pub use ibc::Ibc; +pub use node::Node; +pub use tx::Tx; + +// this two containt structs that are helpers for the queries +pub use gov::*; +pub use staking::*; diff --git a/cw-orch-daemon/src/queriers/grpc_old/node.rs b/cw-orch-daemon/src/queriers/grpc_old/node.rs new file mode 100644 index 000000000..825122a52 --- /dev/null +++ b/cw-orch-daemon/src/queriers/grpc_old/node.rs @@ -0,0 +1,427 @@ +use std::{cmp::min, time::Duration}; + +use crate::{ + cosmos_modules, env::DaemonEnvVars, error::DaemonError, senders::query::QuerySender, + tx_resp::CosmTxResponse, DaemonBase, +}; + +use cosmrs::{ + proto::cosmos::{ + base::query::v1beta1::PageRequest, + tx::v1beta1::{OrderBy, SimulateResponse}, + }, + tendermint::{Block, Time}, +}; +use cosmwasm_std::BlockInfo; +use cw_orch_core::{ + environment::{NodeQuerier, Querier, QuerierGetter}, + log::query_target, +}; +use tokio::runtime::Handle; +use tonic::transport::Channel; + +/// Querier for the Tendermint node. +/// Supports queries for block and tx information +/// All the async function are prefixed with `_` +pub struct Node { + pub channel: Channel, + pub rt_handle: Option, +} + +impl Node { + pub fn new(daemon: &DaemonBase) -> 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 QuerierGetter for DaemonBase { + fn querier(&self) -> Node { + Node::new(self) + } +} + +impl Querier for Node { + type Error = DaemonError; +} + +impl Node { + /// Returns node info + pub async fn _info( + &self, + ) -> Result { + let mut client = + cosmos_modules::tendermint::service_client::ServiceClient::new(self.channel.clone()); + + let resp = client + .get_node_info(cosmos_modules::tendermint::GetNodeInfoRequest {}) + .await? + .into_inner(); + + Ok(resp) + } + + /// Queries node syncing + pub async fn _syncing(&self) -> Result { + let mut client = + cosmos_modules::tendermint::service_client::ServiceClient::new(self.channel.clone()); + + let resp = client + .get_syncing(cosmos_modules::tendermint::GetSyncingRequest {}) + .await? + .into_inner(); + + Ok(resp.syncing) + } + + /// Returns latests block information + pub async fn _latest_block(&self) -> Result { + let mut client = + cosmos_modules::tendermint::service_client::ServiceClient::new(self.channel.clone()); + + let resp = client + .get_latest_block(cosmos_modules::tendermint::GetLatestBlockRequest {}) + .await? + .into_inner(); + + Ok(Block::try_from(resp.block.unwrap())?) + } + + /// Returns block information fetched by height + pub async fn _block_by_height(&self, height: u64) -> Result { + let mut client = + cosmos_modules::tendermint::service_client::ServiceClient::new(self.channel.clone()); + + let resp = client + .get_block_by_height(cosmos_modules::tendermint::GetBlockByHeightRequest { + height: height as i64, + }) + .await? + .into_inner(); + + Ok(Block::try_from(resp.block.unwrap())?) + } + + /// Return the average block time for the last 50 blocks or since inception + /// This is used to estimate the time when a tx will be included in a block + pub async fn _average_block_speed( + &self, + multiplier: Option, + ) -> Result { + // get latest block time and height + let mut latest_block = self._latest_block().await?; + let latest_block_time = latest_block.header.time; + let mut latest_block_height = latest_block.header.height.value(); + + while latest_block_height <= 1 { + // wait to get some blocks + tokio::time::sleep(Duration::from_secs(1)).await; + latest_block = self._latest_block().await?; + latest_block_height = latest_block.header.height.value(); + } + + // let avg period + let avg_period = min(latest_block_height - 1, 50); + + // get block time for block avg_period blocks ago + let block_avg_period_ago = self + ._block_by_height(latest_block_height - avg_period) + .await?; + let block_avg_period_ago_time = block_avg_period_ago.header.time; + + // calculate average block time + let average_block_time = latest_block_time.duration_since(block_avg_period_ago_time)?; + let average_block_time = average_block_time.div_f64(avg_period as f64); + + // multiply by multiplier if provided + let average_block_time = match multiplier { + Some(multiplier) => average_block_time.mul_f32(multiplier), + None => average_block_time, + }; + + Ok(average_block_time) + } + + /// Returns latests validator set + pub async fn _latest_validator_set( + &self, + pagination: Option, + ) -> Result { + let mut client = + cosmos_modules::tendermint::service_client::ServiceClient::new(self.channel.clone()); + + let resp = client + .get_latest_validator_set(cosmos_modules::tendermint::GetLatestValidatorSetRequest { + pagination, + }) + .await? + .into_inner(); + + Ok(resp) + } + + /// Returns latests validator set fetched by height + pub async fn _validator_set_by_height( + &self, + height: i64, + pagination: Option, + ) -> Result { + let mut client = + cosmos_modules::tendermint::service_client::ServiceClient::new(self.channel.clone()); + + let resp = client + .get_validator_set_by_height( + cosmos_modules::tendermint::GetValidatorSetByHeightRequest { height, pagination }, + ) + .await? + .into_inner(); + + Ok(resp) + } + + /// Returns current block height + pub async fn _block_height(&self) -> Result { + let block = self._latest_block().await?; + Ok(block.header.height.value()) + } + + /// Returns the block timestamp (since unix epoch) in nanos + pub async fn _block_time(&self) -> Result { + let block = self._latest_block().await?; + Ok(block + .header + .time + .duration_since(Time::unix_epoch())? + .as_nanos()) + } + + /// Simulate TX + pub async fn _simulate_tx(&self, tx_bytes: Vec) -> Result { + let mut client = + cosmos_modules::tx::service_client::ServiceClient::new(self.channel.clone()); + #[allow(deprecated)] + let resp: SimulateResponse = client + .simulate(cosmos_modules::tx::SimulateRequest { tx: None, tx_bytes }) + .await? + .into_inner(); + let gas_used = resp.gas_info.unwrap().gas_used; + Ok(gas_used) + } + + /// Returns all the block info + pub async fn _block_info(&self) -> Result { + let block = self._latest_block().await?; + + block_to_block_info(block) + } + + /// Find TX by hash + pub async fn _find_tx(&self, hash: String) -> Result { + self._find_tx_with_retries(hash, DaemonEnvVars::max_tx_query_retries()) + .await + } + + /// Find TX by hash with a given amount of retries + pub async fn _find_tx_with_retries( + &self, + hash: String, + retries: usize, + ) -> Result { + let mut client = + cosmos_modules::tx::service_client::ServiceClient::new(self.channel.clone()); + + let request = cosmos_modules::tx::GetTxRequest { hash: hash.clone() }; + let mut block_speed = self._average_block_speed(Some(0.7)).await?; + let max_block_time = DaemonEnvVars::max_block_time(); + if let Some(max_time) = max_block_time { + block_speed = block_speed.min(max_time); + } else { + let min_block_time = DaemonEnvVars::min_block_time(); + block_speed = block_speed.max(min_block_time); + } + + for _ in 0..retries { + match client.get_tx(request.clone()).await { + Ok(tx) => { + let resp = tx.into_inner().tx_response.unwrap().into(); + log::debug!(target: &query_target(), "TX found: {:?}", resp); + return Ok(resp); + } + Err(err) => { + // increase wait time + block_speed = block_speed.mul_f64(1.6); + if let Some(max_time) = max_block_time { + block_speed = block_speed.min(max_time) + } + log::debug!(target: &query_target(), "TX not found with error: {:?}", err); + log::debug!(target: &query_target(), "Waiting {} milli-seconds", block_speed.as_millis()); + tokio::time::sleep(block_speed).await; + } + } + } + + // return error if tx not found by now + Err(DaemonError::TXNotFound(hash, retries)) + } + + /// Find TX by events + pub async fn _find_tx_by_events( + &self, + events: Vec, + page: Option, + order_by: Option, + ) -> Result, DaemonError> { + self._find_tx_by_events_with_retries( + events, + page, + order_by, + false, + DaemonEnvVars::max_tx_query_retries(), + ) + .await + } + + /// Find Tx by events + /// This function will consider that no transactions found is an error + /// This either returns a non empty vector or errors + pub async fn _find_some_tx_by_events( + &self, + events: Vec, + page: Option, + order_by: Option, + ) -> Result, DaemonError> { + self._find_tx_by_events_with_retries( + events, + page, + order_by, + true, + DaemonEnvVars::max_tx_query_retries(), + ) + .await + } + + /// Find TX by events with : + /// 1. Specify if an empty tx object is a valid response + /// 2. Specify a given amount of retries + pub async fn _find_tx_by_events_with_retries( + &self, + events: Vec, + page: Option, + order_by: Option, + retry_on_empty: bool, + retries: usize, + ) -> Result, DaemonError> { + let mut client = crate::cosmos_proto_patches::v0_50::tx::service_client::ServiceClient::new( + self.channel.clone(), + ); + + #[allow(deprecated)] + let request = crate::cosmos_proto_patches::v0_50::tx::GetTxsEventRequest { + events: events.clone(), + pagination: None, + order_by: order_by.unwrap_or(OrderBy::Desc).into(), + page: page.unwrap_or(0), + limit: 100, + query: events.join(" AND "), + }; + + for _ in 0..retries { + match client.get_txs_event(request.clone()).await { + Ok(tx) => { + let resp = tx.into_inner().tx_responses; + if retry_on_empty && resp.is_empty() { + log::debug!(target: &query_target(), "No TX found with events {:?}", events); + log::debug!(target: &query_target(), "Waiting 10s"); + tokio::time::sleep(Duration::from_secs(10)).await; + } else { + log::debug!( + target: &query_target(), + "TX found by events: {:?}", + resp.iter().map(|t| t.txhash.clone()) + ); + return Ok(resp.iter().map(|r| r.clone().into()).collect()); + } + } + Err(err) => { + log::debug!(target: &query_target(), "TX not found with error: {:?}", err); + log::debug!(target: &query_target(), "Waiting 10s"); + tokio::time::sleep(Duration::from_secs(10)).await; + } + } + } + // return error if tx not found by now + Err(DaemonError::TXNotFound( + format!("with events {:?}", events), + DaemonEnvVars::max_tx_query_retries(), + )) + } +} + +// Now we define traits + +impl NodeQuerier for Node { + type Response = CosmTxResponse; + + fn latest_block(&self) -> Result { + self.rt_handle + .as_ref() + .ok_or(DaemonError::QuerierNeedRuntime)? + .block_on(self._block_info()) + } + + fn block_by_height(&self, height: u64) -> Result { + let block = self + .rt_handle + .as_ref() + .ok_or(DaemonError::QuerierNeedRuntime)? + .block_on(self._block_by_height(height))?; + + block_to_block_info(block) + } + + fn block_height(&self) -> Result { + self.rt_handle + .as_ref() + .ok_or(DaemonError::QuerierNeedRuntime)? + .block_on(self._block_height()) + } + + fn block_time(&self) -> Result { + self.rt_handle + .as_ref() + .ok_or(DaemonError::QuerierNeedRuntime)? + .block_on(self._block_time()) + } + + fn simulate_tx(&self, tx_bytes: Vec) -> Result { + self.rt_handle + .as_ref() + .ok_or(DaemonError::QuerierNeedRuntime)? + .block_on(self._simulate_tx(tx_bytes)) + } + + fn find_tx(&self, hash: String) -> Result { + self.rt_handle + .as_ref() + .ok_or(DaemonError::QuerierNeedRuntime)? + .block_on(self._find_tx(hash)) + } +} + +fn block_to_block_info(block: Block) -> Result { + let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; + let time = cosmwasm_std::Timestamp::from_nanos(since_epoch.as_nanos() as u64); + Ok(cosmwasm_std::BlockInfo { + height: block.header.height.value(), + time, + chain_id: block.header.chain_id.to_string(), + }) +} diff --git a/cw-orch-daemon/src/queriers/grpc_old/staking.rs b/cw-orch-daemon/src/queriers/grpc_old/staking.rs new file mode 100644 index 000000000..63d8b8860 --- /dev/null +++ b/cw-orch-daemon/src/queriers/grpc_old/staking.rs @@ -0,0 +1,347 @@ +use std::fmt::Display; + +use crate::{cosmos_modules, error::DaemonError, Daemon}; +use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; +use cosmwasm_std::{Addr, StdError}; +use cw_orch_core::environment::{Querier, QuerierGetter}; +use tokio::runtime::Handle; +use tonic::transport::Channel; + +use super::bank::cosmrs_to_cosmwasm_coin; + +/// Querier for the Cosmos Staking module +/// All the async function are prefixed with `_` +pub struct Staking { + pub channel: Channel, + pub rt_handle: Option, +} + +impl Staking { + 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 Staking { + type Error = DaemonError; +} + +impl QuerierGetter for Daemon { + fn querier(&self) -> Staking { + Staking::new(self) + } +} +impl Staking { + /// Queries validator info for given validator address + pub async fn _validator( + &self, + validator_addr: impl Into, + ) -> Result { + let validator_response: cosmos_modules::staking::QueryValidatorResponse = cosmos_query!( + self, + staking, + validator, + QueryValidatorRequest { + validator_addr: validator_addr.into() + } + ); + + Ok(cosmrs_to_cosmwasm_validator( + validator_response.validator.unwrap(), + )?) + } + + /// Queries all validators that match the given status + /// + /// see [StakingBondStatus] for available statuses + pub async fn _validators( + &self, + status: StakingBondStatus, + ) -> Result, DaemonError> { + let validators: cosmos_modules::staking::QueryValidatorsResponse = cosmos_query!( + self, + staking, + validators, + QueryValidatorsRequest { + status: status.to_string(), + pagination: None, + } + ); + + Ok(validators + .validators + .into_iter() + .map(cosmrs_to_cosmwasm_validator) + .collect::>()?) + } + + /// Query validator delegations info for given validator + /// + /// see [PageRequest] for pagination + pub async fn _delegations( + &self, + validator_addr: impl Into, + pagination: Option, + ) -> Result, DaemonError> { + let validator_delegations: cosmos_modules::staking::QueryValidatorDelegationsResponse = cosmos_query!( + self, + staking, + validator_delegations, + QueryValidatorDelegationsRequest { + validator_addr: validator_addr.into(), + pagination: pagination + } + ); + Ok(validator_delegations + .delegation_responses + .into_iter() + .map(cosmrs_to_cosmwasm_delegation) + .collect::>()?) + } + + /// Query validator unbonding delegations of a validator + pub async fn _unbonding_delegations( + &self, + validator_addr: impl Into, + ) -> Result, DaemonError> { + let validator_unbonding_delegations: cosmos_modules::staking::QueryValidatorUnbondingDelegationsResponse = cosmos_query!( + self, + staking, + validator_unbonding_delegations, + QueryValidatorUnbondingDelegationsRequest { + validator_addr: validator_addr.into(), + pagination: None + } + ); + Ok(validator_unbonding_delegations.unbonding_responses) + } + + /// Query delegation info for given validator for a delegator + pub async fn _delegation( + &self, + validator_addr: impl Into, + delegator_addr: impl Into, + ) -> Result { + let delegation: cosmos_modules::staking::QueryDelegationResponse = cosmos_query!( + self, + staking, + delegation, + QueryDelegationRequest { + validator_addr: validator_addr.into(), + delegator_addr: delegator_addr.into() + } + ); + Ok(cosmrs_to_cosmwasm_delegation( + delegation.delegation_response.unwrap(), + )?) + } + + /// Query unbonding delegation info for given validator delegator + pub async fn _unbonding_delegation( + &self, + validator_addr: impl Into, + delegator_addr: impl Into, + ) -> Result { + let unbonding_delegation: cosmos_modules::staking::QueryUnbondingDelegationResponse = cosmos_query!( + self, + staking, + unbonding_delegation, + QueryUnbondingDelegationRequest { + validator_addr: validator_addr.into(), + delegator_addr: delegator_addr.into() + } + ); + Ok(unbonding_delegation.unbond.unwrap()) + } + + /// Query all delegator delegations of a given delegator address + /// + /// see [PageRequest] for pagination + pub async fn _delegator_delegations( + &self, + delegator_addr: impl Into, + pagination: Option, + ) -> Result { + let delegator_delegations: cosmos_modules::staking::QueryDelegatorDelegationsResponse = cosmos_query!( + self, + staking, + delegator_delegations, + QueryDelegatorDelegationsRequest { + delegator_addr: delegator_addr.into(), + pagination: pagination + } + ); + Ok(delegator_delegations) + } + + /// Queries all unbonding delegations of a given delegator address. + /// + /// see [PageRequest] for pagination + pub async fn _delegator_unbonding_delegations( + &self, + delegator_addr: impl Into, + pagination: Option, + ) -> Result + { + let delegator_unbonding_delegations: cosmos_modules::staking::QueryDelegatorUnbondingDelegationsResponse = cosmos_query!( + self, + staking, + delegator_unbonding_delegations, + QueryDelegatorUnbondingDelegationsRequest { + delegator_addr: delegator_addr.into(), + pagination: pagination + } + ); + Ok(delegator_unbonding_delegations) + } + + /// Query redelegations of a given address + /// + /// see [PageRequest] for pagination + pub async fn _redelegations( + &self, + delegator_addr: impl Into, + src_validator_addr: impl Into, + dst_validator_addr: impl Into, + pagination: Option, + ) -> Result { + let redelegations: cosmos_modules::staking::QueryRedelegationsResponse = cosmos_query!( + self, + staking, + redelegations, + QueryRedelegationsRequest { + delegator_addr: delegator_addr.into(), + src_validator_addr: src_validator_addr.into(), + dst_validator_addr: dst_validator_addr.into(), + pagination: pagination + } + ); + Ok(redelegations) + } + + /// Query delegator validators info for given delegator address. + pub async fn _delegator_validator( + &self, + validator_addr: impl Into, + delegator_addr: impl Into, + ) -> Result { + let delegator_validator: cosmos_modules::staking::QueryDelegatorValidatorResponse = cosmos_query!( + self, + staking, + delegator_validator, + QueryDelegatorValidatorRequest { + validator_addr: validator_addr.into(), + delegator_addr: delegator_addr.into(), + } + ); + Ok(delegator_validator) + } + + /// Query delegator validators info for given delegator address + /// + /// see [PageRequest] for pagination + pub async fn _delegator_validators( + &self, + delegator_addr: impl Into, + pagination: Option, + ) -> Result { + let delegator_validators: cosmos_modules::staking::QueryDelegatorValidatorsResponse = cosmos_query!( + self, + staking, + delegator_validators, + QueryDelegatorValidatorsRequest { + delegator_addr: delegator_addr.into(), + pagination: pagination + } + ); + + Ok(delegator_validators) + } + + /// Query historical info info for given height + pub async fn _historical_info( + &self, + height: i64, + ) -> Result { + let historical_info: cosmos_modules::staking::QueryHistoricalInfoResponse = cosmos_query!( + self, + staking, + historical_info, + QueryHistoricalInfoRequest { height: height } + ); + Ok(historical_info) + } + + /// Query the pool info + pub async fn _pool(&self) -> Result { + let pool: cosmos_modules::staking::QueryPoolResponse = + cosmos_query!(self, staking, pool, QueryPoolRequest {}); + Ok(pool) + } + + /// Query staking parameters + pub async fn _params( + &self, + ) -> Result { + let params: cosmos_modules::staking::QueryParamsResponse = + cosmos_query!(self, staking, params, QueryParamsRequest {}); + Ok(params) + } +} + +/// Staking bond statuses +pub enum StakingBondStatus { + /// UNSPECIFIED defines an invalid validator status. + Unspecified = 0, + /// UNBONDED defines a validator that is not bonded. + Unbonded = 1, + /// UNBONDING defines a validator that is unbonding. + Unbonding = 2, + /// BONDED defines a validator that is bonded. + Bonded = 3, +} + +impl Display for StakingBondStatus { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let str = match self { + StakingBondStatus::Unspecified => "BOND_STATUS_UNSPECIFIED", + StakingBondStatus::Unbonded => "BOND_STATUS_UNBONDED", + StakingBondStatus::Unbonding => "BOND_STATUS_UNBONDING", + StakingBondStatus::Bonded => "BOND_STATUS_BONDED", + }; + write!(f, "{}", str) + } +} + +pub fn cosmrs_to_cosmwasm_validator( + validator: cosmrs::proto::cosmos::staking::v1beta1::Validator, +) -> Result { + let comission = validator.commission.unwrap().commission_rates.unwrap(); + Ok(cosmwasm_std::Validator { + address: validator.operator_address, + commission: comission.rate.parse()?, + max_commission: comission.max_rate.parse()?, + max_change_rate: comission.max_change_rate.parse()?, + }) +} + +pub fn cosmrs_to_cosmwasm_delegation( + delegation_response: cosmrs::proto::cosmos::staking::v1beta1::DelegationResponse, +) -> Result { + let delegation = delegation_response.delegation.unwrap(); + Ok(cosmwasm_std::Delegation { + delegator: Addr::unchecked(delegation.delegator_address), + validator: delegation.validator_address, + amount: cosmrs_to_cosmwasm_coin(delegation_response.balance.unwrap())?, + }) +} diff --git a/cw-orch-daemon/src/queriers/grpc_old/tx.rs b/cw-orch-daemon/src/queriers/grpc_old/tx.rs new file mode 100644 index 000000000..9853a16b3 --- /dev/null +++ b/cw-orch-daemon/src/queriers/grpc_old/tx.rs @@ -0,0 +1,35 @@ +// Only a simple implementation to not overload the tx builder + +use cosmrs::{proto::cosmos::base::abci::v1beta1::TxResponse, tx::Raw}; +use tonic::transport::Channel; + +use crate::{cosmos_modules, queriers::DaemonQuerier, DaemonError}; + +/// Queries for Cosmos Bank Module +pub struct Tx { + channel: Channel, +} + +impl DaemonQuerier for Tx { + fn new(channel: Channel) -> Self { + Self { channel } + } +} + +impl Tx { + /// Query spendable balance for address + pub async fn broadcast(&self, tx: Raw) -> Result { + let mut client = + cosmos_modules::tx::service_client::ServiceClient::new(self.channel.clone()); + + let resp = client + .broadcast_tx(cosmos_modules::tx::BroadcastTxRequest { + tx_bytes: tx.to_bytes()?, + mode: cosmos_modules::tx::BroadcastMode::Sync.into(), + }) + .await? + .into_inner(); + + Ok(resp.tx_response.unwrap()) + } +} diff --git a/cw-orch-daemon/src/queriers/rpc/mod.rs b/cw-orch-daemon/src/queriers/rpc.rs similarity index 80% rename from cw-orch-daemon/src/queriers/rpc/mod.rs rename to cw-orch-daemon/src/queriers/rpc.rs index eaef0cd43..cef0ed1a5 100644 --- a/cw-orch-daemon/src/queriers/rpc/mod.rs +++ b/cw-orch-daemon/src/queriers/rpc.rs @@ -1,18 +1,3 @@ -mod auth; -mod bank; -mod cosmwasm; -mod node; -mod staking; -mod tx; - -pub use auth::Auth; -pub use bank::Bank; -pub use cosmwasm::CosmWasm; -pub use node::Node; -pub use staking::Staking; -pub use tx::Tx; -// pub use feegrant::Feegrant; -// pub use ibc::Ibc; /// macro for constructing and performing a query on a CosmosSDK module. #[macro_export] diff --git a/cw-orch-daemon/src/queriers/rpc/auth.rs b/cw-orch-daemon/src/queriers/rpc_old/auth.rs similarity index 100% rename from cw-orch-daemon/src/queriers/rpc/auth.rs rename to cw-orch-daemon/src/queriers/rpc_old/auth.rs diff --git a/cw-orch-daemon/src/queriers/rpc/bank.rs b/cw-orch-daemon/src/queriers/rpc_old/bank.rs similarity index 100% rename from cw-orch-daemon/src/queriers/rpc/bank.rs rename to cw-orch-daemon/src/queriers/rpc_old/bank.rs diff --git a/cw-orch-daemon/src/queriers/rpc/cosmwasm.rs b/cw-orch-daemon/src/queriers/rpc_old/cosmwasm.rs similarity index 100% rename from cw-orch-daemon/src/queriers/rpc/cosmwasm.rs rename to cw-orch-daemon/src/queriers/rpc_old/cosmwasm.rs diff --git a/cw-orch-daemon/src/queriers/rpc_old/mod.rs b/cw-orch-daemon/src/queriers/rpc_old/mod.rs new file mode 100644 index 000000000..37d714af7 --- /dev/null +++ b/cw-orch-daemon/src/queriers/rpc_old/mod.rs @@ -0,0 +1,15 @@ +mod auth; +mod bank; +mod cosmwasm; +mod node; +mod staking; +mod tx; + +pub use auth::Auth; +pub use bank::Bank; +pub use cosmwasm::CosmWasm; +pub use node::Node; +pub use staking::Staking; +pub use tx::Tx; +// pub use feegrant::Feegrant; +// pub use ibc::Ibc; diff --git a/cw-orch-daemon/src/queriers/rpc/node.rs b/cw-orch-daemon/src/queriers/rpc_old/node.rs similarity index 100% rename from cw-orch-daemon/src/queriers/rpc/node.rs rename to cw-orch-daemon/src/queriers/rpc_old/node.rs diff --git a/cw-orch-daemon/src/queriers/rpc/staking.rs b/cw-orch-daemon/src/queriers/rpc_old/staking.rs similarity index 100% rename from cw-orch-daemon/src/queriers/rpc/staking.rs rename to cw-orch-daemon/src/queriers/rpc_old/staking.rs diff --git a/cw-orch-daemon/src/queriers/rpc/tx.rs b/cw-orch-daemon/src/queriers/rpc_old/tx.rs similarity index 100% rename from cw-orch-daemon/src/queriers/rpc/tx.rs rename to cw-orch-daemon/src/queriers/rpc_old/tx.rs diff --git a/cw-orch-daemon/src/rpc_channel.rs b/cw-orch-daemon/src/rpc_channel.rs deleted file mode 100644 index f84705c33..000000000 --- a/cw-orch-daemon/src/rpc_channel.rs +++ /dev/null @@ -1,101 +0,0 @@ -use cosmrs::rpc::{Client, HttpClient}; -use ibc_chain_registry::chain::Rpc; -use ibc_relayer_types::core::ics24_host::identifier::ChainId; - -use super::error::DaemonError; - -/// A helper for constructing a gRPC channel -pub struct RpcChannel {} - -impl RpcChannel { - /// Connect to any of the provided gRPC endpoints - pub async fn connect(rpc: &[Rpc], chain_id: &ChainId) -> Result { - let mut successful_connections = vec![]; - - for Rpc { address, .. } in rpc.iter() { - log::info!("Trying to connect to endpoint: {}", address); - - // try to connect to rpc endpoint - let maybe_client = HttpClient::new(address.as_str()); - - // connection succeeded or try the next rpc endpoint - let client = if maybe_client.is_ok() { - maybe_client? - } else { - continue; - }; - - // get client information for verification down below - let node_info = client.status().await?.node_info; - - // local juno does not return a proper ChainId with epoch format - if ChainId::is_epoch_format(node_info.network.as_str()) { - // verify we are connected to the spected network - if node_info.network.as_str().ne(chain_id.as_str()) { - log::error!( - "Network mismatch: connection:{} != config:{}", - node_info.network, - chain_id.as_str() - ); - continue; - } - } - - // add endpoint to succesful connections - successful_connections.push(client) - } - - // we could not get any succesful connections - if successful_connections.is_empty() { - return Err(DaemonError::CannotConnectRPC); - } - - Ok(successful_connections.pop().unwrap()) - } -} - -#[cfg(test)] -mod tests { - /* - This test asserts breaking issues around the GRPC connection - */ - - use crate::DaemonAsync; - use speculoos::prelude::*; - - #[tokio::test] - async fn no_connection() { - let mut chain = cw_orch_daemon::networks::LOCAL_JUNO; - let grpcs = &vec!["https://127.0.0.1:99999"]; - chain.grpc_urls = grpcs; - - let build_res = DaemonAsync::builder() - .chain(chain) - .deployment_id("v0.1.0") - .build() - .await; - - asserting!("there is no GRPC connection") - .that(&build_res.err().unwrap().to_string()) - .is_equal_to(String::from( - "Can not connect to any grpc endpoint that was provided.", - )) - } - - #[tokio::test] - async fn network_grpcs_list_is_empty() { - let mut chain = cw_orch_daemon::networks::LOCAL_JUNO; - let grpcs: &Vec<&str> = &vec![]; - chain.grpc_urls = grpcs; - - let build_res = DaemonAsync::builder() - .chain(chain) - .deployment_id("v0.1.0") - .build() - .await; - - asserting!("GRPC list is empty") - .that(&build_res.err().unwrap().to_string()) - .is_equal_to(String::from("The list of grpc endpoints is empty")) - } -} diff --git a/cw-orch-daemon/src/senders/client.rs b/cw-orch-daemon/src/senders/client.rs new file mode 100644 index 000000000..591cd2463 --- /dev/null +++ b/cw-orch-daemon/src/senders/client.rs @@ -0,0 +1,294 @@ +use std::future::Future; + +use crate::error::DaemonError; +use cw_orch_core::environment::ChainInfoOwned; +/// A client for sending messages to a CosmosSDK chain. +/// If the `grpc` feature is enabled, the client will use gRPC to send messages. +/// If the `rpc` feature is enabled, the client will use HTTP to send messages. +#[derive(Debug, Clone)] +pub struct CosmosClient { + // TODO: if both enabled, default to RPC + #[cfg(feature = "grpc")] + inner: tonic::transport::Channel, + #[cfg(feature = "rpc")] + inner: HttpClient, +} + +impl CosmosClient { + /// Create a gRPC channel from the chain info + pub async fn from_chain_info(chain_info: &ChainInfoOwned) -> Result { + // RPC overwrites gRPC if both features are enabled + #[cfg(feature = "rpc")] + if cfg!(feature = "rpc") { + return Ok(Self { + inner: self::rpc::RpcChannel::connect(&chain_info.grpc_urls, &chain_info.chain_id) + .await?, + }); + } + + #[cfg(feature = "grpc")] + Ok(Self { + inner: self::grpc::GrpcChannel::connect( + &chain_info.grpc_urls, + &chain_info.chain_id, + ) + .await?, + }) + } +} + +trait Connect { + type Connection; + /// Connect to an endpoint and return a client + fn connect( + urls: &[String], + chain_id: &str, + ) -> impl Future> + Send; +} + +mod rpc { + use cosmrs::rpc::{Client, HttpClient}; + + use crate::error::DaemonError; + + use super::Connect; + + /// A helper for constructing a gRPC channel + pub struct RpcChannel {} + + impl Connect for RpcChannel { + type Connection = HttpClient; + + /// Connect to any of the provided gRPC endpoints + async fn connect(urls: &[String], chain_id: &str) -> Result { + let mut successful_connections = vec![]; + + for address in urls.iter() { + log::info!("Trying to connect to endpoint: {}", address); + + // try to connect to rpc endpoint + let maybe_client = HttpClient::new(address.as_str()); + + // connection succeeded or try the next rpc endpoint + let client = if maybe_client.is_ok() { + maybe_client? + } else { + continue; + }; + + // get client information for verification down below + let node_info = client.status().await?.node_info; + + // verify we are connected to the spected network + if node_info.network.as_str().ne(chain_id) { + log::error!( + "Network mismatch: connection:{} != config:{}", + node_info.network, + chain_id + ); + continue; + } + + // add endpoint to succesful connections + successful_connections.push(client) + } + + // we could not get any succesful connections + if successful_connections.is_empty() { + return Err(DaemonError::CannotConnectRPC); + } + + Ok(successful_connections.pop().unwrap()) + } + } + + #[cfg(test)] + mod tests { + /* + This test asserts breaking issues around the GRPC connection + */ + + use crate::DaemonAsync; + use speculoos::prelude::*; + + #[tokio::test] + async fn no_connection() { + let mut chain = cw_orch_daemon::networks::LOCAL_JUNO; + let grpcs = &vec!["https://127.0.0.1:99999"]; + chain.grpc_urls = grpcs; + + let build_res = DaemonAsync::builder(chain) + .deployment_id("v0.1.0") + .build() + .await; + + asserting!("there is no GRPC connection") + .that(&build_res.err().unwrap().to_string()) + .is_equal_to(String::from( + "Can not connect to any grpc endpoint that was provided.", + )) + } + + #[tokio::test] + async fn network_grpcs_list_is_empty() { + let mut chain = cw_orch_daemon::networks::LOCAL_JUNO; + let grpcs: &Vec<&str> = &vec![]; + chain.grpc_urls = grpcs; + + let build_res = DaemonAsync::builder(chain) + .deployment_id("v0.1.0") + .build() + .await; + + asserting!("GRPC list is empty") + .that(&build_res.err().unwrap().to_string()) + .is_equal_to(String::from("The list of grpc endpoints is empty")) + } + } +} + +mod grpc { + use cosmrs::proto::cosmos::base::tendermint::v1beta1::{ + service_client::ServiceClient, GetNodeInfoRequest, + }; + use cw_orch_core::{environment::ChainInfoOwned, log::connectivity_target}; + use tonic::transport::{Channel, ClientTlsConfig}; + + use crate::error::DaemonError; + + use super::Connect; + + /// A helper for constructing a gRPC channel + pub struct GrpcChannel {} + + impl Connect for GrpcChannel { + type Connection = Channel; + + /// Connect to any of the provided gRPC endpoints + async fn connect(grpc: &[String], chain_id: &str) -> Result { + if grpc.is_empty() { + return Err(DaemonError::GRPCListIsEmpty); + } + + let mut successful_connections = vec![]; + + for address in grpc.iter() { + log::debug!(target: &connectivity_target(), "Trying to connect to endpoint: {}", address); + + // get grpc endpoint + let endpoint = Channel::builder(address.clone().try_into().unwrap()); + + // try to connect to grpc endpoint + let maybe_client = ServiceClient::connect(endpoint.clone()).await; + + // connection succeeded + let mut client = if maybe_client.is_ok() { + maybe_client? + } else { + log::warn!( + "Cannot connect to gRPC endpoint: {}, {:?}", + address, + maybe_client.unwrap_err() + ); + + // try HTTPS approach + // https://github.com/hyperium/tonic/issues/363#issuecomment-638545965 + if !(address.contains("https") || address.contains("443")) { + continue; + }; + + log::debug!(target: &connectivity_target(), "Attempting to connect with TLS"); + + // re attempt to connect + let endpoint = endpoint.clone().tls_config(ClientTlsConfig::new())?; + let maybe_client = ServiceClient::connect(endpoint.clone()).await; + + // connection still fails + if maybe_client.is_err() { + log::warn!( + "Cannot connect to gRPC endpoint: {}, {:?}", + address, + maybe_client.unwrap_err() + ); + continue; + }; + + maybe_client? + }; + + // get client information for verification down below + let node_info = client + .get_node_info(GetNodeInfoRequest {}) + .await? + .into_inner(); + + // local juno does not return a proper ChainId with epoch format + // verify we are connected to the expected network + if node_info.default_node_info.as_ref().unwrap().network != chain_id { + log::error!( + "Network mismatch: connection:{} != config:{}", + node_info.default_node_info.as_ref().unwrap().network, + chain_id + ); + continue; + } + + // add endpoint to succesful connections + successful_connections.push(endpoint.connect().await?) + } + + // we could not get any succesful connections + if successful_connections.is_empty() { + return Err(DaemonError::CannotConnectGRPC); + } + + Ok(successful_connections.pop().unwrap()) + } + } + + #[cfg(test)] + mod tests { + /* + This test asserts breaking issues around the GRPC connection + */ + + use crate::DaemonAsync; + use speculoos::prelude::*; + + #[tokio::test] + #[serial_test::serial] + async fn no_connection() { + let mut chain = cw_orch_daemon::networks::LOCAL_JUNO; + let grpcs = &["https://127.0.0.1:99999"]; + chain.grpc_urls = grpcs; + + let build_res = DaemonAsync::builder(chain) + .deployment_id("v0.1.0") + .build_sender(()) + .await; + + asserting!("there is no GRPC connection") + .that(&build_res.err().unwrap().to_string()) + .is_equal_to(String::from( + "Can not connect to any grpc endpoint that was provided.", + )) + } + + #[tokio::test] + #[serial_test::serial] + async fn network_grpcs_list_is_empty() { + let mut chain = cw_orch_daemon::networks::LOCAL_JUNO; + let grpcs = &[]; + chain.grpc_urls = grpcs; + + let build_res = DaemonAsync::builder(chain) + .deployment_id("v0.1.0") + .build_sender(()) + .await; + + asserting!("GRPC list is empty") + .that(&build_res.err().unwrap().to_string()) + .is_equal_to(String::from("The list of grpc endpoints is empty")) + } + } +} diff --git a/cw-orch-daemon/src/senders/cosmos.rs b/cw-orch-daemon/src/senders/cosmos.rs index d1aa639b2..d84b5e846 100644 --- a/cw-orch-daemon/src/senders/cosmos.rs +++ b/cw-orch-daemon/src/senders/cosmos.rs @@ -6,7 +6,7 @@ use crate::{ account_sequence_strategy, assert_broadcast_code_cosm_response, insufficient_fee_strategy, TxBroadcaster, }, - CosmosOptions, GrpcChannel, + CosmosOptions, }; use crate::proto::injective::InjectiveEthAccount; @@ -43,7 +43,9 @@ use std::{str::FromStr, sync::Arc}; use cosmos_modules::vesting::PeriodicVestingAccount; use tonic::transport::Channel; -use super::{cosmos_options::CosmosWalletKey, query::QuerySender, tx::TxSender}; +use super::{ + client::CosmosClient, cosmos_options::CosmosWalletKey, query::QuerySender, tx::TxSender, +}; const GAS_BUFFER: f64 = 1.3; const BUFFER_THRESHOLD: u64 = 200_000; @@ -57,8 +59,7 @@ pub type Wallet = CosmosSender; #[derive(Clone)] pub struct CosmosSender { pub private_key: PrivateKey, - /// gRPC channel - pub grpc_channel: Channel, + pub client: CosmosClient, /// Information about the chain pub chain_info: Arc, pub(crate) options: CosmosOptions, @@ -105,7 +106,7 @@ impl Wallet { Ok(Self { chain_info: chain_info.clone(), - grpc_channel: GrpcChannel::from_chain_info(chain_info.as_ref()).await?, + client: CosmosClient::from_chain_info(chain_info.as_ref()).await?, private_key: pk, secp, options, diff --git a/cw-orch-daemon/src/senders/mod.rs b/cw-orch-daemon/src/senders/mod.rs index e637e4470..710fb1de9 100644 --- a/cw-orch-daemon/src/senders/mod.rs +++ b/cw-orch-daemon/src/senders/mod.rs @@ -4,6 +4,7 @@ pub mod query; pub mod tx; // Senders +mod client; mod cosmos; mod cosmos_batch; mod cosmos_options; @@ -14,4 +15,5 @@ pub use { cosmos_batch::{options::CosmosBatchOptions, BatchDaemon, CosmosBatchSender}, cosmos_options::{CosmosOptions, CosmosWalletKey}, query_only::{QueryOnlyDaemon, QueryOnlySender}, + client::CosmosClient, }; diff --git a/cw-orch-daemon/src/senders/query_only.rs b/cw-orch-daemon/src/senders/query_only.rs index 1f30ede1d..58d37d8ec 100644 --- a/cw-orch-daemon/src/senders/query_only.rs +++ b/cw-orch-daemon/src/senders/query_only.rs @@ -6,7 +6,7 @@ use cw_orch_core::environment::ChainInfoOwned; use tonic::transport::Channel; -use super::{builder::SenderBuilder, query::QuerySender}; +use super::{builder::SenderBuilder, client::CosmosClient, query::QuerySender}; /// Daemon that does not support signing. /// Will err on any attempt to sign a transaction or retrieve a sender address. @@ -15,8 +15,8 @@ pub type QueryOnlyDaemon = DaemonBase; /// Signer of the transactions and helper for address derivation #[derive(Clone)] pub struct QueryOnlySender { - /// gRPC channel - pub channel: Channel, + /// CosmosSDK Client + pub client: CosmosClient, /// Information about the chain pub chain_info: Arc, } @@ -26,10 +26,10 @@ impl SenderBuilder for () { type Sender = QueryOnlySender; async fn build(&self, chain_info: &Arc) -> Result { - let channel = GrpcChannel::from_chain_info(chain_info.as_ref()).await?; + let client = CosmosClient::from_chain_info(chain_info.as_ref()).await?; Ok(QueryOnlySender { - channel, + client, chain_info: chain_info.clone(), }) } diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index 9e4477a0b..25961b9ca 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -1,11 +1,11 @@ use super::error::DaemonError; use crate::networks::ChainKind; +use crate::env::{default_state_folder, DaemonEnvVars}; #[cfg(feature = "grpc")] use crate::grpc_channel::GrpcChannel; #[cfg(feature = "rpc")] use crate::rpc_channel::RpcChannel; -use crate::env::{default_state_folder, DaemonEnvVars}; use crate::{json_lock::JsonLockedState, networks::ChainKind}; use cosmwasm_std::Addr; @@ -14,8 +14,8 @@ use cw_orch_core::{environment::StateInterface, log::local_target, CwEnvError}; use once_cell::sync::Lazy; use serde::Serialize; use serde_json::{json, Value}; -use std::{collections::HashMap, env, fs::File, path::Path}; use std::sync::Arc; +use std::{collections::HashMap, env, fs::File, path::Path}; use std::{ collections::{HashMap, HashSet}, path::Path, diff --git a/packages/cw-orch-networks/src/lib.rs b/packages/cw-orch-networks/src/lib.rs index 4fda3fee8..f52c972f1 100644 --- a/packages/cw-orch-networks/src/lib.rs +++ b/packages/cw-orch-networks/src/lib.rs @@ -1 +1,23 @@ pub mod networks; + +pub use cw_orch_core::environment::{ChainInfo, ChainKind, NetworkInfo}; + +use networks::SUPPORTED_NETWORKS; + +/// A helper function to retrieve a [`ChainInfo`] struct for a given chain-id. +/// +/// ## Example +/// ```rust,no_run +/// use cw_orch_networks::networks::{parse_network, ChainInfo}; +/// +/// let juno_mainnet: ChainInfo = parse_network("juno-1").unwrap(); +/// ``` +/// --- +/// supported chains are defined by the `SUPPORT_NETWORKS` variable +pub fn parse_network(net_id: &str) -> Result { + SUPPORTED_NETWORKS + .iter() + .find(|net| net.chain_id == net_id) + .cloned() + .ok_or(format!("Network not found: {}", net_id)) +} diff --git a/packages/cw-orch-networks/src/networks/archway.rs b/packages/cw-orch-networks/src/networks/archway.rs index 7762e5fc6..3d586dc25 100644 --- a/packages/cw-orch-networks/src/networks/archway.rs +++ b/packages/cw-orch-networks/src/networks/archway.rs @@ -1,4 +1,4 @@ -use crate::networks::{ChainInfo, ChainKind, NetworkInfo}; +use crate::{ChainInfo, ChainKind, NetworkInfo}; // ANCHOR: archway pub const ARCHWAY_NETWORK: NetworkInfo = NetworkInfo { diff --git a/packages/cw-orch-networks/src/networks/doravota.rs b/packages/cw-orch-networks/src/networks/doravota.rs index 5583a4867..3c9eab4df 100644 --- a/packages/cw-orch-networks/src/networks/doravota.rs +++ b/packages/cw-orch-networks/src/networks/doravota.rs @@ -14,6 +14,7 @@ pub const VOTA_ASH: ChainInfo = ChainInfo { chain_id: "vota-ash", gas_denom: "peaka", gas_price: 100000000000f64, + rpc_urls: &[], grpc_urls: &["https://vota-grpc.dorafactory.org:443"], network_info: DORAVOTA_NETWORK, lcd_url: None, @@ -25,6 +26,7 @@ pub const VOTA_TESTNET: ChainInfo = ChainInfo { chain_id: "vota-testnet", gas_denom: "peaka", gas_price: 100000000000f64, + rpc_urls: &[], grpc_urls: &["https://vota-testnet-grpc.dorafactory.org:443"], network_info: DORAVOTA_NETWORK, lcd_url: None, diff --git a/packages/cw-orch-networks/src/networks/injective.rs b/packages/cw-orch-networks/src/networks/injective.rs index 12a95f3c7..80c9a3fde 100644 --- a/packages/cw-orch-networks/src/networks/injective.rs +++ b/packages/cw-orch-networks/src/networks/injective.rs @@ -1,4 +1,4 @@ -use crate::networks::{ChainInfo, ChainKind, NetworkInfo}; +use crate::{ChainInfo, ChainKind, NetworkInfo}; // ANCHOR: injective pub const INJECTIVE_NETWORK: NetworkInfo = NetworkInfo { diff --git a/packages/cw-orch-networks/src/networks/kujira.rs b/packages/cw-orch-networks/src/networks/kujira.rs index 15f194314..5b24bc71a 100644 --- a/packages/cw-orch-networks/src/networks/kujira.rs +++ b/packages/cw-orch-networks/src/networks/kujira.rs @@ -1,4 +1,4 @@ -use crate::networks::{ChainInfo, ChainKind, NetworkInfo}; +use crate::{ChainInfo, ChainKind, NetworkInfo}; // ANCHOR: kujira pub const KUJIRA_NETWORK: NetworkInfo = NetworkInfo { diff --git a/packages/cw-orch-networks/src/networks/landslide.rs b/packages/cw-orch-networks/src/networks/landslide.rs index 7d05fb0d9..4002cbdd7 100644 --- a/packages/cw-orch-networks/src/networks/landslide.rs +++ b/packages/cw-orch-networks/src/networks/landslide.rs @@ -12,6 +12,7 @@ pub const LOCAL_LANDSLIDE: ChainInfo = ChainInfo { chain_id: "landslide-test", gas_denom: "stake", gas_price: 1_f64, + rpc_urls: &[], grpc_urls: &["http://127.0.0.1:9090"], network_info: LANDSLIDE_NETWORK, lcd_url: None, diff --git a/packages/cw-orch-networks/src/networks/migaloo.rs b/packages/cw-orch-networks/src/networks/migaloo.rs index aa5c3f02b..85f0be97e 100644 --- a/packages/cw-orch-networks/src/networks/migaloo.rs +++ b/packages/cw-orch-networks/src/networks/migaloo.rs @@ -1,4 +1,4 @@ -use crate::networks::{ChainInfo, ChainKind, NetworkInfo}; +use crate::{ChainInfo, ChainKind, NetworkInfo}; // ANCHOR: migaloo pub const MIGALOO_NETWORK: NetworkInfo = NetworkInfo { diff --git a/packages/cw-orch-networks/src/networks/mod.rs b/packages/cw-orch-networks/src/networks/mod.rs index a01d80546..e84f292f2 100644 --- a/packages/cw-orch-networks/src/networks/mod.rs +++ b/packages/cw-orch-networks/src/networks/mod.rs @@ -19,7 +19,7 @@ pub mod wasm; pub mod xion; pub use archway::{ARCHWAY_1, CONSTANTINE_3}; -pub use cw_orch_core::environment::{ChainInfo, ChainKind, NetworkInfo}; +use cw_orch_core::environment::ChainInfo; pub use doravota::{VOTA_ASH, VOTA_TESTNET}; pub use injective::{INJECTIVE_1, INJECTIVE_888}; pub use juno::{JUNO_1, LOCAL_JUNO, UNI_6}; @@ -35,24 +35,6 @@ pub use terra::{LOCAL_TERRA, PHOENIX_1, PISCO_1}; pub use wasm::LOCAL_WASMD; pub use xion::XION_TESTNET_1; -/// A helper function to retrieve a [`ChainInfo`] struct for a given chain-id. -/// -/// ## Example -/// ```rust,no_run -/// use cw_orch_networks::networks::{parse_network, ChainInfo}; -/// let juno_mainnet: ChainInfo = parse_network("juno-1").unwrap(); -/// ``` -/// --- -/// supported chains are defined by the `SUPPORT_NETWORKS` variable - -pub fn parse_network(net_id: &str) -> Result { - SUPPORTED_NETWORKS - .iter() - .find(|net| net.chain_id == net_id) - .cloned() - .ok_or(format!("Network not found: {}", net_id)) -} - pub const SUPPORTED_NETWORKS: &[ChainInfo] = &[ UNI_6, JUNO_1, diff --git a/packages/cw-orch-networks/src/networks/neutron.rs b/packages/cw-orch-networks/src/networks/neutron.rs index b418b3056..a5ad5bbef 100644 --- a/packages/cw-orch-networks/src/networks/neutron.rs +++ b/packages/cw-orch-networks/src/networks/neutron.rs @@ -1,4 +1,4 @@ -use crate::networks::{ChainInfo, ChainKind, NetworkInfo}; +use crate::{ChainInfo, ChainKind, NetworkInfo}; // ANCHOR: neutron pub const NEUTRON_NETWORK: NetworkInfo = NetworkInfo { diff --git a/packages/cw-orch-networks/src/networks/nibiru.rs b/packages/cw-orch-networks/src/networks/nibiru.rs index 9ec1bd43d..0d79af930 100644 --- a/packages/cw-orch-networks/src/networks/nibiru.rs +++ b/packages/cw-orch-networks/src/networks/nibiru.rs @@ -12,6 +12,7 @@ pub const NIBIRU_ITN_2: ChainInfo = ChainInfo { chain_id: "nibiru-itn-2", gas_denom: "unibi", gas_price: 0.025, + rpc_urls: &[], grpc_urls: &["https://nibiru-testnet.grpc.kjnodes.com:443"], network_info: NIBIRU_NETWORK, lcd_url: None, diff --git a/packages/cw-orch-networks/src/networks/osmosis.rs b/packages/cw-orch-networks/src/networks/osmosis.rs index 46638f0a6..d48551161 100644 --- a/packages/cw-orch-networks/src/networks/osmosis.rs +++ b/packages/cw-orch-networks/src/networks/osmosis.rs @@ -13,7 +13,7 @@ pub const OSMOSIS_1: ChainInfo = ChainInfo { gas_denom: "uosmo", gas_price: 0.025, grpc_urls: &["https://grpc.osmosis.zone:443"], - + rpc_urls: &[], network_info: OSMO_NETWORK, lcd_url: None, fcd_url: None, diff --git a/packages/cw-orch-networks/src/networks/rollkit.rs b/packages/cw-orch-networks/src/networks/rollkit.rs index 994f6bd49..2b6dae525 100644 --- a/packages/cw-orch-networks/src/networks/rollkit.rs +++ b/packages/cw-orch-networks/src/networks/rollkit.rs @@ -12,6 +12,7 @@ pub const LOCAL_ROLLKIT: ChainInfo = ChainInfo { chain_id: "celeswasm", gas_denom: "uwasm", gas_price: 0.025, + rpc_urls: &[], grpc_urls: &["http://localhost:9290"], network_info: ROLLKIT_NETWORK, lcd_url: None, @@ -23,6 +24,7 @@ pub const ROLLKIT_TESTNET: ChainInfo = ChainInfo { chain_id: "rosm", gas_denom: "urosm", gas_price: 0.025, + rpc_urls: &[], grpc_urls: &["http://grpc.rosm.rollkit.dev:9290"], network_info: ROLLKIT_NETWORK, lcd_url: None, diff --git a/packages/cw-orch-networks/src/networks/sei.rs b/packages/cw-orch-networks/src/networks/sei.rs index 2d8b0dbba..40df8d323 100644 --- a/packages/cw-orch-networks/src/networks/sei.rs +++ b/packages/cw-orch-networks/src/networks/sei.rs @@ -1,4 +1,4 @@ -use crate::networks::{ChainInfo, ChainKind, NetworkInfo}; +use crate::{ChainInfo, ChainKind, NetworkInfo}; // ANCHOR: sei pub const SEI_NETWORK: NetworkInfo = NetworkInfo { @@ -48,6 +48,7 @@ pub const PACIFIC_1: ChainInfo = ChainInfo { chain_id: "pacific-1", gas_denom: "usei", gas_price: 0.1, + rpc_urls: &[], grpc_urls: &["http://sei-grpc.polkachu.com:11990"], network_info: SEI_NETWORK, lcd_url: None, diff --git a/packages/cw-orch-networks/src/networks/wasm.rs b/packages/cw-orch-networks/src/networks/wasm.rs index 9a9fa1b32..71482e600 100644 --- a/packages/cw-orch-networks/src/networks/wasm.rs +++ b/packages/cw-orch-networks/src/networks/wasm.rs @@ -1,9 +1,9 @@ -use crate::chain_info::{ChainInfo, ChainKind, NetworkInfo}; +use crate::{ChainInfo, ChainKind, NetworkInfo}; // ANCHOR: wasmd /// Wasmd local network pub const WASMD_NETWORK: NetworkInfo = NetworkInfo { - id: "wasm", + chain_name: "wasm", pub_address_prefix: "wasm", coin_type: 118u32, }; diff --git a/packages/cw-orch-networks/src/networks/xion.rs b/packages/cw-orch-networks/src/networks/xion.rs index fe5f6a608..b06efb1b2 100644 --- a/packages/cw-orch-networks/src/networks/xion.rs +++ b/packages/cw-orch-networks/src/networks/xion.rs @@ -12,6 +12,7 @@ pub const XION_TESTNET_1: ChainInfo = ChainInfo { chain_id: "xion-testnet-1", gas_denom: "uxion", gas_price: 0.0, + rpc_urls: &[], grpc_urls: &["http://xion-testnet-grpc.polkachu.com:22390"], network_info: XION_NETWORK, lcd_url: None, From e851876d53175241ad116b40ded3cae90d3afbfe Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Tue, 23 Jul 2024 11:38:31 +0200 Subject: [PATCH 8/8] remove duplicate macro --- cw-orch-daemon/src/queriers.rs | 22 ---------------------- cw-orch-daemon/src/queriers/clients/mod.rs | 22 ---------------------- 2 files changed, 44 deletions(-) diff --git a/cw-orch-daemon/src/queriers.rs b/cw-orch-daemon/src/queriers.rs index b5c436bd6..02c05195b 100644 --- a/cw-orch-daemon/src/queriers.rs +++ b/cw-orch-daemon/src/queriers.rs @@ -43,28 +43,6 @@ pub trait DaemonQuerier { fn new(channel: tonic::transport::Channel) -> Self; } -/// macro for constructing and performing a query on a CosmosSDK module. -#[macro_export] -macro_rules! cosmos_query { - ($self:ident, $module:ident, $func_name:ident, $request_type:ident { $($field:ident : $value:expr),* $(,)? }) => { - { - use $crate::cosmos_modules::$module::{ - query_client::QueryClient, $request_type, - }; - let mut client = QueryClient::new($self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = $request_type { $($field : $value),* }; - let response = client.$func_name(request.clone()).await?.into_inner(); - ::log::trace!( - "cosmos_query: {:?} resulted in: {:?}", - request, - response - ); - response - } -}; -} - mod authz; mod env; // mod bank; diff --git a/cw-orch-daemon/src/queriers/clients/mod.rs b/cw-orch-daemon/src/queriers/clients/mod.rs index b888ff429..85b5586e8 100644 --- a/cw-orch-daemon/src/queriers/clients/mod.rs +++ b/cw-orch-daemon/src/queriers/clients/mod.rs @@ -22,28 +22,6 @@ //! # }) //! ``` -/// macro for constructing and performing a query on a CosmosSDK module. -#[macro_export] -macro_rules! cosmos_query { - ($self:ident, $module:ident, $func_name:ident, $request_type:ident { $($field:ident : $value:expr),* $(,)? }) => { - { - use $crate::cosmos_modules::$module::{ - query_client::QueryClient, $request_type, - }; - let mut client = QueryClient::new($self.channel.clone()); - #[allow(clippy::redundant_field_names)] - let request = $request_type { $($field : $value),* }; - let response = client.$func_name(request.clone()).await?.into_inner(); - ::log::trace!( - "cosmos_query: {:?} resulted in: {:?}", - request, - response - ); - response - } -}; -} - mod auth; mod bank; mod cosmwasm;