Sorry, I think it's a basic question but I've been trying to solve it for days. I'm learning to create a DAO. I'm following the OpenZeppelin documentation. I created all my contracts using the OP's Wizard, they follow in this repository.

I'm writing a test to go through the entire Proposal Lifecycle:

const { expect } = require("chai");
const { ethers } = require("hardhat");
const { BigNumber } = require("bignumber.js");

describe("SaudeVapor Governance test.", function () {
  it("Should go through the entire Proposal Lifecycle", async function () {

    const accounts = await ethers.getSigners();

    //Deploy Token and mint 1 token to owner
    const SVToken = await ethers.getContractFactory("SVToken");
    const svtoken = await SVToken.deploy();    
    await svtoken.deployed();
    const setsvTOKENTx = await svtoken.safeMint( accounts[0].address , " String Test ");
    await setsvTOKENTx.wait(1);
    const transactionResponse = await svtoken.delegate(accounts[0].address)
    await transactionResponse.wait(1)

    expect(await svtoken.totalSupply()).to.equal(1);
    console.log(`Token Collection address: ${svtoken.address}`)
    console.log(`Total supply: ${await svtoken.totalSupply()}`)

    //Deploy Timelock    
    const TimeLock = await ethers.getContractFactory("TimeLock");
    const timelock = await TimeLock.deploy(0, [], []);    
    await timelock.deployed();
    console.log(`Timelock address: ${timelock.address}`)  

    //Deploy Governor
    const SVGovernor = await ethers.getContractFactory("SVGovernor");
    const svgovernor = await SVGovernor.deploy(svtoken.address, timelock.address);    
    await svgovernor.deployed();
    console.log(`SVGovernor address: ${svgovernor.address}`)  

    // Setting up contracts for roles
    const proposerRole = await timelock.PROPOSER_ROLE()
    const executorRole = await timelock.EXECUTOR_ROLE()
    const adminRole = await timelock.TIMELOCK_ADMIN_ROLE()  
    const proposerTx = await timelock.grantRole(proposerRole, svgovernor.address)
    const proposerole = await proposerTx.wait(1)
    const executorTx = await timelock.grantRole(executorRole, "0x0000000000000000000000000000000000000000")
    const executorole = await executorTx.wait(1)
    const revokeTx = await timelock.revokeRole(adminRole, accounts[0].address)
    const revokerole = await revokeTx.wait(1)
    console.log(`Proposer role: ${proposerole.events[0].args.account}`)
    console.log(`Executor role: ${executorole.events[0].args.account}`)
    console.log(`Revoke role: ${revokerole.events[0].args.account}`)

    // Transfer ownership
    const transferOwnershipTx = await svtoken.transferOwnership(timelock.address)
    const newTokenContractOwner = await transferOwnershipTx.wait(1)
    console.log(`New SVToken contract ower: ${newTokenContractOwner.events[0].args.newOwner}`)

    //Create a proposal
    const token = await ethers.getContractAt("SVToken", svtoken.address);    
    const addressTo = accounts[1].address;
    const stringUri = " String Test 1 ";
    const transferCalldata = token.interface.encodeFunctionData("safeMint", [addressTo, stringUri]);
    const description = `Create a token to: ${addressTo}, for contribuition of: ${stringUri}`
    const proposeTX = await svgovernor.propose(
    await proposeTX.wait(1);
    const data = await proposeTX.wait(1);
    const proposalId = data.events[0].args.proposalId.toString()
    await hre.network.provider.send("hardhat_mine");  // Mine 1 block = votingDelay()
    const proposalState = await svgovernor.state(proposalId)    
    expect(proposalState).to.equal(0); // The state of the proposal. 1 is not passed. 0 is passed.
      proposalState: ${proposalState},
      proposalId: ${data.events[0].args.proposalId.toString()},
      tokenAddress: ${data.events[0].args.targets.toString()},
      amount: ${data.events[0].args[3].toString()},
      transferCalldata: ${data.events[0].args.calldatas.toString()},
      description: ${data.events[0].args.description},
      startBlock:  ${data.events[0].args.startBlock.toString()}`)

    // Cast a Vote
    const voteWay = 1 // 0 = Against, 1 = For, 2 = Abstain
    const voteTx = await svgovernor.castVote(proposalId, voteWay)
    await voteTx.wait(1)
    expect(await voteTx.wait(1)); 
    const hasVoted = await svgovernor.hasVoted(proposalId , accounts[0].address)
    expect(await hasVoted).to.equal(true);
    console.log(`AgainstVotes: ${(await svgovernor.proposalVotes(proposalId)).againstVotes.toString()}`)
    console.log(`ForVotes: ${(await svgovernor.proposalVotes(proposalId)).forVotes.toString()}`)
    console.log(`AbstainVotes: ${(await svgovernor.proposalVotes(proposalId)).abstainVotes.toString()}`)
    console.log(`Quorum: ${(await svgovernor.quorum(data.events[0].args.startBlock.toString()))}`)

    // TokenAddress, amount and transferCalldata from event
    expect(await data.events[0].args.targets.toString()).to.equal(svtoken.address);
    expect((new BigNumber(await data.events[0].args[3])).c[0]).to.equal(0);
    expect(await data.events[0].args.calldatas.toString()).to.equal(transferCalldata);
    expect(await data.events[0].args.description).to.equal(description);

    // Queue a proposal
    await hre.network.provider.send("hardhat_mine");  // Mine a block
    const descriptionHash = ethers.utils.id(await data.events[0].args.description);
    const queueTX = await svgovernor.queue(
      [(new BigNumber(await data.events[0].args[3])).c[0]],
      [await data.events[0].args.calldatas.toString()],
    await queueTX.wait(1);
    console.log(await queueTX.wait(1));

    // // Mine 45819 block = votingPeriod() + 1
    // await hre.network.provider.send("hardhat_mine", ["0xB2FB"]);

    // // Execute the Proposal
    // const executeTX = await svgovernor.execute(
    // [data.events[0].args.targets.toString()],
    // [(new BigNumber(await data.events[0].args[3])).c[0]],
    // [await data.events[0].args.calldatas.toString()],
    // descriptionHash,
    // );
    // await executeTX.wait(1);
    // console.log(await executeTX.wait(1));


I can create a token, governance and timelock contract.

I can change the roles of my contracts.

I can create a proposal, and even cast a valid vote on it.

But whenever I try to Queue or Execute, I always get the same error message: Governor: proposal not successful

Can anyone give me a light? I feel like it's simple but I can't figure it out


1 Answer 1


take a look at the following diagram: enter image description here

In order to be able to enqueue a proposal you should previously cast votes, met the required quorum (proposal in state ProposalState.Succeeded)

And after the voting period ends you will be able to enqueue the proposal.

  • You are right. I don't know why but the votes are not being counted. That's why it doesn't reach the quorum. Thank you for you attetion. Jun 28 at 16:43
  • I suggest you to update your code (because of code where the castVote should happen is commented) in order to show exactly what are you trying Jun 28 at 19:54
  • Thanks. Updated. I can cast a vote, but the Quorum is always 0. I am doing all operations using the same account. I'll try to make the proposal using another account (I have to find out how to do it using hardhat, .connect(address) doesn't seem to work.). Jun 28 at 20:12
  • ok here is the problem, you are not mining the correct amount of blocks in order to wait for the voting period to end try this before queuing the proposal await network.provider.send('hardhat_mine', ["0x284696"]); Jun 28 at 20:42
  • UHuuuuuu!!!!! You are the best.. It was just that. I thought you could Queue anytime, but no, only after votingPeriod. Awesome.. Thank you so much!!! Jun 28 at 20:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.