-
Notifications
You must be signed in to change notification settings - Fork 108
/
FacadeWrite.sol
205 lines (173 loc) · 7.09 KB
/
FacadeWrite.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
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.17;
import "../interfaces/IFacadeWrite.sol";
import "./lib/FacadeWriteLib.sol";
/**
* @title FacadeWrite
* @notice A UX-friendly layer to interact with the protocol
* @dev Under the hood, uses two external libs to deal with blocksize limits.
*/
contract FacadeWrite is IFacadeWrite {
using FacadeWriteLib for address;
IDeployer public immutable deployer;
constructor(IDeployer deployer_) {
require(address(deployer_) != address(0), "invalid address");
deployer = deployer_;
}
/// Step 1
function deployRToken(ConfigurationParams calldata config, SetupParams calldata setup)
external
returns (address)
{
// Perform validations
require(setup.primaryBasket.length > 0, "no collateral");
require(setup.primaryBasket.length == setup.weights.length, "invalid length");
// Validate backups
for (uint256 i = 0; i < setup.backups.length; ++i) {
require(setup.backups[i].backupCollateral.length > 0, "no backup collateral");
}
// Validate beneficiaries
for (uint256 i = 0; i < setup.beneficiaries.length; ++i) {
require(
setup.beneficiaries[i].beneficiary != address(0) &&
(setup.beneficiaries[i].revShare.rTokenDist > 0 ||
setup.beneficiaries[i].revShare.rsrDist > 0),
"beneficiary revShare mismatch"
);
}
// Deploy contracts
IRToken rToken = IRToken(
deployer.deploy(
config.name,
config.symbol,
config.mandate,
address(this), // set as owner
config.params
)
);
// Get Main
IMain main = rToken.main();
IAssetRegistry assetRegistry = main.assetRegistry();
IBasketHandler basketHandler = main.basketHandler();
// Register assets
for (uint256 i = 0; i < setup.assets.length; ++i) {
require(assetRegistry.register(setup.assets[i]), "duplicate asset");
}
// Setup basket
{
IERC20[] memory basketERC20s = new IERC20[](setup.primaryBasket.length);
// Register collateral
for (uint256 i = 0; i < setup.primaryBasket.length; ++i) {
require(assetRegistry.register(setup.primaryBasket[i]), "duplicate collateral");
IERC20 erc20 = setup.primaryBasket[i].erc20();
basketERC20s[i] = erc20;
}
// Set basket
basketHandler.setPrimeBasket(basketERC20s, setup.weights);
basketHandler.refreshBasket();
}
// Setup backup config
{
for (uint256 i = 0; i < setup.backups.length; ++i) {
IERC20[] memory backupERC20s = new IERC20[](
setup.backups[i].backupCollateral.length
);
for (uint256 j = 0; j < setup.backups[i].backupCollateral.length; ++j) {
ICollateral backupColl = setup.backups[i].backupCollateral[j];
assetRegistry.register(backupColl); // do not require the asset is new
IERC20 erc20 = backupColl.erc20();
backupERC20s[j] = erc20;
}
basketHandler.setBackupConfig(
setup.backups[i].backupUnit,
setup.backups[i].diversityFactor,
backupERC20s
);
}
}
// Setup revshare beneficiaries
for (uint256 i = 0; i < setup.beneficiaries.length; ++i) {
main.distributor().setDistribution(
setup.beneficiaries[i].beneficiary,
setup.beneficiaries[i].revShare
);
}
// Pause until setupGovernance
main.pause();
// Setup deployer as owner to complete next step - do not renounce roles yet
main.grantRole(OWNER, msg.sender);
// Return rToken address
return address(rToken);
}
/// Step 2
/// @return newOwner The address of the new owner
function setupGovernance(
IRToken rToken,
bool deployGovernance,
bool unpause,
GovernanceParams calldata govParams,
address owner,
address guardian,
address pauser
) external returns (address newOwner) {
// Get Main
IMain main = rToken.main();
require(main.hasRole(OWNER, address(this)), "ownership already transferred");
require(main.hasRole(OWNER, msg.sender), "not initial deployer");
// Remove ownership to sender
main.revokeRole(OWNER, msg.sender);
if (deployGovernance) {
require(owner == address(0), "owner should be empty");
TimelockController timelock = new TimelockController(
govParams.timelockDelay,
new address[](0),
new address[](0)
);
// Deploy Governance contract
address governance = FacadeWriteLib.deployGovernance(
IStRSRVotes(address(main.stRSR())),
timelock,
govParams.votingDelay,
govParams.votingPeriod,
govParams.proposalThresholdAsMicroPercent,
govParams.quorumPercent
);
emit GovernanceCreated(rToken, governance, address(timelock));
// Setup Roles
timelock.grantRole(timelock.PROPOSER_ROLE(), governance); // Gov only proposer
timelock.grantRole(timelock.CANCELLER_ROLE(), guardian); // Guardian as canceller
timelock.grantRole(timelock.EXECUTOR_ROLE(), address(0)); // Anyone as executor
timelock.revokeRole(timelock.TIMELOCK_ADMIN_ROLE(), address(this)); // Revoke admin role
// Set new owner to timelock
newOwner = address(timelock);
} else {
require(owner != address(0), "owner not defined");
newOwner = owner;
}
// Setup guardian as freeze starter / extender + pauser
if (guardian != address(0)) {
// As a further decentralization step it is suggested to further differentiate between
// these two roles. But this is what will make sense for simple system setup.
main.grantRole(SHORT_FREEZER, guardian);
main.grantRole(LONG_FREEZER, guardian);
main.grantRole(PAUSER, guardian);
}
// Setup Pauser
if (pauser != address(0)) {
main.grantRole(PAUSER, pauser);
}
// Unpause if required
if (unpause) {
main.unpause();
}
// Transfer Ownership and renounce roles
main.grantRole(OWNER, newOwner);
main.grantRole(SHORT_FREEZER, newOwner);
main.grantRole(LONG_FREEZER, newOwner);
main.grantRole(PAUSER, newOwner);
main.renounceRole(OWNER, address(this));
main.renounceRole(SHORT_FREEZER, address(this));
main.renounceRole(LONG_FREEZER, address(this));
main.renounceRole(PAUSER, address(this));
}
}