The sample contract is below. Only ADMIN_ROLE can setup or revoke MINTER/BURNER roles. Now, from your ReactJs application, you need to use web3js or ethersjs libraries to interact with deployed MyToken contract by sending setupMinter
and setupBurner
transactions. Those transactions must be sent by that account with ADMIN_ROLE, the contract deployer in our case. You can update setupMinter
and setupBurner
functions for different logic, but the main idea must be clear.
contract MyToken is ERC20, AccessControl {
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
constructor() ERC20("MyToken", "TKN") {
_setupRole(ADMIN_ROLE, msg.sender);
}
function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {
_mint(to, amount);
}
function burn(address from, uint256 amount) public onlyRole(BURNER_ROLE) {
_burn(from, amount);
}
/// @dev Grants or revokes MINTER_ROLE
function setupMinter(address minter, bool enabled) external onlyRole(ADMIN_ROLE) {
require(minter != address(0), "!minter");
if (enabled) _setupRole(MINTER_ROLE, minter);
else _revokeRole(MINTER_ROLE, minter);
}
/// @dev Grants or revokes BURNER_ROLE
function setupBurner(address burner, bool enabled) external onlyRole(ADMIN_ROLE) {
require(burner != address(0), "!burner");
if (enabled) _setupRole(BURNER_ROLE, burner);
else _revokeRole(BURNER_ROLE, burner);
}
/// @dev Returns true if MINTER
function isMinter(address minter) external view returns(bool) {
return hasRole(MINTER_ROLE, minter);
}
/// @dev Returns true if BURNER
function isBurner(address burner) external view returns(bool) {
return hasRole(BURNER_ROLE, burner);
}
}
https://docs.openzeppelin.com/contracts/4.x/access-control#granting-and-revoking