• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

delvtech / hyperdrive / 9927927593

14 Jul 2024 12:26PM UTC coverage: 92.277%. First build
9927927593

Pull #1081

github

web-flow
Merge e5af82277 into 683f2f7cd
Pull Request #1081: Added an Aave integration

120 of 142 new or added lines in 33 files covered. (84.51%)

2115 of 2292 relevant lines covered (92.28%)

376654.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.68
/contracts/src/deployers/HyperdriveDeployerCoordinator.sol
1
// SPDX-License-Identifier: Apache-2.0
2
pragma solidity 0.8.20;
3

4
import { IHyperdrive } from "../interfaces/IHyperdrive.sol";
5
import { IHyperdriveCoreDeployer } from "../interfaces/IHyperdriveCoreDeployer.sol";
6
import { IHyperdriveDeployerCoordinator } from "../interfaces/IHyperdriveDeployerCoordinator.sol";
7
import { IHyperdriveTargetDeployer } from "../interfaces/IHyperdriveTargetDeployer.sol";
8
import { VERSION, NUM_TARGETS } from "../libraries/Constants.sol";
9
import { ONE } from "../libraries/FixedPointMath.sol";
10

11
/// @author DELV
12
/// @title HyperdriveDeployerCoordinator
13
/// @notice This Hyperdrive deployer coordinates the process of deploying the
14
///         Hyperdrive system utilizing several child deployers.
15
/// @dev We use multiple deployers to avoid the maximum code size.
16
/// @custom:disclaimer The language used in this code is for coding convenience
17
///                    only, and is not intended to, and does not, have any
18
///                    particular legal or regulatory significance.
19
abstract contract HyperdriveDeployerCoordinator is
20
    IHyperdriveDeployerCoordinator
