-
Notifications
You must be signed in to change notification settings - Fork 30
/
Copy pathL2ArbitrumGovernor.sol
239 lines (221 loc) · 10.3 KB
/
L2ArbitrumGovernor.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
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.16;
import "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol";
import
"@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesQuorumFractionUpgradeable.sol";
import
"@openzeppelin/contracts-upgradeable/governance/extensions/GovernorPreventLateQuorumUpgradeable.sol";
import
"@openzeppelin/contracts-upgradeable/governance/extensions/GovernorCountingSimpleUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesUpgradeable.sol";
import
"@openzeppelin/contracts-upgradeable/governance/extensions/GovernorTimelockControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
/// @title L2ArbitrumGovernor
/// @notice Governance controls for the Arbitrum DAO
/// @dev Standard governor with some special functionality to avoid counting
/// votes of some excluded tokens. Also allows for an owner to set parameters by calling
/// relay.
contract L2ArbitrumGovernor is
Initializable,
GovernorSettingsUpgradeable,
GovernorCountingSimpleUpgradeable,
GovernorVotesUpgradeable,
GovernorTimelockControlUpgradeable,
GovernorVotesQuorumFractionUpgradeable,
GovernorPreventLateQuorumUpgradeable,
OwnableUpgradeable
{
/// @notice address for which votes will not be counted toward quorum
/// @dev A portion of the Arbitrum tokens will be held by entities (eg the treasury) that
/// are not eligible to vote. However, even if their voting/delegation is restricted their
/// tokens will still count towards the total supply, and will therefore affect the quorum.
/// Restricted addresses should be forced to delegate their votes to this special exclude
/// addresses which is not counted when calculating quorum
/// Example address that should be excluded: DAO treasury, foundation, unclaimed tokens,
/// burned tokens and swept (see TokenDistributor) tokens.
/// Note that Excluded Address is a readable name with no code of PK associated with it, and thus can't vote.
address public constant EXCLUDE_ADDRESS = address(0xA4b86);
constructor() {
_disableInitializers();
}
/// @param _token The token to read vote delegation from
/// @param _timelock A time lock for proposal execution
/// @param _owner The executor through which all upgrades should be finalised
/// @param _votingDelay The delay between a proposal submission and voting starts
/// @param _votingPeriod The period for which the vote lasts
/// @param _quorumNumerator The proportion of the circulating supply required to reach a quorum
/// @param _proposalThreshold The number of delegated votes required to create a proposal
/// @param _minPeriodAfterQuorum The minimum number of blocks available for voting after the quorum is reached
function initialize(
IVotesUpgradeable _token,
TimelockControllerUpgradeable _timelock,
address _owner,
uint256 _votingDelay,
uint256 _votingPeriod,
uint256 _quorumNumerator,
uint256 _proposalThreshold,
uint64 _minPeriodAfterQuorum
) external initializer {
__Governor_init("L2ArbitrumGovernor");
__GovernorSettings_init(_votingDelay, _votingPeriod, _proposalThreshold);
__GovernorCountingSimple_init();
__GovernorVotes_init(_token);
__GovernorTimelockControl_init(_timelock);
__GovernorVotesQuorumFraction_init(_quorumNumerator);
__GovernorPreventLateQuorum_init(_minPeriodAfterQuorum);
_transferOwnership(_owner);
}
/// @notice Allows the owner to make calls from the governor
/// @dev We want the owner to be able to upgrade settings and parametes on this Governor
/// however we can't use onlyGovernance as it requires calls originate from the governor
/// contract. The normal flow for onlyGovernance to work is to call execute on the governor
/// which will then call out to the _executor(), which will then call back in to the governor to set
/// a parameter. At the point of setting the parameter onlyGovernance is checked, and this includes
/// a check this call originated in the execute() function. The purpose of this is an added
/// safety measure that ensure that all calls originate at the governor, and if second entrypoint is
/// added to the _executor() contract, that new entrypoint will not be able to pass the onlyGovernance check.
/// You can read more about this in the comments on onlyGovernance()
/// This flow doesn't work for Arbitrum governance as we require an proposal on L2 to first
/// be relayed to L1, and then back again to L2 before calling into the governor to update
/// settings. This means that updating settings can't be done in a single transaction.
/// There are two potential solutions to this problem:
/// 1. Use a more persistent record that a specific upgrade is taking place. This adds
/// a lot of complexity, as we have multiple layers of calldata wrapping each other to
/// define the multiple transactions that occur in a round-trip upgrade. So safely recording
/// execution of the would be difficult and brittle.
/// 2. Override this protection and just ensure elsewhere that the executor only has the
/// the correct entrypoints and access control. We've gone for this option.
/// By overriding the relay function we allow the executor to make any call originating
/// from the governor, and by setting the _executor() to be the governor itself we can use the
/// relay function to call back into the governor to update settings e.g:
///
/// l2ArbitrumGovernor.relay(
/// address(l2ArbitrumGovernor),
/// 0,
/// abi.encodeWithSelector(l2ArbitrumGovernor.updateQuorumNumerator.selector, 4)
/// );
function relay(address target, uint256 value, bytes calldata data)
external
virtual
override
onlyOwner
{
AddressUpgradeable.functionCallWithValue(target, data, value);
}
/// @notice returns l2 executor address; used internally for onlyGovernance check
function _executor()
internal
view
override(GovernorTimelockControlUpgradeable, GovernorUpgradeable)
returns (address)
{
return address(this);
}
/// @notice Get "circulating" votes supply; i.e., total minus excluded vote exclude address.
function getPastCirculatingSupply(uint256 blockNumber) public view virtual returns (uint256) {
return
token.getPastTotalSupply(blockNumber) - token.getPastVotes(EXCLUDE_ADDRESS, blockNumber);
}
/// @notice Calculates the quorum size, excludes token delegated to the exclude address
function quorum(uint256 blockNumber)
public
view
override(IGovernorUpgradeable, GovernorVotesQuorumFractionUpgradeable)
returns (uint256)
{
return (getPastCirculatingSupply(blockNumber) * quorumNumerator(blockNumber))
/ quorumDenominator();
}
/// @inheritdoc GovernorVotesQuorumFractionUpgradeable
function quorumDenominator()
public
pure
override(GovernorVotesQuorumFractionUpgradeable)
returns (uint256)
{
// update to 10k to allow for higher precision
return 10_000;
}
// Overrides:
// @notice Votes required for proposal.
function proposalThreshold()
public
view
override(GovernorSettingsUpgradeable, GovernorUpgradeable)
returns (uint256)
{
return GovernorSettingsUpgradeable.proposalThreshold();
}
function state(uint256 proposalId)
public
view
override(GovernorUpgradeable, GovernorTimelockControlUpgradeable)
returns (ProposalState)
{
return GovernorTimelockControlUpgradeable.state(proposalId);
}
function _castVote(
uint256 proposalId,
address account,
uint8 support,
string memory reason,
bytes memory params
)
internal
override(GovernorUpgradeable, GovernorPreventLateQuorumUpgradeable)
returns (uint256)
{
return GovernorPreventLateQuorumUpgradeable._castVote(
proposalId, account, support, reason, params
);
}
function proposalDeadline(uint256 proposalId)
public
view
override(IGovernorUpgradeable, GovernorUpgradeable, GovernorPreventLateQuorumUpgradeable)
returns (uint256)
{
return GovernorPreventLateQuorumUpgradeable.proposalDeadline(proposalId);
}
function _execute(
uint256 proposalId,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal override(GovernorUpgradeable, GovernorTimelockControlUpgradeable) {
GovernorTimelockControlUpgradeable._execute(
proposalId, targets, values, calldatas, descriptionHash
);
}
function _cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
)
internal
override(GovernorUpgradeable, GovernorTimelockControlUpgradeable)
returns (uint256)
{
return
GovernorTimelockControlUpgradeable._cancel(targets, values, calldatas, descriptionHash);
}
function supportsInterface(bytes4 interfaceId)
public
view
override(GovernorUpgradeable, GovernorTimelockControlUpgradeable)
returns (bool)
{
return GovernorTimelockControlUpgradeable.supportsInterface(interfaceId);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}