/
LibGovernance.sol
182 lines (157 loc) · 6.2 KB
/
LibGovernance.sol
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
// SPDX-License-Identifier: MIT
pragma solidity 0.8.3;
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
library LibGovernance {
using EnumerableSet for EnumerableSet.AddressSet;
bytes32 constant STORAGE_POSITION = keccak256("governance.storage");
struct Storage {
bool initialized;
// Set of active validators
EnumerableSet.AddressSet membersSet;
// A 1:1 map of active validators -> validator admin
mapping(address => address) membersAdmins;
// Precision for calculation of minimum amount of members signatures required
uint256 precision;
// Percentage for minimum amount of members signatures required
uint256 percentage;
// Admin of the contract
address admin;
// used to restrict certain functionality in case of an emergency stop
bool paused;
}
function governanceStorage() internal pure returns (Storage storage gs) {
bytes32 position = STORAGE_POSITION;
assembly {
gs.slot := position
}
}
/// @return Returns the admin
function admin() internal view returns (address) {
return governanceStorage().admin;
}
/// @return Returns true if the contract is paused, and false otherwise
function paused() internal view returns (bool) {
return governanceStorage().paused;
}
/// @return The current percentage for minimum amount of members signatures
function percentage() internal view returns (uint256) {
Storage storage gs = governanceStorage();
return gs.percentage;
}
/// @return The current precision for minimum amount of members signatures
function precision() internal view returns (uint256) {
Storage storage gs = governanceStorage();
return gs.precision;
}
function enforceNotPaused() internal view {
require(!governanceStorage().paused, "LibGovernance: paused");
}
function enforcePaused() internal view {
require(governanceStorage().paused, "LibGovernance: not paused");
}
function updateAdmin(address _newAdmin) internal {
Storage storage ds = governanceStorage();
ds.admin = _newAdmin;
}
function pause() internal {
enforceNotPaused();
Storage storage ds = governanceStorage();
ds.paused = true;
}
function unpause() internal {
enforcePaused();
Storage storage ds = governanceStorage();
ds.paused = false;
}
function updateMembersPercentage(uint256 _newPercentage) internal {
Storage storage gs = governanceStorage();
require(_newPercentage != 0, "LibGovernance: percentage must not be 0");
require(
_newPercentage < gs.precision,
"LibGovernance: percentage must be less than precision"
);
gs.percentage = _newPercentage;
}
/// @notice Adds/removes a validator from the member set
function updateMember(address _account, bool _status) internal {
Storage storage gs = governanceStorage();
if (_status) {
require(
gs.membersSet.add(_account),
"LibGovernance: Account already added"
);
} else if (!_status) {
require(
LibGovernance.membersCount() > 1,
"LibGovernance: contract would become memberless"
);
require(
gs.membersSet.remove(_account),
"LibGovernance: Account is not a member"
);
}
}
function updateMemberAdmin(address _account, address _admin) internal {
governanceStorage().membersAdmins[_account] = _admin;
}
/// @notice Returns true/false depending on whether a given address is member or not
function isMember(address _member) internal view returns (bool) {
Storage storage gs = governanceStorage();
return gs.membersSet.contains(_member);
}
/// @notice Returns the count of the members
function membersCount() internal view returns (uint256) {
Storage storage gs = governanceStorage();
return gs.membersSet.length();
}
/// @notice Returns the address of a member at a given index
function memberAt(uint256 _index) internal view returns (address) {
Storage storage gs = governanceStorage();
return gs.membersSet.at(_index);
}
/// @notice Returns the admin of the member
function memberAdmin(address _account) internal view returns (address) {
Storage storage gs = governanceStorage();
return gs.membersAdmins[_account];
}
/// @notice Checks if the provided amount of signatures is enough for submission
function hasValidSignaturesLength(uint256 _n) internal view returns (bool) {
Storage storage gs = governanceStorage();
uint256 members = gs.membersSet.length();
if (_n > members) {
return false;
}
uint256 mulMembersPercentage = members * gs.percentage;
uint256 requiredSignaturesLength = mulMembersPercentage / gs.precision;
if (mulMembersPercentage % gs.precision != 0) {
requiredSignaturesLength++;
}
return _n >= requiredSignaturesLength;
}
/// @notice Validates the provided signatures length
function validateSignaturesLength(uint256 _n) internal view {
require(
hasValidSignaturesLength(_n),
"LibGovernance: Invalid number of signatures"
);
}
/// @notice Validates the provided signatures against the member set
function validateSignatures(bytes32 _ethHash, bytes[] calldata _signatures)
internal
view
{
address[] memory signers = new address[](_signatures.length);
for (uint256 i = 0; i < _signatures.length; i++) {
address signer = ECDSA.recover(_ethHash, _signatures[i]);
require(isMember(signer), "LibGovernance: invalid signer");
for (uint256 j = 0; j < i; j++) {
require(
signer != signers[j],
"LibGovernance: duplicate signatures"
);
}
signers[i] = signer;
}
}
}