diff --git a/CHANGELOG.md b/CHANGELOG.md index 33374dd7dd..3145adec2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,17 @@ This affects the following commands: - `dfx canister update-settings` - `dfx ledger fabricate-cycles` +### chore: improve `dfx deploy` messages. + +If users run `dfx deploy` without enough cycles, show additional messages to indicate what to do next. +``` +Error explanation: +Insufficient cycles balance to create the canister. +How to resolve the error: +Please top up your cycles balance by converting ICP to cycles like below: +'dfx cycles convert --amount=0.123'. +``` + # 0.24.3 ### feat: Bitcoin support in PocketIC diff --git a/e2e/tests-dfx/cycles-ledger.bash b/e2e/tests-dfx/cycles-ledger.bash index 3a279b6c0b..83fa0ddd99 100644 --- a/e2e/tests-dfx/cycles-ledger.bash +++ b/e2e/tests-dfx/cycles-ledger.bash @@ -591,9 +591,22 @@ current_time_nanoseconds() { assert_command dfx deploy + # Test canister creation failure before topping up cycles. + cd .. + dfx_new canister_creation_failed + + # shellcheck disable=SC2030,SC2031 + export DFX_DISABLE_AUTO_WALLET=1 + assert_command_fail dfx canister create canister_creation_failed_backend --with-cycles 1T --identity alice + assert_contains "Insufficient cycles balance to create the canister." + + ## Back to top up cycles. + cd ../temporary + assert_command dfx canister call depositor deposit "(record {to = record{owner = principal \"$ALICE\";};cycles = 13_400_000_000_000;})" --identity cycle-giver assert_command dfx canister call depositor deposit "(record {to = record{owner = principal \"$ALICE\"; subaccount = opt blob \"$ALICE_SUBACCT1_CANDID\"};cycles = 2_600_000_000_000;})" --identity cycle-giver + # shellcheck disable=SC2103 cd .. dfx_new # setup done diff --git a/src/dfx/src/lib/diagnosis.rs b/src/dfx/src/lib/diagnosis.rs index 150f1c9b56..b346341cf7 100644 --- a/src/dfx/src/lib/diagnosis.rs +++ b/src/dfx/src/lib/diagnosis.rs @@ -1,6 +1,8 @@ +use crate::lib::cycles_ledger_types::create_canister::CreateCanisterError; use crate::lib::error_code; use anyhow::Error as AnyhowError; use dfx_core::error::root_key::FetchRootKeyError; +use dfx_core::network::provider::get_network_context; use ic_agent::agent::{RejectCode, RejectResponse}; use ic_agent::AgentError; use ic_asset::error::{GatherAssetDescriptorsError, SyncError, UploadContentError}; @@ -72,6 +74,12 @@ pub fn diagnose(err: &AnyhowError) -> Diagnosis { } } + if let Some(create_canister_err) = err.downcast_ref::() { + if insufficient_cycles(create_canister_err) { + return diagnose_insufficient_cycles(); + } + } + NULL_DIAGNOSIS } @@ -262,3 +270,28 @@ fn diagnose_ledger_not_found() -> Diagnosis { (Some(explanation.to_string()), Some(suggestion.to_string())) } + +fn insufficient_cycles(err: &CreateCanisterError) -> bool { + matches!(err, CreateCanisterError::InsufficientFunds { balance: _ }) +} + +fn diagnose_insufficient_cycles() -> Diagnosis { + let network = match get_network_context() { + Ok(value) => { + if value == "local" { + "".to_string() + } else { + format!(" --network {}", value) + } + } + Err(_) => "".to_string(), + }; + + let explanation = "Insufficient cycles balance to create the canister."; + let suggestion = format!( + "Please top up your cycles balance by converting ICP to cycles like below: +'dfx cycles convert --amount=0.123{}'", + network + ); + (Some(explanation.to_string()), Some(suggestion)) +}