Access Control
This example shows how you can use the implementation of access-control to provide rights for usage of specific smart contract functions.
Step 1: Import default implementation
With default Cargo.toml
,
you need to enable access-control
feature, embed modules data structures and implement them via #[openbrush::implementation]
macro
as described in that section.
The main trait is AccessControl
.
Step 2: Define constructor
Define constructor where you grant MINTER
role(or any another role) to the caller.
// You can manually set the number for the role.
// But better to use a hash of the variable name.
// It will generate a unique identifier of this role.
// And will reduce the chance to have overlapping roles.
const MINTER: RoleType = ink::selector_id!("MINTER");
impl Contract {
#[ink(constructor)]
pub fn new() -> Self {
let mut instance = Self::default();
let caller = instance.env().caller();
access_control::Internal::_init_with_admin(&mut instance, Some(caller));
// We grant minter role to caller in constructor, so he can mint/burn tokens
AccessControl::grant_role(&mut instance, MINTER, Some(caller)).expect("Should grant MINTER role");
instance
}
}
Step 3: Customize your contract
Customize it by adding access control logic. We will add a restricted_function
to Contract
implementation,
which will use the only_role
modifier with MINTER
parameter, which verifies that the caller has the MINTER
role.
#![cfg_attr(not(feature = "std"), no_std, no_main)]
#[openbrush::implementation(PSP34, PSP34Burnable, PSP34Mintable, AccessControl)]
#[openbrush::contract]
pub mod my_access_control {
use openbrush::{
modifiers,
traits::Storage,
};
#[ink(storage)]
#[derive(Default, Storage)]
pub struct Contract {
#[storage_field]
psp34: psp34::Data,
#[storage_field]
access: access_control::Data,
}
// You can manually set the number for the role.
// But better to use a hash of the variable name.
// It will generate a unique identifier of this role.
// And will reduce the chance to have overlapping roles.
const MINTER: RoleType = ink::selector_id!("MINTER");
#[default_impl(PSP34Burnable)]
#[modifiers(only_role(MINTER))]
fn burn() {}
#[default_impl(PSP34Mintable)]
#[modifiers(only_role(MINTER))]
fn mint() {}
impl Contract {
#[ink(constructor)]
pub fn new() -> Self {
let mut instance = Self::default();
let caller = instance.env().caller();
access_control::Internal::_init_with_admin(&mut instance, Some(caller));
// We grant minter role to caller in constructor, so he can mint/burn tokens
AccessControl::grant_role(&mut instance, MINTER, Some(caller)).expect("Should grant MINTER role");
instance
}
}
}
You can check an example of the usage of Access Control.