Oracle Relayer
1. Summary
The OracleRelayer
functions as an interface contract between FSM
s and the SAFEEngine
and only stores the current collateralType
list as well as the current redemptionPrice
and redemptionRate
. The relayer will depend on governance to set each collateral's safety and liquidation ratios and might also depend on an external feedback mechanism to update the redemptionRate
which affects the redemptionPrice
.
2. Contract Variables & Functions
Variables
contractEnabled
- settlement flag (1
or0
).authorizedAccounts[usr: address]
- addresses allowed to callmodifyParameters()
anddisableContract()
.collateralTypes[collateralType: bytes32]
- mapping of each collateral typecdpEngine
- address of theCDPEngine
contractredemptionRate
- the current redemption rate that reprices the system coin internally and changes user incentives_redemptionPrice
- virtual variable that does not reflect the latestredemptionPrice
redemptionPriceUpdateTime
- last time when the redemption price was updatedredemptionRateUpperBound
- maximum value that theredemptionRate
can haveredemptionRateLowerBound
- minimum value that theredemptionRate
can haveRAY
- number with 27 decimals
Data Structures
CollateralType
- struct with data about each collateral typeorcl
- the address of a price feed, usually anOSM
safetyCRatio
- the collateralization ratio used to compute thesafetyPrice
of a collateral typeliquidationCRatio
- the collateralization ratio used to compute theliquidationPrice
of a collateral type
Modifiers
isAuthorized
**** - checks whether an address is part ofauthorizedAddresses
(and thus can call authed functions).
Functions
modifyParameters(parameter: bytes32
,data: uint256)
- update auint256
parameter.modifyParameters(parameter: bytes32
,data: uint256)
- update a collateral related parameter.modifyParameters(collateralType: bytes32
,parameter: bytes32
,data: address)
- update anaddress
parameter.addAuthorization(usr: address)
- add an address toauthorizedAddresses
.removeAuthorization(usr: address)
- remove an address fromauthorizedAddresses
.updateRedemptionPrice()
- internal function used to update the redemption price using theredemptionRate
redemptionPrice() external view returns (uint256)
- getter function that updates and retrieves the virtual_redemptionPrice
updateCollateralPrice(collateralType: bytes32)
- update the safety and liquidation prices of a collateral price and store them in theCDPEngine
disableContract()
- disables the relayersafetyCRatio(collateralType: bytes32) external view returns (uint256)
- getter for a collateral's safety CRatioliquidationCRatio(collateralType: bytes32) external view returns (uint256)
- getter for a collateral's liquidation CRatioorcl(collateralType: bytes32) external view returns (address)
- getter for a collateral type's oracle
Events
AddAuthorization
- emitted when a new address becomes authorized. Contains:account
- the new authorized account
RemoveAuthorization
- emitted when an address is de-authorized. Contains:account
- the address that was de-authorized
DisableContract
- emitted when the contract is disabled.ModifyParameters
- emitted when a parameter is updated.UpdateRedemptionPrice
- emitted when the redemption price is updated. Contains:redemptionPrice
- the latest redemption price
UpdateCollateralPrice
- emitted when the safety and liquidation prices of a specific collateral price are updated. Contains:collateralType
- the collateral type whose prices are updatedpriceFeedValue
- the new price feed coming from the collateral's oraclesafetyPrice
- the price computed by dividing the feed value by theredemptionPrice
and then dividing the result again by the collateral'ssafetyCRatio
liquidationPrice
- the price computed by dividing the feed value by theredemptionPrice
and then dividing the result again by the collateral'sliquidationCRatio
3. Walkthrough
UpdateCollateralPrice
updateCollateralPrice
is a non-authenticated function. The function takes in a bytes32
representing a collateralType
whose (safety and liquidation) prices need to be updated. updateCollateralPrice
has three stages:
getResultWithValidity
- interacts with thecollateralType
'sorcl
and returns avalue
and whether itisValid
(a boolean which is false if the price is invalid). The second external call only happens ifisValid == true
.- When calculating the
safetyPrice
and theliquidationPrice
, the_redemptionPrice
is crucial as it defines the relationship between the system coin and one unit of collateral. Thevalue
from theOSM
is divided by the (updated)redemptionPrice
(to get a ratio of collateralvalue
to system coins) and then the result is divided again by thecollateralType.safetyCRatio
(when calculating thesafetyPrice
) and by thecollateralType.liquidationCRatio
(when calculating theliquidationPrice
). cdpEngine.modifyParameters
is then called to update the collateral's prices inside the system.
Redemption Price
Every time someone wants to read the _redemptionPrice
its value will first be updated using the redemptionRate
and then the output will be returned. We chose this design in order to ensure a smooth redemptionPrice
pro-ration (using the virtual variable + a state modifying getter).
Updating the Redemption Rate
Every time the redemptionRate
is updated, the contract makes sure to bound the value that is can be set to.
4. Gotchas
The methods in the oracleRelayer
are relatively basic compared to most other portions of geb
. There is not much room for user error in the single unauthed method updateCollateralPrice
. If an incorrect bytes32
is supplied the call will fail.
Any module that is authed against the oracleRelayer
has full root access, and can, therefore, add and remove which collateralTypes
can be "updateCollateralPrice"'d. While not completely breaking the system, this could cause considerable risk. An authed caller can also update the redemptionRate
and redemptionPrice
, causing considerable impact depending on the values used.
5. Failure Modes
Coding Error
A bug in oracleRelayer
would most likely result in the prices for collaterals and the redemptionRate not being updated anymore. In this case, the system would need to authorize a new oracleRelayer
which would then be able to update the prices/rate. Overall this is not a catastrophic failure as this would only pause all price fluctuation for some period.
Feeds
The oracleRelayer
relies upon a set of trusted oracles to provide price data. Should these price feeds fail, it would become possible for unbacked Coin to be minted, or Safes could be unfairly liquidated.
Spot Price Becoming Stale
When updateCollateralPrice
is not called frequently enough, the SAFE
's safetyPrice
price will become stale. This could arise for a few reasons including tragedy of the commons or miner collusion and could lead to negative outcomes such as inappropriate liquidations, or the prevention of liquidations that should be possible.