UNPKG

@gooddollar/goodprotocol

Version:
343 lines (294 loc) 11.2 kB
import { ethers, upgrades } from "hardhat"; import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; import { expect } from "chai"; import { GReputation, CompoundVotingMachine } from "../../types"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signer-with-address"; import { createDAO } from "../helpers"; const BN = ethers.BigNumber; export const NULL_ADDRESS = "0x0000000000000000000000000000000000000000"; let grep: GReputation, grepWithOwner: GReputation, identity, gd, bounty; let signers: SignerWithAddress[], founder, repOwner, rep1, rep2, rep3; const encodeParameters = (types, values) => ethers.utils.defaultAbiCoder.encode(types, values); const advanceBlocks = async (blocks: number) => { let ps = []; for (let i = 0; i < blocks; i++) { ps.push(ethers.provider.send("evm_mine", [])); if (i % 5000 === 0) { await Promise.all(ps); ps = []; } } }; async function increaseTime(seconds) { await ethers.provider.send("evm_increaseTime", [seconds]); await advanceBlocks(1); } async function setTime(seconds) { await ethers.provider.send("evm_setTime", [new Date(seconds * 1000)]); } const states = [ "Pending", "Active", "ActiveTimelock", "Canceled", "Defeated", "Succeeded", "Expired", "Executed" ]; describe("CompoundVotingMachine#States", () => { let gov: CompoundVotingMachine, root: SignerWithAddress, acct: SignerWithAddress; let trivialProposal, targets, values, signatures, callDatas; let proposalBlock, proposalId, voteDelay, votePeriod, queuePeriod, gracePeriod; before(async () => { [root, acct, ...signers] = await ethers.getSigners(); let { setSchemes, avatar, reputation, setDAOAddress, nameService, votingMachine } = await loadFixture(createDAO); grep = (await ethers.getContractAt( "GReputation", reputation )) as GReputation; gov = votingMachine; //this will give root minter permissions await setDAOAddress("GDAO_CLAIMERS", root.address); //set voting machiine as scheme with permissions await setSchemes([gov.address]); await grep.mint(root.address, ethers.BigNumber.from("1000000")); await grep.mint(acct.address, ethers.BigNumber.from("500000")); targets = [grep.address, grep.address]; values = ["0", "0"]; signatures = ["balanceOf(address)", ""]; callDatas = [ encodeParameters(["address"], [acct.address]), grep.interface.encodeFunctionData("balanceOf", [acct.address]) ]; await gov["propose(address[],uint256[],string[],bytes[],string)"]( targets, values, signatures, callDatas, "do nothing" ); proposalBlock = +(await ethers.provider.getBlockNumber()); proposalId = await gov.latestProposalIds(root.address); trivialProposal = await gov.proposals(proposalId); voteDelay = await gov.votingDelay().then(_ => _.toNumber()); votePeriod = await gov.votingPeriod().then(_ => _.toNumber()); queuePeriod = await gov.queuePeriod().then(_ => _.toNumber()); gracePeriod = await gov.gracePeriod().then(_ => _.toNumber()); //disable guardian await gov.renounceGuardian() }); it("Invalid for proposal not found", async () => { await expect(gov.state(5)).to.revertedWith( /CompoundVotingMachine::state: invalid proposal id/ ); }); it("Pending", async () => { expect(states[await gov.state(trivialProposal.id)]).to.equal("Pending"); }); it("Active", async () => { await ethers.provider.send("evm_mine", []); await ethers.provider.send("evm_mine", []); expect(states[await gov.state(trivialProposal.id)]).to.equal("Active"); }); it("Canceled", async () => { let actor = signers[4]; await grep.delegateTo(actor.address); await gov .connect(actor) ["propose(address[],uint256[],string[],bytes[],string)"]( targets, values, signatures, callDatas, "do nothing" ); let newProposalId = await gov.proposalCount(); // send away the delegates await grep.undelegate(); await gov.cancel(newProposalId); expect(states[await gov.state(newProposalId)]).to.equal("Canceled"); }); it("Defeated", async () => { // travel to end block await advanceBlocks(votePeriod + 2); expect(states[await gov.state(trivialProposal.id)]).to.equal("Defeated"); }); describe("queue period", async () => { let proposalId; before(async () => { await advanceBlocks(1); await gov["propose(address[],uint256[],string[],bytes[],string)"]( targets, values, signatures, callDatas, "do nothing" ); proposalId = await gov.latestProposalIds(root.address); await advanceBlocks(1); }); it("ActiveTimelock in queue period", async () => { await gov.castVote(proposalId, true); await advanceBlocks(1); expect((await gov.proposals(proposalId)).eta).to.gt(0); await increaseTime(queuePeriod / 2); expect(states[await gov.state(proposalId)]).to.equal("ActiveTimelock"); //active while in queue period }); it("succeeded after queue period", async () => { await increaseTime(queuePeriod / 2); expect(states[await gov.state(proposalId)]).to.equal("Succeeded"); }); it("Still succeeded in grace period", async () => { await increaseTime(gracePeriod - 10); expect(states[await gov.state(proposalId)]).to.equal("Succeeded"); }); it("Cant vote in grace period", async () => { let proposalId = await gov.latestProposalIds(root.address); await expect( gov.connect(acct).castVote(proposalId, false) ).to.be.revertedWith( /CompoundVotingMachine::_castVote: voting is closed/ ); }); it("Expired after grace period", async () => { await increaseTime(10); expect(states[await gov.state(proposalId)]).to.equal("Expired"); await expect(gov.execute(proposalId)).to.be.revertedWith( /CompoundVotingMachine::execute: proposal can only be executed if it is succeeded/ ); }); }); // it("ActiveTimelock in queue period then defeated", async () => { let actor = signers[0]; await grep.mint(actor.address, ethers.BigNumber.from("1000000")); await advanceBlocks(1); await gov .connect(actor) ["propose(address[],uint256[],string[],bytes[],string)"]( targets, values, signatures, callDatas, "do nothing" ); let proposalId = await gov.latestProposalIds(actor.address); await advanceBlocks(1); await gov.connect(actor).castVote(proposalId, false); expect((await gov.proposals(proposalId)).eta).to.gt(0); await increaseTime(queuePeriod / 2); expect(states[await gov.state(proposalId)]).to.equal("ActiveTimelock"); //active while in queue period await increaseTime(queuePeriod / 2); expect(states[await gov.state(proposalId)]).to.equal("Defeated"); await increaseTime(gracePeriod); expect(states[await gov.state(proposalId)]).to.equal("Defeated"); //still defeated after expiration }); it("Executed", async () => { let actor = signers[1]; await grep.mint(actor.address, ethers.BigNumber.from("1000000")); await advanceBlocks(1); await gov .connect(actor) ["propose(address[],uint256[],string[],bytes[],string)"]( targets, values, signatures, callDatas, "do nothing" ); let proposalId = await gov.latestProposalIds(actor.address); await advanceBlocks(1); await gov.connect(actor).castVote(proposalId, true); expect((await gov.proposals(proposalId)).eta).to.gt(0); await increaseTime(queuePeriod); expect(states[await gov.state(proposalId)]).to.equal("Succeeded"); let eta = (await gov.proposals(proposalId)).eta; await increaseTime(gracePeriod / 2); expect(states[await gov.state(proposalId)]).to.equal("Succeeded"); //still succeeded await gov.execute(proposalId); expect(states[await gov.state(proposalId)]).to.equal("Executed"); // still executed even though would be expired await increaseTime(gracePeriod); expect(states[await gov.state(proposalId)]).to.equal("Executed"); }); it("can not cancel executed proposal", async () => { let actor = signers[1]; let proposalId = await gov.latestProposalIds(actor.address); await expect(gov.cancel(proposalId)).to.be.revertedWith( /cannot cancel executed proposal/ ); }); it("Game changer extends eta", async () => { let gameChangerPeriod = await gov .gameChangerPeriod() .then(_ => _.toNumber()); await advanceBlocks(1); await gov["propose(address[],uint256[],string[],bytes[],string)"]( targets, values, signatures, callDatas, "do nothing" ); let proposalId = await gov.latestProposalIds(root.address); await advanceBlocks(1); await gov.connect(acct).castVote(proposalId, false); let firstEta = (await gov.proposals(proposalId)).eta; expect(firstEta).to.gt(0); await increaseTime(queuePeriod / 2); expect(states[await gov.state(proposalId)]).to.equal("ActiveTimelock"); //active while in queue period await increaseTime(queuePeriod / 2 - 10); //almost to end of queue period await gov.castVote(proposalId, true); let secondEta = (await gov.proposals(proposalId)).eta; expect(firstEta).to.lt(secondEta); //eta should now end in atleast gameChangrePeriod // expect(secondEta.sub(firstEta)).to.eq(gameChangerPeriod.sub(1)); await increaseTime(gameChangerPeriod - 10); //almost to end of gameChangerPeriod expect(states[await gov.state(proposalId)]).to.equal("ActiveTimelock"); //should be still active after almost 24hours await increaseTime(10); expect(states[await gov.state(proposalId)]).to.equal("Succeeded"); await increaseTime(gracePeriod); }); it("can be executed after short activetimelock period when passed with absolute majority", async () => { let fastQueuePeriod = await gov.fastQueuePeriod().then(_ => _.toNumber()); await advanceBlocks(1); await gov .connect(signers[0]) ["propose(address[],uint256[],string[],bytes[],string)"]( targets, values, signatures, callDatas, "do nothing" ); let proposalId = await gov.proposalCount(); await advanceBlocks(1); //vote with > 50% await gov.connect(root).castVote(proposalId, true); await gov.connect(signers[0]).castVote(proposalId, true); await gov.connect(signers[1]).castVote(proposalId, true); let proposal = await gov.proposals(proposalId); expect(proposal.forVotes).gt((await grep.totalSupply()).div(2)); //verify absolute majority await increaseTime(fastQueuePeriod / 2); expect(states[await gov.state(proposalId)]).to.equal("ActiveTimelock"); //active while in queue period await increaseTime(fastQueuePeriod / 2); await advanceBlocks(1); expect(states[await gov.state(proposalId)]).to.equal("Succeeded"); await expect(gov.execute(proposalId)).to.not.reverted; }); });