How DEFAULT_ADMIN_ROLE account is set?

Can some one please tell me how msg.sender is set as default admin role account and where in the code this operation is performed.

I have tried implementing basic ERC20 token and in constructor I call only _grantRole(MINTER_ROLE,minter) but in the logs 2 grant events are raised one for admin role and one for minter role, just wanted to know how default admin role was set even though not specified in constructor

:1234: Code to reproduce

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";

contract ERC20Token is ERC20,AccessControl {

    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    constructor(string memory _name, string memory _symbol,address _minter) ERC20(_name, _symbol){

        // Grant the minter role to a specified account
        _grantRole(MINTER_ROLE, _minter);

    function mint(uint _amount) public onlyRole(MINTER_ROLE){
        require(hasRole(MINTER_ROLE, msg.sender), "transaction invoked by invalid role address");
        _mint(msg.sender, _amount * 10**uint(decimals()));  

do you mean default admin role for mint?

Yes. The default admin role for minter was set to transaction sender address even though I have not explicitly called

_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);

I can see here

it checks if address has admin role

Ideally it should not grant minter role right since I have not explicitly setup current account(account that initiated deploy contract transaction) as DEFAULT ADMIN ROLE

who has permission in onlyRole? btw i can't see the onlyRole modifier on the code you give...

Openzeppelin's access control contract has onlyrole modifier( ) which will be applied to grant_role function when called from my sample ERC20 token

address used to deploy contract doesn't have default admin role still grant_role(MINTER,address) succeeds. Am i missing something here? Is this expected that any account can call grant_role?

my guess is this line _mint(msg.sender, _amount * 10**uint(decimals()));
because you tell _mint to msg.sender even if you don't define the msg.sender to be the default minter, CMIIW.

Got the issue there are 2 different grantRole and _grantRole functions, as I was assigning minter role with _grantRole function which doesn't have any modifier check that's why I was able to assign MINTER role without holding default admin role in my minter,burner,adminrole structure.

So the approach I followed was to have my contract deployer address to be set as DEFAULT ADMIN and create a new administrator role. and allow only this administrator role to be able to create minter,burner role for provided addresses using grant function which has modifier check(this will be allowed if initiator of transaction has administrator role)

Please let me know if there is any other better approach to add roles.


In the code that you shared initially, the admin role isn't being granted anywhere, so I doubt that would generate a RoleGranted event for the admin role. If you believe that was the case please double check and let me know.

Setting up roles in the constructor using _grantRole is fine. But setting up a default admin and doing it later through the external functions is also fine. You should do whatever works best for your deployment process.