Is there a limit of one admin account per role using AccessControl?

Am I correct in thinking that the AccessControl contract 3.x only allows one admin per user created role?

The Event RoleAdminChanged states:

Emitted when newAdminRole is set as role 's admin role, replacing previousAdminRole

Which leads me to think it is a hard coded limitation of one admin per role? Clearly increases security - but would increase the chances of the contract being rendered unusable if the Admin account had some catastrophe which renders the key unobtainable (e.g. hardware failure, death, etc) without a second Admin in place?

2 Likes

Hi @PreciousChicken,

A role only has a single admin role. A role (including admin roles) can have many account members.

Note: After deployment, only an account with the admin role for a role can grant/revoke the role.

You could use _setupRole in the contracts constructor to set an accounts role, including to an admin role. You could do this for multiple accounts.

Though the admin for the default admin role is the default admin role.

You can create complex permissioning structures.

I suggest trying it out, my example is below:

MyContract.sol

I set the contract deployer to be a member of the Default Admin Role.

// contracts/MyToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

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

contract MyContract is AccessControl {
    bytes32 public constant SPECIAL_ROLE = keccak256("SPECIAL_ROLE");

    event DoneStuff();

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

    function doStuff() public {
        require(hasRole(SPECIAL_ROLE, msg.sender), "MyContract: Caller does not have special role");
        emit DoneStuff();
    }
}

Deploy MyContract

$ npx oz deploy
✓ Compiled contracts with solc 0.6.12 (commit.27d51765)
? Choose the kind of deployment regular
? Pick a network development
? Pick a contract to deploy MyContract
✓ Deployed instance of MyContract
0xe78A0F7E598Cc8b0Bb87894B0F60dD2a88d6a8Ab

Get accounts

$ npx oz accounts
? Pick a network development
Accounts for dev-1597819766918:
Default: 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1
All:
- 0: 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1
- 1: 0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0
- 2: 0x22d491Bde2303f2f43325b2108D26f1eAbA1e32b
...

Get Default Admin Role

$ npx oz call
? Pick a network development
? Pick an instance MyContract at 0xe78A0F7E598Cc8b0Bb87894B0F60dD2a88d6a8Ab
? Select which function DEFAULT_ADMIN_ROLE()
✓ Method 'DEFAULT_ADMIN_ROLE()' returned: 0x0000000000000000000000000000000000000000000000000000000000000000
0x0000000000000000000000000000000000000000000000000000000000000000

Grant Default Admin Role

Note only an account that is a member of the admin role of the role can grant the role.

$ npx oz send-tx
? Pick a network development
? Pick an instance MyContract at 0xe78A0F7E598Cc8b0Bb87894B0F60dD2a88d6a8Ab
? Select which function grantRole(role: bytes32, account: address)
? role: bytes32: 0x0000000000000000000000000000000000000000000000000000000000000000
? account: address: 0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0
✓ Transaction successful. Transaction hash: 0x17c8689a1562e1ca8f0538cb2a4149ffb047c65cf09bba2bdca77e72ad8c57c4
Events emitted:
 - RoleGranted(0x0000000000000000000000000000000000000000000000000000000000000000, 0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0, 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1)

Get count of members of Default Admin Role

$ npx oz call
? Pick a network development
? Pick an instance MyContract at 0xe78A0F7E598Cc8b0Bb87894B0F60dD2a88d6a8Ab
? Select which function getRoleMemberCount(role: bytes32)
? role: bytes32: 0x0000000000000000000000000000000000000000000000000000000000000000
✓ Method 'getRoleMemberCount(bytes32)' returned: 2
2

Get the admin of the Default Admin Role

$ npx oz call
? Pick a network development
? Pick an instance MyContract at 0xe78A0F7E598Cc8b0Bb87894B0F60dD2a88d6a8Ab
? Select which function getRoleAdmin(role: bytes32)
? role: bytes32: 0x0000000000000000000000000000000000000000000000000000000000000000
✓ Method 'getRoleAdmin(bytes32)' returned: 0x0000000000000000000000000000000000000000000000000000000000000000
0x0000000000000000000000000000000000000000000000000000000000000000
1 Like

Thanks for confirming. I’m actually working on an AccessControl based contract now, but it is far from finished. I’ll share when done.

1 Like

Hi @PreciousChicken,

I had the briefest of looks at your code. You shouldn’t need to use Ownable and AccessControl. AccessControl should be flexible enough to create a complex permissioning structure.

I look forward to hearing about your project when you are ready.

1 Like

That’s a good point, thanks :+1:. I don’t actually use onlyOwner anywhere in the contract, it was just a hangover from when I created it. There are other duplicates in there too, for instance a couple of other places where I use a require plus a function modifier that does the same thing - I need to remove those as well.

1 Like

I’ve made it an issue now, that means I’ll definitely do it :laughing:

1 Like

how do I get the actual account address from the method 'getRoleAdmin(bytes32)' ? Is the bytes32 object returned contain the wallet address of the admin account that has the role assigned to it