Skip to content

Commit

Permalink
feat: add relay mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
ericnordelo committed Nov 8, 2024
1 parent 42a70b5 commit 1d2e400
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
///
/// Extension of GovernorComponent that binds the execution process to an instance of a contract
/// implementing TimelockControllerComponent. This adds a delay, enforced by the TimelockController
/// to all successful proposal (in addition to the voting duration). The Governor needs the proposer
/// (and ideally the executor and canceller) roles for the Governor to work properly.
/// to all successful proposal (in addition to the voting duration).
///
/// NOTE: The Governor needs the PROPOSER, EXECUTOR, and CANCELLER roles to work properly.
///
/// Using this model means the proposal will be operated by the TimelockController and not by the
/// Governor. Thus, the assets and permissions must be attached to the TimelockController. Any asset
Expand All @@ -26,7 +27,7 @@ pub mod GovernorTimelockExecutionComponent {
InternalExtendedTrait, ComponentState as GovernorComponentState
};
use crate::governor::GovernorComponent;
use crate::governor::extensions::interface::ITimelockController;
use crate::governor::extensions::interface::ITimelocked;
use crate::governor::interface::ProposalState;
use crate::timelock::interface::{ITimelockDispatcher, ITimelockDispatcherTrait};
use openzeppelin_introspection::src5::SRC5Component;
Expand Down Expand Up @@ -202,8 +203,8 @@ pub mod GovernorTimelockExecutionComponent {
// External
//

#[embeddable_as(TimelockControllerImpl)]
impl TimelockController<
#[embeddable_as(TimelockedImpl)]
impl Timelocked<
TContractState,
+HasComponent<TContractState>,
+GovernorComponent::GovernorSettingsTrait<TContractState>,
Expand All @@ -212,7 +213,7 @@ pub mod GovernorTimelockExecutionComponent {
+SRC5Component::HasComponent<TContractState>,
+GovernorComponent::HasComponent<TContractState>,
+Drop<TContractState>
> of ITimelockController<ComponentState<TContractState>> {
> of ITimelocked<ComponentState<TContractState>> {
/// Returns the token that voting power is sourced from.
fn timelock(self: @ComponentState<TContractState>) -> ContractAddress {
self.Governor_timelock_controller.read()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub trait IVotesToken<TState> {
}

#[starknet::interface]
pub trait ITimelockController<TState> {
pub trait ITimelocked<TState> {
/// Returns address of the associated timelock.
fn timelock(self: @TState) -> ContractAddress;

Expand Down
16 changes: 15 additions & 1 deletion packages/governance/src/governor/governor.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ pub mod GovernorComponent {
use openzeppelin_utils::bytearray::ByteArrayExtTrait;
use openzeppelin_utils::cryptography::snip12::SNIP12Metadata;
use openzeppelin_utils::structs::{DoubleEndedQueue, DoubleEndedQueueTrait};
use starknet::ContractAddress;
use starknet::account::Call;
use starknet::storage::{Map, StorageMapReadAccess, StorageMapWriteAccess};
use starknet::{ContractAddress, SyscallResultTrait};

#[storage]
pub struct Storage {
Expand Down Expand Up @@ -624,6 +624,20 @@ pub mod GovernorComponent {
) -> u256 {
1
}

/// Relays a transaction or function call to an arbitrary target.
///
/// In cases where the governance executor is some contract other than the governor itself,
/// like when using a timelock, this function can be invoked in a governance proposal to
/// recover tokens that was sent to the governor contract by mistake.
///
/// NOTE: If the executor is simply the governor itself, use of `relay` is redundant.
fn relay(ref self: ComponentState<TContractState>, call: Call) {
self.assert_only_governance();

let Call { to, selector, calldata } = call;
starknet::syscalls::call_contract_syscall(to, selector, calldata).unwrap_syscall();
}
}

//
Expand Down
9 changes: 9 additions & 0 deletions packages/governance/src/governor/interface.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -198,4 +198,13 @@ pub trait IGovernor<TState> {
params: ByteArray,
signature: Span<felt252>
) -> u256;

/// Relays a transaction or function call to an arbitrary target.
///
/// In cases where the governance executor is some contract other than the governor itself, like
/// when using a timelock, this function can be invoked in a governance proposal to recover
/// tokens that was sent to the governor contract by mistake.
///
/// NOTE: If the executor is simply the governor itself, use of `relay` is redundant.
fn relay(ref self: TState, call: Call);
}
7 changes: 4 additions & 3 deletions packages/governance/src/tests/governor/test_governor.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ fn test_get_proposal() {
#[test]
fn test_is_valid_description_too_short() {
let state = COMPONENT_STATE();
let short_description: ByteArray
= "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
let short_description: ByteArray =
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
assert_eq!(short_description.len(), 75);

let is_valid = state.is_valid_description_for_proposer(ADMIN(), @short_description);
Expand All @@ -96,7 +96,8 @@ fn test_is_valid_description_wrong_suffix() {
#[test]
fn test_is_valid_description_wrong_proposer() {
let state = COMPONENT_STATE();
let description = "#proposer=0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d";
let description =
"#proposer=0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d";

let is_valid = state.is_valid_description_for_proposer(ADMIN(), @description);
assert!(!is_valid);
Expand Down

0 comments on commit 1d2e400

Please sign in to comment.