Entry Point Function Generation
Contract execution and querying is so common that we felt the need to improve the method of calling them. To do this we created two macros: ExecuteFns
and QueryFns
. As their name implies they can be used to automatically generate functions for executing and querying your contract through the interface.
Execution
To get started, find the ExecuteMsg
definition for your contract. In our case it’s located in counter/src/msg.rs
. Then add the following line to your ExecuteMsg
enum:
#[cw_serde]
#[derive(cw_orch::ExecuteFns)] // Function generation
/// Execute methods for counter
pub enum ExecuteMsg {
/// Increment count by one
Increment {},
/// Reset count
Reset {
/// Count value after reset
count: i32,
},
}
The functions are implemented as a trait named ExecuteMsgFns
which is implemented on any interface that uses this ExecuteMsg
as an entrypoint message.
Using the trait then becomes as simple as:
// in integration_tests.rs
// Reset
use counter_contract::CounterExecuteMsgFns;
contract.reset(0)?;
Query
Generating query functions is a similar process but has the added advantage of using the cosmwasm-schema
return tags to detect the query’s return type. This allows for type-safe query functions!
#[cw_serde]
#[derive(cw_orch::QueryFns)] // Function generation
#[derive(QueryResponses)]
/// Query methods for counter
pub enum QueryMsg {
/// GetCount returns the current count as a json-encoded number
#[returns(GetCountResponse)]
GetCount {},
}
// Custom response for the query
#[cw_serde]
/// Response from get_count query
pub struct GetCountResponse {
/// Current count in the state
pub count: i32,
}
Keep in mind that you NEED to derive the cosmwasm_schema::QueryResponses
trait on your QueryMsgs for the QueryFns
macro to compile.
Using it is just as simple as the execution functions:
// in integration_tests.rs
// Get the count.
use counter_contract::CounterQueryMsgFns;
let count1 = contract.get_count()?;
// or query it manually
let count2: GetCountResponse = contract.query(&QueryMsg::GetCount {})?;
assert_eq!(count1.count, count2.count);
Just like the interface it can be beneficial to re-export the trait in your lib.rs
or interface.rs
file.
In the counter contract we re-export in lib.rs
;
pub use crate::msg::{
AsyncQueryMsgFns as AsyncCounterQueryMsgFns, ExecuteMsgFns as CounterExecuteMsgFns,
QueryMsgFns as CounterQueryMsgFns,
};
Async functions
In the case of queries, async functions get generated by the derive macro as well. These have the same arguments and return the same type as their synchronous counterparts, but are asynchronous and are suffixed with _async
:
use counter_contract::AsyncCounterQueryMsgFns;
use counter_contract::CounterContract;
use cw_orch::{anyhow, prelude::*, tokio};
// From https://github.com/CosmosContracts/juno/blob/32568dba828ff7783aea8cb5bb4b8b5832888255/docker/test-user.env#L2
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("LOCAL_MNEMONIC", LOCAL_MNEMONIC);
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::LOCAL_JUNO;
let chain = DaemonAsyncBuilder::new(network).build().await?;
let counter = CounterContract::new(chain);
let count = counter.get_count_async().await?;
assert_eq!(count.count, 1);
Ok(())
}
Additional Remarks on QueryFns
and ExecuteFns
The QueryFns
and ExecuteFns
derive macros generate traits that are implemented on any Contract structure (defined by the interface
macro) that have the matching execute and query types. Because of the nature of rust traits, you need to import the traits in your application to use the simplifying syntax. Those traits are named ExecuteMsgFns
and QueryMsgFns
.
Any variant of the ExecuteMsg
and QueryMsg
that has a #[derive(ExecuteFns)]
or #[derive(QueryFns)]
will have a function implemented on the interface (e.g. CounterContract
) through a trait. Here are the main things you need to know about the behavior of those macros:
- The function created will have the snake_case name of the variant and will take the same arguments as the variant.
- The arguments are ordered in alphabetical order to prevent attribute ordering from changing the function signature.
- If coins need to be sent along with the message you can add
#[cw_orch(payable)]
to the variant and the function will take aVec<Coin>
as the last argument. - The
cw_orch::QueryFns
macro needs yourQueryMsg
struct to have thecosmwasm_schema::QueryResponses
macro implemented (this is good practice even outside of use withcw-orch
).
Additional configuration
payable
Attribute
Let’s see an example for executing a message (from a money market for instance).
money_market.deposit_stable()?;
There’s a problem with the above function. The money market only knows how much you deposit into it by looking at the funds you send along with the transaction. Cw-orchestrator doesn’t ask for funds by default. However, to allow attaching funds to a transaction, you can add the #[cw_orch(payable)]
attribute on your enum variant like so:
#[derive(ExecuteFns)]
enum ExecuteMsg{
UpdateConfig{
config_field: String
},
#[cw_orch(payable)]
DepositStable{}
...
}
Be defining this attribute, you can now use:
use cosmwasm_std::coins;
money_market.deposit_stable(&coins(456, "ujunox"))?;
fn_name
Attribute
#[derive(cw_orch::ExecuteFns)]
pub enum ExecuteMsg{
Execute{
msg: CosmoMsg
}
}
The following command will error because the execute
function is reserved for contract execution. This will not even compile actually.
// Doesn't compile
money_market.execute(message_to_execute_via_a_proxy)?;
This can happen in numerous cases actually, when using reserved keywords of cw-orch (or even rust). If this happens, you can use the fn_name
attribute to rename a generated function.
#[derive(cw_orch::ExecuteFns)]
pub enum ExecuteMsg{
#[cw_orch(fn_name("proxy_execute"))]
Execute{
msg: CosmoMsg
}
}
// This works smoothly !
money_market.proxy_execute(message_to_execute_via_a_proxy)?;
This is also true for query functions.
Nested Messages
For nested messages (execute and query), you need to do 2 things:
- Derive
ExecuteFns
orQueryFns
on the underlying structures - Implement
From<Underlying>
for your contract message type
In general, every structure that implements the Into
trait for the contract message will make the function available on the contract. To make that clearer, here’s an example:
use cw_orch::interface;
use cw_orch::prelude::*;
// An execute message that is generic.
#[cosmwasm_schema::cw_serde]
pub enum GenericExecuteMsg<T> {
Generic(T),
Nested(NestedMessageType),
}
// This is the message that will be used on our contract
type ExecuteMsg = GenericExecuteMsg<Foo>;
#[cosmwasm_schema::cw_serde]
#[derive(cw_orch::ExecuteFns)]
pub enum Foo {
Bar { a: String },
}
impl From<Foo> for ExecuteMsg {
fn from(msg: Foo) -> Self {
ExecuteMsg::Generic(msg)
}
}
#[cosmwasm_schema::cw_serde]
#[derive(cw_orch::ExecuteFns)]
pub enum NestedMessageType {
Test { b: u64 },
}
impl From<NestedMessageType> for ExecuteMsg {
fn from(msg: NestedMessageType) -> Self {
ExecuteMsg::Nested(msg)
}
}
#[interface(Empty, ExecuteMsg, Empty, Empty)]
struct Example<Chain>;
impl<Chain: CwEnv> Example<Chain> {
pub fn test_macro(&self) {
// function `bar` is available now!
self.bar("hello".to_string()).unwrap();
// function `test` is available now!
self.test(65u64).unwrap();
}
}
Into
String
and Uint*
(e.g. Uint128
) types will have relaxed trait bounds on the argument that the generated function receives. This allows for easier handling of the cw-orch generated functions. For other types, you can add the #[cw_orch(into)]
attribute on a field to also accept structures that implement Into
the field type. For instance:
pub enum ExecuteMsg<T>{
#[cw_orch(payable)]
SecondMessage {
/// test doc-comment
#[cw_orch(into)]
t: T,
},
}
// Will produce:
fn second_message<T>(&self, t: impl Into<T>, funds: &[Coin]){}
disable_fields_sorting
Attribute
By default the ExecuteFns
and QueryFns
derived traits will sort the fields of each enum member. For instance,
#[cw_serde]
#[derive(cw_orch::ExecuteFns)]
pub enum ExecuteMsgOrdered {
Test { b: u64, a: String },
}
will generate
pub fn bar(a: String, b: u64) -> ...{
...
}
You see in this example that the fields of the bar function are sorted lexicographically. We decided to put this behavior as default to prevent potential errors when rearranging the order of enum fields. If you don’t want this behavior, you can disable it by using the disable_fields_sorting
attribute. This is the resulting behavior:
#[cw_serde]
#[derive(cw_orch::ExecuteFns)]
#[cw_orch(disable_fields_sorting)]
pub enum ExecuteMsg {
Test { b: u64, a: String },
}
pub fn bar(b: u64, a: String) -> ...{
...
}
NOTE: This behavior CAN be dangerous if your struct members have the same type. In that case, if you want to rearrange the order of the members inside the struct definition, you will have to be careful that you respect the orders in which you want to pass them.