-
Notifications
You must be signed in to change notification settings - Fork 480
Expand file tree
/
Copy pathStrategyFactory.sol
More file actions
185 lines (162 loc) · 7.27 KB
/
Copy pathStrategyFactory.sol
File metadata and controls
185 lines (162 loc) · 7.27 KB
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
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;
import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
import "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol";
import "./StrategyFactoryStorage.sol";
import "./StrategyBase.sol";
import "./DurationVaultStrategy.sol";
import "../interfaces/IDurationVaultStrategy.sol";
import "../permissions/Pausable.sol";
/// @title Factory contract for deploying BeaconProxies of a Strategy contract implementation for arbitrary ERC20 tokens
/// and automatically adding them to the StrategyWhitelist in EigenLayer.
/// @author Layr Labs, Inc.
/// @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
/// @dev This may not be compatible with non-standard ERC20 tokens. Caution is warranted.
contract StrategyFactory is StrategyFactoryStorage, OwnableUpgradeable, Pausable {
uint8 internal constant PAUSED_NEW_STRATEGIES = 0;
/// @notice EigenLayer's StrategyManager contract
IStrategyManager public immutable strategyManager;
/// @notice Upgradeable beacon used for baseline strategies deployed by this contract.
IBeacon public immutable strategyBeacon;
/// @notice Upgradeable beacon used for duration vault strategies deployed by this contract.
IBeacon public immutable durationVaultBeacon;
/// @notice Since this contract is designed to be initializable, the constructor simply sets the immutable variables.
constructor(
IStrategyManager _strategyManager,
IPauserRegistry _pauserRegistry,
IBeacon _strategyBeacon,
IBeacon _durationVaultBeacon
) Pausable(_pauserRegistry) {
strategyManager = _strategyManager;
strategyBeacon = _strategyBeacon;
durationVaultBeacon = _durationVaultBeacon;
_disableInitializers();
}
function initialize(
address _initialOwner,
uint256 _initialPausedStatus
) public virtual initializer {
_transferOwnership(_initialOwner);
_setPausedStatus(_initialPausedStatus);
}
/// @notice Deploy a new StrategyBase contract for the ERC20 token, using a beacon proxy
/// @dev A strategy contract must not yet exist for the token.
/// @dev Immense caution is warranted for non-standard ERC20 tokens, particularly "reentrant" tokens
/// like those that conform to ERC777.
function deployNewStrategy(
IERC20 token
) external onlyWhenNotPaused(PAUSED_NEW_STRATEGIES) returns (IStrategy newStrategy) {
require(!isBlacklisted[token], BlacklistedToken());
require(deployedStrategies[token] == IStrategy(address(0)), StrategyAlreadyExists());
IStrategy strategy = IStrategy(
address(
new BeaconProxy(
address(strategyBeacon), abi.encodeWithSelector(StrategyBase.initialize.selector, token)
)
)
);
_setStrategyForToken(token, strategy);
IStrategy[] memory strategiesToWhitelist = new IStrategy[](1);
strategiesToWhitelist[0] = strategy;
strategyManager.addStrategiesToDepositWhitelist(strategiesToWhitelist);
return strategy;
}
/// @notice Owner-only function to prevent strategies from being created for given tokens.
/// @param tokens An array of token addresses to blacklist.
function blacklistTokens(
IERC20[] calldata tokens
) external onlyOwner {
IStrategy[] memory strategiesToRemove = new IStrategy[](tokens.length);
uint256 removeIdx = 0;
for (uint256 i; i < tokens.length; ++i) {
require(!isBlacklisted[tokens[i]], AlreadyBlacklisted());
isBlacklisted[tokens[i]] = true;
emit TokenBlacklisted(tokens[i]);
// If someone has already deployed a strategy for this token, add it
// to the list of strategies to remove from the StrategyManager whitelist
IStrategy deployed = deployedStrategies[tokens[i]];
if (deployed != IStrategy(address(0))) {
strategiesToRemove[removeIdx] = deployed;
removeIdx++;
}
}
// Manually adjust length to remove unused entries
// New length == removeIdx
assembly {
mstore(strategiesToRemove, removeIdx)
}
if (strategiesToRemove.length > 0) {
strategyManager.removeStrategiesFromDepositWhitelist(strategiesToRemove);
}
}
/// @notice Owner-only function to pass through a call to `StrategyManager.addStrategiesToDepositWhitelist`
function whitelistStrategies(
IStrategy[] calldata strategiesToWhitelist
) external onlyOwner {
strategyManager.addStrategiesToDepositWhitelist(strategiesToWhitelist);
}
/// @notice Deploys a new duration vault strategy backed by the configured beacon.
function deployDurationVaultStrategy(
IDurationVaultStrategy.VaultConfig calldata config
) external onlyWhenNotPaused(PAUSED_NEW_STRATEGIES) returns (IDurationVaultStrategy newVault) {
IERC20 underlyingToken = config.underlyingToken;
require(!isBlacklisted[underlyingToken], BlacklistedToken());
newVault = IDurationVaultStrategy(
address(
new BeaconProxy(
address(durationVaultBeacon),
abi.encodeWithSelector(DurationVaultStrategy.initialize.selector, config)
)
)
);
_registerDurationVault(underlyingToken, newVault);
IStrategy[] memory strategiesToWhitelist = new IStrategy[](1);
strategiesToWhitelist[0] = newVault;
strategyManager.addStrategiesToDepositWhitelist(strategiesToWhitelist);
_emitDurationVaultDeployed(newVault, underlyingToken, config);
}
/// @notice Owner-only function to pass through a call to `StrategyManager.removeStrategiesFromDepositWhitelist`
function removeStrategiesFromWhitelist(
IStrategy[] calldata strategiesToRemoveFromWhitelist
) external onlyOwner {
strategyManager.removeStrategiesFromDepositWhitelist(strategiesToRemoveFromWhitelist);
}
function _setStrategyForToken(
IERC20 token,
IStrategy strategy
) internal {
deployedStrategies[token] = strategy;
emit StrategySetForToken(token, strategy);
}
/// @inheritdoc IStrategyFactory
function getDurationVaults(
IERC20 token
) external view returns (IDurationVaultStrategy[] memory) {
// NOTE: Consider using the public durationVaultsByToken mapping directly
// for on-chain integrations to avoid potential OOG issues with large arrays.
return durationVaultsByToken[token];
}
function _registerDurationVault(
IERC20 token,
IDurationVaultStrategy vault
) internal {
durationVaultsByToken[token].push(vault);
}
function _emitDurationVaultDeployed(
IDurationVaultStrategy vault,
IERC20 underlyingToken,
IDurationVaultStrategy.VaultConfig calldata config
) internal {
emit DurationVaultDeployed(
vault,
underlyingToken,
config.vaultAdmin,
config.duration,
config.maxPerDeposit,
config.stakeCap,
config.metadataURI,
config.operatorSet.avs,
config.operatorSet.id
);
}
}