可升级合约
在Starknet中,合约分为两个部分:合约类和合约实例。 这种划分遵循了面向对象编程语言中的类和实例的概念。 这样,我们区分了对象的定义和实现。
合约类是合约的定义:它指定了合约的行为方式。 合约类包含了关键信息,如Cairo字节码、提示信息、入口点名称等, 以及一切明确定义合约类语义的内容。
为了识别不同的合约类,Starknet为每个类分配一个唯一的标识符:类哈希。 合约实例是对应于特定合约类的已部署合约。 可以将其视为在诸如Java等语言中对象的一个实例。
每个类由其类哈希值标识,类似于面向对象编程语言中的类名。合约实例是对应于某个类的已部署合约。
当调用replace_class_syscall
函数,你可以将已部署的合约升级到更新的版本。通过使用这个函数,你可以更新与已部署合约相关联的类哈希,从而有效地升级合约的实现。然而,这不会修改合约中的存储,因此合约中存储的所有数据将保持不变。
为了说明这个概念,让我们以两个合约为例:UpgradeableContract_V0
和UpgradeableContract_V1
。
首先,部署UpgradeableContract_V0
作为初始版本。接下来,发送一个调用upgrade
函数的交易,将部署合约的类哈希升级为UpgradeableContract_V1
的类哈希。然后,调用合约上的version
方法,查看合约是否已升级到V1版本。
use starknet::class_hash::ClassHash;
#[starknet::interface]
trait IUpgradeableContract<TContractState> {
fn upgrade(ref self: TContractState, impl_hash: ClassHash);
fn version(self: @TContractState) -> u8;
}
#[starknet::contract]
mod UpgradeableContract_V0 {
use starknet::class_hash::ClassHash;
use starknet::SyscallResultTrait;
#[storage]
struct Storage {}
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
Upgraded: Upgraded
}
#[derive(Drop, starknet::Event)]
struct Upgraded {
implementation: ClassHash
}
#[abi(embed_v0)]
impl UpgradeableContract of super::IUpgradeableContract<ContractState> {
fn upgrade(ref self: ContractState, impl_hash: ClassHash) {
assert(!impl_hash.is_zero(), 'Class hash cannot be zero');
starknet::replace_class_syscall(impl_hash).unwrap_syscall();
self.emit(Event::Upgraded(Upgraded { implementation: impl_hash }))
}
fn version(self: @ContractState) -> u8 {
0
}
}
}
use starknet::class_hash::ClassHash;
#[starknet::interface]
trait IUpgradeableContract<TContractState> {
fn upgrade(ref self: TContractState, impl_hash: ClassHash);
fn version(self: @TContractState) -> u8;
}
#[starknet::contract]
mod UpgradeableContract_V1 {
use starknet::class_hash::ClassHash;
use starknet::SyscallResultTrait;
#[storage]
struct Storage {}
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
Upgraded: Upgraded
}
#[derive(Drop, starknet::Event)]
struct Upgraded {
implementation: ClassHash
}
#[abi(embed_v0)]
impl UpgradeableContract of super::IUpgradeableContract<ContractState> {
fn upgrade(ref self: ContractState, impl_hash: ClassHash) {
assert(!impl_hash.is_zero(), 'Class hash cannot be zero');
starknet::replace_class_syscall(impl_hash).unwrap_syscall();
self.emit(Event::Upgraded(Upgraded { implementation: impl_hash }))
}
fn version(self: @ContractState) -> u8 {
1
}
}
}