-
Notifications
You must be signed in to change notification settings - Fork 6
/
AlligatorOP_V5.sol
761 lines (681 loc) · 29.5 KB
/
AlligatorOP_V5.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
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
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.19;
import {AlligatorProxy} from "./AlligatorProxy.sol";
import {SubdelegationRules, AllowanceType} from "../structs/RulesV3.sol";
import {IAlligatorOPV5} from "../interfaces/IAlligatorOPV5.sol";
import {IRule} from "../interfaces/IRule.sol";
import {IOptimismGovernor} from "../interfaces/IOptimismGovernor.sol";
import {IVotes} from "@openzeppelin/contracts/governance/utils/IVotes.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import {ECDSAUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
/**
* Liquid delegator contract for OP Governor.
* Based on Alligator V2 (https://github.com/voteagora/liquid-delegator).
*
* Modifications from AlligatorOP:
* - uses hashed proxy rules to reduce calldata size
* - Assumes 1 proxy per owner
* - Use proxies without deploying them
* - Casts votes in batch directly to governor via `castVoteFromAlligator`
* - Add alt methods to limit the sender's voting power when casting votes
* - Upgradeable version of the contract
* - Add castVoteBySigBatched
*/
contract AlligatorOPV5 is IAlligatorOPV5, UUPSUpgradeable, OwnableUpgradeable, PausableUpgradeable {
// =============================================================
// ERRORS
// =============================================================
error LengthMismatch();
error InvalidSignature(ECDSAUpgradeable.RecoverError recoverError);
error ZeroVotesToCast();
error NotDelegated(address from, address to);
error TooManyRedelegations(address from, address to);
error NotValidYet(address from, address to, uint256 willBeValidFrom);
error NotValidAnymore(address from, address to, uint256 wasValidUntil);
error TooEarly(address from, address to, uint256 blocksBeforeVoteCloses);
error InvalidCustomRule(address from, address to, address customRule);
// =============================================================
// EVENTS
// =============================================================
event SubDelegation(address indexed from, address indexed to, SubdelegationRules subdelegationRules);
event SubDelegations(address indexed from, address[] to, SubdelegationRules subdelegationRules);
event SubDelegations(address indexed from, address[] to, SubdelegationRules[] subdelegationRules);
event VoteCast(
address indexed proxy, address indexed voter, address[] authority, uint256 proposalId, uint8 support
);
event VotesCast(
address[] proxies, address indexed voter, address[][] authorities, uint256 proposalId, uint8 support
);
// =============================================================
// LIBRARIES
// =============================================================
// =============================================================
// IMMUTABLE STORAGE
// =============================================================
address public constant GOVERNOR = 0xcDF27F107725988f2261Ce2256bDfCdE8B382B10;
address public constant OP_TOKEN = 0x4200000000000000000000000000000000000042;
bytes32 public constant DOMAIN_TYPEHASH =
keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support,address[] authority)");
bytes32 public constant BALLOT_WITHPARAMS_TYPEHASH =
keccak256("Ballot(uint256 proposalId,uint8 support,address[] authority,string reason,bytes params)");
bytes32 public constant BALLOT_WITHPARAMS_BATCHED_TYPEHASH = keccak256(
"Ballot(uint256 proposalId,uint8 support,uint256 maxVotingPower,address[][] authorities,string reason,bytes params)"
);
// =============================================================
// MUTABLE STORAGE
// =============================================================
// Subdelegation rules `from` => `to`
mapping(address from => mapping(address to => SubdelegationRules subdelegationRules)) public subdelegations;
// Records of votes cast across an authority chain, to prevent double voting from the same proxy
mapping(address proxy => mapping(uint256 proposalId => mapping(address voter => uint256))) public votesCast;
// =============================================================
// CONSTRUCTOR
// =============================================================
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize(address _initOwner) external initializer {
PausableUpgradeable.__Pausable_init();
OwnableUpgradeable.__Ownable_init();
UUPSUpgradeable.__UUPSUpgradeable_init();
_transferOwnership(_initOwner);
}
// =============================================================
// GOVERNOR OPERATIONS
// =============================================================
/**
* Validate subdelegation rules and cast a vote on the governor.
*
* @param authority The authority chain to validate against.
* @param proposalId The id of the proposal to vote on
* @param support The support value for the vote. 0=against, 1=for, 2=abstain
*/
function castVote(address[] calldata authority, uint256 proposalId, uint8 support) public override whenNotPaused {
_castVoteWithReasonAndParams(msg.sender, authority, proposalId, support, "", "");
}
/**
* Validate subdelegation rules and cast a vote with reason on the governor.
*
* @param authority The authority chain to validate against.
* @param proposalId The id of the proposal to vote on
* @param support The support value for the vote. 0=against, 1=for, 2=abstain
* @param reason The reason given for the vote by the voter
*/
function castVoteWithReason(address[] calldata authority, uint256 proposalId, uint8 support, string calldata reason)
public
override
whenNotPaused
{
_castVoteWithReasonAndParams(msg.sender, authority, proposalId, support, reason, "");
}
/**
* Validate subdelegation rules and cast a vote with reason on the governor.
*
* @param authority The authority chain to validate against.
* @param proposalId The id of the proposal to vote on
* @param support The support value for the vote. 0=against, 1=for, 2=abstain
* @param reason The reason given for the vote by the voter
* @param params The custom params of the vote
*/
function castVoteWithReasonAndParams(
address[] calldata authority,
uint256 proposalId,
uint8 support,
string memory reason,
bytes memory params
) public override whenNotPaused {
_castVoteWithReasonAndParams(msg.sender, authority, proposalId, support, reason, params);
}
/**
* Validate subdelegation rules and cast multiple votes with reason on the governor.
*
* @param authorities The authority chains to validate against.
* @param proposalId The id of the proposal to vote on
* @param support The support value for the vote. 0=against, 1=for, 2=abstain
* @param reason The reason given for the vote by the voter
* @param params The custom params of the vote
*
* @dev Authority chains with 0 votes to cast are skipped instead of triggering a revert.
*/
function castVoteWithReasonAndParamsBatched(
address[][] memory authorities,
uint256 proposalId,
uint8 support,
string memory reason,
bytes memory params
) public override whenNotPaused {
_limitedCastVoteWithReasonAndParamsBatched(
msg.sender, type(uint256).max, authorities, proposalId, support, reason, params
);
}
/**
* Validate subdelegation rules and cast multiple votes with reason on the governor.
* Limits the max number of votes used to `maxVotingPower`, blocking iterations once reached.
*
* @param maxVotingPower The maximum voting power allowed to be used for the batchVote
* @param authorities The authority chains to validate against.
* @param proposalId The id of the proposal to vote on
* @param support The support value for the vote. 0=against, 1=for, 2=abstain
* @param reason The reason given for the vote by the voter
* @param params The custom params of the vote
*
* @dev Authority chains with 0 votes to cast are skipped instead of triggering a revert.
*/
function limitedCastVoteWithReasonAndParamsBatched(
uint256 maxVotingPower,
address[][] memory authorities,
uint256 proposalId,
uint8 support,
string memory reason,
bytes memory params
) public override whenNotPaused {
_limitedCastVoteWithReasonAndParamsBatched(
msg.sender, maxVotingPower, authorities, proposalId, support, reason, params
);
}
/**
* Validate subdelegation rules and cast a vote by signature on the governor.
*
* @param authority The authority chain to validate against.
* @param proposalId The id of the proposal to vote on
* @param support The support value for the vote. 0=against, 1=for, 2=abstain
*/
function castVoteBySig(
address[] calldata authority,
uint256 proposalId,
uint8 support,
uint8 v,
bytes32 r,
bytes32 s
) public override whenNotPaused {
address signatory =
_getSignatory(keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support, authority)), v, r, s);
_castVoteWithReasonAndParams(signatory, authority, proposalId, support, "", "");
}
/**
* Validate subdelegation rules and cast a vote with reason and params by signature on the governor.
*
* @param authority The authority chain to validate against.
* @param proposalId The id of the proposal to vote on
* @param support The support value for the vote. 0=against, 1=for, 2=abstain
* @param reason The reason given for the vote by the signatory
* @param params The custom params of the vote
*/
function castVoteWithReasonAndParamsBySig(
address[] calldata authority,
uint256 proposalId,
uint8 support,
string memory reason,
bytes memory params,
uint8 v,
bytes32 r,
bytes32 s
) public override whenNotPaused {
address signatory = _getSignatory(
keccak256(
abi.encode(
BALLOT_WITHPARAMS_TYPEHASH,
proposalId,
support,
authority,
keccak256(bytes(reason)),
keccak256(params)
)
),
v,
r,
s
);
_castVoteWithReasonAndParams(signatory, authority, proposalId, support, reason, params);
}
/**
* Validate subdelegation rules and cast a vote with reason and params by signature on the governor.
*
* @param maxVotingPower The maximum voting power allowed to be used for the batchVote
* @param authorities The authority chains to validate against.
* @param proposalId The id of the proposal to vote on
* @param support The support value for the vote. 0=against, 1=for, 2=abstain
* @param reason The reason given for the vote by the signatory
* @param params The custom params of the vote
*/
function limitedCastVoteWithReasonAndParamsBatchedBySig(
uint256 maxVotingPower,
address[][] memory authorities,
uint256 proposalId,
uint8 support,
string memory reason,
bytes memory params,
uint8 v,
bytes32 r,
bytes32 s
) public override whenNotPaused {
address signatory = _getSignatory(
keccak256(
abi.encode(
BALLOT_WITHPARAMS_BATCHED_TYPEHASH,
proposalId,
support,
maxVotingPower,
authorities,
keccak256(bytes(reason)),
keccak256(params)
)
),
v,
r,
s
);
_limitedCastVoteWithReasonAndParamsBatched(
signatory, maxVotingPower, authorities, proposalId, support, reason, params
);
}
/**
* Validate subdelegation rules and cast a vote with reason on the governor.
*
* @param voter The address of the voter
* @param authority The authority chain to validate against.
* @param proposalId The id of the proposal to vote on
* @param support The support value for the vote. 0=against, 1=for, 2=abstain
* @param reason The reason given for the vote by the voter
* @param params The custom params of the vote
*
* @dev Reverts if there are no votes to cast.
*/
function _castVoteWithReasonAndParams(
address voter,
address[] calldata authority,
uint256 proposalId,
uint8 support,
string memory reason,
bytes memory params
) internal {
address proxy = proxyAddress(authority[0]);
uint256 proxyTotalVotes = IVotes(OP_TOKEN).getPastVotes(proxy, _proposalSnapshot(proposalId));
(uint256 votesToCast, uint256 k) = validate(proxy, voter, authority, proposalId, support, proxyTotalVotes);
if (votesToCast == 0) revert ZeroVotesToCast();
_recordVotesToCast(k, proxy, proposalId, authority, votesToCast, proxyTotalVotes);
_castVote(voter, proposalId, support, reason, votesToCast, params);
emit VoteCast(proxy, voter, authority, proposalId, support);
}
/**
* Validate subdelegation rules and cast multiple votes with reason on the governor.
* Limits the max number of votes used to `maxVotingPower`, blocking iterations once reached.
*
* @param voter The address of the voter
* @param maxVotingPower The maximum voting power allowed to be used for the batchVote
* @param authorities The authority chains to validate against.
* @param proposalId The id of the proposal to vote on
* @param support The support value for the vote. 0=against, 1=for, 2=abstain
* @param reason The reason given for the vote by the voter
* @param params The custom params of the vote
*
* @dev Authority chains with 0 votes to cast are skipped instead of triggering a revert.
*/
function _limitedCastVoteWithReasonAndParamsBatched(
address voter,
uint256 maxVotingPower,
address[][] memory authorities,
uint256 proposalId,
uint8 support,
string memory reason,
bytes memory params
) internal {
uint256 snapshotBlock = _proposalSnapshot(proposalId);
address[] memory proxies = new address[](authorities.length);
uint256 votesToCast;
uint256 totalVotesToCast;
uint256 proxyTotalVotes;
uint256 k;
for (uint256 i; i < authorities.length;) {
proxies[i] = proxyAddress(authorities[i][0]);
proxyTotalVotes = IVotes(OP_TOKEN).getPastVotes(proxies[i], snapshotBlock);
(votesToCast, k) = validate(proxies[i], voter, authorities[i], proposalId, support, proxyTotalVotes);
if (votesToCast != 0) {
// Increase `totalVotesToCast` and check if it exceeds `maxVotingPower`
if ((totalVotesToCast += votesToCast) < maxVotingPower) {
_recordVotesToCast(k, proxies[i], proposalId, authorities[i], votesToCast, proxyTotalVotes);
} else {
// If `totalVotesToCast` exceeds `maxVotingPower`, calculate the remaining votes to cast
votesToCast = maxVotingPower - (totalVotesToCast - votesToCast);
_recordVotesToCast(k, proxies[i], proposalId, authorities[i], votesToCast, proxyTotalVotes);
totalVotesToCast = maxVotingPower;
break;
}
}
unchecked {
++i;
}
}
if (totalVotesToCast == 0) revert ZeroVotesToCast();
_castVote(voter, proposalId, support, reason, totalVotesToCast, params);
emit VotesCast(proxies, voter, authorities, proposalId, support);
}
function _recordVotesToCast(
uint256 k,
address proxy,
uint256 proposalId,
address[] memory authority,
uint256 votesToCast,
uint256 proxyTotalVotes
) internal {
// Record weight cast for a proxy, on the governor
IOptimismGovernor(GOVERNOR).increaseWeightCast(proposalId, proxy, votesToCast, proxyTotalVotes);
if (k != 0) {
// Record `votesToCast` across the authority chain, only for voters whose allowance does not exceed proxy
// remaining votes. This is because it would be unnecessary to do so as if they voted they would exhaust the
// proxy votes regardless of votes cast by their delegates.
uint256 authorityLength = authority.length;
for (k; k < authorityLength;) {
/// @dev cumulative votesCast cannot exceed proxy voting power, thus cannot overflow
unchecked {
votesCast[proxy][proposalId][authority[k]] += votesToCast;
++k;
}
}
}
}
function _getSignatory(bytes32 structHash, uint8 v, bytes32 r, bytes32 s)
internal
view
returns (address signatory)
{
ECDSAUpgradeable.RecoverError recoverError;
(signatory, recoverError) = ECDSAUpgradeable.tryRecover(
keccak256(
abi.encodePacked(
"\x19\x01",
keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256("Alligator"), block.chainid, address(this))),
structHash
)
),
v,
r,
s
);
if (signatory == address(0)) {
revert InvalidSignature(recoverError);
}
}
// =============================================================
// SUBDELEGATIONS
// =============================================================
/**
* Subdelegate `to` with `subdelegationRules`.
* Creates a proxy for `msg.sender` if it does not exist.
*
* @param to The address to subdelegate to.
* @param subdelegationRules The rules to apply to the subdelegation.
*/
function subdelegate(address to, SubdelegationRules calldata subdelegationRules) public override whenNotPaused {
subdelegations[msg.sender][to] = subdelegationRules;
emit SubDelegation(msg.sender, to, subdelegationRules);
}
/**
* Subdelegate `targets` with `subdelegationRules`.
*
* @param targets The addresses to subdelegate to.
* @param subdelegationRules The rules to apply to the subdelegations.
*/
function subdelegateBatched(address[] calldata targets, SubdelegationRules calldata subdelegationRules)
public
override
whenNotPaused
{
uint256 targetsLength = targets.length;
for (uint256 i; i < targetsLength;) {
subdelegations[msg.sender][targets[i]] = subdelegationRules;
unchecked {
++i;
}
}
emit SubDelegations(msg.sender, targets, subdelegationRules);
}
/**
* Subdelegate `targets` with different `subdelegationRules` for each target.
*
* @param targets The addresses to subdelegate to.
* @param subdelegationRules The rules to apply to the subdelegations.
*/
function subdelegateBatched(address[] calldata targets, SubdelegationRules[] calldata subdelegationRules)
public
override
whenNotPaused
{
uint256 targetsLength = targets.length;
if (targetsLength != subdelegationRules.length) revert LengthMismatch();
for (uint256 i; i < targetsLength;) {
subdelegations[msg.sender][targets[i]] = subdelegationRules[i];
unchecked {
++i;
}
}
emit SubDelegations(msg.sender, targets, subdelegationRules);
}
// =============================================================
// VIEW FUNCTIONS
// =============================================================
/**
* Validate subdelegation rules and partial delegation allowances.
*
* @param proxy The address of the proxy.
* @param sender The sender address to validate.
* @param authority The authority chain to validate against.
* @param proposalId The id of the proposal for which validation is being performed.
* @param support The support value for the vote. 0=against, 1=for, 2=abstain, 0xFF=proposal
* @param voterAllowance The allowance of the voter.
*
* @return votesToCast The number of votes to cast by `sender`.
*/
function validate(
address proxy,
address sender,
address[] memory authority,
uint256 proposalId,
uint256 support,
uint256 voterAllowance
) internal view returns (uint256 votesToCast, uint256 k) {
address from = authority[0];
/// @dev Cannot underflow as `weightCast` is always less than or equal to total votes.
unchecked {
uint256 weightCast = _weightCast(proposalId, proxy);
votesToCast = weightCast == 0 ? voterAllowance : voterAllowance - weightCast;
}
// If `sender` is the proxy owner, only the proxy rules are validated.
if (from == sender) {
return (votesToCast, k);
}
uint256 delegatorsVotes;
uint256 toVotesCast;
address to;
SubdelegationRules memory subdelegationRules;
for (uint256 i = 1; i < authority.length;) {
to = authority[i];
subdelegationRules = subdelegations[from][to];
if (subdelegationRules.allowance == 0) {
revert NotDelegated(from, to);
}
// Calculate `voterAllowance` based on allowance given by `from`
voterAllowance =
_getVoterAllowance(subdelegationRules.allowanceType, subdelegationRules.allowance, voterAllowance);
// Record the highest `delegatorsVotes` in the authority chain
toVotesCast = votesCast[proxy][proposalId][to];
if (toVotesCast > delegatorsVotes) {
delegatorsVotes = toVotesCast;
}
// If subdelegation allowance is lower than proxy remaining votes, record the point in the authority chain
// after which we need to keep track of votes cast.
if (k == 0) {
if (
subdelegationRules.allowance
< (subdelegationRules.allowanceType == AllowanceType.Relative ? 1e5 : votesToCast)
) {
k = i;
}
}
unchecked {
_validateRules(
subdelegationRules,
sender,
authority.length,
proposalId,
support,
from,
to,
++i // pass `i + 1` and increment at the same time
);
}
from = to;
}
if (from != sender) revert NotDelegated(from, sender);
// Prevent double spending of votes already cast by previous delegators.
// Reverts for underflow when `delegatorsVotes > voterAllowance`, meaning that `sender` has no votes left.
if (delegatorsVotes != 0) {
voterAllowance -= delegatorsVotes;
}
votesToCast = voterAllowance > votesToCast ? votesToCast : voterAllowance;
}
/**
* Returns the address of the proxy contract for a given owner.
*
* @param proxyOwner The owner of the proxy.
* @return endpoint The address of the proxy.
*/
function proxyAddress(address proxyOwner) public view override returns (address endpoint) {
endpoint = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
bytes32(uint256(uint160(proxyOwner))), // salt
keccak256(abi.encodePacked(type(AlligatorProxy).creationCode, abi.encode(GOVERNOR)))
)
)
)
)
);
}
// =============================================================
// CUSTOM GOVERNOR FUNCTIONS
// =============================================================
/**
* Cast a vote on the governor with reason and params.
*
* @param voter The address of the voter
* @param proposalId The id of the proposal to vote on
* @param support The support value for the vote. 0=against, 1=for, 2=abstain
* @param reason The reason given for the vote by the voter
* @param params The params to be passed to the governor
*/
function _castVote(
address voter,
uint256 proposalId,
uint8 support,
string memory reason,
uint256 votes,
bytes memory params
) internal {
IOptimismGovernor(GOVERNOR).castVoteFromAlligator(proposalId, voter, support, reason, votes, params);
}
/**
* Retrieve number of the proposal's end block.
*
* @param proposalId The id of the proposal to vote on
* @return endBlock Proposal's end block number
*/
function _proposalEndBlock(uint256 proposalId) internal view returns (uint256) {
return IOptimismGovernor(GOVERNOR).proposalDeadline(proposalId);
}
/**
* Retrieve number of the proposal's snapshot.
*
* @param proposalId The id of the proposal to vote on
* @return snapshotBlock Proposal's snapshot block number
*/
function _proposalSnapshot(uint256 proposalId) internal view returns (uint256) {
return IOptimismGovernor(GOVERNOR).proposalSnapshot(proposalId);
}
/**
* Retrieve number of the proposal's snapshot.
*
* @param proposalId The id of the proposal to vote on
* @param proxy The address of the proxy
* @return weightCast Weight cast by the proxy
*/
function _weightCast(uint256 proposalId, address proxy) internal view returns (uint256) {
return IOptimismGovernor(GOVERNOR).weightCast(proposalId, proxy);
}
// =============================================================
// RESTRICTED, INTERNAL
// =============================================================
function _authorizeUpgrade(address) internal override onlyOwner {}
/**
* Pauses and unpauses propose, vote and sign operations.
*
* @dev Only contract owner can toggle pause.
*/
function _togglePause() external onlyOwner {
if (!paused()) {
_pause();
} else {
_unpause();
}
}
function _validateRules(
SubdelegationRules memory rules,
address sender,
uint256 authorityLength,
uint256 proposalId,
uint256 support,
address from,
address to,
uint256 redelegationIndex
) internal view {
/// @dev `maxRedelegation` cannot overflow as it increases by 1 each iteration
/// @dev block.number + rules.blocksBeforeVoteCloses cannot overflow uint256
unchecked {
if (uint256(rules.maxRedelegations) + redelegationIndex < authorityLength) {
revert TooManyRedelegations(from, to);
}
if (block.timestamp < rules.notValidBefore) {
revert NotValidYet(from, to, rules.notValidBefore);
}
if (rules.notValidAfter != 0) {
if (block.timestamp > rules.notValidAfter) revert NotValidAnymore(from, to, rules.notValidAfter);
}
if (rules.blocksBeforeVoteCloses != 0) {
if (_proposalEndBlock(proposalId) > uint256(block.number) + uint256(rules.blocksBeforeVoteCloses)) {
revert TooEarly(from, to, rules.blocksBeforeVoteCloses);
}
}
if (rules.customRule != address(0)) {
if (
IRule(rules.customRule).validate(GOVERNOR, sender, proposalId, uint8(support))
!= IRule.validate.selector
) {
revert InvalidCustomRule(from, to, rules.customRule);
}
}
}
}
/**
* Return the allowance of a voter, used in `validate`.
*/
function _getVoterAllowance(AllowanceType allowanceType, uint256 subdelegationAllowance, uint256 delegatorAllowance)
private
pure
returns (uint256)
{
if (allowanceType == AllowanceType.Relative) {
return
subdelegationAllowance >= 1e5 ? delegatorAllowance : delegatorAllowance * subdelegationAllowance / 1e5;
}
// else if (allowanceType == AllowanceType.Absolute)
return delegatorAllowance > subdelegationAllowance ? subdelegationAllowance : delegatorAllowance;
}
}