From f7dc1459d298db1edde9e5c8ffdda94f2e210c0d Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 15 Mar 2024 16:41:49 +0100 Subject: [PATCH 1/5] loosen trait bounds on Contract implementation --- packages/cw-orch-contract-derive/src/lib.rs | 24 +-- .../src/contract/contract_instance.rs | 149 +++++++++--------- .../src/contract/interface_traits.rs | 4 +- 3 files changed, 91 insertions(+), 86 deletions(-) diff --git a/packages/cw-orch-contract-derive/src/lib.rs b/packages/cw-orch-contract-derive/src/lib.rs index 842ad0f58..b1f6a40fe 100644 --- a/packages/cw-orch-contract-derive/src/lib.rs +++ b/packages/cw-orch-contract-derive/src/lib.rs @@ -123,7 +123,7 @@ The interface can be linked to its source code by implementing the `Uploadable` ```ignore use cw_orch::prelude::*; -impl Uploadable for Cw20 { +impl Uploadable for Cw20 { fn wrapper(&self) -> ::ContractSource { Box::new( ContractWrapper::new_with_empty( @@ -204,7 +204,7 @@ pub fn interface(attrs: TokenStream, input: TokenStream) -> TokenStream { let name = cw_orch_struct.ident.clone(); let default_num = if let Some(id_expr) = default_id { quote!( - impl #name { + impl #name { pub fn new(chain: Chain) -> Self { Self( ::cw_orch::contract::Contract::new(#id_expr, chain) @@ -214,7 +214,7 @@ pub fn interface(attrs: TokenStream, input: TokenStream) -> TokenStream { ) } else { quote!( - impl #name { + impl #name { pub fn new(contract_id: impl ToString, chain: Chain) -> Self { Self( ::cw_orch::contract::Contract::new(contract_id, chain) @@ -227,11 +227,11 @@ pub fn interface(attrs: TokenStream, input: TokenStream) -> TokenStream { #[derive( ::std::clone::Clone, )] - pub struct #name(::cw_orch::contract::Contract, #(#all_phantom_markers,)*); + pub struct #name(::cw_orch::contract::Contract, #(#all_phantom_markers,)*); #default_num - impl ::cw_orch::prelude::ContractInstance for #name { + impl ::cw_orch::prelude::ContractInstance for #name { fn as_instance(&self) -> &::cw_orch::contract::Contract { &self.0 } @@ -240,19 +240,19 @@ pub fn interface(attrs: TokenStream, input: TokenStream) -> TokenStream { } } - impl ::cw_orch::prelude::InstantiableContract for #name #all_debug_serialize { - type InstantiateMsg = #init; + impl ::cw_orch::prelude::QueryableContract for #name #all_debug_serialize { + type QueryMsg = #query; } - impl ::cw_orch::prelude::ExecutableContract for #name #all_debug_serialize { - type ExecuteMsg = #exec; + impl ::cw_orch::prelude::InstantiableContract for #name #all_debug_serialize { + type InstantiateMsg = #init; } - impl ::cw_orch::prelude::QueryableContract for #name #all_debug_serialize { - type QueryMsg = #query; + impl ::cw_orch::prelude::ExecutableContract for #name #all_debug_serialize { + type ExecuteMsg = #exec; } - impl ::cw_orch::prelude::MigratableContract for #name #all_debug_serialize { + impl ::cw_orch::prelude::MigratableContract for #name #all_debug_serialize { type MigrateMsg = #migrate; } ); diff --git a/packages/cw-orch-core/src/contract/contract_instance.rs b/packages/cw-orch-core/src/contract/contract_instance.rs index 8e2fdee9d..76ff7fda2 100644 --- a/packages/cw-orch-core/src/contract/contract_instance.rs +++ b/packages/cw-orch-core/src/contract/contract_instance.rs @@ -1,7 +1,7 @@ //! Main functional component for interacting with a contract. Used as the base for generating contract interfaces. use super::interface_traits::Uploadable; use crate::{ - environment::{IndexResponse, StateInterface, TxHandler, TxResponse}, + environment::{ChainState, IndexResponse, StateInterface, TxHandler, TxResponse}, error::CwEnvError, log::{contract_target, transaction_target}, CwOrchEnvVars, @@ -15,7 +15,7 @@ use std::fmt::Debug; /// An instance of a contract. Contains references to the execution environment (chain) and a local state (state) /// The state is used to store contract addresses/code-ids #[derive(Clone)] -pub struct Contract { +pub struct Contract { /// ID of the contract, used to retrieve addr/code-id pub id: String, /// Chain object that handles tx execution and queries. @@ -26,8 +26,7 @@ pub struct Contract { pub default_address: Option, } -/// Expose chain and state function to call them on the contract -impl Contract { +impl Contract { /// Creates a new contract instance pub fn new(id: impl ToString, chain: Chain) -> Self { Contract { @@ -43,6 +42,19 @@ impl Contract { &self.chain } + /// Sets default code_id for contract (used only if not present in state) + pub fn set_default_code_id(&mut self, code_id: u64) { + self.default_code_id = Some(code_id); + } + + /// Sets default address for contract (used only if not present in state) + pub fn set_default_address(&mut self, address: &Addr) { + self.default_address = Some(address.clone()); + } +} + +// State interfaces +impl Contract { /// Sets the address of the contract in the local state pub fn with_address(self, address: Option<&Addr>) -> Self { if let Some(address) = address { @@ -51,6 +63,67 @@ impl Contract { self } + /// Returns state address for contract + pub fn address(&self) -> Result { + let state_address = self.chain.state().get_address(&self.id); + // If the state address is not present, we default to the default address or an error + state_address.or(self + .default_address + .clone() + .ok_or(CwEnvError::AddrNotInStore(self.id.clone()))) + } + + /// Sets state address for contract + pub fn set_address(&self, address: &Addr) { + self.chain.state().set_address(&self.id, address) + } + + /// Returns state code_id for contract + pub fn code_id(&self) -> Result { + let state_code_id = self.chain.state().get_code_id(&self.id); + // If the code_ids is not present, we default to the default code_id or an error + state_code_id.or(self + .default_code_id + .ok_or(CwEnvError::CodeIdNotInStore(self.id.clone()))) + } + + /// Sets state code_id for contract + pub fn set_code_id(&self, code_id: u64) { + self.chain.state().set_code_id(&self.id, code_id) + } +} + +impl Contract { + /// Query the contract + pub fn query( + &self, + query_msg: &Q, + ) -> Result { + log::debug!( + target: &contract_target(), + "[{}][Query][{}] {}", + self.id, + self.address()?, + log_serialize_message(query_msg)? + ); + + let resp = self + .chain + .query(query_msg, &self.address()?) + .map_err(Into::into)?; + + log::debug!( + target: &contract_target(), + "[{}][Queried][{}] response {}", + self.id, + self.address()?, + log_serialize_message(&resp)? + ); + Ok(resp) + } +} + +impl Contract { // Chain interfaces /// Upload a contract given its source @@ -223,34 +296,6 @@ impl Contract { Ok(resp) } - /// Query the contract - pub fn query( - &self, - query_msg: &Q, - ) -> Result { - log::debug!( - target: &contract_target(), - "[{}][Query][{}] {}", - self.id, - self.address()?, - log_serialize_message(query_msg)? - ); - - let resp = self - .chain - .query(query_msg, &self.address()?) - .map_err(Into::into)?; - - log::debug!( - target: &contract_target(), - "[{}][Queried][{}] response {}", - self.id, - self.address()?, - log_serialize_message(&resp)? - ); - Ok(resp) - } - /// Migrates the contract pub fn migrate( &self, @@ -292,46 +337,6 @@ impl Contract { ); Ok(resp) } - - // State interfaces - /// Returns state address for contract - pub fn address(&self) -> Result { - let state_address = self.chain.state().get_address(&self.id); - // If the state address is not present, we default to the default address or an error - state_address.or(self - .default_address - .clone() - .ok_or(CwEnvError::AddrNotInStore(self.id.clone()))) - } - - /// Sets state address for contract - pub fn set_address(&self, address: &Addr) { - self.chain.state().set_address(&self.id, address) - } - - /// Sets default address for contract (used only if not present in state) - pub fn set_default_address(&mut self, address: &Addr) { - self.default_address = Some(address.clone()); - } - - /// Returns state code_id for contract - pub fn code_id(&self) -> Result { - let state_code_id = self.chain.state().get_code_id(&self.id); - // If the code_ids is not present, we default to the default code_id or an error - state_code_id.or(self - .default_code_id - .ok_or(CwEnvError::CodeIdNotInStore(self.id.clone()))) - } - - /// Sets state code_id for contract - pub fn set_code_id(&self, code_id: u64) { - self.chain.state().set_code_id(&self.id, code_id) - } - - /// Sets default code_id for contract (used only if not present in state) - pub fn set_default_code_id(&mut self, code_id: u64) { - self.default_code_id = Some(code_id); - } } /// Helper to serialize objects (JSON or Rust DEBUG) diff --git a/packages/cw-orch-core/src/contract/interface_traits.rs b/packages/cw-orch-core/src/contract/interface_traits.rs index dabd08dca..b37061227 100644 --- a/packages/cw-orch-core/src/contract/interface_traits.rs +++ b/packages/cw-orch-core/src/contract/interface_traits.rs @@ -1,6 +1,6 @@ use super::{Contract, WasmPath}; use crate::{ - environment::{CwEnv, QueryHandler, TxHandler, TxResponse, WasmQuerier}, + environment::{ChainState, CwEnv, TxHandler, TxResponse, WasmQuerier}, error::CwEnvError, log::contract_target, }; @@ -11,7 +11,7 @@ use std::fmt::Debug; // Fn for custom implementation to return ContractInstance /// Interface to the underlying `Contract` struct. Implemented automatically when using our macros. -pub trait ContractInstance { +pub trait ContractInstance { /// Return a reference to the underlying contract instance. fn as_instance(&self) -> &Contract; From 289f74d9e4f1e97b02c887cdc4ccd3f873da1169 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 15 Mar 2024 16:45:02 +0100 Subject: [PATCH 2/5] nit --- cw-orch/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cw-orch/src/lib.rs b/cw-orch/src/lib.rs index 703afc5f5..8ee11e1b8 100644 --- a/cw-orch/src/lib.rs +++ b/cw-orch/src/lib.rs @@ -15,7 +15,7 @@ pub mod prelude; pub use cw_orch_core::build; pub use cw_orch_core::contract; -/// Related to execution environents and variables +/// Related to execution environments and variables pub mod environment { pub use cw_orch_core::env::{default_state_folder, CwOrchEnvVars}; pub use cw_orch_core::environment::*; From d4cfcf906c33dad930f0c6e3a7acdca199412336 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Thu, 21 Mar 2024 14:48:11 +0100 Subject: [PATCH 3/5] add async example --- contracts/counter/examples/async.rs | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 contracts/counter/examples/async.rs diff --git a/contracts/counter/examples/async.rs b/contracts/counter/examples/async.rs new file mode 100644 index 000000000..057aa0fb5 --- /dev/null +++ b/contracts/counter/examples/async.rs @@ -0,0 +1,34 @@ +// ANCHOR: full_counter_example +use counter_contract::{ + msg::InstantiateMsg, CounterContract, CounterExecuteMsgFns, AsyncCounterQueryMsgFns, +}; +use cw_orch::{anyhow, prelude::*, tokio}; +use tokio::runtime::Runtime; + +const LOCAL_MNEMONIC: &str = "clip hire initial neck maid actor venue client foam budget lock catalog sweet steak waste crater broccoli pipe steak sister coyote moment obvious choose"; + +#[tokio::main] +pub async fn main() -> anyhow::Result<()> { + std::env::set_var("TEST_MNEMONIC", LOCAL_MNEMONIC); + // ANCHOR: chain_construction + dotenv::dotenv().ok(); // Used to load the `.env` file if any + pretty_env_logger::init(); // Used to log contract and chain interactions + + let network = networks::UNI_6; + let chain = DaemonAsyncBuilder::default() + .chain(network) + .build().await?; + // ANCHOR_END: chain_construction + + // ANCHOR: contract_interaction + + let counter = CounterContract::new(chain); + + let count = counter.get_count().await?; + assert_eq!(count.count, 1); + // ANCHOR_END: clean_example + // ANCHOR_END: contract_interaction + + Ok(()) +} +// ANCHOR_END: full_counter_example From a7b45737388448ddfdf12a3837949c986cdc1953 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Thu, 21 Mar 2024 14:58:21 +0100 Subject: [PATCH 4/5] implement async query --- contracts/counter/Cargo.toml | 4 + contracts/counter/examples/async.rs | 6 +- contracts/counter/src/lib.rs | 5 +- cw-orch-daemon/src/core.rs | 59 +++++--- cw-orch/src/prelude.rs | 4 +- packages/cw-orch-contract-derive/src/lib.rs | 37 +++-- .../src/contract/contract_instance.rs | 35 ++++- .../src/contract/interface_traits.rs | 20 ++- packages/cw-orch-core/src/environment/mod.rs | 2 +- .../src/environment/queriers/wasm.rs | 8 ++ packages/cw-orch-fns-derive/src/lib.rs | 9 +- packages/cw-orch-fns-derive/src/query_fns.rs | 136 +++++++++++++++++- 12 files changed, 275 insertions(+), 50 deletions(-) diff --git a/contracts/counter/Cargo.toml b/contracts/counter/Cargo.toml index 32afa7a7d..358b7d687 100644 --- a/contracts/counter/Cargo.toml +++ b/contracts/counter/Cargo.toml @@ -9,6 +9,10 @@ exclude = [".env"] [lib] crate-type = ["cdylib", "rlib"] +[[example]] +name = "async" +required-features = ["interface"] + [features] default = ["export"] export = [] diff --git a/contracts/counter/examples/async.rs b/contracts/counter/examples/async.rs index 057aa0fb5..4943fd0dc 100644 --- a/contracts/counter/examples/async.rs +++ b/contracts/counter/examples/async.rs @@ -1,6 +1,6 @@ // ANCHOR: full_counter_example use counter_contract::{ - msg::InstantiateMsg, CounterContract, CounterExecuteMsgFns, AsyncCounterQueryMsgFns, + msg::InstantiateMsg, AsyncCounterQueryMsgFns, CounterContract, CounterExecuteMsgFns, }; use cw_orch::{anyhow, prelude::*, tokio}; use tokio::runtime::Runtime; @@ -15,9 +15,7 @@ pub async fn main() -> anyhow::Result<()> { pretty_env_logger::init(); // Used to log contract and chain interactions let network = networks::UNI_6; - let chain = DaemonAsyncBuilder::default() - .chain(network) - .build().await?; + let chain = DaemonAsyncBuilder::default().chain(network).build().await?; // ANCHOR_END: chain_construction // ANCHOR: contract_interaction diff --git a/contracts/counter/src/lib.rs b/contracts/counter/src/lib.rs index 26a0f19b6..4769d4dfd 100644 --- a/contracts/counter/src/lib.rs +++ b/contracts/counter/src/lib.rs @@ -7,7 +7,10 @@ pub mod state; pub use crate::error::ContractError; // ANCHOR: fn_re_export -pub use crate::msg::{ExecuteMsgFns as CounterExecuteMsgFns, QueryMsgFns as CounterQueryMsgFns}; +pub use crate::msg::{ + AsyncQueryMsgFns as AsyncCounterQueryMsgFns, ExecuteMsgFns as CounterExecuteMsgFns, + QueryMsgFns as CounterQueryMsgFns, +}; // ANCHOR_END: fn_re_export // ANCHOR: custom_interface diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index 1897a7b2e..e47b09f4b 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -14,7 +14,7 @@ use cosmrs::{ use cosmwasm_std::{Addr, Binary, Coin}; use cw_orch_core::{ contract::interface_traits::Uploadable, - environment::{ChainState, IndexResponse}, + environment::{AsyncWasmQuerier, ChainState, IndexResponse, Querier}, log::transaction_target, }; use flate2::{write, Compression}; @@ -127,6 +127,23 @@ impl DaemonAsync { Ok(result) } + /// Query a contract. + pub async fn query( + &self, + query_msg: &Q, + contract_address: &Addr, + ) -> Result { + let mut client = cosmos_modules::cosmwasm::query_client::QueryClient::new(self.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())?) + } + /// Instantiate a contract. pub async fn instantiate( &self, @@ -192,23 +209,6 @@ impl DaemonAsync { Ok(result) } - /// Query a contract. - pub async fn query( - &self, - query_msg: &Q, - contract_address: &Addr, - ) -> Result { - let mut client = cosmos_modules::cosmwasm::query_client::QueryClient::new(self.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, @@ -315,6 +315,29 @@ impl DaemonAsync { } } +impl Querier for DaemonAsync { + type Error = DaemonError; +} + +impl AsyncWasmQuerier for DaemonAsync { + /// Query a contract. + async fn smart_query( + &self, + address: impl Into, + query_msg: &Q, + ) -> Result { + let mut client = cosmos_modules::cosmwasm::query_client::QueryClient::new(self.channel()); + let resp = client + .smart_contract_state(cosmos_modules::cosmwasm::QuerySmartContractStateRequest { + address: address.into(), + query_data: serde_json::to_vec(&query_msg)?, + }) + .await?; + + Ok(from_str(from_utf8(&resp.into_inner().data).unwrap())?) + } +} + pub(crate) fn parse_cw_coins( coins: &[cosmwasm_std::Coin], ) -> Result, DaemonError> { diff --git a/cw-orch/src/prelude.rs b/cw-orch/src/prelude.rs index 533d30a93..fe3020235 100644 --- a/cw-orch/src/prelude.rs +++ b/cw-orch/src/prelude.rs @@ -12,8 +12,8 @@ // Contract traits pub use crate::contract::interface_traits::{ - CallAs, ConditionalMigrate, ConditionalUpload, ContractInstance, CwOrchExecute, - CwOrchInstantiate, CwOrchMigrate, CwOrchQuery, CwOrchUpload, ExecutableContract, + AsyncCwOrchQuery, CallAs, ConditionalMigrate, ConditionalUpload, ContractInstance, + CwOrchExecute, CwOrchInstantiate, CwOrchMigrate, CwOrchQuery, CwOrchUpload, ExecutableContract, InstantiableContract, MigratableContract, QueryableContract, Uploadable, }; diff --git a/packages/cw-orch-contract-derive/src/lib.rs b/packages/cw-orch-contract-derive/src/lib.rs index f0c0b7121..5d63071b4 100644 --- a/packages/cw-orch-contract-derive/src/lib.rs +++ b/packages/cw-orch-contract-derive/src/lib.rs @@ -248,24 +248,23 @@ pub fn interface(attrs: TokenStream, input: TokenStream) -> TokenStream { &mut self.0 } } - } - #[cfg(not(target_arch = "wasm32"))] - impl ::cw_orch::prelude::QueryableContract for #name #all_debug_serialize { - type QueryMsg = #query; - } - #[cfg(not(target_arch = "wasm32"))] - impl ::cw_orch::prelude::InstantiableContract for #name #all_debug_serialize { - type InstantiateMsg = #init; - } - #[cfg(not(target_arch = "wasm32"))] - impl ::cw_orch::prelude::ExecutableContract for #name #all_debug_serialize { - type ExecuteMsg = #exec; - } - #[cfg(not(target_arch = "wasm32"))] - impl ::cw_orch::prelude::MigratableContract for #name #all_debug_serialize { - type MigrateMsg = #migrate; - } - ); - struct_def.into() + #[cfg(not(target_arch = "wasm32"))] + impl ::cw_orch::prelude::QueryableContract for #name #all_debug_serialize { + type QueryMsg = #query; + } + #[cfg(not(target_arch = "wasm32"))] + impl ::cw_orch::prelude::InstantiableContract for #name #all_debug_serialize { + type InstantiateMsg = #init; + } + #[cfg(not(target_arch = "wasm32"))] + impl ::cw_orch::prelude::ExecutableContract for #name #all_debug_serialize { + type ExecuteMsg = #exec; + } + #[cfg(not(target_arch = "wasm32"))] + impl ::cw_orch::prelude::MigratableContract for #name #all_debug_serialize { + type MigrateMsg = #migrate; + } + ); + struct_def.into() } diff --git a/packages/cw-orch-core/src/contract/contract_instance.rs b/packages/cw-orch-core/src/contract/contract_instance.rs index 76ff7fda2..e2a08da49 100644 --- a/packages/cw-orch-core/src/contract/contract_instance.rs +++ b/packages/cw-orch-core/src/contract/contract_instance.rs @@ -1,7 +1,9 @@ //! Main functional component for interacting with a contract. Used as the base for generating contract interfaces. use super::interface_traits::Uploadable; use crate::{ - environment::{ChainState, IndexResponse, StateInterface, TxHandler, TxResponse}, + environment::{ + AsyncWasmQuerier, ChainState, IndexResponse, StateInterface, TxHandler, TxResponse, + }, error::CwEnvError, log::{contract_target, transaction_target}, CwOrchEnvVars, @@ -123,6 +125,37 @@ impl Contract { } } +impl Contract { + /// Query the contract + pub async fn async_query( + &self, + query_msg: &Q, + ) -> Result { + log::debug!( + target: &contract_target(), + "[{}][Query][{}] {}", + self.id, + self.address()?, + log_serialize_message(query_msg)? + ); + + let resp = self + .chain + .smart_query(&self.address()?, query_msg) + .await + .map_err(Into::into)?; + + log::debug!( + target: &contract_target(), + "[{}][Queried][{}] response {}", + self.id, + self.address()?, + log_serialize_message(&resp)? + ); + Ok(resp) + } +} + impl Contract { // Chain interfaces diff --git a/packages/cw-orch-core/src/contract/interface_traits.rs b/packages/cw-orch-core/src/contract/interface_traits.rs index b37061227..161dd3208 100644 --- a/packages/cw-orch-core/src/contract/interface_traits.rs +++ b/packages/cw-orch-core/src/contract/interface_traits.rs @@ -1,6 +1,6 @@ use super::{Contract, WasmPath}; use crate::{ - environment::{ChainState, CwEnv, TxHandler, TxResponse, WasmQuerier}, + environment::{AsyncWasmQuerier, ChainState, CwEnv, TxHandler, TxResponse, WasmQuerier}, error::CwEnvError, log::contract_target, }; @@ -150,6 +150,24 @@ pub trait CwOrchQuery: QueryableContract + ContractInstance impl, Chain: CwEnv> CwOrchQuery for T {} +/// Smart contract query entry point. +pub trait AsyncCwOrchQuery: + QueryableContract + ContractInstance +{ + /// Query the contract. + async fn async_query( + &self, + query_msg: &Self::QueryMsg, + ) -> Result { + self.as_instance().async_query(query_msg).await + } +} + +impl, Chain: AsyncWasmQuerier + ChainState> + AsyncCwOrchQuery for T +{ +} + /// Smart contract migrate entry point. pub trait CwOrchMigrate: MigratableContract + ContractInstance { /// Migrate the contract. diff --git a/packages/cw-orch-core/src/environment/mod.rs b/packages/cw-orch-core/src/environment/mod.rs index 932fe5540..4e08f66a3 100644 --- a/packages/cw-orch-core/src/environment/mod.rs +++ b/packages/cw-orch-core/src/environment/mod.rs @@ -11,7 +11,7 @@ pub use queriers::{ bank::BankQuerier, env::{EnvironmentInfo, EnvironmentQuerier}, node::NodeQuerier, - wasm::WasmQuerier, + wasm::{AsyncWasmQuerier, WasmQuerier}, DefaultQueriers, Querier, QuerierGetter, QueryHandler, }; pub use state::{ChainState, StateInterface}; diff --git a/packages/cw-orch-core/src/environment/queriers/wasm.rs b/packages/cw-orch-core/src/environment/queriers/wasm.rs index d7b5cd138..044a6993e 100644 --- a/packages/cw-orch-core/src/environment/queriers/wasm.rs +++ b/packages/cw-orch-core/src/environment/queriers/wasm.rs @@ -48,3 +48,11 @@ pub trait WasmQuerier: Querier { salt: cosmwasm_std::Binary, ) -> Result; } + +pub trait AsyncWasmQuerier: Querier { + async fn smart_query( + &self, + address: impl Into, + query_msg: &Q, + ) -> Result; +} diff --git a/packages/cw-orch-fns-derive/src/lib.rs b/packages/cw-orch-fns-derive/src/lib.rs index b9de0ee00..323816de7 100644 --- a/packages/cw-orch-fns-derive/src/lib.rs +++ b/packages/cw-orch-fns-derive/src/lib.rs @@ -25,5 +25,12 @@ pub fn cw_orch_execute(input: TokenStream) -> TokenStream { )] pub fn cw_orch_query(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as ItemEnum); - query_fns::query_fns_derive(ast) + let sync_tokens = query_fns::query_fns_derive(ast.clone()); + let async_tokens = query_fns::async_query_fns_derive(ast); + + let tokens = quote::quote! { + #sync_tokens + #async_tokens + }; + tokens.into() } diff --git a/packages/cw-orch-fns-derive/src/query_fns.rs b/packages/cw-orch-fns-derive/src/query_fns.rs index 7759626b5..55589e570 100644 --- a/packages/cw-orch-fns-derive/src/query_fns.rs +++ b/packages/cw-orch-fns-derive/src/query_fns.rs @@ -20,7 +20,7 @@ fn parse_query_type(v: &syn::Variant) -> Type { response_ty } -pub fn query_fns_derive(input: ItemEnum) -> TokenStream { +pub fn query_fns_derive(input: ItemEnum) -> proc_macro2::TokenStream { let name = &input.ident; let bname = Ident::new(&format!("{name}Fns"), name.span()); @@ -157,5 +157,137 @@ pub fn query_fns_derive(input: ItemEnum) -> TokenStream { #derived_trait_impl ); - expand.into() + expand +} + +pub fn async_query_fns_derive(input: ItemEnum) -> proc_macro2::TokenStream { + let name = &input.ident; + let bname = Ident::new(&format!("Async{name}Fns"), name.span()); + + let generics = input.generics.clone(); + let (_impl_generics, ty_generics, where_clause) = generics.split_for_impl().clone(); + let (maybe_into, entrypoint_msg_type, type_generics) = + process_impl_into(&input.attrs, name, input.generics); + + let is_attributes_sorted = process_sorting(&input.attrs); + + let variants = input.variants; + + let variant_fns = variants.into_iter().map( |mut variant|{ + let variant_name = variant.ident.clone(); + let response = parse_query_type(&variant); + let mut variant_func_name = + format_ident!("{}", process_fn_name(&variant).to_case(Case::Snake)); + variant_func_name.set_span(variant_name.span()); + + let variant_doc: syn::Attribute = { + let doc = format!("Automatically generated wrapper around {}::{} variant", name, variant_name); + syn::parse_quote!( + #[doc=#doc] + ) + }; + + match &mut variant.fields { + Fields::Unnamed(variant_fields) => { + let mut variant_idents = variant_fields.unnamed.clone(); + + // remove attributes from fields + variant_idents.iter_mut().for_each(|f| f.attrs = vec![]); + + // Parse these fields as arguments to function + + // We need to figure out a parameter name for all fields associated to their types + // They will be numbered from 0 to n-1 + let variant_ident_content_names = variant_idents + .iter() + .enumerate() + .map(|(i, _)| Ident::new(&format!("arg{}", i), Span::call_site())); + + let variant_attr = variant_idents.clone().into_iter() + .enumerate() + .map(|(i, mut id)| { + id.ident = Some(Ident::new(&format!("arg{}", i), Span::call_site())); + id + }); + + quote!( + #variant_doc + #[allow(clippy::too_many_arguments)] + async fn #variant_func_name(&self, #(#variant_attr,)*) -> ::core::result::Result<#response, ::cw_orch::prelude::CwOrchError> { + let msg = #name::#variant_name (#(#variant_ident_content_names,)*); + self.async_query(&msg #maybe_into).await + } + ) + } + Fields::Unit => { + quote!( + #variant_doc + async fn #variant_func_name(&self) -> ::core::result::Result<#response, ::cw_orch::prelude::CwOrchError> { + let msg = #name::#variant_name; + self.async_query(&msg #maybe_into).await + } + ) + }, + Fields::Named(variant_fields) => { + if is_attributes_sorted{ + // sort fields on field name + LexiographicMatching::default().visit_fields_named_mut(variant_fields); + } + + // remove attributes from fields + variant_fields.named.iter_mut().for_each(|f| f.attrs = vec![]); + + // Parse these fields as arguments to function + let variant_fields = variant_fields.named.clone(); + let variant_idents = variant_fields.iter().map(|f|f.ident.clone().unwrap()); + + let variant_attr = variant_fields.iter(); + quote!( + #variant_doc + #[allow(clippy::too_many_arguments)] + async fn #variant_func_name(&self, #(#variant_attr,)*) -> ::core::result::Result<#response, ::cw_orch::prelude::CwOrchError> { + let msg = #name::#variant_name { + #(#variant_idents,)* + }; + self.async_query(&msg #maybe_into).await + } + ) + } + } + }); + + let derived_trait = quote!( + /// Automatically derived trait that allows you to call the variants of the message directly without the need to construct the struct yourself. + pub trait #bname: ::cw_orch::prelude::AsyncCwOrchQuery #where_clause { + #(#variant_fns)* + } + ); + + // We need to merge the where clauses (rust doesn't support 2 wheres) + // If there is no where clause, we simply add the necessary where + let necessary_where = quote!(SupportedContract: ::cw_orch::prelude::AsyncCwOrchQuery); + let combined_where_clause = where_clause + .map(|w| { + quote!( + #w #necessary_where + ) + }) + .unwrap_or(quote!( + where + #necessary_where + )); + + let derived_trait_impl = quote!( + #[automatically_derived] + impl #bname for SupportedContract + #combined_where_clause {} + ); + + let expand = quote!( + #derived_trait + + #derived_trait_impl + ); + + expand } From 08040954f1f9d5d3e4baa8c04ec29cd52849bb08 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Thu, 21 Mar 2024 14:58:39 +0100 Subject: [PATCH 5/5] format --- packages/cw-orch-contract-derive/src/lib.rs | 82 ++++++++++----------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/packages/cw-orch-contract-derive/src/lib.rs b/packages/cw-orch-contract-derive/src/lib.rs index 5d63071b4..ed871b7c9 100644 --- a/packages/cw-orch-contract-derive/src/lib.rs +++ b/packages/cw-orch-contract-derive/src/lib.rs @@ -224,47 +224,47 @@ pub fn interface(attrs: TokenStream, input: TokenStream) -> TokenStream { ) }; let struct_def = quote!( - #[cfg(not(target_arch = "wasm32"))] - #[derive( - ::std::clone::Clone, - )] - pub struct #name(::cw_orch::contract::Contract, #(#all_phantom_markers,)*); - - #[cfg(target_arch = "wasm32")] - #[derive( - ::std::clone::Clone, - )] - pub struct #name; - - #[cfg(not(target_arch = "wasm32"))] - #default_num - - #[cfg(not(target_arch = "wasm32"))] - impl ::cw_orch::prelude::ContractInstance for #name { - fn as_instance(&self) -> &::cw_orch::contract::Contract { - &self.0 - } - fn as_instance_mut(&mut self) -> &mut ::cw_orch::contract::Contract { - &mut self.0 - } - } - - #[cfg(not(target_arch = "wasm32"))] - impl ::cw_orch::prelude::QueryableContract for #name #all_debug_serialize { - type QueryMsg = #query; - } - #[cfg(not(target_arch = "wasm32"))] - impl ::cw_orch::prelude::InstantiableContract for #name #all_debug_serialize { - type InstantiateMsg = #init; - } - #[cfg(not(target_arch = "wasm32"))] - impl ::cw_orch::prelude::ExecutableContract for #name #all_debug_serialize { - type ExecuteMsg = #exec; + #[cfg(not(target_arch = "wasm32"))] + #[derive( + ::std::clone::Clone, + )] + pub struct #name(::cw_orch::contract::Contract, #(#all_phantom_markers,)*); + + #[cfg(target_arch = "wasm32")] + #[derive( + ::std::clone::Clone, + )] + pub struct #name; + + #[cfg(not(target_arch = "wasm32"))] + #default_num + + #[cfg(not(target_arch = "wasm32"))] + impl ::cw_orch::prelude::ContractInstance for #name { + fn as_instance(&self) -> &::cw_orch::contract::Contract { + &self.0 } - #[cfg(not(target_arch = "wasm32"))] - impl ::cw_orch::prelude::MigratableContract for #name #all_debug_serialize { - type MigrateMsg = #migrate; + fn as_instance_mut(&mut self) -> &mut ::cw_orch::contract::Contract { + &mut self.0 } - ); - struct_def.into() + } + + #[cfg(not(target_arch = "wasm32"))] + impl ::cw_orch::prelude::QueryableContract for #name #all_debug_serialize { + type QueryMsg = #query; + } + #[cfg(not(target_arch = "wasm32"))] + impl ::cw_orch::prelude::InstantiableContract for #name #all_debug_serialize { + type InstantiateMsg = #init; + } + #[cfg(not(target_arch = "wasm32"))] + impl ::cw_orch::prelude::ExecutableContract for #name #all_debug_serialize { + type ExecuteMsg = #exec; + } + #[cfg(not(target_arch = "wasm32"))] + impl ::cw_orch::prelude::MigratableContract for #name #all_debug_serialize { + type MigrateMsg = #migrate; + } + ); + struct_def.into() }