Skip to content

Commit

Permalink
allow canceling a proposal for the specified breach timestamp
Browse files Browse the repository at this point in the history
  • Loading branch information
moodysalem committed Apr 23, 2024
1 parent c29f630 commit ac1c302
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 18 deletions.
51 changes: 33 additions & 18 deletions src/governor.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,15 @@ pub trait IGovernor<TContractState> {
// Vote on the given proposal.
fn vote(ref self: TContractState, id: felt252, yea: bool);

// Cancel the given proposal. The proposer may cancel the proposal at any time during before or during the voting period.
// Cancellation can happen by any address if the average voting weight is below the proposal_creation_threshold.
// Cancel the proposal with the given ID. Same as #cancel_at_timestamp, but uses the current timestamp for computing the voting weight.
fn cancel(ref self: TContractState, id: felt252);

// Cancel the proposal with the given ID. The proposal may be canceled at any time before it is executed.
// There are two ways the proposal cancellation can be authorized:
// - The proposer can cancel the proposal
// - Anyone can cancel if the average voting weight of the proposer was below the proposal_creation_threshold during the voting period (at the given breach_timestamp)
fn cancel_at_timestamp(ref self: TContractState, id: felt252, breach_timestamp: u64);

// Execute the given proposal.
fn execute(ref self: TContractState, call: Call) -> Span<felt252>;

Expand Down Expand Up @@ -100,6 +105,7 @@ pub mod Governor {
#[derive(starknet::Event, Drop)]
pub struct Canceled {
pub id: felt252,
pub breach_timestamp: u64,
}

#[derive(starknet::Event, Drop)]
Expand Down Expand Up @@ -242,46 +248,55 @@ pub mod Governor {


fn cancel(ref self: ContractState, id: felt252) {
self.cancel_at_timestamp(id, get_block_timestamp())
}

fn cancel_at_timestamp(ref self: ContractState, id: felt252, breach_timestamp: u64) {
let config = self.config.read();
let staker = self.staker.read();
let mut proposal = self.proposals.read(id);

assert(proposal.proposer.is_non_zero(), 'DOES_NOT_EXIST');

if (proposal.proposer != get_caller_address()) {
// if at any point the average voting weight is below the proposal_creation_threshold for the proposer, it can be canceled
assert(proposal.execution_state.canceled.is_zero(), 'ALREADY_CANCELED');
assert(proposal.execution_state.executed.is_zero(), 'ALREADY_EXECUTED');
assert(breach_timestamp >= proposal.execution_state.created, 'PROPOSAL_NOT_CREATED');

assert(
breach_timestamp < (proposal.execution_state.created
+ config.voting_start_delay
+ config.voting_period),
'VOTING_ENDED'
);

// iff the proposer is not calling this we need to check the voting weight
if proposal.proposer != get_caller_address() {
// if at the given timestamp (during the voting period),
// the average voting weight is below the proposal_creation_threshold for the proposer, it can be canceled
assert(
staker
.get_average_delegated_over_last(
.get_average_delegated(
delegate: proposal.proposer,
period: config.voting_weight_smoothing_duration
start: breach_timestamp - config.voting_weight_smoothing_duration,
end: breach_timestamp
) < config
.proposal_creation_threshold,
'THRESHOLD_NOT_BREACHED'
);
}

let timestamp_current = get_block_timestamp();

assert(
timestamp_current < (proposal.execution_state.created
+ config.voting_start_delay
+ config.voting_period),
'VOTING_ENDED'
);

// we know it's not executed since we check voting has not ended
proposal
.execution_state =
ExecutionState {
created: proposal.execution_state.created,
// we asserted that it is not already executed
executed: 0,
canceled: timestamp_current
canceled: get_block_timestamp()
};

self.proposals.write(id, proposal);

self.emit(Canceled { id });
self.emit(Canceled { id, breach_timestamp });
}

fn execute(ref self: ContractState, call: Call) -> Span<felt252> {
Expand Down
24 changes: 24 additions & 0 deletions src/governor_test.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,14 @@ fn test_vote_after_voting_period_should_fail() {
governor.vote(id, true); // vote should fail
}

#[test]
#[should_panic(expected: ('DOES_NOT_EXIST', 'ENTRYPOINT_FAILED'))]
fn test_cancel_fails_if_proposal_not_exists() {
let (_staker, _token, governor, _config) = setup();
let id = 1234;
governor.cancel(id);
}

#[test]
fn test_cancel_by_proposer() {
let (staker, token, governor, config) = setup();
Expand Down Expand Up @@ -453,6 +461,22 @@ fn test_cancel_by_proposer() {
);
}

#[test]
#[should_panic(expected: ('ALREADY_CANCELED', 'ENTRYPOINT_FAILED'))]
fn test_double_cancel_by_proposer() {
let (staker, token, governor, _config) = setup();

let proposer = proposer();

let id = create_proposal(governor, token, staker);

advance_time(30);

set_contract_address(proposer);
governor.cancel(id);
governor.cancel(id);
}

#[test]
fn test_cancel_by_non_proposer() {
let (staker, token, governor, config) = setup();
Expand Down

0 comments on commit ac1c302

Please sign in to comment.