started on tests for lightweight bench
This commit is contained in:
parent
da20410f87
commit
3c4a5cbb7a
|
@ -28,7 +28,7 @@ if (ENABLE_STAKING === 'false') {
|
|||
const start = async () => {
|
||||
dao.on('ValidationPoolInitiated', async (poolIndex) => {
|
||||
console.log('Validation Pool Initiated, index', poolIndex);
|
||||
const pool = await dao.validationPools(poolIndex);
|
||||
const pool = await dao.getValidationPool(poolIndex);
|
||||
// Read post from database
|
||||
let post;
|
||||
try {
|
||||
|
|
|
@ -4,16 +4,16 @@ pragma solidity ^0.8.24;
|
|||
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
import "./Reputation.sol";
|
||||
import "./Bench.sol";
|
||||
import "./LightweightBench.sol";
|
||||
import "../GlobalForum.sol";
|
||||
import "../interfaces/IAcceptAvailability.sol";
|
||||
import "../interfaces/IOnValidate.sol";
|
||||
|
||||
import "hardhat/console.sol";
|
||||
|
||||
contract DAO {
|
||||
Reputation rep;
|
||||
GlobalForum forum;
|
||||
Bench bench;
|
||||
LightweightBench lightweightBench;
|
||||
mapping(uint => address) public members;
|
||||
uint public memberCount;
|
||||
mapping(address => bool) public isMember;
|
||||
|
@ -31,16 +31,28 @@ contract DAO {
|
|||
bool votePasses,
|
||||
bool quorumMet
|
||||
);
|
||||
event LWResultProposed(
|
||||
uint poolIndex,
|
||||
uint proposedResultIndex,
|
||||
string proposedResultHash
|
||||
);
|
||||
|
||||
constructor(Reputation reputation_, Bench bench_, GlobalForum forum_) {
|
||||
constructor(
|
||||
Reputation reputation_,
|
||||
Bench bench_,
|
||||
LightweightBench lightweightBench_,
|
||||
GlobalForum forum_
|
||||
) {
|
||||
rep = reputation_;
|
||||
bench = bench_;
|
||||
lightweightBench = lightweightBench_;
|
||||
forum = forum_;
|
||||
rep.registerDAO(this);
|
||||
bench.registerDAO(this, forum);
|
||||
lightweightBench.registerDAO(this);
|
||||
}
|
||||
|
||||
function emitPostAdded(string memory id) public {
|
||||
function emitPostAdded(string calldata id) public {
|
||||
emit PostAdded(id);
|
||||
}
|
||||
|
||||
|
@ -60,9 +72,22 @@ contract DAO {
|
|||
emit LWValidationPoolInitiated(poolIndex);
|
||||
}
|
||||
|
||||
function emitLWResultProposed(
|
||||
uint poolIndex,
|
||||
uint proposedResultIndex,
|
||||
string calldata proposedResultHash
|
||||
) public {
|
||||
emit LWResultProposed(
|
||||
poolIndex,
|
||||
proposedResultIndex,
|
||||
proposedResultHash
|
||||
);
|
||||
}
|
||||
|
||||
function update(address from, address to, uint256 value) public {
|
||||
require(
|
||||
msg.sender == address(bench),
|
||||
msg.sender == address(lightweightBench) ||
|
||||
msg.sender == address(bench),
|
||||
"Only DAO core contracts may call update"
|
||||
);
|
||||
rep.update(from, to, value);
|
||||
|
@ -70,7 +95,8 @@ contract DAO {
|
|||
|
||||
function mint(address account, uint256 value) public {
|
||||
require(
|
||||
msg.sender == address(bench),
|
||||
msg.sender == address(lightweightBench) ||
|
||||
msg.sender == address(bench),
|
||||
"Only DAO core contracts may call mint"
|
||||
);
|
||||
rep.mint(account, value);
|
||||
|
@ -78,7 +104,8 @@ contract DAO {
|
|||
|
||||
function burn(address account, uint256 value) public {
|
||||
require(
|
||||
msg.sender == address(bench),
|
||||
msg.sender == address(lightweightBench) ||
|
||||
msg.sender == address(bench),
|
||||
"Only DAO core contracts may call burn"
|
||||
);
|
||||
rep.burn(account, value);
|
||||
|
@ -86,7 +113,8 @@ contract DAO {
|
|||
|
||||
function registerMember(address account) public {
|
||||
require(
|
||||
msg.sender == address(bench),
|
||||
msg.sender == address(lightweightBench) ||
|
||||
msg.sender == address(bench),
|
||||
"Only DAO core contracts may call registerMember"
|
||||
);
|
||||
if (!isMember[account]) {
|
||||
|
@ -155,7 +183,7 @@ contract DAO {
|
|||
}
|
||||
}
|
||||
|
||||
function validationPools(
|
||||
function getValidationPool(
|
||||
uint poolIndex
|
||||
)
|
||||
public
|
||||
|
@ -173,7 +201,7 @@ contract DAO {
|
|||
return bench.validationPools(poolIndex);
|
||||
}
|
||||
|
||||
function validationPoolCount() public view returns (uint) {
|
||||
function getValidationPoolCount() public view returns (uint) {
|
||||
return bench.validationPoolCount();
|
||||
}
|
||||
|
||||
|
@ -210,7 +238,6 @@ contract DAO {
|
|||
balanceOf(msg.sender) >= amount,
|
||||
"Insufficient REP balance to cover stake"
|
||||
);
|
||||
// TODO: Encumber tokens
|
||||
bench.stakeOnValidationPool(poolIndex, msg.sender, amount, inFavor);
|
||||
}
|
||||
|
||||
|
@ -232,6 +259,104 @@ contract DAO {
|
|||
return bench.evaluateOutcome(poolIndex);
|
||||
}
|
||||
|
||||
function getLWValidationPool(
|
||||
uint poolIndex
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (
|
||||
uint id,
|
||||
address sender,
|
||||
uint stakeCount,
|
||||
LWVPoolParams memory params,
|
||||
LWVPoolProps memory props,
|
||||
bool callbackOnValidate,
|
||||
bytes memory callbackData
|
||||
)
|
||||
{
|
||||
return lightweightBench.validationPools(poolIndex);
|
||||
}
|
||||
|
||||
function getLWValidationPoolCount() public view returns (uint) {
|
||||
return lightweightBench.validationPoolCount();
|
||||
}
|
||||
|
||||
function initiateLWValidationPool(
|
||||
string calldata postId,
|
||||
uint duration,
|
||||
uint[2] calldata quorum, // [Numerator, Denominator]
|
||||
uint[2] calldata winRatio, // [Numerator, Denominator]
|
||||
uint bindingPercent,
|
||||
bool redistributeLosingStakes,
|
||||
bool callbackOnValidate,
|
||||
bytes calldata callbackData
|
||||
) external payable returns (uint) {
|
||||
return
|
||||
lightweightBench.initiateValidationPool{value: msg.value}(
|
||||
msg.sender,
|
||||
postId,
|
||||
duration,
|
||||
quorum,
|
||||
winRatio,
|
||||
bindingPercent,
|
||||
redistributeLosingStakes,
|
||||
callbackOnValidate,
|
||||
callbackData
|
||||
);
|
||||
}
|
||||
|
||||
function proposeLWResult(
|
||||
uint poolIndex,
|
||||
string calldata resultHash,
|
||||
Transfer[] calldata transfers
|
||||
) external {
|
||||
lightweightBench.proposeResult(poolIndex, resultHash, transfers);
|
||||
}
|
||||
|
||||
function stakeOnLWValidationPool(
|
||||
uint poolIndex,
|
||||
string calldata resultHash,
|
||||
uint256 amount,
|
||||
bool inFavor
|
||||
) public {
|
||||
require(
|
||||
balanceOf(msg.sender) >= amount,
|
||||
"Insufficient REP balance to cover stake"
|
||||
);
|
||||
lightweightBench.stakeOnValidationPool(
|
||||
poolIndex,
|
||||
resultHash,
|
||||
msg.sender,
|
||||
amount,
|
||||
inFavor
|
||||
);
|
||||
}
|
||||
|
||||
/// Accept reputation stakes toward a validation pool
|
||||
function delegatedStakeOnLWValidationPool(
|
||||
uint poolIndex,
|
||||
string calldata resultHash,
|
||||
address owner,
|
||||
uint256 amount,
|
||||
bool inFavor
|
||||
) public {
|
||||
if (allowance(owner, msg.sender) < amount) {
|
||||
amount = allowance(owner, msg.sender);
|
||||
}
|
||||
rep.spendAllowance(owner, msg.sender, amount);
|
||||
lightweightBench.stakeOnValidationPool(
|
||||
poolIndex,
|
||||
resultHash,
|
||||
owner,
|
||||
amount,
|
||||
inFavor
|
||||
);
|
||||
}
|
||||
|
||||
function evaluateLWOutcome(uint poolIndex) public returns (bool) {
|
||||
return lightweightBench.evaluateOutcome(poolIndex);
|
||||
}
|
||||
|
||||
function onValidate(
|
||||
address target,
|
||||
bool votePasses,
|
||||
|
@ -241,7 +366,8 @@ contract DAO {
|
|||
bytes calldata callbackData
|
||||
) public {
|
||||
require(
|
||||
msg.sender == address(bench),
|
||||
msg.sender == address(lightweightBench) ||
|
||||
msg.sender == address(bench),
|
||||
"Only DAO core contracts may call onValidate"
|
||||
);
|
||||
IOnValidate(target).onValidate(
|
||||
|
|
|
@ -20,13 +20,13 @@ struct LWVPoolProps {
|
|||
bool outcome;
|
||||
}
|
||||
|
||||
contract LightweightBench {
|
||||
struct Transfer {
|
||||
address from;
|
||||
address to;
|
||||
uint amount;
|
||||
}
|
||||
struct Transfer {
|
||||
address from;
|
||||
address to;
|
||||
uint amount;
|
||||
}
|
||||
|
||||
contract LightweightBench {
|
||||
struct ProposedResult {
|
||||
Transfer[] transfers;
|
||||
uint stakedFor;
|
||||
|
@ -121,6 +121,10 @@ contract LightweightBench {
|
|||
string calldata resultHash,
|
||||
Transfer[] calldata transfers
|
||||
) external {
|
||||
require(
|
||||
transfers.length > 0,
|
||||
"The proposed result contains no transfers"
|
||||
);
|
||||
Pool storage pool = validationPools[poolIndex];
|
||||
require(
|
||||
block.timestamp <= pool.props.endTime,
|
||||
|
@ -129,14 +133,16 @@ contract LightweightBench {
|
|||
ProposedResult storage proposedResult = pool.proposedResults[
|
||||
resultHash
|
||||
];
|
||||
pool.proposedResultHashes.push(resultHash);
|
||||
require(
|
||||
proposedResult.transfers.length == 0,
|
||||
"This result hash has already been proposed"
|
||||
);
|
||||
uint resultIndex = pool.proposedResultHashes.length;
|
||||
pool.proposedResultHashes.push(resultHash);
|
||||
for (uint i = 0; i < transfers.length; i++) {
|
||||
proposedResult.transfers.push(transfers[i]);
|
||||
}
|
||||
dao.emitLWResultProposed(poolIndex, resultIndex, resultHash);
|
||||
}
|
||||
|
||||
/// Register a stake for/against a validation pool
|
||||
|
|
|
@ -39,7 +39,7 @@ const fetchPost = async (postIndex) => {
|
|||
const fetchValidationPool = async (poolIndex) => {
|
||||
const {
|
||||
id, postIndex, sender, stakeCount, fee, duration, endTime, resolved, outcome,
|
||||
} = await dao.validationPools(poolIndex);
|
||||
} = await dao.getValidationPool(poolIndex);
|
||||
const pool = {
|
||||
id, postIndex, sender, stakeCount, fee, duration, endTime, resolved, outcome,
|
||||
};
|
||||
|
@ -49,7 +49,7 @@ const fetchValidationPool = async (poolIndex) => {
|
|||
};
|
||||
|
||||
const fetchValidationPools = async () => {
|
||||
const count = await dao.validationPoolCount();
|
||||
const count = await dao.getValidationPoolCount();
|
||||
console.log(`validation pool count: ${count}`);
|
||||
const promises = [];
|
||||
validationPools = [];
|
||||
|
|
|
@ -113,7 +113,7 @@ describe('Forum', () => {
|
|||
await addPost(account1, 'content-id', []);
|
||||
await addPost(account2, 'second-content-id', [{ weightPPM: 500000, targetPostId: 'content-id' }]);
|
||||
await initiateValidationPool({ postId: 'second-content-id' });
|
||||
const pool = await dao.validationPools(0);
|
||||
const pool = await dao.getValidationPool(0);
|
||||
expect(pool.props.postId).to.equal('second-content-id');
|
||||
await dao.evaluateOutcome(0);
|
||||
expect(await dao.balanceOf(account1)).to.equal(50);
|
||||
|
@ -127,7 +127,7 @@ describe('Forum', () => {
|
|||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
await addPost(account2, 'second-content-id', [{ weightPPM: -500000, targetPostId: 'content-id' }]);
|
||||
await initiateValidationPool({ postId: 'second-content-id' });
|
||||
const pool = await dao.validationPools(1);
|
||||
const pool = await dao.getValidationPool(1);
|
||||
expect(pool.props.postId).to.equal('second-content-id');
|
||||
await time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(1);
|
||||
|
@ -147,7 +147,7 @@ describe('Forum', () => {
|
|||
{ weightPPM: 1000000, targetPostId: 'second-content-id' },
|
||||
]);
|
||||
await initiateValidationPool({ postId: 'third-content-id' });
|
||||
const pool = await dao.validationPools(1);
|
||||
const pool = await dao.getValidationPool(1);
|
||||
expect(pool.props.postId).to.equal('third-content-id');
|
||||
await time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(1);
|
||||
|
@ -183,7 +183,7 @@ describe('Forum', () => {
|
|||
{ weightPPM: 100000, targetPostId: 'nonexistent-content-id' },
|
||||
]);
|
||||
await initiateValidationPool({ postId: 'second-content-id' });
|
||||
const pool = await dao.validationPools(0);
|
||||
const pool = await dao.getValidationPool(0);
|
||||
expect(pool.props.postId).to.equal('second-content-id');
|
||||
await dao.evaluateOutcome(0);
|
||||
expect(await dao.balanceOf(account1)).to.equal(10);
|
||||
|
|
|
@ -0,0 +1,318 @@
|
|||
const {
|
||||
time,
|
||||
loadFixture,
|
||||
} = require('@nomicfoundation/hardhat-toolbox/network-helpers');
|
||||
const { expect } = require('chai');
|
||||
const { ethers } = require('hardhat');
|
||||
const deployDAO = require('./util/deploy-dao');
|
||||
|
||||
describe('Lightweight Validation Pools', () => {
|
||||
async function deploy() {
|
||||
const [account1, account2] = await ethers.getSigners();
|
||||
const { dao, forum } = await deployDAO();
|
||||
return {
|
||||
dao, forum, account1, account2,
|
||||
};
|
||||
}
|
||||
let dao;
|
||||
let forum;
|
||||
let account1;
|
||||
let account2;
|
||||
const POOL_DURATION = 3600; // 1 hour
|
||||
const POOL_FEE = 100;
|
||||
const emptyCallbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
|
||||
|
||||
const initiateValidationPool = ({
|
||||
postId, duration,
|
||||
quorum, winRatio, bindingPercent,
|
||||
redistributeLosingStakes, callbackOnValidate,
|
||||
callbackData, fee,
|
||||
} = {}) => dao.initiateLWValidationPool(
|
||||
postId ?? 'content-id',
|
||||
duration ?? POOL_DURATION,
|
||||
quorum ?? [1, 3],
|
||||
winRatio ?? [1, 2],
|
||||
bindingPercent ?? 100,
|
||||
redistributeLosingStakes ?? true,
|
||||
callbackOnValidate ?? false,
|
||||
callbackData ?? emptyCallbackData,
|
||||
{ value: fee ?? POOL_FEE },
|
||||
);
|
||||
|
||||
beforeEach(async () => {
|
||||
({
|
||||
dao, forum, account1, account2,
|
||||
} = await loadFixture(deploy));
|
||||
await forum.addPost([{ weightPPM: 1000000, authorAddress: account1 }], 'content-id', []);
|
||||
const init = () => initiateValidationPool({ fee: POOL_FEE });
|
||||
await expect(init()).to.emit(dao, 'LWValidationPoolInitiated').withArgs(0);
|
||||
expect(await dao.getLWValidationPoolCount()).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 be able to initiate a validation pool without a fee', async () => {
|
||||
const init = () => initiateValidationPool({ fee: 0 });
|
||||
await expect(init()).to.emit(dao, 'LWValidationPoolInitiated');
|
||||
});
|
||||
|
||||
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, 'LWValidationPoolInitiated').withArgs(1);
|
||||
expect(await dao.getLWValidationPoolCount()).to.equal(2);
|
||||
});
|
||||
|
||||
it('Should be able to fetch pool instance', async () => {
|
||||
const pool = await dao.getLWValidationPool(0);
|
||||
expect(pool).to.exist;
|
||||
expect(pool.params.duration).to.equal(POOL_DURATION);
|
||||
expect(pool.props.postId).to.equal('content-id');
|
||||
expect(pool.props.resolved).to.be.false;
|
||||
expect(pool.sender).to.equal(account1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Propose Result', () => {
|
||||
it('should not be able to propose an empty result', async () => {
|
||||
await expect(dao.proposeLWResult(0, 'some-hash', [])).to.be.revertedWith('The proposed result contains no transfers');
|
||||
});
|
||||
|
||||
it('should be able to propose a result', async () => {
|
||||
await expect(dao.proposeLWResult(0, 'some-hash', [{ from: account1, to: account2, amount: 0 }])).to.emit(dao, 'LWResultProposed').withArgs(0, 0, 'some-hash');
|
||||
await expect(dao.proposeLWResult(0, 'some-other-hash', [{ from: account1, to: account2, amount: 0 }])).to.emit(dao, 'LWResultProposed').withArgs(0, 1, 'some-other-hash');
|
||||
});
|
||||
|
||||
it('should not be able to propose the same result twice', async () => {
|
||||
await expect(dao.proposeLWResult(0, 'some-hash', [{ from: account1, to: account2, amount: 0 }])).to.emit(dao, 'LWResultProposed').withArgs(0, 0, 'some-hash');
|
||||
await expect(dao.proposeLWResult(0, 'some-hash', [{ from: account1, to: account2, amount: 0 }])).to.be.revertedWith('This result hash has already been proposed');
|
||||
});
|
||||
});
|
||||
|
||||
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.stakeOnLWValidationPool(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.getValidationPool(1);
|
||||
expect(pool.props.outcome).to.be.false;
|
||||
});
|
||||
|
||||
it('should not be able to stake more REP than the sender owns', async () => {
|
||||
await expect(dao.stakeOnValidationPool(1, 200, true)).to.be.revertedWith('Insufficient REP balance to cover stake');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Delegated stake', () => {
|
||||
it('should stake the lesser of the allowed amount or the owner\'s remaining balance', async () => {
|
||||
// TODO: owner delegates stake and then loses rep
|
||||
});
|
||||
});
|
||||
|
||||
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.getValidationPool(0);
|
||||
expect(pool.props.resolved).to.be.true;
|
||||
expect(pool.props.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 = () => initiateValidationPool();
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
|
||||
expect(await dao.getValidationPoolCount()).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.getValidationPoolCount()).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 forum.addPost([{ weightPPM: 1000000, authorAddress: account2 }], 'content-id-2', []);
|
||||
const init = () => initiateValidationPool({ postId: 'content-id-2' });
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -76,7 +76,7 @@ describe('Onboarding', () => {
|
|||
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.validationPools(1);
|
||||
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);
|
||||
|
@ -127,7 +127,7 @@ describe('Onboarding', () => {
|
|||
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.validationPools(2);
|
||||
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);
|
||||
|
|
|
@ -224,7 +224,7 @@ describe('Proposal', () => {
|
|||
});
|
||||
|
||||
afterEach(async () => {
|
||||
const pool = await dao.validationPools(3);
|
||||
const pool = await dao.getValidationPool(3);
|
||||
expect(pool.props.resolved).to.be.true;
|
||||
});
|
||||
|
||||
|
@ -310,7 +310,7 @@ describe('Proposal', () => {
|
|||
});
|
||||
|
||||
afterEach(async () => {
|
||||
const pool = await dao.validationPools(4);
|
||||
const pool = await dao.getValidationPool(4);
|
||||
expect(pool.props.resolved).to.be.true;
|
||||
});
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ describe('Validation Pools', () => {
|
|||
await forum.addPost([{ weightPPM: 1000000, authorAddress: 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.getValidationPoolCount()).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);
|
||||
|
@ -86,11 +86,11 @@ describe('Validation Pools', () => {
|
|||
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);
|
||||
expect(await dao.getValidationPoolCount()).to.equal(2);
|
||||
});
|
||||
|
||||
it('Should be able to fetch pool instance', async () => {
|
||||
const pool = await dao.validationPools(0);
|
||||
const pool = await dao.getValidationPool(0);
|
||||
expect(pool).to.exist;
|
||||
expect(pool.params.duration).to.equal(POOL_DURATION);
|
||||
expect(pool.props.postId).to.equal('content-id');
|
||||
|
@ -133,7 +133,7 @@ describe('Validation Pools', () => {
|
|||
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);
|
||||
const pool = await dao.getValidationPool(1);
|
||||
expect(pool.props.outcome).to.be.false;
|
||||
});
|
||||
|
||||
|
@ -170,7 +170,7 @@ describe('Validation Pools', () => {
|
|||
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);
|
||||
const pool = await dao.getValidationPool(0);
|
||||
expect(pool.props.resolved).to.be.true;
|
||||
expect(pool.props.outcome).to.be.true;
|
||||
});
|
||||
|
@ -184,7 +184,7 @@ describe('Validation Pools', () => {
|
|||
it('should be able to evaluate outcome of second validation pool', async () => {
|
||||
const init = () => initiateValidationPool();
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
|
||||
expect(await dao.validationPoolCount()).to.equal(2);
|
||||
expect(await dao.getValidationPoolCount()).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);
|
||||
|
@ -198,7 +198,7 @@ describe('Validation Pools', () => {
|
|||
|
||||
const init = () => initiateValidationPool({ quorum: [1, 1] });
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
|
||||
expect(await dao.validationPoolCount()).to.equal(2);
|
||||
expect(await dao.getValidationPoolCount()).to.equal(2);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, false, false);
|
||||
});
|
||||
|
|
|
@ -229,7 +229,7 @@ describe('Work1', () => {
|
|||
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.validationPools(1);
|
||||
const pool = await dao.getValidationPool(1);
|
||||
expect(pool.props.fee).to.equal(WORK1_PRICE);
|
||||
expect(pool.sender).to.equal(work1.target);
|
||||
expect(pool.props.postId).to.equal('evidence-content-id');
|
||||
|
|
|
@ -3,14 +3,17 @@ const { ethers } = require('hardhat');
|
|||
const deployDAO = async () => {
|
||||
const Reputation = await ethers.getContractFactory('Reputation');
|
||||
const Bench = await ethers.getContractFactory('Bench');
|
||||
const LightweightBench = await ethers.getContractFactory('LightweightBench');
|
||||
const DAO = await ethers.getContractFactory('DAO');
|
||||
const GlobalForum = await ethers.getContractFactory('GlobalForum');
|
||||
const forum = await GlobalForum.deploy();
|
||||
const reputation = await Reputation.deploy();
|
||||
const bench = await Bench.deploy();
|
||||
const lightweightBench = await LightweightBench.deploy();
|
||||
const dao = await DAO.deploy(
|
||||
reputation.target,
|
||||
bench.target,
|
||||
lightweightBench.target,
|
||||
forum.target,
|
||||
);
|
||||
return {
|
||||
|
|
Loading…
Reference in New Issue