Daemon
Daemon is a CosmWasm execution environment for interacting with CosmosSDK chains. The Daemon allows you to deploy/migrate/configure your contracts on main and testnets as well as locally running chain instances. Furthermore it provides a wide range of tools to interact with the chains. We describe those tools in depth in this page.
Quick Start
Before starting, here are a few examples utilizing the daemon structure:
Interacting with the daemon is really straightforward. Creating a daemon instance is shown below:
use cw_orch::prelude::*;
// We start by creating a daemon. This daemon will be used to interact with the chain.
let daemon = Daemon::builder(cw_orch::daemon::networks::LOCAL_JUNO) // chain parameter
.build()
.unwrap();
- The
chainparameter allows you to specify which chain you want to interact with. The chains that are officially supported can be found in thecw_orch::daemon::networksmodule. You can also add additional chains yourself by simply defining a variable of typeChainInfoand using it in your script. Don’t hesitate to open a PR on the cw-orchestrator repo, if you would like us to include a chain by default. The variables needed for creating the variable can be found in the documentation of the chain you want to connect to or in the Cosmos Directory.
This simple script actually hides another parameter which is the LOCAL_MNEMONIC environment variable. This variable is used when interacting with local chains. See the part dedicated to Environment Vars for more details.
NOTE: When using
daemon, you are interacting directly with a live chain. The program won’t ask you for your permission at each step of the script. We advise you to test ALL your deployments on test chain before deploying to mainnet.Under the hood, the
DaemonBuilderstruct creates atokio::Runtime. Be careful because this builder is not usable in anasyncfunction. In such function, you can useDaemonAsync
When using multiple Daemons with the same state file, you should re-use a single Daemon State to avoid conflicts and panics:
let daemon1 = Daemon::builder(OSMOSIS_1).build()?;
// If you don't use the `state` method here, this will fail with:
// State file <file-name> already locked, use another state file, clone daemon which holds the lock, or use `state` method of Builder
let daemon2 = Daemon::builder(JUNO_1)
.state(daemon1.state())
.build()?;
Interacting with contracts
You can then use the resulting Daemon variable to interact with your contracts:
let counter = CounterContract::new(daemon.clone());
let upload_res = counter.upload();
assert!(upload_res.is_ok());
let init_res = counter.instantiate(
&InstantiateMsg { count: 0 },
Some(&counter.environment().sender_addr()),
None,
);
assert!(init_res.is_ok());
All contract operations will return an object of type cw_orch::prelude::CosmTxResponse. This represents a successful transaction. Using the txhash of the tx, you can also inspect the operations on a chain explorer.
ADVICE: Add
RUST_LOG=INFOto your environment and use theenv_logger::init()initializer to get detailed information about your script execution. Cw-orchestrator provides enhanced logging tools for following the deployment and potentially pick up where you left off. This environment needs wasm artifacts to deploy the contracts to the chains. Don’t forget to compile all your wasms before deploying your contracts !
State management
In order to manage your contract deployments cw-orchestrator saves the contract addresses and code ids for each network you’re interacting with in a JSON formatted state file. This state file represents all your past. You can customize the path to this state file using the STATE_FILE env variable.
When calling the upload function on a contract, if the tx is successful, the daemon will get the uploaded code_id and save it to file, like so:
{
"juno": {
"juno-1": {
"code_ids": {
"counter_contract": 1356,
},
}
}
}
In this example: counter_contract corresponds to the contract_idvariable (the one that you can set in the contract interface constructor).
When calling the instantiate function, if the tx is successful, the daemon will get the contract address and save it to file, like so:
{
"juno": {
"juno-1": {
"code_ids": {
"counter_contract": 1356,
},
"default": {
"counter_contract": "juno1wug8sewp6cedgkmrmvhl3lf3tulagm9hnvy8p0rppz9yjw0g4wtqwrw37d"
}
}
}
}
In this example, the default keyword corresponds to the deployment namespace. This can be set when building the daemon object (using the DaemonBuilder::deployment_id method) in order to separate multiple deployments. For instance for a DEX (decentralized exchange), you can have a single code-id but multiple pool addresses for all your liquidity pools. You would have a juno-usdc and a usdt-usdc deployment, sharing the same code-ids but different contract instances.
Configuration
When creating a Daemon, use the DaemonBuilder object to set options for the structure.
Here are the available options and fields you can use in the builder object:
chain(required) specifies the chain thedaemonobject will interact with. Documentation Linkdeployment_id(optional) is used when loading and saving blockchain state (addresses and code-ids). It is useful when you have multiple instances of the same contract on a single chain. It will allow you to keep those multiple instances in the same state file without overriding state.Documentation Linkhandle(optional) is thetokioruntime handled used to await async functions.cw-orchprovides a default runtime if not specified. Documentation Linkmnemonic(optional) is the mnemonic that will be used to create the sender associated with the resultingDaemonObject. It is not compatible with thesendermethod. Documentation Linksender(optional) is the sender that will be uses with theresultingDaemon Object. It is not compatible with themnemonicmethod. Documentation Linkauthz_granter(optional) allows you to use the authz module. If this field is specified, the sender will send transactions wrapped inside an authz message sent by the specifiedgranter. More info on the authz module. Documentation Linkfee_granter(optional) allows you to use the fee-grant module. If this field is specified, the sender will try to pay for transactions using the specifiedgranter. More info on the fee grant module. Documentation Linkhd_index(optional) allows to set the index of the HD path for the account associated with theDaemonobject. More info on the derivation path and index. Documentation Link
NOTE: if none of
senderormnemonicis specified, env variables will be used to construct the sender object.
Keep in mind that those options can’t be changed once the Daemon object is built, using the build function. It is possible to create a new DaemonBuilder structure from a Daemon object by using the rebuild method and specifying the options that you need to change.
Additional tools
The Daemon environment provides a bunch of tools for you to interact in a much easier way with the blockchain. Here is a non-exhaustive list:
-
Send usual transactions:
#![allow(unused)] fn main() { let wallet = daemon.sender(); let rt = daemon.rt_handle.clone(); rt.block_on(wallet.bank_send( &Addr::unchecked("<address-of-my-sister>"), coins(345, "ujunox"), ))?; } -
Send any transaction type registered with
cosmrs:#![allow(unused)] fn main() { let tx_msg = cosmrs::staking::MsgBeginRedelegate { // Delegator's address. delegator_address: AccountId::from_str("<my-address>").unwrap(), // Source validator's address. validator_src_address: AccountId::from_str("<my-least-favorite-validator>").unwrap(), // Destination validator's address. validator_dst_address: AccountId::from_str("<my-favorite-validator>").unwrap(), // Amount to UnDelegate amount: Coin { amount: 100_000_000_000_000u128, denom: Denom::from_str("ujuno").unwrap(), }, }; rt.block_on(wallet.commit_tx(vec![tx_msg.clone()], None))?; } -
Send any type of transactions (Using an
Anytype):#![allow(unused)] fn main() { rt.block_on(wallet.commit_tx_any( vec![cosmrs::Any { type_url: "/cosmos.staking.v1beta1.MsgBeginRedelegate".to_string(), value: tx_msg.to_any().unwrap().value, }], None, ))?; } -
Simulate a transaction without sending it
#![allow(unused)] fn main() { let (gas_needed, fee_needed) = rt.block_on(wallet.simulate(vec![tx_msg.to_any().unwrap()], None))?; log::info!( "Submitting this transaction will necessitate: - {gas_needed} gas - {fee_needed} for the tx fee" ); }
Queries
The daemon object can also be used to execute queries to the chains we are interacting with. This opens up a lot more applications to cw-orchestrator as this tools can also be used to manage off-chain applications.
Querying the chain for data using a daemon looks like:
let bank_query_client: Bank = daemon.querier();
let sender = Addr::unchecked("valid_sender_addr");
let balance_result = bank_query_client.balance(&sender, None)?;
println!("Balance of {} : {:?}", sender, balance_result);
For more information and queries, visit the daemon querier implementations directly
Example of code leveraging Daemon capabilities
Here is an example of a script that deploys the counter contract only after a specific block_height.
{{#include ../../../contracts/counter/src/interface.rs:daemon}}