_beforeTokenTransfer not virtual on ERC20PresetMinterPauserUpgradeSafe

:computer: Environment

@openzeppelin/cli@2.8.2
@openzeppelin/contracts-ethereum-package@3.0.0

:memo:Details

I’m trying to extend the ERC20PresetMinterPauserUpgradeSafe contract but I’m having some issues with _beforeTokenTransfer when compiling.

Compilation errors:
@openzeppelin/contracts-ethereum-package/contracts/presets/ERC20PresetMinterPauser.sol:102:5: TypeError: Trying to override non-virtual function. Did you forget to add "virtual"?
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal override(ERC20UpgradeSafe, ERC20PausableUpgradeSafe) {
    ^ (Relevant source part starts here and spans across multiple lines).
contracts/presets/ERC20Base.sol:12:5: Overriding function is here:
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override(ERC20PresetMinterPauserUpgradeSafe) {
    ^ (Relevant source part starts here and spans across multiple lines).

It seems like ERC20PresetMinterPauserUpgradeSafe in ERC20PresetMinterPauser.sol doesn’t use virtual when overriding _beforeTokenTransfer

Is this indeed the problem or is there an issue with my implementation?

:1234: Code to reproduce

ERC20Base.sol
pragma solidity ^0.6.0;

import "@openzeppelin/contracts-ethereum-package/contracts/Initializable.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/presets/ERC20PresetMinterPauser.sol";

contract ERC20Base is Initializable, ERC20PresetMinterPauserUpgradeSafe {

    function frozen(address account) public view returns (bool) {
        return true; // normally, logic to return the frozen state would in here
    }

    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override(ERC20PresetMinterPauserUpgradeSafe) {
        super._beforeTokenTransfer(from, to, amount);
        require(!frozen(from), "ERC20Base: account is frozen");
    }

}
1 Like

Hi @ExeciN,

I need to find out why _beforeTokenTransfer in ERC20PresetMinterPauserUpgradeSafe isn’t virtual.

As a work around, instead of extending the Preset you can clone the pieces that you need.


Apologies for the issue with setting the topic title. [Update] I have worked with Discourse to resolve and this shouldn’t happen again. I am sorry that this was so frustrating. I don’t want this to happen to anyone in the community.

1 Like

Any update on the matter? Cloning the pieces I need would work but it means I have to maintain one more implementation.
Using ERC20PresetMinterPauserUpgradeSafe as a base and extending it is more favorable.

1 Like

Hi @ExeciN,

This should be resolved in the next version of OpenZeppelin Contracts Upgrade Safe, which is estimated to be two weeks away.

Hi @ExeciN,

Just following up on this.

OpenZeppelin Contracts v3.0.0 didn’t have _beforeTokenTransfer as virtual, hence neither did OpenZeppelin Contracts Ethereum Package.v3.0.0.

This was changed in OpenZeppelin Contracts v3.1.0

So will be in the next release of OpenZeppelin Contracts Upgrade Safe which is approximately two weeks away.