Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 56 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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:
Expand All @@ -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.

---
Expand All @@ -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

Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
```

Expand All @@ -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
```

---
Expand Down Expand Up @@ -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.

---

Expand All @@ -344,26 +367,27 @@ 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

**Total**: 17 documentation files with 7,700+ lines covering architecture, development, API reference, and examples.

For a complete overview, see [DOCUMENTATION_INDEX.md](DOCUMENTATION_INDEX.md).


34 changes: 27 additions & 7 deletions src/commands/deploy.rs
Original file line number Diff line number Diff line change
@@ -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::*;
Expand All @@ -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 {
Expand Down Expand Up @@ -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());
Expand All @@ -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
));
}
Expand Down Expand Up @@ -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.");
}
Expand Down Expand Up @@ -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!();
Expand All @@ -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!();
Expand Down
53 changes: 53 additions & 0 deletions src/utils/soroban.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pub struct SimulationResult {
pub return_value: String,
pub fee: u64,
pub events: Vec<String>,
#[serde(default)]
pub errors: Vec<String>,
}

#[derive(Debug, Serialize, Deserialize)]
Expand Down Expand Up @@ -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<SimulationResult> {
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),
})
}

Expand Down Expand Up @@ -331,6 +360,13 @@ fn build_and_sign_transaction(
))
}

fn build_deploy_transaction_xdr(wasm_hash: &str, wallet: &WalletEntry, network: &str) -> Result<String> {
Ok(format!(
"mock_deploy_transaction_xdr_{}_{}_{}",
wasm_hash, wallet.public_key, network
))
}

fn decode_return_value(result: &serde_json::Value) -> Result<String> {
// Simplified return value decoding
// In production, decode actual XDR ScVal to human-readable format
Expand Down Expand Up @@ -361,6 +397,22 @@ fn extract_events(result: &serde_json::Value) -> Result<Vec<String>> {
Ok(Vec::new())
}

fn extract_simulation_errors(result: &serde_json::Value) -> Vec<String> {
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::<Vec<_>>()
})
.unwrap_or_default()
}

fn extract_transaction_hash(result: &serde_json::Value) -> Result<String> {
// Extract transaction hash from submission result
if let Some(hash) = result.get("hash") {
Expand Down Expand Up @@ -509,3 +561,4 @@ mod tests {
.contains("Expected a Stellar contract strkey"));
}
}

Loading