RAAC pmUSD to USD Oracle¶
Overview¶
The PmUSDToUSDOracle is a robust, multi-tiered price oracle that provides the pmUSD/USD exchange rate for the RAAC protocol. It implements a sophisticated fallback mechanism to ensure price availability even during oracle failures, with built-in circuit breakers to protect against depeg events.
The oracle uses a three-tier fallback system:
- Primary: Direct Chainlink pmUSD/USD feed — not configured
- Secondary: ETH/USD + ETH/pmUSD (Curve TriCRV pool) — not configured
- Tertiary: Hard-coded fallback price
Current configuration: tertiary only
The primary and secondary sources are not set. crvUSDDataFeed, ethDataFeed and curveOracle are all left at the zero address, and the constructor does not populate them — they are owner-set through their respective setters.
Both branches of _crvUSDPriceInUSD() are gated on those addresses being non-zero, so with them unset the oracle serves the hard-coded fallback price (level 2) on every query: 1e18, or 1.00 USD. The multi-tier logic below describes the mechanism that becomes active once the feeds are configured; it is not what the oracle does today.
Non-Reverting Design
The oracle is designed to never revert on price queries. Instead, it gracefully falls back through multiple price sources to ensure continuous operation of the protocol.
Architecture¶
Fallback Levels¶
| Level | Source | Description | Staleness Check | Status |
|---|---|---|---|---|
| 0 | Chainlink pmUSD/USD | Direct price feed from Chainlink | 1 day + 1 hour | Not configured |
| 1 | ETH/USD + ETH/pmUSD | Chainlink ETH/USD × Curve TriCRV ETH/pmUSD | 1 hour + 10 minutes | Not configured |
| 2 | Hard-coded fallback | Protocol-set fallback price (default: $1.00) | N/A | Active |
Purpose¶
- Price Conversion: Convert pmUSD prices to USD for house price calculations
- Reliability: Ensure price availability through multiple fallback mechanisms
- Depeg Protection: Circuit breaker prevents extreme price deviations
- Protocol Integration: Seamless integration with
RAACHousePricesfor price conversion
Key Features¶
Multi-Tier Fallback System¶
The oracle implements a sophisticated three-tier fallback mechanism:
- Primary Source (not configured): a Chainlink pmUSD/USD feed
- Secondary Source (not configured): a Chainlink ETH/USD feed combined with a Curve TriCRV pool ETH/pmUSD price
- Tertiary Source (active): hard-coded fallback price, configurable by the owner
Circuit Breaker Protection¶
- Default thresholds: 0.9 USD (min) to 1.1 USD (max)
- Configurable: Owner can adjust thresholds or disable circuit breaker
- Depeg protection: Prevents extreme price deviations during market stress
Price Sources¶
Primary: Chainlink pmUSD/USD — not configured¶
crvUSDDataFeed is unset, so level 0 is skipped. Once a feed is set, the values that apply are:
- Staleness:
maxStableStaleTime, 1 day + 1 hour - Decimals: normalized to 18 decimals
Secondary: ETH/USD + Curve TriCRV — not configured¶
ethDataFeed and curveOracle are unset, and level 1 requires both, so the branch is skipped. Once they are set, the values that apply are:
- Calculation:
pmUSD/USD = ETH/USD ÷ ETH/pmUSD - Staleness:
maxUnstableStaleTime, 1 hour + 10 minutes for ETH/USD - K Value: 0 (ETH index in the TriCRV pool)
Tertiary: Hard-coded fallback — active¶
- Value:
fallbackPriceInUSD, set to1e18(1.00 USD) in the constructor - Timestamp:
lastFallbackUpdateTimestamp, set at deployment and on eachsetFallbackPriceInUSDcall - Owner-configurable via
setFallbackPriceInUSD(uint256)
Key Functions¶
Price Queries¶
| Function | Description | Returns |
|---|---|---|
getLatestPrice() |
Full price data with fallback level | (price, timestamp, fallbackLevel) |
getPrice() |
Simple price query | uint256 price |
decimals() |
Price decimals (always 18) | uint8 |
Configuration (Owner Only)¶
| Function | Description | Parameters |
|---|---|---|
setCrvUSDDataFeed(address) |
Set Chainlink pmUSD/USD feed | _crvUSDDataFeed |
setEthDataFeed(address) |
Set Chainlink ETH/USD feed | _ethDataFeed |
setCurveOracle(address) |
Set Curve TriCRV oracle | _curveOracle |
setMaxStableStaleTime(uint256) |
Set pmUSD feed staleness limit | _maxStableStaleTime |
setMaxUnstableStaleTime(uint256) |
Set ETH feed staleness limit | _maxUnstableStaleTime |
setFallbackPriceInUSD(uint256) |
Set hard-coded fallback price | _fallbackPriceInUSD |
setCircuitBreakerConfig(bool, uint256, uint256) |
Configure circuit breaker | enabled, minThreshold, maxThreshold |
setK(uint256) |
Set Curve pool token index | _K |
Fallback Logic Details¶
Level 0: Direct Chainlink Feed¶
// Check if pmUSD/USD feed is available and fresh
if (address(crvUSDDataFeed) != address(0)) {
(, int256 answer, , uint256 updatedAt, ) = crvUSDDataFeed.latestRoundData();
if (answer > 0 && block.timestamp - updatedAt <= maxStableStaleTime) {
return normalizeTo18Decimals(uint256(answer), crvUSDDataFeed.decimals());
}
}
Level 1: ETH/USD + Curve Calculation¶
// Calculate pmUSD/USD = ETH/USD ÷ ETH/pmUSD
uint256 ethPriceInUSD = normalizeTo18Decimals(ethAnswer, ethDataFeed.decimals());
uint256 ETHPriceInCrvUSD = curveOracle.price_oracle(K);
uint256 crvUSDPriceInUSD = (ethPriceInUSD * 10**18) / ETHPriceInCrvUSD;
Level 2: Hard-coded Fallback¶
Circuit Breaker Configuration¶
Default Settings¶
- Enabled:
true - Min Threshold:
0.9e18(0.90 USD) - Max Threshold:
1.1e18(1.10 USD)
Circuit Breaker Logic¶
function _clampCircuitBreaker(uint256 price) internal view returns (uint256) {
if (!circuitBreakerEnabled) return price;
if (price < minPriceThreshold) return minPriceThreshold;
if (price > maxPriceThreshold) return maxPriceThreshold;
return price;
}
Integration with RAACHousePrices¶
The oracle is integrated with RAACHousePrices for price conversion:
// In RAACHousePrices.getLatestPrice()
uint256 usdPrice = tokenToHousePrice[_tokenId];
uint256 crvUSDPrice = (usdPrice * 10**crvUSDToUSDOracle.decimals()) / crvUSDToUSDOracle.getPrice();
This ensures house prices stored in USD are properly converted to pmUSD for protocol calculations.
Operational Considerations¶
Monitoring¶
- Monitor fallback level usage to detect oracle issues
- Track circuit breaker activations during market stress
- Ensure all price feeds remain healthy and updated
Emergency Procedures¶
- Oracle Failure: System automatically falls back to the next configured source; with the primary and secondary unset, every query resolves to the tertiary fallback
- Depeg Event: Circuit breaker clamps prices within safe thresholds
- Feed Staleness: Automatic fallback to fresher sources
Owner Responsibilities
The owner must ensure all oracle addresses are correctly configured and monitor the system for proper operation. Incorrect configuration could lead to stale or incorrect prices.