New: AI Agent SDK is live — integrate Claude-powered wallet actions into your app. Read the docs
GuidesChoosing a Network

Choosing a Network

Stellar offers three networks for development and production. This guide helps you choose the right one for your needs.

Network Comparison Table

AspectFuturenetTestnetMainnet
PurposeLatest featuresDevelopment/TestingProduction
Reset PolicyMonthlyMonthlyNever
Uptime~95%~99%99.95%+
Real XLMNo (free)No (free)Yes (real money)
Use CaseExperimental featuresApp development & QAProduction dApps
FriendbotYesYesNo (must buy XLM)
RPC Endpointsoroban-futurenet.stellar.orgsoroban-testnet.stellar.orgNo public host — paid provider (see Mainnet RPC)
Horizon APIhorizon-futurenet.stellar.orghorizon-testnet.stellar.orghorizon.stellar.org
Network IDStellarFuturenetNetworkTest SDF NetworkPublic Global Stellar
Fee Rate~100 stroops/tx~100 stroops/tx~100 stroops/tx

Futurenet

For: Testing new Soroban features, experimenting with upcoming protocol changes

When to Use Futurenet

  • ✅ Testing cutting-edge Soroban features not yet on Testnet
  • ✅ Experimenting with protocol changes before they reach Testnet
  • ✅ Rapid iteration on contracts
  • ❌ NOT for production
  • ❌ NOT for user-facing features (data resets monthly)

Futurenet Setup

# Environment variables for Futurenet
export STELLAR_NETWORK="futurenet"
export SOROBAN_RPC_HOST="https://soroban-futurenet.stellar.org"
export SOROBAN_RPC_PORT="443"
export HORIZON_URL="https://horizon-futurenet.stellar.org"
export FRIENDBOT_URL="https://friendbot-futurenet.stellar.org"
export NETWORK_PASSPHRASE="StellarFuturenetNetwork"

Get Free Futurenet XLM

# Get XLM via Friendbot
curl "https://friendbot-futurenet.stellar.org/?addr=GBBD47XCZVNQWMFE2IWHHT4AFYK6QKM3ZDMK5GVLL5IXacelași5QNTUVA"

Veil Futurenet Deployment

# Deploy Veil contract to Futurenet
soroban contract deploy \
  --wasm target/wasm32-unknown-unknown/release/veil_wallet.wasm \
  --rpc-url https://soroban-futurenet.stellar.org \
  --network-passphrase "StellarFuturenetNetwork"
⚠️

Futurenet data resets monthly. Never store production data or user funds here. All accounts and contracts are wiped.

Testnet

For: Active development, integration testing, QA before production

When to Use Testnet

  • ✅ Normal app development
  • ✅ Testing wallet features
  • ✅ Integration testing
  • ✅ User acceptance testing (before mainnet)
  • ❌ NOT for production
  • ❌ NOT for long-term data storage (data resets monthly)

Testnet Setup

# Environment variables for Testnet
export STELLAR_NETWORK="testnet"
export SOROBAN_RPC_HOST="https://soroban-testnet.stellar.org"
export SOROBAN_RPC_PORT="443"
export HORIZON_URL="https://horizon-testnet.stellar.org"
export FRIENDBOT_URL="https://friendbot-testnet.stellar.org"
export NETWORK_PASSPHRASE="Test SDF Network ; September 2015"

Get Free Testnet XLM

# Get XLM via Friendbot
curl "https://friendbot-testnet.stellar.org/?addr=GBBD47XCZVNQWMFE2IWHHT4AFYK6QKM3ZDMK5GVLL5IXSAMQ5QNTUVA"
 
# Or use the Stellar Laboratory
# https://laboratory.stellar.org/#account-creator

Testnet Explorer

Veil Testnet Deployment

# Deploy Veil contract to Testnet
soroban contract deploy \
  --wasm target/wasm32-unknown-unknown/release/veil_wallet.wasm \
  --rpc-url https://soroban-testnet.stellar.org \
  --network-passphrase "Test SDF Network ; September 2015"
 
