How to deploy the Stellar Tipz contract and frontend to Testnet and Mainnet.
- Soroban CLI installed (
soroban --version→ 21.0+) - Rust +
wasm32-unknown-unknowntarget - A funded Stellar account (Testnet: use Friendbot; Mainnet: real XLM)
- Node.js 18+ (for frontend)
- Vercel CLI (optional, for frontend deployment)
Complete before any deployment (and re-verify before mainnet):
Security
-
cargo testpasses for the contract (contracts/tipz) -
cargo fmt --checkandcargo clippy -- -D warningsare clean - For mainnet: third-party security audit completed and findings resolved
- Admin key custody decided (hardware wallet or multisig for mainnet)
- Fee basis points reviewed and within the contract cap (≤ 1000 bps / 10%)
Testing
- Full happy path exercised on testnet (register → tip → withdraw)
- Edge cases verified (dust withdrawal fee, overflow, unregistered profile)
- Frontend smoke-tested against the deployed testnet contract
Resource / cost estimates
- Wasm built in
--releaseand (for mainnet)soroban contract optimizerun - Deploy +
initializeresource fees estimated with--sim/ dry-run - Deployer account funded with enough XLM for deploy and storage rent
- Storage TTL strategy understood (see
docs/adr/ADR-004-storage-strategy.md)
| Setting | Testnet | Mainnet |
|---|---|---|
VITE_NETWORK / REACT_APP_NETWORK |
TESTNET |
PUBLIC |
| Network passphrase | Test SDF Network ; September 2015 |
Public Global Stellar Network ; September 2015 |
| Soroban RPC URL | https://soroban-testnet.stellar.org |
a mainnet RPC provider |
Native XLM SAC (--native_token) |
CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC |
resolve via soroban contract id asset --asset native --network mainnet |
| Deployer funding | Friendbot | real XLM |
| Admin key | dev keypair | hardware wallet / multisig |
cd contracts
# Run tests first
cargo test
# Build optimized release binary
cargo build --target wasm32-unknown-unknown --release
# The Wasm file will be at:
# target/wasm32-unknown-unknown/release/tipz.wasm# Generate a deploy key (one time)
soroban keys generate tipz-deployer --network testnet
# Fund it via Friendbot
curl "https://friendbot.stellar.org?addr=$(soroban keys address tipz-deployer)"
# Deploy
soroban contract deploy \
--wasm target/wasm32-unknown-unknown/release/tipz.wasm \
--source tipz-deployer \
--network testnet
# Save the contract ID! Example output:
# CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSCCONTRACT_ID="<your-contract-id>"
DEPLOYER_ADDR="$(soroban keys address tipz-deployer)"
# Resolve the native XLM SAC address for testnet:
NATIVE_TOKEN=$(stellar contract id asset --asset native --network testnet)
# Testnet default: CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC
soroban contract invoke \
--id $CONTRACT_ID \
--source tipz-deployer \
--network testnet \
-- \
initialize \
--admin $DEPLOYER_ADDR \
--fee_collector $DEPLOYER_ADDR \
--fee_bps 200 \
--native_token $NATIVE_TOKEN# Check contract stats
soroban contract invoke \
--id $CONTRACT_ID \
--source tipz-deployer \
--network testnet \
-- \
get_statsCreate frontend-scaffold/.env:
CONTRACT_ID=<deployed-contract-id>
REACT_APP_NETWORK=TESTNETcd frontend-scaffold
npm install --legacy-peer-deps
npm run buildThe production build will be in frontend-scaffold/build/.
The repo includes a vercel.json at the root:
# Install Vercel CLI
npm i -g vercel
# Deploy (from repo root)
vercel
# Or deploy to production
vercel --prodVercel configuration in vercel.json handles:
- Build command:
cd frontend-scaffold && npm install --legacy-peer-deps && npm run build - Output directory:
frontend-scaffold/build - SPA rewrites: all routes →
index.html
cd frontend-scaffold
# Build image
docker build -t stellar-tipz-frontend .
# Run locally
docker run -p 8080:80 stellar-tipz-frontend
⚠️ Mainnet deployment requires a security audit first.
- Security audit — Third-party audit of the Soroban contract
- Config changes:
- Update
REACT_APP_NETWORK=PUBLIC - Update RPC URL to mainnet
- Update network passphrase to
Public Global Stellar Network ; September 2015
- Update
- Real XLM — Deployer account needs real XLM for deployment
- Admin key security — Use a hardware wallet or multisig for the admin key
- Monitoring — Set up event monitoring and alerting
Located in scripts/:
Fully automated testnet deployment — builds, deploys, and initializes the contract in one step.
# Deploy with the pre-built wasm (default):
./scripts/deploy-testnet.sh
# Build the contract first, then deploy:
./scripts/deploy-testnet.sh --build
# Use an optimized wasm (run `soroban contract optimize` first):
./scripts/deploy-testnet.sh --optimized
# Validate inputs and wasm path without actually deploying:
./scripts/deploy-testnet.sh --dry-run
# Use a custom key name (defaults to "tipz-deployer"):
./scripts/deploy-testnet.sh my-key-name
# Override the native XLM SAC address via env var:
NATIVE_TOKEN_ID=<SAC_ADDRESS> ./scripts/deploy-testnet.shThe script automatically funds the deployer account via Friendbot and calls
initialize with --native_token set to the testnet XLM SAC address
(CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC by default,
overrideable via the NATIVE_TOKEN_ID environment variable).
Fund a testnet account:
./scripts/fund-account.sh <PUBLIC_KEY>Generate TypeScript bindings from the deployed contract:
./scripts/generate-bindings.sh <CONTRACT_ID>- Contract deployed and initialized
-
get_stats()returns expected initial values - Test
register_profile()with a test account - Test
send_tip()between two test accounts - Test
withdraw_tips()and verify fee deduction - Frontend
.envupdated with contract ID - Frontend builds successfully
- Frontend deployed and accessible
- Freighter wallet connects on deployed frontend
- End-to-end happy path works (register → tip → withdraw)
The contract supports an admin pause that blocks state-changing entry points (tips, withdrawals) while reads stay available. On a suspected exploit or critical bug:
- As admin, call the contract's
pause(seeadmin.rs) to halt mutations. - Communicate status to users (status page / social) — the frontend should surface a maintenance banner.
- Investigate with on-chain events and logs before resuming.
- Call
unpauseonly once the issue is understood and mitigated.
The frontend is immutable per deployment, so rollback is instant:
# List recent deployments and promote a known-good one
vercel ls
vercel promote <previous-deployment-url>
# or, in the Vercel dashboard: Deployments → previous → "Promote to Production"If the issue is purely a bad VITE_CONTRACT_ID/network value, fix the env var
and redeploy rather than rolling back code.
- Upgrade (preferred): the contract is upgradeable (
ContractVersionis bumped on upgrade). Ship a fixed Wasm viasoroban contract install+ the admin-gated upgrade path; storage and the contract ID are preserved. - Redeploy (last resort): if state is corrupt or the ID must change, deploy a fresh contract, migrate/re-initialize required state, then point the frontend at the new contract ID. There is no automatic state migration — plan it explicitly.
If the admin key is compromised: pause immediately, transfer admin to a new secure key (hardware wallet / multisig) via the admin-transfer path, rotate any related operational secrets, and post-mortem before unpausing.
- Root cause documented (consider a new
docs/adr/entry if architectural) - Regression test added under
contracts/tipz/src/test - Fix deployed and verified against the post-deployment checklist above