Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions contracts/GovernanceEngine.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

/**
* @title VotingContract
* @dev Implements voting process along with vote delegation
*/
contract VotingContract is ReentrancyGuard, Ownable {
using SafeMath for uint256;

struct Voter {
bool voted; // if true, that person already voted
address delegate; // person delegated to
uint vote; // index of the voted proposal
}

struct Proposal {
bytes32 name; // short name (up to 32 bytes)
uint voteCount; // number of accumulated votes
}

address public chairperson;

mapping(address => Voter) public voters;

Proposal[] public proposals;

event VoteCasted(address indexed voter, uint proposalIndex);
event VotingDelegated(address indexed from, address indexed to);
event ProposalAdded(bytes32 proposalName);
event ChairpersonChanged(address indexed newChairperson);

modifier onlyChairperson() {
require(msg.sender == chairperson, "Caller is not the chairperson");
_;
}

constructor(bytes32[] memory proposalNames) {
chairperson = msg.sender;
voters[chairperson].voted = false;

for (uint i = 0; i < proposalNames.length; i++) {
proposals.push(Proposal({
name: proposalNames[i],
voteCount: 0
}));
emit ProposalAdded(proposalNames[i]);
}
}

/**
* @dev Give your vote (including votes delegated to you) to proposal `proposals[proposal].name`.
* @param proposal index of proposal in the proposals array
*/
function vote(uint proposal) external nonReentrant {
Voter storage sender = voters[msg.sender];
require(!sender.voted, "Already voted.");
require(proposal < proposals.length, "Invalid proposal index.");

sender.voted = true;
sender.vote = proposal;

proposals[proposal].voteCount += 1;
emit VoteCasted(msg.sender, proposal);
}

/**
* @dev Delegate your vote to the voter `to`.
* @param to address to which vote is delegated
*/
function delegate(address to) external nonReentrant {
Voter storage sender = voters[msg.sender];
require(!sender.voted, "You already voted.");
require(to != msg.sender, "Self-delegation is disallowed.");

while (voters[to].delegate != address(0)) {
to = voters[to].delegate;

// We found a loop in the delegation, not allowed.
require(to != msg.sender, "Found loop in delegation.");
}

sender.voted = true;
sender.delegate = to;
Voter storage delegate_ = voters[to];
if (delegate_.voted) {
proposals[delegate_.vote].voteCount += 1;
}
emit VotingDelegated(msg.sender, to);
}

/**
* @dev Computes the winning proposal taking all previous votes into account.
* @return winningProposal_ index of winning proposal in the proposals array
*/
function winningProposal() public view returns (uint winningProposal_) {
uint winningVoteCount = 0;
for (uint p = 0; p < proposals.length; p++) {
if (proposals[p].voteCount > winningVoteCount) {
winningVoteCount = proposals[p].voteCount;
winningProposal_ = p;
}
}
}

/**
* @dev Gets the name of the winning proposal
* @return winnerName_ name of the winning proposal
*/
function winnerName() external view returns (bytes32 winnerName_) {
winnerName_ = proposals[winningProposal()].name;
}

/**
* @dev Changes the chairperson of the voting contract
* @param newChairperson address of the new chairperson
*/
function changeChairperson(address newChairperson) external onlyOwner {
require(newChairperson != address(0), "Zero address not allowed.");
chairperson = newChairperson;
emit ChairpersonChanged(newChairperson);
}

/**
* @dev Adds a new proposal to the list of proposals
* @param proposalName name of the new proposal
*/
function addProposal(bytes32 proposalName) external onlyChairperson {
proposals.push(Proposal({
name: proposalName,
voteCount: 0
}));
emit ProposalAdded(proposalName);
}
}
82 changes: 82 additions & 0 deletions contracts/GovernanceHub.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract GovernanceHub {
struct Proposal {
uint256 id;
address proposer;
string description;
uint256 deadline;
uint256 votesFor;
uint256 votesAgainst;
bool executed;
}

mapping(uint256 => Proposal) public proposals;
mapping(address => mapping(uint256 => bool)) public hasVoted;
uint256 public proposalCount;
uint256 public quorum;
address public executionAuthority;

event ProposalCreated(uint256 indexed id, address indexed proposer, string description, uint256 deadline);
event Voted(uint256 indexed proposalId, address indexed voter, bool support);
event ProposalExecuted(uint256 indexed proposalId);

modifier onlyExecutionAuthority() {
require(msg.sender == executionAuthority, "Not execution authority");
_;
}

constructor(uint256 _quorum, address _executionAuthority) {
quorum = _quorum;
executionAuthority = _executionAuthority;
}

function createProposal(string memory description, uint256 duration) external {
require(bytes(description).length > 0, "Description cannot be empty");

uint256 deadline = block.timestamp + duration;

proposals[proposalCount] = Proposal({
id: proposalCount,
proposer: msg.sender,
description: description,
deadline: deadline,
votesFor: 0,
votesAgainst: 0,
executed: false
});

emit ProposalCreated(proposalCount, msg.sender, description, deadline);
proposalCount++;
}

function vote(uint256 proposalId, bool support) external {
Proposal storage proposal = proposals[proposalId];
require(block.timestamp < proposal.deadline, "Voting period has ended");
require(!hasVoted[msg.sender][proposalId], "Already voted");

if (support) {
proposal.votesFor++;
} else {
proposal.votesAgainst++;
}
hasVoted[msg.sender][proposalId] = true;

emit Voted(proposalId, msg.sender, support);
}

function executeProposal(uint256 proposalId) external onlyExecutionAuthority {
Proposal storage proposal = proposals[proposalId];
require(block.timestamp >= proposal.deadline, "Proposal is still active");
require(!proposal.executed, "Proposal already executed");
require(proposal.votesFor + proposal.votesAgainst >= quorum, "Quorum not reached");

proposal.executed = true;

// Placeholder for execution logic
// execute(proposal);

emit ProposalExecuted(proposalId);
}
}
20 changes: 20 additions & 0 deletions hardhat.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
require("@nomicfoundation/hardhat-toolbox");

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: {
version: "0.8.19",
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
},
paths: {
sources: "./contracts",
tests: "./test",
cache: "./cache",
artifacts: "./artifacts"
}
};
17 changes: 17 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "verisphere-governance",
"version": "1.0.0",
"description": "VeriSphere Governance Engine smart contracts",
"scripts": {
"compile": "hardhat compile",
"test": "hardhat test",
"test:coverage": "hardhat coverage"
},
"devDependencies": {
"@nomicfoundation/hardhat-toolbox": "^4.0.0",
"@nomicfoundation/hardhat-network-helpers": "^1.0.10",
"@openzeppelin/contracts": "^5.0.1",
"hardhat": "^2.19.4"
},
"license": "MIT"
}
Loading