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
19 changes: 14 additions & 5 deletions contracts/facade/oracles/ExchangeRateOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,24 @@
* @title ExchangeRateOracle
* @notice An immutable Exchange Rate Oracle for an RToken (eg: ETH+/ETH)
*
* ::Assumption::
* Constant basket target definition of only a single target unit, of the correct magnitude.
* For example, an ETH-pegged RToken should define the basket as 1 ETH, and a USD-pegged RToken
* should define the basket as 1 USD. The basket target units should not be redefined after.
*
* ::Notice::
* The oracle does not call refresh() on the RToken or the underlying assets, so the price can be
* stale. This is generally not an issue for active RTokens as they are refreshed often by other
* The oracle does not call refresh() on the RToken, so the exchange rate can be stale.
* This is generally not an issue for active RTokens as they are refreshed by other
* protocol operations, however do keep this in mind when using this for low-activity RTokens.
* This can lead to the exchange rate being underestimated by the amount of unrealized RToken
* melting (exchange-rate appreciation).
*
* If you need the freshest possible price, consider using RTokenAsset.latestPrice() instead,
* however it is a mutator function instead of a view-only function hence not compatible with
* If you need a fresher exchange-rate, consider calling `furnace.melt()` or
* `RTokenAsset.refresh()`. Note these are mutators, and hence not compatible with
* Chainlink style interfaces.
*
* ::Warning:: In the event of an RToken taking a loss in excess of the StRSR overcollateralization
* ::Warning::
* In the event of an RToken taking a loss in excess of the StRSR overcollateralization
* layer, the devaluation will not be reflected until the RToken is done trading. This causes
* the exchange rate to be too high during the rebalancing phase. If the exchange rate is relied
* upon naively, then it could be misleading.
Expand All @@ -30,12 +38,13 @@
*
* However, note that `fullyCollateralized()` is extremely gas-costly. We recommend executing
* the function off-chain. `status()` is cheap and more reasonable to be called on-chain.
*
*/
contract ExchangeRateOracle is IExchangeRateOracle {
error ZeroAddress();

IRToken public immutable rToken;
uint256 public constant override version = 1;

Check warning on line 47 in contracts/facade/oracles/ExchangeRateOracle.sol

View workflow job for this annotation

GitHub Actions / Lint Checks

Constant name must be in capitalized SNAKE_CASE

constructor(address _rToken) {
if (_rToken == address(0)) {
Expand Down
26 changes: 11 additions & 15 deletions contracts/facade/oracles/ReferenceRateOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,24 @@
* @notice An immutable Reference Rate Oracle for an RToken (eg: ETH+/USD)
*
* Composes oracles used by the protocol internally to calculate the reference price of an RToken,
* in UoA terms, usually USD.
* in UoA terms, usually USD. Inherits the deviations of the underlying oracles in proportion to
* their presence in the basket by value. Refer to `RTokenAsset.tryPrice()` for more detailed
* information about the pricing method.
*
* ::Notice::
* The oracle does not call refresh() on the RToken or the underlying assets, so the price can be
* stale. This is generally not an issue for active RTokens as they are refreshed often by other
* protocol operations, however do keep this in mind when using this for low-activity RTokens.
*
* If you need the freshest possible price, consider using RTokenAsset.latestPrice() instead,
* however it is a mutator function instead of a view-only function hence not compatible with
* Chainlink style interfaces, and additionally can revert.
*
* As a consumer of this oracle, you may want to guard against this case by monitoring:
* `basketHandler.status() == 0 && basketHandler.fullyCollateralized()`
* where `basketHandler` can be safely cached from `rToken.main().basketHandler()`.
*
* However, note that `fullyCollateralized()` is extremely gas-costly. We recommend executing
* the function off-chain. `status()` is cheap and more reasonable to be called on-chain.
* If you need a fresher price, consider using `RTokenAsset.latestPrice()` instead. Precede with a
* call to `RTokenAsset.forceUpdatePrice()` if a 15-minute caching period is not acceptable.
* However, note both these functions are mutators, and hence not compatible with
* Chainlink style interfaces (and can revert).
*/
contract ReferenceRateOracle is IExchangeRateOracle {
error ZeroAddress();

uint256 public constant override version = 1;

Check warning on line 33 in contracts/facade/oracles/ReferenceRateOracle.sol

View workflow job for this annotation

GitHub Actions / Lint Checks

Constant name must be in capitalized SNAKE_CASE

IRToken public immutable rToken;
IAssetRegistry public immutable assetRegistry;
Expand All @@ -49,7 +45,7 @@
}

function decimals() external view override returns (uint8) {
return rToken.decimals();
return 18;
}

function description() external view override returns (string memory) {
Expand All @@ -67,7 +63,7 @@
require(lower != 0 && upper < FIX_MAX, "invalid price");

/**
* In >=4.2.0 (not yet deployed) there is a feature called the "issuance premium",
* In >=4.0.0 there is a feature called the "issuance premium",
* which if enabled, will cause the high price to remain relatively static,
* even when an RToken collateral is under peg.
*
Expand All @@ -77,8 +73,8 @@
* Using the average of the issuance redemption cost in this case can result in a quantity
* biased upwards.
*
* If you need the *lowest* possible price the RToken can have, do not use this approach.
* Instead, use the `lower` price directly. Include our check above that `low > 0`.
* If you need the redemption value of the RToken, do not use this approach.
* Instead, use the `lower` price directly. Include our check above that `lower != 0`.
*/

return (lower + upper) / 2;
Expand Down
Loading