Proposals contract: attestation
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
790ff5db62
commit
1306fe2414
|
@ -218,13 +218,11 @@ contract DAO is ERC20("Reputation", "REP") {
|
|||
? ((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);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
pragma solidity ^0.8.24;
|
||||
|
||||
import "./DAO.sol";
|
||||
import "hardhat/console.sol";
|
||||
|
||||
contract Proposals is DAOContract {
|
||||
struct Attestation {
|
||||
|
@ -16,27 +17,44 @@ contract Proposals is DAOContract {
|
|||
Referendum100,
|
||||
Closed
|
||||
}
|
||||
|
||||
struct Referendum {
|
||||
uint duration;
|
||||
uint poolIndex;
|
||||
}
|
||||
|
||||
struct Proposal {
|
||||
address sender;
|
||||
string contentId;
|
||||
uint fee;
|
||||
uint feeRemaining;
|
||||
uint postIndex;
|
||||
uint startTime;
|
||||
Stage stage;
|
||||
mapping(uint => Attestation) attestations;
|
||||
uint attestationCount;
|
||||
Referendum[3] referenda;
|
||||
}
|
||||
|
||||
mapping(uint => Proposal) proposals;
|
||||
uint proposalCount;
|
||||
mapping(uint => Proposal) public proposals;
|
||||
uint public proposalCount;
|
||||
|
||||
constructor(DAO dao) DAOContract(dao) {}
|
||||
|
||||
function propose(
|
||||
string calldata contentId
|
||||
) external returns (uint proposalIndex) {
|
||||
uint postIndex,
|
||||
uint referendum0Duration,
|
||||
uint referendum1Duration,
|
||||
uint referendum100Duration
|
||||
) external payable returns (uint proposalIndex) {
|
||||
proposalIndex = proposalCount++;
|
||||
Proposal storage proposal = proposals[proposalIndex];
|
||||
proposal.postIndex = postIndex;
|
||||
proposal.startTime = block.timestamp;
|
||||
proposal.contentId = contentId;
|
||||
proposal.referenda[0].duration = referendum0Duration;
|
||||
proposal.referenda[1].duration = referendum1Duration;
|
||||
proposal.referenda[2].duration = referendum100Duration;
|
||||
proposal.fee = msg.value;
|
||||
proposal.feeRemaining = proposal.fee;
|
||||
}
|
||||
|
||||
function attest(uint proposalIndex, uint amount) external {
|
||||
|
@ -55,6 +73,12 @@ contract Proposals is DAOContract {
|
|||
attestation.amount = amount;
|
||||
}
|
||||
|
||||
// todo onValidate() {
|
||||
|
||||
// This callback will get proposalIndex
|
||||
|
||||
// todo }
|
||||
|
||||
function evaluateAttestation(uint proposalIndex) external returns (bool) {
|
||||
Proposal storage proposal = proposals[proposalIndex];
|
||||
require(
|
||||
|
@ -66,16 +90,30 @@ contract Proposals is DAOContract {
|
|||
totalAttestation += proposal.attestations[i].amount;
|
||||
}
|
||||
bool meetsAttestation = 10 * totalAttestation >= dao.totalSupply();
|
||||
bool expired = block.timestamp > proposal.startTime + 365 days;
|
||||
if (!meetsAttestation) {
|
||||
if (block.timestamp > proposal.startTime + 365 days) {
|
||||
if (expired) {
|
||||
proposal.stage = Stage.Closed;
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initiate validation pool
|
||||
proposal.stage = Stage.Referendum0;
|
||||
// TODO: make referendum0 duration a parameter
|
||||
uint thisFee = proposal.fee / 3;
|
||||
proposal.feeRemaining -= thisFee;
|
||||
proposal.referenda[0].poolIndex = dao.initiateValidationPool{
|
||||
value: thisFee
|
||||
}(
|
||||
proposal.postIndex, // uint postIndex,
|
||||
proposal.referenda[0].duration, // uint duration,
|
||||
1, // uint quorumNumerator,
|
||||
3, // uint quorumDenominator,
|
||||
0, // uint bindingPercent,
|
||||
false, // bool redistributeLosingStakes,
|
||||
false, // TODO bool callbackOnValidate : true,
|
||||
"" // TODO bytes calldata callbackData : This should probably be proposalIndex
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
const {
|
||||
time,
|
||||
loadFixture,
|
||||
} = require('@nomicfoundation/hardhat-toolbox/network-helpers');
|
||||
const { expect } = require('chai');
|
||||
const { ethers } = require('hardhat');
|
||||
const { beforeEach } = require('mocha');
|
||||
|
||||
describe('Proposal', () => {
|
||||
async function deploy() {
|
||||
// Contracts are deployed using the first signer/account by default
|
||||
const [account1, account2] = await ethers.getSigners();
|
||||
|
||||
const DAO = await ethers.getContractFactory('DAO');
|
||||
const dao = await DAO.deploy();
|
||||
const Proposals = await ethers.getContractFactory('Proposals');
|
||||
const proposals = await Proposals.deploy(dao.target);
|
||||
|
||||
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 time.increase(61);
|
||||
await dao.evaluateOutcome(0);
|
||||
await dao.evaluateOutcome(1);
|
||||
|
||||
return {
|
||||
dao, proposals, account1, account2,
|
||||
};
|
||||
}
|
||||
|
||||
it('Should deploy', async () => {
|
||||
const { dao, proposals, account1 } = await loadFixture(deploy);
|
||||
expect(dao).to.exist;
|
||||
expect(proposals).to.exist;
|
||||
expect(await dao.memberCount()).to.equal(2);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.totalSupply()).to.equal(200);
|
||||
expect(await proposals.proposalCount()).to.equal(0);
|
||||
});
|
||||
|
||||
describe('Attestation', () => {
|
||||
let dao;
|
||||
let proposals;
|
||||
let account1;
|
||||
// let account2;
|
||||
let proposal;
|
||||
|
||||
beforeEach(async () => {
|
||||
({
|
||||
dao,
|
||||
proposals,
|
||||
account1,
|
||||
// account2,
|
||||
} = await loadFixture(deploy));
|
||||
|
||||
console.log('postCount', await dao.postCount());
|
||||
await dao.addPost(account1, 'proposal-content-id');
|
||||
const postIndex = await dao.postCount() - BigInt(1);
|
||||
const post = await dao.posts(postIndex);
|
||||
expect(await post.contentId).to.equal('proposal-content-id');
|
||||
await proposals.propose(postIndex, 20, 20, 20, { value: 100 });
|
||||
expect(await proposals.proposalCount()).to.equal(1);
|
||||
proposal = await proposals.proposals(0);
|
||||
expect(proposal.postIndex).to.equal(postIndex);
|
||||
expect(proposal.stage).to.equal(0);
|
||||
});
|
||||
|
||||
it('Can submit a proposal', async () => {
|
||||
// Nothing to do here -- this just tests our beforeEach
|
||||
});
|
||||
|
||||
it('Can attest for a proposal', async () => {
|
||||
await proposals.connect(account1).attest(0, 50);
|
||||
// Nonbinding, non-encumbering
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
});
|
||||
|
||||
describe('Evaluate attestation', () => {
|
||||
it('when threshold is met, advance to referendum 0% binding', async () => {
|
||||
await proposals.attest(0, 20);
|
||||
await proposals.evaluateAttestation(0);
|
||||
proposal = await proposals.proposals(0);
|
||||
expect(proposal.stage).to.equal(1);
|
||||
});
|
||||
|
||||
it('when threshold is not met, and duration has not elapsed, do nothing', async () => {
|
||||
await proposals.evaluateAttestation(0);
|
||||
expect(proposal.stage).to.equal(0);
|
||||
});
|
||||
|
||||
it('when threshold is not met, and duration has elapsed, close the proposal', async () => {
|
||||
await time.increase(365 * 86400 + 1); // 1 year + 1 second
|
||||
await proposals.evaluateAttestation(0);
|
||||
proposal = await proposals.proposals(0);
|
||||
expect(proposal.stage).to.equal(4); // Stage.Closed
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue