-
Notifications
You must be signed in to change notification settings - Fork 239
/
Copy pathSuperTokenFactory.sol
431 lines (372 loc) · 15.3 KB
/
SuperTokenFactory.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
// SPDX-License-Identifier: AGPLv3
pragma solidity ^0.8.23;
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import {
ISuperTokenFactory,
ISuperToken
} from "../interfaces/superfluid/ISuperTokenFactory.sol";
import {
ISuperfluid, IPoolAdminNFT, IPoolMemberNFT
} from "../interfaces/superfluid/ISuperfluid.sol";
import { UUPSProxy } from "../upgradability/UUPSProxy.sol";
import { UUPSProxiable } from "../upgradability/UUPSProxiable.sol";
import { FullUpgradableSuperTokenProxy } from "./FullUpgradableSuperTokenProxy.sol";
import { IConstantOutflowNFT, IConstantInflowNFT } from "./SuperToken.sol";
abstract contract SuperTokenFactoryBase is
UUPSProxiable,
ISuperTokenFactory
{
struct InitializeData {
address underlyingToken;
address superToken;
}
/**************************************************************************
* Immutable Variables
**************************************************************************/
// solhint-disable-next-line var-name-mixedcase
ISuperToken immutable public _SUPER_TOKEN_LOGIC;
ISuperfluid immutable internal _host;
// solhint-disable-next-line var-name-mixedcase
IConstantOutflowNFT immutable public CONSTANT_OUTFLOW_NFT_LOGIC;
// solhint-disable-next-line var-name-mixedcase
IConstantInflowNFT immutable public CONSTANT_INFLOW_NFT_LOGIC;
// solhint-disable-next-line var-name-mixedcase
IPoolAdminNFT immutable public POOL_ADMIN_NFT_LOGIC;
// solhint-disable-next-line var-name-mixedcase
IPoolMemberNFT immutable public POOL_MEMBER_NFT_LOGIC;
/**************************************************************************
* Storage Variables
**************************************************************************/
/* WARNING: NEVER RE-ORDER VARIABLES! Including the base contracts.
Always double-check that new
variables are added APPEND-ONLY. Re-ordering variables can
permanently BREAK the deployed proxy contract. */
// @dev This is the old SuperToken logic contract that is no longer used
// It is kept here for backwards compatibility due to the fact that we cannot
// change the storage layout of the contract
ISuperToken internal _superTokenLogicDeprecated;
/// @notice A mapping from underlying token addresses to canonical wrapper super token addresses
/// @dev Reasoning: (1) provide backwards compatibility for existing listed wrapper super tokens
/// @dev (2) prevent address retrieval issues if we ever choose to modify the bytecode of the UUPSProxy contract
/// @dev NOTE: address(0) key points to the NativeAssetSuperToken on the network.
mapping(address => address) internal _canonicalWrapperSuperTokens;
/// NOTE: Whenever modifying the storage layout here it is important to update the validateStorageLayout
/// function in its respective mock contract to ensure that it doesn't break anything or lead to unexpected
/// behaviors/layout when upgrading
error SUPER_TOKEN_FACTORY_ONLY_GOVERNANCE_OWNER();
constructor(
ISuperfluid host,
ISuperToken superTokenLogic,
IConstantOutflowNFT constantOutflowNFTLogic,
IConstantInflowNFT constantInflowNFTLogic,
IPoolAdminNFT poolAdminNFTLogic,
IPoolMemberNFT poolMemberNFTLogic
) {
_host = host;
// SuperToken logic is now deployed prior to new factory logic deployment
// and passed in as a parameter to SuperTokenFactory constructor
_SUPER_TOKEN_LOGIC = superTokenLogic;
// this is optional - allow to fail in order to not force re-deployment
// solhint-disable-next-line no-empty-blocks
try UUPSProxiable(address(_SUPER_TOKEN_LOGIC)).castrate() {}
// solhint-disable-next-line no-empty-blocks
catch {}
CONSTANT_OUTFLOW_NFT_LOGIC = constantOutflowNFTLogic;
CONSTANT_INFLOW_NFT_LOGIC = constantInflowNFTLogic;
POOL_ADMIN_NFT_LOGIC = poolAdminNFTLogic;
POOL_MEMBER_NFT_LOGIC = poolMemberNFTLogic;
// emit SuperTokenLogicCreated event
// note that creation here means the setting of the super token logic contract
// as the canonical super token logic for the Superfluid framework and not the
// actual contract creation
emit SuperTokenLogicCreated(_SUPER_TOKEN_LOGIC);
}
/// @inheritdoc ISuperTokenFactory
function getHost()
external view
override(ISuperTokenFactory)
returns(address host)
{
return address(_host);
}
/**************************************************************************
* UUPSProxiable
**************************************************************************/
/// @inheritdoc ISuperTokenFactory
function initialize()
external
override
initializer // OpenZeppelin Initializable
// solhint-disable-next-line no-empty-blocks
{
}
function proxiableUUID() public pure override returns (bytes32) {
return keccak256("org.superfluid-finance.contracts.SuperTokenFactory.implementation");
}
/// @notice Updates the logic contract for the SuperTokenFactory
/// @dev This function updates the logic contract for the SuperTokenFactory
/// @param newAddress the new address of the SuperTokenFactory logic contract
function updateCode(address newAddress) external override {
if (msg.sender != address(_host)) {
revert SUPER_TOKEN_FACTORY_ONLY_HOST();
}
_updateCodeAddress(newAddress);
// Upgrade the Flow NFT logic contracts on the canonical proxies
// We only do this if the new logic contracts passed in updating the SuperTokenFactory
// are different from the current logic contracts
SuperTokenFactory newFactory = SuperTokenFactory(newAddress);
if (address(POOL_ADMIN_NFT_LOGIC) != address(newFactory.POOL_ADMIN_NFT_LOGIC())) {
UUPSProxiable(address(_SUPER_TOKEN_LOGIC.POOL_ADMIN_NFT())).updateCode(
address(newFactory.POOL_ADMIN_NFT_LOGIC())
);
}
if (address(POOL_MEMBER_NFT_LOGIC) != address(newFactory.POOL_MEMBER_NFT_LOGIC())) {
UUPSProxiable(address(_SUPER_TOKEN_LOGIC.POOL_MEMBER_NFT())).updateCode(
address(newFactory.POOL_MEMBER_NFT_LOGIC())
);
}
}
/**************************************************************************
* ISuperTokenFactory
**************************************************************************/
/// @inheritdoc ISuperTokenFactory
function getSuperTokenLogic()
external view override
returns (ISuperToken)
{
return _SUPER_TOKEN_LOGIC;
}
/// @inheritdoc ISuperTokenFactory
function createCanonicalERC20Wrapper(IERC20Metadata _underlyingToken)
external
returns (ISuperToken)
{
// we use this to check if we have initialized the _canonicalWrapperSuperTokens mapping
// @note we must set this during initialization
if (_canonicalWrapperSuperTokens[address(0)] == address(0)) {
revert SUPER_TOKEN_FACTORY_UNINITIALIZED();
}
address underlyingTokenAddress = address(_underlyingToken);
address canonicalSuperTokenAddress = _canonicalWrapperSuperTokens[
underlyingTokenAddress
];
// if the canonical super token address exists, revert with custom error
if (canonicalSuperTokenAddress != address(0)) {
revert SUPER_TOKEN_FACTORY_ALREADY_EXISTS();
}
// use create2 to deterministically create the proxy contract for the wrapper super token
bytes32 salt = keccak256(abi.encode(underlyingTokenAddress));
UUPSProxy proxy = new UUPSProxy{ salt: salt }();
// NOTE: address(proxy) is equivalent to address(superToken)
_canonicalWrapperSuperTokens[underlyingTokenAddress] = address(
proxy
);
// set the implementation/logic contract address for the newly deployed proxy
proxy.initializeProxy(address(_SUPER_TOKEN_LOGIC));
// cast it as the same type as the logic contract
ISuperToken superToken = ISuperToken(address(proxy));
// get underlying token info
uint8 underlyingDecimals = _underlyingToken.decimals();
string memory underlyingName = _underlyingToken.name();
string memory underlyingSymbol = _underlyingToken.symbol();
// initialize the contract (proxy constructor)
superToken.initialize(
_underlyingToken,
underlyingDecimals,
string.concat("Super ", underlyingName),
string.concat(underlyingSymbol, "x")
);
emit SuperTokenCreated(superToken);
return superToken;
}
function createERC20Wrapper(
IERC20Metadata underlyingToken,
uint8 underlyingDecimals,
Upgradability upgradability,
string calldata name,
string calldata symbol,
address admin
) public override returns (ISuperToken superToken) {
if (address(underlyingToken) == address(0)) {
revert SUPER_TOKEN_FACTORY_ZERO_ADDRESS();
}
if (upgradability == Upgradability.NON_UPGRADABLE) {
revert SUPER_TOKEN_FACTORY_NON_UPGRADEABLE_IS_DEPRECATED();
} else if (upgradability == Upgradability.SEMI_UPGRADABLE) {
UUPSProxy proxy = new UUPSProxy();
// initialize the wrapper
proxy.initializeProxy(address(_SUPER_TOKEN_LOGIC));
superToken = ISuperToken(address(proxy));
} else /* if (type == Upgradability.FULL_UPGRADABLE) */ {
FullUpgradableSuperTokenProxy proxy = new FullUpgradableSuperTokenProxy();
proxy.initialize();
superToken = ISuperToken(address(proxy));
}
// initialize the token
superToken.initializeWithAdmin(
underlyingToken,
underlyingDecimals,
name,
symbol,
admin
);
emit SuperTokenCreated(superToken);
}
/// @inheritdoc ISuperTokenFactory
function createERC20Wrapper(
IERC20Metadata underlyingToken,
uint8 underlyingDecimals,
Upgradability upgradability,
string calldata name,
string calldata symbol
)
external override
returns (ISuperToken superToken)
{
return createERC20Wrapper(
underlyingToken,
underlyingDecimals,
upgradability,
name,
symbol,
address(0)
);
}
/// @inheritdoc ISuperTokenFactory
function createERC20Wrapper(
IERC20Metadata underlyingToken,
Upgradability upgradability,
string calldata name,
string calldata symbol,
address admin
)
external override
returns (ISuperToken superToken)
{
return createERC20Wrapper(
underlyingToken,
underlyingToken.decimals(),
upgradability,
name,
symbol,
admin
);
}
/// @inheritdoc ISuperTokenFactory
function createERC20Wrapper(
IERC20Metadata underlyingToken,
Upgradability upgradability,
string calldata name,
string calldata symbol
)
external override
returns (ISuperToken superToken)
{
return createERC20Wrapper(
underlyingToken,
underlyingToken.decimals(),
upgradability,
name,
symbol,
address(0)
);
}
/// @inheritdoc ISuperTokenFactory
function initializeCustomSuperToken(
address customSuperTokenProxy
)
external override
{
// odd solidity stuff..
// NOTE payable necessary because UUPSProxy has a payable fallback function
address payable a = payable(address(uint160(customSuperTokenProxy)));
UUPSProxy(a).initializeProxy(address(_SUPER_TOKEN_LOGIC));
emit CustomSuperTokenCreated(ISuperToken(customSuperTokenProxy));
}
/// @inheritdoc ISuperTokenFactory
function computeCanonicalERC20WrapperAddress(address _underlyingToken)
external
view
returns (address superTokenAddress, bool isDeployed)
{
address existingAddress = _canonicalWrapperSuperTokens[
_underlyingToken
];
if (existingAddress != address(0)) {
superTokenAddress = existingAddress;
isDeployed = true;
} else {
bytes memory bytecode = type(UUPSProxy).creationCode;
superTokenAddress = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
keccak256(abi.encode(_underlyingToken)),
keccak256(bytecode)
)
)
)
)
);
isDeployed = false;
}
}
/// @inheritdoc ISuperTokenFactory
function getCanonicalERC20Wrapper(address _underlyingTokenAddress)
external
view
returns (address superTokenAddress)
{
superTokenAddress = _canonicalWrapperSuperTokens[
_underlyingTokenAddress
];
}
/// @notice Initializes list of canonical wrapper super tokens.
/// @dev Note that this should also be kind of a throwaway function which will be executed only once.
/// @param _data an array of canonical wrappper super tokens to be set
function initializeCanonicalWrapperSuperTokens(
InitializeData[] calldata _data
) external virtual {
Ownable gov = Ownable(address(_host.getGovernance()));
if (msg.sender != gov.owner()) revert SUPER_TOKEN_FACTORY_ONLY_GOVERNANCE_OWNER();
// once the list has been set, it cannot be reset
// @note this means that we must set the 0 address (Native Asset Super Token) when we call this the first time
if (_canonicalWrapperSuperTokens[address(0)] != address(0)) {
revert SUPER_TOKEN_FACTORY_ALREADY_EXISTS();
}
// initialize mapping
for (uint256 i = 0; i < _data.length; i++) {
_canonicalWrapperSuperTokens[_data[i].underlyingToken] = _data[i]
.superToken;
}
}
}
contract SuperTokenFactory is SuperTokenFactoryBase
{
/* WARNING: NEVER RE-ORDER VARIABLES! Including the base contracts.
Always double-check that new
variables are added APPEND-ONLY. Re-ordering variables can
permanently BREAK the deployed proxy contract. */
constructor(
ISuperfluid host,
ISuperToken superTokenLogic,
IConstantOutflowNFT constantOutflowNFTLogic,
IConstantInflowNFT constantInflowNFTLogic,
IPoolAdminNFT poolAdminNFTLogic,
IPoolMemberNFT poolMemberNFTLogic
)
SuperTokenFactoryBase(
host,
superTokenLogic,
constantOutflowNFTLogic,
constantInflowNFTLogic,
poolAdminNFTLogic,
poolMemberNFTLogic
)
// solhint-disable-next-line no-empty-blocks
{}
}