upgrade-stellar-contracts

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Stellar Upgrades

Stellar/Soroban合约升级

Contents

目录

Soroban Upgrade Model

Soroban升级模型

Soroban contracts are mutable by default. Mutability refers to the ability of a smart contract to modify its own WASM bytecode, altering its function interface, execution logic, or metadata. Soroban provides a built-in, protocol-level mechanism for contract upgrades — no proxy pattern is needed.
A contract can upgrade itself if it is explicitly designed to do so. Conversely, a contract becomes immutable simply by not provisioning any upgrade function. This is fundamentally different from EVM proxy patterns:
SorobanEVM (proxy pattern)Starknet
MechanismNative WASM bytecode replacementProxy
delegatecall
s to implementation contract
replace_class_syscall
swaps class hash in-place
Proxy contract neededNo — the contract upgrades itselfYes — a proxy sits in front of the implementationNo — the contract upgrades itself
Storage locationBelongs to the contract directlyLives in the proxy, accessed via delegatecallBelongs to the contract directly
Opt-in to immutabilityDon't expose an upgrade functionDon't deploy a proxyDon't call the syscall
One advantage of protocol-level upgradeability is a significantly reduced risk surface compared to platforms that require proxy contracts and delegatecall forwarding.
The new implementation only becomes effective after the current invocation completes. This means if migration logic is defined in the new implementation, it cannot execute within the same call as the upgrade. An auxiliary
Upgrader
contract can wrap both calls to achieve atomicity (see below).
Soroban合约默认是可修改的。可修改性指的是智能合约能够修改自身WASM字节码的能力,以此改变其函数接口、执行逻辑或元数据。Soroban为合约升级提供了内置的协议级机制——无需使用代理模式。
合约可以通过显式设计实现自我升级。反之,如果不提供任何升级函数,合约将变为不可变。这与EVM的代理模式有着本质区别:
SorobanEVM(代理模式)Starknet
实现机制原生WASM字节码替换代理通过
delegatecall
调用实现合约
通过
replace_class_syscall
原地替换类哈希
是否需要代理合约不需要——合约自行升级需要——代理位于实现合约前端不需要——合约自行升级
存储位置直接属于合约本身存储在代理中,通过delegatecall访问直接属于合约本身
如何选择不可变性不暴露升级函数不部署代理合约不调用系统调用
与需要代理合约和delegatecall转发的平台相比,协议级可升级性的一个优势是显著降低了风险面。
新的实现仅在当前调用完成后生效。这意味着如果新实现中定义了迁移逻辑,它无法在与升级相同的调用中执行。可以通过一个辅助的
Upgrader
合约将这两个调用打包,以实现原子性(见下文)。

Using the OpenZeppelin Upgradeable Module

使用OpenZeppelin可升级模块

OpenZeppelin Stellar Soroban Contracts provides an
upgradeable
module in the
contract-utils
package with two main components:
ComponentUse when
Upgradeable
Only the WASM binary needs to be updated — no storage migration required
UpgradeableMigratable
The WASM binary and specific storage entries need to be modified during the upgrade
The recommended way to use these is through derive macros:
#[derive(Upgradeable)]
and
#[derive(UpgradeableMigratable)]
. These macros handle the implementation of necessary functions and set the crate version from
Cargo.toml
as the binary version in WASM metadata, aligning with SEP-49 guidelines.
OpenZeppelin Stellar Soroban合约在
contract-utils
包中提供了一个
upgradeable
模块,包含两个主要组件:
组件使用场景
Upgradeable
仅需更新WASM二进制文件——无需存储迁移
UpgradeableMigratable
升级期间需要修改WASM二进制文件和特定存储条目
推荐通过派生宏来使用这些组件:
#[derive(Upgradeable)]
#[derive(UpgradeableMigratable)]
。这些宏会处理必要函数的实现,并将
Cargo.toml
中的crate版本设置为WASM元数据中的二进制版本,符合SEP-49规范。

Upgrade only

仅升级

Derive
Upgradeable
on the contract struct, then implement
UpgradeableInternal
with a single required method:
  • _require_auth(e: &Env, operator: &Address)
    — verify the operator is authorized to perform the upgrade (e.g., check against a stored owner address)
