Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Scarb.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,21 @@ version = 1
[[package]]
name = "cohort_4"
version = "0.1.0"
dependencies = [
"snforge_std",
]

[[package]]
name = "snforge_scarb_plugin"
version = "0.40.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:7c3b21f6cdab14fc63e19f9e6789b6a3d44f5618ebcf02d03b397375304e1891"

[[package]]
name = "snforge_std"
version = "0.40.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:0221bbe959eec72eb2e30be68df66c4ff5dcd924ec491f285c974e49671fabc0"
dependencies = [
"snforge_scarb_plugin",
]
49 changes: 44 additions & 5 deletions Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,50 @@ edition = "2024_07"

# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html
# [executable]
[dependencies]

[cairo]
enable-gas = false
[dependencies]
starknet = "2.11.2"

[dev-dependencies]
cairo_test = "2.11.2"
# cairo_execute = "2.11.3"
snforge_std = "0.40.0"
assert_macros = "2.11.2"

[[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

7 changes: 7 additions & 0 deletions assignments/task_4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Write comprehensive tests for newly added contracts:
- `Counter`
- `KillSwitch`
- `Aggregator`

### Objective
The goal of this assignment is to familiarize developers with dispatchers and how they can be leveraged to interact with other contracts outside the file where it was created thus making contracts composable. Through this assignment, you will learn how to send modify and retrieve state variables of other contracts
11 changes: 11 additions & 0 deletions src/IHello.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/// Interface representing `HelloContract`.
/// This interface allows modification and retrieval of the contract balance.
#[starknet::interface]
pub trait IHelloStarknet<TContractState> {
/// Increase contract balance.
fn increase_balance(ref self: TContractState, amount: felt252);
/// Retrieve contract balance.
fn get_balance(self: @TContractState) -> felt252;

fn add_and_subtract(ref self: TContractState, amount: felt252);
}
8 changes: 8 additions & 0 deletions src/INumber.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/// A trait for a contract that can store and retrieve a number
#[starknet::interface]
pub trait INumber<TContractState> {
/// Sets the number to the given value
fn set_number(ref self: TContractState, amount: u8);
/// Returns the current number
fn get_number(self: @TContractState) -> u8;
}
77 changes: 77 additions & 0 deletions src/aggregator.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#[starknet::interface]
pub trait IAggregator<TContractState> {
/// Increase contract count.
fn increase_count(ref self: TContractState, amount: u32);
/// Increase contract count.
///
fn increase_counter_count(ref self: TContractState, amount: u32);

/// Retrieve contract count.
fn decrease_count_by_one(ref self: TContractState);
/// Retrieve contract count.
fn get_count(self: @TContractState) -> u32;

fn activate_switch(ref self: TContractState);
}

/// Simple contract for managing count.
#[starknet::contract]
mod Agggregator {
use cohort_4::counter::{ICounterDispatcher, ICounterDispatcherTrait};
use cohort_4::killswitch::{IKillSwitchDispatcher, IKillSwitchDispatcherTrait};
use starknet::ContractAddress;
use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};


#[storage]
struct Storage {
count: u32,
counter: ContractAddress,
killswitch: ContractAddress,
}

#[constructor]
fn constructor(ref self: ContractState, counter: ContractAddress, killswitch: ContractAddress) {
self.counter.write(counter);
self.killswitch.write(killswitch);
}


#[abi(embed_v0)]
impl AggregatorImpl of super::IAggregator<ContractState> {
fn increase_count(ref self: ContractState, amount: u32) {
assert(amount > 0, 'Amount cannot be 0');
let counter = ICounterDispatcher { contract_address: self.counter.read() };
let counter_count = counter.get_count();
self.count.write(counter_count + amount);
}

fn increase_counter_count(ref self: ContractState, amount: u32) {
let killswitch: IKillSwitchDispatcher = IKillSwitchDispatcher {
contract_address: self.killswitch.read(),
};
assert(killswitch.get_status(), 'not active');
ICounterDispatcher { contract_address: self.counter.read() }.increase_count(amount)
}

fn decrease_count_by_one(ref self: ContractState) {
let current_count = self.get_count();
assert!(current_count != 0, "Amount cannot be 0");
self.count.write(current_count - 1);
}

fn activate_switch(ref self: ContractState) {
let killswitch: IKillSwitchDispatcher = IKillSwitchDispatcher {
contract_address: self.killswitch.read(),
};

if !killswitch.get_status() {
killswitch.switch()
}
}

fn get_count(self: @ContractState) -> u32 {
self.count.read()
}
}
}
43 changes: 43 additions & 0 deletions src/counter.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/// Interface representing `Counter`.
/// This interface allows modification and retrieval of the contract count.
#[starknet::interface]
pub trait ICounter<TContractState> {
/// Increase contract count.
fn increase_count(ref self: TContractState, amount: u32);

/// Decrease contract count by one
fn decrease_count_by_one(ref self: TContractState);

/// Retrieve contract count.
fn get_count(self: @TContractState) -> u32;
}

/// Simple contract for managing count.
#[starknet::contract]
mod Counter {
use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};

#[storage]
struct Storage {
count: u32,
}

#[abi(embed_v0)]
impl CounterImpl of super::ICounter<ContractState> {
fn increase_count(ref self: ContractState, amount: u32) {
assert(amount > 0, 'Amount cannot be 0');
let counter_count = self.get_count();
self.count.write(counter_count + amount);
}

fn decrease_count_by_one(ref self: ContractState) {
let current_count = self.get_count();
assert(current_count > 0, 'Amount cannot be 0');
self.count.write(current_count - 1);
}

fn get_count(self: @ContractState) -> u32 {
self.count.read()
}
}
}
55 changes: 55 additions & 0 deletions src/hello.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#[starknet::contract]
mod HelloStarknet {
use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
use crate::IHello::IHelloStarknet;
use crate::INumber::INumber;

#[storage]
struct Storage {
balance: felt252,
}

#[generate_trait]
impl InternalTrait of InternalImpl {
fn _add(ref self: ContractState, amount: felt252) {
let old_balance = self.balance.read();
self.balance.write(old_balance + amount);
}

fn _subtract(ref self: ContractState, amount: felt252) {
let old_balance = self.balance.read();
self.balance.write(old_balance - amount);
}
}

fn add(amount: felt252) -> felt252 {
amount + 2
}


#[abi(embed_v0)]
impl HelloStarknet of IHelloStarknet<ContractState> {
fn increase_balance(ref self: ContractState, amount: felt252) {
let upgraded_score = add(amount);
self._add(upgraded_score);
}

fn get_balance(self: @ContractState) -> felt252 {
self.balance.read()
}

fn add_and_subtract(ref self: ContractState, amount: felt252) {
self._add(amount);
self._subtract(amount);
}
}

#[abi(embed_v0)]
impl INumberImpl of INumber<ContractState> {
fn set_number(ref self: ContractState, amount: u8) {}
fn get_number(self: @ContractState) -> u8 {
let number: u8 = 8;
number
}
}
}
35 changes: 35 additions & 0 deletions src/killswitch.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/// Interface representing `HelloContract`.
/// This interface allows modification and retrieval of the contract count.
#[starknet::interface]
pub trait IKillSwitch<TContractState> {
/// Increase contract count.
fn switch(ref self: TContractState);

/// Retrieve contract count.
fn get_status(self: @TContractState) -> bool;
}

