From 8c98c3861a42d4d0ed08a20002a05b46e1b7f831 Mon Sep 17 00:00:00 2001 From: yahia008 <120284117+yahia008@users.noreply.github.com> Date: Tue, 28 Apr 2026 16:34:12 +0000 Subject: [PATCH 01/16] assignments 1 --- yahia008/assignment_1/.gitignore | 1 + yahia008/assignment_1/Scarb.lock | 6 ++ yahia008/assignment_1/Scarb.toml | 14 +++++ yahia008/assignment_1/src/assignment1.cairo | 62 +++++++++++++++++++++ yahia008/assignment_1/src/lib.cairo | 1 + 5 files changed, 84 insertions(+) create mode 100644 yahia008/assignment_1/.gitignore create mode 100644 yahia008/assignment_1/Scarb.lock create mode 100644 yahia008/assignment_1/Scarb.toml create mode 100644 yahia008/assignment_1/src/assignment1.cairo create mode 100644 yahia008/assignment_1/src/lib.cairo diff --git a/yahia008/assignment_1/.gitignore b/yahia008/assignment_1/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/yahia008/assignment_1/.gitignore @@ -0,0 +1 @@ +target diff --git a/yahia008/assignment_1/Scarb.lock b/yahia008/assignment_1/Scarb.lock new file mode 100644 index 0000000..560210b --- /dev/null +++ b/yahia008/assignment_1/Scarb.lock @@ -0,0 +1,6 @@ +# Code generated by scarb DO NOT EDIT. +version = 1 + +[[package]] +name = "assignment_1" +version = "0.1.0" diff --git a/yahia008/assignment_1/Scarb.toml b/yahia008/assignment_1/Scarb.toml new file mode 100644 index 0000000..1b8c858 --- /dev/null +++ b/yahia008/assignment_1/Scarb.toml @@ -0,0 +1,14 @@ +[package] +name = "assignment_1" +version = "0.1.0" +edition = "2025_12" + +# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html + +[executable] + +[cairo] +enable-gas = false + +[dependencies] +cairo_execute = "2.17.0" diff --git a/yahia008/assignment_1/src/assignment1.cairo b/yahia008/assignment_1/src/assignment1.cairo new file mode 100644 index 0000000..203821f --- /dev/null +++ b/yahia008/assignment_1/src/assignment1.cairo @@ -0,0 +1,62 @@ +use core::num::traits::CheckedMul; +use core::num::traits::CheckedAdd; +use core::num::traits::CheckedSub; + +#[derive(Drop)] +enum MathError { + Overflow, + DivisionByZero, + Underflow, +} + + +#[executable] +fn main() { + let add_result = add_number(10, 5); + match add_result { + Result::Ok(v) => println!("add value {}", v), + Result::Err(_) => println!("add error ", ), + }; + + let mul_result = multiply_number(10, 5); + match mul_result { + Result::Ok(v) => println!("mul value {}", v), + Result::Err(_) => println!("mul error"), + }; + + + let div_result = divide(10, 2); + match div_result { + Result::Ok(v) => println!("div value {}", v), + Result::Err(_) => println!("div error", ), + }; + + let sub_result = sub_num(10, 7); + match sub_result { + Result::Ok(v) => println!("sub value {}", v), + Result::Err(_) => println!("sub error"), + }; +} + + + +fn add_number(x: u128, y: u128) -> Result { + x.checked_add(y).ok_or(MathError::Overflow) +} + +fn multiply_number(x:u128, y:u128) -> Result { + x.checked_mul(y).ok_or(MathError::Overflow) +} + +fn divide(x: u128, y: u128) -> Result{ + if y == 0 { + return Result::Err(MathError::DivisionByZero); + } + + Result::Ok(x / y) +} + +fn sub_num(x:u128, y:u128) -> Result{ + x.checked_sub(y).ok_or(MathError::Underflow) +} + diff --git a/yahia008/assignment_1/src/lib.cairo b/yahia008/assignment_1/src/lib.cairo new file mode 100644 index 0000000..e45ed60 --- /dev/null +++ b/yahia008/assignment_1/src/lib.cairo @@ -0,0 +1 @@ +mod assignment1; From e6e2170358f52febb3a14acb26a07b08215f1425 Mon Sep 17 00:00:00 2001 From: yahia008 <120284117+yahia008@users.noreply.github.com> Date: Tue, 28 Apr 2026 17:12:02 +0000 Subject: [PATCH 02/16] assertion --- yahia008/assignment_1/src/assignment1.cairo | 20 +++++-- yahia008/assignment_2/.gitignore | 5 ++ yahia008/assignment_2/Scarb.lock | 24 +++++++++ yahia008/assignment_2/Scarb.toml | 52 +++++++++++++++++++ yahia008/assignment_2/snfoundry.toml | 11 ++++ yahia008/assignment_2/src/lib.cairo | 32 ++++++++++++ .../assignment_2/tests/test_contract.cairo | 47 +++++++++++++++++ 7 files changed, 187 insertions(+), 4 deletions(-) create mode 100644 yahia008/assignment_2/.gitignore create mode 100644 yahia008/assignment_2/Scarb.lock create mode 100644 yahia008/assignment_2/Scarb.toml create mode 100644 yahia008/assignment_2/snfoundry.toml create mode 100644 yahia008/assignment_2/src/lib.cairo create mode 100644 yahia008/assignment_2/tests/test_contract.cairo diff --git a/yahia008/assignment_1/src/assignment1.cairo b/yahia008/assignment_1/src/assignment1.cairo index 203821f..588bba7 100644 --- a/yahia008/assignment_1/src/assignment1.cairo +++ b/yahia008/assignment_1/src/assignment1.cairo @@ -14,26 +14,38 @@ enum MathError { fn main() { let add_result = add_number(10, 5); match add_result { - Result::Ok(v) => println!("add value {}", v), + Result::Ok(v) => { + assert!(v==15, "assertion failed") + println!("add value {}", v) + }, Result::Err(_) => println!("add error ", ), }; let mul_result = multiply_number(10, 5); match mul_result { - Result::Ok(v) => println!("mul value {}", v), + Result::Ok(v) => { + assert!(v==50, "assertion failed") + println!("mul value {}", v) + }, Result::Err(_) => println!("mul error"), }; let div_result = divide(10, 2); match div_result { - Result::Ok(v) => println!("div value {}", v), + Result::Ok(v) => { + assert!(v==5, "assertion failed") + println!("div value {}", v) + }, Result::Err(_) => println!("div error", ), }; let sub_result = sub_num(10, 7); match sub_result { - Result::Ok(v) => println!("sub value {}", v), + Result::Ok(v) => { + assert!(v==3, "assertion failed") + println!("sub value {}", v) + }, Result::Err(_) => println!("sub error"), }; } diff --git a/yahia008/assignment_2/.gitignore b/yahia008/assignment_2/.gitignore new file mode 100644 index 0000000..4096f8b --- /dev/null +++ b/yahia008/assignment_2/.gitignore @@ -0,0 +1,5 @@ +target +.snfoundry_cache/ +snfoundry_trace/ +coverage/ +profile/ diff --git a/yahia008/assignment_2/Scarb.lock b/yahia008/assignment_2/Scarb.lock new file mode 100644 index 0000000..7c5aa84 --- /dev/null +++ b/yahia008/assignment_2/Scarb.lock @@ -0,0 +1,24 @@ +# Code generated by scarb DO NOT EDIT. +version = 1 + +[[package]] +name = "assignment_2" +version = "0.1.0" +dependencies = [ + "snforge_std", +] + +[[package]] +name = "snforge_scarb_plugin" +version = "0.59.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:871fba677c03b66a1bf40815dac0ab1b385eb1b9be6e6c3cf2ad9788eeb2b6bb" + +[[package]] +name = "snforge_std" +version = "0.59.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:3620924fa08bd2d740b2b5b01ef86c8dab3d4b9c2206387c8dbdc8d2ec15133e" +dependencies = [ + "snforge_scarb_plugin", +] diff --git a/yahia008/assignment_2/Scarb.toml b/yahia008/assignment_2/Scarb.toml new file mode 100644 index 0000000..4eb7af4 --- /dev/null +++ b/yahia008/assignment_2/Scarb.toml @@ -0,0 +1,52 @@ +[package] +name = "assignment_2" +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.17.0" + +[dev-dependencies] +snforge_std = "0.59.0" +assert_macros = "2.17.0" + +[[target.starknet-contract]] +sierra = true + +[scripts] +test = "snforge test" + +[tool.scarb] +allow-prebuilt-plugins = ["snforge_std"] + +# Visit https://foundry-rs.github.io/starknet-foundry/appendix/scarb-toml.html for more information + +# [tool.snforge] # Define `snforge` tool section +# exit_first = true # Stop tests execution immediately upon the first failure +# fuzzer_runs = 1234 # Number of runs of the random fuzzer +# fuzzer_seed = 1111 # Seed for the random fuzzer + +# [[tool.snforge.fork]] # Used for fork testing +# name = "SOME_NAME" # Fork name +# url = "http://your.rpc.url" # Url of the RPC provider +# block_id.tag = "latest" # Block to fork from (block tag) + +# [[tool.snforge.fork]] +# name = "SOME_SECOND_NAME" +# url = "http://your.second.rpc.url" +# block_id.number = "123" # Block to fork from (block number) + +# [[tool.snforge.fork]] +# name = "SOME_THIRD_NAME" +# url = "http://your.third.rpc.url" +# block_id.hash = "0x123" # Block to fork from (block hash) + +# [profile.dev.cairo] # Configure Cairo compiler +# unstable-add-statements-code-locations-debug-info = true # Should be used if you want to use coverage +# unstable-add-statements-functions-debug-info = true # Should be used if you want to use coverage/profiler +# inlining-strategy = "avoid" # Should be used if you want to use coverage + +# [features] # Used for conditional compilation +# enable_for_tests = [] # Feature name and list of other features that should be enabled with it diff --git a/yahia008/assignment_2/snfoundry.toml b/yahia008/assignment_2/snfoundry.toml new file mode 100644 index 0000000..686c2ab --- /dev/null +++ b/yahia008/assignment_2/snfoundry.toml @@ -0,0 +1,11 @@ +# Visit https://foundry-rs.github.io/starknet-foundry/appendix/snfoundry-toml.html +# and https://foundry-rs.github.io/starknet-foundry/projects/configuration.html for more information + +# [sncast.default] # Define a profile name +# url = "https://api.zan.top/public/starknet-sepolia/rpc/v0_10" # Url of the RPC provider +# accounts-file = "../account-file" # Path to the file with the account data +# account = "mainuser" # Account from `accounts_file` or default account file that will be used for the transactions +# keystore = "~/keystore" # Path to the keystore file +# wait-params = { timeout = 300, retry-interval = 10 } # Wait for submitted transaction parameters +# block-explorer = "Voyager" # Block explorer service used to display links to transaction details +# show-explorer-links = true # Print links pointing to pages with transaction details in the chosen block explorer diff --git a/yahia008/assignment_2/src/lib.cairo b/yahia008/assignment_2/src/lib.cairo new file mode 100644 index 0000000..9a718e3 --- /dev/null +++ b/yahia008/assignment_2/src/lib.cairo @@ -0,0 +1,32 @@ +/// Interface representing `HelloContract`. +/// This interface allows modification and retrieval of the contract balance. +#[starknet::interface] +pub trait IHelloStarknet { + /// Increase contract balance. + fn increase_balance(ref self: TContractState, amount: felt252); + /// Retrieve contract balance. + fn get_balance(self: @TContractState) -> felt252; +} + +/// Simple contract for managing balance. +#[starknet::contract] +mod HelloStarknet { + use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; + + #[storage] + struct Storage { + balance: felt252, + } + + #[abi(embed_v0)] + impl HelloStarknetImpl of super::IHelloStarknet { + fn increase_balance(ref self: ContractState, amount: felt252) { + assert(amount != 0, 'Amount cannot be 0'); + self.balance.write(self.balance.read() + amount); + } + + fn get_balance(self: @ContractState) -> felt252 { + self.balance.read() + } + } +} diff --git a/yahia008/assignment_2/tests/test_contract.cairo b/yahia008/assignment_2/tests/test_contract.cairo new file mode 100644 index 0000000..1961e75 --- /dev/null +++ b/yahia008/assignment_2/tests/test_contract.cairo @@ -0,0 +1,47 @@ +use starknet::ContractAddress; + +use snforge_std::{declare, ContractClassTrait, DeclareResultTrait}; + +use assignment_2::IHelloStarknetSafeDispatcher; +use assignment_2::IHelloStarknetSafeDispatcherTrait; +use assignment_2::IHelloStarknetDispatcher; +use assignment_2::IHelloStarknetDispatcherTrait; + +fn deploy_contract(name: ByteArray) -> ContractAddress { + let contract = declare(name).unwrap().contract_class(); + let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); + contract_address +} + +#[test] +fn test_increase_balance() { + let contract_address = deploy_contract("HelloStarknet"); + + let dispatcher = IHelloStarknetDispatcher { contract_address }; + + let balance_before = dispatcher.get_balance(); + assert(balance_before == 0, 'Invalid balance'); + + dispatcher.increase_balance(42); + + let balance_after = dispatcher.get_balance(); + assert(balance_after == 42, 'Invalid balance'); +} + +#[test] +#[feature("safe_dispatcher")] +fn test_cannot_increase_balance_with_zero_value() { + let contract_address = deploy_contract("HelloStarknet"); + + let safe_dispatcher = IHelloStarknetSafeDispatcher { contract_address }; + + let balance_before = safe_dispatcher.get_balance().unwrap(); + assert(balance_before == 0, 'Invalid balance'); + + match safe_dispatcher.increase_balance(0) { + Result::Ok(_) => core::panic_with_felt252('Should have panicked'), + Result::Err(panic_data) => { + assert(*panic_data.at(0) == 'Amount cannot be 0', *panic_data.at(0)); + } + }; +} From 94556a48a2e44deaa80103488a6a8740cf5684c3 Mon Sep 17 00:00:00 2001 From: yahia008 <120284117+yahia008@users.noreply.github.com> Date: Tue, 28 Apr 2026 17:59:14 +0000 Subject: [PATCH 03/16] contract on progress --- yahia008/assignment_2/src/lib.cairo | 44 ++++++++--------- .../assignment_2/tests/test_contract.cairo | 47 ------------------- 2 files changed, 20 insertions(+), 71 deletions(-) delete mode 100644 yahia008/assignment_2/tests/test_contract.cairo diff --git a/yahia008/assignment_2/src/lib.cairo b/yahia008/assignment_2/src/lib.cairo index 9a718e3..476eb93 100644 --- a/yahia008/assignment_2/src/lib.cairo +++ b/yahia008/assignment_2/src/lib.cairo @@ -1,32 +1,28 @@ -/// Interface representing `HelloContract`. -/// This interface allows modification and retrieval of the contract balance. +use starknet::{ContractAddress, get_caller_address}; #[starknet::interface] -pub trait IHelloStarknet { - /// Increase contract balance. - fn increase_balance(ref self: TContractState, amount: felt252); - /// Retrieve contract balance. - fn get_balance(self: @TContractState) -> felt252; +pub trait ICounter { + fn increase_count(ref self: T, amount: u32); + fn transfer_ownership(ref self: T, new_owner: ContractAddress); + fn get_count(self: @T) -> u32; + fn get_owner(self: @T) -> ContractAddress; + } -/// Simple contract for managing balance. #[starknet::contract] -mod HelloStarknet { - use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; +mod CounterV2{ + use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; - #[storage] - struct Storage { - balance: felt252, - } + use super::ICounter; + use starknet::ContractAddress; - #[abi(embed_v0)] - impl HelloStarknetImpl of super::IHelloStarknet { - fn increase_balance(ref self: ContractState, amount: felt252) { - assert(amount != 0, 'Amount cannot be 0'); - self.balance.write(self.balance.read() + amount); - } + #[storage] + struct Storage{ + count:u32, + owner:ContractAddress + } - fn get_balance(self: @ContractState) -> felt252 { - self.balance.read() - } + #[constructor] + fn constructor(ref self:ContractState, initial_owner:ContractAddress){ + self.owner.write(initial_owner); } -} +} \ No newline at end of file diff --git a/yahia008/assignment_2/tests/test_contract.cairo b/yahia008/assignment_2/tests/test_contract.cairo deleted file mode 100644 index 1961e75..0000000 --- a/yahia008/assignment_2/tests/test_contract.cairo +++ /dev/null @@ -1,47 +0,0 @@ -use starknet::ContractAddress; - -use snforge_std::{declare, ContractClassTrait, DeclareResultTrait}; - -use assignment_2::IHelloStarknetSafeDispatcher; -use assignment_2::IHelloStarknetSafeDispatcherTrait; -use assignment_2::IHelloStarknetDispatcher; -use assignment_2::IHelloStarknetDispatcherTrait; - -fn deploy_contract(name: ByteArray) -> ContractAddress { - let contract = declare(name).unwrap().contract_class(); - let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); - contract_address -} - -#[test] -fn test_increase_balance() { - let contract_address = deploy_contract("HelloStarknet"); - - let dispatcher = IHelloStarknetDispatcher { contract_address }; - - let balance_before = dispatcher.get_balance(); - assert(balance_before == 0, 'Invalid balance'); - - dispatcher.increase_balance(42); - - let balance_after = dispatcher.get_balance(); - assert(balance_after == 42, 'Invalid balance'); -} - -#[test] -#[feature("safe_dispatcher")] -fn test_cannot_increase_balance_with_zero_value() { - let contract_address = deploy_contract("HelloStarknet"); - - let safe_dispatcher = IHelloStarknetSafeDispatcher { contract_address }; - - let balance_before = safe_dispatcher.get_balance().unwrap(); - assert(balance_before == 0, 'Invalid balance'); - - match safe_dispatcher.increase_balance(0) { - Result::Ok(_) => core::panic_with_felt252('Should have panicked'), - Result::Err(panic_data) => { - assert(*panic_data.at(0) == 'Amount cannot be 0', *panic_data.at(0)); - } - }; -} From 062cb3e69884252fb4cabcbffefa4210e03035fc Mon Sep 17 00:00:00 2001 From: yahia008 <120284117+yahia008@users.noreply.github.com> Date: Wed, 29 Apr 2026 00:00:23 +0000 Subject: [PATCH 04/16] contract build successfully --- yahia008/assignment_2/src/lib.cairo | 61 ++++++++++++++++++++++--- yahia008/assignment_2/src/mylogic.cairo | 16 +++++++ 2 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 yahia008/assignment_2/src/mylogic.cairo diff --git a/yahia008/assignment_2/src/lib.cairo b/yahia008/assignment_2/src/lib.cairo index 476eb93..f177898 100644 --- a/yahia008/assignment_2/src/lib.cairo +++ b/yahia008/assignment_2/src/lib.cairo @@ -1,28 +1,77 @@ -use starknet::{ContractAddress, get_caller_address}; +mod mylogic; +use starknet::{ContractAddress}; #[starknet::interface] pub trait ICounter { - fn increase_count(ref self: T, amount: u32); - fn transfer_ownership(ref self: T, new_owner: ContractAddress); - fn get_count(self: @T) -> u32; + fn get_count(self: @T) -> u128; fn get_owner(self: @T) -> ContractAddress; + fn transfer_ownership(ref self: T, new_owner: ContractAddress); + fn increase_count(ref self: T, amount: u128); + fn decrease_count (ref self:T, amount:u128); + fn reset_count(ref self:T); + + } #[starknet::contract] mod CounterV2{ - use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; + use crate::mylogic; +use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; use super::ICounter; use starknet::ContractAddress; + use starknet::get_caller_address; + #[storage] struct Storage{ - count:u32, + count:u128, owner:ContractAddress } #[constructor] fn constructor(ref self:ContractState, initial_owner:ContractAddress){ self.owner.write(initial_owner); + self.count.write(0); } + + #[abi(embed_v0)] + impl ICounterimpl of ICounter { + + fn get_count(self: @ContractState) -> u128 { + self.count.read() + } + fn get_owner(self: @ContractState) -> ContractAddress{ + self.owner.read() + } + fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress){ + let caller = get_caller_address(); + assert!(caller == self.owner.read(), "Only owner can transfer"); + self.owner.write(new_owner); + } + fn increase_count(ref self: ContractState, amount: u128){ + let caller:ContractAddress = get_caller_address(); + assert!(caller == self.owner.read(), "Only owner can change state"); + + let current = self.count.read(); + let new:u128 = mylogic::increase_count(current, amount); + self.count.write(new); + } + fn decrease_count (ref self:ContractState, amount:u128){ + let caller:ContractAddress = get_caller_address(); + assert!(caller == self.owner.read(), "Only owner can change state"); + + let current = self.count.read(); + let new:u128 = mylogic::decrease_count(current, amount); + self.count.write(new); + } + fn reset_count(ref self:ContractState){ + let caller = get_caller_address(); + assert!(caller == self.owner.read(), "Only owner can change state"); + let new:u128 = mylogic::reset_count(); + self.count.write(new); + + + } + } } \ No newline at end of file diff --git a/yahia008/assignment_2/src/mylogic.cairo b/yahia008/assignment_2/src/mylogic.cairo new file mode 100644 index 0000000..a411d43 --- /dev/null +++ b/yahia008/assignment_2/src/mylogic.cairo @@ -0,0 +1,16 @@ +use core::num::traits::CheckedAdd; +use core::num::traits::CheckedSub; + + + +pub fn increase_count(current: u128, amount: u128) -> u128{ + current.checked_add(amount).expect('overflow') +} + +pub fn decrease_count(current: u128, amount: u128) -> u128 { + current.checked_sub(amount).expect('underflow') +} + +pub fn reset_count() -> u128 { + 0 +} \ No newline at end of file From 4e224871bc84e8f0ef6e471dedf19a9432f78edf Mon Sep 17 00:00:00 2001 From: yahia008 <120284117+yahia008@users.noreply.github.com> Date: Wed, 29 Apr 2026 12:21:04 +0000 Subject: [PATCH 05/16] update --- yahia008/assignment_1/src/assignment1.cairo | 4 ++-- yahia008/assignment_2/src/mylogic.cairo | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/yahia008/assignment_1/src/assignment1.cairo b/yahia008/assignment_1/src/assignment1.cairo index 588bba7..12e2a8c 100644 --- a/yahia008/assignment_1/src/assignment1.cairo +++ b/yahia008/assignment_1/src/assignment1.cairo @@ -12,10 +12,10 @@ enum MathError { #[executable] fn main() { - let add_result = add_number(10, 5); + let add_result = add_number(0, 0); match add_result { Result::Ok(v) => { - assert!(v==15, "assertion failed") + assert!(v==0, "assertion failed") println!("add value {}", v) }, Result::Err(_) => println!("add error ", ), diff --git a/yahia008/assignment_2/src/mylogic.cairo b/yahia008/assignment_2/src/mylogic.cairo index a411d43..e716f0a 100644 --- a/yahia008/assignment_2/src/mylogic.cairo +++ b/yahia008/assignment_2/src/mylogic.cairo @@ -4,10 +4,18 @@ use core::num::traits::CheckedSub; pub fn increase_count(current: u128, amount: u128) -> u128{ + if amount == 0 + { + return current; + } current.checked_add(amount).expect('overflow') } pub fn decrease_count(current: u128, amount: u128) -> u128 { + if amount == 0 + { + return current; + } current.checked_sub(amount).expect('underflow') } From 34590c4a75714cf287c0f7bf3d461aec0a633fd3 Mon Sep 17 00:00:00 2001 From: yahia008 <120284117+yahia008@users.noreply.github.com> Date: Wed, 29 Apr 2026 17:30:40 +0000 Subject: [PATCH 06/16] module --- yahia008/av2_pro/arithmetic_logic/.gitignore | 1 + yahia008/av2_pro/arithmetic_logic/Scarb.lock | 6 ++ yahia008/av2_pro/arithmetic_logic/Scarb.toml | 19 +++++ .../av2_pro/arithmetic_logic/src/lib.cairo | 1 + .../arithmetic_logic/src/mylogic.cairo | 43 +++++++++++ yahia008/av2_pro/counterv2_pro/.gitignore | 5 ++ yahia008/av2_pro/counterv2_pro/Scarb.lock | 29 +++++++ yahia008/av2_pro/counterv2_pro/Scarb.toml | 53 +++++++++++++ yahia008/av2_pro/counterv2_pro/snfoundry.toml | 11 +++ yahia008/av2_pro/counterv2_pro/src/lib.cairo | 75 +++++++++++++++++++ yahia008/av2_pro/use_arithmetic/.gitignore | 1 + yahia008/av2_pro/use_arithmetic/Scarb.lock | 13 ++++ yahia008/av2_pro/use_arithmetic/Scarb.toml | 15 ++++ .../use_arithmetic/src/hello_world.cairo | 31 ++++++++ yahia008/av2_pro/use_arithmetic/src/lib.cairo | 1 + 15 files changed, 304 insertions(+) create mode 100644 yahia008/av2_pro/arithmetic_logic/.gitignore create mode 100644 yahia008/av2_pro/arithmetic_logic/Scarb.lock create mode 100644 yahia008/av2_pro/arithmetic_logic/Scarb.toml create mode 100644 yahia008/av2_pro/arithmetic_logic/src/lib.cairo create mode 100644 yahia008/av2_pro/arithmetic_logic/src/mylogic.cairo create mode 100644 yahia008/av2_pro/counterv2_pro/.gitignore create mode 100644 yahia008/av2_pro/counterv2_pro/Scarb.lock create mode 100644 yahia008/av2_pro/counterv2_pro/Scarb.toml create mode 100644 yahia008/av2_pro/counterv2_pro/snfoundry.toml create mode 100644 yahia008/av2_pro/counterv2_pro/src/lib.cairo create mode 100644 yahia008/av2_pro/use_arithmetic/.gitignore create mode 100644 yahia008/av2_pro/use_arithmetic/Scarb.lock create mode 100644 yahia008/av2_pro/use_arithmetic/Scarb.toml create mode 100644 yahia008/av2_pro/use_arithmetic/src/hello_world.cairo create mode 100644 yahia008/av2_pro/use_arithmetic/src/lib.cairo diff --git a/yahia008/av2_pro/arithmetic_logic/.gitignore b/yahia008/av2_pro/arithmetic_logic/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/yahia008/av2_pro/arithmetic_logic/.gitignore @@ -0,0 +1 @@ +target diff --git a/yahia008/av2_pro/arithmetic_logic/Scarb.lock b/yahia008/av2_pro/arithmetic_logic/Scarb.lock new file mode 100644 index 0000000..f50718d --- /dev/null +++ b/yahia008/av2_pro/arithmetic_logic/Scarb.lock @@ -0,0 +1,6 @@ +# Code generated by scarb DO NOT EDIT. +version = 1 + +[[package]] +name = "arithmetic_logic" +version = "0.1.0" diff --git a/yahia008/av2_pro/arithmetic_logic/Scarb.toml b/yahia008/av2_pro/arithmetic_logic/Scarb.toml new file mode 100644 index 0000000..6e598c9 --- /dev/null +++ b/yahia008/av2_pro/arithmetic_logic/Scarb.toml @@ -0,0 +1,19 @@ +[package] +name = "arithmetic_logic" +version = "0.1.0" +edition = "2025_12" + + +[lib] +sierra = true + +# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html + +[executable] + +[cairo] +enable-gas = false + +[dependencies] +cairo_execute = "2.17.0" + diff --git a/yahia008/av2_pro/arithmetic_logic/src/lib.cairo b/yahia008/av2_pro/arithmetic_logic/src/lib.cairo new file mode 100644 index 0000000..7fa7860 --- /dev/null +++ b/yahia008/av2_pro/arithmetic_logic/src/lib.cairo @@ -0,0 +1 @@ +pub mod mylogic; \ No newline at end of file diff --git a/yahia008/av2_pro/arithmetic_logic/src/mylogic.cairo b/yahia008/av2_pro/arithmetic_logic/src/mylogic.cairo new file mode 100644 index 0000000..b6dd43a --- /dev/null +++ b/yahia008/av2_pro/arithmetic_logic/src/mylogic.cairo @@ -0,0 +1,43 @@ +use core::num::traits::CheckedAdd; +use core::num::traits::CheckedSub; +use core::num::traits::CheckedMul; + +#[derive(Drop)] +enum MathError { + Overflow, + DivisionByZero, + Underflow, +} + + +pub fn add_num(x: u128, y: u128) -> u128{ + if y == 0 + { + return x; + } + x.checked_add(y).expect('overflow') +} + +pub fn sub_num(x: u128, y: u128) -> u128 { + if y == 0 + { + return x; + } + x.checked_sub(y).expect('underflow') +} + +pub fn multiply_number(x:u128, y:u128) -> Result { + x.checked_mul(y).ok_or(MathError::Overflow) +} + +pub fn divide(x: u128, y: u128) -> Result{ + if y == 0 { + return Result::Err(MathError::DivisionByZero); + } + + Result::Ok(x / y) +} + +pub fn reset_count() -> u128 { + 0 +} \ No newline at end of file diff --git a/yahia008/av2_pro/counterv2_pro/.gitignore b/yahia008/av2_pro/counterv2_pro/.gitignore new file mode 100644 index 0000000..4096f8b --- /dev/null +++ b/yahia008/av2_pro/counterv2_pro/.gitignore @@ -0,0 +1,5 @@ +target +.snfoundry_cache/ +snfoundry_trace/ +coverage/ +profile/ diff --git a/yahia008/av2_pro/counterv2_pro/Scarb.lock b/yahia008/av2_pro/counterv2_pro/Scarb.lock new file mode 100644 index 0000000..bcc4c05 --- /dev/null +++ b/yahia008/av2_pro/counterv2_pro/Scarb.lock @@ -0,0 +1,29 @@ +# Code generated by scarb DO NOT EDIT. +version = 1 + +[[package]] +name = "arithmetic_logic" +version = "0.1.0" + +[[package]] +name = "counterv2_pro" +version = "0.1.0" +dependencies = [ + "arithmetic_logic", + "snforge_std", +] + +[[package]] +name = "snforge_scarb_plugin" +version = "0.59.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:871fba677c03b66a1bf40815dac0ab1b385eb1b9be6e6c3cf2ad9788eeb2b6bb" + +[[package]] +name = "snforge_std" +version = "0.59.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:3620924fa08bd2d740b2b5b01ef86c8dab3d4b9c2206387c8dbdc8d2ec15133e" +dependencies = [ + "snforge_scarb_plugin", +] diff --git a/yahia008/av2_pro/counterv2_pro/Scarb.toml b/yahia008/av2_pro/counterv2_pro/Scarb.toml new file mode 100644 index 0000000..059e06e --- /dev/null +++ b/yahia008/av2_pro/counterv2_pro/Scarb.toml @@ -0,0 +1,53 @@ +[package] +name = "counterv2_pro" +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.17.0" +arithmetic_logic = {path = "../arithmetic_logic"} + +[dev-dependencies] +snforge_std = "0.59.0" +assert_macros = "2.17.0" + +[[target.starknet-contract]] +sierra = true + +[scripts] +test = "snforge test" + +[tool.scarb] +allow-prebuilt-plugins = ["snforge_std"] + +# Visit https://foundry-rs.github.io/starknet-foundry/appendix/scarb-toml.html for more information + +# [tool.snforge] # Define `snforge` tool section +# exit_first = true # Stop tests execution immediately upon the first failure +# fuzzer_runs = 1234 # Number of runs of the random fuzzer +# fuzzer_seed = 1111 # Seed for the random fuzzer + +# [[tool.snforge.fork]] # Used for fork testing +# name = "SOME_NAME" # Fork name +# url = "http://your.rpc.url" # Url of the RPC provider +# block_id.tag = "latest" # Block to fork from (block tag) + +# [[tool.snforge.fork]] +# name = "SOME_SECOND_NAME" +# url = "http://your.second.rpc.url" +# block_id.number = "123" # Block to fork from (block number) + +# [[tool.snforge.fork]] +# name = "SOME_THIRD_NAME" +# url = "http://your.third.rpc.url" +# block_id.hash = "0x123" # Block to fork from (block hash) + +# [profile.dev.cairo] # Configure Cairo compiler +# unstable-add-statements-code-locations-debug-info = true # Should be used if you want to use coverage +# unstable-add-statements-functions-debug-info = true # Should be used if you want to use coverage/profiler +# inlining-strategy = "avoid" # Should be used if you want to use coverage + +# [features] # Used for conditional compilation +# enable_for_tests = [] # Feature name and list of other features that should be enabled with it diff --git a/yahia008/av2_pro/counterv2_pro/snfoundry.toml b/yahia008/av2_pro/counterv2_pro/snfoundry.toml new file mode 100644 index 0000000..686c2ab --- /dev/null +++ b/yahia008/av2_pro/counterv2_pro/snfoundry.toml @@ -0,0 +1,11 @@ +# Visit https://foundry-rs.github.io/starknet-foundry/appendix/snfoundry-toml.html +# and https://foundry-rs.github.io/starknet-foundry/projects/configuration.html for more information + +# [sncast.default] # Define a profile name +# url = "https://api.zan.top/public/starknet-sepolia/rpc/v0_10" # Url of the RPC provider +# accounts-file = "../account-file" # Path to the file with the account data +# account = "mainuser" # Account from `accounts_file` or default account file that will be used for the transactions +# keystore = "~/keystore" # Path to the keystore file +# wait-params = { timeout = 300, retry-interval = 10 } # Wait for submitted transaction parameters +# block-explorer = "Voyager" # Block explorer service used to display links to transaction details +# show-explorer-links = true # Print links pointing to pages with transaction details in the chosen block explorer diff --git a/yahia008/av2_pro/counterv2_pro/src/lib.cairo b/yahia008/av2_pro/counterv2_pro/src/lib.cairo new file mode 100644 index 0000000..9b757f5 --- /dev/null +++ b/yahia008/av2_pro/counterv2_pro/src/lib.cairo @@ -0,0 +1,75 @@ +use arithmetic_logic::mylogic; +use starknet::{ContractAddress}; +#[starknet::interface] +pub trait ICounter { + fn get_count(self: @T) -> u128; + fn get_owner(self: @T) -> ContractAddress; + fn transfer_ownership(ref self: T, new_owner: ContractAddress); + fn increase_count(ref self: T, amount: u128); + fn decrease_count (ref self:T, amount:u128); + fn reset_count(ref self:T); + + + +} + +#[starknet::contract] +mod CounterV2{ + use crate::mylogic; +use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; + + use super::ICounter; + use starknet::ContractAddress; + use starknet::get_caller_address; + + + #[storage] + struct Storage{ + count:u128, + owner:ContractAddress + } + + #[constructor] + fn constructor(ref self:ContractState, initial_owner:ContractAddress){ + self.owner.write(initial_owner); + self.count.write(0); + } + + #[abi(embed_v0)] + impl ICounterimpl of ICounter { + + fn get_count(self: @ContractState) -> u128 { + self.count.read() + } + fn get_owner(self: @ContractState) -> ContractAddress{ + self.owner.read() + } + fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress){ + let caller = get_caller_address(); + assert!(caller == self.owner.read(), "Only owner can transfer"); + self.owner.write(new_owner); + } + fn increase_count(ref self: ContractState, amount: u128){ + let caller:ContractAddress = get_caller_address(); + assert!(caller == self.owner.read(), "Only owner can change state"); + + let current = self.count.read(); + let new:u128 = mylogic::add_num(current, amount); + self.count.write(new); + } + fn decrease_count (ref self:ContractState, amount:u128){ + let caller:ContractAddress = get_caller_address(); + assert!(caller == self.owner.read(), "Only owner can change state"); + + let current = self.count.read(); + let new:u128 = mylogic::sub_num(current, amount); + self.count.write(new); + } + fn reset_count(ref self:ContractState){ + let caller = get_caller_address(); + assert!(caller == self.owner.read(), "Only owner can change state"); + let new:u128 = mylogic::reset_count(); + self.count.write(new); + } + } +} \ No newline at end of file diff --git a/yahia008/av2_pro/use_arithmetic/.gitignore b/yahia008/av2_pro/use_arithmetic/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/yahia008/av2_pro/use_arithmetic/.gitignore @@ -0,0 +1 @@ +target diff --git a/yahia008/av2_pro/use_arithmetic/Scarb.lock b/yahia008/av2_pro/use_arithmetic/Scarb.lock new file mode 100644 index 0000000..c8d9a3c --- /dev/null +++ b/yahia008/av2_pro/use_arithmetic/Scarb.lock @@ -0,0 +1,13 @@ +# Code generated by scarb DO NOT EDIT. +version = 1 + +[[package]] +name = "arithmetic_logic" +version = "0.1.0" + +[[package]] +name = "use_arithmetic" +version = "0.1.0" +dependencies = [ + "arithmetic_logic", +] diff --git a/yahia008/av2_pro/use_arithmetic/Scarb.toml b/yahia008/av2_pro/use_arithmetic/Scarb.toml new file mode 100644 index 0000000..3fdc2ec --- /dev/null +++ b/yahia008/av2_pro/use_arithmetic/Scarb.toml @@ -0,0 +1,15 @@ +[package] +name = "use_arithmetic" +version = "0.1.0" +edition = "2025_12" + +# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html + +[executable] + +[cairo] +enable-gas = false + +[dependencies] +cairo_execute = "2.17.0" +arithmetic_logic = {path = "../arithmetic_logic"} diff --git a/yahia008/av2_pro/use_arithmetic/src/hello_world.cairo b/yahia008/av2_pro/use_arithmetic/src/hello_world.cairo new file mode 100644 index 0000000..437ca78 --- /dev/null +++ b/yahia008/av2_pro/use_arithmetic/src/hello_world.cairo @@ -0,0 +1,31 @@ +use arithmetic_logic::mylogic; +#[executable] + +fn main() { + + let add_result = mylogic::add_num(10, 5); + assert!(add_result == 15, "result must be 15 "); + + let sub_result = mylogic::sub_num(10, 5); + assert!(sub_result == 5, "result mudt be 5"); + + let mul_result = mylogic::multiply_number(10, 5); + match mul_result { + Result::Ok(v) => { + assert!(v==50, "assertion failed") + println!("mul value {}", v) + }, + Result::Err(_) => println!("mul error"), + }; + + + let div_result = mylogic::divide(10, 2); + match div_result { + Result::Ok(v) => { + assert!(v==5, "assertion failed") + println!("div value {}", v) + }, + Result::Err(_) => println!("div error", ), + }; + +} diff --git a/yahia008/av2_pro/use_arithmetic/src/lib.cairo b/yahia008/av2_pro/use_arithmetic/src/lib.cairo new file mode 100644 index 0000000..e5eb338 --- /dev/null +++ b/yahia008/av2_pro/use_arithmetic/src/lib.cairo @@ -0,0 +1 @@ +mod hello_world; From b870e5669d814365d2cdc6ebae9316fcbaab3b59 Mon Sep 17 00:00:00 2001 From: yahia008 <120284117+yahia008@users.noreply.github.com> Date: Thu, 30 Apr 2026 08:50:19 +0000 Subject: [PATCH 07/16] change --- yahia008/av2_pro/counterv2_pro/src/lib.cairo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yahia008/av2_pro/counterv2_pro/src/lib.cairo b/yahia008/av2_pro/counterv2_pro/src/lib.cairo index 9b757f5..6d02c9b 100644 --- a/yahia008/av2_pro/counterv2_pro/src/lib.cairo +++ b/yahia008/av2_pro/counterv2_pro/src/lib.cairo @@ -15,13 +15,13 @@ pub trait ICounter { #[starknet::contract] mod CounterV2{ - use crate::mylogic; + use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; use super::ICounter; use starknet::ContractAddress; use starknet::get_caller_address; - + use super::mylogic; #[storage] struct Storage{ From fa7c1c32ace659c07abe92b9476da75a8d841c99 Mon Sep 17 00:00:00 2001 From: yahia008 <120284117+yahia008@users.noreply.github.com> Date: Thu, 30 Apr 2026 10:08:52 +0000 Subject: [PATCH 08/16] jst little refining --- yahia008/av2_pro/arithmetic_logic/src/mylogic.cairo | 3 +++ yahia008/av2_pro/counterv2_pro/src/lib.cairo | 5 +---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/yahia008/av2_pro/arithmetic_logic/src/mylogic.cairo b/yahia008/av2_pro/arithmetic_logic/src/mylogic.cairo index b6dd43a..b91c594 100644 --- a/yahia008/av2_pro/arithmetic_logic/src/mylogic.cairo +++ b/yahia008/av2_pro/arithmetic_logic/src/mylogic.cairo @@ -27,6 +27,9 @@ pub fn sub_num(x: u128, y: u128) -> u128 { } pub fn multiply_number(x:u128, y:u128) -> Result { + if x == 0 || y == 0 { + return Result::Ok(0); + } x.checked_mul(y).ok_or(MathError::Overflow) } diff --git a/yahia008/av2_pro/counterv2_pro/src/lib.cairo b/yahia008/av2_pro/counterv2_pro/src/lib.cairo index 6d02c9b..ea88953 100644 --- a/yahia008/av2_pro/counterv2_pro/src/lib.cairo +++ b/yahia008/av2_pro/counterv2_pro/src/lib.cairo @@ -7,10 +7,7 @@ pub trait ICounter { fn transfer_ownership(ref self: T, new_owner: ContractAddress); fn increase_count(ref self: T, amount: u128); fn decrease_count (ref self:T, amount:u128); - fn reset_count(ref self:T); - - - + fn reset_count(ref self:T); } #[starknet::contract] From 01a009fc1cdd6483fd82d0881a80d12d60f606fe Mon Sep 17 00:00:00 2001 From: yahia008 <120284117+yahia008@users.noreply.github.com> Date: Sun, 10 May 2026 23:21:21 +0000 Subject: [PATCH 09/16] test --- .../av2_pro/counterv2_pro/src/counter.cairo | 76 ++++++ yahia008/av2_pro/counterv2_pro/src/lib.cairo | 76 +----- .../counterv2_pro/tests/test_couter.cairo | 236 ++++++++++++++++++ 3 files changed, 316 insertions(+), 72 deletions(-) create mode 100644 yahia008/av2_pro/counterv2_pro/src/counter.cairo create mode 100644 yahia008/av2_pro/counterv2_pro/tests/test_couter.cairo diff --git a/yahia008/av2_pro/counterv2_pro/src/counter.cairo b/yahia008/av2_pro/counterv2_pro/src/counter.cairo new file mode 100644 index 0000000..7d9c225 --- /dev/null +++ b/yahia008/av2_pro/counterv2_pro/src/counter.cairo @@ -0,0 +1,76 @@ +use arithmetic_logic::mylogic; +use starknet::{ContractAddress}; +#[starknet::interface] +pub trait ICounter { + fn get_count(self: @T) -> u128; + fn get_owner(self: @T) -> ContractAddress; + fn transfer_ownership(ref self: T, new_owner: ContractAddress); + fn increase_count(ref self: T, amount: u128); + fn decrease_count (ref self:T, amount:u128); + fn reset_count(ref self:T); +} + +#[starknet::contract] +pub mod CounterV2{ + +use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; + + use super::ICounter; + use starknet::ContractAddress; + use starknet::get_caller_address; + use super::mylogic; + + #[storage] + struct Storage{ + count:u128, + owner:ContractAddress + } + + #[constructor] + fn constructor(ref self:ContractState, initial_owner:ContractAddress){ + self.owner.write(initial_owner); + self.count.write(0); + } + + #[abi(embed_v0)] + impl ICounterimpl of ICounter { + + fn get_count(self: @ContractState) -> u128 { + self.count.read() + } + fn get_owner(self: @ContractState) -> ContractAddress{ + self.owner.read() + } + fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress){ + let caller = get_caller_address(); + assert!(caller == self.owner.read(), "Only owner can transfer"); + self.owner.write(new_owner); + } + fn increase_count(ref self: ContractState, amount: u128){ + let caller:ContractAddress = get_caller_address(); + assert!(caller == self.owner.read(), "Only owner can change state"); + + let current = self.count.read(); + let new:u128 = mylogic::add_num(current, amount); + self.count.write(new); + } + fn decrease_count (ref self:ContractState, amount:u128){ + let caller:ContractAddress = get_caller_address(); + assert!(caller == self.owner.read(), "Only owner can change state"); + + let current = self.count.read(); + let new:u128 = mylogic::sub_num(current, amount); + self.count.write(new); + } + fn reset_count(ref self:ContractState){ + let caller = get_caller_address(); + assert!(caller == self.owner.read(), "Only owner can change state"); + let current= self.count.read(); + if current != 0 { + let new:u128 = mylogic::reset_count(); + self.count.write(new); + } + // emit it already zero + } + } +} \ No newline at end of file diff --git a/yahia008/av2_pro/counterv2_pro/src/lib.cairo b/yahia008/av2_pro/counterv2_pro/src/lib.cairo index ea88953..22bde39 100644 --- a/yahia008/av2_pro/counterv2_pro/src/lib.cairo +++ b/yahia008/av2_pro/counterv2_pro/src/lib.cairo @@ -1,72 +1,4 @@ -use arithmetic_logic::mylogic; -use starknet::{ContractAddress}; -#[starknet::interface] -pub trait ICounter { - fn get_count(self: @T) -> u128; - fn get_owner(self: @T) -> ContractAddress; - fn transfer_ownership(ref self: T, new_owner: ContractAddress); - fn increase_count(ref self: T, amount: u128); - fn decrease_count (ref self:T, amount:u128); - fn reset_count(ref self:T); -} - -#[starknet::contract] -mod CounterV2{ - -use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; - - use super::ICounter; - use starknet::ContractAddress; - use starknet::get_caller_address; - use super::mylogic; - - #[storage] - struct Storage{ - count:u128, - owner:ContractAddress - } - - #[constructor] - fn constructor(ref self:ContractState, initial_owner:ContractAddress){ - self.owner.write(initial_owner); - self.count.write(0); - } - - #[abi(embed_v0)] - impl ICounterimpl of ICounter { - - fn get_count(self: @ContractState) -> u128 { - self.count.read() - } - fn get_owner(self: @ContractState) -> ContractAddress{ - self.owner.read() - } - fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress){ - let caller = get_caller_address(); - assert!(caller == self.owner.read(), "Only owner can transfer"); - self.owner.write(new_owner); - } - fn increase_count(ref self: ContractState, amount: u128){ - let caller:ContractAddress = get_caller_address(); - assert!(caller == self.owner.read(), "Only owner can change state"); - - let current = self.count.read(); - let new:u128 = mylogic::add_num(current, amount); - self.count.write(new); - } - fn decrease_count (ref self:ContractState, amount:u128){ - let caller:ContractAddress = get_caller_address(); - assert!(caller == self.owner.read(), "Only owner can change state"); - - let current = self.count.read(); - let new:u128 = mylogic::sub_num(current, amount); - self.count.write(new); - } - fn reset_count(ref self:ContractState){ - let caller = get_caller_address(); - assert!(caller == self.owner.read(), "Only owner can change state"); - let new:u128 = mylogic::reset_count(); - self.count.write(new); - } - } -} \ No newline at end of file +mod counter; +pub use counter::CounterV2; +pub use counter::ICounterDispatcher; +pub use counter::ICounterDispatcherTrait; \ No newline at end of file diff --git a/yahia008/av2_pro/counterv2_pro/tests/test_couter.cairo b/yahia008/av2_pro/counterv2_pro/tests/test_couter.cairo new file mode 100644 index 0000000..84f1c32 --- /dev/null +++ b/yahia008/av2_pro/counterv2_pro/tests/test_couter.cairo @@ -0,0 +1,236 @@ +use starknet::SyscallResultTrait; +use snforge_std::DeclareResultTrait; +use counterv2_pro::CounterV2; +use starknet::testing::{ + set_contract_address, set_caller_address +}; +use snforge_std::{declare, ContractClassTrait, start_cheat_caller_address, }; +use counterv2_pro::{ICounterDispatcher, ICounterDispatcherTrait}; +use starknet::syscalls::deploy_syscall; +use starknet::ContractAddress; +use starknet::contract_address_const; + +const U128_MAX: u128 = 340282366920938463463374607431768211455; + + fn owner() -> ContractAddress { + + contract_address_const::<0x1>() + } + + fn attacker() -> ContractAddress { + contract_address_const::<0x2>() + } + + fn new_owner() -> ContractAddress { + contract_address_const::<0x3>() + } + +fn deploy_contract(initial_owner: ContractAddress) -> ICounterDispatcher { + let contract = declare("CounterV2").unwrap_syscall().contract_class(); + + let mut calldata = array![initial_owner.into()]; + + let (contract_address, _) = contract.deploy(@calldata).unwrap_syscall(); + + ICounterDispatcher { contract_address } +} + +#[cfg(test)] +mod tests { + use super::*; + + use super::new_owner; +use counterv2_pro::ICounterDispatcherTrait; +use snforge_std::start_cheat_caller_address; +use super::deploy_contract; +use super::attacker; +use super::owner; +use starknet::SyscallResultTrait; + #[test] + fn test_constructor(){ + let initial_owner = owner(); + let counter = deploy_contract(initial_owner); + assert!(counter.get_count() == 0, "initial count should be zero") + assert!(counter.get_owner() == initial_owner, "owner is initial owner"); + } + + #[test] + fn test_increase_count() { + let owner = owner(); + let counter = deploy_contract(owner); + + start_cheat_caller_address(counter.contract_address, owner); + + counter.increase_count(5); + assert_eq!(counter.get_count(), 5); + + counter.increase_count(3); + assert_eq!(counter.get_count(), 8); + } + + #[test] + #[should_panic(expected: "Only owner can change state")] + fn test_increase_count_not_owner() { + let owner = owner(); + let counter = deploy_contract(owner); + + let attacker = attacker(); + start_cheat_caller_address(counter.contract_address, attacker); + + counter.increase_count(5); // Should panic + } + #[test] + fn test_increase_count_by_zero() { + let owner = owner(); + let counter = deploy_contract(owner); + + start_cheat_caller_address(counter.contract_address, owner); + counter.increase_count(0); + assert_eq!(counter.get_count(), 0); + + counter.increase_count(5); + counter.increase_count(0); + + assert_eq!(counter.get_count(), 5,); + + } + + #[test] + #[should_panic] + + fn test_increase_count_overflow() { + let owner = owner(); + let counter = deploy_contract(owner); + + start_cheat_caller_address(counter.contract_address, owner); + + counter.increase_count(U128_MAX); + assert_eq!(counter.get_count(), U128_MAX); + + counter.increase_count(1); +} + + #[test] + fn test_decrease_count_by_owner() { + let owner = owner(); + let counter = deploy_contract(owner); + + start_cheat_caller_address(counter.contract_address, owner); + counter.increase_count(20); + counter.decrease_count(7); + assert!(counter.get_count() == 13, "Count should be 13 after decrease"); + } + + + #[test] + fn test_decrease_count_to_zero() { + let owner = owner(); + let counter = deploy_contract(owner); + start_cheat_caller_address(counter.contract_address, owner); + counter.increase_count(5); + counter.decrease_count(5); + assert!(counter.get_count() == 0, "Count should reach exactly 0"); + } + + + #[test] + #[should_panic] + fn test_decrease_count_below_zero_panics() { + let owner = owner(); + let counter = deploy_contract(owner); + + start_cheat_caller_address(counter.contract_address, owner); + + counter.decrease_count(1); // count is 0, underflow expected + } + + + #[test] + #[should_panic(expected: "Only owner can change state")] + fn test_decrease_count_by_non_owner_panics() { + let owner = owner(); + let counter = deploy_contract(owner); + counter.increase_count(10); + + start_cheat_caller_address(counter.contract_address, attacker()); + counter.decrease_count(5); + } + + #[test] + fn test_decrease_count_by_zero() { + let owner = owner(); + let counter = deploy_contract(owner); + + start_cheat_caller_address(counter.contract_address, owner); + counter.increase_count(10); + assert_eq!(counter.get_count(), 10); + + counter.decrease_count(0); + + assert_eq!(counter.get_count(), 10,); + + } + + #[test] + fn test_reset_count() { + let owner = contract_address_const::<'owner'>(); + let counter = deploy_contract(owner); + + start_cheat_caller_address(counter.contract_address, owner); + + counter.reset_count(); + assert_eq!(counter.get_count(), 0); + + counter.increase_count(42); + assert_eq!(counter.get_count(), 42); + + counter.reset_count(); + assert_eq!(counter.get_count(), 0); + + counter.reset_count(); + assert_eq!(counter.get_count(), 0); + } + + + #[test] + #[should_panic(expected: "Only owner can change state")] + fn test_reset_count_not_owner() { + let owner = contract_address_const::<'owner'>(); + let counter = deploy_contract(owner); + + let attacker = attacker(); + start_cheat_caller_address(counter.contract_address, attacker); + + counter.reset_count(); + } + + #[test] + fn test_transfer_ownership() { + let owner = owner(); + let new_owner = new_owner(); + let counter = deploy_contract(owner); + + start_cheat_caller_address(counter.contract_address, owner); + + counter.transfer_ownership(new_owner); + assert_eq!(counter.get_owner(), new_owner); + + // New owner should now have access + start_cheat_caller_address(counter.contract_address, new_owner); + counter.increase_count(5); + assert_eq!(counter.get_count(), 5); + } + + #[test] + #[should_panic(expected: "Only owner can transfer")] + fn test_transfer_ownership_not_owner() { + let owner = owner(); + let counter = deploy_contract(owner); + + let attacker = attacker(); + start_cheat_caller_address(counter.contract_address, attacker); + + counter.transfer_ownership(attacker); // Should panic + } + + } From fd0278f0d88ccbc372393b70a86c259cd2d88e79 Mon Sep 17 00:00:00 2001 From: yahia008 <120284117+yahia008@users.noreply.github.com> Date: Wed, 20 May 2026 16:18:55 +0000 Subject: [PATCH 10/16] version1 --- .../counterv2_pro/tests/test_couter.cairo | 1 - yahia008/blockheader_test/.gitignore | 5 + yahia008/blockheader_test/Scarb.lock | 24 ++ yahia008/blockheader_test/Scarb.toml | 52 +++ yahia008/blockheader_test/snfoundry.toml | 11 + yahia008/blockheader_test/src/lib.cairo | 1 + yahia008/blockheader_test/src/token.cairo | 311 ++++++++++++++++++ 7 files changed, 404 insertions(+), 1 deletion(-) create mode 100644 yahia008/blockheader_test/.gitignore create mode 100644 yahia008/blockheader_test/Scarb.lock create mode 100644 yahia008/blockheader_test/Scarb.toml create mode 100644 yahia008/blockheader_test/snfoundry.toml create mode 100644 yahia008/blockheader_test/src/lib.cairo create mode 100644 yahia008/blockheader_test/src/token.cairo diff --git a/yahia008/av2_pro/counterv2_pro/tests/test_couter.cairo b/yahia008/av2_pro/counterv2_pro/tests/test_couter.cairo index 84f1c32..5b5afcd 100644 --- a/yahia008/av2_pro/counterv2_pro/tests/test_couter.cairo +++ b/yahia008/av2_pro/counterv2_pro/tests/test_couter.cairo @@ -132,7 +132,6 @@ use starknet::SyscallResultTrait; assert!(counter.get_count() == 0, "Count should reach exactly 0"); } - #[test] #[should_panic] fn test_decrease_count_below_zero_panics() { diff --git a/yahia008/blockheader_test/.gitignore b/yahia008/blockheader_test/.gitignore new file mode 100644 index 0000000..4096f8b --- /dev/null +++ b/yahia008/blockheader_test/.gitignore @@ -0,0 +1,5 @@ +target +.snfoundry_cache/ +snfoundry_trace/ +coverage/ +profile/ diff --git a/yahia008/blockheader_test/Scarb.lock b/yahia008/blockheader_test/Scarb.lock new file mode 100644 index 0000000..d13958f --- /dev/null +++ b/yahia008/blockheader_test/Scarb.lock @@ -0,0 +1,24 @@ +# Code generated by scarb DO NOT EDIT. +version = 1 + +[[package]] +name = "blockheader_test" +version = "0.1.0" +dependencies = [ + "snforge_std", +] + +[[package]] +name = "snforge_scarb_plugin" +version = "0.59.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:871fba677c03b66a1bf40815dac0ab1b385eb1b9be6e6c3cf2ad9788eeb2b6bb" + +[[package]] +name = "snforge_std" +version = "0.59.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:3620924fa08bd2d740b2b5b01ef86c8dab3d4b9c2206387c8dbdc8d2ec15133e" +dependencies = [ + "snforge_scarb_plugin", +] diff --git a/yahia008/blockheader_test/Scarb.toml b/yahia008/blockheader_test/Scarb.toml new file mode 100644 index 0000000..2407a1f --- /dev/null +++ b/yahia008/blockheader_test/Scarb.toml @@ -0,0 +1,52 @@ +[package] +name = "blockheader_test" +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.17.0" + +[dev-dependencies] +snforge_std = "0.59.0" +assert_macros = "2.17.0" + +[[target.starknet-contract]] +sierra = true + +[scripts] +test = "snforge test" + +[tool.scarb] +allow-prebuilt-plugins = ["snforge_std"] + +# Visit https://foundry-rs.github.io/starknet-foundry/appendix/scarb-toml.html for more information + +# [tool.snforge] # Define `snforge` tool section +# exit_first = true # Stop tests execution immediately upon the first failure +# fuzzer_runs = 1234 # Number of runs of the random fuzzer +# fuzzer_seed = 1111 # Seed for the random fuzzer + +# [[tool.snforge.fork]] # Used for fork testing +# name = "SOME_NAME" # Fork name +# url = "http://your.rpc.url" # Url of the RPC provider +# block_id.tag = "latest" # Block to fork from (block tag) + +# [[tool.snforge.fork]] +# name = "SOME_SECOND_NAME" +# url = "http://your.second.rpc.url" +# block_id.number = "123" # Block to fork from (block number) + +# [[tool.snforge.fork]] +# name = "SOME_THIRD_NAME" +# url = "http://your.third.rpc.url" +# block_id.hash = "0x123" # Block to fork from (block hash) + +# [profile.dev.cairo] # Configure Cairo compiler +# unstable-add-statements-code-locations-debug-info = true # Should be used if you want to use coverage +# unstable-add-statements-functions-debug-info = true # Should be used if you want to use coverage/profiler +# inlining-strategy = "avoid" # Should be used if you want to use coverage + +# [features] # Used for conditional compilation +# enable_for_tests = [] # Feature name and list of other features that should be enabled with it diff --git a/yahia008/blockheader_test/snfoundry.toml b/yahia008/blockheader_test/snfoundry.toml new file mode 100644 index 0000000..686c2ab --- /dev/null +++ b/yahia008/blockheader_test/snfoundry.toml @@ -0,0 +1,11 @@ +# Visit https://foundry-rs.github.io/starknet-foundry/appendix/snfoundry-toml.html +# and https://foundry-rs.github.io/starknet-foundry/projects/configuration.html for more information + +# [sncast.default] # Define a profile name +# url = "https://api.zan.top/public/starknet-sepolia/rpc/v0_10" # Url of the RPC provider +# accounts-file = "../account-file" # Path to the file with the account data +# account = "mainuser" # Account from `accounts_file` or default account file that will be used for the transactions +# keystore = "~/keystore" # Path to the keystore file +# wait-params = { timeout = 300, retry-interval = 10 } # Wait for submitted transaction parameters +# block-explorer = "Voyager" # Block explorer service used to display links to transaction details +# show-explorer-links = true # Print links pointing to pages with transaction details in the chosen block explorer diff --git a/yahia008/blockheader_test/src/lib.cairo b/yahia008/blockheader_test/src/lib.cairo new file mode 100644 index 0000000..2108f68 --- /dev/null +++ b/yahia008/blockheader_test/src/lib.cairo @@ -0,0 +1 @@ +pub mod token; \ No newline at end of file diff --git a/yahia008/blockheader_test/src/token.cairo b/yahia008/blockheader_test/src/token.cairo new file mode 100644 index 0000000..4e87506 --- /dev/null +++ b/yahia008/blockheader_test/src/token.cairo @@ -0,0 +1,311 @@ +use starknet::ContractAddress; + +#[starknet::interface] +pub trait IERC20 { + fn get_name(self: @TContractState) -> felt252; + fn get_symbol(self: @TContractState) -> felt252; + fn get_decimals(self: @TContractState) -> u8; + fn get_total_supply(self: @TContractState) -> u256; + fn balance_of(self: @TContractState, account: ContractAddress) -> u256; + fn allowance(self: @TContractState, owner: ContractAddress, spender: ContractAddress) -> u256; + fn transfer(ref self: TContractState, recipient: ContractAddress, amount: u256); + fn transfer_from(ref self: TContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256); + fn approve(ref self: TContractState, spender: ContractAddress, amount: u256); + fn increase_allowance(ref self: TContractState, spender: ContractAddress, added_value: u256); + fn decrease_allowance(ref self: TContractState, spender: ContractAddress, subtracted_value: u256); +} + +#[starknet::interface] +pub trait IAdminRestricted { + fn set_transfer_limit(ref self: TContractState, new_limit: u256); + fn burn(ref self: TContractState, account: ContractAddress, amount: u256); + fn revoke_transfers(ref self: TContractState); + fn enable_transfers(ref self: TContractState); + fn get_transfer_limit(self: @TContractState) -> u256; + fn is_transfers_enabled(self: @TContractState) -> bool; + fn admin_revoke_spender(ref self: TContractState, owner: ContractAddress, spender: ContractAddress); +} + +#[starknet::contract] +pub mod erc20 { + use core::num::traits::Zero; + use starknet::get_caller_address; + use starknet::contract_address_const; + use starknet::ContractAddress; + use starknet::storage::{ + Map, StorageMapReadAccess, StorageMapWriteAccess, StoragePointerReadAccess, + StoragePointerWriteAccess, + }; + use core::num::traits::Bounded; + + #[storage] + struct Storage { + name: felt252, + symbol: felt252, + decimals: u8, + total_supply: u256, + balances: Map::, + allowances: Map::<(ContractAddress, ContractAddress), u256>, + revoked_user: Map::, + admin: ContractAddress, + transfer_limit: u256, + transfers_enabled: bool, + } + + #[event] + #[derive(Copy, Drop, Debug, PartialEq, starknet::Event)] + pub enum Event { + Transfer: Transfer, + Approval: Approval, + TransferLimitUpdated: TransferLimitUpdated, + TransfersRevoked: TransfersRevoked, + TransfersEnabled: TransfersEnabled, + Burn: Burn, + } + + #[derive(Copy, Drop, Debug, PartialEq, starknet::Event)] + pub struct Transfer { + #[key] + pub from: ContractAddress, + #[key] + pub to: ContractAddress, + pub value: u256, + } + + #[derive(Copy, Drop, Debug, PartialEq, starknet::Event)] + pub struct Approval { + #[key] + pub owner: ContractAddress, + #[key] + pub spender: ContractAddress, + pub value: u256, + } + + #[derive(Copy, Drop, Debug, PartialEq, starknet::Event)] + pub struct TransferLimitUpdated { + pub old_limit: u256, + pub new_limit: u256, + pub updated_by: ContractAddress, + } + + #[derive(Copy, Drop, Debug, PartialEq, starknet::Event)] + pub struct TransfersRevoked { + pub revoked_by: ContractAddress, + } + + #[derive(Copy, Drop, Debug, PartialEq, starknet::Event)] + pub struct TransfersEnabled { + pub enabled_by: ContractAddress, + } + + #[derive(Copy, Drop, Debug, PartialEq, starknet::Event)] + pub struct Burn { + #[key] + pub from: ContractAddress, + pub value: u256, + pub burned_by: ContractAddress, + } + + mod Errors { + pub const APPROVE_TO_ZERO: felt252 = 'ERC20: approve to 0'; + pub const TRANSFER_FROM_ZERO: felt252 = 'ERC20: transfer from 0'; + pub const TRANSFER_TO_ZERO: felt252 = 'ERC20: transfer to 0'; + pub const BURN_FROM_ZERO: felt252 = 'ERC20: burn from 0'; + pub const MINT_TO_ZERO: felt252 = 'ERC20: mint to 0'; + pub const NOT_ADMIN: felt252 = 'ERC20: not admin'; + pub const TRANSFERS_DISABLED: felt252 = 'ERC20: transfers disabled'; + pub const EXCEEDS_MAX_LIMIT: felt252 = 'ERC20: exceeds max limit'; + pub const INSUFFICIENT_BALANCE: felt252 = 'ERC20: insufficient balance'; + pub const INVALID_LIMIT: felt252 = 'ERC20: limit must be >0'; + pub const BURN_AMOUNT_ZERO: felt252 = 'ERC20: burn amount >0'; + pub const INSUFFICIENT_ALLOWANCE: felt252 = 'ERC20: insufficient allowance'; + pub const SPENDER_REVOKED: felt252 = 'ERC20: spender revoked'; + pub const ZERO_ADDRESS: felt252 = 'ERC20: zero address'; + } + + // Adjusted limit assuming 18 decimals for context, tweak as needed! + const MAX_LIMIT: u256 = 10000; + + #[constructor] + fn constructor( + ref self: ContractState, + recipient: ContractAddress, + name: felt252, + decimals: u8, + initial_supply: u256, + symbol: felt252, + admin: ContractAddress, + ) { + self.name.write(name); + self.symbol.write(symbol); + self.decimals.write(decimals); + self.admin.write(admin); + self.transfer_limit.write(MAX_LIMIT); + self.transfers_enabled.write(true); + self.mint(recipient, initial_supply); + } + + #[abi(embed_v0)] + impl IERC20Impl of super::IERC20 { + fn get_name(self: @ContractState) -> felt252 { self.name.read() } + fn get_symbol(self: @ContractState) -> felt252 { self.symbol.read() } + fn get_decimals(self: @ContractState) -> u8 { self.decimals.read() } + fn get_total_supply(self: @ContractState) -> u256 { self.total_supply.read() } + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { self.balances.read(account) } + fn allowance(self: @ContractState, owner: ContractAddress, spender: ContractAddress) -> u256 { + self.allowances.read((owner, spender)) + } + + fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) { + let sender = get_caller_address(); + self._validate_transfer(sender, recipient, amount); + self._transfer(sender, recipient, amount); + } + + fn transfer_from( + ref self: ContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256, + ) { + let caller = get_caller_address(); + + + assert(!self.revoked_user.read(caller), Errors::SPENDER_REVOKED); + + self._validate_transfer(sender, recipient, amount); + self.spend_allowance(sender, caller, amount); + self._transfer(sender, recipient, amount); + } + + fn approve(ref self: ContractState, spender: ContractAddress, amount: u256) { + let caller = get_caller_address(); + assert(!self.revoked_user.read(spender), Errors::SPENDER_REVOKED); + self.approve_helper(caller, spender, amount); + } + + fn increase_allowance(ref self: ContractState, spender: ContractAddress, added_value: u256) { + let caller = get_caller_address(); + assert(!self.revoked_user.read(spender), Errors::SPENDER_REVOKED); + let current_allowance = self.allowances.read((caller, spender)); + self.approve_helper(caller, spender, current_allowance + added_value); + } + + fn decrease_allowance(ref self: ContractState, spender: ContractAddress, subtracted_value: u256) { + let caller = get_caller_address(); + let current_allowance = self.allowances.read((caller, spender)); + assert(current_allowance >= subtracted_value, Errors::INSUFFICIENT_ALLOWANCE); + self.approve_helper(caller, spender, current_allowance - subtracted_value); + } + } + + #[abi(embed_v0)] + impl IAdminRestrictedImpl of super::IAdminRestricted { + fn set_transfer_limit(ref self: ContractState, new_limit: u256) { + self._only_admin(); + assert(new_limit > 0, Errors::INVALID_LIMIT); + assert(new_limit <= MAX_LIMIT, Errors::EXCEEDS_MAX_LIMIT); + + let old_limit = self.transfer_limit.read(); + self.transfer_limit.write(new_limit); + + self.emit(TransferLimitUpdated { old_limit, new_limit, updated_by: get_caller_address() }); + } + + fn burn(ref self: ContractState, account: ContractAddress, amount: u256) { + self._only_admin(); + assert(account.is_non_zero(), Errors::BURN_FROM_ZERO); + assert(amount > 0, Errors::BURN_AMOUNT_ZERO); + + let balance = self.balances.read(account); + assert(balance >= amount, Errors::INSUFFICIENT_BALANCE); + + self.balances.write(account, balance - amount); + self.total_supply.write(self.total_supply.read() - amount); + + self.emit(Burn { from: account, value: amount, burned_by: get_caller_address() }); + self.emit(Transfer { from: account, to: contract_address_const::<0>(), value: amount }); + } + + fn revoke_transfers(ref self: ContractState) { + self._only_admin(); + self.transfers_enabled.write(false); + self.emit(TransfersRevoked { revoked_by: get_caller_address() }); + } + + fn enable_transfers(ref self: ContractState) { + self._only_admin(); + self.transfers_enabled.write(true); + self.emit(TransfersEnabled { enabled_by: get_caller_address() }); + } + + fn get_transfer_limit(self: @ContractState) -> u256 { self.transfer_limit.read() } + fn is_transfers_enabled(self: @ContractState) -> bool { self.transfers_enabled.read() } + + fn admin_revoke_spender( + ref self: ContractState, owner: ContractAddress, spender: ContractAddress + ) { + self._only_admin(); + assert(owner.is_non_zero() && spender.is_non_zero(), Errors::ZERO_ADDRESS); + + let is_already_revoked = self.revoked_user.read(spender); + assert(!is_already_revoked, 'ERC20: already revoked'); + + self.approve_helper(owner, spender, 0); + self.revoked_user.write(spender, true); + } + } + + #[generate_trait] + impl InternalImpl of InternalTrait { + fn _only_admin(self: @ContractState) { + let caller = get_caller_address(); + assert(caller == self.admin.read(), Errors::NOT_ADMIN); + } + + fn _validate_transfer( + self: @ContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256, + ) { + assert(sender.is_non_zero(), Errors::TRANSFER_FROM_ZERO); + assert(recipient.is_non_zero(), Errors::TRANSFER_TO_ZERO); + assert(self.transfers_enabled.read(), Errors::TRANSFERS_DISABLED); + + let current_limit = self.transfer_limit.read(); + assert(amount <= current_limit, Errors::EXCEEDS_MAX_LIMIT); + + let sender_balance = self.balances.read(sender); + assert(sender_balance >= amount, Errors::INSUFFICIENT_BALANCE); + } + + fn _transfer( + ref self: ContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256, + ) { + let sender_balance = self.balances.read(sender); + self.balances.write(sender, sender_balance - amount); + self.balances.write(recipient, self.balances.read(recipient) + amount); + self.emit(Transfer { from: sender, to: recipient, value: amount }); + } + + fn spend_allowance( + ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256, + ) { + let current_allowance = self.allowances.read((owner, spender)); + if current_allowance != Bounded::MAX { + assert(current_allowance >= amount, Errors::INSUFFICIENT_ALLOWANCE); + self.allowances.write((owner, spender), current_allowance - amount); + } + } + + fn approve_helper( + ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256, + ) { + assert(spender.is_non_zero(), Errors::APPROVE_TO_ZERO); + self.allowances.write((owner, spender), amount); + self.emit(Approval { owner, spender, value: amount }); + } + + fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) { + assert(recipient.is_non_zero(), Errors::MINT_TO_ZERO); + self.total_supply.write(self.total_supply.read() + amount); + self.balances.write(recipient, self.balances.read(recipient) + amount); + self.emit(Transfer { from: contract_address_const::<0>(), to: recipient, value: amount }); + } + } +} \ No newline at end of file From 3a051e536a77a1f303d7f8add93bf49fc1ef2e08 Mon Sep 17 00:00:00 2001 From: yahia008 <120284117+yahia008@users.noreply.github.com> Date: Wed, 20 May 2026 16:55:04 +0000 Subject: [PATCH 11/16] update --- yahia008/blockheader_test/src/token.cairo | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/yahia008/blockheader_test/src/token.cairo b/yahia008/blockheader_test/src/token.cairo index 4e87506..c565dc2 100644 --- a/yahia008/blockheader_test/src/token.cairo +++ b/yahia008/blockheader_test/src/token.cairo @@ -124,7 +124,7 @@ pub mod erc20 { } // Adjusted limit assuming 18 decimals for context, tweak as needed! - const MAX_LIMIT: u256 = 10000; + const MAX_LIMIT: u256 = 10000_u256; #[constructor] fn constructor( @@ -136,6 +136,7 @@ pub mod erc20 { symbol: felt252, admin: ContractAddress, ) { + assert(admin.is_non_zero(), Errors::ZERO_ADDRESS); self.name.write(name); self.symbol.write(symbol); self.decimals.write(decimals); @@ -277,6 +278,8 @@ pub mod erc20 { fn _transfer( ref self: ContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256, ) { + assert(sender.is_non_zero(), Errors::ZERO_ADDRESS); + assert(recipient.is_non_zero(), Errors::ZERO_ADDRESS); let sender_balance = self.balances.read(sender); self.balances.write(sender, sender_balance - amount); self.balances.write(recipient, self.balances.read(recipient) + amount); From eeca3636ab79054c45e64ba07f40ac2a58f740ab Mon Sep 17 00:00:00 2001 From: yahia008 Date: Thu, 21 May 2026 18:03:42 +0100 Subject: [PATCH 12/16] update --- yahia008/blockheader_test/src/token.cairo | 130 +++++++++++++++------- 1 file changed, 87 insertions(+), 43 deletions(-) diff --git a/yahia008/blockheader_test/src/token.cairo b/yahia008/blockheader_test/src/token.cairo index c565dc2..e70aca7 100644 --- a/yahia008/blockheader_test/src/token.cairo +++ b/yahia008/blockheader_test/src/token.cairo @@ -9,10 +9,14 @@ pub trait IERC20 { fn balance_of(self: @TContractState, account: ContractAddress) -> u256; fn allowance(self: @TContractState, owner: ContractAddress, spender: ContractAddress) -> u256; fn transfer(ref self: TContractState, recipient: ContractAddress, amount: u256); - fn transfer_from(ref self: TContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256); + fn transfer_from( + ref self: TContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256, + ); fn approve(ref self: TContractState, spender: ContractAddress, amount: u256); fn increase_allowance(ref self: TContractState, spender: ContractAddress, added_value: u256); - fn decrease_allowance(ref self: TContractState, spender: ContractAddress, subtracted_value: u256); + fn decrease_allowance( + ref self: TContractState, spender: ContractAddress, subtracted_value: u256, + ); } #[starknet::interface] @@ -23,20 +27,19 @@ pub trait IAdminRestricted { fn enable_transfers(ref self: TContractState); fn get_transfer_limit(self: @TContractState) -> u256; fn is_transfers_enabled(self: @TContractState) -> bool; - fn admin_revoke_spender(ref self: TContractState, owner: ContractAddress, spender: ContractAddress); + fn admin_revoke_spender( + ref self: TContractState, owner: ContractAddress, spender: ContractAddress, + ); } #[starknet::contract] pub mod erc20 { - use core::num::traits::Zero; - use starknet::get_caller_address; - use starknet::contract_address_const; - use starknet::ContractAddress; + use core::num::traits::{Bounded, Zero}; use starknet::storage::{ Map, StorageMapReadAccess, StorageMapWriteAccess, StoragePointerReadAccess, StoragePointerWriteAccess, }; - use core::num::traits::Bounded; + use starknet::{ContractAddress, contract_address_const, get_caller_address}; #[storage] struct Storage { @@ -44,9 +47,9 @@ pub mod erc20 { symbol: felt252, decimals: u8, total_supply: u256, - balances: Map::, - allowances: Map::<(ContractAddress, ContractAddress), u256>, - revoked_user: Map::, + balances: Map, + allowances: Map<(ContractAddress, ContractAddress), u256>, + revoked_user: Map, admin: ContractAddress, transfer_limit: u256, transfers_enabled: bool, @@ -122,9 +125,9 @@ pub mod erc20 { pub const SPENDER_REVOKED: felt252 = 'ERC20: spender revoked'; pub const ZERO_ADDRESS: felt252 = 'ERC20: zero address'; } - + // Adjusted limit assuming 18 decimals for context, tweak as needed! - const MAX_LIMIT: u256 = 10000_u256; + const MAX_LIMIT: u256 = 10000_u256; #[constructor] fn constructor( @@ -148,12 +151,24 @@ pub mod erc20 { #[abi(embed_v0)] impl IERC20Impl of super::IERC20 { - fn get_name(self: @ContractState) -> felt252 { self.name.read() } - fn get_symbol(self: @ContractState) -> felt252 { self.symbol.read() } - fn get_decimals(self: @ContractState) -> u8 { self.decimals.read() } - fn get_total_supply(self: @ContractState) -> u256 { self.total_supply.read() } - fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { self.balances.read(account) } - fn allowance(self: @ContractState, owner: ContractAddress, spender: ContractAddress) -> u256 { + fn get_name(self: @ContractState) -> felt252 { + self.name.read() + } + fn get_symbol(self: @ContractState) -> felt252 { + self.symbol.read() + } + fn get_decimals(self: @ContractState) -> u8 { + self.decimals.read() + } + fn get_total_supply(self: @ContractState) -> u256 { + self.total_supply.read() + } + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { + self.balances.read(account) + } + fn allowance( + self: @ContractState, owner: ContractAddress, spender: ContractAddress, + ) -> u256 { self.allowances.read((owner, spender)) } @@ -164,13 +179,15 @@ pub mod erc20 { } fn transfer_from( - ref self: ContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256, + ref self: ContractState, + sender: ContractAddress, + recipient: ContractAddress, + amount: u256, ) { let caller = get_caller_address(); - - + assert(!self.revoked_user.read(caller), Errors::SPENDER_REVOKED); - + self._validate_transfer(sender, recipient, amount); self.spend_allowance(sender, caller, amount); self._transfer(sender, recipient, amount); @@ -179,17 +196,24 @@ pub mod erc20 { fn approve(ref self: ContractState, spender: ContractAddress, amount: u256) { let caller = get_caller_address(); assert(!self.revoked_user.read(spender), Errors::SPENDER_REVOKED); + assert(amount != 0, 'amount cant be zero'); self.approve_helper(caller, spender, amount); } - fn increase_allowance(ref self: ContractState, spender: ContractAddress, added_value: u256) { + fn increase_allowance( + ref self: ContractState, spender: ContractAddress, added_value: u256, + ) { + self._only_admin(); let caller = get_caller_address(); assert(!self.revoked_user.read(spender), Errors::SPENDER_REVOKED); let current_allowance = self.allowances.read((caller, spender)); self.approve_helper(caller, spender, current_allowance + added_value); } - fn decrease_allowance(ref self: ContractState, spender: ContractAddress, subtracted_value: u256) { + fn decrease_allowance( + ref self: ContractState, spender: ContractAddress, subtracted_value: u256, + ) { + self._only_admin(); let caller = get_caller_address(); let current_allowance = self.allowances.read((caller, spender)); assert(current_allowance >= subtracted_value, Errors::INSUFFICIENT_ALLOWANCE); @@ -202,53 +226,66 @@ pub mod erc20 { fn set_transfer_limit(ref self: ContractState, new_limit: u256) { self._only_admin(); assert(new_limit > 0, Errors::INVALID_LIMIT); - assert(new_limit <= MAX_LIMIT, Errors::EXCEEDS_MAX_LIMIT); - + //assert(new_limit <= MAX_LIMIT, Errors::EXCEEDS_MAX_LIMIT); + let old_limit = self.transfer_limit.read(); self.transfer_limit.write(new_limit); - - self.emit(TransferLimitUpdated { old_limit, new_limit, updated_by: get_caller_address() }); + + self + .emit( + TransferLimitUpdated { old_limit, new_limit, updated_by: get_caller_address() }, + ); } fn burn(ref self: ContractState, account: ContractAddress, amount: u256) { self._only_admin(); assert(account.is_non_zero(), Errors::BURN_FROM_ZERO); assert(amount > 0, Errors::BURN_AMOUNT_ZERO); - + let balance = self.balances.read(account); assert(balance >= amount, Errors::INSUFFICIENT_BALANCE); - + self.balances.write(account, balance - amount); self.total_supply.write(self.total_supply.read() - amount); - + + let zero_address = Zero::zero(); + let zero_balance = self.balances.read(zero_address); + self.balances.write(zero_address, zero_balance + amount); + self.emit(Burn { from: account, value: amount, burned_by: get_caller_address() }); - self.emit(Transfer { from: account, to: contract_address_const::<0>(), value: amount }); + self.emit(Transfer { from: account, to: zero_address, value: amount }); } fn revoke_transfers(ref self: ContractState) { self._only_admin(); + assert(self.transfers_enabled.read() == true, 'transfer not enabled'); self.transfers_enabled.write(false); self.emit(TransfersRevoked { revoked_by: get_caller_address() }); } fn enable_transfers(ref self: ContractState) { self._only_admin(); + assert(self.transfers_enabled.read() == false, 'transfer enabled'); self.transfers_enabled.write(true); self.emit(TransfersEnabled { enabled_by: get_caller_address() }); } - fn get_transfer_limit(self: @ContractState) -> u256 { self.transfer_limit.read() } - fn is_transfers_enabled(self: @ContractState) -> bool { self.transfers_enabled.read() } + fn get_transfer_limit(self: @ContractState) -> u256 { + self.transfer_limit.read() + } + fn is_transfers_enabled(self: @ContractState) -> bool { + self.transfers_enabled.read() + } fn admin_revoke_spender( - ref self: ContractState, owner: ContractAddress, spender: ContractAddress + ref self: ContractState, owner: ContractAddress, spender: ContractAddress, ) { self._only_admin(); assert(owner.is_non_zero() && spender.is_non_zero(), Errors::ZERO_ADDRESS); - + let is_already_revoked = self.revoked_user.read(spender); assert(!is_already_revoked, 'ERC20: already revoked'); - + self.approve_helper(owner, spender, 0); self.revoked_user.write(spender, true); } @@ -267,17 +304,21 @@ pub mod erc20 { assert(sender.is_non_zero(), Errors::TRANSFER_FROM_ZERO); assert(recipient.is_non_zero(), Errors::TRANSFER_TO_ZERO); assert(self.transfers_enabled.read(), Errors::TRANSFERS_DISABLED); - + let current_limit = self.transfer_limit.read(); assert(amount <= current_limit, Errors::EXCEEDS_MAX_LIMIT); - + let sender_balance = self.balances.read(sender); assert(sender_balance >= amount, Errors::INSUFFICIENT_BALANCE); } fn _transfer( - ref self: ContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256, + ref self: ContractState, + sender: ContractAddress, + recipient: ContractAddress, + amount: u256, ) { + // assert(self.transfers_enabled.read() == true, 'transfer is not enable'); assert(sender.is_non_zero(), Errors::ZERO_ADDRESS); assert(recipient.is_non_zero(), Errors::ZERO_ADDRESS); let sender_balance = self.balances.read(sender); @@ -308,7 +349,10 @@ pub mod erc20 { assert(recipient.is_non_zero(), Errors::MINT_TO_ZERO); self.total_supply.write(self.total_supply.read() + amount); self.balances.write(recipient, self.balances.read(recipient) + amount); - self.emit(Transfer { from: contract_address_const::<0>(), to: recipient, value: amount }); + self + .emit( + Transfer { from: contract_address_const::<0>(), to: recipient, value: amount }, + ); } } -} \ No newline at end of file +} From a2e71d186113b812f2423dd55615548108059b9f Mon Sep 17 00:00:00 2001 From: yahia008 Date: Thu, 21 May 2026 19:14:57 +0100 Subject: [PATCH 13/16] readme --- README.md | 180 +++++++++++++++++++++++++++++++++++++++++++---- starknet-agentic | 1 + starknet.md | 48 +++++++++++++ 3 files changed, 214 insertions(+), 15 deletions(-) create mode 160000 starknet-agentic create mode 100644 starknet.md diff --git a/README.md b/README.md index ece307b..9484d03 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,172 @@ -# Cairo Bootcamp 6.0 +# StarkNet Restricted ERC-20 Token -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. +A secure ERC-20 token implementation on StarkNet with administrative controls, transfer restrictions, and burn functionality. ---- +## Overview -## About the Bootcamp +This contract extends standard ERC-20 behavior with admin-managed safeguards. It is designed for cases where token movement must follow configurable rules, while still supporting the usual transfer and allowance flows. -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 +## Features +### Core ERC-20 Functionality ---- +- Standard token operations: `transfer`, `approve`, and `transfer_from` +- Balance tracking and allowance management +- Token metadata: `name`, `symbol`, `decimals`, and `total_supply` -## 📁 Repository Structure -. -├── cairo_programs/ # Pure Cairo programs (stateless logic) -├── starknet_contracts/ # Smart contracts (stateful logic) -└── README.md \ No newline at end of file +### Admin Controls + +- Transfer limit management: the admin can update the maximum transfer amount +- Global transfer toggle: the admin can disable or re-enable all transfers +- Burn mechanism: the admin can burn tokens from any address +- Spender revocation: the admin can revoke specific spenders from using allowances + +### Security Features + +- Maximum transfer limit enforcement +- Zero-address validation +- Insufficient balance checks +- Allowance validation +- Revoked spender tracking +- Admin-only privileged functions + +## Contract Architecture + +### Interfaces + +#### `IERC20` + +Standard ERC-20 interface with the required functions: + +- `get_name()` +- `get_symbol()` +- `get_decimals()` +- `get_total_supply()` +- `balance_of()` +- `allowance()` +- `transfer()` +- `transfer_from()` +- `approve()` +- `increase_allowance()` +- `decrease_allowance()` + +#### `IAdminRestricted` + +Admin-specific functions: + +- `set_transfer_limit()` - Updates the maximum transfer amount +- `burn()` - Burns tokens from any account +- `revoke_transfers()` - Disables all transfers +- `enable_transfers()` - Re-enables transfers +- `admin_revoke_spender()` - Revokes a specific spender + +### Storage Variables + +```rust +name: felt252 +symbol: felt252 +decimals: u8 +total_supply: u256 +balances: Map +allowances: Map<(ContractAddress, ContractAddress), u256> +revoked_user: Map +admin: ContractAddress +transfer_limit: u256 +transfers_enabled: bool +``` + +### Events + +- `Transfer` - Emitted on token transfers +- `Approval` - Emitted on allowance changes +- `TransferLimitUpdated` - Emitted when the admin updates the transfer limit +- `TransfersRevoked` / `TransfersEnabled` - Emitted when global transfer status changes +- `Burn` - Emitted when tokens are burned + +## Deployment + +### Constructor Parameters + +```rust +fn constructor( + recipient: ContractAddress, + name: felt252, + decimals: u8, + initial_supply: u256, + symbol: felt252, + admin: ContractAddress +) +``` + +Parameter summary: + +- `recipient` - Initial token recipient +- `name` - Token name +- `decimals` - Token decimals +- `initial_supply` - Initial amount to mint +- `symbol` - Token symbol +- `admin` - Admin address + +### Example Deployment + +```cairo +let token = erc20::constructor( + recipient: 0x123..., + name: 'Restricted Token', + decimals: 18, + initial_supply: 1000000_u256, + symbol: 'RST', + admin: 0x456... +); +``` + +## Usage Examples + +### Transfer Validation + +The following transfer will fail if: + +- The amount exceeds `transfer_limit` +- The sender has insufficient balance +- Global transfers are disabled +- A zero address is used + +```cairo +token.transfer(recipient, 5000_u256); // Works +token.transfer(recipient, 15000_u256); // Fails: exceeds limit +``` + +### Admin Operations + +```cairo +// Update transfer limit +token.set_transfer_limit(20000_u256); + +// Burn tokens from any address +token.burn(account_address, 1000_u256); + +// Emergency stop all transfers +token.revoke_transfers(); +token.transfer(recipient, 1000_u256); // Fails: transfers disabled + +// Re-enable transfers +token.enable_transfers(); + +// Revoke a specific spender +token.admin_revoke_spender(owner, spender); +``` + +## Error Codes + +| Error | Description | +| --- | --- | +| `ERC20: not admin` | Caller is not the contract admin | +| `ERC20: transfers disabled` | Global transfers are currently disabled | +| `ERC20: exceeds max limit` | Transfer amount exceeds the current limit | +| `ERC20: insufficient balance` | Sender has insufficient tokens | +| `ERC20: insufficient allowance` | Insufficient allowance for `transfer_from` | +| `ERC20: spender revoked` | Spender has been revoked by the admin | +| `ERC20: limit must be >0` | Transfer limit must be positive | +| `ERC20: zero address` | Zero address is not allowed for the operation | +| `ERC20: approve to 0` | Cannot approve the zero address | +| `ERC20: burn amount >0` | Burn amount must be positive | diff --git a/starknet-agentic b/starknet-agentic new file mode 160000 index 0000000..79d018c --- /dev/null +++ b/starknet-agentic @@ -0,0 +1 @@ +Subproject commit 79d018c1502b58345bc4c867ef15f80419af50c0 diff --git a/starknet.md b/starknet.md new file mode 100644 index 0000000..6496936 --- /dev/null +++ b/starknet.md @@ -0,0 +1,48 @@ +# StarkNet MCP Transfer Agent + +An autonomous multi-step agent workflow built on StarkNet-agentic for intelligent, condition-based token transfers with monitoring and alerting. + +## Objective + +Build a composable autonomous transfer agent using an MCP (Model Context Protocol) server architecture that can: + +- Fetch wallet balances +- Validate transfer conditions +- Execute conditional transfers +- Chain multiple agent calls +- Log execution results +- Trigger alerts on low balance + +## Architecture + +The workflow follows this sequence: + +1. Fetch balance +2. Validate transfer conditions +3. Execute transfer +4. Call another agent if needed +5. Log the result +6. Trigger an alert when balances are low + +## Components + +### MCP Server Structure + +```typescript +const server = new McpServer({ + name: "starknet-transfer-agent", + version: "1.0.0" +}); +``` + +### Available Tools + +| Tool | Description | Parameters | +| --- | --- | --- | +| `fetch_balance` | Get wallet balance | `wallet_address` | +| `validate_transfer` | Check transfer conditions | `amount`, `balance`, `threshold` | +| `execute_transfer` | Perform token transfer | `to`, `amount`, `token_address` | +| `call_agent` | Invoke another agent | `agent_name`, `params` | +| `log_result` | Record execution results | `status`, `details`, `timestamp` | +| `check_threshold` | Monitor balance threshold | `balance`, `threshold` | +| `trigger_alert` | Send a low-balance notification | `wallet`, `current_balance` | From 02c7b5211cb919c430c2ecc5852eaf09050719da Mon Sep 17 00:00:00 2001 From: yahia008 <120284117+yahia008@users.noreply.github.com> Date: Thu, 21 May 2026 19:34:04 +0100 Subject: [PATCH 14/16] Update README with project file paths Added file paths for related projects in README. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 9484d03..87d00f2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ # StarkNet Restricted ERC-20 Token A secure ERC-20 token implementation on StarkNet with administrative controls, transfer restrictions, and burn functionality. +File-path yahia008/blockheader_test +File-path yahia008/assignment_1 +file-path yahia008/assignment_2 +File-path yahia008/av2_pro ## Overview From 25ca25b4001235770d49509f52c3574de0469d33 Mon Sep 17 00:00:00 2001 From: yahia008 <120284117+yahia008@users.noreply.github.com> Date: Thu, 21 May 2026 19:34:32 +0100 Subject: [PATCH 15/16] Update README with file paths for assignments Added file paths for various assignments and tests. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 87d00f2..662a079 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # StarkNet Restricted ERC-20 Token A secure ERC-20 token implementation on StarkNet with administrative controls, transfer restrictions, and burn functionality. + + File-path yahia008/blockheader_test File-path yahia008/assignment_1 file-path yahia008/assignment_2 From f1f29e1db815cc94ecf575fac22f47e1426d0a1b Mon Sep 17 00:00:00 2001 From: yahia008 <120284117+yahia008@users.noreply.github.com> Date: Thu, 21 May 2026 19:36:26 +0100 Subject: [PATCH 16/16] Refactor file-path entries in README.md Updated file-path formatting in README.md for consistency. --- README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 662a079..8d2b394 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,11 @@ A secure ERC-20 token implementation on StarkNet with administrative controls, transfer restrictions, and burn functionality. - -File-path yahia008/blockheader_test -File-path yahia008/assignment_1 -file-path yahia008/assignment_2 -File-path yahia008/av2_pro - +* **File-path:** `yahia008/blockheader_test` +* **File-path:** `yahia008/assignment_1` +* **File-path:** `yahia008/assignment_2` +* **File-path:** `yahia008/av2_pro` + ## Overview This contract extends standard ERC-20 behavior with admin-managed safeguards. It is designed for cases where token movement must follow configurable rules, while still supporting the usual transfer and allowance flows.