Cannot call OpenZeppelin contract with `AccessControl` in the `mint` function, even though the account has admin permission

I am interacting with an OpenZeppelin ERC-1155 contract of this form: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC1155/presets/ERC1155PresetMinterPauser.sol, which is implementing AccessControl per this doc: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/AccessControl.sol.

I use hardhat, and the contract is deployed onto ropsten network using my PUBLIC_KEY. On the server side, I am able to load this contract and interact with it using my PUBLIC_KEY. The following code successfully mint with this command:

npx hardhat run scripts/index.js --network ropsten


function main(){
	const accounts = await ethers.provider.listAccounts();
	let [ PUBLIC_KEY ] = accounts;

	const Contract1155 = await ethers.getContractFactory("ERC1155PresetMinterPauser");
	const nft = await Contract1155.attach('...');

	await nft.mint(PUBLIC_KEY,2,3453,'0x00');
	await nft.mintBatch( PUBLIC_KEY, [1,2], [101,201], '0x00');

}

main();

However when I load the contract on the browser, with the same PUBLIC_KEY from my Metamask wallet, I get the following error:

	index.js:50 Uncaught (in promise) Error: execution reverted: ERC1155PresetMinterPauser: must have minter role to mint
	{
	  "originalError": {
	    "code": 3,
	    "data": "0x08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000038455243313135355072657365744d696e7465725061757365723a206d7573742068617665206d696e74657220726f6c6520746f206d696e740000000000000000",
	    "message": "execution reverted: ERC1155PresetMinterPauser: must have minter role to mint"
	  }
	}

This is despite the fact that I can confirm PUBLIC_KEY does have minter access on the browser. Below is how I initiated web3 in the browser:

import Web3 from 'web3'
import detectEthereumProvider from '@metamask/detect-provider';
const Contract_115 = require('../../ERC1155PresetMinterPauser.json')


function maybe_mint(){

	let ethereum = await detectEthereumProvider() 
	let web3 = new Web3(ethereum)

	let abi = Contract_115.abi
	let ropsten_address = '0x...';
    const contract = new web3.eth.Contract(abi, ropsten_address);
     
    // this is the same PUBLIC_KEY as the server side.
	let accts = await web3.eth.getAccounts()
	let PUBLIC_KEY = accts[0]

	let MINTER = await contract.methods.MINTER_ROLE().call();

	// this code says that `PUBLIC_KEY` does have minter privlidge
	let pk_is_minter = await contract.methods.hasRole(MINTER,PUBLIC_KEY).call();
	console.log('PUBLIC_KEY is minter: ', pk_is_minter) // returns true

	// this fails with error: account does not have minter prilidge
	const fn = contract.methods.mintBatch( PUBLIC_KEY, [1,2], [301,901], '0x00');
	let gas = await fn.estimateGas();

}
1 Like

Hi @Xiao_Ling,

I am not sure why you pass in an address to your mint function.

I would suggest trying with a simple ERC721 (you can create with OpenZeppelin Contracts Wizard: https://zpl.in/wizard)

You could also use Hardhats console.log in your contracts: https://hardhat.org/hardhat-network/#console-log

@abcoathup do you mean the public key in: mint(PUBLIC_KEY, 2, 3452, '0x00') Im following the docs on this line: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC1155/presets/ERC1155PresetMinterPauser.sol#L49. namely:

function mint(address to, uint256 id, uint256 amount, bytes memory data) public virtual
and
function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) public virtual

The server side main functions runs using the parameters I input, on the client side both mintBatch and mint fails with must have minter permission to mint:

    const fn = contract.methods.mint( PUBLIC_KEY, 1, 100, '0x00')
    const gas = await fn.estimateGas()

This fails with ERC721 too, and that GUI you sent generates the ERC721preset that you can find on github. Once you add roles, you get this line that gets call before minting is called:

require(hasRole(MINTER_ROLE, _msgSender()), "ERC1155PresetMinterPauser: must have minter role to mint");
(https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC1155/presets/ERC1155PresetMinterPauser.sol#L50)

Both on hardhat console for localhost and ropsten, minting works as expected because PUBLIC_KEY has MINTER_ROLE, but on the client (web) side it fails.

1 Like

Hi @Xiao_Ling,

Sorry I missed that the first parameter was the address that is the recipient of the token.

If it is working in the console, then I assume that on the client side you are not calling the function from an address that has the minter role.

I recommend verifying the contract and then interact via Etherscan and MetaMask. You can import your test key into MetaMask and call the mint function. This will show that the issue is with the client.

Hi @Xiao_Ling, I had the same problem. With localhost, I could mint from the terminal by running scripts with hardhat, but not from a web browser using MetaMask and ethers. It turned out to be a problem with MetaMask and hardhat not using the same chain ID for localhost, and was resolved by updating my hardhat config. More detail on this here:

1 Like