Aave V3 Oracles
Accurate and reliable price feeds are the bedrock of any lending protocol like Aave V3. They are essential for determining borrowing power, checking collateral sufficiency, and triggering liquidations. Aave employs a robust system primarily centered around the AaveOracle contract, with an additional safety layer, the PriceOracleSentinel, often used in Layer 2 deployments.
1. AaveOracle: The Primary Price Provider
The AaveOracle contract serves as the main source of truth for asset prices within an Aave market. It's designed to be flexible, primarily relying on Chainlink price feeds but incorporating a fallback mechanism for resilience.
- Core Interface:
IAaveOracle(which inherits fromIPriceOracleGetter). - Key Goal: To provide the price of any listed asset relative to a common
BASE_CURRENCY(usually USD, represented by address(0), or sometimes ETH).
Architecture & Key Concepts:
- Primary Sources (
_assetsSources): The oracle maintains an internal mapping (conceptuallymapping(address => AggregatorInterface) internal _assetsSources;) linking each underlying asset address to its primary price feed contract. In most Aave V3 deployments, these are ChainlinkAggregatorV3Interfacecontracts. - Fallback Oracle (
_fallbackOracle): An optional secondary oracle address (IPriceOracleGetter) can be configured. If the primary source for an asset is missing, unresponsive, or returns an invalid price (e.g., zero or negative), theAaveOraclewill consult this fallback oracle. This adds redundancy. The fallback could be another, perhaps less frequently updated, Chainlink feed, a different oracle provider, or even a simpler manual oracle for specific assets. - Base Currency: Prices are returned relative to a defined
BASE_CURRENCY. TheBASE_CURRENCY_UNIT()function specifies the unit of this base currency (e.g., 1e8 for USD, 1e18 for ETH). The price of the base currency itself is defined asBASE_CURRENCY_UNIT().
Key Functions:
-
getAssetPrice(address asset):- Purpose: This is the most critical function, called frequently by the Pool's logic libraries (like
GenericLogicduring health factor calculations). - Logic (Conceptual):
- Check if
assetis theBASE_CURRENCY. If so, returnBASE_CURRENCY_UNIT(). - Look up the primary source address for
assetin the_assetsSourcesmapping. - If a primary source exists:
- Call
latestAnswer()on the source (ChainlinkAggregatorInterface). - Validate the returned price (e.g., ensure it's positive).
- If valid, potentially adjust for decimals and return the price relative to the
BASE_CURRENCY.
- Call
- If no primary source exists or the price was invalid, check if a
_fallbackOracleis set. - If a fallback exists:
- Call
getAssetPrice(asset)on the_fallbackOracle. - Return the result (assuming the fallback provides a valid price).
- Call
- If no primary source, fallback, or if both return invalid prices, the function typically reverts (as seen in
AaveOracleTest.t.sol'stestAssetZeroPriceWithoutFallbacktest case expecting a revert).
- Check if
- Interface (
IPriceOracleGetter.sol):function getAssetPrice(address asset) external view returns (uint256);
- Purpose: This is the most critical function, called frequently by the Pool's logic libraries (like
-
setAssetSources(address[] calldata assets, address[] calldata sources):- Purpose: Allows administrators to register or update the primary price feed addresses for multiple assets.
- Permissions: Requires the caller to have the
ASSET_LISTING_ADMIN_ROLEorPOOL_ADMIN_ROLEgranted via theACLManager. TheAaveOraclefinds theACLManagervia thePoolAddressesProvider. - Interface (
IAaveOracle.solimplies this, though not explicitly inIPriceOracleGetter): Often paired with configuration functions. - Validation: Ensures
assetsandsourcesarrays have the same length (test_revert_setAssetSources_inconsistentParamsinAaveOracle.t.sol).
-
setFallbackOracle(address fallbackOracle):- Purpose: Allows administrators to set or update the fallback oracle address.
- Permissions: Typically requires
POOL_ADMIN_ROLE. - Interface (
IAaveOracle.solimplies this): Configuration function.
2. PriceOracleSentinel: L2 Sequencer Safety Net
Layer 2 rollups introduce a potential risk: the centralized sequencer responsible for ordering transactions might become unavailable. If this happens, price feeds might not update, potentially allowing liquidations based on stale, incorrect prices. The PriceOracleSentinel acts as a safety mechanism to mitigate this risk.
- Purpose: To pause critical, price-dependent operations (borrows and liquidations) if the L2 sequencer appears down or hasn't reported activity recently.
- Core Interface:
IPriceOracleSentinel.
Architecture & Key Concepts:
- Sequencer Oracle (
_sequencerOracle): The sentinel relies on an external oracle contract (ISequencerOracle) that reports the status and last update time of the L2 sequencer. Chainlink often provides "Sequencer Uptime Feeds" for this purpose (like theSequencerOraclemock used in tests). - Grace Period (
_gracePeriod): A configurable duration (e.g., 1 hour, 1 day). Even if the sequencer is reporting as up, actions might still be paused if the time since the sequencer's last reported update (latestRoundData().updatedAt) exceeds the_gracePeriod. This acts as a buffer against potential delays or intermittent issues.
Key Functions:
-
isBorrowAllowed()/isLiquidationAllowed():- Purpose: Called by the Pool's logic libraries (
ValidationLogic) before executing a borrow or liquidation. - Logic: Both functions rely on the same internal check (conceptually
_isSequencerUpAndGracePeriodPassed()):- Call
latestRoundData()on the configured_sequencerOracle. - Check the sequencer status from the returned data (typically, the answer indicates if it's down). If down, return
false. - If up, check the timestamp of the last update (
updatedAtfromlatestRoundData). - Compare the current block timestamp (
block.timestamp) withupdatedAt + _gracePeriod. - Return
trueonly if the sequencer is reported as up ANDblock.timestamp > updatedAt + _gracePeriod. Otherwise, returnfalse.
- Call
- Tests: The tests in
PriceOracleSentinel.t.soldemonstrate this:test_isLiquidationAllowed_network_down: Returnsfalseif sequencer reports down.test_isLiquidationAllowed_network_up_not_grace_period: Returnsfalseif sequencer is up but grace period hasn't passed.test_isLiquidationAllowed_true_network_up_grace_period_pass: Returnstrueonly when up and grace period passed.
- Interface (
IPriceOracleSentinel.sol):function isBorrowAllowed() external view returns (bool); function isLiquidationAllowed() external view returns (bool);
- Purpose: Called by the Pool's logic libraries (
-
setSequencerOracle(address newSequencerOracle):- Purpose: Updates the address of the Sequencer Uptime Feed.
- Permissions: Requires
POOL_ADMIN_ROLE(checked viaACLManagerfound viaPoolAddressesProvider). Tested intest_reverts_setSequencerOracle_not_poolAdmin. - Event: Emits
SequencerOracleUpdated. - Interface (
IPriceOracleSentinel.sol):function setSequencerOracle(address newSequencerOracle) external;
-
setGracePeriod(uint256 newGracePeriod):- Purpose: Updates the grace period duration.
- Permissions: Requires
RISK_ADMIN_ROLEorPOOL_ADMIN_ROLE. Tested intest_reverts_setGracePeriod_not_poolAdmin. - Event: Emits
GracePeriodUpdated. - Interface (
IPriceOracleSentinel.sol):function setGracePeriod(uint256 newGracePeriod) external;
Integration
The Pool contract (or rather, its logic libraries like ValidationLogic) uses the PoolAddressesProvider to get the addresses of both IAaveOracle (getPriceOracle()) and IPriceOracleSentinel (getPriceOracleSentinel()).
- Prices are fetched using
IAaveOracle.getAssetPrice(). - Before critical operations like
borroworliquidationCallon L2s, the logic checks:if (IPriceOracleSentinel(sentinelAddress) != address(0)) { require(IPriceOracleSentinel(sentinelAddress).isBorrowAllowed() / isLiquidationAllowed(), ...); }
Conclusion
Aave V3's oracle system is designed for reliability and safety. The AaveOracle provides core price information, primarily using Chainlink feeds with a fallback option for robustness. On Layer 2 deployments, the PriceOracleSentinel adds a crucial safety layer, pausing potentially risky operations like borrows and liquidations if the L2 sequencer shows signs of downtime or stale reporting, thus protecting the protocol from exploiting potentially inaccurate, outdated prices during sequencer outages. Both components are configured and managed via roles defined in the ACLManager, accessed through the central PoolAddressesProvider.

