From 3c6ec4d273977666279b47365c634aa69a022233 Mon Sep 17 00:00:00 2001 From: Illia Kripaka <30872146+ikripaka@users.noreply.github.com> Date: Fri, 20 Mar 2026 14:08:23 +0200 Subject: [PATCH 01/13] Add clippy ci entry (#24) * Add cargo clippy ci entry * Minor code fixes --- .github/workflows/ci.yml | 25 +++++++ crates/build/src/generator.rs | 10 ++- .../build/src/macros/{macros.rs => core.rs} | 0 crates/build/src/macros/mod.rs | 4 +- crates/build/src/resolver.rs | 7 +- crates/cli/src/cli.rs | 2 +- .../cli/src/commands/{commands.rs => core.rs} | 0 crates/cli/src/commands/mod.rs | 4 +- crates/cli/src/commands/regtest.rs | 2 +- crates/cli/src/commands/test.rs | 6 +- crates/cli/src/config/{config.rs => core.rs} | 0 crates/cli/src/config/mod.rs | 4 +- crates/regtest/src/client.rs | 10 ++- crates/regtest/src/regtest.rs | 2 +- .../sdk/src/program/{program.rs => core.rs} | 10 +-- crates/sdk/src/program/mod.rs | 4 +- .../sdk/src/provider/{provider.rs => core.rs} | 0 crates/sdk/src/provider/esplora.rs | 23 +++---- crates/sdk/src/provider/mod.rs | 4 +- crates/sdk/src/provider/rpc/elements.rs | 6 +- crates/sdk/src/provider/simplex.rs | 12 ++-- crates/sdk/src/signer/{signer.rs => core.rs} | 55 +++++++-------- crates/sdk/src/signer/mod.rs | 4 +- .../sdk/src/transaction/final_transaction.rs | 68 ++++++++----------- crates/sdk/src/transaction/partial_input.rs | 51 +++++++------- crates/sdk/src/transaction/partial_output.rs | 6 +- crates/simplex/tests/compile_test.rs | 2 +- crates/test/src/config.rs | 4 +- crates/test/src/context.rs | 12 ++-- crates/test/src/macros/{macros.rs => core.rs} | 0 crates/test/src/macros/mod.rs | 4 +- 31 files changed, 168 insertions(+), 173 deletions(-) rename crates/build/src/macros/{macros.rs => core.rs} (100%) rename crates/cli/src/commands/{commands.rs => core.rs} (100%) rename crates/cli/src/config/{config.rs => core.rs} (100%) rename crates/sdk/src/program/{program.rs => core.rs} (96%) rename crates/sdk/src/provider/{provider.rs => core.rs} (100%) rename crates/sdk/src/signer/{signer.rs => core.rs} (90%) rename crates/test/src/macros/{macros.rs => core.rs} (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 23971f4..d13cd8d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -113,6 +113,30 @@ jobs: with: command: check bans licenses sources + clippy: + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + persist-credentials: false + submodules: true + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + components: clippy + - uses: Swatinem/rust-cache@v2 + + - name: Run clippy checks + run: cargo clippy --workspace --all-targets --all-features + env: + RUSTFLAGS: -Dwarnings + ci-success: runs-on: ubuntu-latest if: always() @@ -120,6 +144,7 @@ jobs: - rust-tests - rustfmt - crate-checks + - clippy - deny timeout-minutes: 30 steps: diff --git a/crates/build/src/generator.rs b/crates/build/src/generator.rs index 484d57b..317fcac 100644 --- a/crates/build/src/generator.rs +++ b/crates/build/src/generator.rs @@ -79,14 +79,14 @@ impl ArtifactsGenerator { fn generate_bindings(out_dir: &Path, path_tree: TreeNode) -> Result, BuildError> { fs::create_dir_all(out_dir)?; - let mut mod_filenames = Self::generate_simfs(&out_dir, &path_tree.files)?; + let mut mod_filenames = Self::generate_simfs(out_dir, &path_tree.files)?; for (dir_name, tree_node) in path_tree.dirs.into_iter() { Self::generate_bindings(&out_dir.join(&dir_name), tree_node)?; mod_filenames.push(dir_name); } - Self::generate_mod_rs(&out_dir, &mod_filenames)?; + Self::generate_mod_rs(out_dir, &mod_filenames)?; Ok(mod_filenames) } @@ -156,10 +156,8 @@ impl ArtifactsGenerator { let include_simf_source_const = convert_contract_name_to_contract_source_const(contract_name); let include_simf_module = convert_contract_name_to_contract_module(contract_name); - let pathdiff = pathdiff::diff_paths(&simf_file, &cwd).ok_or(BuildError::FailedToFindCorrectRelativePath { - cwd: cwd, - simf_file: simf_file, - })?; + let pathdiff = pathdiff::diff_paths(&simf_file, &cwd) + .ok_or(BuildError::FailedToFindCorrectRelativePath { cwd, simf_file })?; let pathdiff = pathdiff.to_string_lossy().into_owned(); let code = quote! { diff --git a/crates/build/src/macros/macros.rs b/crates/build/src/macros/core.rs similarity index 100% rename from crates/build/src/macros/macros.rs rename to crates/build/src/macros/core.rs diff --git a/crates/build/src/macros/mod.rs b/crates/build/src/macros/mod.rs index 62e9c9f..c22c745 100644 --- a/crates/build/src/macros/mod.rs +++ b/crates/build/src/macros/mod.rs @@ -1,7 +1,7 @@ pub mod codegen; -pub mod macros; +mod core; pub mod parse; pub mod program; pub mod types; -pub use macros::expand; +pub use core::expand; diff --git a/crates/build/src/resolver.rs b/crates/build/src/resolver.rs index 4051d0d..18c9286 100644 --- a/crates/build/src/resolver.rs +++ b/crates/build/src/resolver.rs @@ -8,17 +8,16 @@ use super::error::BuildError; pub struct ArtifactsResolver {} impl ArtifactsResolver { - pub fn resolve_files_to_build(src_dir: &String, simfs: &Vec) -> Result, BuildError> { + pub fn resolve_files_to_build(src_dir: &String, simfs: &[String]) -> Result, BuildError> { let cwd = env::current_dir()?; let base = cwd.join(src_dir); let mut paths = Vec::new(); - let walker = globwalk::GlobWalkerBuilder::from_patterns(base, &simfs) + let walker = globwalk::GlobWalkerBuilder::from_patterns(base, simfs) .follow_links(true) .file_type(FileType::FILE) .build()? - .into_iter() .filter_map(Result::ok); for img in walker { @@ -34,7 +33,7 @@ impl ArtifactsResolver { if !path_outer.is_absolute() { let manifest_dir = env::current_dir()?; - let mut path_local = PathBuf::from(manifest_dir); + let mut path_local = manifest_dir; path_local.push(path_outer); path_outer = path_local; diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index 7194c75..09a98a5 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -2,8 +2,8 @@ use std::path::PathBuf; use clap::Parser; +use crate::commands::Command; use crate::commands::build::Build; -use crate::commands::commands::Command; use crate::commands::regtest::Regtest; use crate::commands::test::Test; use crate::config::{Config, INIT_CONFIG}; diff --git a/crates/cli/src/commands/commands.rs b/crates/cli/src/commands/core.rs similarity index 100% rename from crates/cli/src/commands/commands.rs rename to crates/cli/src/commands/core.rs diff --git a/crates/cli/src/commands/mod.rs b/crates/cli/src/commands/mod.rs index c8bc2d7..ff3c5ed 100644 --- a/crates/cli/src/commands/mod.rs +++ b/crates/cli/src/commands/mod.rs @@ -1,5 +1,7 @@ pub mod build; -pub mod commands; +mod core; pub mod error; pub mod regtest; pub mod test; + +pub use core::*; diff --git a/crates/cli/src/commands/regtest.rs b/crates/cli/src/commands/regtest.rs index 5e01c1b..4c44eea 100644 --- a/crates/cli/src/commands/regtest.rs +++ b/crates/cli/src/commands/regtest.rs @@ -10,7 +10,7 @@ pub struct Regtest {} impl Regtest { pub fn run(config: RegtestConfig) -> Result<(), CommandError> { - let (mut client, signer) = RegtestRunner::new(config)?; + let (mut client, signer) = RegtestRunner::from_config(config)?; let running = Arc::new(AtomicBool::new(true)); let r = running.clone(); diff --git a/crates/cli/src/commands/test.rs b/crates/cli/src/commands/test.rs index 2fdbe35..05adaf5 100644 --- a/crates/cli/src/commands/test.rs +++ b/crates/cli/src/commands/test.rs @@ -3,7 +3,7 @@ use std::process::Stdio; use smplx_test::TestConfig; -use super::commands::{TestCommand, TestFlags}; +use super::core::{TestCommand, TestFlags}; use super::error::CommandError; pub struct Test {} @@ -40,7 +40,7 @@ impl Test { TestCommand::Integration { additional_flags } => { command_as_arg.push_str("cargo test --tests"); - let flag_args = Self::build_test_flags(&additional_flags); + let flag_args = Self::build_test_flags(additional_flags); if !flag_args.is_empty() { command_as_arg.push_str(" --"); @@ -64,7 +64,7 @@ impl Test { command_as_arg.push_str(&arg); } - let flag_args = Self::build_test_flags(&additional_flags); + let flag_args = Self::build_test_flags(additional_flags); if !flag_args.is_empty() { command_as_arg.push_str(" --"); diff --git a/crates/cli/src/config/config.rs b/crates/cli/src/config/core.rs similarity index 100% rename from crates/cli/src/config/config.rs rename to crates/cli/src/config/core.rs diff --git a/crates/cli/src/config/mod.rs b/crates/cli/src/config/mod.rs index 8557703..067f356 100644 --- a/crates/cli/src/config/mod.rs +++ b/crates/cli/src/config/mod.rs @@ -1,4 +1,4 @@ -pub mod config; +mod core; pub mod error; -pub use config::{Config, INIT_CONFIG}; +pub use core::{CONFIG_FILENAME, Config, INIT_CONFIG}; diff --git a/crates/regtest/src/client.rs b/crates/regtest/src/client.rs index af4589b..79bc003 100644 --- a/crates/regtest/src/client.rs +++ b/crates/regtest/src/client.rs @@ -15,17 +15,15 @@ pub struct RegtestClient { } impl RegtestClient { - // TODO: pass custom config + // TODO: pass custom config, remove clippy tag + #[allow(clippy::new_without_default)] pub fn new() -> Self { let (electrs_path, elementsd_path) = Self::default_bin_paths(); let zmq_addr = Self::get_zmq_addr(); let elements = Self::create_bitcoind_node(elementsd_path, &zmq_addr); let electrs = Self::create_electrs_node(electrs_path, &elements, &zmq_addr); - Self { - electrs: electrs, - elements: elements, - } + Self { electrs, elements } } pub fn default_bin_paths() -> (PathBuf, PathBuf) { @@ -97,6 +95,6 @@ impl RegtestClient { conf.http_enabled = true; conf.network = "liquidregtest"; - ElectrsD::with_conf(bin_path.as_ref(), &elementsd, &conf).unwrap() + ElectrsD::with_conf(bin_path.as_ref(), elementsd, &conf).unwrap() } } diff --git a/crates/regtest/src/regtest.rs b/crates/regtest/src/regtest.rs index 789945d..e53dfc3 100644 --- a/crates/regtest/src/regtest.rs +++ b/crates/regtest/src/regtest.rs @@ -12,7 +12,7 @@ use super::error::RegtestError; pub struct Regtest {} impl Regtest { - pub fn new(config: RegtestConfig) -> Result<(RegtestClient, Signer), RegtestError> { + pub fn from_config(config: RegtestConfig) -> Result<(RegtestClient, Signer), RegtestError> { let client = RegtestClient::new(); let provider = Box::new(SimplexProvider::new( diff --git a/crates/sdk/src/program/program.rs b/crates/sdk/src/program/core.rs similarity index 96% rename from crates/sdk/src/program/program.rs rename to crates/sdk/src/program/core.rs index 24b2daa..9aea929 100644 --- a/crates/sdk/src/program/program.rs +++ b/crates/sdk/src/program/core.rs @@ -71,7 +71,7 @@ impl ProgramTrait for Program { } let target_utxo = &utxos[input_index]; - let script_pubkey = self.get_tr_address(&network)?.script_pubkey(); + let script_pubkey = self.get_tr_address(network)?.script_pubkey(); if target_utxo.script_pubkey != script_pubkey { return Err(ProgramError::ScriptPubkeyMismatch { @@ -129,7 +129,7 @@ impl ProgramTrait for Program { input_index: usize, network: &SimplicityNetwork, ) -> Result>, ProgramError> { - let pruned = self.execute(&pst, witness, input_index, network)?.0; + let pruned = self.execute(pst, witness, input_index, network)?.0; let (simplicity_program_bytes, simplicity_witness_bytes) = pruned.to_vec_with_witness(); let cmr = pruned.cmr(); @@ -146,9 +146,9 @@ impl ProgramTrait for Program { impl Program { pub fn new(source: &'static str, pub_key: XOnlyPublicKey, arguments: Box) -> Self { Self { - source: source, - pub_key: pub_key, - arguments: arguments, + source, + pub_key, + arguments, } } diff --git a/crates/sdk/src/program/mod.rs b/crates/sdk/src/program/mod.rs index 7d924e5..ca596cd 100644 --- a/crates/sdk/src/program/mod.rs +++ b/crates/sdk/src/program/mod.rs @@ -1,9 +1,9 @@ pub mod arguments; +mod core; pub mod error; -pub mod program; pub mod witness; pub use arguments::ArgumentsTrait; +pub use core::{Program, ProgramTrait}; pub use error::ProgramError; -pub use program::{Program, ProgramTrait}; pub use witness::WitnessTrait; diff --git a/crates/sdk/src/provider/provider.rs b/crates/sdk/src/provider/core.rs similarity index 100% rename from crates/sdk/src/provider/provider.rs rename to crates/sdk/src/provider/core.rs diff --git a/crates/sdk/src/provider/esplora.rs b/crates/sdk/src/provider/esplora.rs index fbe640d..856e67e 100644 --- a/crates/sdk/src/provider/esplora.rs +++ b/crates/sdk/src/provider/esplora.rs @@ -12,8 +12,8 @@ use serde::Deserialize; use crate::provider::SimplicityNetwork; +use super::core::{DEFAULT_ESPLORA_TIMEOUT_SECS, ProviderTrait}; use super::error::ProviderError; -use super::provider::{DEFAULT_ESPLORA_TIMEOUT_SECS, ProviderTrait}; pub struct EsploraProvider { pub esplora_url: String, @@ -53,7 +53,7 @@ impl EsploraProvider { pub fn new(url: String, network: SimplicityNetwork) -> Self { Self { esplora_url: url, - network: network, + network, timeout: Duration::from_secs(DEFAULT_ESPLORA_TIMEOUT_SECS), } } @@ -64,11 +64,8 @@ impl EsploraProvider { Ok(OutPoint::new(txid, utxo.vout)) } - fn populate_txouts_from_outpoints( - &self, - outpoints: &Vec, - ) -> Result, ProviderError> { - let set: HashSet<_> = outpoints.into_iter().collect(); + fn populate_txouts_from_outpoints(&self, outpoints: &[OutPoint]) -> Result, ProviderError> { + let set: HashSet<_> = outpoints.iter().collect(); let mut map = HashMap::new(); // filter unique transactions @@ -117,14 +114,14 @@ impl ProviderTrait for EsploraProvider { }); } - Ok(Txid::from_str(&body).map_err(|e| ProviderError::InvalidTxid(e.to_string()))?) + Txid::from_str(&body).map_err(|e| ProviderError::InvalidTxid(e.to_string())) } fn wait(&self, txid: &Txid) -> Result<(), ProviderError> { let url = format!("{}/tx/{}/status", self.esplora_url, txid); let timeout_secs = self.timeout.as_secs(); - let confirmation_poll = match self.network.clone() { + let confirmation_poll = match self.network { SimplicityNetwork::ElementsRegtest { .. } => Duration::from_millis(100), _ => Duration::from_secs(10), }; @@ -194,10 +191,10 @@ impl ProviderTrait for EsploraProvider { let utxos: Vec = response.json().map_err(|e| ProviderError::Deserialize(e.to_string()))?; let outpoints = utxos .iter() - .map(|utxo| Ok(self.esplora_utxo_to_outpoint(&utxo)?)) + .map(|utxo| self.esplora_utxo_to_outpoint(utxo)) .collect::, ProviderError>>()?; - Ok(self.populate_txouts_from_outpoints(&outpoints)?) + self.populate_txouts_from_outpoints(&outpoints) } fn fetch_scripthash_utxos(&self, script: &Script) -> Result, ProviderError> { @@ -223,10 +220,10 @@ impl ProviderTrait for EsploraProvider { let utxos: Vec = response.json().map_err(|e| ProviderError::Deserialize(e.to_string()))?; let outpoints = utxos .iter() - .map(|utxo| Ok(self.esplora_utxo_to_outpoint(&utxo)?)) + .map(|utxo| self.esplora_utxo_to_outpoint(utxo)) .collect::, ProviderError>>()?; - Ok(self.populate_txouts_from_outpoints(&outpoints)?) + self.populate_txouts_from_outpoints(&outpoints) } fn fetch_fee_estimates(&self) -> Result, ProviderError> { diff --git a/crates/sdk/src/provider/mod.rs b/crates/sdk/src/provider/mod.rs index b369c8c..85838e4 100644 --- a/crates/sdk/src/provider/mod.rs +++ b/crates/sdk/src/provider/mod.rs @@ -1,12 +1,12 @@ +mod core; pub mod error; pub mod esplora; pub mod network; -pub mod provider; pub mod rpc; pub mod simplex; +pub use core::ProviderTrait; pub use esplora::EsploraProvider; -pub use provider::ProviderTrait; pub use rpc::elements::ElementsRpc; pub use simplex::SimplexProvider; diff --git a/crates/sdk/src/provider/rpc/elements.rs b/crates/sdk/src/provider/rpc/elements.rs index 1a7437e..93140e2 100644 --- a/crates/sdk/src/provider/rpc/elements.rs +++ b/crates/sdk/src/provider/rpc/elements.rs @@ -21,11 +21,7 @@ impl ElementsRpc { let inner = Client::new(url.as_str(), auth.clone())?; inner.ping()?; - Ok(Self { - inner: inner, - auth: auth, - url: url, - }) + Ok(Self { inner, auth, url }) } pub fn height(&self) -> Result { diff --git a/crates/sdk/src/provider/simplex.rs b/crates/sdk/src/provider/simplex.rs index 2fab759..52e8b8d 100644 --- a/crates/sdk/src/provider/simplex.rs +++ b/crates/sdk/src/provider/simplex.rs @@ -6,8 +6,8 @@ use simplicityhl::elements::{Address, OutPoint, Script, Transaction, TxOut, Txid use crate::provider::SimplicityNetwork; +use super::core::ProviderTrait; use super::error::ProviderError; -use super::provider::ProviderTrait; use super::{ElementsRpc, EsploraProvider}; @@ -44,22 +44,22 @@ impl ProviderTrait for SimplexProvider { } fn wait(&self, txid: &Txid) -> Result<(), ProviderError> { - Ok(self.esplora.wait(txid)?) + self.esplora.wait(txid) } fn fetch_transaction(&self, txid: &Txid) -> Result { - Ok(self.esplora.fetch_transaction(txid)?) + self.esplora.fetch_transaction(txid) } fn fetch_address_utxos(&self, address: &Address) -> Result, ProviderError> { - Ok(self.esplora.fetch_address_utxos(address)?) + self.esplora.fetch_address_utxos(address) } fn fetch_scripthash_utxos(&self, script: &Script) -> Result, ProviderError> { - Ok(self.esplora.fetch_scripthash_utxos(script)?) + self.esplora.fetch_scripthash_utxos(script) } fn fetch_fee_estimates(&self) -> Result, ProviderError> { - Ok(self.esplora.fetch_fee_estimates()?) + self.esplora.fetch_fee_estimates() } } diff --git a/crates/sdk/src/signer/signer.rs b/crates/sdk/src/signer/core.rs similarity index 90% rename from crates/sdk/src/signer/signer.rs rename to crates/sdk/src/signer/core.rs index 532854b..427f6b2 100644 --- a/crates/sdk/src/signer/signer.rs +++ b/crates/sdk/src/signer/core.rs @@ -44,7 +44,7 @@ pub trait SignerTrait { fn sign_program( &self, pst: &PartiallySignedTransaction, - program: &Box, + program: &dyn ProgramTrait, input_index: usize, network: &SimplicityNetwork, ) -> Result; @@ -67,11 +67,11 @@ impl SignerTrait for Signer { fn sign_program( &self, pst: &PartiallySignedTransaction, - program: &Box, + program: &dyn ProgramTrait, input_index: usize, network: &SimplicityNetwork, ) -> Result { - let env = program.get_env(&pst, input_index, network)?; + let env = program.get_env(pst, input_index, network)?; let msg = Message::from_digest(env.c_tx_env().sighash_all().to_byte_array()); let private_key = self.get_private_key()?; @@ -117,13 +117,13 @@ impl Signer { let seed = mnemonic.to_seed(""); let xprv = Xpriv::new_master(NetworkKind::Test, &seed)?; - let network = provider.get_network().clone(); + let network = *provider.get_network(); Ok(Self { xprv, - provider: provider, - network: network, - secp: secp, + provider, + network, + secp, }) } @@ -133,7 +133,7 @@ impl Signer { for input in tx.inputs() { set.insert(OutPoint { - txid: input.partial_input.witness_txid.clone(), + txid: input.partial_input.witness_txid, vout: input.partial_input.witness_output_index, }); } @@ -192,8 +192,8 @@ impl Signer { } } - pub fn get_provider(&self) -> &Box { - &self.provider + pub fn get_provider(&self) -> &dyn ProviderTrait { + self.provider.as_ref() } pub fn get_wpkh_address(&self) -> Result { @@ -291,32 +291,29 @@ impl Signer { // finalize the tx with fee and without the change let final_tx = self.sign_tx(&fee_tx)?; - return Ok(Estimate::Success(final_tx, fee)); + Ok(Estimate::Success(final_tx, fee)) } fn sign_tx(&self, tx: &FinalTransaction) -> Result { let mut pst = tx.extract_pst(); let inputs = tx.inputs(); - for index in 0..inputs.len() { - let input = inputs[index].clone(); - + for (index, input_i) in inputs.iter().enumerate() { // we need to prune the program - if input.program_input.is_some() { - let program = input.program_input.unwrap(); - let signed_witness: Result = match input.required_sig { + if let Some(program_input) = &input_i.program_input { + let signed_witness: Result = match &input_i.required_sig { // sign the program and insert the signature into the witness RequiredSignature::Witness(witness_name) => Ok(self.get_signed_program_witness( &pst, - &program.program, - &program.witness.build_witness(), - &witness_name, + program_input.program.as_ref(), + &program_input.witness.build_witness(), + witness_name, index, )?), // just build the passed witness - _ => Ok(program.witness.build_witness()), + _ => Ok(program_input.witness.build_witness()), }; - let pruned_witness = program + let pruned_witness = program_input .program .finalize(&pst, &signed_witness.unwrap(), index, &self.network) .unwrap(); @@ -338,9 +335,9 @@ impl Signer { fn get_signed_program_witness( &self, pst: &PartiallySignedTransaction, - program: &Box, + program: &dyn ProgramTrait, witness: &WitnessValues, - witness_name: &String, + witness_name: &str, index: usize, ) -> Result { let signature = self.sign_program(pst, program, index, &self.network)?; @@ -352,7 +349,7 @@ impl Signer { }); hm.insert( - WitnessName::from_str_unchecked(witness_name.as_str()), + WitnessName::from_str_unchecked(witness_name), Value::byte_array(signature.serialize()), ); @@ -364,7 +361,7 @@ impl Signer { } fn master_xpriv(&self) -> Result { - Ok(self.derive_xpriv(&DerivationPath::master())?) + self.derive_xpriv(&DerivationPath::master()) } fn derive_xpub(&self, path: &DerivationPath) -> Result { @@ -374,7 +371,7 @@ impl Signer { } fn master_xpub(&self) -> Result { - Ok(self.derive_xpub(&DerivationPath::master())?) + self.derive_xpub(&DerivationPath::master()) } fn fingerprint(&self) -> Result { @@ -385,7 +382,7 @@ impl Signer { let coin_type = if self.network.is_mainnet() { 1776 } else { 1 }; let path = format!("84h/{coin_type}h/0h"); - Ok(DerivationPath::from_str(&format!("m/{path}")).map_err(|e| SignerError::DerivationPath(e.to_string()))?) + DerivationPath::from_str(&format!("m/{path}")).map_err(|e| SignerError::DerivationPath(e.to_string())) } } @@ -402,7 +399,7 @@ mod tests { let signer = Signer::new( "exist carry drive collect lend cereal occur much tiger just involve mean", - Box::new(EsploraProvider::new(url, network.clone())), + Box::new(EsploraProvider::new(url, network)), ) .unwrap(); diff --git a/crates/sdk/src/signer/mod.rs b/crates/sdk/src/signer/mod.rs index ae3f09a..e93c3a4 100644 --- a/crates/sdk/src/signer/mod.rs +++ b/crates/sdk/src/signer/mod.rs @@ -1,5 +1,5 @@ +mod core; pub mod error; -pub mod signer; +pub use core::{Signer, SignerTrait}; pub use error::SignerError; -pub use signer::{Signer, SignerTrait}; diff --git a/crates/sdk/src/transaction/final_transaction.rs b/crates/sdk/src/transaction/final_transaction.rs index 2680bce..5150ccd 100644 --- a/crates/sdk/src/transaction/final_transaction.rs +++ b/crates/sdk/src/transaction/final_transaction.rs @@ -28,7 +28,7 @@ pub struct FinalTransaction { impl FinalTransaction { pub fn new(network: SimplicityNetwork) -> Self { Self { - network: network, + network, inputs: Vec::new(), outputs: Vec::new(), } @@ -39,20 +39,17 @@ impl FinalTransaction { partial_input: PartialInput, required_sig: RequiredSignature, ) -> Result<(), TransactionError> { - match required_sig { - RequiredSignature::Witness(_) => { - return Err(TransactionError::SignatureRequest( - "Requested signature is not NativeEcdsa or None".to_string(), - )); - } - _ => {} + if let RequiredSignature::Witness(_) = required_sig { + return Err(TransactionError::SignatureRequest( + "Requested signature is not NativeEcdsa or None".to_string(), + )); } self.inputs.push(FinalInput { - partial_input: partial_input, + partial_input, program_input: None, issuance_input: None, - required_sig: required_sig, + required_sig, }); Ok(()) @@ -64,20 +61,17 @@ impl FinalTransaction { program_input: ProgramInput, required_sig: RequiredSignature, ) -> Result<(), TransactionError> { - match required_sig { - RequiredSignature::NativeEcdsa => { - return Err(TransactionError::SignatureRequest( - "Requested signature is not Witness or None".to_string(), - )); - } - _ => {} + if let RequiredSignature::NativeEcdsa = required_sig { + return Err(TransactionError::SignatureRequest( + "Requested signature is not Witness or None".to_string(), + )); } self.inputs.push(FinalInput { - partial_input: partial_input, + partial_input, program_input: Some(program_input), issuance_input: None, - required_sig: required_sig, + required_sig, }); Ok(()) @@ -89,22 +83,19 @@ impl FinalTransaction { issuance_input: IssuanceInput, required_sig: RequiredSignature, ) -> Result { - match required_sig { - RequiredSignature::Witness(_) => { - return Err(TransactionError::SignatureRequest( - "Requested signature is not NativeEcdsa or None".to_string(), - )); - } - _ => {} + if let RequiredSignature::Witness(_) = required_sig { + return Err(TransactionError::SignatureRequest( + "Requested signature is not NativeEcdsa or None".to_string(), + )); } let asset_id = AssetId::from_entropy(asset_entropy(&partial_input.outpoint(), issuance_input.asset_entropy)); self.inputs.push(FinalInput { - partial_input: partial_input, + partial_input, program_input: None, issuance_input: Some(issuance_input), - required_sig: required_sig, + required_sig, }); Ok(asset_id) @@ -117,22 +108,19 @@ impl FinalTransaction { issuance_input: IssuanceInput, required_sig: RequiredSignature, ) -> Result { - match required_sig { - RequiredSignature::NativeEcdsa => { - return Err(TransactionError::SignatureRequest( - "Requested signature is not Witness or None".to_string(), - )); - } - _ => {} + if let RequiredSignature::NativeEcdsa = required_sig { + return Err(TransactionError::SignatureRequest( + "Requested signature is not Witness or None".to_string(), + )); } let asset_id = AssetId::from_entropy(asset_entropy(&partial_input.outpoint(), issuance_input.asset_entropy)); self.inputs.push(FinalInput { - partial_input: partial_input, + partial_input, program_input: Some(program_input), issuance_input: Some(issuance_input), - required_sig: required_sig, + required_sig, }); Ok(asset_id) @@ -186,14 +174,14 @@ impl FinalTransaction { let available_amount = self .inputs .iter() - .filter(|input| input.partial_input.asset.clone().unwrap() == self.network.policy_asset()) - .fold(0 as u64, |acc, input| acc + input.partial_input.amount.clone().unwrap()); + .filter(|input| input.partial_input.asset.unwrap() == self.network.policy_asset()) + .fold(0_u64, |acc, input| acc + input.partial_input.amount.unwrap()); let consumed_amount = self .outputs .iter() .filter(|output| output.asset == self.network.policy_asset()) - .fold(0 as u64, |acc, output| acc + output.amount); + .fold(0_u64, |acc, output| acc + output.amount); available_amount as i64 - consumed_amount as i64 } diff --git a/crates/sdk/src/transaction/partial_input.rs b/crates/sdk/src/transaction/partial_input.rs index 7eed8b5..5f58324 100644 --- a/crates/sdk/src/transaction/partial_input.rs +++ b/crates/sdk/src/transaction/partial_input.rs @@ -53,58 +53,53 @@ impl PartialInput { witness_txid: outpoint.txid, witness_output_index: outpoint.vout, witness_utxo: txout, - sequence: sequence, - amount: amount, - asset: asset, + sequence, + amount, + asset, } } pub fn outpoint(&self) -> OutPoint { OutPoint { - txid: self.witness_txid.clone(), + txid: self.witness_txid, vout: self.witness_output_index, } } pub fn input(&self) -> Input { - let mut input = Input::default(); - - input.previous_txid = self.witness_txid.clone(); - input.previous_output_index = self.witness_output_index; - input.witness_utxo = Some(self.witness_utxo.clone()); - input.sequence = Some(self.sequence.clone()); - input.amount = self.amount.clone(); - input.asset = self.asset.clone(); - - input + Input { + previous_txid: self.witness_txid, + previous_output_index: self.witness_output_index, + witness_utxo: Some(self.witness_utxo.clone()), + sequence: Some(self.sequence), + amount: self.amount, + asset: self.asset, + ..Default::default() + } } } impl ProgramInput { pub fn new(program: Box, witness: Box) -> Self { - Self { - program: program, - witness: witness, - } + Self { program, witness } } } impl IssuanceInput { pub fn new(issuance_amount: u64, asset_entropy: [u8; 32]) -> Self { Self { - issuance_amount: issuance_amount, - asset_entropy: asset_entropy, + issuance_amount, + asset_entropy, } } pub fn input(&self) -> Input { - let mut input = Input::default(); - - input.issuance_value_amount = Some(self.issuance_amount); - input.issuance_asset_entropy = Some(self.asset_entropy); - input.issuance_inflation_keys = None; - input.blinded_issuance = Some(0x00); - - input + Input { + issuance_value_amount: Some(self.issuance_amount), + issuance_asset_entropy: Some(self.asset_entropy), + issuance_inflation_keys: None, + blinded_issuance: Some(0x00), + ..Default::default() + } } } diff --git a/crates/sdk/src/transaction/partial_output.rs b/crates/sdk/src/transaction/partial_output.rs index 27235bb..3335963 100644 --- a/crates/sdk/src/transaction/partial_output.rs +++ b/crates/sdk/src/transaction/partial_output.rs @@ -12,12 +12,12 @@ impl PartialOutput { pub fn new(script: Script, amount: u64, asset: AssetId) -> Self { Self { script_pubkey: script, - amount: amount, - asset: asset, + amount, + asset, } } pub fn to_output(&self) -> Output { - Output::new_explicit(self.script_pubkey.clone(), self.amount, self.asset.clone(), None) + Output::new_explicit(self.script_pubkey.clone(), self.amount, self.asset, None) } } diff --git a/crates/simplex/tests/compile_test.rs b/crates/simplex/tests/compile_test.rs index 01ebee1..8469a55 100644 --- a/crates/simplex/tests/compile_test.rs +++ b/crates/simplex/tests/compile_test.rs @@ -2,7 +2,7 @@ const SLOW_TEST_ENV: &str = "RUN_UI_TESTS"; #[test] fn ui() { - if let Err(_) = std::env::var(SLOW_TEST_ENV) { + if std::env::var(SLOW_TEST_ENV).is_err() { eprintln!("Set '{SLOW_TEST_ENV}' to true in order to run a test"); return; } diff --git a/crates/test/src/config.rs b/crates/test/src/config.rs index be6c30a..2c84360 100644 --- a/crates/test/src/config.rs +++ b/crates/test/src/config.rs @@ -56,9 +56,9 @@ impl TestConfig { fs::create_dir_all(parent_dir)?; } - let mut file = OpenOptions::new().create(true).write(true).truncate(true).open(&path)?; + let mut file = OpenOptions::new().create(true).write(true).truncate(true).open(path)?; - file.write(toml::to_string_pretty(&self).unwrap().as_bytes())?; + file.write_all(toml::to_string_pretty(&self).unwrap().as_bytes())?; file.flush()?; Ok(()) diff --git a/crates/test/src/context.rs b/crates/test/src/context.rs index e64dc88..1f61942 100644 --- a/crates/test/src/context.rs +++ b/crates/test/src/context.rs @@ -26,13 +26,13 @@ impl TestContext { Ok(Self { _client: client, - config: config, - signer: signer, + config, + signer, }) } - pub fn get_provider(&self) -> &Box { - &self.signer.get_provider() + pub fn get_provider(&self) -> &dyn ProviderTrait { + self.signer.get_provider() } pub fn get_config(&self) -> &TestConfig { @@ -40,7 +40,7 @@ impl TestContext { } pub fn get_network(&self) -> &SimplicityNetwork { - &self.signer.get_provider().get_network() + self.signer.get_provider().get_network() } pub fn get_signer(&self) -> &Signer { @@ -81,7 +81,7 @@ impl TestContext { }, None => { // simplex inner network - let (regtest_client, regtest_signer) = Regtest::new(config.to_regtest_config())?; + let (regtest_client, regtest_signer) = Regtest::from_config(config.to_regtest_config())?; client = Some(regtest_client); signer = regtest_signer; diff --git a/crates/test/src/macros/macros.rs b/crates/test/src/macros/core.rs similarity index 100% rename from crates/test/src/macros/macros.rs rename to crates/test/src/macros/core.rs diff --git a/crates/test/src/macros/mod.rs b/crates/test/src/macros/mod.rs index eb7bb1d..97e0d08 100644 --- a/crates/test/src/macros/mod.rs +++ b/crates/test/src/macros/mod.rs @@ -1,3 +1,3 @@ -pub mod macros; +mod core; -pub use macros::expand; +pub use core::expand; From 060e83ca942f42711ba254c9a8efb38f38dbe118 Mon Sep 17 00:00:00 2001 From: Oleh Komendant <44612825+Hrom131@users.noreply.github.com> Date: Fri, 20 Mar 2026 14:09:10 +0200 Subject: [PATCH 02/13] Fix an issue with multiple OP_RETURN outputs in the tests (#30) --- crates/regtest/src/args.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/regtest/src/args.rs b/crates/regtest/src/args.rs index 23fcbb1..420044f 100644 --- a/crates/regtest/src/args.rs +++ b/crates/regtest/src/args.rs @@ -13,6 +13,7 @@ pub fn get_elementsd_bin_args() -> Vec { "-initialfreecoins=2100000000000000".to_string(), "-listen=1".to_string(), "-txindex=1".to_string(), + "-multi_data_permitted".to_string(), ] } From 8c39419bb06dd9a7cf02e2dedbc5ea2bc7c3329b Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Fri, 20 Mar 2026 14:23:35 +0200 Subject: [PATCH 03/13] fix visibility --- crates/build/src/generator.rs | 2 ++ crates/build/src/macros/mod.rs | 2 +- crates/cli/src/commands/mod.rs | 2 +- crates/cli/src/config/mod.rs | 2 +- crates/sdk/src/program/mod.rs | 2 +- crates/sdk/src/provider/mod.rs | 2 +- crates/sdk/src/signer/mod.rs | 2 +- crates/test/src/macros/mod.rs | 2 +- 8 files changed, 9 insertions(+), 7 deletions(-) diff --git a/crates/build/src/generator.rs b/crates/build/src/generator.rs index 317fcac..7e6a38b 100644 --- a/crates/build/src/generator.rs +++ b/crates/build/src/generator.rs @@ -197,6 +197,8 @@ impl ArtifactsGenerator { let mod_names = mod_names.iter().map(|x| format_ident!("{x}")).collect::>(); let code = quote! { + #![allow(clippy::all)] + #(pub mod #mod_names);*; }; diff --git a/crates/build/src/macros/mod.rs b/crates/build/src/macros/mod.rs index c22c745..d29996e 100644 --- a/crates/build/src/macros/mod.rs +++ b/crates/build/src/macros/mod.rs @@ -1,5 +1,5 @@ pub mod codegen; -mod core; +pub mod core; pub mod parse; pub mod program; pub mod types; diff --git a/crates/cli/src/commands/mod.rs b/crates/cli/src/commands/mod.rs index ff3c5ed..81faac2 100644 --- a/crates/cli/src/commands/mod.rs +++ b/crates/cli/src/commands/mod.rs @@ -1,5 +1,5 @@ pub mod build; -mod core; +pub mod core; pub mod error; pub mod regtest; pub mod test; diff --git a/crates/cli/src/config/mod.rs b/crates/cli/src/config/mod.rs index 067f356..bccd9b0 100644 --- a/crates/cli/src/config/mod.rs +++ b/crates/cli/src/config/mod.rs @@ -1,4 +1,4 @@ -mod core; +pub mod core; pub mod error; pub use core::{CONFIG_FILENAME, Config, INIT_CONFIG}; diff --git a/crates/sdk/src/program/mod.rs b/crates/sdk/src/program/mod.rs index ca596cd..6982d16 100644 --- a/crates/sdk/src/program/mod.rs +++ b/crates/sdk/src/program/mod.rs @@ -1,5 +1,5 @@ pub mod arguments; -mod core; +pub mod core; pub mod error; pub mod witness; diff --git a/crates/sdk/src/provider/mod.rs b/crates/sdk/src/provider/mod.rs index 85838e4..9cae962 100644 --- a/crates/sdk/src/provider/mod.rs +++ b/crates/sdk/src/provider/mod.rs @@ -1,4 +1,4 @@ -mod core; +pub mod core; pub mod error; pub mod esplora; pub mod network; diff --git a/crates/sdk/src/signer/mod.rs b/crates/sdk/src/signer/mod.rs index e93c3a4..97c19b1 100644 --- a/crates/sdk/src/signer/mod.rs +++ b/crates/sdk/src/signer/mod.rs @@ -1,4 +1,4 @@ -mod core; +pub mod core; pub mod error; pub use core::{Signer, SignerTrait}; diff --git a/crates/test/src/macros/mod.rs b/crates/test/src/macros/mod.rs index 97e0d08..9e9d50f 100644 --- a/crates/test/src/macros/mod.rs +++ b/crates/test/src/macros/mod.rs @@ -1,3 +1,3 @@ -mod core; +pub mod core; pub use core::expand; From 17d6071c5fdbcd74ed8e7f1be41a5d579685005f Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Fri, 20 Mar 2026 18:55:45 +0200 Subject: [PATCH 04/13] sdk improvements --- crates/sdk/src/provider/core.rs | 4 ++ crates/sdk/src/provider/esplora.rs | 78 ++++++++++++++++++++++++++++++ crates/sdk/src/provider/simplex.rs | 8 +++ crates/sdk/src/signer/core.rs | 46 +++++++++++++++--- 4 files changed, 129 insertions(+), 7 deletions(-) diff --git a/crates/sdk/src/provider/core.rs b/crates/sdk/src/provider/core.rs index 6bc187f..fbea454 100644 --- a/crates/sdk/src/provider/core.rs +++ b/crates/sdk/src/provider/core.rs @@ -16,6 +16,10 @@ pub trait ProviderTrait { fn wait(&self, txid: &Txid) -> Result<(), ProviderError>; + fn fetch_tip_height(&self) -> Result; + + fn fetch_tip_timestamp(&self) -> Result; + fn fetch_transaction(&self, txid: &Txid) -> Result; fn fetch_address_utxos(&self, address: &Address) -> Result, ProviderError>; diff --git a/crates/sdk/src/provider/esplora.rs b/crates/sdk/src/provider/esplora.rs index 856e67e..62563a9 100644 --- a/crates/sdk/src/provider/esplora.rs +++ b/crates/sdk/src/provider/esplora.rs @@ -28,6 +28,15 @@ struct TxStatus { block_height: Option, } +#[derive(Deserialize)] +#[allow(dead_code)] +struct EsploraBlock { + id: String, + height: u32, + timestamp: u64, + tx_count: u32, +} + #[derive(Clone, Deserialize)] #[allow(dead_code)] struct UtxoStatus { @@ -150,6 +159,75 @@ impl ProviderTrait for EsploraProvider { Err(ProviderError::Confirmation()) } + fn fetch_tip_height(&self) -> Result { + let url = format!("{}/blocks/tip/height", self.esplora_url); + let timeout_secs = self.timeout.as_secs(); + + let response = minreq::get(&url) + .with_timeout(timeout_secs) + .send() + .map_err(|e| ProviderError::Request(e.to_string()))?; + + if response.status_code != 200 { + return Err(ProviderError::Request(format!( + "HTTP {}: {}", + response.status_code, response.reason_phrase + ))); + } + + let body_str = response + .as_str() + .map_err(|e| ProviderError::Deserialize(e.to_string()))?; + + let height: u32 = body_str + .trim() + .parse() + .map_err(|e: std::num::ParseIntError| ProviderError::Deserialize(e.to_string()))?; + + Ok(height) + } + + fn fetch_tip_timestamp(&self) -> Result { + let timeout_secs = self.timeout.as_secs(); + + let hash_url = format!("{}/blocks/tip/hash", self.esplora_url); + let hash_response = minreq::get(&hash_url) + .with_timeout(timeout_secs) + .send() + .map_err(|e| ProviderError::Request(e.to_string()))?; + + if hash_response.status_code != 200 { + return Err(ProviderError::Request(format!( + "HTTP {}: {}", + hash_response.status_code, hash_response.reason_phrase + ))); + } + + let tip_hash = hash_response + .as_str() + .map_err(|e| ProviderError::Deserialize(e.to_string()))? + .trim(); + + let block_url = format!("{}/block/{}", self.esplora_url, tip_hash); + let block_response = minreq::get(&block_url) + .with_timeout(timeout_secs) + .send() + .map_err(|e| ProviderError::Request(e.to_string()))?; + + if block_response.status_code != 200 { + return Err(ProviderError::Request(format!( + "HTTP {}: {}", + block_response.status_code, block_response.reason_phrase + ))); + } + + let block: EsploraBlock = block_response + .json() + .map_err(|e| ProviderError::Deserialize(e.to_string()))?; + + Ok(block.timestamp) + } + fn fetch_transaction(&self, txid: &Txid) -> Result { let url = format!("{}/tx/{}/raw", self.esplora_url, txid); let timeout_secs = self.timeout.as_secs(); diff --git a/crates/sdk/src/provider/simplex.rs b/crates/sdk/src/provider/simplex.rs index 52e8b8d..d94bf8b 100644 --- a/crates/sdk/src/provider/simplex.rs +++ b/crates/sdk/src/provider/simplex.rs @@ -47,6 +47,14 @@ impl ProviderTrait for SimplexProvider { self.esplora.wait(txid) } + fn fetch_tip_height(&self) -> Result { + self.esplora.fetch_tip_height() + } + + fn fetch_tip_timestamp(&self) -> Result { + self.esplora.fetch_tip_timestamp() + } + fn fetch_transaction(&self, txid: &Txid) -> Result { self.esplora.fetch_transaction(txid) } diff --git a/crates/sdk/src/signer/core.rs b/crates/sdk/src/signer/core.rs index 427f6b2..47b2dfb 100644 --- a/crates/sdk/src/signer/core.rs +++ b/crates/sdk/src/signer/core.rs @@ -4,11 +4,12 @@ use std::str::FromStr; use elements_miniscript::Descriptor; use elements_miniscript::bitcoin::PublicKey; use elements_miniscript::descriptor::Wpkh; + use simplicityhl::Value; use simplicityhl::WitnessValues; use simplicityhl::elements::pset::PartiallySignedTransaction; use simplicityhl::elements::secp256k1_zkp::{All, Keypair, Message, Secp256k1, ecdsa, schnorr}; -use simplicityhl::elements::{Address, OutPoint, Script, Transaction, TxOut}; +use simplicityhl::elements::{Address, AssetId, OutPoint, Script, Transaction, TxOut, Txid}; use simplicityhl::simplicity::bitcoin::XOnlyPublicKey; use simplicityhl::simplicity::hashes::Hash; use simplicityhl::str::WitnessName; @@ -127,8 +128,20 @@ impl Signer { }) } - pub fn finalize(&self, tx: &FinalTransaction, target_blocks: u32) -> Result<(Transaction, u64), SignerError> { - let mut signer_utxos = self.get_wpkh_utxos()?; + pub fn send_policy(&self, to: Script, amount: u64) -> Result<(Transaction, u64), SignerError> { + self.send(to, self.network.policy_asset(), amount) + } + + pub fn send(&self, to: Script, asset: AssetId, amount: u64) -> Result<(Transaction, u64), SignerError> { + let mut ft = FinalTransaction::new(self.network); + + ft.add_output(PartialOutput::new(to, amount, asset)); + + self.finalize(&ft) + } + + pub fn finalize(&self, tx: &FinalTransaction) -> Result<(Transaction, u64), SignerError> { + let mut signer_utxos = self.get_wpkh_utxos_asset(self.network.policy_asset())?; let mut set = HashSet::new(); for input in tx.inputs() { @@ -138,13 +151,12 @@ impl Signer { }); } - signer_utxos - .retain(|utxo| utxo.1.asset.explicit().unwrap() == self.network.policy_asset() && !set.contains(&utxo.0)); + signer_utxos.retain(|(outpoint, _)| !set.contains(&outpoint)); signer_utxos.sort_by(|a, b| b.1.value.cmp(&a.1.value)); let mut fee_tx = tx.clone(); let mut curr_fee = MIN_FEE; - let fee_rate = self.provider.fetch_fee_rate(target_blocks)?; + let fee_rate = self.provider.fetch_fee_rate(1)?; for utxo in signer_utxos { let policy_amount_delta = fee_tx.calculate_fee_delta(); @@ -212,7 +224,27 @@ impl Signer { } pub fn get_wpkh_utxos(&self) -> Result, SignerError> { - Ok(self.provider.fetch_address_utxos(&self.get_wpkh_address()?)?) + self.get_wpkh_utxos_filter(|_| true) + } + + pub fn get_wpkh_utxos_asset(&self, asset: AssetId) -> Result, SignerError> { + self.get_wpkh_utxos_filter(|(_, txout)| txout.asset.explicit().unwrap() == asset) + } + + // TODO: can this be optimized to not populate TxOuts that are filtered out? + pub fn get_wpkh_utxos_txid(&self, txid: Txid) -> Result, SignerError> { + self.get_wpkh_utxos_filter(|(outpoint, _)| outpoint.txid == txid) + } + + pub fn get_wpkh_utxos_filter(&self, filter: F) -> Result, SignerError> + where + F: FnMut(&(OutPoint, TxOut)) -> bool, + { + let mut utxos = self.provider.fetch_address_utxos(&self.get_wpkh_address()?)?; + + utxos.retain(filter); + + Ok(utxos) } pub fn get_schnorr_public_key(&self) -> Result { From 18bee6784b760431c660bd5380a8ceb75225e128 Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Fri, 20 Mar 2026 19:30:39 +0200 Subject: [PATCH 05/13] add initial bitcoins --- README.md | 4 +++ crates/cli/Simplex.default.toml | 2 ++ crates/regtest/src/config.rs | 3 ++ crates/regtest/src/regtest.rs | 12 ++++---- crates/sdk/src/provider/rpc/elements.rs | 40 +++++++++---------------- crates/sdk/src/utils.rs | 9 ++++-- crates/test/src/config.rs | 4 +++ examples/basic/README.md | 2 +- 8 files changed, 40 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 51b5429..ef111d3 100644 --- a/README.md +++ b/README.md @@ -57,9 +57,11 @@ out_dir = "./src/artifacts" [regtest] mnemonic = "exist carry drive collect lend cereal occur much tiger just involve mean" +bitcoins = 10_000_000 [test] mnemonic = "exist carry drive collect lend cereal occur much tiger just involve mean" +bitcoins = 10_000_000 [test.esplora] url = "" @@ -79,6 +81,7 @@ Where: - `out_dir` - The output directory where contracts artifacts are generated. - `regtest` (`simplex regtest` config) - `mnemonic` - The signer's mnemonic regtest will send initial funds to. + - `bitcoins` - Initial coins available to the signer - `test` (`simplex test` config) - `esplora` - `url` - Esplora API endpoint url @@ -88,6 +91,7 @@ Where: - `username` - Elements RPC username - `password` - Elements RPC password - `mnemonic` - The signer's mnemonic internal regtest will send initial funds to. + - `bitcoins` - Initial coins available to the signer ### CLI diff --git a/crates/cli/Simplex.default.toml b/crates/cli/Simplex.default.toml index b571f40..dbd76d7 100644 --- a/crates/cli/Simplex.default.toml +++ b/crates/cli/Simplex.default.toml @@ -7,9 +7,11 @@ # [regtest] # mnemonic = "exist carry drive collect lend cereal occur much tiger just involve mean" +# bitcoins = 10_000_000 # [test] # mnemonic = "exist carry drive collect lend cereal occur much tiger just involve mean" +# bitcoins = 10_000_000 # [test.esplora] # url = "" diff --git a/crates/regtest/src/config.rs b/crates/regtest/src/config.rs index 64e3a0c..f853460 100644 --- a/crates/regtest/src/config.rs +++ b/crates/regtest/src/config.rs @@ -7,11 +7,13 @@ use serde::Deserialize; use super::error::RegtestError; pub const DEFAULT_REGTEST_MNEMONIC: &str = "exist carry drive collect lend cereal occur much tiger just involve mean"; +pub const DEFAULT_BITCOINS: u64 = 10_000_000; #[derive(Debug, Clone, Deserialize)] #[serde(default)] pub struct RegtestConfig { pub mnemonic: String, + pub bitcoins: u64, } impl RegtestConfig { @@ -29,6 +31,7 @@ impl Default for RegtestConfig { fn default() -> Self { Self { mnemonic: DEFAULT_REGTEST_MNEMONIC.to_string(), + bitcoins: DEFAULT_BITCOINS, } } } diff --git a/crates/regtest/src/regtest.rs b/crates/regtest/src/regtest.rs index e53dfc3..530f4f1 100644 --- a/crates/regtest/src/regtest.rs +++ b/crates/regtest/src/regtest.rs @@ -4,6 +4,7 @@ use smplx_sdk::provider::ElementsRpc; use smplx_sdk::provider::SimplexProvider; use smplx_sdk::provider::SimplicityNetwork; use smplx_sdk::signer::Signer; +use smplx_sdk::utils::btc2sat; use super::RegtestConfig; use super::client::RegtestClient; @@ -24,21 +25,20 @@ impl Regtest { let signer = Signer::new(config.mnemonic.as_str(), provider)?; - Self::prepare_signer(&client, &signer)?; + Self::prepare_signer(&client, &signer, config.bitcoins)?; Ok((client, signer)) } - fn prepare_signer(client: &RegtestClient, signer: &Signer) -> Result<(), RegtestError> { + fn prepare_signer(client: &RegtestClient, signer: &Signer, bitcoins: u64) -> Result<(), RegtestError> { let rpc_provider = ElementsRpc::new(client.rpc_url(), client.auth())?; rpc_provider.generate_blocks(1)?; - rpc_provider.rescanblockchain(None, None)?; - rpc_provider.sweep_initialfreecoins()?; + rpc_provider.rescan_blockchain(None, None)?; + rpc_provider.sweep_initial_freecoins()?; rpc_provider.generate_blocks(100)?; - // 20 million BTC - rpc_provider.sendtoaddress(&signer.get_wpkh_address()?, 20_000_000 * u64::pow(10, 8), None)?; + rpc_provider.send_to_address(&signer.get_wpkh_address()?, btc2sat(bitcoins), None)?; // wait for electrs to index let mut attempts = 0; diff --git a/crates/sdk/src/provider/rpc/elements.rs b/crates/sdk/src/provider/rpc/elements.rs index 93140e2..ca257c9 100644 --- a/crates/sdk/src/provider/rpc/elements.rs +++ b/crates/sdk/src/provider/rpc/elements.rs @@ -5,6 +5,7 @@ use electrsd::bitcoind::bitcoincore_rpc::{Auth, Client, RpcApi}; use serde_json::Value; use simplicityhl::elements::{Address, AssetId, BlockHash, Txid}; +use simplicityhl::simplicity::bitcoin; use super::error::RpcError; @@ -24,27 +25,22 @@ impl ElementsRpc { Ok(Self { inner, auth, url }) } - pub fn height(&self) -> Result { - const METHOD: &str = "getblockcount"; - - self.inner - .call::(METHOD, &[])? - .as_u64() - .ok_or_else(|| RpcError::ElementsRpcUnexpectedReturn(METHOD.into())) - } - - pub fn block_hash(&self, height: u64) -> Result { - const METHOD: &str = "getblockhash"; + pub fn get_new_address(&self, label: &str) -> Result { + const METHOD: &str = "getnewaddress"; - let raw: Value = self.inner.call(METHOD, &[height.into()])?; + let addr: Value = self.inner.call(METHOD, &[label.into(), "bech32".to_string().into()])?; - Ok(BlockHash::from_str(raw.as_str().unwrap())?) + Ok(Address::from_str(addr.as_str().unwrap()).unwrap()) } - pub fn sendtoaddress(&self, address: &Address, satoshi: u64, asset: Option) -> Result { + pub fn send_to_address(&self, address: &Address, satoshi: u64, asset: Option) -> Result { const METHOD: &str = "sendtoaddress"; let btc = sat2btc(satoshi); + let btc = bitcoin::amount::Amount::from_btc(btc) + .unwrap() + .to_string_in(bitcoin::amount::Denomination::Bitcoin); + let r = match asset { Some(asset) => self.inner.call::( METHOD, @@ -69,7 +65,7 @@ impl ElementsRpc { Ok(Txid::from_str(r.as_str().unwrap()).unwrap()) } - pub fn rescanblockchain(&self, start: Option, stop: Option) -> Result<(), RpcError> { + pub fn rescan_blockchain(&self, start: Option, stop: Option) -> Result<(), RpcError> { const METHOD: &str = "rescanblockchain"; let mut args = Vec::with_capacity(2); @@ -87,27 +83,19 @@ impl ElementsRpc { Ok(()) } - pub fn getnewaddress(&self, label: &str) -> Result { - const METHOD: &str = "getnewaddress"; - - let addr: Value = self.inner.call(METHOD, &[label.into(), "bech32".to_string().into()])?; - - Ok(Address::from_str(addr.as_str().unwrap()).unwrap()) - } - pub fn generate_blocks(&self, block_num: u32) -> Result<(), RpcError> { const METHOD: &str = "generatetoaddress"; - let address = self.getnewaddress("")?.to_string(); + let address = self.get_new_address("")?.to_string(); self.inner.call::(METHOD, &[block_num.into(), address.into()])?; Ok(()) } - pub fn sweep_initialfreecoins(&self) -> Result<(), RpcError> { + pub fn sweep_initial_freecoins(&self) -> Result<(), RpcError> { const METHOD: &str = "sendtoaddress"; - let address = self.getnewaddress("")?; + let address = self.get_new_address("")?; self.inner.call::( METHOD, &[ diff --git a/crates/sdk/src/utils.rs b/crates/sdk/src/utils.rs index 434a175..d522aa0 100644 --- a/crates/sdk/src/utils.rs +++ b/crates/sdk/src/utils.rs @@ -25,7 +25,10 @@ pub fn hash_script(script: &Script) -> [u8; 32] { hasher.finalize().into() } -pub fn sat2btc(sat: u64) -> String { - let amount = bitcoin::Amount::from_sat(sat); - amount.to_string_in(bitcoin::amount::Denomination::Bitcoin) +pub fn sat2btc(sat: u64) -> f64 { + bitcoin::Amount::from_sat(sat).to_btc() +} + +pub fn btc2sat(btc: u64) -> u64 { + bitcoin::Amount::from_int_btc(btc).to_sat() } diff --git a/crates/test/src/config.rs b/crates/test/src/config.rs index 2c84360..929e1af 100644 --- a/crates/test/src/config.rs +++ b/crates/test/src/config.rs @@ -12,11 +12,13 @@ use super::error::TestError; pub const TEST_ENV_NAME: &str = "SIMPLEX_TEST_ENV"; pub const DEFAULT_TEST_MNEMONIC: &str = "exist carry drive collect lend cereal occur much tiger just involve mean"; +pub const DEFAULT_BITCOINS: u64 = 10_000_000; #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(default)] pub struct TestConfig { pub mnemonic: String, + pub bitcoins: u64, pub esplora: Option, pub rpc: Option, } @@ -48,6 +50,7 @@ impl TestConfig { pub fn to_regtest_config(&self) -> RegtestConfig { RegtestConfig { mnemonic: self.mnemonic.clone(), + bitcoins: self.bitcoins, } } @@ -69,6 +72,7 @@ impl Default for TestConfig { fn default() -> Self { Self { mnemonic: DEFAULT_TEST_MNEMONIC.to_string(), + bitcoins: DEFAULT_BITCOINS, esplora: None, rpc: None, } diff --git a/examples/basic/README.md b/examples/basic/README.md index cfaf573..a3f8be4 100644 --- a/examples/basic/README.md +++ b/examples/basic/README.md @@ -6,7 +6,7 @@ This is an example project to get started with Simplex. The project assumes that The repository structure is the following: -```ml +```md root ├── simf — Simplicity smart contracts source directory ├── src — The project's Rust code (witness and transaction builders) From 0378cc9515a164babbecb93ad0562076da1c8716 Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Fri, 20 Mar 2026 19:35:00 +0200 Subject: [PATCH 06/13] fix cargo.tomls --- crates/cli/Cargo.toml | 2 ++ crates/macros/Cargo.toml | 2 ++ crates/regtest/Cargo.toml | 2 ++ crates/sdk/Cargo.toml | 2 ++ crates/sdk/src/provider/rpc/elements.rs | 2 +- crates/test/Cargo.toml | 2 ++ 6 files changed, 11 insertions(+), 1 deletion(-) diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 5bb32b1..eda9790 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -4,6 +4,8 @@ version = "0.0.1" description = "Simplex cli with various utilities to manage a simplicity project" license.workspace = true edition.workspace = true +repository = "https://github.com/BlockstreamResearch/smplx" +readme = "../../README.md" [[bin]] name = "simplex" diff --git a/crates/macros/Cargo.toml b/crates/macros/Cargo.toml index d84f839..2ef7e74 100644 --- a/crates/macros/Cargo.toml +++ b/crates/macros/Cargo.toml @@ -4,6 +4,8 @@ version = "0.0.1" description = "Simplex macros re-export package" license.workspace = true edition.workspace = true +repository = "https://github.com/BlockstreamResearch/smplx" +readme = "../../README.md" [lib] proc-macro = true diff --git a/crates/regtest/Cargo.toml b/crates/regtest/Cargo.toml index edd5724..0007018 100644 --- a/crates/regtest/Cargo.toml +++ b/crates/regtest/Cargo.toml @@ -4,6 +4,8 @@ version = "0.0.1" description = "Simplex regtest command internal implementation" license.workspace = true edition.workspace = true +repository = "https://github.com/BlockstreamResearch/smplx" +readme = "../../README.md" [lints] workspace = true diff --git a/crates/sdk/Cargo.toml b/crates/sdk/Cargo.toml index 2dfb31f..59428ef 100644 --- a/crates/sdk/Cargo.toml +++ b/crates/sdk/Cargo.toml @@ -4,6 +4,8 @@ version = "0.0.1" description = "Simplex sdk to simplify the development with simplicity" license.workspace = true edition.workspace = true +repository = "https://github.com/BlockstreamResearch/smplx" +readme = "../../README.md" [lints] workspace = true diff --git a/crates/sdk/src/provider/rpc/elements.rs b/crates/sdk/src/provider/rpc/elements.rs index ca257c9..e40c5eb 100644 --- a/crates/sdk/src/provider/rpc/elements.rs +++ b/crates/sdk/src/provider/rpc/elements.rs @@ -4,7 +4,7 @@ use electrsd::bitcoind::bitcoincore_rpc::{Auth, Client, RpcApi}; use serde_json::Value; -use simplicityhl::elements::{Address, AssetId, BlockHash, Txid}; +use simplicityhl::elements::{Address, AssetId, Txid}; use simplicityhl::simplicity::bitcoin; use super::error::RpcError; diff --git a/crates/test/Cargo.toml b/crates/test/Cargo.toml index ed0d4e6..e76586c 100644 --- a/crates/test/Cargo.toml +++ b/crates/test/Cargo.toml @@ -4,6 +4,8 @@ version = "0.0.1" description = "Simplex test command internal implementation" license.workspace = true edition.workspace = true +repository = "https://github.com/BlockstreamResearch/smplx" +readme = "../../README.md" [lints] workspace = true From 91808011487894beaf6b56c6800d1c048045797f Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Fri, 20 Mar 2026 19:48:48 +0200 Subject: [PATCH 07/13] fix clippy --- crates/regtest/src/regtest.rs | 2 +- crates/sdk/src/provider/rpc/elements.rs | 2 +- crates/sdk/src/signer/core.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/regtest/src/regtest.rs b/crates/regtest/src/regtest.rs index 530f4f1..c99386a 100644 --- a/crates/regtest/src/regtest.rs +++ b/crates/regtest/src/regtest.rs @@ -35,7 +35,7 @@ impl Regtest { rpc_provider.generate_blocks(1)?; rpc_provider.rescan_blockchain(None, None)?; - rpc_provider.sweep_initial_freecoins()?; + rpc_provider.sweep_initialfreecoins()?; rpc_provider.generate_blocks(100)?; rpc_provider.send_to_address(&signer.get_wpkh_address()?, btc2sat(bitcoins), None)?; diff --git a/crates/sdk/src/provider/rpc/elements.rs b/crates/sdk/src/provider/rpc/elements.rs index e40c5eb..9f9d6fd 100644 --- a/crates/sdk/src/provider/rpc/elements.rs +++ b/crates/sdk/src/provider/rpc/elements.rs @@ -92,7 +92,7 @@ impl ElementsRpc { Ok(()) } - pub fn sweep_initial_freecoins(&self) -> Result<(), RpcError> { + pub fn sweep_initialfreecoins(&self) -> Result<(), RpcError> { const METHOD: &str = "sendtoaddress"; let address = self.get_new_address("")?; diff --git a/crates/sdk/src/signer/core.rs b/crates/sdk/src/signer/core.rs index 47b2dfb..520e64e 100644 --- a/crates/sdk/src/signer/core.rs +++ b/crates/sdk/src/signer/core.rs @@ -151,7 +151,7 @@ impl Signer { }); } - signer_utxos.retain(|(outpoint, _)| !set.contains(&outpoint)); + signer_utxos.retain(|(outpoint, _)| !set.contains(outpoint)); signer_utxos.sort_by(|a, b| b.1.value.cmp(&a.1.value)); let mut fee_tx = tx.clone(); From b88c0e9f5a1700e2b12af58b099dd7639c45074f Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Fri, 20 Mar 2026 20:02:19 +0200 Subject: [PATCH 08/13] fix assets sending --- crates/sdk/src/signer/core.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/sdk/src/signer/core.rs b/crates/sdk/src/signer/core.rs index 520e64e..c2ffc2d 100644 --- a/crates/sdk/src/signer/core.rs +++ b/crates/sdk/src/signer/core.rs @@ -128,14 +128,11 @@ impl Signer { }) } - pub fn send_policy(&self, to: Script, amount: u64) -> Result<(Transaction, u64), SignerError> { - self.send(to, self.network.policy_asset(), amount) - } - - pub fn send(&self, to: Script, asset: AssetId, amount: u64) -> Result<(Transaction, u64), SignerError> { + // TODO: add an ability to send arbitrary assets + pub fn send(&self, to: Script, amount: u64) -> Result<(Transaction, u64), SignerError> { let mut ft = FinalTransaction::new(self.network); - ft.add_output(PartialOutput::new(to, amount, asset)); + ft.add_output(PartialOutput::new(to, amount, self.network.policy_asset())); self.finalize(&ft) } From df841dbd7ad3b8d27c26154ec7d4c5300970acde Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Fri, 20 Mar 2026 20:22:27 +0200 Subject: [PATCH 09/13] quick debug --- crates/sdk/src/transaction/partial_input.rs | 2 +- crates/sdk/src/transaction/partial_output.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/sdk/src/transaction/partial_input.rs b/crates/sdk/src/transaction/partial_input.rs index 5f58324..b68fa7c 100644 --- a/crates/sdk/src/transaction/partial_input.rs +++ b/crates/sdk/src/transaction/partial_input.rs @@ -12,7 +12,7 @@ pub enum RequiredSignature { Witness(String), } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct PartialInput { pub witness_txid: Txid, pub witness_output_index: u32, diff --git a/crates/sdk/src/transaction/partial_output.rs b/crates/sdk/src/transaction/partial_output.rs index 3335963..31fca32 100644 --- a/crates/sdk/src/transaction/partial_output.rs +++ b/crates/sdk/src/transaction/partial_output.rs @@ -1,7 +1,7 @@ use simplicityhl::elements::pset::Output; use simplicityhl::elements::{AssetId, Script}; -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct PartialOutput { pub script_pubkey: Script, pub amount: u64, From 3809e0898e07e4acdda675f157df01fbf80750d9 Mon Sep 17 00:00:00 2001 From: Illia Kripaka <30872146+ikripaka@users.noreply.github.com> Date: Mon, 23 Mar 2026 16:53:06 +0200 Subject: [PATCH 10/13] Extend init and clean commands (#31) * Extend init and clean commands chore: * introduce *Result types for simplification * move Simplex.default.toml to crates/cli/assets folder * Move errors to src/commands/error * Add additional files to generate in `init --lib` command * Mix imports * clean up --------- Co-authored-by: Artem Chystiakov --- Cargo.lock | 21 ++- README.md | 5 +- crates/cli/Cargo.toml | 9 +- crates/cli/{ => assets}/Simplex.default.toml | 0 crates/cli/src/cli.rs | 19 ++- crates/cli/src/commands/clean.rs | 97 ++++++++++++ crates/cli/src/commands/core.rs | 30 +++- crates/cli/src/commands/error.rs | 47 ++++++ crates/cli/src/commands/init.rs | 153 +++++++++++++++++++ crates/cli/src/commands/mod.rs | 2 + crates/cli/src/config/core.rs | 8 +- 11 files changed, 369 insertions(+), 22 deletions(-) rename crates/cli/{ => assets}/Simplex.default.toml (100%) create mode 100644 crates/cli/src/commands/clean.rs create mode 100644 crates/cli/src/commands/init.rs diff --git a/Cargo.lock b/Cargo.lock index 3af41ae..6f459ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1313,9 +1313,9 @@ dependencies = [ "clap", "ctrlc", "dotenvy", - "electrsd", + "minreq", "serde", - "simplicityhl", + "serde_json", "smplx-build", "smplx-regtest", "smplx-sdk", @@ -1323,6 +1323,7 @@ dependencies = [ "thiserror", "tokio", "toml 0.9.12+spec-1.1.0", + "toml_edit", ] [[package]] @@ -1554,6 +1555,19 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_edit" +version = "0.23.10+spec-1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +dependencies = [ + "indexmap", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow", +] + [[package]] name = "toml_parser" version = "1.0.9+spec-1.1.0" @@ -1877,6 +1891,9 @@ name = "winnow" version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] [[package]] name = "wit-bindgen" diff --git a/README.md b/README.md index ef111d3..5ef0d6d 100644 --- a/README.md +++ b/README.md @@ -97,11 +97,12 @@ Where: Simplex CLI provides the following commands: -- `simplex init` - Initializes a Simplex project. +- `simplex init` - Initializes a new Simplex project. - `simplex config` - Prints the current config. - `simplex build` - Generates simplicity artifacts. - `simplex regtest` - Spins up local Electrs + Elements nodes. - `simplex test` - Runs Simplex tests. +- `simplex clean` - Cleans up generated artifacts. To view the available options, run the help command: @@ -119,7 +120,7 @@ We are open to any mind-blowing ideas! Please take a look at our [contributing g ## Future work -- [ ] Complete `simplex init` and `simplex clean` tasks. +- [x] Complete `simplex init` and `simplex clean` tasks. - [ ] SDK support for confidential assets, taproot signer, and custom witness signatures. - [ ] Local regtest 10x speedup. - [ ] Regtest cheat codes. diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index eda9790..a96e5f8 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -17,17 +17,18 @@ workspace = true [dependencies] smplx-regtest = { workspace = true } smplx-test = { workspace = true } -smplx-build = { workspace = true} +smplx-build = { workspace = true } smplx-sdk = { workspace = true } -simplicityhl = { workspace = true } -electrsd = { workspace = true } thiserror = { workspace = true } serde = { workspace = true } toml = { workspace = true } +minreq = { workspace = true } anyhow = "1" dotenvy = "0.15" clap = { version = "4", features = ["derive", "env"] } tokio = { version = "1", features = ["rt-multi-thread", "macros"] } -ctrlc = { version = "3.5.2", features = ["termination"] } +toml_edit = { version = "0.23.9" } +ctrlc = { version = "3.5.2", features = ["termination"] } +serde_json = { version = "1.0.149" } diff --git a/crates/cli/Simplex.default.toml b/crates/cli/assets/Simplex.default.toml similarity index 100% rename from crates/cli/Simplex.default.toml rename to crates/cli/assets/Simplex.default.toml diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index 09a98a5..ad0d15d 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -4,9 +4,11 @@ use clap::Parser; use crate::commands::Command; use crate::commands::build::Build; +use crate::commands::clean::Clean; +use crate::commands::init::Init; use crate::commands::regtest::Regtest; use crate::commands::test::Test; -use crate::config::{Config, INIT_CONFIG}; +use crate::config::Config; use crate::error::CliError; #[derive(Debug, Parser)] @@ -22,13 +24,10 @@ pub struct Cli { impl Cli { pub async fn run(&self) -> Result<(), CliError> { match &self.command { - Command::Init => { - let config_path = Config::get_default_path()?; - std::fs::write(&config_path, INIT_CONFIG)?; - - println!("Config written to: '{}'", config_path.display()); + Command::Init { additional_flags } => { + let simplex_conf_path = Config::get_default_path()?; - Ok(()) + Ok(Init::run(*additional_flags, simplex_conf_path)?) } Command::Config => { let config_path = Config::get_default_path()?; @@ -56,6 +55,12 @@ impl Cli { Ok(Build::run(loaded_config.build)?) } + Command::Clean { additional_flags } => { + let config_path = Config::get_default_path()?; + let loaded_config = Config::load(&config_path)?; + + Ok(Clean::run(loaded_config.build, *additional_flags, config_path)?) + } } } } diff --git a/crates/cli/src/commands/clean.rs b/crates/cli/src/commands/clean.rs new file mode 100644 index 0000000..220b246 --- /dev/null +++ b/crates/cli/src/commands/clean.rs @@ -0,0 +1,97 @@ +use std::{ + fmt::Display, + fs, + path::{Path, PathBuf}, +}; + +use smplx_build::{ArtifactsResolver, BuildConfig}; + +use crate::commands::error::CommandError; +use crate::commands::{CleanFlags, error::CleanError}; + +pub struct Clean; + +pub struct DeletedItems(Vec); + +impl Clean { + pub fn run( + config: BuildConfig, + additional_flags: CleanFlags, + config_path: impl AsRef, + ) -> Result<(), CommandError> { + let deleted_files = Self::delete_files(config, additional_flags, config_path)?; + + println!("Deleted files: {deleted_files}"); + + Ok(()) + } + + fn delete_files( + config: BuildConfig, + additional_flags: CleanFlags, + smplx_toml_path: impl AsRef, + ) -> Result { + let mut deleted_items = Vec::with_capacity(2); + let generated_artifacts = Self::remove_artifacts(config)?; + + if let Some(artifacts_dir) = generated_artifacts { + deleted_items.push(artifacts_dir); + } + + if additional_flags.all { + let simplex_toml_path = Self::remove_config(smplx_toml_path)?; + + if let Some(simplex_toml) = simplex_toml_path { + deleted_items.push(simplex_toml); + } + } + + Ok(DeletedItems(deleted_items)) + } + + fn remove_artifacts(config: BuildConfig) -> Result, CleanError> { + let output_dir = ArtifactsResolver::resolve_local_dir(&config.out_dir) + .map_err(|e| CleanError::ResolveOutDir(e.to_string()))?; + + let res = if output_dir.exists() { + fs::remove_dir_all(&output_dir).map_err(|e| CleanError::RemoveOutDir(e, output_dir.to_path_buf()))?; + Some(output_dir) + } else { + None + }; + + Ok(res) + } + + fn remove_config(config_path: impl AsRef) -> Result, CleanError> { + let config_path = config_path.as_ref().to_path_buf(); + + if config_path.exists() && config_path.is_file() { + fs::remove_file(&config_path).map_err(|e| CleanError::RemoveFile(e, config_path.to_path_buf()))?; + + Ok(Some(config_path)) + } else { + Ok(None) + } + } +} + +impl Display for DeletedItems { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let paths_len = self.0.len(); + let mut result = String::from("["); + + for (index, path) in self.0.iter().enumerate() { + result.push_str(&format!("\n\t{}", path.display())); + + if index < paths_len - 1 { + result.push(','); + } + result.push('\n'); + } + + result.push(']'); + + write!(f, "{}", result) + } +} diff --git a/crates/cli/src/commands/core.rs b/crates/cli/src/commands/core.rs index 1e59c70..2ff8f51 100644 --- a/crates/cli/src/commands/core.rs +++ b/crates/cli/src/commands/core.rs @@ -2,19 +2,27 @@ use clap::{Args, Subcommand}; #[derive(Debug, Subcommand)] pub enum Command { - /// Initializes the Simplex project (TODO) - Init, - /// Prints the current Simplex config in use + /// Initializes Simplex project + Init { + #[command(flatten)] + additional_flags: InitFlags, + }, + /// Prints current Simplex config in use Config, /// Spins up the local Electrs + Elements regtest Regtest, - /// Runs the Simplex tests + /// Runs Simplex tests Test { #[command(subcommand)] command: TestCommand, }, /// Generates the simplicity contracts artifacts Build, + /// Clean Simplex artifacts in the current directory + Clean { + #[command(flatten)] + additional_flags: CleanFlags, + }, } #[derive(Debug, Subcommand)] @@ -46,3 +54,17 @@ pub struct TestFlags { #[arg(long)] pub ignored: bool, } + +#[derive(Debug, Args, Copy, Clone)] +pub struct InitFlags { + /// Generate a draft Rust library instead of just `Simplex.toml` + #[arg(long)] + pub lib: bool, +} + +#[derive(Debug, Args, Copy, Clone)] +pub struct CleanFlags { + /// Remove `Simplex.toml` as well + #[arg(long)] + pub all: bool, +} diff --git a/crates/cli/src/commands/error.rs b/crates/cli/src/commands/error.rs index 6cadf5f..38e27c1 100644 --- a/crates/cli/src/commands/error.rs +++ b/crates/cli/src/commands/error.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + #[derive(thiserror::Error, Debug)] pub enum CommandError { #[error(transparent)] @@ -12,6 +14,51 @@ pub enum CommandError { #[error(transparent)] Build(#[from] smplx_build::error::BuildError), + #[error(transparent)] + Init(#[from] InitError), + + #[error(transparent)] + Clean(#[from] CleanError), + #[error("IO error: {0}")] Io(#[from] std::io::Error), } + +#[derive(thiserror::Error, Debug)] +pub enum InitError { + #[error("Failed to open file '{1}': {0}")] + OpenFile(std::io::Error, PathBuf), + + #[error("Failed to write to file '{1}': {0}")] + WriteToFile(std::io::Error, PathBuf), + + #[error("Failed to format file with rustfmt: {0}")] + FmtError(std::io::Error), + + #[error("Failed to resolve parent directory for: {0}")] + ResolveParent(PathBuf), + + #[error("Failed to create directories at '{1}': {0}")] + CreateDirs(std::io::Error, PathBuf), + + #[error("Failed to fetch crate info from crates.io: {0}")] + CratesIoFetch(String), + + #[error("Cannot auto-detect package name from path: {0}")] + PackageName(PathBuf), + + #[error("Cannot create package with a non-unicode name: '{0}'")] + NonUnicodeName(String), +} + +#[derive(thiserror::Error, Debug)] +pub enum CleanError { + #[error("Failed to resolve out_dir from config, err: '{0}'")] + ResolveOutDir(String), + + #[error("Failed to remove output directory '{1}': {0}")] + RemoveOutDir(std::io::Error, PathBuf), + + #[error("Failed to remove file '{1}': {0}")] + RemoveFile(std::io::Error, PathBuf), +} diff --git a/crates/cli/src/commands/init.rs b/crates/cli/src/commands/init.rs new file mode 100644 index 0000000..97865e2 --- /dev/null +++ b/crates/cli/src/commands/init.rs @@ -0,0 +1,153 @@ +use std::{fs, fs::OpenOptions, io::Write, path::Path}; + +use crate::commands::error::CommandError; +use crate::commands::{InitFlags, error::InitError}; +use crate::config::INIT_CONFIG; + +pub const SIMPLEX_CRATE_NAME: &str = "smplx-std"; + +pub struct Init; + +impl Init { + pub fn run(conf: InitFlags, smplx_conf_path: impl AsRef) -> Result<(), CommandError> { + if conf.lib { + Self::generate_lib_inplace(&smplx_conf_path)? + } + + Self::fill_simplex_toml(smplx_conf_path)?; + + Ok(()) + } + + fn fill_simplex_toml(config_path: impl AsRef) -> Result<(), InitError> { + let path_to_write = config_path.as_ref(); + Self::write_to_file(path_to_write, INIT_CONFIG)?; + + println!("Config written to: '{}'", path_to_write.display()); + + Ok(()) + } + + fn generate_lib_inplace(config_path: impl AsRef) -> Result<(), InitError> { + let pwd = config_path.as_ref().parent().unwrap(); + let name = Self::get_project_name(pwd)?; + + // Create `Cargo.toml` file + let manifest = { + let mut manifest = toml_edit::DocumentMut::new(); + manifest["package"] = toml_edit::Item::Table(toml_edit::Table::new()); + manifest["package"]["name"] = toml_edit::value(name); + manifest["package"]["version"] = toml_edit::value("0.1.0"); + manifest["package"]["edition"] = toml_edit::value("2024"); + + let mut dep_table = toml_edit::Table::default(); + dep_table.insert( + SIMPLEX_CRATE_NAME, + toml_edit::Item::Value(toml_edit::Value::String(toml_edit::Formatted::new( + Self::get_smplx_max_version()?, + ))), + ); + + manifest["dependencies"] = toml_edit::Item::Table(dep_table); + manifest + }; + + let default_lib_rs_file_content: &[u8] = { b"mod artifacts;" }; + let default_test_file_content: &[u8] = { + b"\ +#[simplex::test] +fn dummy_test(context: simplex::TestContext) { + // your test code here + todo!() +}" + }; + let default_p2pk_simf_file_content: &[u8] = { + b"\ +fn main() { + jet::bip_0340_verify((param::PUBLIC_KEY, jet::sig_all_hash()), witness::SIGNATURE) +}" + }; + let default_gitignore_file_content: &[u8] = { b"src/artifacts" }; + + let manifest_path = pwd.join("Cargo.toml"); + let lib_rs_path = pwd.join("src/lib.rs"); + let p2pk_simf_content = pwd.join("simf/p2pk.simf"); + let test_rs_path = pwd.join("tests/p2pk_test.rs"); + let gitignore_path = pwd.join(".gitignore"); + + Self::write_to_file(manifest_path, manifest.to_string())?; + Self::write_to_file(&lib_rs_path, default_lib_rs_file_content)?; + Self::write_to_file(&test_rs_path, default_test_file_content)?; + Self::write_to_file(&p2pk_simf_content, default_p2pk_simf_file_content)?; + Self::write_to_file(&gitignore_path, default_gitignore_file_content)?; + + Self::execute_cargo_fmt(lib_rs_path)?; + + Ok(()) + } + + fn get_project_name(path: &Path) -> Result<&str, InitError> { + let file_name = path + .file_name() + .ok_or_else(|| InitError::PackageName(path.to_path_buf()))?; + + file_name + .to_str() + .ok_or_else(|| InitError::NonUnicodeName(format!("{file_name:?}"))) + } + + fn get_smplx_max_version() -> Result { + let url = format!("https://crates.io/api/v1/crates/{}", SIMPLEX_CRATE_NAME); + + let response = minreq::get(&url) + .with_header("User-Agent", "simplex_generator") + .send() + .map_err(|e| InitError::CratesIoFetch(format!("Failed to fetch crate info: {}", e)))?; + + let body = response + .as_str() + .map_err(|e| InitError::CratesIoFetch(format!("Invalid response body: {}", e)))?; + + let json: serde_json::Value = + serde_json::from_str(body).map_err(|e| InitError::CratesIoFetch(format!("Failed to parse JSON: {}", e)))?; + + let latest_version = json["crate"]["max_stable_version"] + .as_str() + .ok_or_else(|| InitError::CratesIoFetch("Could not find max_version in response".to_string()))?; + + Ok(latest_version.to_string()) + } + + fn write_to_file(path: impl AsRef, content: impl AsRef<[u8]>) -> Result<(), InitError> { + let path = path.as_ref(); + + fs::create_dir_all( + path.parent() + .ok_or_else(|| InitError::ResolveParent(path.to_path_buf()))?, + ) + .map_err(|e| InitError::CreateDirs(e, path.to_path_buf()))?; + + let mut file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(path) + .map_err(|e| InitError::OpenFile(e, path.to_path_buf()))?; + file.write_all(content.as_ref()) + .map_err(|e| InitError::WriteToFile(e, path.to_path_buf()))?; + file.flush() + .map_err(|e| InitError::WriteToFile(e, path.to_path_buf()))?; + + Ok(()) + } + + fn execute_cargo_fmt(file: impl AsRef) -> Result<(), InitError> { + let mut cargo_test_command = std::process::Command::new("sh"); + + cargo_test_command.args(["-c".to_string(), format!("rustfmt {}", file.as_ref().display())]); + + let _output = cargo_test_command.output().map_err(InitError::FmtError); + + Ok(()) + } +} diff --git a/crates/cli/src/commands/mod.rs b/crates/cli/src/commands/mod.rs index 81faac2..50cb5fb 100644 --- a/crates/cli/src/commands/mod.rs +++ b/crates/cli/src/commands/mod.rs @@ -1,6 +1,8 @@ pub mod build; +pub mod clean; pub mod core; pub mod error; +pub mod init; pub mod regtest; pub mod test; diff --git a/crates/cli/src/config/core.rs b/crates/cli/src/config/core.rs index e03b6a3..14caf75 100644 --- a/crates/cli/src/config/core.rs +++ b/crates/cli/src/config/core.rs @@ -7,7 +7,7 @@ use smplx_test::TestConfig; use super::error::ConfigError; -pub const INIT_CONFIG: &str = include_str!("../../Simplex.default.toml"); +pub const INIT_CONFIG: &str = include_str!("../../assets/Simplex.default.toml"); pub const CONFIG_FILENAME: &str = "Simplex.toml"; #[derive(Debug, Default, Clone, Deserialize)] @@ -20,9 +20,11 @@ pub struct Config { impl Config { pub fn get_default_path() -> Result { - let cwd = std::env::current_dir()?; + Self::get_path(std::env::current_dir()?) + } - Ok(cwd.join(CONFIG_FILENAME)) + pub fn get_path(path: impl AsRef) -> Result { + Ok(path.as_ref().join(CONFIG_FILENAME)) } pub fn load(path_buf: impl AsRef) -> Result { From 230be3dce30f6af216cc39d5a619372708bcb701 Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Mon, 23 Mar 2026 17:14:27 +0200 Subject: [PATCH 11/13] versions --- CHANGELOG.md | 14 ++++++++++++-- Cargo.lock | 14 +++++++------- Cargo.toml | 12 ++++++------ crates/build/Cargo.toml | 2 +- crates/cli/Cargo.toml | 2 +- crates/macros/Cargo.toml | 2 +- crates/regtest/Cargo.toml | 2 +- crates/sdk/Cargo.toml | 2 +- crates/sdk/src/transaction/partial_input.rs | 2 +- crates/simplex/Cargo.toml | 2 +- crates/test/Cargo.toml | 2 +- simplexup/simplexup | 2 +- 12 files changed, 34 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aabcc9f..b08c67f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog -## [unreleased] +## [0.0.2] -- Simplex MVP is in progress. +- Implemented `simplex init` and `simplex clean` commands. +- Added "initial signer bitcoins" to the Simplex configuration. +- Added `fetch_tip_height` and `fetch_tip_timestamp` methods to the providers. +- Added clippy check to CI. +- Fixed regtest not accepting transactions with multiple OP_RETURNs. +- Added `send` method to the signer to be able to quickly send a policy asset. +- Extended `get_wpkh_utxos` method to be able to filter signer's UTXOs on the fly. + +## [0.0.1] + +- Initial Simplex release! diff --git a/Cargo.lock b/Cargo.lock index 6f459ed..73cb02e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1290,7 +1290,7 @@ dependencies = [ [[package]] name = "smplx-build" -version = "0.0.1" +version = "0.0.2" dependencies = [ "glob", "globwalk", @@ -1307,7 +1307,7 @@ dependencies = [ [[package]] name = "smplx-cli" -version = "0.0.1" +version = "0.0.2" dependencies = [ "anyhow", "clap", @@ -1328,7 +1328,7 @@ dependencies = [ [[package]] name = "smplx-macros" -version = "0.0.1" +version = "0.0.2" dependencies = [ "smplx-build", "smplx-test", @@ -1337,7 +1337,7 @@ dependencies = [ [[package]] name = "smplx-regtest" -version = "0.0.1" +version = "0.0.2" dependencies = [ "electrsd", "serde", @@ -1348,7 +1348,7 @@ dependencies = [ [[package]] name = "smplx-sdk" -version = "0.0.1" +version = "0.0.2" dependencies = [ "bip39", "bitcoin_hashes", @@ -1366,7 +1366,7 @@ dependencies = [ [[package]] name = "smplx-std" -version = "0.0.1" +version = "0.0.2" dependencies = [ "either", "serde", @@ -1379,7 +1379,7 @@ dependencies = [ [[package]] name = "smplx-test" -version = "0.0.1" +version = "0.0.2" dependencies = [ "electrsd", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index de0cf06..c0e910c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,12 +13,12 @@ edition = "2024" multiple_crate_versions = "allow" [workspace.dependencies] -smplx-macros = { path = "./crates/macros", version = "0.0.1" } -smplx-build = { path = "./crates/build", version = "0.0.1" } -smplx-test = { path = "./crates/test", version = "0.0.1" } -smplx-regtest = { path = "./crates/regtest", version = "0.0.1" } -smplx-sdk = { path = "./crates/sdk", version = "0.0.1" } -smplx-std = { path = "./crates/simplex", version = "0.0.1" } +smplx-macros = { path = "./crates/macros", version = "0.0.2" } +smplx-build = { path = "./crates/build", version = "0.0.2" } +smplx-test = { path = "./crates/test", version = "0.0.2" } +smplx-regtest = { path = "./crates/regtest", version = "0.0.2" } +smplx-sdk = { path = "./crates/sdk", version = "0.0.2" } +smplx-std = { path = "./crates/simplex", version = "0.0.2" } serde = { version = "1.0.228", features = ["derive"]} hex = { version = "0.4.3" } diff --git a/crates/build/Cargo.toml b/crates/build/Cargo.toml index d6a8bf4..c2a1389 100644 --- a/crates/build/Cargo.toml +++ b/crates/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smplx-build" -version = "0.0.1" +version = "0.0.2" description = "Simplex build command internal implementation" license.workspace = true edition.workspace = true diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index a96e5f8..6fd3227 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smplx-cli" -version = "0.0.1" +version = "0.0.2" description = "Simplex cli with various utilities to manage a simplicity project" license.workspace = true edition.workspace = true diff --git a/crates/macros/Cargo.toml b/crates/macros/Cargo.toml index 2ef7e74..6f2b2e1 100644 --- a/crates/macros/Cargo.toml +++ b/crates/macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smplx-macros" -version = "0.0.1" +version = "0.0.2" description = "Simplex macros re-export package" license.workspace = true edition.workspace = true diff --git a/crates/regtest/Cargo.toml b/crates/regtest/Cargo.toml index 0007018..ae489f3 100644 --- a/crates/regtest/Cargo.toml +++ b/crates/regtest/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smplx-regtest" -version = "0.0.1" +version = "0.0.2" description = "Simplex regtest command internal implementation" license.workspace = true edition.workspace = true diff --git a/crates/sdk/Cargo.toml b/crates/sdk/Cargo.toml index 59428ef..9cf0aae 100644 --- a/crates/sdk/Cargo.toml +++ b/crates/sdk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smplx-sdk" -version = "0.0.1" +version = "0.0.2" description = "Simplex sdk to simplify the development with simplicity" license.workspace = true edition.workspace = true diff --git a/crates/sdk/src/transaction/partial_input.rs b/crates/sdk/src/transaction/partial_input.rs index b68fa7c..0cc6b53 100644 --- a/crates/sdk/src/transaction/partial_input.rs +++ b/crates/sdk/src/transaction/partial_input.rs @@ -5,7 +5,7 @@ use simplicityhl::elements::{AssetId, OutPoint, Sequence, TxOut, Txid}; use crate::program::ProgramTrait; use crate::program::WitnessTrait; -#[derive(Clone)] +#[derive(Debug, Clone)] pub enum RequiredSignature { None, NativeEcdsa, diff --git a/crates/simplex/Cargo.toml b/crates/simplex/Cargo.toml index 4f3d308..f16e06e 100644 --- a/crates/simplex/Cargo.toml +++ b/crates/simplex/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smplx-std" -version = "0.0.1" +version = "0.0.2" description = "A blazingly-fast, ux-first simplicity development framework" license.workspace = true edition.workspace = true diff --git a/crates/test/Cargo.toml b/crates/test/Cargo.toml index e76586c..1d643e6 100644 --- a/crates/test/Cargo.toml +++ b/crates/test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smplx-test" -version = "0.0.1" +version = "0.0.2" description = "Simplex test command internal implementation" license.workspace = true edition.workspace = true diff --git a/simplexup/simplexup b/simplexup/simplexup index 923c0ea..369701d 100755 --- a/simplexup/simplexup +++ b/simplexup/simplexup @@ -3,7 +3,7 @@ set -eo pipefail # NOTE: if you make modifications to this script, please increment the version number. # WARNING: the SemVer pattern: major.minor.patch must be followed as we use it to determine if the script is up to date. -SIMPLEXUP_INSTALLER_VERSION="0.0.1" +SIMPLEXUP_INSTALLER_VERSION="0.0.2" BASE_DIR=${XDG_CONFIG_HOME:-$HOME} SIMPLEX_DIR=${SIMPLEX_DIR:-"$BASE_DIR/.simplex"} From ecb93e2ccee22c60065802c387c040683d2a7906 Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Mon, 23 Mar 2026 18:25:33 +0200 Subject: [PATCH 12/13] fix init --- crates/cli/assets/Simplex.default.toml | 2 +- crates/cli/src/commands/clean.rs | 5 +++-- crates/cli/src/commands/init.rs | 10 ++++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/crates/cli/assets/Simplex.default.toml b/crates/cli/assets/Simplex.default.toml index dbd76d7..6d52346 100644 --- a/crates/cli/assets/Simplex.default.toml +++ b/crates/cli/assets/Simplex.default.toml @@ -1,4 +1,4 @@ -# TEST CONFIG +# DEFAULT CONFIG # [build] # src_dir = "./simf" diff --git a/crates/cli/src/commands/clean.rs b/crates/cli/src/commands/clean.rs index 220b246..60ef63e 100644 --- a/crates/cli/src/commands/clean.rs +++ b/crates/cli/src/commands/clean.rs @@ -82,12 +82,13 @@ impl Display for DeletedItems { let mut result = String::from("["); for (index, path) in self.0.iter().enumerate() { - result.push_str(&format!("\n\t{}", path.display())); + result.push_str(&format!("\n {}", path.display())); if index < paths_len - 1 { result.push(','); + } else { + result.push('\n'); } - result.push('\n'); } result.push(']'); diff --git a/crates/cli/src/commands/init.rs b/crates/cli/src/commands/init.rs index 97865e2..fc8fa1e 100644 --- a/crates/cli/src/commands/init.rs +++ b/crates/cli/src/commands/init.rs @@ -52,7 +52,7 @@ impl Init { manifest }; - let default_lib_rs_file_content: &[u8] = { b"mod artifacts;" }; + let default_lib_rs_file_content: &[u8] = { b"pub mod artifacts;" }; let default_test_file_content: &[u8] = { b"\ #[simplex::test] @@ -86,14 +86,16 @@ fn main() { Ok(()) } - fn get_project_name(path: &Path) -> Result<&str, InitError> { + fn get_project_name(path: &Path) -> Result { let file_name = path .file_name() .ok_or_else(|| InitError::PackageName(path.to_path_buf()))?; - file_name + let file_name = file_name .to_str() - .ok_or_else(|| InitError::NonUnicodeName(format!("{file_name:?}"))) + .ok_or_else(|| InitError::NonUnicodeName(format!("{file_name:?}")))?; + + Ok(format!("simplex_{}", file_name)) } fn get_smplx_max_version() -> Result { From 2885f626f8595631667dd8087ce13a4e00f78836 Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Mon, 23 Mar 2026 18:33:15 +0200 Subject: [PATCH 13/13] remove --all flag --- crates/cli/src/cli.rs | 4 +-- crates/cli/src/commands/clean.rs | 44 +++++--------------------------- crates/cli/src/commands/core.rs | 12 +-------- 3 files changed, 9 insertions(+), 51 deletions(-) diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index ad0d15d..ff44693 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -55,11 +55,11 @@ impl Cli { Ok(Build::run(loaded_config.build)?) } - Command::Clean { additional_flags } => { + Command::Clean => { let config_path = Config::get_default_path()?; let loaded_config = Config::load(&config_path)?; - Ok(Clean::run(loaded_config.build, *additional_flags, config_path)?) + Ok(Clean::run(loaded_config.build)?) } } } diff --git a/crates/cli/src/commands/clean.rs b/crates/cli/src/commands/clean.rs index 60ef63e..586def0 100644 --- a/crates/cli/src/commands/clean.rs +++ b/crates/cli/src/commands/clean.rs @@ -1,51 +1,31 @@ -use std::{ - fmt::Display, - fs, - path::{Path, PathBuf}, -}; +use std::{fmt::Display, fs, path::PathBuf}; use smplx_build::{ArtifactsResolver, BuildConfig}; +use crate::commands::error::CleanError; use crate::commands::error::CommandError; -use crate::commands::{CleanFlags, error::CleanError}; pub struct Clean; pub struct DeletedItems(Vec); impl Clean { - pub fn run( - config: BuildConfig, - additional_flags: CleanFlags, - config_path: impl AsRef, - ) -> Result<(), CommandError> { - let deleted_files = Self::delete_files(config, additional_flags, config_path)?; + pub fn run(config: BuildConfig) -> Result<(), CommandError> { + let deleted_files = Self::delete_files(config)?; println!("Deleted files: {deleted_files}"); Ok(()) } - fn delete_files( - config: BuildConfig, - additional_flags: CleanFlags, - smplx_toml_path: impl AsRef, - ) -> Result { - let mut deleted_items = Vec::with_capacity(2); + fn delete_files(config: BuildConfig) -> Result { + let mut deleted_items = Vec::with_capacity(1); let generated_artifacts = Self::remove_artifacts(config)?; if let Some(artifacts_dir) = generated_artifacts { deleted_items.push(artifacts_dir); } - if additional_flags.all { - let simplex_toml_path = Self::remove_config(smplx_toml_path)?; - - if let Some(simplex_toml) = simplex_toml_path { - deleted_items.push(simplex_toml); - } - } - Ok(DeletedItems(deleted_items)) } @@ -62,18 +42,6 @@ impl Clean { Ok(res) } - - fn remove_config(config_path: impl AsRef) -> Result, CleanError> { - let config_path = config_path.as_ref().to_path_buf(); - - if config_path.exists() && config_path.is_file() { - fs::remove_file(&config_path).map_err(|e| CleanError::RemoveFile(e, config_path.to_path_buf()))?; - - Ok(Some(config_path)) - } else { - Ok(None) - } - } } impl Display for DeletedItems { diff --git a/crates/cli/src/commands/core.rs b/crates/cli/src/commands/core.rs index 2ff8f51..1d84676 100644 --- a/crates/cli/src/commands/core.rs +++ b/crates/cli/src/commands/core.rs @@ -19,10 +19,7 @@ pub enum Command { /// Generates the simplicity contracts artifacts Build, /// Clean Simplex artifacts in the current directory - Clean { - #[command(flatten)] - additional_flags: CleanFlags, - }, + Clean, } #[derive(Debug, Subcommand)] @@ -61,10 +58,3 @@ pub struct InitFlags { #[arg(long)] pub lib: bool, } - -#[derive(Debug, Args, Copy, Clone)] -pub struct CleanFlags { - /// Remove `Simplex.toml` as well - #[arg(long)] - pub all: bool, -}