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 a Vec<Coin> as the last argument.
  • The cw_orch::QueryFns macro needs your QueryMsg struct to have the cosmwasm_schema::QueryResponses macro implemented (this is good practice even outside of use with cw-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 or QueryFns 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.