Skip to content
Closed
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
72 changes: 72 additions & 0 deletions src/commands/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ pub enum ContractCommands {
Invoke(InvokeArgs),
/// Inspect a deployed Soroban contract instance
Inspect(InspectArgs),
/// Upload a WASM binary to the Stellar network (upload-only step)
///
/// See: https://developers.stellar.org/docs/build/smart-contracts/getting-started/deploy-increment-contract
Upload(UploadArgs),
}

#[derive(Args)]
Expand Down Expand Up @@ -43,10 +47,24 @@ pub struct InspectArgs {
pub network: Option<String>,
}

#[derive(Args)]
pub struct UploadArgs {
/// Path to the compiled WASM file
#[arg(long)]
pub wasm: String,
/// Network to use
#[arg(long, default_value = "testnet", value_parser = ["testnet", "mainnet"])]
pub network: String,
/// Wallet name to use for signing
#[arg(long)]
pub wallet: Option<String>,
}

pub fn handle(cmd: ContractCommands) -> Result<()> {
match cmd {
ContractCommands::Invoke(args) => handle_invoke(args),
ContractCommands::Inspect(args) => handle_inspect(args),
ContractCommands::Upload(args) => handle_upload(args),
}
}

Expand Down Expand Up @@ -262,6 +280,60 @@ fn handle_invoke(args: InvokeArgs) -> Result<()> {
Ok(())
}

fn handle_upload(args: UploadArgs) -> Result<()> {
config::validate_network(&args.network)?;

p::header("Upload WASM to Stellar Network");
p::separator();
p::kv("WASM", &args.wasm);
p::kv("Network", &args.network);

if args.network == "mainnet" {
p::warn("You are uploading on MAINNET. This will cost real XLM.");
}

let cfg = config::load()?;
let wallet = if let Some(ref name) = args.wallet {
cfg.wallets
.iter()
.find(|w| &w.name == name)
.ok_or_else(|| {
anyhow::anyhow!("Wallet '{}' not found. Run `starforge wallet list`", name)
})?
.clone()
} else if !cfg.wallets.is_empty() {
p::info(&format!(
"No --wallet specified. Using: {}",
cfg.wallets[0].name.cyan()
));
cfg.wallets[0].clone()
} else {
anyhow::bail!(
"No wallets found. Create one first:\n starforge wallet create deployer --fund"
);
};

p::kv("Wallet", &wallet.name);
p::separator();

println!();
p::step(1, 1, "Uploading WASM binary…");

let wasm_hash = soroban::upload_wasm(&args.wasm, &args.network, &wallet)?;

println!();
p::kv_accent("WASM Hash", &wasm_hash);
p::success("WASM uploaded successfully.");
println!();
p::info("Next step — create the contract instance:");
p::info(&format!(
" stellar contract deploy --wasm-hash {} --network {} --source {}",
wasm_hash, args.network, wallet.name
));
println!();
Ok(())
}

fn resolve_network(network_override: Option<String>) -> Result<String> {
let network = network_override.unwrap_or(config::load()?.network);
match network.as_str() {
Expand Down
38 changes: 38 additions & 0 deletions src/utils/soroban.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,44 @@ pub fn submit_transaction(
Ok(TransactionResult { hash, return_value })
}

pub fn upload_wasm(
wasm_path: &str,
network: &str,
wallet: &crate::utils::config::WalletEntry,
) -> Result<String> {
use std::process::Command;

let rpc_url = get_rpc_url(network);

let output = Command::new("stellar")
.args([
"contract",
"upload",
"--wasm",
wasm_path,
"--rpc-url",
&rpc_url,
"--source",
&wallet.name,
"--network-passphrase",
if network == "mainnet" {
"Public Global Stellar Network ; September 2015"
} else {
"Test SDF Network ; September 2015"
},
])
.output()
.context("Failed to run `stellar contract upload`. Is the Stellar CLI installed?")?;

if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
anyhow::bail!("WASM upload failed: {}", stderr.trim());
}

let wasm_hash = String::from_utf8_lossy(&output.stdout).trim().to_string();
Ok(wasm_hash)
}

pub fn inspect_contract(contract_id: &str, network: &str) -> Result<ContractInspectResult> {
let ledger_key = build_contract_instance_key(contract_id)?;
let ledger_key_xdr = ledger_key_to_xdr_base64(&ledger_key)?;
Expand Down