重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
upgrade-stellar-contracts by openzeppelin/openzeppelin-skills
npx skills add https://github.com/openzeppelin/openzeppelin-skills --skill upgrade-stellar-contractsSoroban 合约默认是可变的。可变性指的是智能合约修改其自身 WASM 字节码的能力,从而改变其函数接口、执行逻辑或元数据。Soroban 提供了一个内置的、协议级别的机制用于合约升级——无需代理模式。
合约如果被明确设计为可升级,则可以自行升级。反之,一个合约只需不提供任何升级函数即可变得不可变。这与 EVM 代理模式有根本性的不同:
| Soroban | EVM (代理模式) | Starknet
---|---|---|---
机制 | 原生 WASM 字节码替换 | 代理通过 delegatecall 调用实现合约 | replace_class_syscall 原地交换类哈希
是否需要代理合约 | 否——合约自行升级 | 是——一个代理位于实现合约之前 | 否——合约自行升级
存储位置 | 直接属于合约 | 位于代理中,通过 delegatecall 访问 | 直接属于合约
选择不可变性 | 不暴露升级函数 | 不部署代理 | 不调用该 syscall
协议级别可升级性的一个优势是,与需要代理合约和 delegatecall 转发的平台相比,风险面显著减少。
新的实现只有在当前调用完成后才会生效。这意味着如果迁移逻辑定义在新的实现中,它不能在升级的同一调用内执行。一个辅助的 Upgrader 合约可以包装这两个调用以实现原子性(见下文)。
OpenZeppelin Stellar Soroban Contracts 在 contract-utils 包中提供了一个 模块,包含两个主要组件:
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
upgradeable| 组件 | 使用场景 |
|---|---|
Upgradeable | 仅需要更新 WASM 二进制文件——无需存储迁移 |
UpgradeableMigratable | 在升级过程中需要修改 WASM 二进制文件和特定的存储条目 |
推荐的使用方式是通过派生宏:#[derive(Upgradeable)] 和 #[derive(UpgradeableMigratable)]。这些宏处理必要函数的实现,并将 Cargo.toml 中的 crate 版本设置为 WASM 元数据中的二进制版本,符合 SEP-49 指南。
在合约结构体上派生 Upgradeable,然后实现 UpgradeableInternal,它有一个必需的方法:
_require_auth(e: &Env, operator: &Address) —— 验证操作员是否有权执行升级(例如,对照存储的所有者地址进行检查)operator 参数是升级函数的调用者,可用于基于角色的访问控制。
在合约结构体上派生 UpgradeableMigratable,然后实现 UpgradeableMigratableInternal,包含:
MigrationData 类型,定义传递给迁移函数的数据_require_auth(e, operator) —— 与上述相同的授权检查_migrate(e: &Env, data: &Self::MigrationData) —— 使用提供的迁移数据执行存储修改派生宏确保迁移只能在成功升级之后被调用,防止状态不一致和存储损坏。
由于新的实现只在当前调用完成后生效,新合约中的迁移逻辑不能在升级的同一调用中运行。一个辅助的 Upgrader 合约可以原子性地包装这两个调用:
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模式。
upgradeable 模块故意没有嵌入访问控制本身。您必须在 UpgradeableInternal 或 UpgradeableMigratableInternal 的 _require_auth 方法中定义授权。忘记这一点将允许任何人替换您合约的代码。
常见的访问控制选项:
access 包中可用)access 包中可用)该框架构建了升级流程,但不执行更深层次的检查:
当替换 WASM 二进制文件时,现有存储会被新代码重新解释。不兼容的更改会破坏状态:
symbol_short!("OWNER")),因此键的命名至关重要 —— 与 EVM 的顺序槽不同,这里没有顺序依赖派生宏会自动从 Cargo.toml 中提取 crate 版本,并将其作为二进制版本嵌入到 WASM 元数据中,遵循 SEP-49。这使得链上版本跟踪成为可能,并可用于协调升级路径。
在升级生产合约之前:
stellar-cli)上部署 V1upgradeUpgrader 模式测试原子性升级和迁移每周安装量
79
仓库
GitHub 星标数
159
首次出现
2026年3月5日
安全审计
安装于
opencode77
cursor76
gemini-cli76
codex76
cline76
kimi-cli76
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:
| Soroban | EVM (proxy pattern) | Starknet
---|---|---|---
Mechanism | Native WASM bytecode replacement | Proxy delegatecalls to implementation contract | replace_class_syscall swaps class hash in-place
Proxy contract needed | No — the contract upgrades itself | Yes — a proxy sits in front of the implementation | No — the contract upgrades itself
Storage location | Belongs to the contract directly | Lives in the proxy, accessed via delegatecall | Belongs to the contract directly
Opt-in to immutability | Don't expose an upgrade function | Don't deploy a proxy | Don'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).
OpenZeppelin Stellar Soroban Contracts provides an upgradeable module in the contract-utils package with two main components:
| Component | Use 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.
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.
Derive UpgradeableMigratable on the contract struct, then implement UpgradeableMigratableInternal with:
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 dataThe derive macro ensures that migration can only be invoked after a successful upgrade, preventing state inconsistencies and storage corruption.
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:
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 bothUpgradeableandUpgradeableMigratable, including theUpgraderpattern.
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:
access package)access package)The framework structures the upgrade flow but does not perform deeper checks:
When replacing the WASM binary, existing storage is reinterpreted by the new code. Incompatible changes corrupt state:
symbol_short!("OWNER")), so key naming is critical — unlike EVM sequential slots, there is no ordering dependencyThe 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.
Before upgrading a production contract:
stellar-cli with local network)upgradeUpgrader pattern if migration is neededWeekly Installs
79
Repository
GitHub Stars
159
First Seen
Mar 5, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
opencode77
cursor76
gemini-cli76
codex76
cline76
kimi-cli76
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
123,700 周安装