Platform: Code4rena
Start Date: 25/08/2022
Pot Size: $75,000 USDC
Total HM: 35
Participants: 147
Period: 7 days
Judge: 0xean
Total Solo HM: 15
Id: 156
League: ETH
Rank: 44/147
Findings: 2
Award: $401.57
🌟 Selected for report: 1
🚀 Solo Findings: 0
🌟 Selected for report: okkothejawa
347.2615 DAI - $347.26
https://github.com/code-423n4/2022-08-olympus/blob/main/src/modules/PRICE.sol#L165-L171
Price oracle may fail and revert due to the inconsistency in the staleness checks.
In the getCurrentPrice()
of PRICE.sol
, Chainlink oracles are used to get the price of OHM against a reserve token, and a staleness check is used to make sure the price oracles are reporting fresh data. Yet the freshness requirements are inconsistent, for OHM, updatedAt
should be lower than current timestamp minus three times the observation frequency, while for the reserve price, it is required that updatedAt
should be lower than current timestamp minus the observation frequency. Our understanding is that that frequency is multiplied by 3 so that there can be some meaningful room where price data is accepted, as the time frame of only observation frequency (multiplied by 1) may not be enough for the oracle to realistically update its data. (In other words, the frequency of new price information might be lower than the observation frequency, which is probably the case as third multiple is used for the OHM price). If this is the case, this inconsistency may lead to the getCurrentPrice()
reverting as while third multiple of the observation frequency might give enough space for the first oracle, second oracle's first multiple of frequency time frame might not be enough and it couldn't pass the staleness check due to unrealistic expectation of freshness.
Manual review, talking with devs
Change the line 171 to
if (updatedAt < block.timestamp - 3 * uint256(observationFrequency))
like line 165.
#0 - Oighty
2022-09-06T18:36:07Z
This should indeed be the same. We will update to fix.
🌟 Selected for report: zzzitron
Also found by: 0x040, 0x1f8b, 0x52, 0x85102, 0xDjango, 0xNazgul, 0xNineDec, 0xSky, 0xSmartContract, 0xkatana, 8olidity, Aymen0909, Bahurum, BipinSah, Bnke0x0, CRYP70, CertoraInc, Ch_301, Chandr, Chom, CodingNameKiki, Deivitto, DimSon, Diraco, ElKu, EthLedger, Funen, GalloDaSballo, Guardian, IllIllI, JansenC, Jeiwan, Lambda, LeoS, Margaret, MasterCookie, PPrieditis, PaludoX0, Picodes, PwnPatrol, RaymondFam, ReyAdmirado, Rohan16, Rolezn, Ruhum, Sm4rty, StevenL, The_GUILD, TomJ, Tomo, Trust, Waze, __141345__, ajtra, ak1, apostle0x01, aviggiano, bin2chen, bobirichman, brgltd, c3phas, cRat1st0s, carlitox477, cccz, ch13fd357r0y3r, cloudjunky, cryptphi, csanuragjain, d3e4, datapunk, delfin454000, devtooligan, dipp, djxploit, durianSausage, eierina, enckrish, erictee, fatherOfBlocks, gogo, grGred, hansfriese, hyh, ignacio, indijanc, itsmeSTYJ, ladboy233, lukris02, martin, medikko, mics, natzuu, ne0n, nxrblsrpr, okkothejawa, oyc_109, p_crypt0, pfapostol, prasantgupta52, rajatbeladiya, rbserver, reassor, ret2basic, robee, rokinot, rvierdiiev, shenwilly, sikorico, sorrynotsorry, tnevler, tonisives, w0Lfrum, yixxas
54.3128 DAI - $54.31
lastBeat
might lagThe variable lastBeat
is used to store the timestamp of the last beat, yet it is incremented only by frequency()
. While this would work perfectly in the scenario that keeper bots are consistently calling this function with a frequency of frequency()
for rewards, lastBeat
may start lagging behind the actual time in the case there are some update made to the PRICE
contract (updating observation frequency or the moving average duration) and thus PRICE
contract is back in the initialized == false
state which would result in the call to updateMovingAverage()
failing as the function would revert when PRICE
is not in the initialized state. This scenario would result in any calls made to the beat
reverting, and if the PRICE
contract is not initialized immediately (so there is some delay) after the updates, lastBeat
would lag behind as it would start getting incremented from the old value up to old value + multiples of frequency()
, never reaching the actual timestamp of beat
s. This can be solved with by making a call to resetBeat
or by making sure the updates and initializations are done in an atomic fashion operationally, and thus we think this creates only a low risk.
There are three open TODOs in this function, and according to our understanding, this function revokePolicyApprovals
does not require a check to see if the address supplied is a deactivated policy, as TRSRY
should be only called by policies, and thus only policies would require approvals and revoking the approval of something that is not a policy would not do anything. So, these TODOs can be removed.
(https://github.com/code-423n4/2022-08-olympus/blob/main/src/policies/TreasuryCustodian.sol#L56)
There is a typo in this line (https://github.com/code-423n4/2022-08-olympus/blob/main/src/modules/PRICE.sol#L126), numbe
should be number
.
#0 - 0xLienid
2022-09-09T02:27:28Z
Technically true. Tough to address the lastBeat issue and TODOs were known about.