-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathTaikoDaoFactory.sol
403 lines (353 loc) · 18 KB
/
TaikoDaoFactory.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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;
import {DAO} from "@aragon/osx/core/dao/DAO.sol";
import {DAOFactory} from "@aragon/osx/framework/dao/DAOFactory.sol";
import {Multisig} from "../Multisig.sol";
import {EmergencyMultisig} from "../EmergencyMultisig.sol";
import {SignerList, UPDATE_SIGNER_LIST_SETTINGS_PERMISSION_ID} from "../SignerList.sol";
import {EncryptionRegistry} from "../EncryptionRegistry.sol";
import {OptimisticTokenVotingPlugin} from "../OptimisticTokenVotingPlugin.sol";
import {OptimisticTokenVotingPluginSetup} from "../setup/OptimisticTokenVotingPluginSetup.sol";
import {MultisigPluginSetup} from "../setup/MultisigPluginSetup.sol";
import {EmergencyMultisigPluginSetup} from "../setup/EmergencyMultisigPluginSetup.sol";
import {Addresslist} from "@aragon/osx/plugins/utils/Addresslist.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import {PluginSetupProcessor} from "@aragon/osx/framework/plugin/setup/PluginSetupProcessor.sol";
import {hashHelpers, PluginSetupRef} from "@aragon/osx/framework/plugin/setup/PluginSetupProcessorHelpers.sol";
import {PluginRepoFactory} from "@aragon/osx/framework/plugin/repo/PluginRepoFactory.sol";
import {PluginRepo} from "@aragon/osx/framework/plugin/repo/PluginRepo.sol";
import {IPluginSetup} from "@aragon/osx/framework/plugin/setup/IPluginSetup.sol";
import {IVotesUpgradeable} from "@openzeppelin/contracts-upgradeable/governance/utils/IVotesUpgradeable.sol";
import {GovernanceERC20} from "@aragon/osx/token/ERC20/governance/GovernanceERC20.sol";
import {createERC1967Proxy} from "@aragon/osx/utils/Proxy.sol";
import {PermissionLib} from "@aragon/osx/core/permission/PermissionLib.sol";
contract TaikoDaoFactory {
/// @notice The struct containing all the parameters to deploy the DAO
/// @param tokenAddress The address of the IVotes compatible ERC20 token contract to use for the voting power
/// @param taikoL1ContractAddress The contract where the status of the L1 can be retrieved
/// @param taikoBridgeAddress The address of the bridge. NOTE: will be using the vault instead
/// @param l2InactivityPeriod How many seconds until the lack of L2 blocks is considered as an inactive L2
/// @param l2AggregationGracePeriod How many additional seconds will be allowed for L2 sourced vetoes to be relayed
/// @param skipL2 Whether L2 votes should be ignored for vetoing
/// @param minVetoRatio The minimum percentage of the effective token supply required to defeat a proposal
/// @param minStdProposalDuration The amount of seconds that an optimistic proposal will last before it can (eventually) be executed
/// @param minStdApprovals The number of approvals needed for a multisig proposal to be relayed to the optimistic voting phase
/// @param minEmergencyApprovals The amount of approvals required for the super majority to be able to execute an emergency proposal on the DAO
/// @param osxDaoFactory The address of the OSx DAO factory contract, used to retrieve the DAO implementation address
/// @param pluginSetupProcessor The address of the OSx PluginSetupProcessor contract on the target chain
/// @param pluginRepoFactory The address of the OSx PluginRepoFactory contract on the target chain
/// @param multisigPluginSetup The address of the already deployed plugin setup for the standard multisig
/// @param emergencyMultisigPluginSetup The address of the already deployed plugin setup for the emergency multisig
/// @param optimisticTokenVotingPluginSetup The address of the already deployed plugin setup for the optimistic voting plugin
/// @param multisigMembers The list of addresses to be defined as the initial multisig signers
/// @param multisigExpirationPeriod How many seconds until a pending multisig proposal expires
/// @param stdMultisigEnsDomain The subdomain to use as the ENS for the standard mulsitig plugin setup. Note: it must be unique and available.
/// @param emergencyMultisigEnsDomain The subdomain to use as the ENS for the emergency multisig plugin setup. Note: it must be unique and available.
/// @param optimisticTokenVotingEnsDomain The subdomain to use as the ENS for the optimistic voting plugin setup. Note: it must be unique and available.
struct DeploymentSettings {
// Taiko contract settings
IVotesUpgradeable tokenAddress;
address taikoL1ContractAddress;
address taikoBridgeAddress;
uint32 timelockPeriod;
uint32 l2InactivityPeriod;
uint32 l2AggregationGracePeriod;
bool skipL2;
// Voting settings
uint32 minVetoRatio;
uint32 minStdProposalDuration;
uint16 minStdApprovals;
uint16 minEmergencyApprovals;
// OSx contracts
address osxDaoFactory;
PluginSetupProcessor pluginSetupProcessor;
PluginRepoFactory pluginRepoFactory;
// Plugin setup's
MultisigPluginSetup multisigPluginSetup;
EmergencyMultisigPluginSetup emergencyMultisigPluginSetup;
OptimisticTokenVotingPluginSetup optimisticTokenVotingPluginSetup;
// Multisig
address[] multisigMembers;
uint32 multisigExpirationPeriod;
// ENS
string stdMultisigEnsDomain;
string emergencyMultisigEnsDomain;
string optimisticTokenVotingEnsDomain;
}
struct Deployment {
DAO dao;
// Plugins
Multisig multisigPlugin;
EmergencyMultisig emergencyMultisigPlugin;
OptimisticTokenVotingPlugin optimisticTokenVotingPlugin;
// Helpers
SignerList signerList;
EncryptionRegistry encryptionRegistry;
// Plugin repo's
PluginRepo multisigPluginRepo;
PluginRepo emergencyMultisigPluginRepo;
PluginRepo optimisticTokenVotingPluginRepo;
}
/// @notice Thrown when attempting to call deployOnce() when the DAO is already deployed.
error AlreadyDeployed();
DeploymentSettings settings;
Deployment deployment;
/// @notice Initializes the factory and performs the full deployment. Values become read-only after that.
/// @param _settings The settings of the one-time deployment.
constructor(DeploymentSettings memory _settings) {
settings = _settings;
}
function deployOnce() public {
if (address(deployment.dao) != address(0)) revert AlreadyDeployed();
IPluginSetup.PreparedSetupData memory preparedMultisigSetupData;
IPluginSetup.PreparedSetupData memory preparedEmergencyMultisigSetupData;
IPluginSetup.PreparedSetupData memory preparedOptimisticSetupData;
// DEPLOY THE DAO (The factory is the interim owner)
DAO dao = prepareDao();
deployment.dao = dao;
// DEPLOY THE SIGNER LIST AND THE ENCRYPTION REGISTRY
(deployment.signerList, deployment.encryptionRegistry) = prepareSignerListAndEncryptionRegistry(dao);
// DEPLOY THE PLUGINS
(deployment.multisigPlugin, deployment.multisigPluginRepo, preparedMultisigSetupData) =
prepareMultisig(dao, deployment.signerList);
(deployment.emergencyMultisigPlugin, deployment.emergencyMultisigPluginRepo, preparedEmergencyMultisigSetupData)
= prepareEmergencyMultisig(dao, deployment.signerList);
(
deployment.optimisticTokenVotingPlugin,
deployment.optimisticTokenVotingPluginRepo,
preparedOptimisticSetupData
) = prepareOptimisticTokenVoting(
dao, address(deployment.multisigPlugin), address(deployment.emergencyMultisigPlugin)
);
// APPLY THE INSTALLATIONS
grantApplyInstallationPermissions(dao);
applyPluginInstallation(
dao, address(deployment.multisigPlugin), deployment.multisigPluginRepo, preparedMultisigSetupData
);
applyPluginInstallation(
dao,
address(deployment.emergencyMultisigPlugin),
deployment.emergencyMultisigPluginRepo,
preparedEmergencyMultisigSetupData
);
applyPluginInstallation(
dao,
address(deployment.optimisticTokenVotingPlugin),
deployment.optimisticTokenVotingPluginRepo,
preparedOptimisticSetupData
);
revokeApplyInstallationPermissions(dao);
// REMOVE THIS CONTRACT AS OWNER
revokeOwnerPermission(deployment.dao);
}
function prepareDao() internal returns (DAO dao) {
address daoBase = DAOFactory(settings.osxDaoFactory).daoBase();
dao = DAO(
payable(
createERC1967Proxy(
address(daoBase),
abi.encodeCall(
DAO.initialize,
(
"", // Metadata URI
address(this), // initialOwner
address(0x0), // Trusted forwarder
"" // DAO URI
)
)
)
)
);
// Grant DAO all the needed permissions on itself
PermissionLib.SingleTargetPermission[] memory items = new PermissionLib.SingleTargetPermission[](3);
items[0] =
PermissionLib.SingleTargetPermission(PermissionLib.Operation.Grant, address(dao), dao.ROOT_PERMISSION_ID());
items[1] = PermissionLib.SingleTargetPermission(
PermissionLib.Operation.Grant, address(dao), dao.UPGRADE_DAO_PERMISSION_ID()
);
items[2] = PermissionLib.SingleTargetPermission(
PermissionLib.Operation.Grant, address(dao), dao.REGISTER_STANDARD_CALLBACK_PERMISSION_ID()
);
dao.applySingleTargetPermissions(address(dao), items);
}
function prepareSignerListAndEncryptionRegistry(DAO dao)
internal
returns (SignerList signerList, EncryptionRegistry encryptionRegistry)
{
signerList = deploySignerListWithoutSettings(dao);
encryptionRegistry = new EncryptionRegistry(signerList);
// Link them together
{
// Grant temporary permission to update the settings
dao.grant(address(signerList), address(this), UPDATE_SIGNER_LIST_SETTINGS_PERMISSION_ID);
signerList.updateSettings(SignerList.Settings(encryptionRegistry, uint16(settings.multisigMembers.length)));
// Revoke the remporary permission
dao.revoke(address(signerList), address(this), UPDATE_SIGNER_LIST_SETTINGS_PERMISSION_ID);
}
// Make the DAO own the signers list
dao.grant(address(signerList), address(dao), UPDATE_SIGNER_LIST_SETTINGS_PERMISSION_ID);
}
function prepareMultisig(DAO dao, SignerList signerList)
internal
returns (Multisig, PluginRepo, IPluginSetup.PreparedSetupData memory)
{
// Publish repo
PluginRepo pluginRepo = PluginRepoFactory(settings.pluginRepoFactory).createPluginRepoWithFirstVersion(
settings.stdMultisigEnsDomain, address(settings.multisigPluginSetup), address(dao), " ", " "
);
dao.grant(address(pluginRepo), address(dao), pluginRepo.MAINTAINER_PERMISSION_ID());
// UPGRADE_REPO_PERMISSION_ID can be granted eventually
bytes memory settingsData = settings.multisigPluginSetup.encodeInstallationParameters(
Multisig.MultisigSettings(
true, // onlyListed
settings.minStdApprovals,
settings.minStdProposalDuration, // destination minDuration
signerList,
settings.multisigExpirationPeriod
)
);
(address plugin, IPluginSetup.PreparedSetupData memory preparedSetupData) = settings
.pluginSetupProcessor
.prepareInstallation(
address(dao),
PluginSetupProcessor.PrepareInstallationParams(
PluginSetupRef(PluginRepo.Tag(1, 1), PluginRepo(pluginRepo)), settingsData
)
);
return (Multisig(plugin), pluginRepo, preparedSetupData);
}
function prepareEmergencyMultisig(DAO dao, SignerList signerList)
internal
returns (EmergencyMultisig, PluginRepo, IPluginSetup.PreparedSetupData memory)
{
// Publish repo
PluginRepo pluginRepo = PluginRepoFactory(settings.pluginRepoFactory).createPluginRepoWithFirstVersion(
settings.emergencyMultisigEnsDomain, address(settings.emergencyMultisigPluginSetup), address(dao), " ", " "
);
dao.grant(address(pluginRepo), address(dao), pluginRepo.MAINTAINER_PERMISSION_ID());
// UPGRADE_REPO_PERMISSION_ID can be granted eventually
bytes memory settingsData = settings.emergencyMultisigPluginSetup.encodeInstallationParameters(
EmergencyMultisig.MultisigSettings(
true, // onlyListed
settings.minEmergencyApprovals, // minAppovals
signerList,
settings.multisigExpirationPeriod
)
);
(address plugin, IPluginSetup.PreparedSetupData memory preparedSetupData) = settings
.pluginSetupProcessor
.prepareInstallation(
address(dao),
PluginSetupProcessor.PrepareInstallationParams(
PluginSetupRef(PluginRepo.Tag(1, 1), PluginRepo(pluginRepo)), settingsData
)
);
return (EmergencyMultisig(plugin), pluginRepo, preparedSetupData);
}
function prepareOptimisticTokenVoting(DAO dao, address stdProposer, address emergencyProposer)
internal
returns (OptimisticTokenVotingPlugin, PluginRepo, IPluginSetup.PreparedSetupData memory)
{
// Publish repo
PluginRepo pluginRepo = PluginRepoFactory(settings.pluginRepoFactory).createPluginRepoWithFirstVersion(
settings.optimisticTokenVotingEnsDomain,
address(settings.optimisticTokenVotingPluginSetup),
address(dao),
" ",
" "
);
dao.grant(address(pluginRepo), address(dao), pluginRepo.MAINTAINER_PERMISSION_ID());
// UPGRADE_REPO_PERMISSION_ID can be granted eventually
// Plugin settings
bytes memory settingsData;
{
OptimisticTokenVotingPlugin.OptimisticGovernanceSettings memory votingSettings = OptimisticTokenVotingPlugin
.OptimisticGovernanceSettings(
settings.minVetoRatio,
0, // minDuration (the condition contract will enforce it)
settings.timelockPeriod,
settings.l2InactivityPeriod,
settings.l2AggregationGracePeriod,
settings.skipL2
);
OptimisticTokenVotingPluginSetup.TokenSettings memory existingTokenSettings =
OptimisticTokenVotingPluginSetup.TokenSettings(address(settings.tokenAddress), "Taiko", "TKO");
GovernanceERC20.MintSettings memory unusedMintSettings =
GovernanceERC20.MintSettings(new address[](0), new uint256[](0));
settingsData = settings.optimisticTokenVotingPluginSetup.encodeInstallationParams(
OptimisticTokenVotingPluginSetup.InstallationParameters(
votingSettings,
existingTokenSettings,
unusedMintSettings,
settings.taikoL1ContractAddress,
settings.taikoBridgeAddress,
settings.minStdProposalDuration,
stdProposer,
emergencyProposer
)
);
}
(address plugin, IPluginSetup.PreparedSetupData memory preparedSetupData) = settings
.pluginSetupProcessor
.prepareInstallation(
address(dao),
PluginSetupProcessor.PrepareInstallationParams(
PluginSetupRef(PluginRepo.Tag(1, 1), PluginRepo(pluginRepo)), settingsData
)
);
return (OptimisticTokenVotingPlugin(plugin), pluginRepo, preparedSetupData);
}
function deploySignerListWithoutSettings(DAO dao) internal returns (SignerList helper) {
helper = SignerList(
createERC1967Proxy(
address(new SignerList()), abi.encodeCall(SignerList.initialize, (dao, settings.multisigMembers))
)
);
}
function applyPluginInstallation(
DAO dao,
address plugin,
PluginRepo pluginRepo,
IPluginSetup.PreparedSetupData memory preparedSetupData
) internal {
settings.pluginSetupProcessor.applyInstallation(
address(dao),
PluginSetupProcessor.ApplyInstallationParams(
PluginSetupRef(PluginRepo.Tag(1, 1), pluginRepo),
plugin,
preparedSetupData.permissions,
hashHelpers(preparedSetupData.helpers)
)
);
}
function grantApplyInstallationPermissions(DAO dao) internal {
// The PSP can manage permissions on the new DAO
dao.grant(address(dao), address(settings.pluginSetupProcessor), dao.ROOT_PERMISSION_ID());
// This factory can call applyInstallation() on the PSP
dao.grant(
address(settings.pluginSetupProcessor),
address(this),
settings.pluginSetupProcessor.APPLY_INSTALLATION_PERMISSION_ID()
);
}
function revokeApplyInstallationPermissions(DAO dao) internal {
// Revoking the permission for the factory to call applyInstallation() on the PSP
dao.revoke(
address(settings.pluginSetupProcessor),
address(this),
settings.pluginSetupProcessor.APPLY_INSTALLATION_PERMISSION_ID()
);
// Revoke the PSP permission to manage permissions on the new DAO
dao.revoke(address(dao), address(settings.pluginSetupProcessor), dao.ROOT_PERMISSION_ID());
}
function revokeOwnerPermission(DAO dao) internal {
dao.revoke(address(dao), address(this), dao.ROOT_PERMISSION_ID());
}
// Getters
function getSettings() public view returns (DeploymentSettings memory) {
return settings;
}
function getDeployment() public view returns (Deployment memory) {
return deployment;
}
}