Error when executing a proposal from within a proxy contract

Hi guys,

Im having some issues with executing proposals through my proxy contract.
Im trying to exectute a proposal from a goveror contract, but through my proxy contract. the governor contract itself is not the implemenation contract.

  • i keep getting a message :

revert -- Reason given: TimelockController: underlying transaction reverted.

been trying to figure out why the transaction is reverting for a bout two days but to no avail.

Note: i'm a beginner, my apologies if my contract design implementation is not that great

my code:
governor contract

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

import "@openzeppelin/contracts/governance/Governor.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";

import "./Constants.sol"; 

/// @title Governor module for the IkonDAO
/// @author Fernando M. Trouw
/// @notice this contract experimental 
/// @notice this contract is an experimental contract and should not be used to initiate project that will hold real value

/// @custom:security-contact ftrouw@protonmail.com
contract IkonDAOGovernor is Governor, AccessControl, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockControl, Constants {

    // string private _name; 
    string public _version;
    uint256 private _votingDelay; 
    uint256 private _votingPeriod;
    uint256 private _owner;  

    constructor(ERC20Votes _govToken, address _timelock, uint256 _delay, uint256 _period)
        Governor("IkonDaoGovernor")
        GovernorVotes(_govToken)
        GovernorVotesQuorumFraction(4)
        GovernorTimelockControl(TimelockController(payable(_timelock))) 
        
    {
        /// contract metadata 
        _votingDelay = _delay;
        _votingPeriod = _period;
        _version = "1.0.0";

        /// accesscontrol
        _setupRole(ADMIN_ROLE, _msgSender());
        _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE);
    }

    function version() public view override(Governor, IGovernor) returns (string memory) {
        return _version;
    }

    function votingDelay() public view override returns (uint256) {
        return _votingDelay; // 1 block
    }

    function votingPeriod() public view override returns (uint256) {
        return _votingPeriod; // 1 minutes 
    }

    function setVotingPeriod(uint256 _period) external onlyRole(ADMIN_ROLE) {
        _setVotingPeriod(_period);
    }

    function _setVotingPeriod(uint256 _period) private {
        _votingPeriod = _period; 
    }

    function setVotingDelay(uint256 _delay) public onlyRole(ADMIN_ROLE) {
        _setVotingDelay(_delay); 
    } 

    function _setVotingDelay(uint256 _delay) private {
        _votingDelay = _delay;
    } 
    
    /// @dev see IkonDAO{castVote}
    function castVote(uint256 proposalId, address voter, uint8 support) external onlyRole(ADMIN_ROLE) returns (uint256) {
        return _castVote(proposalId, voter, support, "");
    }

    /// @dev see IkonDAO{castVote}
    function castVote(uint256 proposalId, uint8 support) public override(Governor, IGovernor) onlyRole(ADMIN_ROLE) returns(uint256) {
        return super.castVote(proposalId, support);
    }

    function queue(
        address[] memory targets,
        uint256[] memory values,
        bytes[] memory calldatas,
        bytes32 descriptionHash
    ) public override onlyRole(ADMIN_ROLE) returns (uint256) {
        return super.queue(targets, values, calldatas, descriptionHash);
    }

    function castVoteWithReason(
        uint256 proposalId, 
        uint8 support, 
        string calldata reason) 
        public 
        override(Governor, IGovernor) 
        onlyRole(ADMIN_ROLE) returns(uint256){
        return super.castVoteWithReason(proposalId, support, reason);
    }

    function castVoteBySig(
        uint256 proposalId, 
        uint8 support, 
        uint8 v,
        bytes32 r, 
        bytes32 s) 
        public 
        override(Governor, IGovernor) 
        onlyRole(ADMIN_ROLE) returns(uint256){
        return super.castVoteBySig(proposalId, support, v, r, s);
    }

    // The following functions are overrides required by Solidity.
    function quorum(uint256 blockNumber)
        public
        view
        override(IGovernor, GovernorVotesQuorumFraction)
        returns (uint256)
    {
        return super.quorum(blockNumber);
    }

    function getVotes(address account, uint256 blockNumber)
        public
        view
        override(IGovernor, GovernorVotes)
        returns (uint256)
    {
        return super.getVotes(account, blockNumber);
    }

    function state(uint256 proposalId)
        public
        view
        override(Governor, GovernorTimelockControl)
        returns (ProposalState)
    {
        return super.state(proposalId);
    }
    
   

    function propose(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description)
        public
        override(Governor, IGovernor) onlyRole(ADMIN_ROLE)
        returns (uint256)
    {
        return super.propose(targets, values, calldatas, description);
    }

    function _execute(uint256 proposalId, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash)
        internal
        override(Governor, GovernorTimelockControl)
    {
        super._execute(proposalId, targets, values, calldatas, descriptionHash);
    }

    function _cancel(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash)
        internal
        override(Governor, GovernorTimelockControl)
        returns (uint256)
    {
        return super._cancel(targets, values, calldatas, descriptionHash);
    }

    function _executor()
        internal
        view
        override(Governor, GovernorTimelockControl)
        returns (address)
    {
        return super._executor();
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(Governor, GovernorTimelockControl, AccessControl)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}

implemenation contract:

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

/// @title The IKONDAO - a distributed collective for icon, vector graphics, animated svg's creators and creative artists
/// @author Fernando M. Trouw
/// @notice this contract experimental 
/// @notice this contract is an experimental contract and should not be used to initiate project that will hold real value

import "@openzeppelin/contracts/governance/IGovernor.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; 
import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol";

import "./IkonDAOGovernor.sol";
import "./IkonDAOGovernanceToken.sol";
import "./IkonDAOToken.sol";
import "./Constants.sol";
import "./IIkonDAO.sol";

