Skip to content

Commit

Permalink
feat: add upgradable component
Browse files Browse the repository at this point in the history
  • Loading branch information
Harsh Bajpai authored and Harsh Bajpai committed Oct 17, 2023
1 parent 772d2e0 commit 4879b20
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 9 deletions.
2 changes: 1 addition & 1 deletion crates/contracts/src/components.cairo
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
mod ownable;
mod upgradeable;
mod upgradable;
39 changes: 39 additions & 0 deletions crates/contracts/src/components/upgradable.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use starknet::{replace_class_syscall, ClassHash};

#[starknet::interface]
trait IUpgradable<TContractState> {
fn upgrade_contract(ref self: TContractState, class_hash: ClassHash);
}


#[starknet::component]
mod upgradable_component {
use starknet::ClassHash;
use starknet::info::get_caller_address;

#[storage]
struct Storage {}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
ContractUpgrated: ContractUpgrated
}

#[derive(Drop, starknet::Event)]
struct ContractUpgrated {
new_class_hash: ClassHash
}

#[embeddable_as(UpgradableImpl)]
impl Upgradable<
TContractState, +HasComponent<TContractState>
> of super::IUpgradable<ComponentState<TContractState>> {
fn upgrade_contract(
ref self: ComponentState<TContractState>, class_hash: starknet::ClassHash
) {
starknet::replace_class_syscall(class_hash);
self.emit(ContractUpgrated { new_class_hash: class_hash });
}
}
}
3 changes: 0 additions & 3 deletions crates/contracts/src/components/upgradeable.cairo

This file was deleted.