# Initialize wallet
soroban contract invoke \
  --contract-id CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUFC \
  --function init \
  --arg-xdr-base64 "AAAAQQAAAAACc4fQYlzfXGqoHUZRXlzfXGqoHUZRXlzfXGqoHUZRXlzfXGqoHUZRXl=" \
  --rpc-url https://soroban-testnet.stellar.org \
  --network-passphrase "Test SDF Network ; September 2015"

Most Veil development happens on Testnet. It has stable features and monthly resets are acceptable.

Mainnet

For: Production deployments, real user wallets, actual XLM transactions

When to Use Mainnet

  • ✅ Production dApps
  • ✅ Real user wallets
  • ✅ Live payments
  • ✅ Real XLM settlement
  • ❌ NOT for testing (uses real money)
  • ❌ NOT for unaudited code

Mainnet Setup

# Environment variables for Mainnet
export STELLAR_NETWORK="mainnet"
# There is NO public mainnet Soroban RPC. Point this at your own paid/keyed
# provider endpoint (see "Mainnet RPC is paid" below). Horizon, by contrast,
# does have a free public host.
export SOROBAN_RPC_HOST="$MAINNET_RPC_URL"
export SOROBAN_RPC_PORT="443"
export HORIZON_URL="https://horizon.stellar.org"
export NETWORK_PASSPHRASE="Public Global Stellar Network ; September 2015"

Mainnet RPC is paid (and why the wallet proxies it)

Unlike Futurenet and Testnet, Stellar runs no free public Soroban RPC for mainnet. There is no soroban-mainnet.stellar.org host — following any table that lists one lands on a dead DNS name. Mainnet Soroban RPC is a paid, keyed service. Practical providers:

Because that endpoint carries an account key, the Veil wallet never ships it to the browser. It lives in a server-only variable and is reached through a same-origin proxy, app/api/rpc/mainnet/route.ts:

  • Keep the URL server-side. Set MAINNET_RPC_URL (note: no NEXT_PUBLIC_ prefix). A NEXT_PUBLIC_ variable is inlined into the client bundle, so it would publish the key to every visitor of the live site — an immediate, metered cost. SOROBAN_MAINNET_RPC_URL is also accepted.
  • The proxy is not an open relay. It allow-lists only the eleven JSON-RPC methods the wallet actually calls (getHealth, getNetwork, getVersionInfo, getLatestLedger, getFeeStats, getLedgerEntries, getEvents, getTransaction, getTransactions, simulateTransaction, sendTransaction) and rejects anything else with 403, so a public endpoint can’t be used to drain the quota. The upstream URL is never echoed in an error.
  • The UI checks before offering mainnet. GET /api/rpc/mainnet returns {"configured": <bool>} — true when a URL is set, false otherwise. The network switcher uses this to decide whether to offer the mainnet toggle at all; when unset, POST replies 503 and the wallet stays on testnet rather than failing halfway through a transaction.

For local development against your own unkeyed or self-hosted RPC, an explicit NEXT_PUBLIC_MAINNET_RPC_URL overrides the proxy so the browser talks to it directly.

Get Mainnet XLM

Mainnet has no Friendbot. You must buy XLM from an exchange:

  1. Kraken — https://www.kraken.com
  2. Binance — https://www.binance.com
  3. Coinbase — https://www.coinbase.com
  4. CoinMarketCap — https://coinmarketcap.com/currencies/stellar/

Mainnet Explorer

Veil Mainnet Deployment

# Deploy Veil contract to Mainnet (REAL XLM WILL BE CHARGED)
# $MAINNET_RPC_URL is your own paid provider endpoint — there is no public one.
soroban contract deploy \
  --wasm target/wasm32-unknown-unknown/release/veil_wallet.wasm \
  --rpc-url "$MAINNET_RPC_URL" \
  --network-passphrase "Public Global Stellar Network ; September 2015"
 
