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

delvtech / hyperdrive / 8732730472

18 Apr 2024 05:12AM UTC coverage: 93.378% (+0.04%) from 93.337%
8732730472

Pull #989

github

web-flow
Merge e5c94423d into 78a1b5959
Pull Request #989: Metadata Functions

2 of 2 new or added lines in 2 files covered. (100.0%)

52 existing lines in 7 files now uncovered.

1805 of 1933 relevant lines covered (93.38%)

383731.12 hits per line

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

89.83
/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 { ONE } from "../libraries/FixedPointMath.sol";
9

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

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

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

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

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

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

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

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

69
    /// @notice A mapping from deployment ID to deployment.
70
    mapping(bytes32 => Deployment) internal _deployments;
71

72
    /// @notice Instantiates the deployer coordinator.
73
    /// @param _factory The factory that this deployer will be registered with.
74
    /// @param _coreDeployer The core deployer.
75
    /// @param _target0Deployer The target0 deployer.
76
    /// @param _target1Deployer The target1 deployer.
77
    /// @param _target2Deployer The target2 deployer.
78
    /// @param _target4Deployer The target4 deployer.
79
    constructor(
80
        address _factory,
81
        address _coreDeployer,
82
        address _target0Deployer,
83
        address _target1Deployer,
84
        address _target2Deployer,
85
        address _target3Deployer,
86
        address _target4Deployer
87
    ) {
UNCOV
88
        factory = _factory;
×
89
        coreDeployer = _coreDeployer;
×
90
        target0Deployer = _target0Deployer;
×
91
        target1Deployer = _target1Deployer;
×
92
        target2Deployer = _target2Deployer;
×
93
        target3Deployer = _target3Deployer;
×
94
        target4Deployer = _target4Deployer;
×
95
    }
96

97
    /// @dev Ensures that the contract is being called by the associated
98
    ///      factory.
99
    modifier onlyFactory() {
100
        if (msg.sender != factory) {
17,306✔
101
            revert IHyperdriveDeployerCoordinator.SenderIsNotFactory();
6✔
102
        }
103
        _;
104
    }
105

106
    /// @notice Returns the deployer coordinator's name.
107
    /// @notice The deployer coordinator's name.
108
    function name() external pure virtual returns (string memory);
109

110
    /// @notice Returns the deployer coordinator's version.
111
    /// @notice The deployer coordinator's version.
112
    function version() external pure virtual returns (string memory);
113

114
    /// @notice Deploys a Hyperdrive instance with the given parameters.
115
    /// @dev This can only be deployed by the associated factory.
116
    /// @param _deploymentId The ID of the deployment.
117
    /// @param _deployConfig The deploy configuration of the Hyperdrive pool.
118
    /// @param _extraData The extra data that contains the pool and sweep targets.
119
    /// @param _salt The create2 salt used to deploy Hyperdrive.
120
    /// @return The address of the newly deployed Hyperdrive instance.
