Solidity ERC-20 – How to Create ERC20 Contract with OpenZeppelin

erc-20openzeppelinsolidity

I am trying to learn how to create ERC20 contract using OpenZeppelin library.

I am trying to create ERC20 contract and set total supply and balance in the code below:

pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract sampleToken is ERC20 
{
string public name = "ExampleToken";
string public symbol = "EGT";
uint public decimals = 18;
uint public INITIAL_SUPPLY = 10000 * (10 ** decimals);
constructor() public 
    {
_totalSupply = INITIAL_SUPPLY;
_balances[msg.sender] = INITIAL_SUPPLY;
    }
}

in my first try, I have gone to ERC20.sol of openzeppelin library and changed _totalSupply & _balances to internal and I got the below errors

> Compilation warnings encountered:

    Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
--> project:/contracts/sampleToken.sol

,Warning: Visibility for constructor is ignored. If you want the contract to be non-deployable, making it "abstract" is sufficient.
  --> project:/contracts/sampleToken.sol:11:1:
   |
11 | constructor() public 
   | ^ (Relevant source part starts here and spans across multiple lines).


TypeError: Overriding public state variable is missing "override" specifier.
 --> project:/contracts/sampleToken.sol:7:1:
  |
7 | string public name = "ExampleToken";
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Note: Overridden public state variable is here:
  --> @openzeppelin/contracts/token/ERC20/ERC20.sol:61:5:
   |
