11// [!region contract]
22#[starknet:: interface]
3- pub trait ISimpleContract <TContractState > {
4- fn get_value (self : @ TContractState ) -> u32 ;
5- fn get_owner (self : @ TContractState ) -> starknet :: ContractAddress ;
6- fn set_value (ref self : TContractState , value : u32 );
3+ pub trait IInventoryContract <TContractState > {
4+ fn get_inventory_count (self : @ TContractState ) -> u32 ;
5+ fn get_max_capacity (self : @ TContractState ) -> u32 ;
6+ fn update_inventory (ref self : TContractState , new_count : u32 );
7+ }
8+
9+ /// An external function that encodes constraints for update inventory
10+ fn check_update_inventory (new_count : u32 , max_capacity : u32 ) -> Result <u32 , felt252 > {
11+ if new_count == 0 {
12+ return Result :: Err (' OutOfStock' );
13+ }
14+ if new_count > max_capacity {
15+ return Result :: Err (' ExceedsCapacity' );
16+ }
17+
18+ Result :: Ok (new_count )
719}
820
921#[starknet:: contract]
10- pub mod SimpleContract {
22+ pub mod InventoryContract {
23+ use super :: check_update_inventory;
1124 use starknet :: {get_caller_address, ContractAddress };
1225 use starknet :: storage :: {StoragePointerReadAccess , StoragePointerWriteAccess };
1326
1427 #[storage]
15- struct Storage {
16- pub value : u32 ,
28+ pub struct Storage {
29+ pub inventory_count : u32 ,
30+ pub max_capacity : u32 ,
1731 pub owner : ContractAddress ,
1832 }
1933
34+ #[event]
35+ #[derive(Copy , Drop , Debug , PartialEq , starknet:: Event )]
36+ pub enum Event {
37+ InventoryUpdated : InventoryUpdated ,
38+ }
39+
40+ #[derive(Copy , Drop , Debug , PartialEq , starknet:: Event )]
41+ pub struct InventoryUpdated {
42+ pub new_count : u32 ,
43+ }
44+
2045 #[constructor]
21- pub fn constructor (ref self : ContractState , initial_value : u32 ) {
22- self . value. write (initial_value );
46+ pub fn constructor (ref self : ContractState , max_capacity : u32 ) {
47+ self . inventory_count. write (0 );
48+ self . max_capacity. write (max_capacity );
2349 self . owner. write (get_caller_address ());
2450 }
2551
2652 #[abi(embed_v0)]
27- pub impl SimpleContractImpl of super :: ISimpleContract <ContractState > {
28- fn get_value (self : @ ContractState ) -> u32 {
29- self . value . read ()
53+ pub impl InventoryContractImpl of super :: IInventoryContract <ContractState > {
54+ fn get_inventory_count (self : @ ContractState ) -> u32 {
55+ self . inventory_count . read ()
3056 }
3157
32- fn get_owner (self : @ ContractState ) -> ContractAddress {
33- self . owner . read ()
58+ fn get_max_capacity (self : @ ContractState ) -> u32 {
59+ self . max_capacity . read ()
3460 }
3561
36- fn set_value (ref self : ContractState , value : u32 ) {
62+ fn update_inventory (ref self : ContractState , new_count : u32 ) {
3763 assert (self . owner. read () == get_caller_address (), ' Not owner' );
38- self . value. write (value );
64+
65+ match check_update_inventory (new_count , self . max_capacity. read ()) {
66+ Result :: Ok (new_count ) => self . inventory_count. write (new_count ),
67+ Result :: Err (error ) => { panic! (" {}" , error ); },
68+ }
69+
70+ self . emit (Event :: InventoryUpdated (InventoryUpdated { new_count }));
3971 }
4072 }
4173}
@@ -44,178 +76,24 @@ pub mod SimpleContract {
4476// [!region tests]
4577#[cfg(test)]
4678mod tests {
47- // Import the interface and dispatcher to be able to interact with the contract.
48- use super :: {SimpleContract , ISimpleContractDispatcher , ISimpleContractDispatcherTrait };
49-
50- // Import the deploy syscall to be able to deploy the contract.
51- use starknet :: syscalls :: deploy_syscall;
52- use starknet :: {get_contract_address, contract_address_const};
53-
54- // Use starknet test utils to fake the contract_address
55- use starknet :: testing :: set_contract_address;
56-
57- // Deploy the contract and return its dispatcher.
58- fn deploy (initial_value : u32 ) -> ISimpleContractDispatcher {
59- // Declare and deploy
60- let (contract_address , _ ) = deploy_syscall (
61- SimpleContract :: TEST_CLASS_HASH . try_into (). unwrap (),
62- 0 ,
63- array! [initial_value . into ()]. span (),
64- false ,
65- )
66- . unwrap ();
67-
68- // Return the dispatcher.
69- // The dispatcher allows to interact with the contract based on its interface.
70- ISimpleContractDispatcher { contract_address }
71- }
79+ use super :: check_update_inventory;
7280
7381 #[test]
74- fn test_deploy () {
75- let initial_value : u32 = 10 ;
76- let contract = deploy (initial_value );
77-
78- assert_eq! (contract . get_value (), initial_value );
79- assert_eq! (contract . get_owner (), get_contract_address ());
82+ fn test_check_update_inventory () {
83+ let result = check_update_inventory (10 , 100 );
84+ assert_eq! (result , Result :: Ok (10 ));
8085 }
8186
8287 #[test]
83- fn test_set_as_owner () {
84- // Fake the contract address to owner
85- let owner = contract_address_const :: <' owner' >();
86- set_contract_address (owner );
87-
88- // When deploying the contract, the owner is the caller.
89- let contract = deploy (10 );
90- assert_eq! (contract . get_owner (), owner );
91-
92- // As the current caller is the owner, the value can be set.
93- let new_value : u32 = 20 ;
94- contract . set_value (new_value );
95-
96- assert_eq! (contract . get_value (), new_value );
88+ fn test_check_update_inventory_out_of_stock () {
89+ let result = check_update_inventory (0 , 100 );
90+ assert_eq! (result , Result :: Err (' OutOfStock' ));
9791 }
9892
9993 #[test]
100- #[should_panic]
101- fn test_set_not_owner () {
102- let owner = contract_address_const :: <' owner' >();
103- set_contract_address (owner );
104- let contract = deploy (10 );
105-
106- // Fake the contract address to another address
107- let not_owner = contract_address_const :: <' not owner' >();
108- set_contract_address (not_owner );
109-
110- // As the current caller is not the owner, the value cannot be set.
111- let new_value : u32 = 20 ;
112- contract . set_value (new_value );
113- // Panic expected
114- }
115-
116- #[test]
117- #[available_gas(150000)]
118- fn test_deploy_gas () {
119- deploy (10 );
120- }
121- }
122- // [!endregion tests]
123-
124- // [!region tests_with_state]
125- #[cfg(test)]
126- mod tests_with_states {
127- // Only import the contract and implementation
128- use super :: SimpleContract ;
129- use SimpleContract :: SimpleContractImpl ;
130-
131- use starknet :: contract_address_const;
132- use starknet :: testing :: set_caller_address;
133- use core :: num :: traits :: Zero ;
134- use starknet :: storage :: {StoragePointerReadAccess , StoragePointerWriteAccess };
135-
136- #[test]
137- fn test_standalone_state () {
138- let mut state = SimpleContract :: contract_state_for_testing ();
139-
140- // As no contract was deployed, the constructor was not called on the state
141- // - with valueContractMemberStateTrait
142- assert_eq! (state . value. read (), 0 );
143- // - with SimpleContractImpl
144- assert_eq! (state . get_value (), 0 );
145- assert_eq! (state . owner. read (), Zero :: zero ());
146-
147- // We can still directly call the constructor to initialize the state.
148- let owner = contract_address_const :: <' owner' >();
149- // We are not setting the contract address but the caller address here,
150- // as we are not deploying the contract but directly calling the constructor function.
151- set_caller_address (owner );
152-
153- let initial_value : u32 = 10 ;
154- SimpleContract :: constructor (ref state , initial_value );
155- assert_eq! (state . get_value (), initial_value );
156- assert_eq! (state . get_owner (), owner );
157-
158- // As the current caller is the owner, the value can be set.
159- let new_value : u32 = 20 ;
160- state . set_value (new_value );
161- assert_eq! (state . get_value (), new_value );
162- }
163-
164- // But we can also deploy the contract and interact with it using the dispatcher
165- // as shown in the previous tests, and still use the state for testing.
166- use super :: {ISimpleContractDispatcher , ISimpleContractDispatcherTrait };
167- use starknet :: {syscalls :: deploy_syscall, testing :: set_contract_address};
168-
169- #[test]
170- fn test_state_with_contract () {
171- let owner = contract_address_const :: <' owner' >();
172- let not_owner = contract_address_const :: <' not owner' >();
173-
174- // Deploy as owner
175- let initial_value : u32 = 10 ;
176- set_contract_address (owner );
177- let (contract_address , _ ) = deploy_syscall (
178- SimpleContract :: TEST_CLASS_HASH . try_into (). unwrap (),
179- 0 ,
180- array! [initial_value . into ()]. span (),
181- false ,
182- )
183- . unwrap ();
184- let mut contract = ISimpleContractDispatcher { contract_address };
185-
186- // create the state
187- // - Set back as not owner
188- set_contract_address (not_owner );
189- let mut state = SimpleContract :: contract_state_for_testing ();
190- // - Currently, the state is not 'linked' to the contract
191- assert_ne! (state . get_value (), initial_value );
192- assert_ne! (state . get_owner (), owner );
193- // - Link the state to the contract by setting the contract address
194- set_contract_address (contract . contract_address);
195- assert_eq! (state . get_value (), initial_value );
196- assert_eq! (state . get_owner (), owner );
197-
198- // Mutating the state from the contract changes the testing state
199- set_contract_address (owner );
200- let new_value : u32 = 20 ;
201- contract . set_value (new_value );
202- set_contract_address (contract . contract_address);
203- assert_eq! (state . get_value (), new_value );
204-
205- // Mutating the state from the testing state changes the contract state
206- set_caller_address (owner );
207- state . set_value (initial_value );
208- assert_eq! (contract . get_value (), initial_value );
209-
210- // Directly mutating the state allows to change state
211- // in ways that are not allowed by the contract, such as changing the owner.
212- let new_owner = contract_address_const :: <' new owner' >();
213- state . owner. write (new_owner );
214- assert_eq! (contract . get_owner (), new_owner );
215-
216- set_caller_address (new_owner );
217- state . set_value (new_value );
218- assert_eq! (contract . get_value (), new_value );
94+ fn test_check_update_inventory_exceeds_capacity () {
95+ let result = check_update_inventory (101 , 100 );
96+ assert_eq! (result , Result :: Err (' ExceedsCapacity' ));
21997 }
22098}
22199// [!endregion tests]
0 commit comments