AccessControlUpgradeable "account x is missing role y" but x is the contract address

I've deployed a transparent proxy upgradeable contract ERC721 token that uses AccessControlUpgradeable. In my initializer I use _configRole(MINTER_ROLE, x) to set another contract 2 ("STOREFRONT") as a valid minter. However, when I use STOREFRONT to call the minting function on my token, it reverts:

AccessControl: account 0x2c277145fa6098a9b355bfc610d7833f5453b7d8 is missing role 0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6

where 0x2c277145fa6098a9b355bfc610d7833f5453b7d8 is my proxy contract for my token on the rinkeby network and 0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6 is my MINTER_ROLE.

I am guessing this has something to do with the upgradeable contract malfunctioning but I don't know how to proceed on this.

here is my deploy main snippet:

    const [deployer] = await ethers.getSigners();

    const AftersTokenLib = await ethers.getContractFactory("AftersTokenLib");
    const aftersTokenLib = await AftersTokenLib.deploy();
    await aftersTokenLib.deployed();

    const TokenStorage = await ethers.getContractFactory("AftersTokenStorage");
    const tokenStorage = await upgrades.deployProxy(TokenStorage, []);
    await tokenStorage.deployed();
    console.log(`Storage address: ${tokenStorage.address}`);

    const Storefront = await ethers.getContractFactory("AftersStorefront");
    const storefront = await upgrades.deployProxy(Storefront, [[deployer.address], [1], deployer.address]);
    await storefront.deployed();
    console.log(`Storefront address: ${storefront.address}`);

    const Token = await ethers.getContractFactory("AftersToken");
    const token = await upgrades.deployProxy(Token, [tokenStorage.address, storefront.address]);
    await token.deployed();
    console.log(`Token address: ${token.address}`);

    await storefront.setToken(token.address, tokenStorage.address);

And here is my topmost contract for the ERC721 token:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

import "./IAftersTokenStorage.sol";
import "./AftersTokenBase.sol";
import "./AftersStorefront.sol";

contract AftersToken is AftersTokenBase {
    /**
        Give the minter flexibility to mint into any period even if it's not
        a current one.
     */
    function safeMint(
        uint16 periodId,
        uint8 tokenTypeId,
        bool isPresale,
        address to,
        uint16 quantity,
        string memory imageCIDOverride
    ) public onlyRole(MINTER_ROLE) whenNotPaused {
        require(quantity > 0, "QUANTITY_ZERO");
        require(to != address(0), "NONZERO_TO");

        for (uint16 i = 0; i < quantity; i++) {
            uint256 lastCount = isPresale
                ? _storage.incrementPresaleCount(periodId, tokenTypeId)
                : _storage.incrementSaleCount(periodId, tokenTypeId);

            uint256 tokenId = AftersTokenLib.nextTokenId(
                periodId,
                tokenTypeId,
                isPresale,
                lastCount
            );

            _safeMint(to, tokenId);
            if (bytes(imageCIDOverride).length > 0) {
                _storage.addTokenCIDOverride(tokenId, imageCIDOverride);
            }
        }
    }

    function initialize(IAftersTokenStorage _storage, address storefront)
        public
        initializer
    {
        __AftersTokenBase_init(_storage, storefront);
    }
}

Would you be able to share the code for AftersTokenBase? I don't see where you're granting the minter role.

Sure, here it is:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol";
import "./IAftersTokenStorage.sol";
import "./AftersTokenLib.sol";

/**
    Putting all the non-standard and utility stuff in here.
 */
contract AftersTokenBase is
    AccessControlUpgradeable,
    PausableUpgradeable,
    ERC721Upgradeable,
    ERC721BurnableUpgradeable
{
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    IAftersTokenStorage public _storage;

    function pause() public onlyRole(PAUSER_ROLE) {
        _pause();
    }

    function unpause() public onlyRole(PAUSER_ROLE) {
        _unpause();
    }

    function addMinter(address minter_) public onlyRole(DEFAULT_ADMIN_ROLE) {
        _grantRole(MINTER_ROLE, minter_);
    }

    function removeMinter(address minter_) public onlyRole(DEFAULT_ADMIN_ROLE) {
        _revokeRole(MINTER_ROLE, minter_);
    }

    function tokenExpired(uint256 tokenId) public view returns (bool) {
        require(_exists(tokenId), "token does not exist");
        IAftersTokenStorage.Period memory p = _storage.getPeriod(
            AftersTokenLib.getTokenPeriod(tokenId)
        );
        return
            !AftersTokenLib.pointBetween(
                uint64(block.timestamp),
                p.start,
                p.end
            );
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721Upgradeable)
        returns (string memory)
    {
        require(_exists(tokenId), "token does not exist");
        IAftersTokenStorage.TokenType memory _tokenType = _storage.getTokenType(
            AftersTokenLib.getTokenTypeId(tokenId)
        );
        string memory imageOverride = _storage.getTokenCIDOverride(tokenId);
        string memory imageCID = bytes(imageOverride).length == 0
            ? _tokenType.imageCID
            : imageOverride;

        IAftersTokenStorage.Period memory period = _storage.getPeriod(
            AftersTokenLib.getTokenPeriod(tokenId)
        );

        return
            AftersTokenLib.tokenDataUri(
                _tokenType.name,
                _tokenType.description,
                imageCID,
                tokenId,
                period.start,
                period.end
            );
    }

    // FIXME: add all needed interfaces
    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721Upgradeable, AccessControlUpgradeable)
        returns (bool)
    {
        return
            ERC721Upgradeable.supportsInterface(interfaceId) ||
            AccessControlUpgradeable.supportsInterface(interfaceId);
    }

    function __AftersTokenBase_init(
        IAftersTokenStorage storage_,
        address storefrontAddress
    ) public initializer {
        __AccessControl_init();
        __ERC721_init("AftersXyzToken", "AFTERS");
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
        _setupRole(PAUSER_ROLE, _msgSender());
        _setupRole(MINTER_ROLE, storefrontAddress);
        _storage = storage_;
    }
}

I'm not sure why it would fail. For debugging, you should call the function hasRole to check if your account has the minter role or not.

OK I'll give it a shot, but it's pretty weird that it's giving the proxy contract address where msgSender should be.

having the same issue! Any update ?