Complete guide for deploying the Stellar-Save smart contract to Stellar testnet and mainnet.
Version: 2.0.0
Last Updated: 2026-04-25
- Automated CI/CD Pipeline
- Promotion Process: Testnet → Staging → Mainnet
- Release Tags and What CI/CD Does Automatically
- Pre-Deployment Checklist
- Prerequisites
- Environment Setup
- Building the Contract
- Testnet Deployment
- Mainnet Deployment
- Post-Deployment Configuration
- Verification
- Troubleshooting
- Deployment Checklist
The deployment pipeline lives in .github/workflows/deploy.yml and runs four jobs in sequence:
push / workflow_dispatch
│
▼
┌─────────────────────┐
│ pre-deploy │ clippy · cargo audit · WASM size · secrets scan · unit tests
└────────┬────────────┘
│ artifact: stellar_save.wasm + sha256 hash
▼
┌─────────────────────┐ ┌─────────────────────┐
│ deploy-testnet │ OR │ deploy-mainnet │
│ (develop branch) │ │ (main branch) │
│ │ │ ⚠️ requires approval │
└────────┬────────────┘ └────────┬─────────────┘
│ │
▼ ▼
verify_contract.sh verify_contract.sh
smoke_test_post_deploy.sh smoke_test_post_deploy.sh
deployment-record artifact GitHub Release created
Rollback is a separate manual job triggered via workflow_dispatch with a rollback_artifact run ID.
Configure these in Settings → Secrets and variables → Actions:
| Secret | Description |
|---|---|
TESTNET_DEPLOYER_SECRET |
Stellar secret key (S…) for the testnet deployer account |
MAINNET_DEPLOYER_SECRET |
Stellar secret key (S…) for the mainnet deployer account |
The deployer accounts must be funded before deployment:
- Testnet: use Friendbot
- Mainnet: fund with real XLM (minimum ~2 XLM for contract deployment fees)
Create two environments in Settings → Environments:
testnet
- No required reviewers (auto-deploys from
develop) - Optional: add deployment branch rule to
developonly
production
- Add at least one required reviewer
- Restrict to
mainbranch only - This gate is what prevents accidental mainnet deploys
Automatic (recommended)
| Branch push | Target |
|---|---|
develop |
Testnet |
main |
Mainnet (after reviewer approval) |
Manual via workflow_dispatch
GitHub → Actions → "Contract Deployment Pipeline" → Run workflow
network: testnet | mainnet
rollback_artifact: (leave empty for normal deploy)
- Find the GitHub Actions run ID of the last known-good deployment (visible in the Actions URL:
.../runs/<RUN_ID>). - Trigger the workflow manually:
network: testnet | mainnet rollback_artifact: <RUN_ID> - The pipeline will:
- Download the WASM from that run's artifact
- Re-deploy it to the target network
- Run
verify_contract.shandsmoke_test_post_deploy.sh - Print the new contract ID
Note: Soroban contracts are immutable once deployed. "Rollback" means deploying a new contract instance from the old WASM. Update your frontend
CONTRACT_IDenv var to point to the new address.
| Script | Purpose | Key checks |
|---|---|---|
scripts/pre_deploy_check.sh |
Validation gate — blocks deploy on failure | Clippy, cargo audit, WASM size ≤ 100 KB, secrets scan, unit tests |
scripts/verify_contract.sh |
Post-deploy integrity check | Contract exists on-chain, WASM hash matches, contract is callable |
scripts/smoke_test_post_deploy.sh |
Live network smoke tests | RPC reachable, contract exists, read-only call, write-path (testnet only) |
scripts/rollback.sh |
Re-deploy previous WASM | Downloads artifact, deploys, verifies, smoke tests |
Running scripts locally
# Pre-deploy check (builds WASM if needed)
WASM_SIZE_LIMIT_KB=100 bash scripts/pre_deploy_check.sh
# Verify a deployed contract
CONTRACT_ID=C... \
STELLAR_NETWORK=testnet \
STELLAR_RPC_URL=https://soroban-testnet.stellar.org \
EXPECTED_WASM_HASH=<sha256> \
bash scripts/verify_contract.sh
# Smoke test a deployed contract
CONTRACT_ID=C... \
STELLAR_NETWORK=testnet \
STELLAR_RPC_URL=https://soroban-testnet.stellar.org \
bash scripts/smoke_test_post_deploy.shStellar-Save follows a three-stage promotion model. Code only reaches mainnet after passing every gate below.
feature branch
│ PR review + CI (unit tests, clippy, cargo audit)
▼
develop ──push──► Testnet (auto-deploy via deploy.yml)
│ │
│ smoke tests pass?
│ │ yes
▼ ▼
main ──push──► Mainnet (requires reviewer approval in GitHub "production" environment)
| Trigger | push to develop (or workflow_dispatch with network: testnet) |
|---|---|
| Gate | pre-deploy job: clippy, cargo audit, WASM ≤ 100 KB, unit tests |
| Post-deploy | verify_contract.sh + smoke_test_post_deploy.sh |
| Artifact | deployment-record-testnet-<sha>.json retained 90 days |
Testnet is the integration environment. All new features must be validated here before promotion.
Staging is enforced through the GitHub production environment protection rules rather than a separate network. Before the mainnet job runs, GitHub pauses and requires at least one designated reviewer to approve. Use this window to:
- Confirm testnet smoke tests passed in the previous run.
- Review the deployment record artifact (
deployment-record-testnet-<sha>.json). - Verify the WASM hash matches the build you audited.
- Check the pre-deployment checklist below.
To approve: Actions → the pending workflow run → Review deployments → Approve.
| Trigger | push to main (or workflow_dispatch with network: mainnet) after reviewer approval |
|---|---|
| Gate | Same pre-deploy job + reviewer approval in production environment |
| Post-deploy | verify_contract.sh + smoke_test_post_deploy.sh (read-only path only) |
| Artifact | deployment-record-mainnet-<sha>.json retained 365 days |
| Release | GitHub Release created automatically (see next section) |
A mainnet deployment automatically creates a Git tag and GitHub Release. You do not need to tag manually. The tag format is:
contract-mainnet-<first-8-chars-of-commit-sha>
Example: contract-mainnet-a1b2c3d4
If you want to cut a named semantic-version release before merging to main:
git tag -a v1.2.0 -m "Release v1.2.0 — <brief description>"
git push origin v1.2.0Then open a PR from develop → main and merge. The pipeline will deploy and attach the release notes to the tag.
| Step | Job | What happens |
|---|---|---|
| 1 | pre-deploy |
Runs clippy, cargo audit, checks WASM ≤ 100 KB, runs unit tests, builds optimised WASM, uploads artifact |
| 2 | deploy-mainnet |
Waits for reviewer approval, downloads WASM artifact, deploys via stellar contract deploy, captures contract ID |
| 3 | deploy-mainnet |
Runs verify_contract.sh — confirms contract exists on-chain, WASM hash matches, contract is callable |
| 4 | deploy-mainnet |
Runs smoke_test_post_deploy.sh — read-only call against live mainnet |
| 5 | deploy-mainnet |
Saves deployment-record-mainnet-<sha>.json artifact (365-day retention) |
| 6 | deploy-mainnet |
Creates Git tag contract-mainnet-<sha> and GitHub Release with contract ID, WASM hash, and explorer link |
The rollback job is not triggered automatically — it requires a manual workflow_dispatch with a rollback_artifact run ID.
Complete every item before approving a mainnet deployment in the GitHub environment gate.
- All unit and property-based tests pass locally (
cargo test --workspace) -
cargo auditreports no critical or high-severity advisories (scripts/pre_deploy_check.shenforces this in CI) - WASM binary size is within the 100 KB limit (enforced by
pre-deployjob) - A manual or third-party security audit has been completed for any new contract logic (required before first mainnet deploy and after significant changes)
- No hardcoded secrets, private keys, or test-only addresses remain in contract source
- Contract upgrade/migration path is documented if storage layout changed (see
docs/migration.md)
- Frontend builds without errors:
cd frontend && npm run build - All frontend unit tests pass:
npm test run - Frontend
.env(or CI environment variables) updated with the new contract ID from testnet smoke test - End-to-end tests pass against the testnet deployment:
npx playwright test - Bundle size has not regressed unexpectedly (
npm run buildoutput reviewed)
- Any new Prisma schema changes have a migration file:
npx prisma migrate dev --name <description> - Migration has been applied and tested against the staging database
-
npx prisma migrate statusshows no pending migrations on the target environment - Backward-compatible: the new backend can run against the old schema during a rolling deploy (or a maintenance window is scheduled)
- Database backup taken immediately before applying migrations to production
- Testnet deployment record artifact reviewed and WASM hash confirmed
- At least one reviewer has approved the GitHub
productionenvironment gate - Rollback plan confirmed: previous deployment record artifact run ID noted in case
rollbackjob is needed - On-call engineer identified for the 30 minutes following mainnet deploy
-
Rust Toolchain (1.70+)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh rustup target add wasm32-unknown-unknown
-
Stellar CLI (Latest)
cargo install --locked stellar-cli
-
Git
git --version # Verify installation
- OS: Linux, macOS, or Windows (WSL2)
- RAM: 4GB minimum
- Disk: 2GB free space
- Network: Stable internet connection
- Basic understanding of Stellar blockchain
- Familiarity with command-line tools
- Understanding of smart contract deployment
git clone https://github.com/Xoulomon/Stellar-Save.git
cd Stellar-Savecp .env.example .envEdit .env with your settings:
# Network Configuration
STELLAR_NETWORK=testnet
STELLAR_RPC_URL=https://soroban-testnet.stellar.org
# Contract IDs (will be filled after deployment)
CONTRACT_STELLAR_SAVE=
# Frontend Configuration
VITE_STELLAR_NETWORK=testnet
VITE_STELLAR_RPC_URL=https://soroban-testnet.stellar.orgFor Testnet:
stellar keys generate deployer --network testnetFor Mainnet:
stellar keys generate deployer --network mainnetView your address:
stellar keys address deployerTestnet (Free):
stellar keys fund deployer --network testnetOr use the Stellar Laboratory Friendbot.
Mainnet:
- Purchase XLM from an exchange
- Send to your deployer address
- Minimum: ~10 XLM for deployment fees
Ensure all tests pass before deployment:
cargo test --workspaceExpected output:
running 50 tests
test result: ok. 50 passed; 0 failed; 0 ignored
./scripts/build.shOr manually:
cargo build --target wasm32-unknown-unknown --release --package stellar-savels -lh target/wasm32-unknown-unknown/release/stellar_save.wasmExpected size: ~100-200 KB
For production, optimize the WASM file:
stellar contract optimize \
--wasm target/wasm32-unknown-unknown/release/stellar_save.wasmUse the automated script:
./scripts/deploy_testnet.shexport STELLAR_NETWORK=testnet
export STELLAR_RPC_URL=https://soroban-testnet.stellar.orgstellar contract deploy \
--wasm target/wasm32-unknown-unknown/release/stellar_save.wasm \
--source deployer \
--network testnetOutput:
Contract deployed successfully!
Contract ID: CBQHNAXSI55GX2GN6D67GK7BHKQKJNYBNZW7M5QWSXMEEJ6RVAHTYU7
export CONTRACT_ID=CBQHNAXSI55GX2GN6D67GK7BHKQKJNYBNZW7M5QWSXMEEJ6RVAHTYU7
echo "CONTRACT_STELLAR_SAVE=$CONTRACT_ID" >> .envIf your contract requires initialization:
stellar contract invoke \
--id $CONTRACT_ID \
--source deployer \
--network testnet \
-- update_config \
--new_config '{"admin":"'$(stellar keys address deployer)'","min_contribution":"10000000","max_contribution":"1000000000","min_members":"2","max_members":"50","min_cycle_duration":"86400","max_cycle_duration":"2592000"}'- All tests passing on testnet
- Contract audited (recommended)
- Sufficient XLM in deployer account (~10 XLM)
- Backup of deployer keys
- Deployment plan documented
- Rollback strategy prepared
export STELLAR_NETWORK=mainnet
export STELLAR_RPC_URL=https://soroban-mainnet.stellar.orgUpdate .env:
STELLAR_NETWORK=mainnet
STELLAR_RPC_URL=https://soroban-mainnet.stellar.orgstellar account balance deployer --network mainnetEnsure you have at least 10 XLM.
stellar contract deploy \
--wasm target/wasm32-unknown-unknown/release/stellar_save.wasm \
--source deployer \
--network mainnetexport MAINNET_CONTRACT_ID=<your_mainnet_contract_id>
echo "CONTRACT_STELLAR_SAVE_MAINNET=$MAINNET_CONTRACT_ID" >> .envstellar contract invoke \
--id $MAINNET_CONTRACT_ID \
--source deployer \
--network mainnet \
-- update_config \
--new_config '{"admin":"'$(stellar keys address deployer)'","min_contribution":"10000000","max_contribution":"1000000000","min_members":"2","max_members":"50","min_cycle_duration":"86400","max_cycle_duration":"2592000"}'Edit frontend/.env:
VITE_CONTRACT_ID=<your_contract_id>
VITE_STELLAR_NETWORK=testnet # or mainnet
VITE_STELLAR_RPC_URL=https://soroban-testnet.stellar.orgSet global configuration:
stellar contract invoke \
--id $CONTRACT_ID \
--source deployer \
--network testnet \
-- update_config \
--new_config '{
"admin": "'$(stellar keys address deployer)'",
"min_contribution": "10000000",
"max_contribution": "1000000000",
"min_members": "2",
"max_members": "50",
"min_cycle_duration": "86400",
"max_cycle_duration": "2592000"
}'Create a deployment record:
cat > DEPLOYMENT_RECORD.md << EOF
# Deployment Record
**Date**: $(date)
**Network**: $STELLAR_NETWORK
**Contract ID**: $CONTRACT_ID
**Deployer**: $(stellar keys address deployer)
**WASM Hash**: $(sha256sum target/wasm32-unknown-unknown/release/stellar_save.wasm | cut -d' ' -f1)
## Configuration
- Min Contribution: 1 XLM
- Max Contribution: 100 XLM
- Min Members: 2
- Max Members: 50
- Min Cycle: 1 day
- Max Cycle: 30 days
EOFstellar contract info \
--id $CONTRACT_ID \
--network testnetCreate a test group:
stellar contract invoke \
--id $CONTRACT_ID \
--source deployer \
--network testnet \
-- create_group \
--creator $(stellar keys address deployer) \
--contribution_amount 100000000 \
--cycle_duration 604800 \
--max_members 5Get group details:
stellar contract invoke \
--id $CONTRACT_ID \
--network testnet \
-- get_group \
--group_id 1Testnet:
https://stellar.expert/explorer/testnet/contract/$CONTRACT_ID
Mainnet:
https://stellar.expert/explorer/public/contract/$CONTRACT_ID
Problem: Not enough XLM for deployment fees.
Solution:
# Testnet
stellar keys fund deployer --network testnet
# Mainnet
# Send more XLM to your deployer addressProblem: Contract not built.
Solution:
./scripts/build.shProblem: RPC URL incorrect or network down.
Solution:
# Check RPC status
curl https://soroban-testnet.stellar.org/health
# Try alternative RPC
export STELLAR_RPC_URL=https://horizon-testnet.stellar.orgProblem: Trying to deploy same contract twice.
Solution: Use the existing contract ID or deploy with different parameters.
Problem: Wrong identity or insufficient permissions.
Solution:
# Verify identity
stellar keys show deployer
# Ensure identity is funded
stellar account balance deployer --network testnetEnable verbose logging:
export RUST_LOG=debug
stellar contract deploy \
--wasm target/wasm32-unknown-unknown/release/stellar_save.wasm \
--source deployer \
--network testnet \
--verbose- All tests passing (
cargo test --workspace) - Contract built successfully (
./scripts/build.sh) - Deployer identity created and funded
- Environment variables configured
- Network RPC URL verified
- Deployment script reviewed
- Network set correctly (testnet/mainnet)
- Contract deployed successfully
- Contract ID recorded
- Transaction hash saved
- Deployment costs documented
- Contract verified on explorer
- Test functions executed successfully
- Configuration initialized
- Frontend updated with contract ID
- Deployment documented
- Team notified
- Monitoring setup (if applicable)
Builds the contract for deployment:
#!/bin/bash
cargo build --target wasm32-unknown-unknown --release --package stellar-saveAutomated testnet deployment:
#!/bin/bash
set -e
echo "Building contract..."
./scripts/build.sh
echo "Deploying to testnet..."
CONTRACT_ID=$(stellar contract deploy \
--wasm target/wasm32-unknown-unknown/release/stellar_save.wasm \
--source deployer \
--network testnet)
echo "Contract deployed: $CONTRACT_ID"
echo "CONTRACT_STELLAR_SAVE=$CONTRACT_ID" >> .envAutomated mainnet deployment with safety checks:
#!/bin/bash
set -e
echo "⚠️ MAINNET DEPLOYMENT - This will cost real XLM"
read -p "Are you sure? (yes/no): " confirm
if [ "$confirm" != "yes" ]; then
echo "Deployment cancelled"
exit 1
fi
echo "Building contract..."
./scripts/build.sh
echo "Deploying to mainnet..."
CONTRACT_ID=$(stellar contract deploy \
--wasm target/wasm32-unknown-unknown/release/stellar_save.wasm \
--source deployer \
--network mainnet)
echo "Contract deployed: $CONTRACT_ID"
echo "CONTRACT_STELLAR_SAVE_MAINNET=$CONTRACT_ID" >> .env- RPC:
https://soroban-testnet.stellar.org - Horizon:
https://horizon-testnet.stellar.org - Explorer:
https://stellar.expert/explorer/testnet - Friendbot:
https://friendbot.stellar.org
- RPC:
https://soroban-mainnet.stellar.org - Horizon:
https://horizon.stellar.org - Explorer:
https://stellar.expert/explorer/public
- Deployment: Free (funded by Friendbot)
- Transactions: Free
- Contract Deployment: ~5-10 XLM
- Contract Initialization: ~0.1 XLM
- Transaction Fees: ~0.00001 XLM per operation
Note: Costs vary based on network congestion and contract size.
- Never commit private keys to version control
- Use hardware wallets for mainnet deployments
- Test thoroughly on testnet before mainnet
- Audit contracts before mainnet deployment
- Keep backups of deployer keys
- Monitor contract activity post-deployment
- Have a rollback plan ready
- Use multi-sig for admin operations (recommended)
- Documentation: GitHub Docs
- Issues: GitHub Issues
- Stellar Discord: discord.gg/stellar
- Stellar Developers: developers.stellar.org
After successful deployment:
- ✅ Update frontend with contract ID
- ✅ Configure contract parameters
- ✅ Test all contract functions
- ✅ Set up monitoring and alerts
- ✅ Document deployment for team
- ✅ Announce to community
Deployment Guide Version: 1.0.0
Contract Version: 1.0.0
Last Updated: 2026-02-24
- Verify Deployment
Check contract is deployed:
stellar contract info --id <contract_id> --network testnet
Mainnet deployment requires careful verification and should only be done after thorough testing.
- All tests pass on testnet
- Contract code audited
- Environment variables set for mainnet
- Backup of current state
- Team approval obtained
-
Configure Environment
export STELLAR_NETWORK="mainnet" export STELLAR_RPC_URL="https://soroban-rpc.mainnet.stellar.gateway.fm"
-
Build Contracts
./scripts/build.sh
-
Deploy Contracts
./scripts/deploy_mainnet.sh
The script will prompt for confirmation before proceeding.
-
Record Contract IDs Update production
.envfile with deployed contract IDs. -
Update Frontend Configuration Ensure frontend environment variables are updated with mainnet contract IDs.
After deployment, verify your contracts on the network:
-
Verify Contract Code
stellar contract verify \ --id <contract_id> \ --network <testnet|mainnet> \ --source <path_to_source>
-
Check Contract Info
stellar contract info --id <contract_id> --network <network>
-
Test Basic Functionality Use Stellar Lab or CLI to invoke contract functions.
- Run Integration Tests
# Update test environment to point to deployed contracts export CONTRACT_STELLAR_SAVE=<deployed_id> cargo test --workspace -- --nocapture
-
Frontend Integration
- Deploy frontend to staging environment
- Test all user flows with deployed contracts
- Verify transaction signing and submission
-
Cross-Contract Interactions
- Test any contract-to-contract calls
- Verify token transfers and allowances
-
Load Testing
- Simulate expected user load
- Monitor RPC rate limits and performance
Build Failures
- Ensure Rust toolchain is properly installed
- Check
rust-toolchain.tomlfor correct version - Run
cargo cleanand rebuild
Deployment Failures
- Verify network connectivity to RPC endpoint
- Check account has sufficient XLM for fees
- Ensure source account is properly funded
Contract Verification Issues
- Confirm source code matches deployed WASM
- Check compiler versions match
- Verify optimization settings
Network-Specific Issues
- Testnet: Check faucet for account funding
- Mainnet: Verify RPC endpoint and network passphrase
- Check existing issues in the repository
- Review Stellar documentation: https://developers.stellar.org/
- Join Stellar Discord for community support
If issues are discovered post-deployment:
- Pause Frontend: Disable user interactions
- Assess Impact: Determine scope of issues
- Deploy Fix: If needed, deploy updated contract
- Migrate State: If required, implement state migration
- Resume Operations: Re-enable frontend after verification c:\Users\USER\Desktop\solo\Stellar-Save\docs\deployment.md