// import "./Helpers.sol"; 

contract IkonDAO is Constants, AccessControlEnumerableUpgradeable, ERC721HolderUpgradeable, UUPSUpgradeable {
    
    IkonDAOGovernor private governor;
    address private timelocker;    
    IkonDAOToken private token; 
    // IIkonDAO private votes;
    // mapping(uint256 => Proposal) public proposals;
    // mapping(address => uint256[]) private memberProposals;
    // mapping(Avatar => IIkonDAO) private avatar;  

    event MemberCreated(address indexed _member); 
    event MemberBanned(address indexed _member); 
    event Log(uint256 id, uint8 support);

    /// @notice unsafe allow     
    constructor (){} 

    /// @notice initializes ikonDAO
    function __IkonDAO_init(address govAddress, address timelockerAddress, address tokenAddress) external initializer(){

        /// @notice instantiate dao interface
        governor = IkonDAOGovernor(govAddress);
        timelocker = timelockerAddress;
        token = IkonDAOToken(tokenAddress);
        // votes = IIkonDAO(govTokenAddress);
                
        /// @notice setRoles
        _setupRole(ADMIN_ROLE, _msgSender());
        _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE);
        _setRoleAdmin(MEMBER_ROLE, ADMIN_ROLE);
        _setRoleAdmin(BANNED_ROLE, ADMIN_ROLE);
        __AccessControlEnumerable_init();
        __ERC721Holder_init(); // give nft holding capacity of erc721 token
    }

    /// @dev creates new dao proposal
    /// @param targets contract from calldatas should be executed
    /// @param values values thaat will be sent to the targets
    /// @param datas datas that will be called by the targets
    /// @param description description of the proposal
    function propose (
        address[] calldata targets, 
        uint256[] calldata values, 
        bytes[] memory datas,
        string calldata description
    ) public onlyRole(MEMBER_ROLE) returns (uint256 _proposalId) {
        _proposalId = governor.propose(targets, values, datas, description);
    }

    function castVote(uint256 proposalId, uint8 support) external onlyRole(MEMBER_ROLE) returns (uint256) {
        emit Log(proposalId, support);
        return governor.castVote(proposalId, _msgSender(), support); 
    }   

    /// @dev executes proposal through dao governor
    /// @param targets contract from calldatas should be executed
    /// @param values values thaat will be sent to the targets
    /// @param datas datas that will be called by the targets
    /// @param descriptionHash description of the proposal
    function execute(
        address[] calldata targets, 
        uint256[] calldata values, 
        bytes[] memory datas,
        bytes32 descriptionHash
    ) public onlyRole(MEMBER_ROLE) returns (uint256) {
        return governor.execute{gas: gasleft()}(targets, values, datas, descriptionHash);
    }

    /// @dev queues a succeeded proposal to the timelock
    /// @param targets contract from calldatas should be executed
    /// @param values values thaat will be sent to the targets
    /// @param datas datas that will be called by the targets
    /// @param descriptionHash description of the proposal
    function queue(
        address[] calldata targets, 
        uint256[] calldata values, 
        bytes[] memory datas,
        bytes32 descriptionHash
    ) public onlyRole(MEMBER_ROLE) returns (uint256) {
        return governor.queue(targets, values, datas, descriptionHash); 
    }

    /// @dev makes a member of the caller of this function
    function createMember() external {
        require(!hasRole(MEMBER_ROLE, _msgSender()), REQUIRE_CREATEMEMBER_ALREADY_CREATED);
        require(!hasRole(BANNED_ROLE, _msgSender()), REQUIRE_CREATEMEMBER_USER_BANNED);
        _setupRole(MEMBER_ROLE, _msgSender());
        emit MemberCreated(_msgSender());
    }
   
    /// @dev bans member
    /// @param _account target account to be banned
    function banMember(address _account) external onlyRole(ADMIN_ROLE) {
        require(!hasRole(BANNED_ROLE, _account), REQUIRE_BANMEMBER_ALREADY_BANNED);
        require(hasRole(MEMBER_ROLE, _account), REQUIRE_BANMEMBER_ONLY_MEMBERS);
        revokeRole(MEMBER_ROLE, _account);
        _setupRole(BANNED_ROLE, _account);
        emit MemberBanned(_account);
    } 

    /// @dev transfers utility tokens for user rewards
    function transferTokensToTimelocker() external onlyRole(ADMIN_ROLE) {
        uint256 baseReward = token.getBaseReward(); 
        token.transfer(address(timelocker), baseReward);
    } 

    /// @dev mints tokens to the proxy address
    /// @param amount amount of tokens that will be minted to proxy address
    function mintUtilityTokens(uint256 amount) external onlyRole(ADMIN_ROLE) {
        token.mint(address(this), amount * 10 ** token.decimals());
    }

    /// @dev authorizes upgrades to this contract
    /// @param newImplementation address to wich to upgrade
    function _authorizeUpgrade(address newImplementation) internal override onlyRole(ADMIN_ROLE) {}

    // /// @dev upgrades the implemenation contract
    // /// @param newImplementation address to wich to upgrade
    // function upgradeTo(address newImplementation) external override onlyRole(ADMIN_ROLE) {
    //     super.upgradeTo(newImplementation);
    // }

}

Enironment:
Fedora34
ganache
truffle

You can reproduce the revert by cloning this repo:

if your run truffle test test/dao.test.js
you will see that it reverts

Thanks in advance if you can help me figure out what the problem is.

1 Like

Hi @RasenGUY, Sorry about the late reply. Were you able to find the cause of the revert?