61 |     function name() public view virtual override returns (string memory) {
   |     ^ (Relevant source part starts here and spans across multiple lines).

,TypeError: Public state variables can only override functions with external visibility.
 --> project:/contracts/sampleToken.sol:7:1:
  |
7 | string public name = "ExampleToken";
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Note: Overridden function is here:
  --> @openzeppelin/contracts/token/ERC20/ERC20.sol:61:5:
   |
61 |     function name() public view virtual override returns (string memory) {
   |     ^ (Relevant source part starts here and spans across multiple lines).

,TypeError: Overriding public state variable is missing "override" specifier.
 --> project:/contracts/sampleToken.sol:8:1:
  |
8 | string public symbol = "EGT";
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Note: Overridden public state variable is here:
  --> @openzeppelin/contracts/token/ERC20/ERC20.sol:69:5:
   |
69 |     function symbol() public view virtual override returns (string memory) {
   |     ^ (Relevant source part starts here and spans across multiple lines).

,TypeError: Public state variables can only override functions with external visibility.
 --> project:/contracts/sampleToken.sol:8:1:
  |
8 | string public symbol = "EGT";
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Note: Overridden function is here:
  --> @openzeppelin/contracts/token/ERC20/ERC20.sol:69:5:
   |
69 |     function symbol() public view virtual override returns (string memory) {
   |     ^ (Relevant source part starts here and spans across multiple lines).

,TypeError: Overriding public state variable is missing "override" specifier.
 --> project:/contracts/sampleToken.sol:9:1:
  |
9 | uint public decimals = 18;
  | ^^^^^^^^^^^^^^^^^^^^^^^^^
Note: Overridden public state variable is here:
  --> @openzeppelin/contracts/token/ERC20/ERC20.sol:86:5:
   |
86 |     function decimals() public view virtual override returns (uint8) {
   |     ^ (Relevant source part starts here and spans across multiple lines).

,TypeError: Public state variables can only override functions with external visibility.
 --> project:/contracts/sampleToken.sol:9:1:
  |
9 | uint public decimals = 18;
  | ^^^^^^^^^^^^^^^^^^^^^^^^^
Note: Overridden function is here:
  --> @openzeppelin/contracts/token/ERC20/ERC20.sol:86:5:
   |
86 |     function decimals() public view virtual override returns (uint8) {
   |     ^ (Relevant source part starts here and spans across multiple lines).

,TypeError: Overriding public state variable return types differ.
 --> project:/contracts/sampleToken.sol:9:1:
  |
9 | uint public decimals = 18;
  | ^^^^^^^^^^^^^^^^^^^^^^^^^
Note: Overridden public state variable is here:
  --> @openzeppelin/contracts/token/ERC20/ERC20.sol:86:5:
   |
86 |     function decimals() public view virtual override returns (uint8) {
   |     ^ (Relevant source part starts here and spans across multiple lines).

,TypeError: Contract "sampleToken" should be marked as abstract.
 --> project:/contracts/sampleToken.sol:5:1:
  |
5 | contract sampleToken is ERC20 
  | ^ (Relevant source part starts here and spans across multiple lines).
Note: Missing implementation: 
  --> @openzeppelin/contracts/token/ERC20/ERC20.sol:53:5:
   |
53 |     constructor(string memory name_, string memory symbol_) {
   |     ^ (Relevant

Then after my research, I have return all the state of the variables back to private and used _mint function instead as can be seen below

pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract sampleToken is ERC20 
{
string public name = "ExampleToken";
string public symbol = "EGT";
uint public decimals = 18;
uint public INITIAL_SUPPLY = 10000 * (10 ** decimals);
constructor() public 
    _mint(msg.sender, INITIAL_SUPPLY);
}

and I get the below error

> Compilation warnings encountered:

    Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
--> project:/contracts/sampleToken.sol

,Warning: Visibility for constructor is ignored. If you want the contract to be non-deployable, making it "abstract" is sufficient.
  --> project:/contracts/sampleToken.sol:11:1:
   |
11 | constructor() public 
   | ^ (Relevant source part starts here and spans across multiple lines).


SyntaxError: Functions without implementation cannot have modifiers.
  --> project:/contracts/sampleToken.sol:11:1:
   |
11 | constructor() public 
   | ^ (Relevant source part starts here and spans across multiple lines).

,TypeError: Overriding public state variable is missing "override" specifier.
 --> project:/contracts/sampleToken.sol:7:1:
  |
7 | string public name = "ExampleToken";
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Note: Overridden public state variable is here:
  --> @openzeppelin/contracts/token/ERC20/ERC20.sol:61:5:
   |
61 |     function name() public view virtual override returns (string memory) {
   |     ^ (Relevant source part starts here and spans across multiple lines).

,TypeError: Public state variables can only override functions with external visibility.
 --> project:/contracts/sampleToken.sol:7:1:
  |
7 | string public name = "ExampleToken";
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Note: Overridden function is here:
  --> @openzeppelin/contracts/token/ERC20/ERC20.sol:61:5:
   |
61 |     function name() public view virtual override returns (string memory) {
   |     ^ (Relevant source part starts here and spans across multiple lines).

,TypeError: Overriding public state variable is missing "override" specifier.
 --> project:/contracts/sampleToken.sol:8:1:
  |
8 | string public symbol = "EGT";
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Note: Overridden public state variable is here:
  --> @openzeppelin/contracts/token/ERC20/ERC20.sol:69:5:
   |
69 |     function symbol() public view virtual override returns (string memory) {
   |     ^ (Relevant source part starts here and spans across multiple lines).

,TypeError: Public state variables can only override functions with external visibility.
 --> project:/contracts/sampleToken.sol:8:1:
  |
8 | string public symbol = "EGT";
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Note: Overridden function is here:
  --> @openzeppelin/contracts/token/ERC20/ERC20.sol:69:5:
   |
69 |     function symbol() public view virtual override returns (string memory) {
   |     ^ (Relevant source part starts here and spans across multiple lines).

,TypeError: Overriding public state variable is missing "override" specifier.
 --> project:/contracts/sampleToken.sol:9:1:
  |
9 | uint public decimals = 18;
  | ^^^^^^^^^^^^^^^^^^^^^^^^^
Note: Overridden public state variable is here:
  --> @openzeppelin/contracts/token/ERC20/ERC20.sol:86:5:
   |
86 |     function decimals() public view virtual override returns (uint8) {
   |     ^ (Relevant source part starts here and spans across multiple lines).

,TypeError: Public state variables can only override functions with external visibility.
 --> project:/contracts/sampleToken.sol:9:1:
  |
9 | uint public decimals = 18;
  | ^^^^^^^^^^^^^^^^^^^^^^^^^
Note: Overridden function is here:
  --> @openzeppelin/contracts/token/ERC20/ERC20.sol:86:5:
   |
86 |     function decimals() public view virtual override returns (uint8) {
   |     ^ (Relevant source part starts here and spans across multiple lines).

,TypeError: Overriding public state variable return types differ.
 --> project:/contracts/sampleToken.sol:9:1:
  |
9 | uint public decimals = 18;
  | ^^^^^^^^^^^^^^^^^^^^^^^^^
Note: Overridden public state variable is here:
  --> @openzeppelin/contracts/token/ERC20/ERC20.sol:86:5:
   |
86 |     function decimals() public view virtual override returns (uint8) {
   |     ^ (Relevant source part starts here and spans across multiple lines).

,TypeError: Contract "sampleToken" should be marked as abstract.
 --> project:/contracts/sampleToken.sol:5:1:
  |
5 | contract sampleToken is ERC20 
  | ^ (Relevant source part starts here and spans across multiple lines).
Note: Missing implementation: 
  --> @openzeppelin/contracts/token/ERC20/ERC20.sol:53:5:
   |
53 |     constructor(string memory name_, string memory symbol_) {
   |     ^ (Relevant source part starts here and spans across multiple lines).

,TypeError: Referenced declaration is neither modifier nor base class.
  --> project:/contracts/sampleToken.sol:12:5:
   |
12 |     _mint(msg.sender, INITIAL_SUPPLY);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

,TypeError: Constructor must be implemented if declared.
  --> project:/contracts/sampleToken.sol:11:1:
   |
11 | constructor() public 
   | ^ (Relevant source part starts here and spans across multiple lines).

please guide me on how to fix these issues.

Best Answer

Your biggest issue comes from not calling the ERC-20 constructor. Notice ERC20(name, symbol) on the constructor line.

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

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract myERC20Token is ERC20 
{
    constructor(string memory name, string memory symbol, uint256 initialSupply) ERC20(name, symbol) {
        _mint(msg.sender, initialSupply);
    }
    
    function decimals() public view virtual override returns (uint8) {
        return 10;
    }
}

Then, the decimals is not a variable in the Open zeppelin implementations, it is a function that you can override if you need to, just like I did in the previous code example :

If you are fine with the default 18 decimals :

function decimals() public view virtual override returns (uint8) {
    return 18;
}

then there is no need to override the decimals function.

By the way the ERC-20 implementation already internally defines :

uint256 private _totalSupply;
string private _name;
string private _symbol;

So there is really no need for you to hold similar variables in your child contract.

Related Topic