-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathPoolFactory.sol
294 lines (259 loc) · 9.4 KB
/
PoolFactory.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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.26;
import {Pool} from "./Pool.sol";
import {BondToken} from "./BondToken.sol";
import {Distributor} from "./Distributor.sol";
import {LeverageToken} from "./LeverageToken.sol";
import {Create3} from "@create3/contracts/Create3.sol";
import {Deployer} from "./utils/Deployer.sol";
import {ERC20Extensions} from "./lib/ERC20Extensions.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
/**
* @title PoolFactory
* @dev This contract is responsible for creating and managing pools.
* It inherits from various OpenZeppelin upgradeable contracts for enhanced functionality and security.
*/
contract PoolFactory is Initializable, AccessControlUpgradeable, UUPSUpgradeable, PausableUpgradeable {
using SafeERC20 for IERC20;
using ERC20Extensions for IERC20;
bytes32 public constant GOV_ROLE = keccak256("GOV_ROLE");
bytes32 public constant POOL_ROLE = keccak256("POOL_ROLE");
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant SECURITY_COUNCIL_ROLE = keccak256("SECURITY_COUNCIL_ROLE");
struct PoolParams {
uint256 fee;
address reserveToken;
address couponToken;
uint256 distributionPeriod;
uint256 sharesPerToken;
address feeBeneficiary;
}
/// @dev Array to store addresses of created pools
address[] public pools;
/// @dev Address of the governance contract
address public governance;
/// @dev Address of the OracleFeeds contract
address public oracleFeeds;
/// @dev Instance of the Deployer contract
Deployer private deployer;
/// @dev Address of the UpgradeableBeacon for Pool
address public poolBeacon;
/// @dev Address of the UpgradeableBeacon for BondToken
address public bondBeacon;
/// @dev Address of the UpgradeableBeacon for LeverageToken
address public leverageBeacon;
/// @dev Address of the UpgradeableBeacon for Distributor
address public distributorBeacon;
/// @dev Mapping to store distributor addresses for each pool
mapping(address => address) public distributors;
/// @dev Error thrown when bond amount is zero
error ZeroDebtAmount();
/// @dev Error thrown when reserve amount is zero
error ZeroReserveAmount();
/// @dev Error thrown when leverage amount is zero
error ZeroLeverageAmount();
/**
* @dev Emitted when a new pool is created
* @param pool Address of the newly created pool
* @param reserveAmount Amount of reserve tokens
* @param bondAmount Amount of bond tokens
* @param leverageAmount Amount of leverage tokens
*/
event PoolCreated(address pool, uint256 reserveAmount, uint256 bondAmount, uint256 leverageAmount);
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/**
* @dev Initializes the contract with the governance address and sets up roles.
* This function is called once during deployment or upgrading to initialize state variables.
* @param _governance Address of the governance account that will have the GOV_ROLE.
* @param _deployer Address of the Deployer contract.
* @param _oracleFeeds Address of the OracleFeeds contract.
* @param _poolImplementation Address of the Pool implementation contract.
* @param _bondImplementation Address of the BondToken implementation contract.
* @param _leverageImplementation Address of the LeverageToken implementation contract.
* @param _distributorImplementation Address of the Distributor implementation contract.
*/
function initialize(
address _governance,
address _deployer,
address _oracleFeeds,
address _poolImplementation,
address _bondImplementation,
address _leverageImplementation,
address _distributorImplementation
) initializer public {
__UUPSUpgradeable_init();
__Pausable_init();
deployer = Deployer(_deployer);
governance = _governance;
oracleFeeds = _oracleFeeds;
_grantRole(GOV_ROLE, _governance);
// Stores beacon implementation addresses
poolBeacon = _poolImplementation;
bondBeacon = _bondImplementation;
leverageBeacon = _leverageImplementation;
distributorBeacon = _distributorImplementation;
}
/**
* @dev Creates a new pool with the given parameters
* @param params Struct containing pool parameters
* @param reserveAmount Amount of reserve tokens to seed the pool
* @param bondAmount Amount of bond tokens to mint
* @param leverageAmount Amount of leverage tokens to mint
* @return Address of the newly created pool
*/
function createPool(
PoolParams calldata params,
uint256 reserveAmount,
uint256 bondAmount,
uint256 leverageAmount,
string memory bondName,
string memory bondSymbol,
string memory leverageName,
string memory leverageSymbol,
bool pauseOnCreation
) external whenNotPaused() onlyRole(POOL_ROLE) returns (address) {
if (reserveAmount == 0) {
revert ZeroReserveAmount();
}
if (bondAmount == 0) {
revert ZeroDebtAmount();
}
if (leverageAmount == 0) {
revert ZeroLeverageAmount();
}
// Deploy Bond token
BondToken bondToken = BondToken(deployer.deployBondToken(
bondBeacon,
bondName,
bondSymbol,
address(this),
address(this),
address(this),
params.sharesPerToken
));
// Deploy Leverage token
LeverageToken lToken = LeverageToken(deployer.deployLeverageToken(
leverageBeacon,
leverageName,
leverageSymbol,
address(this),
address(this),
address(this)
));
// Deploy pool contract as a BeaconProxy
bytes memory initData = abi.encodeCall(
Pool.initialize,
(
address(this),
params.fee,
params.reserveToken,
address(bondToken),
address(lToken),
params.couponToken,
params.sharesPerToken,
params.distributionPeriod,
params.feeBeneficiary,
oracleFeeds,
pauseOnCreation
)
);
address pool = Create3.create3(
keccak256(
abi.encodePacked(
params.reserveToken,
params.couponToken,
bondToken.symbol(),
lToken.symbol()
)
),
abi.encodePacked(
type(BeaconProxy).creationCode,
abi.encode(poolBeacon, initData)
)
);
// Deploy Distributor contract
Distributor distributor = Distributor(deployer.deployDistributor(
distributorBeacon,
pool,
address(this)
));
distributors[pool] = address(distributor);
bondToken.grantRole(MINTER_ROLE, pool);
lToken.grantRole(MINTER_ROLE, pool);
bondToken.grantRole(bondToken.DISTRIBUTOR_ROLE(), pool);
bondToken.grantRole(bondToken.DISTRIBUTOR_ROLE(), address(distributor));
// set token governance
bondToken.grantRole(GOV_ROLE, governance);
lToken.grantRole(GOV_ROLE, governance);
// renounce governance from factory
bondToken.revokeRole(GOV_ROLE, address(this));
lToken.revokeRole(GOV_ROLE, address(this));
pools.push(pool);
emit PoolCreated(pool, reserveAmount, bondAmount, leverageAmount);
// Send seed reserves to pool
IERC20(params.reserveToken).safeTransferFrom(msg.sender, pool, reserveAmount);
// Mint seed amounts
bondToken.mint(msg.sender, bondAmount);
lToken.mint(msg.sender, leverageAmount);
// Revoke minter role from factory
bondToken.revokeRole(MINTER_ROLE, address(this));
lToken.revokeRole(MINTER_ROLE, address(this));
return pool;
}
/**
* @dev Returns the number of pools created.
* @return The length of the pools array.
*/
function poolsLength() external view returns (uint256) {
return pools.length;
}
/**
* @dev Grants `role` to `account`.
* If `account` had not been already granted `role`, emits a {RoleGranted} event.
* @param role The role to grant
* @param account The account to grant the role to
*/
function grantRole(bytes32 role, address account) public virtual override onlyRole(GOV_ROLE) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
* If `account` had been granted `role`, emits a {RoleRevoked} event.
* @param role The role to revoke
* @param account The account to revoke the role from
*/
function revokeRole(bytes32 role, address account) public virtual override onlyRole(GOV_ROLE) {
_revokeRole(role, account);
}
/**
* @dev Pauses contract. Reverts any interaction except upgrade.
*/
function pause() external onlyRole(SECURITY_COUNCIL_ROLE) {
_pause();
}
/**
* @dev Unpauses contract.
*/
function unpause() external onlyRole(SECURITY_COUNCIL_ROLE) {
_unpause();
}
/**
* @dev Authorizes an upgrade to a new implementation.
* Can only be called by the owner of the contract.
* @param newImplementation Address of the new implementation
*/
function _authorizeUpgrade(address newImplementation)
internal
onlyRole(GOV_ROLE)
override
{}
}