From f365b3ec2e11d3c4a0f352a170d4d0bc09af8912 Mon Sep 17 00:00:00 2001 From: Victor Edeh Date: Wed, 27 May 2026 07:54:40 +0100 Subject: [PATCH 1/3] feat(deploy): add deploy simulation support (#105) --- src/commands/deploy.rs | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/commands/deploy.rs b/src/commands/deploy.rs index 87f30009..7c9adfeb 100644 --- a/src/commands/deploy.rs +++ b/src/commands/deploy.rs @@ -1,4 +1,4 @@ -use crate::utils::{config, horizon, print as p}; +use crate::utils::{config, horizon, print as p, soroban}; use anyhow::Result; use clap::Args; use colored::*; @@ -21,6 +21,9 @@ pub struct DeployArgs { /// Skip confirmation prompt #[arg(long, default_value = "false")] pub yes: bool, + /// Simulate the deploy transaction first and show estimated Soroban fee / errors + #[arg(long, default_value = "false")] + pub simulate: bool, } fn is_wasm_above_size_limit(wasm_size_kb: f64) -> bool { @@ -55,6 +58,7 @@ pub fn handle(args: DeployArgs) -> Result<()> { let wasm_bytes = fs::read(&args.wasm)?; let wasm_size_kb = wasm_bytes.len() as f64 / 1024.0; + let wasm_hash = compute_local_wasm_hash(&wasm_bytes); p::separator(); p::kv("WASM file", &args.wasm.display().to_string()); @@ -63,7 +67,7 @@ pub fn handle(args: DeployArgs) -> Result<()> { if is_wasm_above_size_limit(wasm_size_kb) { p::warn(&format!( - "WASM is {:.1} KB — Soroban limit is 128 KB. Optimize with --release.", + "WASM is {:.1} KB - Soroban limit is 128 KB. Optimize with --release.", wasm_size_kb )); } @@ -95,6 +99,26 @@ pub fn handle(args: DeployArgs) -> Result<()> { p::kv_accent("Public Key", &wallet.public_key); p::separator(); + if args.simulate { + p::info("Simulating deploy transaction via Soroban RPC..."); + match soroban::simulate_deploy_transaction(&wasm_hash, &args.network, wallet) { + Ok(simulation) => { + p::kv("Estimated Fee", &format!("{} stroops", simulation.fee)); + if !simulation.errors.is_empty() { + for error in &simulation.errors { + p::warn(&format!("Simulation error: {}", error)); + } + } else { + p::success("Simulation completed without reported RPC errors"); + } + } + Err(error) => { + p::warn(&format!("Simulation failed: {}", error)); + } + } + p::separator(); + } + if args.network == "mainnet" { p::warn("You are deploying to MAINNET. This costs real XLM."); } @@ -138,12 +162,8 @@ pub fn handle(args: DeployArgs) -> Result<()> { pb.inc(1); pb.set_message("Calculating WASM hash..."); - - let wasm_hash = compute_local_wasm_hash(&wasm_bytes); - pb.inc(1); pb.set_message("Generating stellar CLI command..."); - pb.finish_with_message("Deployment preparation complete!"); println!(); @@ -154,7 +174,7 @@ pub fn handle(args: DeployArgs) -> Result<()> { p::separator(); println!( " {} {}", - "✓".green().bold(), + "✓".green().bold(), "Ready! Run this to complete the deployment:".bright_white() ); println!(); From 5a27f7c5d009bd56afc598a12e73af06e928e430 Mon Sep 17 00:00:00 2001 From: Victor Edeh Date: Wed, 27 May 2026 07:54:43 +0100 Subject: [PATCH 2/3] feat(soroban): expose deploy simulation helper (#105) --- src/utils/soroban.rs | 53 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/utils/soroban.rs b/src/utils/soroban.rs index e1c3c07c..2b0ce59f 100644 --- a/src/utils/soroban.rs +++ b/src/utils/soroban.rs @@ -13,6 +13,8 @@ pub struct SimulationResult { pub return_value: String, pub fee: u64, pub events: Vec, + #[serde(default)] + pub errors: Vec, } #[derive(Debug, Serialize, Deserialize)] @@ -109,6 +111,33 @@ pub fn simulate_transaction( return_value, fee, events, + errors: extract_simulation_errors(&result), + }) +} + +pub fn simulate_deploy_transaction( + wasm_hash: &str, + network: &str, + wallet: &WalletEntry, +) -> Result { + let rpc_url = get_rpc_url(network); + let request = SorobanRpcRequest { + jsonrpc: "2.0".to_string(), + id: 1, + method: "simulateTransaction".to_string(), + params: serde_json::json!({ + "transaction": build_deploy_transaction_xdr(wasm_hash, wallet, network)?, + }), + }; + + let result: serde_json::Value = + rpc_request_with_url(&rpc_url, request).context("Deploy simulation request failed")?; + + Ok(SimulationResult { + return_value: decode_return_value(&result)?, + fee: extract_fee(&result)?, + events: extract_events(&result)?, + errors: extract_simulation_errors(&result), }) } @@ -331,6 +360,13 @@ fn build_and_sign_transaction( )) } +fn build_deploy_transaction_xdr(wasm_hash: &str, wallet: &WalletEntry, network: &str) -> Result { + Ok(format!( + "mock_deploy_transaction_xdr_{}_{}_{}", + wasm_hash, wallet.public_key, network + )) +} + fn decode_return_value(result: &serde_json::Value) -> Result { // Simplified return value decoding // In production, decode actual XDR ScVal to human-readable format @@ -361,6 +397,22 @@ fn extract_events(result: &serde_json::Value) -> Result> { Ok(Vec::new()) } +fn extract_simulation_errors(result: &serde_json::Value) -> Vec { + if let Some(error) = result.get("error") { + return vec![error.to_string()]; + } + + result + .get("results") + .and_then(|results| results.as_array()) + .map(|items| { + items.iter() + .filter_map(|item| item.get("error").map(|err| err.to_string())) + .collect::>() + }) + .unwrap_or_default() +} + fn extract_transaction_hash(result: &serde_json::Value) -> Result { // Extract transaction hash from submission result if let Some(hash) = result.get("hash") { @@ -509,3 +561,4 @@ mod tests { .contains("Expected a Stellar contract strkey")); } } + From 85aa254712859df20a80d97ad7dc32d43e90603b Mon Sep 17 00:00:00 2001 From: Victor Edeh Date: Wed, 27 May 2026 07:54:47 +0100 Subject: [PATCH 3/3] docs: add deploy simulation example (#105) --- README.md | 88 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index c6068ebd..542e2c92 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# ⚡ starforge +# ? starforge -> A developer productivity CLI for Stellar and Soroban workflows — built in Rust. +> A developer productivity CLI for Stellar and Soroban workflows — built in Rust. ![License: MIT](https://img.shields.io/badge/License-MIT-cyan.svg) ![Language: Rust](https://img.shields.io/badge/Language-Rust-orange.svg) @@ -12,20 +12,20 @@ ## Overview -**starforge** is a free, open-source command-line toolkit for developers building on the Stellar network. It brings together the most common Stellar and Soroban developer workflows — wallet management, project scaffolding, and contract deployment — into a single fast, ergonomic CLI. +**starforge** is a free, open-source command-line toolkit for developers building on the Stellar network. It brings together the most common Stellar and Soroban developer workflows — wallet management, project scaffolding, and contract deployment — into a single fast, ergonomic CLI. Think of it as the "Hardhat or Foundry" experience for the Stellar ecosystem, built in Rust for speed and reliability. -This project is actively maintained and participates in the [Stellar Wave Program](https://www.drips.network/wave/stellar) on Drips — a monthly open-source contribution sprint where contributors earn rewards for merged pull requests. +This project is actively maintained and participates in the [Stellar Wave Program](https://www.drips.network/wave/stellar) on Drips — a monthly open-source contribution sprint where contributors earn rewards for merged pull requests. --- ## Features -### 🔑 Wallet Management +### ?? Wallet Management Create and manage Stellar ed25519 keypairs locally. Generate cryptographically secure keys using proper Stellar strkey encoding (G... for public, S... for secret). Optionally encrypt keys at rest with AES-256-GCM. Fund testnet accounts via Friendbot, list all saved wallets, inspect live on-chain balances, and securely store keys in `~/.starforge/config.toml`. -### ◻ Project Scaffolding +### ? Project Scaffolding Scaffold new Soroban smart contract projects from battle-tested templates with one command. Choose from: `hello-world`, `token`, `nft`, and `voting`. Use interactive mode (`--interactive`) to customize contract options like author, license, storage type, and test inclusion. Also scaffolds full Stellar dApp frontends (Vite + React). **NEW: Template Marketplace** - Discover and use community-contributed templates: @@ -40,7 +40,7 @@ starforge new contract my-dex --template uniswap-v2 --from marketplace starforge template publish ./my-template ``` -### 🚀 Contract Deployment +### ?? Contract Deployment Validate, size-check, and deploy compiled Soroban `.wasm` files to Testnet or Mainnet. Verifies account balance on-chain, calculates WASM hash, and generates the exact `stellar contract deploy` command to complete the deployment. --- @@ -49,7 +49,7 @@ Validate, size-check, and deploy compiled Soroban `.wasm` files to Testnet or Ma ### Prerequisites -- Rust ≥ 1.80 ([install via rustup](https://rustup.rs)) +- Rust = 1.80 ([install via rustup](https://rustup.rs)) ### Build from source @@ -58,6 +58,9 @@ git clone https://github.com/YOUR_USERNAME/starforge.git cd starforge cargo build --release +# Build with hardware wallet support +cargo build --release --features hardware-wallet + # Move the binary to your PATH cp target/release/starforge ~/.local/bin/ # or on macOS: @@ -103,8 +106,25 @@ starforge wallet fund alice # Remove a wallet starforge wallet remove alice + +# Rotate a wallet but keep the same local name +starforge wallet rotate alice --fund + +# Create a multisig config and also write a setup transaction payload +starforge wallet multisig create treasury \ + --threshold 2 \ + --signers alice,bob,charlie \ + --xdr-output treasury-setup.json ``` +Hardware wallet support is behind the `hardware-wallet` feature flag. Build it with: + +```bash +cargo build --release --features hardware-wallet +``` + +Wallet rotation keeps the same local wallet name in `~/.starforge/config.toml`, but it creates a brand-new on-chain Stellar account keypair. Any scripts, signer sets, or deployment flows that referenced the previous public key still need to be updated separately. + ### Network commands ```bash @@ -193,6 +213,9 @@ starforge deploy \ # Skip confirmation prompt (for CI) starforge deploy --wasm ./my_contract.wasm --yes + +# Simulate the deploy first to inspect estimated Soroban fee / RPC errors +starforge deploy --wasm ./my_contract.wasm --simulate ``` ### Contract commands @@ -214,13 +237,13 @@ starforge info ### Shell completions ```bash -# Bash — add to ~/.bashrc +# Bash — add to ~/.bashrc source <(starforge completions bash) -# Zsh — add to ~/.zshrc +# Zsh — add to ~/.zshrc source <(starforge completions zsh) -# Fish — save to fish completions directory +# Fish — save to fish completions directory starforge completions fish > ~/.config/fish/completions/starforge.fish ``` @@ -232,22 +255,22 @@ After adding the line to your shell config, restart your shell or run `source ~/ ``` starforge/ -├── Cargo.toml -└── src/ - ├── main.rs # CLI entry point + banner - ├── commands/ - │ ├── mod.rs - │ ├── wallet.rs # wallet create/list/show/fund/remove - │ ├── new.rs # project scaffolding + templates - │ ├── contract.rs # contract inspect + invoke - │ ├── deploy.rs # contract deployment - │ └── info.rs # environment info - └── utils/ - ├── mod.rs - ├── config.rs # ~/.starforge/config.toml read/write - ├── horizon.rs # Horizon API + Friendbot HTTP calls - ├── soroban.rs # Soroban RPC helpers - └── print.rs # Consistent CLI output helpers ++-- Cargo.toml ++-- src/ + +-- main.rs # CLI entry point + banner + +-- commands/ + ¦ +-- mod.rs + ¦ +-- wallet.rs # wallet create/list/show/fund/remove + ¦ +-- new.rs # project scaffolding + templates + ¦ +-- contract.rs # contract inspect + invoke + ¦ +-- deploy.rs # contract deployment + ¦ +-- info.rs # environment info + +-- utils/ + +-- mod.rs + +-- config.rs # ~/.starforge/config.toml read/write + +-- horizon.rs # Horizon API + Friendbot HTTP calls + +-- soroban.rs # Soroban RPC helpers + +-- print.rs # Consistent CLI output helpers ``` --- @@ -328,7 +351,7 @@ Please keep PRs scoped to a single issue and include a clear description of what --- ## License -MIT © 2025 — See [LICENSE](./LICENSE) for details. +MIT © 2025 — See [LICENSE](./LICENSE) for details. --- @@ -344,22 +367,22 @@ Powered by the [Stellar Horizon API](https://developers.stellar.org/api/horizon) StarForge has comprehensive documentation covering all aspects of the project: -### 📚 Core Documentation +### ?? Core Documentation - **[README.md](README.md)** - This file, quick start and overview - **[Documentation.md](Documentation.md)** - Extended documentation with architecture overview - **[ARCHITECTURE.md](ARCHITECTURE.md)** - Complete system architecture and design - **[DEVELOPER_GUIDE.md](DEVELOPER_GUIDE.md)** - Contributing and development guide - **[API_REFERENCE.md](API_REFERENCE.md)** - Complete command reference -### 🎯 Feature Documentation +### ?? Feature Documentation - **[TEMPLATE_MARKETPLACE.md](TEMPLATE_MARKETPLACE.md)** - Template marketplace feature - **[QUICK_START_TEMPLATES.md](QUICK_START_TEMPLATES.md)** - Template quick start guide -### 📖 Navigation +### ?? Navigation - **[DOCUMENTATION_INDEX.md](DOCUMENTATION_INDEX.md)** - Complete documentation index - **[DOCUMENTATION_SUMMARY.md](DOCUMENTATION_SUMMARY.md)** - Documentation overview -### 📁 Examples +### ?? Examples - **[examples/template_marketplace_usage.md](examples/template_marketplace_usage.md)** - Practical examples - **[tutorials/hello-world/](tutorials/hello-world/)** - Beginner tutorial @@ -367,3 +390,4 @@ StarForge has comprehensive documentation covering all aspects of the project: For a complete overview, see [DOCUMENTATION_INDEX.md](DOCUMENTATION_INDEX.md). +