const { time, loadFixture, } = require('@nomicfoundation/hardhat-toolbox/network-helpers'); const { expect } = require('chai'); const { ethers } = require('hardhat'); const deployDAO = require('./util/deploy-dao'); describe('Onboarding', () => { const PRICE = 100; const STAKE_DURATION = 60; async function deploy() { // Contracts are deployed using the first signer/account by default const [account1, account2] = await ethers.getSigners(); const { dao, forum } = await deployDAO(); const Proposals = await ethers.getContractFactory('Proposals'); const proposals = await Proposals.deploy(dao.target); const Onboarding = await ethers.getContractFactory('Onboarding'); const onboarding = await Onboarding.deploy(dao.target, forum.target, proposals.target, PRICE); await forum.addPost([{ weightPPM: 1000000, authorAddress: account1 }], 'content-id', []); const callbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []); await dao.initiateValidationPool( 'content-id', 60, [1, 3], [1, 2], 100, true, false, callbackData, { value: 100 }, ); await time.increase(61); await dao.evaluateOutcome(0); expect(await dao.balanceOf(account1)).to.equal(100); return { dao, forum, onboarding, account1, account2, }; } it('Should deploy', async () => { const { dao, onboarding, account1 } = await loadFixture(deploy); expect(dao).to.exist; expect(onboarding).to.exist; expect(await dao.memberCount()).to.equal(1); expect(await dao.balanceOf(account1)).to.equal(100); expect(await dao.totalSupply()).to.equal(100); expect(await onboarding.stakeCount()).to.equal(0); }); describe('Work approval/disapproval', () => { let dao; let forum; let onboarding; let account1; let account2; beforeEach(async () => { ({ dao, forum, onboarding, account1, account2, } = await loadFixture(deploy)); await dao.stakeAvailability(onboarding.target, 50, STAKE_DURATION); }); it('should be able to submit work approval', async () => { await onboarding.connect(account2).requestWork('req-content-id', { value: PRICE }); await onboarding.submitWorkEvidence(0, 'evidence-content-id', []); await expect(onboarding.submitWorkApproval(0, true)) .to.emit(dao, 'ValidationPoolInitiated').withArgs(1) .to.emit(onboarding, 'WorkApprovalSubmitted').withArgs(0, true); const post = await forum.getPost('evidence-content-id'); expect(post.sender).to.equal(onboarding.target); expect(post.authors).to.have.length(1); expect(post.authors[0].weightPPM).to.equal(1000000); expect(post.authors[0].authorAddress).to.equal(account1); const pool = await dao.getValidationPool(1); expect(pool.props.postId).to.equal('evidence-content-id'); expect(pool.props.fee).to.equal(PRICE * 0.9); expect(pool.sender).to.equal(onboarding.target); }); it('should be able to submit work disapproval', async () => { await onboarding.connect(account2).requestWork('req-content-id', { value: PRICE }); await onboarding.submitWorkEvidence(0, 'evidence-content-id', []); await expect(onboarding.submitWorkApproval(0, false)) .to.emit(dao, 'ValidationPoolInitiated').withArgs(1) .to.emit(onboarding, 'WorkApprovalSubmitted').withArgs(0, false); }); it('should not be able to submit work approval/disapproval twice', async () => { await onboarding.connect(account2).requestWork('req-content-id', { value: PRICE }); await onboarding.submitWorkEvidence(0, 'evidence-content-id', []); await expect(onboarding.submitWorkApproval(0, true)).to.emit(dao, 'ValidationPoolInitiated').withArgs(1); await expect(onboarding.submitWorkApproval(0, true)).to.be.revertedWith('Status must be EvidenceSubmitted'); }); it('should not be able to submit work evidence after work approval', async () => { await onboarding.connect(account2).requestWork('req-content-id', { value: PRICE }); await onboarding.submitWorkEvidence(0, 'evidence-content-id', []); await expect(onboarding.submitWorkApproval(0, true)).to.emit(dao, 'ValidationPoolInitiated').withArgs(1); await expect(onboarding.submitWorkEvidence(0, 'evidence-content-id', [])).to.be.revertedWith('Status must be Requested'); }); it('should not be able to submit work approval/disapproval before work evidence', async () => { await onboarding.connect(account2).requestWork('req-content-id', { value: PRICE }); await expect(onboarding.submitWorkApproval(0, true)).to.be.revertedWith('Status must be EvidenceSubmitted'); }); }); describe('Onboarding followup', () => { it('resolving the first validation pool should trigger a second pool', async () => { const { dao, forum, onboarding, account2, } = await loadFixture(deploy); await dao.stakeAvailability(onboarding.target, 50, STAKE_DURATION); await onboarding.connect(account2).requestWork('req-content-id', { value: PRICE }); await onboarding.submitWorkEvidence(0, 'evidence-content-id', []); await expect(onboarding.submitWorkApproval(0, true)).to.emit(dao, 'ValidationPoolInitiated').withArgs(1); await time.increase(86401); await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolInitiated').withArgs(2); expect(await forum.postCount()).to.equal(3); const post = await forum.getPost('req-content-id'); expect(post.sender).to.equal(onboarding.target); expect(post.authors).to.have.length(1); expect(post.authors[0].weightPPM).to.equal(1000000); expect(post.authors[0].authorAddress).to.equal(account2); const pool = await dao.getValidationPool(2); expect(pool.props.postId).to.equal('req-content-id'); expect(pool.props.fee).to.equal(PRICE * 0.1); expect(pool.sender).to.equal(onboarding.target); expect(pool.props.fee); }); it('if the first validation pool is rejected it should not trigger a second pool', async () => { const { dao, forum, onboarding, account2, } = await loadFixture(deploy); await dao.stakeAvailability(onboarding.target, 40, STAKE_DURATION); await onboarding.connect(account2).requestWork('req-content-id', { value: PRICE }); await onboarding.submitWorkEvidence(0, 'evidence-content-id', []); await expect(onboarding.submitWorkApproval(0, true)).to.emit(dao, 'ValidationPoolInitiated').withArgs(1); await dao.stakeOnValidationPool(1, 60, false); await time.increase(86401); await expect(dao.evaluateOutcome(1)).not.to.emit(dao, 'ValidationPoolInitiated'); expect(await forum.postCount()).to.equal(2); }); }); });