diff --git a/.env.example b/.env.example index d1c74398..14c7ff41 100644 --- a/.env.example +++ b/.env.example @@ -1 +1 @@ -NETWORK_URL \ No newline at end of file +NETWORK_URL= diff --git a/package.json b/package.json index 1aad5c3c..f3a2277f 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "license": "MIT", "scripts": { "format:ts": "eslint . --ext .ts --fix", - "test:eth-sig-auth": "jest -c jest.config.ts" + "test:sig-auths": "jest -c jest.config.ts --runInBand" }, "devDependencies": { "@types/jest": "^29.5.3", @@ -23,9 +23,7 @@ "starknet": "^5.14.1", "ts-jest": "^29.1.1", "ts-node": "^10.9.1", - "typescript": "^5.1.6" - }, - "dependencies": { + "typescript": "^5.1.6", "prettier": "^3.0.1" } } diff --git a/starknet/src/authenticators.cairo b/starknet/src/authenticators.cairo index 3b2fa347..4984e198 100644 --- a/starknet/src/authenticators.cairo +++ b/starknet/src/authenticators.cairo @@ -3,3 +3,7 @@ mod vanilla; mod eth_tx; mod eth_sig; + +mod stark_sig; + +mod stark_tx; diff --git a/starknet/src/authenticators/eth_tx.cairo b/starknet/src/authenticators/eth_tx.cairo index b651383a..beb7c552 100644 --- a/starknet/src/authenticators/eth_tx.cairo +++ b/starknet/src/authenticators/eth_tx.cairo @@ -61,7 +61,7 @@ mod EthTxAuthenticator { user_proposal_validation_params: Array, metadata_URI: Array ) { - let mut payload = ArrayTrait::::new(); + let mut payload = array![]; target.serialize(ref payload); PROPOSE_SELECTOR.serialize(ref payload); author.serialize(ref payload); @@ -92,7 +92,7 @@ mod EthTxAuthenticator { user_voting_strategies: Array, metadata_URI: Array ) { - let mut payload = ArrayTrait::::new(); + let mut payload = array![]; target.serialize(ref payload); VOTE_SELECTOR.serialize(ref payload); voter.serialize(ref payload); @@ -124,7 +124,7 @@ mod EthTxAuthenticator { execution_strategy: Strategy, metadata_URI: Array ) { - let mut payload = ArrayTrait::::new(); + let mut payload = array![]; target.serialize(ref payload); UPDATE_PROPOSAL_SELECTOR.serialize(ref payload); author.serialize(ref payload); @@ -163,7 +163,7 @@ mod EthTxAuthenticator { fn consume_commit(ref self: ContractState, hash: felt252, sender_address: EthAddress) { let committer_address = self._commits.read(hash); - assert(committer_address.is_zero(), 'Commit not found'); + assert(!committer_address.is_zero(), 'Commit not found'); assert(committer_address == sender_address, 'Invalid sender address'); // Delete the commit to prevent replay attacks. self._commits.write(hash, Zeroable::zero()); diff --git a/starknet/src/authenticators/stark_sig.cairo b/starknet/src/authenticators/stark_sig.cairo new file mode 100644 index 00000000..b2a59e43 --- /dev/null +++ b/starknet/src/authenticators/stark_sig.cairo @@ -0,0 +1,172 @@ +use starknet::ContractAddress; +use sx::types::{Strategy, IndexedStrategy, Choice}; + +#[starknet::interface] +trait IStarkSigAuthenticator { + fn authenticate_propose( + ref self: TContractState, + signature: Array, + target: ContractAddress, + author: ContractAddress, + execution_strategy: Strategy, + user_proposal_validation_params: Array, + metadata_URI: Array, + salt: felt252, + account_type: felt252 + ); + fn authenticate_vote( + ref self: TContractState, + signature: Array, + target: ContractAddress, + voter: ContractAddress, + proposal_id: u256, + choice: Choice, + user_voting_strategies: Array, + metadata_URI: Array, + account_type: felt252 + ); + fn authenticate_update_proposal( + ref self: TContractState, + signature: Array, + target: ContractAddress, + author: ContractAddress, + proposal_id: u256, + execution_strategy: Strategy, + metadata_URI: Array, + salt: felt252, + account_type: felt252 + ); +} + +#[starknet::contract] +mod StarkSigAuthenticator { + use super::IStarkSigAuthenticator; + use starknet::{ContractAddress, info}; + use core::array::{ArrayTrait, SpanTrait}; + use serde::Serde; + use sx::space::space::{ISpaceDispatcher, ISpaceDispatcherTrait}; + use sx::types::{Strategy, IndexedStrategy, UserAddress, Choice}; + use sx::utils::stark_eip712; + + #[storage] + struct Storage { + _domain_hash: felt252, + _used_salts: LegacyMap::<(ContractAddress, felt252), bool> + } + + #[external(v0)] + impl StarkSigAuthenticator of IStarkSigAuthenticator { + fn authenticate_propose( + ref self: ContractState, + signature: Array, + target: ContractAddress, + author: ContractAddress, + execution_strategy: Strategy, + user_proposal_validation_params: Array, + metadata_URI: Array, + salt: felt252, + account_type: felt252 + ) { + assert(!self._used_salts.read((author, salt)), 'Salt Already Used'); + + stark_eip712::verify_propose_sig( + self._domain_hash.read(), + signature, + target, + author, + @execution_strategy, + user_proposal_validation_params.span(), + metadata_URI.span(), + salt, + account_type + ); + + self._used_salts.write((author, salt), true); + ISpaceDispatcher { + contract_address: target + } + .propose( + UserAddress::Starknet(author), + execution_strategy, + user_proposal_validation_params, + metadata_URI + ); + } + + fn authenticate_vote( + ref self: ContractState, + signature: Array, + target: ContractAddress, + voter: ContractAddress, + proposal_id: u256, + choice: Choice, + user_voting_strategies: Array, + metadata_URI: Array, + account_type: felt252 + ) { + // No need to check salts here, as double voting is prevented by the space itself. + + stark_eip712::verify_vote_sig( + self._domain_hash.read(), + signature, + target, + voter, + proposal_id, + choice, + user_voting_strategies.span(), + metadata_URI.span(), + account_type + ); + + ISpaceDispatcher { + contract_address: target + } + .vote( + UserAddress::Starknet(voter), + proposal_id, + choice, + user_voting_strategies, + metadata_URI + ); + } + + fn authenticate_update_proposal( + ref self: ContractState, + signature: Array, + target: ContractAddress, + author: ContractAddress, + proposal_id: u256, + execution_strategy: Strategy, + metadata_URI: Array, + salt: felt252, + account_type: felt252 + ) { + assert(!self._used_salts.read((author, salt)), 'Salt Already Used'); + + stark_eip712::verify_update_proposal_sig( + self._domain_hash.read(), + signature, + target, + author, + proposal_id, + @execution_strategy, + metadata_URI.span(), + salt, + account_type + ); + + self._used_salts.write((author, salt), true); + ISpaceDispatcher { + contract_address: target + } + .update_proposal( + UserAddress::Starknet(author), proposal_id, execution_strategy, metadata_URI + ); + } + } + #[constructor] + fn constructor(ref self: ContractState, name: felt252, version: felt252) { + // TODO: store domain hash in stark_eip712 component once syntax is live. + self._domain_hash.write(stark_eip712::get_domain_hash(name, version)); + } +} diff --git a/starknet/src/authenticators/stark_tx.cairo b/starknet/src/authenticators/stark_tx.cairo new file mode 100644 index 00000000..3f8f88a8 --- /dev/null +++ b/starknet/src/authenticators/stark_tx.cairo @@ -0,0 +1,108 @@ +use starknet::{ContractAddress}; +use sx::types::{Strategy, IndexedStrategy, Choice}; + +#[starknet::interface] +trait IStarkTxAuthenticator { + fn authenticate_propose( + ref self: TContractState, + space: ContractAddress, + author: ContractAddress, + execution_strategy: Strategy, + user_proposal_validation_params: Array, + metadata_URI: Array + ); + fn authenticate_vote( + ref self: TContractState, + space: ContractAddress, + voter: ContractAddress, + proposal_id: u256, + choice: Choice, + user_voting_strategies: Array, + metadata_URI: Array + ); + fn authenticate_update_proposal( + ref self: TContractState, + space: ContractAddress, + author: ContractAddress, + proposal_id: u256, + execution_strategy: Strategy, + metadata_URI: Array + ); +} + +#[starknet::contract] +mod StarkTxAuthenticator { + use super::IStarkTxAuthenticator; + use starknet::{ContractAddress, info}; + use core::array::ArrayTrait; + use sx::space::space::{ISpaceDispatcher, ISpaceDispatcherTrait}; + use sx::types::{UserAddress, Strategy, IndexedStrategy, Choice}; + + #[storage] + struct Storage {} + + #[external(v0)] + impl StarkTxAuthenticator of IStarkTxAuthenticator { + fn authenticate_propose( + ref self: ContractState, + space: ContractAddress, + author: ContractAddress, + execution_strategy: Strategy, + user_proposal_validation_params: Array, + metadata_URI: Array + ) { + assert(info::get_caller_address() == author, 'Invalid Caller'); + + ISpaceDispatcher { + contract_address: space + } + .propose( + UserAddress::Starknet(author), + execution_strategy, + user_proposal_validation_params, + metadata_URI + ); + } + + fn authenticate_vote( + ref self: ContractState, + space: ContractAddress, + voter: ContractAddress, + proposal_id: u256, + choice: Choice, + user_voting_strategies: Array, + metadata_URI: Array + ) { + assert(info::get_caller_address() == voter, 'Invalid Caller'); + + ISpaceDispatcher { + contract_address: space + } + .vote( + UserAddress::Starknet(voter), + proposal_id, + choice, + user_voting_strategies, + metadata_URI + ); + } + + fn authenticate_update_proposal( + ref self: ContractState, + space: ContractAddress, + author: ContractAddress, + proposal_id: u256, + execution_strategy: Strategy, + metadata_URI: Array + ) { + assert(info::get_caller_address() == author, 'Invalid Caller'); + + ISpaceDispatcher { + contract_address: space + } + .update_proposal( + UserAddress::Starknet(author), proposal_id, execution_strategy, metadata_URI + ); + } + } +} diff --git a/starknet/src/execution_strategies/eth_relayer.cairo b/starknet/src/execution_strategies/eth_relayer.cairo index 50a4be99..ccf5344a 100644 --- a/starknet/src/execution_strategies/eth_relayer.cairo +++ b/starknet/src/execution_strategies/eth_relayer.cairo @@ -22,7 +22,7 @@ mod EthRelayerExecutionStrategy { // keccak hash of the proposal execution payload let execution_hash = u256 { low: payload[2], high: payload[1] }; - let mut message_payload = ArrayTrait::::new(); + let mut message_payload = array![]; space.serialize(mut message_payload); proposal.serialize(mut message_payload); votes_for.serialize(mut message_payload); diff --git a/starknet/src/execution_strategies/simple_quorum.cairo b/starknet/src/execution_strategies/simple_quorum.cairo index ccdbda9d..1053ec2d 100644 --- a/starknet/src/execution_strategies/simple_quorum.cairo +++ b/starknet/src/execution_strategies/simple_quorum.cairo @@ -30,16 +30,16 @@ mod SimpleQuorumExecutionStrategy { let accepted = _quorum_reached(self._quorum.read(), votes_for, votes_against, votes_abstain) & _supported(votes_for, votes_against); - let block_number = info::get_block_number().try_into().unwrap(); + let timestamp = info::get_block_timestamp().try_into().unwrap(); if *proposal.finalization_status == FinalizationStatus::Cancelled(()) { ProposalStatus::Cancelled(()) } else if *proposal.finalization_status == FinalizationStatus::Executed(()) { ProposalStatus::Executed(()) - } else if block_number < *proposal.start_block_number { + } else if timestamp < *proposal.start_timestamp { ProposalStatus::VotingDelay(()) - } else if block_number < *proposal.min_end_block_number { + } else if timestamp < *proposal.min_end_timestamp { ProposalStatus::VotingPeriod(()) - } else if block_number < *proposal.max_end_block_number { + } else if timestamp < *proposal.max_end_timestamp { if accepted { ProposalStatus::VotingPeriodAccepted(()) } else { diff --git a/starknet/src/interfaces.cairo b/starknet/src/interfaces.cairo index 010d25f7..3b37529f 100644 --- a/starknet/src/interfaces.cairo +++ b/starknet/src/interfaces.cairo @@ -1,6 +1,7 @@ mod i_voting_strategy; mod i_execution_strategy; mod i_proposal_validation_strategy; +mod i_account; use i_voting_strategy::{IVotingStrategy, IVotingStrategyDispatcher, IVotingStrategyDispatcherTrait}; use i_execution_strategy::{ @@ -10,3 +11,7 @@ use i_proposal_validation_strategy::{ IProposalValidationStrategy, IProposalValidationStrategyDispatcher, IProposalValidationStrategyDispatcherTrait }; +use i_account::{ + AccountABI, AccountABIDispatcher, AccountABIDispatcherTrait, AccountCamelABI, + AccountCamelABIDispatcher, AccountCamelABIDispatcherTrait +}; diff --git a/starknet/src/interfaces/i_account.cairo b/starknet/src/interfaces/i_account.cairo new file mode 100644 index 00000000..21b94f82 --- /dev/null +++ b/starknet/src/interfaces/i_account.cairo @@ -0,0 +1,34 @@ +use array::ArrayTrait; +use array::SpanTrait; +use starknet::account::Call; +use starknet::ContractAddress; + +// Interfaces from OZ: https://github.com/OpenZeppelin/cairo-contracts/blob/cairo-2/src/account/interface.cairo +#[starknet::interface] +trait AccountABI { + fn __execute__(self: @TState, calls: Array) -> Array>; + fn __validate__(self: @TState, calls: Array) -> felt252; + fn __validate_declare__(self: @TState, class_hash: felt252) -> felt252; + fn __validate_deploy__( + self: @TState, class_hash: felt252, contract_address_salt: felt252, _public_key: felt252 + ) -> felt252; + fn set_public_key(ref self: TState, new_public_key: felt252); + fn get_public_key(self: @TState) -> felt252; + fn is_valid_signature(self: @TState, hash: felt252, signature: Array) -> felt252; + fn supports_interface(self: @TState, interface_id: felt252) -> bool; +} + +// Entry points case-convention is enforced by the protocol +#[starknet::interface] +trait AccountCamelABI { + fn __execute__(self: @TState, calls: Array) -> Array>; + fn __validate__(self: @TState, calls: Array) -> felt252; + fn __validate_declare__(self: @TState, classHash: felt252) -> felt252; + fn __validate_deploy__( + self: @TState, classHash: felt252, contractAddressSalt: felt252, _publicKey: felt252 + ) -> felt252; + fn setPublicKey(ref self: TState, newPublicKey: felt252); + fn getPublicKey(self: @TState) -> felt252; + fn isValidSignature(self: @TState, hash: felt252, signature: Array) -> felt252; + fn supportsInterface(self: @TState, interfaceId: felt252) -> bool; +} diff --git a/starknet/src/interfaces/i_voting_strategy.cairo b/starknet/src/interfaces/i_voting_strategy.cairo index 220a09c3..fbdc3477 100644 --- a/starknet/src/interfaces/i_voting_strategy.cairo +++ b/starknet/src/interfaces/i_voting_strategy.cairo @@ -5,7 +5,7 @@ use sx::types::UserAddress; trait IVotingStrategy { fn get_voting_power( self: @TContractState, - block_number: u32, + timestamp: u32, voter: UserAddress, params: Array, user_params: Array, diff --git a/starknet/src/space/space.cairo b/starknet/src/space/space.cairo index 12e0a731..fd2f18a3 100644 --- a/starknet/src/space/space.cairo +++ b/starknet/src/space/space.cairo @@ -203,11 +203,11 @@ mod Space { ); assert(is_valid, 'Proposal is not valid'); - // The snapshot block number is the start of the voting period - let start_block_number = info::get_block_number().try_into().unwrap() + // The snapshot block timestamp is the start of the voting period + let start_timestamp = info::get_block_timestamp().try_into().unwrap() + self._voting_delay.read(); - let min_end_block_number = start_block_number + self._min_voting_duration.read(); - let max_end_block_number = start_block_number + self._max_voting_duration.read(); + let min_end_timestamp = start_timestamp + self._min_voting_duration.read(); + let max_end_timestamp = start_timestamp + self._max_voting_duration.read(); // TODO: we use a felt252 for the hash despite felts being discouraged // a new field would just replace the hash. Might be worth casting to a Uint256 though? @@ -216,9 +216,9 @@ mod Space { ); let proposal = Proposal { - start_block_number: start_block_number, - min_end_block_number: min_end_block_number, - max_end_block_number: max_end_block_number, + start_timestamp: start_timestamp, + min_end_timestamp: min_end_timestamp, + max_end_timestamp: max_end_timestamp, execution_payload_hash: execution_payload_hash, execution_strategy: execution_strategy.address, author: author, @@ -230,7 +230,7 @@ mod Space { // TODO: Lots of copying, maybe figure out how to pass snapshots to events/storage writers. self._proposals.write(proposal_id, proposal); - self._next_proposal_id.write(proposal_id + u256 { low: 1_u128, high: 0_u128 }); + self._next_proposal_id.write(proposal_id + 1_u256); ProposalCreated( proposal_id, author, snap_proposal, @execution_strategy.params, @metadata_URI @@ -249,10 +249,10 @@ mod Space { let proposal = self._proposals.read(proposal_id); assert_proposal_exists(@proposal); - let block_number = info::get_block_number().try_into().unwrap(); + let timestamp = info::get_block_timestamp().try_into().unwrap(); - assert(block_number < proposal.max_end_block_number, 'Voting period has ended'); - assert(block_number >= proposal.start_block_number, 'Voting period has not started'); + assert(timestamp < proposal.max_end_timestamp, 'Voting period has ended'); + assert(timestamp >= proposal.start_timestamp, 'Voting period has not started'); assert( proposal.finalization_status == FinalizationStatus::Pending(()), 'Proposal has been finalized' @@ -264,12 +264,12 @@ mod Space { let voting_power = _get_cumulative_power( @self, voter, - proposal.start_block_number, + proposal.start_timestamp, user_voting_strategies, proposal.active_voting_strategies ); - assert(voting_power > u256 { low: 0_u128, high: 0_u128 }, 'User has no voting power'); + assert(voting_power > 0_u256, 'User has no voting power'); self ._vote_power .write( @@ -315,7 +315,7 @@ mod Space { assert_proposal_exists(@proposal); assert(proposal.author == author, 'Only Author'); assert( - info::get_block_number() < proposal.start_block_number.into(), + info::get_block_timestamp() < proposal.start_timestamp.into(), 'Voting period started' ); @@ -510,7 +510,7 @@ mod Space { _set_proposal_validation_strategy(ref self, _proposal_validation_strategy.clone()); _add_voting_strategies(ref self, _voting_strategies.clone()); _add_authenticators(ref self, _authenticators.clone()); - self._next_proposal_id.write(u256 { low: 1_u128, high: 0_u128 }); + self._next_proposal_id.write(1_u256); SpaceCreated( info::get_contract_address(), _owner, @@ -537,18 +537,18 @@ mod Space { } fn assert_proposal_exists(proposal: @Proposal) { - assert(!(*proposal.start_block_number).is_zero(), 'Proposal does not exist'); + assert(!(*proposal.start_timestamp).is_zero(), 'Proposal does not exist'); } fn _get_cumulative_power( self: @ContractState, voter: UserAddress, - block_number: u32, + timestamp: u32, user_strategies: Array, allowed_strategies: u256 ) -> u256 { user_strategies.assert_no_duplicate_indices(); - let mut total_voting_power = u256 { low: 0_u128, high: 0_u128 }; + let mut total_voting_power = 0_u256; let mut i = 0_usize; loop { if i >= user_strategies.len() { @@ -561,7 +561,7 @@ mod Space { contract_address: strategy.address } .get_voting_power( - block_number, voter, strategy.params, user_strategies.at(i).params.clone() + timestamp, voter, strategy.params, user_strategies.at(i).params.clone() ); i += 1; }; diff --git a/starknet/src/tests.cairo b/starknet/src/tests.cairo index 996a672c..f8471302 100644 --- a/starknet/src/tests.cairo +++ b/starknet/src/tests.cairo @@ -2,6 +2,7 @@ mod test_merkle_whitelist; mod test_factory; mod test_space; mod test_upgrade; +mod test_stark_tx_auth; mod mocks; mod setup; diff --git a/starknet/src/tests/setup/setup.cairo b/starknet/src/tests/setup/setup.cairo index 4ff1cb0c..dd7bccd5 100644 --- a/starknet/src/tests/setup/setup.cairo +++ b/starknet/src/tests/setup/setup.cairo @@ -44,10 +44,7 @@ mod setup { // Deploy Vanilla Authenticator let (vanilla_authenticator_address, _) = deploy_syscall( - VanillaAuthenticator::TEST_CLASS_HASH.try_into().unwrap(), - 0, - array::ArrayTrait::::new().span(), - false + VanillaAuthenticator::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false ) .unwrap(); let mut authenticators = ArrayTrait::::new(); @@ -57,7 +54,7 @@ mod setup { let (vanilla_proposal_validation_address, _) = deploy_syscall( VanillaProposalValidationStrategy::TEST_CLASS_HASH.try_into().unwrap(), 0, - array::ArrayTrait::::new().span(), + array![].span(), false ) .unwrap(); @@ -67,22 +64,15 @@ mod setup { // Deploy Vanilla Voting Strategy let (vanilla_voting_strategy_address, _) = deploy_syscall( - VanillaVotingStrategy::TEST_CLASS_HASH.try_into().unwrap(), - 0, - array::ArrayTrait::::new().span(), - false + VanillaVotingStrategy::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false ) .unwrap(); let mut voting_strategies = ArrayTrait::::new(); voting_strategies - .append( - Strategy { - address: vanilla_voting_strategy_address, params: ArrayTrait::::new() - } - ); + .append(Strategy { address: vanilla_voting_strategy_address, params: array![] }); // Deploy Vanilla Execution Strategy - let mut constructor_calldata = ArrayTrait::::new(); + let mut constructor_calldata = array![]; quorum.serialize(ref constructor_calldata); let (vanilla_execution_strategy_address, _) = deploy_syscall( VanillaExecutionStrategy::TEST_CLASS_HASH.try_into().unwrap(), @@ -116,7 +106,7 @@ mod setup { authenticators: @Array ) -> Array { // Using empty arrays for all the metadata fields - let mut constructor_calldata = array::ArrayTrait::::new(); + let mut constructor_calldata = array![]; constructor_calldata.append((*owner).into()); constructor_calldata.append((*max_voting_duration).into()); constructor_calldata.append((*min_voting_duration).into()); @@ -137,10 +127,7 @@ mod setup { let contract_address_salt = 0; let (factory_address, _) = deploy_syscall( - Factory::TEST_CLASS_HASH.try_into().unwrap(), - 0, - ArrayTrait::::new().span(), - false + Factory::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false ) .unwrap(); diff --git a/starknet/src/tests/test_factory.cairo b/starknet/src/tests/test_factory.cairo index 35ed60d6..d6dd87d6 100644 --- a/starknet/src/tests/test_factory.cairo +++ b/starknet/src/tests/test_factory.cairo @@ -26,7 +26,7 @@ mod tests { #[test] #[available_gas(10000000000)] fn test_deploy_reuse_salt() { - let mut constructor_calldata = ArrayTrait::::new(); + let mut constructor_calldata = array![]; let (factory_address, _) = deploy_syscall( Factory::TEST_CLASS_HASH.try_into().unwrap(), 0, constructor_calldata.span(), false diff --git a/starknet/src/tests/test_merkle_whitelist.cairo b/starknet/src/tests/test_merkle_whitelist.cairo index a7124049..559cd639 100644 --- a/starknet/src/tests/test_merkle_whitelist.cairo +++ b/starknet/src/tests/test_merkle_whitelist.cairo @@ -13,7 +13,7 @@ mod merkle_utils { impl SpanIntoArray, impl TDrop: Drop> of Into, Array> { fn into(self: Span) -> Array { let mut self = self; - let mut output = ArrayTrait::::new(); + let mut output = array![]; loop { match self.pop_front() { Option::Some(val) => output.append(val.clone()), @@ -28,7 +28,7 @@ mod merkle_utils { // Generates the proof for the given `index` in the `merkle_data`. fn generate_proof(mut merkle_data: Span, mut index: usize) -> Array { - let mut proof = ArrayTrait::new(); + let mut proof = array![]; loop { if merkle_data.len() == 1 { @@ -82,7 +82,7 @@ mod merkle_utils { } fn get_next_level(mut merkle_data: Span) -> Array { - let mut next_level = ArrayTrait::::new(); + let mut next_level = array![]; loop { match merkle_data.pop_front() { Option::Some(a) => { @@ -114,7 +114,7 @@ mod merkle_utils { // The `merkle_data` corresponds to the hashes leaves of the members. fn generate_merkle_data(members: Span) -> Array { let mut members_ = members; - let mut output = ArrayTrait::::new(); + let mut output = array![]; loop { match members_.pop_front() { Option::Some(leaf) => { @@ -132,7 +132,7 @@ mod merkle_utils { // address 1, 2, 3, ... // Even members will be Ethereum addresses and odd members will be Starknet addresses. fn generate_n_members(n: usize) -> Array { - let mut members = ArrayTrait::::new(); + let mut members = array![]; let mut i = 1_usize; loop { if i >= n + 1 { @@ -251,7 +251,7 @@ mod assert_valid_proof { let leaf = Leaf { address: UserAddress::Starknet(contract_address_const::<0>()), voting_power: 0 }; - let proof = ArrayTrait::new(); + let proof = array![]; assert_valid_proof(root, leaf, proof.span()); } @@ -259,37 +259,19 @@ mod assert_valid_proof { #[available_gas(10000000)] #[should_panic(expected: ('Merkle: Invalid proof', ))] fn invalid_extra_node() { - let mut members = ArrayTrait::new(); - members - .append( - Leaf { - address: UserAddress::Starknet(contract_address_const::<5>()), voting_power: 5 - } - ); - members - .append( - Leaf { - address: UserAddress::Starknet(contract_address_const::<4>()), voting_power: 4 - } - ); - members - .append( - Leaf { - address: UserAddress::Starknet(contract_address_const::<3>()), voting_power: 3 - } - ); - members - .append( - Leaf { - address: UserAddress::Starknet(contract_address_const::<2>()), voting_power: 2 - } - ); - members - .append( - Leaf { - address: UserAddress::Starknet(contract_address_const::<1>()), voting_power: 1 - } - ); + let mut members = array![ + Leaf { + address: UserAddress::Starknet(contract_address_const::<5>()), voting_power: 5 + }, Leaf { + address: UserAddress::Starknet(contract_address_const::<4>()), voting_power: 4 + }, Leaf { + address: UserAddress::Starknet(contract_address_const::<3>()), voting_power: 3 + }, Leaf { + address: UserAddress::Starknet(contract_address_const::<2>()), voting_power: 2 + }, Leaf { + address: UserAddress::Starknet(contract_address_const::<1>()), voting_power: 1 + }, + ]; let merkle_data = generate_merkle_data(members.span()); let root = generate_merkle_root(merkle_data.span()); @@ -304,37 +286,19 @@ mod assert_valid_proof { #[available_gas(10000000)] #[should_panic(expected: ('Merkle: Invalid proof', ))] fn invalid_proof() { - let mut members = ArrayTrait::new(); - members - .append( - Leaf { - address: UserAddress::Starknet(contract_address_const::<5>()), voting_power: 5 - } - ); - members - .append( - Leaf { - address: UserAddress::Starknet(contract_address_const::<4>()), voting_power: 4 - } - ); - members - .append( - Leaf { - address: UserAddress::Starknet(contract_address_const::<3>()), voting_power: 3 - } - ); - members - .append( - Leaf { - address: UserAddress::Starknet(contract_address_const::<2>()), voting_power: 2 - } - ); - members - .append( - Leaf { - address: UserAddress::Starknet(contract_address_const::<1>()), voting_power: 1 - } - ); + let mut members = array![ + Leaf { + address: UserAddress::Starknet(contract_address_const::<5>()), voting_power: 5 + }, Leaf { + address: UserAddress::Starknet(contract_address_const::<4>()), voting_power: 4 + }, Leaf { + address: UserAddress::Starknet(contract_address_const::<3>()), voting_power: 3 + }, Leaf { + address: UserAddress::Starknet(contract_address_const::<2>()), voting_power: 2 + }, Leaf { + address: UserAddress::Starknet(contract_address_const::<1>()), voting_power: 1 + }, + ]; let merkle_data = generate_merkle_data(members.span()); let root = generate_merkle_root(merkle_data.span()); @@ -373,7 +337,7 @@ mod merkle_whitelist_voting_power { let (contract, _) = deploy_syscall( MerkleWhitelistVotingStrategy::TEST_CLASS_HASH.try_into().unwrap(), 0, - array::ArrayTrait::::new().span(), + array![].span(), false, ) .unwrap(); @@ -387,10 +351,10 @@ mod merkle_whitelist_voting_power { let root = generate_merkle_root(merkle_data.span()); let proof = generate_proof(merkle_data.span(), index); - let mut params = ArrayTrait::::new(); + let mut params = array![]; root.serialize(ref params); - let mut user_params = ArrayTrait::::new(); + let mut user_params = array![]; leaf.serialize(ref user_params); proof.serialize(ref user_params); @@ -406,7 +370,7 @@ mod merkle_whitelist_voting_power { let (contract, _) = deploy_syscall( MerkleWhitelistVotingStrategy::TEST_CLASS_HASH.try_into().unwrap(), 0, - array::ArrayTrait::::new().span(), + array![].span(), false, ) .unwrap(); @@ -420,10 +384,10 @@ mod merkle_whitelist_voting_power { let root = generate_merkle_root(merkle_data.span()); let proof = generate_proof(merkle_data.span(), index); - let mut params = ArrayTrait::::new(); + let mut params = array![]; root.serialize(ref params); - let mut user_params = ArrayTrait::::new(); + let mut user_params = array![]; let fake_leaf = Leaf { address: leaf.address, voting_power: leaf.voting_power + 1, }; // lying about voting power here @@ -442,7 +406,7 @@ mod merkle_whitelist_voting_power { let (contract, _) = deploy_syscall( MerkleWhitelistVotingStrategy::TEST_CLASS_HASH.try_into().unwrap(), 0, - array::ArrayTrait::::new().span(), + array![].span(), false, ) .unwrap(); @@ -456,10 +420,10 @@ mod merkle_whitelist_voting_power { let root = generate_merkle_root(merkle_data.span()); let proof = generate_proof(merkle_data.span(), index); - let mut params = ArrayTrait::::new(); + let mut params = array![]; root.serialize(ref params); - let mut user_params = ArrayTrait::::new(); + let mut user_params = array![]; let fake_leaf = Leaf { address: UserAddress::Starknet(contract_address_const::<0x1337>()), voting_power: leaf.voting_power, diff --git a/starknet/src/tests/test_space.cairo b/starknet/src/tests/test_space.cairo index be177ef8..7ff7556a 100644 --- a/starknet/src/tests/test_space.cairo +++ b/starknet/src/tests/test_space.cairo @@ -45,20 +45,16 @@ mod tests { // Deploy Vanilla Authenticator let (vanilla_authenticator_address, _) = deploy_syscall( - VanillaAuthenticator::TEST_CLASS_HASH.try_into().unwrap(), - 0, - array::ArrayTrait::::new().span(), - false + VanillaAuthenticator::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false ) .unwrap(); - let mut authenticators = ArrayTrait::::new(); - authenticators.append(vanilla_authenticator_address); + let mut authenticators = array![vanilla_authenticator_address]; // Deploy Vanilla Proposal Validation Strategy let (vanilla_proposal_validation_address, _) = deploy_syscall( VanillaProposalValidationStrategy::TEST_CLASS_HASH.try_into().unwrap(), 0, - array::ArrayTrait::::new().span(), + array![].span(), false ) .unwrap(); @@ -68,26 +64,20 @@ mod tests { // Deploy Vanilla Voting Strategy let (vanilla_voting_strategy_address, _) = deploy_syscall( - VanillaVotingStrategy::TEST_CLASS_HASH.try_into().unwrap(), - 0, - array::ArrayTrait::::new().span(), - false + VanillaVotingStrategy::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false ) .unwrap(); - let mut voting_strategies = ArrayTrait::::new(); - voting_strategies - .append( - Strategy { - address: vanilla_voting_strategy_address, params: ArrayTrait::::new() - } - ); + let mut voting_strategies = array![ + Strategy { address: vanilla_voting_strategy_address, params: array![] } + ]; // Deploy Space - let mut constructor_calldata = array::ArrayTrait::::new(); - constructor_calldata.append(owner.into()); - constructor_calldata.append(max_voting_duration.into()); - constructor_calldata.append(min_voting_duration.into()); - constructor_calldata.append(voting_delay.into()); + let mut constructor_calldata = array![ + owner.into(), + max_voting_duration.into(), + min_voting_duration.into(), + voting_delay.into() + ]; vanilla_proposal_validation_strategy.serialize(ref constructor_calldata); ArrayTrait::::new().serialize(ref constructor_calldata); voting_strategies.serialize(ref constructor_calldata); @@ -124,7 +114,7 @@ mod tests { }; let quorum = u256_from_felt252(1); - let mut constructor_calldata = ArrayTrait::::new(); + let mut constructor_calldata = array![]; quorum.serialize(ref constructor_calldata); let (vanilla_execution_strategy_address, _) = deploy_syscall( @@ -138,7 +128,7 @@ mod tests { vanilla_execution_strategy_address ); let author = UserAddress::Starknet(contract_address_const::<0x5678>()); - let mut propose_calldata = array::ArrayTrait::::new(); + let mut propose_calldata = array![]; author.serialize(ref propose_calldata); vanilla_execution_strategy.serialize(ref propose_calldata); ArrayTrait::::new().serialize(ref propose_calldata); @@ -147,17 +137,14 @@ mod tests { // Create Proposal authenticator.authenticate(space.contract_address, PROPOSE_SELECTOR, propose_calldata); - assert( - space.next_proposal_id() == u256 { low: 2_u128, high: 0_u128 }, - 'next_proposal_id should be 2' - ); + assert(space.next_proposal_id() == 2_u256, 'next_proposal_id should be 2'); let proposal = space.proposals(u256_from_felt252(1)); - let block_number = info::get_block_number().try_into().unwrap(); + let timestamp = info::get_block_timestamp().try_into().unwrap(); let expected_proposal = Proposal { - start_block_number: block_number + 1_u32, - min_end_block_number: block_number + 2_u32, - max_end_block_number: block_number + 3_u32, + start_timestamp: timestamp + 1_u32, + 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() ), @@ -169,13 +156,12 @@ mod tests { assert(proposal == expected_proposal, 'proposal state'); // Update Proposal - let mut update_calldata = array::ArrayTrait::::new(); + let mut update_calldata = array![]; author.serialize(ref update_calldata); let proposal_id = u256_from_felt252(1); proposal_id.serialize(ref update_calldata); // Keeping the same execution strategy contract but changing the payload - let mut new_payload = ArrayTrait::::new(); - new_payload.append(1); + let mut new_payload = array![1]; let execution_strategy = Strategy { address: vanilla_execution_strategy.address, params: new_payload }; @@ -185,26 +171,24 @@ mod tests { authenticator .authenticate(space.contract_address, UPDATE_PROPOSAL_SELECTOR, update_calldata); - // Increasing block block_number by 1 to pass voting delay - testing::set_block_number(1_u64); + // Increasing block timestamp by 1 to pass voting delay + testing::set_block_timestamp(1_u64); - let mut vote_calldata = array::ArrayTrait::::new(); + 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 = ArrayTrait::::new(); - user_voting_strategies - .append(IndexedStrategy { index: 0_u8, params: ArrayTrait::::new() }); + 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_number(2_u64); + testing::set_block_timestamp(2_u64); // Execute Proposal space.execute(u256_from_felt252(1), vanilla_execution_strategy.params); @@ -221,7 +205,7 @@ mod tests { }; let quorum = u256_from_felt252(1); - let mut constructor_calldata = ArrayTrait::::new(); + let mut constructor_calldata = array![]; quorum.serialize(ref constructor_calldata); let (vanilla_execution_strategy_address, _) = deploy_syscall( @@ -235,16 +219,13 @@ mod tests { vanilla_execution_strategy_address ); - assert( - space.next_proposal_id() == u256 { low: 1_u128, high: 0_u128 }, - 'next_proposal_id should be 1' - ); + assert(space.next_proposal_id() == 1_u256, 'next_proposal_id should be 1'); // Replace proposal validation strategy with one that always fails let (strategy_address, _) = deploy_syscall( AlwaysFailProposalValidationStrategy::TEST_CLASS_HASH.try_into().unwrap(), 0, - array::ArrayTrait::::new().span(), + array![].span(), false ) .unwrap(); @@ -257,7 +238,7 @@ mod tests { space.update_settings(input); let author = UserAddress::Starknet(contract_address_const::<0x5678>()); - let mut propose_calldata = array::ArrayTrait::::new(); + let mut propose_calldata = array![]; author.serialize(ref propose_calldata); vanilla_execution_strategy.serialize(ref propose_calldata); ArrayTrait::::new().serialize(ref propose_calldata); @@ -282,7 +263,7 @@ mod tests { }; let quorum = u256_from_felt252(1); - let mut constructor_calldata = ArrayTrait::::new(); + let mut constructor_calldata = array![]; quorum.serialize(ref constructor_calldata); let (vanilla_execution_strategy_address, _) = deploy_syscall( @@ -295,7 +276,7 @@ mod tests { let vanilla_execution_strategy = StrategyImpl::from_address( vanilla_execution_strategy_address ); - let mut propose_calldata = array::ArrayTrait::::new(); + let mut propose_calldata = array![]; let author = UserAddress::Starknet(contract_address_const::<0x5678>()); author.serialize(ref propose_calldata); vanilla_execution_strategy.serialize(ref propose_calldata); @@ -306,8 +287,8 @@ mod tests { authenticator.authenticate(space.contract_address, PROPOSE_SELECTOR, propose_calldata); let proposal_id = u256_from_felt252(1); - // Increasing block block_number by 1 to pass voting delay - testing::set_block_number(1_u64); + // Increasing block timestamp by 1 to pass voting delay + testing::set_block_timestamp(1_u64); let proposal = space.proposals(proposal_id); assert(proposal.finalization_status == FinalizationStatus::Pending(()), 'pending'); @@ -320,15 +301,13 @@ mod tests { assert(proposal.finalization_status == FinalizationStatus::Cancelled(()), 'cancelled'); // Try to cast vote on Cancelled Proposal - let mut vote_calldata = array::ArrayTrait::::new(); + let mut vote_calldata = array![]; let voter = UserAddress::Starknet(contract_address_const::<0x8765>()); voter.serialize(ref vote_calldata); proposal_id.serialize(ref vote_calldata); let choice = Choice::For(()); choice.serialize(ref vote_calldata); - let mut user_voting_strategies = ArrayTrait::::new(); - user_voting_strategies - .append(IndexedStrategy { index: 0_u8, params: ArrayTrait::::new() }); + 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); authenticator.authenticate(space.contract_address, VOTE_SELECTOR, vote_calldata); diff --git a/starknet/src/tests/test_stark_tx_auth.cairo b/starknet/src/tests/test_stark_tx_auth.cairo new file mode 100644 index 00000000..96e0800e --- /dev/null +++ b/starknet/src/tests/test_stark_tx_auth.cairo @@ -0,0 +1,259 @@ +#[cfg(test)] +mod tests { + use array::ArrayTrait; + use starknet::{ + ContractAddress, syscalls::deploy_syscall, testing, contract_address_const, info + }; + use traits::{Into, TryInto}; + use result::ResultTrait; + use option::OptionTrait; + use integer::u256_from_felt252; + use clone::Clone; + use serde::{Serde}; + use sx::space::space::{Space, ISpaceDispatcher, ISpaceDispatcherTrait}; + use sx::authenticators::stark_tx::{ + StarkTxAuthenticator, IStarkTxAuthenticatorDispatcher, IStarkTxAuthenticatorDispatcherTrait + }; + use sx::execution_strategies::vanilla::VanillaExecutionStrategy; + use sx::voting_strategies::vanilla::VanillaVotingStrategy; + use sx::proposal_validation_strategies::vanilla::VanillaProposalValidationStrategy; + use sx::tests::setup::setup::setup::{setup, deploy}; + use sx::types::{ + UserAddress, Strategy, IndexedStrategy, Choice, FinalizationStatus, Proposal, + UpdateSettingsCalldataImpl, UpdateSettingsCalldata + }; + use sx::tests::utils::strategy_trait::{StrategyImpl}; + + fn setup_stark_tx_auth( + space: ISpaceDispatcher, owner: ContractAddress + ) -> IStarkTxAuthenticatorDispatcher { + // Deploy Stark Tx Authenticator and whitelist in Space + let (stark_tx_authenticator_address, _) = deploy_syscall( + StarkTxAuthenticator::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false + ) + .unwrap(); + + let authenticator = IStarkTxAuthenticatorDispatcher { + contract_address: stark_tx_authenticator_address, + }; + let mut updateSettingsCalldata: UpdateSettingsCalldata = + UpdateSettingsCalldataImpl::default(); + updateSettingsCalldata.authenticators_to_add = array![stark_tx_authenticator_address]; + testing::set_contract_address(owner); + space.update_settings(updateSettingsCalldata); + + authenticator + } + + #[test] + #[available_gas(10000000000)] + fn test_propose_update_vote() { + let config = setup(); + let (factory, space) = deploy(@config); + let authenticator = setup_stark_tx_auth(space, config.owner); + + 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 = contract_address_const::<0x5678>(); + + // Create Proposal + testing::set_contract_address(author); + authenticator + .authenticate_propose( + space.contract_address, author, vanilla_execution_strategy, array![], array![] + ); + + assert(space.next_proposal_id() == 2_u256, 'next_proposal_id should be 2'); + + // Update Proposal + + let proposal_id = u256_from_felt252(1); + // Keeping the same execution strategy contract but changing the payload + let mut new_payload = array![1]; + let new_execution_strategy = Strategy { + address: vanilla_execution_strategy_address, params: new_payload.clone() + }; + + testing::set_contract_address(author); + authenticator + .authenticate_update_proposal( + space.contract_address, author, proposal_id, new_execution_strategy, array![] + ); + + // Increasing block timestamp by 1 to pass voting delay + testing::set_block_timestamp(1_u64); + + let voter = contract_address_const::<0x8765>(); + let choice = Choice::For(()); + let mut user_voting_strategies = array![IndexedStrategy { index: 0_u8, params: array![] }]; + + // Vote on Proposal + testing::set_contract_address(voter); + authenticator + .authenticate_vote( + space.contract_address, voter, proposal_id, choice, user_voting_strategies, array![] + ); + + testing::set_block_timestamp(2_u64); + + // Execute Proposal + space.execute(u256_from_felt252(1), new_payload); + } + + #[test] + #[available_gas(10000000000)] + #[should_panic(expected: ('Invalid Caller', 'ENTRYPOINT_FAILED'))] + fn test_propose_invalid_caller() { + let config = setup(); + let (factory, space) = deploy(@config); + let authenticator = setup_stark_tx_auth(space, config.owner); + + 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 = contract_address_const::<0x5678>(); + + // Create Proposal not from author account + testing::set_contract_address(config.owner); + authenticator + .authenticate_propose( + space.contract_address, author, vanilla_execution_strategy, array![], array![] + ); + } + + #[test] + #[available_gas(10000000000)] + #[should_panic(expected: ('Invalid Caller', 'ENTRYPOINT_FAILED'))] + fn test_update_proposal_invalid_caller() { + let config = setup(); + let (factory, space) = deploy(@config); + let authenticator = setup_stark_tx_auth(space, config.owner); + + 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 = contract_address_const::<0x5678>(); + + // Create Proposal + testing::set_contract_address(author); + authenticator + .authenticate_propose( + space.contract_address, author, vanilla_execution_strategy, array![], array![] + ); + + assert(space.next_proposal_id() == 2_u256, 'next_proposal_id should be 2'); + + // Update Proposal + + let proposal_id = u256_from_felt252(1); + // Keeping the same execution strategy contract but changing the payload + let mut new_payload = array![1]; + let new_execution_strategy = Strategy { + address: vanilla_execution_strategy_address, params: new_payload.clone() + }; + + // Update proposal not from author account + testing::set_contract_address(config.owner); + authenticator + .authenticate_update_proposal( + space.contract_address, author, proposal_id, new_execution_strategy, array![] + ); + } + + #[test] + #[available_gas(10000000000)] + #[should_panic(expected: ('Invalid Caller', 'ENTRYPOINT_FAILED'))] + fn test_vote_invalid_caller() { + let config = setup(); + let (factory, space) = deploy(@config); + let authenticator = setup_stark_tx_auth(space, config.owner); + + 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 = contract_address_const::<0x5678>(); + + // Create Proposal + testing::set_contract_address(author); + authenticator + .authenticate_propose( + space.contract_address, author, vanilla_execution_strategy, array![], array![] + ); + + assert(space.next_proposal_id() == 2_u256, 'next_proposal_id should be 2'); + + // Update Proposal + + let proposal_id = u256_from_felt252(1); + // Keeping the same execution strategy contract but changing the payload + let mut new_payload = array![1]; + let new_execution_strategy = Strategy { + address: vanilla_execution_strategy_address, params: new_payload.clone() + }; + + testing::set_contract_address(author); + authenticator + .authenticate_update_proposal( + space.contract_address, author, proposal_id, new_execution_strategy, array![] + ); + + // Increasing block timestamp by 1 to pass voting delay + testing::set_block_timestamp(1_u64); + + let voter = contract_address_const::<0x8765>(); + let choice = Choice::For(()); + let mut user_voting_strategies = array![IndexedStrategy { index: 0_u8, params: array![] }]; + + // Vote on Proposal not from voter account + testing::set_contract_address(config.owner); + authenticator + .authenticate_vote( + space.contract_address, voter, proposal_id, choice, user_voting_strategies, array![] + ); + } +} diff --git a/starknet/src/tests/test_upgrade.cairo b/starknet/src/tests/test_upgrade.cairo index ccad2cbd..4cec393b 100644 --- a/starknet/src/tests/test_upgrade.cairo +++ b/starknet/src/tests/test_upgrade.cairo @@ -58,8 +58,8 @@ mod tests { }; let author = UserAddress::Starknet(contract_address_const::<0x7777777777>()); - let params = ArrayTrait::::new(); - let user_params = ArrayTrait::::new(); + let params = array![]; + let user_params = array![]; let res = new_space.validate(author, params, user_params); assert(res == false, 'Strategy did not return false'); } @@ -79,7 +79,7 @@ mod tests { let (execution_contract_address, _) = deploy_syscall( ExecutorExecutionStrategy::TEST_CLASS_HASH.try_into().unwrap(), 0, - ArrayTrait::::new().span(), + array![].span(), false ) .unwrap(); @@ -90,18 +90,18 @@ mod tests { // keccak256("upgrade") & (2**250 - 1) let selector = 0xf2f7c15cbe06c8d94597cd91fd7f3369eae842359235712def5584f8d270cd; - let mut tx_calldata = ArrayTrait::new(); + let mut tx_calldata = array![]; new_implem.serialize(ref tx_calldata); let tx = Transaction { target: space.contract_address, selector, data: tx_calldata, }; - let mut execution_params = ArrayTrait::::new(); + let mut execution_params = array![]; tx.serialize(ref execution_params); let execution_strategy = Strategy { address: execution_contract_address, params: execution_params.clone(), }; - let mut propose_calldata = ArrayTrait::::new(); + let mut propose_calldata = array![]; UserAddress::Starknet(contract_address_const::<0x7676>()).serialize(ref propose_calldata); execution_strategy.serialize(ref propose_calldata); ArrayTrait::::new().serialize(ref propose_calldata); @@ -122,8 +122,8 @@ mod tests { }; let author = UserAddress::Starknet(contract_address_const::<0x7777777777>()); - let params = ArrayTrait::::new(); - let user_params = ArrayTrait::::new(); + let params = array![]; + let user_params = array![]; let res = new_space.validate(author, params, user_params); assert(res == false, 'Strategy did not return false'); } diff --git a/starknet/src/tests/utils/strategy_trait.cairo b/starknet/src/tests/utils/strategy_trait.cairo index 5d68ebc8..2ac75cd0 100644 --- a/starknet/src/tests/utils/strategy_trait.cairo +++ b/starknet/src/tests/utils/strategy_trait.cairo @@ -9,12 +9,10 @@ trait StrategyTrait { impl StrategyImpl of StrategyTrait { fn test_value() -> Strategy { - Strategy { - address: contract_address_const::<0x5c011>(), params: ArrayTrait::::new(), - } + Strategy { address: contract_address_const::<0x5c011>(), params: array![], } } fn from_address(addr: ContractAddress) -> Strategy { - Strategy { address: addr, params: ArrayTrait::::new(), } + Strategy { address: addr, params: array![], } } } diff --git a/starknet/src/types/indexed_strategy.cairo b/starknet/src/types/indexed_strategy.cairo index ff691baa..d1ca8787 100644 --- a/starknet/src/types/indexed_strategy.cairo +++ b/starknet/src/types/indexed_strategy.cairo @@ -20,16 +20,16 @@ impl IndexedStrategyImpl of IndexedStrategyTrait { return (); } - let mut bit_map = u256 { low: 0_u128, high: 0_u128 }; + 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(u256 { low: 2_u128, high: 0_u128 }, *self.at(i).index); + let s = math::pow(2_u256, *self.at(i).index); - assert((bit_map & s) == u256 { low: 1_u128, high: 0_u128 }, 'Duplicate Found'); + assert((bit_map & s) == 1_u256, 'Duplicate Found'); // Update aforementioned bit. bit_map = bit_map | s; i += 1; diff --git a/starknet/src/types/proposal.cairo b/starknet/src/types/proposal.cairo index d2dbfab9..4e8f196d 100644 --- a/starknet/src/types/proposal.cairo +++ b/starknet/src/types/proposal.cairo @@ -5,9 +5,9 @@ use sx::types::{FinalizationStatus, UserAddress}; #[derive(Clone, Drop, Serde, PartialEq, starknet::Store)] struct Proposal { - start_block_number: u32, - min_end_block_number: u32, - max_end_block_number: u32, + start_timestamp: u32, + min_end_timestamp: u32, + max_end_timestamp: u32, execution_payload_hash: felt252, execution_strategy: ContractAddress, author: UserAddress, diff --git a/starknet/src/types/strategy.cairo b/starknet/src/types/strategy.cairo index 79fab588..04fb38a7 100644 --- a/starknet/src/types/strategy.cairo +++ b/starknet/src/types/strategy.cairo @@ -43,7 +43,7 @@ impl StoreFelt252Array of Store> { fn read_at_offset( address_domain: u32, base: StorageBaseAddress, mut offset: u8 ) -> SyscallResult> { - let mut arr: Array = ArrayTrait::new(); + let mut arr = array![]; // Read the stored array's length. If the length is superior to 255, the read will fail. let len: u8 = Store::::read_at_offset(address_domain, base, offset) diff --git a/starknet/src/types/update_settings_calldata.cairo b/starknet/src/types/update_settings_calldata.cairo index 1b3c7b6a..3c6588c7 100644 --- a/starknet/src/types/update_settings_calldata.cairo +++ b/starknet/src/types/update_settings_calldata.cairo @@ -69,7 +69,7 @@ impl NoUpdateStrategy of NoUpdateTrait { fn no_update() -> Strategy { Strategy { address: contract_address_const::<0xf2cda9b13ed04e585461605c0d6e804933ca828111bd94d4e6a96c75e8b048>(), - params: array::ArrayTrait::new(), + params: array![], } } @@ -82,7 +82,7 @@ impl NoUpdateStrategy of NoUpdateTrait { // TODO: find a way for "Strings" impl NoUpdateArray of NoUpdateTrait> { fn no_update() -> Array { - array::ArrayTrait::::new() + array![] } fn should_update(self: @Array) -> bool { diff --git a/starknet/src/utils.cairo b/starknet/src/utils.cairo index d0202501..4c00f3c7 100644 --- a/starknet/src/utils.cairo +++ b/starknet/src/utils.cairo @@ -10,6 +10,8 @@ mod math; mod merkle; +mod struct_hash; + mod single_slot_proof; mod eip712; @@ -17,3 +19,5 @@ mod eip712; mod endian; mod keccak; + +mod stark_eip712; diff --git a/starknet/src/utils/bits.cairo b/starknet/src/utils/bits.cairo index 6b9a7c0b..cdc3d27d 100644 --- a/starknet/src/utils/bits.cairo +++ b/starknet/src/utils/bits.cairo @@ -12,7 +12,7 @@ impl U256BitSetter of BitSetter { /// Sets the bit at the given index to 1. #[inline(always)] fn set_bit(ref self: u256, index: u8, bit: bool) { - let mask = pow(u256 { low: 2_u128, high: 0_u128 }, index); + let mask = pow(2_u256, index); if bit { self = self | mask; } else { @@ -23,7 +23,7 @@ impl U256BitSetter of BitSetter { /// Returns true if the bit at the given index is set to 1. #[inline(always)] fn is_bit_set(self: u256, index: u8) -> bool { - let mask = pow(u256 { low: 2_u128, high: 0_u128 }, index); + let mask = pow(2_u256, index); (self & mask).is_non_zero() } } diff --git a/starknet/src/utils/constants.cairo b/starknet/src/utils/constants.cairo index e473fc9f..59ba47fb 100644 --- a/starknet/src/utils/constants.cairo +++ b/starknet/src/utils/constants.cairo @@ -3,6 +3,8 @@ const VOTE_SELECTOR: felt252 = 0x132bdf85fc8aa10ac3c22f02317f8f53d4b4f52235ed1ea const UPDATE_PROPOSAL_SELECTOR: felt252 = 0x1f93122f646d968b0ce8c1a4986533f8b4ed3f099122381a4f77478a480c2c3; +// ------ Ethereum Signature Constants ------ + const ETHEREUM_PREFIX: u128 = 0x1901; // keccak256("EIP712Domain(uint256 chainId)") @@ -34,3 +36,41 @@ const STRATEGY_TYPEHASH_LOW: u128 = 0x312314462975078b4bdad10feee486d9; // keccak256("IndexedStrategy(uint256 index,uint256[] params)") const INDEXED_STRATEGY_TYPEHASH_HIGH: u128 = 0xf4acb5967e70f3ad896d52230fe743c9; const INDEXED_STRATEGY_TYPEHASH_LOW: u128 = 0x1d011b57ff63174d8f2b064ab6ce9cc6; + +// ------ Stark Signature Constants ------ + +const STARKNET_MESSAGE: felt252 = 'StarkNet Message'; + +// StarknetKeccak('StarkNetDomain(name:felt252,version:felt252,chainId:felt252,verifyingContract:ContractAddress)') +const DOMAIN_TYPEHASH: felt252 = 0xa9974a36dee531bbc36aad5eeab4ade4df5ad388a296bb14d28ad4e9bf2164; + +// StarknetKeccak('Propose(space:ContractAddress,author:ContractAddress,executionStrategy:Strategy, +// userProposalValidationParams:felt*,metadataURI:felt*,salt:felt252)Strategy(address:felt252,params:felt*)') +const PROPOSE_TYPEHASH: felt252 = 0x248246f9067bb8dd7f7661e894ff088ed3e08cb0957df6cf9c9044cc71fffcb; + +// StarknetKeccak('Vote(space:ContractAddress,voter:ContractAddress,proposalId:u256,choice:felt252, +// userVotingStrategies:IndexedStrategy*,metadataURI:felt*)IndexedStrategy(index:felt252,params:felt*) +// u256(low:felt252,high:felt252)') +const VOTE_TYPEHASH: felt252 = 0x3ef46c9599d94309c080fa67cc9f79a94483b2a3ac938a28bba717aca5e1983; + +// StarknetKeccak('UpdateProposal(space:ContractAddress,author:ContractAddress,proposalId:u256,executionStrategy:Strategy, +// metadataURI:felt*,salt:felt252)Strategy(address:felt252,params:felt*)u256(low:felt252,high:felt252)') +const UPDATE_PROPOSAL_TYPEHASH: felt252 = + 0x2dc58602de2862bc8c8dfae763fd5d754f9f4d0b5e6268a403783b7d9164c67; + +// StarknetKeccak('Strategy(address:felt252,params:felt*)') +const STRATEGY_TYPEHASH: felt252 = + 0x39154ec0efadcd0deffdfc2044cf45dd986d260e59c26d69564b50a18f40f6b; + +// StarknetKeccak('IndexedStrategy(index:felt252,params:felt*)') +const INDEXED_STRATEGY_TYPEHASH: felt252 = + 0x1f464f3e668281a899c5f3fc74a009ccd1df05fd0b9331b0460dc3f8054f64c; + +// StarknetKeccak('u256(low:felt252,high:felt252)') +const U256_TYPEHASH: felt252 = 0x1094260a770342332e6a73e9256b901d484a438925316205b4b6ff25df4a97a; + +// ------ ERC165 Interface Ids ------ +// For more information, refer to: https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md + +const ERC165_ACCOUNT_INTERFACE_ID: felt252 = 0xa66bd575; // snake +const ERC165_OLD_ACCOUNT_INTERFACE_ID: felt252 = 0x3943f10f; // camel diff --git a/starknet/src/utils/legacy_hash.cairo b/starknet/src/utils/legacy_hash.cairo index 6d4eacf6..5a12fe1e 100644 --- a/starknet/src/utils/legacy_hash.cairo +++ b/starknet/src/utils/legacy_hash.cairo @@ -1,8 +1,8 @@ use hash::LegacyHash; use traits::Into; +use array::SpanTrait; use starknet::EthAddress; use sx::types::{Choice, UserAddress}; -use array::{ArrayTrait, SpanTrait}; impl LegacyHashChoice of LegacyHash { fn hash(state: felt252, value: Choice) -> felt252 { @@ -17,22 +17,19 @@ impl LegacyHashEthAddress of LegacyHash { } } -impl LegacyHashSpan of LegacyHash> { - fn hash(mut state: felt252, mut value: Span) -> felt252 { - let len = value.len(); +impl LegacyHashSpanFelt252 of LegacyHash> { + fn hash(state: felt252, mut value: Span) -> felt252 { + let mut call_data_state: felt252 = 0; loop { match value.pop_front() { - Option::Some(current) => { - state = LegacyHash::hash(state, *current); + Option::Some(item) => { + call_data_state = LegacyHash::hash(call_data_state, *item); }, - Option::None => { - break; + Option::None(_) => { + break call_data_state; }, }; - }; - LegacyHash::hash( - state, len - ) // append the length to conform to computeHashOnElements in starknet.js + } } } diff --git a/starknet/src/utils/math.cairo b/starknet/src/utils/math.cairo index 535bc3ac..7315a35e 100644 --- a/starknet/src/utils/math.cairo +++ b/starknet/src/utils/math.cairo @@ -16,7 +16,7 @@ impl U64Zeroable of Zeroable { } fn pow(base: u256, mut exp: u8) -> u256 { - let mut res = u256 { low: 1_u128, high: 0_u128 }; + let mut res = 1_u256; loop { if exp == 0 { break res; diff --git a/starknet/src/utils/merkle.cairo b/starknet/src/utils/merkle.cairo index 3132df8f..eac03d73 100644 --- a/starknet/src/utils/merkle.cairo +++ b/starknet/src/utils/merkle.cairo @@ -6,7 +6,7 @@ use sx::types::UserAddress; use clone::Clone; use hash::{LegacyHash}; use debug::PrintTrait; -use sx::utils::legacy_hash::LegacyHashSpan; +use sx::utils::legacy_hash::LegacyHashSpanFelt252; /// Leaf struct for the merkle tree #[derive(Copy, Clone, Drop, Serde)] @@ -21,7 +21,7 @@ trait Hash { impl HashSerde> of Hash { fn hash(self: @T) -> felt252 { - let mut serialized = ArrayTrait::new(); + let mut serialized = array![]; Serde::::serialize(self, ref serialized); let hashed = LegacyHash::hash(0, serialized.span()); hashed diff --git a/starknet/src/utils/single_slot_proof.cairo b/starknet/src/utils/single_slot_proof.cairo index 942e05f7..e152e64e 100644 --- a/starknet/src/utils/single_slot_proof.cairo +++ b/starknet/src/utils/single_slot_proof.cairo @@ -56,16 +56,14 @@ mod SingleSlotProof { #[internal] fn get_mapping_slot_key(slot_index: u256, mapping_key: u256) -> u256 { - let mut encoded_array = ArrayTrait::::new(); - encoded_array.append(mapping_key); - encoded_array.append(slot_index); + let mut encoded_array = array![mapping_key, slot_index]; keccak::keccak_u256s_le_inputs(encoded_array.span()) } #[internal] fn get_storage_slot( self: @ContractState, - block_number: u32, + timestamp: u32, contract_address: felt252, slot_index: u256, mapping_key: u256, @@ -90,7 +88,7 @@ mod SingleSlotProof { contract_address: self._facts_registry.read() } .get_storage_uint( - block_number.into(), + timestamp.into(), contract_address, proofs.slot, proofs.proof_size_bytes, diff --git a/starknet/src/utils/stark_eip712.cairo b/starknet/src/utils/stark_eip712.cairo new file mode 100644 index 00000000..97a8446c --- /dev/null +++ b/starknet/src/utils/stark_eip712.cairo @@ -0,0 +1,182 @@ +use core::starknet::SyscallResultTrait; +use starknet::{ContractAddress, get_tx_info, get_contract_address}; +use array::{ArrayTrait, SpanTrait}; +use traits::Into; +use box::BoxTrait; +use serde::Serde; +use sx::types::{Strategy, IndexedStrategy, Choice}; +use sx::utils::{ + struct_hash::StructHash, + constants::{ + STARKNET_MESSAGE, DOMAIN_TYPEHASH, PROPOSE_TYPEHASH, VOTE_TYPEHASH, + UPDATE_PROPOSAL_TYPEHASH, ERC165_ACCOUNT_INTERFACE_ID, ERC165_OLD_ACCOUNT_INTERFACE_ID + } +}; +use sx::interfaces::{ + AccountABIDispatcher, AccountABIDispatcherTrait, AccountCamelABIDispatcher, + AccountCamelABIDispatcherTrait +}; + +fn verify_propose_sig( + domain_hash: felt252, + signature: Array, + target: ContractAddress, + author: ContractAddress, + execution_strategy: @Strategy, + user_proposal_validation_params: Span, + metadata_URI: Span, + salt: felt252, + account_type: felt252, +) { + let digest: felt252 = get_propose_digest( + domain_hash, + target, + author, + execution_strategy, + user_proposal_validation_params, + metadata_URI, + salt + ); + + verify_signature(digest, signature, author, account_type); +} + +fn verify_vote_sig( + domain_hash: felt252, + signature: Array, + target: ContractAddress, + voter: ContractAddress, + proposal_id: u256, + choice: Choice, + user_voting_strategies: Span, + metadata_URI: Span, + account_type: felt252, +) { + let digest: felt252 = get_vote_digest( + domain_hash, target, voter, proposal_id, choice, user_voting_strategies, metadata_URI + ); + verify_signature(digest, signature, voter, account_type); +} + +fn verify_update_proposal_sig( + domain_hash: felt252, + signature: Array, + target: ContractAddress, + author: ContractAddress, + proposal_id: u256, + execution_strategy: @Strategy, + metadata_URI: Span, + salt: felt252, + account_type: felt252, +) { + let digest: felt252 = get_update_proposal_digest( + domain_hash, target, author, proposal_id, execution_strategy, metadata_URI, salt + ); + verify_signature(digest, signature, author, account_type); +} + +fn get_propose_digest( + domain_hash: felt252, + space: ContractAddress, + author: ContractAddress, + execution_strategy: @Strategy, + user_proposal_validation_params: Span, + metadata_URI: Span, + salt: felt252 +) -> felt252 { + let mut encoded_data = array![]; + PROPOSE_TYPEHASH.serialize(ref encoded_data); + space.serialize(ref encoded_data); + author.serialize(ref encoded_data); + execution_strategy.struct_hash().serialize(ref encoded_data); + user_proposal_validation_params.struct_hash().serialize(ref encoded_data); + metadata_URI.struct_hash().serialize(ref encoded_data); + salt.serialize(ref encoded_data); + hash_typed_data(domain_hash, encoded_data.span().struct_hash(), author) +} + +fn get_vote_digest( + domain_hash: felt252, + space: ContractAddress, + voter: ContractAddress, + proposal_id: u256, + choice: Choice, + user_voting_strategies: Span, + metadata_URI: Span, +) -> felt252 { + let mut encoded_data = array![]; + VOTE_TYPEHASH.serialize(ref encoded_data); + space.serialize(ref encoded_data); + voter.serialize(ref encoded_data); + proposal_id.struct_hash().serialize(ref encoded_data); + choice.serialize(ref encoded_data); + user_voting_strategies.struct_hash().serialize(ref encoded_data); + metadata_URI.struct_hash().serialize(ref encoded_data); + hash_typed_data(domain_hash, encoded_data.span().struct_hash(), voter) +} + +fn get_update_proposal_digest( + domain_hash: felt252, + space: ContractAddress, + author: ContractAddress, + proposal_id: u256, + execution_strategy: @Strategy, + metadata_URI: Span, + salt: felt252 +) -> felt252 { + let mut encoded_data = array![]; + UPDATE_PROPOSAL_TYPEHASH.serialize(ref encoded_data); + space.serialize(ref encoded_data); + author.serialize(ref encoded_data); + proposal_id.struct_hash().serialize(ref encoded_data); + execution_strategy.struct_hash().serialize(ref encoded_data); + metadata_URI.struct_hash().serialize(ref encoded_data); + salt.serialize(ref encoded_data); + hash_typed_data(domain_hash, encoded_data.span().struct_hash(), author) +} + +fn get_domain_hash(name: felt252, version: felt252) -> felt252 { + let mut encoded_data = array![]; + DOMAIN_TYPEHASH.serialize(ref encoded_data); + name.serialize(ref encoded_data); + version.serialize(ref encoded_data); + get_tx_info().unbox().chain_id.serialize(ref encoded_data); + get_contract_address().serialize(ref encoded_data); + encoded_data.span().struct_hash() +} + +fn hash_typed_data( + domain_hash: felt252, message_hash: felt252, signer: ContractAddress +) -> felt252 { + let mut encoded_data = array![]; + STARKNET_MESSAGE.serialize(ref encoded_data); + domain_hash.serialize(ref encoded_data); + signer.serialize(ref encoded_data); + message_hash.serialize(ref encoded_data); + encoded_data.span().struct_hash() +} + +/// Verifies the signature of a message by calling the account contract. +fn verify_signature( + digest: felt252, signature: Array, account: ContractAddress, account_type: felt252 +) { + if account_type == 'snake' { + assert( + AccountCamelABIDispatcher { + contract_address: account + }.supportsInterface(ERC165_ACCOUNT_INTERFACE_ID) == true, + 'Invalid Account' + ); + AccountCamelABIDispatcher { contract_address: account }.isValidSignature(digest, signature); + } else if account_type == 'camel' { + assert( + AccountABIDispatcher { + contract_address: account + }.supports_interface(ERC165_OLD_ACCOUNT_INTERFACE_ID) == true, + 'Invalid Account' + ); + AccountABIDispatcher { contract_address: account }.is_valid_signature(digest, signature); + } else { + panic_with_felt252('Invalid Account Type'); + } +} diff --git a/starknet/src/utils/struct_hash.cairo b/starknet/src/utils/struct_hash.cairo new file mode 100644 index 00000000..bcbabbeb --- /dev/null +++ b/starknet/src/utils/struct_hash.cairo @@ -0,0 +1,69 @@ +use array::{ArrayTrait, SpanTrait}; +use hash::LegacyHash; +use serde::Serde; +use sx::types::{Strategy, IndexedStrategy}; +use sx::utils::{ + constants::{ + STARKNET_MESSAGE, DOMAIN_TYPEHASH, STRATEGY_TYPEHASH, INDEXED_STRATEGY_TYPEHASH, + U256_TYPEHASH, PROPOSE_TYPEHASH, VOTE_TYPEHASH, UPDATE_PROPOSAL_TYPEHASH + } +}; +use sx::utils::legacy_hash::LegacyHashSpanFelt252; + +trait StructHash { + fn struct_hash(self: @T) -> felt252; +} + +impl StructHashSpanFelt252 of StructHash> { + fn struct_hash(self: @Span) -> felt252 { + let mut call_data_state = LegacyHash::hash(0, *self); + call_data_state = LegacyHash::hash(call_data_state, (*self).len()); + call_data_state + } +} + +impl StructHashStrategy of StructHash { + fn struct_hash(self: @Strategy) -> felt252 { + let mut encoded_data = array![]; + STRATEGY_TYPEHASH.serialize(ref encoded_data); + (*self.address).serialize(ref encoded_data); + self.params.span().struct_hash().serialize(ref encoded_data); + encoded_data.span().struct_hash() + } +} + +impl StructHashIndexedStrategy of StructHash { + fn struct_hash(self: @IndexedStrategy) -> felt252 { + let mut encoded_data = array![]; + INDEXED_STRATEGY_TYPEHASH.serialize(ref encoded_data); + (*self.index).serialize(ref encoded_data); + self.params.span().struct_hash().serialize(ref encoded_data); + encoded_data.span().struct_hash() + } +} + +impl StructHashIndexedStrategySpan of StructHash> { + fn struct_hash(self: @Span) -> felt252 { + let mut self_ = *self; + let mut encoded_data = array![]; + loop { + match self_.pop_front() { + Option::Some(item) => { + encoded_data.append(item.struct_hash()); + }, + Option::None(_) => { + break encoded_data.span().struct_hash(); + }, + }; + } + } +} + +impl StructHashU256 of StructHash { + fn struct_hash(self: @u256) -> felt252 { + let mut encoded_data = array![]; + U256_TYPEHASH.serialize(ref encoded_data); + self.serialize(ref encoded_data); + encoded_data.span().struct_hash() + } +} diff --git a/starknet/src/voting_strategies/eth_balance_of.cairo b/starknet/src/voting_strategies/eth_balance_of.cairo index 40f92b71..357f9ad5 100644 --- a/starknet/src/voting_strategies/eth_balance_of.cairo +++ b/starknet/src/voting_strategies/eth_balance_of.cairo @@ -13,7 +13,7 @@ mod EthBalanceOfVotingStrategy { impl EthBalanceOfVotingStrategy of IVotingStrategy { fn get_voting_power( self: @ContractState, - block_number: u32, + timestamp: u32, voter: UserAddress, params: Array, user_params: Array, @@ -30,9 +30,9 @@ mod EthBalanceOfVotingStrategy { let state: SingleSlotProof::ContractState = SingleSlotProof::unsafe_new_contract_state(); - // Get the balance of the voter at the given block number + // Get the balance of the voter at the given block timestamp let balance = SingleSlotProof::get_storage_slot( - @state, block_number, voter.into(), contract_address, slot_index, user_params + @state, timestamp, voter.into(), contract_address, slot_index, user_params ); balance } diff --git a/starknet/src/voting_strategies/merkle_whitelist.cairo b/starknet/src/voting_strategies/merkle_whitelist.cairo index fb4a6f4c..6afeb9b5 100644 --- a/starknet/src/voting_strategies/merkle_whitelist.cairo +++ b/starknet/src/voting_strategies/merkle_whitelist.cairo @@ -17,7 +17,7 @@ mod MerkleWhitelistVotingStrategy { impl MerkleWhitelistImpl of IVotingStrategy { fn get_voting_power( self: @ContractState, - block_number: u32, + timestamp: u32, voter: UserAddress, params: Array, // [root] user_params: Array, // [Serde(leaf), Serde(proofs)] diff --git a/starknet/src/voting_strategies/vanilla.cairo b/starknet/src/voting_strategies/vanilla.cairo index 63435fda..0e880acf 100644 --- a/starknet/src/voting_strategies/vanilla.cairo +++ b/starknet/src/voting_strategies/vanilla.cairo @@ -11,12 +11,12 @@ mod VanillaVotingStrategy { impl VanillaVotingStrategy of IVotingStrategy { fn get_voting_power( self: @ContractState, - block_number: u32, + timestamp: u32, voter: UserAddress, params: Array, user_params: Array, ) -> u256 { - u256 { low: 1_u128, high: 0_u128 } + 1_u256 } } } diff --git a/starknet/tests/eth-sig-auth.test.ts b/starknet/tests/eth-sig-auth.test.ts index 0fd5b00c..613b24fb 100644 --- a/starknet/tests/eth-sig-auth.test.ts +++ b/starknet/tests/eth-sig-auth.test.ts @@ -9,7 +9,7 @@ import { Vote, updateProposalTypes, UpdateProposal, -} from './types'; +} from './eth-sig-types'; dotenv.config(); @@ -96,7 +96,7 @@ describe('Ethereum Signature Authenticator', () => { _owner: 1, _max_voting_duration: 100, _min_voting_duration: 100, - _voting_delay: 1, + _voting_delay: 20, _proposal_validation_strategy: { address: vanillaProposalValidationStrategyAddress, params: [[]], @@ -211,6 +211,18 @@ describe('Ethereum Signature Authenticator', () => { ), constructorCalldata: CallData.compile({}), }); + + await account0.declareAndDeploy({ + contract: json.parse( + fs + .readFileSync('starknet/target/dev/sx_EthSigAuthenticator.sierra.json') + .toString('ascii'), + ), + casm: json.parse( + fs.readFileSync('starknet/target/dev/sx_EthSigAuthenticator.casm.json').toString('ascii'), + ), + constructorCalldata: CallData.compile({}), + }); } // VOTE diff --git a/starknet/tests/types.ts b/starknet/tests/eth-sig-types.ts similarity index 100% rename from starknet/tests/types.ts rename to starknet/tests/eth-sig-types.ts diff --git a/starknet/tests/stark-sig-auth.test.ts b/starknet/tests/stark-sig-auth.test.ts new file mode 100644 index 00000000..eec073bb --- /dev/null +++ b/starknet/tests/stark-sig-auth.test.ts @@ -0,0 +1,256 @@ +import fs from 'fs'; +import dotenv from 'dotenv'; +import { Provider, Account, CallData, typedData, shortString, json } from 'starknet'; +import { + proposeTypes, + voteTypes, + updateProposalTypes, + Propose, + Vote, + UpdateProposal, + StarknetSigProposeCalldata, + StarknetSigVoteCalldata, + StarknetSigUpdateProposalCalldata, +} from './stark-sig-types'; + +dotenv.config(); + +const network = process.env.NETWORK_URL || ''; + +describe('Starknet Signature Authenticator', () => { + const provider = new Provider({ sequencer: { baseUrl: network } }); + // starknet devnet predeployed account 0 with seed 0 + const privateKey_0 = '0xe3e70682c2094cac629f6fbed82c07cd'; + const address0 = '0x7e00d496e324876bbc8531f2d9a82bf154d1a04a50218ee74cdd372f75a551a'; + const account0 = new Account(provider, address0, privateKey_0); + + // change this to 'camel' if the account interface uses camel case + const account0Type = shortString.encodeShortString('snake'); + + let spaceAddress: string; + let vanillaVotingStrategyAddress: string; + let vanillaProposalValidationStrategyAddress: string; + let starkSigAuthAddress: string; + let domain: any; + + beforeAll(async () => { + // Deploy Starknet Signature Authenticator + const starkSigAuthSierra = json.parse( + fs.readFileSync('starknet/target/dev/sx_StarkSigAuthenticator.sierra.json').toString('ascii'), + ); + const starkSigAuthCasm = json.parse( + fs.readFileSync('starknet/target/dev/sx_StarkSigAuthenticator.casm.json').toString('ascii'), + ); + + let deployResponse = await account0.declareAndDeploy({ + contract: starkSigAuthSierra, + casm: starkSigAuthCasm, + constructorCalldata: CallData.compile({ name: 'sx-sn', version: '0.1.0' }), + }); + starkSigAuthAddress = deployResponse.deploy.contract_address; + + // Deploy Vanilla Voting Strategy + const vanillaVotingStrategySierra = json.parse( + fs.readFileSync('starknet/target/dev/sx_VanillaVotingStrategy.sierra.json').toString('ascii'), + ); + const vanillaVotingStrategyCasm = json.parse( + fs.readFileSync('starknet/target/dev/sx_VanillaVotingStrategy.casm.json').toString('ascii'), + ); + + deployResponse = await account0.declareAndDeploy({ + contract: vanillaVotingStrategySierra, + casm: vanillaVotingStrategyCasm, + constructorCalldata: CallData.compile({}), + }); + vanillaVotingStrategyAddress = deployResponse.deploy.contract_address; + + // Deploy Vanilla Proposal Validation Strategy + const vanillaProposalValidationStrategySierra = json.parse( + fs + .readFileSync('starknet/target/dev/sx_VanillaProposalValidationStrategy.sierra.json') + .toString('ascii'), + ); + const vanillaProposalValidationStrategyCasm = json.parse( + fs + .readFileSync('starknet/target/dev/sx_VanillaProposalValidationStrategy.casm.json') + .toString('ascii'), + ); + + deployResponse = await account0.declareAndDeploy({ + contract: vanillaProposalValidationStrategySierra, + casm: vanillaProposalValidationStrategyCasm, + constructorCalldata: CallData.compile({}), + }); + vanillaProposalValidationStrategyAddress = deployResponse.deploy.contract_address; + + // Deploy Space + const spaceSierra = json.parse( + fs.readFileSync('starknet/target/dev/sx_Space.sierra.json').toString('ascii'), + ); + const spaceCasm = json.parse( + fs.readFileSync('starknet/target/dev/sx_Space.casm.json').toString('ascii'), + ); + + deployResponse = await account0.declareAndDeploy({ + contract: spaceSierra, + casm: spaceCasm, + constructorCalldata: CallData.compile({ + _owner: 1, + _max_voting_duration: 100, + _min_voting_duration: 100, + _voting_delay: 15, + _proposal_validation_strategy: { + address: vanillaProposalValidationStrategyAddress, + params: [[]], + }, + _proposal_validation_strategy_metadata_URI: [], + _voting_strategies: [{ address: vanillaVotingStrategyAddress, params: [] }], + _voting_strategies_metadata_URI: [], + _authenticators: [starkSigAuthAddress], + _metadata_URI: [], + _dao_URI: [], + }), + }); + spaceAddress = deployResponse.deploy.contract_address; + + domain = { + name: 'sx-sn', + version: '0.1.0', + chainId: '0x534e5f474f45524c49', // devnet id + verifyingContract: starkSigAuthAddress, + }; + }, 100000); + test('can authenticate a proposal, a vote, and a proposal update', async () => { + // PROPOSE + const proposeMsg: Propose = { + space: spaceAddress, + author: address0, + executionStrategy: { + address: '0x0000000000000000000000000000000000001234', + params: ['0x5', '0x6', '0x7', '0x8'], + }, + userProposalValidationParams: ['0x1', '0x2', '0x3', '0x4'], + metadataURI: ['0x1', '0x2', '0x3', '0x4'], + salt: '0x0', + }; + + const proposeData: typedData.TypedData = { + types: proposeTypes, + primaryType: 'Propose', + domain: domain, + message: proposeMsg as any, + }; + + const proposeSig = (await account0.signMessage(proposeData)) as any; + + const proposeCalldata: StarknetSigProposeCalldata = { + signature: [proposeSig.r, proposeSig.s], + ...proposeMsg, + accountType: account0Type, + }; + + await account0.execute({ + contractAddress: starkSigAuthAddress, + entrypoint: 'authenticate_propose', + calldata: CallData.compile(proposeCalldata as any), + }); + + // UPDATE PROPOSAL + + const updateProposalMsg: UpdateProposal = { + space: spaceAddress, + author: address0, + proposalId: { low: '0x1', high: '0x0' }, + executionStrategy: { + address: '0x0000000000000000000000000000000000005678', + params: ['0x5', '0x6', '0x7', '0x8'], + }, + metadataURI: ['0x1', '0x2', '0x3', '0x4'], + salt: '0x1', + }; + const updateProposalData: typedData.TypedData = { + types: updateProposalTypes, + primaryType: 'UpdateProposal', + domain: domain, + message: updateProposalMsg as any, + }; + + const updateProposalSig = (await account0.signMessage(updateProposalData)) as any; + + const updateProposalCalldata: StarknetSigUpdateProposalCalldata = { + signature: [updateProposalSig.r, updateProposalSig.s], + ...updateProposalMsg, + accountType: account0Type, + }; + + await account0.execute({ + contractAddress: starkSigAuthAddress, + entrypoint: 'authenticate_update_proposal', + calldata: CallData.compile(updateProposalCalldata as any), + }); + + { + // Random Tx just to advance the block number on the devnet so the voting period begins. + + await account0.declareAndDeploy({ + contract: json.parse( + fs + .readFileSync('starknet/target/dev/sx_StarkSigAuthenticator.sierra.json') + .toString('ascii'), + ), + casm: json.parse( + fs + .readFileSync('starknet/target/dev/sx_StarkSigAuthenticator.casm.json') + .toString('ascii'), + ), + constructorCalldata: CallData.compile({ name: 'sx-sn', version: '0.1.0' }), + }); + + await account0.declareAndDeploy({ + contract: json.parse( + fs + .readFileSync('starknet/target/dev/sx_StarkSigAuthenticator.sierra.json') + .toString('ascii'), + ), + casm: json.parse( + fs + .readFileSync('starknet/target/dev/sx_StarkSigAuthenticator.casm.json') + .toString('ascii'), + ), + constructorCalldata: CallData.compile({ name: 'sx-sn', version: '0.1.0' }), + }); + } + + // VOTE + + const voteMsg: Vote = { + space: spaceAddress, + voter: address0, + proposalId: { low: '0x1', high: '0x0' }, + choice: '0x1', + userVotingStrategies: [{ index: '0x0', params: ['0x1', '0x2', '0x3', '0x4'] }], + metadataURI: ['0x1', '0x2', '0x3', '0x4'], + }; + + const voteData: typedData.TypedData = { + types: voteTypes, + primaryType: 'Vote', + domain: domain, + message: voteMsg as any, + }; + + const voteSig = (await account0.signMessage(voteData)) as any; + + const voteCalldata: StarknetSigVoteCalldata = { + signature: [voteSig.r, voteSig.s], + ...voteMsg, + accountType: account0Type, + }; + + await account0.execute({ + contractAddress: starkSigAuthAddress, + entrypoint: 'authenticate_vote', + calldata: CallData.compile(voteCalldata as any), + }); + }, 1000000); +}); diff --git a/starknet/tests/stark-sig-types.ts b/starknet/tests/stark-sig-types.ts new file mode 100644 index 00000000..1d5613b7 --- /dev/null +++ b/starknet/tests/stark-sig-types.ts @@ -0,0 +1,121 @@ +export const domainTypes = { + StarkNetDomain: [ + { name: 'name', type: 'felt252' }, + { name: 'version', type: 'felt252' }, + { name: 'chainId', type: 'felt252' }, + { name: 'verifyingContract', type: 'ContractAddress' }, + ], +}; + +export const sharedTypes = { + Strategy: [ + { name: 'address', type: 'felt252' }, + { name: 'params', type: 'felt*' }, + ], + IndexedStrategy: [ + { name: 'index', type: 'felt252' }, + { name: 'params', type: 'felt*' }, + ], + u256: [ + { name: 'low', type: 'felt252' }, + { name: 'high', type: 'felt252' }, + ], +}; + +export const proposeTypes = { + StarkNetDomain: domainTypes.StarkNetDomain, + Propose: [ + { name: 'space', type: 'ContractAddress' }, + { name: 'author', type: 'ContractAddress' }, + { name: 'executionStrategy', type: 'Strategy' }, + { name: 'userProposalValidationParams', type: 'felt*' }, + { name: 'metadataURI', type: 'felt*' }, + { name: 'salt', type: 'felt252' }, + ], + Strategy: sharedTypes.Strategy, +}; + +export const voteTypes = { + StarkNetDomain: domainTypes.StarkNetDomain, + Vote: [ + { name: 'space', type: 'ContractAddress' }, + { name: 'voter', type: 'ContractAddress' }, + { name: 'proposalId', type: 'u256' }, + { name: 'choice', type: 'felt252' }, + { name: 'userVotingStrategies', type: 'IndexedStrategy*' }, + { name: 'metadataURI', type: 'felt*' }, + ], + IndexedStrategy: sharedTypes.IndexedStrategy, + u256: sharedTypes.u256, +}; + +export const updateProposalTypes = { + StarkNetDomain: domainTypes.StarkNetDomain, + UpdateProposal: [ + { name: 'space', type: 'ContractAddress' }, + { name: 'author', type: 'ContractAddress' }, + { name: 'proposalId', type: 'u256' }, + { name: 'executionStrategy', type: 'Strategy' }, + { name: 'metadataURI', type: 'felt*' }, + { name: 'salt', type: 'felt252' }, + ], + Strategy: sharedTypes.Strategy, + u256: sharedTypes.u256, +}; + +export interface Strategy { + address: string; + params: string[]; +} + +export interface IndexedStrategy { + index: string; + params: string[]; +} + +export interface u256 { + low: string; + high: string; +} + +export interface Propose { + space: string; + author: string; + executionStrategy: Strategy; + userProposalValidationParams: string[]; + metadataURI: string[]; + salt: string; +} + +export interface Vote { + space: string; + voter: string; + proposalId: u256; + choice: string; + userVotingStrategies: IndexedStrategy[]; + metadataURI: string[]; +} + +export interface UpdateProposal { + space: string; + author: string; + proposalId: u256; + executionStrategy: Strategy; + metadataURI: string[]; + salt: string; +} + +export interface StarknetSigProposeCalldata extends Propose { + signature: string[]; + accountType: string; +} + +export interface StarknetSigVoteCalldata extends Vote { + signature: string[]; + accountType: string; +} + +export interface StarknetSigUpdateProposalCalldata extends UpdateProposal { + signature: string[]; + accountType: string; +} diff --git a/yarn.lock b/yarn.lock index d71a58af..9fcc44fc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20,13 +20,12 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.10", "@babel/code-frame@^7.22.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.10.tgz#1c20e612b768fefa75f6e90d6ecb86329247f0a3" - integrity sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" + integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== dependencies: - "@babel/highlight" "^7.22.10" - chalk "^2.4.2" + "@babel/highlight" "^7.22.5" "@babel/compat-data@^7.22.9": version "7.22.9" @@ -34,40 +33,40 @@ integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== "@babel/core@^7.11.6", "@babel/core@^7.12.3": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.10.tgz#aad442c7bcd1582252cb4576747ace35bc122f35" - integrity sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw== + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.9.tgz#bd96492c68822198f33e8a256061da3cf391f58f" + integrity sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.22.10" - "@babel/generator" "^7.22.10" - "@babel/helper-compilation-targets" "^7.22.10" + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.9" + "@babel/helper-compilation-targets" "^7.22.9" "@babel/helper-module-transforms" "^7.22.9" - "@babel/helpers" "^7.22.10" - "@babel/parser" "^7.22.10" + "@babel/helpers" "^7.22.6" + "@babel/parser" "^7.22.7" "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.10" - "@babel/types" "^7.22.10" + "@babel/traverse" "^7.22.8" + "@babel/types" "^7.22.5" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.2" semver "^6.3.1" -"@babel/generator@^7.22.10", "@babel/generator@^7.7.2": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.10.tgz#c92254361f398e160645ac58831069707382b722" - integrity sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A== +"@babel/generator@^7.22.7", "@babel/generator@^7.22.9", "@babel/generator@^7.7.2": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.9.tgz#572ecfa7a31002fa1de2a9d91621fd895da8493d" + integrity sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw== dependencies: - "@babel/types" "^7.22.10" + "@babel/types" "^7.22.5" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/helper-compilation-targets@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz#01d648bbc25dd88f513d862ee0df27b7d4e67024" - integrity sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q== +"@babel/helper-compilation-targets@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz#f9d0a7aaaa7cd32a3f31c9316a69f5a9bcacb892" + integrity sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw== dependencies: "@babel/compat-data" "^7.22.9" "@babel/helper-validator-option" "^7.22.5" @@ -147,28 +146,28 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== -"@babel/helpers@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.10.tgz#ae6005c539dfbcb5cd71fb51bfc8a52ba63bc37a" - integrity sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw== +"@babel/helpers@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.6.tgz#8e61d3395a4f0c5a8060f309fb008200969b5ecd" + integrity sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA== dependencies: "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.10" - "@babel/types" "^7.22.10" + "@babel/traverse" "^7.22.6" + "@babel/types" "^7.22.5" -"@babel/highlight@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.10.tgz#02a3f6d8c1cb4521b2fd0ab0da8f4739936137d7" - integrity sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ== +"@babel/highlight@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" + integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== dependencies: "@babel/helper-validator-identifier" "^7.22.5" - chalk "^2.4.2" + chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.10", "@babel/parser@^7.22.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.10.tgz#e37634f9a12a1716136c44624ef54283cabd3f55" - integrity sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.5", "@babel/parser@^7.22.7": + version "7.22.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae" + integrity sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -277,26 +276,26 @@ "@babel/parser" "^7.22.5" "@babel/types" "^7.22.5" -"@babel/traverse@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.10.tgz#20252acb240e746d27c2e82b4484f199cf8141aa" - integrity sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig== +"@babel/traverse@^7.22.6", "@babel/traverse@^7.22.8": + version "7.22.8" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.8.tgz#4d4451d31bc34efeae01eac222b514a77aa4000e" + integrity sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw== dependencies: - "@babel/code-frame" "^7.22.10" - "@babel/generator" "^7.22.10" + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.7" "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-function-name" "^7.22.5" "@babel/helper-hoist-variables" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.22.10" - "@babel/types" "^7.22.10" + "@babel/parser" "^7.22.7" + "@babel/types" "^7.22.5" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.10", "@babel/types@^7.22.5", "@babel/types@^7.3.3": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.10.tgz#4a9e76446048f2c66982d1a989dd12b8a2d2dc03" - integrity sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg== +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.5", "@babel/types@^7.3.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" + integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA== dependencies: "@babel/helper-string-parser" "^7.22.5" "@babel/helper-validator-identifier" "^7.22.5" @@ -582,7 +581,12 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": +"@jridgewell/resolve-uri@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + +"@jridgewell/resolve-uri@^3.0.3": version "3.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== @@ -592,7 +596,12 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": +"@jridgewell/sourcemap-codec@1.4.14": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/sourcemap-codec@^1.4.10": version "1.4.15" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== @@ -606,12 +615,12 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.19" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" - integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" "@noble/curves@~1.0.0": version "1.0.0" @@ -784,16 +793,21 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA== -"@types/node@*", "@types/node@^20.4.8": - version "20.4.8" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.8.tgz#b5dda19adaa473a9bf0ab5cbd8f30ec7d43f5c85" - integrity sha512-0mHckf6D2DiIAzh8fM8f3HQCvMKDpK94YQ0DSVkfWTG9BZleYIWudw9cJxX8oCk9bM+vAkDyujDV6dmKHbvQpg== +"@types/node@*": + version "20.4.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.6.tgz#b66b66c9bb5d49b199f03399e341c9d6036e9e88" + integrity sha512-q0RkvNgMweWWIvSMDiXhflGUKMdIxBo2M2tYM/0kEGDueQByFzK4KZAgu5YHGFNxziTlppNpTIBcqHQAxlfHdA== "@types/node@18.15.13": version "18.15.13" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== +"@types/node@^20.4.8": + version "20.4.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.8.tgz#b5dda19adaa473a9bf0ab5cbd8f30ec7d43f5c85" + integrity sha512-0mHckf6D2DiIAzh8fM8f3HQCvMKDpK94YQ0DSVkfWTG9BZleYIWudw9cJxX8oCk9bM+vAkDyujDV6dmKHbvQpg== + "@types/semver@^7.5.0": version "7.5.0" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a" @@ -1141,7 +1155,7 @@ caniuse-lite@^1.0.30001517: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001519.tgz#3e7b8b8a7077e78b0eb054d69e6edf5c7df35601" integrity sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg== -chalk@^2.4.2: +chalk@^2.0.0: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1325,9 +1339,9 @@ dotenv@^16.3.1: integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== electron-to-chromium@^1.4.477: - version "1.4.487" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.487.tgz#e2ef8b15f2791bf68fa6f38f2656f1a551d360ae" - integrity sha512-XbCRs/34l31np/p33m+5tdBrdXu9jJkZxSbNxj5I0H1KtV2ZMSB+i/HYqDiRzHaFx2T5EdytjoBRe8QRJE2vQg== + version "1.4.482" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.482.tgz#77c5ed37b93d4dda860e27538e0e2a01d6a19e02" + integrity sha512-h+UqpfmEr1Qkk0zp7ej/jid7CXoq4m4QzW6wNTb0ELJ/BZCpA4wgUylBIMGCe621tnr4l5VmoHjdoSx2lbnNJA== emittery@^0.13.1: version "0.13.1" @@ -1792,10 +1806,10 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== -is-core-module@^2.13.0: - version "2.13.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" - integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== +is-core-module@^2.11.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" + integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== dependencies: has "^1.0.3" @@ -2734,11 +2748,11 @@ resolve.exports@^2.0.0: integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== resolve@^1.20.0: - version "1.22.4" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" - integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== dependencies: - is-core-module "^2.13.0" + is-core-module "^2.11.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0"