MinimalForwarderUpgradeable fail to override execute

Hi, I am designing a Forwarder with whitelisting function so that this Forwarder only can call by some whitelisted address, so I create a new var _whitelistCaller and a modifier isWhitelistCaller. However, when I override the function "execute", the compiler return TypeError: Trying to override non-virtual function. Did you forget to add "virtual"? So how to solve this issue.

:1234: Code to reproduce

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import '@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol';
import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
import '@openzeppelin/contracts-upgradeable/metatx/MinimalForwarderUpgradeable.sol';
import './storages/ForwarderStorage.sol';

contract Forwarder is
    Initializable,
    UUPSUpgradeable,
    OwnableUpgradeable,
    MinimalForwarderUpgradeable,
    ForwarderStorage
{
    // --- EVENT ---
    event AddWhitelistCaller(address newWhitelistAddress);
    event RemoveWhitelistCaller(address removedWhitelistAddress);

    // --- MODIFIER ---
    modifier onlyWhitelistCaller() {
        require(
            isWhitelistCaller(msg.sender),
            'Only can call by whitelisted Caller'
        );
        _;
    }

    // --- CONSTRUCTOR ---
    function initialize() public initializer {
        __Ownable_init();
        __UUPSUpgradeable_init();
    }

    // --- EXTERNAL ---
    // --- PUBLIC ---
    function addWhitelistCaller(address targetAddress) public onlyOwner {
        require(targetAddress != address(0), 'targetAddress should not be 0');
        require(
            !isWhitelistCaller(targetAddress),
            'targetAddress is in whitelist'
        );

        _whitelistCaller[targetAddress] = true;
        emit AddWhitelistCaller(targetAddress);
    }

    function removeWhitelistCaller(address targetAddress) public onlyOwner {
        require(targetAddress != address(0), 'targetAddress should not be 0');
        require(
            isWhitelistCaller(targetAddress),
            'targetAddress is not in whitelist'
        );

        _whitelistCaller[targetAddress] = false;
        emit RemoveWhitelistCaller(targetAddress);
    }

    function execute(ForwardRequest calldata req, bytes calldata signature)
        public
        payable
        override
        onlyWhitelistCaller
        returns (bool, bytes memory)
    {
        return super.execute(req, signature);
    }

    // --- PUBLIC FOR VIEW ---
    function isWhitelistCaller(address targetAddress)
        public
        view
        returns (bool)
    {
        return _whitelistCaller[targetAddress];
    }

    // --- INTERNAL ---
    function _authorizeUpgrade(address newImplementation)
        internal
        override
        onlyOwner
    {}
}
pragma solidity ^0.8.9;

abstract contract ForwarderStorage {
    mapping(address => bool) internal _whitelistCaller;
}

:computer: Environment

  • hardhat 2.10.2
  • solidity version: 0.8.10

For a function to be overridable it should have the virtual keyword in its declaration, for example:

contract Foo {
    function myFunction() public view virtual {}
    function myFunction2() public view {}
}

myFunction will be overridable inside a contract inheriting from Foo, like:

contract Bar is Foo {
    function myFunction() public view virtual override {}
}

But if I try to do the same with myFunction2 I would get the error: TypeError: Trying to override non-virtual function. Did you forget to add "virtual"?

The reason you are getting that error is because the function you are trying to override, execute, is not virtual.