add binding percent and redistribute losing stakes parameters to validation pool
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 34s
Details
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 34s
Details
This commit is contained in:
parent
5455fb4c15
commit
d93b0cb9c7
|
@ -220,6 +220,8 @@ function App() {
|
|||
poolDuration ?? 3600,
|
||||
1,
|
||||
3,
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
web3.eth.abi.encodeParameter('bytes', '0x00'),
|
||||
).send({
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"localhost": {
|
||||
"DAO": "0x9151C9bA7b159B13C9ae51042eac73115D898657",
|
||||
"Work1": "0xef57508e07F8b6460f7a3aD14ceE0Be23Bb93055",
|
||||
"Onboarding": "0x5865349932DDf62D7593061bB5463E9D72Fd6b7d"
|
||||
"DAO": "0xadB35d0E9d7B33441C7ED26add5D42F873430790",
|
||||
"Work1": "0xb3FaD3422e1Edf2f7035952aa0f8760df466213a",
|
||||
"Onboarding": "0xd7a4eDcC2Fbc139DfD88E25a996a8a9Dd385B1f0"
|
||||
},
|
||||
"sepolia": {
|
||||
"DAO": "0x8Cb4ab513A863ac29e855c85064ea53dec7dA24C",
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"localhost": {
|
||||
"DAO": "0x9151C9bA7b159B13C9ae51042eac73115D898657",
|
||||
"Work1": "0xef57508e07F8b6460f7a3aD14ceE0Be23Bb93055",
|
||||
"Onboarding": "0x5865349932DDf62D7593061bB5463E9D72Fd6b7d"
|
||||
"DAO": "0xadB35d0E9d7B33441C7ED26add5D42F873430790",
|
||||
"Work1": "0xb3FaD3422e1Edf2f7035952aa0f8760df466213a",
|
||||
"Onboarding": "0xd7a4eDcC2Fbc139DfD88E25a996a8a9Dd385B1f0"
|
||||
},
|
||||
"sepolia": {
|
||||
"DAO": "0x8Cb4ab513A863ac29e855c85064ea53dec7dA24C",
|
||||
|
|
|
@ -19,6 +19,13 @@ struct Stake {
|
|||
bool inFavor;
|
||||
uint256 amount;
|
||||
address sender;
|
||||
bool fromMint;
|
||||
}
|
||||
|
||||
struct ValidationPoolParams {
|
||||
uint quorumPPB;
|
||||
uint bindingPercent;
|
||||
bool redistributeLosingStakes;
|
||||
}
|
||||
|
||||
struct ValidationPool {
|
||||
|
@ -27,8 +34,8 @@ struct ValidationPool {
|
|||
address sender;
|
||||
mapping(uint => Stake) stakes;
|
||||
uint stakeCount;
|
||||
ValidationPoolParams params;
|
||||
uint256 fee;
|
||||
uint quorumPPB;
|
||||
uint duration;
|
||||
uint endTime;
|
||||
bool resolved;
|
||||
|
@ -49,16 +56,13 @@ contract DAO is ERC20("Reputation", "REP") {
|
|||
mapping(uint => Post) public posts;
|
||||
uint public postCount;
|
||||
|
||||
// ufixed8x1 constant mintingRatio = 1;
|
||||
// ufixed8x1 constant quorum = 0;
|
||||
// ufixed8x1 constant stakeForAuthor = 0.5;
|
||||
// ufixed8x1 constant winningRatio = 0.5;
|
||||
// TODO: Make parameters adjustable
|
||||
// TODO: possible parameter for minting ratio
|
||||
// TODO: possible parameter for stakeForAuthor
|
||||
// TODO: possible parameter for winningRatio
|
||||
// TODO: Add forum parameters
|
||||
|
||||
uint public constant minDuration = 1; // 1 second
|
||||
uint public constant maxDuration = 365_000_000 days; // 1 million years
|
||||
|
||||
uint public constant minQuorumPPB = 333_333_333; // Parts per billion
|
||||
|
||||
event PostAdded(uint postIndex);
|
||||
|
@ -80,12 +84,13 @@ contract DAO is ERC20("Reputation", "REP") {
|
|||
|
||||
/// Accept fee to initiate a validation pool
|
||||
/// TODO: Handle multiple authors
|
||||
/// TODO: Constrain duration to allowable range
|
||||
function initiateValidationPool(
|
||||
uint postIndex,
|
||||
uint duration,
|
||||
uint quorumNumerator,
|
||||
uint quorumDenominator,
|
||||
uint bindingPercent,
|
||||
bool redistributeLosingStakes,
|
||||
bool callbackOnValidate,
|
||||
bytes calldata callbackData
|
||||
) external payable returns (uint poolIndex) {
|
||||
|
@ -101,6 +106,7 @@ contract DAO is ERC20("Reputation", "REP") {
|
|||
quorumNumerator <= quorumDenominator,
|
||||
"Quorum is greater than one"
|
||||
);
|
||||
require(bindingPercent <= 100, "Binding percent must be <= 100");
|
||||
Post storage post = posts[postIndex];
|
||||
require(post.author != address(0), "Target post not found");
|
||||
poolIndex = validationPoolCount++;
|
||||
|
@ -108,7 +114,11 @@ contract DAO is ERC20("Reputation", "REP") {
|
|||
pool.sender = msg.sender;
|
||||
pool.postIndex = postIndex;
|
||||
pool.fee = msg.value;
|
||||
pool.quorumPPB = (1_000_000_000 * quorumNumerator) / quorumDenominator;
|
||||
pool.params.quorumPPB =
|
||||
(1_000_000_000 * quorumNumerator) /
|
||||
quorumDenominator;
|
||||
pool.params.bindingPercent = bindingPercent;
|
||||
pool.params.redistributeLosingStakes = redistributeLosingStakes;
|
||||
pool.duration = duration;
|
||||
pool.endTime = block.timestamp + duration;
|
||||
pool.id = poolIndex;
|
||||
|
@ -123,8 +133,8 @@ contract DAO is ERC20("Reputation", "REP") {
|
|||
// TODO: Make stakeForAuthor an adjustable parameter
|
||||
_mint(post.author, msg.value);
|
||||
// TODO: We need a way to exclude this pending reputation from the total supply when computing fee distribution
|
||||
_stake(pool, post.author, msg.value / 2, true);
|
||||
_stake(pool, post.author, msg.value / 2, false);
|
||||
_stake(pool, post.author, msg.value / 2, true, true);
|
||||
_stake(pool, post.author, msg.value / 2, false, true);
|
||||
emit ValidationPoolInitiated(poolIndex);
|
||||
}
|
||||
|
||||
|
@ -133,22 +143,24 @@ contract DAO is ERC20("Reputation", "REP") {
|
|||
ValidationPool storage pool,
|
||||
address sender,
|
||||
uint256 amount,
|
||||
bool inFavor
|
||||
bool inFavor,
|
||||
bool fromMint
|
||||
) internal {
|
||||
require(block.timestamp <= pool.endTime, "Pool end time has passed");
|
||||
_transfer(sender, address(this), amount);
|
||||
_update(sender, address(this), amount);
|
||||
uint stakeIndex = pool.stakeCount++;
|
||||
Stake storage s = pool.stakes[stakeIndex];
|
||||
s.sender = sender;
|
||||
s.inFavor = inFavor;
|
||||
s.amount = amount;
|
||||
s.id = stakeIndex;
|
||||
s.fromMint = fromMint;
|
||||
}
|
||||
|
||||
/// Accept reputation stakes toward a validation pool
|
||||
function stake(uint poolIndex, uint256 amount, bool inFavor) public {
|
||||
ValidationPool storage pool = validationPools[poolIndex];
|
||||
_stake(pool, msg.sender, amount, inFavor);
|
||||
_stake(pool, msg.sender, amount, inFavor, false);
|
||||
}
|
||||
|
||||
/// Evaluate outcome of a validation pool
|
||||
|
@ -174,7 +186,7 @@ contract DAO is ERC20("Reputation", "REP") {
|
|||
// Check that quorum is met
|
||||
require(
|
||||
1_000_000_000 * (stakedFor + stakedAgainst) >=
|
||||
totalSupply() * pool.quorumPPB,
|
||||
totalSupply() * pool.params.quorumPPB,
|
||||
"Quorum for this pool was not met"
|
||||
);
|
||||
// A tie is resolved in favor of the validation pool.
|
||||
|
@ -185,32 +197,51 @@ contract DAO is ERC20("Reputation", "REP") {
|
|||
members[memberCount++] = post.author;
|
||||
isMember[post.author] = true;
|
||||
}
|
||||
console.log(
|
||||
"stakedFor: %d, stakedAgainst: %d, stakeCount: %d",
|
||||
stakedFor,
|
||||
stakedAgainst,
|
||||
pool.stakeCount
|
||||
);
|
||||
pool.resolved = true;
|
||||
pool.outcome = votePasses;
|
||||
emit ValidationPoolResolved(poolIndex, votePasses);
|
||||
// Value of losing stakes should be di stributed among winners, in proportion to their stakes
|
||||
// Value of losing stakes should be distributed among winners, in proportion to their stakes
|
||||
uint256 amountFromWinners = votePasses ? stakedFor : stakedAgainst;
|
||||
uint256 amountFromLosers = votePasses ? stakedAgainst : stakedFor;
|
||||
uint256 totalRewards;
|
||||
// Only bindingPercent % should be redistributed
|
||||
// Stake senders should get (100-bindingPercent) % back
|
||||
uint256 totalAllocated;
|
||||
for (uint i = 0; i < pool.stakeCount; i++) {
|
||||
s = pool.stakes[i];
|
||||
bool redistributeLosingStakes = s.fromMint ||
|
||||
pool.params.redistributeLosingStakes;
|
||||
uint bindingPercent = s.fromMint ? 100 : pool.params.bindingPercent;
|
||||
if (votePasses == s.inFavor) {
|
||||
uint256 reward = (amountFromLosers * s.amount) /
|
||||
amountFromWinners;
|
||||
_transfer(address(this), s.sender, s.amount + reward);
|
||||
totalRewards += reward;
|
||||
// Winning stake
|
||||
// If this stake is from the minted fee, always redistribute it to the winners
|
||||
uint reward = redistributeLosingStakes
|
||||
? ((s.amount * amountFromLosers) / amountFromWinners) *
|
||||
(bindingPercent / 100)
|
||||
: 0;
|
||||
uint balance = balanceOf(address(this));
|
||||
_update(address(this), s.sender, s.amount + reward);
|
||||
totalAllocated += reward;
|
||||
} else {
|
||||
// Losing stake
|
||||
uint refund = (s.amount * (100 - bindingPercent)) / 100;
|
||||
uint balance = balanceOf(address(this));
|
||||
if (refund > 0) {
|
||||
_update(address(this), s.sender, refund);
|
||||
}
|
||||
// If this stake is from the minted fee, don't burn it
|
||||
if (!redistributeLosingStakes) {
|
||||
uint amountToBurn = (s.amount *
|
||||
pool.params.bindingPercent) / 100;
|
||||
_burn(address(this), amountToBurn);
|
||||
totalAllocated += amountToBurn;
|
||||
}
|
||||
totalAllocated += refund;
|
||||
}
|
||||
}
|
||||
// Due to rounding, there may be some reward left over. Include this as a reward to the author.
|
||||
uint256 remainder = amountFromLosers - totalRewards;
|
||||
// Due to rounding, there may be some REP left over. Include this as a reward to the author.
|
||||
uint256 remainder = amountFromLosers - totalAllocated;
|
||||
if (remainder > 0) {
|
||||
_transfer(address(this), post.author, remainder);
|
||||
_update(address(this), post.author, remainder);
|
||||
}
|
||||
// Distribute fee proportionatly among all reputation holders
|
||||
for (uint i = 0; i < memberCount; i++) {
|
||||
|
@ -240,3 +271,13 @@ contract DAO is ERC20("Reputation", "REP") {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience contract to extend for other contracts that will be initialized to
|
||||
/// interact with a DAO contract.
|
||||
contract DAOContract {
|
||||
DAO immutable dao;
|
||||
|
||||
constructor(DAO dao_) {
|
||||
dao = dao_;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,16 @@ contract Onboarding is WorkContract, IOnValidate {
|
|||
// Initiate validation pool
|
||||
uint poolIndex = dao.initiateValidationPool{
|
||||
value: request.fee - request.fee / 10
|
||||
}(postIndex, POOL_DURATION, 1, 3, true, abi.encode(requestIndex));
|
||||
}(
|
||||
postIndex,
|
||||
POOL_DURATION,
|
||||
1,
|
||||
3,
|
||||
100,
|
||||
true,
|
||||
true,
|
||||
abi.encode(requestIndex)
|
||||
);
|
||||
dao.stake(poolIndex, stake.amount, true);
|
||||
}
|
||||
|
||||
|
@ -53,6 +62,8 @@ contract Onboarding is WorkContract, IOnValidate {
|
|||
POOL_DURATION,
|
||||
1,
|
||||
3,
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
""
|
||||
);
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
// SPDX-License-Identifier: Unlicense
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
import "./DAO.sol";
|
||||
|
||||
contract Proposals is DAOContract {
|
||||
struct Attestation {
|
||||
address sender;
|
||||
uint amount;
|
||||
}
|
||||
|
||||
enum Stage {
|
||||
Proposal,
|
||||
Referendum0,
|
||||
Referendum1,
|
||||
Referendum100,
|
||||
Closed
|
||||
}
|
||||
struct Proposal {
|
||||
address sender;
|
||||
string contentId;
|
||||
uint startTime;
|
||||
Stage stage;
|
||||
mapping(uint => Attestation) attestations;
|
||||
uint attestationCount;
|
||||
}
|
||||
|
||||
mapping(uint => Proposal) proposals;
|
||||
uint proposalCount;
|
||||
|
||||
constructor(DAO dao) DAOContract(dao) {}
|
||||
|
||||
function propose(
|
||||
string calldata contentId
|
||||
) external returns (uint proposalIndex) {
|
||||
proposalIndex = proposalCount++;
|
||||
Proposal storage proposal = proposals[proposalIndex];
|
||||
proposal.startTime = block.timestamp;
|
||||
proposal.contentId = contentId;
|
||||
}
|
||||
|
||||
function attest(uint proposalIndex, uint amount) external {
|
||||
// Since this is non-binding, non-encumbering, we only need to verify that
|
||||
// the sender actually has the rep they claim to stake.
|
||||
require(
|
||||
dao.balanceOf(msg.sender) >= amount,
|
||||
"Sender has insufficient REP balance"
|
||||
);
|
||||
Proposal storage proposal = proposals[proposalIndex];
|
||||
uint attestationIndex = proposal.attestationCount++;
|
||||
Attestation storage attestation = proposal.attestations[
|
||||
attestationIndex
|
||||
];
|
||||
attestation.sender = msg.sender;
|
||||
attestation.amount = amount;
|
||||
}
|
||||
|
||||
function evaluateAttestation(uint proposalIndex) external returns (bool) {
|
||||
Proposal storage proposal = proposals[proposalIndex];
|
||||
require(
|
||||
proposal.stage == Stage.Proposal,
|
||||
"Attestation only pertains to Proposal stage"
|
||||
);
|
||||
uint totalAttestation;
|
||||
for (uint i = 0; i < proposal.attestationCount; i++) {
|
||||
totalAttestation += proposal.attestations[i].amount;
|
||||
}
|
||||
bool meetsAttestation = 10 * totalAttestation >= dao.totalSupply();
|
||||
if (!meetsAttestation) {
|
||||
if (block.timestamp > proposal.startTime + 365 days) {
|
||||
proposal.stage = Stage.Closed;
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initiate validation pool
|
||||
proposal.stage = Stage.Referendum0;
|
||||
// TODO: make referendum0 duration a parameter
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ pragma solidity ^0.8.24;
|
|||
import "./DAO.sol";
|
||||
import "./IAcceptAvailability.sol";
|
||||
|
||||
abstract contract WorkContract is IAcceptAvailability {
|
||||
abstract contract WorkContract is DAOContract, IAcceptAvailability {
|
||||
struct AvailabilityStake {
|
||||
address worker;
|
||||
uint256 amount;
|
||||
|
@ -30,7 +30,6 @@ abstract contract WorkContract is IAcceptAvailability {
|
|||
bool approval;
|
||||
}
|
||||
|
||||
DAO immutable dao;
|
||||
uint public immutable price;
|
||||
mapping(uint => AvailabilityStake) public stakes;
|
||||
uint public stakeCount;
|
||||
|
@ -44,8 +43,7 @@ abstract contract WorkContract is IAcceptAvailability {
|
|||
event WorkEvidenceSubmitted(uint requestIndex);
|
||||
event WorkApprovalSubmitted(uint requestIndex, bool approval);
|
||||
|
||||
constructor(DAO dao_, uint price_) {
|
||||
dao = dao_;
|
||||
constructor(DAO dao, uint price_) DAOContract(dao) {
|
||||
price = price_;
|
||||
}
|
||||
|
||||
|
@ -180,6 +178,8 @@ abstract contract WorkContract is IAcceptAvailability {
|
|||
POOL_DURATION,
|
||||
1,
|
||||
3,
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
""
|
||||
);
|
||||
|
|
|
@ -44,18 +44,21 @@ describe('DAO', () => {
|
|||
describe('Validation Pool', () => {
|
||||
let dao;
|
||||
let account1;
|
||||
let account2;
|
||||
const POOL_DURATION = 3600; // 1 hour
|
||||
const POOL_FEE = 100;
|
||||
const callbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
|
||||
|
||||
beforeEach(async () => {
|
||||
({ dao, account1 } = await loadFixture(deploy));
|
||||
({ dao, account1, account2 } = await loadFixture(deploy));
|
||||
await dao.addPost(account1, 'content-id');
|
||||
const init = () => dao.initiateValidationPool(
|
||||
0,
|
||||
POOL_DURATION,
|
||||
1,
|
||||
3,
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
callbackData,
|
||||
{ value: POOL_FEE },
|
||||
|
@ -75,6 +78,8 @@ describe('DAO', () => {
|
|||
POOL_DURATION,
|
||||
1,
|
||||
3,
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
callbackData,
|
||||
);
|
||||
|
@ -88,6 +93,8 @@ describe('DAO', () => {
|
|||
POOL_DURATION,
|
||||
1,
|
||||
4,
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
callbackData,
|
||||
{ value: POOL_FEE },
|
||||
|
@ -102,6 +109,8 @@ describe('DAO', () => {
|
|||
POOL_DURATION,
|
||||
11,
|
||||
10,
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
callbackData,
|
||||
{ value: POOL_FEE },
|
||||
|
@ -116,6 +125,8 @@ describe('DAO', () => {
|
|||
0,
|
||||
1,
|
||||
3,
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
callbackData,
|
||||
{ value: POOL_FEE },
|
||||
|
@ -130,6 +141,8 @@ describe('DAO', () => {
|
|||
40000000000000,
|
||||
1,
|
||||
3,
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
callbackData,
|
||||
{ value: POOL_FEE },
|
||||
|
@ -137,12 +150,30 @@ describe('DAO', () => {
|
|||
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 setup = await loadFixture(deploy);
|
||||
const init = () => setup.dao.initiateValidationPool(
|
||||
0,
|
||||
POOL_DURATION,
|
||||
1,
|
||||
3,
|
||||
101,
|
||||
true,
|
||||
false,
|
||||
callbackData,
|
||||
{ value: POOL_FEE },
|
||||
);
|
||||
await expect(init()).to.be.revertedWith('Binding percent must be <= 100');
|
||||
});
|
||||
|
||||
it('should be able to initiate a second validation pool', async () => {
|
||||
const init = () => dao.initiateValidationPool(
|
||||
0,
|
||||
POOL_DURATION,
|
||||
1,
|
||||
3,
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
callbackData,
|
||||
{ value: POOL_FEE },
|
||||
|
@ -161,77 +192,21 @@ describe('DAO', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Evaluate outcome', () => {
|
||||
it('should not be able to evaluate outcome before duration has elapsed', async () => {
|
||||
await expect(dao.evaluateOutcome(0)).to.be.revertedWith('Pool end time has not yet arrived');
|
||||
});
|
||||
|
||||
it('should be able to evaluate outcome after duration has elapsed', async () => {
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(0)).to.emit(dao, 'ValidationPoolResolved').withArgs(0, true);
|
||||
expect(await dao.memberCount()).to.equal(1);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
const pool = await dao.validationPools(0);
|
||||
expect(pool.resolved).to.be.true;
|
||||
expect(pool.outcome).to.be.true;
|
||||
});
|
||||
|
||||
it('should not be able to evaluate outcome more than once', async () => {
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(0)).to.emit(dao, 'ValidationPoolResolved').withArgs(0, true);
|
||||
await expect(dao.evaluateOutcome(0)).to.be.revertedWith('Pool is already resolved');
|
||||
});
|
||||
|
||||
it('should be able to evaluate outcome of second validation pool', async () => {
|
||||
const init = () => dao.initiateValidationPool(
|
||||
0,
|
||||
POOL_DURATION,
|
||||
1,
|
||||
3,
|
||||
false,
|
||||
callbackData,
|
||||
{ value: POOL_FEE },
|
||||
);
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
|
||||
expect(await dao.validationPoolCount()).to.equal(2);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(0)).to.emit(dao, 'ValidationPoolResolved').withArgs(0, true);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, true);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
const init = () => dao.initiateValidationPool(
|
||||
0,
|
||||
POOL_DURATION,
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
callbackData,
|
||||
{ value: POOL_FEE },
|
||||
);
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
|
||||
expect(await dao.validationPoolCount()).to.equal(2);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(1)).to.be.revertedWith('Quorum for this pool was not met');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Stake', async () => {
|
||||
beforeEach(async () => {
|
||||
time.increase(POOL_DURATION + 1);
|
||||
console.log('evaluating first pool');
|
||||
await dao.evaluateOutcome(0);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
console.log('initiating second pool');
|
||||
await dao.initiateValidationPool(
|
||||
0,
|
||||
POOL_DURATION,
|
||||
1,
|
||||
3,
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
callbackData,
|
||||
{ value: POOL_FEE },
|
||||
|
@ -240,10 +215,12 @@ describe('DAO', () => {
|
|||
});
|
||||
|
||||
it('should be able to stake before validation pool has elapsed', async () => {
|
||||
console.log('staking on second pool');
|
||||
await dao.stake(1, 10, true);
|
||||
expect(await dao.balanceOf(account1)).to.equal(90);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(110);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
console.log('evaluating second pool');
|
||||
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, true);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
|
@ -266,5 +243,194 @@ describe('DAO', () => {
|
|||
expect(pool.outcome).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('Evaluate outcome', () => {
|
||||
it('should not be able to evaluate outcome before duration has elapsed', async () => {
|
||||
await expect(dao.evaluateOutcome(0)).to.be.revertedWith('Pool end time has not yet arrived');
|
||||
});
|
||||
|
||||
it('should be able to evaluate outcome after duration has elapsed', async () => {
|
||||
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);
|
||||
expect(await dao.memberCount()).to.equal(1);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
const pool = await dao.validationPools(0);
|
||||
expect(pool.resolved).to.be.true;
|
||||
expect(pool.outcome).to.be.true;
|
||||
});
|
||||
|
||||
it('should not be able to evaluate outcome more than once', async () => {
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(0)).to.emit(dao, 'ValidationPoolResolved').withArgs(0, true);
|
||||
await expect(dao.evaluateOutcome(0)).to.be.revertedWith('Pool is already resolved');
|
||||
});
|
||||
|
||||
it('should be able to evaluate outcome of second validation pool', async () => {
|
||||
const init = () => dao.initiateValidationPool(
|
||||
0,
|
||||
POOL_DURATION,
|
||||
1,
|
||||
3,
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
callbackData,
|
||||
{ value: POOL_FEE },
|
||||
);
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
|
||||
expect(await dao.validationPoolCount()).to.equal(2);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(0)).to.emit(dao, 'ValidationPoolResolved').withArgs(0, true);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, true);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
const init = () => dao.initiateValidationPool(
|
||||
0,
|
||||
POOL_DURATION,
|
||||
1,
|
||||
1,
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
callbackData,
|
||||
{ value: POOL_FEE },
|
||||
);
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
|
||||
expect(await dao.validationPoolCount()).to.equal(2);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(1)).to.be.revertedWith('Quorum for this pool was not met');
|
||||
});
|
||||
|
||||
describe('Validation pool options', () => {
|
||||
beforeEach(async () => {
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(0);
|
||||
await dao.addPost(account2, 'content-id');
|
||||
const init = () => dao.initiateValidationPool(
|
||||
1,
|
||||
POOL_DURATION,
|
||||
1,
|
||||
3,
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
callbackData,
|
||||
{ value: POOL_FEE },
|
||||
);
|
||||
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 = () => dao.initiateValidationPool(
|
||||
0,
|
||||
POOL_DURATION,
|
||||
1,
|
||||
3,
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
callbackData,
|
||||
{ value: POOL_FEE },
|
||||
);
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
await dao.connect(account1).stake(2, 10, true);
|
||||
await dao.connect(account2).stake(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(90);
|
||||
expect(await dao.balanceOf(account2)).to.equal(90);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(120);
|
||||
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 = () => dao.initiateValidationPool(
|
||||
0,
|
||||
POOL_DURATION,
|
||||
1,
|
||||
3,
|
||||
0,
|
||||
true,
|
||||
false,
|
||||
callbackData,
|
||||
{ value: POOL_FEE },
|
||||
);
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
await dao.connect(account1).stake(2, 10, true);
|
||||
await dao.connect(account2).stake(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(90);
|
||||
expect(await dao.balanceOf(account2)).to.equal(90);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(120);
|
||||
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 = () => dao.initiateValidationPool(
|
||||
0,
|
||||
POOL_DURATION,
|
||||
1,
|
||||
3,
|
||||
50,
|
||||
true,
|
||||
false,
|
||||
callbackData,
|
||||
{ value: POOL_FEE },
|
||||
);
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
await dao.connect(account1).stake(2, 10, true);
|
||||
await dao.connect(account2).stake(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(90);
|
||||
expect(await dao.balanceOf(account2)).to.equal(90);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(120);
|
||||
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 = () => dao.initiateValidationPool(
|
||||
0,
|
||||
POOL_DURATION,
|
||||
1,
|
||||
3,
|
||||
50,
|
||||
false,
|
||||
false,
|
||||
callbackData,
|
||||
{ value: POOL_FEE },
|
||||
);
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
await dao.connect(account1).stake(2, 10, true);
|
||||
await dao.connect(account2).stake(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(90);
|
||||
expect(await dao.balanceOf(account2)).to.equal(90);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(120);
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,7 +19,7 @@ describe('Onboarding', () => {
|
|||
|
||||
await dao.addPost(account1, 'content-id');
|
||||
const callbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
|
||||
await dao.initiateValidationPool(0, 60, 1, 3, false, callbackData, { value: 100 });
|
||||
await dao.initiateValidationPool(0, 60, 1, 3, 100, true, false, callbackData, { value: 100 });
|
||||
await time.increase(61);
|
||||
await dao.evaluateOutcome(0);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
|
|
|
@ -19,7 +19,7 @@ describe('Work1', () => {
|
|||
|
||||
await dao.addPost(account1, 'some-content-id');
|
||||
const callbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
|
||||
await dao.initiateValidationPool(0, 60, 1, 3, false, callbackData, { value: 100 });
|
||||
await dao.initiateValidationPool(0, 60, 1, 3, 100, true, false, callbackData, { value: 100 });
|
||||
await time.increase(61);
|
||||
await dao.evaluateOutcome(0);
|
||||
|
||||
|
|
Loading…
Reference in New Issue