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