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

Add Ownable2Step contract #173

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ psp34 = ["openbrush_contracts/psp34"]
psp37 = ["openbrush_contracts/psp37"]
access_control = ["openbrush_contracts/access_control"]
ownable = ["openbrush_contracts/ownable"]
ownable_2_step = ["openbrush_contracts/ownable_2_step"]
payment_splitter = ["openbrush_contracts/payment_splitter"]
reentrancy_guard = ["openbrush_contracts/reentrancy_guard"]
pausable = ["openbrush_contracts/pausable"]
Expand Down
1 change: 1 addition & 0 deletions contracts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ psp34 = []
psp37 = []
access_control = []
ownable = []
ownable_2_step = ["ownable"]
payment_splitter = []
reentrancy_guard = []
pausable = []
Expand Down
2 changes: 2 additions & 0 deletions contracts/src/access/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@
pub mod access_control;
#[cfg(feature = "ownable")]
pub mod ownable;
#[cfg(feature = "ownable_2_step")]
pub mod ownable_2_step;
17 changes: 11 additions & 6 deletions contracts/src/access/ownable/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,16 @@ pub trait OwnableImpl: Storage<Data> + Internal {

#[modifiers(only_owner)]
fn renounce_ownership(&mut self) -> Result<(), OwnableError> {
let old_owner = self.data().owner.get_or_default();
self.data().owner.set(&None);
self._emit_ownership_transferred_event(old_owner, None);
self._transfer_ownership(None)?;
Ok(())
}

#[modifiers(only_owner)]
fn transfer_ownership(&mut self, new_owner: Option<AccountId>) -> Result<(), OwnableError> {
let old_owner = self.data().owner.get_or_default();
if new_owner == None {
return Err(OwnableError::NewOwnerIsNotSet)
}
self.data().owner.set(&new_owner);
self._emit_ownership_transferred_event(old_owner, new_owner);
self._transfer_ownership(new_owner)?;
Ok(())
}
}
Expand All @@ -67,6 +63,8 @@ pub trait Internal {
fn _emit_ownership_transferred_event(&self, _previous: Option<AccountId>, _new: Option<AccountId>);

fn _init_with_owner(&mut self, owner: AccountId);

fn _transfer_ownership(&mut self, new_owner: Option<AccountId>) -> Result<(), OwnableError>;
}

pub trait InternalImpl: Storage<Data> + Internal {
Expand All @@ -76,4 +74,11 @@ pub trait InternalImpl: Storage<Data> + Internal {
self.data().owner.set(&Some(owner));
Internal::_emit_ownership_transferred_event(self, None, Some(owner));
}

fn _transfer_ownership(&mut self, new_owner: Option<AccountId>) -> Result<(), OwnableError> {
let old_owner = self.data().owner.get_or_default();
self.data().owner.set(&new_owner);
Internal::_emit_ownership_transferred_event(self, old_owner, new_owner);
Ok(())
}
}
61 changes: 61 additions & 0 deletions contracts/src/access/ownable_2_step/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
pub use crate::{
ownable_2_step,
traits::ownable_2_step::*,
ownable,
ownable::*,
traits::ownable::*,
};
use openbrush::{
modifiers,
traits::{
AccountId,
Storage,
},
};

#[derive(Default, Debug)]
#[openbrush::storage_item]
pub struct Data {
#[lazy]
pub pending_owner: Option<AccountId>,
}

pub trait Ownable2StepImpl: Storage<Data> + Storage<ownable::Data> + Internal + OwnableImpl {
fn pending_owner(&self) -> Option<AccountId> {
self.data::<Data>().pending_owner.get_or_default()
}

#[modifiers(only_owner)]
fn transfer_ownership(&mut self, new_owner: AccountId) -> Result<(), OwnableError> {
self.data::<Data>().pending_owner.set(&Some(new_owner));
let owner = self.data::<ownable::Data>().owner.get_or_default();
self._emit_ownership_transferred_started_event(owner, Some(new_owner));
Ok(())
}

fn accept_ownership(&mut self) -> Result<(), OwnableError> {
let caller = Self::env().caller();
if self.data::<Data>().pending_owner.get_or_default() != Some(caller) {
return Err(OwnableError::OwnableUnauthorizedAccount)
}
self::Internal::_transfer_ownership(self, Some(caller))?;
Ok(())
}
}

pub trait Internal {
/// User must override this method in their contract.
fn _emit_ownership_transferred_started_event(&self, _previous: Option<AccountId>, _new: Option<AccountId>);

fn _transfer_ownership(&mut self, new_owner: Option<AccountId>) -> Result<(), OwnableError>;
}