The
operator
parameter is the invoker of the upgrade function and can be used for role-based access control.
在合约结构体上派生
Upgradeable
,然后实现
UpgradeableInternal
,仅需实现一个必填方法:
  • _require_auth(e: &Env, operator: &Address)
    — 验证操作方是否有权执行升级(例如,与存储的所有者地址进行校验)
operator
参数是升级函数的调用者,可用于基于角色的访问控制。

Upgrade and migrate

升级与迁移

Derive
UpgradeableMigratable
on the contract struct, then implement
UpgradeableMigratableInternal
with:
  • An associated
    MigrationData
    type defining the data passed to the migration function
  • _require_auth(e, operator)
    — same authorization check as above
  • _migrate(e: &Env, data: &Self::MigrationData)
    — perform storage modifications using the provided migration data
The derive macro ensures that migration can only be invoked after a successful upgrade, preventing state inconsistencies and storage corruption.
在合约结构体上派生
UpgradeableMigratable
,然后实现
UpgradeableMigratableInternal
,需要包含:
  • 关联类型
    MigrationData
    ,用于定义传递给迁移函数的数据
  • _require_auth(e, operator)
    — 与上述相同的权限校验
  • _migrate(e: &Env, data: &Self::MigrationData)
    — 使用提供的迁移数据执行存储修改
派生宏确保迁移只能在升级成功后被调用,从而防止状态不一致和存储损坏。

Atomic upgrade and migration

原子化升级与迁移

Because the new implementation only takes effect after the current invocation completes, migration logic in the new contract cannot run in the same call as the upgrade. An auxiliary
Upgrader
contract wraps both calls atomically:
rust
use soroban_sdk::{contract, contractimpl, symbol_short, Address, BytesN, Env, Val};
use stellar_contract_utils::upgradeable::UpgradeableClient;

#[contract]
pub struct Upgrader;

#[contractimpl]
impl Upgrader {
    pub fn upgrade_and_migrate(
        env: Env,
        contract_address: Address,
        operator: Address,
        wasm_hash: BytesN<32>,
        migration_data: soroban_sdk::Vec<Val>,
    ) {
        operator.require_auth();
        let contract_client = UpgradeableClient::new(&env, &contract_address);
        contract_client.upgrade(&wasm_hash, &operator);
        env.invoke_contract::<()>(
            &contract_address,
            &symbol_short!("migrate"),
            migration_data,
        );
    }
}
Guard
upgrade_and_migrate
with proper access control (e.g.,
Ownable
from the
access
package with
#[only_owner]
).
If a rollback is required, the contract can be upgraded to a newer version where rollback-specific logic is defined and performed as a migration.
Examples: See the
examples/
directory of the stellar-contracts repository for full working integration examples of both
Upgradeable
and
UpgradeableMigratable
, including the
Upgrader
pattern.
由于新实现仅在当前调用完成后生效,新合约中的迁移逻辑无法与升级在同一个调用中运行。可以通过一个辅助的
Upgrader
合约将这两个调用原子化打包:
rust
use soroban_sdk::{contract, contractimpl, symbol_short, Address, BytesN, Env, Val};
use stellar_contract_utils::upgradeable::UpgradeableClient;

#[contract]
pub struct Upgrader;

#[contractimpl]
impl Upgrader {
    pub fn upgrade_and_migrate(
        env: Env,
        contract_address: Address,
        operator: Address,
        wasm_hash: BytesN<32>,
        migration_data: soroban_sdk::Vec<Val>,
    ) {
        operator.require_auth();
        let contract_client = UpgradeableClient::new(&env, &contract_address);
        contract_client.upgrade(&wasm_hash, &operator);
        env.invoke_contract::<()>(
            &contract_address,
            &symbol_short!("migrate"),
            migration_data,
        );
    }
}
upgrade_and_migrate
添加适当的访问控制(例如,使用
access
包中的
Ownable
并配合
#[only_owner]
)。
如果需要回滚,可以将合约升级到包含回滚特定逻辑的新版本,并通过迁移执行回滚操作。
示例: 请查看stellar-contracts仓库
examples/
目录,获取
Upgradeable
UpgradeableMigratable
的完整集成示例,包括
Upgrader
模式。

Access Control

访问控制

The
upgradeable
module deliberately does not embed access control itself. You must define authorization in the
_require_auth
method of
UpgradeableInternal
or
UpgradeableMigratableInternal
. Forgetting this allows anyone to replace your contract's code.
Common access control options:
  • Ownable — single owner, simplest pattern (available in the
    access
    package)
  • AccessControl / RBAC — role-based, finer granularity (available in the
    access
    package)
  • Multisig or governance — for production contracts managing significant value
