diff --git a/starknet/src/interfaces/i_proposal_validation_strategy.cairo b/starknet/src/interfaces/i_proposal_validation_strategy.cairo index d22eb037..527afa40 100644 --- a/starknet/src/interfaces/i_proposal_validation_strategy.cairo +++ b/starknet/src/interfaces/i_proposal_validation_strategy.cairo @@ -6,7 +6,7 @@ trait IProposalValidationStrategy { fn validate( self: @TContractState, author: UserAddress, - params: Array, - user_params: Array + params: Span, + user_params: Span ) -> bool; } diff --git a/starknet/src/interfaces/i_voting_strategy.cairo b/starknet/src/interfaces/i_voting_strategy.cairo index fbdc3477..563bf29f 100644 --- a/starknet/src/interfaces/i_voting_strategy.cairo +++ b/starknet/src/interfaces/i_voting_strategy.cairo @@ -7,7 +7,7 @@ trait IVotingStrategy { self: @TContractState, timestamp: u32, voter: UserAddress, - params: Array, - user_params: Array, + params: Span, + user_params: Span, ) -> u256; } diff --git a/starknet/src/proposal_validation_strategies/proposing_power.cairo b/starknet/src/proposal_validation_strategies/proposing_power.cairo index cdbff952..8e2aa248 100644 --- a/starknet/src/proposal_validation_strategies/proposing_power.cairo +++ b/starknet/src/proposal_validation_strategies/proposing_power.cairo @@ -23,8 +23,8 @@ mod ProposingPowerProposalValidationStrategy { fn validate( self: @ContractState, author: UserAddress, - params: Array, // [proposal_threshold: u256, allowed_strategies: Array] - user_params: Array // [user_strategies: Array] + params: Span, // [proposal_threshold: u256, allowed_strategies: Array] + user_params: Span // [user_strategies: Array] ) -> bool { _validate(author, params, user_params) } diff --git a/starknet/src/proposal_validation_strategies/vanilla.cairo b/starknet/src/proposal_validation_strategies/vanilla.cairo index 54a2fa17..6786687d 100644 --- a/starknet/src/proposal_validation_strategies/vanilla.cairo +++ b/starknet/src/proposal_validation_strategies/vanilla.cairo @@ -12,8 +12,8 @@ mod VanillaProposalValidationStrategy { fn validate( self: @ContractState, author: UserAddress, - params: Array, - user_params: Array + params: Span, + user_params: Span ) -> bool { true } diff --git a/starknet/src/space/space.cairo b/starknet/src/space/space.cairo index 26da1c84..17760b50 100644 --- a/starknet/src/space/space.cairo +++ b/starknet/src/space/space.cairo @@ -297,12 +297,14 @@ mod Space { let mut state = Ownable::unsafe_new_contract_state(); Ownable::initializer(ref state); Ownable::transfer_ownership(ref state, owner); + _set_max_voting_duration( + ref self, max_voting_duration + ); // Need to set max before min, or else `max == 0` and set_min will revert _set_min_voting_duration(ref self, min_voting_duration); - _set_max_voting_duration(ref self, max_voting_duration); _set_voting_delay(ref self, voting_delay); _set_proposal_validation_strategy(ref self, proposal_validation_strategy); - _add_voting_strategies(ref self, voting_strategies); - _add_authenticators(ref self, authenticators); + _add_voting_strategies(ref self, voting_strategies.span()); + _add_authenticators(ref self, authenticators.span()); self._next_proposal_id.write(1_u256); } @@ -323,7 +325,9 @@ mod Space { contract_address: proposal_validation_strategy.address } .validate( - author, proposal_validation_strategy.params, user_proposal_validation_params + author, + proposal_validation_strategy.params.span(), + user_proposal_validation_params.span() ); assert(is_valid, 'Proposal is not valid'); @@ -399,7 +403,7 @@ mod Space { @self, voter, proposal.start_timestamp, - user_voting_strategies, + user_voting_strategies.span(), proposal.active_voting_strategies ); @@ -430,6 +434,15 @@ mod Space { let mut proposal = self._proposals.read(proposal_id); assert_proposal_exists(@proposal); + let recovered_hash = poseidon::poseidon_hash_span(execution_payload.span()); + // Check that payload matches + assert(recovered_hash == proposal.execution_payload_hash, 'Invalid payload hash'); + + // Check that finalization status is not pending + assert( + proposal.finalization_status == FinalizationStatus::Pending(()), 'Already finalized' + ); + IExecutionStrategyDispatcher { contract_address: proposal.execution_strategy } @@ -469,7 +482,7 @@ mod Space { proposal .execution_payload_hash = - poseidon::poseidon_hash_span(execution_strategy.clone().params.span()); + poseidon::poseidon_hash_span(execution_strategy.params.span()); self._proposals.write(proposal_id, proposal); @@ -581,10 +594,29 @@ mod Space { let state = Ownable::unsafe_new_contract_state(); Ownable::assert_only_owner(@state); - // if not NO_UPDATE - if NoUpdateU32::should_update(@input.max_voting_duration) { - _set_max_voting_duration(ref self, input.max_voting_duration); + // Needed because the compiler will go crazy if we try to use `input` directly + let _min_voting_duration = input.min_voting_duration; + let _max_voting_duration = input.max_voting_duration; + + if NoUpdateU32::should_update(@_max_voting_duration) + && NoUpdateU32::should_update(@_min_voting_duration) { + // Check that min and max voting durations are valid + // We don't use the internal `_set_min_voting_duration` and `_set_max_voting_duration` functions because + // it would revert when `_min_voting_duration > max_voting_duration` (when the new `_min` is + // bigger than the current `max`). + assert(_min_voting_duration <= _max_voting_duration, 'Invalid duration'); + + self._min_voting_duration.write(input.min_voting_duration); + self + .emit( + Event::MinVotingDurationUpdated( + MinVotingDurationUpdated { + min_voting_duration: input.min_voting_duration + } + ) + ); + self._max_voting_duration.write(input.max_voting_duration); self .emit( Event::MaxVotingDurationUpdated( @@ -593,11 +625,8 @@ mod Space { } ) ); - } - - if NoUpdateU32::should_update(@input.min_voting_duration) { + } else if NoUpdateU32::should_update(@_min_voting_duration) { _set_min_voting_duration(ref self, input.min_voting_duration); - self .emit( Event::MinVotingDurationUpdated( @@ -606,6 +635,16 @@ mod Space { } ) ); + } else if NoUpdateU32::should_update(@_max_voting_duration) { + _set_max_voting_duration(ref self, input.max_voting_duration); + self + .emit( + Event::MaxVotingDurationUpdated( + MaxVotingDurationUpdated { + max_voting_duration: input.max_voting_duration + } + ) + ); } if NoUpdateU32::should_update(@input.voting_delay) { @@ -632,7 +671,6 @@ mod Space { self.emit(Event::DaoUriUpdated(DaoUriUpdated { dao_URI: input.dao_URI.span() })); } - // if not NO_UPDATE if NoUpdateStrategy::should_update((@input).proposal_validation_strategy) { _set_proposal_validation_strategy( ref self, input.proposal_validation_strategy.clone() @@ -653,7 +691,7 @@ mod Space { } if NoUpdateArray::should_update((@input).authenticators_to_add) { - _add_authenticators(ref self, input.authenticators_to_add.clone()); + _add_authenticators(ref self, input.authenticators_to_add.span()); self .emit( Event::AuthenticatorsAdded( @@ -664,9 +702,8 @@ mod Space { ); } - // if not NO_UPDATE if NoUpdateArray::should_update((@input).authenticators_to_remove) { - _remove_authenticators(ref self, input.authenticators_to_remove.clone()); + _remove_authenticators(ref self, input.authenticators_to_remove.span()); self .emit( Event::AuthenticatorsRemoved( @@ -677,9 +714,8 @@ mod Space { ); } - // if not NO_UPDATE if NoUpdateArray::should_update((@input).voting_strategies_to_add) { - _add_voting_strategies(ref self, input.voting_strategies_to_add.clone()); + _add_voting_strategies(ref self, input.voting_strategies_to_add.span()); self .emit( Event::VotingStrategiesAdded( @@ -693,9 +729,8 @@ mod Space { ); } - // if not NO_UPDATE if NoUpdateArray::should_update((@input).voting_strategies_to_remove) { - _remove_voting_strategies(ref self, input.voting_strategies_to_remove.clone()); + _remove_voting_strategies(ref self, input.voting_strategies_to_remove.span()); self .emit( Event::VotingStrategiesRemoved( @@ -743,35 +778,41 @@ mod Space { self: @ContractState, voter: UserAddress, timestamp: u32, - user_strategies: Array, + mut user_strategies: Span, allowed_strategies: u256 ) -> u256 { user_strategies.assert_no_duplicate_indices(); let mut total_voting_power = 0_u256; - let mut i = 0_usize; loop { - if i >= user_strategies.len() { - break (); - } - let strategy_index = user_strategies.at(i).index; - assert(allowed_strategies.is_bit_set(*strategy_index), 'Invalid strategy index'); - let strategy = self._voting_strategies.read(*strategy_index); - total_voting_power += IVotingStrategyDispatcher { - contract_address: strategy.address - } - .get_voting_power( - timestamp, voter, strategy.params, user_strategies.at(i).params.clone() - ); - i += 1; + match user_strategies.pop_front() { + Option::Some(strategy_index) => { + assert( + allowed_strategies.is_bit_set(*strategy_index.index), + 'Invalid strategy index' + ); + let strategy = self._voting_strategies.read(*strategy_index.index); + total_voting_power += IVotingStrategyDispatcher { + contract_address: strategy.address + } + .get_voting_power( + timestamp, voter, strategy.params.span(), strategy_index.params.span() + ); + }, + Option::None => { + break; + }, + }; }; total_voting_power } fn _set_max_voting_duration(ref self: ContractState, _max_voting_duration: u32) { + assert(_max_voting_duration >= self._min_voting_duration.read(), 'Invalid duration'); self._max_voting_duration.write(_max_voting_duration); } fn _set_min_voting_duration(ref self: ContractState, _min_voting_duration: u32) { + assert(_min_voting_duration <= self._max_voting_duration.read(), 'Invalid duration'); self._min_voting_duration.write(_min_voting_duration); } @@ -785,73 +826,71 @@ mod Space { self._proposal_validation_strategy.write(_proposal_validation_strategy); } - fn _add_voting_strategies(ref self: ContractState, _voting_strategies: Array) { + fn _add_voting_strategies(ref self: ContractState, mut _voting_strategies: Span) { let mut cachedActiveVotingStrategies = self._active_voting_strategies.read(); let mut cachedNextVotingStrategyIndex = self._next_voting_strategy_index.read(); assert( cachedNextVotingStrategyIndex.into() < 256_u32 - _voting_strategies.len(), 'Exceeds Voting Strategy Limit' ); - let mut _voting_strategies_span = _voting_strategies.span(); - let mut i = 0_usize; loop { - if i >= _voting_strategies.len() { - break (); - } - - let strategy = _voting_strategies_span.pop_front().unwrap().clone(); - assert(!strategy.address.is_zero(), 'Invalid voting strategy'); - cachedActiveVotingStrategies.set_bit(cachedNextVotingStrategyIndex, true); - self._voting_strategies.write(cachedNextVotingStrategyIndex, strategy); - cachedNextVotingStrategyIndex += 1_u8; - i += 1; + match _voting_strategies.pop_front() { + Option::Some(strategy) => { + assert(!(*strategy.address).is_zero(), 'Invalid voting strategy'); + cachedActiveVotingStrategies.set_bit(cachedNextVotingStrategyIndex, true); + self._voting_strategies.write(cachedNextVotingStrategyIndex, strategy.clone()); + cachedNextVotingStrategyIndex += 1_u8; + }, + Option::None => { + break; + }, + }; }; self._active_voting_strategies.write(cachedActiveVotingStrategies); self._next_voting_strategy_index.write(cachedNextVotingStrategyIndex); } - fn _remove_voting_strategies(ref self: ContractState, _voting_strategies: Array) { + fn _remove_voting_strategies(ref self: ContractState, mut _voting_strategies: Span) { let mut cachedActiveVotingStrategies = self._active_voting_strategies.read(); - let mut _voting_strategies_span = _voting_strategies.span(); - let mut i = 0_usize; loop { - if i >= _voting_strategies.len() { - break (); - } - - let index = _voting_strategies_span.pop_front().unwrap(); - cachedActiveVotingStrategies.set_bit(*index, false); - i += 1; + match _voting_strategies.pop_front() { + Option::Some(index) => { + cachedActiveVotingStrategies.set_bit(*index, false); + }, + Option::None => { + break; + }, + }; }; - if cachedActiveVotingStrategies == 0 { - panic_with_felt252('No active voting strategy left'); - } + assert(cachedActiveVotingStrategies != 0, 'No active voting strategy left'); self._active_voting_strategies.write(cachedActiveVotingStrategies); } - fn _add_authenticators(ref self: ContractState, _authenticators: Array) { - let mut _authenticators_span = _authenticators.span(); - let mut i = 0_usize; + fn _add_authenticators(ref self: ContractState, mut _authenticators: Span) { loop { - if i >= _authenticators.len() { - break (); - } - self._authenticators.write(*_authenticators_span.pop_front().unwrap(), true); - i += 1; + match _authenticators.pop_front() { + Option::Some(authenticator) => { + self._authenticators.write(*authenticator, true); + }, + Option::None => { + break; + }, + }; } } - fn _remove_authenticators(ref self: ContractState, _authenticators: Array) { - let mut _authenticators_span = _authenticators.span(); - let mut i = 0_usize; + fn _remove_authenticators(ref self: ContractState, mut _authenticators: Span) { loop { - if i >= _authenticators.len() { - break (); - } - self._authenticators.write(*_authenticators_span.pop_front().unwrap(), false); - i += 1; + match _authenticators.pop_front() { + Option::Some(authenticator) => { + self._authenticators.write(*authenticator, false); + }, + Option::None => { + break; + }, + }; } } } diff --git a/starknet/src/tests/mocks/no_voting_power.cairo b/starknet/src/tests/mocks/no_voting_power.cairo index fc4d60ca..b4cd799f 100644 --- a/starknet/src/tests/mocks/no_voting_power.cairo +++ b/starknet/src/tests/mocks/no_voting_power.cairo @@ -13,8 +13,8 @@ mod NoVotingPowerVotingStrategy { self: @ContractState, timestamp: u32, voter: UserAddress, - params: Array, - user_params: Array, + params: Span, + user_params: Span, ) -> u256 { 0 } diff --git a/starknet/src/tests/mocks/proposal_validation_always_fail.cairo b/starknet/src/tests/mocks/proposal_validation_always_fail.cairo index 2d418b35..d0ba185a 100644 --- a/starknet/src/tests/mocks/proposal_validation_always_fail.cairo +++ b/starknet/src/tests/mocks/proposal_validation_always_fail.cairo @@ -12,8 +12,8 @@ mod AlwaysFailProposalValidationStrategy { fn validate( self: @ContractState, author: UserAddress, - params: Array, - user_params: Array + params: Span, + user_params: Span ) -> bool { false } diff --git a/starknet/src/tests/proposal_validation_strategies/proposing_power.cairo b/starknet/src/tests/proposal_validation_strategies/proposing_power.cairo index b587e4a6..007059c8 100644 --- a/starknet/src/tests/proposal_validation_strategies/proposing_power.cairo +++ b/starknet/src/tests/proposal_validation_strategies/proposing_power.cairo @@ -73,7 +73,7 @@ mod tests { let author = UserAddress::Starknet(contract_address_const::<0x123456789>()); // Vanilla should return 1 so it should be fine - let is_validated = contract.validate(author, params, user_params.clone()); + let is_validated = contract.validate(author, params.span(), user_params.span()); assert(is_validated, 'not enough VP'); // Now increase threshold @@ -83,7 +83,7 @@ mod tests { allowed_strategies.serialize(ref params); // Threshold is 2 but VP should be 1 - let is_validated = contract.validate(author, params.clone(), user_params); + let is_validated = contract.validate(author, params.span(), user_params.span()); assert(!is_validated, 'Threshold should not be reached'); // But now if we add the vanilla voting strategy twice then it should be fine @@ -101,7 +101,7 @@ mod tests { let mut user_params = array![]; used_strategies.serialize(ref user_params); - let is_validated = contract.validate(author, params, user_params); + let is_validated = contract.validate(author, params.span(), user_params.span()); assert(is_validated, 'should have 2 VP'); } @@ -174,7 +174,7 @@ mod tests { let mut user_params = array![]; used_strategies.serialize(ref user_params); - let is_validated = contract.validate(author, params.clone(), user_params.clone()); + let is_validated = contract.validate(author, params.span(), user_params.span()); assert(!is_validated, 'should not have enough VP'); // setup for voter2 @@ -187,8 +187,8 @@ mod tests { let mut user_params = array![]; used_strategies.serialize(ref user_params); - let is_validated = contract.validate(author, params.clone(), user_params.clone()); - assert(is_validated, 'should have enough VP'); + let is_validated = contract.validate(author, params.span(), user_params.span()); + assert(is_validated, 'should have enough VP1'); // setup for voter3 let author = leaf3.address; @@ -200,8 +200,8 @@ mod tests { let mut user_params = array![]; used_strategies.serialize(ref user_params); - let is_validated = contract.validate(author, params.clone(), user_params.clone()); - assert(is_validated, 'should have enough VP'); + let is_validated = contract.validate(author, params.span(), user_params.span()); + assert(is_validated, 'should have enough VP2'); // -- Now let's mix merkle and vanilla voting strategies -- @@ -233,8 +233,8 @@ mod tests { let mut user_params = array![]; used_strategies.serialize(ref user_params); - let is_validated = contract.validate(author, params.clone(), user_params.clone()); - assert(is_validated, 'should have enough VP'); + let is_validated = contract.validate(author, params.span(), user_params.span()); + assert(is_validated, 'should have enough VP2'); // and a random voter that doesn't use the whitelist should not have enough VP let author = UserAddress::Starknet(contract_address_const::<0x123456789>()); @@ -242,7 +242,7 @@ mod tests { let mut user_params = array![]; used_strategies.serialize(ref user_params); - let is_validated = contract.validate(author, params.clone(), user_params.clone()); + let is_validated = contract.validate(author, params.span(), user_params.span()); assert(!is_validated, 'should not have enough VP'); } @@ -313,7 +313,7 @@ mod tests { let author = UserAddress::Starknet(owner); - let is_validated = contract.validate(author, params, user_params.clone()); + let is_validated = contract.validate(author, params.span(), user_params.span()); assert(is_validated, 'not enough VP'); // Now increase threshold @@ -322,7 +322,7 @@ mod tests { proposal_threshold.serialize(ref params); allowed_strategies.serialize(ref params); - let is_validated = contract.validate(author, params.clone(), user_params); + let is_validated = contract.validate(author, params.span(), user_params.span()); assert(!is_validated, 'Threshold should not be reached'); } } diff --git a/starknet/src/tests/test_merkle_whitelist.cairo b/starknet/src/tests/test_merkle_whitelist.cairo index 559cd639..38e774fe 100644 --- a/starknet/src/tests/test_merkle_whitelist.cairo +++ b/starknet/src/tests/test_merkle_whitelist.cairo @@ -151,6 +151,7 @@ mod merkle_utils { members } } + #[cfg(test)] mod assert_valid_proof { use sx::tests::setup::setup::setup::{setup, deploy}; @@ -358,7 +359,7 @@ mod merkle_whitelist_voting_power { leaf.serialize(ref user_params); proof.serialize(ref user_params); - voting_strategy.get_voting_power(timestamp, voter, params, user_params); + voting_strategy.get_voting_power(timestamp, voter, params.span(), user_params.span()); } #[test] @@ -394,7 +395,7 @@ mod merkle_whitelist_voting_power { fake_leaf.serialize(ref user_params); proof.serialize(ref user_params); - voting_strategy.get_voting_power(timestamp, voter, params, user_params); + voting_strategy.get_voting_power(timestamp, voter, params.span(), user_params.span()); } #[test] @@ -431,6 +432,6 @@ mod merkle_whitelist_voting_power { fake_leaf.serialize(ref user_params); proof.serialize(ref user_params); - voting_strategy.get_voting_power(timestamp, voter, params, user_params); + voting_strategy.get_voting_power(timestamp, voter, params.span(), user_params.span()); } } diff --git a/starknet/src/tests/test_space.cairo b/starknet/src/tests/test_space.cairo index 7e66afc0..6bae94af 100644 --- a/starknet/src/tests/test_space.cairo +++ b/starknet/src/tests/test_space.cairo @@ -239,7 +239,7 @@ mod tests { min_end_timestamp: timestamp + 2_u32, max_end_timestamp: timestamp + 3_u32, execution_payload_hash: poseidon::poseidon_hash_span( - vanilla_execution_strategy.clone().params.span() + vanilla_execution_strategy.params.span() ), execution_strategy: vanilla_execution_strategy.address, author: author, @@ -256,7 +256,7 @@ mod tests { // Keeping the same execution strategy contract but changing the payload let mut new_payload = array![1]; let execution_strategy = Strategy { - address: vanilla_execution_strategy.address, params: new_payload + address: vanilla_execution_strategy.address, params: new_payload.clone() }; execution_strategy.serialize(ref update_calldata); ArrayTrait::::new().serialize(ref update_calldata); @@ -264,8 +264,7 @@ mod tests { authenticator .authenticate(space.contract_address, UPDATE_PROPOSAL_SELECTOR, update_calldata); - // Increasing block timestamp by 1 to pass voting delay - testing::set_block_timestamp(1_u64); + testing::set_block_timestamp(config.voting_delay); let mut vote_calldata = array![]; let voter = UserAddress::Starknet(contract_address_const::<0x8765>()); @@ -281,10 +280,10 @@ mod tests { // Vote on Proposal authenticator.authenticate(space.contract_address, VOTE_SELECTOR, vote_calldata); - testing::set_block_timestamp(2_u64); + testing::set_block_timestamp(config.voting_delay + config.max_voting_duration); // Execute Proposal - space.execute(u256_from_felt252(1), vanilla_execution_strategy.params); + space.execute(u256_from_felt252(1), new_payload); } #[test] @@ -341,6 +340,68 @@ mod tests { authenticator.authenticate(space.contract_address, PROPOSE_SELECTOR, propose_calldata); } + #[test] + #[available_gas(10000000000)] + #[should_panic(expected: ('Already finalized', 'ENTRYPOINT_FAILED'))] + fn execute_already_finalized() { + let config = setup(); + let (factory, space) = deploy(@config); + + let authenticator = IVanillaAuthenticatorDispatcher { + contract_address: *config.authenticators.at(0), + }; + + let quorum = u256_from_felt252(1); + let mut constructor_calldata = array![]; + quorum.serialize(ref constructor_calldata); + + let (vanilla_execution_strategy_address, _) = deploy_syscall( + VanillaExecutionStrategy::TEST_CLASS_HASH.try_into().unwrap(), + 0, + constructor_calldata.span(), + false + ) + .unwrap(); + let vanilla_execution_strategy = StrategyImpl::from_address( + vanilla_execution_strategy_address + ); + let author = UserAddress::Starknet(contract_address_const::<0x5678>()); + let mut propose_calldata = array![]; + author.serialize(ref propose_calldata); + vanilla_execution_strategy.serialize(ref propose_calldata); + ArrayTrait::::new().serialize(ref propose_calldata); + ArrayTrait::::new().serialize(ref propose_calldata); + + // Create Proposal + authenticator.authenticate(space.contract_address, PROPOSE_SELECTOR, propose_calldata); + + assert(space.next_proposal_id() == 2_u256, 'next_proposal_id should be 2'); + + testing::set_block_timestamp(config.voting_delay); + + let mut vote_calldata = array![]; + let voter = UserAddress::Starknet(contract_address_const::<0x8765>()); + voter.serialize(ref vote_calldata); + let proposal_id = u256_from_felt252(1); + proposal_id.serialize(ref vote_calldata); + let choice = Choice::For(()); + choice.serialize(ref vote_calldata); + let mut user_voting_strategies = array![IndexedStrategy { index: 0_u8, params: array![] }]; + user_voting_strategies.serialize(ref vote_calldata); + ArrayTrait::::new().serialize(ref vote_calldata); + + // Vote on Proposal + authenticator.authenticate(space.contract_address, VOTE_SELECTOR, vote_calldata); + + testing::set_block_timestamp(config.voting_delay + config.max_voting_duration); + + // Execute Proposal + space.execute(u256_from_felt252(1), vanilla_execution_strategy.params.clone()); + + // Execute a second time + space.execute(u256_from_felt252(1), vanilla_execution_strategy.params.clone()); + } + #[test] #[available_gas(10000000000)] #[should_panic( @@ -380,8 +441,7 @@ mod tests { authenticator.authenticate(space.contract_address, PROPOSE_SELECTOR, propose_calldata); let proposal_id = u256_from_felt252(1); - // Increasing block timestamp by 1 to pass voting delay - testing::set_block_timestamp(1_u64); + testing::set_block_timestamp(config.voting_delay); let proposal = space.proposals(proposal_id); assert(proposal.finalization_status == FinalizationStatus::Pending(()), 'pending'); diff --git a/starknet/src/types/indexed_strategy.cairo b/starknet/src/types/indexed_strategy.cairo index e7dbfb59..85c30a28 100644 --- a/starknet/src/types/indexed_strategy.cairo +++ b/starknet/src/types/indexed_strategy.cairo @@ -1,3 +1,4 @@ +use core::array::SpanTrait; use option::OptionTrait; use serde::Serde; use clone::Clone; @@ -11,28 +12,31 @@ struct IndexedStrategy { } trait IndexedStrategyTrait { - fn assert_no_duplicate_indices(self: @Array); + fn assert_no_duplicate_indices(self: Span); } impl IndexedStrategyImpl of IndexedStrategyTrait { - fn assert_no_duplicate_indices(self: @Array) { + fn assert_no_duplicate_indices(self: Span) { + let mut self = self; if self.len() < 2 { return (); } let mut bit_map = 0_u256; - let mut i = 0_usize; loop { - if i >= self.len() { - break (); - } - // Check that bit at index `strats[i].index` is not set. - let s = math::pow(2_u256, *self.at(i).index); + match self.pop_front() { + Option::Some(indexed_strategy) => { + // Check that bit at index `strats[i].index` is not set. + let s = math::pow(2_u256, *indexed_strategy.index); - assert((bit_map & s) != u256 { low: 1_u128, high: 0_u128 }, 'Duplicate Found'); - // Update aforementioned bit. - bit_map = bit_map | s; - i += 1; + assert((bit_map & s) != 1_u256, 'Duplicate Found'); + // Update aforementioned bit. + bit_map = bit_map | s; + }, + Option::None => { + break; + }, + }; }; } } diff --git a/starknet/src/utils/felt_arr_to_uint_arr.cairo b/starknet/src/utils/felt_arr_to_uint_arr.cairo index 7822e0a2..5811c9d1 100644 --- a/starknet/src/utils/felt_arr_to_uint_arr.cairo +++ b/starknet/src/utils/felt_arr_to_uint_arr.cairo @@ -2,15 +2,17 @@ use array::ArrayTrait; use traits::Into; impl Felt252ArrayIntoU256Array of Into, Array> { - fn into(self: Array) -> Array { - let mut arr = ArrayTrait::::new(); - let mut i = 0_usize; + fn into(mut self: Array) -> Array { + let mut arr = array![]; loop { - if i >= self.len() { - break (); - } - arr.append((*self.at(i)).into()); - i += 1; + match self.pop_front() { + Option::Some(el) => { + arr.append(el.into()); + }, + Option::None => { + break; + } + }; }; arr } diff --git a/starknet/src/utils/proposition_power.cairo b/starknet/src/utils/proposition_power.cairo index 4da92af3..f87c2f60 100644 --- a/starknet/src/utils/proposition_power.cairo +++ b/starknet/src/utils/proposition_power.cairo @@ -15,22 +15,25 @@ use clone::Clone; fn _get_cumulative_power( voter: UserAddress, timestamp: u32, - mut user_strategies: Array, - allowed_strategies: Array, + mut user_strategies: Span, + allowed_strategies: Span, ) -> u256 { user_strategies.assert_no_duplicate_indices(); let mut total_voting_power = 0_u256; loop { match user_strategies.pop_front() { Option::Some(indexed_strategy) => { - match allowed_strategies.get(indexed_strategy.index.into()) { + match allowed_strategies.get((*indexed_strategy.index).into()) { Option::Some(strategy) => { let strategy: Strategy = strategy.unbox().clone(); total_voting_power += IVotingStrategyDispatcher { contract_address: strategy.address } .get_voting_power( - timestamp, voter, strategy.params, indexed_strategy.params, + timestamp, + voter, + strategy.params.span(), + indexed_strategy.params.span(), ); }, Option::None => { @@ -47,22 +50,19 @@ fn _get_cumulative_power( fn _validate( author: UserAddress, - params: Array, // [proposal_threshold: u256, allowed_strategies: Array] - user_params: Array // [user_strategies: Array] + mut params: Span, // [proposal_threshold: u256, allowed_strategies: Array] + mut user_params: Span // [user_strategies: Array] ) -> bool { - let mut params_span = params.span(); let (proposal_threshold, allowed_strategies) = Serde::<( u256, Array - )>::deserialize(ref params_span) + )>::deserialize(ref params) .unwrap(); - let mut user_params_span = user_params.span(); - let user_strategies = Serde::>::deserialize(ref user_params_span) - .unwrap(); + let user_strategies = Serde::>::deserialize(ref user_params).unwrap(); let timestamp: u32 = info::get_block_timestamp().try_into().unwrap() - 1; let voting_power = _get_cumulative_power( - author, timestamp, user_strategies, allowed_strategies + author, timestamp, user_strategies.span(), allowed_strategies.span() ); voting_power >= proposal_threshold } diff --git a/starknet/src/utils/single_slot_proof.cairo b/starknet/src/utils/single_slot_proof.cairo index e152e64e..072d3918 100644 --- a/starknet/src/utils/single_slot_proof.cairo +++ b/starknet/src/utils/single_slot_proof.cairo @@ -67,11 +67,10 @@ mod SingleSlotProof { contract_address: felt252, slot_index: u256, mapping_key: u256, - encoded_proofs: Array + mut encoded_proofs: Span ) -> u256 { let slot_key = get_mapping_slot_key(slot_index, mapping_key); - let mut s = encoded_proofs.span(); - let proofs: Proofs = Serde::::deserialize(ref s).unwrap(); + let proofs: Proofs = Serde::::deserialize(ref encoded_proofs).unwrap(); // Check proof corresponds to correct storage slot. assert( diff --git a/starknet/src/voting_strategies/erc20_votes.cairo b/starknet/src/voting_strategies/erc20_votes.cairo index cb3e85af..65e3685f 100644 --- a/starknet/src/voting_strategies/erc20_votes.cairo +++ b/starknet/src/voting_strategies/erc20_votes.cairo @@ -22,15 +22,14 @@ mod ERC20VotesVotingStrategy { self: @ContractState, timestamp: u32, voter: UserAddress, - params: Array, - user_params: Array, + mut params: Span, + user_params: Span, ) -> u256 { // Cast voter address to a Starknet address // Will revert if the address is not a Starknet address let voter = voter.to_starknet_address(); // Get the ERC20 contract address from the params array - let mut params = params.span(); let erc20_contract_address = Serde::::deserialize(ref params).unwrap(); let erc20 = IVotesDispatcher { contract_address: erc20_contract_address, }; diff --git a/starknet/src/voting_strategies/eth_balance_of.cairo b/starknet/src/voting_strategies/eth_balance_of.cairo index e060e0c1..9287fd32 100644 --- a/starknet/src/voting_strategies/eth_balance_of.cairo +++ b/starknet/src/voting_strategies/eth_balance_of.cairo @@ -15,8 +15,8 @@ mod EthBalanceOfVotingStrategy { self: @ContractState, timestamp: u32, voter: UserAddress, - params: Array, - user_params: Array, + params: Span, + user_params: Span, ) -> u256 { // Cast voter address to an Ethereum address // Will revert if the address is not an Ethereum address diff --git a/starknet/src/voting_strategies/merkle_whitelist.cairo b/starknet/src/voting_strategies/merkle_whitelist.cairo index 182608e7..fdad645e 100644 --- a/starknet/src/voting_strategies/merkle_whitelist.cairo +++ b/starknet/src/voting_strategies/merkle_whitelist.cairo @@ -18,10 +18,10 @@ mod MerkleWhitelistVotingStrategy { self: @ContractState, timestamp: u32, voter: UserAddress, - params: Array, // [root: felt252] - user_params: Array, // [leaf: Leaf, proof: Array)] + params: Span, // [root: felt252] + user_params: Span, // [leaf: Leaf, proof: Array] ) -> u256 { - let cache = user_params.span(); // cache + let cache = user_params; // cache let mut leaf_raw = cache.slice(0, LEAF_SIZE); let leaf = Serde::::deserialize(ref leaf_raw).unwrap(); diff --git a/starknet/src/voting_strategies/vanilla.cairo b/starknet/src/voting_strategies/vanilla.cairo index 0e880acf..83af8616 100644 --- a/starknet/src/voting_strategies/vanilla.cairo +++ b/starknet/src/voting_strategies/vanilla.cairo @@ -13,8 +13,8 @@ mod VanillaVotingStrategy { self: @ContractState, timestamp: u32, voter: UserAddress, - params: Array, - user_params: Array, + params: Span, + user_params: Span, ) -> u256 { 1_u256 }