15 changes: 11 additions & 4 deletions crates/contracts/src/kakarot_core/kakarot.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ struct ContractAccountStorage {
mod KakarotCore {
use contracts::components::ownable::ownable_component::InternalTrait;
use contracts::components::ownable::{ownable_component};
use contracts::components::upgradable::IUpgradable;
use contracts::components::upgradable::{upgradable_component};
use contracts::kakarot_core::interface::IKakarotCore;
use contracts::kakarot_core::interface;
use core::hash::{HashStateExTrait, HashStateTrait};
Expand All @@ -35,12 +37,15 @@ mod KakarotCore {
use utils::traits::U256TryIntoContractAddress;

component!(path: ownable_component, storage: ownable, event: OwnableEvent);
component!(path: upgradable_component, storage: upgradable, event: UpgradableEvent);

#[abi(embed_v0)]
impl OwnableImpl = ownable_component::Ownable<ContractState>;

impl OwnableInternalImpl = ownable_component::InternalImpl<ContractState>;

impl UpgradableImpl = upgradable_component::UpgradableImpl<ContractState>;

#[storage]
struct Storage {
/// Kakarot storage for accounts: Externally Owned Accounts (EOA) and Contract Accounts (CA)
Expand All @@ -64,12 +69,15 @@ mod KakarotCore {
// Components
#[substorage(v0)]
ownable: ownable_component::Storage,
#[substorage(v0)]
upgradable: upgradable_component::Storage,
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
OwnableEvent: ownable_component::Event,
UpgradableEvent: upgradable_component::Event,
EOADeployed: EOADeployed,
}

Expand Down Expand Up @@ -248,9 +256,9 @@ mod KakarotCore {

/// Upgrade the KakarotCore smart contract
/// Using replace_class_syscall
fn upgrade(
ref self: ContractState, new_class_hash: ClassHash
) { //TODO: implement upgrade logic
fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {
self.ownable.assert_only_owner();
self.upgradable.upgrade_contract(new_class_hash);
}
}

Expand All @@ -269,4 +277,3 @@ mod KakarotCore {
}
}
}

18 changes: 18 additions & 0 deletions crates/contracts/src/tests/test_kakarot_core.cairo
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use contracts::components::ownable::ownable_component;
use contracts::kakarot_core::{interface::IExtendedKakarotCoreDispatcherImpl, KakarotCore};
use contracts::tests::test_upgradeable::{
MockContractUpdatableV1, IMockContractUpdatableDispatcher, IMockContractUpdatableDispatcherTrait
};
use contracts::tests::utils;
use debug::PrintTrait;
use eoa::externally_owned_account::ExternallyOwnedAccount;
Expand Down Expand Up @@ -97,3 +100,18 @@ fn test_kakarot_core_compute_starknet_address() {
assert(eoa_starknet_address == expected_starknet_address, 'wrong starknet address');
}

#[test]
#[available_gas(20000000)]
fn test_kakarot_core_upgrade_contract() {
let kakarot_core = utils::deploy_kakarot_core(test_utils::native_token());
let class_hash: ClassHash = MockContractUpdatableV1::TEST_CLASS_HASH.try_into().unwrap();

testing::set_contract_address(utils::other_starknet_address());
kakarot_core.upgrade(class_hash);

let version = IMockContractUpdatableDispatcher {
contract_address: kakarot_core.contract_address
}
.version();
assert(version == 1, 'version is not 1');
}
99 changes: 98 additions & 1 deletion crates/contracts/src/tests/test_upgradeable.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,100 @@
// TODO
use MockContractUpdatableV0::HasComponentImpl_upgradable_component;
use contracts::components::upgradable::{IUpgradableDispatcher, IUpgradableDispatcherTrait};
use contracts::components::upgradable::{upgradable_component};
use contracts::tests::utils;
use debug::PrintTrait;
use serde::Serde;
use starknet::{deploy_syscall, ClassHash, ContractAddress, testing};

use upgradable_component::{UpgradableImpl};

#[starknet::interface]
trait IMockContractUpdatable<TContractState> {
fn version(self: @TContractState) -> felt252;
}

#[starknet::contract]
mod MockContractUpdatableV0 {
use contracts::components::upgradable::{upgradable_component};
use super::IMockContractUpdatable;
component!(path: upgradable_component, storage: upgradable, event: UpgradableEvent);

#[abi(embed_v0)]
impl UpgradableImpl = upgradable_component::UpgradableImpl<ContractState>;

#[storage]
struct Storage {
#[substorage(v0)]
upgradable: upgradable_component::Storage
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
UpgradableEvent: upgradable_component::Event
}

#[external(v0)]
impl MockContractUpdatableImpl of IMockContractUpdatable<ContractState> {
fn version(self: @ContractState) -> felt252 {
0
}
}
}

type TestingState = upgradable_component::ComponentState<MockContractUpdatableV0::ContractState>;

impl TestingStateDefault of Default<TestingState> {
fn default() -> TestingState {
upgradable_component::component_state_for_testing()
}
}

#[test]
#[available_gas(500000)]
fn test_upgradable_update_contract() {
let (contract_address, _) = deploy_syscall(
MockContractUpdatableV0::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false
)
.unwrap();

let version = IMockContractUpdatableDispatcher { contract_address: contract_address }.version();

assert(version == 0, 'version is not 0');

let mut call_data: Array<felt252> = array![];

let new_class_hash: ClassHash = MockContractUpdatableV1::TEST_CLASS_HASH.try_into().unwrap();

IUpgradableDispatcher { contract_address: contract_address }.upgrade_contract(new_class_hash);

let version = IMockContractUpdatableDispatcher { contract_address: contract_address }.version();
assert(version == 1, 'version is not 1');
}


#[starknet::contract]
mod MockContractUpdatableV1 {
use contracts::components::upgradable::{upgradable_component};
use super::IMockContractUpdatable;
component!(path: upgradable_component, storage: upgradable, event: UpgradableEvent);

#[storage]
struct Storage {
#[substorage(v0)]
upgradable: upgradable_component::Storage
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
UpgradableEvent: upgradable_component::Event
}

#[external(v0)]
impl MockContractUpdatableImpl of IMockContractUpdatable<ContractState> {
fn version(self: @ContractState) -> felt252 {
1
}
}
}

0 comments on commit 4879b20

Please sign in to comment.