Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate Initializable to component #764

Merged
merged 10 commits into from
Oct 12, 2023
Merged
17 changes: 13 additions & 4 deletions src/security/initializable.cairo
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts for Cairo v0.7.0 (security/initializable.cairo)

#[starknet::contract]
/// # Initializable Component
///
/// The Initializable component provides a simple mechanism that executes
/// logic once and only once. This can be useful for setting a contract's
/// initial state in scenarios where a constructor cannot be used.
#[starknet::component]
mod Initializable {
#[storage]
struct Storage {
Expand All @@ -13,12 +18,16 @@ mod Initializable {
}

#[generate_trait]
impl InternalImpl of InternalTrait {
fn is_initialized(self: @ContractState) -> bool {
impl InternalImpl<
TContractState, +HasComponent<TContractState>
> of InternalTrait<TContractState> {
/// Returns true if the using contract executed `initialize`.
fn is_initialized(self: @ComponentState<TContractState>) -> bool {
self.Initializable_initialized.read()
}

fn initialize(ref self: ContractState) {
/// Ensures the calling function can only be called once.
fn initialize(ref self: ComponentState<TContractState>) {
assert(!self.is_initialized(), Errors::INITIALIZED);
self.Initializable_initialized.write(true);
}
Expand Down
1 change: 1 addition & 0 deletions src/tests/mocks.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod dual_ownable_mocks;
mod erc20_panic;
mod erc721_panic_mock;
mod erc721_receiver;
mod initializable_mock;
mod non_implementing_mock;
mod reentrancy_attacker_mock;
mod reentrancy_mock;
Expand Down
20 changes: 20 additions & 0 deletions src/tests/mocks/initializable_mock.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#[starknet::contract]
mod InitializableMock {
use openzeppelin::security::initializable::Initializable as initializable_component;

component!(path: initializable_component, storage: initializable, event: InitializableEvent);

impl InternalImpl = initializable_component::InternalImpl<ContractState>;

#[storage]
struct Storage {
#[substorage(v0)]
initializable: initializable_component::Storage
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
InitializableEvent: initializable_component::Event
}
}
16 changes: 8 additions & 8 deletions src/tests/security/test_initializable.cairo
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
use openzeppelin::security::initializable::Initializable::InternalImpl;
use openzeppelin::security::initializable::Initializable;
use openzeppelin::tests::mocks::initializable_mock::InitializableMock;

fn STATE() -> Initializable::ContractState {
Initializable::contract_state_for_testing()
fn STATE() -> InitializableMock::ContractState {
InitializableMock::contract_state_for_testing()
}

#[test]
#[available_gas(2000000)]
fn test_initialize() {
let mut state = STATE();
assert(!InternalImpl::is_initialized(@state), 'Should not be initialized');
InternalImpl::initialize(ref state);
assert(InternalImpl::is_initialized(@state), 'Should be initialized');
assert(!state.initializable.is_initialized(), 'Should not be initialized');
state.initializable.initialize();
assert(state.initializable.is_initialized(), 'Should be initialized');
}

#[test]
#[available_gas(2000000)]
#[should_panic(expected: ('Initializable: is initialized',))]
fn test_initialize_when_initialized() {
let mut state = STATE();
InternalImpl::initialize(ref state);
InternalImpl::initialize(ref state);
state.initializable.initialize();
state.initializable.initialize();
}