This document describes how the Lily Protocol contracts can be upgraded in the future without losing on-chain state. It is intentionally written as a strategy guide: the current contracts do not yet expose an upgrade entrypoint, so the playbook below defines the requirements and guardrails for when that work is undertaken.
The four deployed contracts (identity, payments, protocol, wallet) store state in Soroban's instance and persistent storage. None of them currently exposes an upgrade entrypoint or a storage-version guard. Adding upgrade support is listed as future work in README.md.
On Soroban, contract code is identified by a wasm hash stored on-chain. Upgrading a contract means replacing the hash that the contract instance points to.
- Each contract must pin the hash of the wasm it was initialized with.
- A new wasm must be built, optimized, and its hash computed locally before deployment.
- The upgrade transaction must explicitly reference both the old and new wasm hashes so the operation is auditable.
- Avoid unbounded wasm growth; each new version should justify its additional size.
build new wasm
-> compute wasm hash
-> store wasm on-chain (if not already stored)
-> invoke upgrade(new_wasm_hash) from the admin account
-> run post-upgrade migration (if needed)
-> emit upgrade event
Storage layouts change over time. A storage_version: u32 value should be stored in instance storage for each contract.
- Initial deployment uses
storage_version = 1. - Every upgrade that changes storage layout must bump the version.
- Migration logic must read the current version and apply only the deltas required to reach the target version.
- Downgrades are rejected; versions only increase monotonically.
const CURRENT_STORAGE_VERSION: u32 = 1;
fn ensure_storage_version(env: &Env) {
let version: u32 = env.storage().instance().get(&DataKey::StorageVersion)
.unwrap_or(0);
require(env, version == CURRENT_STORAGE_VERSION, ProtocolError::InvalidInput);
}Upgrades should not happen instantaneously without notice. A migration window gives integrators time to react.
- Announcement: Publish the new wasm hash, storage changes, and migration script at least one week before on-chain execution.
- Freeze period: Pause state-changing admin operations during the upgrade block if the change affects core data layouts.
- Execution: Admin calls
upgradein a single transaction that updates the wasm hash and runs the migration. - Verification: Run a suite of read-only integration tests against the upgraded contract before unfreezing user operations.
- Rollback plan: Keep the previous wasm hash available on-chain so the instance can be reverted if a critical issue is found within the rollback window.
When an upgrade entrypoint is added to each contract, it should satisfy the following:
- Authorization: Only the contract admin can invoke it.
- Wasm hash validation: Accept exactly one
BytesN<32>argument representing the new wasm hash. - Storage migration: Call an internal
migrate()function that bumpsstorage_versionand rewrites any changed keys. - Event emission: Emit an
("upgrade", admin, old_wasm_hash, new_wasm_hash, new_storage_version)event. - Idempotency: Re-invoking with the same wasm hash should be a no-op or should revert.
- Size limits: Verify the new wasm is within acceptable deployment limits.
pub fn upgrade(env: Env, new_wasm_hash: BytesN<32>);fn migrate(env: &Env, old_version: u32, new_version: u32) {
// Apply deltas between old_version and new_version.
env.storage().instance().set(&DataKey::StorageVersion, &new_version);
}protocolandpaymentsshare the concept of admin, treasury, and fee basis points. Upgrading one may require the other to understand new config shapes.identityprofiles are referenced bywalletandpayments. Any change toAgentProfilemust be coordinated across consumers.- Event topics and payload shapes are part of the observable interface. Versioning events is recommended if breaking changes are introduced.
Before executing an upgrade:
- New wasm is built with
make build-wasmand hash is recorded. - Storage version delta is documented in this file.
- Migration script is reviewed and tested against a local network.
- Integrators have been notified of breaking event or storage changes.
- Admin key is available and has been tested on a testnet deployment.
- Rollback wasm hash is known and stored on-chain.
- Soroban contract upgrade docs
README.mdfuture work section for upgrade and migration playbooksdocs/EVENTS.mdfor event-versioning guidancedocs/ARCHITECTURE.mdfor per-contract storage layouts (when available)