diff --git a/errors/src/errors/package/package_errors.rs b/errors/src/errors/package/package_errors.rs index 7346d39705..7f748f2d44 100644 --- a/errors/src/errors/package/package_errors.rs +++ b/errors/src/errors/package/package_errors.rs @@ -416,6 +416,6 @@ create_messages!( execution_error { args: (error: impl Display), msg: format!("❌ Execution error: {error}"), - help: None, + help: Some("Make sure that you are using the right network. Use `--network testnet` to switch to testnet.".to_string()), } ); diff --git a/errors/src/errors/utils/util_errors.rs b/errors/src/errors/utils/util_errors.rs index f91cfbd59f..1d3bea68a3 100644 --- a/errors/src/errors/utils/util_errors.rs +++ b/errors/src/errors/utils/util_errors.rs @@ -65,7 +65,7 @@ create_messages!( network_error { args: (url: impl Display, status: impl Display), msg: format!("Failed network request to {url}. Status: {status}"), - help: None, + help: Some("Make sure that you are using the right network and endpoint. Use `--network testnet` to switch to testnet.".to_string()), } @formatted diff --git a/leo/cli/cli.rs b/leo/cli/cli.rs index bc3b647821..67b009bb21 100644 --- a/leo/cli/cli.rs +++ b/leo/cli/cli.rs @@ -126,7 +126,7 @@ pub fn run_with_args(cli: CLI) -> Result<()> { // Get custom root folder and create context for it. // If not specified, default context will be created in cwd. - let context = handle_error(Context::new(cli.path, cli.home)); + let context = handle_error(Context::new(cli.path, cli.home, false)); match cli.command { Commands::Add { command } => command.try_execute(context), diff --git a/leo/cli/commands/deploy.rs b/leo/cli/commands/deploy.rs index 978398a2a6..c2aa9ba1ed 100644 --- a/leo/cli/commands/deploy.rs +++ b/leo/cli/commands/deploy.rs @@ -128,15 +128,17 @@ fn handle_deploy, N: Network>( let vm = VM::from(store)?; // Compute the minimum deployment cost. - let (total_cost, (storage_cost, synthesis_cost, namespace_cost)) = deployment_cost(&deployment)?; + let (mut total_cost, (storage_cost, synthesis_cost, namespace_cost)) = deployment_cost(&deployment)?; // Display the deployment cost breakdown using `credit` denomination. + total_cost += command.fee_options.priority_fee; deploy_cost_breakdown( name, total_cost as f64 / 1_000_000.0, storage_cost as f64 / 1_000_000.0, synthesis_cost as f64 / 1_000_000.0, namespace_cost as f64 / 1_000_000.0, + command.fee_options.priority_fee as f64 / 1_000_000.0, ); // Initialize an RNG. @@ -157,6 +159,8 @@ fn handle_deploy, N: Network>( vm.execute_fee_authorization(fee_authorization, Some(query.clone()), rng)? } None => { + // Make sure the user has enough public balance to pay for the deployment. + check_balance(&private_key, &command.options.endpoint, &command.options.network, context.clone(), total_cost)?; let fee_authorization = vm.authorize_fee_public( &private_key, total_cost, @@ -193,7 +197,7 @@ fn handle_deploy, N: Network>( } // A helper function to display a cost breakdown of the deployment. -fn deploy_cost_breakdown(name: &String, total_cost: f64, storage_cost: f64, synthesis_cost: f64, namespace_cost: f64) { +fn deploy_cost_breakdown(name: &String, total_cost: f64, storage_cost: f64, synthesis_cost: f64, namespace_cost: f64, priority_fee: f64) { println!("Base deployment cost for '{}' is {} credits.", name.bold(), total_cost); // Display the cost breakdown in a table. let data = [ @@ -207,6 +211,7 @@ fn deploy_cost_breakdown(name: &String, total_cost: f64, storage_cost: f64, synt "Namespace", &format!("{:.6}", namespace_cost), ], + ["Priority Fee", &format!("{:.6}", priority_fee)], ["Total", &format!("{:.6}", total_cost)], ]; let mut out = Vec::new(); diff --git a/leo/cli/commands/execute.rs b/leo/cli/commands/execute.rs index beed7cc6dd..9a237c5972 100644 --- a/leo/cli/commands/execute.rs +++ b/leo/cli/commands/execute.rs @@ -186,35 +186,25 @@ fn handle_execute(command: Execute, context: Context) -> Result<::try_from(ViewKey::try_from(&private_key)?)?; - // Query the public balance of the address on the `account` mapping from `credits.aleo`. - let mut public_balance = Query { - endpoint: command.compiler_options.endpoint.clone(), - network: command.compiler_options.network.clone(), - command: QueryCommands::Program { - command: crate::cli::commands::query::Program { - name: "credits".to_string(), - mappings: false, - mapping_value: Some(vec!["account".to_string(), address.to_string()]), - }, - }, - } - .execute(context.clone())?; - // Check balance. - // Remove the last 3 characters since they represent the `u64` suffix. - public_balance.truncate(public_balance.len() - 3); - if public_balance.parse::().unwrap() < total_cost { - return Err(PackageError::insufficient_balance(public_balance, total_cost).into()); - } + check_balance::(&private_key, &command.compiler_options.endpoint, &command.compiler_options.network, context, total_cost)?; } println!("✅ Created execution transaction for '{}'", program_id.to_string().bold()); @@ -331,7 +321,7 @@ fn load_program_from_network( }, }, } - .execute(context.clone())?; + .execute(Context::new(context.path.clone(), context.home.clone(), true)?)?; let program = SnarkVMProgram::::from_str(&program_src).unwrap(); // Return early if the program is already loaded. @@ -357,7 +347,7 @@ fn load_program_from_network( } // A helper function to display a cost breakdown of the execution. -fn execution_cost_breakdown(name: &String, total_cost: f64, storage_cost: f64, finalize_cost: f64) { +fn execution_cost_breakdown(name: &String, total_cost: f64, storage_cost: f64, finalize_cost: f64, priority_fee: f64) { println!("Base execution cost for '{}' is {} credits.", name.bold(), total_cost); // Display the cost breakdown in a table. let data = [ @@ -370,6 +360,10 @@ fn execution_cost_breakdown(name: &String, total_cost: f64, storage_cost: f64, f "On-chain Execution", &format!("{:.6}", finalize_cost), ], + [ + "Priority Fee", + &format!("{:.6}", priority_fee), + ], ["Total", &format!("{:.6}", total_cost)], ]; let mut out = Vec::new(); diff --git a/leo/cli/commands/mod.rs b/leo/cli/commands/mod.rs index b5344ecced..7ecece51a2 100644 --- a/leo/cli/commands/mod.rs +++ b/leo/cli/commands/mod.rs @@ -57,7 +57,7 @@ use super::*; use crate::cli::helpers::context::*; use leo_errors::{emitter::Handler, CliError, PackageError, Result}; use leo_package::{build::*, outputs::OutputsDirectory, package::*}; -use snarkvm::prelude::{block::Transaction, Ciphertext, Plaintext, PrivateKey, Record, ViewKey}; +use snarkvm::prelude::{Address, block::Transaction, Ciphertext, Plaintext, PrivateKey, Record, ViewKey}; use clap::Parser; use colored::Colorize; @@ -65,6 +65,7 @@ use std::str::FromStr; use tracing::span::Span; use snarkvm::console::network::Network; +use crate::cli::query::QueryCommands; /// Base trait for the Leo CLI, see methods and their documentation for details. pub trait Command { @@ -238,6 +239,32 @@ pub fn parse_record(private_key: &PrivateKey, record: &str) -> Re } } +fn check_balance(private_key: &PrivateKey, endpoint: &String, network: &String, context: Context, total_cost: u64) -> Result<()> { + // Derive the account address. + let address = Address::::try_from(ViewKey::try_from(private_key)?)?; + // Query the public balance of the address on the `account` mapping from `credits.aleo`. + let mut public_balance = Query { + endpoint: endpoint.clone(), + network: network.clone(), + command: QueryCommands::Program { + command: crate::cli::commands::query::Program { + name: "credits".to_string(), + mappings: false, + mapping_value: Some(vec!["account".to_string(), address.to_string()]), + }, + }, + } + .execute(Context::new(context.path.clone(), context.home.clone(), true)?)?; + // Remove the last 3 characters since they represent the `u64` suffix. + public_balance.truncate(public_balance.len() - 3); + // Compare balance. + if public_balance.parse::().unwrap() < total_cost { + return Err(PackageError::insufficient_balance(public_balance, total_cost).into()); + } else { + Ok(()) + } +} + /// Determine if the transaction should be broadcast or displayed to user. fn handle_broadcast(endpoint: &String, transaction: Transaction, operation: &String) -> Result<()> { println!("Broadcasting transaction to {}...", endpoint.clone()); diff --git a/leo/cli/commands/query/mod.rs b/leo/cli/commands/query/mod.rs index be64a3a7df..2ca7645165 100644 --- a/leo/cli/commands/query/mod.rs +++ b/leo/cli/commands/query/mod.rs @@ -72,6 +72,7 @@ impl Command for Query { } fn apply(self, context: Context, _: Self::Input) -> Result { + let recursive = context.recursive; let output = match self.command { QueryCommands::Block { command } => command.apply(context, ())?, QueryCommands::Transaction { command } => command.apply(context, ())?, @@ -103,10 +104,12 @@ impl Command for Query { .call() .map_err(|err| UtilError::failed_to_retrieve_from_endpoint(err, Default::default()))?; if response.status() == 200 { - tracing::info!("✅ Successfully retrieved data from '{url}'.\n"); // Unescape the newlines. let result = response.into_string().unwrap().replace("\\n", "\n").replace('\"', ""); - println!("{}\n", result); + if !recursive { + tracing::info!("✅ Successfully retrieved data from '{url}'.\n"); + println!("{}\n", result); + } Ok(result) } else { Err(UtilError::network_error(url, response.status(), Default::default()).into()) diff --git a/leo/cli/helpers/context.rs b/leo/cli/helpers/context.rs index e80ec3ddcc..007c5f9075 100644 --- a/leo/cli/helpers/context.rs +++ b/leo/cli/helpers/context.rs @@ -39,11 +39,13 @@ pub struct Context { pub path: Option, /// Path to use for the Aleo registry, None when default pub home: Option, + /// Recursive flag. + pub recursive: bool, } impl Context { - pub fn new(path: Option, home: Option) -> Result { - Ok(Context { path, home }) + pub fn new(path: Option, home: Option, recursive: bool) -> Result { + Ok(Context { path, home, recursive }) } /// Returns the path of the parent directory to the Leo package.