pub trait InternalImpl: Storage<Data> + Internal + ownable::Internal {
fn _emit_ownership_transferred_started_event(&self, _previous: Option<AccountId>, _new: Option<AccountId>) {}

fn _transfer_ownership(&mut self, new_owner: Option<AccountId>) -> Result<(), OwnableError> {
self.data::<Data>().pending_owner.set(&None);
ownable::Internal::_transfer_ownership(self, new_owner)?;
Ok(())
}
}
2 changes: 2 additions & 0 deletions contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub mod diamond;
pub use access::access_control;
#[cfg(feature = "ownable")]
pub use access::ownable;
#[cfg(feature = "ownable_2_step")]
pub use access::ownable_2_step;
#[cfg(feature = "payment_splitter")]
pub use finance::payment_splitter;
#[cfg(feature = "timelock_controller")]
Expand Down
6 changes: 6 additions & 0 deletions contracts/src/traits/errors/flashloan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ impl From<OwnableError> for FlashBorrowerError {
OwnableError::NewOwnerIsNotSet => {
FlashBorrowerError::FlashloanRejected(String::from("O::NewOwnerIsNotSet"))
}
OwnableError::OwnableUnauthorizedAccount => {
FlashBorrowerError::FlashloanRejected(String::from("O::OwnableUnauthorizedAccount"))
}
}
}
}
Expand Down Expand Up @@ -119,6 +122,9 @@ impl From<OwnableError> for FlashLenderError {
match ownable {
OwnableError::CallerIsNotOwner => FlashLenderError::Custom(String::from("O::CallerIsNotOwner")),
OwnableError::NewOwnerIsNotSet => FlashLenderError::Custom(String::from("O::NewOwnerIsNotSet")),
OwnableError::OwnableUnauthorizedAccount => {
FlashLenderError::Custom(String::from("O::OwnableUnauthorizedAccount"))
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions contracts/src/traits/errors/ownable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@
pub enum OwnableError {
CallerIsNotOwner,
NewOwnerIsNotSet,
OwnableUnauthorizedAccount,
}
3 changes: 3 additions & 0 deletions contracts/src/traits/errors/payment_splitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ impl From<OwnableError> for PaymentSplitterError {
fn from(ownable: OwnableError) -> Self {
match ownable {
OwnableError::CallerIsNotOwner => PaymentSplitterError::Custom(String::from("O::CallerIsNotOwner")),
OwnableError::OwnableUnauthorizedAccount => {
PaymentSplitterError::Custom(String::from("O::OwnableUnauthorizedAccount"))
},
OwnableError::NewOwnerIsNotSet => PaymentSplitterError::Custom(String::from("O::NewOwnerIsNotSet")),
}
}
Expand Down
3 changes: 3 additions & 0 deletions contracts/src/traits/errors/psp22.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ impl From<OwnableError> for PSP22Error {
match ownable {
OwnableError::CallerIsNotOwner => PSP22Error::Custom(String::from("O::CallerIsNotOwner")),
OwnableError::NewOwnerIsNotSet => PSP22Error::Custom(String::from("O::NewOwnerIsNotSet")),
OwnableError::OwnableUnauthorizedAccount => {
PSP22Error::Custom(String::from("O::OwnableUnauthorizedAccount"))
}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions contracts/src/traits/errors/psp34.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ impl From<OwnableError> for PSP34Error {
match ownable {
OwnableError::CallerIsNotOwner => PSP34Error::Custom(String::from("O::CallerIsNotOwner")),
OwnableError::NewOwnerIsNotSet => PSP34Error::Custom(String::from("O::NewOwnerIsNotSet")),
OwnableError::OwnableUnauthorizedAccount => {
PSP34Error::Custom(String::from("O::OwnableUnauthorizedAccount"))
}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions contracts/src/traits/errors/psp37.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ impl From<OwnableError> for PSP37Error {
match ownable {
OwnableError::CallerIsNotOwner => PSP37Error::Custom(String::from("O::CallerIsNotOwner")),
OwnableError::NewOwnerIsNotSet => PSP37Error::Custom(String::from("O::NewOwnerIsNotSet")),
OwnableError::OwnableUnauthorizedAccount => {
PSP37Error::Custom(String::from("O::OwnableUnauthorizedAccount"))
}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions contracts/src/traits/errors/timelock_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ impl From<OwnableError> for TimelockControllerError {
match ownable {
OwnableError::CallerIsNotOwner => TimelockControllerError::Custom(String::from("O::CallerIsNotOwner")),
OwnableError::NewOwnerIsNotSet => TimelockControllerError::Custom(String::from("O::NewOwnerIsNotSet")),
OwnableError::OwnableUnauthorizedAccount => {
TimelockControllerError::Custom(String::from("O::OwnableUnauthorizedAccount"))
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions contracts/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub mod flashloan;
pub mod governance;
pub mod nonces;
pub mod ownable;
pub mod ownable_2_step;
pub mod pausable;
pub mod payment_splitter;
pub mod proxy;
Expand Down
36 changes: 36 additions & 0 deletions contracts/src/traits/ownable_2_step/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
pub use crate::traits::errors::OwnableError;
use openbrush::traits::AccountId;

#[openbrush::wrapper]
pub type Ownable2StepRef = dyn Ownable2Step;

/// Contract module which provides a 2 step ownership mechanism, where an
/// owner can transfer ownership to a new address which must then accept
/// the transfer before becoming the new owner.
#[openbrush::trait_definition]
pub trait Ownable2Step {
#[ink(message)]
fn pending_owner(&self) -> Option<AccountId>;
/// Transfers ownership of the contract to a `pending_owner`.
/// Can only be called by the current owner.
///
/// On success a `OwnershipTransferStarted` event is emitted.
///
/// # Errors
///
/// Panics with `CallerIsNotOwner` error if caller is not owner.
#[ink(message)]
fn transfer_ownership(&mut self, new_owner: AccountId) -> Result<(), OwnableError>;

/// Accepts ownership of the contract.
/// Can only be called by pending owner.
///
///
/// On success a `OwnershipTransferred` event is emitted.
///
/// # Errors
///
/// Panics with `CallerIsNotPendingOwner` error if caller is not pending owner.
#[ink(message)]
fn accept_ownership(&mut self) -> Result<(), OwnableError>;
}
9 changes: 9 additions & 0 deletions examples/ownable_2_step/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Ignore build artifacts from the local tests sub-crate.
/target/

# Ignore backup files creates by cargo fmt.
**/*.rs.bk

# Remove Cargo.lock when creating an executable, leave it for libraries
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
Cargo.lock
38 changes: 38 additions & 0 deletions examples/ownable_2_step/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[package]
name = "my_ownable_2_step"
version= "4.0.0-beta.1"
authors = ["Brushfam <[email protected]>"]
edition = "2021"

[dependencies]
ink = { version = "4.3.0", default-features = false}

scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true }

# These dependencies
openbrush = { path = "../..", default-features = false, features = ["psp37", "ownable_2_step"] }

[dev-dependencies]
ink_e2e = "4.3.0"
test_helpers = { path = "../test_helpers", default-features = false }

[lib]
name = "my_ownable_2_step"
path = "lib.rs"


[features]
default = ["std"]
std = [
"ink/std",
"scale/std",
"scale-info/std",
# These dependencies
"openbrush/std",
]
ink-as-dependency = []
e2e-tests = []

[profile.dev]
codegen-units = 16
38 changes: 38 additions & 0 deletions examples/ownable_2_step/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#![cfg_attr(not(feature = "std"), no_std, no_main)]

#[openbrush::implementation(Ownable2Step, Ownable, PSP37, PSP37Burnable, PSP37Mintable)]
#[openbrush::contract]
pub mod ownable_2_step {
use openbrush::{
modifiers,
traits::{Storage},
};

#[ink(storage)]
#[derive(Default, Storage)]
pub struct Contract {
#[storage_field]
psp37: psp37::Data,
#[storage_field]
ownable: ownable::Data,
#[storage_field]
ownable_2_step: ownable_2_step::Data,
}

impl Contract {
#[ink(constructor)]
pub fn new() -> Self {
let mut instance = Self::default();
ownable::Internal::_init_with_owner(&mut instance, Self::env().caller());
instance
}
}

#[default_impl(PSP37Mintable)]
#[modifiers(only_owner)]
fn mint(&mut self) {}

#[default_impl(PSP37Burnable)]
#[modifiers(only_owner)]
fn burn(&mut self) {}
}
1 change: 1 addition & 0 deletions lang/codegen/src/implementation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ pub fn generate(attrs: TokenStream, input: TokenStream) -> TokenStream {
"PSP37Mintable" => impl_psp37_mintable(&mut impl_args),
"PSP37Enumerable" => impl_psp37_enumerable(&mut impl_args),
"Ownable" => impl_ownable(&mut impl_args),
"Ownable2Step" => impl_ownable_2_step(&mut impl_args),
"PaymentSplitter" => impl_payment_splitter(&mut impl_args),
"AccessControl" => impl_access_control(&mut impl_args),
"AccessControlEnumerable" => impl_access_control_enumerable(&mut impl_args),
Expand Down
Loading
Loading