add binding percent and redistribute losing stakes parameters to validation pool
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 34s Details

This commit is contained in:
Ladd Hoffman 2024-03-26 18:28:38 -05:00
parent 5455fb4c15
commit d93b0cb9c7
13 changed files with 442 additions and 114 deletions

View File

@ -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

View File

@ -1,8 +1,8 @@
{
"localhost": {
"DAO": "0x9151C9bA7b159B13C9ae51042eac73115D898657",
"Work1": "0xef57508e07F8b6460f7a3aD14ceE0Be23Bb93055",
"Onboarding": "0x5865349932DDf62D7593061bB5463E9D72Fd6b7d"
"DAO": "0xadB35d0E9d7B33441C7ED26add5D42F873430790",
"Work1": "0xb3FaD3422e1Edf2f7035952aa0f8760df466213a",
"Onboarding": "0xd7a4eDcC2Fbc139DfD88E25a996a8a9Dd385B1f0"
},
"sepolia": {
"DAO": "0x8Cb4ab513A863ac29e855c85064ea53dec7dA24C",

View File

@ -1,8 +1,8 @@
{
"localhost": {
"DAO": "0x9151C9bA7b159B13C9ae51042eac73115D898657",
"Work1": "0xef57508e07F8b6460f7a3aD14ceE0Be23Bb93055",
"Onboarding": "0x5865349932DDf62D7593061bB5463E9D72Fd6b7d"
"DAO": "0xadB35d0E9d7B33441C7ED26add5D42F873430790",
"Work1": "0xb3FaD3422e1Edf2f7035952aa0f8760df466213a",
"Onboarding": "0xd7a4eDcC2Fbc139DfD88E25a996a8a9Dd385B1f0"
},
"sepolia": {
"DAO": "0x8Cb4ab513A863ac29e855c85064ea53dec7dA24C",

View File

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

View File

@ -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,
""
);

View File

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

View File

@ -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,
""
);

View File

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

View File

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

View File

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