Skip to content

Commit c797b76

Browse files
authored
Merge pull request #14 from SetProtocol/brian/self-service-manager
Self-service manager contract system foundation, DelegatedManager, DelegatedManagerFactory, and BaseGlobalExtension.
2 parents 2ecd816 + fcc5163 commit c797b76

18 files changed

+3282
-4
lines changed
Lines changed: 387 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,387 @@
1+
/*
2+
Copyright 2022 Set Labs Inc.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
16+
SPDX-License-Identifier: Apache License, Version 2.0
17+
*/
18+
19+
pragma solidity 0.6.10;
20+
pragma experimental ABIEncoderV2;
21+
22+
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
23+
import { ISetToken } from "@setprotocol/set-protocol-v2/contracts/interfaces/ISetToken.sol";
24+
25+
import { AddressArrayUtils } from "../lib/AddressArrayUtils.sol";
26+
import { DelegatedManager } from "../manager/DelegatedManager.sol";
27+
import { IDelegatedManager } from "../interfaces/IDelegatedManager.sol";
28+
import { ISetTokenCreator } from "../interfaces/ISetTokenCreator.sol";
29+
30+
/**
31+
* @title DelegatedManagerFactory
32+
* @author Set Protocol
33+
*
34+
* Factory smart contract which gives asset managers the ability to:
35+
* > create a Set Token managed with a DelegatedManager contract
36+
* > create a DelegatedManager contract for an existing Set Token to migrate to
37+
* > initialize extensions and modules for SetTokens using the DelegatedManager system
38+
*/
39+
contract DelegatedManagerFactory {
40+
using AddressArrayUtils for address[];
41+
using Address for address;
42+
43+
/* ============ Structs ============ */
44+
45+
struct InitializeParams{
46+
address deployer;
47+
address owner;
48+
IDelegatedManager manager;
49+
bool isPending;
50+
}
51+
52+
/* ============ Events ============ */
53+
54+
/**
55+
* @dev Emitted on DelegatedManager creation
56+
* @param _setToken Instance of the SetToken being created
57+
* @param _manager Address of the DelegatedManager
58+
* @param _deployer Address of the deployer
59+
*/
60+
event DelegatedManagerCreated(
61+
ISetToken indexed _setToken,
62+
DelegatedManager indexed _manager,
63+
address _deployer
64+
);
65+
66+
/**
67+
* @dev Emitted on DelegatedManager initialization
68+
* @param _setToken Instance of the SetToken being initialized
69+
* @param _manager Address of the DelegatedManager owner
70+
*/
71+
event DelegatedManagerInitialized(
72+
ISetToken indexed _setToken,
73+
IDelegatedManager indexed _manager
74+
);
75+
76+
/* ============ State Variables ============ */
77+
78+
// SetTokenFactory address
79+
ISetTokenCreator public setTokenFactory;
80+
81+
// Mapping which stores manager creation metadata between creation and initialization steps
82+
mapping(ISetToken=>InitializeParams) public initializeState;
83+
84+
/* ============ Constructor ============ */
85+
86+
/**
87+
* @dev Sets setTokenFactory address.
88+
* @param _setTokenFactory Address of SetTokenFactory protocol contract
89+
*/
90+
constructor(ISetTokenCreator _setTokenFactory) public {
91+
setTokenFactory = _setTokenFactory;
92+
}
93+
94+
/* ============ External Functions ============ */
95+
96+
/**
97+
* ANYONE CAN CALL: Deploys a new SetToken and DelegatedManager. Sets some temporary metadata about
98+
* the deployment which will be read during a subsequent intialization step which wires everything
99+
* together.
100+
*
101+
* @param _components List of addresses of components for initial Positions
102+
* @param _units List of units. Each unit is the # of components per 10^18 of a SetToken
103+
* @param _name Name of the SetToken
104+
* @param _symbol Symbol of the SetToken
105+
* @param _owner Address to set as the DelegateManager's `owner` role
106+
* @param _methodologist Address to set as the DelegateManager's methodologist role
107+
* @param _modules List of modules to enable. All modules must be approved by the Controller
108+
* @param _operators List of operators authorized for the DelegateManager
109+
* @param _assets List of assets DelegateManager can trade. When empty, asset allow list is not enforced
110+
* @param _extensions List of extensions authorized for the DelegateManager
111+
*
112+
* @return (ISetToken, address) The created SetToken and DelegatedManager addresses, respectively
113+
*/
114+
function createSetAndManager(
115+
address[] memory _components,
116+
int256[] memory _units,
117+
string memory _name,
118+
string memory _symbol,
119+
address _owner,
120+
address _methodologist,
121+
address[] memory _modules,
122+
address[] memory _operators,
123+
address[] memory _assets,
124+
address[] memory _extensions
125+
)
126+
external
127+
returns (ISetToken, address)
128+
{
129+
_validateManagerParameters(_components, _extensions, _assets);
130+
131+
ISetToken setToken = _deploySet(
132+
_components,
133+
_units,
134+
_modules,
135+
_name,
136+
_symbol
137+
);
138+
139+
DelegatedManager manager = _deployManager(
140+
setToken,
141+
_methodologist,
142+
_extensions,
143+
_operators,
144+
_assets
145+
);
146+
147+
_setInitializationState(setToken, address(manager), _owner);
148+
149+
return (setToken, address(manager));
150+
}
151+
152+
/**
153+
* ONLY SETTOKEN MANAGER: Deploys a DelegatedManager and sets some temporary metadata about the
154+
* deployment which will be read during a subsequent intialization step which wires everything together.
155+
* This method is used when migrating an existing SetToken to the DelegatedManager system.
156+
*
157+
* (Note: This flow should work well for SetTokens managed by an EOA. However, existing
158+
* contract-managed Sets may need to have their ownership temporarily transferred to an EOA when
159+
* migrating. We don't anticipate high demand for this migration case though.)
160+
*
161+
* @param _setToken Instance of SetToken to migrate to the DelegatedManager system
162+
* @param _owner Address to set as the DelegateManager's `owner` role
163+
* @param _methodologist Address to set as the DelegateManager's methodologist role
164+
* @param _operators List of operators authorized for the DelegateManager
165+
* @param _assets List of assets DelegateManager can trade. When empty, asset allow list is not enforced
166+
* @param _extensions List of extensions authorized for the DelegateManager
167+
*
168+
* @return (address) Address of the created DelegatedManager
169+
*/
170+
function createManager(
171+
ISetToken _setToken,
172+
address _owner,
173+
address _methodologist,
174+
address[] memory _operators,
175+
address[] memory _assets,
176+
address[] memory _extensions
177+
)
178+
external
179+
returns (address)
180+
{
181+
require(msg.sender == _setToken.manager(), "Must be manager");
182+
183+
_validateManagerParameters(_setToken.getComponents(), _extensions, _assets);
184+
185+
DelegatedManager manager = _deployManager(
186+
_setToken,
187+
_methodologist,
188+
_extensions,
189+
_operators,
190+
_assets
191+
);
192+
193+
_setInitializationState(_setToken, address(manager), _owner);
194+
195+
return address(manager);
196+
}
197+
198+
/**
199+
* ONLY DEPLOYER: Wires SetToken, DelegatedManager, global manager extensions, and modules together
200+
* into a functioning package.
201+
*
202+
* NOTE: When migrating to this manager system from an existing SetToken, the SetToken's current manager address
203+
* must be reset to point at the newly deployed DelegatedManager contract in a separate, final transaction.
204+
*
205+
*
206+
* @param _setToken Instance of the SetToken
207+
* @param _ownerFeeSplit Percent of fees in precise units (10^16 = 1%) sent to operator, rest to methodologist
208+
* @param _ownerFeeRecipient Address which receives owner's share of fees when they're distributed
209+
* @param _initializeTargets List of addresses of any extensions or modules which need to be initialized
210+
* @param _initializeBytecode List of bytecode encoded calls to relevant target's initialize function
211+
*/
212+
function initialize(
213+
ISetToken _setToken,
214+
uint256 _ownerFeeSplit,
215+
address _ownerFeeRecipient,
216+
address[] memory _initializeTargets,
217+
bytes[] memory _initializeBytecode
218+
)
219+
external
220+
{
221+
require(initializeState[_setToken].isPending, "Manager must be awaiting initialization");
222+
require(msg.sender == initializeState[_setToken].deployer, "Only deployer can initialize manager");
223+
_initializeTargets.validatePairsWithArray(_initializeBytecode);
224+
225+
IDelegatedManager manager = initializeState[_setToken].manager;
226+
manager.updateOwnerFeeSplit(_ownerFeeSplit);
227+
manager.updateOwnerFeeRecipient(_ownerFeeRecipient);
228+
229+
for (uint256 i = 0; i < _initializeTargets.length; i++) {
230+
// Because we validate uniqueness of _initializeTargets only one transaction can be sent to each module or extension during this
231+
// transaction. Due to this no modules/extension can be used for any SetToken transactions other than initializing these contracts
232+
_initializeTargets[i].functionCallWithValue(_initializeBytecode[i], 0);
233+
}
234+
235+
// If the SetToken was factory-deployed & factory is its current `manager`, transfer
236+
// managership to the new DelegatedManager
237+
if (_setToken.manager() == address(this)) {
238+
_setToken.setManager(address(manager));
239+
}
240+
241+
manager.transferOwnership(initializeState[_setToken].owner);
242+
243+
delete initializeState[_setToken];
244+
245+
emit DelegatedManagerInitialized(_setToken, manager);
246+
}
247+
248+
/* ============ Internal Functions ============ */
249+
250+
/**
251+
* Deploys a SetToken, setting this factory as its manager temporarily, pending initialization.
252+
* Managership is transferred to a newly created DelegatedManager during `initialize`
253+
*
254+
* @param _components List of addresses of components for initial Positions
255+
* @param _units List of units. Each unit is the # of components per 10^18 of a SetToken
256+
* @param _modules List of modules to enable. All modules must be approved by the Controller
257+
* @param _name Name of the SetToken
258+
* @param _symbol Symbol of the SetToken
259+
*
260+
* @return Address of created SetToken;
261+
*/
262+
function _deploySet(
263+
address[] memory _components,
264+
int256[] memory _units,
265+
address[] memory _modules,
266+
string memory _name,
267+
string memory _symbol
268+
)
269+
internal
270+
returns (ISetToken)
271+
{
272+
address setToken = setTokenFactory.create(
273+
_components,
274+
_units,
275+
_modules,
276+
address(this),
277+
_name,
278+
_symbol
279+
);
280+
281+
return ISetToken(setToken);
282+
}
283+
284+
/**
285+
* Deploys a DelegatedManager
286+
*
287+
* @param _setToken Instance of SetToken to migrate to the DelegatedManager system
288+
* @param _methodologist Address to set as the DelegateManager's methodologist role
289+
* @param _extensions List of extensions authorized for the DelegateManager
290+
* @param _operators List of operators authorized for the DelegateManager
291+
* @param _assets List of assets DelegateManager can trade. When empty, asset allow list is not enforced
292+
*
293+
* @return Address of created DelegatedManager
294+
*/
295+
function _deployManager(
296+
ISetToken _setToken,
297+
address _methodologist,
298+
address[] memory _extensions,
299+
address[] memory _operators,
300+
address[] memory _assets
301+
)
302+
internal
303+
returns (DelegatedManager)
304+
{
305+
// If asset array is empty, manager's useAssetAllowList will be set to false
306+
// and the asset allow list is not enforced
307+
bool useAssetAllowlist = _assets.length > 0;
308+
309+
DelegatedManager newManager = new DelegatedManager(
310+
_setToken,
311+
address(this),
312+
_methodologist,
313+
_extensions,
314+
_operators,
315+
_assets,
316+
useAssetAllowlist
317+
);
318+
319+
emit DelegatedManagerCreated(
320+
_setToken,
321+
newManager,
322+
msg.sender
323+
);
324+
325+
return newManager;
326+
}
327+
328+
/**
329+
* Stores temporary creation metadata during the contract creation step. Data is retrieved, read and
330+
* finally deleted during `initialize`.
331+
*
332+
* @param _setToken Instance of SetToken
333+
* @param _manager Address of DelegatedManager created for SetToken
334+
* @param _owner Address that will be given the `owner` DelegatedManager's role on initialization
335+
*/
336+
function _setInitializationState(
337+
ISetToken _setToken,
338+
address _manager,
339+
address _owner
340+
) internal {
341+
initializeState[_setToken] = InitializeParams({
342+
deployer: msg.sender,
343+
owner: _owner,
344+
manager: IDelegatedManager(_manager),
345+
isPending: true
346+
});
347+
}
348+
349+
/**
350+
* Validates that all components currently held by the Set are on the asset allow list. Validate that the manager is
351+
* deployed with at least one extension in the PENDING state.
352+
*
353+
* @param _components List of addresses of components for initial/current Set positions
354+
* @param _extensions List of extensions authorized for the DelegateManager
355+
* @param _assets List of assets DelegateManager can trade. When empty, asset allow list is not enforced
356+
*/
357+
function _validateManagerParameters(
358+
address[] memory _components,
359+
address[] memory _extensions,
360+
address[] memory _assets
361+
)
362+
internal
363+
pure
364+
{
365+
require(_extensions.length > 0, "Must have at least 1 extension");
366+
367+
if (_assets.length != 0) {
368+
_validateComponentsIncludedInAssetsList(_components, _assets);
369+
}
370+
}
371+
372+
/**
373+
* Validates that all SetToken components are included in the assets whitelist. This prevents the
374+
* DelegatedManager from being initialized with some components in an untrade-able state.
375+
*
376+
* @param _components List of addresses of components for initial Positions
377+
* @param _assets List of assets DelegateManager can trade.
378+
*/
379+
function _validateComponentsIncludedInAssetsList(
380+
address[] memory _components,
381+
address[] memory _assets
382+
) internal pure {
383+
for (uint256 i = 0; i < _components.length; i++) {
384+
require(_assets.contains(_components[i]), "Asset list must include all components");
385+
}
386+
}
387+
}

0 commit comments

Comments
 (0)