121
    function deploy(
122
        bytes32 _deploymentId,
123
        IHyperdrive.PoolDeployConfig memory _deployConfig,
124
        bytes memory _extraData,
125
        bytes32 _salt
126
    ) external onlyFactory returns (address) {
127
        // Ensure that the Hyperdrive entrypoint has not already been deployed.
128
        Deployment memory deployment = _deployments[_deploymentId];
3,430✔
129
        if (deployment.hyperdrive != address(0)) {
5,145✔
130
            revert IHyperdriveDeployerCoordinator.HyperdriveAlreadyDeployed();
6✔
131
        }
132

133
        // Ensure that the deployment is not a fresh deployment. We can check
134
        // this by ensuring that the config hash is set.
135
        if (deployment.configHash == bytes32(0)) {
5,136✔
136
            revert IHyperdriveDeployerCoordinator.DeploymentDoesNotExist();
6✔
137
        }
138

139
        // Ensure that all of the targets have been deployed.
140
        if (
141
            deployment.target0 == address(0) ||
11,963✔
142
            deployment.target1 == address(0) ||
5,127✔
143
            deployment.target2 == address(0) ||
5,118✔
144
            deployment.target3 == address(0) ||
5,109✔
145
            deployment.target4 == address(0)
5,100✔
146
        ) {
147
            revert IHyperdriveDeployerCoordinator.IncompleteDeployment();
24✔
148
        }
149

150
        // Ensure that the provided config matches the config hash.
151
        if (keccak256(abi.encode(_deployConfig)) != deployment.configHash) {
5,091✔
152
            revert IHyperdriveDeployerCoordinator.MismatchedConfig();
6✔
153
        }
154

155
        // Ensure that the provided extra data matches the extra data hash.
156
        if (keccak256(_extraData) != deployment.extraDataHash) {
5,082✔
157
            revert IHyperdriveDeployerCoordinator.MismatchedExtraData();
6✔
158
        }
159

160
        // Check the pool configuration to ensure that it's a valid
161
        // configuration for this instance. This was already done when deploying
162
        // target0, but we check again as a precaution in case the check relies
163
        // on state that can change.
164
        _checkPoolConfig(_deployConfig);
3,382✔
165

166
        // Convert the deploy config into the pool config and set the initial
167
        // vault share price.
168
        IHyperdrive.PoolConfig memory config = _copyPoolConfig(_deployConfig);
5,064✔
169
        config.initialVaultSharePrice = deployment.initialSharePrice;
3,376✔
170

171
        // Deploy the Hyperdrive instance and add it to the deployment struct.
172
        bytes32 deploymentId = _deploymentId; // Avoid stack too deep error
3,376✔
173
        bytes32 salt = _salt; // Avoid stack too deep error
3,376✔
174
        address hyperdrive = IHyperdriveCoreDeployer(coreDeployer).deploy(
5,064✔
175
            config,
176
            _extraData,
177
            deployment.target0,
178
            deployment.target1,
179
            deployment.target2,
180
            deployment.target3,
181
            deployment.target4,
182
            // NOTE: We hash the deployment ID with the salt to prevent the
183
            // front-running of deployments.
184
            keccak256(abi.encode(deploymentId, salt))
185
        );
186
        _deployments[_deploymentId].hyperdrive = hyperdrive;
3,376✔
187

188
        return hyperdrive;
3,376✔
189
    }
190

191
    /// @notice Deploys a Hyperdrive target instance with the given parameters.
192
    /// @dev This can only be deployed by the associated factory.
193
    /// @dev As a convention, target0 must be deployed first. After this, the
194
    ///      targets can be deployed in any order.
195
    /// @param _deploymentId The ID of the deployment.
196
    /// @param _deployConfig The deploy configuration of the Hyperdrive pool.
197
    /// @param _extraData The extra data that contains the pool and sweep targets.
198
    /// @param _targetIndex The index of the target to deploy.
199
    /// @param _salt The create2 salt used to deploy the target.
200
    /// @return target The address of the newly deployed target instance.
