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

delvtech / hyperdrive / 8870025498

28 Apr 2024 07:57PM UTC coverage: 91.947%. First build
8870025498

push

github

jalextowle
Added the `addLiquidity` circuit-breakers

9 of 11 new or added lines in 3 files covered. (81.82%)

1804 of 1962 relevant lines covered (91.95%)

188140.52 hits per line

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

72.8
/contracts/src/internal/HyperdriveLP.sol
1
// SPDX-License-Identifier: Apache-2.0
2
pragma solidity 0.8.20;
3

4
import { IHyperdrive } from "../interfaces/IHyperdrive.sol";
5
import { IHyperdriveEvents } from "../interfaces/IHyperdriveEvents.sol";
6
import { AssetId } from "../libraries/AssetId.sol";
7
import { FixedPointMath } from "../libraries/FixedPointMath.sol";
8
import { HyperdriveMath } from "../libraries/HyperdriveMath.sol";
9
import { LPMath } from "../libraries/LPMath.sol";
10
import { SafeCast } from "../libraries/SafeCast.sol";
11
import { HyperdriveBase } from "./HyperdriveBase.sol";
12
import { HyperdriveMultiToken } from "./HyperdriveMultiToken.sol";
13

14
/// @author DELV
15
/// @title HyperdriveLP
16
/// @notice Implements the LP accounting for Hyperdrive.
17
/// @custom:disclaimer The language used in this code is for coding convenience
18
///                    only, and is not intended to, and does not, have any
19
///                    particular legal or regulatory significance.
20
abstract contract HyperdriveLP is
21
    IHyperdriveEvents,
22
    HyperdriveBase,
23
    HyperdriveMultiToken
