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.
wasm32-unknown-unknown target.nova-rewards instance 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-cli
In 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.wasm
Use 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:
MigrationVersion is incremented.PendingWasmHash.env.deployer().update_current_contract_wasm(new_wasm_hash) swaps the bytecode.stellar contract invoke \
--network testnet \
--source-account alice \
--id <CONTRACT_ID> \
-- migrate
What happens internally:
migrated_version < migration_version; panics with "migration already applied" if already done.MigratedVersion = MigrationVersion.upgraded event 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_version
Confirm the upgraded event appears in the transaction record on the Stellar explorer.
get_migrated_version and confirm it matches CONTRACT_VERSION in contracts/nova-rewards/src/lib.rs.get_balance to confirm state survived the code swap.contracts/nova-rewards/tests/upgrade.rs.admin address set during initialize may call upgrade or migrate.migrate() panics with "migration already applied" if called more than once per version.Soroban does not support automatic rollback. To revert:
upgrade() with the old hash.migrate() — add compensating data transformations if needed.