This guide covers the upgrade path for the standalone contracts/nova-rewards Soroban contract. The contract exposes two admin-only entrypoints for this process:
The nova-rewards Soroban contract supports in-place WASM upgrades via
env.deployer().update_current_contract_wasm(). All instance storage
(balances, admin, version counters) persists across upgrades.
Two storage keys track upgrade state:
| Key | Description |
|---|---|
MigrationVersion |
Target version — incremented by upgrade() |
MigratedVersion |
Last completed migration — incremented by migrate() |
migrate() is gated: it only runs when migrated_version < migration_version,
so it is safe to call exactly once per upgrade and will panic if called again.
Because the contract stores its operational state in instance storage, balances, staking records, swap configuration, and the saved migration version remain available after a successful code swap.
- Install Rust and the
wasm32-unknown-unknowntarget. - Install the Stellar CLI used by the repository deployment workflow.
- Have the admin secret, or another signer authorized to act for the current admin.
- Know the deployed contract ID for the
nova-rewardsinstance you are updating.
# Rust + wasm32 target
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add wasm32-unknown-unknown
# Soroban CLI
cargo install --locked soroban-cliIn contracts/nova-rewards/src/lib.rs, inside migrate(), add a versioned
block for any data transformations needed by the new release:
// Example: backfill a new field for version 2
if migration_version == 2 {
// ... transform storage ...
}The build artifact is:
cd contracts/nova-rewards
cargo build --release --target wasm32-unknown-unknown
# Output: ../../target/wasm32-unknown-unknown/release/nova_rewards.wasmUse the optimized artifact if wasm-opt is available; otherwise use the raw release WASM.
Upload the artifact to the target network and capture the returned hash:
stellar contract upload \
--wasm contracts/nova-rewards/target/wasm32-unknown-unknown/release/nova_rewards.optimized.wasm \
--network-passphrase "Test SDF Network ; September 2015" \
--rpc-url https://soroban-testnet.stellar.org \
--source <ADMIN_SECRET>This prints the 64-character hex WASM hash, e.g. abc123...def456.
stellar contract invoke \
--network testnet \
--source-account alice \
--id <CONTRACT_ID> \
-- upgrade \
--new_wasm_hash <WASM_HASH_FROM_STEP_3>What happens internally:
MigrationVersionis incremented.- The new WASM hash is stored under
PendingWasmHash. env.deployer().update_current_contract_wasm(new_wasm_hash)swaps the bytecode.
stellar contract invoke \
--network testnet \
--source-account alice \
--id <CONTRACT_ID> \
-- migrateWhat happens internally:
- Checks
migrated_version < migration_version; panics with"migration already applied"if already done. - Runs version-specific data transformations.
- Sets
MigratedVersion = MigrationVersion. - Emits
upgradedevent with(wasm_hash, migration_version).
# Both counters should match after a successful migrate()
soroban contract invoke --network testnet --id <CONTRACT_ID> -- get_migration_version
soroban contract invoke --network testnet --id <CONTRACT_ID> -- get_migrated_versionConfirm the upgraded event appears in the transaction record on the Stellar explorer.
- Invoke
get_migrated_versionand confirm it matchesCONTRACT_VERSIONincontracts/nova-rewards/src/lib.rs. - Query representative balances with
get_balanceto confirm state survived the code swap. - Re-run
contracts/nova-rewards/tests/upgrade.rs. - If the release touched swaps or staking, also run the swap and staking test suites.
- Only the
adminaddress set duringinitializemay callupgradeormigrate. migrate()panics with"migration already applied"if called more than once per version.- All instance storage (balances, admin, version counters) survives the WASM swap.
Soroban does not support automatic rollback. To revert:
- Install the previous WASM and note its hash.
- Call
upgrade()with the old hash. - Call
migrate()— add compensating data transformations if needed.