Access control for upgradeable contracts

Hey guys,

I’ve developed a smart contract and want to make it upgradeable now. I followed the steps in the tutorials and moved from the constructor to an initialize method. However, I’m a bit stuck with getting the access rights correctly I guess. See the code snippet.

:computer: Environment

"@openzeppelin/contracts-upgradeable": "^3.4.0",

:memo:Details
My understanding was that I can now deploy the contract and call initialize and set the admin role to the _owner. I would expect that now only the owner can call seeStorage.

So my question would be if this is the correct way for the setup, especially with __AccessControl_init();?

Thank you very much in advance

:1234: Code to reproduce

pragma solidity >=0.5.8 <0.7.0;

import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";

contract TestContract is AccessControlUpgradeable {
    uint256 storage;

    function initialize(
        uint256 _storage,
        address _owner
    ) public initializer {
        __AccessControl_init();
        _setupRole(DEFAULT_ADMIN_ROLE, _owner);
        storage = _storage;
    }
    function seeStorage() public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Unauthorized to see the storage");
    }
}
1 Like

I found my mistake, which was not related to the contract or the way how to call the initialize method, it was just my mistake. Fortunately, I have a bunch of tests and as it seems I can omit the __AccessControl_init(); without breaking. Is there a reason for this?

1 Like

Hello @kiliw

__AccessControl_init(); replaces the constructor of the classic AccessControl. This function is generated automatically by the transpiler that produces the upgradeable version of contracts. In the case of AccessControl, this will:

  • perform the setup that is part of AccessControl’s constructor … which doesn’t do anything since AccessControl has an empty constructor
  • perform the setup of all inherited contracts … which in this case is only Context … which doesn’t do anything since Context has an empty constructor.

So in this case calling the internal initializer doesn’t do anything. But other contract, that have an “active” constructor would require this initialization step.

1 Like

Hey @Amxx,

thanks a lot for your fast response and the explanation. That made it clear.

Cheers.

1 Like