Bird
Raised Fist0
Blockchain / Solidityprogramming~20 mins

Access control with OpenZeppelin in Blockchain / Solidity - Practice Problems & Coding Challenges

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Challenge - 5 Problems
🎖️
OpenZeppelin Access Control Master
Get all challenges correct to earn this badge!
Test your skills under time pressure!
Predict Output
intermediate
2:00remaining
What is the output of this contract call?

Consider the following Solidity contract using OpenZeppelin's AccessControl. What will be the result of calling checkRoleForUser() for address 0x123...?

Blockchain / Solidity
pragma solidity ^0.8.0;

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

contract RoleTest is AccessControl {
    bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");

    constructor() {
        _setupRole(ADMIN_ROLE, msg.sender);
    }

    function checkRoleForUser(address user) public view returns (bool) {
        return hasRole(ADMIN_ROLE, user);
    }
}
Atrue if user is deployer, false otherwise
Bcompilation error due to missing constructor visibility
Cfalse for any address
Dtrue for any address
Attempts:
2 left
💡 Hint

Remember who is assigned the ADMIN_ROLE in the constructor.

Predict Output
intermediate
2:00remaining
What error occurs when calling a restricted function without role?

Given this contract snippet, what error message or behavior occurs when an address without the MINTER_ROLE calls mint()?

Blockchain / Solidity
pragma solidity ^0.8.0;

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

contract Token is AccessControl {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    constructor() {
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
    }

    function mint(address to, uint256 amount) public {
        require(hasRole(MINTER_ROLE, msg.sender), "Caller is not a minter");
        // mint logic here
    }
}
ACompilation error due to missing override
BTransaction succeeds but does nothing
CTransaction reverts with "AccessControl: account is missing role"
DTransaction reverts with "Caller is not a minter"
Attempts:
2 left
💡 Hint

Look at the require statement inside mint().

🔧 Debug
advanced
3:00remaining
Why does this contract fail to grant role?

This contract tries to grant EDITOR_ROLE but fails silently. Identify the bug.

Blockchain / Solidity
pragma solidity ^0.8.0;

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

contract EditorControl is AccessControl {
    bytes32 public constant EDITOR_ROLE = keccak256("EDITOR_ROLE");

    constructor() {
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
    }

    function addEditor(address user) public {
        grantRole(EDITOR_ROLE, user);
    }
}
ACaller lacks DEFAULT_ADMIN_ROLE, so grantRole reverts
BMissing override for grantRole function
CEDITOR_ROLE is not initialized properly
DaddEditor function should be external, not public
Attempts:
2 left
💡 Hint

Who can call grantRole successfully?

📝 Syntax
advanced
2:00remaining
Which option fixes the syntax error in this role assignment?

Fix the syntax error in this role assignment snippet:

_setupRole(EDITOR_ROLE user);
A_setupRole(EDITOR_ROLE: user);
B_setupRole(EDITOR_ROLE, user);
C_setupRole(EDITOR_ROLE => user);
D_setupRole(EDITOR_ROLE user);
Attempts:
2 left
💡 Hint

Check the correct syntax for function arguments in Solidity.

🚀 Application
expert
3:00remaining
How many roles does this contract have after deployment?

Analyze this contract and determine how many distinct roles exist after deployment.

Blockchain / Solidity
pragma solidity ^0.8.0;

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

contract MultiRole is AccessControl {
    bytes32 public constant ROLE_A = keccak256("ROLE_A");
    bytes32 public constant ROLE_B = keccak256("ROLE_B");

    constructor() {
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _setupRole(ROLE_A, msg.sender);
    }

    function assignRoleB(address user) public {
        grantRole(ROLE_B, user);
    }
}
A1 role: DEFAULT_ADMIN_ROLE only
B2 roles: DEFAULT_ADMIN_ROLE, ROLE_A
C3 roles: DEFAULT_ADMIN_ROLE, ROLE_A, ROLE_B
D4 roles: DEFAULT_ADMIN_ROLE, ROLE_A, ROLE_B, and a hidden role
Attempts:
2 left
💡 Hint

Consider all roles declared and used in the contract.

Practice

(1/5)
1. What is the main purpose of using onlyRole modifier in OpenZeppelin's Access Control?
easy
A. To restrict function access to accounts with a specific role
B. To automatically assign roles to all users
C. To allow anyone to call the function without restrictions
D. To log all function calls for auditing

Solution

  1. Step 1: Understand the purpose of onlyRole

    The onlyRole modifier is used to limit access to functions so only users with a certain role can execute them.
  2. Step 2: Analyze the options

    To restrict function access to accounts with a specific role correctly states that it restricts function access to accounts with a specific role. Other options describe unrelated behaviors.
  3. Final Answer:

    To restrict function access to accounts with a specific role -> Option A
  4. Quick Check:

    Access control = restrict by role [OK]
Hint: Remember: onlyRole means only users with that role can call [OK]
Common Mistakes:
  • Thinking onlyRole assigns roles automatically
  • Believing onlyRole allows open access
  • Confusing onlyRole with event logging
