Starting a Token for Educational Purpose

Hello OpenZeppelin Community,

Me and my friends are currently learning about Cryptocurrency Contract Creation while also implementing it directly on the Ethereum Blockchain for future uses for our Thesis.

We're planning to create the contract using the Contracts Wizard with the following code:

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

import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-ERC20PermitUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

/// @custom:security-contact
contract WeLearn is Initializable, ERC20Upgradeable, ERC20BurnableUpgradeable, PausableUpgradeable, AccessControlUpgradeable, ERC20PermitUpgradeable {
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {

    function initialize() initializer public {
        __ERC20_init("WeLearn", "WLRN");

        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(PAUSER_ROLE, msg.sender);
        _grantRole(MINTER_ROLE, msg.sender);

    function pause() public onlyRole(PAUSER_ROLE) {

    function unpause() public onlyRole(PAUSER_ROLE) {

    function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {
        _mint(to, amount);

    function _beforeTokenTransfer(address from, address to, uint256 amount)
        super._beforeTokenTransfer(from, to, amount);

We will then be using the to compile and deploy the contract via their compiler 0.8.9

Now my question would be the following before we spend our money deploying this,

  1. Will I, as the contract deployer, be able to allow certain addresses to Mint and Burn tokens via the Roles? And how will they be able to Mint and Burn tokens?
  2. If ever in the future we need to change something in the contract, will we be able to do it without deploying it again? Or how does this upgradeable works?
  3. Will they be able to mint and burn tokens without paying gas fees due to the Permit role?

We're still in the learning curve and are currently reading out through the Documents libraries but it seems a bit more complicated for us to understand some parts of it so we would like to ask directly.
Thank you so much for helping.

Solidity code that runs on an EVM (most blockchains) are immutable, only the states (cq: storage/variable) can change. This means if you deploy a smart contract to the blockchain the code itself can not be changed. To get around this limitation there have been several "workarounds". What happens is that when you call a function on smart contract A, the execution is delegated to smart contract B.
This means that, no it is not possible to change your smart contract, however if you use an upgradable smart contract it allows you to deploy a new smart contract, and tell smart contract a to delegate to the new contract, however upgrading is not easily done using remix if you don't know what you're doing.

My advice would be to not deploy to ethereum mainnet, instead deploy to the ethereum testnet. this allows to you to deploy and test code for free instead of having to pay gas fees.

There are a lot of great beginner tutorials out there which will explain and teach you the basics of solidity. For starters you can check out the tutorials on

Thanks for the information you provided mate.

Also may I ask, how do I add whitelist address to enable controls for the token? Is it the same as Permit? Or Whitelist is a different one?

How do I add whitelist address for this? Whitelist doesn't seemed to be added on the code yet.

As you are using AccessControlUpgradeable you can add admins by using the function '
function grantRole(bytes32 role, address account) and giving them the admin role.

How to do that exactly i leave that up for you to figure out :smiley: