const { time, loadFixture, } = require('@nomicfoundation/hardhat-toolbox/network-helpers'); const { expect } = require('chai'); const { ethers } = require('hardhat'); describe('DAO', () => { async function deploy() { const [account1, account2] = await ethers.getSigners(); const DAO = await ethers.getContractFactory('DAO'); const dao = await DAO.deploy(); return { dao, account1, account2 }; } it('Should deploy', async () => { const { dao } = await loadFixture(deploy); expect(dao).to.exist; expect(await dao.totalSupply()).to.equal(0); }); describe('Post', () => { it('should be able to add a post', async () => { const { dao, account1 } = await loadFixture(deploy); const contentId = 'some-id'; await expect(dao.addPost(account1, contentId)).to.emit(dao, 'PostAdded').withArgs(0); const post = await dao.posts(0); expect(post.author).to.equal(account1); expect(post.sender).to.equal(account1); expect(post.contentId).to.equal(contentId); }); it('should be able to add a post on behalf of another account', async () => { const { dao, account1, account2 } = await loadFixture(deploy); const contentId = 'some-id'; await dao.addPost(account2, contentId); const post = await dao.posts(0); expect(post.author).to.equal(account2); expect(post.sender).to.equal(account1); expect(post.contentId).to.equal(contentId); }); }); describe('Validation Pool', () => { let dao; let account1; const POOL_DURATION = 3600; // 1 hour const POOL_FEE = 100; const callbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []); beforeEach(async () => { ({ dao, account1 } = await loadFixture(deploy)); await dao.addPost(account1, 'content-id'); const init = () => dao.initiateValidationPool( 0, POOL_DURATION, false, callbackData, { value: POOL_FEE }, ); await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(0); expect(await dao.validationPoolCount()).to.equal(1); expect(await dao.memberCount()).to.equal(0); expect(await dao.balanceOf(account1)).to.equal(0); expect(await dao.totalSupply()).to.equal(POOL_FEE); }); describe('Initiate', () => { it('should not be able to initiate a validation pool without a fee', async () => { const setup = await loadFixture(deploy); const init = () => setup.dao.initiateValidationPool(0, POOL_DURATION, false, callbackData); await expect(init()).to.be.revertedWith('Fee is required to initiate validation pool'); }); it('should not be able to initiate a validation pool with duration below minimum', async () => { const setup = await loadFixture(deploy); const init = () => setup.dao.initiateValidationPool( 0, 59, false, callbackData, { value: POOL_FEE }, ); await expect(init()).to.be.revertedWith('Duration is too short'); }); it('should not be able to initiate a validation pool with duration above maximum', async () => { const setup = await loadFixture(deploy); const init = () => setup.dao.initiateValidationPool( 0, 86401, false, callbackData, { value: POOL_FEE }, ); await expect(init()).to.be.revertedWith('Duration is too long'); }); it('should be able to initiate a second validation pool', async () => { const init = () => dao.initiateValidationPool( 0, POOL_DURATION, false, callbackData, { value: POOL_FEE }, ); await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(1); expect(await dao.validationPoolCount()).to.equal(2); }); it('Should be able to fetch pool instance', async () => { const pool = await dao.validationPools(0); expect(pool).to.exist; expect(pool.duration).to.equal(POOL_DURATION); expect(pool.postIndex).to.equal(0); expect(pool.resolved).to.be.false; expect(pool.sender).to.equal(account1); }); }); describe('Evaluate outcome', () => { it('should not be able to evaluate outcome before duration has elapsed', async () => { await expect(dao.evaluateOutcome(0)).to.be.revertedWith('Pool end time has not yet arrived'); }); it('should be able to evaluate outcome after duration has elapsed', async () => { time.increase(POOL_DURATION + 1); await expect(dao.evaluateOutcome(0)).to.emit(dao, 'ValidationPoolResolved').withArgs(0, true); expect(await dao.memberCount()).to.equal(1); expect(await dao.balanceOf(account1)).to.equal(100); const pool = await dao.validationPools(0); expect(pool.resolved).to.be.true; expect(pool.outcome).to.be.true; }); it('should not be able to evaluate outcome more than once', async () => { time.increase(POOL_DURATION + 1); await expect(dao.evaluateOutcome(0)).to.emit(dao, 'ValidationPoolResolved').withArgs(0, true); await expect(dao.evaluateOutcome(0)).to.be.revertedWith('Pool is already resolved'); }); it('should be able to evaluate outcome of second validation pool', async () => { const init = () => dao.initiateValidationPool( 0, POOL_DURATION, false, callbackData, { value: POOL_FEE }, ); await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(1); expect(await dao.validationPoolCount()).to.equal(2); time.increase(POOL_DURATION + 1); await expect(dao.evaluateOutcome(0)).to.emit(dao, 'ValidationPoolResolved').withArgs(0, true); expect(await dao.balanceOf(account1)).to.equal(100); await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, true); expect(await dao.balanceOf(account1)).to.equal(200); }); }); describe('Stake', async () => { beforeEach(async () => { time.increase(POOL_DURATION + 1); await dao.evaluateOutcome(0); expect(await dao.balanceOf(account1)).to.equal(100); expect(await dao.balanceOf(dao.target)).to.equal(0); await dao.initiateValidationPool( 0, POOL_DURATION, false, callbackData, { value: POOL_FEE }, ); expect(await dao.balanceOf(dao.target)).to.equal(100); }); it('should be able to stake before validation pool has elapsed', async () => { await dao.stake(1, 10, true); expect(await dao.balanceOf(account1)).to.equal(90); expect(await dao.balanceOf(dao.target)).to.equal(110); time.increase(POOL_DURATION + 1); await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, true); expect(await dao.balanceOf(dao.target)).to.equal(0); expect(await dao.balanceOf(account1)).to.equal(200); }); it('should not be able to stake after validation pool has elapsed', async () => { time.increase(POOL_DURATION + 1); await expect(dao.stake(1, 10, true)).to.be.revertedWith('Pool end time has passed'); }); it('should be able to stake against a validation pool', async () => { await dao.stake(1, 10, false); expect(await dao.balanceOf(account1)).to.equal(90); expect(await dao.balanceOf(dao.target)).to.equal(110); time.increase(POOL_DURATION + 1); await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, false); expect(await dao.balanceOf(dao.target)).to.equal(0); expect(await dao.balanceOf(account1)).to.equal(200); const pool = await dao.validationPools(1); expect(pool.outcome).to.be.false; }); }); }); });