From 96a61403c7c936425ae041412c859dec2cc34d9f Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Fri, 3 Oct 2025 12:49:59 +0200 Subject: [PATCH] Allow declaring with deployment commit-id:82e786a7 # Conflicts: # crates/sncast/src/main.rs --- crates/sncast/src/helpers/block_explorer.rs | 11 +- crates/sncast/src/main.rs | 85 +++++- crates/sncast/src/response/deploy.rs | 135 +++++++-- crates/sncast/src/starknet_commands/deploy.rs | 23 +- crates/sncast/src/state/state_file.rs | 10 +- crates/sncast/tests/e2e/deploy.rs | 263 +++++++++++++++++- docs/src/starknet/deploy.md | 47 +++- 7 files changed, 520 insertions(+), 54 deletions(-) diff --git a/crates/sncast/src/helpers/block_explorer.rs b/crates/sncast/src/helpers/block_explorer.rs index 566fb1bf99..ca1243498f 100644 --- a/crates/sncast/src/helpers/block_explorer.rs +++ b/crates/sncast/src/helpers/block_explorer.rs @@ -129,33 +129,34 @@ impl LinkProvider for OkLink { #[cfg(test)] mod tests { + use crate::response::deploy::DeployResponse; use crate::{ Network, helpers::block_explorer::Service, - response::{deploy::DeployResponse, explorer_link::OutputLink}, + response::{deploy::StandardDeployResponse, explorer_link::OutputLink}, }; use conversions::padded_felt::PaddedFelt; use regex::Regex; use starknet::macros::felt; use test_case::test_case; - const MAINNET_RESPONSE: DeployResponse = DeployResponse { + const MAINNET_RESPONSE: DeployResponse = DeployResponse::Deploy(StandardDeployResponse { contract_address: PaddedFelt(felt!( "0x03241d40a2af53a34274dd411e090ccac1ea80e0380a0303fe76d71b046cfecb" )), transaction_hash: PaddedFelt(felt!( "0x7605291e593e0c6ad85681d09e27a601befb85033bdf1805aabf5d84617cf68" )), - }; + }); - const SEPOLIA_RESPONSE: DeployResponse = DeployResponse { + const SEPOLIA_RESPONSE: DeployResponse = DeployResponse::Deploy(StandardDeployResponse { contract_address: PaddedFelt(felt!( "0x0716b5f1e3bd760c489272fd6530462a09578109049e26e3f4c70492676eae17" )), transaction_hash: PaddedFelt(felt!( "0x1cde70aae10f79d2d1289c923a1eeca7b81a2a6691c32551ec540fa2cb29c33" )), - }; + }); async fn assert_valid_links(input: &str) { let pattern = Regex::new(r"transaction: |contract: |class: ").unwrap(); diff --git a/crates/sncast/src/main.rs b/crates/sncast/src/main.rs index 19db9d3254..427959d14e 100644 --- a/crates/sncast/src/main.rs +++ b/crates/sncast/src/main.rs @@ -1,3 +1,4 @@ +use crate::starknet_commands::declare::declare; use crate::starknet_commands::declare_from::DeclareFrom; use crate::starknet_commands::deploy::DeployArguments; use crate::starknet_commands::multicall; @@ -11,6 +12,7 @@ use anyhow::{Context, Result, bail}; use camino::Utf8PathBuf; use clap::{CommandFactory, Parser, Subcommand}; use configuration::load_config; +use conversions::IntoConv; use data_transformer::transform; use foundry_ui::components::warning::WarningMessage; use foundry_ui::{Message, UI}; @@ -24,7 +26,10 @@ use sncast::helpers::rpc::generate_network_flag; use sncast::helpers::scarb_utils::{ BuildConfig, assert_manifest_path_exists, build_and_load_artifacts, get_package_metadata, }; -use sncast::response::declare::{DeclareResponse, DeployCommandMessage}; +use sncast::response::declare::{ + AlreadyDeclaredResponse, DeclareResponse, DeclareTransactionResponse, DeployCommandMessage, +}; +use sncast::response::deploy::{DeployResponse, DeployResponseWithDeclare}; use sncast::response::errors::handle_starknet_command_error; use sncast::response::explorer_link::block_explorer_link_if_allowed; use sncast::response::transformed_call::transform_response; @@ -390,9 +395,11 @@ async fn run_async_command(cli: Cli, config: CastConfig, ui: &UI) -> Result<()> Commands::Deploy(deploy) => { let Deploy { + contract_identifier: identifier, arguments, fee_args, rpc, + mut nonce, .. } = deploy; @@ -406,21 +413,80 @@ async fn run_async_command(cli: Cli, config: CastConfig, ui: &UI) -> Result<()> ) .await?; + let (class_hash, declare_response) = if let Some(class_hash) = identifier.class_hash { + (class_hash, None) + } else if let Some(contract_name) = identifier.contract_name { + let manifest_path = assert_manifest_path_exists()?; + let package_metadata = get_package_metadata(&manifest_path, &None)?; + let artifacts = build_and_load_artifacts( + &package_metadata, + &BuildConfig { + scarb_toml_path: manifest_path, + json: cli.json, + profile: cli.profile.unwrap_or("release".to_string()), + }, + false, + ui, + ) + .expect("Failed to build contract"); + + let declare_result = declare( + contract_name, + fee_args.clone(), + nonce, + &account, + &artifacts, + WaitForTx { + wait: true, + wait_params: wait_config.wait_params, + show_ui_outputs: true, + }, + true, + ui, + ) + .await + .map_err(handle_starknet_command_error); + + // Increment nonce after successful declare if it was explicitly provided + nonce = nonce.map(|n| n + Felt::ONE); + + match declare_result { + Ok(DeclareResponse::AlreadyDeclared(AlreadyDeclaredResponse { + class_hash, + })) => (class_hash.into_(), None), + Ok(DeclareResponse::Success(declare_transaction_response)) => ( + declare_transaction_response.class_hash.into_(), + Some(declare_transaction_response), + ), + Err(err) => { + process_command_result::( + "deploy", + Err(err), + ui, + None, + ); + return Ok(()); + } + } + } else { + unreachable!("One of class_hash or contract_name must be provided"); + }; + // safe to unwrap because "constructor" is a standardized name let selector = get_selector_from_name("constructor").unwrap(); - let contract_class = get_contract_class(deploy.class_hash, &provider).await?; + let contract_class = get_contract_class(class_hash, &provider).await?; let arguments: Arguments = arguments.into(); let calldata = arguments.try_into_calldata(contract_class, &selector)?; let result = starknet_commands::deploy::deploy( - deploy.class_hash, + class_hash, &calldata, deploy.salt, deploy.unique, fee_args, - deploy.nonce, + nonce, &account, wait_config, ui, @@ -428,6 +494,17 @@ async fn run_async_command(cli: Cli, config: CastConfig, ui: &UI) -> Result<()> .await .map_err(handle_starknet_command_error); + let result = if let Some(declare_response) = declare_response { + result.map(|r| { + DeployResponse::DeployWithDeclare(DeployResponseWithDeclare::from_responses( + &r, + &declare_response, + )) + }) + } else { + result.map(DeployResponse::Deploy) + }; + let block_explorer_link = block_explorer_link_if_allowed(&result, provider.chain_id().await?, &rpc, &config); process_command_result("deploy", result, ui, block_explorer_link); diff --git a/crates/sncast/src/response/deploy.rs b/crates/sncast/src/response/deploy.rs index eebddedbe5..c2d01d0b8e 100644 --- a/crates/sncast/src/response/deploy.rs +++ b/crates/sncast/src/response/deploy.rs @@ -1,38 +1,31 @@ +use super::command::CommandResponse; use crate::helpers::block_explorer::LinkProvider; +use crate::response::cast_message::SncastMessage; +use crate::response::declare::DeclareTransactionResponse; +use crate::response::explorer_link::OutputLink; use conversions::string::IntoPaddedHexStr; use conversions::{padded_felt::PaddedFelt, serde::serialize::CairoSerialize}; use foundry_ui::Message; use foundry_ui::styling; use indoc::formatdoc; use serde::{Deserialize, Serialize}; -use serde_json::Value; -use serde_json::json; - -use super::{command::CommandResponse, explorer_link::OutputLink}; -use crate::response::cast_message::SncastMessage; +use serde_json::{Value, json}; #[derive(Clone, Serialize, Deserialize, CairoSerialize, Debug, PartialEq)] -pub struct DeployResponse { - pub contract_address: PaddedFelt, - pub transaction_hash: PaddedFelt, +#[serde(untagged)] +pub enum DeployResponse { + Deploy(StandardDeployResponse), + DeployWithDeclare(DeployResponseWithDeclare), } impl CommandResponse for DeployResponse {} impl Message for SncastMessage { fn text(&self) -> String { - styling::OutputBuilder::new() - .success_message("Deployment completed") - .blank_line() - .field( - "Contract Address", - &self.command_response.contract_address.into_padded_hex_str(), - ) - .field( - "Transaction Hash", - &self.command_response.transaction_hash.into_padded_hex_str(), - ) - .build() + match &self.command_response { + DeployResponse::Deploy(response) => response.text(), + DeployResponse::DeployWithDeclare(response) => response.text(), + } } fn json(&self) -> Value { @@ -50,13 +43,99 @@ impl OutputLink for DeployResponse { const TITLE: &'static str = "deployment"; fn format_links(&self, provider: Box) -> String { - formatdoc!( - " - contract: {} - transaction: {} - ", - provider.contract(self.contract_address), - provider.transaction(self.transaction_hash) - ) + match self { + DeployResponse::Deploy(deploy) => { + formatdoc!( + " + contract: {} + transaction: {} + ", + provider.contract(deploy.contract_address), + provider.transaction(deploy.transaction_hash) + ) + } + DeployResponse::DeployWithDeclare(deploy_with_declare) => { + formatdoc!( + " + contract: {} + class: {} + declare transaction: {} + deploy transaction: {} + ", + provider.contract(deploy_with_declare.contract_address), + provider.class(deploy_with_declare.class_hash), + provider.transaction(deploy_with_declare.declare_transaction_hash), + provider.transaction(deploy_with_declare.deploy_transaction_hash) + ) + } + } + } +} + +#[derive(Clone, Serialize, Deserialize, CairoSerialize, Debug, PartialEq)] +pub struct StandardDeployResponse { + pub contract_address: PaddedFelt, + pub transaction_hash: PaddedFelt, +} + +impl StandardDeployResponse { + fn text(&self) -> String { + styling::OutputBuilder::new() + .success_message("Deployment completed") + .blank_line() + .field( + "Contract Address", + &self.contract_address.into_padded_hex_str(), + ) + .field( + "Transaction Hash", + &self.transaction_hash.into_padded_hex_str(), + ) + .build() + } +} + +#[derive(Clone, Serialize, Deserialize, CairoSerialize, Debug, PartialEq)] +pub struct DeployResponseWithDeclare { + contract_address: PaddedFelt, + class_hash: PaddedFelt, + deploy_transaction_hash: PaddedFelt, + declare_transaction_hash: PaddedFelt, +} + +impl DeployResponseWithDeclare { + #[must_use] + pub fn from_responses( + deploy: &StandardDeployResponse, + declare: &DeclareTransactionResponse, + ) -> Self { + Self { + contract_address: deploy.contract_address, + class_hash: declare.class_hash, + deploy_transaction_hash: deploy.transaction_hash, + declare_transaction_hash: declare.transaction_hash, + } + } +} + +impl DeployResponseWithDeclare { + fn text(&self) -> String { + styling::OutputBuilder::new() + .success_message("Deployment completed") + .blank_line() + .field( + "Contract Address", + &self.contract_address.into_padded_hex_str(), + ) + .field("Class Hash", &self.class_hash.into_padded_hex_str()) + .field( + "Declare Transaction Hash", + &self.declare_transaction_hash.into_padded_hex_str(), + ) + .field( + "Deploy Transaction Hash", + &self.deploy_transaction_hash.into_padded_hex_str(), + ) + .build() } } diff --git a/crates/sncast/src/starknet_commands/deploy.rs b/crates/sncast/src/starknet_commands/deploy.rs index 203fa2ec13..9141855fa0 100644 --- a/crates/sncast/src/starknet_commands/deploy.rs +++ b/crates/sncast/src/starknet_commands/deploy.rs @@ -4,7 +4,7 @@ use conversions::IntoConv; use foundry_ui::UI; use sncast::helpers::fee::{FeeArgs, FeeSettings}; use sncast::helpers::rpc::RpcArgs; -use sncast::response::deploy::DeployResponse; +use sncast::response::deploy::StandardDeployResponse; use sncast::response::errors::StarknetCommandError; use sncast::{WaitForTx, apply_optional_fields, handle_wait_for_tx}; use sncast::{extract_or_generate_salt, udc_uniqueness}; @@ -17,12 +17,23 @@ use starknet::providers::jsonrpc::HttpTransport; use starknet::signers::LocalWallet; use starknet_types_core::felt::Felt; +#[derive(Args, Debug, Clone)] +#[group(required = true, multiple = false)] +pub struct ContractIdentifier { + /// Class hash of contract to deploy + #[arg(short = 'g', long)] + pub class_hash: Option, + + /// Contract name + #[arg(long, conflicts_with = "class_hash")] + pub contract_name: Option, +} + #[derive(Args)] #[command(about = "Deploy a contract on Starknet")] pub struct Deploy { - /// Class hash of contract to deploy - #[arg(short = 'g', long)] - pub class_hash: Felt, + #[command(flatten)] + pub contract_identifier: ContractIdentifier, #[command(flatten)] pub arguments: DeployArguments, @@ -69,7 +80,7 @@ pub async fn deploy( account: &SingleOwnerAccount<&JsonRpcClient, LocalWallet>, wait_config: WaitForTx, ui: &UI, -) -> Result { +) -> Result { let salt = extract_or_generate_salt(salt); // TODO(#3628): Use `ContractFactory::new` once new UDC address is the default one in starknet-rs @@ -114,7 +125,7 @@ pub async fn deploy( Ok(result) => handle_wait_for_tx( account.provider(), result.transaction_hash, - DeployResponse { + StandardDeployResponse { contract_address: get_udc_deployed_address( salt, class_hash, diff --git a/crates/sncast/src/state/state_file.rs b/crates/sncast/src/state/state_file.rs index ef783c683f..4333aa4034 100644 --- a/crates/sncast/src/state/state_file.rs +++ b/crates/sncast/src/state/state_file.rs @@ -1,7 +1,7 @@ use crate::WaitForTransactionError; use crate::helpers::constants::STATE_FILE_VERSION; use crate::response::declare::DeclareResponse; -use crate::response::deploy::DeployResponse; +use crate::response::deploy::StandardDeployResponse; use crate::response::errors::StarknetCommandError; use crate::response::invoke::InvokeResponse; use crate::state::hashing::generate_id; @@ -179,7 +179,7 @@ impl ScriptTransactionEntry { pub enum ScriptTransactionOutput { InvokeResponse(InvokeResponse), DeclareResponse(DeclareResponse), - DeployResponse(DeployResponse), + DeployResponse(StandardDeployResponse), ErrorResponse(ErrorResponse), } @@ -195,8 +195,8 @@ impl From for ScriptTransactionOutput { } } -impl From for ScriptTransactionOutput { - fn from(value: DeployResponse) -> Self { +impl From for ScriptTransactionOutput { + fn from(value: StandardDeployResponse) -> Self { Self::DeployResponse(value) } } @@ -608,7 +608,7 @@ mod tests { let new_transaction = ScriptTransactionEntry { name: "deploy".to_string(), - output: ScriptTransactionOutput::DeployResponse(DeployResponse { + output: ScriptTransactionOutput::DeployResponse(StandardDeployResponse { transaction_hash: Felt::try_from_hex_str("0x3").unwrap().into_(), contract_address: Felt::try_from_hex_str("0x333").unwrap().into_(), }), diff --git a/crates/sncast/tests/e2e/deploy.rs b/crates/sncast/tests/e2e/deploy.rs index 76cb451d15..d66a048322 100644 --- a/crates/sncast/tests/e2e/deploy.rs +++ b/crates/sncast/tests/e2e/deploy.rs @@ -1,25 +1,30 @@ use crate::helpers::constants::{ - ACCOUNT, ACCOUNT_FILE_PATH, CONSTRUCTOR_WITH_PARAMS_CONTRACT_CLASS_HASH_SEPOLIA, + ACCOUNT, ACCOUNT_FILE_PATH, CONSTRUCTOR_WITH_PARAMS_CONTRACT_CLASS_HASH_SEPOLIA, CONTRACTS_DIR, DEVNET_OZ_CLASS_HASH_CAIRO_0, MAP_CONTRACT_CLASS_HASH_SEPOLIA, URL, }; use crate::helpers::fee::apply_test_resource_bounds_flags; use crate::helpers::fixtures::{ - create_and_deploy_account, create_and_deploy_oz_account, get_transaction_by_hash, - get_transaction_hash, get_transaction_receipt, + create_and_deploy_account, create_and_deploy_oz_account, create_test_provider, + duplicate_contract_directory_with_salt, get_transaction_by_hash, get_transaction_hash, + get_transaction_receipt, join_tempdirs, }; use crate::helpers::runner::runner; use crate::helpers::shell::os_specific_shell; use camino::Utf8PathBuf; use indoc::indoc; -use shared::test_utils::output_assert::{assert_stderr_contains, assert_stdout_contains}; +use shared::test_utils::output_assert::{AsOutput, assert_stderr_contains, assert_stdout_contains}; use snapbox::cmd::cargo_bin; use sncast::AccountType; use sncast::helpers::constants::OZ_CLASS_HASH; use sncast::helpers::fee::FeeArgs; use starknet::core::types::TransactionReceipt::Invoke; -use starknet::core::types::{InvokeTransaction, Transaction, TransactionExecutionStatus}; +use starknet::core::types::{ + BlockId, BlockTag, InvokeTransaction, Transaction, TransactionExecutionStatus, +}; +use starknet::providers::Provider; use starknet_types_core::felt::{Felt, NonZeroFelt}; use test_case::test_case; +use toml::Value; #[tokio::test] async fn test_happy_case_human_readable() { @@ -405,3 +410,251 @@ async fn test_json_output_format() { "#}, ); } + +#[tokio::test] +async fn test_happy_case_with_declare() { + let contract_path = duplicate_contract_directory_with_salt( + CONTRACTS_DIR.to_string() + "/map", + "put", + "with_declare", + ); + let tempdir = create_and_deploy_oz_account().await; + join_tempdirs(&contract_path, &tempdir); + + let args = vec![ + "--accounts-file", + "accounts.json", + "--account", + "my_account", + "deploy", + "--url", + URL, + "--constructor-calldata", + "0x1", + "0x1", + "0x0", + "--contract-name", + "Map", + ]; + let args = apply_test_resource_bounds_flags(args); + + let snapbox = runner(&args) + .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") + .current_dir(tempdir.path()); + let output = snapbox.assert().success(); + + assert_stdout_contains( + output, + indoc! { + " + Success: Deployment completed + + Contract Address: 0x0[..] + Class Hash: 0x0[..] + Declare Transaction Hash: 0x0[..] + Deploy Transaction Hash: 0x0[..] + + To see deployment details, visit: + contract: [..] + class: [..] + declare transaction: [..] + deploy transaction: [..] + " + }, + ); +} + +#[tokio::test] +async fn test_happy_case_with_already_declared() { + let contract_path = duplicate_contract_directory_with_salt( + CONTRACTS_DIR.to_string() + "/map", + "put", + "with_redeclare", + ); + let tempdir = create_and_deploy_oz_account().await; + join_tempdirs(&contract_path, &tempdir); + + { + // Declare the contract first + let args = vec![ + "--accounts-file", + "accounts.json", + "--account", + "my_account", + "declare", + "--url", + URL, + "--contract-name", + "Map", + ]; + let args = apply_test_resource_bounds_flags(args); + + runner(&args).current_dir(tempdir.path()).assert().success(); + } + + // Deploy the contract with declaring + let args = vec![ + "--accounts-file", + "accounts.json", + "--account", + "my_account", + "deploy", + "--url", + URL, + "--constructor-calldata", + "0x1", + "0x1", + "0x0", + "--contract-name", + "Map", + ]; + let args = apply_test_resource_bounds_flags(args); + + let snapbox = runner(&args) + .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") + .current_dir(tempdir.path()); + let output = snapbox.assert().success(); + + assert_stdout_contains( + output, + indoc! { + " + Success: Deployment completed + + Contract Address: 0x0[..] + Transaction Hash: 0x0[..] + + To see deployment details, visit: + contract: [..] + transaction: [..] + " + }, + ); +} + +#[tokio::test] +async fn test_happy_case_with_declare_nonce() { + let contract_path = duplicate_contract_directory_with_salt( + CONTRACTS_DIR.to_string() + "/map", + "put", + "with_declare_nonce", + ); + let tempdir = create_and_deploy_oz_account().await; + join_tempdirs(&contract_path, &tempdir); + + let nonce = { + // Get nonce + let provider = create_test_provider(); + let args = vec![ + "--accounts-file", + "accounts.json", + "--json", + "account", + "list", + ]; + + let snapbox = runner(&args).current_dir(tempdir.path()); + let output = snapbox.assert().success(); + + let value: Value = serde_json::from_str(output.as_stdout()).unwrap(); + let account_address = value["my_account"]["address"].as_str().unwrap(); + + provider + .get_nonce( + BlockId::Tag(BlockTag::Latest), + Felt::from_hex(account_address).unwrap(), + ) + .await + .unwrap() + .to_string() + }; + + let args = vec![ + "--accounts-file", + "accounts.json", + "--account", + "my_account", + "deploy", + "--url", + URL, + "--constructor-calldata", + "0x1", + "0x1", + "0x0", + "--contract-name", + "Map", + "--nonce", + nonce.as_str(), + ]; + let args = apply_test_resource_bounds_flags(args); + + let snapbox = runner(&args) + .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") + .current_dir(tempdir.path()); + let output = snapbox.assert().success(); + + assert_stdout_contains( + output, + indoc! { + " + Success: Deployment completed + + Contract Address: 0x0[..] + Class Hash: 0x0[..] + Declare Transaction Hash: 0x0[..] + Deploy Transaction Hash: 0x0[..] + + To see deployment details, visit: + contract: [..] + class: [..] + declare transaction: [..] + deploy transaction: [..] + " + }, + ); +} + +#[tokio::test] +async fn test_deploy_with_declare_nonexistent_contract() { + let contract_path = duplicate_contract_directory_with_salt( + CONTRACTS_DIR.to_string() + "/map", + "put", + "with_redeclare", + ); + let tempdir = create_and_deploy_oz_account().await; + join_tempdirs(&contract_path, &tempdir); + + let args = vec![ + "--accounts-file", + "accounts.json", + "--account", + "my_account", + "deploy", + "--url", + URL, + "--constructor-calldata", + "0x1", + "0x1", + "0x0", + "--contract-name", + "Map", + "--nonce", + "0x123456", + ]; + let args = apply_test_resource_bounds_flags(args); + + let snapbox = runner(&args) + .env("SNCAST_FORCE_SHOW_EXPLORER_LINKS", "1") + .current_dir(tempdir.path()); + let output = snapbox.assert().success(); + + assert_stderr_contains( + output, + indoc! { + " + Command: deploy + Error: Invalid transaction nonce + " + }, + ); +} diff --git a/docs/src/starknet/deploy.md b/docs/src/starknet/deploy.md index e03701c035..fb001f3d02 100644 --- a/docs/src/starknet/deploy.md +++ b/docs/src/starknet/deploy.md @@ -4,7 +4,12 @@ Starknet Foundry `sncast` supports deploying smart contracts to a given network with the `sncast deploy` command. -It works by invoking a [Universal Deployer Contract](https://docs.openzeppelin.com/contracts-cairo/0.19.0/udc), which deploys the contract with the given class hash and constructor arguments. +It works by invoking a [Universal Deployer Contract](https://docs.openzeppelin.com/contracts-cairo/0.19.0/udc), which +deploys the contract with the given class hash and constructor arguments. + +For contract to be deployed on starknet, it must be declared first. +It can be done with the [declare command](./declare.md) or by using the [`--contract-name`](#deploying-by-contract-name) +flag in the `deploy` command. For detailed CLI description, see [deploy command reference](../appendix/sncast/deploy.md). @@ -82,6 +87,46 @@ transaction: https://sepolia.starkscan.co/tx/[..] > It is important to know how types are serialized because all values passed as constructor calldata are > interpreted as a field elements (felt252). +### Deploying by Contract Name + +Instead of providing the `--class-hash` of an already declared contract, you can pass the name of the +contract from a Scarb project by providing the `--contract-name` flag. +Under the hood, if the passed contract was never declared to starknet, it will run the [declare](../starknet/declare.md) +command first and then execute the contract deployment. + +> 📝 **Note** +> When passing `--contract-name` flag, `sncast` must wait for the declare transaction to be completed first. +> The contract might wait for a few seconds before executing the deployment. + +> 📝 **Note** +> If fee arguments are provided to the method, they will be shared between declare and deploy transactions. + + + +```shell +$ sncast deploy \ + --contract-name HelloSncast +``` + +
+Output: + +```shell +Success: Deployment completed + +Contract Address: 0x0[..] +Class Hash: 0x0[..] +Declare Transaction Hash: 0x0[..] +Deploy Transaction Hash: 0x0[..] + +To see deployment details, visit: +contract: [..] +class: [..] +declare transaction: [..] +deploy transaction: [..] +``` +
+ ### Passing `salt` Argument Salt is a parameter which modifies contract's address, if not passed it will be automatically generated.