Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions contracts/src/Interfaces/IOracleAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,11 @@ interface IOracleAdapter {
* @return denominator The denominator of the rate
*/
function getFXRateIfValid(address rateFeedID) external view returns (uint256 numerator, uint256 denominator);

/**
* @notice Returns true if the L2 sequencer has been up and operational for at least the specified duration.
* @param since The minimum number of seconds the L2 sequencer must have been up (e.g., 1 hours = 3600).
* @return up True if the sequencer has been up for at least `since` seconds, false otherwise
*/
function isL2SequencerUp(uint256 since) external view returns (bool up);
}
1 change: 1 addition & 0 deletions contracts/src/Interfaces/IPriceFeed.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ pragma solidity ^0.8.0;

interface IPriceFeed {
function fetchPrice() external returns (uint256);
function isL2SequencerUp() external view returns (bool);
}
72 changes: 65 additions & 7 deletions contracts/src/PriceFeeds/FXPriceFeed.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ contract FXPriceFeed is IPriceFeed, OwnableUpgradeable {
/// @notice The identifier address for the specific rate feed to query
address public rateFeedID;

// @notice Whether the rate from the OracleAdapter should be inverted
bool public invertRateFeed;

/// @notice The grace period for the L2 sequencer to recover from failure
uint256 public l2SequencerGracePeriod;

/// @notice The watchdog contract address authorized to trigger emergency shutdown
address public watchdogAddress;

Expand All @@ -35,15 +41,20 @@ contract FXPriceFeed is IPriceFeed, OwnableUpgradeable {
/// @notice Whether the contract has been shutdown due to an oracle failure
bool public isShutdown;

// @notice Whether the rate from the OracleAdapter should be inverted
bool public invertRateFeed;
/// @notice Thrown when the attempting to shutdown an already shutdown contract
error AlreadyShutdown();

/// @notice Thrown when the attempting to shutdown the contract when it is already shutdown
error IsShutDown();
/// @notice Thrown when a non-watchdog address attempts to shutdown the contract
error CallerNotWatchdog();
error OnlyWatchdog();
/// @notice Thrown when a zero address is provided as a parameter
error ZeroAddress();
/// @notice Thrown when an invalid grace period is provided
error InvalidL2SequencerGracePeriod();

/// @notice Emitted when the OracleAdapter contract is updated
/// @param _oldOracleAdapterAddress The previous OracleAdapter contract
/// @param _newOracleAdapterAddress The new OracleAdapter contract
event OracleAdapterUpdated(address indexed _oldOracleAdapterAddress, address indexed _newOracleAdapterAddress);

/// @notice Emitted when the rate feed ID is updated
/// @param _oldRateFeedID The previous rate feed ID
Expand All @@ -55,6 +66,11 @@ contract FXPriceFeed is IPriceFeed, OwnableUpgradeable {
/// @param _newInvertRateFeed The new invert rate feed flag
event InvertRateFeedUpdated(bool _oldInvertRateFeed, bool _newInvertRateFeed);

/// @notice Emitted when the L2 sequencer grace period is updated
/// @param _oldL2SequencerGracePeriod The previous L2 sequencer grace period
/// @param _newL2SequencerGracePeriod The new L2 sequencer grace period
event L2SequencerGracePeriodUpdated(uint256 indexed _oldL2SequencerGracePeriod, uint256 indexed _newL2SequencerGracePeriod);

/// @notice Emitted when the watchdog address is updated
/// @param _oldWatchdogAddress The previous watchdog address
/// @param _newWatchdogAddress The new watchdog address
Expand All @@ -78,6 +94,7 @@ contract FXPriceFeed is IPriceFeed, OwnableUpgradeable {
* @param _oracleAdapterAddress The address of the OracleAdapter contract
* @param _rateFeedID The address of the rate feed ID
* @param _invertRateFeed Whether the rate from the OracleAdapter should be inverted
* @param _l2SequencerGracePeriod The grace period for the L2 sequencer to recover from failure
* @param _borrowerOperationsAddress The address of the BorrowerOperations contract
* @param _watchdogAddress The address of the watchdog contract
* @param _initialOwner The address of the initial owner
Expand All @@ -86,6 +103,7 @@ contract FXPriceFeed is IPriceFeed, OwnableUpgradeable {
address _oracleAdapterAddress,
address _rateFeedID,
bool _invertRateFeed,
uint256 _l2SequencerGracePeriod,
address _borrowerOperationsAddress,
address _watchdogAddress,
address _initialOwner
Expand All @@ -99,6 +117,7 @@ contract FXPriceFeed is IPriceFeed, OwnableUpgradeable {
oracleAdapter = IOracleAdapter(_oracleAdapterAddress);
rateFeedID = _rateFeedID;
invertRateFeed = _invertRateFeed;
l2SequencerGracePeriod = _l2SequencerGracePeriod;
borrowerOperations = IBorrowerOperations(_borrowerOperationsAddress);
watchdogAddress = _watchdogAddress;

Expand All @@ -107,6 +126,24 @@ contract FXPriceFeed is IPriceFeed, OwnableUpgradeable {
_transferOwnership(_initialOwner);
}


/**
* @notice Sets the OracleAdapter contract
* @param _newOracleAdapterAddress The address of the new OracleAdapter contract
*/
function setOracleAdapter(address _newOracleAdapterAddress) external onlyOwner {
if (_newOracleAdapterAddress == address(0)) revert ZeroAddress();

address oldOracleAdapter = address(oracleAdapter);
oracleAdapter = IOracleAdapter(_newOracleAdapterAddress);

emit OracleAdapterUpdated(oldOracleAdapter, _newOracleAdapterAddress);
}

/**
* @notice Sets the rate feed ID to be queried
* @param _newRateFeedID The address of the new rate feed ID
*/
function setRateFeedID(address _newRateFeedID) external onlyOwner {
if (_newRateFeedID == address(0)) revert ZeroAddress();

Expand All @@ -127,6 +164,19 @@ contract FXPriceFeed is IPriceFeed, OwnableUpgradeable {
emit InvertRateFeedUpdated(oldInvertRateFeed, _invertRateFeed);
}

/**
* @notice Sets the L2 sequencer grace period
* @param _newL2SequencerGracePeriod The new L2 sequencer grace period (in seconds)
*/
function setL2SequencerGracePeriod(uint256 _newL2SequencerGracePeriod) external onlyOwner {
if (_newL2SequencerGracePeriod == 0) revert InvalidL2SequencerGracePeriod();

uint256 oldL2SequencerGracePeriod = l2SequencerGracePeriod;
l2SequencerGracePeriod = _newL2SequencerGracePeriod;

emit L2SequencerGracePeriodUpdated(oldL2SequencerGracePeriod, _newL2SequencerGracePeriod);
}

/**
* @notice Sets the watchdog address
* @param _newWatchdogAddress The address of the new watchdog contract
Expand All @@ -140,6 +190,14 @@ contract FXPriceFeed is IPriceFeed, OwnableUpgradeable {
emit WatchdogAddressUpdated(oldWatchdogAddress, _newWatchdogAddress);
}

/**
* @notice Checks if the L2 sequencer is up and the grace period has passed
* @return True if the L2 sequencer is up and the grace period has passed, false otherwise
*/
function isL2SequencerUp() public view returns (bool) {
return oracleAdapter.isL2SequencerUp(l2SequencerGracePeriod);
}

/**
* @notice Fetches the price of the FX rate, if valid
* @dev If the contract is shutdown due to oracle failure, the last valid price is returned
Expand Down Expand Up @@ -175,8 +233,8 @@ contract FXPriceFeed is IPriceFeed, OwnableUpgradeable {
* - The shutdown state is permanent and cannot be reversed
*/
function shutdown() external {
if (isShutdown) revert IsShutDown();
if (msg.sender != watchdogAddress) revert CallerNotWatchdog();
if (isShutdown) revert AlreadyShutdown();
if (msg.sender != watchdogAddress) revert OnlyWatchdog();

isShutdown = true;
borrowerOperations.shutdownFromOracleFailure();
Expand Down
8 changes: 8 additions & 0 deletions contracts/src/TroveManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ contract TroveManager is LiquityBase, ITroveManager, ITroveEvents {
error NotEnoughBoldBalance();
error MinCollNotReached(uint256 _coll);
error BatchSharesRatioTooHigh();
error L2SequencerDown();

// --- Events ---

Expand Down Expand Up @@ -402,6 +403,7 @@ contract TroveManager is LiquityBase, ITroveManager, ITroveEvents {
* Attempt to liquidate a custom list of troves provided by the caller.
*/
function batchLiquidateTroves(uint256[] memory _troveArray) public override {
_requireL2SequencerIsUp();
if (_troveArray.length == 0) {
revert EmptyData();
}
Expand Down Expand Up @@ -1198,6 +1200,12 @@ contract TroveManager is LiquityBase, ITroveManager, ITroveEvents {
}
}

function _requireL2SequencerIsUp() internal view {
if (!priceFeed.isL2SequencerUp()) {
revert L2SequencerDown();
}
}

// --- Trove property getters ---

function getUnbackedPortionPriceAndRedeemability() external returns (uint256, uint256, bool) {
Expand Down
Loading