diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 25a93787e4..7343786e16 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -2,6 +2,27 @@ == DFX +- feat: add option to specify initial cycles for newly created canisters (#1433) + +Added option to `dfx canister create` and `dfx deploy` commands: `--with-cycles `. +This allows the user to specify the initial cycle balance of a canister created by their wallet. +This option is a no-op for the Sodium network. + +[source, bash] +---- +dfx canister create --with-cycles 8000000000 some_canister +dfx deploy --with-cycles 8000000000 +---- + +Help string: +[source, bash] +---- +Specifies the initial cycle balance to deposit into the newly +created canister. The specified amount needs to take the +canister create fee into account. This amount is deducted +from the wallet's cycle balance +---- + - feat: install `dfx` by version or tag (#1426) This feature adds a new dfx command `toolchain` which have intuitive subcommands. diff --git a/src/dfx/src/commands/canister/create.rs b/src/dfx/src/commands/canister/create.rs index 1a3c4a951a..840e5d98ce 100644 --- a/src/dfx/src/commands/canister/create.rs +++ b/src/dfx/src/commands/canister/create.rs @@ -2,6 +2,7 @@ use crate::lib::environment::Environment; use crate::lib::error::DfxResult; use crate::lib::operations::canister::create_canister; use crate::lib::root_key::fetch_root_key_if_needed; +use crate::util::clap::validators::cycle_amount_validator; use crate::util::expiry_duration; use anyhow::bail; @@ -17,6 +18,12 @@ pub struct CanisterCreateOpts { /// Creates all canisters configured in dfx.json. #[clap(long, required_unless_present("canister-name"))] all: bool, + + /// Specifies the initial cycle balance to deposit into the newly created canister. + /// The specified amount needs to take the canister create fee into account. + /// This amount is deducted from the wallet's cycle balance. + #[clap(long, validator(cycle_amount_validator))] + with_cycles: Option, } pub async fn exec(env: &dyn Environment, opts: CanisterCreateOpts) -> DfxResult { @@ -24,14 +31,14 @@ pub async fn exec(env: &dyn Environment, opts: CanisterCreateOpts) -> DfxResult let timeout = expiry_duration(); fetch_root_key_if_needed(env).await?; - + let with_cycles = opts.with_cycles.as_deref(); if let Some(canister_name) = opts.canister_name.clone() { - create_canister(env, canister_name.as_str(), timeout).await + create_canister(env, canister_name.as_str(), timeout, with_cycles).await } else if opts.all { // Create all canisters. if let Some(canisters) = &config.get_config().canisters { for canister_name in canisters.keys() { - create_canister(env, canister_name, timeout).await?; + create_canister(env, canister_name, timeout, with_cycles).await?; } } Ok(()) diff --git a/src/dfx/src/commands/deploy.rs b/src/dfx/src/commands/deploy.rs index c1f780cdf2..26c565742f 100644 --- a/src/dfx/src/commands/deploy.rs +++ b/src/dfx/src/commands/deploy.rs @@ -3,6 +3,7 @@ use crate::lib::error::DfxResult; use crate::lib::operations::canister::deploy_canisters; use crate::lib::provider::create_agent_environment; use crate::lib::root_key::fetch_root_key_if_needed; +use crate::util::clap::validators::cycle_amount_validator; use crate::util::expiry_duration; use clap::Clap; @@ -29,6 +30,12 @@ pub struct DeployOpts { /// "http://localhost:12345/" is a valid network name. #[clap(long)] network: Option, + + /// Specifies the initial cycle balance to deposit into the newly created canister. + /// The specified amount needs to take the canister create fee into account. + /// This amount is deducted from the wallet's cycle balance. + #[clap(long, validator(cycle_amount_validator))] + with_cycles: Option, } pub fn exec(env: &dyn Environment, opts: DeployOpts) -> DfxResult { @@ -38,6 +45,7 @@ pub fn exec(env: &dyn Environment, opts: DeployOpts) -> DfxResult { let canister_name = opts.canister_name.as_deref(); let argument = opts.argument.as_deref(); let argument_type = opts.argument_type.as_deref(); + let with_cycles = opts.with_cycles.as_deref(); let mut runtime = Runtime::new().expect("Unable to create a runtime"); runtime.block_on(fetch_root_key_if_needed(&env))?; @@ -48,5 +56,6 @@ pub fn exec(env: &dyn Environment, opts: DeployOpts) -> DfxResult { argument, argument_type, timeout, + with_cycles, )) } diff --git a/src/dfx/src/lib/operations/canister/create_canister.rs b/src/dfx/src/lib/operations/canister/create_canister.rs index c0a686cecc..84ed153459 100644 --- a/src/dfx/src/lib/operations/canister/create_canister.rs +++ b/src/dfx/src/lib/operations/canister/create_canister.rs @@ -16,6 +16,7 @@ pub async fn create_canister( env: &dyn Environment, canister_name: &str, timeout: Duration, + with_cycles: Option<&str>, ) -> DfxResult { let log = env.get_logger(); info!(log, "Creating canister {:?}...", canister_name); @@ -68,8 +69,12 @@ pub async fn create_canister( .await?; create_result.canister_id } else { + let cycles = match with_cycles { + None => 1000000000001_u64, + Some(amount) => amount.parse::()?, + }; wallet - .wallet_create_canister(1000000000001_u64, None) + .wallet_create_canister(cycles, None) .call_and_wait(waiter_with_timeout(timeout)) .await? .0 diff --git a/src/dfx/src/lib/operations/canister/deploy_canisters.rs b/src/dfx/src/lib/operations/canister/deploy_canisters.rs index edac1111c0..486f465bb1 100644 --- a/src/dfx/src/lib/operations/canister/deploy_canisters.rs +++ b/src/dfx/src/lib/operations/canister/deploy_canisters.rs @@ -23,6 +23,7 @@ pub async fn deploy_canisters( argument: Option<&str>, argument_type: Option<&str>, timeout: Duration, + with_cycles: Option<&str>, ) -> DfxResult { let log = env.get_logger(); @@ -38,7 +39,14 @@ pub async fn deploy_canisters( info!(log, "Deploying all canisters."); } - register_canisters(env, &canister_names, &initial_canister_id_store, timeout).await?; + register_canisters( + env, + &canister_names, + &initial_canister_id_store, + timeout, + with_cycles, + ) + .await?; build_canisters(env, &canister_names, &config)?; @@ -71,6 +79,7 @@ async fn register_canisters( canister_names: &[String], canister_id_store: &CanisterIdStore, timeout: Duration, + with_cycles: Option<&str>, ) -> DfxResult { let canisters_to_create = canister_names .iter() @@ -82,7 +91,7 @@ async fn register_canisters( } else { info!(env.get_logger(), "Creating canisters..."); for canister_name in &canisters_to_create { - create_canister(env, &canister_name, timeout).await?; + create_canister(env, &canister_name, timeout, with_cycles).await?; } } Ok(()) diff --git a/src/dfx/src/util/clap/validators.rs b/src/dfx/src/util/clap/validators.rs index cee06fe2d8..1961be7997 100644 --- a/src/dfx/src/util/clap/validators.rs +++ b/src/dfx/src/util/clap/validators.rs @@ -17,6 +17,13 @@ pub fn is_request_id(v: &str) -> Result<(), String> { } } +pub fn cycle_amount_validator(cycles: &str) -> Result<(), String> { + if cycles.parse::().is_ok() { + return Ok(()); + } + Err("Must be a non negative amount.".to_string()) +} + pub fn compute_allocation_validator(compute_allocation: &str) -> Result<(), String> { if let Ok(num) = compute_allocation.parse::() { if num <= 100 {