Skip to content

Commit 1b56757

Browse files
authored
Refactor snforge_std - typed_checked_cheatcode (#2833)
<!-- Reference any GitHub issues resolved by this PR --> I am happy to announce the first pr from our mailing list! 🔥🔥 Thanks Peter for your contribution ## Introduced changes <!-- A brief description of the changes --> - ## Checklist <!-- Make sure all of these are complete --> - [x] Linked relevant issue - [x] Updated relevant documentation - [x] Added relevant tests - [x] Performed self-review of the code - [x] Added changes to `CHANGELOG.md`
1 parent 64df8c4 commit 1b56757

16 files changed

+90
-183
lines changed

snforge_std/src/_cheatcode.cairo

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,32 @@
1-
use core::option::OptionTrait;
2-
use core::array::ArrayTrait;
3-
use core::traits::Into;
4-
use core::array::SpanTrait;
1+
pub(crate) fn execute_cheatcode<const selector: felt252>(input: Span<felt252>) -> Span<felt252> {
2+
let result = starknet::testing::cheatcode::<selector>(input);
3+
let enum_variant = *result.at(0);
4+
let result_content = result.slice(1, result.len() - 1);
55

6-
pub(crate) fn handle_cheatcode(input: Span<felt252>) -> Span<felt252> {
7-
let first = *input.at(0);
8-
let input = input.slice(1, input.len() - 1);
9-
10-
if first == 1 {
6+
if enum_variant == 1 { // Check if the result is an `Err`
117
let mut arr = array![core::byte_array::BYTE_ARRAY_MAGIC];
128

13-
arr.append_span(input);
9+
arr.append_span(result_content);
1410

1511
panic(arr)
1612
} else {
17-
input
13+
result_content
14+
}
15+
}
16+
17+
pub(crate) fn execute_cheatcode_and_deserialize<const selector: felt252, T, +Serde<T>>(
18+
input: Span<felt252>
19+
) -> T {
20+
let mut serialized_output = execute_cheatcode::<selector>(input);
21+
22+
match Serde::deserialize(ref serialized_output) {
23+
Option::Some(output) => output,
24+
Option::None => panic!("snforge_std version mismatch: check the warning above")
1825
}
1926
}
2027

2128
// Do not use this function directly.
2229
// It is an internal part of the snforge architecture used by macros.
2330
pub fn _is_config_run() -> bool {
24-
let mut res = handle_cheatcode(
25-
starknet::testing::cheatcode::<'is_config_mode'>(array![].span())
26-
);
27-
28-
Serde::deserialize(ref res).unwrap_or(false)
31+
execute_cheatcode_and_deserialize::<'is_config_mode'>(array![].span())
2932
}

snforge_std/src/byte_array.cairo

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use core::option::OptionTrait;
21
use core::byte_array::BYTE_ARRAY_MAGIC;
32

43
pub fn byte_array_as_felt_array(self: @ByteArray) -> Array<felt252> {

snforge_std/src/cheatcodes.cairo

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use starknet::{ContractAddress, ClassHash, contract_address_const};
2-
use starknet::testing::cheatcode;
3-
use super::_cheatcode::handle_cheatcode;
2+
use super::_cheatcode::execute_cheatcode_and_deserialize;
43

54
pub mod events;
65
pub mod l1_handler;
@@ -58,7 +57,7 @@ pub fn mock_call<T, impl TSerde: core::serde::Serde<T>, impl TDestruct: Destruct
5857

5958
ret_data_arr.serialize(ref inputs);
6059

61-
handle_cheatcode(cheatcode::<'mock_call'>(inputs.span()));
60+
execute_cheatcode_and_deserialize::<'mock_call', ()>(inputs.span());
6261
}
6362

6463

@@ -81,7 +80,7 @@ pub fn start_mock_call<T, impl TSerde: core::serde::Serde<T>, impl TDestruct: De
8180

8281
ret_data_arr.serialize(ref inputs);
8382

84-
handle_cheatcode(cheatcode::<'mock_call'>(inputs.span()));
83+
execute_cheatcode_and_deserialize::<'mock_call', ()>(inputs.span());
8584
}
8685

8786
/// Cancels the `mock_call` / `start_mock_call` for the function with given name and contract
@@ -91,9 +90,9 @@ pub fn start_mock_call<T, impl TSerde: core::serde::Serde<T>, impl TDestruct: De
9190
/// macro)
9291
pub fn stop_mock_call(contract_address: ContractAddress, function_selector: felt252) {
9392
let contract_address_felt: felt252 = contract_address.into();
94-
handle_cheatcode(
95-
cheatcode::<'stop_mock_call'>(array![contract_address_felt, function_selector].span())
96-
);
93+
execute_cheatcode_and_deserialize::<
94+
'stop_mock_call', ()
95+
>(array![contract_address_felt, function_selector].span());
9796
}
9897

9998
#[derive(Drop, Serde, PartialEq, Debug)]
@@ -114,9 +113,7 @@ pub enum ReplaceBytecodeError {
114113
pub fn replace_bytecode(
115114
contract: ContractAddress, new_class: ClassHash
116115
) -> Result<(), ReplaceBytecodeError> {
117-
let mut cheat_result = handle_cheatcode(
118-
cheatcode::<'replace_bytecode'>(array![contract.into(), new_class.into()].span())
119-
);
120-
121-
Serde::deserialize(ref cheat_result).unwrap()
116+
execute_cheatcode_and_deserialize::<
117+
'replace_bytecode'
118+
>(array![contract.into(), new_class.into()].span())
122119
}

snforge_std/src/cheatcodes/contract_class.cairo

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
use core::clone::Clone;
2-
use core::serde::Serde;
3-
use core::traits::TryInto;
4-
use starknet::{ContractAddress, ClassHash, testing::cheatcode, SyscallResult};
1+
use starknet::{ContractAddress, ClassHash, SyscallResult};
52
use super::super::byte_array::byte_array_as_felt_array;
6-
use super::super::_cheatcode::handle_cheatcode;
7-
use core::traits::Into;
3+
use super::super::_cheatcode::execute_cheatcode_and_deserialize;
84

95
#[derive(Drop, Serde, Copy)]
106
pub struct ContractClass {
@@ -64,19 +60,15 @@ impl ContractClassImpl of ContractClassTrait {
6460
) -> ContractAddress {
6561
let mut inputs = _prepare_calldata(self.class_hash, constructor_calldata);
6662

67-
let mut outputs = handle_cheatcode(cheatcode::<'precalculate_address'>(inputs.span()));
68-
69-
Serde::deserialize(ref outputs).unwrap()
63+
execute_cheatcode_and_deserialize::<'precalculate_address'>(inputs.span())
7064
}
7165

7266
fn deploy(
7367
self: @ContractClass, constructor_calldata: @Array::<felt252>
7468
) -> SyscallResult<(ContractAddress, Span<felt252>)> {
7569
let mut inputs = _prepare_calldata(self.class_hash, constructor_calldata);
7670

77-
let mut outputs = handle_cheatcode(cheatcode::<'deploy'>(inputs.span()));
78-
79-
Serde::deserialize(ref outputs).unwrap()
71+
execute_cheatcode_and_deserialize::<'deploy'>(inputs.span())
8072
}
8173

8274
fn deploy_at(
@@ -87,9 +79,7 @@ impl ContractClassImpl of ContractClassTrait {
8779
let mut inputs = _prepare_calldata(self.class_hash, constructor_calldata);
8880
inputs.append(contract_address.into());
8981

90-
let mut outputs = handle_cheatcode(cheatcode::<'deploy_at'>(inputs.span()));
91-
92-
Serde::deserialize(ref outputs).unwrap()
82+
execute_cheatcode_and_deserialize::<'deploy_at'>(inputs.span())
9383
}
9484

9585
fn new<T, +Into<T, ClassHash>>(class_hash: T) -> ContractClass {
@@ -121,22 +111,14 @@ impl DeclareResultImpl of DeclareResultTrait {
121111
/// - `AlreadyDeclared`: Contains `ContractClass` and signals that the contract has already been
122112
/// declared.
123113
pub fn declare(contract: ByteArray) -> Result<DeclareResult, Array<felt252>> {
124-
let mut span = handle_cheatcode(
125-
cheatcode::<'declare'>(byte_array_as_felt_array(@contract).span())
126-
);
127-
128-
Serde::deserialize(ref span).unwrap()
114+
execute_cheatcode_and_deserialize::<'declare'>(byte_array_as_felt_array(@contract).span())
129115
}
130116

131117
/// Retrieves a class hash of a contract deployed under the given address
132118
/// `contract_address` - target contract address
133119
/// Returns the `ClassHash` under given address
134120
pub fn get_class_hash(contract_address: ContractAddress) -> ClassHash {
135-
let mut span = handle_cheatcode(
136-
cheatcode::<'get_class_hash'>(array![contract_address.into()].span())
137-
);
138-
139-
Serde::deserialize(ref span).unwrap()
121+
execute_cheatcode_and_deserialize::<'get_class_hash'>(array![contract_address.into()].span())
140122
}
141123

142124
fn _prepare_calldata(

snforge_std/src/cheatcodes/events.cairo

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
1-
use core::array::ArrayTrait;
2-
use core::option::OptionTrait;
3-
use starknet::testing::cheatcode;
41
use starknet::ContractAddress;
5-
use super::super::_cheatcode::handle_cheatcode;
2+
use super::super::_cheatcode::execute_cheatcode_and_deserialize;
3+
64

75
/// Creates `EventSpy` instance that spies on all events emitted after its creation.
86
pub fn spy_events() -> EventSpy {
9-
let mut event_offset = handle_cheatcode(cheatcode::<'spy_events'>(array![].span()));
10-
let parsed_event_offset: usize = Serde::<usize>::deserialize(ref event_offset).unwrap();
11-
12-
EventSpy { event_offset: parsed_event_offset }
7+
execute_cheatcode_and_deserialize::<'spy_events'>(array![].span())
138
}
149

1510
/// Raw event format (as seen via the RPC-API), can be used for asserting the emitted events.
@@ -38,12 +33,7 @@ pub trait EventSpyTrait {
3833

3934
impl EventSpyTraitImpl of EventSpyTrait {
4035
fn get_events(ref self: EventSpy) -> Events {
41-
let mut output = handle_cheatcode(
42-
cheatcode::<'get_events'>(array![self.event_offset.into()].span())
43-
);
44-
let events = Serde::<Array<(ContractAddress, Event)>>::deserialize(ref output).unwrap();
45-
46-
Events { events }
36+
execute_cheatcode_and_deserialize::<'get_events'>(array![self.event_offset.into()].span())
4737
}
4838
}
4939

snforge_std/src/cheatcodes/execution_info.cairo

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
use starknet::{ContractAddress, testing::cheatcode, contract_address_const};
1+
use starknet::ContractAddress;
22
use starknet::ResourcesBounds;
33
use snforge_std::cheatcodes::CheatSpan;
4-
use super::super::_cheatcode::handle_cheatcode;
4+
use super::super::_cheatcode::execute_cheatcode_and_deserialize;
55

66
pub mod caller_address;
77
pub mod block_number;
@@ -145,5 +145,5 @@ fn cheat_execution_info(execution_info_mock: ExecutionInfoMock) {
145145

146146
execution_info_mock.serialize(ref inputs);
147147

148-
handle_cheatcode(cheatcode::<'cheat_execution_info'>(inputs.span()));
148+
execute_cheatcode_and_deserialize::<'cheat_execution_info', ()>(inputs.span());
149149
}
Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
1-
use starknet::testing::cheatcode;
2-
use core::serde::Serde;
3-
use super::super::_cheatcode::handle_cheatcode;
1+
use super::super::_cheatcode::execute_cheatcode_and_deserialize;
42

53

64
/// Generates a random felt value
75
///
86
/// Returns a random felt within the range of 0 and 2^252 - 1
97
pub fn generate_random_felt() -> felt252 {
10-
let mut random_felt = handle_cheatcode(cheatcode::<'generate_random_felt'>(array![].span()));
11-
12-
Serde::deserialize(ref random_felt).unwrap()
8+
execute_cheatcode_and_deserialize::<'generate_random_felt'>(array![].span())
139
}

snforge_std/src/cheatcodes/l1_handler.cairo

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
use core::array::SpanTrait;
2-
use core::serde::Serde;
3-
use starknet::{ContractAddress, testing::cheatcode, SyscallResult};
4-
use super::super::_cheatcode::handle_cheatcode;
1+
use starknet::{ContractAddress, SyscallResult};
2+
use super::super::_cheatcode::execute_cheatcode_and_deserialize;
53

64
#[derive(Drop, Clone)]
75
pub struct L1Handler {
@@ -38,14 +36,6 @@ impl L1HandlerImpl of L1HandlerTrait {
3836
];
3937
payload.serialize(ref inputs);
4038

41-
let mut outputs = handle_cheatcode(cheatcode::<'l1_handler_execute'>(inputs.span()));
42-
let exit_code = *outputs.pop_front().unwrap();
43-
44-
if exit_code == 0 {
45-
SyscallResult::Ok(())
46-
} else {
47-
let panic_data = Serde::<Array<felt252>>::deserialize(ref outputs).unwrap();
48-
SyscallResult::Err(panic_data)
49-
}
39+
execute_cheatcode_and_deserialize::<'l1_handler_execute'>(inputs.span())
5040
}
5141
}

snforge_std/src/cheatcodes/message_to_l1.cairo

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
1-
use core::array::ArrayTrait;
2-
use core::option::OptionTrait;
3-
use starknet::testing::cheatcode;
41
use starknet::{ContractAddress, EthAddress};
5-
use super::super::_cheatcode::handle_cheatcode;
2+
use super::super::_cheatcode::execute_cheatcode_and_deserialize;
63

74
/// Creates `MessageToL1Spy` instance that spies on all messages sent to L1
85
pub fn spy_messages_to_l1() -> MessageToL1Spy {
9-
let mut message_offset = handle_cheatcode(cheatcode::<'spy_messages_to_l1'>(array![].span()));
10-
let parsed_message_offset: usize = Serde::<usize>::deserialize(ref message_offset).unwrap();
11-
12-
MessageToL1Spy { message_offset: parsed_message_offset }
6+
execute_cheatcode_and_deserialize::<'spy_messages_to_l1'>(array![].span())
137
}
148

159
/// Raw message to L1 format (as seen via the RPC-API), can be used for asserting the sent messages.
@@ -40,13 +34,9 @@ pub trait MessageToL1SpyTrait {
4034

4135
impl MessageToL1SpyTraitImpl of MessageToL1SpyTrait {
4236
fn get_messages(ref self: MessageToL1Spy) -> MessagesToL1 {
43-
let mut output = handle_cheatcode(
44-
cheatcode::<'get_messages_to_l1'>(array![self.message_offset.into()].span())
45-
);
46-
let messages = Serde::<Array<(ContractAddress, MessageToL1)>>::deserialize(ref output)
47-
.unwrap();
48-
49-
MessagesToL1 { messages }
37+
execute_cheatcode_and_deserialize::<
38+
'get_messages_to_l1'
39+
>(array![self.message_offset.into()].span())
5040
}
5141
}
5242

@@ -144,4 +134,3 @@ fn is_sent(
144134
};
145135
return is_emitted;
146136
}
147-

snforge_std/src/cheatcodes/storage.cairo

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
1-
use core::array::ArrayTrait;
2-
use core::traits::Into;
3-
use core::option::OptionTrait;
4-
use core::traits::TryInto;
5-
use starknet::{testing::cheatcode, ContractAddress, StorageAddress};
6-
use core::panic_with_felt252;
7-
use super::super::_cheatcode::handle_cheatcode;
1+
use starknet::{ContractAddress, StorageAddress};
2+
use super::super::_cheatcode::execute_cheatcode_and_deserialize;
83

94
fn validate_storage_address_felt(storage_address_felt: felt252) {
105
let result: Option<StorageAddress> = storage_address_felt.try_into();
@@ -19,13 +14,13 @@ fn validate_storage_address_felt(storage_address_felt: felt252) {
1914
fn store_felt252(target: ContractAddress, storage_address: felt252, value: felt252) {
2015
validate_storage_address_felt(storage_address);
2116
let inputs = array![target.into(), storage_address.into(), value];
22-
handle_cheatcode(cheatcode::<'store'>(inputs.span()));
17+
execute_cheatcode_and_deserialize::<'store', ()>(inputs.span());
2318
}
2419

2520
fn load_felt252(target: ContractAddress, storage_address: felt252) -> felt252 {
2621
validate_storage_address_felt(storage_address);
2722
let inputs = array![target.into(), storage_address];
28-
*handle_cheatcode(cheatcode::<'load'>(inputs.span())).at(0)
23+
execute_cheatcode_and_deserialize::<'load'>(inputs.span())
2924
}
3025

3126
/// Stores felts from `serialized_value` in `target` contract's storage, starting at
@@ -61,5 +56,5 @@ pub fn load(target: ContractAddress, storage_address: felt252, size: felt252) ->
6156
pub fn map_entry_address(map_selector: felt252, keys: Span<felt252>) -> felt252 {
6257
let mut inputs = array![map_selector];
6358
keys.serialize(ref inputs);
64-
*handle_cheatcode(cheatcode::<'map_entry_address'>(inputs.span())).at(0)
59+
execute_cheatcode_and_deserialize::<'map_entry_address'>(inputs.span())
6560
}

0 commit comments

Comments
 (0)