/// Simple contract for managing count.
#[starknet::contract]
mod KillSwitch {
use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};

#[storage]
struct Storage {
status: bool,
}


#[abi(embed_v0)]
impl KillSwitchImpl of super::IKillSwitch<ContractState> {
fn switch(ref self: ContractState) {
// assert(amount != 0, 'Amount cannot be 0');
self.status.write(!self.status.read());
}


fn get_status(self: @ContractState) -> bool {
self.status.read()
}
}
}
9 changes: 9 additions & 0 deletions src/lib.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
pub mod hello;
pub mod IHello;
pub mod INumber;
pub mod counter;
pub mod killswitch;
pub mod aggregator;



fn main() {
// Function calls (Uncomment to execute them)
// say_name("Sylvia Nnoruka!");
Expand Down
49 changes: 49 additions & 0 deletions tests/test_contract.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use starknet::ContractAddress;

use snforge_std::{declare, ContractClassTrait, DeclareResultTrait};

use cohort_4::counter::{ICounterDispatcher, ICounterDispatcherTrait, ICounterSafeDispatcher, ICounterSafeDispatcherTrait};

fn deploy_contract() -> ContractAddress {
let countract_name: ByteArray = "Counter";
let contract = declare(countract_name).unwrap().contract_class();
let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap();
contract_address
}

#[test]
fn test_increase_count() {
let contract_address = deploy_contract();

let dispatcher = ICounterDispatcher { contract_address };

let balance_before = dispatcher.get_count();
assert(balance_before == 0, 'Invalid balance');

dispatcher.increase_count(42);

let balance_after = dispatcher.get_count();
assert(balance_after == 42, 'Invalid balance');
}

#[test]
#[feature("safe_dispatcher")]
fn test_cannot_increase_balance_with_zero_value() {
let contract_address = deploy_contract();

let dispatcher = ICounterDispatcher { contract_address };

let balance_before = dispatcher.get_count();
assert(balance_before == 0 , 'Invalid balance');

let safe_dispatcher = ICounterSafeDispatcher { contract_address };

match safe_dispatcher.increase_count(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));
}
};


}
Loading