@venusprotocol/governance-contracts 0.0.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/contracts/Governance/AccessControlManager.sol +97 -0
- package/contracts/Governance/AccessControlledV5.sol +56 -0
- package/contracts/Governance/AccessControlledV8.sol +83 -0
- package/contracts/Governance/GovernorBravoDelegate.sol +516 -0
- package/contracts/Governance/GovernorBravoDelegator.sol +92 -0
- package/contracts/Governance/GovernorBravoInterfaces.sol +234 -0
- package/contracts/Governance/IAccessControlManagerV5.sol +120 -0
- package/contracts/Governance/IAccessControlManagerV8.sol +22 -0
- package/contracts/Governance/Timelock.sol +152 -0
- package/contracts/Utils/SafeMath.sol +163 -0
- package/contracts/test/MockAccessTest.sol +13 -0
- package/contracts/test/MockImports.sol +8 -0
- package/package.json +100 -0
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
pragma solidity ^0.5.16;
|
|
2
|
+
pragma experimental ABIEncoderV2;
|
|
3
|
+
|
|
4
|
+
import "./GovernorBravoInterfaces.sol";
|
|
5
|
+
|
|
6
|
+
contract GovernorBravoDelegate is GovernorBravoDelegateStorageV2, GovernorBravoEvents {
|
|
7
|
+
/// @notice The name of this contract
|
|
8
|
+
string public constant name = "Venus Governor Bravo";
|
|
9
|
+
|
|
10
|
+
/// @notice The minimum setable proposal threshold
|
|
11
|
+
uint public constant MIN_PROPOSAL_THRESHOLD = 150000e18; // 150,000 Xvs
|
|
12
|
+
|
|
13
|
+
/// @notice The maximum setable proposal threshold
|
|
14
|
+
uint public constant MAX_PROPOSAL_THRESHOLD = 300000e18; //300,000 Xvs
|
|
15
|
+
|
|
16
|
+
/// @notice The minimum setable voting period
|
|
17
|
+
uint public constant MIN_VOTING_PERIOD = 20 * 60 * 3; // About 3 hours, 3 secs per block
|
|
18
|
+
|
|
19
|
+
/// @notice The max setable voting period
|
|
20
|
+
uint public constant MAX_VOTING_PERIOD = 20 * 60 * 24 * 14; // About 2 weeks, 3 secs per block
|
|
21
|
+
|
|
22
|
+
/// @notice The min setable voting delay
|
|
23
|
+
uint public constant MIN_VOTING_DELAY = 1;
|
|
24
|
+
|
|
25
|
+
/// @notice The max setable voting delay
|
|
26
|
+
uint public constant MAX_VOTING_DELAY = 20 * 60 * 24 * 7; // About 1 week, 3 secs per block
|
|
27
|
+
|
|
28
|
+
/// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed
|
|
29
|
+
uint public constant quorumVotes = 600000e18; // 600,000 = 2% of Xvs
|
|
30
|
+
|
|
31
|
+
/// @notice The EIP-712 typehash for the contract's domain
|
|
32
|
+
bytes32 public constant DOMAIN_TYPEHASH =
|
|
33
|
+
keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
|
|
34
|
+
|
|
35
|
+
/// @notice The EIP-712 typehash for the ballot struct used by the contract
|
|
36
|
+
bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)");
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @notice Used to initialize the contract during delegator contructor
|
|
40
|
+
* @param xvsVault_ The address of the XvsVault
|
|
41
|
+
* @param proposalConfigs_ Governance configs for each governance route
|
|
42
|
+
* @param timelocks Timelock addresses for each governance route
|
|
43
|
+
*/
|
|
44
|
+
function initialize(
|
|
45
|
+
address xvsVault_,
|
|
46
|
+
ProposalConfig[] memory proposalConfigs_,
|
|
47
|
+
TimelockInterface[] memory timelocks,
|
|
48
|
+
address guardian_
|
|
49
|
+
) public {
|
|
50
|
+
require(address(proposalTimelocks[0]) == address(0), "GovernorBravo::initialize: cannot initialize twice");
|
|
51
|
+
require(msg.sender == admin, "GovernorBravo::initialize: admin only");
|
|
52
|
+
require(xvsVault_ != address(0), "GovernorBravo::initialize: invalid xvs address");
|
|
53
|
+
require(guardian_ != address(0), "GovernorBravo::initialize: invalid guardian");
|
|
54
|
+
require(
|
|
55
|
+
timelocks.length == uint8(ProposalType.CRITICAL) + 1,
|
|
56
|
+
"GovernorBravo::initialize:number of timelocks should match number of governance routes"
|
|
57
|
+
);
|
|
58
|
+
require(
|
|
59
|
+
proposalConfigs_.length == uint8(ProposalType.CRITICAL) + 1,
|
|
60
|
+
"GovernorBravo::initialize:number of proposal configs should match number of governance routes"
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
xvsVault = XvsVaultInterface(xvsVault_);
|
|
64
|
+
proposalMaxOperations = 10;
|
|
65
|
+
guardian = guardian_;
|
|
66
|
+
|
|
67
|
+
//Set parameters for each Governance Route
|
|
68
|
+
uint256 arrLength = proposalConfigs_.length;
|
|
69
|
+
for (uint256 i; i < arrLength; ++i) {
|
|
70
|
+
require(
|
|
71
|
+
proposalConfigs_[i].votingPeriod >= MIN_VOTING_PERIOD,
|
|
72
|
+
"GovernorBravo::initialize: invalid min voting period"
|
|
73
|
+
);
|
|
74
|
+
require(
|
|
75
|
+
proposalConfigs_[i].votingPeriod <= MAX_VOTING_PERIOD,
|
|
76
|
+
"GovernorBravo::initialize: invalid max voting period"
|
|
77
|
+
);
|
|
78
|
+
require(
|
|
79
|
+
proposalConfigs_[i].votingDelay >= MIN_VOTING_DELAY,
|
|
80
|
+
"GovernorBravo::initialize: invalid min voting delay"
|
|
81
|
+
);
|
|
82
|
+
require(
|
|
83
|
+
proposalConfigs_[i].votingDelay <= MAX_VOTING_DELAY,
|
|
84
|
+
"GovernorBravo::initialize: invalid max voting delay"
|
|
85
|
+
);
|
|
86
|
+
require(
|
|
87
|
+
proposalConfigs_[i].proposalThreshold >= MIN_PROPOSAL_THRESHOLD,
|
|
88
|
+
"GovernorBravo::initialize: invalid min proposal threshold"
|
|
89
|
+
);
|
|
90
|
+
require(
|
|
91
|
+
proposalConfigs_[i].proposalThreshold <= MAX_PROPOSAL_THRESHOLD,
|
|
92
|
+
"GovernorBravo::initialize: invalid max proposal threshold"
|
|
93
|
+
);
|
|
94
|
+
require(address(timelocks[i]) != address(0), "GovernorBravo::initialize:invalid timelock address");
|
|
95
|
+
|
|
96
|
+
proposalConfigs[i] = proposalConfigs_[i];
|
|
97
|
+
proposalTimelocks[i] = timelocks[i];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @notice Function used to propose a new proposal. Sender must have delegates above the proposal threshold.
|
|
103
|
+
* targets, values, signatures, and calldatas must be of equal length
|
|
104
|
+
* @dev NOTE: Proposals with duplicate set of actions can not be queued for execution. If the proposals consists
|
|
105
|
+
* of duplicate actions, it's recommended to split those actions into separate proposals
|
|
106
|
+
* @param targets Target addresses for proposal calls
|
|
107
|
+
* @param values BNB values for proposal calls
|
|
108
|
+
* @param signatures Function signatures for proposal calls
|
|
109
|
+
* @param calldatas Calldatas for proposal calls
|
|
110
|
+
* @param description String description of the proposal
|
|
111
|
+
* @param proposalType the type of the proposal (e.g NORMAL, FASTTRACK, CRITICAL)
|
|
112
|
+
* @return Proposal id of new proposal
|
|
113
|
+
*/
|
|
114
|
+
function propose(
|
|
115
|
+
address[] memory targets,
|
|
116
|
+
uint[] memory values,
|
|
117
|
+
string[] memory signatures,
|
|
118
|
+
bytes[] memory calldatas,
|
|
119
|
+
string memory description,
|
|
120
|
+
ProposalType proposalType
|
|
121
|
+
) public returns (uint) {
|
|
122
|
+
// Reject proposals before initiating as Governor
|
|
123
|
+
require(initialProposalId != 0, "GovernorBravo::propose: Governor Bravo not active");
|
|
124
|
+
require(
|
|
125
|
+
xvsVault.getPriorVotes(msg.sender, sub256(block.number, 1)) >=
|
|
126
|
+
proposalConfigs[uint8(proposalType)].proposalThreshold,
|
|
127
|
+
"GovernorBravo::propose: proposer votes below proposal threshold"
|
|
128
|
+
);
|
|
129
|
+
require(
|
|
130
|
+
targets.length == values.length &&
|
|
131
|
+
targets.length == signatures.length &&
|
|
132
|
+
targets.length == calldatas.length,
|
|
133
|
+
"GovernorBravo::propose: proposal function information arity mismatch"
|
|
134
|
+
);
|
|
135
|
+
require(targets.length != 0, "GovernorBravo::propose: must provide actions");
|
|
136
|
+
require(targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions");
|
|
137
|
+
|
|
138
|
+
uint latestProposalId = latestProposalIds[msg.sender];
|
|
139
|
+
if (latestProposalId != 0) {
|
|
140
|
+
ProposalState proposersLatestProposalState = state(latestProposalId);
|
|
141
|
+
require(
|
|
142
|
+
proposersLatestProposalState != ProposalState.Active,
|
|
143
|
+
"GovernorBravo::propose: one live proposal per proposer, found an already active proposal"
|
|
144
|
+
);
|
|
145
|
+
require(
|
|
146
|
+
proposersLatestProposalState != ProposalState.Pending,
|
|
147
|
+
"GovernorBravo::propose: one live proposal per proposer, found an already pending proposal"
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
uint startBlock = add256(block.number, proposalConfigs[uint8(proposalType)].votingDelay);
|
|
152
|
+
uint endBlock = add256(startBlock, proposalConfigs[uint8(proposalType)].votingPeriod);
|
|
153
|
+
|
|
154
|
+
proposalCount++;
|
|
155
|
+
Proposal memory newProposal = Proposal({
|
|
156
|
+
id: proposalCount,
|
|
157
|
+
proposer: msg.sender,
|
|
158
|
+
eta: 0,
|
|
159
|
+
targets: targets,
|
|
160
|
+
values: values,
|
|
161
|
+
signatures: signatures,
|
|
162
|
+
calldatas: calldatas,
|
|
163
|
+
startBlock: startBlock,
|
|
164
|
+
endBlock: endBlock,
|
|
165
|
+
forVotes: 0,
|
|
166
|
+
againstVotes: 0,
|
|
167
|
+
abstainVotes: 0,
|
|
168
|
+
canceled: false,
|
|
169
|
+
executed: false,
|
|
170
|
+
proposalType: uint8(proposalType)
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
proposals[newProposal.id] = newProposal;
|
|
174
|
+
latestProposalIds[newProposal.proposer] = newProposal.id;
|
|
175
|
+
|
|
176
|
+
emit ProposalCreated(
|
|
177
|
+
newProposal.id,
|
|
178
|
+
msg.sender,
|
|
179
|
+
targets,
|
|
180
|
+
values,
|
|
181
|
+
signatures,
|
|
182
|
+
calldatas,
|
|
183
|
+
startBlock,
|
|
184
|
+
endBlock,
|
|
185
|
+
description,
|
|
186
|
+
uint8(proposalType)
|
|
187
|
+
);
|
|
188
|
+
return newProposal.id;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* @notice Queues a proposal of state succeeded
|
|
193
|
+
* @param proposalId The id of the proposal to queue
|
|
194
|
+
*/
|
|
195
|
+
function queue(uint proposalId) external {
|
|
196
|
+
require(
|
|
197
|
+
state(proposalId) == ProposalState.Succeeded,
|
|
198
|
+
"GovernorBravo::queue: proposal can only be queued if it is succeeded"
|
|
199
|
+
);
|
|
200
|
+
Proposal storage proposal = proposals[proposalId];
|
|
201
|
+
uint eta = add256(block.timestamp, proposalTimelocks[uint8(proposal.proposalType)].delay());
|
|
202
|
+
for (uint i; i < proposal.targets.length; ++i) {
|
|
203
|
+
queueOrRevertInternal(
|
|
204
|
+
proposal.targets[i],
|
|
205
|
+
proposal.values[i],
|
|
206
|
+
proposal.signatures[i],
|
|
207
|
+
proposal.calldatas[i],
|
|
208
|
+
eta,
|
|
209
|
+
uint8(proposal.proposalType)
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
proposal.eta = eta;
|
|
213
|
+
emit ProposalQueued(proposalId, eta);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function queueOrRevertInternal(
|
|
217
|
+
address target,
|
|
218
|
+
uint value,
|
|
219
|
+
string memory signature,
|
|
220
|
+
bytes memory data,
|
|
221
|
+
uint eta,
|
|
222
|
+
uint8 proposalType
|
|
223
|
+
) internal {
|
|
224
|
+
require(
|
|
225
|
+
!proposalTimelocks[proposalType].queuedTransactions(
|
|
226
|
+
keccak256(abi.encode(target, value, signature, data, eta))
|
|
227
|
+
),
|
|
228
|
+
"GovernorBravo::queueOrRevertInternal: identical proposal action already queued at eta"
|
|
229
|
+
);
|
|
230
|
+
proposalTimelocks[proposalType].queueTransaction(target, value, signature, data, eta);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* @notice Executes a queued proposal if eta has passed
|
|
235
|
+
* @param proposalId The id of the proposal to execute
|
|
236
|
+
*/
|
|
237
|
+
function execute(uint proposalId) external {
|
|
238
|
+
require(
|
|
239
|
+
state(proposalId) == ProposalState.Queued,
|
|
240
|
+
"GovernorBravo::execute: proposal can only be executed if it is queued"
|
|
241
|
+
);
|
|
242
|
+
Proposal storage proposal = proposals[proposalId];
|
|
243
|
+
proposal.executed = true;
|
|
244
|
+
for (uint i; i < proposal.targets.length; ++i) {
|
|
245
|
+
proposalTimelocks[uint8(proposal.proposalType)].executeTransaction(
|
|
246
|
+
proposal.targets[i],
|
|
247
|
+
proposal.values[i],
|
|
248
|
+
proposal.signatures[i],
|
|
249
|
+
proposal.calldatas[i],
|
|
250
|
+
proposal.eta
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
emit ProposalExecuted(proposalId);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* @notice Cancels a proposal only if sender is the proposer, or proposer delegates dropped below proposal threshold
|
|
258
|
+
* @param proposalId The id of the proposal to cancel
|
|
259
|
+
*/
|
|
260
|
+
function cancel(uint proposalId) external {
|
|
261
|
+
require(state(proposalId) != ProposalState.Executed, "GovernorBravo::cancel: cannot cancel executed proposal");
|
|
262
|
+
|
|
263
|
+
Proposal storage proposal = proposals[proposalId];
|
|
264
|
+
require(
|
|
265
|
+
msg.sender == guardian ||
|
|
266
|
+
msg.sender == proposal.proposer ||
|
|
267
|
+
xvsVault.getPriorVotes(proposal.proposer, sub256(block.number, 1)) <
|
|
268
|
+
proposalConfigs[proposal.proposalType].proposalThreshold,
|
|
269
|
+
"GovernorBravo::cancel: proposer above threshold"
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
proposal.canceled = true;
|
|
273
|
+
for (uint i = 0; i < proposal.targets.length; i++) {
|
|
274
|
+
proposalTimelocks[proposal.proposalType].cancelTransaction(
|
|
275
|
+
proposal.targets[i],
|
|
276
|
+
proposal.values[i],
|
|
277
|
+
proposal.signatures[i],
|
|
278
|
+
proposal.calldatas[i],
|
|
279
|
+
proposal.eta
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
emit ProposalCanceled(proposalId);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* @notice Gets actions of a proposal
|
|
288
|
+
* @param proposalId the id of the proposal
|
|
289
|
+
* @return targets, values, signatures, and calldatas of the proposal actions
|
|
290
|
+
*/
|
|
291
|
+
function getActions(
|
|
292
|
+
uint proposalId
|
|
293
|
+
)
|
|
294
|
+
external
|
|
295
|
+
view
|
|
296
|
+
returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas)
|
|
297
|
+
{
|
|
298
|
+
Proposal storage p = proposals[proposalId];
|
|
299
|
+
return (p.targets, p.values, p.signatures, p.calldatas);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* @notice Gets the receipt for a voter on a given proposal
|
|
304
|
+
* @param proposalId the id of proposal
|
|
305
|
+
* @param voter The address of the voter
|
|
306
|
+
* @return The voting receipt
|
|
307
|
+
*/
|
|
308
|
+
function getReceipt(uint proposalId, address voter) external view returns (Receipt memory) {
|
|
309
|
+
return proposals[proposalId].receipts[voter];
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* @notice Gets the state of a proposal
|
|
314
|
+
* @param proposalId The id of the proposal
|
|
315
|
+
* @return Proposal state
|
|
316
|
+
*/
|
|
317
|
+
function state(uint proposalId) public view returns (ProposalState) {
|
|
318
|
+
require(
|
|
319
|
+
proposalCount >= proposalId && proposalId > initialProposalId,
|
|
320
|
+
"GovernorBravo::state: invalid proposal id"
|
|
321
|
+
);
|
|
322
|
+
Proposal storage proposal = proposals[proposalId];
|
|
323
|
+
if (proposal.canceled) {
|
|
324
|
+
return ProposalState.Canceled;
|
|
325
|
+
} else if (block.number <= proposal.startBlock) {
|
|
326
|
+
return ProposalState.Pending;
|
|
327
|
+
} else if (block.number <= proposal.endBlock) {
|
|
328
|
+
return ProposalState.Active;
|
|
329
|
+
} else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes) {
|
|
330
|
+
return ProposalState.Defeated;
|
|
331
|
+
} else if (proposal.eta == 0) {
|
|
332
|
+
return ProposalState.Succeeded;
|
|
333
|
+
} else if (proposal.executed) {
|
|
334
|
+
return ProposalState.Executed;
|
|
335
|
+
} else if (
|
|
336
|
+
block.timestamp >= add256(proposal.eta, proposalTimelocks[uint8(proposal.proposalType)].GRACE_PERIOD())
|
|
337
|
+
) {
|
|
338
|
+
return ProposalState.Expired;
|
|
339
|
+
} else {
|
|
340
|
+
return ProposalState.Queued;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* @notice Cast a vote for a proposal
|
|
346
|
+
* @param proposalId The id of the proposal to vote on
|
|
347
|
+
* @param support The support value for the vote. 0=against, 1=for, 2=abstain
|
|
348
|
+
*/
|
|
349
|
+
function castVote(uint proposalId, uint8 support) external {
|
|
350
|
+
emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), "");
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* @notice Cast a vote for a proposal with a reason
|
|
355
|
+
* @param proposalId The id of the proposal to vote on
|
|
356
|
+
* @param support The support value for the vote. 0=against, 1=for, 2=abstain
|
|
357
|
+
* @param reason The reason given for the vote by the voter
|
|
358
|
+
*/
|
|
359
|
+
function castVoteWithReason(uint proposalId, uint8 support, string calldata reason) external {
|
|
360
|
+
emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), reason);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* @notice Cast a vote for a proposal by signature
|
|
365
|
+
* @dev External function that accepts EIP-712 signatures for voting on proposals.
|
|
366
|
+
* @param proposalId The id of the proposal to vote on
|
|
367
|
+
* @param support The support value for the vote. 0=against, 1=for, 2=abstain
|
|
368
|
+
* @param v recovery id of ECDSA signature
|
|
369
|
+
* @param r part of the ECDSA sig output
|
|
370
|
+
* @param s part of the ECDSA sig output
|
|
371
|
+
*/
|
|
372
|
+
function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external {
|
|
373
|
+
bytes32 domainSeparator = keccak256(
|
|
374
|
+
abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))
|
|
375
|
+
);
|
|
376
|
+
bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
|
|
377
|
+
bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
|
|
378
|
+
address signatory = ecrecover(digest, v, r, s);
|
|
379
|
+
require(signatory != address(0), "GovernorBravo::castVoteBySig: invalid signature");
|
|
380
|
+
emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), "");
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* @notice Internal function that caries out voting logic
|
|
385
|
+
* @param voter The voter that is casting their vote
|
|
386
|
+
* @param proposalId The id of the proposal to vote on
|
|
387
|
+
* @param support The support value for the vote. 0=against, 1=for, 2=abstain
|
|
388
|
+
* @return The number of votes cast
|
|
389
|
+
*/
|
|
390
|
+
function castVoteInternal(address voter, uint proposalId, uint8 support) internal returns (uint96) {
|
|
391
|
+
require(state(proposalId) == ProposalState.Active, "GovernorBravo::castVoteInternal: voting is closed");
|
|
392
|
+
require(support <= 2, "GovernorBravo::castVoteInternal: invalid vote type");
|
|
393
|
+
Proposal storage proposal = proposals[proposalId];
|
|
394
|
+
Receipt storage receipt = proposal.receipts[voter];
|
|
395
|
+
require(receipt.hasVoted == false, "GovernorBravo::castVoteInternal: voter already voted");
|
|
396
|
+
uint96 votes = xvsVault.getPriorVotes(voter, proposal.startBlock);
|
|
397
|
+
|
|
398
|
+
if (support == 0) {
|
|
399
|
+
proposal.againstVotes = add256(proposal.againstVotes, votes);
|
|
400
|
+
} else if (support == 1) {
|
|
401
|
+
proposal.forVotes = add256(proposal.forVotes, votes);
|
|
402
|
+
} else if (support == 2) {
|
|
403
|
+
proposal.abstainVotes = add256(proposal.abstainVotes, votes);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
receipt.hasVoted = true;
|
|
407
|
+
receipt.support = support;
|
|
408
|
+
receipt.votes = votes;
|
|
409
|
+
|
|
410
|
+
return votes;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* @notice Sets the new governance guardian
|
|
415
|
+
* @param newGuardian the address of the new guardian
|
|
416
|
+
*/
|
|
417
|
+
function _setGuardian(address newGuardian) external {
|
|
418
|
+
require(msg.sender == guardian || msg.sender == admin, "GovernorBravo::_setGuardian: admin or guardian only");
|
|
419
|
+
require(newGuardian != address(0), "GovernorBravo::_setGuardian: cannot live without a guardian");
|
|
420
|
+
address oldGuardian = guardian;
|
|
421
|
+
guardian = newGuardian;
|
|
422
|
+
|
|
423
|
+
emit NewGuardian(oldGuardian, newGuardian);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* @notice Initiate the GovernorBravo contract
|
|
428
|
+
* @dev Admin only. Sets initial proposal id which initiates the contract, ensuring a continuous proposal id count
|
|
429
|
+
* @param governorAlpha The address for the Governor to continue the proposal id count from
|
|
430
|
+
*/
|
|
431
|
+
function _initiate(address governorAlpha) external {
|
|
432
|
+
require(msg.sender == admin, "GovernorBravo::_initiate: admin only");
|
|
433
|
+
require(initialProposalId == 0, "GovernorBravo::_initiate: can only initiate once");
|
|
434
|
+
proposalCount = GovernorAlphaInterface(governorAlpha).proposalCount();
|
|
435
|
+
initialProposalId = proposalCount;
|
|
436
|
+
for (uint256 i; i < uint8(ProposalType.CRITICAL) + 1; ++i) {
|
|
437
|
+
proposalTimelocks[i].acceptAdmin();
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* @notice Set max proposal operations
|
|
443
|
+
* @dev Admin only.
|
|
444
|
+
* @param proposalMaxOperations_ Max proposal operations
|
|
445
|
+
*/
|
|
446
|
+
function _setProposalMaxOperations(uint proposalMaxOperations_) external {
|
|
447
|
+
require(msg.sender == admin, "GovernorBravo::_setProposalMaxOperations: admin only");
|
|
448
|
+
uint oldProposalMaxOperations = proposalMaxOperations;
|
|
449
|
+
proposalMaxOperations = proposalMaxOperations_;
|
|
450
|
+
|
|
451
|
+
emit ProposalMaxOperationsUpdated(oldProposalMaxOperations, proposalMaxOperations_);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
|
|
456
|
+
* @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
|
|
457
|
+
* @param newPendingAdmin New pending admin.
|
|
458
|
+
*/
|
|
459
|
+
function _setPendingAdmin(address newPendingAdmin) external {
|
|
460
|
+
// Check caller = admin
|
|
461
|
+
require(msg.sender == admin, "GovernorBravo:_setPendingAdmin: admin only");
|
|
462
|
+
|
|
463
|
+
// Save current value, if any, for inclusion in log
|
|
464
|
+
address oldPendingAdmin = pendingAdmin;
|
|
465
|
+
|
|
466
|
+
// Store pendingAdmin with value newPendingAdmin
|
|
467
|
+
pendingAdmin = newPendingAdmin;
|
|
468
|
+
|
|
469
|
+
// Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin)
|
|
470
|
+
emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin
|
|
475
|
+
* @dev Admin function for pending admin to accept role and update admin
|
|
476
|
+
*/
|
|
477
|
+
function _acceptAdmin() external {
|
|
478
|
+
// Check caller is pendingAdmin and pendingAdmin ≠ address(0)
|
|
479
|
+
require(
|
|
480
|
+
msg.sender == pendingAdmin && msg.sender != address(0),
|
|
481
|
+
"GovernorBravo:_acceptAdmin: pending admin only"
|
|
482
|
+
);
|
|
483
|
+
|
|
484
|
+
// Save current values for inclusion in log
|
|
485
|
+
address oldAdmin = admin;
|
|
486
|
+
address oldPendingAdmin = pendingAdmin;
|
|
487
|
+
|
|
488
|
+
// Store admin with value pendingAdmin
|
|
489
|
+
admin = pendingAdmin;
|
|
490
|
+
|
|
491
|
+
// Clear the pending value
|
|
492
|
+
pendingAdmin = address(0);
|
|
493
|
+
|
|
494
|
+
emit NewAdmin(oldAdmin, admin);
|
|
495
|
+
emit NewPendingAdmin(oldPendingAdmin, pendingAdmin);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function add256(uint256 a, uint256 b) internal pure returns (uint) {
|
|
499
|
+
uint c = a + b;
|
|
500
|
+
require(c >= a, "addition overflow");
|
|
501
|
+
return c;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function sub256(uint256 a, uint256 b) internal pure returns (uint) {
|
|
505
|
+
require(b <= a, "subtraction underflow");
|
|
506
|
+
return a - b;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function getChainIdInternal() internal pure returns (uint) {
|
|
510
|
+
uint chainId;
|
|
511
|
+
assembly {
|
|
512
|
+
chainId := chainid()
|
|
513
|
+
}
|
|
514
|
+
return chainId;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
pragma solidity ^0.5.16;
|
|
2
|
+
pragma experimental ABIEncoderV2;
|
|
3
|
+
|
|
4
|
+
import "./GovernorBravoInterfaces.sol";
|
|
5
|
+
|
|
6
|
+
contract GovernorBravoDelegator is GovernorBravoDelegatorStorage, GovernorBravoEvents {
|
|
7
|
+
constructor(
|
|
8
|
+
address timelock_,
|
|
9
|
+
address xvsVault_,
|
|
10
|
+
address admin_,
|
|
11
|
+
address implementation_,
|
|
12
|
+
uint votingPeriod_,
|
|
13
|
+
uint votingDelay_,
|
|
14
|
+
uint proposalThreshold_,
|
|
15
|
+
address guardian_
|
|
16
|
+
) public {
|
|
17
|
+
// Admin set to msg.sender for initialization
|
|
18
|
+
admin = msg.sender;
|
|
19
|
+
|
|
20
|
+
delegateTo(
|
|
21
|
+
implementation_,
|
|
22
|
+
abi.encodeWithSignature(
|
|
23
|
+
"initialize(address,address,uint256,uint256,uint256,address)",
|
|
24
|
+
timelock_,
|
|
25
|
+
xvsVault_,
|
|
26
|
+
votingPeriod_,
|
|
27
|
+
votingDelay_,
|
|
28
|
+
proposalThreshold_,
|
|
29
|
+
guardian_
|
|
30
|
+
)
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
_setImplementation(implementation_);
|
|
34
|
+
|
|
35
|
+
admin = admin_;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @notice Called by the admin to update the implementation of the delegator
|
|
40
|
+
* @param implementation_ The address of the new implementation for delegation
|
|
41
|
+
*/
|
|
42
|
+
function _setImplementation(address implementation_) public {
|
|
43
|
+
require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only");
|
|
44
|
+
require(
|
|
45
|
+
implementation_ != address(0),
|
|
46
|
+
"GovernorBravoDelegator::_setImplementation: invalid implementation address"
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
address oldImplementation = implementation;
|
|
50
|
+
implementation = implementation_;
|
|
51
|
+
|
|
52
|
+
emit NewImplementation(oldImplementation, implementation);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @notice Internal method to delegate execution to another contract
|
|
57
|
+
* @dev It returns to the external caller whatever the implementation returns or forwards reverts
|
|
58
|
+
* @param callee The contract to delegatecall
|
|
59
|
+
* @param data The raw data to delegatecall
|
|
60
|
+
*/
|
|
61
|
+
function delegateTo(address callee, bytes memory data) internal {
|
|
62
|
+
(bool success, bytes memory returnData) = callee.delegatecall(data);
|
|
63
|
+
assembly {
|
|
64
|
+
if eq(success, 0) {
|
|
65
|
+
revert(add(returnData, 0x20), returndatasize)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @dev Delegates execution to an implementation contract.
|
|
72
|
+
* It returns to the external caller whatever the implementation returns
|
|
73
|
+
* or forwards reverts.
|
|
74
|
+
*/
|
|
75
|
+
function() external payable {
|
|
76
|
+
// delegate all other functions to current implementation
|
|
77
|
+
(bool success, ) = implementation.delegatecall(msg.data);
|
|
78
|
+
|
|
79
|
+
assembly {
|
|
80
|
+
let free_mem_ptr := mload(0x40)
|
|
81
|
+
returndatacopy(free_mem_ptr, 0, returndatasize)
|
|
82
|
+
|
|
83
|
+
switch success
|
|
84
|
+
case 0 {
|
|
85
|
+
revert(free_mem_ptr, returndatasize)
|
|
86
|
+
}
|
|
87
|
+
default {
|
|
88
|
+
return(free_mem_ptr, returndatasize)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|