# Initialize wallet on Mainnet
soroban contract invoke \
  --contract-id CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUFC \
  --function init \
  --arg-xdr-base64 "AAAAQQAAAAACc4fQYlzfXGqoHUZRXlzfXGqoHUZRXlzfXGqoHUZRXlzfXGqoHUZRXlzfXGqoHUZRXl=" \
  --rpc-url "$MAINNET_RPC_URL" \
  --network-passphrase "Public Global Stellar Network ; September 2015"
⚠️

BE CAREFUL ON MAINNET. Every transaction costs real XLM. Test thoroughly on Testnet first. Never deploy unaudited code to Mainnet.

Runtime Network Switching & Per-Network Storage

The deployed wallet is not pinned to one network at build time — a visitor can switch between Testnet and Mainnet from the NetworkSwitcher control without a redeploy. When mainnet traffic goes through the same-origin proxy (the default), the switcher first probes GET /api/rpc/mainnet and only enables the Mainnet option when it reports {"configured": true} — meaning the server has MAINNET_RPC_URL set. A deployment that instead points NEXT_PUBLIC_MAINNET_RPC_URL straight at an RPC skips the probe and enables Mainnet directly. Either way, an unconfigured Mainnet renders disabled with a tooltip explaining why, rather than as an enabled control that would fail on first use.

⚠️

The same passkey resolves to a DIFFERENT wallet address on each network. The wallet contract is deployed per network — Testnet and Mainnet each have their own factory contract (CAUK4… vs CCZ3J…) — so the C… address your passkey derives on Testnet is not the same account as the one it derives on Mainnet. Any integration, support flow, or mental model that assumes “one passkey → one address” is wrong for this wallet.

Why storage is namespaced per network

Early on, wallet identity (invisible_wallet_address, veil_signer_secret, and related keys — see the full list in lib/network.ts’s WALLET_KEYS) was stored under plain, un-namespaced localStorage/sessionStorage keys. Because one passkey maps to two different addresses, this let one network’s data clobber the other’s:

  • Switching to Testnet stranded the Mainnet address.
  • Registering a new wallet on one network silently overwrote the other’s keys.
  • Worst case: using “Reset wallet” while on Testnet could delete the bare keys and destroy a real-funds Mainnet wallet, since Testnet reads/writes those same un-suffixed keys.

The fix (mirrored from the mobile app’s lib/walletStore.ts) gives each network its own storage slot:

  • Testnet keeps the historical, un-suffixed keys, so existing installs keep working with no migration needed.
  • Mainnet keys carry a _mainnet suffix.
  • namespaceKey() in lib/network.ts is the single place that maps a logical key to its per-network physical slot; lib/walletStorage.ts wraps localStorage/sessionStorage with it so app code never has to namespace keys by hand.
  • clearActiveNetworkWallet() (the “Reset wallet” primitive) only ever touches the active network’s namespaced keys — it cannot reach the other network’s slot.
  • A one-time legacy migration (migrateLegacyStorageOnce, gated by a veil_storage_schema flag) copies a pre-namespacing Mainnet install’s bare keys into the new _mainnet slots the first time this code runs, so existing Mainnet users don’t lose access to an already-funded wallet.

What happens on switch

setActiveNetwork() persists the choice to localStorage and then reloads the page — deliberately, rather than swapping state in place. Every module that captured a value derived from the old network (the SDK client, cached balances, in-flight requests) is rebuilt from scratch against the new one, which avoids a stale RPC client quietly reading Testnet data while the UI claims to be on Mainnet.

The switch also drops the current network’s session address (invisible_wallet_address in sessionStorage) before reloading. This is intentional: after the reload, /lock re-derives the address against the new network’s factory. If the old address were left in place, /lock would see a stored address that doesn’t match the freshly-derived one and treat it as tampering (“Account mismatch”) — the wrong verdict for someone who simply changed networks. The fee-payer key is not dropped on switch, since a Stellar G… keypair is the same address on every network.

