Skip to content

Commit

Permalink
feat: add get_proposal_status (#527)
Browse files Browse the repository at this point in the history
* feat: add get_proposal_status

* expose get_proposal_status on space

* unit test simple quorum execution strategy
  • Loading branch information
pscott authored Sep 5, 2023
1 parent 0c19ab4 commit 10f373d
Show file tree
Hide file tree
Showing 10 changed files with 378 additions and 19 deletions.
13 changes: 12 additions & 1 deletion starknet/src/execution_strategies/eth_relayer.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ mod EthRelayerExecutionStrategy {
use serde::Serde;
use starknet::{info, syscalls, EthAddress};
use sx::interfaces::IExecutionStrategy;
use sx::types::Proposal;
use sx::types::{Proposal, ProposalStatus};

#[storage]
struct Storage {}
Expand Down Expand Up @@ -53,5 +53,16 @@ mod EthRelayerExecutionStrategy {
fn get_strategy_type(self: @ContractState) -> felt252 {
'EthRelayer'
}

fn get_proposal_status(
self: @ContractState,
proposal: Proposal,
votes_for: u256,
votes_against: u256,
votes_abstain: u256,
) -> ProposalStatus {
panic_with_felt252('unimplemented');
ProposalStatus::Cancelled(())
}
}
}
230 changes: 229 additions & 1 deletion starknet/src/execution_strategies/simple_quorum.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ mod SimpleQuorumExecutionStrategy {
proposal: @Proposal,
votes_for: u256,
votes_against: u256,
votes_abstain: u256
votes_abstain: u256,
) -> ProposalStatus {
let accepted = _quorum_reached(self._quorum.read(), votes_for, votes_against, votes_abstain)
& _supported(votes_for, votes_against);
Expand Down Expand Up @@ -67,3 +67,231 @@ mod SimpleQuorumExecutionStrategy {
votes_for > votes_against
}
}

#[cfg(test)]
mod tests {
use super::SimpleQuorumExecutionStrategy;
use super::SimpleQuorumExecutionStrategy::{get_proposal_status, initializer};
use sx::types::{Proposal, proposal::ProposalDefault, FinalizationStatus, ProposalStatus};

#[test]
#[available_gas(10000000)]
fn cancelled() {
let mut state = SimpleQuorumExecutionStrategy::unsafe_new_contract_state();

let mut proposal = ProposalDefault::default();
proposal.finalization_status = FinalizationStatus::Cancelled(());
let votes_for = 0;
let votes_against = 0;
let votes_abstain = 0;
let result = get_proposal_status(
@state, @proposal, votes_for, votes_against, votes_abstain
);
assert(result == ProposalStatus::Cancelled(()), 'failed cancelled');
}

#[test]
#[available_gas(10000000)]
fn executed() {
let mut state = SimpleQuorumExecutionStrategy::unsafe_new_contract_state();

let mut proposal = ProposalDefault::default();
proposal.finalization_status = FinalizationStatus::Executed(());
let votes_for = 0;
let votes_against = 0;
let votes_abstain = 0;
let result = get_proposal_status(
@state, @proposal, votes_for, votes_against, votes_abstain
);
assert(result == ProposalStatus::Executed(()), 'failed executed');
}

#[test]
#[available_gas(10000000)]
fn voting_delay() {
let mut state = SimpleQuorumExecutionStrategy::unsafe_new_contract_state();

let mut proposal = ProposalDefault::default();
proposal.start_timestamp = 42424242;
let votes_for = 0;
let votes_against = 0;
let votes_abstain = 0;
let result = get_proposal_status(
@state, @proposal, votes_for, votes_against, votes_abstain
);
assert(result == ProposalStatus::VotingDelay(()), 'failed voting_delay');
}

#[test]
#[available_gas(10000000)]
fn voting_period() {
let mut state = SimpleQuorumExecutionStrategy::unsafe_new_contract_state();

let mut proposal = ProposalDefault::default();
proposal.min_end_timestamp = 42424242;
let votes_for = 0;
let votes_against = 0;
let votes_abstain = 0;
let result = get_proposal_status(
@state, @proposal, votes_for, votes_against, votes_abstain
);
assert(result == ProposalStatus::VotingPeriod(()), 'failed min_end_timestamp');
}

#[test]
#[available_gas(10000000)]
fn shortcut_accepted() {
let mut state = SimpleQuorumExecutionStrategy::unsafe_new_contract_state();
let quorum = 2;
initializer(ref state, quorum);

let mut proposal = ProposalDefault::default();
proposal.max_end_timestamp = 10;
let votes_for = quorum;
let votes_against = 0;
let votes_abstain = 0;
let result = get_proposal_status(
@state, @proposal, votes_for, votes_against, votes_abstain
);
assert(result == ProposalStatus::VotingPeriodAccepted(()), 'failed shortcut_accepted');
}

#[test]
#[available_gas(10000000)]
fn shortcut_only_abstains() {
let mut state = SimpleQuorumExecutionStrategy::unsafe_new_contract_state();
let quorum = 2;
initializer(ref state, quorum);

let mut proposal = ProposalDefault::default();
proposal.max_end_timestamp = 10;
let votes_for = 0;
let votes_against = 0;
let votes_abstain = quorum;
let result = get_proposal_status(
@state, @proposal, votes_for, votes_against, votes_abstain
);
assert(result == ProposalStatus::VotingPeriod(()), 'failed shortcut_only_abstains');
}

#[test]
#[available_gas(10000000)]
fn shortcut_only_againsts() {
let mut state = SimpleQuorumExecutionStrategy::unsafe_new_contract_state();
let quorum = 2;
initializer(ref state, quorum);

let mut proposal = ProposalDefault::default();
proposal.max_end_timestamp = 10;
let votes_for = 0;
let votes_against = quorum;
let votes_abstain = 0;
let result = get_proposal_status(
@state, @proposal, votes_for, votes_against, votes_abstain
);
assert(result == ProposalStatus::VotingPeriod(()), 'failed shortcut_only_againsts');
}

#[test]
#[available_gas(10000000)]
fn shortcut_balanced() {
let mut state = SimpleQuorumExecutionStrategy::unsafe_new_contract_state();
let quorum = 2;
initializer(ref state, quorum);

let mut proposal = ProposalDefault::default();
proposal.max_end_timestamp = 10;
let votes_for = quorum;
let votes_against = quorum;
let votes_abstain = 0;
let result = get_proposal_status(
@state, @proposal, votes_for, votes_against, votes_abstain
);
assert(result == ProposalStatus::VotingPeriod(()), 'failed shortcut_balanced');
}

#[test]
#[available_gas(10000000)]
fn balanced() {
let mut state = SimpleQuorumExecutionStrategy::unsafe_new_contract_state();
let quorum = 2;
initializer(ref state, quorum);

let mut proposal = ProposalDefault::default();
let votes_for = quorum;
let votes_against = quorum;
let votes_abstain = 0;
let result = get_proposal_status(
@state, @proposal, votes_for, votes_against, votes_abstain
);
assert(result == ProposalStatus::Rejected(()), 'failed balanced');
}

#[test]
#[available_gas(10000000)]
fn accepted() {
let mut state = SimpleQuorumExecutionStrategy::unsafe_new_contract_state();
let quorum = 2;
initializer(ref state, quorum);

let mut proposal = ProposalDefault::default();
let votes_for = quorum;
let votes_against = quorum - 1;
let votes_abstain = 0;
let result = get_proposal_status(
@state, @proposal, votes_for, votes_against, votes_abstain
);
assert(result == ProposalStatus::Accepted(()), 'failed accepted');
}

#[test]
#[available_gas(10000000)]
fn accepted_with_abstains() {
let mut state = SimpleQuorumExecutionStrategy::unsafe_new_contract_state();
let quorum = 5;
initializer(ref state, quorum);

let mut proposal = ProposalDefault::default();
let votes_for = 2;
let votes_against = 1;
let votes_abstain = 10;
let result = get_proposal_status(
@state, @proposal, votes_for, votes_against, votes_abstain
);
assert(result == ProposalStatus::Accepted(()), 'failed accepted abstains');
}

#[test]
#[available_gas(10000000)]
fn rejected_only_againsts() {
let mut state = SimpleQuorumExecutionStrategy::unsafe_new_contract_state();
let quorum = 0;
initializer(ref state, quorum);

let mut proposal = ProposalDefault::default();
let votes_for = 0;
let votes_against = 1;
let votes_abstain = 0;
let result = get_proposal_status(
@state, @proposal, votes_for, votes_against, votes_abstain
);
assert(result == ProposalStatus::Rejected(()), 'failed rejected');
}

#[test]
#[available_gas(10000000)]
fn quorum_not_reached() {
let mut state = SimpleQuorumExecutionStrategy::unsafe_new_contract_state();
let quorum = 3;
initializer(ref state, quorum);

let mut proposal = ProposalDefault::default();
let votes_for = 2;
let votes_against = 0;
let votes_abstain = 0;
let result = get_proposal_status(
@state, @proposal, votes_for, votes_against, votes_abstain
);
assert(result == ProposalStatus::Rejected(()), 'failed quorum_not_reached');
}
}
23 changes: 17 additions & 6 deletions starknet/src/execution_strategies/vanilla.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,8 @@ mod VanillaExecutionStrategy {
votes_abstain: u256,
payload: Array<felt252>
) {
let mut state: SimpleQuorumExecutionStrategy::ContractState =
SimpleQuorumExecutionStrategy::unsafe_new_contract_state();

let proposal_status = SimpleQuorumExecutionStrategy::get_proposal_status(
@state, @proposal, votes_for, votes_against, votes_abstain
);
let proposal_status = self
.get_proposal_status(proposal, votes_for, votes_against, votes_abstain,);
assert(
(proposal_status == ProposalStatus::Accepted(()))
| (proposal_status == ProposalStatus::VotingPeriodAccepted(())),
Expand All @@ -43,6 +39,21 @@ mod VanillaExecutionStrategy {
self._num_executed.write(self._num_executed.read() + 1);
}

fn get_proposal_status(
self: @ContractState,
proposal: Proposal,
votes_for: u256,
votes_against: u256,
votes_abstain: u256,
) -> ProposalStatus {
let mut state: SimpleQuorumExecutionStrategy::ContractState =
SimpleQuorumExecutionStrategy::unsafe_new_contract_state();

SimpleQuorumExecutionStrategy::get_proposal_status(
@state, @proposal, votes_for, votes_against, votes_abstain,
)
}

fn get_strategy_type(self: @ContractState) -> felt252 {
'SimpleQuorumVanilla'
}
Expand Down
10 changes: 9 additions & 1 deletion starknet/src/interfaces/i_execution_strategy.cairo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use sx::types::Proposal;
use sx::types::{Proposal, ProposalStatus};

#[starknet::interface]
trait IExecutionStrategy<TContractState> {
Expand All @@ -11,5 +11,13 @@ trait IExecutionStrategy<TContractState> {
payload: Array<felt252>
);

fn get_proposal_status(
self: @TContractState,
proposal: Proposal,
votes_for: u256,
votes_against: u256,
votes_abstain: u256,
) -> ProposalStatus;

fn get_strategy_type(self: @TContractState) -> felt252;
}
24 changes: 18 additions & 6 deletions starknet/src/space/space.cairo
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use starknet::{ClassHash, ContractAddress};
use sx::types::{UserAddress, Strategy, Proposal, IndexedStrategy, Choice, UpdateSettingsCalldata};
use sx::types::{
UserAddress, Strategy, Proposal, IndexedStrategy, Choice, UpdateSettingsCalldata, ProposalStatus
};

#[starknet::interface]
trait ISpace<TContractState> {
Expand All @@ -19,8 +21,7 @@ trait ISpace<TContractState> {
// #[view]
// fn vote_registry(proposal_id: u256, voter: ContractAddress) -> bool;
fn proposals(self: @TContractState, proposal_id: u256) -> Proposal;
// #[view]
// fn get_proposal_status(proposal_id: u256) -> u8;
fn get_proposal_status(self: @TContractState, proposal_id: u256) -> ProposalStatus;

// Owner Actions
fn update_settings(ref self: TContractState, input: UpdateSettingsCalldata);
Expand Down Expand Up @@ -85,8 +86,8 @@ mod Space {
},
types::{
UserAddress, Choice, FinalizationStatus, Strategy, IndexedStrategy, Proposal,
PackedProposal, IndexedStrategyTrait, IndexedStrategyImpl, UpdateSettingsCalldata,
NoUpdateTrait, NoUpdateString,
ProposalStatus, PackedProposal, IndexedStrategyTrait, IndexedStrategyImpl,
UpdateSettingsCalldata, NoUpdateTrait, NoUpdateString,
},
utils::{
reinitializable::{Reinitializable}, ReinitializableImpl, bits::BitSetter,
Expand All @@ -98,7 +99,6 @@ mod Space {
external::ownable::Ownable
};


#[storage]
struct Storage {
_min_voting_duration: u32,
Expand Down Expand Up @@ -582,6 +582,18 @@ mod Space {
self._proposals.read(proposal_id)
}

fn get_proposal_status(self: @ContractState, proposal_id: u256) -> ProposalStatus {
let proposal = self._proposals.read(proposal_id);
assert_proposal_exists(@proposal);

let votes_for = self._vote_power.read((proposal_id, Choice::For(())));
let votes_against = self._vote_power.read((proposal_id, Choice::Against(())));
let votes_abstain = self._vote_power.read((proposal_id, Choice::Abstain(())));

IExecutionStrategyDispatcher { contract_address: proposal.execution_strategy }
.get_proposal_status(proposal, votes_for, votes_against, votes_abstain)
}

fn update_settings(ref self: ContractState, input: UpdateSettingsCalldata) {
//TODO: temporary component syntax
let state = Ownable::unsafe_new_contract_state();
Expand Down
Loading

0 comments on commit 10f373d

Please sign in to comment.