@leofcoin/standards 0.2.15 → 0.3.0

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.
Files changed (59) hide show
  1. package/.gitattributes +2 -2
  2. package/.github/workflows/test.yml +33 -0
  3. package/.prettierrc +8 -8
  4. package/CHANGELOG.md +16 -3
  5. package/LICENSE +21 -21
  6. package/README.md +25 -23
  7. package/exports/helpers.js +1 -1
  8. package/exports/i-public-voting.js +1 -0
  9. package/exports/index.d.ts +2 -1
  10. package/exports/index.js +2 -3
  11. package/exports/meta-D7uruGOw.js +28 -0
  12. package/exports/meta.d.ts +11 -0
  13. package/exports/private-voting.js +104 -11
  14. package/exports/public-voting.js +103 -4
  15. package/exports/roles.d.ts +5 -10
  16. package/exports/roles.js +7 -8
  17. package/exports/token-receiver.d.ts +5 -7
  18. package/exports/token-receiver.js +25 -14
  19. package/exports/token.d.ts +14 -27
  20. package/exports/token.js +14 -48
  21. package/exports/types.d.ts +21 -0
  22. package/exports/voting/private-voting.d.ts +108 -11
  23. package/exports/voting/public-voting.d.ts +45 -8
  24. package/exports/voting/types.d.ts +11 -7
  25. package/package.json +9 -14
  26. package/rollup.config.js +28 -28
  27. package/src/helpers.ts +19 -19
  28. package/src/index.ts +8 -7
  29. package/src/meta.ts +31 -0
  30. package/src/roles.ts +88 -89
  31. package/src/token-receiver.ts +196 -175
  32. package/src/token.ts +162 -198
  33. package/src/types.ts +15 -0
  34. package/src/voting/interfaces/i-public-voting.ts +4 -0
  35. package/src/voting/private-voting.ts +187 -69
  36. package/src/voting/public-voting.ts +134 -14
  37. package/src/voting/types.ts +30 -24
  38. package/test/helpers.js +51 -0
  39. package/test/public-voting.js +365 -6
  40. package/test/roles.js +186 -0
  41. package/test/token.js +211 -0
  42. package/tsconfig.json +16 -12
  43. package/.changeset/README.md +0 -8
  44. package/.changeset/config.json +0 -11
  45. package/exports/contract-creator.d.ts +0 -11
  46. package/exports/contract-creator.js +0 -20
  47. package/exports/lock.d.ts +0 -37
  48. package/exports/staking.d.ts +0 -40
  49. package/exports/voting/interfaces/i-voting.d.ts +0 -5
  50. package/exports/voting/voting.d.ts +0 -38
  51. package/exports/voting-C0KVNQO3.js +0 -112
  52. package/exports/voting-xYjJlN2h.js +0 -112
  53. package/src/contract-creator.ts +0 -24
  54. package/src/decorators/time.ts +0 -9
  55. package/src/interfaces/i-token.ts +0 -9
  56. package/src/lock.ts +0 -167
  57. package/src/staking.ts +0 -166
  58. package/src/voting/interfaces/i-voting.ts +0 -5
  59. package/src/voting/voting.ts +0 -123