upgradeable
模块特意没有内置访问控制功能。你必须在
UpgradeableInternal
UpgradeableMigratableInternal
_require_auth
方法中定义权限校验。如果遗漏这一步,任何人都可以替换你的合约代码。
常见的访问控制选项:
  • Ownable — 单所有者模式,最简单的方案(可在
    access
    包中获取)
  • AccessControl / RBAC — 基于角色的访问控制,粒度更细(可在
    access
    包中获取)
  • 多签或治理 — 适用于管理大额资产的生产级合约

Upgrade Safety

升级安全性

Caveats

注意事项

The framework structures the upgrade flow but does not perform deeper checks:
  • The new contract's constructor will not be invoked — any initialization must happen via migration or a separate call
  • There is no automatic check that the new contract includes an upgrade mechanism — an upgrade to a contract without one permanently loses upgradeability
  • Storage consistency is not verified — the new contract may inadvertently introduce storage mismatches
该框架对升级流程进行了结构化处理,但执行更深层次的检查:
  • 新合约的构造函数不会被调用——任何初始化操作必须通过迁移或单独的调用完成
  • 不会自动检查新合约是否包含升级机制——如果升级到没有该机制的合约,将永久失去可升级性
  • 不会验证存储一致性——新合约可能会无意中导致存储不匹配

Storage compatibility

存储兼容性

When replacing the WASM binary, existing storage is reinterpreted by the new code. Incompatible changes corrupt state:
  • Do not remove or rename existing storage keys
  • Do not change the type of values stored under existing keys
  • Adding new storage keys is safe
  • Soroban storage uses explicit string keys (e.g.,
    symbol_short!("OWNER")
    ), so key naming is critical — unlike EVM sequential slots, there is no ordering dependency
当替换WASM二进制文件时,现有存储会被新代码重新解析。不兼容的更改会损坏状态:
  • 不要删除或重命名现有存储键
  • 不要更改现有存储键对应的值类型
  • 添加新的存储键是安全的
  • Soroban存储使用显式字符串键(例如
    symbol_short!("OWNER")
    ),因此键的命名至关重要——与EVM的顺序插槽不同,它没有顺序依赖

Version tracking

版本跟踪

The derive macros automatically extract the crate version from
Cargo.toml
and embed it as the binary version in the WASM metadata, following SEP-49. This enables on-chain version tracking and can be used to coordinate upgrade paths.
派生宏会自动从
Cargo.toml
中提取crate版本,并按照SEP-49规范将其作为二进制版本嵌入到WASM元数据中。这实现了链上版本跟踪,并可用于协调升级路径。

Testing upgrade paths

测试升级路径

Before upgrading a production contract:
  • Deploy V1 on a local Soroban testnet (e.g.,
    stellar-cli
    with local network)
  • Write state with V1, upgrade to V2, and verify that all existing state reads correctly
  • Verify new functionality works as expected after the upgrade
  • Confirm access control — only authorized callers can invoke
    upgrade
  • Check that V2 includes an upgrade mechanism — otherwise upgradeability is permanently lost
  • Verify storage key compatibility — ensure no removals, renames, or type changes to existing keys
  • Test atomic upgrade-and-migrate using the
    Upgrader
    pattern if migration is needed
  • Manual review — there is no automated storage compatibility validation for Soroban; use the derive macros for safe upgrade scaffolding and rely on testnet testing
在升级生产级合约之前,请完成以下步骤:
  • 在本地Soroban测试网部署V1版本(例如使用
    stellar-cli
    搭建本地网络)
  • 使用V1版本写入状态,升级到V2版本,验证所有现有状态都能被正确读取
  • 验证升级后新功能是否按预期工作
  • 确认访问控制——仅授权调用者可以执行
    upgrade
    操作
  • 检查V2版本是否包含升级机制——否则将永久失去可升级性
  • 验证存储键兼容性——确保没有删除、重命名或更改现有存储键的类型
  • 如果需要迁移,使用
    Upgrader
    模式测试原子化升级与迁移
  • 人工审核——目前没有针对Soroban的自动化存储兼容性验证工具;请使用派生宏搭建安全的升级框架,并依赖测试网进行测试