21
{
22
    struct Deployment {
23
        /// @dev The hash of the config used in this deployment. This is used to
24
        ///      ensure that the config is the same across all deployments in
25
        ///      the batch.
26
        bytes32 configHash;
27
        /// @dev The hash of the extra data passed to the child deployers. This
28
        ///      is used to ensure that the extra data is the same across all
29
        ///      deployments in the batch.
30
        bytes32 extraDataHash;
31
        /// @dev The initial share price used in the first part of this
32
        ///      deployment. This is used to ensure that the initial share price
33
        ///      is the same across all deployments in the batch.
34
        uint256 initialSharePrice;
35
        /// @dev The address of the Hyperdrive entrypoint.
36
        address hyperdrive;
37
        /// @dev The address of the HyperdriveTarget0 contract.
38
        address target0;
39
        /// @dev The address of the HyperdriveTarget1 contract.
40
        address target1;
41
        /// @dev The address of the HyperdriveTarget2 contract.
42
        address target2;
43
        /// @dev The address of the HyperdriveTarget3 contract.
44
        address target3;
45
        /// @dev The address of the HyperdriveTarget4 contract.
46
        address target4;
47
    }
48

49
    /// @notice The factory that this deployer will be registered with.
50
    address public immutable factory;
51

52
    /// @notice The contract used to deploy new instances of Hyperdrive.
53
    address public immutable coreDeployer;
54

55
    /// @notice The contract used to deploy new instances of HyperdriveTarget0.
56
    address public immutable target0Deployer;
57

58
    /// @notice The contract used to deploy new instances of HyperdriveTarget1.
59
    address public immutable target1Deployer;
60

61
    /// @notice The contract used to deploy new instances of HyperdriveTarget2.
62
    address public immutable target2Deployer;
63

64
    /// @notice The contract used to deploy new instances of HyperdriveTarget3.
65
    address public immutable target3Deployer;
66

67
    /// @notice The contract used to deploy new instances of HyperdriveTarget4.
68
    address public immutable target4Deployer;
69

70
    /// @notice The deployer coordinator's name.
71
    string public override name;
72

73
    /// @notice A mapping from deployment ID to deployment.
74
    mapping(bytes32 => Deployment) internal _deployments;
75

76
    /// @notice Instantiates the deployer coordinator.
77
    /// @param _name The deployer coordinator's name.
78
    /// @param _factory The factory that this deployer will be registered with.
79
    /// @param _coreDeployer The core deployer.
80
    /// @param _target0Deployer The target0 deployer.
81
    /// @param _target1Deployer The target1 deployer.
82
    /// @param _target2Deployer The target2 deployer.
83
    /// @param _target3Deployer The target3 deployer.
84
    /// @param _target4Deployer The target4 deployer.
85
    constructor(
86
        string memory _name,
87
        address _factory,
88
        address _coreDeployer,
89
        address _target0Deployer,
90
        address _target1Deployer,
91
        address _target2Deployer,
92
        address _target3Deployer,
93
        address _target4Deployer
94
    ) {
95
        name = _name;
×
96
        factory = _factory;
×
97
        coreDeployer = _coreDeployer;
×
98
        target0Deployer = _target0Deployer;
×
99
        target1Deployer = _target1Deployer;
×
100
        target2Deployer = _target2Deployer;
×
101
        target3Deployer = _target3Deployer;
×
NEW
102
        target4Deployer = _target4Deployer;
×
103
    }
104

105
    /// @dev Ensures that the contract is being called by the associated
106
    ///      factory.
107
    modifier onlyFactory() {
108
        if (msg.sender != factory) {
1,763✔
109
            revert IHyperdriveDeployerCoordinator.SenderIsNotFactory();
3✔
110
        }
111
        _;
112
    }
113

114
    /// @notice Gets the deployer coordinator's kind.
115
    /// @notice The deployer coordinator's kind.
116
    function kind() external pure virtual returns (string memory);
117

118
    /// @notice Returns the deployer coordinator's version.
119
    /// @notice The deployer coordinator's version.
120
    function version() external pure returns (string memory) {
121
        return VERSION;
6✔
122
    }
123

124
    /// @notice Deploys a Hyperdrive instance with the given parameters.
125
    /// @dev This can only be deployed by the associated factory.
126
    /// @param _deploymentId The ID of the deployment.
127
    /// @param __name The name of the Hyperdrive pool.
128
    /// @param _deployConfig The deploy configuration of the Hyperdrive pool.
129
    /// @param _extraData The extra data that contains the pool and sweep targets.
130
    /// @param _salt The create2 salt used to deploy Hyperdrive.
131
    /// @return The address of the newly deployed Hyperdrive instance.
132
    function deployHyperdrive(
133
        bytes32 _deploymentId,
134
        string memory __name,
135
        IHyperdrive.PoolDeployConfig memory _deployConfig,
136
        bytes memory _extraData,
137
        bytes32 _salt
138
    ) external onlyFactory returns (address) {
139
        // Ensure that the Hyperdrive entrypoint has not already been deployed.
140
        Deployment memory deployment = _deployments[_deploymentId];
1,793✔
141
        if (deployment.hyperdrive != address(0)) {
1,793✔
142
            revert IHyperdriveDeployerCoordinator.HyperdriveAlreadyDeployed();
3✔
143
        }
144

145
        // Ensure that the deployment is not a fresh deployment. We can check
146
        // this by ensuring that the config hash is set.
147
        if (deployment.configHash == bytes32(0)) {
1,790✔
148
            revert IHyperdriveDeployerCoordinator.DeploymentDoesNotExist();
3✔
149
        }
150

151
        // Ensure that all of the targets have been deployed.
152
        if (
153
            deployment.target0 == address(0) ||
1,787✔
154
            deployment.target1 == address(0) ||
1,787✔
155
            deployment.target2 == address(0) ||
1,784✔
156
            deployment.target3 == address(0) ||
1,781✔
157
            deployment.target4 == address(0)
1,778✔
158
        ) {
159
            revert IHyperdriveDeployerCoordinator.IncompleteDeployment();
12✔
160
        }
161

162
        // Ensure that the provided config matches the config hash.
163
        if (keccak256(abi.encode(_deployConfig)) != deployment.configHash) {
1,775✔
164
            revert IHyperdriveDeployerCoordinator.MismatchedConfig();
3✔
165
        }
166

167
        // Ensure that the provided extra data matches the extra data hash.
168
        if (keccak256(_extraData) != deployment.extraDataHash) {
1,772✔
169
            revert IHyperdriveDeployerCoordinator.MismatchedExtraData();
3✔
170
        }
171

172
        // Check the pool configuration to ensure that it's a valid
173
        // configuration for this instance. This was already done when deploying
174
        // target0, but we check again as a precaution in case the check relies
175
        // on state that can change.
176
        _checkPoolConfig(_deployConfig);
1,769✔
177

178
        // Convert the deploy config into the pool config and set the initial
179
        // vault share price.
180
        IHyperdrive.PoolConfig memory config = _copyPoolConfig(_deployConfig);
1,766✔
181
        config.initialVaultSharePrice = deployment.initialSharePrice;
1,766✔
182

183
        // Deploy the Hyperdrive instance and add it to the deployment struct.
184
        bytes32 deploymentId = _deploymentId; // Avoid stack too deep error
1,766✔
185
        bytes32 salt = _salt; // Avoid stack too deep error
1,766✔
186
        address hyperdrive = IHyperdriveCoreDeployer(coreDeployer)
1,766✔
187
            .deployHyperdrive(
188
                __name,
189
                config,
190
                _extraData,
191
                deployment.target0,
192
                deployment.target1,
193
                deployment.target2,
194
                deployment.target3,
195
                deployment.target4,
196
                // NOTE: We hash the deployment ID with the salt to prevent the
197
                // front-running of deployments.
198
                keccak256(abi.encode(deploymentId, salt))
199
            );
200
        _deployments[_deploymentId].hyperdrive = hyperdrive;
1,766✔
201

202
        return hyperdrive;
1,766✔
203
    }
204

205
    /// @notice Deploys a Hyperdrive target instance with the given parameters.
206
    /// @dev This can only be deployed by the associated factory.
207
    /// @dev As a convention, target0 must be deployed first. After this, the
208
    ///      targets can be deployed in any order.
209
    /// @param _deploymentId The ID of the deployment.
210
    /// @param _deployConfig The deploy configuration of the Hyperdrive pool.
211
    /// @param _extraData The extra data that contains the pool and sweep targets.
212
    /// @param _targetIndex The index of the target to deploy.
213
    /// @param _salt The create2 salt used to deploy the target.
214
    /// @return target The address of the newly deployed target instance.
215
    function deployTarget(
216
        bytes32 _deploymentId,
217
        IHyperdrive.PoolDeployConfig memory _deployConfig,
218
        bytes memory _extraData,
219
        uint256 _targetIndex,
220
        bytes32 _salt
221
    ) external onlyFactory returns (address target) {
222
        // If the target index is 0, then we're deploying the target0 instance.
223
        // By convention, this target must be deployed first, and as part of the
224
        // deployment of target0, we will register the deployment in the state.
225
        Deployment storage deployment = _deployments[_deploymentId];
9,064✔
226
        if (_targetIndex == 0) {
9,064✔
227
            // Ensure that the deployment is a fresh deployment. We can check
228
            // this by ensuring that the config hash is not set.
229
            if (deployment.configHash != bytes32(0)) {
1,853✔
230
                revert IHyperdriveDeployerCoordinator.DeploymentAlreadyExists();
3✔
231
            }
232

233
            // Check the pool configuration to ensure that it's a valid
234
            // configuration for this instance.
235
            _checkPoolConfig(_deployConfig);
1,850✔
236

237
            // Get the initial share price and the hashes of the config and extra
238
            // data.
239
            uint256 initialSharePrice = _getInitialVaultSharePrice(
1,823✔
240
                _deployConfig,
241
                _extraData
242
            );
243
            bytes32 configHash_ = keccak256(abi.encode(_deployConfig));
1,823✔
244
            bytes32 extraDataHash = keccak256(_extraData);
1,823✔
245

246
            // Convert the deploy config into the pool config and set the initial
247
            // vault share price.
248
            IHyperdrive.PoolConfig memory config_ = _copyPoolConfig(
1,823✔
249
                _deployConfig
250
            );
251
            config_.initialVaultSharePrice = initialSharePrice;
1,823✔
252

253
            // Deploy the target0 contract.
254
            target = IHyperdriveTargetDeployer(target0Deployer).deployTarget(
1,823✔
255
                config_,
256
                _extraData,
257
                // NOTE: We hash the deployment ID with the salt to prevent the
258
                // front-running of deployments.
259
                keccak256(abi.encode(_deploymentId, _salt))
260
            );
261

262
            // Store the deployment.
263
            deployment.configHash = configHash_;
1,823✔
264
            deployment.extraDataHash = extraDataHash;
1,823✔
265
            deployment.initialSharePrice = initialSharePrice;
1,823✔
266
            deployment.target0 = target;
1,823✔
267

268
            return target;
1,823✔
269
        }
270

271
        // Ensure that the deployment is not a fresh deployment. We can check
272
        // this by ensuring that the config hash is set.
273
        bytes32 configHash = _deployments[_deploymentId].configHash;
7,211✔
274
        if (configHash == bytes32(0)) {
7,211✔
275
            revert IHyperdriveDeployerCoordinator.DeploymentDoesNotExist();
3✔
276
        }
277

278
        // Ensure that the provided config matches the config hash.
279
        if (keccak256(abi.encode(_deployConfig)) != configHash) {
7,208✔
280
            revert IHyperdriveDeployerCoordinator.MismatchedConfig();
3✔
281
        }
282

283
        // Ensure that the provided extra data matches the extra data hash.
284
        if (keccak256(_extraData) != deployment.extraDataHash) {
7,205✔
285
            revert IHyperdriveDeployerCoordinator.MismatchedExtraData();
3✔
286
        }
287

288
        // Check the pool configuration to ensure that it's a valid
289
        // configuration for this instance. This was already done when deploying
290
        // target0, but we check again as a precaution in case the check relies
291
        // on state that can change.
292
        _checkPoolConfig(_deployConfig);
7,202✔
293

294
        // Convert the deploy config into the pool config and set the initial
295
        // vault share price.
296
        IHyperdrive.PoolConfig memory config = _copyPoolConfig(_deployConfig);
7,199✔
297
        config.initialVaultSharePrice = deployment.initialSharePrice;
7,199✔
298

299
        // If the target index is greater than 0, then we're deploying one of
300
        // the other target instances. We don't allow targets to be deployed
301
        // more than once, and their addresses are stored in the deployment
302
        // state.
303
        if (_targetIndex == 1) {
7,199✔
304
            if (deployment.target1 != address(0)) {
1,799✔
305
                revert IHyperdriveDeployerCoordinator.TargetAlreadyDeployed();
3✔
306
            }
307
            target = IHyperdriveTargetDeployer(target1Deployer).deployTarget(
1,796✔
308
                config,
309
                _extraData,
310
                keccak256(abi.encode(msg.sender, _deploymentId, _salt))
311
            );
312
            deployment.target1 = target;
1,796✔
313
        } else if (_targetIndex == 2) {
5,400✔
314
            if (deployment.target2 != address(0)) {
1,799✔
315
                revert IHyperdriveDeployerCoordinator.TargetAlreadyDeployed();
3✔
316
            }
317
            target = IHyperdriveTargetDeployer(target2Deployer).deployTarget(
1,796✔
318
                config,
319
                _extraData,
320
                keccak256(abi.encode(msg.sender, _deploymentId, _salt))
321
            );
322
            deployment.target2 = target;
1,796✔
323
        } else if (_targetIndex == 3) {
3,601✔
324
            if (deployment.target3 != address(0)) {
1,799✔
325
                revert IHyperdriveDeployerCoordinator.TargetAlreadyDeployed();
3✔
326
            }
327
            target = IHyperdriveTargetDeployer(target3Deployer).deployTarget(
1,796✔
328
                config,
329
                _extraData,
330
                keccak256(abi.encode(msg.sender, _deploymentId, _salt))
331
            );
332
            deployment.target3 = target;
1,796✔
333
        } else if (_targetIndex == 4) {
1,802✔
334
            if (deployment.target4 != address(0)) {
1,799✔
335
                revert IHyperdriveDeployerCoordinator.TargetAlreadyDeployed();
3✔
336
            }
337
            target = IHyperdriveTargetDeployer(target4Deployer).deployTarget(
1,796✔
338
                config,
339
                _extraData,
340
                keccak256(abi.encode(msg.sender, _deploymentId, _salt))
341
            );
342
            deployment.target4 = target;
1,796✔
343
        } else {
344
            revert IHyperdriveDeployerCoordinator.InvalidTargetIndex();
3✔
345
        }
346

347
        return target;
7,184✔
348
    }
349

350
    /// @notice Initializes a pool that was deployed by this coordinator.
351
    /// @dev This can only be deployed by the associated factory.
352
    /// @dev This function utilizes several helper functions that provide
353
    ///      flexibility to implementations.
354
    /// @param _deploymentId The ID of the deployment.
355
    /// @param _lp The LP that is initializing the pool.
356
    /// @param _contribution The amount of capital to supply. The units of this
357
    ///        quantity are either base or vault shares, depending on the value
358
    ///        of `_options.asBase`.
359
    /// @param _apr The target APR.
360
    /// @param _options The options that configure how the initialization is
361
    ///        settled.
362
    /// @return lpShares The initial number of LP shares created.
363
    function initialize(
364
        bytes32 _deploymentId,
365
        address _lp,
366
        uint256 _contribution,
367
        uint256 _apr,
368
        IHyperdrive.Options memory _options
369
    ) external payable onlyFactory returns (uint256 lpShares) {
370
        // Check that the message value is valid.
371
        _checkMessageValue();
1,760✔
372

373
        // Ensure that the instance has been fully deployed.
374
        IHyperdrive hyperdrive = IHyperdrive(
1,754✔
375
            _deployments[_deploymentId].hyperdrive
376
        );
377
        if (address(hyperdrive) == address(0)) {
1,754✔
378
            revert IHyperdriveDeployerCoordinator.HyperdriveIsNotDeployed();
3✔
379
        }
380

381
        // Prepare for initialization by drawing funds from the user.
382
        uint256 value = _prepareInitialize(
1,751✔
383
            hyperdrive,
384
            _lp,
385
            _contribution,
386
            _options
387
        );
388

389
        // Initialize the deployment.
390
        lpShares = hyperdrive.initialize{ value: value }(
1,751✔
391
            _contribution,
392
            _apr,
393
            _options
394
        );
395

396
        // Refund any excess ether that was sent.
397
        uint256 refund = msg.value - value;
1,751✔
398
        if (refund > 0) {
1,751✔
399
            (bool success, ) = payable(msg.sender).call{ value: refund }("");
1✔
400
            if (!success) {
1✔
401
                revert IHyperdriveDeployerCoordinator.TransferFailed();
×
402
            }
403
        }
404

405
        return lpShares;
1,751✔
406
    }
407

408
    /// @notice Gets the deployment specified by the deployment ID.
409
    /// @param _deploymentId The deployment ID.
410
    /// @return The deployment.
411
    function deployments(
412
        bytes32 _deploymentId
413
    ) external view returns (Deployment memory) {
414
        return _deployments[_deploymentId];
9✔
415
    }
416

417
    /// @notice Gets the number of targets that need to be deployed for a full
418
    ///         deployment.
419
    /// @return numTargets The number of targets that need to be deployed for a
420
    ///         full deployment.
421
    function getNumberOfTargets() external pure returns (uint256) {
422
        return NUM_TARGETS;
10,779✔
423
    }
424

425
    /// @dev Prepares the coordinator for initialization by drawing funds from
426
    ///      the LP, if necessary.
427
    /// @param _hyperdrive The Hyperdrive instance that is being initialized.
428
    /// @param _lp The LP that is initializing the pool.
429
    /// @param _contribution The amount of capital to supply. The units of this
430
    ///        quantity are either base or vault shares, depending on the value
431
    ///        of `_options.asBase`.
432
    /// @param _options The options that configure how the initialization is
433
    ///        settled.
434
    /// @return value The value that should be sent in the initialize
435
    ///         transaction.
436
    function _prepareInitialize(
437
        IHyperdrive _hyperdrive,
438
        address _lp,
439
        uint256 _contribution,
440
        IHyperdrive.Options memory _options
441
    ) internal virtual returns (uint256 value);
442

443
    /// @dev A yield source dependent check that prevents ether from being
444
    ///      transferred to Hyperdrive instances that don't accept ether.
445
    function _checkMessageValue() internal view virtual;
446

447
    /// @dev Checks the pool configuration to ensure that it is valid.
448
    /// @param _deployConfig The deploy configuration of the Hyperdrive pool.
449
    function _checkPoolConfig(
450
        IHyperdrive.PoolDeployConfig memory _deployConfig
451
    ) internal view virtual {
452
        // Ensure that the minimum share reserves is at least 1e3. Deployer
453
        // coordinators should override this to be stricter.
454
        if (_deployConfig.minimumShareReserves < 1e3) {
10,812✔
455
            revert IHyperdriveDeployerCoordinator.InvalidMinimumShareReserves();
3✔
456
        }
457

458
        if (_deployConfig.checkpointDuration == 0) {
10,809✔
459
            revert IHyperdriveDeployerCoordinator.InvalidCheckpointDuration();
3✔
460
        }
461
        if (
462
            _deployConfig.positionDuration < _deployConfig.checkpointDuration ||
10,806✔
463
            _deployConfig.positionDuration % _deployConfig.checkpointDuration !=
10,803✔
464
            0
465
        ) {
466
            revert IHyperdriveDeployerCoordinator.InvalidPositionDuration();
6✔
467
        }
468

469
        // Ensure that the fees don't exceed 100%.
470
        if (
471
            _deployConfig.fees.curve > ONE ||
10,800✔
472
            _deployConfig.fees.flat > ONE ||
10,797✔
473
            _deployConfig.fees.governanceLP > ONE ||
10,794✔
474
            _deployConfig.fees.governanceZombie > ONE
10,791✔
475
        ) {
476
            revert IHyperdriveDeployerCoordinator.InvalidFeeAmounts();
12✔
477
        }
478
    }
479

480
    /// @dev Gets the initial vault share price of the Hyperdrive pool.
481
    /// @param _deployConfig The deploy config that will be used to deploy the
482
    ///        pool.
483
    /// @param _extraData The extra data passed to the child deployers.
484
    /// @return The initial vault share price of the Hyperdrive pool.
485
    function _getInitialVaultSharePrice(
486
        IHyperdrive.PoolDeployConfig memory _deployConfig,
487
        bytes memory _extraData
488
    ) internal view virtual returns (uint256);
489

490
    /// @notice Copies the deploy config into a pool config.
491
    /// @param _deployConfig The deploy configuration of the Hyperdrive pool.
492
    /// @return _config The pool configuration of the Hyperdrive pool.
493
    function _copyPoolConfig(
494
        IHyperdrive.PoolDeployConfig memory _deployConfig
495
    ) internal pure returns (IHyperdrive.PoolConfig memory _config) {
496
        // Copy the `PoolDeployConfig` into a `PoolConfig` struct.
497
        _config.baseToken = _deployConfig.baseToken;
10,788✔
498
        _config.vaultSharesToken = _deployConfig.vaultSharesToken;
10,788✔
499
        _config.linkerFactory = _deployConfig.linkerFactory;
10,788✔
500
        _config.linkerCodeHash = _deployConfig.linkerCodeHash;
10,788✔
501
        _config.minimumShareReserves = _deployConfig.minimumShareReserves;
10,788✔
502
        _config.minimumTransactionAmount = _deployConfig
10,788✔
503
            .minimumTransactionAmount;
504
        _config.circuitBreakerDelta = _deployConfig.circuitBreakerDelta;
10,788✔
505
        _config.positionDuration = _deployConfig.positionDuration;
10,788✔
506
        _config.checkpointDuration = _deployConfig.checkpointDuration;
10,788✔
507
        _config.timeStretch = _deployConfig.timeStretch;
10,788✔
508
        _config.governance = _deployConfig.governance;
10,788✔
509
        _config.feeCollector = _deployConfig.feeCollector;
10,788✔
510
        _config.sweepCollector = _deployConfig.sweepCollector;
10,788✔
511
        _config.checkpointRewarder = _deployConfig.checkpointRewarder;
10,788✔
512
        _config.fees = _deployConfig.fees;
10,788✔
513
    }
514
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc