@leofcoin/standards 0.2.16 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +2 -2
- package/.github/workflows/test.yml +33 -0
- package/.prettierrc +8 -8
- package/CHANGELOG.md +16 -3
- package/LICENSE +21 -21
- package/README.md +25 -23
- package/exports/helpers.js +1 -1
- package/exports/i-public-voting.js +1 -0
- package/exports/index.d.ts +2 -1
- package/exports/index.js +2 -3
- package/exports/meta-D7uruGOw.js +28 -0
- package/exports/meta.d.ts +11 -0
- package/exports/private-voting.js +104 -11
- package/exports/public-voting.js +103 -4
- package/exports/roles.d.ts +5 -10
- package/exports/roles.js +7 -8
- package/exports/token-receiver.d.ts +5 -7
- package/exports/token-receiver.js +25 -14
- package/exports/token.d.ts +14 -27
- package/exports/token.js +14 -48
- package/exports/types.d.ts +21 -0
- package/exports/voting/private-voting.d.ts +109 -11
- package/exports/voting/public-voting.d.ts +45 -8
- package/exports/voting/types.d.ts +11 -7
- package/package.json +10 -18
- package/rollup.config.js +28 -28
- package/src/helpers.ts +19 -19
- package/src/index.ts +8 -7
- package/src/meta.ts +31 -0
- package/src/roles.ts +88 -89
- package/src/token-receiver.ts +196 -175
- package/src/token.ts +162 -198
- package/src/types.ts +15 -0
- package/src/voting/interfaces/i-public-voting.ts +4 -0
- package/src/voting/private-voting.ts +187 -69
- package/src/voting/public-voting.ts +134 -14
- package/src/voting/types.ts +30 -24
- package/test/helpers.js +51 -0
- package/test/public-voting.js +365 -6
- package/test/roles.js +186 -0
- package/test/token.js +211 -0
- package/tsconfig.json +16 -12
- package/.changeset/README.md +0 -8
- package/.changeset/config.json +0 -11
- package/exports/contract-creator.d.ts +0 -11
- package/exports/contract-creator.js +0 -20
- package/exports/decorators/time.d.ts +0 -1
- package/exports/interfaces/i-token.d.ts +0 -10
- package/exports/lock.d.ts +0 -37
- package/exports/staking.d.ts +0 -40
- package/exports/voting/interfaces/i-voting.d.ts +0 -6
- package/exports/voting/voting.d.ts +0 -38
- package/exports/voting-C0KVNQO3.js +0 -112
- package/exports/voting-xYjJlN2h.js +0 -112
- package/src/contract-creator.ts +0 -24
- package/src/decorators/time.ts +0 -9
- package/src/interfaces/i-token.ts +0 -10
- package/src/lock.ts +0 -167
- package/src/staking.ts +0 -166
- package/src/voting/interfaces/i-voting.ts +0 -7
- package/src/voting/voting.ts +0 -123
|
@@ -1,14 +1,134 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { VotingState, VoteResult } from './types.js'
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import Meta from '../meta.js'
|
|
2
|
+
import { VotingState, VoteResult } from './types.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* allows everybody that has a balance greater or equal to tokenAmountToReceive to vote
|
|
6
|
+
*/
|
|
7
|
+
export default class PublicVoting extends Meta {
|
|
8
|
+
#votes: VotingState['votes']
|
|
9
|
+
#votingDisabled: boolean
|
|
10
|
+
#votingDuration: number = 172800000
|
|
11
|
+
|
|
12
|
+
constructor(state: VotingState) {
|
|
13
|
+
super(state)
|
|
14
|
+
if (state) {
|
|
15
|
+
this.#votes = state.votes
|
|
16
|
+
this.#votingDisabled = state.votingDisabled
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get votes() {
|
|
21
|
+
return { ...this.#votes }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get votingDuration() {
|
|
25
|
+
return this.#votingDuration
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get votingDisabled() {
|
|
29
|
+
return this.#votingDisabled
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
*
|
|
34
|
+
*/
|
|
35
|
+
get state() {
|
|
36
|
+
return {
|
|
37
|
+
...super.state,
|
|
38
|
+
votes: this.#votes,
|
|
39
|
+
votingDisabled: this.#votingDisabled,
|
|
40
|
+
votingDuration: this.#votingDuration
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get inProgress() {
|
|
45
|
+
return Object.entries(this.#votes)
|
|
46
|
+
.filter(([id, vote]) => !vote.finished)
|
|
47
|
+
.map(([id, vote]) => {
|
|
48
|
+
return { ...vote, id }
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* create vote
|
|
53
|
+
* @param {string} vote
|
|
54
|
+
* @param {string} description
|
|
55
|
+
* @param {number} endTime
|
|
56
|
+
* @param {string} method function to run when agree amount is bigger
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
createVote(
|
|
60
|
+
title: string,
|
|
61
|
+
description: string,
|
|
62
|
+
endTime: EpochTimeStamp,
|
|
63
|
+
method: string,
|
|
64
|
+
args: any[] = []
|
|
65
|
+
) {
|
|
66
|
+
if (!this.#canVote()) throw new Error(`Not allowed to create a vote`)
|
|
67
|
+
const id = crypto.randomUUID()
|
|
68
|
+
this.#votes[id] = {
|
|
69
|
+
title,
|
|
70
|
+
description,
|
|
71
|
+
method,
|
|
72
|
+
endTime,
|
|
73
|
+
args
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
#canVote() {
|
|
78
|
+
// @ts-expect-error
|
|
79
|
+
return this._canVote?.()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
#beforeVote() {
|
|
83
|
+
// @ts-expect-error
|
|
84
|
+
return this._beforeVote?.()
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
#endVoting(voteId) {
|
|
88
|
+
let agree = Object.values(this.#votes[voteId].results).filter(
|
|
89
|
+
(result) => result === 1
|
|
90
|
+
)
|
|
91
|
+
let disagree = Object.values(this.#votes[voteId].results).filter(
|
|
92
|
+
(result) => result === 0
|
|
93
|
+
)
|
|
94
|
+
if (agree.length > disagree.length && this.#votes[voteId].enoughVotes)
|
|
95
|
+
this[this.#votes[voteId].method](...this.#votes[voteId].args)
|
|
96
|
+
this.#votes[voteId].finished = true
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async vote(voteId: string, vote: VoteResult) {
|
|
100
|
+
vote = Number(vote) as VoteResult
|
|
101
|
+
if (vote !== 0 && vote !== 0.5 && vote !== 1)
|
|
102
|
+
throw new Error(`invalid vote value ${vote}`)
|
|
103
|
+
if (!this.#votes[voteId]) throw new Error(`Nothing found for ${voteId}`)
|
|
104
|
+
const ended = new Date().getTime() > this.#votes[voteId].endTime
|
|
105
|
+
if (ended && !this.#votes[voteId].finished) this.#endVoting(voteId)
|
|
106
|
+
if (ended) throw new Error('voting already ended')
|
|
107
|
+
if (!this.#canVote()) throw new Error(`Not allowed to vote`)
|
|
108
|
+
await this.#beforeVote()
|
|
109
|
+
this.#votes[voteId][msg.sender] = vote
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
#disableVoting() {
|
|
113
|
+
this.#votingDisabled = true
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
disableVoting() {
|
|
117
|
+
if (!this.#canVote()) throw new Error('not a allowed')
|
|
118
|
+
else {
|
|
119
|
+
this.createVote(
|
|
120
|
+
`disable voting`,
|
|
121
|
+
`Warning this disables all voting features forever`,
|
|
122
|
+
new Date().getTime() + this.#votingDuration,
|
|
123
|
+
'#disableVoting',
|
|
124
|
+
[]
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
_sync() {
|
|
130
|
+
for (const vote of this.inProgress) {
|
|
131
|
+
if (vote.endTime < new Date().getTime()) this.#endVoting(vote.id)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
package/src/voting/types.ts
CHANGED
|
@@ -1,24 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
1
|
+
import { MetaState } from '../types.js'
|
|
2
|
+
|
|
3
|
+
export interface VotingState extends MetaState {
|
|
4
|
+
votes: {
|
|
5
|
+
[id: string]: Vote
|
|
6
|
+
}
|
|
7
|
+
votingDisabled: boolean
|
|
8
|
+
votingDuration: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface PrivateVotingState extends VotingState {
|
|
12
|
+
voters: address[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type VoteResult = 0 | 0.5 | 1
|
|
16
|
+
|
|
17
|
+
export type Vote = {
|
|
18
|
+
title: string
|
|
19
|
+
method: string
|
|
20
|
+
args: any[]
|
|
21
|
+
description: string
|
|
22
|
+
endTime: EpochTimeStamp
|
|
23
|
+
results?: { [address: string]: VoteResult }
|
|
24
|
+
finished?: boolean
|
|
25
|
+
enoughVotes?: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface VoteView extends Vote {
|
|
29
|
+
id: string
|
|
30
|
+
}
|
package/test/helpers.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import { restoreBalances, restoreApprovals } from './../exports/helpers.js'
|
|
4
|
+
|
|
5
|
+
test('Helpers - restoreBalances converts string balances to BigInt', () => {
|
|
6
|
+
const balances = {
|
|
7
|
+
'0x1': '1000',
|
|
8
|
+
'0x2': '2000',
|
|
9
|
+
'0x3': '5000'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const restored = restoreBalances(balances)
|
|
13
|
+
|
|
14
|
+
assert.equal(restored['0x1'], 1000n)
|
|
15
|
+
assert.equal(restored['0x2'], 2000n)
|
|
16
|
+
assert.equal(restored['0x3'], 5000n)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test('Helpers - restoreBalances handles empty balances', () => {
|
|
20
|
+
const balances = {}
|
|
21
|
+
|
|
22
|
+
const restored = restoreBalances(balances)
|
|
23
|
+
|
|
24
|
+
assert.deepEqual(restored, {})
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test('Helpers - restoreApprovals converts nested string approvals to BigInt', () => {
|
|
28
|
+
const approvals = {
|
|
29
|
+
'0x1': {
|
|
30
|
+
'0x2': '500',
|
|
31
|
+
'0x3': '1000'
|
|
32
|
+
},
|
|
33
|
+
'0x2': {
|
|
34
|
+
'0x3': '250'
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const restored = restoreApprovals(approvals)
|
|
39
|
+
|
|
40
|
+
assert.equal(restored['0x1']['0x2'], 500n)
|
|
41
|
+
assert.equal(restored['0x1']['0x3'], 1000n)
|
|
42
|
+
assert.equal(restored['0x2']['0x3'], 250n)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('Helpers - restoreApprovals handles empty approvals', () => {
|
|
46
|
+
const approvals = {}
|
|
47
|
+
|
|
48
|
+
const restored = restoreApprovals(approvals)
|
|
49
|
+
|
|
50
|
+
assert.deepEqual(restored, {})
|
|
51
|
+
})
|
package/test/public-voting.js
CHANGED
|
@@ -1,6 +1,365 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import Voting from './../exports/public-voting.js'
|
|
4
|
+
|
|
5
|
+
test('PublicVoting - constructor initializes with correct state', () => {
|
|
6
|
+
const state = {
|
|
7
|
+
votes: {},
|
|
8
|
+
votingDisabled: false,
|
|
9
|
+
votingDuration: 172800000
|
|
10
|
+
}
|
|
11
|
+
const voting = new Voting(state)
|
|
12
|
+
|
|
13
|
+
assert.deepEqual(voting.votes, {})
|
|
14
|
+
assert.equal(voting.votingDisabled, false)
|
|
15
|
+
assert.equal(voting.votingDuration, 172800000)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('PublicVoting - votingDuration getter returns correct value', () => {
|
|
19
|
+
const state = {
|
|
20
|
+
votes: {},
|
|
21
|
+
votingDisabled: false
|
|
22
|
+
}
|
|
23
|
+
const voting = new Voting(state)
|
|
24
|
+
|
|
25
|
+
assert.equal(voting.votingDuration, 172800000)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('PublicVoting - votingDisabled getter returns correct value', () => {
|
|
29
|
+
const state = {
|
|
30
|
+
votes: {},
|
|
31
|
+
votingDisabled: true
|
|
32
|
+
}
|
|
33
|
+
const voting = new Voting(state)
|
|
34
|
+
|
|
35
|
+
assert.equal(voting.votingDisabled, true)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('PublicVoting - inProgress returns empty array when no votes', () => {
|
|
39
|
+
const state = {
|
|
40
|
+
votes: {},
|
|
41
|
+
votingDisabled: false
|
|
42
|
+
}
|
|
43
|
+
const voting = new Voting(state)
|
|
44
|
+
|
|
45
|
+
assert.deepEqual(voting.inProgress, [])
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('PublicVoting - votes getter returns existing votes', () => {
|
|
49
|
+
const voteId = '123e4567-e89b-12d3-a456-426614174000'
|
|
50
|
+
const state = {
|
|
51
|
+
creator: '0x1234567890123456789012345678901234567890',
|
|
52
|
+
votes: {
|
|
53
|
+
[voteId]: {
|
|
54
|
+
title: 'Test Vote',
|
|
55
|
+
description: 'A test vote',
|
|
56
|
+
method: 'testMethod',
|
|
57
|
+
endTime: Date.now() + 100000,
|
|
58
|
+
args: [],
|
|
59
|
+
results: {},
|
|
60
|
+
finished: false
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
votingDisabled: false
|
|
64
|
+
}
|
|
65
|
+
const voting = new Voting(state)
|
|
66
|
+
const votes = voting.votes
|
|
67
|
+
|
|
68
|
+
assert.equal(Object.keys(votes).length, 1)
|
|
69
|
+
assert.equal(votes[voteId].title, 'Test Vote')
|
|
70
|
+
assert.equal(votes[voteId].description, 'A test vote')
|
|
71
|
+
assert.equal(votes[voteId].method, 'testMethod')
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test('PublicVoting - inProgress returns only unfinished votes', () => {
|
|
75
|
+
const activeVoteId = '123e4567-e89b-12d3-a456-426614174000'
|
|
76
|
+
const finishedVoteId = '987e6543-e21b-12d3-a456-426614174001'
|
|
77
|
+
const state = {
|
|
78
|
+
creator: '0x1234567890123456789012345678901234567890',
|
|
79
|
+
votes: {
|
|
80
|
+
[activeVoteId]: {
|
|
81
|
+
title: 'Active Vote',
|
|
82
|
+
description: 'An active vote',
|
|
83
|
+
method: 'testMethod',
|
|
84
|
+
endTime: Date.now() + 100000,
|
|
85
|
+
args: [],
|
|
86
|
+
finished: false
|
|
87
|
+
},
|
|
88
|
+
[finishedVoteId]: {
|
|
89
|
+
title: 'Finished Vote',
|
|
90
|
+
description: 'A finished vote',
|
|
91
|
+
method: 'testMethod',
|
|
92
|
+
endTime: Date.now() - 100000,
|
|
93
|
+
args: [],
|
|
94
|
+
finished: true
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
votingDisabled: false
|
|
98
|
+
}
|
|
99
|
+
const voting = new Voting(state)
|
|
100
|
+
const inProgress = voting.inProgress
|
|
101
|
+
|
|
102
|
+
assert.equal(inProgress.length, 1)
|
|
103
|
+
assert.equal(inProgress[0].title, 'Active Vote')
|
|
104
|
+
assert.equal(inProgress[0].id, activeVoteId)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
test('PublicVoting - vote throws error for invalid vote value', async () => {
|
|
108
|
+
const voteId = '123e4567-e89b-12d3-a456-426614174000'
|
|
109
|
+
const state = {
|
|
110
|
+
creator: '0x1234567890123456789012345678901234567890',
|
|
111
|
+
votes: {
|
|
112
|
+
[voteId]: {
|
|
113
|
+
title: 'Test Vote',
|
|
114
|
+
description: 'A test vote',
|
|
115
|
+
method: 'testMethod',
|
|
116
|
+
endTime: Date.now() + 100000,
|
|
117
|
+
args: [],
|
|
118
|
+
results: {},
|
|
119
|
+
finished: false
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
votingDisabled: false
|
|
123
|
+
}
|
|
124
|
+
const voting = new Voting(state)
|
|
125
|
+
|
|
126
|
+
await assert.rejects(async () => voting.vote(voteId, 2), /invalid vote value/)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
test('PublicVoting - vote throws error for non-existent vote', async () => {
|
|
130
|
+
const state = {
|
|
131
|
+
creator: '0x1234567890123456789012345678901234567890',
|
|
132
|
+
votes: {},
|
|
133
|
+
votingDisabled: false
|
|
134
|
+
}
|
|
135
|
+
const voting = new Voting(state)
|
|
136
|
+
|
|
137
|
+
await assert.rejects(
|
|
138
|
+
async () => voting.vote('non-existent-id', 1),
|
|
139
|
+
/Nothing found for/
|
|
140
|
+
)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
test('PublicVoting - vote throws error for ended vote', async () => {
|
|
144
|
+
const voteId = '123e4567-e89b-12d3-a456-426614174000'
|
|
145
|
+
const state = {
|
|
146
|
+
creator: '0x1234567890123456789012345678901234567890',
|
|
147
|
+
votes: {
|
|
148
|
+
[voteId]: {
|
|
149
|
+
title: 'Expired Vote',
|
|
150
|
+
description: 'An expired vote',
|
|
151
|
+
method: 'testMethod',
|
|
152
|
+
endTime: Date.now() - 100000, // Already ended
|
|
153
|
+
args: [],
|
|
154
|
+
results: {},
|
|
155
|
+
finished: false
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
votingDisabled: false
|
|
159
|
+
}
|
|
160
|
+
const voting = new Voting(state)
|
|
161
|
+
|
|
162
|
+
await assert.rejects(
|
|
163
|
+
async () => voting.vote(voteId, 1),
|
|
164
|
+
/voting already ended/
|
|
165
|
+
)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
test('PublicVoting - vote ending marks vote as finished when time expires', () => {
|
|
169
|
+
const voteId = '123e4567-e89b-12d3-a456-426614174000'
|
|
170
|
+
const state = {
|
|
171
|
+
creator: '0x1234567890123456789012345678901234567890',
|
|
172
|
+
votes: {
|
|
173
|
+
[voteId]: {
|
|
174
|
+
title: 'Expired Vote',
|
|
175
|
+
description: 'A vote that should end',
|
|
176
|
+
method: 'testMethod',
|
|
177
|
+
endTime: Date.now() - 100000, // Already ended
|
|
178
|
+
args: [],
|
|
179
|
+
results: {
|
|
180
|
+
'0x1': 0, // disagree
|
|
181
|
+
'0x2': 0, // disagree
|
|
182
|
+
'0x3': 1 // agree
|
|
183
|
+
},
|
|
184
|
+
finished: false,
|
|
185
|
+
enoughVotes: true
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
votingDisabled: false
|
|
189
|
+
}
|
|
190
|
+
const voting = new Voting(state)
|
|
191
|
+
|
|
192
|
+
// Before sync, should be in progress
|
|
193
|
+
assert.equal(voting.inProgress.length, 1)
|
|
194
|
+
|
|
195
|
+
// Run sync to end expired vote
|
|
196
|
+
voting._sync()
|
|
197
|
+
|
|
198
|
+
// After sync, should not be in progress anymore
|
|
199
|
+
assert.equal(voting.inProgress.length, 0)
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
test('PublicVoting - _sync ends all expired in-progress votes', () => {
|
|
203
|
+
const expiredVoteId1 = '123e4567-e89b-12d3-a456-426614174000'
|
|
204
|
+
const expiredVoteId2 = '123e4567-e89b-12d3-a456-426614174001'
|
|
205
|
+
const activeVoteId = '123e4567-e89b-12d3-a456-426614174002'
|
|
206
|
+
|
|
207
|
+
const state = {
|
|
208
|
+
creator: '0x1234567890123456789012345678901234567890',
|
|
209
|
+
votes: {
|
|
210
|
+
[expiredVoteId1]: {
|
|
211
|
+
title: 'Expired Vote 1',
|
|
212
|
+
description: 'First expired vote',
|
|
213
|
+
method: 'testMethod',
|
|
214
|
+
endTime: Date.now() - 50000,
|
|
215
|
+
args: [],
|
|
216
|
+
results: { '0x1': 1, '0x2': 0 },
|
|
217
|
+
finished: false
|
|
218
|
+
},
|
|
219
|
+
[expiredVoteId2]: {
|
|
220
|
+
title: 'Expired Vote 2',
|
|
221
|
+
description: 'Second expired vote',
|
|
222
|
+
method: 'testMethod',
|
|
223
|
+
endTime: Date.now() - 100000,
|
|
224
|
+
args: [],
|
|
225
|
+
results: { '0x1': 0, '0x2': 0 },
|
|
226
|
+
finished: false
|
|
227
|
+
},
|
|
228
|
+
[activeVoteId]: {
|
|
229
|
+
title: 'Active Vote',
|
|
230
|
+
description: 'Still active vote',
|
|
231
|
+
method: 'testMethod',
|
|
232
|
+
endTime: Date.now() + 100000,
|
|
233
|
+
args: [],
|
|
234
|
+
results: {},
|
|
235
|
+
finished: false
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
votingDisabled: false
|
|
239
|
+
}
|
|
240
|
+
const voting = new Voting(state)
|
|
241
|
+
|
|
242
|
+
// Before sync, should have 3 in-progress votes
|
|
243
|
+
assert.equal(voting.inProgress.length, 3)
|
|
244
|
+
|
|
245
|
+
// Run sync to end expired votes
|
|
246
|
+
voting._sync()
|
|
247
|
+
|
|
248
|
+
// After sync, only the active vote should remain in progress
|
|
249
|
+
const inProgress = voting.inProgress
|
|
250
|
+
assert.equal(inProgress.length, 1)
|
|
251
|
+
assert.equal(inProgress[0].id, activeVoteId)
|
|
252
|
+
|
|
253
|
+
// Check that expired votes are marked as finished
|
|
254
|
+
const votes = voting.votes
|
|
255
|
+
assert.equal(votes[expiredVoteId1].finished, true)
|
|
256
|
+
assert.equal(votes[expiredVoteId2].finished, true)
|
|
257
|
+
assert.equal(votes[activeVoteId].finished, false)
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
test('PublicVoting - vote ending with more agrees than disagrees', () => {
|
|
261
|
+
const voteId = '123e4567-e89b-12d3-a456-426614174000'
|
|
262
|
+
const state = {
|
|
263
|
+
creator: '0x1234567890123456789012345678901234567890',
|
|
264
|
+
votes: {
|
|
265
|
+
[voteId]: {
|
|
266
|
+
title: 'Vote with Majority Agree',
|
|
267
|
+
description: 'Should pass with majority',
|
|
268
|
+
method: 'testMethod',
|
|
269
|
+
endTime: Date.now() - 1000,
|
|
270
|
+
args: ['arg1', 'arg2'],
|
|
271
|
+
results: {
|
|
272
|
+
'0x1': 1, // agree
|
|
273
|
+
'0x2': 1, // agree
|
|
274
|
+
'0x3': 1, // agree
|
|
275
|
+
'0x4': 0, // disagree
|
|
276
|
+
'0x5': 0 // disagree
|
|
277
|
+
},
|
|
278
|
+
finished: false,
|
|
279
|
+
enoughVotes: false // Not enough votes to execute method
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
votingDisabled: false
|
|
283
|
+
}
|
|
284
|
+
const voting = new Voting(state)
|
|
285
|
+
|
|
286
|
+
// Before sync, vote should be in progress
|
|
287
|
+
assert.equal(voting.inProgress.length, 1)
|
|
288
|
+
|
|
289
|
+
// Run sync to trigger vote ending
|
|
290
|
+
voting._sync()
|
|
291
|
+
|
|
292
|
+
// After sync, vote should not be in progress
|
|
293
|
+
assert.equal(voting.inProgress.length, 0)
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
test('PublicVoting - vote ending with more disagrees than agrees', async () => {
|
|
297
|
+
const voteId = '123e4567-e89b-12d3-a456-426614174000'
|
|
298
|
+
const state = {
|
|
299
|
+
creator: '0x1234567890123456789012345678901234567890',
|
|
300
|
+
votes: {
|
|
301
|
+
[voteId]: {
|
|
302
|
+
title: 'Vote with Majority Disagree',
|
|
303
|
+
description: 'Should not pass',
|
|
304
|
+
method: 'testMethod',
|
|
305
|
+
endTime: Date.now() - 1000,
|
|
306
|
+
args: [],
|
|
307
|
+
results: {
|
|
308
|
+
'0x1': 0, // disagree
|
|
309
|
+
'0x2': 0, // disagree
|
|
310
|
+
'0x3': 0, // disagree
|
|
311
|
+
'0x4': 1, // agree
|
|
312
|
+
'0x5': 1 // agree
|
|
313
|
+
},
|
|
314
|
+
finished: false,
|
|
315
|
+
enoughVotes: true
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
votingDisabled: false
|
|
319
|
+
}
|
|
320
|
+
const voting = new Voting(state)
|
|
321
|
+
|
|
322
|
+
// Trigger vote ending
|
|
323
|
+
try {
|
|
324
|
+
await voting.vote(voteId, 1)
|
|
325
|
+
} catch (err) {
|
|
326
|
+
// Expected to throw "voting already ended"
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Verify vote is finished even though it didn't pass
|
|
330
|
+
assert.equal(voting.votes[voteId].finished, true)
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
test('PublicVoting - vote ending without enoughVotes flag does not execute method', async () => {
|
|
334
|
+
const voteId = '123e4567-e89b-12d3-a456-426614174000'
|
|
335
|
+
const state = {
|
|
336
|
+
creator: '0x1234567890123456789012345678901234567890',
|
|
337
|
+
votes: {
|
|
338
|
+
[voteId]: {
|
|
339
|
+
title: 'Vote Without Enough Votes',
|
|
340
|
+
description: 'Has majority but not enough votes',
|
|
341
|
+
method: 'testMethod',
|
|
342
|
+
endTime: Date.now() - 1000,
|
|
343
|
+
args: [],
|
|
344
|
+
results: {
|
|
345
|
+
'0x1': 1, // agree
|
|
346
|
+
'0x2': 0 // disagree
|
|
347
|
+
},
|
|
348
|
+
finished: false,
|
|
349
|
+
enoughVotes: false // Not enough votes to execute
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
votingDisabled: false
|
|
353
|
+
}
|
|
354
|
+
const voting = new Voting(state)
|
|
355
|
+
|
|
356
|
+
// Trigger vote ending
|
|
357
|
+
try {
|
|
358
|
+
await voting.vote(voteId, 1)
|
|
359
|
+
} catch (err) {
|
|
360
|
+
// Expected to throw "voting already ended"
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Vote should be marked as finished
|
|
364
|
+
assert.equal(voting.votes[voteId].finished, true)
|
|
365
|
+
})
|