mirror of
https://github.com/Instadapp/fluid-contracts-public.git
synced 2024-07-29 21:57:37 +00:00
152 lines
6.2 KiB
Solidity
152 lines
6.2 KiB
Solidity
|
// SPDX-License-Identifier: BUSL-1.1
|
||
|
pragma solidity 0.8.21;
|
||
|
|
||
|
import { IFluidOracle } from "./interfaces/iFluidOracle.sol";
|
||
|
import { ErrorTypes } from "./errorTypes.sol";
|
||
|
import { IChainlinkAggregatorV3 } from "./interfaces/external/IChainlinkAggregatorV3.sol";
|
||
|
import { Error as OracleError } from "./error.sol";
|
||
|
|
||
|
/// @title FluidOracleL2
|
||
|
/// @notice Base contract that any Fluid Oracle L2 must implement
|
||
|
abstract contract FluidOracleL2 is IFluidOracle, OracleError {
|
||
|
/// @dev Chainlink L2 Sequencer Uptime feed to detect sequencer outages
|
||
|
IChainlinkAggregatorV3 internal _SEQUENCER_ORACLE;
|
||
|
/// @dev max time period until oracle assumes normal behavior after a sequencer outage.
|
||
|
uint256 internal constant _SEQUENCER_MAX_GRACE_PERIOD = 45 minutes;
|
||
|
|
||
|
/// @notice sets the L2 sequencer uptime Chainlink feed
|
||
|
constructor(address sequencerUptimeFeed_) {
|
||
|
_SEQUENCER_ORACLE = IChainlinkAggregatorV3(sequencerUptimeFeed_);
|
||
|
}
|
||
|
|
||
|
/// @notice returns all sequencer uptime feed related data
|
||
|
function sequencerL2Data()
|
||
|
public
|
||
|
view
|
||
|
returns (
|
||
|
address sequencerUptimeFeed_,
|
||
|
uint256 maxGracePeriod_,
|
||
|
bool isSequencerUp_,
|
||
|
uint256 lastUptimeStartedAt_,
|
||
|
uint256 gracePeriod_,
|
||
|
bool gracePeriodPassed_,
|
||
|
uint256 lastOutageStartedAt_,
|
||
|
bool isSequencerUpAndValid_
|
||
|
)
|
||
|
{
|
||
|
uint80 uptimeStartRoundId_;
|
||
|
(isSequencerUp_, uptimeStartRoundId_, lastUptimeStartedAt_) = _sequencerUpStatus();
|
||
|
|
||
|
if (isSequencerUp_) {
|
||
|
(gracePeriod_, gracePeriodPassed_, lastOutageStartedAt_) = _gracePeriod(
|
||
|
uptimeStartRoundId_,
|
||
|
lastUptimeStartedAt_
|
||
|
);
|
||
|
} else {
|
||
|
gracePeriod_ = _SEQUENCER_MAX_GRACE_PERIOD;
|
||
|
(uint80 roundId_, , , , ) = _SEQUENCER_ORACLE.latestRoundData();
|
||
|
lastOutageStartedAt_ = _lastSequencerOutageStart(roundId_ + 1);
|
||
|
}
|
||
|
|
||
|
return (
|
||
|
address(_SEQUENCER_ORACLE),
|
||
|
_SEQUENCER_MAX_GRACE_PERIOD,
|
||
|
isSequencerUp_,
|
||
|
lastUptimeStartedAt_,
|
||
|
gracePeriod_,
|
||
|
gracePeriodPassed_,
|
||
|
lastOutageStartedAt_,
|
||
|
isSequencerUp_ && gracePeriodPassed_
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/// @dev ensures that the sequencer is up and grace period has passed
|
||
|
function _ensureSequencerUpAndValid() internal view {
|
||
|
(bool isSequencerUp_, uint80 uptimeStartRoundId_, uint256 uptimeStartedAt_) = _sequencerUpStatus();
|
||
|
|
||
|
if (!isSequencerUp_) {
|
||
|
revert FluidOracleError(ErrorTypes.FluidOracleL2__SequencerOutage);
|
||
|
}
|
||
|
|
||
|
(, bool gracePeriodPassed_, ) = _gracePeriod(uptimeStartRoundId_, uptimeStartedAt_);
|
||
|
if (!gracePeriodPassed_) {
|
||
|
revert FluidOracleError(ErrorTypes.FluidOracleL2__SequencerOutage);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// @inheritdoc IFluidOracle
|
||
|
function getExchangeRate() external view virtual returns (uint256 exchangeRate_);
|
||
|
|
||
|
/// @inheritdoc IFluidOracle
|
||
|
function getExchangeRateOperate() external view virtual returns (uint256 exchangeRate_);
|
||
|
|
||
|
/// @inheritdoc IFluidOracle
|
||
|
function getExchangeRateLiquidate() external view virtual returns (uint256 exchangeRate_);
|
||
|
|
||
|
/// @dev finds last round before `uptimeStartRoundId_` where sequencer status was down, incl. handling cases of
|
||
|
/// consecutive rounds where status was down.
|
||
|
function _lastSequencerOutageStart(uint80 uptimeStartRoundId_) private view returns (uint256 outageStartedAt_) {
|
||
|
uint80 roundId_ = uptimeStartRoundId_;
|
||
|
int256 answer_;
|
||
|
uint256 startedAt_;
|
||
|
do {
|
||
|
(roundId_, answer_, startedAt_, , ) = _SEQUENCER_ORACLE.getRoundData(roundId_ - 1);
|
||
|
if (answer_ != 0) {
|
||
|
// sequencer was down at this round, update outage started at data
|
||
|
outageStartedAt_ = startedAt_;
|
||
|
} // else: while loop is going to break
|
||
|
} while (answer_ != 0 && startedAt_ > 0);
|
||
|
}
|
||
|
|
||
|
/// @dev finds last round where sequencer status was up, incl. handling cases of consecutive rounds where status was up.
|
||
|
function _sequencerUpStatus()
|
||
|
private
|
||
|
view
|
||
|
returns (bool isSequencerUp_, uint80 uptimeStartRoundId_, uint256 uptimeStartedAt_)
|
||
|
{
|
||
|
(uint80 roundId_, int256 answer_, uint256 startedAt_, , ) = _SEQUENCER_ORACLE.latestRoundData();
|
||
|
if (answer_ != 0) {
|
||
|
// sequencer is down currently.
|
||
|
return (false, 0, 0);
|
||
|
}
|
||
|
|
||
|
isSequencerUp_ = true;
|
||
|
|
||
|
// cover case where there were other consecutive uptime report rounds in between
|
||
|
uptimeStartRoundId_ = roundId_;
|
||
|
uptimeStartedAt_ = startedAt_;
|
||
|
if (uptimeStartedAt_ > 0) {
|
||
|
do {
|
||
|
(roundId_, answer_, startedAt_, , ) = _SEQUENCER_ORACLE.getRoundData(roundId_ - 1);
|
||
|
if (answer_ == 0) {
|
||
|
// sequencer was up at this round, consecutive uptime so update uptime start data
|
||
|
uptimeStartRoundId_ = roundId_;
|
||
|
uptimeStartedAt_ = startedAt_;
|
||
|
} // else: while loop is going to break
|
||
|
} while (answer_ == 0 && startedAt_ > 0);
|
||
|
} // else if startedAt == 0, then it is the first ever round.
|
||
|
}
|
||
|
|
||
|
/// @dev returns the `gracePeriod_` duration and if the grace period has `passed_` based on
|
||
|
/// current uptime round data vs the last sequencer outage duration.
|
||
|
function _gracePeriod(
|
||
|
uint80 uptimeStartRoundId_,
|
||
|
uint256 uptimeStartedAt_
|
||
|
) private view returns (uint256 gracePeriod_, bool passed_, uint256 outageStartedAt_) {
|
||
|
uint256 uptimeDuration_ = block.timestamp - uptimeStartedAt_;
|
||
|
if (uptimeStartedAt_ == 0 || uptimeDuration_ > _SEQUENCER_MAX_GRACE_PERIOD) {
|
||
|
return (_SEQUENCER_MAX_GRACE_PERIOD, true, 0);
|
||
|
}
|
||
|
|
||
|
outageStartedAt_ = _lastSequencerOutageStart(uptimeStartRoundId_);
|
||
|
|
||
|
// grace period is outage duration, capped at _SEQUENCER_MAX_GRACE_PERIOD
|
||
|
gracePeriod_ = uptimeStartedAt_ - outageStartedAt_; // outage duration
|
||
|
if (gracePeriod_ > _SEQUENCER_MAX_GRACE_PERIOD) {
|
||
|
gracePeriod_ = _SEQUENCER_MAX_GRACE_PERIOD;
|
||
|
}
|
||
|
|
||
|
return (gracePeriod_, uptimeDuration_ > gracePeriod_, outageStartedAt_);
|
||
|
}
|
||
|
}
|