Add win ratio VP param
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 32s
Details
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 32s
Details
This commit is contained in:
parent
41a0025311
commit
7ffd532292
|
@ -218,8 +218,8 @@ function App() {
|
|||
await DAO.methods.initiateValidationPool(
|
||||
postIndex,
|
||||
poolDuration ?? 3600,
|
||||
1,
|
||||
3,
|
||||
[1, 3],
|
||||
[1, 2],
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
import { useContext, useMemo } from 'react';
|
||||
import { PropTypes } from 'prop-types';
|
||||
import useList from '../utils/List';
|
||||
import WorkContractContext from '../contexts/WorkContractContext';
|
||||
import AvailabilityStakes from './work-contracts/AvailabilityStakes';
|
||||
import WorkRequests from './work-contracts/WorkRequests';
|
||||
import Web3Context from '../contexts/Web3Context';
|
||||
|
||||
function Proposals() {
|
||||
const [proposals, dispatchProposal] = useList();
|
||||
const {provider} = useContext(Web3Context);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>Proposals</h2>
|
||||
<div>
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
{/* <th>Pool ID</th> */}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{proposals.filter((x) => !!x).map((request) => (
|
||||
<tr key={request.id}>
|
||||
<td>{request.id.toString()}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
WorkContract.propTypes = {
|
||||
workContract: PropTypes.any.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
showRequestWork: PropTypes.bool,
|
||||
title: PropTypes.string.isRequired,
|
||||
verb: PropTypes.string.isRequired,
|
||||
showAvailabilityActions: PropTypes.bool,
|
||||
showAvailabilityAmount: PropTypes.bool,
|
||||
onlyShowAvailable: PropTypes.bool,
|
||||
};
|
||||
|
||||
WorkContract.defaultProps = {
|
||||
showRequestWork: false,
|
||||
showAvailabilityActions: true,
|
||||
showAvailabilityAmount: true,
|
||||
onlyShowAvailable: false,
|
||||
};
|
||||
|
||||
export default WorkContract;
|
|
@ -26,6 +26,7 @@ struct ValidationPoolParams {
|
|||
uint quorumPPB;
|
||||
uint bindingPercent;
|
||||
bool redistributeLosingStakes;
|
||||
uint[2] winRatio; // [ Numerator, Denominator ]
|
||||
}
|
||||
|
||||
struct ValidationPool {
|
||||
|
@ -91,8 +92,8 @@ contract DAO is ERC20("Reputation", "REP") {
|
|||
function initiateValidationPool(
|
||||
uint postIndex,
|
||||
uint duration,
|
||||
uint quorumNumerator,
|
||||
uint quorumDenominator,
|
||||
uint[2] calldata quorum, // [Numerator, Denominator]
|
||||
uint[2] calldata winRatio, // [Numerator, Denominator]
|
||||
uint bindingPercent,
|
||||
bool redistributeLosingStakes,
|
||||
bool callbackOnValidate,
|
||||
|
@ -102,14 +103,11 @@ contract DAO is ERC20("Reputation", "REP") {
|
|||
require(duration >= minDuration, "Duration is too short");
|
||||
require(duration <= maxDuration, "Duration is too long");
|
||||
require(
|
||||
(1_000_000_000 * quorumNumerator) / quorumDenominator >=
|
||||
minQuorumPPB,
|
||||
(1_000_000_000 * quorum[0]) / quorum[1] >= minQuorumPPB,
|
||||
"Quorum is below minimum"
|
||||
);
|
||||
require(
|
||||
quorumNumerator <= quorumDenominator,
|
||||
"Quorum is greater than one"
|
||||
);
|
||||
require(quorum[0] <= quorum[1], "Quorum is greater than one");
|
||||
require(winRatio[0] <= winRatio[1], "Win ratio 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");
|
||||
|
@ -118,9 +116,8 @@ contract DAO is ERC20("Reputation", "REP") {
|
|||
pool.sender = msg.sender;
|
||||
pool.postIndex = postIndex;
|
||||
pool.fee = msg.value;
|
||||
pool.params.quorumPPB =
|
||||
(1_000_000_000 * quorumNumerator) /
|
||||
quorumDenominator;
|
||||
pool.params.quorumPPB = (1_000_000_000 * quorum[0]) / quorum[1];
|
||||
pool.params.winRatio = winRatio;
|
||||
pool.params.bindingPercent = bindingPercent;
|
||||
pool.params.redistributeLosingStakes = redistributeLosingStakes;
|
||||
pool.duration = duration;
|
||||
|
@ -198,6 +195,8 @@ contract DAO is ERC20("Reputation", "REP") {
|
|||
IOnValidate(pool.sender).onValidate(
|
||||
votePasses,
|
||||
false,
|
||||
stakedFor,
|
||||
stakedAgainst,
|
||||
pool.callbackData
|
||||
);
|
||||
}
|
||||
|
@ -207,7 +206,18 @@ contract DAO is ERC20("Reputation", "REP") {
|
|||
// 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.
|
||||
votePasses = stakedFor >= stakedAgainst;
|
||||
// jconsole.log(
|
||||
// "staked for %d against %d, win ratio %d / %d",
|
||||
console.log("stakedFor", stakedFor);
|
||||
console.log("stakedAgainst", stakedAgainst);
|
||||
console.log(
|
||||
"winRatio",
|
||||
pool.params.winRatio[0],
|
||||
pool.params.winRatio[1]
|
||||
);
|
||||
votePasses =
|
||||
stakedFor * pool.params.winRatio[1] >=
|
||||
(stakedFor + stakedAgainst) * pool.params.winRatio[0];
|
||||
if (votePasses && !isMember[post.author]) {
|
||||
members[memberCount++] = post.author;
|
||||
isMember[post.author] = true;
|
||||
|
@ -268,6 +278,8 @@ contract DAO is ERC20("Reputation", "REP") {
|
|||
IOnValidate(pool.sender).onValidate(
|
||||
votePasses,
|
||||
true,
|
||||
stakedFor,
|
||||
stakedAgainst,
|
||||
pool.callbackData
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ interface IOnValidate {
|
|||
function onValidate(
|
||||
bool votePasses,
|
||||
bool quorumMet,
|
||||
uint stakedFor,
|
||||
uint stakedAgainst,
|
||||
bytes calldata callbackData
|
||||
) external;
|
||||
}
|
||||
|
|
|
@ -30,8 +30,8 @@ contract Onboarding is WorkContract, IOnValidate {
|
|||
}(
|
||||
postIndex,
|
||||
POOL_DURATION,
|
||||
1,
|
||||
3,
|
||||
[uint256(1), uint256(3)],
|
||||
[uint256(1), uint256(2)],
|
||||
100,
|
||||
true,
|
||||
true,
|
||||
|
@ -44,6 +44,8 @@ contract Onboarding is WorkContract, IOnValidate {
|
|||
function onValidate(
|
||||
bool votePasses,
|
||||
bool quorumMet,
|
||||
uint,
|
||||
uint,
|
||||
bytes calldata callbackData
|
||||
) external {
|
||||
require(
|
||||
|
@ -64,8 +66,8 @@ contract Onboarding is WorkContract, IOnValidate {
|
|||
dao.initiateValidationPool{value: request.fee / 10}(
|
||||
postIndex,
|
||||
POOL_DURATION,
|
||||
1,
|
||||
3,
|
||||
[uint256(1), uint256(3)],
|
||||
[uint256(1), uint256(2)],
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
pragma solidity ^0.8.24;
|
||||
|
||||
import "./DAO.sol";
|
||||
import "./IOnValidate.sol";
|
||||
|
||||
import "hardhat/console.sol";
|
||||
|
||||
contract Proposals is DAOContract {
|
||||
contract Proposals is DAOContract, IOnValidate {
|
||||
struct Attestation {
|
||||
address sender;
|
||||
uint amount;
|
||||
|
@ -19,9 +21,18 @@ contract Proposals is DAOContract {
|
|||
Accepted
|
||||
}
|
||||
|
||||
struct Pool {
|
||||
uint poolIndex;
|
||||
uint stakedFor;
|
||||
uint stakedAgainst;
|
||||
}
|
||||
|
||||
struct Referendum {
|
||||
uint duration;
|
||||
uint fee;
|
||||
// Each referendum may retry up to 3x
|
||||
Pool[] pools;
|
||||
uint[3] retryCount;
|
||||
}
|
||||
|
||||
struct Proposal {
|
||||
|
@ -33,7 +44,6 @@ contract Proposals is DAOContract {
|
|||
mapping(uint => Attestation) attestations;
|
||||
uint attestationCount;
|
||||
Referendum[3] referenda;
|
||||
uint[3] retryCount;
|
||||
}
|
||||
|
||||
mapping(uint => Proposal) public proposals;
|
||||
|
@ -91,6 +101,8 @@ contract Proposals is DAOContract {
|
|||
bool[3] referendaRedistributeLosingStakes = [false, false, true];
|
||||
// For each referendum, a numerator-denominator pair representing its quorum
|
||||
uint[2][3] referendaQuora = [[1, 10], [1, 2], [1, 3]];
|
||||
// Win ratios
|
||||
uint[2][3] referendaWinRatio = [[2, 3], [2, 3], [2, 3]];
|
||||
|
||||
/// Internal convenience function to wrap our call to dao.initiateValidationPool
|
||||
/// and to emit an event
|
||||
|
@ -108,13 +120,15 @@ contract Proposals is DAOContract {
|
|||
}(
|
||||
proposal.postIndex,
|
||||
proposal.referenda[referendumIndex].duration,
|
||||
referendaQuora[referendumIndex][0],
|
||||
referendaQuora[referendumIndex][1],
|
||||
referendaQuora[referendumIndex],
|
||||
referendaWinRatio[referendumIndex],
|
||||
bindingPercent,
|
||||
redistributeLosingStakes,
|
||||
true,
|
||||
abi.encode(proposalIndex)
|
||||
abi.encode(proposalIndex, referendumIndex)
|
||||
);
|
||||
Pool storage pool = proposal.referenda[referendumIndex].pools.push();
|
||||
pool.poolIndex = poolIndex;
|
||||
emit ReferendumStarted(proposalIndex, poolIndex);
|
||||
}
|
||||
|
||||
|
@ -122,37 +136,54 @@ contract Proposals is DAOContract {
|
|||
function onValidate(
|
||||
bool votePasses,
|
||||
bool quorumMet,
|
||||
uint stakedFor,
|
||||
uint stakedAgainst,
|
||||
bytes calldata callbackData
|
||||
) external {
|
||||
require(
|
||||
msg.sender == address(dao),
|
||||
"onValidate may only be called by the DAO contract"
|
||||
);
|
||||
uint proposalIndex = abi.decode(callbackData, (uint));
|
||||
(uint proposalIndex, uint referendumIndex) = abi.decode(
|
||||
callbackData,
|
||||
(uint, uint)
|
||||
);
|
||||
Proposal storage proposal = proposals[proposalIndex];
|
||||
if (!quorumMet) {
|
||||
proposal.stage = Stage.Failed;
|
||||
emit ProposalFailed(proposalIndex, "Quorum not met");
|
||||
return;
|
||||
}
|
||||
Referendum storage referendum = proposal.referenda[referendumIndex];
|
||||
Pool storage pool = referendum.pools[referendum.pools.length - 1];
|
||||
// Make a record of this result
|
||||
pool.stakedFor = stakedFor;
|
||||
pool.stakedAgainst = stakedAgainst;
|
||||
// Handle Referendum 0%
|
||||
if (proposal.stage == Stage.Referendum0) {
|
||||
if (votePasses) {
|
||||
bool participationAboveThreshold = 2 *
|
||||
(stakedFor + stakedAgainst) >=
|
||||
dao.totalSupply();
|
||||
// If vote passes (2/3 majority) and has >= 50% participation
|
||||
if (votePasses && participationAboveThreshold) {
|
||||
proposal.stage = Stage.Referendum1;
|
||||
} else if (proposal.retryCount[0] >= 3) {
|
||||
} else if (referendum.retryCount[0] >= 3) {
|
||||
proposal.stage = Stage.Failed;
|
||||
emit ProposalFailed(proposalIndex, "Retry count exceeded");
|
||||
} else {
|
||||
proposal.retryCount[0] += 1;
|
||||
referendum.retryCount[0] += 1;
|
||||
}
|
||||
// Handle Referendum 1%
|
||||
} else if (proposal.stage == Stage.Referendum1) {
|
||||
if (votePasses) {
|
||||
proposal.stage = Stage.Referendum100;
|
||||
} else if (proposal.retryCount[1] >= 3) {
|
||||
} else if (referendum.retryCount[1] >= 3) {
|
||||
proposal.stage = Stage.Failed;
|
||||
emit ProposalFailed(proposalIndex, "Retry count exceeded");
|
||||
} else {
|
||||
proposal.retryCount[1] += 1;
|
||||
referendum.retryCount[1] += 1;
|
||||
}
|
||||
// Handle Referendum 100%
|
||||
} else if (proposal.stage == Stage.Referendum100) {
|
||||
// Note that no retries are attempted for referendum 100%
|
||||
if (votePasses) {
|
||||
|
|
|
@ -176,8 +176,8 @@ abstract contract WorkContract is DAOContract, IAcceptAvailability {
|
|||
uint poolIndex = dao.initiateValidationPool{value: request.fee}(
|
||||
postIndex,
|
||||
POOL_DURATION,
|
||||
1,
|
||||
3,
|
||||
[uint256(1), uint256(3)],
|
||||
[uint256(1), uint256(2)],
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
|
|
|
@ -51,14 +51,14 @@ describe('DAO', () => {
|
|||
|
||||
const initiateValidationPool = ({
|
||||
postIndex, duration,
|
||||
quorumNumerator, quorumDenominator, bindingPercent,
|
||||
quorum, winRatio, bindingPercent,
|
||||
redistributeLosingStakes, callbackOnValidate,
|
||||
callbackData, fee,
|
||||
} = {}) => dao.initiateValidationPool(
|
||||
postIndex ?? 0,
|
||||
duration ?? POOL_DURATION,
|
||||
quorumNumerator ?? 1,
|
||||
quorumDenominator ?? 3,
|
||||
quorum ?? [1, 3],
|
||||
winRatio ?? [1, 2],
|
||||
bindingPercent ?? 100,
|
||||
redistributeLosingStakes ?? true,
|
||||
callbackOnValidate ?? false,
|
||||
|
@ -84,12 +84,12 @@ describe('DAO', () => {
|
|||
});
|
||||
|
||||
it('should not be able to initiate a validation pool with a quorum below the minimum', async () => {
|
||||
const init = () => initiateValidationPool({ quorumNumerator: 1, quorumDenominator: 11 });
|
||||
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({ quorumNumerator: 11, quorumDenominator: 10 });
|
||||
const init = () => initiateValidationPool({ quorum: [11, 10] });
|
||||
await expect(init()).to.be.revertedWith('Quorum is greater than one');
|
||||
});
|
||||
|
||||
|
@ -192,8 +192,8 @@ describe('DAO', () => {
|
|||
const init = () => dao.initiateValidationPool(
|
||||
0,
|
||||
POOL_DURATION,
|
||||
1,
|
||||
3,
|
||||
[1, 3],
|
||||
[1, 2],
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
|
@ -213,7 +213,7 @@ describe('DAO', () => {
|
|||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(0)).to.emit(dao, 'ValidationPoolResolved').withArgs(0, true, true);
|
||||
|
||||
const init = () => initiateValidationPool({ quorumNumerator: 1, quorumDenominator: 1 });
|
||||
const init = () => initiateValidationPool({ quorum: [1, 1] });
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
|
||||
expect(await dao.validationPoolCount()).to.equal(2);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
|
|
|
@ -19,7 +19,17 @@ describe('Onboarding', () => {
|
|||
|
||||
await dao.addPost(account1, 'content-id');
|
||||
const callbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
|
||||
await dao.initiateValidationPool(0, 60, 1, 3, 100, true, false, callbackData, { value: 100 });
|
||||
await dao.initiateValidationPool(
|
||||
0,
|
||||
60,
|
||||
[1, 3],
|
||||
[1, 2],
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
callbackData,
|
||||
{ value: 100 },
|
||||
);
|
||||
await time.increase(61);
|
||||
await dao.evaluateOutcome(0);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
|
|
|
@ -19,8 +19,28 @@ describe('Proposal', () => {
|
|||
await dao.addPost(account1, 'some-content-id');
|
||||
await dao.addPost(account2, 'some-other-content-id');
|
||||
const callbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
|
||||
await dao.initiateValidationPool(0, 60, 1, 3, 100, true, false, callbackData, { value: 100 });
|
||||
await dao.initiateValidationPool(1, 60, 1, 3, 100, true, false, callbackData, { value: 100 });
|
||||
await dao.initiateValidationPool(
|
||||
0,
|
||||
60,
|
||||
[1, 3],
|
||||
[1, 2],
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
callbackData,
|
||||
{ value: 100 },
|
||||
);
|
||||
await dao.initiateValidationPool(
|
||||
1,
|
||||
60,
|
||||
[1, 3],
|
||||
[1, 2],
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
callbackData,
|
||||
{ value: 100 },
|
||||
);
|
||||
await time.increase(61);
|
||||
await dao.evaluateOutcome(0);
|
||||
await dao.evaluateOutcome(1);
|
||||
|
|
|
@ -19,7 +19,17 @@ describe('Work1', () => {
|
|||
|
||||
await dao.addPost(account1, 'some-content-id');
|
||||
const callbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
|
||||
await dao.initiateValidationPool(0, 60, 1, 3, 100, true, false, callbackData, { value: 100 });
|
||||
await dao.initiateValidationPool(
|
||||
0,
|
||||
60,
|
||||
[1, 3],
|
||||
[1, 2],
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
callbackData,
|
||||
{ value: 100 },
|
||||
);
|
||||
await time.increase(61);
|
||||
await dao.evaluateOutcome(0);
|
||||
|
||||
|
|
Loading…
Reference in New Issue