diff --git a/docs/book/src/forc/plugins/forc_client/index.md b/docs/book/src/forc/plugins/forc_client/index.md index ce81872e9e1..2ecce8826b6 100644 --- a/docs/book/src/forc/plugins/forc_client/index.md +++ b/docs/book/src/forc/plugins/forc_client/index.md @@ -56,20 +56,26 @@ By default `--default-signer` flag would sign your transactions with the followi ## Interacting with the testnet -While using `forc-deploy` or `forc-run` to interact with the testnet you need to pass the testnet end point with `--node-url` +To interact with the latest testnet, use the `--testnet` flag. When this flag is passed, transactions created by `forc-deploy` will be sent to the `beta-4` testnet. ```sh -forc-deploy --node-url https://beta-3.fuel.network/graphql +forc-deploy --testnet ``` -Since deploying and running projects on the testnet cost gas, you will need coins to pay for them. You can get some using the [testnet faucet](https://faucet-beta-3.fuel.network/). +It is also possible to pass the exact node url while using `forc-deploy` or `forc-run` which can be done using `--node-url` flag. -Also the default value of the "gas price" parameter is 0 for both `forc-deploy` and `forc-run`. Without changing it you will get an error complaining about gas price being too low. While using testnet you can pass `--gas-price 1` to overcome this issue. So a complete command for deploying to the testnet would look like: +```sh +forc-deploy --node-url https://beta-3.fuel.network +``` + +Another alternative is the `--target` option, which provides useful aliases to all targets. For example if you want to deploy to `beta-3` you can use: ```sh -forc-deploy --node-url https://beta-3.fuel.network/graphql --gas-price 1 +forc-deploy --target beta-3 ``` +Since deploying and running projects on the testnet cost gas, you will need coins to pay for them. You can get some using the [testnet faucet](https://faucet-beta-4.fuel.network/). + ## Deployment Artifacts forc-deploy saves the details of each deployment in the `out/deployments` folder within the project's root directory. Below is an example of a deployment artifact: diff --git a/forc-pkg/src/manifest.rs b/forc-pkg/src/manifest.rs index b5904c11efa..9e273e7a361 100644 --- a/forc-pkg/src/manifest.rs +++ b/forc-pkg/src/manifest.rs @@ -1,6 +1,6 @@ use crate::pkg::{manifest_file_missing, parsing_failed, wrong_program_type}; use anyhow::{anyhow, bail, Context, Result}; -use forc_tracing::println_yellow_err; +use forc_tracing::println_warning; use forc_util::{find_nested_manifest_dir, find_parent_manifest_dir, validate_name}; use serde::{Deserialize, Serialize}; use std::{ @@ -508,7 +508,7 @@ impl PackageManifest { }) .map_err(|e| anyhow!("failed to parse manifest: {}.", e))?; for warning in warnings { - println_yellow_err(&warning); + println_warning(&warning); } manifest.implicitly_include_std_if_missing(); manifest.implicitly_include_default_build_profiles_if_missing(); @@ -930,7 +930,7 @@ impl WorkspaceManifest { }) .map_err(|e| anyhow!("failed to parse manifest: {}.", e))?; for warning in warnings { - println_yellow_err(&warning); + println_warning(&warning); } Ok(manifest) } diff --git a/forc-plugins/forc-client/src/bin/deploy.rs b/forc-plugins/forc-client/src/bin/deploy.rs index bbe3e5f73f7..030db172bc3 100644 --- a/forc-plugins/forc-client/src/bin/deploy.rs +++ b/forc-plugins/forc-client/src/bin/deploy.rs @@ -1,12 +1,12 @@ use clap::Parser; -use forc_tracing::init_tracing_subscriber; +use forc_tracing::{init_tracing_subscriber, println_error}; #[tokio::main] async fn main() { init_tracing_subscriber(Default::default()); let command = forc_client::cmd::Deploy::parse(); if let Err(err) = forc_client::op::deploy(command).await { - tracing::error!("Error: {:?}", err); + println_error(&format!("{}", err)); std::process::exit(1); } } diff --git a/forc-plugins/forc-client/src/bin/run.rs b/forc-plugins/forc-client/src/bin/run.rs index 9599accbd86..77305f90bfa 100644 --- a/forc-plugins/forc-client/src/bin/run.rs +++ b/forc-plugins/forc-client/src/bin/run.rs @@ -1,12 +1,12 @@ use clap::Parser; -use forc_tracing::init_tracing_subscriber; +use forc_tracing::{init_tracing_subscriber, println_error}; #[tokio::main] async fn main() { init_tracing_subscriber(Default::default()); let command = forc_client::cmd::Run::parse(); if let Err(err) = forc_client::op::run(command).await { - tracing::error!("Error: {:?}", err); + println_error(&format!("{}", err)); std::process::exit(1); } } diff --git a/forc-plugins/forc-client/src/bin/submit.rs b/forc-plugins/forc-client/src/bin/submit.rs index 3561cf35297..cb0155a1c62 100644 --- a/forc-plugins/forc-client/src/bin/submit.rs +++ b/forc-plugins/forc-client/src/bin/submit.rs @@ -1,12 +1,12 @@ use clap::Parser; -use forc_tracing::init_tracing_subscriber; +use forc_tracing::{init_tracing_subscriber, println_error}; #[tokio::main] async fn main() { init_tracing_subscriber(Default::default()); let command = forc_client::cmd::Submit::parse(); if let Err(err) = forc_client::op::submit(command).await { - tracing::error!("Error: {:?}", err); + println_error(&format!("{}", err)); std::process::exit(1); } } diff --git a/forc-plugins/forc-client/src/cmd/deploy.rs b/forc-plugins/forc-client/src/cmd/deploy.rs index 5352440ef17..23a38ada2d9 100644 --- a/forc-plugins/forc-client/src/cmd/deploy.rs +++ b/forc-plugins/forc-client/src/cmd/deploy.rs @@ -1,11 +1,12 @@ use clap::Parser; use fuel_crypto::SecretKey; -pub use crate::util::Target; pub use forc::cli::shared::{BuildOutput, BuildProfile, Minify, Pkg, Print}; pub use forc_tx::{Gas, Maturity}; pub use forc_util::tx_utils::Salt; +use crate::NodeTarget; + #[derive(Debug, Default, Parser)] #[clap(bin_name = "forc deploy", version)] pub struct Command { @@ -19,6 +20,8 @@ pub struct Command { pub gas: Gas, #[clap(flatten)] pub maturity: Maturity, + #[clap(flatten)] + pub node: NodeTarget, /// Optional 256-bit hexadecimal literal(s) to redeploy contracts. /// /// For a single contract, use `--salt `, eg.: forc deploy --salt 0x0000000000000000000000000000000000000000000000000000000000000001 @@ -38,11 +41,6 @@ pub struct Command { pub build_output: BuildOutput, #[clap(flatten)] pub build_profile: BuildProfile, - /// The URL of the Fuel node to which we're submitting the transaction. - /// If unspecified, checks the manifest's `network` table, then falls back - /// to [`crate::default::NODE_URL`]. - #[clap(long, env = "FUEL_NODE_URL")] - pub node_url: Option, /// Sign the transaction with default signer that is pre-funded by fuel-core. Useful for testing against local node. #[clap(long)] pub default_signer: bool, @@ -54,12 +52,4 @@ pub struct Command { /// Sign the deployment transaction manually. #[clap(long)] pub manual_signing: bool, - /// Use preset configurations for deploying to a specific target. - /// - /// Possible values are: [beta-1, beta-2, beta-3, latest] - #[clap(long)] - pub target: Option, - /// Use preset configuration for the latest testnet. - #[clap(long)] - pub testnet: bool, } diff --git a/forc-plugins/forc-client/src/cmd/run.rs b/forc-plugins/forc-client/src/cmd/run.rs index 665be9dd6a7..df2f2f69aef 100644 --- a/forc-plugins/forc-client/src/cmd/run.rs +++ b/forc-plugins/forc-client/src/cmd/run.rs @@ -1,3 +1,4 @@ +use crate::NodeTarget; use clap::Parser; use fuel_crypto::SecretKey; @@ -24,11 +25,8 @@ pub struct Command { pub build_output: BuildOutput, #[clap(flatten)] pub build_profile: BuildProfile, - /// The URL of the Fuel node to which we're submitting the transaction. - /// If unspecified, checks the manifest's `network` table, then falls back - /// to [`crate::default::NODE_URL`]. - #[clap(long, env = "FUEL_NODE_URL")] - pub node_url: Option, + #[clap(flatten)] + pub node: NodeTarget, /// Hex string of data to input to script. #[clap(short, long)] pub data: Option, diff --git a/forc-plugins/forc-client/src/cmd/submit.rs b/forc-plugins/forc-client/src/cmd/submit.rs index 7a12d17a06f..1c71e1b2570 100644 --- a/forc-plugins/forc-client/src/cmd/submit.rs +++ b/forc-plugins/forc-client/src/cmd/submit.rs @@ -1,3 +1,4 @@ +use crate::NodeTarget; use devault::Devault; use std::path::PathBuf; @@ -20,10 +21,8 @@ pub struct Command { /// Options related to networking. #[derive(Debug, Devault, clap::Args)] pub struct Network { - /// The URL of the Fuel node to which we're submitting the transaction. - #[clap(long, env = "FUEL_NODE_URL", default_value_t = String::from(crate::default::NODE_URL))] - #[devault("String::from(crate::default::NODE_URL)")] - pub node_url: String, + #[clap(flatten)] + pub node: NodeTarget, /// Whether or not to await confirmation that the transaction has been committed. /// /// When `true`, await commitment and output the transaction status. diff --git a/forc-plugins/forc-client/src/constants.rs b/forc-plugins/forc-client/src/constants.rs new file mode 100644 index 00000000000..41e6dde59e7 --- /dev/null +++ b/forc-plugins/forc-client/src/constants.rs @@ -0,0 +1,6 @@ +/// Default to localhost to favour the common case of testing. +pub const NODE_URL: &str = sway_utils::constants::DEFAULT_NODE_URL; +pub const BETA_2_ENDPOINT_URL: &str = "https://node-beta-2.fuel.network"; +pub const BETA_3_ENDPOINT_URL: &str = "https://beta-3.fuel.network"; +pub const BETA_4_ENDPOINT_URL: &str = "https://beta-4.fuel.network"; +pub const BETA_4_FAUCET_URL: &str = "https://faucet-beta-4.fuel.network"; diff --git a/forc-plugins/forc-client/src/lib.rs b/forc-plugins/forc-client/src/lib.rs index f0e95a13e9a..0c8cb335f5f 100644 --- a/forc-plugins/forc-client/src/lib.rs +++ b/forc-plugins/forc-client/src/lib.rs @@ -1,11 +1,32 @@ pub mod cmd; +mod constants; pub mod op; mod util; -pub mod default { - /// Default to localhost to favour the common case of testing. - pub const NODE_URL: &str = sway_utils::constants::DEFAULT_NODE_URL; - pub const BETA_2_ENDPOINT_URL: &str = "node-beta-2.fuel.network/graphql"; - pub const BETA_3_ENDPOINT_URL: &str = "beta-3.fuel.network/graphql"; - pub const BETA_4_FAUCET_URL: &str = "https://faucet-beta-4.fuel.network"; +use clap::Parser; +use serde::{Deserialize, Serialize}; +use util::target::Target; + +/// Flags for specifying the node to target. +#[derive(Debug, Default, Parser, Deserialize, Serialize)] +pub struct NodeTarget { + /// The URL of the Fuel node to which we're submitting the transaction. + /// If unspecified, checks the manifest's `network` table, then falls back + /// to `http://127.0.0.1:4000` + /// + /// You can also use `--target` or `--testnet` to specify the Fuel node. + #[clap(long, env = "FUEL_NODE_URL")] + pub node_url: Option, + /// Use preset configurations for deploying to a specific target. + /// + /// You can also use `--node-url` or `--testnet` to specify the Fuel node. + /// + /// Possible values are: [beta-1, beta-2, beta-3, beta-4, local] + #[clap(long)] + pub target: Option, + /// Use preset configuration for the latest testnet. + /// + /// You can also use `--node-url` or `--target` to specify the Fuel node. + #[clap(long)] + pub testnet: bool, } diff --git a/forc-plugins/forc-client/src/op/deploy.rs b/forc-plugins/forc-client/src/op/deploy.rs index 92bc43ff9d4..b86ca2e2134 100644 --- a/forc-plugins/forc-client/src/op/deploy.rs +++ b/forc-plugins/forc-client/src/op/deploy.rs @@ -1,13 +1,15 @@ use crate::{ - cmd::{self, deploy::Target}, + cmd, util::{ + gas::{get_gas_limit, get_gas_price}, + node_url::get_node_url, pkg::built_pkgs, tx::{TransactionBuilderExt, WalletSelectionMode, TX_SUBMIT_TIMEOUT_MS}, }, }; use anyhow::{bail, Context, Result}; use forc_pkg::{self as pkg, PackageManifestFile}; -use forc_tx::Gas; +use forc_tracing::println_warning; use forc_util::default_output_directory; use fuel_core_client::client::types::TransactionStatus; use fuel_core_client::client::FuelClient; @@ -24,7 +26,7 @@ use std::{ }; use sway_core::language::parsed::TreeType; use sway_core::BuildTarget; -use tracing::{info, warn}; +use tracing::info; #[derive(Debug)] pub struct DeployedContract { @@ -116,11 +118,10 @@ fn validate_and_parse_salts<'a>( /// /// When deploying a single contract, only that contract's ID is returned. pub async fn deploy(command: cmd::Deploy) -> Result> { - let mut command = apply_target(command)?; if command.unsigned { - warn!(" Warning: --unsigned flag is deprecated, please prefer using --default-signer. Assuming `--default-signer` is passed. This means your transaction will be signed by an account that is funded by fuel-core by default for testing purposes."); - command.default_signer = true; + println_warning("--unsigned flag is deprecated, please prefer using --default-signer. Assuming `--default-signer` is passed. This means your transaction will be signed by an account that is funded by fuel-core by default for testing purposes."); } + let mut contract_ids = Vec::new(); let curr_dir = if let Some(ref path) = command.pkg.path { PathBuf::from(path) @@ -131,6 +132,11 @@ pub async fn deploy(command: cmd::Deploy) -> Result> { let build_opts = build_opts_from_cmd(&command); let built_pkgs = built_pkgs(&curr_dir, build_opts)?; + if built_pkgs.is_empty() { + println_warning("No deployable contracts found in the current directory."); + return Ok(contract_ids); + } + let contract_salt_map = if let Some(salt_input) = &command.salt { // If we're building 1 package, we just parse the salt as a string, ie. 0x00... // If we're building >1 package, we must parse the salt as a pair of strings, ie. contract_name:0x00... @@ -197,48 +203,6 @@ pub async fn deploy(command: cmd::Deploy) -> Result> { Ok(contract_ids) } -/// Applies specified target information to the provided arguments. -/// -/// Basically provides preset configurations for known test-nets. -fn apply_target(command: cmd::Deploy) -> Result { - let deploy_to_latest_testnet = command.testnet; - let target = if deploy_to_latest_testnet { - if command.target.is_some() { - bail!("Both `--testnet` and `--target` were specified: must choose one") - } - Some(Target::Beta3) - } else { - command.target.clone() - }; - - if let Some(target) = target { - match target { - cmd::deploy::Target::Beta2 | cmd::deploy::Target::Beta3 => { - // If the user did not specified a gas price, we can use `1` as a gas price for - // beta test-nets. - let gas_price = if command.gas.price == 0 { - 1 - } else { - command.gas.price - }; - - let target_url = Some(target.target_url().to_string()); - Ok(cmd::Deploy { - gas: Gas { - price: gas_price, - ..command.gas - }, - node_url: target_url, - ..command - }) - } - cmd::deploy::Target::LATEST => Ok(command), - } - } else { - Ok(command) - } -} - /// Deploy a single pkg given deploy command and the manifest file pub async fn deploy_pkg( command: &cmd::Deploy, @@ -246,12 +210,8 @@ pub async fn deploy_pkg( compiled: &BuiltPackage, salt: Salt, ) -> Result { - let node_url = command - .node_url - .as_deref() - .or_else(|| manifest.network.as_ref().map(|nw| &nw.url[..])) - .unwrap_or(crate::default::NODE_URL); - let client = FuelClient::new(node_url)?; + let node_url = get_node_url(&command.node, &manifest.network)?; + let client = FuelClient::new(node_url.clone())?; let bytecode = &compiled.bytecode.bytes; @@ -270,13 +230,13 @@ pub async fn deploy_pkg( }; let tx = TransactionBuilder::create(bytecode.as_slice().into(), salt, storage_slots.clone()) - .gas_limit(command.gas.limit) - .gas_price(command.gas.price) + .gas_limit(get_gas_limit(&command.gas, client.chain_info().await?)) + .gas_price(get_gas_price(&command.gas, client.node_info().await?)) .maturity(command.maturity.maturity.into()) .add_output(Output::contract_created(contract_id, state_root)) .finalize_signed( client.clone(), - command.default_signer, + command.default_signer || command.unsigned, command.signing_key, wallet_mode, ) diff --git a/forc-plugins/forc-client/src/op/run/mod.rs b/forc-plugins/forc-client/src/op/run/mod.rs index 4e8ffbc89c3..ea17dd723a7 100644 --- a/forc-plugins/forc-client/src/op/run/mod.rs +++ b/forc-plugins/forc-client/src/op/run/mod.rs @@ -2,12 +2,15 @@ mod encode; use crate::{ cmd, util::{ + gas::{get_gas_limit, get_gas_price}, + node_url::get_node_url, pkg::built_pkgs, tx::{TransactionBuilderExt, WalletSelectionMode, TX_SUBMIT_TIMEOUT_MS}, }, }; use anyhow::{anyhow, bail, Context, Result}; use forc_pkg::{self as pkg, fuel_core_not_running, PackageManifestFile}; +use forc_tracing::println_warning; use forc_util::tx_utils::format_log_receipts; use fuel_core_client::client::FuelClient; use fuel_tx::{ContractId, Transaction, TransactionBuilder}; @@ -17,7 +20,7 @@ use std::{path::PathBuf, str::FromStr}; use sway_core::language::parsed::TreeType; use sway_core::BuildTarget; use tokio::time::timeout; -use tracing::{info, warn}; +use tracing::info; use self::encode::ScriptCallHandler; @@ -34,7 +37,7 @@ pub struct RanScript { pub async fn run(command: cmd::Run) -> Result> { let mut command = command; if command.unsigned { - warn!(" Warning: --unsigned flag is deprecated, please prefer using --default-signer. Assuming `--default-signer` is passed. This means your transaction will be signed by an account that is funded by fuel-core by default for testing purposes."); + println_warning("--unsigned flag is deprecated, please prefer using --default-signer. Assuming `--default-signer` is passed. This means your transaction will be signed by an account that is funded by fuel-core by default for testing purposes."); command.default_signer = true; } let mut receipts = Vec::new(); @@ -65,6 +68,9 @@ pub async fn run_pkg( manifest: &PackageManifestFile, compiled: &BuiltPackage, ) -> Result { + let node_url = get_node_url(&command.node, &manifest.network)?; + let client = FuelClient::new(node_url.clone())?; + let script_data = match (&command.data, &command.args) { (None, Some(args)) => { let minify_json_abi = true; @@ -86,12 +92,6 @@ pub async fn run_pkg( } }; - let node_url = command - .node_url - .as_deref() - .or_else(|| manifest.network.as_ref().map(|nw| &nw.url[..])) - .unwrap_or(crate::default::NODE_URL); - let client = FuelClient::new(node_url)?; let contract_ids = command .contract .as_ref() @@ -109,8 +109,8 @@ pub async fn run_pkg( }; let tx = TransactionBuilder::script(compiled.bytecode.bytes.clone(), script_data) - .gas_limit(command.gas.limit) - .gas_price(command.gas.price) + .gas_limit(get_gas_limit(&command.gas, client.chain_info().await?)) + .gas_price(get_gas_price(&command.gas, client.node_info().await?)) .maturity(command.maturity.maturity.into()) .add_contracts(contract_ids) .finalize_signed( @@ -124,8 +124,13 @@ pub async fn run_pkg( info!("{:?}", tx); Ok(RanScript { receipts: vec![] }) } else { - let receipts = - try_send_tx(node_url, &tx.into(), command.pretty_print, command.simulate).await?; + let receipts = try_send_tx( + node_url.as_str(), + &tx.into(), + command.pretty_print, + command.simulate, + ) + .await?; Ok(RanScript { receipts }) } } diff --git a/forc-plugins/forc-client/src/op/submit.rs b/forc-plugins/forc-client/src/op/submit.rs index 8f66f7df379..ca0640e4955 100644 --- a/forc-plugins/forc-client/src/op/submit.rs +++ b/forc-plugins/forc-client/src/op/submit.rs @@ -1,11 +1,12 @@ -use crate::cmd; +use crate::{cmd, util::node_url::get_node_url}; use anyhow::Context; use fuel_core_client::client::{types::TransactionStatus, FuelClient}; /// A command for submitting transactions to a Fuel network. pub async fn submit(cmd: cmd::Submit) -> anyhow::Result<()> { let tx = read_tx(&cmd.tx_path)?; - let client = FuelClient::new(&cmd.network.node_url)?; + let node_url = get_node_url(&cmd.network.node, &None)?; + let client = FuelClient::new(node_url)?; if cmd.network.await_ { let status = client .submit_and_await_commit(&tx) diff --git a/forc-plugins/forc-client/src/util/gas.rs b/forc-plugins/forc-client/src/util/gas.rs new file mode 100644 index 00000000000..1a900746135 --- /dev/null +++ b/forc-plugins/forc-client/src/util/gas.rs @@ -0,0 +1,22 @@ +use forc_tx::Gas; +use fuel_core_client::client::types::{ChainInfo, NodeInfo}; + +/// Returns the gas to use for deployment, overriding default values if necessary. +pub fn get_gas_price(gas: &Gas, node_info: NodeInfo) -> u64 { + // TODO: write unit tests for this function once https://github.com/FuelLabs/fuel-core/issues/1312 is resolved. + if let Some(gas_price) = gas.price { + gas_price + } else { + node_info.min_gas_price + } +} + +/// Returns the gas to use for deployment, overriding default values if necessary. +pub fn get_gas_limit(gas: &Gas, chain_info: ChainInfo) -> u64 { + // TODO: write unit tests for this function once https://github.com/FuelLabs/fuel-core/issues/1312 is resolved. + if let Some(gas_limit) = gas.limit { + gas_limit + } else { + chain_info.consensus_parameters.max_gas_per_tx + } +} diff --git a/forc-plugins/forc-client/src/util/mod.rs b/forc-plugins/forc-client/src/util/mod.rs index fe43858ff87..3b99a89659f 100644 --- a/forc-plugins/forc-client/src/util/mod.rs +++ b/forc-plugins/forc-client/src/util/mod.rs @@ -1,49 +1,6 @@ -use std::str::FromStr; - pub(crate) mod encode; +pub(crate) mod gas; +pub(crate) mod node_url; pub(crate) mod pkg; +pub(crate) mod target; pub(crate) mod tx; - -use crate::default::{BETA_2_ENDPOINT_URL, BETA_3_ENDPOINT_URL, NODE_URL}; - -#[derive(Debug, Clone)] -/// Possible target values that forc-client can interact with. -pub enum Target { - Beta2, - Beta3, - LATEST, -} - -impl Default for Target { - fn default() -> Self { - Self::LATEST - } -} - -impl Target { - pub fn target_url(&self) -> &str { - match self { - Target::Beta2 => BETA_2_ENDPOINT_URL, - Target::Beta3 => BETA_3_ENDPOINT_URL, - Target::LATEST => NODE_URL, - } - } -} - -impl FromStr for Target { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - if s == "latest" { - Ok(Target::LATEST) - } else if s == "beta-2" { - Ok(Target::Beta2) - } else if s == "beta-3" { - Ok(Target::Beta3) - } else { - anyhow::bail!( - "invalid testnet name provided. Possible values are 'beta-2', 'beta-3', 'latest'." - ) - } - } -} diff --git a/forc-plugins/forc-client/src/util/node_url.rs b/forc-plugins/forc-client/src/util/node_url.rs new file mode 100644 index 00000000000..bfe73070caa --- /dev/null +++ b/forc-plugins/forc-client/src/util/node_url.rs @@ -0,0 +1,139 @@ +use anyhow::{bail, Result}; +use forc_pkg::manifest::Network; + +use crate::NodeTarget; + +use super::target::Target; + +/// Returns the URL to use for connecting to Fuel Core node. +pub fn get_node_url( + node_target: &NodeTarget, + manifest_network: &Option, +) -> Result { + let node_url = match ( + node_target.testnet, + node_target.target.clone(), + node_target.node_url.clone(), + ) { + (true, None, None) => Target::Beta4.target_url(), + (false, Some(target), None) => target.target_url(), + (false, None, Some(node_url)) => node_url, + (false, None, None) => manifest_network + .as_ref() + .map(|nw| &nw.url[..]) + .unwrap_or(crate::constants::NODE_URL) + .to_string(), + _ => bail!("Only one of `--testnet`, `--target`, or `--node-url` should be specified"), + }; + + Ok(node_url) +} + +#[test] +fn test_get_node_url_testnet() { + let input = NodeTarget { + target: None, + node_url: None, + testnet: true, + }; + + let actual = get_node_url(&input, &None).unwrap(); + assert_eq!("https://beta-4.fuel.network", actual); +} + +#[test] +fn test_get_node_url_beta4() { + let input = NodeTarget { + target: Some(Target::Beta4), + node_url: None, + testnet: false, + }; + let actual = get_node_url(&input, &None).unwrap(); + assert_eq!("https://beta-4.fuel.network", actual); +} + +#[test] +fn test_get_node_url_beta4_url() { + let input = NodeTarget { + target: None, + node_url: Some("https://beta-4.fuel.network".to_string()), + testnet: false, + }; + let actual = get_node_url(&input, &None).unwrap(); + assert_eq!("https://beta-4.fuel.network", actual); +} + +#[test] +fn test_get_node_url_url_beta4_manifest() { + let network = Network { + url: "https://beta-4.fuel.network".to_string(), + }; + let input = NodeTarget { + target: None, + node_url: None, + testnet: false, + }; + + let actual = get_node_url(&input, &Some(network)).unwrap(); + assert_eq!("https://beta-4.fuel.network", actual); +} + +#[test] +fn test_get_node_url_default() { + let input = NodeTarget { + target: None, + node_url: None, + testnet: false, + }; + + let actual = get_node_url(&input, &None).unwrap(); + assert_eq!("http://127.0.0.1:4000", actual); +} + +#[test] +fn test_get_node_url_beta3() { + let input = NodeTarget { + target: Some(Target::Beta3), + node_url: None, + testnet: false, + }; + let actual = get_node_url(&input, &None).unwrap(); + assert_eq!("https://beta-3.fuel.network", actual); +} + +#[test] +fn test_get_node_url_local() { + let input = NodeTarget { + target: Some(Target::Local), + node_url: None, + testnet: false, + }; + let actual = get_node_url(&input, &None).unwrap(); + assert_eq!("http://127.0.0.1:4000", actual); +} + +#[test] +#[should_panic( + expected = "Only one of `--testnet`, `--target`, or `--node-url` should be specified" +)] +fn test_get_node_url_local_testnet() { + let input = NodeTarget { + target: Some(Target::Local), + node_url: None, + testnet: true, + }; + get_node_url(&input, &None).unwrap(); +} + +#[test] +#[should_panic( + expected = "Only one of `--testnet`, `--target`, or `--node-url` should be specified" +)] +fn test_get_node_url_same_url() { + let input = NodeTarget { + target: Some(Target::Beta3), + node_url: Some("beta-3.fuel.network".to_string()), + testnet: false, + }; + get_node_url(&input, &None).unwrap(); +} diff --git a/forc-plugins/forc-client/src/util/target.rs b/forc-plugins/forc-client/src/util/target.rs new file mode 100644 index 00000000000..bda5518a802 --- /dev/null +++ b/forc-plugins/forc-client/src/util/target.rs @@ -0,0 +1,80 @@ +use crate::constants::{BETA_2_ENDPOINT_URL, BETA_3_ENDPOINT_URL, BETA_4_ENDPOINT_URL, NODE_URL}; +use anyhow::{bail, Result}; +use serde::{Deserialize, Serialize}; +use std::str::FromStr; + +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] +/// Possible target values that forc-client can interact with. +pub enum Target { + Beta2, + Beta3, + Beta4, + Local, +} + +impl Default for Target { + fn default() -> Self { + Self::Local + } +} + +impl Target { + pub fn target_url(&self) -> String { + let url = match self { + Target::Beta2 => BETA_2_ENDPOINT_URL, + Target::Beta3 => BETA_3_ENDPOINT_URL, + Target::Beta4 => BETA_4_ENDPOINT_URL, + Target::Local => NODE_URL, + }; + url.to_string() + } + + pub fn from_target_url(target_url: &str) -> Option { + match target_url { + BETA_2_ENDPOINT_URL => Some(Target::Beta2), + BETA_3_ENDPOINT_URL => Some(Target::Beta3), + BETA_4_ENDPOINT_URL => Some(Target::Beta4), + NODE_URL => Some(Target::Local), + _ => None, + } + } + + pub fn is_testnet(&self) -> bool { + match self { + Target::Beta2 | Target::Beta3 | Target::Beta4 => true, + Target::Local => false, + } + } +} + +impl FromStr for Target { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + match s { + "beta-2" => Ok(Target::Beta2), + "beta-3" => Ok(Target::Beta3), + "beta-4" => Ok(Target::Beta4), + "local" => Ok(Target::Local), + _ => bail!( + "'{s}' is not a valid target name. Possible values: '{}', '{}', '{}', '{}'", + Target::Beta2, + Target::Beta3, + Target::Beta4, + Target::Local + ), + } + } +} + +impl std::fmt::Display for Target { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = match self { + Target::Beta2 => "beta-2", + Target::Beta3 => "beta-3", + Target::Beta4 => "beta-4", + Target::Local => "local", + }; + write!(f, "{}", s) + } +} diff --git a/forc-plugins/forc-client/src/util/tx.rs b/forc-plugins/forc-client/src/util/tx.rs index 17a9f619f12..957c293b99a 100644 --- a/forc-plugins/forc-client/src/util/tx.rs +++ b/forc-plugins/forc-client/src/util/tx.rs @@ -2,6 +2,7 @@ use std::{io::Write, str::FromStr}; use anyhow::{Error, Result}; use async_trait::async_trait; +use forc_tracing::println_warning; use fuel_core_client::client::FuelClient; use fuel_crypto::{Message, PublicKey, SecretKey, Signature}; use fuel_tx::{ @@ -25,7 +26,7 @@ use forc_wallet::{ utils::default_wallet_path, }; -use crate::default::BETA_4_FAUCET_URL; +use crate::constants::BETA_4_FAUCET_URL; /// The maximum time to wait for a transaction to be included in a block by the node pub const TX_SUBMIT_TIMEOUT_MS: u64 = 30_000u64; @@ -249,9 +250,7 @@ impl TransactionBuild Some(secret_key) } (WalletSelectionMode::ForcWallet, Some(key), _) => { - tracing::warn!( - "Signing key is provided while requesting to sign with forc-wallet or with default signer. Using signing key" - ); + println_warning("Signing key is provided while requesting to sign with forc-wallet or with default signer. Using signing key"); Some(key) } (WalletSelectionMode::Manual, None, false) => None, @@ -263,9 +262,7 @@ impl TransactionBuild Some(secret_key) } (WalletSelectionMode::Manual, Some(key), true) => { - tracing::warn!( - "Signing key is provided while requesting to sign with a default signer. Using signing key" - ); + println_warning("Signing key is provided while requesting to sign with a default signer. Using signing key"); Some(key) } }; diff --git a/forc-plugins/forc-tx/src/lib.rs b/forc-plugins/forc-tx/src/lib.rs index dded3cd78cf..dc357028627 100644 --- a/forc-plugins/forc-tx/src/lib.rs +++ b/forc-plugins/forc-tx/src/lib.rs @@ -101,13 +101,11 @@ pub struct Script { #[derive(Debug, Devault, Parser, Deserialize, Serialize)] pub struct Gas { /// Gas price for the transaction. - #[clap(long = "gas-price", default_value_t = 0)] - #[devault("0")] - pub price: u64, + #[clap(long = "gas-price")] + pub price: Option, /// Gas limit for the transaction. - #[clap(long = "gas-limit", default_value_t = fuel_tx::ConsensusParameters::DEFAULT.max_gas_per_tx)] - #[devault("fuel_tx::ConsensusParameters::DEFAULT.max_gas_per_tx")] - pub limit: u64, + #[clap(long = "gas-limit")] + pub limit: Option, } /// Block until which tx cannot be included. @@ -624,8 +622,11 @@ impl TryFrom for fuel_tx::Create { .map(|s| fuel_tx::Witness::from(s.as_bytes())) .collect(); let create = fuel_tx::Transaction::create( - create.gas.price, - create.gas.limit, + create.gas.price.unwrap_or_default(), + create + .gas + .limit + .unwrap_or(fuel_tx::ConsensusParameters::DEFAULT.max_gas_per_tx), create.maturity.maturity.into(), create.bytecode_witness_index, create.salt.salt.unwrap_or_default(), @@ -668,8 +669,11 @@ impl TryFrom