If you’re integrating against this wallet: never assume a user’s address is stable across a network switch, and always re-read the active address after a switch completes rather than caching it.

Stage 1: Local Development

# Start local Soroban simulator
soroban network start local
 
# Test contracts locally
soroban contract test --network local

Stage 2: Futurenet (Optional)

# Test experimental features
export STELLAR_NETWORK="futurenet"
export SOROBAN_RPC_HOST="https://soroban-futurenet.stellar.org"
 
# Deploy and test
soroban contract deploy --network-passphrase "StellarFuturenetNetwork"

Stage 3: Testnet (Required)

# Full integration testing
export STELLAR_NETWORK="testnet"
export SOROBAN_RPC_HOST="https://soroban-testnet.stellar.org"
 
# Test with realistic conditions
soroban contract deploy --network-passphrase "Test SDF Network ; September 2015"
 
# Load test with multiple transactions
npm run test:integration

Stage 4: Mainnet (Production)

# Only after:
# - Security audit ✓
# - Testnet validation ✓
# - Mainnet simulation ✓
 
export STELLAR_NETWORK="mainnet"
export SOROBAN_RPC_HOST="$MAINNET_RPC_URL"   # your paid provider — no public host
 
# Deploy with caution
soroban contract deploy --network-passphrase "Public Global Stellar Network ; September 2015"

Network Passphrase Reference

Keep these handy:

# Futurenet
StellarFuturenetNetwork
 
# Testnet
Test SDF Network ; September 2015
 
# Mainnet
Public Global Stellar Network ; September 2015

Quick Network Switcher

Create a shell script to switch networks quickly:

#!/bin/bash
 
# ~/.stellar-network
 
case "$1" in
  futurenet)
    export STELLAR_NETWORK="futurenet"
    export SOROBAN_RPC_HOST="https://soroban-futurenet.stellar.org"
    export NETWORK_PASSPHRASE="StellarFuturenetNetwork"
    ;;
  testnet)
    export STELLAR_NETWORK="testnet"
    export SOROBAN_RPC_HOST="https://soroban-testnet.stellar.org"
    export NETWORK_PASSPHRASE="Test SDF Network ; September 2015"
    ;;
  mainnet)
    export STELLAR_NETWORK="mainnet"
    # No public mainnet RPC — set MAINNET_RPC_URL to your paid provider endpoint.
    export SOROBAN_RPC_HOST="$MAINNET_RPC_URL"
    export NETWORK_PASSPHRASE="Public Global Stellar Network ; September 2015"
    ;;
  *)
    echo "Usage: source ~/.stellar-network {futurenet|testnet|mainnet}"
    exit 1
    ;;
esac
 
echo "✓ Network set to: $STELLAR_NETWORK"
echo "✓ RPC: $SOROBAN_RPC_HOST"

Usage:

source ~/.stellar-network testnet
# ✓ Network set to: testnet
# ✓ RPC: https://soroban-testnet.stellar.org
 
source ~/.stellar-network mainnet
# ✓ Network set to: mainnet
# ✓ RPC: $MAINNET_RPC_URL   (your paid provider — no public host)

Network Status & Monitoring

Troubleshooting Network Issues

”Cannot connect to RPC"

# Check network connectivity
curl https://soroban-testnet.stellar.org
 
# Verify firewall isn't blocking port 443
telnet soroban-testnet.stellar.org 443

"Invalid network passphrase"

# Double-check your passphrase (exact spelling matters)
# Testnet: "Test SDF Network ; September 2015"  ← note the semicolon
# Mainnet: "Public Global Stellar Network ; September 2015"

"Insufficient balance"

# Check your account balance
soroban contract invoke \
  --contract-id native \
  --function balance \
  --account YOUR_ACCOUNT

"Contract not found”

# Verify contract is deployed on the right network
# Use the network's block explorer
# Testnet: https://testnet.steexp.com
# Mainnet: https://steexp.com

Still stuck? Ask in the Stellar Discord #soroban-support channel. The community is very helpful!