From e95a8c0cb9b5791e39d00ef77d05e60b4abb0a2c Mon Sep 17 00:00:00 2001 From: Ladd Hoffman Date: Thu, 28 Mar 2024 18:01:55 -0500 Subject: [PATCH] prevent stacking attestation from same sender --- client/src/App.jsx | 14 +- client/src/assets/DAO.json | 27 +- client/src/assets/Onboarding.json | 19 +- client/src/assets/Proposals.json | 247 ++++++++++++++++++ client/src/assets/Work1.json | 4 +- client/src/components/Proposals.jsx | 113 ++++++-- client/src/contract-addresses.json | 7 +- ethereum/contract-addresses.json | 7 +- .../contracts/{Proposal.sol => Proposals.sol} | 28 +- ethereum/scripts/deploy-proposals.js | 10 + ethereum/scripts/deploy.js | 2 + ethereum/scripts/util/deploy-dao-contract.js | 13 + 12 files changed, 424 insertions(+), 67 deletions(-) create mode 100644 client/src/assets/Proposals.json rename ethereum/contracts/{Proposal.sol => Proposals.sol} (92%) create mode 100644 ethereum/scripts/deploy-proposals.js create mode 100644 ethereum/scripts/util/deploy-dao-contract.js diff --git a/client/src/App.jsx b/client/src/App.jsx index dde9299..3690617 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -24,6 +24,7 @@ import WorkContract from './components/work-contracts/WorkContract'; import AddPostModal from './components/posts/AddPostModal'; import ViewPostModal from './components/posts/ViewPostModal'; import Post from './utils/Post'; +import Proposals from './components/Proposals'; function App() { const { @@ -147,17 +148,17 @@ function App() { // TODO: Unsubscribe from events when effect is to be rerun DAOContract.events.PostAdded({ fromBlock: 'latest' }).on('data', (event) => { - console.log('event: post added', event); + console.log('event: post added'); fetchPost(event.returnValues.postIndex); }); DAOContract.events.ValidationPoolInitiated({ fromBlock: 'latest' }).on('data', (event) => { - console.log('event: validation pool initiated', event); + console.log('event: validation pool initiated'); fetchValidationPool(event.returnValues.poolIndex); }); DAOContract.events.ValidationPoolResolved({ fromBlock: 'latest' }).on('data', (event) => { - console.log('event: validation pool resolved', event); + console.log('event: validation pool resolved'); fetchReputation(); fetchValidationPool(event.returnValues.poolIndex); }); @@ -357,8 +358,8 @@ function App() { {' '} Initiate Validation Pool {' '} - {' '} +
- {/* */} + + + + {proposals.filter((x) => !!x).map((request) => ( + + + + ))} @@ -34,21 +114,4 @@ function Proposals() { ); } -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; +export default Proposals; diff --git a/client/src/contract-addresses.json b/client/src/contract-addresses.json index a36d9db..8036deb 100644 --- a/client/src/contract-addresses.json +++ b/client/src/contract-addresses.json @@ -1,8 +1,9 @@ { "localhost": { - "DAO": "0xadB35d0E9d7B33441C7ED26add5D42F873430790", - "Work1": "0xb3FaD3422e1Edf2f7035952aa0f8760df466213a", - "Onboarding": "0xd7a4eDcC2Fbc139DfD88E25a996a8a9Dd385B1f0" + "DAO": "0x65B0922fe7F0c4012aa38704071f26aeF6F22650", + "Work1": "0x95673D8710A8eD59f8551e9B12509D6812e0623e", + "Onboarding": "0xc6b3b8A641c52F7bC13a9D444e1f0759CA3b87b4", + "Proposals": "0x71cb20D63576a0Fa4F620a2E96C73F82848B09e1" }, "sepolia": { "DAO": "0x8Cb4ab513A863ac29e855c85064ea53dec7dA24C", diff --git a/ethereum/contract-addresses.json b/ethereum/contract-addresses.json index a36d9db..8036deb 100644 --- a/ethereum/contract-addresses.json +++ b/ethereum/contract-addresses.json @@ -1,8 +1,9 @@ { "localhost": { - "DAO": "0xadB35d0E9d7B33441C7ED26add5D42F873430790", - "Work1": "0xb3FaD3422e1Edf2f7035952aa0f8760df466213a", - "Onboarding": "0xd7a4eDcC2Fbc139DfD88E25a996a8a9Dd385B1f0" + "DAO": "0x65B0922fe7F0c4012aa38704071f26aeF6F22650", + "Work1": "0x95673D8710A8eD59f8551e9B12509D6812e0623e", + "Onboarding": "0xc6b3b8A641c52F7bC13a9D444e1f0759CA3b87b4", + "Proposals": "0x71cb20D63576a0Fa4F620a2E96C73F82848B09e1" }, "sepolia": { "DAO": "0x8Cb4ab513A863ac29e855c85064ea53dec7dA24C", diff --git a/ethereum/contracts/Proposal.sol b/ethereum/contracts/Proposals.sol similarity index 92% rename from ethereum/contracts/Proposal.sol rename to ethereum/contracts/Proposals.sol index faea108..292a08c 100644 --- a/ethereum/contracts/Proposal.sol +++ b/ethereum/contracts/Proposals.sol @@ -7,11 +7,6 @@ import "./IOnValidate.sol"; import "hardhat/console.sol"; contract Proposals is DAOContract, IOnValidate { - struct Attestation { - address sender; - uint amount; - } - enum Stage { Proposal, Referendum0, @@ -41,8 +36,8 @@ contract Proposals is DAOContract, IOnValidate { uint postIndex; uint startTime; Stage stage; - mapping(uint => Attestation) attestations; - uint attestationCount; + mapping(address => uint) attestations; + uint attestationTotal; Referendum[3] referenda; } @@ -57,11 +52,12 @@ contract Proposals is DAOContract, IOnValidate { constructor(DAO dao) DAOContract(dao) {} function propose( - uint postIndex, + string calldata contentId, uint referendum0Duration, uint referendum1Duration, uint referendum100Duration ) external payable returns (uint proposalIndex) { + uint postIndex = dao.addPost(msg.sender, contentId); proposalIndex = proposalCount++; Proposal storage proposal = proposals[proposalIndex]; proposal.postIndex = postIndex; @@ -86,12 +82,9 @@ contract Proposals is DAOContract, IOnValidate { "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; + proposal.attestationTotal -= proposal.attestations[msg.sender]; + proposal.attestations[msg.sender] = amount; + proposal.attestationTotal += amount; } // --- Sequences of validation pool parameters --- @@ -214,11 +207,8 @@ contract Proposals is DAOContract, IOnValidate { 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(); + bool meetsAttestation = 10 * proposal.attestationTotal >= + dao.totalSupply(); bool expired = block.timestamp > proposal.startTime + 365 days; if (!meetsAttestation) { if (expired) { diff --git a/ethereum/scripts/deploy-proposals.js b/ethereum/scripts/deploy-proposals.js new file mode 100644 index 0000000..e396346 --- /dev/null +++ b/ethereum/scripts/deploy-proposals.js @@ -0,0 +1,10 @@ +const deployDAOContract = require('./util/deploy-dao-contract'); + +async function main() { + await deployDAOContract('Proposals'); +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/ethereum/scripts/deploy.js b/ethereum/scripts/deploy.js index 3ab1685..5da922c 100644 --- a/ethereum/scripts/deploy.js +++ b/ethereum/scripts/deploy.js @@ -1,10 +1,12 @@ const deployWorkContract = require('./util/deploy-work-contract'); const deployContract = require('./util/deploy-contract'); +const deployDAOContract = require('./util/deploy-dao-contract'); async function main() { await deployContract('DAO'); await deployWorkContract('Work1'); await deployWorkContract('Onboarding'); + await deployDAOContract('Proposals'); } main().catch((error) => { diff --git a/ethereum/scripts/util/deploy-dao-contract.js b/ethereum/scripts/util/deploy-dao-contract.js new file mode 100644 index 0000000..1cdbea8 --- /dev/null +++ b/ethereum/scripts/util/deploy-dao-contract.js @@ -0,0 +1,13 @@ +const deployContract = require('./deploy-contract'); + +const contractAddresses = require('../../contract-addresses.json'); + +require('dotenv').config(); + +const network = process.env.HARDHAT_NETWORK; + +const deployDAOContract = async (name) => { + await deployContract(name, [contractAddresses[network].DAO]); +}; + +module.exports = deployDAOContract;
IDPool IDFeeStageAttestationActions
{request.id.toString()}{request.fee.toString()}{request.stage.toString()}{request.attestationTotal.toString()} + {request.stage === 0n && ( + + )} +