This repository has been archived by the owner on Jun 20, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 488
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #285 from starknet-edu/examples
Fix examples storage
- Loading branch information
Showing
9 changed files
with
274 additions
and
2 deletions.
There are no files selected for viewing
Submodule Ownable-Components
deleted from
cce3af
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
Submodule vote-contract
deleted from
fa7c34
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
} | ||
} | ||
} |