201
    function deployTarget(
202
        bytes32 _deploymentId,
203
        IHyperdrive.PoolDeployConfig memory _deployConfig,
204
        bytes memory _extraData,
205
        uint256 _targetIndex,
206
        bytes32 _salt
207
    ) external onlyFactory returns (address target) {
208
        // If the target index is 0, then we're deploying the target0 instance.
209
        // By convention, this target must be deployed first, and as part of the
210
        // deployment of target0, we will register the deployment in the state.
211
        Deployment storage deployment = _deployments[_deploymentId];
17,300✔
212
        if (_targetIndex == 0) {
17,300✔
213
            // Ensure that the deployment is a fresh deployment. We can check
214
            // this by ensuring that the config hash is not set.
215
            if (deployment.configHash != bytes32(0)) {
5,253✔
216
                revert IHyperdriveDeployerCoordinator.DeploymentAlreadyExists();
6✔
217
            }
218

219
            // Check the pool configuration to ensure that it's a valid
220
            // configuration for this instance.
221
            _checkPoolConfig(_deployConfig);
3,496✔
222

223
            // Get the initial share price and the hashes of the config and extra
224
            // data.
225
            uint256 initialSharePrice = _getInitialVaultSharePrice(
5,235✔
226
                _deployConfig,
227
                _extraData
228
            );
229
            bytes32 configHash_ = keccak256(abi.encode(_deployConfig));
5,235✔
230
            bytes32 extraDataHash = keccak256(_extraData);
5,235✔
231

232
            // Convert the deploy config into the pool config and set the initial
233
            // vault share price.
234
            IHyperdrive.PoolConfig memory config_ = _copyPoolConfig(
5,235✔
235
                _deployConfig
236
            );
237
            config_.initialVaultSharePrice = initialSharePrice;
3,490✔
238

239
            // Deploy the target0 contract.
240
            target = IHyperdriveTargetDeployer(target0Deployer).deploy(
3,490✔
241
                config_,
242
                _extraData,
243
                // NOTE: We hash the deployment ID with the salt to prevent the
244
                // front-running of deployments.
245
                keccak256(abi.encode(_deploymentId, _salt))
246
            );
247

248
            // Store the deployment.
249
            deployment.configHash = configHash_;
3,490✔
250
            deployment.extraDataHash = extraDataHash;
3,490✔
251
            deployment.initialSharePrice = initialSharePrice;
3,490✔
252
            deployment.target0 = target;
3,490✔
253

254
            return target;
3,490✔
255
        }
256

257
        // Ensure that the deployment is not a fresh deployment. We can check
258
        // this by ensuring that the config hash is set.
259
        bytes32 configHash = _deployments[_deploymentId].configHash;
13,798✔
260
        if (configHash == bytes32(0)) {
20,697✔
261
            revert IHyperdriveDeployerCoordinator.DeploymentDoesNotExist();
6✔
262
        }
263

264
        // Ensure that the provided config matches the config hash.
265
        if (keccak256(abi.encode(_deployConfig)) != configHash) {
20,688✔
266
            revert IHyperdriveDeployerCoordinator.MismatchedConfig();
6✔
267
        }
268

269
        // Ensure that the provided extra data matches the extra data hash.
270
        if (keccak256(_extraData) != deployment.extraDataHash) {
20,679✔
271
            revert IHyperdriveDeployerCoordinator.MismatchedExtraData();
6✔
272
        }
273

274
        // Check the pool configuration to ensure that it's a valid
275
        // configuration for this instance. This was already done when deploying
276
        // target0, but we check again as a precaution in case the check relies
277
        // on state that can change.
278
        _checkPoolConfig(_deployConfig);
13,780✔
279

280
        // Convert the deploy config into the pool config and set the initial
281
        // vault share price.
282
        IHyperdrive.PoolConfig memory config = _copyPoolConfig(_deployConfig);
20,661✔
283
        config.initialVaultSharePrice = deployment.initialSharePrice;
13,774✔
284

285
        // If the target index is greater than 0, then we're deploying one of
286
        // the other target instances. We don't allow targets to be deployed
287
        // more than once, and their addresses are stored in the deployment
288
        // state.
289
        if (_targetIndex == 1) {
13,774✔
290
            if (deployment.target1 != address(0)) {
5,163✔
291
                revert IHyperdriveDeployerCoordinator.TargetAlreadyDeployed();
6✔
292
            }
293
            target = IHyperdriveTargetDeployer(target1Deployer).deploy(
3,436✔
294
                config,
295
                _extraData,
296
                keccak256(abi.encode(msg.sender, _deploymentId, _salt))
297
            );
298
            deployment.target1 = target;
3,436✔
299
        } else if (_targetIndex == 2) {
10,332✔
300
            if (deployment.target2 != address(0)) {
5,163✔
301
                revert IHyperdriveDeployerCoordinator.TargetAlreadyDeployed();
6✔
302
            }
303
            target = IHyperdriveTargetDeployer(target2Deployer).deploy(
3,436✔
304
                config,
305
                _extraData,
306
                keccak256(abi.encode(msg.sender, _deploymentId, _salt))
307
            );
308
            deployment.target2 = target;
3,436✔
309
        } else if (_targetIndex == 3) {
6,890✔
310
            if (deployment.target3 != address(0)) {
5,163✔
311
                revert IHyperdriveDeployerCoordinator.TargetAlreadyDeployed();
6✔
312
            }
313
            target = IHyperdriveTargetDeployer(target3Deployer).deploy(
3,436✔
314
                config,
315
                _extraData,
316
                keccak256(abi.encode(msg.sender, _deploymentId, _salt))
317
            );
318
            deployment.target3 = target;
3,436✔
319
        } else if (_targetIndex == 4) {
3,448✔
320
            if (deployment.target4 != address(0)) {
5,163✔
321
                revert IHyperdriveDeployerCoordinator.TargetAlreadyDeployed();
6✔
322
            }
323
            target = IHyperdriveTargetDeployer(target4Deployer).deploy(
3,436✔
324
                config,
325
                _extraData,
326
                keccak256(abi.encode(msg.sender, _deploymentId, _salt))
327
            );
328
            deployment.target4 = target;
3,436✔
329
        } else {
330
            revert IHyperdriveDeployerCoordinator.InvalidTargetIndex();
6✔
331
        }
332

333
        return target;
13,744✔
334
    }
