Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Unreleased

* chore!: use `--tcycles` for consistency instead of raw `--cycles` amounts
* fix: Validate explicit canister paths and throw an error if `canister.yaml` is not found

# v0.1.0-beta.3
Expand Down
6 changes: 4 additions & 2 deletions crates/icp-canister-interfaces/src/cycles_ledger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,10 @@ impl CreateCanisterError {
CreateCanisterError::TooOld => "created_at_time is too old.".to_string(),
CreateCanisterError::InsufficientFunds { balance } => {
format!(
"Insufficient cycles. Requested: {requested_cycles} cycles, available balance: {balance} cycles.
use `icp cycles mint` to get more cycles or use `--cycles` to specify a different amount."
"Insufficient cycles. Requested: {} TCYCLES, available balance: {} TCYCLES.
use `icp cycles mint` to get more cycles or use `--tcycles` to specify a different amount.",
BigDecimal::new(requested_cycles.into(), CYCLES_LEDGER_DECIMALS),
BigDecimal::from_biguint(balance.0.clone(), CYCLES_LEDGER_DECIMALS)
)
}
}
Expand Down
13 changes: 7 additions & 6 deletions crates/icp-cli/src/commands/canister/create.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use anyhow::anyhow;
use bigdecimal::BigDecimal;
use candid::{Nat, Principal};
use clap::Args;
use icp::context::Context;
use icp::{Canister, context::CanisterSelection, prelude::*};
use icp::{Canister, context::CanisterSelection};
use icp_canister_interfaces::cycles_ledger::CanisterSettingsArg;

