Skip to content

Commit 0df5596

Browse files
authored
feat: Transitioning Cairo Content to Cairo Book and Introducing Intermediate to Advanced Smart Contract Examples (#317)
* feat: add examples section to SUMMARY.md * feat: add details on the SN network * feat: Add voting contract cairo code and md * feat: add other examples section * feat: Improve name of voting contract chapter * feat: typo
1 parent a1d8f74 commit 0df5596

File tree

5 files changed

+432
-0
lines changed

5 files changed

+432
-0
lines changed
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
/// @dev Core Library Imports for the Traits outside the Starknet Contract
2+
use starknet::ContractAddress;
3+
4+
/// @dev Trait defining the functions that can be implemented or called by the Starknet Contract
5+
#[starknet::interface]
6+
trait VoteTrait<T> {
7+
/// @dev Function that returns the current vote status
8+
fn get_vote_status(self: @T) -> (u8, u8, u8, u8);
9+
/// @dev Function that checks if the user at the specified address is allowed to vote
10+
fn voter_can_vote(self: @T, user_address: ContractAddress) -> bool;
11+
/// @dev Function that checks if the specified address is registered as a voter
12+
fn is_voter_registered(self: @T, address: ContractAddress) -> bool;
13+
/// @dev Function that allows a user to vote
14+
fn vote(ref self: T, vote: u8);
15+
}
16+
17+
/// @dev Starknet Contract allowing three registered voters to vote on a proposal
18+
#[starknet::contract]
19+
mod Vote {
20+
use starknet::ContractAddress;
21+
use starknet::get_caller_address;
22+
23+
const YES: u8 = 1_u8;
24+
const NO: u8 = 0_u8;
25+
26+
/// @dev Structure that stores vote counts and voter states
27+
#[storage]
28+
struct Storage {
29+
yes_votes: u8,
30+
no_votes: u8,
31+
can_vote: LegacyMap::<ContractAddress, bool>,
32+
registered_voter: LegacyMap::<ContractAddress, bool>,
33+
}
34+
35+
/// @dev Contract constructor initializing the contract with a list of registered voters and 0 vote count
36+
#[constructor]
37+
fn constructor(
38+
ref self: ContractState,
39+
voter_1: ContractAddress,
40+
voter_2: ContractAddress,
41+
voter_3: ContractAddress
42+
) {
43+
// Register all voters by calling the _register_voters function
44+
self._register_voters(voter_1, voter_2, voter_3);
45+
46+
// Initialize the vote count to 0
47+
self.yes_votes.write(0_u8);
48+
self.no_votes.write(0_u8);
49+
}
50+
51+
/// @dev Event that gets emitted when a vote is cast
52+
#[event]
53+
#[derive(Drop, starknet::Event)]
54+
enum Event {
55+
VoteCast: VoteCast,
56+
UnauthorizedAttempt: UnauthorizedAttempt,
57+
}
58+
59+
/// @dev Represents a vote that was cast
60+
#[derive(Drop, starknet::Event)]
61+
struct VoteCast {
62+
voter: ContractAddress,
63+
vote: u8,
64+
}
65+
66+
/// @dev Represents an unauthorized attempt to vote
67+
#[derive(Drop, starknet::Event)]
68+
struct UnauthorizedAttempt {
69+
unauthorized_address: ContractAddress,
70+
}
71+
72+
/// @dev Implementation of VoteTrait for ContractState
73+
#[external(v0)]
74+
impl VoteImpl of super::VoteTrait<ContractState> {
75+
/// @dev Returns the voting results
76+
fn get_vote_status(self: @ContractState) -> (u8, u8, u8, u8) {
77+
let (n_yes, n_no) = self._get_voting_result();
78+
let (yes_percentage, no_percentage) = self._get_voting_result_in_percentage();
79+
return (n_yes, n_no, yes_percentage, no_percentage);
80+
}
81+
82+
/// @dev Check whether a voter is allowed to vote
83+
fn voter_can_vote(self: @ContractState, user_address: ContractAddress) -> bool {
84+
self.can_vote.read(user_address)
85+
}
86+
87+
/// @dev Check whether an address is registered as a voter
88+
fn is_voter_registered(self: @ContractState, address: ContractAddress) -> bool {
89+
self.registered_voter.read(address)
90+
}
91+
92+
/// @dev Submit a vote
93+
fn vote(ref self: ContractState, vote: u8) {
94+
assert(vote == NO || vote == YES, 'VOTE_0_OR_1');
95+
let caller: ContractAddress = get_caller_address();
96+
self._assert_allowed(caller);
97+
self.can_vote.write(caller, false);
98+
99+
if (vote == NO) {
100+
self.no_votes.write(self.no_votes.read() + 1_u8);
101+
}
102+
if (vote == YES) {
103+
self.yes_votes.write(self.yes_votes.read() + 1_u8);
104+
}
105+
106+
self.emit(VoteCast { voter: caller, vote: vote, });
107+
}
108+
}
109+
110+
/// @dev Internal Functions implementation for the Vote contract
111+
#[generate_trait]
112+
impl InternalFunctions of InternalFunctionsTrait {
113+
/// @dev Registers the voters and initializes their voting status to true (can vote)
114+
fn _register_voters(
115+
ref self: ContractState,
116+
voter_1: ContractAddress,
117+
voter_2: ContractAddress,
118+
voter_3: ContractAddress
119+
) {
120+
self.registered_voter.write(voter_1, true);
121+
self.can_vote.write(voter_1, true);
122+
123+
self.registered_voter.write(voter_2, true);
124+
self.can_vote.write(voter_2, true);
125+
126+
self.registered_voter.write(voter_3, true);
127+
self.can_vote.write(voter_3, true);
128+
}
129+
}
130+
131+
/// @dev Asserts implementation for the Vote contract
132+
#[generate_trait]
133+
impl AssertsImpl of AssertsTrait {
134+
// @dev Internal function that checks if an address is allowed to vote
135+
fn _assert_allowed(ref self: ContractState, address: ContractAddress) {
136+
let is_voter: bool = self.registered_voter.read((address));
137+
let can_vote: bool = self.can_vote.read((address));
138+
139+
if (can_vote == false) {
140+
self.emit(UnauthorizedAttempt { unauthorized_address: address, });
141+
}
142+
143+
assert(is_voter == true, 'USER_NOT_REGISTERED');
144+
assert(can_vote == true, 'USER_ALREADY_VOTED');
145+
}
146+
}
147+
148+
/// @dev Implement the VotingResultTrait for the Vote contract
149+
#[generate_trait]
150+
impl VoteResultFunctionsImpl of VoteResultFunctionsTrait {
151+
// @dev Internal function to get the voting results (yes and no vote counts)
152+
fn _get_voting_result(self: @ContractState) -> (u8, u8) {
153+
let n_yes: u8 = self.yes_votes.read();
154+
let n_no: u8 = self.no_votes.read();
155+
156+
return (n_yes, n_no);
157+
}
158+
159+
// @dev Internal function to calculate the voting results in percentage
160+
fn _get_voting_result_in_percentage(self: @ContractState) -> (u8, u8) {
161+
let n_yes: u8 = self.yes_votes.read();
162+
let n_no: u8 = self.no_votes.read();
163+
164+
let total_votes: u8 = n_yes + n_no;
165+
166+
let yes_percentage: u8 = (n_yes * 100_u8) / (total_votes);
167+
let no_percentage: u8 = (n_no * 100_u8) / (total_votes);
168+
169+
return (yes_percentage, no_percentage);
170+
}
171+
}
172+
}

src/SUMMARY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@
9595
- [ABIs and Cross-contract Interactions](./ch99-02-00-abis-and-cross-contract-interactions.md)
9696
- [ABIs and Interfaces](./ch99-02-01-abis-and-interfaces.md)
9797
- [Contract Dispatchers, Library Dispachers and system calls](./ch99-02-02-contract-dispatcher-library-dispatcher-and-system-calls.md)
98+
- [Other examples](./ch99-01-04-00-other-examples.md)
99+
- [Deploying and Interacting with a Voting contract](./ch99-01-04-01-voting-contract.md)
98100
- [L1 <> L2 Messaging](./ch99-04-00-L1-L2-messaging.md)
99101
- [Security Considerations](./ch99-03-security-considerations.md)
100102

src/ch99-00-starknet-smart-contracts.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
All through the previous sections, you've mostly written programs with a `main` entrypoint. In the coming sections, you will learn to write and deploy Starknet contracts.
44

5+
One of the applications of the Cairo language is to write smart contracts for the Starknet network. Starknet is a permissionless network that leverages zk-STARKs technology for scalability. As a Layer-2 scalability solution for Ethereum, Starknet's goal is to offer fast, secure, and low-cost transactions. It functions as a Validity Rollup (commonly known as a zero-knowledge Rollup) and is built on top of the Cairo language and the StarkNet VM.
6+
57
Starknet contracts, in simple words, are programs that can run on the Starknet VM. Since they run on the VM, they have access to Starknet’s persistent state, can alter or modify variables in Starknet’s states, communicate with other contracts, and interact seamlessly with the underlying L1.
68

79
Starknet contracts are denoted by the `#[contract]` attribute. We'll dive deeper into this in the next sections.

src/ch99-01-04-00-other-examples.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Other examples
2+
3+
This section contains additional examples of Starknet smart contracts, utilizing various features of the Cairo programming language. Your contributions are welcome and encouraged, as we aim to gather as many diverse examples as possible.

0 commit comments

Comments
 (0)