Does this simple ERC20 code using OpenZeppelin looks safe?

Hello all,

I have very little experience with solidity.

I need to create an ERC20 token that will be as simple as possible, with the following properties:

  • I must be able to burn some
  • I must be able to mint some
  • It must not have bugs such as backdoors / reentrance issues / race conditions, etc.

Does this code look good? It works great, I'm just wondering if there are any obvious bugs/issues you guys can see? Thanks in advance! Planning to deploy it on Ethereum mainnet using the Remix online IDE. I apologize if this is the wrong place to post this.

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


import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";


contract Token is ERC20, ERC20Burnable, Ownable {

    constructor() ERC20("MyToken", "MYTOK") {
        _mint(msg.sender, 1000 * (10 ** uint256(decimals())));
    }
    function decimals() public pure override returns (uint8) {
        return 6;
    }
    function mint(address to, uint256 amount) public onlyOwner {
        _mint(to, amount);
    }
}

Technically yes, financially no.

Anyone holding liquidity of this token is not safe from the owner of the token contract minting a ton of liquidity for itself, thus reducing the actual worth of all holders but itself to effectively-zero.

Assuming that you are the owner of the token contract, it is financially safe for you, and financially unsafe for each and every one of your users.

Thank you for your reply.
Is there a simple way to set a total maximum supply? Maybe Openzeppelin has some examples?
I don't want to try and improvise and modify the source code myself as I will probably add a bunch of bugs in there.

Well, you can simply add this assertion at the beginning of your mint function:

require(totalSupply() + amount <= YOUR_MAX_TOTAL_SUPPLY, "some error message");

ERC20Burnable will give token holders the ability to burn their own tokens. It does not give the contract owner the ability to arbitrarily burn tokens. It is not clear if that's what you were asking for.

Inherit ERC20Capped and initialize it with your desired maximum supply.

Hi, thanks for your reply.

Adding the following single line in the beginning of the _mint() function seemed to do the trick. require(totalSupply() + amount <= 10_000_000 * 10 ** decimals(), "total supply reached");

What does inheriting ERC20Capped bring on top of that? After thecking the class's source code, it didn't seem like it adds anything really useful, mostly boilerplate (to my noobie eyes, anyway).

Are there any examples of PROPERLY implementing that inheritance? Kinda trying to remain as simple and fool-proof with my contract as possible. The OZ wizard doesn't seem to offer the ERC20Capped class in the options.

Thanks!

  1. Relying on already-existing and thoroughly-verified code base, instead of "inventing the wheel"
  2. Avoiding the additional gas cost of calling decimals() on every transaction, by doing it in the constructor instead (i.e., a single time, upon contract deployment)

The downside is that the check is conducted at the end of the transaction instead of at the beginning, leading to a higher gas consumption for failed transactions (i.e., users will spend more gas whenever their transactions fail).

An additional downside is that this check is conducted for every type of ERC20 transaction, and not just upon mint (i.e., also upon transfer, transfer-from, burn, etc).

TBH, it is not clear to me why it should, because the total supply can grow ONLY as a result of minting.

Perhaps the ERC20Capped is designed to accommodate for anyone choosing to increase the total supply in a non-conventional manner, for example, as a result of entity X transferring to entity Y.

This is for OZ team to answer...

That is correct, they are essentially equivalent. The only benefit of using ERC20Capped is writing less code.