Compare commits
2 Commits
57b64f7977
...
dc18c69b09
Author | SHA1 | Date |
---|---|---|
Ladd Hoffman | dc18c69b09 | |
Ladd Hoffman | 5c712f350a |
|
@ -83,7 +83,7 @@ class Post {
|
|||
|
||||
// Upload hash to blockchain
|
||||
async publish(DAO, account) {
|
||||
await DAO.methods.addPost(account, this.hash).send({
|
||||
await DAO.methods.addPost(account, this.hash, []).send({
|
||||
from: account,
|
||||
gas: 1000000,
|
||||
});
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
pragma solidity ^0.8.24;
|
||||
|
||||
import "./DAO.sol";
|
||||
import "./core/Forum.sol";
|
||||
import "./WorkContract.sol";
|
||||
import "./interfaces/IOnValidate.sol";
|
||||
|
||||
|
@ -26,7 +27,11 @@ contract Onboarding is WorkContract, IOnValidate {
|
|||
request.status = WorkStatus.ApprovalSubmitted;
|
||||
request.approval = approval;
|
||||
// Make work evidence post
|
||||
uint postIndex = dao.addPost(stake.worker, request.evidenceContentId);
|
||||
uint postIndex = dao.addPost(
|
||||
stake.worker,
|
||||
request.evidenceContentId,
|
||||
request.citations
|
||||
);
|
||||
emit WorkApprovalSubmitted(requestIndex, approval);
|
||||
// Initiate validation pool
|
||||
uint poolIndex = dao.initiateValidationPool{
|
||||
|
@ -69,9 +74,11 @@ contract Onboarding is WorkContract, IOnValidate {
|
|||
payable(request.customer).transfer(request.fee / 10);
|
||||
return 1;
|
||||
}
|
||||
Citation[] memory emptyCitations;
|
||||
uint postIndex = dao.addPost(
|
||||
request.customer,
|
||||
request.requestContentId
|
||||
request.requestContentId,
|
||||
emptyCitations
|
||||
);
|
||||
dao.initiateValidationPool{value: request.fee / 10}(
|
||||
postIndex,
|
||||
|
|
|
@ -71,7 +71,9 @@ contract Proposals is DAOContract, IOnValidate {
|
|||
// TODO: Consider taking author as a parameter,
|
||||
// or else accepting a postIndex instead of contentId,
|
||||
// or support post lookup by contentId
|
||||
uint postIndex = dao.addPost(author, contentId);
|
||||
// TODO: Take citations as a parameter
|
||||
Citation[] memory emptyCitations;
|
||||
uint postIndex = dao.addPost(author, contentId, emptyCitations);
|
||||
proposalIndex = proposalCount++;
|
||||
Proposal storage proposal = proposals[proposalIndex];
|
||||
proposal.sender = msg.sender;
|
||||
|
|
|
@ -3,6 +3,7 @@ pragma solidity ^0.8.24;
|
|||
|
||||
import "./DAO.sol";
|
||||
import "./Proposals.sol";
|
||||
import "./core/Forum.sol";
|
||||
import "./interfaces/IAcceptAvailability.sol";
|
||||
import "./interfaces/IOnProposalAccepted.sol";
|
||||
import "hardhat/console.sol";
|
||||
|
@ -33,6 +34,7 @@ abstract contract WorkContract is
|
|||
uint stakeIndex;
|
||||
string requestContentId;
|
||||
string evidenceContentId;
|
||||
Citation[] citations;
|
||||
bool approval;
|
||||
}
|
||||
|
||||
|
@ -142,7 +144,8 @@ abstract contract WorkContract is
|
|||
/// Accept work evidence from worker
|
||||
function submitWorkEvidence(
|
||||
uint requestIndex,
|
||||
string calldata evidenceContentId
|
||||
string calldata evidenceContentId,
|
||||
Citation[] calldata citations
|
||||
) external {
|
||||
WorkRequest storage request = requests[requestIndex];
|
||||
require(
|
||||
|
@ -156,6 +159,9 @@ abstract contract WorkContract is
|
|||
);
|
||||
request.status = WorkStatus.EvidenceSubmitted;
|
||||
request.evidenceContentId = evidenceContentId;
|
||||
for (uint i = 0; i < citations.length; i++) {
|
||||
request.citations.push(citations[i]);
|
||||
}
|
||||
emit WorkEvidenceSubmitted(requestIndex);
|
||||
}
|
||||
|
||||
|
@ -173,7 +179,11 @@ abstract contract WorkContract is
|
|||
request.status = WorkStatus.ApprovalSubmitted;
|
||||
request.approval = approval;
|
||||
// Make work evidence post
|
||||
uint postIndex = dao.addPost(stake.worker, request.evidenceContentId);
|
||||
uint postIndex = dao.addPost(
|
||||
stake.worker,
|
||||
request.evidenceContentId,
|
||||
request.citations
|
||||
);
|
||||
emit WorkApprovalSubmitted(requestIndex, approval);
|
||||
// Initiate validation pool
|
||||
uint poolIndex = dao.initiateValidationPool{value: request.fee}(
|
||||
|
|
|
@ -3,13 +3,17 @@ pragma solidity ^0.8.24;
|
|||
|
||||
import "./Reputation.sol";
|
||||
|
||||
struct Citation {
|
||||
int weightPercent;
|
||||
uint targetPostIndex;
|
||||
}
|
||||
|
||||
struct Post {
|
||||
uint id;
|
||||
address sender;
|
||||
address author;
|
||||
string contentId;
|
||||
uint reputation;
|
||||
// TODO: citations
|
||||
Citation[] citations;
|
||||
}
|
||||
|
||||
contract Forum is Reputation {
|
||||
|
@ -20,7 +24,8 @@ contract Forum is Reputation {
|
|||
|
||||
function addPost(
|
||||
address author,
|
||||
string calldata contentId
|
||||
string calldata contentId,
|
||||
Citation[] calldata citations
|
||||
) external returns (uint postIndex) {
|
||||
postIndex = postCount++;
|
||||
Post storage post = posts[postIndex];
|
||||
|
@ -28,12 +33,29 @@ contract Forum is Reputation {
|
|||
post.sender = msg.sender;
|
||||
post.id = postIndex;
|
||||
post.contentId = contentId;
|
||||
for (uint i = 0; i < citations.length; i++) {
|
||||
post.citations.push(citations[i]);
|
||||
}
|
||||
emit PostAdded(postIndex);
|
||||
}
|
||||
|
||||
function _onValidatePost(uint postIndex, uint amount) internal {
|
||||
_propagateValue(postIndex, int(amount));
|
||||
}
|
||||
|
||||
function _propagateValue(uint postIndex, int amount) internal {
|
||||
Post storage post = posts[postIndex];
|
||||
post.reputation = amount;
|
||||
_update(address(this), post.author, amount);
|
||||
int totalCitationWeight;
|
||||
for (uint i = 0; i < post.citations.length; i++) {
|
||||
totalCitationWeight += post.citations[i].weightPercent;
|
||||
}
|
||||
int totalOutboundAmount;
|
||||
for (uint i = 0; i < post.citations.length; i++) {
|
||||
int share = (amount * post.citations[i].weightPercent) / 100;
|
||||
totalOutboundAmount += share;
|
||||
_propagateValue(post.citations[i].targetPostIndex, share);
|
||||
}
|
||||
uint remaining = uint(amount - totalOutboundAmount);
|
||||
_update(address(this), post.author, remaining);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,328 +0,0 @@
|
|||
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;
|
||||
let account2;
|
||||
const POOL_DURATION = 3600; // 1 hour
|
||||
const POOL_FEE = 100;
|
||||
const emptyCallbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
|
||||
|
||||
const initiateValidationPool = ({
|
||||
postIndex, duration,
|
||||
quorum, winRatio, bindingPercent,
|
||||
redistributeLosingStakes, callbackOnValidate,
|
||||
callbackData, fee,
|
||||
} = {}) => dao.initiateValidationPool(
|
||||
postIndex ?? 0,
|
||||
duration ?? POOL_DURATION,
|
||||
quorum ?? [1, 3],
|
||||
winRatio ?? [1, 2],
|
||||
bindingPercent ?? 100,
|
||||
redistributeLosingStakes ?? true,
|
||||
callbackOnValidate ?? false,
|
||||
callbackData ?? emptyCallbackData,
|
||||
{ value: fee ?? POOL_FEE },
|
||||
);
|
||||
|
||||
beforeEach(async () => {
|
||||
({ dao, account1, account2 } = await loadFixture(deploy));
|
||||
await dao.addPost(account1, 'content-id');
|
||||
const init = () => initiateValidationPool({ fee: 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 init = () => initiateValidationPool({ fee: 0 });
|
||||
await expect(init()).to.be.revertedWith('Fee is required to initiate validation pool');
|
||||
});
|
||||
|
||||
it('should not be able to initiate a validation pool with a quorum below the minimum', async () => {
|
||||
const init = () => initiateValidationPool({ quorum: [1, 11] });
|
||||
await expect(init()).to.be.revertedWith('Quorum is below minimum');
|
||||
});
|
||||
|
||||
it('should not be able to initiate a validation pool with a quorum greater than 1', async () => {
|
||||
const init = () => initiateValidationPool({ quorum: [11, 10] });
|
||||
await expect(init()).to.be.revertedWith('Quorum is greater than one');
|
||||
});
|
||||
|
||||
it('should not be able to initiate a validation pool with duration below minimum', async () => {
|
||||
const init = () => initiateValidationPool({ duration: 0 });
|
||||
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 init = () => initiateValidationPool({ duration: 40000000000000 });
|
||||
await expect(init()).to.be.revertedWith('Duration is too long');
|
||||
});
|
||||
|
||||
it('should not be able to initiate a validation pool with bindingPercent above 100', async () => {
|
||||
const init = () => initiateValidationPool({ bindingPercent: 101 });
|
||||
await expect(init()).to.be.revertedWith('Binding percent must be <= 100');
|
||||
});
|
||||
|
||||
it('should be able to initiate a second validation pool', async () => {
|
||||
const init = () => initiateValidationPool();
|
||||
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.params.duration).to.equal(POOL_DURATION);
|
||||
expect(pool.postIndex).to.equal(0);
|
||||
expect(pool.resolved).to.be.false;
|
||||
expect(pool.sender).to.equal(account1);
|
||||
});
|
||||
});
|
||||
|
||||
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 initiateValidationPool();
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(100);
|
||||
});
|
||||
|
||||
it('should be able to stake before validation pool has elapsed', async () => {
|
||||
await dao.stakeOnValidationPool(1, 10, true);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(100);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, true, true);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
});
|
||||
|
||||
it('should not be able to stake after validation pool has elapsed', async () => {
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.stakeOnValidationPool(1, 10, true)).to.be.revertedWith('Pool end time has passed');
|
||||
});
|
||||
|
||||
it('should be able to stake against a validation pool', async () => {
|
||||
await dao.stakeOnValidationPool(1, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(100);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, false, true);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
const pool = await dao.validationPools(1);
|
||||
expect(pool.outcome).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('Evaluate outcome', () => {
|
||||
it('should not be able to evaluate outcome before duration has elapsed if not all rep has been staked', async () => {
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(0));
|
||||
await initiateValidationPool({ fee: 100 });
|
||||
await expect(dao.evaluateOutcome(1)).to.be.revertedWith('Pool end time has not yet arrived');
|
||||
});
|
||||
|
||||
it('should not be able to evaluate outcome before duration has elapsed unless all rep has been staked', async () => {
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(0));
|
||||
await initiateValidationPool({ fee: 100 });
|
||||
await dao.stakeOnValidationPool(1, 100, true);
|
||||
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, true, true);
|
||||
});
|
||||
|
||||
it('should be able to evaluate outcome after duration has elapsed', async () => {
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(100);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(0)).to.emit(dao, 'ValidationPoolResolved').withArgs(0, true, 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, 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,
|
||||
[1, 3],
|
||||
[1, 2],
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
emptyCallbackData,
|
||||
{ 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, true);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, true, true);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
});
|
||||
|
||||
it('should not be able to evaluate outcome if quorum is not met', async () => {
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(0)).to.emit(dao, 'ValidationPoolResolved').withArgs(0, true, true);
|
||||
|
||||
const init = () => initiateValidationPool({ quorum: [1, 1] });
|
||||
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(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, false, false);
|
||||
});
|
||||
|
||||
describe('Validation pool options', () => {
|
||||
beforeEach(async () => {
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(0);
|
||||
await dao.addPost(account2, 'content-id');
|
||||
const init = () => initiateValidationPool({ postIndex: 1 });
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(1);
|
||||
});
|
||||
|
||||
it('Binding validation pool should redistribute stakes', async () => {
|
||||
const init = () => initiateValidationPool();
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
await dao.connect(account1).stakeOnValidationPool(2, 10, true);
|
||||
await dao.connect(account2).stakeOnValidationPool(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(account2)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(100);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(2);
|
||||
expect(await dao.balanceOf(account1)).to.equal(210);
|
||||
expect(await dao.balanceOf(account2)).to.equal(90);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
});
|
||||
|
||||
it('Non binding validation pool should not redistribute stakes', async () => {
|
||||
const init = () => initiateValidationPool({ bindingPercent: 0 });
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
await dao.connect(account1).stakeOnValidationPool(2, 10, true);
|
||||
await dao.connect(account2).stakeOnValidationPool(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(account2)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(100);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(2);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
expect(await dao.balanceOf(account2)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
});
|
||||
|
||||
it('Partially binding validation pool should redistribute some stakes', async () => {
|
||||
const init = () => initiateValidationPool({ bindingPercent: 50 });
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
await dao.connect(account1).stakeOnValidationPool(2, 10, true);
|
||||
await dao.connect(account2).stakeOnValidationPool(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(account2)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(100);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(2);
|
||||
expect(await dao.balanceOf(account1)).to.equal(205);
|
||||
expect(await dao.balanceOf(account2)).to.equal(95);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
expect(await dao.totalSupply()).to.equal(300);
|
||||
});
|
||||
|
||||
it('If redistributeLosingStakes is false, validation pool should burn binding portion of losing stakes', async () => {
|
||||
const init = () => initiateValidationPool({
|
||||
bindingPercent: 50,
|
||||
redistributeLosingStakes: false,
|
||||
});
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
await dao.connect(account1).stakeOnValidationPool(2, 10, true);
|
||||
await dao.connect(account2).stakeOnValidationPool(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(account2)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(100);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(2);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
expect(await dao.balanceOf(account2)).to.equal(95);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
expect(await dao.totalSupply()).to.equal(295);
|
||||
});
|
||||
|
||||
it('If redistributeLosingStakes is false and bindingPercent is 0, accounts should recover initial balances', async () => {
|
||||
const init = () => initiateValidationPool({
|
||||
bindingPercent: 0,
|
||||
redistributeLosingStakes: false,
|
||||
});
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
await dao.connect(account1).stakeOnValidationPool(2, 10, true);
|
||||
await dao.connect(account2).stakeOnValidationPool(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(account2)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(100);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(2);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
expect(await dao.balanceOf(account2)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
expect(await dao.totalSupply()).to.equal(300);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,73 @@
|
|||
const {
|
||||
time,
|
||||
loadFixture,
|
||||
} = require('@nomicfoundation/hardhat-toolbox/network-helpers');
|
||||
const { expect } = require('chai');
|
||||
const { ethers } = require('hardhat');
|
||||
|
||||
describe('Forum', () => {
|
||||
async function deploy() {
|
||||
const [account1, account2] = await ethers.getSigners();
|
||||
const DAO = await ethers.getContractFactory('DAO');
|
||||
const dao = await DAO.deploy();
|
||||
return { dao, account1, account2 };
|
||||
}
|
||||
let dao;
|
||||
let account1;
|
||||
let account2;
|
||||
const POOL_DURATION = 3600; // 1 hour
|
||||
const POOL_FEE = 100;
|
||||
const emptyCallbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
|
||||
|
||||
const initiateValidationPool = ({
|
||||
postIndex, duration,
|
||||
quorum, winRatio, bindingPercent,
|
||||
redistributeLosingStakes, callbackOnValidate,
|
||||
callbackData, fee,
|
||||
} = {}) => dao.initiateValidationPool(
|
||||
postIndex ?? 0,
|
||||
duration ?? POOL_DURATION,
|
||||
quorum ?? [1, 3],
|
||||
winRatio ?? [1, 2],
|
||||
bindingPercent ?? 100,
|
||||
redistributeLosingStakes ?? true,
|
||||
callbackOnValidate ?? false,
|
||||
callbackData ?? emptyCallbackData,
|
||||
{ value: fee ?? POOL_FEE },
|
||||
);
|
||||
|
||||
describe('Post', () => {
|
||||
beforeEach(async () => {
|
||||
({ dao, account1, account2 } = await loadFixture(deploy));
|
||||
});
|
||||
|
||||
it('should be able to add a post', async () => {
|
||||
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 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);
|
||||
});
|
||||
|
||||
it('should propagate reputation to citations', async () => {
|
||||
await dao.addPost(account1, 'content-id', []);
|
||||
await dao.addPost(account2, 'second-content-id', [{ weightPercent: 50, targetPostIndex: 0 }]);
|
||||
await initiateValidationPool({ postIndex: 1 });
|
||||
const pool = await dao.validationPools(0);
|
||||
expect(pool.postIndex).to.equal(1);
|
||||
await dao.evaluateOutcome(0);
|
||||
expect(await dao.balanceOf(account2)).to.equal(50);
|
||||
expect(await dao.balanceOf(account1)).to.equal(50);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -19,7 +19,7 @@ describe('Onboarding', () => {
|
|||
const Onboarding = await ethers.getContractFactory('Onboarding');
|
||||
const onboarding = await Onboarding.deploy(dao.target, proposals.target, PRICE);
|
||||
|
||||
await dao.addPost(account1, 'content-id');
|
||||
await dao.addPost(account1, 'content-id', []);
|
||||
const callbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
|
||||
await dao.initiateValidationPool(
|
||||
0,
|
||||
|
@ -66,7 +66,7 @@ describe('Onboarding', () => {
|
|||
|
||||
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 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);
|
||||
|
@ -82,7 +82,7 @@ describe('Onboarding', () => {
|
|||
|
||||
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 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);
|
||||
|
@ -90,16 +90,16 @@ describe('Onboarding', () => {
|
|||
|
||||
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 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 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');
|
||||
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 () => {
|
||||
|
@ -115,7 +115,7 @@ describe('Onboarding', () => {
|
|||
} = 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 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);
|
||||
|
@ -137,7 +137,7 @@ describe('Onboarding', () => {
|
|||
} = 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 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);
|
||||
|
|
|
@ -16,8 +16,8 @@ describe('Proposal', () => {
|
|||
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');
|
||||
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,
|
||||
|
|
|
@ -0,0 +1,297 @@
|
|||
const {
|
||||
time,
|
||||
loadFixture,
|
||||
} = require('@nomicfoundation/hardhat-toolbox/network-helpers');
|
||||
const { expect } = require('chai');
|
||||
const { ethers } = require('hardhat');
|
||||
|
||||
describe('Validation Pools', () => {
|
||||
async function deploy() {
|
||||
const [account1, account2] = await ethers.getSigners();
|
||||
const DAO = await ethers.getContractFactory('DAO');
|
||||
const dao = await DAO.deploy();
|
||||
return { dao, account1, account2 };
|
||||
}
|
||||
let dao;
|
||||
let account1;
|
||||
let account2;
|
||||
const POOL_DURATION = 3600; // 1 hour
|
||||
const POOL_FEE = 100;
|
||||
const emptyCallbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
|
||||
|
||||
const initiateValidationPool = ({
|
||||
postIndex, duration,
|
||||
quorum, winRatio, bindingPercent,
|
||||
redistributeLosingStakes, callbackOnValidate,
|
||||
callbackData, fee,
|
||||
} = {}) => dao.initiateValidationPool(
|
||||
postIndex ?? 0,
|
||||
duration ?? POOL_DURATION,
|
||||
quorum ?? [1, 3],
|
||||
winRatio ?? [1, 2],
|
||||
bindingPercent ?? 100,
|
||||
redistributeLosingStakes ?? true,
|
||||
callbackOnValidate ?? false,
|
||||
callbackData ?? emptyCallbackData,
|
||||
{ value: fee ?? POOL_FEE },
|
||||
);
|
||||
|
||||
beforeEach(async () => {
|
||||
({ dao, account1, account2 } = await loadFixture(deploy));
|
||||
await dao.addPost(account1, 'content-id', []);
|
||||
const init = () => initiateValidationPool({ fee: 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 init = () => initiateValidationPool({ fee: 0 });
|
||||
await expect(init()).to.be.revertedWith('Fee is required to initiate validation pool');
|
||||
});
|
||||
|
||||
it('should not be able to initiate a validation pool with a quorum below the minimum', async () => {
|
||||
const init = () => initiateValidationPool({ quorum: [1, 11] });
|
||||
await expect(init()).to.be.revertedWith('Quorum is below minimum');
|
||||
});
|
||||
|
||||
it('should not be able to initiate a validation pool with a quorum greater than 1', async () => {
|
||||
const init = () => initiateValidationPool({ quorum: [11, 10] });
|
||||
await expect(init()).to.be.revertedWith('Quorum is greater than one');
|
||||
});
|
||||
|
||||
it('should not be able to initiate a validation pool with duration below minimum', async () => {
|
||||
const init = () => initiateValidationPool({ duration: 0 });
|
||||
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 init = () => initiateValidationPool({ duration: 40000000000000 });
|
||||
await expect(init()).to.be.revertedWith('Duration is too long');
|
||||
});
|
||||
|
||||
it('should not be able to initiate a validation pool with bindingPercent above 100', async () => {
|
||||
const init = () => initiateValidationPool({ bindingPercent: 101 });
|
||||
await expect(init()).to.be.revertedWith('Binding percent must be <= 100');
|
||||
});
|
||||
|
||||
it('should be able to initiate a second validation pool', async () => {
|
||||
const init = () => initiateValidationPool();
|
||||
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.params.duration).to.equal(POOL_DURATION);
|
||||
expect(pool.postIndex).to.equal(0);
|
||||
expect(pool.resolved).to.be.false;
|
||||
expect(pool.sender).to.equal(account1);
|
||||
});
|
||||
});
|
||||
|
||||
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 initiateValidationPool();
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(100);
|
||||
});
|
||||
|
||||
it('should be able to stake before validation pool has elapsed', async () => {
|
||||
await dao.stakeOnValidationPool(1, 10, true);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(100);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, true, true);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
});
|
||||
|
||||
it('should not be able to stake after validation pool has elapsed', async () => {
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.stakeOnValidationPool(1, 10, true)).to.be.revertedWith('Pool end time has passed');
|
||||
});
|
||||
|
||||
it('should be able to stake against a validation pool', async () => {
|
||||
await dao.stakeOnValidationPool(1, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(100);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, false, true);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
const pool = await dao.validationPools(1);
|
||||
expect(pool.outcome).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('Evaluate outcome', () => {
|
||||
it('should not be able to evaluate outcome before duration has elapsed if not all rep has been staked', async () => {
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(0));
|
||||
await initiateValidationPool({ fee: 100 });
|
||||
await expect(dao.evaluateOutcome(1)).to.be.revertedWith('Pool end time has not yet arrived');
|
||||
});
|
||||
|
||||
it('should not be able to evaluate outcome before duration has elapsed unless all rep has been staked', async () => {
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(0));
|
||||
await initiateValidationPool({ fee: 100 });
|
||||
await dao.stakeOnValidationPool(1, 100, true);
|
||||
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, true, true);
|
||||
});
|
||||
|
||||
it('should be able to evaluate outcome after duration has elapsed', async () => {
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(100);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(0)).to.emit(dao, 'ValidationPoolResolved').withArgs(0, true, 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, 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,
|
||||
[1, 3],
|
||||
[1, 2],
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
emptyCallbackData,
|
||||
{ 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, true);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, true, true);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
});
|
||||
|
||||
it('should not be able to evaluate outcome if quorum is not met', async () => {
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(0)).to.emit(dao, 'ValidationPoolResolved').withArgs(0, true, true);
|
||||
|
||||
const init = () => initiateValidationPool({ quorum: [1, 1] });
|
||||
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(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, false, false);
|
||||
});
|
||||
|
||||
describe('Validation pool options', () => {
|
||||
beforeEach(async () => {
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(0);
|
||||
await dao.addPost(account2, 'content-id', []);
|
||||
const init = () => initiateValidationPool({ postIndex: 1 });
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(1);
|
||||
});
|
||||
|
||||
it('Binding validation pool should redistribute stakes', async () => {
|
||||
const init = () => initiateValidationPool();
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
await dao.connect(account1).stakeOnValidationPool(2, 10, true);
|
||||
await dao.connect(account2).stakeOnValidationPool(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(account2)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(100);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(2);
|
||||
expect(await dao.balanceOf(account1)).to.equal(210);
|
||||
expect(await dao.balanceOf(account2)).to.equal(90);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
});
|
||||
|
||||
it('Non binding validation pool should not redistribute stakes', async () => {
|
||||
const init = () => initiateValidationPool({ bindingPercent: 0 });
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
await dao.connect(account1).stakeOnValidationPool(2, 10, true);
|
||||
await dao.connect(account2).stakeOnValidationPool(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(account2)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(100);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(2);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
expect(await dao.balanceOf(account2)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
});
|
||||
|
||||
it('Partially binding validation pool should redistribute some stakes', async () => {
|
||||
const init = () => initiateValidationPool({ bindingPercent: 50 });
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
await dao.connect(account1).stakeOnValidationPool(2, 10, true);
|
||||
await dao.connect(account2).stakeOnValidationPool(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(account2)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(100);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(2);
|
||||
expect(await dao.balanceOf(account1)).to.equal(205);
|
||||
expect(await dao.balanceOf(account2)).to.equal(95);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
expect(await dao.totalSupply()).to.equal(300);
|
||||
});
|
||||
|
||||
it('If redistributeLosingStakes is false, validation pool should burn binding portion of losing stakes', async () => {
|
||||
const init = () => initiateValidationPool({
|
||||
bindingPercent: 50,
|
||||
redistributeLosingStakes: false,
|
||||
});
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
await dao.connect(account1).stakeOnValidationPool(2, 10, true);
|
||||
await dao.connect(account2).stakeOnValidationPool(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(account2)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(100);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(2);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
expect(await dao.balanceOf(account2)).to.equal(95);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
expect(await dao.totalSupply()).to.equal(295);
|
||||
});
|
||||
|
||||
it('If redistributeLosingStakes is false and bindingPercent is 0, accounts should recover initial balances', async () => {
|
||||
const init = () => initiateValidationPool({
|
||||
bindingPercent: 0,
|
||||
redistributeLosingStakes: false,
|
||||
});
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
await dao.connect(account1).stakeOnValidationPool(2, 10, true);
|
||||
await dao.connect(account2).stakeOnValidationPool(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(account2)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(100);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(2);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
expect(await dao.balanceOf(account2)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
expect(await dao.totalSupply()).to.equal(300);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -19,7 +19,7 @@ describe('Work1', () => {
|
|||
const Work1 = await ethers.getContractFactory('Work1');
|
||||
const work1 = await Work1.deploy(dao.target, proposals.target, WORK1_PRICE);
|
||||
|
||||
await dao.addPost(account1, 'some-content-id');
|
||||
await dao.addPost(account1, 'some-content-id', []);
|
||||
const callbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
|
||||
await dao.initiateValidationPool(
|
||||
0,
|
||||
|
@ -186,24 +186,24 @@ describe('Work1', () => {
|
|||
|
||||
it('should be able to submit work evidence', async () => {
|
||||
await work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await expect(work1.submitWorkEvidence(0, 'evidence-content-id')).to.emit(work1, 'WorkEvidenceSubmitted').withArgs(0);
|
||||
await expect(work1.submitWorkEvidence(0, 'evidence-content-id', [])).to.emit(work1, 'WorkEvidenceSubmitted').withArgs(0);
|
||||
});
|
||||
|
||||
it('should not be able to submit work evidence twice', async () => {
|
||||
await work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await expect(work1.submitWorkEvidence(0, 'evidence-content-id')).to.emit(work1, 'WorkEvidenceSubmitted').withArgs(0);
|
||||
await expect(work1.submitWorkEvidence(0, 'evidence-content-id')).to.be.revertedWith('Status must be Requested');
|
||||
await expect(work1.submitWorkEvidence(0, 'evidence-content-id', [])).to.emit(work1, 'WorkEvidenceSubmitted').withArgs(0);
|
||||
await expect(work1.submitWorkEvidence(0, 'evidence-content-id', [])).to.be.revertedWith('Status must be Requested');
|
||||
});
|
||||
|
||||
it('should not be able to submit work evidence for a different worker', async () => {
|
||||
await work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await expect(work1.connect(account2).submitWorkEvidence(0, 'evidence-content-id'))
|
||||
await expect(work1.connect(account2).submitWorkEvidence(0, 'evidence-content-id', []))
|
||||
.to.be.revertedWith('Worker can only submit evidence for work they are assigned');
|
||||
});
|
||||
|
||||
it('should be able to submit work approval', async () => {
|
||||
await work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await work1.submitWorkEvidence(0, 'evidence-content-id');
|
||||
await work1.submitWorkEvidence(0, 'evidence-content-id', []);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(work1.target)).to.equal(0);
|
||||
await expect(work1.submitWorkApproval(0, true))
|
||||
|
@ -227,7 +227,7 @@ describe('Work1', () => {
|
|||
|
||||
it('should be able to submit work disapproval', async () => {
|
||||
await work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await work1.submitWorkEvidence(0, 'evidence-content-id');
|
||||
await work1.submitWorkEvidence(0, 'evidence-content-id', []);
|
||||
await expect(work1.submitWorkApproval(0, false))
|
||||
.to.emit(dao, 'ValidationPoolInitiated').withArgs(1)
|
||||
.to.emit(work1, 'WorkApprovalSubmitted').withArgs(0, false);
|
||||
|
@ -235,16 +235,16 @@ describe('Work1', () => {
|
|||
|
||||
it('should not be able to submit work approval/disapproval twice', async () => {
|
||||
await work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await work1.submitWorkEvidence(0, 'evidence-content-id');
|
||||
await work1.submitWorkEvidence(0, 'evidence-content-id', []);
|
||||
await expect(work1.submitWorkApproval(0, true)).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
|
||||
await expect(work1.submitWorkApproval(0, true)).to.be.revertedWith('Status must be EvidenceSubmitted');
|
||||
});
|
||||
|
||||
it('should not be able to submit work evidence after work approval', async () => {
|
||||
await work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await work1.submitWorkEvidence(0, 'evidence-content-id');
|
||||
await work1.submitWorkEvidence(0, 'evidence-content-id', []);
|
||||
await expect(work1.submitWorkApproval(0, true)).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
|
||||
await expect(work1.submitWorkEvidence(0, 'evidence-content-id')).to.be.revertedWith('Status must be Requested');
|
||||
await expect(work1.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 () => {
|
||||
|
|
Loading…
Reference in New Issue