All you need to know about openzeppelin access control

what is access control

Alt access control

Basiclly,access control means “who is allowed to do this thing”.It’s quite important for smart contract to specify a particular address who can totally control the whole system.Therefor it’s critical to fully understand how to use access control before using it in your project or just copy some example code from somewhere.
In openzeppelin there are mainly two ways to implementing access control.

  • Single onlyOwner role
  • Role-Based Access Control (RBAC)

Let’s dive into how to master this 2 ways.

master how to use it by practice

OnlyOwner

1
2
3
4
5
6
7
8
9
10
11
12
13
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/Ownable.sol";

contract MyContract is Ownable {
function normalThing() public {
// anyone can call this normalThing()
}

function specialThing() public onlyOwner {
// only the owner can call specialThing()!
}
}

let’s create two user for our test.

  • owner address
  • arbitrary address
1
2
address owner = address(0x100);
address arbitrary = address(0x101);

and the we use owner address to create the smart contract,it’s quite simple you don’t even need to pass any parameters.openzeppelin Ownable would set msg.sender as it’s owner as default.we use foundry for testing,if you’re not familiar with foundry,go and learn it first before proceeding further.

Firstly,we use owner address create our smart contract above in foundry test contract setUp function.

1
2
3
4
5
//let's set address owner to msg.sender.
vm.prank(owner);

//create the contract.
myContract = new MyContract();

And then we use the arbitrary address to invoke the specialThing function which is protected by onlyOwner modifier.As expect the invoke revert because of access control.Here goes the code:

1
2
3
4
//use arbitrary address invoke specialThing() function.
vm.prank(arbitrary);
vm.expectRevert("Ownable: caller is not the owner");
myContract.specialThing();

Finally,we use our owner who master the contract invoke the specialThing function.As you see the owne succeed.

1
2
3
//switch to owner.
vm.prank(owner);
myContract.specialThing();

If you want the whole example codes go for:https://gist.github.com/coffiasd/84552b5a33845fa567bfc3aa5204d460
If you have any questions, you can also reach me through the contact information below.

Role-Based Access Control

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyToken2 is ERC20, AccessControl {
// Create a new role identifier for the minter role
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

constructor(address minter) ERC20("MyToken", "TKN") {
_grantRole(DEFAULT_ADMIN_ROLE, minter);
// Grant the minter role to a specified account
_grantRole(MINTER_ROLE, minter);
}

function mint(address to, uint256 amount) public {
// Check that the calling account has the minter role
require(hasRole(MINTER_ROLE, msg.sender), "Caller is not a minter");
_mint(to, amount);
}
}

We can create whatever role we want in Role-Based Access Control (RBAC) contract.Let’s say we want create a minter role who can mint some ERC20 token.We can use a special constant bytes32 as the name of the role like this:

1
2
// Create a new role identifier for the minter role
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

Firstly,we use owner address as our minter to create our access control contract.

1
myToken = new MyToken2(owner);

And then we use the arbitrary user to mint some ERC20 token:

1
2
3
4
//use arbitrary address invoke mint() function.
vm.prank(arbitrary);
vm.expectRevert("Caller is not a minter");
myToken.mint(address(this), 1);

the above invoke would be revert because of arbitrary don’t hold the minter role.

1
2
3
//switch to owner.
vm.prank(owner);
myToken.mint(address(this), 1);

If we switch msg.sender to owner the call would successed.

What’s more? we can use owner address to grant a minter role whoever we want.Let’s say we grant the minter role to the arbitrary address:

1
2
3
4
5
vm.prank(owner);
myToken.grantRole(keccak256("MINTER_ROLE"), arbitrary);

vm.prank(arbitrary);
myToken.mint(address(this), 1);

After invoke the grantRole function the arbitrary has the right to mint token.

At the same time,arbitrary address don’t has the right to grant role to another address:

1
2
3
vm.prank(arbitrary);
vm.expectRevert();
myToken.grantRole(keccak256("MINTER_ROLE"), address(0x1001));

As the owner we also have the right to revoke role we grant before:

1
2
3
4
5
6
vm.prank(owner);
myToken.revokeRole(keccak256("MINTER_ROLE"), arbitrary);

vm.prank(arbitrary);
vm.expectRevert("Caller is not a minter");
myToken.mint(address(this), 1);

After we invoke role of arbitrary,he can’t mint ERC20 token anymore.

If you want the whole example codes go for:https://gist.github.com/coffiasd/340241d63980dc9d423e4ece2f9b20db
If you have any questions, you can also reach me through the contact information below.

let’s connect

Twitter:https://twitter.com/coffiasse
Github:https://github.com/coffiasd
TG:@coffiasd