Platform: Code4rena
Start Date: 17/02/2022
Pot Size: $75,000 USDC
Total HM: 20
Participants: 39
Period: 7 days
Judges: moose-code, JasoonS
Total Solo HM: 13
Id: 89
League: ETH
Rank: 23/39
Findings: 2
Award: $320.42
🌟 Selected for report: 0
🚀 Solo Findings: 0
https://github.com/code-423n4/2022-02-hubble/blob/ed1d885d5dbc2eae24e43c3ecbf291a0f5a52765/contracts/Oracle.sol#L106-L122 https://github.com/code-423n4/2022-02-hubble/blob/ed1d885d5dbc2eae24e43c3ecbf291a0f5a52765/contracts/Oracle.sol#L134
The getLatestRoundData
function in the contract Oracle.sol fetches the latestPrice
directly from a Chainlink aggregator using the latestRoundData
function. While latestPrice
is checked for < 0 and staleness, there is no check if the value is != 0. This could lead to incorrect prices for the user and general instability.
function getLatestRoundData(AggregatorV3Interface _aggregator) internal view returns ( uint80, uint256 finalPrice, uint256 ) { (uint80 round, int256 latestPrice, , uint256 latestTimestamp, ) = _aggregator.latestRoundData(); finalPrice = uint256(latestPrice); if (latestPrice < 0) { requireEnoughHistory(round); (round, finalPrice, latestTimestamp) = getRoundData(_aggregator, round - 1); } return (round, finalPrice, latestTimestamp); }
getRoundData
has a similar problem here:
https://github.com/code-423n4/2022-02-hubble/blob/ed1d885d5dbc2eae24e43c3ecbf291a0f5a52765/contracts/Oracle.sol#L134
Visual Studio Code, Remix
I recommend making the following change:
function getLatestRoundData(AggregatorV3Interface _aggregator) internal view returns ( uint80, uint256 finalPrice, uint256 ) { (uint80 round, int256 latestPrice, , uint256 latestTimestamp, ) = _aggregator.latestRoundData(); finalPrice = uint256(latestPrice); if (latestPrice <= 0) { // @audit check for zero requireEnoughHistory(round); (round, finalPrice, latestTimestamp) = getRoundData(_aggregator, round - 1); } return (round, finalPrice, latestTimestamp); }
And the fix for getRoundData
at l#134 is similar:
while (latestPrice <= 0) {
#0 - atvanguard
2022-02-24T07:13:43Z
Duplicate of #46
🌟 Selected for report: defsec
Also found by: 0v3rf10w, 0x0x0x, 0x1f8b, 0xwags, CertoraInc, Dravee, IllIllI, Meta0xNull, Nikolay, Omik, WatchPug, bobi, cccz, csanuragjain, danb, gzeon, hubble, hyh, itsmeSTYJ, jayjonah8, kenta, kirk-baird, leastwood, pauliax, peritoflores, rfa, robee, sorrynotsorry, ye0lde
212.4973 USDC - $212.50
Unless otherwise noted, manual auditing and testing were done using Visual Studio Code and Remix. The audit was done from February 17-23, 2022 by ye0lde through code4rena.
Overall, I found the code to be clear to follow and read. I'd recommend the team improve the supporting documentation to give a better overall understanding of the protocol.
price
in setStablePrice
(Oracle.sol)The setStablePrice
function does not do any validation of the price
parameter before setting stablePrice[underlying] = price
. While this is a governance function, once stablePrice[underlying]
is set to any non-zero value this overrides any aggregator calls made to access the current price by function getUnderlyingPrice
and getUnderlyingTwapPrice
. underlying
is checked but not the price
itself.
setStablePrice
is here:
https://github.com/code-423n4/2022-02-hubble/blob/ed1d885d5dbc2eae24e43c3ecbf291a0f5a52765/contracts/Oracle.sol#L169-L172
function setStablePrice(address underlying, int256 price) external onlyGovernance { requireNonEmptyAddress(underlying); stablePrice[underlying] = price; }
Consider adding a check for price > 0
getTwapPrice
(AMM.sol)getTwapPrice
performs an unsafe type cast to uint128 without checking if the value actually fits into 128 bits. This typecast doesn't seem to directly lead to an exploit but safe typecasts should still be implemented for additional security.
The type cast is here: https://github.com/code-423n4/2022-02-hubble/blob/ed1d885d5dbc2eae24e43c3ecbf291a0f5a52765/contracts/AMM.sol#L392
I suggest the following change:
function getTwapPrice(uint256 _intervalInSeconds) public view returns (int256) { return (_calcTwap(_intervalInSeconds).toInt256()); }
getUnderlyingTwapPrice
(Oracle.sol)Code clarity
The typo is here: https://github.com/code-423n4/2022-02-hubble/blob/ed1d885d5dbc2eae24e43c3ecbf291a0f5a52765/contracts/Oracle.sol#L53
Change form
to from
.
#0 - atvanguard
2022-02-26T06:41:30Z
Good minor find.