Skip to content

Commit

Permalink
Add beta-4 target to forc, refactor with tests, and UX improvements (#…
Browse files Browse the repository at this point in the history
…4991)

## Description

Closes #4974

Added the beta-4 target and updated documentation (from
#4974)

Refactored part of forc-deploy and added unit tests.
- No longer mutating the Command, so the Command is always whatever the
user gave us
- Using the default values from the node itself via API call, rather
than hardcoded constants
- The values of `Gas` are now optional, so we only override them when
the user hasn't specified anything

Other changes:
- deployment works using the urls without `/graphql` prefixes, so I
updated the constants to remove the prefix. This is shown to the user
and it looks cleaner without it.
- Added coloring and consistent styling for errors and warnings to
printed to the console. It looks like this now (previously no color)
- `deploy`, `run`, and `submit` all now have the same options for
specifying the node (node_url, testnet, and target), capture in the
`TargetNode` struct. They use the same helper functions to extract the
node url and determine gas limit & price.


![image](https://github.com/FuelLabs/sway/assets/47993817/711382e4-c79e-49f0-85a3-b75704f7af9d)

## Checklist

- [ ] I have linked to any relevant issues.
- [ ] I have commented my code, particularly in hard-to-understand
areas.
- [ ] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [ ] I have added tests that prove my fix is effective or that my
feature works.
- [ ] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [ ] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [ ] I have requested a review from the relevant team or maintainers.

---------

Co-authored-by: kayagokalp <kaya.gokalp@fuel.sh>
Co-authored-by: Joshua Batty <joshpbatty@gmail.com>
  • Loading branch information
3 people authored Aug 23, 2023
1 parent 122fb0b commit ca4ed7c
Show file tree
Hide file tree
Showing 21 changed files with 377 additions and 178 deletions.
16 changes: 11 additions & 5 deletions docs/book/src/forc/plugins/forc_client/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
6 changes: 3 additions & 3 deletions forc-pkg/src/manifest.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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)
}
Expand Down
4 changes: 2 additions & 2 deletions forc-plugins/forc-client/src/bin/deploy.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
4 changes: 2 additions & 2 deletions forc-plugins/forc-client/src/bin/run.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
4 changes: 2 additions & 2 deletions forc-plugins/forc-client/src/bin/submit.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
18 changes: 4 additions & 14 deletions forc-plugins/forc-client/src/cmd/deploy.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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 <SALT>`, eg.: forc deploy --salt 0x0000000000000000000000000000000000000000000000000000000000000001
Expand All @@ -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<String>,
/// 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,
Expand All @@ -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<Target>,
/// Use preset configuration for the latest testnet.
#[clap(long)]
pub testnet: bool,
}
8 changes: 3 additions & 5 deletions forc-plugins/forc-client/src/cmd/run.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::NodeTarget;
use clap::Parser;
use fuel_crypto::SecretKey;

Expand All @@ -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<String>,
#[clap(flatten)]
pub node: NodeTarget,
/// Hex string of data to input to script.
#[clap(short, long)]
pub data: Option<String>,
Expand Down
7 changes: 3 additions & 4 deletions forc-plugins/forc-client/src/cmd/submit.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::NodeTarget;
use devault::Devault;
use std::path::PathBuf;

Expand All @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions forc-plugins/forc-client/src/constants.rs
Original file line number Diff line number Diff line change
@@ -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";
33 changes: 27 additions & 6 deletions forc-plugins/forc-client/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<String>,
/// 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<Target>,
/// 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,
}
74 changes: 17 additions & 57 deletions forc-plugins/forc-client/src/op/deploy.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 {
Expand Down Expand Up @@ -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<Vec<DeployedContract>> {
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)
Expand All @@ -131,6 +132,11 @@ pub async fn deploy(command: cmd::Deploy) -> Result<Vec<DeployedContract>> {
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...
Expand Down Expand Up @@ -197,61 +203,15 @@ pub async fn deploy(command: cmd::Deploy) -> Result<Vec<DeployedContract>> {
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<cmd::Deploy> {
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,
manifest: &PackageManifestFile,
compiled: &BuiltPackage,
salt: Salt,
) -> Result<DeployedContract> {
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;

Expand All @@ -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,
)
Expand Down
Loading

0 comments on commit ca4ed7c

Please sign in to comment.