Skip to content

Commit

Permalink
Merge branch 'master' into abi-supertraits-external-methods
Browse files Browse the repository at this point in the history
  • Loading branch information
IGI-111 authored Aug 14, 2024
2 parents 412665f + 9b87126 commit d9fedaf
Show file tree
Hide file tree
Showing 8 changed files with 504 additions and 293 deletions.
3 changes: 2 additions & 1 deletion docs/book/spell-check-custom-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -215,4 +215,5 @@ booleans
underflows
Codec
bool
str
str
multisig
10 changes: 10 additions & 0 deletions docs/book/src/forc/plugins/forc_client/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,16 @@ forc-deploy --target beta-5

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-testnet.fuel.network/).

## Delayed transactions

For delayed transactions, you can use the `--submit-only` flag. This flag allows you to submit the transaction without waiting for its finalization.

One use case for this is multisig transactions, where a deployment transaction may stay in a pending state while waiting for all signatures.

```sh
forc-deploy --submit-only
```

## 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
3 changes: 3 additions & 0 deletions forc-plugins/forc-client/src/cmd/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ pub struct Command {
/// Deprecated in favor of `--default-signer`.
#[clap(long)]
pub unsigned: bool,
/// Submit the deployment transaction(s) without waiting for execution to complete.
#[clap(long)]
pub submit_only: bool,
/// Set the key to be used for signing.
pub signing_key: Option<SecretKey>,
/// Sign the deployment transaction manually.
Expand Down
186 changes: 120 additions & 66 deletions forc-plugins/forc-client/src/op/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use forc_pkg::{self as pkg, PackageManifestFile};
use forc_tracing::{println_action_green, println_warning};
use forc_util::default_output_directory;
use forc_wallet::utils::default_wallet_path;
use fuel_core_client::client::types::TransactionStatus;
use fuel_core_client::client::types::{ChainInfo, TransactionStatus};
use fuel_core_client::client::FuelClient;
use fuel_crypto::fuel_types::ChainId;
use fuel_tx::{Salt, Transaction};
Expand Down Expand Up @@ -52,7 +52,7 @@ pub struct DeploymentArtifact {
chain_id: ChainId,
contract_id: String,
deployment_size: usize,
deployed_block_height: u32,
deployed_block_height: Option<u32>,
}

impl DeploymentArtifact {
Expand Down Expand Up @@ -442,79 +442,84 @@ pub async fn deploy_pkg(
let chain_info = client.chain_info().await?;
let chain_id = chain_info.consensus_parameters.chain_id();

let deployment_request = client.submit_and_await_commit(&tx).map(|res| match res {
Ok(logs) => match logs {
TransactionStatus::Submitted { .. } => {
bail!("contract {} deployment timed out", &contract_id);
}
TransactionStatus::Success { block_height, .. } => {
let pkg_name = manifest.project_name();
let target = Target::from_str(&chain_info.name).unwrap_or(Target::testnet());
let (contract_url, block_url) = match target.explorer_url() {
Some(explorer_url) => (
format!("{explorer_url}/contract/0x"),
format!("{explorer_url}/block/"),
),
None => ("".to_string(), "".to_string()),
};
println_action_green(
"Finished",
&format!("deploying {pkg_name} {contract_url}{contract_id}"),
);
let block_height_formatted =
match u32::from_str_radix(&block_height.to_string(), 16) {
Ok(decimal) => format!("{block_url}{decimal}"),
Err(_) => block_height.to_string(),
};

println_action_green("Deployed", &format!("in block {block_height_formatted}"));

// If only submitting the transaction, don't wait for the deployment to complete
let contract_id: ContractId = if command.submit_only {
match client.submit(&tx).await {
Ok(transaction_id) => {
// Create a deployment artifact.
let deployment_size = bytecode.len();
let deployment_artifact = DeploymentArtifact {
transaction_id: format!("0x{}", tx.id(&chain_id)),
salt: format!("0x{}", salt),
network_endpoint: node_url.to_string(),
chain_id,
contract_id: format!("0x{}", contract_id),
deployment_size,
deployed_block_height: *block_height,
};

let output_dir = command
.pkg
.output_directory
.as_ref()
.map(PathBuf::from)
.unwrap_or_else(|| default_output_directory(manifest.dir()))
.join("deployments");
deployment_artifact.to_file(&output_dir, pkg_name, contract_id)?;

Ok(contract_id)
create_deployment_artifact(
DeploymentArtifact {
transaction_id: format!("0x{}", transaction_id),
salt: format!("0x{}", salt),
network_endpoint: node_url.to_string(),
chain_id,
contract_id: format!("0x{}", contract_id),
deployment_size: bytecode.len(),
deployed_block_height: None,
},
command,
manifest,
chain_info,
)?;

contract_id
}
e => {
Err(e) => {
bail!(
"contract {} failed to deploy due to an error: {:?}",
&contract_id,
e
)
}
},
Err(e) => bail!("{e}"),
});

// submit contract deployment with a timeout
let contract_id = tokio::time::timeout(
Duration::from_millis(TX_SUBMIT_TIMEOUT_MS),
deployment_request,
)
.await
.with_context(|| {
format!(
"Timed out waiting for contract {} to deploy. The transaction may have been dropped.",
&contract_id
}
} else {
let deployment_request = client.submit_and_await_commit(&tx).map(|res| match res {
Ok(logs) => match logs {
TransactionStatus::Submitted { .. } => {
bail!("contract {} deployment timed out", &contract_id);
}
TransactionStatus::Success { block_height, .. } => {
// Create a deployment artifact.
create_deployment_artifact(
DeploymentArtifact {
transaction_id: format!("0x{}", tx.id(&chain_id)),
salt: format!("0x{}", salt),
network_endpoint: node_url.to_string(),
chain_id,
contract_id: format!("0x{}", contract_id),
deployment_size: bytecode.len(),
deployed_block_height: Some(*block_height),
},
command,
manifest,
chain_info,
)?;

Ok(contract_id)
}
e => {
bail!(
"contract {} failed to deploy due to an error: {:?}",
&contract_id,
e
)
}
},
Err(e) => bail!("{e}"),
});
tokio::time::timeout(
Duration::from_millis(TX_SUBMIT_TIMEOUT_MS),
deployment_request,
)
})??;
.await
.with_context(|| {
format!(
"Timed out waiting for contract {} to deploy. The transaction may have been dropped.",
&contract_id
)
})??
};

Ok(contract_id)
}

Expand Down Expand Up @@ -559,6 +564,55 @@ fn build_opts_from_cmd(cmd: &cmd::Deploy) -> pkg::BuildOpts {
}
}

/// Creates a deployment artifact and writes it to a file.
///
/// This function is used to generate a deployment artifact containing details
/// about the deployment, such as the transaction ID, salt, network endpoint,
/// chain ID, contract ID, deployment size, and deployed block height. It then
/// writes this artifact to a specified output directory.
fn create_deployment_artifact(
deployment_artifact: DeploymentArtifact,
cmd: &cmd::Deploy,
manifest: &PackageManifestFile,
chain_info: ChainInfo,
) -> Result<()> {
let contract_id = ContractId::from_str(&deployment_artifact.contract_id).unwrap();
let pkg_name = manifest.project_name();

let target = Target::from_str(&chain_info.name).unwrap_or(Target::testnet());
let (contract_url, block_url) = match target.explorer_url() {
Some(explorer_url) => (
format!("{explorer_url}/contract/0x"),
format!("{explorer_url}/block/"),
),
None => ("".to_string(), "".to_string()),
};
println_action_green(
"Finished",
&format!("deploying {pkg_name} {contract_url}{contract_id}"),
);

let block_height = deployment_artifact.deployed_block_height;
if block_height.is_some() {
let block_height_formatted =
match u32::from_str_radix(&block_height.unwrap().to_string(), 16) {
Ok(decimal) => format!("{block_url}{decimal}"),
Err(_) => block_height.unwrap().to_string(),
};

println_action_green("Deployed", &format!("in block {block_height_formatted}"));
}

let output_dir = cmd
.pkg
.output_directory
.as_ref()
.map(PathBuf::from)
.unwrap_or_else(|| default_output_directory(manifest.dir()))
.join("deployments");
deployment_artifact.to_file(&output_dir, pkg_name, contract_id)
}

#[cfg(test)]
mod test {
use super::*;
Expand Down
41 changes: 41 additions & 0 deletions forc-plugins/forc-client/tests/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,47 @@ async fn test_simple_deploy() {
assert_eq!(contract_ids, expected)
}

#[tokio::test]
async fn test_deploy_submit_only() {
let (mut node, port) = run_node();
let tmp_dir = tempdir().unwrap();
let project_dir = test_data_path().join("standalone_contract");
copy_dir(&project_dir, tmp_dir.path()).unwrap();
patch_manifest_file_with_path_std(tmp_dir.path()).unwrap();

let pkg = Pkg {
path: Some(tmp_dir.path().display().to_string()),
..Default::default()
};

let node_url = format!("http://127.0.0.1:{}/v1/graphql", port);

let target = NodeTarget {
node_url: Some(node_url),
target: None,
testnet: false,
};
let cmd = cmd::Deploy {
pkg,
salt: Some(vec![format!("{}", Salt::default())]),
node: target,
default_signer: true,
submit_only: true,
..Default::default()
};
let contract_ids = deploy(cmd).await.unwrap();
node.kill().unwrap();
let expected = vec![DeployedContract {
id: ContractId::from_str(
"ad0bba17e0838ef859abe2693d8a5e3bc4e7cfb901601e30f4dc34999fda6335",
)
.unwrap(),
proxy: None,
}];

assert_eq!(contract_ids, expected)
}

#[tokio::test]
async fn test_deploy_fresh_proxy() {
let (mut node, port) = run_node();
Expand Down
40 changes: 33 additions & 7 deletions sway-lib-std/src/math.sw
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
library;

use ::assert::*;
use ::flags::{disable_panic_on_overflow, F_UNSAFEMATH_DISABLE_MASK, set_flags};
use ::flags::{
disable_panic_on_overflow,
F_UNSAFEMATH_DISABLE_MASK,
F_WRAPPING_DISABLE_MASK,
set_flags,
};
use ::registers::{flags, overflow};

/// Calculates the square root.
Expand Down Expand Up @@ -113,27 +118,48 @@ impl Power for u64 {

impl Power for u32 {
fn pow(self, exponent: u32) -> Self {
asm(r1: self, r2: exponent, r3) {
let res = asm(r1: self, r2: exponent, r3) {
exp r3 r1 r2;
r3: Self
r3: u64
};
// If panic on wrapping math is enabled, only then revert
if flags() & F_WRAPPING_DISABLE_MASK == 0 {
assert(res <= Self::max().as_u64());
}
asm(r1: res) {
r1: Self
}
}
}

impl Power for u16 {
fn pow(self, exponent: u32) -> Self {
asm(r1: self, r2: exponent, r3) {
let res = asm(r1: self, r2: exponent, r3) {
exp r3 r1 r2;
r3: Self
r3: u64
};
// If panic on wrapping math is enabled, only then revert
if flags() & F_WRAPPING_DISABLE_MASK == 0 {
assert(res <= Self::max().as_u64());
}
asm(r1: res) {
r1: Self
}
}
}

impl Power for u8 {
fn pow(self, exponent: u32) -> Self {
asm(r1: self, r2: exponent, r3) {
let res = asm(r1: self, r2: exponent, r3) {
exp r3 r1 r2;
r3: Self
r3: u64
};
// If panic on wrapping math is enabled, only then revert
if flags() & F_WRAPPING_DISABLE_MASK == 0 {
assert(res <= Self::max().as_u64());
}
asm(r1: res) {
r1: Self
}
}
}
Expand Down
Loading

0 comments on commit d9fedaf

Please sign in to comment.