Skip to content
This repository has been archived by the owner on Jun 20, 2024. It is now read-only.

Commit

Permalink
Merge pull request #285 from starknet-edu/examples
Browse files Browse the repository at this point in the history
Fix examples storage
  • Loading branch information
gianalarcon authored Jan 5, 2024
2 parents dabc896 + 7205c94 commit 003df13
Show file tree
Hide file tree
Showing 9 changed files with 274 additions and 2 deletions.
1 change: 0 additions & 1 deletion examples/Ownable-Components
Submodule Ownable-Components deleted from cce3af
6 changes: 6 additions & 0 deletions examples/ownable-components/Scarb.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Code generated by scarb DO NOT EDIT.
version = 1

[[package]]
name = "ownable_project"
version = "0.1.0"
12 changes: 12 additions & 0 deletions examples/ownable-components/Scarb.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "ownable_project"
version = "0.1.0"

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

[dependencies]
starknet = ">=2.3.1"

[[target.starknet-contract]]
sierra = true
casm = true
116 changes: 116 additions & 0 deletions examples/ownable-components/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
use starknet::ContractAddress;

#[starknet::interface]
trait IData<T> {
fn get_data(self: @T) -> felt252;
fn set_data(ref self: T, new_value: felt252);
}

#[starknet::interface]
trait IOwnable<T> {
fn transfer_ownership(ref self: T, new_owner: ContractAddress);
fn owner(self: @T) -> ContractAddress;
}

#[starknet::component]
mod ownable_component {
use super::{ContractAddress, IOwnable};
use starknet::get_caller_address;

#[storage]
struct Storage {
owner: ContractAddress
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
OwnershipTransferred: OwnershipTransferred
}

#[derive(Drop, starknet::Event)]
struct OwnershipTransferred {
previous_owner: ContractAddress,
new_owner: ContractAddress,
}

#[embeddable_as(Ownable)]
impl OwnableImpl<
TContractState, +HasComponent<TContractState>
> of IOwnable<ComponentState<TContractState>> {
fn transfer_ownership(
ref self: ComponentState<TContractState>, new_owner: ContractAddress
) {
self.only_owner();
self._transfer_ownership(new_owner);
}
fn owner(self: @ComponentState<TContractState>) -> ContractAddress {
self.owner.read()
}
}

#[generate_trait]
impl InternalImpl<
TContractState, +HasComponent<TContractState>
> of InternalTrait<TContractState> {
fn only_owner(self: @ComponentState<TContractState>) {
let owner: ContractAddress = self.owner.read();
let caller: ContractAddress = get_caller_address();
assert(!caller.is_zero(), 'ZERO_ADDRESS_CALLER');
assert(caller == owner, 'NOT_OWNER');
}

fn _transfer_ownership(
ref self: ComponentState<TContractState>, new_owner: ContractAddress
) {
let previous_owner: ContractAddress = self.owner.read();
self.owner.write(new_owner);
self
.emit(
OwnershipTransferred { previous_owner: previous_owner, new_owner: new_owner }
);
}
}
}

#[starknet::contract]
mod ownable_contract {
use ownable_project::ownable_component;
use super::{ContractAddress, IData};

component!(path: ownable_component, storage: ownable, event: OwnableEvent);

#[abi(embed_v0)]
impl OwnableImpl = ownable_component::Ownable<ContractState>;

impl OwnableInternalImpl = ownable_component::InternalImpl<ContractState>;

#[storage]
struct Storage {
data: felt252,
#[substorage(v0)]
ownable: ownable_component::Storage
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
OwnableEvent: ownable_component::Event
}

#[constructor]
fn constructor(ref self: ContractState, initial_owner: ContractAddress) {
self.ownable.owner.write(initial_owner);
self.data.write(1);
}
#[external(v0)]
impl OwnableDataImpl of IData<ContractState> {
fn get_data(self: @ContractState) -> felt252 {
self.data.read()
}
fn set_data(ref self: ContractState, new_value: felt252) {
self.ownable.only_owner();
self.data.write(new_value);
}
}
}
1 change: 0 additions & 1 deletion examples/vote-contract
Submodule vote-contract deleted from fa7c34
14 changes: 14 additions & 0 deletions examples/vote-contracts/Scarb.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Code generated by scarb DO NOT EDIT.
version = 1

[[package]]
name = "snforge_std"
version = "0.1.0"
source = "git+https://github.com/foundry-rs/starknet-foundry?tag=v0.12.0#0c3d2fe4ab31aa4484fa216417408ae65a149efe"