use crate::{
Expand All @@ -11,7 +12,7 @@ use crate::{
progress::{ProgressManager, ProgressManagerSettings},
};

pub(crate) const DEFAULT_CANISTER_CYCLES: u128 = 2 * TRILLION;
pub(crate) const DEFAULT_CANISTER_TCYCLES: &str = "2";

#[derive(Clone, Debug, Default, Args)]
pub(crate) struct CanisterSettings {
Expand Down Expand Up @@ -49,9 +50,9 @@ pub(crate) struct CreateArgs {
#[arg(long, short = 'q')]
pub(crate) quiet: bool,

/// Cycles to fund canister creation (in raw cycles).
#[arg(long, default_value_t = DEFAULT_CANISTER_CYCLES)]
pub(crate) cycles: u128,
/// Cycles to fund canister creation (in TCYCLES).
#[arg(long, default_value = DEFAULT_CANISTER_TCYCLES)]
pub(crate) tcycles: BigDecimal,

/// The subnet to create canisters on.
#[arg(long)]
Expand Down Expand Up @@ -128,7 +129,7 @@ pub(crate) async fn exec(ctx: &Context, args: &CreateArgs) -> Result<(), anyhow:
.collect();
let progress_manager = ProgressManager::new(ProgressManagerSettings { hidden: ctx.debug });
let create_operation =
CreateOperation::new(agent, args.subnet, args.cycles, existing_canisters);
CreateOperation::new(agent, args.subnet, args.tcycles.clone(), existing_canisters);

let canister_settings = args.canister_settings_with_default(&canister_info);
let pb = progress_manager.create_progress_bar(&canister);
Expand Down
12 changes: 6 additions & 6 deletions crates/icp-cli/src/commands/cycles/mint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@ use crate::operations::token::mint::mint_cycles;
#[derive(Debug, Args)]
pub(crate) struct MintArgs {
/// Amount of ICP to mint to cycles.
#[arg(long, conflicts_with = "cycles")]
#[arg(long, conflicts_with = "tcycles")]
pub(crate) icp: Option<BigDecimal>,

/// Amount of cycles to mint. Automatically determines the amount of ICP needed.
/// Amount of cycles to mint (in TCYCLES). Automatically determines the amount of ICP needed.
#[arg(long, conflicts_with = "icp")]
pub(crate) cycles: Option<u128>,
pub(crate) tcycles: Option<BigDecimal>,

#[command(flatten)]
pub(crate) token_command_args: TokenCommandArgs,
}

pub(crate) async fn exec(ctx: &Context, args: &MintArgs) -> Result<(), anyhow::Error> {
// Validate args
if args.icp.is_none() && args.cycles.is_none() {
bail!("no amount specified. Use --icp or --cycles");
if args.icp.is_none() && args.tcycles.is_none() {
bail!("no amount specified. Use --icp or --tcycles");
}

let selections = args.token_command_args.selections();
Expand All @@ -38,7 +38,7 @@ pub(crate) async fn exec(ctx: &Context, args: &MintArgs) -> Result<(), anyhow::E
.await?;

// Execute mint operation
let mint_info = mint_cycles(&agent, args.icp.as_ref(), args.cycles).await?;
let mint_info = mint_cycles(&agent, args.icp.as_ref(), args.tcycles.as_ref()).await?;

// Display results
let _ = ctx.term.write_line(&format!(
Expand Down
9 changes: 5 additions & 4 deletions crates/icp-cli/src/commands/deploy/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use anyhow::anyhow;
use bigdecimal::BigDecimal;
use candid::{CandidType, Principal};
use clap::Args;
use futures::{StreamExt, future::try_join_all, stream::FuturesOrdered};
Expand Down Expand Up @@ -40,9 +41,9 @@ pub(crate) struct DeployArgs {
#[arg(long)]
pub(crate) controller: Vec<Principal>,

/// Cycles to fund canister creation (in cycles).
#[arg(long, default_value_t = create::DEFAULT_CANISTER_CYCLES)]
pub(crate) cycles: u128,
/// Cycles to fund canister creation (in TCYCLES).
#[arg(long, default_value = create::DEFAULT_CANISTER_TCYCLES)]
pub(crate) tcycles: BigDecimal,

#[command(flatten)]
pub(crate) identity: IdentityOpt,
Expand Down Expand Up @@ -115,7 +116,7 @@ pub(crate) async fn exec(ctx: &Context, args: &DeployArgs) -> Result<(), anyhow:
let create_operation = CreateOperation::new(
agent.clone(),
args.subnet,
args.cycles,
args.tcycles.clone(),
existing_canisters.into_values().collect(),
);
let mut futs = FuturesOrdered::new();
Expand Down
22 changes: 15 additions & 7 deletions crates/icp-cli/src/operations/create.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use bigdecimal::{BigDecimal, ToPrimitive};
use candid::{Decode, Encode, Nat, Principal};
use ic_agent::{Agent, AgentError};
use icp_canister_interfaces::{
cycles_ledger::{
CYCLES_LEDGER_PRINCIPAL, CanisterSettingsArg, CreateCanisterArgs, CreateCanisterResponse,
CreationArgs, SubnetSelectionArg,
CYCLES_LEDGER_DECIMALS, CYCLES_LEDGER_PRINCIPAL, CanisterSettingsArg, CreateCanisterArgs,
CreateCanisterResponse, CreationArgs, SubnetSelectionArg,
},
cycles_minting_canister::CYCLES_MINTING_CANISTER_PRINCIPAL,
registry::{GetSubnetForCanisterRequest, GetSubnetForCanisterResult, REGISTRY_PRINCIPAL},
Expand Down Expand Up @@ -44,12 +45,15 @@ pub enum CreateOperationError {

#[snafu(display("failed to resolve subnet: {message}"))]
SubnetResolution { message: String },

#[snafu(display("cycles amount overflow"))]
CyclesAmountOverflow,
}

struct CreateOperationInner {
agent: Agent,
subnet: Option<Principal>,
cycles: u128,
tcycles: BigDecimal,
existing_canisters: Vec<Principal>,
resolved_subnet: OnceCell<Result<Principal, String>>,
}
Expand All @@ -70,14 +74,14 @@ impl CreateOperation {
pub fn new(
agent: Agent,
subnet: Option<Principal>,
cycles: u128,
tcycles: BigDecimal,
existing_canisters: Vec<Principal>,
) -> Self {
Self {
inner: Arc::new(CreateOperationInner {
agent,
subnet,
cycles,
tcycles,
existing_canisters,
resolved_subnet: OnceCell::new(),
}),
Expand All @@ -92,6 +96,10 @@ impl CreateOperation {
&self,
settings: &CanisterSettingsArg,
) -> Result<Principal, CreateOperationError> {
let raw_cycles = (&self.inner.tcycles * 10u128.pow(CYCLES_LEDGER_DECIMALS as u32))
.to_u128()
.ok_or(CreateOperationError::CyclesAmountOverflow)?;

let creation_args = CreationArgs {
subnet_selection: Some(SubnetSelectionArg::Subnet {
subnet: self
Expand All @@ -104,7 +112,7 @@ impl CreateOperation {
let arg = CreateCanisterArgs {
from_subaccount: None,
created_at_time: None,
amount: Nat::from(self.inner.cycles),
amount: Nat::from(raw_cycles),
creation_args: Some(creation_args),
};

Expand All @@ -123,7 +131,7 @@ impl CreateOperation {
CreateCanisterResponse::Ok { canister_id, .. } => canister_id,
CreateCanisterResponse::Err(err) => {
return CreateCanisterSnafu {
message: err.format_error(self.inner.cycles),
message: err.format_error(raw_cycles),
}
.fail();
}
Expand Down
12 changes: 8 additions & 4 deletions crates/icp-cli/src/operations/token/mint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,17 @@ pub struct MintInfo {
///
/// * `agent` - The IC agent to use for queries and updates
/// * `icp_amount` - Optional ICP amount to convert to cycles
/// * `cycles_amount` - Optional desired cycles amount (will calculate required ICP)
/// * `tcycles_amount` - Optional desired cycles amount in TCYCLES (will calculate required ICP)
///
/// One of `icp_amount` or `cycles_amount` must be provided (but not both).
/// One of `icp_amount` or `tcycles_amount` must be provided (but not both).
///
/// # Returns
///
/// A `MintInfo` struct containing the deposited amount (minus fees) and new balance in TCYCLES
pub async fn mint_cycles(
agent: &Agent,
icp_amount: Option<&BigDecimal>,
cycles_amount: Option<u128>,
tcycles_amount: Option<&BigDecimal>,
) -> Result<MintInfo, MintCyclesError> {
// Get user principal
let user_principal = agent
Expand All @@ -86,7 +86,11 @@ pub async fn mint_cycles(
(icp_amount * 100_000_000_u64)
.to_u64()
.ok_or(MintCyclesError::IcpAmountOverflow)?
} else if let Some(cycles_amount) = cycles_amount {
} else if let Some(tcycles_amount) = tcycles_amount {
let cycles_amount = (tcycles_amount.clone() * 10u128.pow(CYCLES_LEDGER_DECIMALS as u32))
.to_u128()
.ok_or(MintCyclesError::IcpAmountOverflow)?;

// Query CMC for conversion rate
let cmc_response = agent
.query(
Expand Down
8 changes: 4 additions & 4 deletions crates/icp-cli/tests/canister_create_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ async fn canister_create_with_settings() {
"my-canister",
"--environment",
"random-environment",
"--cycles",
&format!("{}", 70 * TRILLION), /* 70 TCYCLES because compute allocation is expensive */
"--tcycles",
"70", /* 70 TCYCLES because compute allocation is expensive */
])
.assert()
.success();
Expand Down Expand Up @@ -202,8 +202,8 @@ async fn canister_create_with_settings_cmdline_override() {
"2",
"--environment",
"random-environment",
"--cycles",
&format!("{}", 70 * TRILLION), /* 70 TCYCLES because compute allocation is expensive */
"--tcycles",
"70", /* 70 TCYCLES because compute allocation is expensive */
])
.assert()
.success();
Expand Down
4 changes: 2 additions & 2 deletions crates/icp-cli/tests/canister_settings_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -682,8 +682,8 @@ async fn canister_settings_update_miscellaneous() {
"deploy",
"--subnet",
common::SUBNET_ID,
"--cycles",
&format!("{}", 120 * TRILLION), // 120 TCYCLES because compute allocation is expensive
"--tcycles",
"120", // 120 TCYCLES because compute allocation is expensive
"--environment",
"random-environment",
])
Expand Down
7 changes: 5 additions & 2 deletions crates/icp-cli/tests/common/clients/icp_cli.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use bigdecimal::BigDecimal;
use candid::Principal;
use icp::{prelude::*, project::DEFAULT_LOCAL_ENVIRONMENT_NAME};
use icp_canister_interfaces::cycles_ledger::CYCLES_LEDGER_DECIMALS;

use crate::common::TestContext;

Expand Down Expand Up @@ -78,14 +80,15 @@ impl<'a> Client<'a> {
}

pub(crate) fn mint_cycles(&self, amount: u128) {
let tcycles = BigDecimal::new(amount.into(), CYCLES_LEDGER_DECIMALS);
self.ctx
.icp()
.current_dir(&self.current_dir)
.args([
"cycles",
"mint",
"--cycles",
&amount.to_string(),
"--tcycles",
&tcycles.to_string(),
"--environment",
&self.environment,
])
Expand Down
8 changes: 4 additions & 4 deletions crates/icp-cli/tests/cycles_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ async fn cycles_balance() {
.args([
"cycles",
"mint",
"--cycles",
"1000000000",
"--tcycles",
"0.001",
"--environment",
"random-environment",
])
Expand All @@ -89,8 +89,8 @@ async fn cycles_balance() {
.args([
"cycles",
"mint",
"--cycles",
"1500000000",
"--tcycles",
"0.0015",
"--environment",
"random-environment",
])
Expand Down
10 changes: 5 additions & 5 deletions docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,9 @@ Create a canister on a network
* `--freezing-threshold <FREEZING_THRESHOLD>` — Optional freezing threshold in seconds. Controls how long a canister can be inactive before being frozen
* `--reserved-cycles-limit <RESERVED_CYCLES_LIMIT>` — Optional reserved cycles limit. If set, the canister cannot consume more than this many cycles
* `-q`, `--quiet` — Suppress human-readable output; print only canister IDs, one per line, to stdout
* `--cycles <CYCLES>` — Cycles to fund canister creation (in raw cycles)
* `--tcycles <TCYCLES>` — Cycles to fund canister creation (in TCYCLES)

Default value: `2000000000000`
Default value: `2`
* `--subnet <SUBNET>` — The subnet to create canisters on


Expand Down Expand Up @@ -445,7 +445,7 @@ Convert icp to cycles
###### **Options:**

* `--icp <ICP>` — Amount of ICP to mint to cycles
* `--cycles <CYCLES>` — Amount of cycles to mint. Automatically determines the amount of ICP needed
* `--tcycles <TCYCLES>` — Amount of cycles to mint (in TCYCLES). Automatically determines the amount of ICP needed
* `--network <NETWORK>` — Name of the network to target, conflicts with environment argument
* `--mainnet` — Shorthand for --network=mainnet
* `-e`, `--environment <ENVIRONMENT>` — Override the environment to connect to. By default, the local environment is used
Expand Down Expand Up @@ -474,9 +474,9 @@ Deploy a project to an environment

* `--subnet <SUBNET>` — The subnet to use for the canisters being deployed
* `--controller <CONTROLLER>` — One or more controllers for the canisters being deployed. Repeat `--controller` to specify multiple
* `--cycles <CYCLES>` — Cycles to fund canister creation (in cycles)
* `--tcycles <TCYCLES>` — Cycles to fund canister creation (in TCYCLES)

Default value: `2000000000000`
Default value: `2`
* `--identity <IDENTITY>` — The user identity to run this command as
* `-e`, `--environment <ENVIRONMENT>` — Override the environment to connect to. By default, the local environment is used
* `--ic` — Shorthand for --environment=ic
Expand Down