335

336
    /// @notice Initializes a pool that was deployed by this coordinator.
337
    /// @dev This can only be deployed by the associated factory.
338
    /// @dev This function utilizes several helper functions that provide
339
    ///      flexibility to implementations.
340
    /// @param _deploymentId The ID of the deployment.
341
    /// @param _lp The LP that is initializing the pool.
342
    /// @param _contribution The amount of capital to supply. The units of this
343
    ///        quantity are either base or vault shares, depending on the value
344
    ///        of `_options.asBase`.
345
    /// @param _apr The target APR.
346
    /// @param _options The options that configure how the initialization is
347
    ///        settled.
348
    /// @return lpShares The initial number of LP shares created.
349
    function initialize(
350
        bytes32 _deploymentId,
351
        address _lp,
352
        uint256 _contribution,
353
        uint256 _apr,
354
        IHyperdrive.Options memory _options
355
    ) external payable onlyFactory returns (uint256 lpShares) {
356
        // Check that the message value is valid.
357
        _checkMessageValue();
3,364✔
358

359
        // Ensure that the instance has been fully deployed.
360
        IHyperdrive hyperdrive = IHyperdrive(
5,028✔
361
            _deployments[_deploymentId].hyperdrive
362
        );
363
        if (address(hyperdrive) == address(0)) {
6,704✔
364
            revert IHyperdriveDeployerCoordinator.HyperdriveIsNotDeployed();
6✔
365
        }
366

367
        // Prepare for initialization by drawing funds from the user.
368
        uint256 value = _prepareInitialize(
5,019✔
369
            hyperdrive,
370
            _lp,
371
            _contribution,
372
            _options
373
        );
374

375
        // Initialize the deployment.
376
        lpShares = hyperdrive.initialize{ value: value }(
3,346✔
377
            _contribution,
378
            _apr,
379
            _options
380
        );
381

382
        // Refund any excess ether that was sent.
383
        uint256 refund = msg.value - value;
5,019✔
384
        if (refund > 0) {
3,346✔
385
            (bool success, ) = payable(msg.sender).call{ value: refund }("");
3✔
386
            if (!success) {
2✔
UNCOV
387
                revert IHyperdriveDeployerCoordinator.TransferFailed();
×
388
            }
389
        }
390

391
        return lpShares;
3,346✔
392
    }
393

394
    /// @notice Gets the deployment specified by the deployment ID.
395
    /// @param _deploymentId The deployment ID.
396
    /// @return The deployment.
397
    function deployments(
398
        bytes32 _deploymentId
399
    ) external view returns (Deployment memory) {
400
        return _deployments[_deploymentId];
18✔
401
    }
402

403
    /// @dev Prepares the coordinator for initialization by drawing funds from
404
    ///      the LP, if necessary.
405
    /// @param _hyperdrive The Hyperdrive instance that is being initialized.
406
    /// @param _lp The LP that is initializing the pool.
407
    /// @param _contribution The amount of capital to supply. The units of this
408
    ///        quantity are either base or vault shares, depending on the value
409
    ///        of `_options.asBase`.
410
    /// @param _options The options that configure how the initialization is
411
    ///        settled.
412
    /// @return value The value that should be sent in the initialize
413
    ///         transaction.
414
    function _prepareInitialize(
415
        IHyperdrive _hyperdrive,
416
        address _lp,
417
        uint256 _contribution,
418
        IHyperdrive.Options memory _options
419
    ) internal virtual returns (uint256 value);
