@gooddollar/goodprotocol
Version:
GoodDollar Protocol
297 lines (243 loc) • 9.7 kB
text/typescript
import hre, { 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, increaseTime } 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;
let nameService;
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 = [];
}
}
};
const states = [
"Pending",
"Active",
"ActiveTimelock",
"Canceled",
"Defeated",
"Succeeded",
"Expired",
"Executed"
];
describe("CompoundVotingMachine#Guardian", () => {
let gov: CompoundVotingMachine,
root: SignerWithAddress,
acct: SignerWithAddress;
let queuePeriod, avatarGenericCall;
let avatar, Controller;
before(async () => {
[root, acct, ...signers] = await ethers.getSigners();
let {
daoCreator,
controller,
avatar: av,
setSchemes,
reputation,
setDAOAddress,
genericCall,
nameService: ns,
votingMachine
} = await loadFixture(createDAO);
Controller = controller;
avatar = av;
avatarGenericCall = genericCall;
nameService = ns;
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"));
queuePeriod = await gov.queuePeriod().then(_ => _.toNumber());
});
it("should set guardian from initializer", async () => {
const votingMachine = (await upgrades.deployProxy(
await ethers.getContractFactory("CompoundVotingMachine"),
[nameService.address, 5760, signers[2].address, NULL_ADDRESS],
{ kind: "uups" }
)) as unknown as CompoundVotingMachine;
expect(await votingMachine.guardian()).to.equal(signers[2].address);
});
it("Should have deployer as guardian", async () => {
expect(await gov.guardian()).to.equal(root.address);
});
it("Should not be able to change guardian if not guardian or avatar", async () => {
await expect(
gov.connect(acct).setGuardian(acct.address)
).to.be.revertedWith(/CompoundVotingMachine: not avatar or guardian/);
});
it("Should not be able to change guardian before foundation release time by avatar", async () => {
const encoded = gov.interface.encodeFunctionData("setGuardian", [
acct.address
]);
await avatarGenericCall(gov.address, encoded);
expect(await gov.guardian()).to.equal(root.address);
});
it("Should not be able to renounce guardian if not guardian", async () => {
await expect(gov.connect(acct).renounceGuardian()).to.be.revertedWith(
/CompoundVotingMachine: not guardian/
);
});
it("Should be able to set guardian by guardian before foundation expired", async () => {
await gov.setGuardian(acct.address);
expect(await gov.guardian()).to.equal(acct.address);
await gov.connect(acct).setGuardian(root.address); //restore
});
it("Should allow set guardian to fix bad guardian deployment", async () => {
const badAddress = "0x4659176E962763e7C8A4eF965ecfD0fdf9f52057";
await gov.setGuardian(badAddress);
expect(await gov.guardian()).to.equal(badAddress);
await gov.connect(acct).fixGuardian(root.address);
expect(await gov.guardian()).to.equal(root.address);
});
it("Should be able to set guardian by avatar if foundation expired", async () => {
await ethers.provider.send("evm_setNextBlockTimestamp", [1672531201]); //1672531200
await ethers.provider.send("evm_mine", []);
const encoded = gov.interface.encodeFunctionData("setGuardian", [
acct.address
]);
await avatarGenericCall(gov.address, encoded);
expect(await gov.guardian()).to.equal(acct.address);
});
it("Should be able to renounce guardian if guardian", async () => {
await expect(gov.connect(acct).renounceGuardian()).to.not.reverted;
expect(await gov.guardian()).to.equal(ethers.constants.AddressZero);
});
it("Should be able to set guardian by avatar if foundation renounced", async () => {
const CompoundVotingMachine = await ethers.getContractFactory(
"CompoundVotingMachine"
);
const gov2 = (await upgrades.deployProxy(
CompoundVotingMachine,
[nameService.address, 5760, root.address, NULL_ADDRESS],
{ kind: "uups" }
)) as CompoundVotingMachine;
await expect(gov2.renounceGuardian()).to.not.reverted;
expect(await gov2.guardian()).to.equal(ethers.constants.AddressZero);
const encoded = gov2.interface.encodeFunctionData("setGuardian", [
acct.address
]);
await avatarGenericCall(gov2.address, encoded);
expect(await gov2.guardian()).to.equal(acct.address);
});
it("cancel when undelegated and proposer votes below threshold", async () => {
await grep.delegateTo(signers[4].address);
let targets = [acct.address];
let values = ["0"];
let signatures = ["getBalanceOf(address)"];
let callDatas = [encodeParameters(["address"], [acct.address])];
//new guardian signers[1]
const encoded = gov.interface.encodeFunctionData("setGuardian", [
signers[1].address
]);
await avatarGenericCall(gov.address, encoded);
await gov
.connect(signers[4])
["propose(address[],uint256[],string[],bytes[],string)"](
targets,
values,
signatures,
callDatas,
"do nothing"
);
let proposalId = await gov.latestProposalIds(signers[4].address);
await advanceBlocks(1);
await expect(gov.cancel(proposalId)).to.be.revertedWith(
/CompoundVotingMachine::cancel: proposer above threshold/
); //should not be cancelable by anyone else buy guardian
await gov.connect(signers[1]).cancel(proposalId);
expect(states[await gov.state(proposalId)]).to.equal("Canceled");
await grep.delegateTo(root.address); //delegate back our votes
});
it("Should not be able to pass proposal without guardian approval", async () => {
let targets = [gov.address];
let values = ["0"];
let signatures = ["setGuardian(address)"];
let callDatas = [encodeParameters(["address"], [signers[1].address])];
await gov
.connect(root)
["propose(address[],uint256[],string[],bytes[],string)"](
targets,
values,
signatures,
callDatas,
"set guardian"
);
let proposalBlock = +(await ethers.provider.getBlockNumber());
let proposalId = await gov.latestProposalIds(root.address);
await advanceBlocks(1);
await gov.connect(root).castVote(proposalId, true);
await increaseTime(queuePeriod);
expect(states[await gov.state(proposalId)]).to.equal("Succeeded");
await expect(gov.execute(proposalId)).revertedWith(/not approved/);
})
it("Should be able to pass proposal to change guardian", async () => {
let targets = [gov.address];
let values = ["0"];
let signatures = ["setGuardian(address)"];
let callDatas = [encodeParameters(["address"], [signers[2].address])];
await gov
.connect(root)
["propose(address[],uint256[],string[],bytes[],string)"](
targets,
values,
signatures,
callDatas,
"set guardian"
);
let proposalBlock = +(await ethers.provider.getBlockNumber());
let proposalId = await gov.latestProposalIds(root.address);
await advanceBlocks(1);
await gov.connect(root).castVote(proposalId, true);
await increaseTime(queuePeriod);
expect(states[await gov.state(proposalId)]).to.equal("Succeeded");
await gov.connect(signers[1]).approveProposal(proposalId)
await gov.execute(proposalId);
expect(states[await gov.state(proposalId)]).to.equal("Executed");
//acct should now have 1M after proposal minted rep
expect(await gov.guardian()).to.equal(signers[2].address);
});
it("Should be able to pass proposal without approval if no guardian set", async () => {
let targets = [gov.address];
let values = ["0"];
let signatures = ["setGuardian(address)"];
let callDatas = [encodeParameters(["address"], [signers[1].address])];
await gov.connect(signers[2]).renounceGuardian()
expect(await gov.guardian()).equal(ethers.constants.AddressZero)
await gov
.connect(root)
["propose(address[],uint256[],string[],bytes[],string)"](
targets,
values,
signatures,
callDatas,
"set guardian"
);
let proposalId = await gov.latestProposalIds(root.address);
await advanceBlocks(1);
await gov.connect(root).castVote(proposalId, true);
await increaseTime(queuePeriod);
expect(states[await gov.state(proposalId)]).to.equal("Succeeded");
await gov.execute(proposalId);
expect(states[await gov.state(proposalId)]).to.equal("Executed");
//acct should now have 1M after proposal minted rep
expect(await gov.guardian()).to.equal(signers[1].address);
});
});