package/src/token.ts CHANGED
@@ -1,198 +1,162 @@
1
- import { restoreApprovals, restoreBalances } from './helpers.js'
2
- import Roles, { RolesState } from './roles.js'
3
-
4
- export declare interface TokenState extends RolesState {
5
- name: string
6
- symbol: string
7
- decimals: number
8
- holders: bigint
9
- balances: { [address: address]: bigint }
10
- approvals: { [owner: address]: { [operator: address]: bigint } }
11
- totalSupply: bigint
12
- maxSupply: bigint // 0 for unlimited
13
- }
14
-
15
- export default class Token extends Roles {
16
- /**
17
- * string
18
- */
19
- #name: string
20
- /**
21
- * String
22
- */
23
- #symbol: string
24
- /**
25
- * uint
26
- */
27
- #holders: bigint = BigInt(0)
28
- /**
29
- * Object => Object => uint
30
- */
31
- #balances = {}
32
- /**
33
- * Object => Object => uint
34
- */
35
- #approvals: { [owner: string]: { [operator: string]: bigint } } = {}
36
-
37
- #decimals = 18
38
-
39
- #totalSupply: bigint = BigInt(0)
40
-
41
- #maxSupply: bigint = BigInt(0)
42
-
43
- #stakingContract: address
44
-
45
- constructor(name: string, symbol: string, decimals: number = 18, state?: TokenState) {
46
- if (!name) throw new Error(`name undefined`)
47
- if (!symbol) throw new Error(`symbol undefined`)
48
-
49
- super(state)
50
-
51
- if (state) {
52
- this.#balances = restoreBalances(state.balances)
53
- this.#approvals = restoreApprovals(state.approvals)
54
- this.#holders = BigInt(state.holders)
55
- this.#totalSupply = BigInt(state.totalSupply)
56
- this.#name = name
57
- this.#symbol = symbol
58
- this.#decimals = decimals
59
- this.#maxSupply = BigInt(state.maxSupply)
60
- } else {
61
- this.#name = name
62
- this.#symbol = symbol
63
- this.#decimals = decimals
64
- }
65
- }
66
-
67
- // enables snapshotting
68
- // needs dev attention so nothing breaks after snapshot happens
69
- // iow everything that is not static needs to be included in the stateObject
70
- // TODO: implement snapshotting test
71
- /**
72
- * @return {Object} {holders, balances, ...}
73
- */
74
- get state(): TokenState {
75
- return {
76
- ...super.state,
77
- name: this.#name,
78
- symbol: this.#symbol,
79
- decimals: this.#decimals,
80
- holders: this.holders,
81
- balances: this.balances,
82
- approvals: { ...this.#approvals },
83
- totalSupply: this.totalSupply,
84
- maxSupply: this.#maxSupply
85
- }
86
- }
87
-
88
- get maxSupply(): TokenState['maxSupply'] {
89
- return this.#maxSupply
90
- }
91
-
92
- get totalSupply(): TokenState['totalSupply'] {
93
- return this.#totalSupply
94
- }
95
-
96
- get name(): TokenState['name'] {
97
- return this.#name
98
- }
99
-
100
- get symbol(): TokenState['symbol'] {
101
- return this.#symbol
102
- }
103
-
104
- get holders(): TokenState['holders'] {
105
- return this.#holders
106
- }
107
-
108
- get balances(): TokenState['balances'] {
109
- return { ...this.#balances }
110
- }
111
-
112
- get approvals(): TokenState['approvals'] {
113
- return this.#approvals
114
- }
115
-
116
- get decimals(): TokenState['decimals'] {
117
- return this.#decimals
118
- }
119
-
120
- mint(to: address, amount: bigint) {
121
- if (!this.hasRole(msg.sender, 'MINT')) throw new Error('mint role required')
122
-
123
- const supply = this.#totalSupply + amount
124
- if (this.#maxSupply === 0n) {
125
- this.#totalSupply = supply
126
- this.#increaseBalance(to, amount)
127
- } else {
128
- if (supply <= this.#maxSupply) {
129
- this.#totalSupply = supply
130
- this.#increaseBalance(to, amount)
131
- } else {
132
- throw new Error('amount exceeds max supply')
133
- }
134
- }
135
- }
136
-
137
- burn(from: address, amount: bigint) {
138
- if (!this.hasRole(msg.sender, 'BURN') && msg.sender !== from) throw new Error('not the owner or burn role required')
139
- if (this.#balances[from] < amount) throw new Error('amount exceeds balance')
140
-
141
- const total = this.#totalSupply - amount
142
- if (total >= 0) {
143
- this.#totalSupply = total
144
- this.#decreaseBalance(from, amount)
145
- } else {
146
- throw new Error('amount exceeds total supply')
147
- }
148
- }
149
-
150
- #beforeTransfer(from: address, to: address, amount: bigint) {
151
- if (!this.#balances[from] || this.#balances[from] < amount) throw new Error('amount exceeds balance')
152
- }
153
-
154
- #updateHolders(address: address, previousBalance: bigint) {
155
- if (this.#balances[address] === 0n) this.#holders -= 1n
156
- else if (this.#balances[address] !== 0n && previousBalance === 0n) this.#holders += 1n
157
- }
158
-
159
- #increaseBalance(address: address, amount: bigint) {
160
- if (!this.#balances[address]) this.#balances[address] = BigInt(0)
161
- const previousBalance = this.#balances[address]
162
-
163
- this.#balances[address] = this.#balances[address] += amount
164
- this.#updateHolders(address, previousBalance)
165
- }
166
-
167
- #decreaseBalance(address: address, amount: bigint) {
168
- const previousBalance = this.#balances[address]
169
- this.#balances[address] = this.#balances[address] -= amount
170
- this.#updateHolders(address, previousBalance)
171
- }
172
-
173
- balance() {
174
- return this.#balances[msg.sender]
175
- }
176
-
177
- balanceOf(address: address): bigint {
178
- return this.#balances[address]
179
- }
180
-
181
- setApproval(operator: address, amount: bigint) {
182
- const owner = msg.sender
183
- if (!this.#approvals[owner]) this.#approvals[owner] = {}
184
- this.#approvals[owner][operator] = BigInt(amount)
185
- }
186
-
187
- approved(owner: address, operator: address, amount: bigint): boolean {
188
- return this.#approvals[owner][operator] === amount
189
- }
190
-
191
- transfer(from: address, to: address, amount: bigint) {
192
- // TODO: is bigint?
193
- amount = BigInt(amount)
194
- this.#beforeTransfer(from, to, amount)
195
- this.#decreaseBalance(from, amount)
196
- this.#increaseBalance(to, amount)
197
- }
198
- }
1
+ import { restoreApprovals, restoreBalances } from './helpers.js'
2
+ import Roles from './roles.js'
3
+ import { TokenState } from './types.js'
4
+
5
+ export default class Token extends Roles {
6
+ /**
7
+ * string
8
+ */
9
+ #name: string
10
+ /**
11
+ * String
12
+ */
13
+ #symbol: string
14
+ /**
15
+ * uint
16
+ */
17
+ #holders: bigint = 0n
18
+ /**
19
+ * Object => Object => uint
20
+ */
21
+ #balances = {}
22
+ /**
23
+ * Object => Object => uint
24
+ */
25
+ #approvals: { [owner: string]: { [operator: string]: bigint } } = {}
26
+
27
+ #decimals = 18
28
+
29
+ #totalSupply: bigint = 0n
30
+
31
+ constructor(
32
+ name: string,
33
+ symbol: string,
34
+ decimals: number = 18,
35
+ state?: TokenState
36
+ ) {
37
+ if (!name) throw new Error(`name undefined`)
38
+ if (!symbol) throw new Error(`symbol undefined`)
39
+
40
+ super(state)
41
+
42
+ if (state) {
43
+ this.#balances = restoreBalances(state.balances)
44
+ this.#approvals = restoreApprovals(state.approvals)
45
+ this.#holders = BigInt(state.holders)
46
+ this.#totalSupply = BigInt(state.totalSupply)
47
+ } else {
48
+ this.#name = name
49
+ this.#symbol = symbol
50
+ this.#decimals = decimals
51
+ }
52
+ }
53
+
54
+ // enables snapshotting
55
+ // needs dev attention so nothing breaks after snapshot happens
56
+ // iow everything that is not static needs to be included in the stateObject
57
+ /**
58
+ * @return {Object} {holders, balances, ...}
59
+ */
60
+ get state(): {} {
61
+ return {
62
+ ...super.state,
63
+ holders: this.holders,
64
+ balances: this.balances,
65
+ approvals: { ...this.#approvals },
66
+ totalSupply: this.totalSupply
67
+ }
68
+ }
69
+
70
+ get totalSupply(): bigint {
71
+ return this.#totalSupply
72
+ }
73
+
74
+ get name(): string {
75
+ return this.#name
76
+ }
77
+
78
+ get symbol(): string {
79
+ return this.#symbol
80
+ }
81
+
82
+ get holders(): {} {
83
+ return this.#holders
84
+ }
85
+
86
+ get balances(): {} {
87
+ return { ...this.#balances }
88
+ }
89
+
90
+ get approvals() {
91
+ return this.#approvals
92
+ }
93
+
94
+ get decimals() {
95
+ return this.#decimals
96
+ }
97
+
98
+ mint(to: address, amount: bigint) {
99
+ if (!this.hasRole(msg.sender, 'MINT')) throw new Error('not allowed')
100
+
101
+ this.#totalSupply = this.#totalSupply + amount
102
+ this.#increaseBalance(to, amount)
103
+ }
104
+
105
+ burn(from: address, amount: bigint) {
106
+ if (!this.hasRole(msg.sender, 'BURN')) throw new Error('not allowed')
107
+
108
+ this.#totalSupply = this.#totalSupply - amount
109
+ this.#decreaseBalance(from, amount)
110
+ }
111
+
112
+ #beforeTransfer(from: address, to: address, amount: bigint) {
113
+ if (!this.#balances[from] || this.#balances[from] < amount)
114
+ throw new Error('amount exceeds balance')
115
+ }
116
+
117
+ #updateHolders(address: address, previousBalance: bigint) {
118
+ if (this.#balances[address] === 0n) this.#holders -= 1n
119
+ else if (this.#balances[address] !== 0n && previousBalance === 0n)
120
+ this.#holders += 1n
121
+ }
122
+
123
+ #increaseBalance(address: address, amount: bigint) {
124
+ if (!this.#balances[address]) this.#balances[address] = 0n
125
+ const previousBalance = this.#balances[address]
126
+
127
+ this.#balances[address] = this.#balances[address] + amount
128
+ this.#updateHolders(address, previousBalance)
129
+ }
130
+
131
+ #decreaseBalance(address: address, amount: bigint) {
132
+ const previousBalance = this.#balances[address]
133
+ this.#balances[address] = this.#balances[address] - amount
134
+ this.#updateHolders(address, previousBalance)
135
+ }
136
+
137
+ balance() {
138
+ return this.#balances[msg.sender]
139
+ }
140
+
141
+ balanceOf(address: address): bigint {
142
+ return this.#balances[address]
143
+ }
144
+
145
+ setApproval(operator: address, amount: bigint) {
146
+ const owner = msg.sender
147
+ if (!this.#approvals[owner]) this.#approvals[owner] = {}
148
+ this.#approvals[owner][operator] = BigInt(amount)
149
+ }
150
+
151
+ approved(owner: address, operator: address, amount: bigint): boolean {
152
+ return this.#approvals[owner][operator] === amount
153
+ }
154
+
155
+ transfer(from: address, to: address, amount: bigint) {
156
+ // TODO: is bigint?
157
+ amount = BigInt(amount)
158
+ this.#beforeTransfer(from, to, amount)
159
+ this.#decreaseBalance(from, amount)
160
+ this.#increaseBalance(to, amount)
161
+ }
162
+ }
package/src/types.ts ADDED
@@ -0,0 +1,15 @@
1
+ export interface MetaState {
2
+ creator: address
3
+ createdAt: bigint
4
+ }
5
+
6
+ export declare interface RolesState extends MetaState {
7
+ roles: { [index: string]: address[] }
8
+ }
9
+
10
+ export declare interface TokenState extends RolesState {
11
+ holders: bigint
12
+ balances: { [address: address]: bigint }
13
+ approvals: { [owner: address]: { [operator: address]: bigint } }
14
+ totalSupply: bigint
15
+ }
@@ -0,0 +1,4 @@
1
+ export interface IPublicVoting {
2
+ _canVote(): Promise<any>
3
+ _beforeVote(): Promise<any>
4
+ }
@@ -1,69 +1,187 @@
1
- import ContractCreator, { ContractCreatorState } from '../contract-creator.js'
2
- import { IVoting } from './interfaces/i-voting.js'
3
- import { VoteResult, VoteView, VotingState } from './types.js'
4
- import Voting from './voting.js'
5
-
6
- export interface PrivateVotingState extends VotingState, ContractCreatorState {
7
- voters: address[]
8
- }
9
-
10
- export default class PrivateVoting extends Voting implements IVoting {
11
- #voters: PrivateVotingState['voters']
12
-
13
- constructor(state: PrivateVotingState) {
14
- super(state)
15
- if (state) {
16
- this.#voters = state.voters
17
- } else {
18
- this.#voters = [msg.sender]
19
- }
20
- }
21
-
22
- get state(): PrivateVotingState {
23
- return {
24
- ...super.state,
25
- voters: this.#voters
26
- }
27
- }
28
-
29
- _canVote(): boolean {
30
- return this.#voters.includes(msg.sender)
31
- }
32
-
33
- #grantVotingPower(address) {
34
- this.#voters.push(address)
35
- }
36
-
37
- #revokeVotingPower(address) {
38
- this.#voters.splice(this.#voters.indexOf(address))
39
- }
40
-
41
- grantVotingPower(address: address, voteId: string) {
42
- if (this.#voters.length === 1 && this._canVote()) this.#grantVotingPower(address)
43
- else {
44
- this.createVote(
45
- `grant voting power to ${address}`,
46
- `Should we grant ${address} voting power?`,
47
- new Date().getTime() + this.votingDuration,
48
- '#grantVotingPower',
49
- [address]
50
- )
51
- }
52
- }
53
-
54
- revokeVotingPower(address: address, voteId: string) {
55
- if (!this._canVote()) throw new Error('not a allowed to vote')
56
- if (this.#voters.length === 1 && address === msg.sender && !this.votingDisabled)
57
- throw new Error('only one voter left, disable voting before making this contract voteless')
58
- if (this.#voters.length === 1) this.#revokeVotingPower(address)
59
- else {
60
- this.createVote(
61
- `revoke voting power for ${address}`,
62
- `Should we revoke ${address} it's voting power?`,
63
- new Date().getTime() + this.votingDuration,
64
- '#revokeVotingPower',
65
- [address]
66
- )
67
- }
68
- }
69
- }
1
+ import Meta from '../meta.js'
2
+ import {
3
+ PrivateVotingState,
4
+ VoteResult,
5
+ VoteView,
6
+ VotingState
7
+ } from './types.js'
8
+
9
+ export default class PrivateVoting extends Meta {
10
+ #voters: PrivateVotingState['voters']
11
+ #votes: PrivateVotingState['votes']
12
+ #votingDisabled: boolean
13
+
14
+ constructor(state: PrivateVotingState) {
15
+ super(state)
16
+ if (state) {
17
+ this.#voters = state.voters
18
+ this.#votes = state.votes
19
+ this.#votingDisabled = state.votingDisabled
20
+ } else {
21
+ this.#voters = [msg.sender]
22
+ }
23
+ }
24
+
25
+ get votes() {
26
+ return { ...this.#votes }
27
+ }
28
+
29
+ get voters() {
30
+ return { ...this.#voters }
31
+ }
32
+
33
+ get votingDisabled() {
34
+ return this.#votingDisabled
35
+ }
36
+
37
+ /**
38
+ *
39
+ */
40
+ get state(): {} {
41
+ return {
42
+ ...super.state,
43
+ voters: this.#voters,
44
+ votes: this.#votes,
45
+ votingDisabled: this.#votingDisabled
46
+ }
47
+ }
48
+
49
+ get inProgress(): VoteView[] {
50
+ return Object.entries(this.#votes)
51
+ .filter(([id, vote]) => !vote.finished)
52
+ .map(([id, vote]) => {
53
+ return { ...vote, id }
54
+ })
55
+ }
56
+ /**
57
+ * create vote
58
+ * @param {string} vote
59
+ * @param {string} description
60
+ * @param {number} endTime
61
+ * @param {string} method function to run when agree amount is bigger
62
+ */
63
+
64
+ createVote(
65
+ title: string,
66
+ description: string,
67
+ endTime: EpochTimeStamp,
68
+ method: string,
69
+ args: any[] = []
70
+ ) {
71
+ if (!this.canVote(msg.sender))
72
+ throw new Error(`Not allowed to create a vote`)
73
+ const id = crypto.randomUUID()
74
+ this.#votes[id] = {
75
+ title,
76
+ description,
77
+ method,
78
+ endTime,
79
+ args
80
+ }
81
+ }
82
+
83
+ canVote(address: address) {
84
+ return this.#voters.includes(address)
85
+ }
86
+
87
+ #enoughVotes(id) {
88
+ return this.#voters.length - 2 <= Object.keys(this.#votes[id]).length
89
+ }
90
+
91
+ #endVoting(voteId) {
92
+ let agree = Object.values(this.#votes[voteId].results).filter(
93
+ (result) => result === 1
94
+ )
95
+ let disagree = Object.values(this.#votes[voteId].results).filter(
96
+ (result) => result === 0
97
+ )
98
+ this.#votes[voteId].enoughVotes = this.#enoughVotes(voteId)
99
+ if (agree.length > disagree.length && this.#votes[voteId].enoughVotes)
100
+ this[this.#votes[voteId].method](...this.#votes[voteId].args)
101
+ this.#votes[voteId].finished = true
102
+ }
103
+
104
+ vote(voteId: string, vote: VoteResult) {
105
+ vote = Number(vote) as VoteResult
106
+ if (vote !== 0 && vote !== 0.5 && vote !== 1)
107
+ throw new Error(`invalid vote value ${vote}`)
108
+ if (!this.#votes[voteId]) throw new Error(`Nothing found for ${voteId}`)
109
+ const ended = new Date().getTime() > this.#votes[voteId].endTime
110
+ if (ended && !this.#votes[voteId].finished) this.#endVoting(voteId)
111
+ if (ended) throw new Error('voting already ended')
112
+ if (!this.canVote(msg.sender)) throw new Error(`Not allowed to vote`)
113
+ this.#votes[voteId][msg.sender] = vote
114
+ if (this.#enoughVotes(voteId)) {
115
+ this.#endVoting(voteId)
116
+ }
117
+ }
118
+
119
+ #disableVoting() {
120
+ this.#votingDisabled = true
121
+ this.#voters = []
122
+ }
123
+
124
+ #grantVotingPower(address) {
125
+ this.#voters.push(address)
126
+ }
127
+
128
+ #revokeVotingPower(address) {
129
+ this.#voters.splice(this.#voters.indexOf(address))
130
+ }
131
+
132
+ disableVoting() {
133
+ if (!this.canVote(msg.sender)) throw new Error('not a allowed')
134
+ if (this.#voters.length === 1) this.#disableVoting()
135
+ else {
136
+ this.createVote(
137
+ `disable voting`,
138
+ `Warning this disables all voting features forever`,
139
+ new Date().getTime() + 172800000,
140
+ '#disableVoting',
141
+ []
142
+ )
143
+ }
144
+ }
145
+
146
+ grantVotingPower(address: address, voteId: string) {
147
+ if (this.#voters.length === 1 && this.canVote(msg.sender))
148
+ this.#grantVotingPower(address)
149
+ else {
150
+ this.createVote(
151
+ `grant voting power to ${address}`,
152
+ `Should we grant ${address} voting power?`,
153
+ new Date().getTime() + 172800000,
154
+ '#grantVotingPower',
155
+ [address]
156
+ )
157
+ }
158
+ }
159
+
160
+ revokeVotingPower(address: address, voteId: string) {
161
+ if (!this.canVote(msg.sender)) throw new Error('not a allowed to vote')
162
+ if (
163
+ this.#voters.length === 1 &&
164
+ address === msg.sender &&
165
+ !this.#votingDisabled
166
+ )
167
+ throw new Error(
168
+ 'only one voter left, disable voting before making this contract voteless'
169
+ )
170
+ if (this.#voters.length === 1) this.#revokeVotingPower(address)
171
+ else {
172
+ this.createVote(
173
+ `revoke voting power for ${address}`,
174
+ `Should we revoke ${address} it's voting power?`,
175
+ new Date().getTime() + 172800000,
176
+ '#revokeVotingPower',
177
+ [address]
178
+ )
179
+ }
180
+ }
181
+
182
+ sync() {
183
+ for (const vote of this.inProgress) {
184
+ if (vote.endTime < new Date().getTime()) this.#endVoting(vote.id)
185
+ }
186
+ }
187
+ }