-
Notifications
You must be signed in to change notification settings - Fork 1
/
Poll.ts
287 lines (251 loc) · 7.95 KB
/
Poll.ts
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
import {
Address,
Client,
Contract,
ContractLink,
ClientConsole,
Decimal,
Deployment,
Fee,
Moment,
Snip20,
Uint128,
bold,
} from './Core';
import AuthProviderDeployment, { Auth, AuthClient } from './Auth';
import TGEDeployment, { RPT_TGE } from './SiennaTGE';
import { Rewards_v4_1 } from './SiennaRewards_v4'
export default class GovernanceDeployment extends Deployment {
version = 'v1'
names = {
/** The name of the auth group that gives the voting contract
* access to the balances in the staking contract, which it
* uses to compute voting power. */
authGroup: 'Rewards_and_Governance',
/** The name of the governance staking pool where voting power is accumulated. */
pool: `SIENNA.Rewards[v4]`,
/** The name of the governance contract where users vote on proposals. */
polls: `SIENNA.Rewards[v4].Polls[${this.version}]`
}
Clients = {
/** The client class used to talk to the governance staking pool. */
Pool: Rewards_v4_1,
/** The client class used to talk to the governance voting polls. */
Polls: Polls
}
/** The TGE containing the token and RPT used by the deployment. */
tge = new TGEDeployment(this as Deployment)
/** The token staked in the governance pool. */
get token () { return this.tge.token }
/** The RPT contract which needs to be reconfigured when we upgrade
* the staking pool, so that the new pool gets rewards budget. */
get rpt () { return this.tge.rpt }
/** The auth provider and oracle used by the deployment. */
auth = new AuthProviderDeployment(this, 'v1', this.names.authGroup)
/** The up-to-date Rewards v4 staking pool with governance support. */
pool = this.contract({ name: this.names.pool, client: this.Clients.Pool })
/** The governance voting contract. */
polls = this.contract({ name: this.names.polls, client: this.Clients.Polls })
/** Display the status of the governance system. */
showStatus = async () => {
const [pool, polls] = await Promise.all([this.pool(), this.polls()])
log.pool(pool)
const stakedToken = await pool.getStakedToken()
const label = '(todo)'
log.stakedToken(stakedToken, label)
log.epoch(await pool.getEpoch())
log.config(await pool.getConfig())
log.pollsContract(polls)
log.pollsAuthProvider(await polls.auth.getProvider())
log.pollsConfig(await polls.getPollConfig())
log.activePolls(await polls.getPolls(+ new Date() / 1000, 0, 10, 0))
}
}
const log = new class SiennaGovernanceConsole extends ClientConsole {
name = 'Sienna Governance'
pool (pool: any) {
this.info('Governance-enabled staking pool:')
this.info(' ', JSON.stringify(pool.asLink))
}
async stakedToken (stakedToken: any, label: any) {
const link = JSON.stringify(stakedToken?.asLink)
this.info('Staked token:')
this.info(` ${label} ${link}`)
}
epoch (epoch: any) {
this.info('Epoch:')
this.info(' ', epoch)
}
config (config: any) {
this.info('Pool config:')
this.info(' ', JSON.stringify(config))
}
pollsContract (contract: any) {
this.info('Governance contract:')
this.info(' ', JSON.stringify(contract.asLink))
}
pollsAuthProvider (provider: any) {
this.info('Auth provider:')
this.info(' ', JSON.stringify(provider))
}
pollsConfig (config: any) {
this.info('Poll config:')
this.info(' ', config)
}
activePolls (polls: any) {
this.info('Active polls:')
this.info(' ', polls)
this.info('')
}
}
const getNow = () => Math.floor(+new Date() / 1000);
export type PollId = number;
/** Supports any number of additions, saved as a string in the contract.
* Limits:
* min length: 5
* max length: 20 */
export enum PollType {
SiennaRewards = 'sienna_rewards',
SiennaSwapParameters = 'sienna_swap_parameters',
Other = 'other',
}
export enum PollStatus {
/** The poll is not expired, voting is still possible */
Active = 'active',
/** The poll has expired, quorum has passed and the poll has passed */
Passed = 'passed',
/** Quorum has not been reached or poll has failed. */
Failed = 'failed',
}
/** Possible vote options */
export enum PollVote {
Yes = 'yes',
No = 'no',
Abstain = 'abstain',
}
/** Describes the conditions under which a poll expires. */
export interface Expiration {
at_time: Moment;
}
export interface PollConfig {
/** Minimum amount of staked tokens needed to create a poll */
threshold: Uint128;
/** The amount of time a poll lasts in seconds */
deadline: Moment;
/** Minimum percentage (0-1) which is needed for a poll to be valid */
quorum: Decimal;
/** Link to the rewards contract */
rewards: ContractLink;
/** Minimum number of tokens staked to be able to vote */
voting_threshold: Uint128;
}
export interface PollMetadata {
/** The title of the poll. Has a default min and max */
title: string;
/** The description of the poll. Has a default min and max */
description: string;
/** Generic type of the poll, underlying type can be any string. */
poll_type: PollType;
}
export interface Poll {
id: PollId;
/** Saved as the user who send the create poll transaction */
creator: Address;
metadata: PollMetadata;
expiration: Expiration;
status: PollStatus;
/** Snapshot of the quorum taken from the configuration at the time of creation.
* Used in calculating results until poll has expired */
current_quorum: Decimal;
}
export interface PollResult {
poll_id: PollId;
/** The total number of yes votes, equals the number of tokens staked.
* As vote = stake power */
yes_votes: Uint128;
no_votes: Uint128;
abstain_votes: Uint128;
}
/** All poll information. */
export interface PollInfo {
/** The poll. */
instance: Poll;
/** The up-to-date results of the poll. */
result: PollResult;
}
export interface VoteStatus {
power: Uint128;
choice: PollVote;
}
export interface GetPollResponse {
poll: PollInfo;
}
export interface PaginatedPollList {
polls: Array<Poll>;
total: number;
total_pages: number;
}
export interface GetPollConfigResponse {
config: PollConfig;
}
export enum SortingDirection {
Ascending = 1,
Descending = 0,
}
export interface PollUser {
created_polls: Array<PollId>;
active_polls: Array<PollId>;
}
export class Polls extends Client {
fees = {
create_poll: new Fee('80000', 'uscrt'),
vote: new Fee('100000', 'uscrt'),
unvote: new Fee('100000', 'uscrt'),
change_vote_choice: new Fee('100000', 'uscrt'),
};
get auth () { return new AuthClient(this.agent, this.address, this.codeHash) }
async createPoll(meta: PollMetadata) {
return this.execute({ create_poll: { meta } });
}
async vote(poll_id: PollId, choice: PollVote) {
return this.execute({ vote: { choice, poll_id } });
}
async unvote(poll_id: PollId) {
return this.execute({ unvote: { poll_id } });
}
async changeVote(poll_id: PollId, choice: PollVote) {
return this.execute({ change_vote_choice: { poll_id, choice } });
}
async getPoll(poll_id: PollId, now: Moment = getNow()): Promise<PollInfo> {
const msg = { poll: { poll_id, now } };
const result: PollInfo = await this.query(msg);
return result;
}
async getPolls(
now: Moment,
page: number,
take: number,
sort: SortingDirection
): Promise<PaginatedPollList> {
const msg = { polls: { now, page, take, asc: !!sort } };
return await this.query(msg);
}
async getVoteStatus(address: Address, poll_id: PollId, auth: Auth): Promise<VoteStatus | null> {
const msg = { vote_status: { address, auth, poll_id } };
const result: VoteStatus = await this.query(msg);
if (!result.choice || !result.power) {
return null;
}
return result;
}
async getUser(auth: Auth): Promise<PollUser> {
const msg = { user: { at: Date.now() } };
const result: { user: PollUser } = await this.query(msg);
return result.user;
}
async getPollConfig(): Promise<PollConfig> {
const msg = { config: {} };
const result: { config: PollConfig } = await this.query(msg);
return result.config;
}
}