Skip to content

Commit

Permalink
Migrate Upgrades to component (OpenZeppelin#792)
Browse files Browse the repository at this point in the history
* fix: link (OpenZeppelin#545)

* feat: update logic to component

* feat: update docs

* feat: apply review updates

* feat: fix pop_log util to check no extra keys as well

* docs: flatten events in examples
  • Loading branch information
ericnordelo authored Oct 24, 2023
1 parent 28a1ee7 commit 6269c5d
Show file tree
Hide file tree
Showing 20 changed files with 220 additions and 159 deletions.
5 changes: 5 additions & 0 deletions docs/modules/ROOT/pages/access.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ mod MyContract {
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
OwnableEvent: ownable_component::Event
}
Expand Down Expand Up @@ -163,7 +164,9 @@ mod MyContract {
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
AccessControlEvent: accesscontrol_component::Event,
#[flat]
SRC5Event: src5_component::Event
}
Expand Down Expand Up @@ -240,7 +243,9 @@ mod MyContract {
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
AccessControlEvent: accesscontrol_component::Event,
#[flat]
SRC5Event: src5_component::Event
}
Expand Down
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/api/upgrades.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ NOTE: This function is usually protected by an xref:access.adoc[Access Control]
use openzeppelin::upgrades::upgradeable::Upgradeable;
```

Upgradeable contract module.
Upgradeable component.

[.contract-index]
.Internal Functions
Expand Down
1 change: 1 addition & 0 deletions docs/modules/ROOT/pages/introspection.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ mod MyContract {
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
SRC5Event: src5_component::Event
}
Expand Down
46 changes: 33 additions & 13 deletions docs/modules/ROOT/pages/upgrades.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ fn _upgrade(new_class_hash: ClassHash) {

NOTE: If a contract is deployed without this mechanism, its class hash can still be replaced through {library_calls}.

== `Upgradeable` module
== `Upgradeable` component

OpenZeppelin Contracts for Cairo provides {upgradeable} to add upgradeability support to your contracts.

Expand All @@ -49,35 +49,55 @@ NOTE: We will be using the following module to implement the {i_upgradeable} int
----
#[starknet::contract]
mod UpgradeableContract {
use openzeppelin::access::ownable::Ownable;
use openzeppelin::upgrades::Upgradeable;
use openzeppelin::access::ownable::Ownable as ownable_component;
use openzeppelin::upgrades::Upgradeable as upgradeable_component;
use openzeppelin::upgrades::interface::IUpgradeable;
use starknet::ClassHash;
use starknet::ContractAddress;
component!(path: ownable_component, storage: ownable, event: OwnableEvent);
component!(path: upgradeable_component, storage: upgradeable, event: UpgradeableEvent);
/// Ownable
#[abi(embed_v0)]
impl OwnableImpl = ownable_component::OwnableImpl<ContractState>;
impl OwnableInternalImpl = ownable_component::InternalImpl<ContractState>;
/// Upgradeable
impl UpgradeableInternalImpl = upgradeable_component::InternalImpl<ContractState>;
#[storage]
struct Storage {}
struct Storage {
#[substorage(v0)]
ownable: ownable_component::Storage,
#[substorage(v0)]
upgradeable: upgradeable_component::Storage
}
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
OwnableEvent: ownable_component::Event,
#[flat]
UpgradeableEvent: upgradeable_component::Event
}
#[constructor]
fn constructor(self: @ContractState, owner: ContractAddress) {
let mut unsafe_state = Ownable::unsafe_new_contract_state();
Ownable::InternalImpl::initializer(ref unsafe_state, owner);
fn constructor(ref self: ContractState, owner: ContractAddress) {
self.ownable.initializer(owner);
}
#[external(v0)]
impl UpgradeableImpl of IUpgradeable<ContractState> {
fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {
// This function can only be called by the owner
let ownable_state = Ownable::unsafe_new_contract_state();
Ownable::InternalImpl::assert_only_owner(@ownable_state);
self.ownable.assert_only_owner();
// Replace the class hash upgrading the contract
let mut upgradeable_state = Upgradeable::unsafe_new_contract_state();
Upgradeable::InternalImpl::_upgrade(ref upgradeable_state, new_class_hash);
self.upgradeable._upgrade(new_class_hash);
}
}
(...)
}
----

Expand Down
3 changes: 1 addition & 2 deletions src/tests/mocks.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,4 @@ mod snake20_mock;
mod snake721_mock;
mod snake_account_mock;
mod src5_mocks;
mod upgrades_v1;
mod upgrades_v2;
mod upgrades_mocks;
6 changes: 6 additions & 0 deletions src/tests/mocks/accesscontrol_mocks.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ mod DualCaseAccessControlMock {
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
AccessControlEvent: accesscontrol_component::Event,
#[flat]
SRC5Event: src5_component::Event
}

Expand Down Expand Up @@ -69,7 +71,9 @@ mod SnakeAccessControlMock {
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
AccessControlEvent: accesscontrol_component::Event,
#[flat]
SRC5Event: src5_component::Event
}

Expand Down Expand Up @@ -109,7 +113,9 @@ mod CamelAccessControlMock {
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
AccessControlEvent: accesscontrol_component::Event,
#[flat]
SRC5Event: src5_component::Event
}

Expand Down
1 change: 1 addition & 0 deletions src/tests/mocks/initializable_mock.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod InitializableMock {
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
InitializableEvent: initializable_component::Event
}
}
3 changes: 3 additions & 0 deletions src/tests/mocks/ownable_mocks.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod DualCaseOwnableMock {
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
OwnableEvent: ownable_component::Event
}

Expand Down Expand Up @@ -50,6 +51,7 @@ mod SnakeOwnableMock {
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
OwnableEvent: ownable_component::Event
}

Expand Down Expand Up @@ -80,6 +82,7 @@ mod CamelOwnableMock {
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
OwnableEvent: ownable_component::Event
}

Expand Down
1 change: 1 addition & 0 deletions src/tests/mocks/pausable_mock.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod PausableMock {
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
PausableEvent: pausable_component::Event
}
}
1 change: 1 addition & 0 deletions src/tests/mocks/reentrancy_mock.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ mod ReentrancyMock {
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
ReentrancyGuardEvent: reentrancy_guard_component::Event
}

Expand Down
3 changes: 3 additions & 0 deletions src/tests/mocks/src5_mocks.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod DualCaseSRC5Mock {
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
SRC5Event: src5_component::Event
}
}
Expand All @@ -43,6 +44,7 @@ mod SnakeSRC5Mock {
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
SRC5Event: src5_component::Event
}
}
Expand All @@ -65,6 +67,7 @@ mod CamelSRC5Mock {
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
SRC5Event: src5_component::Event
}
}
Expand Down
128 changes: 128 additions & 0 deletions src/tests/mocks/upgrades_mocks.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// These contracts are mocks used to test the core functionality of the upgrade functions.
// The functions are NOT PROTECTED.
// DO NOT USE IN PRODUCTION.

use starknet::ClassHash;

#[starknet::interface]
trait IUpgradesV1<TState> {
fn upgrade(ref self: TState, new_class_hash: ClassHash);
fn set_value(ref self: TState, val: felt252);
fn get_value(self: @TState) -> felt252;
fn remove_selector(self: @TState);
}

trait UpgradesV1Trait<TState> {
fn set_value(ref self: TState, val: felt252);
fn get_value(self: @TState) -> felt252;
fn remove_selector(self: @TState);
}

#[starknet::contract]
mod UpgradesV1 {
use openzeppelin::upgrades::Upgradeable as upgradeable_component;
use starknet::ClassHash;
use starknet::ContractAddress;

component!(path: upgradeable_component, storage: upgradeable, event: UpgradeableEvent);

impl InternalImpl = upgradeable_component::InternalImpl<ContractState>;

#[storage]
struct Storage {
#[substorage(v0)]
upgradeable: upgradeable_component::Storage,
value: felt252
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
UpgradeableEvent: upgradeable_component::Event
}

#[external(v0)]
fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {
self.upgradeable._upgrade(new_class_hash);
}

#[external(v0)]
impl UpgradesV1Impl of super::UpgradesV1Trait<ContractState> {
fn set_value(ref self: ContractState, val: felt252) {
self.value.write(val);
}

fn get_value(self: @ContractState) -> felt252 {
self.value.read()
}

fn remove_selector(self: @ContractState) {}
}
}

#[starknet::interface]
trait IUpgradesV2<TState> {
fn upgrade(ref self: TState, new_class_hash: ClassHash);
fn set_value(ref self: TState, val: felt252);
fn set_value2(ref self: TState, val: felt252);
fn get_value(self: @TState) -> felt252;
fn get_value2(self: @TState) -> felt252;
}

trait UpgradesV2Trait<TState> {
fn set_value(ref self: TState, val: felt252);
fn set_value2(ref self: TState, val: felt252);
fn get_value(self: @TState) -> felt252;
fn get_value2(self: @TState) -> felt252;
}

#[starknet::contract]
mod UpgradesV2 {
use openzeppelin::upgrades::Upgradeable as upgradeable_component;
use starknet::ClassHash;
use starknet::ContractAddress;

component!(path: upgradeable_component, storage: upgradeable, event: UpgradeableEvent);

impl InternalImpl = upgradeable_component::InternalImpl<ContractState>;

#[storage]
struct Storage {
#[substorage(v0)]
upgradeable: upgradeable_component::Storage,
value: felt252,
value2: felt252
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
UpgradeableEvent: upgradeable_component::Event
}

#[external(v0)]
fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {
self.upgradeable._upgrade(new_class_hash);
}

#[external(v0)]
impl UpgradesV2Impl of super::UpgradesV2Trait<ContractState> {
fn set_value(ref self: ContractState, val: felt252) {
self.value.write(val);
}

fn set_value2(ref self: ContractState, val: felt252) {
self.value2.write(val);
}

fn get_value(self: @ContractState) -> felt252 {
self.value.read()
}

fn get_value2(self: @ContractState) -> felt252 {
self.value2.read()
}
}
}
Loading

0 comments on commit 6269c5d

Please sign in to comment.