Now support quorum parameter
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 34s Details

This commit is contained in:
Ladd Hoffman 2024-03-26 15:20:54 -05:00
parent 69d91a6acc
commit 7b67526515
12 changed files with 148 additions and 30 deletions

View File

@ -218,6 +218,8 @@ function App() {
await DAO.methods.initiateValidationPool(
postIndex,
poolDuration ?? 3600,
1,
3,
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,12 +1,12 @@
{
"localhost": {
"DAO": "0x8d914D38dD301FC4606f5aa9fEcF8A76389020d3",
"Work1": "0x6c18eb38b7450F8DaE5A5928A40fcA3952493Ee4",
"Onboarding": "0xB8f0cd092979F273b752FDa060F82BF2745f192e"
"DAO": "0x9151C9bA7b159B13C9ae51042eac73115D898657",
"Work1": "0xef57508e07F8b6460f7a3aD14ceE0Be23Bb93055",
"Onboarding": "0x5865349932DDf62D7593061bB5463E9D72Fd6b7d"
},
"sepolia": {
"DAO": "0xc6b3b8A641c52F7bC13a9D444e1f0759CA3b87b4",
"Work1": "0xd2845aE812Ee42cF024fB4C55c052365792aBd78",
"Onboarding": "0xf15aCe29E5e3e4bb31FCddF2C65DF7C440449a57"
"DAO": "0x8Cb4ab513A863ac29e855c85064ea53dec7dA24C",
"Work1": "0xa9d632F689e5bB49A23c055ffFb520702E102C72",
"Onboarding": "0xc79C51c18194228C90ee3a68041d8Eb5cc4730F7"
}
}

View File

@ -1,12 +1,12 @@
{
"localhost": {
"DAO": "0x8d914D38dD301FC4606f5aa9fEcF8A76389020d3",
"Work1": "0x6c18eb38b7450F8DaE5A5928A40fcA3952493Ee4",
"Onboarding": "0xB8f0cd092979F273b752FDa060F82BF2745f192e"
"DAO": "0x9151C9bA7b159B13C9ae51042eac73115D898657",
"Work1": "0xef57508e07F8b6460f7a3aD14ceE0Be23Bb93055",
"Onboarding": "0x5865349932DDf62D7593061bB5463E9D72Fd6b7d"
},
"sepolia": {
"DAO": "0xc6b3b8A641c52F7bC13a9D444e1f0759CA3b87b4",
"Work1": "0xd2845aE812Ee42cF024fB4C55c052365792aBd78",
"Onboarding": "0xf15aCe29E5e3e4bb31FCddF2C65DF7C440449a57"
"DAO": "0x8Cb4ab513A863ac29e855c85064ea53dec7dA24C",
"Work1": "0xa9d632F689e5bB49A23c055ffFb520702E102C72",
"Onboarding": "0xc79C51c18194228C90ee3a68041d8Eb5cc4730F7"
}
}

View File

@ -28,6 +28,7 @@ struct ValidationPool {
mapping(uint => Stake) stakes;
uint stakeCount;
uint256 fee;
uint quorumPPB;
uint duration;
uint endTime;
bool resolved;
@ -55,8 +56,10 @@ contract DAO is ERC20("Reputation", "REP") {
// TODO: Make parameters adjustable
// TODO: Add forum parameters
uint public constant minDuration = 60; // 1 minute
uint public constant maxDuration = 86400; // 1 day
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);
event ValidationPoolInitiated(uint poolIndex);
@ -81,12 +84,23 @@ contract DAO is ERC20("Reputation", "REP") {
function initiateValidationPool(
uint postIndex,
uint duration,
uint quorumNumerator,
uint quorumDenominator,
bool callbackOnValidate,
bytes calldata callbackData
) external payable returns (uint poolIndex) {
require(msg.value > 0, "Fee is required to initiate validation pool");
require(duration >= minDuration, "Duration is too short");
require(duration <= maxDuration, "Duration is too long");
require(
(1_000_000_000 * quorumNumerator) / quorumDenominator >=
minQuorumPPB,
"Quorum is below minimum"
);
require(
quorumNumerator <= quorumDenominator,
"Quorum is greater than one"
);
Post storage post = posts[postIndex];
require(post.author != address(0), "Target post not found");
poolIndex = validationPoolCount++;
@ -94,6 +108,7 @@ 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.duration = duration;
pool.endTime = block.timestamp + duration;
pool.id = poolIndex;
@ -156,8 +171,12 @@ contract DAO is ERC20("Reputation", "REP") {
stakedAgainst += s.amount;
}
}
// Here we assume a quorum of 0
// TODO: Make quorum an adjustable parameter
// Check that quorum is met
require(
1_000_000_000 * (stakedFor + stakedAgainst) >=
totalSupply() * pool.quorumPPB,
"Quorum for this pool was not met"
);
// 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.

View File

@ -27,7 +27,7 @@ contract Onboarding is WorkContract, IOnValidate {
// Initiate validation pool
uint poolIndex = dao.initiateValidationPool{
value: request.fee - request.fee / 10
}(postIndex, POOL_DURATION, true, abi.encode(requestIndex));
}(postIndex, POOL_DURATION, 1, 3, true, abi.encode(requestIndex));
dao.stake(poolIndex, stake.amount, true);
}
@ -51,6 +51,8 @@ contract Onboarding is WorkContract, IOnValidate {
dao.initiateValidationPool{value: request.fee / 10}(
postIndex,
POOL_DURATION,
1,
3,
false,
""
);

View File

@ -37,8 +37,7 @@ abstract contract WorkContract is IAcceptAvailability {
mapping(uint => WorkRequest) public requests;
uint public requestCount;
// TODO: Make parameters configurable
uint constant POOL_DURATION = 60;
uint constant POOL_DURATION = 20;
event AvailabilityStaked(uint stakeIndex);
event WorkAssigned(uint requestIndex, uint stakeIndex);
@ -179,6 +178,8 @@ abstract contract WorkContract is IAcceptAvailability {
uint poolIndex = dao.initiateValidationPool{value: request.fee}(
postIndex,
POOL_DURATION,
1,
3,
false,
""
);

View File

@ -54,6 +54,8 @@ describe('DAO', () => {
const init = () => dao.initiateValidationPool(
0,
POOL_DURATION,
1,
3,
false,
callbackData,
{ value: POOL_FEE },
@ -68,15 +70,52 @@ describe('DAO', () => {
describe('Initiate', () => {
it('should not be able to initiate a validation pool without a fee', async () => {
const setup = await loadFixture(deploy);
const init = () => setup.dao.initiateValidationPool(0, POOL_DURATION, false, callbackData);
const init = () => setup.dao.initiateValidationPool(
0,
POOL_DURATION,
1,
3,
false,
callbackData,
);
await expect(init()).to.be.revertedWith('Fee is required to initiate validation pool');
});
it('should not be able to initiate a validation pool with a quorum below the minimum', async () => {
const setup = await loadFixture(deploy);
const init = () => setup.dao.initiateValidationPool(
0,
POOL_DURATION,
1,
4,
false,
callbackData,
{ value: POOL_FEE },
);
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 setup = await loadFixture(deploy);
const init = () => setup.dao.initiateValidationPool(
0,
POOL_DURATION,
11,
10,
false,
callbackData,
{ value: POOL_FEE },
);
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 setup = await loadFixture(deploy);
const init = () => setup.dao.initiateValidationPool(
0,
59,
0,
1,
3,
false,
callbackData,
{ value: POOL_FEE },
@ -88,7 +127,9 @@ describe('DAO', () => {
const setup = await loadFixture(deploy);
const init = () => setup.dao.initiateValidationPool(
0,
86401,
40000000000000,
1,
3,
false,
callbackData,
{ value: POOL_FEE },
@ -100,6 +141,8 @@ describe('DAO', () => {
const init = () => dao.initiateValidationPool(
0,
POOL_DURATION,
1,
3,
false,
callbackData,
{ value: POOL_FEE },
@ -143,6 +186,8 @@ describe('DAO', () => {
const init = () => dao.initiateValidationPool(
0,
POOL_DURATION,
1,
3,
false,
callbackData,
{ value: POOL_FEE },
@ -155,6 +200,25 @@ describe('DAO', () => {
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 () => {
@ -166,6 +230,8 @@ describe('DAO', () => {
await dao.initiateValidationPool(
0,
POOL_DURATION,
1,
3,
false,
callbackData,
{ value: POOL_FEE },

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, false, callbackData, { value: 100 });
await dao.initiateValidationPool(0, 60, 1, 3, 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, false, callbackData, { value: 100 });
await dao.initiateValidationPool(0, 60, 1, 3, false, callbackData, { value: 100 });
await time.increase(61);
await dao.evaluateOutcome(0);