375 lines
14 KiB
JavaScript
375 lines
14 KiB
JavaScript
const {
|
|
time,
|
|
loadFixture,
|
|
} = require('@nomicfoundation/hardhat-toolbox/network-helpers');
|
|
const { expect } = require('chai');
|
|
const { ethers } = require('hardhat');
|
|
const { beforeEach } = require('mocha');
|
|
|
|
describe('Proposal', () => {
|
|
async function deploy() {
|
|
// Contracts are deployed using the first signer/account by default
|
|
const [account1, account2] = await ethers.getSigners();
|
|
|
|
const DAO = await ethers.getContractFactory('DAO');
|
|
const dao = await DAO.deploy();
|
|
const Proposals = await ethers.getContractFactory('Proposals');
|
|
const proposals = await Proposals.deploy(dao.target);
|
|
|
|
await dao.addPost(account1, 'some-content-id');
|
|
await dao.addPost(account2, 'some-other-content-id');
|
|
const callbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
|
|
await dao.initiateValidationPool(
|
|
0,
|
|
60,
|
|
[1, 3],
|
|
[1, 2],
|
|
100,
|
|
true,
|
|
false,
|
|
callbackData,
|
|
{ value: 1000 },
|
|
);
|
|
await dao.initiateValidationPool(
|
|
1,
|
|
60,
|
|
[1, 3],
|
|
[1, 2],
|
|
100,
|
|
true,
|
|
false,
|
|
callbackData,
|
|
{ value: 1000 },
|
|
);
|
|
await time.increase(61);
|
|
await dao.evaluateOutcome(0);
|
|
await dao.evaluateOutcome(1);
|
|
|
|
return {
|
|
dao, proposals, account1, account2,
|
|
};
|
|
}
|
|
|
|
it('Should deploy', async () => {
|
|
const {
|
|
dao, proposals, account1, account2,
|
|
} = await loadFixture(deploy);
|
|
expect(dao).to.exist;
|
|
expect(proposals).to.exist;
|
|
expect(await dao.memberCount()).to.equal(2);
|
|
expect(await dao.balanceOf(account1)).to.equal(1000);
|
|
expect(await dao.balanceOf(account2)).to.equal(1000);
|
|
expect(await dao.totalSupply()).to.equal(2000);
|
|
expect(await proposals.proposalCount()).to.equal(0);
|
|
});
|
|
|
|
describe('Attestation', () => {
|
|
let dao;
|
|
let proposals;
|
|
let account1;
|
|
let account2;
|
|
let proposal;
|
|
|
|
beforeEach(async () => {
|
|
({
|
|
dao,
|
|
proposals,
|
|
account1,
|
|
account2,
|
|
} = await loadFixture(deploy));
|
|
|
|
const emptyCallbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
|
|
await proposals.propose('proposal-content-id', account1, [20, 20, 20], false, emptyCallbackData, { value: 100 });
|
|
expect(await proposals.proposalCount()).to.equal(1);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.postIndex).to.equal(2);
|
|
expect(proposal.stage).to.equal(0);
|
|
});
|
|
|
|
it('Can submit a proposal', async () => {
|
|
// Nothing to do here -- this just tests our beforeEach
|
|
});
|
|
|
|
it('Can attest for a proposal', async () => {
|
|
await proposals.connect(account1).attest(0, 200);
|
|
// Nonbinding, non-encumbering
|
|
expect(await dao.balanceOf(account1)).to.equal(1000);
|
|
});
|
|
|
|
describe('Evaluate attestation', () => {
|
|
it('when threshold is met, advance to referendum 0% binding', async () => {
|
|
await proposals.attest(0, 200);
|
|
await expect(proposals.evaluateAttestation(0)).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(1);
|
|
});
|
|
|
|
it('threshold may be met by accumulation of attestations', async () => {
|
|
await proposals.connect(account1).attest(0, 100);
|
|
await proposals.connect(account2).attest(0, 100);
|
|
await expect(proposals.evaluateAttestation(0)).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(1);
|
|
});
|
|
|
|
it('when threshold is not met, and duration has not elapsed, do nothing', async () => {
|
|
await proposals.evaluateAttestation(0);
|
|
expect(proposal.stage).to.equal(0);
|
|
});
|
|
|
|
it('when threshold is not met, and duration has elapsed, close the proposal', async () => {
|
|
await time.increase(365 * 86400 + 1); // 1 year + 1 second
|
|
await proposals.evaluateAttestation(0);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(4); // Stage.Failed
|
|
});
|
|
});
|
|
|
|
describe('Referendum 0% binding', () => {
|
|
beforeEach(async () => {
|
|
await proposals.attest(0, 200);
|
|
await expect(proposals.evaluateAttestation(0)).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(1);
|
|
});
|
|
|
|
it('proposal dies if it fails to meet quorum', async () => {
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(2)).to.emit(dao, 'ValidationPoolResolved').withArgs(2, false, false);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(4); // Stage.Failed
|
|
});
|
|
|
|
it('referendum retries if it fails to meet participation rate', async () => {
|
|
await dao.stake(2, 200, true);
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(2))
|
|
.to.emit(dao, 'ValidationPoolResolved').withArgs(2, true, true)
|
|
.to.emit(dao, 'ValidationPoolInitiated').withArgs(3);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(1);
|
|
});
|
|
|
|
it('referendum retries if it fails to meet win ratio', async () => {
|
|
await dao.stake(2, 1000, false);
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(2))
|
|
.to.emit(dao, 'ValidationPoolResolved').withArgs(2, false, true)
|
|
.to.emit(dao, 'ValidationPoolInitiated').withArgs(3);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(1);
|
|
|
|
const pools = await proposals.getPools(0);
|
|
expect(pools[0][0].started).to.be.true;
|
|
expect(pools[0][1].started).to.be.true;
|
|
expect(pools[0][2].started).to.be.false;
|
|
expect(pools[0][0].completed).to.be.true;
|
|
expect(pools[0][1].completed).to.be.false;
|
|
expect(pools[0][2].completed).to.be.false;
|
|
});
|
|
|
|
it('proposal fails if a referendum fails to meet participation rate 3 times', async () => {
|
|
await dao.stake(2, 200, true);
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(2))
|
|
.to.emit(dao, 'ValidationPoolResolved').withArgs(2, true, true)
|
|
.to.emit(dao, 'ValidationPoolInitiated').withArgs(3);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(1);
|
|
|
|
await dao.stake(3, 200, true);
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(3))
|
|
.to.emit(dao, 'ValidationPoolResolved').withArgs(3, true, true)
|
|
.to.emit(dao, 'ValidationPoolInitiated').withArgs(4);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(1);
|
|
|
|
await dao.stake(4, 200, true);
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(4))
|
|
.to.emit(dao, 'ValidationPoolResolved').withArgs(4, true, true);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(4);
|
|
});
|
|
|
|
it('advances to next referendum if it meets participation rate and win ratio', async () => {
|
|
await dao.stake(2, 1000, true);
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(2))
|
|
.to.emit(dao, 'ValidationPoolResolved').withArgs(2, true, true)
|
|
.to.emit(dao, 'ValidationPoolInitiated').withArgs(3);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(2);
|
|
});
|
|
});
|
|
|
|
describe('Referendum 1% binding', () => {
|
|
beforeEach(async () => {
|
|
await proposals.attest(0, 200);
|
|
await expect(proposals.evaluateAttestation(0)).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
|
await dao.stake(2, 1000, true);
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(2))
|
|
.to.emit(dao, 'ValidationPoolResolved').withArgs(2, true, true)
|
|
.to.emit(dao, 'ValidationPoolInitiated').withArgs(3);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(2);
|
|
});
|
|
|
|
afterEach(async () => {
|
|
const pool = await dao.validationPools(3);
|
|
expect(pool.resolved).to.be.true;
|
|
});
|
|
|
|
it('proposal dies if it fails to meet quorum', async () => {
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(3)).to.emit(dao, 'ValidationPoolResolved').withArgs(3, false, false);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(4); // Stage.Failed
|
|
});
|
|
|
|
it('referendum retries if it fails to meet participation rate', async () => {
|
|
await dao.stake(3, 200, true);
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(3))
|
|
.to.emit(dao, 'ValidationPoolResolved').withArgs(3, true, true)
|
|
.to.emit(dao, 'ValidationPoolInitiated').withArgs(4);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(2);
|
|
});
|
|
|
|
it('referendum retries if it fails to meet win ratio', async () => {
|
|
await dao.stake(3, 1000, false);
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(3))
|
|
.to.emit(dao, 'ValidationPoolResolved').withArgs(3, false, true)
|
|
.to.emit(dao, 'ValidationPoolInitiated').withArgs(4);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(2);
|
|
});
|
|
|
|
it('proposal fails if a referendum fails to meet participation rate 3 times', async () => {
|
|
await dao.stake(3, 200, true);
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(3))
|
|
.to.emit(dao, 'ValidationPoolResolved').withArgs(3, true, true)
|
|
.to.emit(dao, 'ValidationPoolInitiated').withArgs(4);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(2);
|
|
|
|
await dao.stake(4, 200, true);
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(4))
|
|
.to.emit(dao, 'ValidationPoolResolved').withArgs(4, true, true)
|
|
.to.emit(dao, 'ValidationPoolInitiated').withArgs(5);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(2);
|
|
|
|
await dao.stake(5, 200, true);
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(5))
|
|
.to.emit(dao, 'ValidationPoolResolved').withArgs(5, true, true);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(4);
|
|
});
|
|
|
|
it('advances to next referendum if it meets participation rate and win ratio', async () => {
|
|
await dao.stake(3, 1000, true);
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(3))
|
|
.to.emit(dao, 'ValidationPoolResolved').withArgs(3, true, true)
|
|
.to.emit(dao, 'ValidationPoolInitiated').withArgs(4);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(3);
|
|
});
|
|
});
|
|
|
|
describe('Referendum 100% binding', () => {
|
|
beforeEach(async () => {
|
|
await proposals.attest(0, 200);
|
|
await expect(proposals.evaluateAttestation(0)).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
|
await dao.stake(2, 1000, true);
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(2))
|
|
.to.emit(dao, 'ValidationPoolResolved').withArgs(2, true, true)
|
|
.to.emit(dao, 'ValidationPoolInitiated').withArgs(3);
|
|
await dao.stake(3, 1000, true);
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(3))
|
|
.to.emit(dao, 'ValidationPoolResolved').withArgs(3, true, true)
|
|
.to.emit(dao, 'ValidationPoolInitiated').withArgs(4);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(3);
|
|
});
|
|
|
|
afterEach(async () => {
|
|
const pool = await dao.validationPools(4);
|
|
expect(pool.resolved).to.be.true;
|
|
});
|
|
|
|
it('proposal dies if it fails to meet quorum', async () => {
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(4)).to.emit(dao, 'ValidationPoolResolved').withArgs(4, false, false);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(4); // Stage.Failed
|
|
});
|
|
|
|
it('referendum retries if it fails to meet participation rate', async () => {
|
|
await dao.stake(4, 200, true);
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(4))
|
|
.to.emit(dao, 'ValidationPoolResolved').withArgs(4, true, true)
|
|
.to.emit(dao, 'ValidationPoolInitiated').withArgs(5);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(3);
|
|
});
|
|
|
|
it('referendum retries if it fails to meet win ratio', async () => {
|
|
await dao.stake(4, 1000, false);
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(4))
|
|
.to.emit(dao, 'ValidationPoolResolved').withArgs(4, false, true)
|
|
.to.emit(dao, 'ValidationPoolInitiated').withArgs(5);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(3);
|
|
});
|
|
|
|
it('proposal fails if a referendum fails to meet participation rate 3 times', async () => {
|
|
await dao.stake(4, 200, true);
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(4))
|
|
.to.emit(dao, 'ValidationPoolResolved').withArgs(4, true, true)
|
|
.to.emit(dao, 'ValidationPoolInitiated').withArgs(5);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(3);
|
|
|
|
await dao.stake(5, 200, true);
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(5))
|
|
.to.emit(dao, 'ValidationPoolResolved').withArgs(5, true, true)
|
|
.to.emit(dao, 'ValidationPoolInitiated').withArgs(6);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(3);
|
|
|
|
await dao.stake(6, 200, true);
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(6))
|
|
.to.emit(dao, 'ValidationPoolResolved').withArgs(6, true, true);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(4);
|
|
});
|
|
|
|
it('advances to accepted stage if it meets participation rate and win ratio', async () => {
|
|
await dao.connect(account1).stake(4, 1000, true);
|
|
await dao.connect(account2).stake(4, 1000, true);
|
|
await time.increase(21);
|
|
await expect(dao.evaluateOutcome(4))
|
|
.to.emit(dao, 'ValidationPoolResolved').withArgs(4, true, true);
|
|
proposal = await proposals.proposals(0);
|
|
expect(proposal.stage).to.equal(5);
|
|
});
|
|
});
|
|
});
|
|
});
|