2. Which of the following is the correct way to declare a role constant in OpenZeppelin Access Control?
easy
A. address constant ADMIN_ROLE = 0x123;
B. string public ADMIN_ROLE = "ADMIN_ROLE";
C. uint256 constant ADMIN_ROLE = 1;
D. bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");

Solution

  1. Step 1: Recall role declaration syntax

    OpenZeppelin uses bytes32 constants with keccak256 hash of a string to define roles.
  2. Step 2: Check each option

    bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); matches the correct pattern. Options B, C, and D use wrong types or formats.
  3. Final Answer:

    bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); -> Option D
  4. Quick Check:

    Role constants use bytes32 + keccak256 [OK]
Hint: Roles are bytes32 constants hashed with keccak256 [OK]
Common Mistakes:
  • Using string instead of bytes32 for roles
  • Assigning numeric or address types to roles
  • Forgetting to use keccak256 hash
3. Given the following Solidity code snippet, what will happen if an account without the ADMIN_ROLE calls secureFunction()?
contract MyContract is AccessControl {
    bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");

    constructor() {
        _grantRole(ADMIN_ROLE, msg.sender);
    }

    function secureFunction() public onlyRole(ADMIN_ROLE) {
        // critical logic
    }
}
medium
A. The function executes normally
B. The call reverts with an access control error
C. The function executes but emits a warning
D. The function executes only if the caller is the contract owner

Solution

  1. Step 1: Understand the onlyRole modifier behavior

    The onlyRole(ADMIN_ROLE) modifier restricts access to accounts with ADMIN_ROLE. If the caller lacks this role, the call reverts.
  2. Step 2: Analyze the scenario

    The constructor grants ADMIN_ROLE only to msg.sender at deployment. Any other account calling secureFunction will trigger a revert due to missing role.
  3. Final Answer:

    The call reverts with an access control error -> Option B
  4. Quick Check:

    Missing role causes revert [OK]
Hint: Only accounts with role can call; others revert [OK]
Common Mistakes:
  • Assuming function runs without role
  • Thinking warnings are emitted instead of revert
  • Confusing role with ownership
4. Identify the error in this OpenZeppelin Access Control code snippet:
contract MyContract is AccessControl {
    bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");

    constructor() {
        _grantRole(ADMIN_ROLE, msg.sender);
    }

    function restricted() public onlyRole(ADMIN_ROLE) {
        // restricted logic
    }

    function grantAdmin(address user) public {
        _grantRole(ADMIN_ROLE, user);
    }
}
medium
A. The grantAdmin function lacks access control and can be called by anyone
B. The _setupRole function is deprecated and should not be used
C. The onlyRole modifier is missing from grantAdmin
D. The ADMIN_ROLE constant is incorrectly declared

Solution

  1. Step 1: Review access control on grantAdmin

    The grantAdmin function calls _grantRole but has no modifier restricting who can call it.
  2. Step 2: Identify security risk

    Without access control, anyone can call grantAdmin and assign ADMIN_ROLE to themselves or others, breaking security.
  3. Final Answer:

    The grantAdmin function lacks access control and can be called by anyone -> Option A
  4. Quick Check:

    Grant functions need access control [OK]
Hint: Always protect grantRole functions with onlyRole [OK]
Common Mistakes:
  • Ignoring missing access control on grant functions
  • Thinking _grantRole is protected like grantRole
  • Confusing role declaration syntax
5. You want to create a smart contract where only users with the MINTER_ROLE can mint tokens, and only the contract owner can assign the MINTER_ROLE. Which OpenZeppelin pattern correctly enforces this?
hard
A. Use Ownable and allow only the owner to mint tokens directly without roles
B. Use AccessControl but allow anyone to assign MINTER_ROLE to themselves
C. Use AccessControl with MINTER_ROLE and add onlyRole(DEFAULT_ADMIN_ROLE) modifier to the role assignment function, granting DEFAULT_ADMIN_ROLE to the owner
D. Use AccessControl and assign MINTER_ROLE to everyone by default

Solution

  1. Step 1: Understand role assignment control

    To restrict who can assign MINTER_ROLE, use AccessControl's DEFAULT_ADMIN_ROLE for admin rights and protect assignment functions with onlyRole(DEFAULT_ADMIN_ROLE).
  2. Step 2: Connect owner with DEFAULT_ADMIN_ROLE

    Grant DEFAULT_ADMIN_ROLE to the contract owner so only they can assign MINTER_ROLE to others.
  3. Final Answer:

    Use AccessControl with MINTER_ROLE and add onlyRole(DEFAULT_ADMIN_ROLE) modifier to the role assignment function, granting DEFAULT_ADMIN_ROLE to the owner -> Option C
  4. Quick Check:

    Admin role controls role assignment [OK]
Hint: Use DEFAULT_ADMIN_ROLE for owner to control role assignments [OK]
Common Mistakes:
  • Allowing anyone to assign roles
  • Using Ownable without roles for minting
  • Assigning roles to everyone by default