-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Admin
committed
Nov 29, 2024
1 parent
0212231
commit 4876c09
Showing
9 changed files
with
882 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.13; | ||
|
||
import "@openzeppelin/contracts/access/AccessControl.sol"; | ||
import "./Governance.sol"; | ||
|
||
/** | ||
* @title CommitteeGovernance | ||
* @dev Manages specialized committees with their own voting domains | ||
*/ | ||
contract CommitteeGovernance is AccessControl { | ||
bytes32 public constant COMMITTEE_ADMIN_ROLE = keccak256("COMMITTEE_ADMIN_ROLE"); | ||
|
||
struct Committee { | ||
string name; | ||
string description; | ||
uint256 votingPowerMultiplier; // Multiplier for committee members' voting power (in basis points) | ||
bool active; | ||
mapping(address => bool) members; | ||
mapping(bytes4 => bool) allowedFunctions; // Function selectors this committee can propose | ||
} | ||
|
||
// Mapping of committee ID to Committee | ||
mapping(uint256 => Committee) public committees; | ||
uint256 public committeeCount; | ||
|
||
// Governance contract reference | ||
Governance public governance; | ||
|
||
event CommitteeCreated(uint256 indexed committeeId, string name); | ||
event MemberAdded(uint256 indexed committeeId, address indexed member); | ||
event MemberRemoved(uint256 indexed committeeId, address indexed member); | ||
event FunctionAllowed(uint256 indexed committeeId, bytes4 indexed functionSelector); | ||
event FunctionDisallowed(uint256 indexed committeeId, bytes4 indexed functionSelector); | ||
|
||
constructor(address _governanceAddress) { | ||
governance = Governance(_governanceAddress); | ||
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender); | ||
_setupRole(COMMITTEE_ADMIN_ROLE, msg.sender); | ||
} | ||
|
||
/** | ||
* @dev Create a new committee | ||
* @param name Name of the committee | ||
* @param description Description of the committee's purpose | ||
* @param votingPowerMultiplier Voting power multiplier for committee members | ||
*/ | ||
function createCommittee(string memory name, string memory description, uint256 votingPowerMultiplier) | ||
external | ||
onlyRole(COMMITTEE_ADMIN_ROLE) | ||
{ | ||
require(votingPowerMultiplier <= 10000, "Multiplier cannot exceed 100x"); | ||
|
||
uint256 committeeId = committeeCount++; | ||
Committee storage committee = committees[committeeId]; | ||
committee.name = name; | ||
committee.description = description; | ||
committee.votingPowerMultiplier = votingPowerMultiplier; | ||
committee.active = true; | ||
|
||
emit CommitteeCreated(committeeId, name); | ||
} | ||
|
||
/** | ||
* @dev Add a member to a committee | ||
* @param committeeId ID of the committee | ||
* @param member Address of the new member | ||
*/ | ||
function addMember(uint256 committeeId, address member) external onlyRole(COMMITTEE_ADMIN_ROLE) { | ||
require(committeeId < committeeCount, "Committee does not exist"); | ||
require(!committees[committeeId].members[member], "Already a member"); | ||
|
||
committees[committeeId].members[member] = true; | ||
emit MemberAdded(committeeId, member); | ||
} | ||
|
||
/** | ||
* @dev Remove a member from a committee | ||
* @param committeeId ID of the committee | ||
* @param member Address of the member to remove | ||
*/ | ||
function removeMember(uint256 committeeId, address member) external onlyRole(COMMITTEE_ADMIN_ROLE) { | ||
require(committeeId < committeeCount, "Committee does not exist"); | ||
require(committees[committeeId].members[member], "Not a member"); | ||
|
||
committees[committeeId].members[member] = false; | ||
emit MemberRemoved(committeeId, member); | ||
} | ||
|
||
/** | ||
* @dev Allow a function to be proposed by a committee | ||
* @param committeeId ID of the committee | ||
* @param functionSelector Function selector to allow | ||
*/ | ||
function allowFunction(uint256 committeeId, bytes4 functionSelector) external onlyRole(COMMITTEE_ADMIN_ROLE) { | ||
require(committeeId < committeeCount, "Committee does not exist"); | ||
committees[committeeId].allowedFunctions[functionSelector] = true; | ||
emit FunctionAllowed(committeeId, functionSelector); | ||
} | ||
|
||
/** | ||
* @dev Check if an address is a member of a committee | ||
* @param committeeId ID of the committee | ||
* @param member Address to check | ||
*/ | ||
function isMember(uint256 committeeId, address member) public view returns (bool) { | ||
return committees[committeeId].members[member]; | ||
} | ||
|
||
/** | ||
* @dev Get the voting power multiplier for a member in a specific committee | ||
* @param committeeId ID of the committee | ||
* @param member Address of the member | ||
*/ | ||
function getVotingPowerMultiplier(uint256 committeeId, address member) public view returns (uint256) { | ||
if (!isMember(committeeId, member)) { | ||
return 0; | ||
} | ||
return committees[committeeId].votingPowerMultiplier; | ||
} | ||
|
||
/** | ||
* @dev Check if a function can be proposed by a committee | ||
* @param committeeId ID of the committee | ||
* @param functionSelector Function selector to check | ||
*/ | ||
function isFunctionAllowed(uint256 committeeId, bytes4 functionSelector) public view returns (bool) { | ||
return committees[committeeId].allowedFunctions[functionSelector]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.13; | ||
|
||
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; | ||
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; | ||
|
||
/** | ||
* @title GaslessVoting | ||
* @dev Implements meta-transactions for gasless voting in governance | ||
*/ | ||
contract GaslessVoting is EIP712 { | ||
using ECDSA for bytes32; | ||
|
||
// Typed struct for EIP-712 | ||
struct VotePermit { | ||
address voter; | ||
uint256 proposalId; | ||
bool support; | ||
uint256 nonce; | ||
uint256 deadline; | ||
} | ||
|
||
// Mapping of voter nonces for replay protection | ||
mapping(address => uint256) public nonces; | ||
|
||
// EIP-712 type hash | ||
bytes32 public constant VOTE_PERMIT_TYPEHASH = | ||
keccak256("VotePermit(address voter,uint256 proposalId,bool support,uint256 nonce,uint256 deadline)"); | ||
|
||
constructor() EIP712("Backr Governance", "1") {} | ||
|
||
/** | ||
* @dev Returns the current nonce for an address | ||
* @param voter Address to get nonce for | ||
*/ | ||
function getNonce(address voter) public view returns (uint256) { | ||
return nonces[voter]; | ||
} | ||
|
||
/** | ||
* @dev Returns the domain separator used in the encoding of the signature for permits, as defined by EIP712 | ||
*/ | ||
function DOMAIN_SEPARATOR() external view returns (bytes32) { | ||
return _domainSeparatorV4(); | ||
} | ||
|
||
/** | ||
* @dev Verifies vote permit signature and returns the signer | ||
* @param permit Vote permit struct | ||
* @param signature Signature bytes | ||
*/ | ||
function verifyVotePermit(VotePermit memory permit, bytes memory signature) public view returns (address) { | ||
bytes32 structHash = keccak256( | ||
abi.encode( | ||
VOTE_PERMIT_TYPEHASH, permit.voter, permit.proposalId, permit.support, permit.nonce, permit.deadline | ||
) | ||
); | ||
|
||
bytes32 hash = _hashTypedDataV4(structHash); | ||
address signer = hash.recover(signature); | ||
|
||
require(signer == permit.voter, "GaslessVoting: invalid signature"); | ||
require(block.timestamp <= permit.deadline, "GaslessVoting: expired deadline"); | ||
require(nonces[permit.voter] == permit.nonce, "GaslessVoting: invalid nonce"); | ||
|
||
return signer; | ||
} | ||
|
||
/** | ||
* @dev Processes a vote permit, incrementing nonce if valid | ||
* @param permit Vote permit struct | ||
* @param signature Signature bytes | ||
*/ | ||
function _processVotePermit(VotePermit memory permit, bytes memory signature) internal { | ||
verifyVotePermit(permit, signature); | ||
nonces[permit.voter]++; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.