Skip to content

Commit dc2bc13

Browse files
authored
Extract build from declare (#1565)
<!-- Reference any GitHub issues resolved by this PR --> -> * Extract build from declare -- * Add support for --package flag (#1569) -- * Add workspace tests (#1572) todo: -- * cleanup scarb_utils, use things from scarb_api/scarb_ui ## Introduced changes Closes #1474 <!-- A brief description of the changes --> - Extracts build logic from declare to avoid multiple artifact builds ## Checklist <!-- Make sure all of these are complete --> - [X] Linked relevant issue - [X] Updated relevant documentation - [X] Added relevant tests - [X] Performed self-review of the code - [X] Added changes to `CHANGELOG.md`
1 parent f58e0ab commit dc2bc13

File tree

8 files changed

+110
-107
lines changed

8 files changed

+110
-107
lines changed

crates/sncast/src/helpers/constants.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,5 @@ pub const DEFAULT_ACCOUNTS_FILE: &str = "~/.starknet_accounts/starknet_open_zepp
2727

2828
pub const KEYSTORE_PASSWORD_ENV_VAR: &str = "KEYSTORE_PASSWORD";
2929
pub const CREATE_KEYSTORE_PASSWORD_ENV_VAR: &str = "CREATE_KEYSTORE_PASSWORD";
30+
31+
pub const SCRIPT_LIB_ARTIFACT_NAME: &str = "__sncast_script_lib";

crates/sncast/src/helpers/scarb_utils.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use super::constants::{WAIT_RETRY_INTERVAL, WAIT_TIMEOUT};
22
use anyhow::{anyhow, bail, Context, Result};
33
use camino::{Utf8Path, Utf8PathBuf};
4-
use scarb_api::ScarbCommand;
4+
use scarb_api::{get_contracts_map, ScarbCommand, StarknetContractArtifacts};
55
use scarb_metadata;
66
use serde::{Deserialize, Serialize};
77
use serde_json::Value;
8+
use std::collections::HashMap;
89
use std::default::Default;
910
use std::env;
1011
use std::fs::canonicalize;
@@ -276,6 +277,28 @@ pub fn get_package_tool_sncast(metadata: &scarb_metadata::Metadata) -> Result<&V
276277
Ok(tool_sncast)
277278
}
278279

280+
pub struct BuildConfig {
281+
pub scarb_toml_path: Utf8PathBuf,
282+
pub json: bool,
283+
}
284+
285+
pub fn build(config: &BuildConfig) -> Result<HashMap<String, StarknetContractArtifacts>> {
286+
let mut cmd = ScarbCommand::new_with_stdio();
287+
cmd.arg("build").manifest_path(&config.scarb_toml_path);
288+
if config.json {
289+
cmd.json();
290+
}
291+
cmd.run()
292+
.map_err(|e| anyhow!(format!("Failed to build using scarb; {e}")))?;
293+
294+
let metadata = get_scarb_metadata_command(&config.scarb_toml_path)?
295+
.exec()
296+
.expect("Failed to obtain metadata");
297+
let package = get_package_metadata(&metadata, &config.scarb_toml_path)
298+
.with_context(|| anyhow!("Failed to find package"))?;
299+
get_contracts_map(&metadata, &package.id)
300+
}
301+
279302
#[cfg(test)]
280303
mod tests {
281304
use crate::helpers::scarb_utils::get_scarb_metadata;

crates/sncast/src/main.rs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ use crate::starknet_commands::{
77
use anyhow::{Context, Result};
88
use sncast::response::print::{print_command_result, OutputFormat};
99

10-
use crate::starknet_commands::declare::BuildConfig;
1110
use camino::Utf8PathBuf;
1211
use clap::{Parser, Subcommand};
1312
use sncast::helpers::constants::{DEFAULT_ACCOUNTS_FILE, DEFAULT_MULTICALL_CONTENTS};
14-
use sncast::helpers::scarb_utils::{parse_scarb_config, CastConfig};
13+
use sncast::helpers::scarb_utils::{
14+
build, get_package_metadata, get_scarb_manifest, get_scarb_metadata_with_deps,
15+
parse_scarb_config, BuildConfig, CastConfig,
16+
};
1517
use sncast::{
1618
chain_id_to_network_name, get_account, get_block_id, get_chain_id, get_nonce, get_provider,
1719
NumbersFormat, WaitForTx,
@@ -122,9 +124,22 @@ fn main() -> Result<()> {
122124
let runtime = Runtime::new().expect("Failed to instantiate Runtime");
123125

124126
if let Commands::Script(script) = cli.command {
127+
let manifest_path = match cli.path_to_scarb_toml {
128+
Some(path) => path,
129+
None => get_scarb_manifest().context("Failed to obtain manifest path from Scarb")?,
130+
};
131+
let metadata = get_scarb_metadata_with_deps(&manifest_path)?;
132+
let package_metadata = get_package_metadata(&metadata, &manifest_path)?;
133+
let mut artifacts = build(&BuildConfig {
134+
scarb_toml_path: manifest_path.clone(),
135+
json: cli.json,
136+
})
137+
.expect("Failed to build script");
125138
let mut result = starknet_commands::script::run(
126139
&script.script_module_name,
127-
&cli.path_to_scarb_toml,
140+
&metadata,
141+
package_metadata,
142+
&mut artifacts,
128143
&provider,
129144
runtime,
130145
&config,
@@ -156,10 +171,6 @@ async fn run_async_command(
156171
timeout: config.wait_timeout,
157172
retry_interval: config.wait_retry_interval,
158173
};
159-
let build_config = BuildConfig {
160-
scarb_toml_path: cli.path_to_scarb_toml.clone(),
161-
json: cli.json,
162-
};
163174
match cli.command {
164175
Commands::Declare(declare) => {
165176
let account = get_account(
@@ -169,12 +180,22 @@ async fn run_async_command(
169180
config.keystore,
170181
)
171182
.await?;
183+
let manifest_path = match cli.path_to_scarb_toml.clone() {
184+
Some(path) => path,
185+
None => {
186+
get_scarb_manifest().context("Failed to obtain manifest path from Scarb")?
187+
}
188+
};
189+
let artifacts = build(&BuildConfig {
190+
scarb_toml_path: manifest_path,
191+
json: cli.json,
192+
})?;
172193
let mut result = starknet_commands::declare::declare(
173194
&declare.contract,
174195
declare.max_fee,
175196
&account,
176197
declare.nonce,
177-
build_config,
198+
&artifacts,
178199
wait_config,
179200
)
180201
.await;

crates/sncast/src/starknet_commands/declare.rs

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
use anyhow::{anyhow, Context, Result};
2-
use camino::Utf8PathBuf;
32
use clap::Args;
4-
use scarb_api::{get_contracts_map, ScarbCommand};
5-
6-
use sncast::helpers::scarb_utils::get_package_metadata;
7-
use sncast::helpers::scarb_utils::get_scarb_manifest;
3+
use scarb_api::StarknetContractArtifacts;
84
use sncast::response::structs::DeclareResponse;
95
use sncast::response::structs::Hex;
106
use sncast::{apply_optional, handle_rpc_error, handle_wait_for_tx, WaitForTx};
@@ -18,6 +14,7 @@ use starknet::{
1814
providers::jsonrpc::{HttpTransport, JsonRpcClient},
1915
signers::LocalWallet,
2016
};
17+
use std::collections::HashMap;
2118
use std::sync::Arc;
2219

2320
#[derive(Args)]
@@ -36,44 +33,17 @@ pub struct Declare {
3633
pub nonce: Option<FieldElement>,
3734
}
3835

39-
pub struct BuildConfig {
40-
pub scarb_toml_path: Option<Utf8PathBuf>,
41-
pub json: bool,
42-
}
43-
4436
#[allow(clippy::too_many_lines)]
4537
pub async fn declare(
4638
contract_name: &str,
4739
max_fee: Option<FieldElement>,
4840
account: &SingleOwnerAccount<&JsonRpcClient<HttpTransport>, LocalWallet>,
4941
nonce: Option<FieldElement>,
50-
build_config: BuildConfig,
42+
artifacts: &HashMap<String, StarknetContractArtifacts>,
5143
wait_config: WaitForTx,
5244
) -> Result<DeclareResponse> {
5345
let contract_name: String = contract_name.to_string();
54-
let manifest_path = match build_config.scarb_toml_path.clone() {
55-
Some(path) => path,
56-
None => get_scarb_manifest().context("Failed to obtain manifest path from Scarb")?,
57-
};
58-
59-
let mut cmd = ScarbCommand::new_with_stdio();
60-
cmd.arg("build").manifest_path(&manifest_path);
61-
if build_config.json {
62-
cmd.json();
63-
}
64-
cmd.run().context("Failed to build contracts with Scarb")?;
65-
66-
let metadata = scarb_metadata::MetadataCommand::new()
67-
.manifest_path(&manifest_path)
68-
.inherit_stderr()
69-
.exec()
70-
.context("Failed to get scarb metadata")?;
71-
72-
let package = get_package_metadata(&metadata, &manifest_path)
73-
.with_context(|| anyhow!("Failed to find package for contract = {contract_name}"))?;
74-
let contracts = get_contracts_map(&metadata, &package.id)?;
75-
76-
let contract_artifacts = contracts
46+
let contract_artifacts = artifacts
7747
.get(&contract_name)
7848
.ok_or(anyhow!("Failed to find artifacts in starknet_artifacts.json file. Please ensure you have enabled sierra and casm code generation in Scarb.toml"))?;
7949

crates/sncast/src/starknet_commands/script.rs

Lines changed: 43 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
use std::collections::HashMap;
22
use std::fs;
33

4-
use crate::starknet_commands::declare::BuildConfig;
54
use crate::starknet_commands::{call, declare, deploy, invoke};
65
use crate::{get_account, get_nonce, WaitForTx};
7-
use anyhow::{anyhow, ensure, Context, Result};
6+
use anyhow::{anyhow, Context, Result};
87
use blockifier::execution::common_hints::ExecutionMode;
98
use blockifier::execution::deprecated_syscalls::DeprecatedSyscallSelector;
109
use blockifier::execution::entry_point::{
@@ -23,7 +22,6 @@ use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
2322
use cairo_vm::types::relocatable::Relocatable;
2423
use cairo_vm::vm::errors::hint_errors::HintError;
2524
use cairo_vm::vm::vm_core::VirtualMachine;
26-
use camino::Utf8PathBuf;
2725
use clap::command;
2826
use clap::Args;
2927
use conversions::{FromConv, IntoConv};
@@ -35,12 +33,11 @@ use runtime::{
3533
CheatcodeHandlingResult, EnhancedHintError, ExtendedRuntime, ExtensionLogic, StarknetRuntime,
3634
SyscallHandlingResult,
3735
};
38-
use scarb_api::{package_matches_version_requirement, ScarbCommand};
39-
use scarb_metadata::Metadata;
36+
use scarb_api::{package_matches_version_requirement, StarknetContractArtifacts};
37+
use scarb_metadata::{Metadata, PackageMetadata};
4038
use semver::{Comparator, Op, Version, VersionReq};
41-
use sncast::helpers::scarb_utils::{
42-
get_package_metadata, get_scarb_manifest, get_scarb_metadata_with_deps, CastConfig,
43-
};
39+
use sncast::helpers::constants::SCRIPT_LIB_ARTIFACT_NAME;
40+
use sncast::helpers::scarb_utils::CastConfig;
4441
use sncast::response::print::print_as_warning;
4542
use sncast::response::structs::ScriptResponse;
4643
use starknet::accounts::Account;
@@ -49,6 +46,8 @@ use starknet::providers::jsonrpc::HttpTransport;
4946
use starknet::providers::JsonRpcClient;
5047
use tokio::runtime::Runtime;
5148

49+
type ScriptStarknetContractArtifacts = StarknetContractArtifacts;
50+
5251
#[derive(Args)]
5352
#[command(about = "Execute a deployment script")]
5453
pub struct Script {
@@ -61,6 +60,7 @@ pub struct CastScriptExtension<'a> {
6160
pub provider: &'a JsonRpcClient<HttpTransport>,
6261
pub tokio_runtime: Runtime,
6362
pub config: &'a CastConfig,
63+
pub artifacts: &'a HashMap<String, StarknetContractArtifacts>,
6464
}
6565

6666
impl<'a> ExtensionLogic for CastScriptExtension<'a> {
@@ -118,10 +118,7 @@ impl<'a> ExtensionLogic for CastScriptExtension<'a> {
118118
max_fee,
119119
&account,
120120
nonce,
121-
BuildConfig {
122-
scarb_toml_path: None,
123-
json: false,
124-
},
121+
self.artifacts,
125122
WaitForTx {
126123
wait: true,
127124
timeout: self.config.wait_timeout,
@@ -253,21 +250,25 @@ impl<'a> ExtensionLogic for CastScriptExtension<'a> {
253250

254251
pub fn run(
255252
module_name: &str,
256-
path_to_scarb_toml: &Option<Utf8PathBuf>,
253+
metadata: &Metadata,
254+
package_metadata: &PackageMetadata,
255+
artifacts: &mut HashMap<String, StarknetContractArtifacts>,
257256
provider: &JsonRpcClient<HttpTransport>,
258257
tokio_runtime: Runtime,
259258
config: &CastConfig,
260259
) -> Result<ScriptResponse> {
261-
let path = compile_script(path_to_scarb_toml.clone())?;
260+
warn_if_sncast_std_not_compatible(metadata)?;
261+
let artifacts = inject_lib_artifact(metadata, package_metadata, artifacts)?;
262262

263-
let sierra_program = serde_json::from_str::<VersionedProgram>(
264-
&fs::read_to_string(path.clone())
265-
.with_context(|| format!("Failed to read Sierra file at path = {path}"))?,
266-
)
267-
.with_context(|| format!("Failed to deserialize Sierra program at path = {path}"))?
268-
.into_v1()
269-
.with_context(|| format!("Failed to load Sierra program at path = {path}"))?
270-
.program;
263+
let artifact = artifacts
264+
.get(SCRIPT_LIB_ARTIFACT_NAME)
265+
.ok_or(anyhow!("Failed to find script artifact"))?;
266+
267+
let sierra_program = serde_json::from_str::<VersionedProgram>(&artifact.sierra)
268+
.with_context(|| "Failed to deserialize Sierra program")?
269+
.into_v1()
270+
.with_context(|| "Failed to load Sierra program")?
271+
.program;
271272

272273
let runner = SierraCasmRunner::new(
273274
sierra_program,
@@ -327,6 +328,7 @@ pub fn run(
327328
provider,
328329
tokio_runtime,
329330
config,
331+
artifacts: &artifacts,
330332
};
331333

332334
let mut cast_runtime = ExtendedRuntime {
@@ -385,42 +387,28 @@ fn warn_if_sncast_std_not_compatible(scarb_metadata: &Metadata) -> Result<()> {
385387
Ok(())
386388
}
387389

388-
fn compile_script(path_to_scarb_toml: Option<Utf8PathBuf>) -> Result<Utf8PathBuf> {
389-
let scripts_manifest_path = path_to_scarb_toml.unwrap_or_else(|| {
390-
get_scarb_manifest()
391-
.context("Failed to retrieve manifest path from scarb")
392-
.unwrap()
393-
});
394-
ensure!(
395-
scripts_manifest_path.exists(),
396-
"The path = {scripts_manifest_path} does not exist"
397-
);
398-
399-
ScarbCommand::new_with_stdio()
400-
.arg("build")
401-
.manifest_path(&scripts_manifest_path)
402-
.run()
403-
.context("failed to compile script with scarb")?;
390+
fn inject_lib_artifact(
391+
metadata: &Metadata,
392+
package_metadata: &PackageMetadata,
393+
artifacts: &mut HashMap<String, StarknetContractArtifacts>,
394+
) -> Result<HashMap<String, StarknetContractArtifacts>> {
395+
let sierra_filename = format!("{}.sierra.json", package_metadata.name);
404396

405-
let metadata = get_scarb_metadata_with_deps(&scripts_manifest_path)?;
406-
407-
warn_if_sncast_std_not_compatible(&metadata)?;
408-
409-
let package_metadata = get_package_metadata(&metadata, &scripts_manifest_path)?;
410-
411-
let filename = format!("{}.sierra.json", package_metadata.name);
412-
let path = metadata
397+
let target_dir = &metadata
413398
.target_dir
414-
.unwrap_or(metadata.workspace.root.join("target"))
415-
.join(metadata.current_profile)
416-
.join(filename.clone());
417-
418-
ensure!(
419-
path.exists(),
420-
"The package has not been compiled, the file at path = {path} does not exist"
421-
);
399+
.clone()
400+
.unwrap_or_else(|| metadata.workspace.root.join("target"));
401+
let sierra_path = &target_dir
402+
.join(&metadata.current_profile)
403+
.join(sierra_filename);
404+
405+
let lib_artifacts = ScriptStarknetContractArtifacts {
406+
sierra: fs::read_to_string(sierra_path)?,
407+
casm: String::new(),
408+
};
422409

423-
Ok(path)
410+
artifacts.insert(SCRIPT_LIB_ARTIFACT_NAME.to_string(), lib_artifacts);
411+
Ok(artifacts.clone())
424412
}
425413

426414
// taken from starknet-foundry/crates/forge/src/test_case_summary.rs

crates/sncast/tests/e2e/declare.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ fn scarb_build_fails(contract_path: &str, accounts_file_path: &str) {
107107
.args(args);
108108

109109
snapbox.assert().stderr_matches(indoc! {r"
110-
command: declare
111-
error: Failed to build contracts with Scarb: `scarb` exited with error
110+
...
111+
Error: Failed to build using scarb; `scarb` exited with error
112112
"});
113113
}
114114

@@ -161,9 +161,9 @@ fn scarb_no_sierra_artifact() {
161161
.current_dir(CONTRACTS_DIR.to_string() + "/no_sierra")
162162
.args(args);
163163

164-
snapbox.assert().success().stderr_matches(indoc! {r"
165-
command: declare
166-
[..]Make sure you have enabled sierra code generation in Scarb.toml[..]
164+
snapbox.assert().failure().stderr_matches(indoc! {r"
165+
[..]Make sure you have enabled sierra code generation in Scarb.toml
166+
...
167167
"});
168168
}
169169

crates/sncast/tests/e2e/script/declare.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ async fn test_missing_field() {
1919
let snapbox = Command::new(cargo_bin!("sncast"))
2020
.current_dir(SCRIPTS_DIR.to_owned() + "/declare/missing_field")
2121
.args(args);
22-
snapbox.assert().success().stdout_matches(indoc! {r"
22+
snapbox.assert().failure().stdout_matches(indoc! {r"
2323
...
2424
error: Wrong number of arguments. Expected 3, found: 2
2525
...

0 commit comments

Comments
 (0)