From b458459f7dde03c93725da9cbb03afb16258afb3 Mon Sep 17 00:00:00 2001 From: Ginika Chinonso Date: Wed, 12 Jul 2023 10:29:23 +0100 Subject: [PATCH 1/2] Converted Exercise 2 to Cairo 2.0 --- src/ex02.cairo | 111 +++++++++++++++++++++++++++++++------------------ 1 file changed, 71 insertions(+), 40 deletions(-) diff --git a/src/ex02.cairo b/src/ex02.cairo index b57c6ff..1ec8a09 100644 --- a/src/ex02.cairo +++ b/src/ex02.cairo @@ -15,8 +15,9 @@ //////////////////////////////// -#[contract] +#[starknet::contract] mod Ex02 { + //////////////////////////////// // Core Library imports // These are syscalls and functionalities that allow you to write Starknet contracts @@ -38,27 +39,19 @@ mod Ex02 { // In Cairo 1, storage is declared in a struct // Storage is not visible by default through the ABI //////////////////////////////// + #[storage] struct Storage { // This variable is a u128, an unsigned integer (only positive values) stored over 128 bits // You can use any of the following types: u8, u16, u32, u64, u128, bool, felt252, ContractAddress // You can also use arrays of any of these types. // For example: Array:: for an array of addresses or Array:: for an array of 8 bytes - // You can read and write to storage variables using the ::write() and ::read() functions, for example: my_secret_value_storage::write(42) + // To access state variables, you need to reference the contract state + // If you intend to modify state, you will need to use `ref self: ContractState` which is a mutable reference to the contract state + // But if you do not intend to modify state, you can use `self: @ContractState` which is a snapshot of the contract state + // You can read and write to storage variables using the `.write()` and `.read()` functions, for example: self.my_secret_value_storage.write(42) my_secret_value_storage: u128, } - //////////////////////////////// - // View Functions - // Public variables should be declared explicitly with a getter function (indicated with #[view]) to be visible through the ABI and callable from other contracts - //////////////////////////////// - #[view] - fn my_secret_value() -> u128 { - // The contract read the value with ::read() - // You may have noticed that in Cairo 1 all lines end with a semicolon. - // But this one doesn't. Why? - // Because, in this case, the return value is the result of the function call. The semicolon is not needed. - my_secret_value_storage::read() - } //////////////////////////////// // Constructor @@ -66,41 +59,79 @@ mod Ex02 { //////////////////////////////// #[constructor] fn constructor( + ref self: ContractState, _tderc20_address: ContractAddress, _players_registry: ContractAddress, _workshop_id: u128, _exercise_id: u128, my_secret_value: u128, ) { - ex_initializer(_tderc20_address, _players_registry, _workshop_id, _exercise_id); - my_secret_value_storage::write(my_secret_value); + // The constructor takes a mutable reference to the contract state to enable it modify the state and instantiate state variables + self.ex_initializer(_tderc20_address, _players_registry, _workshop_id, _exercise_id); + self.my_secret_value_storage.write(my_secret_value); } + + + ////////////////////////////////////// + // Ex02 trait implementation of Ex02Trait + /////////////////////////////////////// + //////////////////////////////// // External functions - // These functions are callable by other contracts or external calls such as DAPP, which are indicated with #[external] (similar to "public" in Solidity) - //////////////////////////////// - #[external] - fn claim_points(my_value: u128) { - // Reading caller address using the Starknet core library function get_caller_address() (similar to msg.sender in Solidity) - // and storing it in a variable called sender_address. - let sender_address = get_caller_address(); - // Reading the secret value from storage using the read function from the storage variable my_secret_value_storage - let my_secret_value = my_secret_value_storage::read(); - // Checking that the value sent is the same as the secret value stored in storage using the assert function - // Using assert this way is similar to using "require" in Solidity - assert(my_value == my_secret_value, 'Wrong secret value'); - // Checking if the user has validated the exercise before sending points using the validate_exercise function from the Ex00Base contract - validate_exercise(sender_address); - // Sending points to the address specified as parameter using the distribute_points function from the Ex00Base contract - distribute_points(sender_address, 2_u128); - } - //////////////////////////////// - // External functions - Administration - // Only admins can call these. You don't need to understand them to finish the exercise. - //////////////////////////////// - #[external] - fn update_class_hash(class_hash: felt252) { - update_class_hash_by_admin(class_hash); + // The functions in the Ex02TraitImpl block are callable by other contracts or external calls and are indicated with #[external(v0)] to make them availiable in the ABI + // The generate_trait attribute is used to automatically generate a trait for the impl block + /////////////////////////////////// + + #[external(v0)] + #[generate_trait] + impl Ex02TraitImpl of Ex02Trait{ + + //////////////////////////////// + // View Functions + // Public variables should be declared explicitly with a getter function to be visible through the ABI and callable from other contracts + // By taking a snapshot of the ContractState, we ensure that this function can not modify the Contract State and hence can be seen as a view function + //////////////////////////////// + fn my_secret_value(self: @ContractState) -> u128 { + // The contract read the value with .read() + // You may have noticed that in Cairo 1 all lines end with a semicolon. + // But this one doesn't. Why? + // Because, in this case, the return value is the result of the function call. The semicolon is not needed. + // Since self is a snapshot of the contract state, it cannot be modified. + self.my_secret_value_storage.read() + } + + + //////////////////////////////// + // Mutable functions + // These functions are callable by other contracts or external calls such as DAPP, + // By taking a reference to the ContractState, this function can modify the Contract State (this is similar to an external or public function in solidity ) + //////////////////////////////// + fn claim_points(ref self: ContractState, my_value: u128) { + // Reading caller address using the Starknet core library function get_caller_address() (similar to msg.sender in Solidity) + // and storing it in a variable called sender_address. + let sender_address = get_caller_address(); + // Reading the secret value from storage using the read function from the storage variable my_secret_value_storage + let my_secret_value = self.my_secret_value_storage.read(); + // Checking that the value sent is the same as the secret value stored in storage using the assert function + // Using assert this way is similar to using "require" in Solidity + assert(my_value == my_secret_value, 'Wrong secret value'); + // Checking if the user has validated the exercise before sending points using the validate_exercise function from the Ex00Base contract + self.validate_exercise(sender_address); + // Sending points to the address specified as parameter using the distribute_points function from the Ex00Base contract + self.distribute_points(sender_address, 2_u128); + } + + + //////////////////////////////// + // External functions - Administration + // Only admins can call these. You don't need to understand them to finish the exercise. + //////////////////////////////// + fn update_class_hash(ref self: ContractState, class_hash: felt252) { + self.update_class_hash_by_admin(class_hash); + } + } + + } From 18fd1135fc1ff41aa9d05fffdec39489f8757cb7 Mon Sep 17 00:00:00 2001 From: Ginika Chinonso Date: Wed, 12 Jul 2023 10:30:10 +0100 Subject: [PATCH 2/2] Converted Exercise 3 to Cairo 2.0 --- src/ex03.cairo | 147 ++++++++++++++++++++++++++++--------------------- 1 file changed, 84 insertions(+), 63 deletions(-) diff --git a/src/ex03.cairo b/src/ex03.cairo index d551a02..d6a25a4 100644 --- a/src/ex03.cairo +++ b/src/ex03.cairo @@ -16,8 +16,11 @@ // - How to throw an error using the Cairo assert function //////////////////////////////// -#[contract] + + +#[starknet::contract] mod Ex03 { + //////////////////////////////// // Core Library imports // These are syscalls and functionalities that allow you to write Starknet contracts @@ -39,12 +42,13 @@ mod Ex03 { // In Cairo 1, storage is declared in a struct // Storage is not visible by default through the ABI //////////////////////////////// + #[storage] struct Storage { // This variable is a LegacyMap. It is equivalent to a mapping in Solidity. // It is used to store a counter (of type u128) for each user address (of type ContractAddress) in the contract's storage // The user_counters variable is private and can only be accessed through the getter function declared below - // To read the u128 value from a LegacyMap, use the read function (e.g. user_counters::read(sender_address)) - // To write a u128 value to a LegacyMap (mapping a u128 to a ContractAddress), use the write function (e.g. user_counters::write(sender_address, 0_u128)) + // To read the u128 value from a LegacyMap, use the read function (e.g. self.user_counters.read(sender_address)) + // To write a u128 value to a LegacyMap (mapping a u128 to a ContractAddress), use the write function (e.g. self.user_counters.write(sender_address, 0_u128)) user_counters: LegacyMap::, } @@ -54,81 +58,98 @@ mod Ex03 { //////////////////////////////// #[constructor] fn constructor( - _tderc20_address: ContractAddress, _players_registry: ContractAddress, _workshop_id: u128, _exercise_id: u128 + ref self: ContractState, _tderc20_address: ContractAddress, _players_registry: ContractAddress, _workshop_id: u128, _exercise_id: u128 ) { - ex_initializer(_tderc20_address, _players_registry, _workshop_id, _exercise_id); + self.ex_initializer(_tderc20_address, _players_registry, _workshop_id, _exercise_id); } - //////////////////////////////// - // View Functions - // Public variables should be declared explicitly with a getter function (indicated with #[view]) to be visible through the ABI and callable from other contracts - //////////////////////////////// - #[view] - fn get_user_counters(account: ContractAddress) -> u128 { - // We are not calling the read function without any parameter (e.g. user_counters::read()) but with a parameter (e.g. user_counters::read(sender_address)) - // because we want to read the value of the mapping for a specific key (the sender address) - let user_counter = user_counters::read(account); - // We return the value of the counter. We can return a value without using the return keyword, similar to Rust - user_counter - } + + + /////////////////////////////////////// + // Ex03 trait implementation of Ex03 trait + /////////////////////////////////////// //////////////////////////////// // External functions - // These functions are callable by other contracts or external calls and are indicated with #[external] (similar to "public" in Solidity) + // The functions in the Ex03TraitImpl block are callable by other contracts or external calls and are indicated with #[external(v0)] to make them availiable in the ABI + // The generate_trait attribute is used to automatically generate a trait for the impl block //////////////////////////////// // You can alter the state of the contract by calling external functions such as increment_counter, decrement_counter and reset_counter // After you altered the state of the contract, you can read from the contract's storage using the get_user_counters function to check if the counter is equal to 3 // Then you can call the claim_points function to check if the counter is equal to 3 and if so credit the user with points - #[external] - fn increment_counter() { - // Reading caller address - let sender_address: ContractAddress = get_caller_address(); - // Reading the counter from storage for the sender address (the key of the mapping) and storing it in a variable - let current_counter_value = user_counters::read(sender_address); - // Writing updated value to storage (incrementing the counter by 2) - user_counters::write(sender_address, current_counter_value + 2_u128); - } + ////////////////////////////////// - #[external] - fn decrement_counter() { - // Reading caller address - let sender_address: ContractAddress = get_caller_address(); - // Reading counter from storage - let current_counter_value = user_counters::read(sender_address); - // Writing updated value to storage (decrementing the counter by 1) - user_counters::write(sender_address, current_counter_value - 1_u128); - } + #[external(v0)] + #[generate_trait] + impl Ex03TraitImpl of Ex03Trait { + //////////////////////////////// + // View Functions + // By taking a snapshot of the ContractState, we ensure that this function can not modify the Contract State and hence can be seen as a view function + //////////////////////////////// + fn get_user_counters(self: @ContractState, account: ContractAddress) -> u128 { + // We are not calling the read function without any parameter (e.g. self.user_counters.read()) but with a parameter (e.g. self.user_counters.read(sender_address)) + // because we want to read the value of the mapping for a specific key (the sender address) + let user_counter = self.user_counters.read(account); + // We return the value of the counter. We can return a value without using the return keyword, similar to Rust + user_counter + } - #[external] - fn reset_counter() { - // Reading caller address - let sender_address: ContractAddress = get_caller_address(); - // Reinitializing the user counter to 0 (resetting it) - user_counters::write(sender_address, 0_u128); - } + //////////////////////////////// + // Mutable Functions + // By taking a reference to the ContractState, this function can modify the Contract State (this is similar to an external or public function in solidity ) + //////////////////////////////// + fn increment_counter(ref self: ContractState) { + // Reading caller address + let sender_address: ContractAddress = get_caller_address(); + // Reading the counter from storage for the sender address (the key of the mapping) and storing it in a variable + let current_counter_value = self.user_counters.read(sender_address); + // Writing updated value to storage (incrementing the counter by 2) + self.user_counters.write(sender_address, current_counter_value + 2_u128); + } - #[external] - fn claim_points() { - // Reading caller address - let sender_address: ContractAddress = get_caller_address(); - // Checking that user's counter is equal to 3 (the value we want to reach) and throwing an error if it is not - let current_counter_value = user_counters::read(sender_address); - // We are using the Cairo assert function to throw an error if the condition is not met - assert(current_counter_value == 3_u128, 'Counter is not equal to 3'); - // Checking if the user has validated the exercise before - validate_exercise(sender_address); - // Sending points to the address specified as parameter - distribute_points(sender_address, 2_u128); - } - //////////////////////////////// - // External functions - Administration - // Only admins can call these. You don't need to understand them to finish the exercise. - //////////////////////////////// - #[external] - fn update_class_hash(class_hash: felt252) { - update_class_hash_by_admin(class_hash); + fn decrement_counter(ref self: ContractState) { + // Reading caller address + let sender_address: ContractAddress = get_caller_address(); + // Reading counter from storage + let current_counter_value = self.user_counters.read(sender_address); + // Writing updated value to storage (decrementing the counter by 1) + self.user_counters.write(sender_address, current_counter_value - 1_u128); + } + + + fn reset_counter(ref self: ContractState) { + // Reading caller address + let sender_address: ContractAddress = get_caller_address(); + // Reinitializing the user counter to 0 (resetting it) + self.user_counters.write(sender_address, 0_u128); + } + + + + fn claim_points(ref self: ContractState) { + // Reading caller address + let sender_address: ContractAddress = get_caller_address(); + // Checking that user's counter is equal to 3 (the value we want to reach) and throwing an error if it is not + let current_counter_value = self.user_counters.read(sender_address); + // We are using the Cairo assert function to throw an error if the condition is not met + assert(current_counter_value == 3_u128, 'Counter is not equal to 3'); + + // Checking if the user has validated the exercise before + self.validate_exercise(sender_address); + // Sending points to the address specified as parameter + self.distribute_points(sender_address, 2_u128); + } + + //////////////////////////////// + // External functions - Administration + // Only admins can call these. You don't need to understand them to finish the exercise. + //////////////////////////////// + fn update_class_hash(ref self: ContractState, class_hash: felt252) { + self.update_class_hash_by_admin(class_hash); + } } + }