@gooddollar/goodprotocol
Version:
GoodDollar Protocol
294 lines (257 loc) • 10.1 kB
text/typescript
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);
describe("CompoundVotingMachine#CastVote", () => {
let gov: any, root: SignerWithAddress, acct: SignerWithAddress;
let trivialProposal, targets, values, signatures, callDatas;
let proposalBlock, proposalId, voteDelay, votePeriod;
before(async () => {
[root, acct, ...signers] = await ethers.getSigners();
let {
daoCreator,
avatar,
reputation,
setDAOAddress,
nameService,
votingMachine
} = await loadFixture(createDAO);
gov = votingMachine;
grep = (await ethers.getContractAt(
"GReputation",
reputation
)) as GReputation;
//this will give root minter permissions
await setDAOAddress("GDAO_CLAIMERS", root.address);
await grep.mint(root.address, ethers.BigNumber.from("1000000"));
targets = [acct.address];
values = ["0"];
signatures = ["getBalanceOf(address)"];
callDatas = [encodeParameters(["address"], [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());
});
describe("We must revert if:", () => {
it("There does not exist a proposal with matching proposal id where the current block number is between the proposal's start block (exclusive) and end block (inclusive)", async () => {
await expect(gov.castVote(proposalId, true)).to.revertedWith(/CompoundVotingMachine::_castVote: voting is closed/
);
});
it("Such proposal already has an entry in its voters set matching the sender", async () => {
await gov.connect(signers[0]).castVote(proposalId, true);
await expect(
gov.connect(signers[0]).castVote(proposalId, true)
).to.revertedWith(/CompoundVotingMachine::_castVote: voter already voted/
);
});
});
describe("Otherwise", () => {
it("we add the sender to the proposal's voters set", async () => {
let receipt = await gov.getReceipt(proposalId, signers[2].address);
expect(receipt.hasVoted).to.equal(false);
await gov.connect(signers[2]).castVote(proposalId, true);
receipt = await gov.getReceipt(proposalId, signers[2].address);
expect(receipt.hasVoted).to.equal(true);
});
describe("and we take the balance returned by GetPriorVotes for the given sender and the proposal's start block, which may be zero,", () => {
it("and we add that ForVotes", async () => {
let actor = signers[3];
await grep.mint(actor.address, ethers.BigNumber.from("100001"));
await gov
.connect(actor)
["propose(address[],uint256[],string[],bytes[],string)"](
targets,
values,
signatures,
callDatas,
"do nothing"
);
await ethers.provider.send("evm_mine", []);
await ethers.provider.send("evm_mine", []);
let proposalId = await gov.latestProposalIds(actor.address);
let beforeFors = (await gov.proposals(proposalId)).forVotes;
await gov.connect(actor).castVote(proposalId, true);
let aftreFors = (await gov.proposals(proposalId)).forVotes;
expect(aftreFors).to.equal(
beforeFors.add(ethers.BigNumber.from("100001"))
);
});
it("or AgainstVotes corresponding to the caller's support flag.", async () => {
let actor = signers[4];
await grep.mint(actor.address, ethers.BigNumber.from("100001"));
console.log(
await grep.balanceOfLocal(actor.address).then(_ => _.toString())
);
let tx = await gov
.connect(actor)
["propose(address[],uint256[],string[],bytes[],string)"](
targets,
values,
signatures,
callDatas,
"do nothing"
);
await ethers.provider.send("evm_mine", []);
await ethers.provider.send("evm_mine", []);
let proposalId = await gov.latestProposalIds(actor.address);
let beforeAgainsts = (await gov.proposals(proposalId)).againstVotes;
tx = await gov.connect(actor).castVote(proposalId, false);
let afterAgainsts = (await gov.proposals(proposalId)).againstVotes;
expect(afterAgainsts).to.equal(
beforeAgainsts.add(ethers.BigNumber.from("100001"))
);
});
describe("castVoteBySig", () => {
const Domain = async gov => ({
name: await gov.name(),
chainId: (await ethers.provider.getNetwork()).chainId,
verifyingContract: gov.address
});
const Types = {
Ballot: [
{ name: "proposalId", type: "uint256" },
{ name: "support", type: "bool" }
]
};
it("reverts if the signatory is invalid", async () => {
await expect(
gov.castVoteBySig(
proposalId,
false,
0,
ethers.utils.hexZeroPad("0xbac", 32),
ethers.utils.hexZeroPad("0xbada", 32)
)
).to.revertedWith(/CompoundVotingMachine::castVoteBySig: invalid signature/
);
});
describe("casts vote on behalf of the signatory", async () => {
let receipt;
it("should cast vote", async () => {
let wallet = ethers.Wallet.createRandom({ gasPrice: 10000000 });
await acct.sendTransaction({
to: wallet.address,
value: ethers.utils.parseEther("9999")
});
wallet = wallet.connect(ethers.provider);
let actor = wallet;
await grep.mint(actor.address, ethers.BigNumber.from("100001"));
await gov
.connect(actor)
["propose(address[],uint256[],string[],bytes[],string)"](
targets,
values,
signatures,
callDatas,
"do nothing"
);
let proposalId = await gov.latestProposalIds(actor.address);
const signature = await wallet._signTypedData(
await Domain(gov),
Types,
{
proposalId: proposalId,
support: true
}
);
const sig = ethers.utils.splitSignature(signature);
let beforeFors = (await gov.proposals(proposalId)).forVotes;
await ethers.provider.send("evm_mine", []);
let tx = await gov.castVoteBySig(
proposalId,
true,
sig.v,
sig.r,
sig.s
);
receipt = await tx.wait();
let afterFors = (await gov.proposals(proposalId)).forVotes;
expect(afterFors).to.equal(
beforeFors.add(ethers.BigNumber.from("100001"))
);
});
xit("gas costs for cast vote by sig", async () => {
expect(receipt.gasUsed.toNumber()).to.be.lt(80000);
});
});
xit("gas costs for multiple sigs", async () => {
let wallet = ethers.Wallet.createRandom();
wallet = wallet.connect(ethers.provider);
let actor = wallet;
await grep.mint(actor.address, ethers.BigNumber.from("100001"));
await gov
.connect(actor)
["propose(address[],uint256[],string[],bytes[],string)"](
targets,
values,
signatures,
callDatas,
"do nothing"
);
let proposalId = await gov.latestProposalIds(actor.address);
const sigsFor = [];
const sigsAgainst = [];
const createSig = async () => {
let wallet = ethers.Wallet.createRandom();
wallet = wallet.connect(ethers.provider);
let actor = wallet;
// await grep.mint(actor.address, ethers.BigNumber.from("1"));
let support = Math.random() < 0.5;
const signature = await wallet._signTypedData(
await Domain(gov),
Types,
{
proposalId: proposalId,
support
}
);
const sig = ethers.utils.splitSignature(signature);
if (support)
sigsFor.push({
support,
v: sig.v,
r: sig.r,
s: sig.s
});
else
sigsAgainst.push({
support,
v: sig.v,
r: sig.r,
s: sig.s
});
};
const ps = [];
for (let i = 1; i < 100; i++) {
ps.push(createSig());
}
await Promise.all(ps);
await ethers.provider.send("evm_mine", []);
let tx = await gov.ecRecoverTest(proposalId, sigsFor, sigsAgainst);
let receipt = await tx.wait();
console.log("gas for sigs:", {
i: sigsFor.length + sigsAgainst.length,
gas: receipt.gasUsed.toNumber()
});
});
});
});
});
});