420

421
    /// @dev A yield source dependent check that prevents ether from being
422
    ///      transferred to Hyperdrive instances that don't accept ether.
423
    function _checkMessageValue() internal view virtual;
424

425
    /// @dev Checks the pool configuration to ensure that it is valid.
426
    /// @param _deployConfig The deploy configuration of the Hyperdrive pool.
427
    function _checkPoolConfig(
428
        IHyperdrive.PoolDeployConfig memory _deployConfig
429
    ) internal view virtual {
430
        // Ensure that the minimum share reserves is at least 1e3. Deployer
431
        // coordinators should override this to be stricter.
432
        if (_deployConfig.minimumShareReserves < 1e3) {
20,088✔
UNCOV
433
            revert IHyperdriveDeployerCoordinator.InvalidMinimumShareReserves();
×
434
        }
435

436
        if (_deployConfig.checkpointDuration == 0) {
20,088✔
UNCOV
437
            revert IHyperdriveDeployerCoordinator.InvalidCheckpointDuration();
×
438
        }
439
        if (
440
            _deployConfig.positionDuration < _deployConfig.checkpointDuration ||
30,132✔
441
            _deployConfig.positionDuration % _deployConfig.checkpointDuration !=
30,132✔
442
            0
443
        ) {
UNCOV
444
            revert IHyperdriveDeployerCoordinator.InvalidPositionDuration();
×
445
        }
446

447
        // Ensure that the fees don't exceed 100%.
448
        if (
449
            _deployConfig.fees.curve > ONE ||
50,220✔
450
            _deployConfig.fees.flat > ONE ||
20,088✔
451
            _deployConfig.fees.governanceLP > ONE ||
20,088✔
452
            _deployConfig.fees.governanceZombie > ONE
20,088✔
453
        ) {
UNCOV
454
            revert IHyperdriveDeployerCoordinator.InvalidFeeAmounts();
×
455
        }
456
    }
457

458
    /// @dev Gets the initial vault share price of the Hyperdrive pool.
459
    /// @param _deployConfig The deploy config that will be used to deploy the
460
    ///        pool.
461
    /// @param _extraData The extra data passed to the child deployers.
462
    /// @return The initial vault share price of the Hyperdrive pool.
463
    function _getInitialVaultSharePrice(
464
        IHyperdrive.PoolDeployConfig memory _deployConfig,
465
        bytes memory _extraData
466
    ) internal view virtual returns (uint256);
467

468
    /// @notice Copies the deploy config into a pool config.
469
    /// @param _deployConfig The deploy configuration of the Hyperdrive pool.
470
    /// @return _config The pool configuration of the Hyperdrive pool.
471
    function _copyPoolConfig(
472
        IHyperdrive.PoolDeployConfig memory _deployConfig
473
    ) internal pure returns (IHyperdrive.PoolConfig memory _config) {
474
        // Copy the `PoolDeployConfig` into a `PoolConfig` struct.
475
        _config.baseToken = _deployConfig.baseToken;
20,640✔
476
        _config.vaultSharesToken = _deployConfig.vaultSharesToken;
20,640✔
477
        _config.linkerFactory = _deployConfig.linkerFactory;
20,640✔
478
        _config.linkerCodeHash = _deployConfig.linkerCodeHash;
20,640✔
479
        _config.minimumShareReserves = _deployConfig.minimumShareReserves;
20,640✔
480
        _config.minimumTransactionAmount = _deployConfig
20,640✔
481
            .minimumTransactionAmount;
482
        _config.positionDuration = _deployConfig.positionDuration;
20,640✔
483
        _config.checkpointDuration = _deployConfig.checkpointDuration;
20,640✔
484
        _config.timeStretch = _deployConfig.timeStretch;
20,640✔
485
        _config.governance = _deployConfig.governance;
20,640✔
486
        _config.feeCollector = _deployConfig.feeCollector;
20,640✔
487
        _config.sweepCollector = _deployConfig.sweepCollector;
20,640✔
488
        _config.fees = _deployConfig.fees;
20,640✔
489
    }
490
}
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