24
{
25
    using FixedPointMath for uint256;
26
    using SafeCast for int256;
27
    using SafeCast for uint256;
28

29
    // FIXME: Make sure this checkpoint's and the previous checkpoint's weighted
30
    //        spot prices are set to non-zero values.
31
    //
32
    /// @dev Allows the first LP to initialize the market with a target APR.
33
    /// @param _contribution The amount of capital to supply. The units of this
34
    ///        quantity are either base or vault shares, depending on the value
35
    ///        of `_options.asBase`.
36
    /// @param _apr The target APR.
37
    /// @param _options The options that configure how the operation is settled.
38
    /// @return lpShares The initial number of LP shares created.
39
    function _initialize(
40
        uint256 _contribution,
41
        uint256 _apr,
42
        IHyperdrive.Options calldata _options
43
    ) internal nonReentrant returns (uint256 lpShares) {
44
        // Check that the message value and base amount are valid.
45
        _checkMessageValue();
17,584✔
46

47
        // Check that the provided options are valid.
48
        _checkOptions(_options);
17,582✔
49

50
        // Ensure that the pool hasn't been initialized yet.
51
        if (_marketState.isInitialized) {
8,790✔
52
            revert IHyperdrive.PoolAlreadyInitialized();
2✔
53
        }
54

55
        // Deposit the users contribution and get the amount of shares that
56
        // their contribution was worth.
57
        (uint256 shareContribution, uint256 vaultSharePrice) = _deposit(
26,367✔
58
            _contribution,
59
            _options
60
        );
61

62
        // Ensure that the contribution is large enough to set aside the minimum
63
        // share reserves permanently. After initialization, none of the LPs
64
        // will have a claim on the minimum share reserves, and longs and shorts
65
        // will not be able to consume this liquidity. This ensures that the
66
        // share reserves are always greater than zero, which prevents a host of
67
        // numerical issues when we are updating the reserves during normal
68
        // operations. As an additional precaution, we will also set aside an
69
        // amount of shares equaling the minimum share reserves as the initial
70
        // LP contribution from the zero address. This ensures that the total
71
        // LP supply will always be greater than or equal to the minimum share
72
        // reserves, which is helping for preventing donation attacks and other
73
        // numerical issues.
74
        if (shareContribution < 2 * _minimumShareReserves) {
26,367✔
75
            revert IHyperdrive.BelowMinimumContribution();
×
76
        }
77
        unchecked {
78
            lpShares = shareContribution - 2 * _minimumShareReserves;
17,578✔
79
        }
80

81
        // Set the initialized state to true.
82
        _marketState.isInitialized = true;
17,578✔
83

84
        // Update the reserves. The bond reserves are calculated so that the
85
        // pool is initialized with the target APR.
86
        _marketState.shareReserves = shareContribution.toUint128();
17,578✔
87
        _marketState.bondReserves = HyperdriveMath
17,578✔
88
            .calculateInitialBondReserves(
89
                shareContribution,
90
                _initialVaultSharePrice,
91
                _apr,
92
                _positionDuration,
93
                _timeStretch
94
            )
95
            .toUint128();
96

97
        // Mint the minimum share reserves to the zero address as a buffer that
98
        // ensures that the total LP supply is always greater than or equal to
99
        // the minimum share reserves. The initializer will receive slightly
100
        // less shares than they contributed to cover the shares set aside as a
101
        // buffer on the share reserves and the shares set aside for the zero
102
        // address, but this is a small price to pay for the added security
103
        // in practice.
104
        _mint(AssetId._LP_ASSET_ID, address(0), _minimumShareReserves);
17,578✔
105
        _mint(AssetId._LP_ASSET_ID, _options.destination, lpShares);
17,578✔
106

107
        // Create an initial checkpoint.
108
        _applyCheckpoint(
17,578✔
109
            _latestCheckpoint(),
110
            vaultSharePrice,
111
            LPMath.SHARE_PROCEEDS_MAX_ITERATIONS
112
        );
113

114
        // Emit an Initialize event.
115
        uint256 baseContribution = _convertToBaseFromOption(
26,367✔
116
            _contribution,
117
            vaultSharePrice,
118
            _options
119
        );
120
        emit Initialize(
17,578✔
121
            _options.destination,
122
            lpShares,
123
            baseContribution, // base contribution
124
            shareContribution, // vault shares contribution
125
            _options.asBase,
126
            _apr
127
        );
128

129
        return lpShares;
17,578✔
130
    }
131

132
    // FIXME: Add circuit breakers.
133
    //
134
    /// @dev Allows LPs to supply liquidity for LP shares.
135
    /// @param _contribution The amount of capital to supply. The units of this
136
    ///        quantity are either base or vault shares, depending on the value
137
    ///        of `_options.asBase`.
138
    /// @param _minLpSharePrice The minimum LP share price the LP is willing
139
    ///        to accept for their shares. LPs incur negative slippage when
140
    ///        adding liquidity if there is a net curve position in the market,
141
    ///        so this allows LPs to protect themselves from high levels of
142
    ///        slippage. The units of this quantity are either base or vault
143
    ///        shares, depending on the value of `_options.asBase`.
144
    /// @param _minApr The minimum APR at which the LP is willing to supply.
145
    /// @param _maxApr The maximum APR at which the LP is willing to supply.
146
    /// @param _options The options that configure how the operation is settled.
147
    /// @return lpShares The number of LP tokens created.
148
    function _addLiquidity(
149
        uint256 _contribution,
150
        uint256 _minLpSharePrice,
151
        uint256 _minApr,
152
        uint256 _maxApr,
153
        IHyperdrive.Options calldata _options
154
    ) internal nonReentrant isNotPaused returns (uint256 lpShares) {
155
        // Check that the message value is valid.
156
        _checkMessageValue();
460✔
157

158
        // Check that the provided options are valid.
159
        _checkOptions(_options);
458✔
160

161
        // Ensure that the contribution is greater than or equal to the minimum
162
        // transaction amount.
163
        if (_contribution < _minimumTransactionAmount) {
456✔
164
            revert IHyperdrive.MinimumTransactionAmount();
2✔
165
        }
166

167
        uint256 latestCheckpoint = _latestCheckpoint();
681✔
168
        {
169
            // Enforce the slippage guard.
170
            uint256 apr = HyperdriveMath.calculateSpotAPR(
681✔
171
                _effectiveShareReserves(),
172
                _marketState.bondReserves,
173
                _initialVaultSharePrice,
174
                _positionDuration,
175
                _timeStretch
176
            );
177
            if (apr < _minApr || apr > _maxApr) {
907✔
178
                revert IHyperdrive.InvalidApr();
6✔
179
            }
180

181
            // Ensure that the spot APR is close enough to the previous weighted
182
            // spot price to fall within the tolerance.
183
            uint256 weightedSpotAPR = HyperdriveMath.calculateAPRFromPrice(
672✔
184
                _checkpoints[latestCheckpoint].weightedSpotPrice,
185
                _positionDuration
186
            );
187
            if (
NEW
188
                apr > weightedSpotAPR + _maximumAddLiquidityAPRDelta ||
×
189
                (weightedSpotAPR > _maximumAddLiquidityAPRDelta &&
190
                    apr < weightedSpotAPR - _maximumAddLiquidityAPRDelta)
191
            ) {
NEW
192
                revert IHyperdrive.CircuitBreakerTriggered();
×
193
            }
194
        }
195

196
        // Deposit for the user, this call also transfers from them
197
        (uint256 shareContribution, uint256 vaultSharePrice) = _deposit(
×
198
            _contribution,
199
            _options
200
        );
201

202
        // Perform a checkpoint.
203
        _applyCheckpoint(
×
204
            latestCheckpoint,
205
            vaultSharePrice,
206
            LPMath.SHARE_PROCEEDS_MAX_ITERATIONS
207
        );
208

209
        // Get the initial value for the total LP supply and the total supply
210
        // of withdrawal shares before the liquidity is added. The total LP
211
        // supply is given by `l = l_a + l_w - l_r` where `l_a` is the total
212
        // supply of active LP shares, `l_w` is the total supply of withdrawal
213
        // shares, and `l_r` is the amount of withdrawal shares ready for
214
        // withdrawal.
215
        uint256 withdrawalSharesOutstanding = _totalSupply[
×
216
            AssetId._WITHDRAWAL_SHARE_ASSET_ID
217
        ] - _withdrawPool.readyToWithdraw;
218
        uint256 lpTotalSupply = _totalSupply[AssetId._LP_ASSET_ID] +
×
219
            withdrawalSharesOutstanding;
220

221
        // Calculate the number of LP shares to mint.
222
        uint256 endingPresentValue;
×
223
        uint256 startingPresentValue;
×
224
        {
225
            // Calculate the present value before updating the reserves.
226
            LPMath.PresentValueParams memory params = _getPresentValueParams(
×
227
                vaultSharePrice
228
            );
229
            startingPresentValue = LPMath.calculatePresentValue(params);
×
230

231
            // Add the liquidity to the pool's reserves and calculate the new
232
            // present value.
233
            _updateLiquidity(shareContribution.toInt256());
×
234
            params.shareReserves = _marketState.shareReserves;
×
235
            params.shareAdjustment = _marketState.shareAdjustment;
×
236
            params.bondReserves = _marketState.bondReserves;
×
237
            endingPresentValue = LPMath.calculatePresentValue(params);
×
238

239
            // Revert if the present value decreased after adding liquidity.
240
            if (endingPresentValue < startingPresentValue) {
×
241
                revert IHyperdrive.DecreasedPresentValueWhenAddingLiquidity();
×
242
            }
243

244
            // NOTE: Round down to underestimate the amount of LP shares minted.
245
            //
246
            // The LP shares minted to the LP is derived by solving for the
247
            // change in LP shares that preserves the ratio of present value to
248
            // total LP shares. This ensures that LPs are fairly rewarded for
249
            // adding liquidity. This is given by:
250
            //
251
            // PV0 / l0 = PV1 / (l0 + dl) => dl = ((PV1 - PV0) * l0) / PV0
252
            lpShares = (endingPresentValue - startingPresentValue).mulDivDown(
×
253
                lpTotalSupply,
254
                startingPresentValue
255
            );
256

257
            // Ensure that enough lp shares are minted so that they can be redeemed.
258
            if (lpShares < _minimumTransactionAmount) {
×
259
                revert IHyperdrive.MinimumTransactionAmount();
×
260
            }
261
        }
262

263
        // NOTE: Round down to make the check more conservative.
264
        //
265
        // Enforce the minimum LP share price slippage guard.
266
        if (_contribution.divDown(lpShares) < _minLpSharePrice) {
×
267
            revert IHyperdrive.OutputLimit();
×
268
        }
269

270
        // Mint LP shares to the supplier.
271
        _mint(AssetId._LP_ASSET_ID, _options.destination, lpShares);
×
272

273
        // Distribute the excess idle to the withdrawal pool. If the distribute
274
        // excess idle calculation fails, we revert to avoid allowing the system
275
        // to enter an unhealthy state. A failure indicates that the present
276
        // value can't be calculated.
277
        bool success = _distributeExcessIdleSafe(vaultSharePrice);
×
278
        if (!success) {
×
279
            revert IHyperdrive.DistributeExcessIdleFailed();
×
280
        }
281

282
        // Emit an AddLiquidity event.
283
        uint256 lpSharePrice = lpTotalSupply == 0
×
284
            ? 0 // NOTE: We always round the LP share price down for consistency.
285
            : startingPresentValue.divDown(lpTotalSupply);
286
        uint256 contribution = _contribution; // avoid stack-too-deep
×
287
        uint256 baseContribution = _convertToBaseFromOption(
×
288
            contribution,
289
            vaultSharePrice,
290
            _options
291
        );
292
        IHyperdrive.Options calldata options = _options; // avoid stack-too-deep
×
293
        emit AddLiquidity(
×
294
            options.destination,
295
            lpShares,
296
            baseContribution, // base contribution
297
            shareContribution, // vault shares contribution
298
            options.asBase,
299
            lpSharePrice
300
        );
301
    }
302

303
    /// @dev Allows an LP to burn shares and withdraw from the pool.
304
    /// @param _lpShares The LP shares to burn.
305
    /// @param _minOutputPerShare The minimum amount the LP expects to receive
306
    ///        for each withdrawal share that is burned. The units of this
307
    ///        quantity are either base or vault shares, depending on the value
308
    ///        of `_options.asBase`.
309
    /// @param _options The options that configure how the operation is settled.
310
    /// @return proceeds The amount the LP removing liquidity receives. The
311
    ///        units of this quantity are either base or vault shares, depending
312
    ///        on the value of `_options.asBase`.
313
    /// @return withdrawalShares The base that the LP receives buys out some of
314
    ///         their LP shares, but it may not be sufficient to fully buy the
315
    ///         LP out. In this case, the LP receives withdrawal shares equal
316
    ///         in value to the present value they are owed. As idle capital
317
    ///         becomes available, the pool will buy back these shares.
318
    function _removeLiquidity(
319
        uint256 _lpShares,
320
        uint256 _minOutputPerShare,
321
        IHyperdrive.Options calldata _options
322
    )
323
        internal
324
        nonReentrant
325
        returns (uint256 proceeds, uint256 withdrawalShares)
326
    {
327
        // Check that the provided options are valid.
328
        _checkOptions(_options);
2,948✔
329

330
        // Ensure that the amount of LP shares to remove is greater than or
331
        // equal to the minimum transaction amount.
332
        if (_lpShares < _minimumTransactionAmount) {
2,946✔
333
            revert IHyperdrive.MinimumTransactionAmount();
2✔
334
        }
335

336
        // Perform a checkpoint.
337
        uint256 vaultSharePrice = _pricePerVaultShare();
4,416✔
338
        _applyCheckpoint(
2,944✔
339
            _latestCheckpoint(),
340
            vaultSharePrice,
341
            LPMath.SHARE_PROCEEDS_MAX_ITERATIONS
342
        );
343

344
        // Burn the LP's shares.
345
        _burn(AssetId._LP_ASSET_ID, msg.sender, _lpShares);
2,944✔
346

347
        // Mint an equivalent amount of withdrawal shares.
348
        _mint(
2,942✔
349
            AssetId._WITHDRAWAL_SHARE_ASSET_ID,
350
            _options.destination,
351
            _lpShares
352
        );
353

354
        // Redeem as many of the withdrawal shares as possible.
355
        uint256 withdrawalSharesRedeemed;
2,942✔
356
        (proceeds, withdrawalSharesRedeemed) = _redeemWithdrawalSharesInternal(
2,942✔
357
            _options.destination,
358
            _lpShares,
359
            vaultSharePrice,
360
            _minOutputPerShare,
361
            _options
362
        );
363
        withdrawalShares = _lpShares - withdrawalSharesRedeemed;
2,942✔
364

365
        // Emit a RemoveLiquidity event. If the LP share price calculation
366
        // fails, we proceed in removing liquidity and just emit the LP share
367
        // price as zero. This ensures that the system's liveness isn't impacted
368
        // by temporarily being unable to calculate the present value.
369
        (uint256 lpSharePrice, ) = _calculateLPSharePriceSafe(vaultSharePrice);
4,413✔
370
        emit RemoveLiquidity(
2,942✔
371
            msg.sender, // provider
372
            _options.destination, // destination
373
            _lpShares,
374
            _convertToBaseFromOption(proceeds, vaultSharePrice, _options), // base proceeds
375
            _convertToVaultSharesFromOption(
376
                proceeds,
377
                vaultSharePrice,
378
                _options
379
            ), // vault shares proceeds
380
            _options.asBase,
381
            uint256(withdrawalShares),
382
            lpSharePrice
383
        );
384

385
        return (proceeds, withdrawalShares);
2,942✔
386
    }
387

388
    /// @dev Redeems withdrawal shares by giving the LP a pro-rata amount of the
389
    ///      withdrawal pool's proceeds. This function redeems the maximum
390
    ///      amount of the specified withdrawal shares given the amount of
391
    ///      withdrawal shares ready to withdraw.
392
    /// @param _withdrawalShares The withdrawal shares to redeem.
393
    /// @param _minOutputPerShare The minimum amount the LP expects to
394
    ///        receive for each withdrawal share that is burned. The units of
395
    ///        this quantity are either base or vault shares, depending on the
396
    ///        value of `_options.asBase`.
397
    /// @param _options The options that configure how the operation is settled.
398
    /// @return proceeds The amount the LP received. The units of this quantity
399
    ///         are either base or vault shares, depending on the value of
400
    ///         `_options.asBase`.
401
    /// @return withdrawalSharesRedeemed The amount of withdrawal shares that
402
    ///         were redeemed.
403
    function _redeemWithdrawalShares(
404
        uint256 _withdrawalShares,
405
        uint256 _minOutputPerShare,
406
        IHyperdrive.Options calldata _options
407
    )
408
        internal
409
        nonReentrant
410
        returns (uint256 proceeds, uint256 withdrawalSharesRedeemed)
411
    {
412
        // Check that the provided options are valid.
413
        _checkOptions(_options);
1,876✔
414

415
        // Perform a checkpoint.
416
        uint256 vaultSharePrice = _pricePerVaultShare();
2,811✔
417
        _applyCheckpoint(
1,874✔
418
            _latestCheckpoint(),
419
            vaultSharePrice,
420
            LPMath.SHARE_PROCEEDS_MAX_ITERATIONS
421
        );
422

423
        // Redeem as many of the withdrawal shares as possible.
424
        (proceeds, withdrawalSharesRedeemed) = _redeemWithdrawalSharesInternal(
1,874✔
425
            msg.sender,
426
            _withdrawalShares,
427
            vaultSharePrice,
428
            _minOutputPerShare,
429
            _options
430
        );
431

432
        // Emit a RedeemWithdrawalShares event.
433
        emit RedeemWithdrawalShares(
1,872✔
434
            msg.sender, // provider
435
            _options.destination, // destination
436
            withdrawalSharesRedeemed,
437
            _convertToBaseFromOption(proceeds, vaultSharePrice, _options), // base proceeds
438
            _convertToVaultSharesFromOption(
439
                proceeds,
440
                vaultSharePrice,
441
                _options
442
            ), // vault shares proceeds
443
            _options.asBase
444
        );
445

446
        return (proceeds, withdrawalSharesRedeemed);
1,872✔
447
    }
448

449
    /// @dev Redeems withdrawal shares by giving the LP a pro-rata amount of the
450
    ///      withdrawal pool's proceeds. This function redeems the maximum
451
    ///      amount of the specified withdrawal shares given the amount of
452
    ///      withdrawal shares ready to withdraw.
453
    /// @param _source The address that owns the withdrawal shares to redeem.
454
    /// @param _withdrawalShares The withdrawal shares to redeem.
455
    /// @param _vaultSharePrice The vault share price.
456
    /// @param _minOutputPerShare The minimum amount the LP expects to
457
    ///        receive for each withdrawal share that is burned. The units of
458
    ///        this quantity are either base or vault shares, depending on the
459
    ///        value of `_options.asBase`.
460
    /// @param _options The options that configure how the operation is settled.
461
    /// @return proceeds The amount the LP received. The units of this quantity
462
    ///         are either base or vault shares, depending on the value of
463
    ///         `_options.asBase`.
464
    /// @return withdrawalSharesRedeemed The amount of withdrawal shares that
465
    ///         were redeemed.
466
    function _redeemWithdrawalSharesInternal(
467
        address _source,
468
        uint256 _withdrawalShares,
469
        uint256 _vaultSharePrice,
470
        uint256 _minOutputPerShare,
471
        IHyperdrive.Options calldata _options
472
    ) internal returns (uint256 proceeds, uint256 withdrawalSharesRedeemed) {
473
        // Distribute the excess idle to the withdrawal pool. If the distribute
474
        // excess idle calculation fails, we proceed with the calculation since
475
        // LPs should be able to redeem their withdrawal shares for existing
476
        // withdrawal proceeds regardless of whether or not idle could be
477
        // distributed.
478
        _distributeExcessIdleSafe(_vaultSharePrice);
4,816✔
479

480
        // Clamp the shares to the total amount of shares ready for withdrawal
481
        // to avoid unnecessary reverts. We exit early if the user has no shares
482
        // available to redeem.
483
        withdrawalSharesRedeemed = _withdrawalShares;
4,816✔
484
        uint128 readyToWithdraw_ = _withdrawPool.readyToWithdraw;
4,816✔
485
        if (withdrawalSharesRedeemed > readyToWithdraw_) {
4,816✔
486
            withdrawalSharesRedeemed = readyToWithdraw_;
1,922✔
487
        }
488
        if (withdrawalSharesRedeemed == 0) return (0, 0);
5,288✔
489

490
        // We burn the shares from the user.
491
        _burn(
3,872✔
492
            AssetId._WITHDRAWAL_SHARE_ASSET_ID,
493
            _source,
494
            withdrawalSharesRedeemed
495
        );
496

497
        // NOTE: Round down to underestimate the share proceeds.
498
        //
499
        // The LP gets the pro-rata amount of the collected proceeds.
500
        uint256 shareProceeds = withdrawalSharesRedeemed.mulDivDown(
5,808✔
501
            _withdrawPool.proceeds,
502
            readyToWithdraw_
503
        );
504

505
        // Apply the update to the withdrawal pool.
506
        _withdrawPool.readyToWithdraw =
3,872✔
507
            readyToWithdraw_ -
508
            withdrawalSharesRedeemed.toUint128();
509
        _withdrawPool.proceeds -= shareProceeds.toUint128();
3,872✔
510

511
        // Withdraw the share proceeds to the user.
512
        proceeds = _withdraw(shareProceeds, _vaultSharePrice, _options);
3,872✔
513

514
        // NOTE: Round up to make the check more conservative.
515
        //
516
        // Enforce the minimum user output per share.
517
        if (proceeds < _minOutputPerShare.mulUp(withdrawalSharesRedeemed)) {
5,808✔
518
            revert IHyperdrive.OutputLimit();
2✔
519
        }
520

521
        return (proceeds, withdrawalSharesRedeemed);
3,870✔
522
    }
523

524
    /// @dev Distribute as much of the excess idle as possible to the withdrawal
525
    ///      pool while holding the LP share price constant.
526
    /// @param _vaultSharePrice The current vault share price.
527
    /// @return A failure flag indicating if the calculation succeeded.
528
    function _distributeExcessIdleSafe(
529
        uint256 _vaultSharePrice
530
    ) internal returns (bool) {
531
        return
101,808✔
532
            _distributeExcessIdleSafe(
101,808✔
533
                _vaultSharePrice,
534
                LPMath.SHARE_PROCEEDS_MAX_ITERATIONS
535
            );
536
    }
537

538
    /// @dev Distribute as much of the excess idle as possible to the withdrawal
539
    ///      pool while holding the LP share price constant.
540
    /// @param _vaultSharePrice The current vault share price.
541
    /// @param _maxIterations The number of iterations to use in the Newton's
542
    ///        method component of `_distributeExcessIdleSafe`. This defaults to
543
    ///        `LPMath.SHARE_PROCEEDS_MAX_ITERATIONS` if the specified value is
544
    ///        smaller than the constant.
545
    /// @return A failure flag indicating if the calculation succeeded.
546
    function _distributeExcessIdleSafe(
547
        uint256 _vaultSharePrice,
548
        uint256 _maxIterations
549
    ) internal returns (bool) {
550
        // If there are no withdrawal shares, then there is nothing to
551
        // distribute.
552
        uint256 withdrawalSharesTotalSupply = _totalSupply[
167,610✔
553
            AssetId._WITHDRAWAL_SHARE_ASSET_ID
554
        ] - _withdrawPool.readyToWithdraw;
555
        if (withdrawalSharesTotalSupply == 0) {
111,740✔
556
            return true;
106,342✔
557
        }
558

559
        // If there is no excess idle, then there is nothing to distribute.
560
        uint256 idle = _calculateIdleShareReserves(_vaultSharePrice);
8,097✔
561
        if (idle == 0) {
5,398✔
562
            return true;
598✔
563
        }
564

565
        // Get the distribute excess idle parameters. If this fails for some
566
        // we return a failure flag so that the caller can handle the failure.
567
        (
4,800✔
568
            LPMath.DistributeExcessIdleParams memory params,
569
            bool success
570
        ) = _getDistributeExcessIdleParamsSafe(
4,800✔
571
                idle,
572
                withdrawalSharesTotalSupply,
573
                _vaultSharePrice
574
            );
575
        if (!success) {
4,800✔
576
            return false;
×
577
        }
578

579
        // Calculate the amount of withdrawal shares that should be redeemed
580
        // and their share proceeds.
581
        (uint256 withdrawalSharesRedeemed, uint256 shareProceeds) = LPMath
7,200✔
582
            .calculateDistributeExcessIdle(params, _maxIterations);
583

584
        // Remove the withdrawal pool proceeds from the reserves.
585
        success = _updateLiquiditySafe(-shareProceeds.toInt256());
4,800✔
586
        if (!success) {
4,800✔
587
            return false;
×
588
        }
589

590
        // Update the withdrawal pool's state.
591
        _withdrawPool.readyToWithdraw += withdrawalSharesRedeemed.toUint128();
4,800✔
592
        _withdrawPool.proceeds += shareProceeds.toUint128();
4,800✔
593

594
        return true;
4,800✔
595
    }
596

597
    /// @dev Updates the pool's liquidity and holds the pool's spot price constant.
598
    /// @param _shareReservesDelta The delta that should be applied to share reserves.
599
    function _updateLiquidity(int256 _shareReservesDelta) internal {
600
        // Attempt updating the pool's liquidity and revert if the update fails.
601
        if (!_updateLiquiditySafe(_shareReservesDelta)) {
3,040✔
602
            revert IHyperdrive.UpdateLiquidityFailed();
200✔
603
        }
604
    }
605

606
    /// @dev Updates the pool's liquidity and holds the pool's spot price constant.
607
    /// @param _shareReservesDelta The delta that should be applied to share reserves.
608
    /// @return A flag indicating if the update succeeded.
609
    function _updateLiquiditySafe(
610
        int256 _shareReservesDelta
611
    ) internal returns (bool) {
612
        // Calculate the updated reserves and return false if the calculation fails.
613
        uint256 shareReserves_ = _marketState.shareReserves;
7,840✔
614
        int256 shareAdjustment_ = _marketState.shareAdjustment;
7,840✔
615
        uint256 bondReserves_ = _marketState.bondReserves;
7,840✔
616
        (
7,840✔
617
            uint256 updatedShareReserves,
618
            int256 updatedShareAdjustment,
619
            uint256 updatedBondReserves,
620
            bool success
621
        ) = LPMath.calculateUpdateLiquiditySafe(
7,840✔
622
                shareReserves_,
623
                shareAdjustment_,
624
                bondReserves_,
625
                _minimumShareReserves,
626
                _shareReservesDelta
627
            );
628
        if (!success) {
7,774✔
629
            return false;
200✔
630
        }
631

632
        // Update the market state and return true since the update was successful.
633
        if (updatedShareReserves != shareReserves_) {
7,574✔
634
            _marketState.shareReserves = updatedShareReserves.toUint128();
6,958✔
635
        }
636
        if (updatedShareAdjustment != shareAdjustment_) {
7,440✔
637
            _marketState.shareAdjustment = updatedShareAdjustment.toInt128();
2,566✔
638
        }
639
        if (updatedBondReserves != bondReserves_) {
7,440✔
640
            _marketState.bondReserves = updatedBondReserves.toUint128();
6,824✔
641
        }
642
        return true;
7,240✔
643
    }
644
}
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