[[package]]
name = "voting"
version = "0.1.0"
dependencies = [
"snforge_std",
]
12 changes: 12 additions & 0 deletions examples/vote-contracts/Scarb.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "voting"
version = "0.1.0"

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

[dependencies]
snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry", tag = "v0.12.0" }
starknet = ">=2.3.1"

[[target.starknet-contract]]
casm = true
43 changes: 43 additions & 0 deletions examples/vote-contracts/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use starknet::ContractAddress;

#[starknet::interface]
trait IVotingContract<TContractState> {
fn vote(ref self: TContractState, vote: felt252);
fn get_votes(self: @TContractState) -> (felt252, felt252);
}

#[starknet::contract]
mod VotingContract {
use starknet::get_caller_address;
use traits::Into;
use super::{IVotingContract, ContractAddress};

#[storage]
struct Storage {
yes_votes: felt252,
no_votes: felt252,
voters: LegacyMap::<ContractAddress, bool>,
}

#[external(v0)]
impl VotingContractImpl of IVotingContract<ContractState> {
fn vote(ref self: ContractState, vote: felt252) {
assert((vote == 0) || (vote == 1), 'vote can only be 0/1');
let caller = get_caller_address();

assert(!self.voters.read(caller), 'you have already voted');

if vote == 0 {
self.no_votes.write(self.no_votes.read() + 1);
} else if vote == 1 {
self.yes_votes.write(self.yes_votes.read() + 1);
}

self.voters.write(caller, true);
}

fn get_votes(self: @ContractState) -> (felt252, felt252) {
(self.no_votes.read(), self.yes_votes.read())
}
}
}
71 changes: 71 additions & 0 deletions examples/vote-contracts/tests/test_contract.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use starknet::ContractAddress;
use snforge_std::{declare, ContractClassTrait, start_prank, CheatTarget};
use voting::{IVotingContractDispatcher, IVotingContractSafeDispatcher};
use voting::{IVotingContractDispatcherTrait, IVotingContractSafeDispatcherTrait};

fn deploy_contract(name: felt252) -> ContractAddress {
let contract = declare(name);
contract.deploy(@array![]).unwrap()
}

fn expect_votes_counters_values(
contract_address: ContractAddress, expected_no_votes: felt252, expected_yes_votes: felt252
) {
let dispatcher = IVotingContractDispatcher { contract_address };
let (no_votes, yes_votes) = dispatcher.get_votes();
assert(no_votes == expected_no_votes, 'no votes value incorrect');
assert(yes_votes == expected_yes_votes, 'yes votes value incorrect');
}

fn vote_with_caller_address(
contract_address: ContractAddress, pranked_address: felt252, value: felt252
) {
start_prank(CheatTarget::One(contract_address), pranked_address.try_into().unwrap());
let dispatcher = IVotingContractDispatcher { contract_address };
dispatcher.vote(value);
}

#[test]
fn test_votes_counter_change() {
let contract_address = deploy_contract('VotingContract');

expect_votes_counters_values(contract_address, 0, 0);

let dispatcher = IVotingContractDispatcher { contract_address };
dispatcher.vote(1);

expect_votes_counters_values(contract_address, 0, 1);
}

#[test]
fn test_voting_multiple_times_from_different_accounts() {
let contract_address = deploy_contract('VotingContract');

expect_votes_counters_values(contract_address, 0, 0);

vote_with_caller_address(contract_address, 101, 1);
vote_with_caller_address(contract_address, 102, 1);
vote_with_caller_address(contract_address, 103, 0);
vote_with_caller_address(contract_address, 104, 0);
vote_with_caller_address(contract_address, 105, 0);

expect_votes_counters_values(contract_address, 3, 2);
}

#[test]
fn test_voting_twice_from_the_same_account() {
let contract_address = deploy_contract('VotingContract');

start_prank(CheatTarget::One(contract_address), 123.try_into().unwrap());

let safe_dispatcher = IVotingContractSafeDispatcher { contract_address };

safe_dispatcher.vote(1).unwrap();

match safe_dispatcher.vote(0) {
Result::Ok(_) => panic(array!['shouldve panicked']),
Result::Err(panic_data) => {
assert(*panic_data.at(0) == 'you have already voted', *panic_data.at(0));
}
}
}

0 comments on commit 003df13

Please sign in to comment.