diff --git a/contracts/src/deployers/HyperdriveDeployerCoordinator.sol b/contracts/src/deployers/HyperdriveDeployerCoordinator.sol index 98c98a4db..a39991250 100644 --- a/contracts/src/deployers/HyperdriveDeployerCoordinator.sol +++ b/contracts/src/deployers/HyperdriveDeployerCoordinator.sol @@ -8,9 +8,6 @@ import { IHyperdriveTargetDeployer } from "../interfaces/IHyperdriveTargetDeploy import { VERSION } from "../libraries/Constants.sol"; import { ONE } from "../libraries/FixedPointMath.sol"; -// FIXME: Add the maximumAddLiquidityAPRDelta to `deployAndInitialize`. We need -// min and max parameters for this delta. -// /// @author DELV /// @title HyperdriveDeployerCoordinator /// @notice This Hyperdrive deployer coordinates the process of deploying the @@ -505,8 +502,7 @@ abstract contract HyperdriveDeployerCoordinator is _config.minimumShareReserves = _deployConfig.minimumShareReserves; _config.minimumTransactionAmount = _deployConfig .minimumTransactionAmount; - _config.maximumAddLiquidityAPRDelta = _deployConfig - .maximumAddLiquidityAPRDelta; + _config.circuitBreakerDelta = _deployConfig.circuitBreakerDelta; _config.positionDuration = _deployConfig.positionDuration; _config.checkpointDuration = _deployConfig.checkpointDuration; _config.timeStretch = _deployConfig.timeStretch; diff --git a/contracts/src/external/HyperdriveTarget0.sol b/contracts/src/external/HyperdriveTarget0.sol index fade59c02..461f0d12f 100644 --- a/contracts/src/external/HyperdriveTarget0.sol +++ b/contracts/src/external/HyperdriveTarget0.sol @@ -298,7 +298,7 @@ abstract contract HyperdriveTarget0 is initialVaultSharePrice: _initialVaultSharePrice, minimumShareReserves: _minimumShareReserves, minimumTransactionAmount: _minimumTransactionAmount, - maximumAddLiquidityAPRDelta: _maximumAddLiquidityAPRDelta, + circuitBreakerDelta: _circuitBreakerDelta, positionDuration: _positionDuration, checkpointDuration: _checkpointDuration, timeStretch: _timeStretch, diff --git a/contracts/src/factory/HyperdriveFactory.sol b/contracts/src/factory/HyperdriveFactory.sol index e09db864f..ef238a69a 100644 --- a/contracts/src/factory/HyperdriveFactory.sol +++ b/contracts/src/factory/HyperdriveFactory.sol @@ -8,9 +8,6 @@ import { FixedPointMath, ONE } from "../libraries/FixedPointMath.sol"; import { VERSION } from "../libraries/Constants.sol"; import { HyperdriveMath } from "../libraries/HyperdriveMath.sol"; -// FIXME: Add the maximumAddLiquidityAPRDelta to `deployAndInitialize`. We need -// min and max parameters for this delta. -// /// @author DELV /// @title HyperdriveFactory /// @notice Deploys hyperdrive instances and initializes them. It also holds a @@ -76,6 +73,14 @@ contract HyperdriveFactory is IHyperdriveFactory { /// deployments. uint256 public maxPositionDuration; + /// @notice The minimum circuit breaker delta that can be used by + /// new deployments. + uint256 public minCircuitBreakerDelta; + + /// @notice The maximum circuit breaker delta that can be used by + /// new deployments. + uint256 public maxCircuitBreakerDelta; + /// @notice The minimum fixed APR that can be used by new deployments. uint256 public minFixedAPR; @@ -122,6 +127,12 @@ contract HyperdriveFactory is IHyperdriveFactory { /// @dev The maximum position duration that can be used in new /// deployments. uint256 maxPositionDuration; + /// @dev The minimum circuit breaker delta that can be used in new + /// deployments. + uint256 minCircuitBreakerDelta; + /// @dev The maximum circuit breaker delta that can be used in new + /// deployments. + uint256 maxCircuitBreakerDelta; /// @dev The minimum fixed APR that can be used in new deployments. uint256 minFixedAPR; /// @dev The maximum fixed APR that can be used in new deployments. @@ -236,6 +247,17 @@ contract HyperdriveFactory is IHyperdriveFactory { } maxPositionDuration = _factoryConfig.maxPositionDuration; + // Ensure that the minimum circuit breaker delta is greater than or + // equal to the maximum circuit breaker delta. + if ( + _factoryConfig.minCircuitBreakerDelta > + _factoryConfig.maxCircuitBreakerDelta + ) { + revert IHyperdriveFactory.InvalidCircuitBreakerDelta(); + } + minCircuitBreakerDelta = _factoryConfig.minCircuitBreakerDelta; + maxCircuitBreakerDelta = _factoryConfig.maxCircuitBreakerDelta; + // Ensure that the minimum fixed APR is less than or equal to the // maximum fixed APR. if (_factoryConfig.minFixedAPR > _factoryConfig.maxFixedAPR) { @@ -470,6 +492,40 @@ contract HyperdriveFactory is IHyperdriveFactory { emit MinPositionDurationUpdated(_minPositionDuration); } + /// @notice Allows governance to update the maximum circuit breaker delta. + /// @param _maxCircuitBreakerDelta The new maximum circuit breaker delta. + function updateMaxCircuitBreakerDelta( + uint256 _maxCircuitBreakerDelta + ) external onlyGovernance { + // Ensure that the maximum circuit breaker delta is greater than or + // equal to the minimum circuit breaker delta. + if (_maxCircuitBreakerDelta < minCircuitBreakerDelta) { + revert IHyperdriveFactory.InvalidMaxCircuitBreakerDelta(); + } + + // Update the maximum circuit breaker delta and emit an event. + maxCircuitBreakerDelta = _maxCircuitBreakerDelta; + emit MaxCircuitBreakerDeltaUpdated(_maxCircuitBreakerDelta); + } + + /// @notice Allows governance to update the minimum circuit breaker delta. + /// @param _minCircuitBreakerDelta The new minimum circuit breaker delta. + function updateMinCircuitBreakerDelta( + uint256 _minCircuitBreakerDelta + ) external onlyGovernance { + // Ensure that the minimum position duration is greater than or equal + // to the maximum checkpoint duration and is a multiple of the + // checkpoint duration resolution. Also ensure that the minimum position + // duration is less than or equal to the maximum position duration. + if (_minCircuitBreakerDelta > maxCircuitBreakerDelta) { + revert IHyperdriveFactory.InvalidMinCircuitBreakerDelta(); + } + + // Update the minimum circuit breaker delta and emit an event. + minCircuitBreakerDelta = _minCircuitBreakerDelta; + emit MinCircuitBreakerDeltaUpdated(_minCircuitBreakerDelta); + } + /// @notice Allows governance to update the maximum fixed APR. /// @param _maxFixedAPR The new maximum fixed APR. function updateMaxFixedAPR(uint256 _maxFixedAPR) external onlyGovernance { @@ -902,6 +958,15 @@ contract HyperdriveFactory is IHyperdriveFactory { revert IHyperdriveFactory.InvalidPositionDuration(); } + // Ensure that the specified circuit breaker delta is within the minimum + // and maximum circuit breaker deltas. + if ( + _config.circuitBreakerDelta < minCircuitBreakerDelta || + _config.circuitBreakerDelta > maxCircuitBreakerDelta + ) { + revert IHyperdriveFactory.InvalidCircuitBreakerDelta(); + } + // Ensure that the specified fees are within the minimum and maximum // fees. The flat fee is annualized so that it is consistent across all // term lengths. diff --git a/contracts/src/interfaces/IHyperdrive.sol b/contracts/src/interfaces/IHyperdrive.sol index a977d40d2..945155f82 100644 --- a/contracts/src/interfaces/IHyperdrive.sol +++ b/contracts/src/interfaces/IHyperdrive.sol @@ -93,7 +93,7 @@ interface IHyperdrive is /// @dev The maximum delta between the last checkpoint's weighted spot /// APR and the current spot APR for an LP to add liquidity. This /// protects LPs from sandwich attacks. - uint256 maximumAddLiquidityAPRDelta; + uint256 circuitBreakerDelta; /// @dev The duration of a position prior to maturity. uint256 positionDuration; /// @dev The duration of a checkpoint. @@ -130,7 +130,7 @@ interface IHyperdrive is /// @dev The maximum delta between the last checkpoint's weighted spot /// APR and the current spot APR for an LP to add liquidity. This /// protects LPs from sandwich attacks. - uint256 maximumAddLiquidityAPRDelta; + uint256 circuitBreakerDelta; /// @dev The duration of a position prior to maturity. uint256 positionDuration; /// @dev The duration of a checkpoint. diff --git a/contracts/src/interfaces/IHyperdriveFactory.sol b/contracts/src/interfaces/IHyperdriveFactory.sol index a77aaed05..39b506f17 100644 --- a/contracts/src/interfaces/IHyperdriveFactory.sol +++ b/contracts/src/interfaces/IHyperdriveFactory.sol @@ -62,6 +62,12 @@ interface IHyperdriveFactory { /// @notice Emitted when the minimum position duration is updated. event MinPositionDurationUpdated(uint256 newMinPositionDuration); + /// @notice Emitted when the maximum circuit breaker delta is updated. + event MaxCircuitBreakerDeltaUpdated(uint256 newMaxCircuitBreakerDelta); + + /// @notice Emitted when the minimum circuit breaker delta is updated. + event MinCircuitBreakerDeltaUpdated(uint256 newMinCircuitBreakerDelta); + /// @notice Emitted when the maximum fixed APR is updated. event MaxFixedAPRUpdated(uint256 newMaxFixedAPR); @@ -168,6 +174,21 @@ interface IHyperdriveFactory { /// maximum position durations. error InvalidPositionDuration(); + /// @notice Thrown when governance attempts to set the maximum circuit + /// breaker delta to a value that is less than the minimum + /// circuit breaker delta. + error InvalidMaxCircuitBreakerDelta(); + + /// @notice Thrown when governance attempts to set the minimum circuit + /// breaker delta to a value that is greater than the maximum + /// circuit breaker delta. + error InvalidMinCircuitBreakerDelta(); + + /// @notice Thrown when the circuit breaker delta passed to + /// `deployAndInitialize` doesn't fall within the range specified by + /// the minimum and maximum circuit breaker delta. + error InvalidCircuitBreakerDelta(); + /// @notice Thrown when governance attempts to set the maximum fixed APR to /// a value that is smaller than the minimum fixed APR. error InvalidMaxFixedAPR(); diff --git a/contracts/src/internal/HyperdriveLP.sol b/contracts/src/internal/HyperdriveLP.sol index e6c6c561a..0583ddd4d 100644 --- a/contracts/src/internal/HyperdriveLP.sol +++ b/contracts/src/internal/HyperdriveLP.sol @@ -27,9 +27,6 @@ abstract contract HyperdriveLP is using SafeCast for int256; using SafeCast for uint256; - // FIXME: Make sure this checkpoint's and the previous checkpoint's weighted - // spot prices are set to non-zero values. - // /// @dev Allows the first LP to initialize the market with a target APR. /// @param _contribution The amount of capital to supply. The units of this /// quantity are either base or vault shares, depending on the value @@ -146,8 +143,6 @@ abstract contract HyperdriveLP is return lpShares; } - // FIXME: Add circuit breakers. - // /// @dev Allows LPs to supply liquidity for LP shares. /// @param _contribution The amount of capital to supply. The units of this /// quantity are either base or vault shares, depending on the value @@ -215,9 +210,9 @@ abstract contract HyperdriveLP is _positionDuration ); if ( - apr > weightedSpotAPR + _maximumAddLiquidityAPRDelta || - (weightedSpotAPR > _maximumAddLiquidityAPRDelta && - apr < weightedSpotAPR - _maximumAddLiquidityAPRDelta) + apr > weightedSpotAPR + _circuitBreakerDelta || + (weightedSpotAPR > _circuitBreakerDelta && + apr < weightedSpotAPR - _circuitBreakerDelta) ) { revert IHyperdrive.CircuitBreakerTriggered(); } diff --git a/contracts/src/internal/HyperdriveStorage.sol b/contracts/src/internal/HyperdriveStorage.sol index 81f4b45a0..872ac5a16 100644 --- a/contracts/src/internal/HyperdriveStorage.sol +++ b/contracts/src/internal/HyperdriveStorage.sol @@ -66,7 +66,7 @@ abstract contract HyperdriveStorage is ReentrancyGuard { /// @dev The maximum delta between the last checkpoint's weighted spot APR /// and the current spot APR for an LP to add liquidity. This protects /// LPs from sandwich attacks. - uint256 internal immutable _maximumAddLiquidityAPRDelta; + uint256 internal immutable _circuitBreakerDelta; /// @dev The state of the market. This includes the reserves, buffers, and /// other data used to price trades and maintain solvency. @@ -159,7 +159,7 @@ abstract contract HyperdriveStorage is ReentrancyGuard { // spot APR and the weighted spot APR from the last checkpoint for an // LP to add liquidity. This mitigates the possibility of LP sandwich // attacks by making them economically infeasible to pull off. - _maximumAddLiquidityAPRDelta = _config.maximumAddLiquidityAPRDelta; + _circuitBreakerDelta = _config.circuitBreakerDelta; // Initialize the time configurations. There must be at least one // checkpoint per term to avoid having a position duration of zero. diff --git a/test/instances/erc4626/ERC4626Hyperdrive.t.sol b/test/instances/erc4626/ERC4626Hyperdrive.t.sol index 853a376c8..69300a17c 100644 --- a/test/instances/erc4626/ERC4626Hyperdrive.t.sol +++ b/test/instances/erc4626/ERC4626Hyperdrive.t.sol @@ -87,6 +87,10 @@ contract ERC4626HyperdriveTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + // NOTE: This is a high max circuit breaker delta to ensure that + // trading during tests isn't impeded by the circuit breaker. + maxCircuitBreakerDelta: 2e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.005e18, @@ -141,7 +145,7 @@ contract ERC4626HyperdriveTest is HyperdriveTest { initialVaultSharePrice: ONE, minimumShareReserves: ONE, minimumTransactionAmount: 0.001e18, - maximumAddLiquidityAPRDelta: 1e18, + circuitBreakerDelta: 1e18, positionDuration: 365 days, checkpointDuration: 1 days, timeStretch: ONE.divDown(22.186877016851916266e18), @@ -325,7 +329,7 @@ contract ERC4626HyperdriveTest is HyperdriveTest { linkerCodeHash: factory.linkerCodeHash(), minimumShareReserves: ONE, minimumTransactionAmount: 0.001e18, - maximumAddLiquidityAPRDelta: 2e18, + circuitBreakerDelta: 2e18, positionDuration: 365 days, checkpointDuration: 1 days, timeStretch: 0, @@ -447,7 +451,7 @@ contract ERC4626HyperdriveTest is HyperdriveTest { linkerCodeHash: factory.linkerCodeHash(), minimumShareReserves: ONE, minimumTransactionAmount: 0.001e18, - maximumAddLiquidityAPRDelta: 2e18, + circuitBreakerDelta: 2e18, positionDuration: 365 days, checkpointDuration: 1 days, timeStretch: 0, diff --git a/test/instances/erc4626/ERC4626Validation.t.sol b/test/instances/erc4626/ERC4626Validation.t.sol index accff0700..e88fb8af5 100644 --- a/test/instances/erc4626/ERC4626Validation.t.sol +++ b/test/instances/erc4626/ERC4626Validation.t.sol @@ -64,6 +64,10 @@ abstract contract ERC4626ValidationTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + // NOTE: This is a high max circuit breaker delta to ensure that + // trading during tests isn't impeded by the circuit breaker. + maxCircuitBreakerDelta: 2e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.005e18, diff --git a/test/instances/erc4626/Sweep.t.sol b/test/instances/erc4626/Sweep.t.sol index 741ebd542..f07208e9c 100644 --- a/test/instances/erc4626/Sweep.t.sol +++ b/test/instances/erc4626/Sweep.t.sol @@ -58,7 +58,7 @@ contract SweepTest is BaseTest, IHyperdriveEvents { initialVaultSharePrice: ONE, minimumShareReserves: ONE, minimumTransactionAmount: 0.001e18, - maximumAddLiquidityAPRDelta: 1e18, + circuitBreakerDelta: 1e18, positionDuration: 365 days, checkpointDuration: 1 days, timeStretch: HyperdriveMath.calculateTimeStretch(0.01e18, 365 days), diff --git a/test/instances/erc4626/UsdcERC4626.t.sol b/test/instances/erc4626/UsdcERC4626.t.sol index 0c61075e9..516bd521d 100644 --- a/test/instances/erc4626/UsdcERC4626.t.sol +++ b/test/instances/erc4626/UsdcERC4626.t.sol @@ -81,6 +81,10 @@ contract UsdcERC4626 is ERC4626ValidationTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + // NOTE: This is a high max circuit breaker delta to ensure that + // trading during tests isn't impeded by the circuit breaker. + maxCircuitBreakerDelta: 2e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.005e18, diff --git a/test/instances/steth/Sweep.t.sol b/test/instances/steth/Sweep.t.sol index 6f8669626..4355c4d65 100644 --- a/test/instances/steth/Sweep.t.sol +++ b/test/instances/steth/Sweep.t.sol @@ -58,7 +58,7 @@ contract SweepTest is BaseTest, IHyperdriveEvents { initialVaultSharePrice: ONE, minimumShareReserves: 1e15, minimumTransactionAmount: 1e15, - maximumAddLiquidityAPRDelta: 1e18, + circuitBreakerDelta: 1e18, positionDuration: 365 days, checkpointDuration: 1 days, timeStretch: HyperdriveMath.calculateTimeStretch(0.01e18, 365 days), diff --git a/test/integrations/deployers/ERC4626DeployerCoordinator.t.sol b/test/integrations/deployers/ERC4626DeployerCoordinator.t.sol index fce23549f..ff28fc4c6 100644 --- a/test/integrations/deployers/ERC4626DeployerCoordinator.t.sol +++ b/test/integrations/deployers/ERC4626DeployerCoordinator.t.sol @@ -71,6 +71,8 @@ contract ERC4626DeployerCoordinatorTest is DeployerCoordinatorTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.005e18, diff --git a/test/integrations/deployers/RethDeployerCoordinator.t.sol b/test/integrations/deployers/RethDeployerCoordinator.t.sol index 0f5591428..391d4ec43 100644 --- a/test/integrations/deployers/RethDeployerCoordinator.t.sol +++ b/test/integrations/deployers/RethDeployerCoordinator.t.sol @@ -63,6 +63,8 @@ contract RethDeployerCoordinatorTest is DeployerCoordinatorTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.005e18, diff --git a/test/integrations/deployers/StethDeployerCoordinator.t.sol b/test/integrations/deployers/StethDeployerCoordinator.t.sol index 6ca9be2da..b39bae782 100644 --- a/test/integrations/deployers/StethDeployerCoordinator.t.sol +++ b/test/integrations/deployers/StethDeployerCoordinator.t.sol @@ -63,6 +63,8 @@ contract StethDeployerCoordinatorTest is DeployerCoordinatorTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.005e18, diff --git a/test/integrations/factory/HyperdriveFactory.t.sol b/test/integrations/factory/HyperdriveFactory.t.sol index ed32d5276..388703089 100644 --- a/test/integrations/factory/HyperdriveFactory.t.sol +++ b/test/integrations/factory/HyperdriveFactory.t.sol @@ -69,6 +69,10 @@ contract HyperdriveFactoryTest is HyperdriveTest { event MinPositionDurationUpdated(uint256 newMinPositionDuration); + event MaxCircuitBreakerDeltaUpdated(uint256 newMaxCircuitBreakerDelta); + + event MinCircuitBreakerDeltaUpdated(uint256 newMinCircuitBreakerDelta); + event MaxFixedAPRUpdated(uint256 newMaxFixedAPR); event MinFixedAPRUpdated(uint256 newMinFixedAPR); @@ -101,6 +105,8 @@ contract HyperdriveFactoryTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.005e18, @@ -144,6 +150,8 @@ contract HyperdriveFactoryTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.01e18, @@ -174,6 +182,8 @@ contract HyperdriveFactoryTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.01e18, @@ -204,6 +214,8 @@ contract HyperdriveFactoryTest is HyperdriveTest { maxCheckpointDuration: 7 hours, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.01e18, @@ -234,6 +246,8 @@ contract HyperdriveFactoryTest is HyperdriveTest { maxCheckpointDuration: 8.5 hours, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.01e18, @@ -261,6 +275,8 @@ contract HyperdriveFactoryTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 8 hours, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.01e18, @@ -289,6 +305,8 @@ contract HyperdriveFactoryTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days + 30 minutes, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.01e18, @@ -316,6 +334,38 @@ contract HyperdriveFactoryTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 6 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, + minFixedAPR: 0.001e18, + maxFixedAPR: 0.5e18, + minTimeStretchAPR: 0.01e18, + maxTimeStretchAPR: 0.5e18, + minFees: IHyperdrive.Fees(0, 0, 0, 0), + maxFees: IHyperdrive.Fees(ONE, ONE, ONE, ONE), + linkerFactory: address(0), + linkerCodeHash: bytes32(0) + }), + NAME + ); + + // Ensure that the factory can't be constructed with a minimum + // circuit breaker delta that is greater than the maximum circuit + // breaker delta. + vm.expectRevert(IHyperdriveFactory.InvalidCircuitBreakerDelta.selector); + new HyperdriveFactory( + HyperdriveFactory.FactoryConfig({ + governance: alice, + hyperdriveGovernance: bob, + feeCollector: feeCollector, + sweepCollector: sweepCollector, + defaultPausers: defaults, + checkpointDurationResolution: 1 hours, + minCheckpointDuration: 8 hours, + maxCheckpointDuration: 1 days, + minPositionDuration: 7 days, + maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.7e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.01e18, @@ -344,6 +394,8 @@ contract HyperdriveFactoryTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days + 30 minutes, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.01e18, @@ -371,6 +423,8 @@ contract HyperdriveFactoryTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.01e18, @@ -398,6 +452,8 @@ contract HyperdriveFactoryTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.01e18, @@ -425,6 +481,8 @@ contract HyperdriveFactoryTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.01e18, @@ -452,6 +510,8 @@ contract HyperdriveFactoryTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.01e18, @@ -479,6 +539,8 @@ contract HyperdriveFactoryTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.01e18, @@ -506,6 +568,8 @@ contract HyperdriveFactoryTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.01e18, @@ -533,6 +597,8 @@ contract HyperdriveFactoryTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.01e18, @@ -560,6 +626,8 @@ contract HyperdriveFactoryTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.01e18, @@ -586,6 +654,8 @@ contract HyperdriveFactoryTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.01e18, @@ -611,6 +681,14 @@ contract HyperdriveFactoryTest is HyperdriveTest { assertEq(factory.maxCheckpointDuration(), config.maxCheckpointDuration); assertEq(factory.minPositionDuration(), config.minPositionDuration); assertEq(factory.maxPositionDuration(), config.maxPositionDuration); + assertEq( + factory.minCircuitBreakerDelta(), + config.minCircuitBreakerDelta + ); + assertEq( + factory.maxCircuitBreakerDelta(), + config.maxCircuitBreakerDelta + ); assertEq(factory.minTimeStretchAPR(), config.minTimeStretchAPR); assertEq(factory.maxTimeStretchAPR(), config.maxTimeStretchAPR); assertEq( @@ -1024,6 +1102,66 @@ contract HyperdriveFactoryTest is HyperdriveTest { assertEq(factory.minPositionDuration(), newMinPositionDuration); } + function test_updateMaxCircuitBreakerDelta() external { + uint256 newMaxCircuitBreakerDelta = 0.6e18; + + // Ensure that the max circuit breaker delta can't be updated by someone + // other than the current governance. + vm.stopPrank(); + vm.startPrank(bob); + vm.expectRevert(IHyperdriveFactory.Unauthorized.selector); + factory.updateMaxCircuitBreakerDelta(newMaxCircuitBreakerDelta); + + // Ensure that the max circuit breaker delta can't be set to a value + // less than the min circuit breaker delta. + vm.stopPrank(); + vm.startPrank(factory.governance()); + uint256 minCircuitBreakerDelta = factory.minCircuitBreakerDelta(); + vm.expectRevert( + IHyperdriveFactory.InvalidMaxCircuitBreakerDelta.selector + ); + factory.updateMaxCircuitBreakerDelta(minCircuitBreakerDelta - 1); + + // Ensure that the max circuit breaker delta was updated successfully + // and that the correct event was emitted. + vm.stopPrank(); + vm.startPrank(factory.governance()); + vm.expectEmit(true, true, true, true); + emit MaxCircuitBreakerDeltaUpdated(newMaxCircuitBreakerDelta); + factory.updateMaxCircuitBreakerDelta(newMaxCircuitBreakerDelta); + assertEq(factory.maxCircuitBreakerDelta(), newMaxCircuitBreakerDelta); + } + + function test_updateMinCircuitBreakerDelta() external { + uint256 newMinCircuitBreakerDelta = 0.15e18; + + // Ensure that the min circuit breaker delta can't be updated by someone + // other than the current governance. + vm.stopPrank(); + vm.startPrank(bob); + vm.expectRevert(IHyperdriveFactory.Unauthorized.selector); + factory.updateMinCircuitBreakerDelta(newMinCircuitBreakerDelta); + + // Ensure that the min circuit breaker delta can't be set to a value + // greater than the max circuit breaker delta. + vm.stopPrank(); + vm.startPrank(factory.governance()); + uint256 maxCircuitBreakerDelta = factory.maxCircuitBreakerDelta(); + vm.expectRevert( + IHyperdriveFactory.InvalidMinCircuitBreakerDelta.selector + ); + factory.updateMinCircuitBreakerDelta(maxCircuitBreakerDelta + 1); + + // Ensure that the min circuit breaker delta was updated successfully + // and that the correct event was emitted. + vm.stopPrank(); + vm.startPrank(factory.governance()); + vm.expectEmit(true, true, true, true); + emit MinCircuitBreakerDeltaUpdated(newMinCircuitBreakerDelta); + factory.updateMinCircuitBreakerDelta(newMinCircuitBreakerDelta); + assertEq(factory.minCircuitBreakerDelta(), newMinCircuitBreakerDelta); + } + function test_updateMaxFixedAPR() external { uint256 newMaxFixedAPR = 0.25e18; @@ -1500,7 +1638,7 @@ contract HyperdriveFactoryTest is HyperdriveTest { vaultSharesToken: IERC20(address(lido)), minimumShareReserves: 1e15, minimumTransactionAmount: 1e15, - maximumAddLiquidityAPRDelta: 2e18, + circuitBreakerDelta: 0.45e18, positionDuration: 365 days, checkpointDuration: 1 days, timeStretch: 0, @@ -1720,6 +1858,62 @@ contract HyperdriveFactoryTest is HyperdriveTest { config.positionDuration = oldPositionDuration; } + // Ensure than an instance can't be deployed with a circuit breaker + // delta that is less than the minimum circuit breaker delta. + { + vm.stopPrank(); + vm.startPrank(bob); + uint256 oldCircuitBreakerDelta = config.circuitBreakerDelta; + config.circuitBreakerDelta = factory.minCircuitBreakerDelta() - 1; + vm.expectRevert( + IHyperdriveFactory.InvalidCircuitBreakerDelta.selector + ); + factory.deployAndInitialize( + deploymentId, + deployerCoordinator, + config, + extraData, + 10_000e18, + 0.02e18, + 0.05e18, + IHyperdrive.Options({ + asBase: true, + destination: bob, + extraData: new bytes(0) + }), + deploymentId + ); + config.circuitBreakerDelta = oldCircuitBreakerDelta; + } + + // Ensure than an instance can't be deployed with a circuit breaker + // delta that is greater than the maximum circuit breaker delta. + { + vm.stopPrank(); + vm.startPrank(bob); + uint256 oldCircuitBreakerDelta = config.circuitBreakerDelta; + config.circuitBreakerDelta = factory.maxCircuitBreakerDelta() + 1; + vm.expectRevert( + IHyperdriveFactory.InvalidCircuitBreakerDelta.selector + ); + factory.deployAndInitialize( + deploymentId, + deployerCoordinator, + config, + extraData, + 10_000e18, + 0.02e18, + 0.05e18, + IHyperdrive.Options({ + asBase: true, + destination: bob, + extraData: new bytes(0) + }), + deploymentId + ); + config.circuitBreakerDelta = oldCircuitBreakerDelta; + } + // Ensure than an instance can't be deployed with a fixed APR less than // the minimum. { @@ -2406,6 +2600,7 @@ contract HyperdriveFactoryTest is HyperdriveTest { config_.minimumTransactionAmount, config.minimumTransactionAmount ); + assertEq(config_.circuitBreakerDelta, config.circuitBreakerDelta); assertEq(config_.positionDuration, config.positionDuration); assertEq(config_.checkpointDuration, config.checkpointDuration); assertEq( @@ -2502,6 +2697,8 @@ contract HyperdriveFactoryBaseTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.01e18, @@ -2550,7 +2747,7 @@ contract HyperdriveFactoryBaseTest is HyperdriveTest { vaultSharesToken: IERC20(address(0)), minimumShareReserves: 1e18, minimumTransactionAmount: 1e15, - maximumAddLiquidityAPRDelta: 2e18, + circuitBreakerDelta: 0.45e18, positionDuration: 365 days, checkpointDuration: 1 days, timeStretch: 0, @@ -3018,6 +3215,8 @@ contract DeployerCoordinatorGetterTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.01e18, @@ -3219,6 +3418,8 @@ contract HyperdriveFactoryAddHyperdriveFactoryTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.01e18, @@ -3327,6 +3528,8 @@ contract HyperdriveFactoryRemoveInstanceTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + maxCircuitBreakerDelta: 0.6e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.01e18, diff --git a/test/units/ForceRevertDelegatecall.t.sol b/test/units/ForceRevertDelegatecall.t.sol index 9991a1718..82666c412 100644 --- a/test/units/ForceRevertDelegatecall.t.sol +++ b/test/units/ForceRevertDelegatecall.t.sol @@ -36,7 +36,7 @@ contract DummyHyperdrive is Hyperdrive, MockHyperdriveBase { initialVaultSharePrice: 1e18, minimumShareReserves: 1e18, minimumTransactionAmount: 1e15, - maximumAddLiquidityAPRDelta: 1e18, + circuitBreakerDelta: 1e18, positionDuration: 365 days, checkpointDuration: 1 days, timeStretch: HyperdriveMath.calculateTimeStretch( diff --git a/test/utils/HyperdriveTest.sol b/test/utils/HyperdriveTest.sol index 44bdfbefe..2e839197f 100644 --- a/test/utils/HyperdriveTest.sol +++ b/test/utils/HyperdriveTest.sol @@ -135,8 +135,7 @@ contract HyperdriveTest is IHyperdriveEvents, BaseTest { _config.minimumShareReserves = _deployConfig.minimumShareReserves; _config.minimumTransactionAmount = _deployConfig .minimumTransactionAmount; - _config.maximumAddLiquidityAPRDelta = _deployConfig - .maximumAddLiquidityAPRDelta; + _config.circuitBreakerDelta = _deployConfig.circuitBreakerDelta; _config.positionDuration = _deployConfig.positionDuration; _config.checkpointDuration = _deployConfig.checkpointDuration; _config.timeStretch = _deployConfig.timeStretch; @@ -165,7 +164,7 @@ contract HyperdriveTest is IHyperdriveEvents, BaseTest { linkerCodeHash: forwarderFactory.ERC20LINK_HASH(), minimumShareReserves: MINIMUM_SHARE_RESERVES, minimumTransactionAmount: MINIMUM_TRANSACTION_AMOUNT, - maximumAddLiquidityAPRDelta: MAXIMUM_ADD_LIQUIDITY_APR_DELTA, + circuitBreakerDelta: MAXIMUM_ADD_LIQUIDITY_APR_DELTA, positionDuration: positionDuration, checkpointDuration: CHECKPOINT_DURATION, timeStretch: HyperdriveMath.calculateTimeStretch( diff --git a/test/utils/InstanceTest.sol b/test/utils/InstanceTest.sol index 101327bd9..1185a6245 100644 --- a/test/utils/InstanceTest.sol +++ b/test/utils/InstanceTest.sol @@ -231,6 +231,10 @@ abstract contract InstanceTest is HyperdriveTest { maxCheckpointDuration: 1 days, minPositionDuration: 7 days, maxPositionDuration: 10 * 365 days, + minCircuitBreakerDelta: 0.15e18, + // NOTE: This is a high max circuit breaker delta to ensure that + // trading during tests isn't impeded by the circuit breaker. + maxCircuitBreakerDelta: 2e18, minFixedAPR: 0.001e18, maxFixedAPR: 0.5e18, minTimeStretchAPR: 0.005e18, @@ -261,7 +265,7 @@ abstract contract InstanceTest is HyperdriveTest { linkerCodeHash: factory.linkerCodeHash(), minimumShareReserves: 1e15, minimumTransactionAmount: config.minTransactionAmount, - maximumAddLiquidityAPRDelta: 2e18, + circuitBreakerDelta: 2e18, positionDuration: config.positionDuration, checkpointDuration: CHECKPOINT_DURATION, timeStretch: 0,