forked from argotorg/fe
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvoting.fe
More file actions
134 lines (111 loc) · 4.74 KB
/
Copy pathvoting.fe
File metadata and controls
134 lines (111 loc) · 4.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/// On-chain voting system with proposals and duplicate-vote prevention.
/// Inspired by governance contracts (OpenZeppelin Governor, Compound).
///
/// Tests: StorageMap with tuple keys, sequential ID generation, duplicate prevention.
msg VotingMsg {
#[selector = 0x01]
CreateProposal -> u256,
#[selector = 0x02]
Vote { voter: u256, proposal: u256 } -> u256,
#[selector = 0x03]
GetVotes { proposal: u256 } -> u256,
#[selector = 0x04]
HasVoted { voter: u256, proposal: u256 } -> u256,
#[selector = 0x05]
GetNumProposals -> u256,
}
struct VotingStore {
num_proposals: u256,
votes: StorageMap<u256, u256>,
voted: StorageMap<(u256, u256), u256>,
}
pub contract Voting {
mut store: VotingStore
init() uses (mut store) {
store.num_proposals = 0
}
recv VotingMsg {
CreateProposal -> u256 uses (mut store) {
store.num_proposals += 1
store.num_proposals
}
Vote { voter, proposal } -> u256 uses (mut store) {
// Invalid proposal
if proposal == 0 {
return 1
}
if proposal > store.num_proposals {
return 1
}
// Already voted
if store.voted.get(key: (voter, proposal)) != 0 {
return 2
}
store.voted.set(key: (voter, proposal), value: 1)
let current = store.votes.get(key: proposal)
store.votes.set(key: proposal, value: current + 1)
return 0
}
GetVotes { proposal } -> u256 uses (store) {
store.votes.get(key: proposal)
}
HasVoted { voter, proposal } -> u256 uses (store) {
store.voted.get(key: (voter, proposal))
}
GetNumProposals -> u256 uses (store) {
store.num_proposals
}
}
}
#[test]
fn test_voting() uses (evm: mut Evm) {
let voting = evm.create2<Voting>(value: 0, args: (), salt: 0)
assert(voting.inner != 0)
// Create 3 proposals
let p1: u256 = evm.call(addr: voting, gas: 100000, value: 0, message: VotingMsg::CreateProposal {})
assert(p1 == 1)
let p2: u256 = evm.call(addr: voting, gas: 100000, value: 0, message: VotingMsg::CreateProposal {})
assert(p2 == 2)
let p3: u256 = evm.call(addr: voting, gas: 100000, value: 0, message: VotingMsg::CreateProposal {})
assert(p3 == 3)
let count: u256 = evm.call(addr: voting, gas: 100000, value: 0, message: VotingMsg::GetNumProposals {})
assert(count == 3)
// Voter 1 votes for proposal 1
let res: u256 = evm.call(addr: voting, gas: 100000, value: 0, message: VotingMsg::Vote { voter: 1, proposal: 1 })
assert(res == 0)
// Voter 2 votes for proposal 1
let res: u256 = evm.call(addr: voting, gas: 100000, value: 0, message: VotingMsg::Vote { voter: 2, proposal: 1 })
assert(res == 0)
// Voter 3 votes for proposal 2
let res: u256 = evm.call(addr: voting, gas: 100000, value: 0, message: VotingMsg::Vote { voter: 3, proposal: 2 })
assert(res == 0)
// Check vote counts
let v1: u256 = evm.call(addr: voting, gas: 100000, value: 0, message: VotingMsg::GetVotes { proposal: 1 })
assert(v1 == 2)
let v2: u256 = evm.call(addr: voting, gas: 100000, value: 0, message: VotingMsg::GetVotes { proposal: 2 })
assert(v2 == 1)
let v3: u256 = evm.call(addr: voting, gas: 100000, value: 0, message: VotingMsg::GetVotes { proposal: 3 })
assert(v3 == 0)
// Voter 1 already voted on proposal 1 (returns 2 = duplicate)
let res: u256 = evm.call(addr: voting, gas: 100000, value: 0, message: VotingMsg::Vote { voter: 1, proposal: 1 })
assert(res == 2)
// Vote count unchanged
let v1: u256 = evm.call(addr: voting, gas: 100000, value: 0, message: VotingMsg::GetVotes { proposal: 1 })
assert(v1 == 2)
// Voter 1 can vote on a different proposal
let res: u256 = evm.call(addr: voting, gas: 100000, value: 0, message: VotingMsg::Vote { voter: 1, proposal: 2 })
assert(res == 0)
let v2: u256 = evm.call(addr: voting, gas: 100000, value: 0, message: VotingMsg::GetVotes { proposal: 2 })
assert(v2 == 2)
// Check HasVoted
let hv: u256 = evm.call(addr: voting, gas: 100000, value: 0, message: VotingMsg::HasVoted { voter: 1, proposal: 1 })
assert(hv == 1)
let hv: u256 = evm.call(addr: voting, gas: 100000, value: 0, message: VotingMsg::HasVoted { voter: 2, proposal: 2 })
assert(hv == 0)
// Invalid proposal (too high)
let res: u256 = evm.call(addr: voting, gas: 100000, value: 0, message: VotingMsg::Vote { voter: 5, proposal: 99 })
assert(res == 1)
// Invalid proposal (zero)
let res: u256 = evm.call(addr: voting, gas: 100000, value: 0, message: VotingMsg::Vote { voter: 5, proposal: 0 })
assert(res == 1)
}