Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduced by-name deployment and declare-deploy command #2331

Open
wants to merge 32 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
eecc2e2
Added a compiled contract representation
integraledelebesgue Jul 24, 2024
c4dbf57
Redesigned `declare` handler
integraledelebesgue Jul 24, 2024
69cf938
Introduced deploy by name
integraledelebesgue Jul 24, 2024
c449de5
Improved deploy-by-name
integraledelebesgue Jul 24, 2024
1f74c9c
Added deploy-by-name tests
integraledelebesgue Jul 24, 2024
b87ef03
Merge branch 'master' into integraledelebesgue/feature-2279
integraledelebesgue Jul 24, 2024
436dd28
Added deploy-by-name to changelog and docs
integraledelebesgue Jul 24, 2024
48ab4a9
Merge branch 'master' into integraledelebesgue/feature-2279
integraledelebesgue Jul 29, 2024
c84afe1
Merge branch 'master' into integraledelebesgue/feature-2279
integraledelebesgue Jul 29, 2024
f691a22
Moved deploy-by-name example higher in the docs
integraledelebesgue Jul 31, 2024
40b8ad7
Improved descriptivity of the `build_artifacts` method
integraledelebesgue Jul 31, 2024
d6b13d3
Merge branch 'master' into integraledelebesgue/feature-2279
integraledelebesgue Jul 31, 2024
873f524
Removed auto-declaration from `deploy` and introduced `declare-deploy…
integraledelebesgue Aug 5, 2024
9739534
Fixed `clippy` warnings
integraledelebesgue Aug 5, 2024
41880aa
Merged `master`
integraledelebesgue Aug 5, 2024
ae24960
Updated `declare-deploy` in docs and changelog
integraledelebesgue Aug 5, 2024
c29673b
Updated doc summary
integraledelebesgue Aug 5, 2024
9af83f9
Updated tests
integraledelebesgue Aug 7, 2024
8f9502a
Merge branch 'master' into integraledelebesgue/feature-2279
integraledelebesgue Aug 8, 2024
6350ac6
Simplified `declare_compiled` flow
integraledelebesgue Aug 8, 2024
67b2b96
Updated tests
integraledelebesgue Aug 9, 2024
79f23e9
Updated docs
integraledelebesgue Aug 9, 2024
fa5052f
Merge branch 'master' into integraledelebesgue/feature-2279
integraledelebesgue Aug 9, 2024
9a60f27
Optimized errors
integraledelebesgue Aug 9, 2024
c272f71
Merge branch 'master' into integraledelebesgue/feature-2279
integraledelebesgue Aug 27, 2024
1d0341e
Removed duplicated test
integraledelebesgue Aug 27, 2024
15d26df
Changed salt name to more professional :woozy_face:
integraledelebesgue Aug 27, 2024
7578221
Changed notes to warnings
integraledelebesgue Aug 27, 2024
f085c3e
Changed `From` to `TryFrom`
integraledelebesgue Aug 27, 2024
7b77cd3
Added block explorer output to `declare-deploy`
integraledelebesgue Aug 27, 2024
85f478e
Removed unnecessary `test-runner` argument
integraledelebesgue Aug 27, 2024
b11b1bc
Fixed logic related to class hashes
integraledelebesgue Aug 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Cast

### Added
- `sncast deploy` can now deploy contracts by name instead of class hash. [Read more here](https://foundry-rs.github.io/starknet-foundry/starknet/deploy.html)
- `sncast declare-deploy` allows to declare and deploy contract at once. [Read more here](https://foundry-rs.github.io/starknet-foundry/starknet/declare-deploy.html)

## [0.28.0] - 2024-08-21

### Forge
Expand Down
35 changes: 35 additions & 0 deletions crates/sncast/src/helpers/deploy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use clap::{Args, ValueEnum};
use starknet_crypto::FieldElement;

#[derive(ValueEnum, Debug, Clone)]
pub enum DeployVersion {
V1,
V3,
}

#[derive(Args)]
pub struct DeployArgs {
// A package to deploy a contract from
#[clap(long)]
pub package: Option<String>,

/// Calldata for the contract constructor
#[clap(short, long, value_delimiter = ' ', num_args = 1..)]
pub constructor_calldata: Vec<FieldElement>,

/// Salt for the address
#[clap(short, long)]
pub salt: Option<FieldElement>,

/// If true, salt will be modified with an account address
#[clap(long)]
pub unique: bool,

/// Nonce of the transaction. If not provided, nonce will be set automatically
#[clap(short, long)]
pub nonce: Option<FieldElement>,

/// Version of the deployment
#[clap(short, long)]
pub version: Option<DeployVersion>,
}
2 changes: 1 addition & 1 deletion crates/sncast/src/helpers/fee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use conversions::serde::deserialize::CairoDeserialize;
use starknet::core::types::{BlockId, FieldElement};
use starknet::providers::Provider;

#[derive(Args, Debug, Clone)]
#[derive(Args, Debug, Clone, Default)]
pub struct FeeArgs {
/// Token that transaction fee will be paid in
#[clap(long)]
Expand Down
1 change: 1 addition & 0 deletions crates/sncast/src/helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod block_explorer;
pub mod braavos;
pub mod configuration;
pub mod constants;
pub mod deploy;
pub mod error;
pub mod fee;
pub mod rpc;
Expand Down
74 changes: 73 additions & 1 deletion crates/sncast/src/helpers/scarb_utils.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use anyhow::{anyhow, Context, Result};
use anyhow::{anyhow, bail, Context, Result};
use camino::{Utf8Path, Utf8PathBuf};
use scarb_api::{
get_contracts_artifacts_and_source_sierra_paths,
Expand All @@ -7,6 +7,14 @@ use scarb_api::{
};
use scarb_ui::args::PackagesFilter;
use shared::{command::CommandExt, print::print_as_warning};
use starknet::{
core::types::{
contract::{CompiledClass, SierraClass},
BlockId, FlattenedSierraClass,
},
providers::{jsonrpc::HttpTransport, JsonRpcClient, Provider, ProviderError},
};
use starknet_crypto::FieldElement;
use std::collections::HashMap;
use std::env;
use std::str::FromStr;
Expand Down Expand Up @@ -176,6 +184,70 @@ pub fn build_and_load_artifacts(
}
}

pub fn read_manifest_and_build_artifacts(
package: &Option<String>,
json: bool,
profile: &Option<String>,
) -> Result<HashMap<String, StarknetContractArtifacts>> {
let manifest_path = assert_manifest_path_exists()?;
let package_metadata = get_package_metadata(&manifest_path, package)?;

let profile = profile.to_owned().unwrap_or("dev".to_string());

let build_config = BuildConfig {
scarb_toml_path: manifest_path,
json,
profile,
};

build_and_load_artifacts(&package_metadata, &build_config).context("Failed to build contract")
}

pub struct CompiledContract {
pub class: FlattenedSierraClass,
pub sierra_class_hash: FieldElement,
pub casm_class_hash: FieldElement,
}

impl TryFrom<&StarknetContractArtifacts> for CompiledContract {
type Error = anyhow::Error;

fn try_from(artifacts: &StarknetContractArtifacts) -> Result<Self, Self::Error> {
let sierra_class = serde_json::from_str::<SierraClass>(&artifacts.sierra)
.context("Failed to parse Sierra artifact")?
.flatten()?;

let compiled_class = serde_json::from_str::<CompiledClass>(&artifacts.casm)
.context("Failed to parse CASM artifact")?;

let sierra_class_hash = sierra_class.class_hash();
let casm_class_hash = compiled_class.class_hash()?;

Ok(Self {
class: sierra_class,
sierra_class_hash,
casm_class_hash,
})
}
}

impl CompiledContract {
pub async fn is_declared(&self, provider: &JsonRpcClient<HttpTransport>) -> Result<bool> {
let block_id = BlockId::Tag(starknet::core::types::BlockTag::Pending);
let class_hash = self.sierra_class_hash;

let response = provider.get_class(block_id, class_hash).await;

match response {
Ok(_) => Ok(true),
Err(ProviderError::StarknetError(
starknet::core::types::StarknetError::ClassHashNotFound,
)) => Ok(false),
Err(other) => bail!(other),
}
}
}

#[cfg(test)]
mod tests {
use crate::helpers::scarb_utils::{get_package_metadata, get_scarb_metadata};
Expand Down
112 changes: 98 additions & 14 deletions crates/sncast/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::starknet_commands::{
account, call::Call, declare::Declare, deploy::Deploy, invoke::Invoke, multicall::Multicall,
script::Script, tx_status::TxStatus,
};
use anyhow::{Context, Result};
use anyhow::{bail, Context, Result};
use configuration::load_global_config;
use sncast::response::explorer_link::print_block_explorer_link_if_allowed;
use sncast::response::print::{print_command_result, OutputFormat};
Expand All @@ -16,15 +16,18 @@ use sncast::helpers::constants::{DEFAULT_ACCOUNTS_FILE, DEFAULT_MULTICALL_CONTEN
use sncast::helpers::fee::PayableTransaction;
use sncast::helpers::scarb_utils::{
assert_manifest_path_exists, build, build_and_load_artifacts, get_package_metadata,
get_scarb_metadata_with_deps, BuildConfig,
get_scarb_metadata_with_deps, read_manifest_and_build_artifacts, BuildConfig,
};
use sncast::response::errors::handle_starknet_command_error;
use sncast::response::structs::DeclareDeployResponse;
use sncast::{
chain_id_to_network_name, get_account, get_block_id, get_chain_id, get_default_state_file_name,
NumbersFormat, ValidatedWaitParams, WaitForTx,
};
use starknet::core::utils::get_selector_from_name;
use starknet_commands::account::list::print_account_list;
use starknet_commands::declare_deploy::DeclareDeploy;
use starknet_commands::deploy::DeployResolved;
use starknet_commands::verify::Verify;
use tokio::runtime::Runtime;

Expand Down Expand Up @@ -118,6 +121,9 @@ enum Commands {
/// Deploy a contract
Deploy(Deploy),

// Declare a contract if it has not been yet and deploy it
DeclareDeploy(DeclareDeploy),

/// Call a contract
Call(Call),

Expand Down Expand Up @@ -191,17 +197,10 @@ async fn run_async_command(
config.keystore,
)
.await?;
let manifest_path = assert_manifest_path_exists()?;
let package_metadata = get_package_metadata(&manifest_path, &declare.package)?;
let artifacts = build_and_load_artifacts(
&package_metadata,
&BuildConfig {
scarb_toml_path: manifest_path,
json: cli.json,
profile: cli.profile.unwrap_or("dev".to_string()),
},
)
.expect("Failed to build contract");

let artifacts =
read_manifest_and_build_artifacts(&declare.package, cli.json, &cli.profile)?;

let result =
starknet_commands::declare::declare(declare, &account, &artifacts, wait_config)
.await
Expand All @@ -215,7 +214,6 @@ async fn run_async_command(
Commands::Deploy(deploy) => {
let provider = deploy.rpc.get_provider(&config).await?;

deploy.validate()?;
let account = get_account(
&config.account,
&config.accounts_file,
Expand All @@ -224,6 +222,22 @@ async fn run_async_command(
)
.await?;

let deploy: DeployResolved = if deploy.class_hash.is_some() {
deploy.try_into().unwrap()
} else {
let contract =
deploy.build_artifacts_and_get_compiled_contract(cli.json, &cli.profile)?;
integraledelebesgue marked this conversation as resolved.
Show resolved Hide resolved
let class_hash = contract.sierra_class_hash;

if !contract.is_declared(&provider).await? {
bail!("Contract with class hash {:x} is not declared", class_hash);
}

deploy.resolved_with_class_hash(class_hash)
};

deploy.validate()?;

Comment on lines +225 to +240
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So now we will explicitly fail if user tries to deploy contract that wasn't declared? What was the previous behavior. This might be worth including in a changelog if this behavior changed.

let result = starknet_commands::deploy::deploy(deploy, &account, wait_config)
.await
.map_err(handle_starknet_command_error);
Expand All @@ -233,6 +247,76 @@ async fn run_async_command(
Ok(())
}

Commands::DeclareDeploy(declare_deploy) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we explicitly document that declare-deploy behave exactly as deploy if the contract has been already declared?

let provider = declare_deploy.rpc.get_provider(&config).await?;

let account = get_account(
&config.account,
&config.accounts_file,
&provider,
config.keystore,
)
.await?;

let declare = Declare::from(&declare_deploy);
let deploy = Deploy::from(declare_deploy);

let contract =
deploy.build_artifacts_and_get_compiled_contract(cli.json, &cli.profile)?;
let class_hash = contract.sierra_class_hash;

let needs_declaration = !contract.is_declared(&provider).await?;

let declare_result = if needs_declaration {
let result = crate::starknet_commands::declare::declare_compiled(
declare,
&account,
contract,
wait_config,
)
.await
.map_err(handle_starknet_command_error);

if result.is_err() {
return print_command_result(
"declare-deploy",
&result,
numbers_format,
output_format,
);
}

Some(result.unwrap())
} else {
None
};

let deploy = deploy.resolved_with_class_hash(class_hash);
deploy.validate()?;

let deploy_result = starknet_commands::deploy::deploy(deploy, &account, wait_config)
.await
.map_err(handle_starknet_command_error);

if deploy_result.is_err() {
return print_command_result(
"declare-deploy",
&deploy_result,
numbers_format,
output_format,
);
}

let result = Ok(DeclareDeployResponse::new(
&declare_result,
deploy_result.unwrap(),
));

print_command_result("declare-deploy", &result, numbers_format, output_format)?;
print_block_explorer_link_if_allowed(&result, output_format, config.block_explorer);
Ok(())
}

Commands::Call(call) => {
let provider = call.rpc.get_provider(&config).await?;

Expand Down
Loading
Loading