forked from OpenZeppelin/cairo-contracts
-
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.
Add Governor component (OpenZeppelin#1180)
* feat: add features * feat: remove access dual dispatchers (OpenZeppelin#1154) * feat: bump scarb * feat: update CHANGELOG * feat: add features * feat: remove account dual dispatchers (OpenZeppelin#1168) * feat: move mocks to test_common * Remove token dual dispatchers (OpenZeppelin#1175) * feat: remove modules * fix: mock * fix: linter * fix: tests * fix: mock * feat: apply review suggestions * feat: update docs * feat: update CHANGELOG * fix: typo * fix: mod * feat: remove unused imports * fix: README * feat: move mocks into test_common * feat: remove mocks from release target * fix: mock * fix: imports * feat: bumo scarb and remove assert_macros from manifest * feat: re-add assert_macros * feat: add test target names * docs: update index * feat: add interface * feat: add double ended queue struct * feat: add proposal core * + * feat: add extension traits * feat: add hash_proposal * feat: add state internal function * feat: add bytearray to utils * fix: bytearray trait types * feat: apply stash * feat: add propose mechanism * feat: add queue mechanism * feat: add execute and cancel * feat: add main trait * feat: finish first extension * feat: add votes quorum fractional extension main logic * feat: add settings extension * feat: add governor votes extension * feat: add core execution extension * feat: add timelock execution extension * refactor: dependencies * feat: add mock * fix: linter * fix: comment * feat: add some tests * fix: linter * refactor: comments * fix: import path * fix: test * feat: add relay mechanism * fix: conditions * feat: add more tests * fix: linter * feat: add more tests * feat: add more tests * Update packages/governance/src/governor/interface.cairo Co-authored-by: Andrew Fleming <fleming.andrew@protonmail.com> * Update packages/governance/src/governor/interface.cairo Co-authored-by: Andrew Fleming <fleming.andrew@protonmail.com> * Update packages/governance/src/governor/governor.cairo Co-authored-by: Andrew Fleming <fleming.andrew@protonmail.com> * Update packages/governance/src/governor/governor.cairo Co-authored-by: Andrew Fleming <fleming.andrew@protonmail.com> * Update packages/governance/src/governor/governor.cairo Co-authored-by: Andrew Fleming <fleming.andrew@protonmail.com> * Update packages/governance/src/governor/proposal_core.cairo Co-authored-by: Andrew Fleming <fleming.andrew@protonmail.com> * feat: apply review updates * fix: linter * fix: bytearray * feat: add more tests * feat: add more tests * Update packages/utils/src/bytearray.cairo Co-authored-by: immrsd <103599616+immrsd@users.noreply.github.com> * feat: apply review updates * feat: add cast vote by sig * feat: add more tests * feat: add more tests * fix: remove import * feat: add yet more tests * feat: update CHANGELOG * feat: apply review updates * feat: add tests for governor core execution * feat: add more tests * feat: add more tests * feat: add more tests * fix: linter * feat: add more tests * feat: add more tests * feat: add more tests * fix: warnings * feat: add more tests * feat: add more tests * fix: linter --------- Co-authored-by: Andrew Fleming <fleming.andrew@protonmail.com> Co-authored-by: immrsd <103599616+immrsd@users.noreply.github.com>
- Loading branch information
1 parent
45b9a72
commit f0edfaa
Showing
37 changed files
with
7,761 additions
and
83 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
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,8 @@ | ||
pub mod extensions; | ||
pub mod governor; | ||
pub mod interface; | ||
pub mod proposal_core; | ||
pub mod vote; | ||
|
||
pub use governor::{GovernorComponent, DefaultConfig}; | ||
pub use proposal_core::ProposalCore; |
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,14 @@ | ||
pub mod governor_core_execution; | ||
pub mod governor_counting_simple; | ||
pub mod governor_settings; | ||
pub mod governor_timelock_execution; | ||
pub mod governor_votes; | ||
pub mod governor_votes_quorum_fraction; | ||
pub mod interface; | ||
|
||
pub use governor_core_execution::GovernorCoreExecutionComponent; | ||
pub use governor_counting_simple::GovernorCountingSimpleComponent; | ||
pub use governor_settings::GovernorSettingsComponent; | ||
pub use governor_timelock_execution::GovernorTimelockExecutionComponent; | ||
pub use governor_votes::GovernorVotesComponent; | ||
pub use governor_votes_quorum_fraction::GovernorVotesQuorumFractionComponent; |
89 changes: 89 additions & 0 deletions
89
packages/governance/src/governor/extensions/governor_core_execution.cairo
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,89 @@ | ||
// SPDX-License-Identifier: MIT | ||
// OpenZeppelin Contracts for Cairo v0.19.0 | ||
// (governance/governor/extensions/governor_core_execution.cairo) | ||
|
||
/// # GovernorCoreExecution Component | ||
/// | ||
/// Extension of GovernorComponent providing an execution mechanism directly through | ||
/// the Governor itself. For a timelocked execution mechanism, see | ||
/// GovernorTimelockExecutionComponent. | ||
#[starknet::component] | ||
pub mod GovernorCoreExecutionComponent { | ||
use crate::governor::GovernorComponent::{ | ||
InternalExtendedTrait, ComponentState as GovernorComponentState | ||
}; | ||
use crate::governor::GovernorComponent; | ||
use crate::governor::interface::ProposalState; | ||
use openzeppelin_introspection::src5::SRC5Component; | ||
use starknet::account::Call; | ||
use starknet::{ContractAddress, SyscallResultTrait}; | ||
|
||
#[storage] | ||
pub struct Storage {} | ||
|
||
// | ||
// Extensions | ||
// | ||
|
||
pub impl GovernorExecution< | ||
TContractState, | ||
+GovernorComponent::HasComponent<TContractState>, | ||
+GovernorComponent::GovernorCountingTrait<TContractState>, | ||
+GovernorComponent::GovernorSettingsTrait<TContractState>, | ||
+GovernorComponent::GovernorVotesTrait<TContractState>, | ||
+SRC5Component::HasComponent<TContractState>, | ||
impl GovernorCoreExecution: HasComponent<TContractState>, | ||
+Drop<TContractState> | ||
> of GovernorComponent::GovernorExecutionTrait<TContractState> { | ||
/// See `GovernorComponent::GovernorExecutionTrait::state`. | ||
fn state( | ||
self: @GovernorComponentState<TContractState>, proposal_id: felt252 | ||
) -> ProposalState { | ||
self._state(proposal_id) | ||
} | ||
|
||
/// See `GovernorComponent::GovernorExecutionTrait::executor`. | ||
fn executor(self: @GovernorComponentState<TContractState>) -> ContractAddress { | ||
starknet::get_contract_address() | ||
} | ||
|
||
/// See `GovernorComponent::GovernorExecutionTrait::execute_operations`. | ||
fn execute_operations( | ||
ref self: GovernorComponentState<TContractState>, | ||
proposal_id: felt252, | ||
calls: Span<Call>, | ||
description_hash: felt252 | ||
) { | ||
for call in calls { | ||
let Call { to, selector, calldata } = *call; | ||
starknet::syscalls::call_contract_syscall(to, selector, calldata).unwrap_syscall(); | ||
}; | ||
} | ||
|
||
/// See `GovernorComponent::GovernorExecutionTrait::queue_operations`. | ||
fn queue_operations( | ||
ref self: GovernorComponentState<TContractState>, | ||
proposal_id: felt252, | ||
calls: Span<Call>, | ||
description_hash: felt252 | ||
) -> u64 { | ||
0 | ||
} | ||
|
||
/// See `GovernorComponent::GovernorExecutionTrait::proposal_needs_queuing`. | ||
fn proposal_needs_queuing( | ||
self: @GovernorComponentState<TContractState>, proposal_id: felt252 | ||
) -> bool { | ||
false | ||
} | ||
|
||
/// See `GovernorComponent::GovernorExecutionTrait::cancel_operations`. | ||
fn cancel_operations( | ||
ref self: GovernorComponentState<TContractState>, | ||
proposal_id: felt252, | ||
description_hash: felt252 | ||
) { | ||
self._cancel(proposal_id, description_hash); | ||
} | ||
} | ||
} |
162 changes: 162 additions & 0 deletions
162
packages/governance/src/governor/extensions/governor_counting_simple.cairo
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,162 @@ | ||
// SPDX-License-Identifier: MIT | ||
// OpenZeppelin Contracts for Cairo v0.19.0 | ||
// (governance/governor/extensions/governor_counting_simple.cairo) | ||
|
||
/// # GovernorCountingSimple Component | ||
/// | ||
/// Extension of GovernorComponent for simple vote counting with three options. | ||
#[starknet::component] | ||
pub mod GovernorCountingSimpleComponent { | ||
use crate::governor::GovernorComponent::{ | ||
InternalTrait, ComponentState as GovernorComponentState | ||
}; | ||
use crate::governor::GovernorComponent; | ||
use openzeppelin_introspection::src5::SRC5Component; | ||
use starknet::ContractAddress; | ||
use starknet::storage::{Map, StoragePathEntry, StorageMapReadAccess, StorageMapWriteAccess}; | ||
use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; | ||
|
||
type ProposalId = felt252; | ||
|
||
#[storage] | ||
pub struct Storage { | ||
pub Governor_proposals_votes: Map<ProposalId, ProposalVote>, | ||
} | ||
|
||
/// Supported vote types. | ||
#[derive(Drop, PartialEq, Debug)] | ||
pub enum VoteType { | ||
Against, | ||
For, | ||
Abstain | ||
} | ||
|
||
impl U8TryIntoVoteType of TryInto<u8, VoteType> { | ||
fn try_into(self: u8) -> Option<VoteType> { | ||
match self { | ||
0 => Option::Some(VoteType::Against), | ||
1 => Option::Some(VoteType::For), | ||
2 => Option::Some(VoteType::Abstain), | ||
_ => Option::None, | ||
} | ||
} | ||
} | ||
|
||
impl VoteTypeIntoU8 of Into<VoteType, u8> { | ||
fn into(self: VoteType) -> u8 { | ||
match self { | ||
VoteType::Against => 0, | ||
VoteType::For => 1, | ||
VoteType::Abstain => 2 | ||
} | ||
} | ||
} | ||
|
||
#[starknet::storage_node] | ||
pub struct ProposalVote { | ||
pub against_votes: u256, | ||
pub for_votes: u256, | ||
pub abstain_votes: u256, | ||
pub has_voted: Map<ContractAddress, bool> | ||
} | ||
|
||
pub mod Errors { | ||
pub const ALREADY_CAST_VOTE: felt252 = 'Already cast vote'; | ||
pub const INVALID_VOTE_TYPE: felt252 = 'Invalid vote type'; | ||
} | ||
|
||
// | ||
// Extensions | ||
// | ||
|
||
pub impl GovernorCounting< | ||
TContractState, | ||
+GovernorComponent::HasComponent<TContractState>, | ||
+GovernorComponent::GovernorQuorumTrait<TContractState>, | ||
+SRC5Component::HasComponent<TContractState>, | ||
impl GovernorCountingSimple: HasComponent<TContractState>, | ||
+Drop<TContractState> | ||
> of GovernorComponent::GovernorCountingTrait<TContractState> { | ||
/// See `GovernorComponent::GovernorCountingTrait::counting_mode`. | ||
fn counting_mode(self: @GovernorComponentState<TContractState>) -> ByteArray { | ||
return "support=bravo&quorum=for,abstain"; | ||
} | ||
|
||
/// See `GovernorComponent::GovernorCountingTrait::count_vote`. | ||
/// | ||
/// In this module, the support follows the `VoteType` enum (from Governor Bravo). | ||
fn count_vote( | ||
ref self: GovernorComponentState<TContractState>, | ||
proposal_id: felt252, | ||
account: ContractAddress, | ||
support: u8, | ||
total_weight: u256, | ||
params: Span<felt252> | ||
) -> u256 { | ||
let mut contract = self.get_contract_mut(); | ||
let mut this_component = GovernorCountingSimple::get_component_mut(ref contract); | ||
|
||
let proposal_votes = this_component.Governor_proposals_votes.entry(proposal_id); | ||
assert(!proposal_votes.has_voted.read(account), Errors::ALREADY_CAST_VOTE); | ||
|
||
proposal_votes.has_voted.write(account, true); | ||
|
||
let support: VoteType = support.try_into().expect(Errors::INVALID_VOTE_TYPE); | ||
match support { | ||
VoteType::Against => { | ||
let current_votes = proposal_votes.against_votes.read(); | ||
proposal_votes.against_votes.write(current_votes + total_weight); | ||
}, | ||
VoteType::For => { | ||
let current_votes = proposal_votes.for_votes.read(); | ||
proposal_votes.for_votes.write(current_votes + total_weight); | ||
}, | ||
VoteType::Abstain => { | ||
let current_votes = proposal_votes.abstain_votes.read(); | ||
proposal_votes.abstain_votes.write(current_votes + total_weight); | ||
} | ||
} | ||
total_weight | ||
} | ||
|
||
/// See `GovernorComponent::GovernorCountingTrait::has_voted`. | ||
fn has_voted( | ||
self: @GovernorComponentState<TContractState>, | ||
proposal_id: felt252, | ||
account: ContractAddress | ||
) -> bool { | ||
let contract = self.get_contract(); | ||
let this_component = GovernorCountingSimple::get_component(contract); | ||
let proposal_votes = this_component.Governor_proposals_votes.entry(proposal_id); | ||
|
||
proposal_votes.has_voted.read(account) | ||
} | ||
|
||
/// See `GovernorComponent::GovernorCountingTrait::quorum_reached`. | ||
fn quorum_reached( | ||
self: @GovernorComponentState<TContractState>, proposal_id: felt252 | ||
) -> bool { | ||
let contract = self.get_contract(); | ||
let this_component = GovernorCountingSimple::get_component(contract); | ||
|
||
let proposal_votes = this_component.Governor_proposals_votes.entry(proposal_id); | ||
let snapshot = self._proposal_snapshot(proposal_id); | ||
|
||
self.quorum(snapshot) <= proposal_votes.for_votes.read() | ||
+ proposal_votes.abstain_votes.read() | ||
} | ||
|
||
/// See `GovernorComponent::GovernorCountingTrait::vote_succeeded`. | ||
/// | ||
/// In this module, the `for_votes` must be strictly over the `against_votes`. | ||
fn vote_succeeded( | ||
self: @GovernorComponentState<TContractState>, proposal_id: felt252 | ||
) -> bool { | ||
let contract = self.get_contract(); | ||
let this_component = GovernorCountingSimple::get_component(contract); | ||
let proposal_votes = this_component.Governor_proposals_votes.entry(proposal_id); | ||
|
||
proposal_votes.for_votes.read() > proposal_votes.against_votes.read() | ||
} | ||
} | ||
} |
Oops, something went wrong.