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.