Skip to content

Commit

Permalink
feat: add veryfing_contract to Delegation
Browse files Browse the repository at this point in the history
  • Loading branch information
ericnordelo committed Nov 14, 2024
1 parent f84b30d commit 11f2648
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 69 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed (Breaking)

- Add `verifying_contract` member to the `Delegation` struct used in Votes `delegate_by_sig` (#)
- VestingComponent `release` function won't emit an event or attempt to transfer when the amount is zero (#1209)
- Bump snforge_std to v0.33.0 (#1203)

Expand Down
16 changes: 10 additions & 6 deletions docs/modules/ROOT/pages/api/governance.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,10 @@ Schedule an operation containing a single transaction.

Requirements:

- the caller must have the `PROPOSER_ROLE` role.
- The caller must have the `PROPOSER_ROLE` role.

Emits {CallScheduled} event.
If `salt` is not zero, emits {CallSalt} event.
Emits {CallSalt} event if `salt` is not zero.

[.contract-item]
[[ITimelock-schedule_batch]]
Expand All @@ -147,7 +147,7 @@ Requirements:
- The caller must have the `PROPOSER_ROLE` role.

Emits one {CallScheduled} event for each transaction in the batch.
If `salt` is not zero, emits {CallSalt} event.
Emits {CallSalt} event if `salt` is not zero.

[.contract-item]
[[ITimelock-cancel]]
Expand Down Expand Up @@ -406,10 +406,12 @@ Schedule an operation containing a single transaction.

Requirements:

- the caller must have the `PROPOSER_ROLE` role.
- The caller must have the `PROPOSER_ROLE` role.
- The proposal must not already exist.
- `delay` must not be greater than or equal to the min delay.

Emits {CallScheduled} event.
If `salt` is not zero, emits {CallSalt} event.
Emits {CallSalt} event if `salt` is not zero.

[.contract-item]
[[TimelockControllerComponent-schedule_batch]]
Expand All @@ -420,9 +422,11 @@ Schedule an operation containing a batch of transactions.
Requirements:

- The caller must have the `PROPOSER_ROLE` role.
- The proposal must not already exist.
- `delay` must not be greater than or equal to the min delay.

Emits one {CallScheduled} event for each transaction in the batch.
If `salt` is not zero, emits {CallSalt} event.
Emits {CallSalt} event if `salt` is not zero.

[.contract-item]
[[TimelockControllerComponent-cancel]]
Expand Down
22 changes: 18 additions & 4 deletions packages/governance/src/multisig/multisig.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ pub mod MultisigComponent {
pub const QUORUM_TOO_HIGH: felt252 = 'Multisig: quorum > signers';
}

//
// External
//

#[embeddable_as(MultisigImpl)]
impl Multisig<
TContractState, +HasComponent<TContractState>, +Drop<TContractState>
Expand All @@ -140,7 +144,7 @@ pub mod MultisigComponent {
self.Multisig_signers_info.read().quorum
}

/// Returns whether the given `signer` is a registered signer.
/// Returns whether the given `signer` is registered.
/// Only registered signers can submit, confirm, or execute transactions.
fn is_signer(self: @ComponentState<TContractState>, signer: ContractAddress) -> bool {
self.Multisig_is_signer.read(signer)
Expand Down Expand Up @@ -173,7 +177,8 @@ pub mod MultisigComponent {
/// NOTE: Even if a `signer` is removed after confirming a transaction, this function will
/// still return true. However, their confirmation does not count toward the quorum when it
/// is checked. Therefore, this function should not be relied upon to verify legitimate
/// confirmations toward meeting the quorum.
/// confirmations toward meeting the quorum. For that, use `get_transaction_confirmations`
/// instead.
fn is_confirmed_by(
self: @ComponentState<TContractState>, id: TransactionID, signer: ContractAddress
) -> bool {
Expand Down Expand Up @@ -227,6 +232,7 @@ pub mod MultisigComponent {
/// - `new_quorum` must be less than or equal to the total number of signers after addition.
///
/// Emits a `SignerAdded` event for each signer added.
/// Emits a `QuorumUpdated` event if the quorum changes.
fn add_signers(
ref self: ComponentState<TContractState>,
new_quorum: u32,
Expand All @@ -244,6 +250,7 @@ pub mod MultisigComponent {
/// - `new_quorum` must be less than or equal to the total number of signers after removal.
///
/// Emits a `SignerRemoved` event for each signer removed.
/// Emits a `QuorumUpdated` event if the quorum changes.
fn remove_signers(
ref self: ComponentState<TContractState>,
new_quorum: u32,
Expand All @@ -260,6 +267,7 @@ pub mod MultisigComponent {
/// - The caller must be the contract itself.
/// - `signer_to_remove` must be an existing signer.
/// - `signer_to_add` must not be an existing signer.
/// - `signer_to_add` must be a non-zero address.
///
/// Emits a `SignerRemoved` event for the removed signer.
/// Emits a `SignerAdded` event for the new signer.
Expand Down Expand Up @@ -291,9 +299,10 @@ pub mod MultisigComponent {
/// Requirements:
///
/// - The caller must be a registered signer.
/// - The transaction must not have been submitted before.
///
/// Emits a `TransactionSubmitted` event.
/// If `salt` is not zero, emits a `CallSalt` event.
/// Emits a `CallSalt` event if `salt` is not zero.
fn submit_transaction(
ref self: ComponentState<TContractState>,
to: ContractAddress,
Expand All @@ -310,9 +319,10 @@ pub mod MultisigComponent {
/// Requirements:
///
/// - The caller must be a registered signer.
/// - The transaction must not have been submitted before.
///
/// Emits a `TransactionSubmitted` event.
/// If `salt` is not zero, emits a `CallSalt` event.
/// Emits a `CallSalt` event if `salt` is not zero.
fn submit_transaction_batch(
ref self: ComponentState<TContractState>, calls: Span<Call>, salt: felt252
) -> TransactionID {
Expand Down Expand Up @@ -441,6 +451,10 @@ pub mod MultisigComponent {
}
}

//
// Internal
//

#[generate_trait]
pub impl InternalImpl<
TContractState, +HasComponent<TContractState>, +Drop<TContractState>
Expand Down
28 changes: 16 additions & 12 deletions packages/governance/src/tests/test_votes.cairo
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use crate::votes::delegation::Delegation;
use crate::votes::votes::VotesComponent::{
DelegateChanged, DelegateVotesChanged, VotesImpl, InternalImpl,
};
use crate::votes::votes::VotesComponent;
use crate::votes::votes::VotingUnitsTrait;
use crate::votes::Delegation;
use crate::votes::VotesComponent::VotingUnitsTrait;
use crate::votes::VotesComponent::{DelegateChanged, DelegateVotesChanged, VotesImpl, InternalImpl,};
use crate::votes::VotesComponent;
use openzeppelin_test_common::mocks::votes::ERC721VotesMock::SNIP12MetadataImpl;
use openzeppelin_test_common::mocks::votes::{ERC721VotesMock, ERC20VotesMock};
use openzeppelin_testing as utils;
Expand Down Expand Up @@ -267,11 +265,12 @@ fn test_delegate_by_sig() {
// Set up delegation parameters
let nonce = 0;
let expiry = 'ts2';
let verifying_contract = contract_address;
let delegator = account;
let delegatee = DELEGATEE();

// Create and sign the delegation message
let delegation = Delegation { delegatee, nonce, expiry };
let delegation = Delegation { verifying_contract, delegatee, nonce, expiry };
let msg_hash = delegation.get_message_hash(delegator);
let (r, s) = key_pair.sign(msg_hash).unwrap();

Expand All @@ -292,8 +291,9 @@ fn test_delegate_by_sig_hash_generation() {
let delegator = contract_address_const::<
0x70b0526a4bfbc9ca717c96aeb5a8afac85181f4585662273668928585a0d628
>();
let verifying_contract = contract_address_const::<'VERIFIER'>();
let delegatee = RECIPIENT();
let delegation = Delegation { delegatee, nonce, expiry };
let delegation = Delegation { verifying_contract, delegatee, nonce, expiry };

let hash = delegation.get_message_hash(delegator);

Expand All @@ -302,11 +302,12 @@ fn test_delegate_by_sig_hash_generation() {
// - version: 'DAPP_VERSION'
// - chainId: 'SN_TEST'
// - account: 0x70b0526a4bfbc9ca717c96aeb5a8afac85181f4585662273668928585a0d628
// - verifying_contract: 'VERIFIER'
// - delegatee: 'RECIPIENT'
// - nonce: 0
// - expiry: 'ts2'
// - revision: '1'
let expected_hash = 0x314bd38b22b62d576691d8dafd9f8ea0601329ebe686bc64ca28e4d8821d5a0;
let expected_hash = 0x1fa1af6d3d0ede7d09790ef20a894668af9445b6bc93714e87a758be40efdc7;
assert_eq!(hash, expected_hash);
}

Expand Down Expand Up @@ -342,7 +343,8 @@ fn test_delegate_by_sig_invalid_signature() {
let expiry = 'ts2';
let delegator = account;
let delegatee = DELEGATEE();
let delegation = Delegation { delegatee, nonce, expiry };
let verifying_contract = test_address();
let delegation = Delegation { verifying_contract, delegatee, nonce, expiry };
let msg_hash = delegation.get_message_hash(delegator);
let (r, s) = key_pair.sign(msg_hash).unwrap();

Expand All @@ -362,8 +364,9 @@ fn test_delegate_by_sig_bad_delegatee() {
let expiry = 'ts2';
let delegator = account;
let delegatee = DELEGATEE();
let verifying_contract = test_address();
let bad_delegatee = contract_address_const::<0x1234>();
let delegation = Delegation { delegatee, nonce, expiry };
let delegation = Delegation { verifying_contract, delegatee, nonce, expiry };
let msg_hash = delegation.get_message_hash(delegator);
let (r, s) = key_pair.sign(msg_hash).unwrap();

Expand All @@ -383,7 +386,8 @@ fn test_delegate_by_sig_reused_signature() {
let expiry = 'ts2';
let delegator = account;
let delegatee = DELEGATEE();
let delegation = Delegation { delegatee, nonce, expiry };
let verifying_contract = test_address();
let delegation = Delegation { verifying_contract, delegatee, nonce, expiry };
let msg_hash = delegation.get_message_hash(delegator);
let (r, s) = key_pair.sign(msg_hash).unwrap();

Expand Down
2 changes: 1 addition & 1 deletion packages/governance/src/timelock.cairo
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
pub mod interface;
pub mod timelock_controller;

pub use timelock_controller::OperationState;
pub use interface::OperationState;
pub use timelock_controller::TimelockControllerComponent::{
PROPOSER_ROLE, CANCELLER_ROLE, EXECUTOR_ROLE
};
Expand Down
9 changes: 8 additions & 1 deletion packages/governance/src/timelock/interface.cairo
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts for Cairo v0.19.0 (governance/timelock/interface.cairo)

use crate::timelock::OperationState;
use starknet::ContractAddress;
use starknet::account::Call;

#[derive(Drop, Serde, PartialEq, Debug)]
pub enum OperationState {
Unset,
Waiting,
Ready,
Done
}

#[starknet::interface]
pub trait ITimelock<TState> {
fn is_operation(self: @TState, id: felt252) -> bool;
Expand Down
49 changes: 25 additions & 24 deletions packages/governance/src/timelock/timelock_controller.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub mod TimelockControllerComponent {
use core::hash::{HashStateTrait, HashStateExTrait};
use core::num::traits::Zero;
use core::pedersen::PedersenTrait;
use crate::timelock::interface::{ITimelock, TimelockABI};
use crate::timelock::interface::{OperationState, ITimelock, TimelockABI};
use crate::utils::call_impls::{HashCallImpl, HashCallsImpl, CallPartialEq};
use openzeppelin_access::accesscontrol::AccessControlComponent::InternalTrait as AccessControlInternalTrait;
use openzeppelin_access::accesscontrol::AccessControlComponent::{
Expand All @@ -30,11 +30,8 @@ pub mod TimelockControllerComponent {
use starknet::ContractAddress;
use starknet::SyscallResultTrait;
use starknet::account::Call;
use starknet::storage::{
Map, StorageMapReadAccess, StorageMapWriteAccess, StoragePointerReadAccess,
StoragePointerWriteAccess
};
use super::OperationState;
use starknet::storage::{Map, StorageMapReadAccess, StorageMapWriteAccess};
use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};

// Constants
pub const PROPOSER_ROLE: felt252 = selector!("PROPOSER_ROLE");
Expand Down Expand Up @@ -112,6 +109,10 @@ pub mod TimelockControllerComponent {
pub const UNAUTHORIZED_CALLER: felt252 = 'Timelock: unauthorized caller';
}

//
// External
//

#[embeddable_as(TimelockImpl)]
impl Timelock<
TContractState,
Expand Down Expand Up @@ -198,14 +199,16 @@ pub mod TimelockControllerComponent {
.finalize()
}

/// Schedule an operation containing a single transaction.
/// Schedules an operation containing a single transaction.
///
/// Requirements:
///
/// - the caller must have the `PROPOSER_ROLE` role.
/// - The caller must have the `PROPOSER_ROLE` role.
/// - The proposal must not already exist.
/// - `delay` must not be greater than or equal to the min delay.
///
/// Emits `CallScheduled` event.
/// If `salt` is not zero, emits `CallSalt` event.
/// Emits `CallSalt` event if `salt` is not zero.
fn schedule(
ref self: ComponentState<TContractState>,
call: Call,
Expand All @@ -224,14 +227,16 @@ pub mod TimelockControllerComponent {
}
}

/// Schedule an operation containing a batch of transactions.
/// Schedules an operation containing a batch of transactions.
///
/// Requirements:
///
/// - the caller must have the `PROPOSER_ROLE` role.
/// - The caller must have the `PROPOSER_ROLE` role.
/// - The proposal must not already exist.
/// - `delay` must not be greater than or equal to the min delay.
///
/// Emits one `CallScheduled` event for each transaction in the batch.
/// If `salt` is not zero, emits `CallSalt` event.
/// Emits `CallSalt` event if `salt` is not zero.
fn schedule_batch(
ref self: ComponentState<TContractState>,
calls: Span<Call>,
Expand All @@ -255,12 +260,12 @@ pub mod TimelockControllerComponent {
}
}

/// Cancel an operation.
/// Cancels an operation.
///
/// Requirements:
///
/// - The caller must have the `CANCELLER_ROLE` role.
/// - `id` must be an operation.
/// - `id` must be a pending operation.
///
/// Emits a `CallCancelled` event.
fn cancel(ref self: ComponentState<TContractState>, id: felt252) {
Expand All @@ -271,7 +276,7 @@ pub mod TimelockControllerComponent {
self.emit(CallCancelled { id });
}

/// Execute a (Ready) operation containing a single Call.
/// Executes a (Ready) operation containing a single Call.
///
/// Requirements:
///
Expand Down Expand Up @@ -299,7 +304,7 @@ pub mod TimelockControllerComponent {
self._after_call(id);
}

/// Execute a (Ready) operation containing a batch of Calls.
/// Executes a (Ready) operation containing a batch of Calls.
///
/// Requirements:
///
Expand Down Expand Up @@ -522,6 +527,10 @@ pub mod TimelockControllerComponent {
}
}

//
// Internal
//

#[generate_trait]
pub impl InternalImpl<
TContractState,
Expand Down Expand Up @@ -651,11 +660,3 @@ pub mod TimelockControllerComponent {
}
}
}

#[derive(Drop, Serde, PartialEq, Debug)]
pub enum OperationState {
Unset,
Waiting,
Ready,
Done
}
1 change: 1 addition & 0 deletions packages/governance/src/votes.cairo
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod delegation;
pub mod interface;
pub mod votes;
pub use delegation::Delegation;

pub use votes::VotesComponent;
Loading

0 comments on commit 11f2648

Please sign in to comment.