ERC20 Votes super._mint and super._burn issue

I use contracts wizard to generate an ERC20 token contract and there is a question that I had about mint and burn, for example, if I want an ERC20 token can vote and after the token transfer, the vote right is also transferred to others. Below is the auto generated code from:

in _mint _burn and _afterTokenTransfer, they all contain a super, but since I have multiple parent contracts, is this super refers to ERC20 contract mint and burn or ERC20Vote?

All I want is after transfer burn or mint, the vote right be transferred, burned or generated.

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

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";

contract MyToken is ERC20, ERC20Burnable, AccessControl, ERC20Permit, ERC20Votes {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    constructor() ERC20("MyToken", "MTK") ERC20Permit("MyToken") {
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _setupRole(MINTER_ROLE, msg.sender);
    }

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

    function _afterTokenTransfer(address from, address to, uint256 amount)
        internal
        override(ERC20, ERC20Votes)
    {
        super._afterTokenTransfer(from, to, amount);
    }

    function _mint(address to, uint256 amount)
        internal
        override(ERC20, ERC20Votes)
    {
        super._mint(to, amount);
    }

    function _burn(address account, uint256 amount)
        internal
        override(ERC20, ERC20Votes)
    {
        super._burn(account, amount);
    }
}

These override functions with super are just necessary for Solidity, they're making sure all of the _mint logic in all of your parents is being executed. Including both ERC20 and ERC20Votes.

but ERC20 votes considered delegrate or vote right isnt it. just want to make sure after I burn and mint, the user's delegate right will be removed or added as well

can u give an example to explain what is both ERC20 and ERC20Vote been included. Besides another question, how do i modify the contract in above to make the token non-transferable

This is the way ERC20Votes works. You don't need to do anything for votes to be transferred burned and minted.

Test the contract and you will see for yourself.

1 Like

If I want to add non-transferable logic to this contract how should I do ? is there any easy way around ?

You can override transfer or _transfer and add a revert statement.

1 Like

Thank you ~ that sounds like a plan. I am gonna implement it and do some local tests. Just one more check, ERC20Votes basically have no substantial difference between ERC20 in transfer, burn and mint

It's only an extension that keeps track of historical voting power.

1 Like

if someone's vote token got burned then his voting right is also been burned or elimiated right, similar for transfer and mint ?

Yes. Feel free to consult the code if you want to check other things.

seems previous i understand super wrong, thank you so much for your time. for non-transferable, this is how i did I added a override function in my code.

contract veToken is ERC20, ERC20Burnable, AccessControl, ERC20Votes {

    address public voteVester;

    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");

    event SetVoteVesterContract (address indexed proposer, address indexed vester);

    constructor(address voteVester_) ERC20("veToken", "veTK") ERC20Permit("veToken") {
        require(voteVester_ != address(0), "veTK: VESTING CONTRACT ZERO ADDRESS");
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _setupRole(MINTER_ROLE, voteVester_);
        _setupRole(BURNER_ROLE, voteVester_);
        voteVester = voteVester_;
        emit SetVoteVesterContract(msg.sender, voteVester);
    }

    function transfer(address recipient, uint256 amount) 
        public
        pure
        override(ERC20) returns (bool)
    {
        revert("veTK::transfer: veToken is non-transferable");
    }

    function transferFrom(address sender, address recipient, uint256 amount) 
        public
        pure
        override(ERC20) returns (bool)
    {
        revert("veERPERC20::transferFrom: veToken is non-transferable");
    }

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

    function burn(address account, uint256 amount) public onlyRole(BURNER_ROLE) {
        _burn(account, amount);
    }

    function _afterTokenTransfer(address from, address to, uint256 amount)
        internal
        override(ERC20, ERC20Votes)
    {
        super._afterTokenTransfer(from, to, amount);
    }

    function _mint(address to, uint256 amount)
        internal
        override(ERC20, ERC20Votes)
    {
        super._mint(to, amount);
    }

    function _burn(address account, uint256 amount)
        internal
        override(ERC20, ERC20Votes)
    {
        super._burn(account, amount);
    }
}

Do you think my sample non-transferbale but mintable and burnable ERC20 make sense ?
basically, I just want a contract can mint and burn the veToken, but ve is non-transferable.

seems i have to revert both transfer and transferFrom, to prevent user increase allowance, and use another account to transferFrom it.

The code makes sense to me. You might as well revert on approve, it might save people some gas.

yeah cool, let me update and share u the new one, thank you, also, I send u a message on twitter ~