This directory contains automation scripts for deploying, upgrading, verifying, and managing Soroban smart contracts on the Stellar network.
- Prerequisites
- Quick Start
- Configuration
- Scripts Reference
- Network Safety
- Deployment Registry
- Troubleshooting
| Tool | Version | Installation |
|---|---|---|
| Stellar CLI | Latest | cargo install --locked stellar-cli |
| jq | 1.6+ | apt install jq or brew install jq |
| Rust | Stable | rustup.rs |
# Check Stellar CLI
stellar --version
# Check jq
jq --version
# Check Rust and WASM target
rustup target list --installed | grep wasm32rustup target add wasm32-unknown-unknown# 1. Create a deployer identity
stellar keys generate --global grainlify-deployer
# 2. Fund the account (testnet only)
stellar keys fund grainlify-deployer --network testnet
# 3. Build contracts
cd soroban && cargo build --release --target wasm32-unknown-unknown
# 4. Deploy
cd .. && ./contracts/scripts/deploy.sh soroban/target/wasm32-unknown-unknown/release/escrow.wasmThis repository includes a matrix runner for validating Soroban contract behavior across multiple SDK versions and network targets.
./scripts/run_contract_matrix.sh --sdk-version <version> --network <local|testnet> [options]
## Configuration
### Environment Files
Configuration is stored in `contracts/scripts/config/`:
| File | Purpose |
|------|---------|
| `testnet.env` | Stellar Testnet configuration |
| `mainnet.env` | Stellar Mainnet configuration (production) |
### Setting Up Testnet
1. **Copy and customize the config:**
```bash
# The default testnet.env works out of the box
# Optionally create a local override:
cp contracts/scripts/config/testnet.env contracts/scripts/config/testnet.env.local- Edit key values:
# contracts/scripts/config/testnet.env
# Network endpoint
SOROBAN_RPC_URL="https://soroban-testnet.stellar.org"
SOROBAN_NETWORK="testnet"
# Your deployer identity (create with: stellar keys generate)
DEPLOYER_IDENTITY="grainlify-deployer"
# Safety settings
REQUIRE_CONFIRMATION="true"
DRY_RUN="false"- Create and fund your identity:
# Generate a new keypair
stellar keys generate --global grainlify-deployer
# View the public address
stellar keys address grainlify-deployer
# Fund with testnet XLM (via Friendbot)
stellar keys fund grainlify-deployer --network testnetWarning: Mainnet deployments use real XLM and are irreversible.
- Create a separate mainnet identity:
# NEVER reuse testnet keys for mainnet
stellar keys generate --global grainlify-mainnet-deployer-
Fund with real XLM:
- Transfer XLM from an exchange or existing wallet
- Minimum ~10 XLM recommended for deployments
-
Update mainnet config:
# contracts/scripts/config/mainnet.env
DEPLOYER_IDENTITY="grainlify-mainnet-deployer"Deploys a new smart contract to the network.
./contracts/scripts/deploy.sh <wasm_file> [options]| Option | Description | Default |
|---|---|---|
-n, --network |
Target network | testnet |
-i, --identity |
Deployer identity | from config |
-N, --name |
Contract name for registry | filename |
--init |
Call init() after deploy | false |
--init-args |
Arguments for init | - |
--dry-run |
Simulate only | false |
-v, --verbose |
Detailed output | false |
# Deploy escrow contract to testnet
./contracts/scripts/deploy.sh soroban/target/wasm32-unknown-unknown/release/escrow.wasm
# Deploy with custom name
./contracts/scripts/deploy.sh escrow.wasm -N bounty-escrow-v1
# Deploy and initialize with admin
./contracts/scripts/deploy.sh escrow.wasm --init --init-args '--admin GABC...'
# Deploy to mainnet (requires confirmation)
./contracts/scripts/deploy.sh escrow.wasm -n mainnet -i mainnet-deployer
# Dry run (see what would happen)
./contracts/scripts/deploy.sh escrow.wasm --dry-run --verboseOn success, the script:
- Prints the new Contract ID
- Records the deployment in
contracts/deployments/<network>.json
Contract ID: CABC123DEF456...
Upgrades an existing contract to a new WASM version.
./contracts/scripts/upgrade.sh <contract_id> <new_wasm_path> [options]| Option | Description | Default |
|---|---|---|
-n, --network |
Target network | testnet |
-s, --source |
Signing identity | from config |
--skip-verify |
Skip post-upgrade check | false |
--dry-run |
Simulate only | false |
# Upgrade contract on testnet
./contracts/scripts/upgrade.sh CABC123... ./target/release/escrow.wasm
# Upgrade on mainnet with specific identity
./contracts/scripts/upgrade.sh CABC123... escrow.wasm -n mainnet -s admin-key
# Upgrade without verification
./contracts/scripts/upgrade.sh CABC123... escrow.wasm --skip-verify
# Preview upgrade (dry run)
./contracts/scripts/upgrade.sh CABC123... escrow.wasm --dry-run- Installs the new WASM code (gets
wasm_hash) - Invokes the contract's
upgrade(new_wasm_hash)function - Verifies the contract responds after upgrade
- Logs the upgrade to
contracts/deployments/upgrades.json
- Contract must have an
upgrade(new_wasm_hash: BytesN<32>)function - Signing identity must be the contract admin
Checks if a deployed contract is healthy and responsive. When
--expected-wasm or --expected-wasm-hash is provided, it reads the deployed
WASM hash with stellar contract info hash --contract-id and compares it with
the expected artifact or hash.
./contracts/scripts/verify-deployment.sh <contract_id> [options]| Option | Description | Default |
|---|---|---|
-n, --network |
Target network | testnet |
-f, --function |
Function to call for the primary responsiveness check | get_version |
--check-admin |
Verify admin address | false |
--expected-admin |
Expected admin value | - |
--expected-wasm |
Local WASM artifact whose SHA-256 must match the deployed hash | - |
--expected-wasm-hash |
Expected deployed WASM hash | - |
--smoke-functions |
Comma-separated read-only functions to smoke test | get_version,get_admin,get_pause_flags |
--skip-smoke |
Skip read-only smoke checks | false |
--json |
Output as JSON | false |
# Basic health check
./contracts/scripts/verify-deployment.sh CABC123...
# Check on mainnet
./contracts/scripts/verify-deployment.sh CABC123... -n mainnet
# Use custom verification function
./contracts/scripts/verify-deployment.sh CABC123... -f get_balance
# Verify admin matches expected
./contracts/scripts/verify-deployment.sh CABC123... --check-admin --expected-admin GABC...
# Verify deployed WASM hash from a local artifact and run default smoke checks
./contracts/scripts/verify-deployment.sh CABC123... --expected-wasm target/escrow.wasm
# Verify against a known deployed WASM hash
./contracts/scripts/verify-deployment.sh CABC123... --expected-wasm-hash 7a8b9c0d...
# Customize or skip smoke checks
./contracts/scripts/verify-deployment.sh CABC123... --smoke-functions get_version,get_admin
./contracts/scripts/verify-deployment.sh CABC123... --skip-smoke
# JSON output (for CI/CD pipelines)
./contracts/scripts/verify-deployment.sh CABC123... --jsonHuman-readable:
Status: HEALTHY
Contract ID: CABC123...
Network: testnet
Function: get_version
Result: 1
JSON mode (--json):
{
"contract_id": "CABC123...",
"network": "testnet",
"status": "HEALTHY",
"verification": {
"function": "get_version",
"result": "1"
},
"wasm_hash": {
"status": "MATCH",
"expected": "7a8b9c0d...",
"actual": "7a8b9c0d...",
"error": null
},
"smoke_tests": {
"status": "PASS",
"results": "get_version:PASS:1; get_admin:PASS:GABC...",
"error": null
},
"verified_at": "2024-01-15T10:30:00Z"
}| Code | Meaning |
|---|---|
0 |
Contract is HEALTHY |
1 |
Contract is UNRESPONSIVE |
2 |
Invalid arguments |
# In a GitHub Action or script
if ./contracts/scripts/verify-deployment.sh "$CONTRACT_ID" --json; then
echo "Deployment verified"
else
echo "Deployment failed verification"
exit 1
fiReverts a contract to a previous WASM version.
./contracts/scripts/rollback.sh <contract_id> <previous_wasm_hash> [options]| Option | Description | Default |
|---|---|---|
-n, --network |
Target network | testnet |
-s, --source |
Signing identity | from config |
--force |
Skip confirmations | false |
--dry-run |
Simulate only | false |
# From upgrade log (most recent)
cat contracts/deployments/upgrades.json | jq -r '.upgrades[-1].old_wasm_hash'
# List all upgrades for a contract
cat contracts/deployments/upgrades.json | jq '.upgrades[] | select(.contract_id == "CABC123...")'
# From deployment log
cat contracts/deployments/testnet.json | jq -r '.deployments[] | select(.contract_name == "escrow") | .wasm_hash'# Rollback to previous version
./contracts/scripts/rollback.sh CABC123... 7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b
# Rollback on mainnet (double confirmation required)
./contracts/scripts/rollback.sh CABC123... 7a8b9c0d... -n mainnet
# Force rollback (skip prompts - dangerous!)
./contracts/scripts/rollback.sh CABC123... 7a8b9c0d... --force
# Dry run
./contracts/scripts/rollback.sh CABC123... 7a8b9c0d... --dry-run╔══════════════════════════════════════════════════════════════════╗
║ ║
║ ⚠️ CRITICAL WARNING ⚠️ ║
║ ║
║ ROLLBACK ONLY REVERTS CONTRACT CODE (LOGIC). ║
║ CONTRACT STATE (DATA) IS NOT REVERTED. ║
║ ║
║ If the upgraded version modified data structures or storage ║
║ keys, rolling back may cause DATA INCOMPATIBILITY. ║
║ ║
║ MANUAL DATA MIGRATION MAY BE REQUIRED AFTER ROLLBACK. ║
║ ║
╚══════════════════════════════════════════════════════════════════╝
After a rollback, always:
- Run
verify-deployment.shto confirm responsiveness - Check if data migration is needed
- Test critical contract functions manually
- Review contract state for inconsistencies
- Notify team members
| Aspect | Testnet | Mainnet |
|---|---|---|
| XLM | Free (Friendbot) | Real money |
| Confirmation | Single prompt | Double confirmation |
| Risk | Low (test data) | High (production) |
| Rollback | Safe to experiment | Requires caution |
-
Confirmation Prompts
- Testnet: Single confirmation for destructive operations
- Mainnet: Double confirmation required
-
Dry Run Mode
- All scripts support
--dry-runto preview actions - No state changes are made
- All scripts support
-
Separate Identities
- Use different keys for testnet and mainnet
- Never reuse testnet keys in production
-
Registry Logging
- All operations are logged with timestamps
- Enables audit trail and rollback capability
Before deploying to mainnet:
- Contract tested thoroughly on testnet
- Code reviewed and/or audited
- Admin addresses verified correct
- Sufficient XLM balance for deployment
- Backup of deployment keys exists
- Team notified of deployment window
- Rollback plan documented
All deployments and operations are logged in contracts/deployments/:
| File | Contents |
|---|---|
testnet.json |
Testnet deployment records |
mainnet.json |
Mainnet deployment records |
upgrades.json |
All upgrade operations |
rollbacks.json |
All rollback operations |
{
"deployments": [
{
"contract_id": "CABC123...",
"wasm_hash": "7a8b9c0d...",
"contract_name": "escrow",
"network": "testnet",
"deployer": "grainlify-deployer",
"deployed_at": "2024-01-15T10:30:00Z",
"status": "deployed"
}
],
"metadata": {
"created": "2024-01-15T10:00:00Z",
"version": "1.0"
}
}# List all deployed contracts
cat contracts/deployments/testnet.json | jq '.deployments[].contract_name'
# Get contract ID by name
cat contracts/deployments/testnet.json | jq -r '.deployments[] | select(.contract_name == "escrow") | .contract_id'
# View upgrade history
cat contracts/deployments/upgrades.json | jq '.upgrades[] | {contract: .contract_name, from: .old_wasm_hash, to: .new_wasm_hash, date: .upgraded_at}'[ERROR] Identity not found: grainlify-deployer
Solution:
# Create the identity
stellar keys generate --global grainlify-deployer
# Fund it (testnet)
stellar keys fund grainlify-deployer --network testnet[ERROR] Transaction failed: insufficient balance
Solution:
# Check balance
stellar keys address grainlify-deployer
# Then check balance on network explorer or:
stellar contract invoke --id <native_token> -- balance --id <your_address>
# Fund more XLM (testnet)
stellar keys fund grainlify-deployer --network testnet[ERROR] Upgrade invocation failed
Possible causes:
- Source identity is not the contract admin
Solution:
- Verify you're using the correct identity that deployed/owns the contract
- Check the contract's admin address matches your identity
[ERROR] Rollback failed!
- WASM hash not installed on network
Solution:
# Install the old WASM file first
stellar contract install --wasm old_contract.wasm --network testnet
# Then retry rollback with the returned hash
./contracts/scripts/rollback.sh CABC123... <returned_hash>[ERROR] Cannot reach network: https://soroban-testnet.stellar.org
Solution:
- Check your internet connection
- Verify the RPC URL in your config
- Try again (network may be temporarily unavailable)
# View script help
./contracts/scripts/deploy.sh --help
./contracts/scripts/upgrade.sh --help
./contracts/scripts/verify-deployment.sh --help
./contracts/scripts/rollback.sh --help
# Enable verbose mode for debugging
./contracts/scripts/deploy.sh contract.wasm --verbosecontracts/
├── scripts/
│ ├── config/
│ │ ├── testnet.env # Testnet configuration
│ │ └── mainnet.env # Mainnet configuration
│ ├── utils/
│ │ └── common.sh # Shared utility functions
│ ├── deploy.sh # Deploy new contracts
│ ├── upgrade.sh # Upgrade existing contracts
│ ├── verify-deployment.sh # Health check contracts
│ ├── rollback.sh # Revert to previous version
│ ├── upgrade_contract.sh # Legacy upgrade script
│ ├── demo_upgrade.sh # Upgrade demonstration
│ └── README.md # This file
└── deployments/
├── .gitkeep
├── testnet.json # Testnet deployment log
├── mainnet.json # Mainnet deployment log
├── upgrades.json # Upgrade history
└── rollbacks.json # Rollback history
When modifying these scripts:
- Always source
utils/common.shfor shared functions - Support standard flags (
-n,-s,--dry-run,-v,-h) - Add mainnet confirmation prompts for destructive operations
- Log all operations to the appropriate registry
- Update this README with new functionality
Part of the Grainlify project. See repository root for license information.