diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index edc55ce..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "snyk.advanced.autoSelectOrganization": true -} \ No newline at end of file diff --git a/DEPLOYMENT_STEPS.md b/DEPLOYMENT_STEPS.md new file mode 100644 index 0000000..bdc1330 --- /dev/null +++ b/DEPLOYMENT_STEPS.md @@ -0,0 +1,188 @@ +# CounterV2 Deployment Steps + +## Step 1: Create The Account Entry + +```bash +sncast account create --name Yusrah --network sepolia +``` + +Save the generated account address and fund it with Sepolia tokens for fees. + +## Step 2: Deploy The Account + +```bash +sncast account deploy --name Yusrah --network sepolia +``` + +## Step 3: List Accounts + +```bash +sncast account list +``` + +Save the address for the `Yusrah` account. + +Yusrah's acc. address 0x72658e0505e77137829e255912c52f8ca0ae73de5dde8332686b1adac3034f0 + +## Step 4: Go Into The Contract Package + +```bash +cd ~/Desktop/cairo-begining/cairo-bootcamp-6/starknet_contracts +``` + +## Step 5: Build The Contract + +```bash +scarb build +``` + +## Step 6: Declare CounterV2 + +```bash +sncast --account Yusrah \ + declare \ + --contract-name CounterV2 \ + --network sepolia \ + --wait +``` + +Save the `class_hash` from the output. + +class harsh 0x1e3a5b348490a07cea9f2788b81e46132c107724621e462494a5d20b67d458d + +## Step 7: Deploy CounterV2 + +Replace: + +- `PASTE_CLASS_HASH_HERE` +- `PASTE_YUSRAH_ADDRESS_HERE` + +```bash +sncast --account Yusrah \ + deploy \ + --class-hash PASTE_CLASS_HASH_HERE \ + --constructor-calldata PASTE_YUSRAH_ADDRESS_HERE \ + --network sepolia \ + --wait +``` + +Save the deployed `contract_address`. + +contract address 0x031e9847e60b0e7224c095cd45e260aa96daa0cec22960a8ab1cc51949997f36 + +## Step 8: Verify The Owner + +Replace `PASTE_CONTRACT_ADDRESS_HERE`. + +```bash +sncast call \ + --contract-address PASTE_CONTRACT_ADDRESS_HERE \ + --function get_owner \ + --network sepolia +``` + +## Step 9: Verify The Initial Count + +```bash +sncast call \ + --contract-address PASTE_CONTRACT_ADDRESS_HERE \ + --function get_count \ + --network sepolia +``` + +Expected: + +```text +0 +``` + +## Step 10: Increase The Count By 10 + +```bash +sncast --account Yusrah \ + invoke \ + --contract-address PASTE_CONTRACT_ADDRESS_HERE \ + --function increase_count \ + --calldata 10 \ + --network sepolia \ + --wait +``` + +## Step 11: Verify The Count After Increasing + +```bash +sncast call \ + --contract-address PASTE_CONTRACT_ADDRESS_HERE \ + --function get_count \ + --network sepolia +``` + +Expected: + +```text +10 +``` + +## Step 12: Reduce The Count By 4 + +```bash +sncast --account Yusrah \ + invoke \ + --contract-address PASTE_CONTRACT_ADDRESS_HERE \ + --function reduce_count \ + --calldata 4 \ + --network sepolia \ + --wait +``` + +## Step 13: Verify The Count After Reducing + +```bash +sncast call \ + --contract-address PASTE_CONTRACT_ADDRESS_HERE \ + --function get_count \ + --network sepolia +``` + +Expected: + +```text +6 +``` + +## Step 14: Demo Subtraction Underflow Protection + +```bash +sncast --account Yusrah \ + invoke \ + --contract-address PASTE_CONTRACT_ADDRESS_HERE \ + --function reduce_count \ + --calldata 200 \ + --network sepolia \ + --wait +``` + +Expected: + +An error because the subtraction underflows. + +## Step 15: Demo Non-Owner Rejection + +Replace `OTHER_ACCOUNT_NAME`. + +```bash +sncast --account OTHER_ACCOUNT_NAME \ + invoke \ + --contract-address PASTE_CONTRACT_ADDRESS_HERE \ + --function increase_count \ + --calldata 1 \ + --network sepolia \ + --wait +``` + +Expected: + +An error because the caller is not the owner. + + +e.g of non owner address 0x00A879C0fEf9C803041Ab7524859879B5d68684f413890401566367ae38a3a10 \ No newline at end of file diff --git a/README.md b/README.md index ece307b..f78bc3f 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,54 @@ -# Cairo Bootcamp 6.0 +# Cairo Bootcamp 6 Assignment -Welcome to **Cairo Bootcamp 6** — a hands-on, developer-focused program designed to help you learn Cairo from first principles and build real-world applications. This repository contains learning materials, examples, and exercises used throughout the bootcamp. +This repo contains my Cairo assignment split into two versions: ---- +- `V1`: the earlier simple Starknet counter contract and standalone arithmetic practice +- `V2`: the improved version where the Starknet contract imports reusable Cairo arithmetic logic and adds access control -## About the Bootcamp +## Assignment Summary -This Cairo Bootcamp is designed to: -- Introduce developers to Cairo -- Teach provable computation fundamentals** -- Explore Starknet and smart contract development -- Encourage hands-on building and experimentation +The assignment required the following: +- add a subtraction check so negative results throw an error +- write multiplication and division functions and handle their error cases +- add `only_owner` to state-changing functions +- make `increase_count` read the current storage value and add the incoming amount instead of overwriting storage +- import Cairo arithmetic logic into the Starknet contract for `increase_count` and `reduce_count` ---- +## Project Structure -## 📁 Repository Structure -. -├── cairo_programs/ # Pure Cairo programs (stateless logic) -├── starknet_contracts/ # Smart contracts (stateful logic) -└── README.md \ No newline at end of file +- `cairo_program/`: plain Cairo logic +- `starknet_contracts/`: Starknet contracts + +## V1 Files + +- Cairo arithmetic practice: [cairo_program/src/integer.cairo](cairo_program/src/integer.cairo) +- Simple Starknet contract: [starknet_contracts/src/counter_v1.cairo](starknet_contracts/src/counter_v1.cairo) + +## V2 Files + +- Reusable arithmetic logic: [cairo_program/src/arithmetic_v2.cairo](cairo_program/src/arithmetic_v2.cairo) +- Cairo library export: [cairo_program/src/lib.cairo](cairo_program/src/lib.cairo) +- Improved Starknet contract: [starknet_contracts/src/counter_v2.cairo](starknet_contracts/src/counter_v2.cairo) +- Starknet package entry: [starknet_contracts/src/lib.cairo](starknet_contracts/src/lib.cairo) + +## What Was Implemented In V2 + +- `sub_num` checks for underflow and throws an error +- `mul_num` checks for overflow +- `div_num` checks for division by zero +- `increase_count` uses imported addition logic +- `reduce_count` uses imported subtraction logic +- `increase_count` reads the current count from storage before updating it +- `only_owner` restricts state-changing functions + +## Build Status + +Both packages compile successfully: + +- `cairo_program` +- `starknet_contracts` + +## Next Step + +The next step after this PR is deployment of `CounterV2`, where the owner address will be passed into the constructor. diff --git a/cairo_program/.vscode/settings.json b/cairo_program/.vscode/settings.json deleted file mode 100644 index edc55ce..0000000 --- a/cairo_program/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "snyk.advanced.autoSelectOrganization": true -} \ No newline at end of file diff --git a/cairo_program/README.md b/cairo_program/README.md index 8cf3016..59bd28f 100644 --- a/cairo_program/README.md +++ b/cairo_program/README.md @@ -1,17 +1,26 @@ # Cairo Program -Welcome to **Cairo Bootcamp 6** — a hands-on, developer-focused program designed to help you learn Cairo from first principles and build real-world applications. This repository contains learning materials, examples, and exercises used throughout the bootcamp. +This package contains the plain Cairo logic used in the assignment. ---- +## Files +- `src/integer.cairo`: V1 arithmetic practice +- `src/arithmetic_v2.cairo`: V2 reusable arithmetic functions +- `src/lib.cairo`: exports the V2 arithmetic module for reuse by the Starknet contract package +## V2 Arithmetic ---- +The reusable V2 module includes: -## 📁 Repository Structure -. -├── src/ -├── starknet_contracts/ # Smart contracts (stateful logic) -├── exercises/ # Practice tasks and assignments -├── notes/ # Learning notes (felt, syntax, etc.) -└── README.md \ No newline at end of file +- `add_num` +- `sub_num` +- `mul_num` +- `div_num` + +Error handling included: + +- subtraction underflow protection +- multiplication overflow protection +- division-by-zero protection + +This package is imported into the V2 Starknet contract so the contract can reuse the arithmetic logic instead of doing the arithmetic inline. diff --git a/cairo_program/Scarb.toml b/cairo_program/Scarb.toml index b6c900a..a1e4f58 100644 --- a/cairo_program/Scarb.toml +++ b/cairo_program/Scarb.toml @@ -5,6 +5,8 @@ edition = "2025_12" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html +[lib] + [executable] [cairo] @@ -13,4 +15,3 @@ enable-gas = false [dependencies] cairo_execute = "2.18.0" - diff --git a/cairo_program/src/arithmetic_v2.cairo b/cairo_program/src/arithmetic_v2.cairo new file mode 100644 index 0000000..ee78037 --- /dev/null +++ b/cairo_program/src/arithmetic_v2.cairo @@ -0,0 +1,29 @@ +pub fn add_num(x: u32, y: u32) -> u32 { + x + y +} + +pub fn sub_num(x: u32, y: u32) -> u32 { + if y > x { + panic!("Sub underflow"); + } + x - y +} + +pub fn mul_num(x: u32, y: u32) -> u32 { + let wide_x: u64 = x.into(); + let wide_y: u64 = y.into(); + let result = wide_x * wide_y; + + if result > 4294967295_u64 { + panic!("Mul overflow"); + } + + result.try_into().unwrap() +} + +pub fn div_num(x: u32, y: u32) -> u32 { + if y == 0 { + panic!("Cannot divide 0"); + } + x / y +} diff --git a/cairo_program/src/bool.cairo b/cairo_program/src/bool.cairo index cafad1e..96267db 100644 --- a/cairo_program/src/bool.cairo +++ b/cairo_program/src/bool.cairo @@ -11,7 +11,7 @@ fn main() { fn is_adult(x: u8) -> bool { // let mut outcome: bool = false; if x <= 18 { - // outcome = false; + =;/ outcome = false; return false; } // outcome = true; diff --git a/cairo_program/src/lib.cairo b/cairo_program/src/lib.cairo index acc7644..95ef023 100644 --- a/cairo_program/src/lib.cairo +++ b/cairo_program/src/lib.cairo @@ -2,4 +2,5 @@ // mod short_string; // mod integer; // mod bool; -mod bytearray; \ No newline at end of file +mod bytearray; +pub mod arithmetic_v2; diff --git a/starknet_contracts/Scarb.lock b/starknet_contracts/Scarb.lock index 6c8c64a..2733aea 100644 --- a/starknet_contracts/Scarb.lock +++ b/starknet_contracts/Scarb.lock @@ -1,6 +1,10 @@ # Code generated by scarb DO NOT EDIT. version = 1 +[[package]] +name = "cairo_6" +version = "0.1.0" + [[package]] name = "snforge_scarb_plugin" version = "0.56.0" @@ -20,5 +24,6 @@ dependencies = [ name = "starknet_contracts" version = "0.1.0" dependencies = [ + "cairo_6", "snforge_std", ] diff --git a/starknet_contracts/Scarb.toml b/starknet_contracts/Scarb.toml index 29cf20f..124c7d3 100644 --- a/starknet_contracts/Scarb.toml +++ b/starknet_contracts/Scarb.toml @@ -7,6 +7,7 @@ edition = "2024_07" [dependencies] starknet = "2.18.0" +cairo_6 = { path = "../cairo_program" } [dev-dependencies] snforge_std = "0.56.0" diff --git a/starknet_contracts/src/counter_v2.cairo b/starknet_contracts/src/counter_v2.cairo new file mode 100644 index 0000000..b0fc1de --- /dev/null +++ b/starknet_contracts/src/counter_v2.cairo @@ -0,0 +1,61 @@ +#[starknet::interface] +pub trait ICounterV2 { + fn increase_count(ref self: T, amount: u32); + fn reduce_count(ref self: T, amount: u32); + fn get_count(self: @T) -> u32; + fn get_owner(self: @T) -> starknet::ContractAddress; +} + +#[starknet::contract] +pub mod CounterV2 { + use cairo_6::arithmetic_v2::{add_num, sub_num}; + use starknet::get_caller_address; + use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; + use starknet::ContractAddress; + + #[storage] + struct Storage { + owner: ContractAddress, + count: u32, + } + + #[constructor] + fn constructor(ref self: ContractState, owner: ContractAddress) { + self.owner.write(owner); + } + + fn assert_only_owner(self: @ContractState) { + let caller = get_caller_address(); + let owner = self.owner.read(); + assert(caller == owner, 'Only owner'); + } + + #[abi(embed_v0)] + impl CounterV2Impl of super::ICounterV2 { + fn increase_count(ref self: ContractState, amount: u32) { + assert_only_owner(@self); + assert(amount != 0, 'Amount cannot be 0'); + + let current_count = self.count.read(); + let updated_count = add_num(current_count, amount); + self.count.write(updated_count); + } + + fn reduce_count(ref self: ContractState, amount: u32) { + assert_only_owner(@self); + assert(amount != 0, 'Amount cannot be 0'); + + let current_count = self.count.read(); + let updated_count = sub_num(current_count, amount); + self.count.write(updated_count); + } + + fn get_count(self: @ContractState) -> u32 { + self.count.read() + } + + fn get_owner(self: @ContractState) -> ContractAddress { + self.owner.read() + } + } +} diff --git a/starknet_contracts/src/lib.cairo b/starknet_contracts/src/lib.cairo index f1b82bb..ca4972a 100644 --- a/starknet_contracts/src/lib.cairo +++ b/starknet_contracts/src/lib.cairo @@ -1,16 +1,11 @@ -/// Interface representing `HelloContract`. -/// This interface allows modification and retrieval of the contract's storage count. #[starknet::interface] pub trait ICounter { - /// Increase count. fn increase_count(ref self: T, amount: u32); - /// Retrieve count. fn get_count(self: @T) -> u32; } -/// Simple contract for managing count. #[starknet::contract] -mod Counter { +pub mod Counter { use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; #[storage] @@ -30,3 +25,5 @@ mod Counter { } } } + +mod counter_v2;