Open code423n4 opened 1 year ago
thereksfour marked the issue as primary issue
thereksfour changed the severity to 2 (Med Risk)
External requirement with oracle errors
tbrent marked the issue as sponsor acknowledged
thereksfour marked the issue as satisfactory
thereksfour marked the issue as selected for report
As documented in Asset.sol
https://github.com/reserve-protocol/protocol/blob/master/contracts/plugins/assets/Asset.sol#L41:
oracleTimeout_ is also used as the timeout value in lotPrice(), should be highest of all assets' oracleTimeout in a collateral if there are multiple oracles
Lines of code
https://github.com/reserve-protocol/protocol/blob/9ee60f142f9f5c1fe8bc50eef915cf33124a534f/contracts/plugins/assets/Asset.sol#L140 https://github.com/reserve-protocol/protocol/blob/9ee60f142f9f5c1fe8bc50eef915cf33124a534f/contracts/plugins/assets/NonFiatCollateral.sol#L52 https://github.com/reserve-protocol/protocol/blob/9ee60f142f9f5c1fe8bc50eef915cf33124a534f/contracts/plugins/assets/Asset.sol#L61 https://github.com/reserve-protocol/protocol/blob/9ee60f142f9f5c1fe8bc50eef915cf33124a534f/contracts/plugins/assets/curve/CurveStableCollateral.sol#L57 https://github.com/reserve-protocol/protocol/blob/9ee60f142f9f5c1fe8bc50eef915cf33124a534f/contracts/plugins/assets/curve/PoolTokens.sol#L287 https://github.com/reserve-protocol/protocol/blob/9ee60f142f9f5c1fe8bc50eef915cf33124a534f/contracts/plugins/assets/curve/PoolTokens.sol#L235
Vulnerability details
Impact
OracleTimeout
is the number of seconds until an oracle value becomes invalid. It is set in the constructor ofAsset
. AndAsset.lotPrice
usesOracleTimeout
to determine if the saved price is stale. However,OracleTimeout
may not be the correct source to determine if the price is stale.Asset.lotPrice
may return the incorrect price.Proof of Concept
OracleTimeout
is set in the constructor ofAsset
. https://github.com/reserve-protocol/protocol/blob/9ee60f142f9f5c1fe8bc50eef915cf33124a534f/contracts/plugins/assets/Asset.sol#L61Asset.lotPrice
useoracleTimeout
to determine if the saved price is in good standing. https://github.com/reserve-protocol/protocol/blob/9ee60f142f9f5c1fe8bc50eef915cf33124a534f/contracts/plugins/assets/Asset.sol#L140However,
oracleTimeout
may not be the accurate source to determine if the saved price is stale. The following examples shows that using onlyoracleTimeout
is vulnerable.NonFiatCollateral.tryPrice
leverages two price feeds to calculate the price. These two feeds have different timeouts. https://github.com/reserve-protocol/protocol/blob/9ee60f142f9f5c1fe8bc50eef915cf33124a534f/contracts/plugins/assets/NonFiatCollateral.sol#L52If
targetUnitChainlinkFeed
is malfunctioning andtargetUnitOracleTimeout
is smaller thanoracleTimeout
,lotPrice()
should not return saved price whendelta > targetUnitOracleTimeout
. However,lotPrice()
only considersoracleTimeout
. It could return the incorrect price whentargetUnitChainlinkFeed
is malfunctioning.To calculate the price,
CurveStableCollateral.tryPrice
callsPoolToken.totalBalancesValue
. AndPoolToken.totalBalancesValue
callsPoolToken.tokenPrice
.PoolToken.tokenPrice
uses multiple feeds to calculate the price. And they could have different timeouts. None of them are used inlotPrice()
. https://github.com/reserve-protocol/protocol/blob/9ee60f142f9f5c1fe8bc50eef915cf33124a534f/contracts/plugins/assets/curve/CurveStableCollateral.sol#L57 https://github.com/reserve-protocol/protocol/blob/9ee60f142f9f5c1fe8bc50eef915cf33124a534f/contracts/plugins/assets/curve/PoolTokens.sol#L287 https://github.com/reserve-protocol/protocol/blob/9ee60f142f9f5c1fe8bc50eef915cf33124a534f/contracts/plugins/assets/curve/PoolTokens.sol#L235We can also find out that
oracleTimeout
is unused. ButlotPrice()
still uses it to determine if the saved price is valid. This case is worse than the first case. https://github.com/reserve-protocol/protocol/blob/9ee60f142f9f5c1fe8bc50eef915cf33124a534f/contracts/plugins/assets/curve/CurveStableCollateral.sol#L28Tools Used
Manual Review
Recommended Mitigation Steps
Since collaterals have various implementations of price feed.
Asset.lotPrice
could be modified like:Then, collaterals can override
actualOracleTimeout
to reflect the correct oracle timeout.Assessed type
Error