propagate reputation to citations
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 37s Details

This commit is contained in:
Ladd Hoffman 2024-04-10 16:31:31 -05:00
parent 5c712f350a
commit dc18c69b09
4 changed files with 387 additions and 334 deletions

View File

@ -4,7 +4,7 @@ pragma solidity ^0.8.24;
import "./Reputation.sol";
struct Citation {
int[2] weight;
int weightPercent;
uint targetPostIndex;
}
@ -13,7 +13,6 @@ struct Post {
address sender;
address author;
string contentId;
uint reputation;
Citation[] citations;
}
@ -41,10 +40,22 @@ contract Forum is Reputation {
}
function _onValidatePost(uint postIndex, uint amount) internal {
Post storage post = posts[postIndex];
post.reputation = amount;
_update(address(this), post.author, amount);
_propagateValue(postIndex, int(amount));
}
function _propagateValue() internal {}
function _propagateValue(uint postIndex, int amount) internal {
Post storage post = posts[postIndex];
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);
}
}

View File

@ -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);
});
});
});
});
});

73
ethereum/test/Forum.js Normal file
View File

@ -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);
});
});
});

View File

@ -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);
});
});
});
});