clarify logic in evaluateOutcome

This commit is contained in:
Ladd Hoffman 2024-04-10 14:55:02 -05:00
parent 77cd551602
commit 989bc9846e
2 changed files with 38 additions and 36 deletions

View File

@ -9,9 +9,8 @@ import "hardhat/console.sol";
struct ValidationPoolStake {
uint id;
bool inFavor;
uint256 amount;
uint amount;
address sender;
bool fromMint;
}
struct ValidationPoolParams {
@ -30,7 +29,7 @@ struct ValidationPool {
mapping(uint => ValidationPoolStake) stakes;
uint stakeCount;
ValidationPoolParams params;
uint256 fee;
uint fee;
uint endTime;
bool resolved;
bool outcome;
@ -64,18 +63,16 @@ contract ValidationPools is Reputation, Forum {
ValidationPool storage pool,
address sender,
uint256 amount,
bool inFavor,
bool fromMint
bool inFavor
) internal {
require(block.timestamp <= pool.endTime, "Pool end time has passed");
//_update(sender, address(this), amount);
// We don't call _update here; We defer that until evaluateOutcome.
uint stakeIndex = pool.stakeCount++;
ValidationPoolStake 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
@ -85,7 +82,7 @@ contract ValidationPools is Reputation, Forum {
bool inFavor
) public {
ValidationPool storage pool = validationPools[poolIndex];
_stakeOnValidationPool(pool, msg.sender, amount, inFavor, false);
_stakeOnValidationPool(pool, msg.sender, amount, inFavor);
}
/// Accept reputation stakes toward a validation pool
@ -97,7 +94,7 @@ contract ValidationPools is Reputation, Forum {
) public {
ValidationPool storage pool = validationPools[poolIndex];
_spendAllowance(owner, msg.sender, amount);
_stakeOnValidationPool(pool, owner, amount, inFavor, false);
_stakeOnValidationPool(pool, owner, amount, inFavor);
}
/// Accept fee to initiate a validation pool
@ -145,8 +142,8 @@ contract ValidationPools is Reputation, Forum {
pool.minted = msg.value;
// Here we assume a stakeForAuthor ratio of 0.5
// TODO: Make stakeForAuthor an adjustable parameter
_stakeOnValidationPool(pool, post.author, msg.value / 2, true, true);
_stakeOnValidationPool(pool, post.author, msg.value / 2, false, true);
// _stakeOnValidationPool(pool, post.author, msg.value / 2, true, true);
// _stakeOnValidationPool(pool, post.author, msg.value / 2, false, true);
emit ValidationPoolInitiated(poolIndex);
}
@ -155,8 +152,8 @@ contract ValidationPools is Reputation, Forum {
ValidationPool storage pool = validationPools[poolIndex];
Post storage post = posts[pool.postIndex];
require(pool.resolved == false, "Pool is already resolved");
uint256 stakedFor;
uint256 stakedAgainst;
uint stakedFor;
uint stakedAgainst;
ValidationPoolStake storage s;
for (uint i = 0; i < pool.stakeCount; i++) {
s = pool.stakes[i];
@ -166,6 +163,8 @@ contract ValidationPools is Reputation, Forum {
stakedAgainst += s.amount;
}
}
stakedFor += pool.minted / 2;
stakedAgainst += pool.minted / 2;
// Special case for early evaluation if dao.totalSupply has been staked
require(
block.timestamp > pool.endTime ||
@ -195,13 +194,6 @@ contract ValidationPools is Reputation, Forum {
return false;
}
// We are holding the REP minted on behalf of the author.
// We already registered their stakes. Now transfer the REP to the author,
// so that the stakes can be fulfilled.
_update(address(this), post.author, pool.minted);
// TODO: Transfer REP to the forum instead of to the author directly
// A tie is resolved in favor of the validation pool.
// This is especially important so that the DAO's first pool can pass,
// when no reputation has yet been minted.
@ -216,24 +208,20 @@ contract ValidationPools is Reputation, Forum {
pool.resolved = true;
pool.outcome = votePasses;
emit ValidationPoolResolved(poolIndex, votePasses, true);
// Value of losing stakes should be distributed among winners, in proportion to their stakes
uint256 amountFromWinners = votePasses ? stakedFor : stakedAgainst;
uint256 amountFromLosers = votePasses ? stakedAgainst : stakedFor;
// Only bindingPercent % should be redistributed
// Stake senders should get (100-bindingPercent) % back
// We have allowances for each stake. Time to collect from the losing stakes.
uint amountFromWinners = votePasses ? stakedFor : stakedAgainst;
uint totalRewards;
uint totalAllocated;
for (uint i = 0; i < pool.stakeCount; i++) {
s = pool.stakes[i];
uint bindingPercent = s.fromMint ? 100 : pool.params.bindingPercent;
bool redistributeLosingStakes = s.fromMint ||
pool.params.redistributeLosingStakes;
if (votePasses != s.inFavor) {
// Losing stake
// If this stake is from the minted fee, don't burn it
uint amount = (s.amount * bindingPercent) / 100;
if (redistributeLosingStakes) {
uint amount = (s.amount * pool.params.bindingPercent) / 100;
if (pool.params.redistributeLosingStakes) {
_update(s.sender, address(this), amount);
totalRewards += amount;
} else {
@ -241,21 +229,35 @@ contract ValidationPools is Reputation, Forum {
}
}
}
if (votePasses) {
// If vote passes, reward the author as though they had staked the winnin portion of the VP initial stake
totalRewards += pool.minted / 2;
uint reward = ((((totalRewards * pool.minted) / 2) /
amountFromWinners) * pool.params.bindingPercent) / 100;
totalAllocated += reward;
_update(address(this), post.author, pool.minted / 2 + reward);
// TODO: Transfer REP to the forum instead of to the author directly
} else {
// If vote does not pass, divide the losing stake among the winners
totalRewards += pool.minted;
}
// Include the losign portion of the VP initial stake
// Issue rewards to the winners
for (uint i = 0; i < pool.stakeCount; i++) {
s = pool.stakes[i];
uint bindingPercent = s.fromMint ? 100 : pool.params.bindingPercent;
bool redistributeLosingStakes = s.fromMint ||
pool.params.redistributeLosingStakes;
if (redistributeLosingStakes && votePasses == s.inFavor) {
if (
pool.params.redistributeLosingStakes && votePasses == s.inFavor
) {
// Winning stake
// If this stake is from the minted fee, always redistribute it to the winners
uint reward = (((amountFromLosers * s.amount) /
amountFromWinners) * bindingPercent) / 100;
uint reward = (((totalRewards * s.amount) / amountFromWinners) *
pool.params.bindingPercent) / 100;
totalAllocated += reward;
_update(address(this), s.sender, reward);
}
}
// Due to rounding, some reward may be left over. Let's give it to the author.
uint remainder = totalRewards - totalAllocated;
if (remainder > 0) {
@ -265,7 +267,7 @@ contract ValidationPools is Reputation, Forum {
// Distribute fee proportionately among all reputation holders
for (uint i = 0; i < memberCount; i++) {
address member = members[i];
uint256 share = (pool.fee * balanceOf(member)) / totalSupply();
uint share = (pool.fee * balanceOf(member)) / totalSupply();
// TODO: For efficiency this could be modified to hold the funds for recipients to withdraw
payable(member).transfer(share);
}

View File

@ -219,7 +219,7 @@ describe('Work1', () => {
expect(pool.fee).to.equal(WORK1_PRICE);
expect(pool.sender).to.equal(work1.target);
expect(pool.postIndex).to.equal(1);
expect(pool.stakeCount).to.equal(3);
expect(pool.stakeCount).to.equal(1);
await time.increase(86401);
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, true, true);
expect(await dao.balanceOf(account1)).to.equal(200);