diff --git a/src/KillSwitchOracle.sol b/src/KillSwitchOracle.sol index d34a37b..e48ad08 100644 --- a/src/KillSwitchOracle.sol +++ b/src/KillSwitchOracle.sol @@ -94,17 +94,18 @@ contract KillSwitchOracle is IKillSwitchOracle, Ownable { /******************************************************************************************************************/ function trigger(address oracle) external override { - require(!triggered, "KillSwitchOracle/already-triggered"); + // Once triggered, the kill switch can be used to disable all borrowing until it is reset + if (!triggered) { + uint256 threshold = oracleThresholds[oracle]; + require(threshold != 0, "KillSwitchOracle/oracle-does-not-exist"); - uint256 threshold = oracleThresholds[oracle]; - require(threshold != 0, "KillSwitchOracle/oracle-does-not-exist"); + int256 price = AggregatorInterface(oracle).latestAnswer(); + require(price > 0, "KillSwitchOracle/invalid-price"); + require(uint256(price) <= threshold, "KillSwitchOracle/price-above-threshold"); - int256 price = AggregatorInterface(oracle).latestAnswer(); - require(price > 0, "KillSwitchOracle/invalid-price"); - require(uint256(price) <= threshold, "KillSwitchOracle/price-above-threshold"); - - triggered = true; - emit Trigger(oracle, threshold, uint256(price)); + triggered = true; + emit Trigger(oracle, threshold, uint256(price)); + } address[] memory assets = pool.getReservesList(); for (uint256 i = 0; i < assets.length; i++) { diff --git a/src/interfaces/IKillSwitchOracle.sol b/src/interfaces/IKillSwitchOracle.sol index 9e6ed14..94e00c9 100644 --- a/src/interfaces/IKillSwitchOracle.sol +++ b/src/interfaces/IKillSwitchOracle.sol @@ -89,7 +89,8 @@ interface IKillSwitchOracle { function disableOracle(address oracle) external; /** - * @dev Resets the trigger, allowing the kill switch to be triggered again. + * @dev Reset the module so that the kill switch can only be triggered if one of + * the oracles is below the threshold. */ function reset() external; @@ -132,9 +133,10 @@ interface IKillSwitchOracle { * @notice Permissionless function to trigger the kill switch. * @dev If the kill switch has not been triggered, the oracle threshold has been defined, * and the oracle is below the threshold, the kill switch is triggered. This will - * disable borrowing on all assets. This function can only be called once and will - * require a call to `reset()` by the owner to be called again. - * @param oracle The address of the oracle which is below the threshold. + * disable borrowing on all assets. If the kill switch has been triggered, this + * function will allow disabling borrowing on all assets until the kill switch is reset. + * @param oracle The address of the oracle which is below the threshold. If the kill switch + * has been triggered, this parameter is ignored. */ function trigger(address oracle) external; diff --git a/test/KillSwitchOracle.t.sol b/test/KillSwitchOracle.t.sol index 1de2b2c..ae95d86 100644 --- a/test/KillSwitchOracle.t.sol +++ b/test/KillSwitchOracle.t.sol @@ -264,15 +264,6 @@ contract KillSwitchOracleTriggerTests is KillSwitchOracleTestBase { using ReserveConfiguration for DataTypes.ReserveConfigurationMap; - function test_trigger_alreadyTriggered() public { - vm.prank(owner); - killSwitchOracle.setOracle(address(oracle1), 1e8); - killSwitchOracle.trigger(address(oracle1)); - - vm.expectRevert("KillSwitchOracle/already-triggered"); - killSwitchOracle.trigger(address(oracle1)); - } - function test_trigger_doesNotExist() public { vm.prank(owner); vm.expectRevert("KillSwitchOracle/oracle-does-not-exist"); @@ -310,43 +301,43 @@ contract KillSwitchOracleTriggerTests is KillSwitchOracleTestBase { ReserveConfigParams[5] memory reserves = [ // Asset with borrow enabled (Ex. ETH, wstETH, DAI) ReserveConfigParams({ - asset: asset1, - active: true, - frozen: false, - paused: false, - borrowingEnabled: true + asset: asset1, + active: true, + frozen: false, + paused: false, + borrowingEnabled: true }), // Collateral-only asset (Ex. sDAI) ReserveConfigParams({ - asset: asset2, - active: true, - frozen: false, - paused: false, - borrowingEnabled: false + asset: asset2, + active: true, + frozen: false, + paused: false, + borrowingEnabled: false }), // Frozen asset (Ex. GNO) ReserveConfigParams({ - asset: asset3, - active: true, - frozen: true, - paused: false, - borrowingEnabled: true + asset: asset3, + active: true, + frozen: true, + paused: false, + borrowingEnabled: true }), // Paused asset ReserveConfigParams({ - asset: asset4, - active: true, - frozen: false, - paused: true, - borrowingEnabled: true + asset: asset4, + active: true, + frozen: false, + paused: true, + borrowingEnabled: true }), // Inactive asset ReserveConfigParams({ - asset: asset5, - active: false, - frozen: false, - paused: false, - borrowingEnabled: false + asset: asset5, + active: false, + frozen: false, + paused: false, + borrowingEnabled: false }) ]; @@ -373,6 +364,21 @@ contract KillSwitchOracleTriggerTests is KillSwitchOracleTestBase { for (uint256 i = 0; i < reserves.length; i++) { _assertReserve(reserves[i]); } + + // New reserve has been added after the trigger (perhaps a pending spell) + _initReserve(ReserveConfigParams({ + asset: asset5, + active: true, + frozen: false, + paused: false, + borrowingEnabled: true + })); + + // Should be able to disable borrowing on this new asset + vm.expectEmit(address(killSwitchOracle)); + emit BorrowDisabled(asset5); + vm.prank(randomAddress); // Permissionless call + killSwitchOracle.trigger(address(0)); // Second trigger oracle address can be anything } function _initReserve(ReserveConfigParams memory params) internal {