-
Notifications
You must be signed in to change notification settings - Fork 2
/
Vote_lib.py
157 lines (128 loc) · 4.3 KB
/
Vote_lib.py
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
'''
Voting utility methods
'''
from utils import global_must_get, increment_global, local_must_get, send_wait_txn
from pyteal import Subroutine, TealType, Expr, Bytes, App, Seq, Assert, And, \
Global, Int, Not, Txn
from algosdk.future.transaction import ApplicationNoOpTxn
# TODO: Go through and double check application array for including proper apps
@Subroutine(TealType.uint64)
def current_stake(stake_app_id, address) -> Expr:
# Helper method to get the current stake for an address
return local_must_get(Bytes("Stake"), stake_app_id, address)
# TODO: Cleanup imports at end
@Subroutine(TealType.uint64)
def has_voted(address, app_id) -> Expr:
# Checks if `address` has voted in the last vote in `app_id`
current_vote_id = global_must_get(Bytes("Vote_id"), app_id)
last_voted_id = App.localGetEx(address, app_id, Bytes("Vote_id"))
# We don't check hasValue because a user may *never* have voted in this
# vote, so a 0 value check is sufficient
return Seq(last_voted_id, current_vote_id == last_voted_id.value())
@Subroutine(TealType.uint64)
def is_resolved(app_id):
# Checks if voting is resolved in `app_id`
return global_must_get(Bytes("Resolved"), app_id)
'''
Internal methods
These methods can be used within a voting contract
'''
@Subroutine(TealType.uint64)
def is_voting_allowed() -> Expr:
# Checks if voting is allowed
return App.globalGet(Bytes("Vote_end")) > Global.latest_timestamp()
@Subroutine(TealType.uint64)
def cancel_vote_check(address) -> Expr:
# Checks if a vote can be cancelled
return And(
is_voting_allowed(),
has_voted(address, App.id()),
)
'''
Core functionality and utility methods
'''
'''
init_vote
Initializing a vote should look similar to the following
init_vote = Seq(
init_vote_core(vote_interval, vote_length),
ZERO_OUT_VOTES_IF_NEEDED(),
Approve(),
)
'''
def init_vote_core(vote_interval, vote_length):
# Runs the core portions of initializing a vote: does basic checks and sets proper variables
return Seq(
Assert(
And(
is_resolved(App.id()), # Previous vote must be resolved
App.globalGet(Bytes("Vote_end")) + vote_interval <= Global.latest_timestamp() # The time since the last vote end must be at least the min_interval
)
),
increment_global(Bytes("Vote_id"), Int(1)),
App.globalPut(Bytes("Resolved"), Int(0)),
App.globalPut(Bytes("Vote_end"), Global.latest_timestamp() + vote_length),
)
'''
close_vote
Closing a vote should look similar to the following
close_vote = Seq(
close_vote_core(),
FIND_WINNER(),
Approve(),
)
'''
def close_vote_core():
# Rybs the core portions of close_cote
return Seq(
Assert(
And(
Not(is_resolved(App.id())),
Not(is_voting_allowed())
)
),
App.globalPut(Bytes("Resolved"), Int(1)),
)
'''
send_vote
Sending a vote should look similar to the following
send_vote = Seq(
send_vote_core(VALID_VOTE_CHECK, NEW_VOTE),
TALLY_VOTE(),
Approve(),
)
'''
def send_vote_core(valid_vote_check, new_vote, stake_app_id):
sender = Txn.sender()
return Seq(
Assert(
And(
valid_vote_check(new_vote),
is_voting_allowed(),
Not(has_voted(sender, App.id())),
)
),
App.localPut(sender, Bytes("Vote_id"), App.globalGet(Bytes("Vote_id"))),
App.localPut(sender, Bytes("Choice"), new_vote),
# We track votes to protect against a weird edge case
App.localPut(sender, Bytes("Used_votes"), current_stake(stake_app_id, sender)),
)
# Calling functionality
# Future improvements may include:
# - Adding ability to do these in a group transaction
# send_vote must be implemented in each vote instance
def cancel_vote(client, sender, app_id):
params = client.suggested_params()
txn = ApplicationNoOpTxn(sender['pk'], params, app_id, ["Cancel"])
stxn = txn.sign(sender['sk'])
return send_wait_txn(client, stxn)
def init_vote(client, sender, app_id):
params = client.suggested_params()
txn = ApplicationNoOpTxn(sender['pk'], params, app_id, ["Init"])
stxn = txn.sign(sender['sk'])
return send_wait_txn(client, stxn)
def close_vote(client, sender, app_id):
params = client.suggested_params()
txn = ApplicationNoOpTxn(sender['pk'], params, app_id, ["Close"])
stxn = txn.sign(sender['sk'])
return send_wait_txn(client, stxn)