Quick-Start Guide
Get ready to change the way you interact with contracts. The following steps will allow you to write clean code such as:
#![allow(unused)] fn main() { counter.upload()?; counter.instantiate(&InstantiateMsg { count: 0 }, None, None)?; counter.increment()?; let count = counter.get_count()?; assert_eq!(count.count, 1); }
In this quick-start guide, we will review the necessary steps in order to integrate cw-orch
into a simple contract crate. We review integration of rust-workspaces (multiple contracts) at the end of this page.
NOTE: Additional content
If you’re moving quicker than everybody else, we suggest looking at a before-after review of this example integration. This will help you catch the additions you need to make to your contract to be able to interact with it using cw-orchestrator.
Summary
Single Contract Integration
Adding cw-orch
to your Cargo.toml
file
To use cw-orchestrator, you need to add cw-orch
to your contract’s TOML file. Run the command below in your contract’s directory:
$ cargo add --optional cw-orch
Alternatively, you can add it manually in your Cargo.toml
file as shown below:
[dependencies]
cw-orch = {version = "0.17.0", optional = true } # Latest version at time of writing
Now that we have added cw-orch
as an optional dependency we will want to enable it through a feature-flag. This ensures that the code added by cw-orch
is not included in the wasm artifact of the contract.
To do this add an interface
feature to the Cargo.toml
and enable cw-orch
when it is enabled like so:
[features]
interface = ["dep:cw-orch"] # Enables cw-orch when the feature is enabled
NOTE: If you are using
rust-analyzer
, you can add the following two lines in yoursettings.json
to make sure the features get taken into account when checking the project:"rust-analyzer.cargo.features": "all", "rust-analyzer.check.features": "all",
Creating an Interface
When using a single contract, we advise creating an interface.rs
file inside your contract’s directory. You then need to add this module to your lib.rs
file. Don’t forget to feature-flag the module in order to be able to use cw-orch
inside it.
#![allow(unused)] fn main() { #[cfg(feature = "interface")] mod interface; }
Then, inside that interface.rs
file, you can define the interface for your contract:
#![allow(unused)] fn main() { use cw_orch::{interface, prelude::*}; use crate::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; #[interface(InstantiateMsg, ExecuteMsg, QueryMsg, MigrateMsg)] pub struct CounterContract; impl<Chain: CwEnv> Uploadable for CounterContract<Chain> { /// Return the path to the wasm file corresponding to the contract fn wasm(&self) -> WasmPath { artifacts_dir_from_workspace!() .find_wasm_path("counter_contract") .unwrap() } /// Returns a CosmWasm contract wrapper fn wrapper(&self) -> Box<dyn MockContract<Empty>> { Box::new( ContractWrapper::new_with_empty( crate::contract::execute, crate::contract::instantiate, crate::contract::query, ) .with_migrate(crate::contract::migrate), ) } } }
Learn more about the content of the interface creation specifics on the interface page
NOTE: It can be useful to re-export this struct to simplify usage (in
lib.rs
):#[cfg(feature = "interface")] pub use crate::interface::CounterContract;
Interaction helpers
cw-orchestrator provides a additional macros that simplify contract calls and queries. The macro implements functions on the interface for each variant of the contract’s ExecuteMsg
and QueryMsg
.
Enabling this functionality is very straightforward. Find your ExecuteMsg
and QueryMsg
definitions (in msg.rs
in our example) and add the ExecuteFns
and QueryFns
derive macros to them like below:
#![allow(unused)] fn main() { #[cw_serde] #[cfg_attr(feature = "interface", 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, }, } #[cw_serde] #[cfg_attr(feature = "interface", 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, } }
Find out more about the interaction helpers on the interface page
NOTE: Again, it can be useful to re-export these generated traits to simplify usage (in
lib.rs
):#[cfg(feature = "interface")] pub use crate::msg::{ExecuteMsgFns as CounterExecuteMsgFns, QueryMsgFns as CounterQueryMsgFns};
Using the integration
Now that all the setup is done, you can use your contract in tests, integration-tests or scripts.
Start by importing your crate, with the interface
feature enabled. Depending on your use-case this will be in [dependencies]
or [dev-dependencies]
:
counter-contract = { path = "../counter-contract", features = ["interface"] }
You can now use:
use counter_contract::{ msg::InstantiateMsg, CounterContract, CounterExecuteMsgFns, CounterQueryMsgFns, }; 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"; pub 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 rt = Runtime::new()?; let network = networks::LOCAL_JUNO; let chain = DaemonBuilder::default() .handle(rt.handle()) .chain(network) .build()?; let counter = CounterContract::new("counter_contract", chain); counter.upload()?; counter.instantiate(&InstantiateMsg { count: 0 }, None, None)?; counter.increment()?; let count = counter.get_count()?; assert_eq!(count.count, 1); Ok(()) }
Integration in a workspace
In this paragraph, we will use the cw-plus
repository as an example. You can review:
- The full integration code with
cw-orch
added - The complete diff that shows you all integration spots (if you want to go fast)
Handling dependencies and features
When using workspaces, you need to do the 2 following actions on all crates that include ExecuteMsg
and QueryMsg
used in your contracts:
- Add
cw-orch
as an optional dependency - Add an
interface
feature (ensurescw-orch
is not compiled into yourwasm
contract)
Refer above to Adding cw-orch
to your Cargo.toml
file for more details on how to do that.
For instance, for the cw20_base
contract, you need to execute those 2 steps on the cw20-base
contract (where the QueryMsg
are defined) as well as on the cw20
package (where the ExecuteMsg
are defined).
Creating an interface crate
When using workspace, we advise you to create a new crate inside your workspace for defining your contract’s interfaces. In order to do that, use:
cargo new interface --lib
cargo add cw-orch --package interface
Add the interface package to your workspace Cargo.toml
file
[workspace]
members = ["packages/*", "contracts/*", "interface"]
Inside this interface
crate, we advise to integrate all your contracts 1 by 1 in separate files. Here is the structure of the cw-plus
integration for reference:
interface (interface collection)
├── Cargo.toml
└── src
├── cw1_subkeys.rs
├── cw1_whitelist.rs
├── cw20_base.rs
├── cw20_ics20.rs
└── ..
When importing your crates to get the messages types, you can use the following command in the interface folder. Don’t forget to activate the interface feature to be able to use the cw_orch functionalities.
cargo add cw20-base --path ../contracts/cw20-base/ --features=interface
cargo add cw20 --path ../packages/cw20 --features=interface
Integrating single contracts
Now that you workspace is setup, you can integrate with single contracts using the above section
More examples and scripts
You can find more example interactions on the counter-contract
example directly in the cw-orchestrator
repo:
- Some examples showcase interacting with live chains.
- Some other examples show how to use the library for testing your contracts.
FINAL ADVICE: Continue to explore those docs to learn more about
cw-orch
. Why not go directly to environment variables?