Upgradeable Contract

In Starknet, contracts are divided into two parts: contract classes and contract instances. This division follows a similar concept used in object-oriented programming languages, where we distinguish between the definition and implementation of objects.

A contract class is the definition of a contract: it specifies how the contract behaves. It contains essential information like the Cairo byte code, hint information, entry point names, and everything that defines its semantics unambiguously.

To identify different contract classes, Starknet assigns a unique identifier to each class: the class hash. A contract instance is a deployed contract that corresponds to a specific contract class. Think of it as an instance of an object in languages like Java.

Cada clase se identifica por su hash de clase, que es análogo a un nombre de clase en un lenguaje de programación orientado a objetos. Una instancia de contrato es un contrato desplegado correspondiente a una clase.

Puedes actualizar un contrato desplegado a una versión más reciente llamando a la función replace_class_syscall. Usando esta función, puedes actualizar el hash de clase asociado con un contrato desplegado, actualizando efectivamente su implementación. Sin embargo, esto no modificará el almacenamiento del contrato, por lo que todos los datos almacenados en el contrato seguirán siendo los mismos.

To illustrate this concept, let's consider an example with two contracts: UpgradeableContract_V0, and UpgradeableContract_V1. Start by deploying UpgradeableContract_V0 as the initial version. Next, send a transaction that invokes the upgrade function, with the class hash of UpgradeableContract_V1 as parameter to upgrade the class hash of the deployed contract to the UpgradeableContract_V1 one. Then, call the version method on the contract to see that the contract was upgraded to the V1 version.

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
        }
    }
}

Visita el contrato en Voyager o juega con él en Remix.

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
        }
    }
}

Visita el contrato en Voyager o juega con él en Remix.

Last change: 2023-10-12, commit: 90aa7c0