diff --git a/.github/workflows/cairo-ci.yml b/.github/workflows/cairo-ci.yml new file mode 100644 index 000000000..029e40c93 --- /dev/null +++ b/.github/workflows/cairo-ci.yml @@ -0,0 +1,45 @@ +name: cairo-CI + +on: + push: + branches: + - main + pull_request: + branches: + - "*" +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +concurrency: + group: cairo-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + paths-filter: + runs-on: ubuntu-latest + outputs: + protocol_handler: ${{ steps.filter.outputs.protocol_handler }} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + protocol_handler: + - 'cairo/protocol_handler/**' + + tests-unit: + runs-on: ubuntu-latest + needs: paths-filter + if: needs.paths-filter.outputs.protocol_handler == 'true' + steps: + - uses: actions/checkout@v4 + - uses: foundry-rs/setup-snfoundry@v3 + - uses: software-mansion/setup-scarb@v1 + with: + tool-versions: ./cairo/protocol_handler/.tool-versions + + - name: Run tests + run: cd cairo/protocol_handler && scarb test diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 134de54b1..4fda38950 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -73,6 +73,8 @@ lint: - cairo/token - cairo/utils - cairo/kakarot-ssj/crates + - cairo/protocol_handler + - cairo/snforge_utils - linters: [ALL] paths: - logs* diff --git a/cairo/kakarot-ssj/Scarb.lock b/cairo/kakarot-ssj/Scarb.lock index 8a345708d..14508716b 100644 --- a/cairo/kakarot-ssj/Scarb.lock +++ b/cairo/kakarot-ssj/Scarb.lock @@ -59,10 +59,6 @@ dependencies = [ [[package]] name = "snforge_utils" version = "0.1.0" -dependencies = [ - "evm", - "snforge_std", -] [[package]] name = "utils" diff --git a/cairo/kakarot-ssj/crates/contracts/Scarb.toml b/cairo/kakarot-ssj/crates/contracts/Scarb.toml index 1e689c03e..f61afd9f3 100644 --- a/cairo/kakarot-ssj/crates/contracts/Scarb.toml +++ b/cairo/kakarot-ssj/crates/contracts/Scarb.toml @@ -25,7 +25,7 @@ name = "contracts" [dev-dependencies] snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.31.0" } assert_macros = "2.8.2" -snforge_utils = { path = "../snforge_utils" } +snforge_utils = { path = "../../../snforge_utils" } [scripts] test = "snforge test --max-n-steps 4294967295" diff --git a/cairo/kakarot-ssj/crates/evm/Scarb.toml b/cairo/kakarot-ssj/crates/evm/Scarb.toml index 1180c54a4..8ee6e0d06 100644 --- a/cairo/kakarot-ssj/crates/evm/Scarb.toml +++ b/cairo/kakarot-ssj/crates/evm/Scarb.toml @@ -14,7 +14,7 @@ garaga = { git = "https://github.com/keep-starknet-strange/garaga.git" } [dev-dependencies] snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.31.0" } -snforge_utils = { path = "../snforge_utils" } +snforge_utils = { path = "../../../snforge_utils" } assert_macros = "2.8.2" [tool] diff --git a/cairo/protocol_handler/.gitignore b/cairo/protocol_handler/.gitignore new file mode 100644 index 000000000..eb5a316cb --- /dev/null +++ b/cairo/protocol_handler/.gitignore @@ -0,0 +1 @@ +target diff --git a/cairo/protocol_handler/.tool-versions b/cairo/protocol_handler/.tool-versions new file mode 100644 index 000000000..5248f18a0 --- /dev/null +++ b/cairo/protocol_handler/.tool-versions @@ -0,0 +1,2 @@ +scarb 2.8.3 +starknet-foundry 0.31.0 diff --git a/cairo/protocol_handler/Scarb.lock b/cairo/protocol_handler/Scarb.lock new file mode 100644 index 000000000..36b152b8c --- /dev/null +++ b/cairo/protocol_handler/Scarb.lock @@ -0,0 +1,130 @@ +# Code generated by scarb DO NOT EDIT. +version = 1 + +[[package]] +name = "openzeppelin" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_finance", + "openzeppelin_governance", + "openzeppelin_introspection", + "openzeppelin_merkle_tree", + "openzeppelin_presets", + "openzeppelin_security", + "openzeppelin_token", + "openzeppelin_upgrades", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_access" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" +dependencies = [ + "openzeppelin_introspection", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_account" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" +dependencies = [ + "openzeppelin_introspection", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_finance" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" +dependencies = [ + "openzeppelin_access", + "openzeppelin_token", +] + +[[package]] +name = "openzeppelin_governance" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" +dependencies = [ + "openzeppelin_access", + "openzeppelin_introspection", +] + +[[package]] +name = "openzeppelin_introspection" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" + +[[package]] +name = "openzeppelin_merkle_tree" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" + +[[package]] +name = "openzeppelin_presets" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_finance", + "openzeppelin_introspection", + "openzeppelin_token", + "openzeppelin_upgrades", +] + +[[package]] +name = "openzeppelin_security" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" + +[[package]] +name = "openzeppelin_token" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" +dependencies = [ + "openzeppelin_account", + "openzeppelin_governance", + "openzeppelin_introspection", +] + +[[package]] +name = "openzeppelin_upgrades" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" + +[[package]] +name = "openzeppelin_utils" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" + +[[package]] +name = "protocol_handler" +version = "0.1.0" +dependencies = [ + "openzeppelin", + "snforge_std", + "snforge_utils", +] + +[[package]] +name = "snforge_scarb_plugin" +version = "0.31.0" +source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.31.0#72ea785ca354e9e506de3e5d687da9fb2c1b3c67" + +[[package]] +name = "snforge_std" +version = "0.31.0" +source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.31.0#72ea785ca354e9e506de3e5d687da9fb2c1b3c67" +dependencies = [ + "snforge_scarb_plugin", +] + +[[package]] +name = "snforge_utils" +version = "0.1.0" diff --git a/cairo/protocol_handler/Scarb.toml b/cairo/protocol_handler/Scarb.toml new file mode 100644 index 000000000..a1142ed13 --- /dev/null +++ b/cairo/protocol_handler/Scarb.toml @@ -0,0 +1,21 @@ +[package] +name = "protocol_handler" +version = "0.1.0" +edition = "2024_07" + +# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html + +[dependencies] +starknet = "2.8.2" +openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.17.0" } + +[dev-dependencies] +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.31.0" } +snforge_utils = { path = "../snforge_utils" } + +[[target.starknet-contract]] +casm = true +casm-add-pythonic-hints = true + +[scripts] +test = "snforge test" diff --git a/cairo/protocol_handler/src/kakarot_interface.cairo b/cairo/protocol_handler/src/kakarot_interface.cairo new file mode 100644 index 000000000..1e66bdee6 --- /dev/null +++ b/cairo/protocol_handler/src/kakarot_interface.cairo @@ -0,0 +1,10 @@ +use starknet::{ContractAddress, ClassHash}; + +#[starknet::interface] +pub trait IKakarot { + fn upgrade(ref self: TContractState, new_class_hash: ClassHash); + fn transfer_ownership(ref self: TContractState, new_owner: ContractAddress); + fn pause(ref self: TContractState); + fn unpause(ref self: TContractState); + fn set_base_fee(ref self: TContractState, new_base_fee: felt252); +} diff --git a/cairo/protocol_handler/src/lib.cairo b/cairo/protocol_handler/src/lib.cairo new file mode 100644 index 000000000..2403b5483 --- /dev/null +++ b/cairo/protocol_handler/src/lib.cairo @@ -0,0 +1,6 @@ +mod protocol_handler; +pub use protocol_handler::{ + ProtocolHandler, IProtocolHandlerDispatcher, IProtocolHandlerDispatcherTrait, + IProtocolHandlerSafeDispatcher, IProtocolHandlerSafeDispatcherTrait +}; +mod kakarot_interface; diff --git a/cairo/protocol_handler/src/protocol_handler.cairo b/cairo/protocol_handler/src/protocol_handler.cairo new file mode 100644 index 000000000..aea87296f --- /dev/null +++ b/cairo/protocol_handler/src/protocol_handler.cairo @@ -0,0 +1,390 @@ +use starknet::account::Call; +use starknet::{ContractAddress, ClassHash}; + +#[starknet::interface] +pub trait IProtocolHandler { + /// Execute a call to the Kakarot contract. + /// Only the security council can call this function. + /// # Arguments + /// * `call` - The call to be executed + /// + /// # Panics + /// * `Caller is missing role` in case the caller is not the security council + /// * `ONLY_KAKAROT_CAN_BE_CALLED` in case the call is not to the Kakarot contract + /// + fn emergency_execution(ref self: TContractState, call: Call); + + /// Upgrade the Kakarot contract to a new version. + /// Only the operator can call this function. + /// # Arguments + /// * `new_class_hash` - The new class hash of the Kakarot contract + /// + /// # Panics + /// * `Caller is missing role` in case the caller is not the operator + fn upgrade(ref self: TContractState, new_class_hash: ClassHash); + + /// Transfer the ownership of the Kakarot contract. + /// Only the security council can call this function. + /// # Arguments + /// * `new_owner` - The new owner of the Kakarot contract + /// + /// # Panics + /// * `Caller is missing role` in case the caller is not the security council + fn transfer_ownership(ref self: TContractState, new_owner: ContractAddress); + + /// Pause the protocol for SOFT_PAUSE_DELAY. + /// Only the guardians can call this function. + /// # Panics + /// * `Caller is missing role` in case the caller is not a guardian + fn soft_pause(ref self: TContractState); + + /// Pause the protocol for HARD_PAUSE_DELAY. + /// Only the security council can call this function. + /// # Panics + /// * `Caller is missing role` in case the caller is not the security council + fn hard_pause(ref self: TContractState); + + /// Unpause the protocol. + /// Only the security council can call this function if the delay is not passed. + /// Else anyone can call this function. + /// # Panics + /// * `Caller is missing role` in case the caller is not the security council + fn unpause(ref self: TContractState); + + /// Set the base fee of the Kakarot contract. + /// Only the gas price admin can call this function. + /// # Arguments + /// * `base_fee` - The new base fee + /// + /// # Panics + /// * `Caller is missing role` in case the caller is not the gas price admin + fn set_base_fee(ref self: TContractState, new_base_fee: felt252); + + /// Execute a call to the Kakarot contract + /// Only the operator can call this function. + /// # Arguments + /// * `call` - The call to be executed + /// + /// # Panics + /// * `Caller is missing role` in case the caller is not the operator + /// * `UNAUTHORIZED_SELECTOR` in case the selector is not authorized + /// * `ONLY_KAKAROT_CAN_BE_CALLED` in case the call is not to the Kakarot contract + fn execute_call(ref self: TContractState, call: Call); +} + +#[starknet::contract] +pub mod ProtocolHandler { + use starknet::event::EventEmitter; + use starknet::account::Call; + use starknet::{ContractAddress, ClassHash, get_block_timestamp, SyscallResultTrait}; + use starknet::storage::{ + Map, StoragePointerReadAccess, StoragePointerWriteAccess, StorageMapReadAccess, + StorageMapWriteAccess + }; + use openzeppelin_access::accesscontrol::AccessControlComponent; + use openzeppelin_introspection::src5::SRC5Component; + use crate::kakarot_interface::{IKakarotDispatcher, IKakarotDispatcherTrait}; + + //* ------------------------------------------------------------------------ *// + //* COMPONENTS *// + //* ------------------------------------------------------------------------ *// + + component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + #[abi(embed_v0)] + impl AccessControlImpl = + AccessControlComponent::AccessControlImpl; + impl AccessControlInternalImpl = AccessControlComponent::InternalImpl; + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + + + //* ------------------------------------------------------------------------ *// + //* CONSTANTS *// + //* ------------------------------------------------------------------------ *// + + // Access controls roles + const SECURITY_COUNCIL_ROLE: felt252 = selector!("SECURITY_COUNCIL_ROLE"); + const OPERATOR_ROLE: felt252 = selector!("OPERATOR_ROLE"); + const GUARDIAN_ROLE: felt252 = selector!("GUARDIAN_ROLE"); + const GAS_PRICE_ADMIN_ROLE: felt252 = selector!("GAS_PRICE_ADMIN_ROLE"); + // Pause delay + pub const SOFT_PAUSE_DELAY: u64 = 12 * 60 * 60; // 12 hours + pub const HARD_PAUSE_DELAY: u64 = 7 * 24 * 60 * 60; // 7 days + + + //* ------------------------------------------------------------------------ *// + //* ERRORS *// + //* ------------------------------------------------------------------------ *// + + pub mod errors { + pub const ONLY_KAKAROT_CAN_BE_CALLED: felt252 = 'ONLY_KAKAROT_CAN_BE_CALLED'; + pub const PROTOCOL_ALREADY_PAUSED: felt252 = 'PROTOCOL_ALREADY_PAUSED'; + pub const UNAUTHORIZED_SELECTOR: felt252 = 'UNAUTHORIZED_SELECTOR'; + } + + //* ------------------------------------------------------------------------ *// + //* STORAGE *// + //* ------------------------------------------------------------------------ *// + + #[storage] + pub struct Storage { + pub kakarot: IKakarotDispatcher, + pub operator: ContractAddress, + pub guardians: Map, + pub gas_price_admin: ContractAddress, + pub protocol_frozen_until: u64, + pub authorized_operator_selector: Map, + #[substorage(v0)] + accesscontrol: AccessControlComponent::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage, + } + + //* ------------------------------------------------------------------------ *// + //* EVENTS *// + //* ------------------------------------------------------------------------ *// + + #[event] + #[derive(Drop, starknet::Event)] + pub enum Event { + EmergencyExecution: EmergencyExecution, + Upgrade: Upgrade, + TransferOwnership: TransferOwnership, + SoftPause: SoftPause, + HardPause: HardPause, + Unpause: Unpause, + BaseFeeChanged: BaseFeeChanged, + Execution: Execution, + #[flat] + AccessControlEvent: AccessControlComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event, + } + + #[derive(Drop, starknet::Event)] + pub struct EmergencyExecution { + pub call: Call + } + + #[derive(Drop, starknet::Event)] + pub struct Upgrade { + pub new_class_hash: ClassHash + } + + #[derive(Drop, starknet::Event)] + pub struct TransferOwnership { + pub new_owner: ContractAddress + } + + #[derive(Drop, starknet::Event)] + pub struct SoftPause { + pub protocol_frozen_until: u64, + } + + #[derive(Drop, starknet::Event)] + pub struct HardPause { + pub protocol_frozen_until: u64, + } + + #[derive(Drop, starknet::Event)] + pub struct Unpause {} + + #[derive(Drop, starknet::Event)] + pub struct BaseFeeChanged { + pub new_base_fee: felt252 + } + + #[derive(Drop, starknet::Event)] + pub struct Execution { + pub call: Call + } + + //* ------------------------------------------------------------------------ *// + //* CONSTRUCTOR *// + //* ------------------------------------------------------------------------ *// + + #[constructor] + fn constructor( + ref self: ContractState, + kakarot: ContractAddress, + security_council: ContractAddress, + operator: ContractAddress, + gas_price_admin: ContractAddress, + mut guardians: Span + ) { + // Store the Kakarot address + self.kakarot.write(IKakarotDispatcher { contract_address: kakarot }); + + // AccessControl-related initialization + self.accesscontrol.initializer(); + + // Grant roles + self.accesscontrol._grant_role(SECURITY_COUNCIL_ROLE, security_council); + self.accesscontrol._grant_role(OPERATOR_ROLE, operator); + for guardian in guardians { + self.accesscontrol._grant_role(GUARDIAN_ROLE, *guardian); + }; + self.accesscontrol._grant_role(GAS_PRICE_ADMIN_ROLE, gas_price_admin); + + // Store the authorized selectors for the operator + self.authorized_operator_selector.write(selector!("set_native_token"), true); + self.authorized_operator_selector.write(selector!("set_coinbase"), true); + self.authorized_operator_selector.write(selector!("set_prev_randao"), true); + self.authorized_operator_selector.write(selector!("set_block_gas_limit"), true); + self.authorized_operator_selector.write(selector!("set_account_contract_class_hash"), true); + self + .authorized_operator_selector + .write(selector!("set_uninitialized_account_class_hash"), true); + self + .authorized_operator_selector + .write(selector!("set_authorized_cairo_precompile_caller"), true); + self.authorized_operator_selector.write(selector!("set_cairo1_helpers_class_hash"), true); + self.authorized_operator_selector.write(selector!("upgrade_account"), true); + self.authorized_operator_selector.write(selector!("set_authorized_pre_eip155_tx"), true); + self + .authorized_operator_selector + .write(selector!("set_l1_messaging_contract_address"), true); + } + + #[abi(embed_v0)] + impl ProtocolHandler of super::IProtocolHandler { + //* ------------------------------------------------------------------------ *// + //* ADMIN FUNCTIONS *// + //* ------------------------------------------------------------------------ *// + + fn emergency_execution(ref self: ContractState, call: Call) { + // Check only security council can call + self.accesscontrol.assert_only_role(SECURITY_COUNCIL_ROLE); + + // Check if the call is to the Kakarot contract + let kakarot = self.kakarot.read().contract_address; + let Call { to, selector, calldata } = call; + assert(to == kakarot, errors::ONLY_KAKAROT_CAN_BE_CALLED); + + // Call Kakarot with syscall + starknet::syscalls::call_contract_syscall(to, selector, calldata).unwrap_syscall(); + + // Emit EmergencyExecution event + self.emit(EmergencyExecution { call }); + } + + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { + // Check only operator can call + self.accesscontrol.assert_only_role(OPERATOR_ROLE); + + // Call the Kakarot upgrade function + let kakarot = self.kakarot.read(); + kakarot.upgrade(new_class_hash); + + // Emit Upgrade event + self.emit(Upgrade { new_class_hash }); + } + + fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { + // Check only security council can call + self.accesscontrol.assert_only_role(SECURITY_COUNCIL_ROLE); + + // Call the Kakarot transfer_ownership function + let kakarot = self.kakarot.read(); + kakarot.transfer_ownership(new_owner); + + // Emit TransferOwnership event + self.emit(TransferOwnership { new_owner }); + } + + fn soft_pause(ref self: ContractState) { + // Check only guardians can call + self.accesscontrol.assert_only_role(GUARDIAN_ROLE); + + // Check if the protocol is already paused + let protocol_frozen_until = self.protocol_frozen_until.read(); + assert(protocol_frozen_until == 0, errors::PROTOCOL_ALREADY_PAUSED); + + // Cache the protocol frozen until timestamp + let protocol_frozen_until = get_block_timestamp().into() + SOFT_PAUSE_DELAY; + + // Update storage + self.protocol_frozen_until.write(protocol_frozen_until); + + // Call the Kakarot pause function + let kakarot = self.kakarot.read(); + kakarot.pause(); + + // Emit SoftPause event + self.emit(SoftPause { protocol_frozen_until: protocol_frozen_until }); + } + + fn hard_pause(ref self: ContractState) { + // Check only security council can call + self.accesscontrol.assert_only_role(SECURITY_COUNCIL_ROLE); + + // Cache the protocol frozen until timestamp + let protocol_frozen_until = get_block_timestamp().into() + HARD_PAUSE_DELAY; + + // Update storage + self.protocol_frozen_until.write(protocol_frozen_until); + + // Call the Kakarot pause function + let kakarot = self.kakarot.read(); + kakarot.pause(); + + // Emit HardPause event + self.emit(HardPause { protocol_frozen_until: protocol_frozen_until }); + } + + fn unpause(ref self: ContractState) { + // Check only security council can call unpause if delay is not passed + let too_soon = get_block_timestamp().into() < self.protocol_frozen_until.read(); + if too_soon { + self.accesscontrol.assert_only_role(SECURITY_COUNCIL_ROLE); + } + + // Call the Kakarot unpause function + let kakarot = self.kakarot.read(); + kakarot.unpause(); + + // Update storage + self.protocol_frozen_until.write(0); + + // Emit Unpause event + self.emit(Unpause {}); + } + + fn set_base_fee(ref self: ContractState, new_base_fee: felt252) { + // Check only gas price admin can call + self.accesscontrol.assert_only_role(GAS_PRICE_ADMIN_ROLE); + + // Call the Kakarot set_base_fee function + let kakarot = self.kakarot.read(); + kakarot.set_base_fee(new_base_fee); + + // Emit BaseFeeChanged + self.emit(BaseFeeChanged { new_base_fee }); + } + + //* ------------------------------------------------------------------------ *// + //* EXECUTE OPERATOR CALL *// + //* ------------------------------------------------------------------------ *// + + fn execute_call(ref self: ContractState, call: Call) { + // Check only operator can call + self.accesscontrol.assert_only_role(OPERATOR_ROLE); + + // Ensure the selector to call is part of the authorized selectors + let authorized = self.authorized_operator_selector.read(call.selector); + assert(authorized, errors::UNAUTHORIZED_SELECTOR); + + // Ensure the call is to the Kakarot + let kakarot = self.kakarot.read(); + assert(call.to == kakarot.contract_address, errors::ONLY_KAKAROT_CAN_BE_CALLED); + + // Call Kakarot with syscall + starknet::syscalls::call_contract_syscall(call.to, call.selector, call.calldata) + .unwrap_syscall(); + + // Emit Event Execution event + self.emit(Execution { call }); + } + } +} diff --git a/cairo/protocol_handler/tests/lib.cairo b/cairo/protocol_handler/tests/lib.cairo new file mode 100644 index 000000000..464ffc0c2 --- /dev/null +++ b/cairo/protocol_handler/tests/lib.cairo @@ -0,0 +1 @@ +mod test_protocol_handler; diff --git a/cairo/protocol_handler/tests/test_protocol_handler.cairo b/cairo/protocol_handler/tests/test_protocol_handler.cairo new file mode 100644 index 000000000..412e5f3f7 --- /dev/null +++ b/cairo/protocol_handler/tests/test_protocol_handler.cairo @@ -0,0 +1,617 @@ +use snforge_std::{ + ContractClassTrait, ContractClass, declare, DeclareResultTrait, EventSpyTrait, + start_cheat_block_timestamp_global, start_cheat_caller_address, mock_call, spy_events, store +}; +use starknet::{ContractAddress, contract_address_const, get_block_timestamp}; +use starknet::account::Call; +use starknet::class_hash::ClassHash; +use protocol_handler::{ + IProtocolHandlerDispatcher, IProtocolHandlerDispatcherTrait, ProtocolHandler, + IProtocolHandlerSafeDispatcher, IProtocolHandlerSafeDispatcherTrait +}; +use snforge_utils::snforge_utils::{ + EventsFilterBuilderTrait, ContractEventsTrait, assert_called_with +}; + +fn kakarot_mock() -> ContractAddress { + contract_address_const::<'security_council_mock'>() +} + +fn security_council_mock() -> ContractAddress { + contract_address_const::<'security_council_mock'>() +} + +fn operator_mock() -> ContractAddress { + contract_address_const::<'operator_mock'>() +} + +fn guardians_mock() -> Span { + array![ + contract_address_const::<'guardian_mock_1'>(), contract_address_const::<'guardian_mock_2'>() + ] + .span() +} + +fn gas_price_admin_mock() -> ContractAddress { + contract_address_const::<'gas_price_admin_mock'>() +} + +fn setup_contracts_for_testing() -> (IProtocolHandlerDispatcher, ContractClass) { + // Mock Kakarot, security council, operator and guardians + let kakarot_mock: ContractAddress = kakarot_mock(); + let security_council_mock: ContractAddress = security_council_mock(); + let operator_mock: ContractAddress = operator_mock(); + let gas_price_admin_mock: ContractAddress = gas_price_admin_mock(); + let guardians: Span = guardians_mock(); + + // Construct the calldata for the ProtocolHandler contrustor + let mut constructor_calldata: Array:: = array![ + kakarot_mock.into(), + security_council_mock.into(), + operator_mock.into(), + gas_price_admin_mock.into() + ]; + Serde::serialize(@guardians, ref constructor_calldata); + + let contract = declare("ProtocolHandler").unwrap().contract_class(); + let (contract_address, _) = contract.deploy(@constructor_calldata).unwrap(); + + // Get The dispatcher for the ProtocolHandler + let protocol_handler = IProtocolHandlerDispatcher { contract_address }; + + return (protocol_handler, *contract); +} + +#[test] +#[should_panic(expected: 'Caller is missing role')] +fn test_protocol_emergency_execution_fail_wrong_caller() { + let (protocol_handler, _) = setup_contracts_for_testing(); + + // Change caller to random caller address + let random_caller = contract_address_const::<'random_caller'>(); + start_cheat_caller_address(protocol_handler.contract_address, random_caller); + + // Call emergency_execution, should fail as caller is not security council + let call = Call { to: kakarot_mock(), selector: 0, calldata: [].span() }; + protocol_handler.emergency_execution(call); +} + +#[test] +#[should_panic(expected: 'ONLY_KAKAROT_CAN_BE_CALLED')] +fn test_protocol_emergency_execution_fail_wrong_destination() { + let (protocol_handler, _) = setup_contracts_for_testing(); + + // Change caller to security council + start_cheat_caller_address(protocol_handler.contract_address, security_council_mock()); + + // Construct the Call to a random address + let random_called_address = contract_address_const::<'random_called_address'>(); + let call = Call { to: random_called_address, selector: 0, calldata: [].span() }; + + // Call emergency_execution, should fail as the call is not to Kakarot + protocol_handler.emergency_execution(call); +} + +#[test] +fn test_protocol_emergency_execution_should_pass() { + let (protocol_handler, contract) = setup_contracts_for_testing(); + + // Change caller to security council + start_cheat_caller_address(protocol_handler.contract_address, security_council_mock()); + + // Mock the call to Kakarot upgrade function + mock_call::<()>(kakarot_mock(), selector!("upgrade"), (), 1); + + // Construct the Call to protocol handler and call emergency_execution + // Should pass as caller is security council and call is to Kakarot + let calldata = contract.class_hash; + let mut serialized_calldata: Array:: = array![]; + Serde::serialize(@calldata, ref serialized_calldata); + let call = Call { + to: kakarot_mock(), selector: selector!("upgrade"), calldata: serialized_calldata.span() + }; + + // Spy on the events + let mut spy = spy_events(); + protocol_handler.emergency_execution(call); + + // Assert that upgrade was called on Kakarot + assert_called_with::(kakarot_mock(), selector!("upgrade"), contract.class_hash); + + // Check the EmergencyExecution event is emitted + let expected = ProtocolHandler::Event::EmergencyExecution( + ProtocolHandler::EmergencyExecution { call: call } + ); + let contract_events = EventsFilterBuilderTrait::from_events(@spy.get_events()) + .with_contract_address(protocol_handler.contract_address) + .build(); + contract_events.assert_emitted(@expected); +} + +#[test] +#[should_panic(expected: 'Caller is missing role')] +fn test_protocol_upgrade_fail_wrong_caller() { + let (protocol_handler, contract) = setup_contracts_for_testing(); + + // Change caller to random caller address + let random_caller = contract_address_const::<'random_caller'>(); + start_cheat_caller_address(protocol_handler.contract_address, random_caller); + + // Call the protocol handler upgrade, should fail as caller is not operator + protocol_handler.upgrade(contract.class_hash); +} + +fn test_protocol_upgrade_should_pass() { + let (protocol_handler, contract) = setup_contracts_for_testing(); + + // Mock the call to Kakarot upgrade function + mock_call::<()>(kakarot_mock(), selector!("upgrade"), (), 1); + + // Change caller to security council + start_cheat_caller_address(protocol_handler.contract_address, operator_mock()); + + // Spy on the events + let mut spy = spy_events(); + + // Call the protocol handler upgrade, should pass as caller is operator + protocol_handler.upgrade(contract.class_hash); + + // Assert that upgrade was called on Kakarot + assert_called_with::(kakarot_mock(), selector!("upgrade"), contract.class_hash); + + // Check the TransferOwnership event is emitted + let expected = ProtocolHandler::Event::Upgrade( + ProtocolHandler::Upgrade { new_class_hash: contract.class_hash } + ); + let contract_events = EventsFilterBuilderTrait::from_events(@spy.get_events()) + .with_contract_address(protocol_handler.contract_address) + .build(); + contract_events.assert_emitted(@expected); +} + +#[test] +#[should_panic(expected: 'Caller is missing role')] +fn test_protocol_handler_transfer_ownership_should_fail_wrong_caller() { + let (protocol_handler, _) = setup_contracts_for_testing(); + + // Change caller to not security council + let not_security_council = contract_address_const::<'not_security_council'>(); + start_cheat_caller_address(protocol_handler.contract_address, not_security_council); + + // Call the protocol handler transfer_ownership, should fail as caller is not security council + let new_owner = contract_address_const::<'new_owner'>(); + protocol_handler.transfer_ownership(new_owner); +} + +#[test] +fn test_protocol_handler_transfer_ownership_should_pass() { + let (protocol_handler, _) = setup_contracts_for_testing(); + + // Change caller to security council + start_cheat_caller_address(protocol_handler.contract_address, security_council_mock()); + + // Mock the call to Kakarot transfer_ownership + mock_call::<()>(kakarot_mock(), selector!("transfer_ownership"), (), 1); + + // Spy on the events + let mut spy = spy_events(); + + // Call the protocol handler transfer_ownership + let new_owner = contract_address_const::<'new_owner'>(); + protocol_handler.transfer_ownership(new_owner); + + // Assert that transfer_ownership was called on Kakarot + assert_called_with::< + ContractAddress + >(kakarot_mock(), selector!("transfer_ownership"), new_owner); + + // Check the TransferOwnership event is emitted + let expected = ProtocolHandler::Event::TransferOwnership( + ProtocolHandler::TransferOwnership { new_owner: new_owner } + ); + let contract_events = EventsFilterBuilderTrait::from_events(@spy.get_events()) + .with_contract_address(protocol_handler.contract_address) + .build(); + contract_events.assert_emitted(@expected); +} + + +#[test] +#[should_panic(expected: 'Caller is missing role')] +fn test_protocol_handler_soft_pause_should_fail_wrong_caller() { + let (protocol_handler, _) = setup_contracts_for_testing(); + + // Change caller to random caller address + let random_caller = contract_address_const::<'random_caller'>(); + start_cheat_caller_address(protocol_handler.contract_address, random_caller); + + // Call the protocol handler soft_pause, should fail as caller is not guardian + protocol_handler.soft_pause(); +} + +#[test] +#[should_panic(expected: 'PROTOCOL_ALREADY_PAUSED')] +fn test_protocol_handler_soft_pause_should_fail_already_paused() { + let (protocol_handler, _) = setup_contracts_for_testing(); + + // Simulate pausing by writing in the storage + // Find the storage address for the ProtocolFrozenUntil + let mut state = ProtocolHandler::contract_state_for_testing(); + let storage_address = state.protocol_frozen_until; + let value = (get_block_timestamp() + 1); + let mut serialized_value: Array:: = array![]; + Serde::serialize(@value, ref serialized_value); + // Store the value in the storage of the protocol handler + store( + protocol_handler.contract_address, storage_address.__base_address__, serialized_value.span() + ); + + // Change caller to a guardian + let guardians = guardians_mock(); + start_cheat_caller_address(protocol_handler.contract_address, *guardians[0]); + + // Call the protocol handler soft_pause, should fail as protocol is already paused + protocol_handler.soft_pause(); +} + +#[test] +fn test_protocol_handler_soft_pause_should_pass() { + let (protocol_handler, _) = setup_contracts_for_testing(); + + // Mock the call to kakarot pause function + mock_call::<()>(kakarot_mock(), selector!("pause"), (), 1); + + // Change caller to a guardian + let guardians = guardians_mock(); + start_cheat_caller_address(protocol_handler.contract_address, *guardians[0]); + + // Spy on the events + let mut spy = spy_events(); + + // Call the protocol handler soft_pause, should pass as caller is guardian + protocol_handler.soft_pause(); + + // Assert that pause was called on Kakarot + assert_called_with::<()>(kakarot_mock(), selector!("pause"), ()); + + // Check the SoftPause event is emitted + let expected = ProtocolHandler::Event::SoftPause( + ProtocolHandler::SoftPause { + protocol_frozen_until: ProtocolHandler::SOFT_PAUSE_DELAY // Blocktimestamp is 0 in tests + } + ); + let contract_events = EventsFilterBuilderTrait::from_events(@spy.get_events()) + .with_contract_address(protocol_handler.contract_address) + .build(); + contract_events.assert_emitted(@expected); +} + +#[test] +#[should_panic(expected: 'Caller is missing role')] +fn test_protocol_handler_hard_pause_should_fail_wrong_caller() { + let (protocol_handler, _) = setup_contracts_for_testing(); + + // Change caller to not security council + let not_security_council = contract_address_const::<'not_security_council'>(); + start_cheat_caller_address(protocol_handler.contract_address, not_security_council); + + // Call the protocol handler hard_pause, should fail as caller is not security council + protocol_handler.hard_pause(); +} + +#[test] +fn test_protocol_handler_hard_pause_should_pass() { + let (protocol_handler, _) = setup_contracts_for_testing(); + + // Change caller to security council + start_cheat_caller_address(protocol_handler.contract_address, security_council_mock()); + + // Mock the call to kakarot pause function + mock_call::<()>(kakarot_mock(), selector!("pause"), (), 1); + + // Spy on the events + let mut spy = spy_events(); + + // Call the protocol handler hard_pause, should pass as caller is security council + protocol_handler.hard_pause(); + + // Assert that pause was called on Kakarot + assert_called_with::<()>(kakarot_mock(), selector!("pause"), ()); + + // Check the HardPause event is emitted + let expected = ProtocolHandler::Event::HardPause( + ProtocolHandler::HardPause { + protocol_frozen_until: ProtocolHandler::HARD_PAUSE_DELAY // Blocktimestamp is 0 in tests + } + ); + let contract_events = EventsFilterBuilderTrait::from_events(@spy.get_events()) + .with_contract_address(protocol_handler.contract_address) + .build(); + contract_events.assert_emitted(@expected); +} + +#[test] +#[should_panic(expected: 'Caller is missing role')] +fn test_protocol_handler_unpause_should_fail_wrong_caller_when_too_soon() { + // Block timestamp is 0 in tests, changing it to 10 + start_cheat_block_timestamp_global(10); + let (protocol_handler, _) = setup_contracts_for_testing(); + + // Simulate pausing by writing in the storage + // Find the storage address for the ProtocolFrozenUntil + let mut state = ProtocolHandler::contract_state_for_testing(); + let storage_address = state.protocol_frozen_until; + let value = (get_block_timestamp() * 10); + let mut serialized_value: Array:: = array![]; + Serde::serialize(@value, ref serialized_value); + // Store the value in the storage of the protocol handler + store( + protocol_handler.contract_address, storage_address.__base_address__, serialized_value.span() + ); + + // Change caller to random caller address + let random_caller = contract_address_const::<'random_caller'>(); + start_cheat_caller_address(protocol_handler.contract_address, random_caller); + + // Call the protocol handler unpause, should fail as caller is not security council + protocol_handler.unpause(); +} + +#[test] +fn test_protocol_handler_unpause_should_pass_security_council() { + // Block timestamp is 0 in tests changing it to 10 + start_cheat_block_timestamp_global(10); + let (protocol_handler, _) = setup_contracts_for_testing(); + + // Simulate pausing by writing in the storage + // Find the storage address for the ProtocolFrozenUntil + let mut state = ProtocolHandler::contract_state_for_testing(); + let storage_address = state.protocol_frozen_until; + let value = (get_block_timestamp() * 10); + let mut serialized_value: Array:: = array![]; + Serde::serialize(@value, ref serialized_value); + // Store the value in the storage of the protocol handler + store( + protocol_handler.contract_address, storage_address.__base_address__, serialized_value.span() + ); + + // Change the caller to security council + start_cheat_caller_address(protocol_handler.contract_address, security_council_mock()); + + // Mock call to kakarot unpause function + mock_call::<()>(kakarot_mock(), selector!("unpause"), (), 1); + + // Spy on the events + let mut spy = spy_events(); + + // Call the protocol handler unpause, should pass as caller is security council + protocol_handler.unpause(); + + // Assert that unpause was called on Kakarot + assert_called_with::<()>(kakarot_mock(), selector!("unpause"), ()); + + // Check the Unpause event is emitted + let expected = ProtocolHandler::Event::Unpause(ProtocolHandler::Unpause {}); + let contract_events = EventsFilterBuilderTrait::from_events(@spy.get_events()) + .with_contract_address(protocol_handler.contract_address) + .build(); + contract_events.assert_emitted(@expected); +} + +#[test] +fn test_protocol_handler_unpause_should_pass_after_delay() { + // Block timestamp is 0 in tests + start_cheat_block_timestamp_global(10); + let (protocol_handler, _) = setup_contracts_for_testing(); + + // Simulate pausing by writing in the storage + // Find the storage address for the ProtocolFrozenUntil + let mut state = ProtocolHandler::contract_state_for_testing(); + let storage_address = state.protocol_frozen_until; + let value = get_block_timestamp() - 1; + let mut serialized_value: Array:: = array![]; + Serde::serialize(@value, ref serialized_value); + // Store the value in the storage of the protocol handler + store( + protocol_handler.contract_address, storage_address.__base_address__, serialized_value.span() + ); + + // Mock call to kakarot unpause function + mock_call::<()>(kakarot_mock(), selector!("unpause"), (), 1); + + // Spy on the events + let mut spy = spy_events(); + + // Call the protocol handler unpause, should pass as caller is security council + protocol_handler.unpause(); + + // Assert that unpause was called on Kakarot + assert_called_with::<()>(kakarot_mock(), selector!("unpause"), ()); + + // Check the Unpause event is emitted + let expected = ProtocolHandler::Event::Unpause(ProtocolHandler::Unpause {}); + let contract_events = EventsFilterBuilderTrait::from_events(@spy.get_events()) + .with_contract_address(protocol_handler.contract_address) + .build(); + contract_events.assert_emitted(@expected); +} + +#[test] +#[should_panic(expected: 'Caller is missing role')] +fn test_protocol_handler_execute_call_should_fail_wrong_caller() { + let (protocol_handler, _) = setup_contracts_for_testing(); + + // Change caller to random caller address + let random_caller = contract_address_const::<'random_caller'>(); + start_cheat_caller_address(protocol_handler.contract_address, random_caller); + + // Call the protocol handler execute_call, should fail as caller is not operator + let call = Call { to: kakarot_mock(), selector: 0, calldata: [].span() }; + protocol_handler.execute_call(call); +} + +#[test] +#[should_panic(expected: 'UNAUTHORIZED_SELECTOR')] +fn test_protocol_handler_execute_call_should_fail_unauthorized_selector() { + let (protocol_handler, _) = setup_contracts_for_testing(); + + // Change caller to operator + start_cheat_caller_address(protocol_handler.contract_address, operator_mock()); + + // Construct the Call to a random address + let random_called_address = contract_address_const::<'random_called_address'>(); + let call = Call { to: random_called_address, selector: 0, calldata: [].span() }; + + // Call the protocol handler execute_call, should fail as the selector is not authorized + protocol_handler.execute_call(call); +} + +#[test] +#[should_panic(expected: 'ONLY_KAKAROT_CAN_BE_CALLED')] +fn test_protocol_handler_execute_call_should_fail_wrong_destination() { + let (protocol_handler, _) = setup_contracts_for_testing(); + + // Change caller to operator + start_cheat_caller_address(protocol_handler.contract_address, operator_mock()); + + // Construct the Call to kakarot + let random_called_address = contract_address_const::<'random_called_address'>(); + let call = Call { + to: random_called_address, selector: selector!("set_native_token"), calldata: [].span() + }; + + // Call the protocol handler execute_call, should fail as the call is not to Kakarot + protocol_handler.execute_call(call); +} + +#[test] +#[should_panic(expected: 'Caller is missing role')] +fn test_protocol_handler_set_base_fee_wrong_caller() { + let (protocol_handler, _) = setup_contracts_for_testing(); + + // Change caller to random caller address + let random_caller = contract_address_const::<'random_caller'>(); + start_cheat_caller_address(protocol_handler.contract_address, random_caller); + + // Call the protocol handler set_base_fee, should fail as caller is not operator + protocol_handler.set_base_fee(0); +} + +#[test] +fn test_protocol_handler_set_base_fee_should_pass() { + let (protocol_handler, _) = setup_contracts_for_testing(); + + // Change caller to gas price admin + start_cheat_caller_address(protocol_handler.contract_address, gas_price_admin_mock()); + + // Mock the call to Kakarot set_base_fee function + mock_call::<()>(kakarot_mock(), selector!("set_base_fee"), (), 1); + + // Spy on the events + let mut spy = spy_events(); + + // Call the protocol handler set_base_fee, should pass as caller is gas price admin + protocol_handler.set_base_fee(0); + + // Assert that unpause was called on Kakarot + assert_called_with::(kakarot_mock(), selector!("set_base_fee"), 0); + + // Check the BaseFeeChanged event is emitted + let expected = ProtocolHandler::Event::BaseFeeChanged( + ProtocolHandler::BaseFeeChanged { new_base_fee: 0 } + ); + let contract_events = EventsFilterBuilderTrait::from_events(@spy.get_events()) + .with_contract_address(protocol_handler.contract_address) + .build(); + contract_events.assert_emitted(@expected); +} + +#[test] +fn test_protocol_handler_execute_call_wrong_selector_should_fail() { + let (protocol_handler, _) = setup_contracts_for_testing(); + + let unauthoried_selectors = [ + selector!("upgrade"), + selector!("transfer_ownership"), + selector!("pause"), + selector!("unpause"), + ]; + + // Change the caller to operator + start_cheat_caller_address(protocol_handler.contract_address, operator_mock()); + + // Get SafeDispatcher of protocolHandler + let safe_dispatcher = IProtocolHandlerSafeDispatcher { + contract_address: protocol_handler.contract_address + }; + + for selector in unauthoried_selectors + .span() { + // Mock the call to the Kakarot entrypoint + mock_call::<()>(kakarot_mock(), *selector, (), 1); + + // Construct the Call to protocol handler and call execute_call + // Should pass as caller is operator and call is to Kakarot + let call = Call { to: kakarot_mock(), selector: *selector, calldata: [].span() }; + + // Call the protocol handler execute_call + #[feature("safe_dispatcher")] + match safe_dispatcher.execute_call(call) { + Result::Ok(_) => panic!("Entrypoint did not panic"), + Result::Err(panic_data) => { + assert(*panic_data.at(0) == 'UNAUTHORIZED_SELECTOR', *panic_data.at(0)); + } + }; + } +} + + +#[test] +fn test_protocol_handler_execute_call_should_pass() { + let (protocol_handler, _) = setup_contracts_for_testing(); + + let authorized_selectors = [ + selector!("set_native_token"), + selector!("set_coinbase"), + selector!("set_prev_randao"), + selector!("set_block_gas_limit"), + selector!("set_account_contract_class_hash"), + selector!("set_uninitialized_account_class_hash"), + selector!("set_authorized_cairo_precompile_caller"), + selector!("set_cairo1_helpers_class_hash"), + selector!("upgrade_account"), + selector!("set_authorized_pre_eip155_tx"), + selector!("set_l1_messaging_contract_address"), + ]; + + // Change caller to operator + start_cheat_caller_address(protocol_handler.contract_address, operator_mock()); + + for selector in authorized_selectors + .span() { + // Mock the call to Kakarot entrypoint + mock_call::<()>(kakarot_mock(), *selector, (), 1); + + // Construct the Call to protocol handler and call execute_call + // Should pass as caller is operator and call is to Kakarot + let call = Call { to: kakarot_mock(), selector: *selector, calldata: [].span() }; + + // Spy on the events + let mut spy = spy_events(); + + // Call the protocol handler execute_call + protocol_handler.execute_call(call); + + // Assert that selector was called on Kakarot + assert_called_with::<()>(kakarot_mock(), *selector, ()); + + // Check the ExecuteCall event is emitted + let expected = ProtocolHandler::Event::Execution( + ProtocolHandler::Execution { call: call } + ); + let contract_events = EventsFilterBuilderTrait::from_events(@spy.get_events()) + .with_contract_address(protocol_handler.contract_address) + .build(); + contract_events.assert_emitted(@expected); + } +} diff --git a/cairo/kakarot-ssj/crates/snforge_utils/.gitignore b/cairo/snforge_utils/.gitignore similarity index 100% rename from cairo/kakarot-ssj/crates/snforge_utils/.gitignore rename to cairo/snforge_utils/.gitignore diff --git a/cairo/snforge_utils/Scarb.lock b/cairo/snforge_utils/Scarb.lock new file mode 100644 index 000000000..2c2ca5cc4 --- /dev/null +++ b/cairo/snforge_utils/Scarb.lock @@ -0,0 +1,22 @@ +# Code generated by scarb DO NOT EDIT. +version = 1 + +[[package]] +name = "snforge_scarb_plugin" +version = "0.31.0" +source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.31.0#72ea785ca354e9e506de3e5d687da9fb2c1b3c67" + +[[package]] +name = "snforge_std" +version = "0.31.0" +source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.31.0#72ea785ca354e9e506de3e5d687da9fb2c1b3c67" +dependencies = [ + "snforge_scarb_plugin", +] + +[[package]] +name = "snforge_utils" +version = "0.1.0" +dependencies = [ + "snforge_std", +] diff --git a/cairo/kakarot-ssj/crates/snforge_utils/Scarb.toml b/cairo/snforge_utils/Scarb.toml similarity index 95% rename from cairo/kakarot-ssj/crates/snforge_utils/Scarb.toml rename to cairo/snforge_utils/Scarb.toml index baa7e7add..2a1e55134 100644 --- a/cairo/kakarot-ssj/crates/snforge_utils/Scarb.toml +++ b/cairo/snforge_utils/Scarb.toml @@ -7,7 +7,6 @@ edition = "2024_07" [dependencies] starknet = "2.8.2" -evm = { path = "../evm" } [dev-dependencies] snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.31.0" } diff --git a/cairo/kakarot-ssj/crates/snforge_utils/src/contracts.cairo b/cairo/snforge_utils/src/contracts.cairo similarity index 100% rename from cairo/kakarot-ssj/crates/snforge_utils/src/contracts.cairo rename to cairo/snforge_utils/src/contracts.cairo diff --git a/cairo/kakarot-ssj/crates/snforge_utils/src/lib.cairo b/cairo/snforge_utils/src/lib.cairo similarity index 93% rename from cairo/kakarot-ssj/crates/snforge_utils/src/lib.cairo rename to cairo/snforge_utils/src/lib.cairo index eba213ef3..81fdb05be 100644 --- a/cairo/kakarot-ssj/crates/snforge_utils/src/lib.cairo +++ b/cairo/snforge_utils/src/lib.cairo @@ -4,10 +4,7 @@ mod contracts; pub mod snforge_utils { use core::array::ArrayTrait; use core::option::OptionTrait; - use evm::state::compute_storage_key; use starknet::ContractAddress; - use evm::model::Address; - use snforge_std::cheatcodes::storage::store_felt252; use snforge_std::Event; use snforge_std::cheatcodes::events::{Events}; use array_utils::ArrayExtTrait; @@ -301,19 +298,4 @@ pub mod snforge_utils { } } } - - /// Stores a value in the EVM storage of a given Starknet contract. - pub fn store_evm(target: Address, evm_key: u256, evm_value: u256) { - let storage_address = compute_storage_key(target.evm, evm_key); - let serialized_value = [evm_value.low.into(), evm_value.high.into()].span(); - for offset in 0 - ..serialized_value - .len() { - store_felt252( - target.starknet, - storage_address + offset.into(), - *serialized_value.at(offset) - ); - }; - } }