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
Interacting with the daemon
is really straightforward. How to creating a daemon instance is shown below:
use cw_orch::prelude::*;
use tokio::runtime::Runtime;
// We start by creating a runtime, which is required for a sync daemon.
let runtime = Runtime::new().unwrap();
// We can now create a daemon. This daemon will be used to interact with the chain.
let daemon = Daemon::builder()
// set the network to use
.chain(cw_orch::daemon::networks::LOCAL_JUNO) // chain parameter
.handle(runtime.handle()) // handler parameter
.build()
.unwrap();
-
The
chain
parameter allows you to specify which chain you want to interact with. The chains that are officially supported can be found in thecw_orch::daemon::networks
module. You can also add additional chains yourself by simply defining a variable of typeChainInfo
and 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. -
The
handle
parameter is a tokio runtime handle.If you don't know what that means
You don’t need to know all the intricacies of tokio and rust-async to use daemons. If you don’t have time to learn more, you can simply use the snippet above and not touch the runtime creation. However for more advanced
daemon
usage and also for your culture, don’t hesitate to learn about those wonderful processes and their management.If you know what that means
This handler is used because all the front-facing daemon methods are synchronous. However everything that’s happening in the background is asynchronous. This handle is used exclusively to await asynchronous function:
runtime.block_on(...)
Because creating runtimes is a costly process, we leave the handler creation and management to the user.
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.
Interacting with contracts
You can then use the resulting Daemon
variable to interact with your contracts:
let counter = CounterContract::new("local:counter", daemon.clone());
let upload_res = counter.upload();
assert!(upload_res.is_ok());
let init_res = counter.instantiate(
&InstantiateMsg { count: 0 },
Some(&counter.get_chain().sender()),
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=INFO
to 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_id
variable (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.
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.wallet(); rt.block_on(wallet.bank_send("<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
Any
type):#![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.query_client();
let sender = "valid_sender_addr";
let balance_result = bank_query_client.balance(sender, None).await?;
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.
impl CounterContract<Daemon> {
/// Deploys the counter contract at a specific block height
pub fn await_launch(&self) -> Result<()> {
let daemon = self.get_chain();
let rt = daemon.rt_handle.clone();
rt.block_on(async {
// Get the node query client, there are a lot of other clients available.
let node = daemon.query_client::<Node>();
let mut latest_block = node.latest_block().await.unwrap();
while latest_block.header.height.value() < 100 {
// wait for the next block
daemon.next_block().unwrap();
latest_block = node.latest_block().await.unwrap();
}
});
let contract = CounterContract::new(CONTRACT_NAME, daemon.clone());
// Upload the contract
contract.upload().unwrap();
// Instantiate the contract
let msg = InstantiateMsg { count: 1i32 };
contract.instantiate(&msg, None, None).unwrap();
Ok(())
}
}