Platform: Code4rena
Start Date: 25/10/2022
Pot Size: $50,000 USDC
Total HM: 18
Participants: 127
Period: 5 days
Judge: 0xean
Total Solo HM: 9
Id: 175
League: ETH
Rank: 46/127
Findings: 3
Award: $58.23
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: trustindistrust
Also found by: 0xbepresent, Jujic, Lambda, RaoulSchaffranek, c7e7eff, catchup, codexploder, cryptonue, d3e4, eierina, jwood, pashov, peanuts, pedroais, simon135
33.634 USDC - $33.63
A lender may be swapped out by governance at any time, or by enough voting power, depending on the governance implementation, without returning the free and/or borrowed balance to the lender. If a single or a group may benefit from it, then there should be measures to prevent it.
n/a
As a minimum I would expect something like the following: gov proposes new lender, old lender executes/finalize the swap in a way that arrangements between the two lenders can be settled outside of the market. This works also in the case the new lender and old lender are owned by same entity.
While the external agreement takes place (or not)
#0 - c4-judge
2022-11-05T22:53:16Z
0xean marked the issue as duplicate
#1 - Simon-Busch
2022-12-05T15:36:54Z
Issue marked as satisfactory as requested by 0xean
#2 - c4-judge
2022-12-07T08:22:05Z
Simon-Busch marked the issue as duplicate of #301
🌟 Selected for report: adriro
Also found by: 8olidity, BClabs, CertoraInc, Chom, Franfran, Lambda, RaoulSchaffranek, Ruhum, codexploder, cryptphi, eierina, joestakey, kaden, neumo, pashov, rvierdiiev, sorrynotsorry
24.2165 USDC - $24.22
https://github.com/code-423n4/2022-10-inverse/blob/3e81f0f5908ea99b36e6ab72f13488bbfe622183/src/Oracle.sol#L87-L88 https://github.com/code-423n4/2022-10-inverse/blob/3e81f0f5908ea99b36e6ab72f13488bbfe622183/src/Oracle.sol#L121-L122
The Oracle's viewPrice() and getPrice() functions retrieve the price of a specific token adjusted for token and feed decimals before applying the collateral factor.
While it uses the token's and feed's decimals()
function to retrieve their respective decimals, it uses a hardcoded (36) value during normalization.
Given the potential consequences of an invalid result, if the feed and/or token decimals are expected to be fixed, either there is no point in querying the decimals count or there shoud be a guard condition (require / if-revert). The hardcoding solution is only mentioned to point out the need for a guard condition or a proper normalization code.
With the current code, depending on the token/feed pair's decimals count, the normalizedPrice may either result in an invalid scaling by excess or by defect. A corrected normalization is proposed, with a tiny change, in the mitigation section.
n/a
Replace the following two lines of code found here and here
uint8 decimals = 36 - feedDecimals - tokenDecimals; uint normalizedPrice = price * (10 ** decimals);
with the following code
uint normalizedPrice = tokenDecimals >= feedDecimals ? (price * 10**(tokenDecimals - feedDecimals)) : (price / 10**(feedDecimals - tokenDecimals));
While not recommending a fit-some-cases
in exchange of the above's fit-all-cases
solution, if still willing to keep the partially hardcoded / partially programmatic decimals normalization code (fit-some-cases
), then at least add the following check to the Oracle's viewPrice() and getPrice() functions or to the setFeed() function.
require(tokenDecimals == 18, "Unsupported token")
#0 - c4-judge
2022-11-05T21:13:36Z
0xean marked the issue as duplicate
#1 - c4-judge
2022-11-28T16:06:30Z
0xean marked the issue as partial-50
#2 - c4-judge
2022-11-28T16:06:37Z
0xean marked the issue as not a duplicate
#3 - c4-judge
2022-11-28T16:06:45Z
0xean marked the issue as duplicate of #540
#4 - Simon-Busch
2022-12-05T15:33:30Z
Issue marked as satisfactory as requested by 0xean
#5 - c4-judge
2022-12-07T08:18:20Z
Simon-Busch marked the issue as duplicate of #533
🌟 Selected for report: rbserver
Also found by: 0x1f8b, 0xNazgul, 0xc0ffEE, 8olidity, Aymen0909, Chom, Franfran, Jeiwan, Jujic, Lambda, M4TZ1P, Olivierdem, Rolezn, Ruhum, TomJ, Wawrdog, __141345__, bin2chen, c7e7eff, carlitox477, catchup, cccz, codexploder, cuteboiz, d3e4, dipp, djxploit, eierina, elprofesor, hansfriese, horsefacts, idkwhatimdoing, imare, immeas, joestakey, ladboy233, leosathya, martin, minhtrng, pashov, peanuts, pedroais, rokinot, rvierdiiev, saneryee, sorrynotsorry, tonisives
0.385 USDC - $0.38
https://github.com/code-423n4/2022-10-inverse/blob/3e81f0f5908ea99b36e6ab72f13488bbfe622183/src/Oracle.sol#L82 https://github.com/code-423n4/2022-10-inverse/blob/3e81f0f5908ea99b36e6ab72f13488bbfe622183/src/Oracle.sol#L116
There are a two issues with the Oracle's viewPrice() and getPrice() functions:
https://github.com/code-423n4/2022-10-inverse/blob/3e81f0f5908ea99b36e6ab72f13488bbfe622183/src/Oracle.sol#L82 https://github.com/code-423n4/2022-10-inverse/blob/3e81f0f5908ea99b36e6ab72f13488bbfe622183/src/Oracle.sol#L116
https://docs.chain.link/docs/data-feeds/price-feeds/api-reference/#latestanswer
https://blog.venus.io/venus-protocol-luna-incident-update-2-c334475d9214
n/a
Use latestRoundData() API and do verify for stale data eventually suspending the service if stale data is found like in cases feed suspension during extreme market volatility (e.g. see Venus Protocol hack during Chainlink LUNA price feed suspension)
diff --git a/src/Oracle.sol b/src/Oracle.sol index 14338ed..d56d118 100644 --- a/src/Oracle.sol +++ b/src/Oracle.sol @@ -79,8 +79,10 @@ contract Oracle { if(fixedPrices[token] > 0) return fixedPrices[token]; if(feeds[token].feed != IChainlinkFeed(address(0))) { // get price from feed - uint price = feeds[token].feed.latestAnswer(); - require(price > 0, "Invalid feed price"); + (, int256 answer, , uint256 updatedAt, ) = feeds[token].feed.latestRoundData(); + require(block.timestamp <= updatedAt + PRICE_MAX_AGE, "Stale feed detected"); + require(answer > 0, "Invalid feed price"); + uint price = uint(answer); // normalize price uint8 feedDecimals = feeds[token].feed.decimals(); uint8 tokenDecimals = feeds[token].tokenDecimals; @@ -113,8 +115,11 @@ contract Oracle { if(fixedPrices[token] > 0) return fixedPrices[token]; if(feeds[token].feed != IChainlinkFeed(address(0))) { // get price from feed - uint price = feeds[token].feed.latestAnswer(); - require(price > 0, "Invalid feed price"); + (, int256 answer, , uint256 updatedAt, ) = feeds[token].feed.latestRoundData(); + require(block.timestamp <= updatedAt + PRICE_MAX_AGE, "Stale feed detected"); + require(answer > 0, "Invalid feed price"); + uint price = uint(answer); // normalize price uint8 feedDecimals = feeds[token].feed.decimals(); uint8 tokenDecimals = feeds[token].tokenDecimals;
Refer to Chainlink's StalenessFlaggingValidator.
If deploying to L2, extra caution should be taken for L2 Sequencer uptime as shown in the following Chainlink example.
#0 - neumoxx
2022-10-31T08:51:37Z
Duplicate of #601
#1 - c4-judge
2022-11-05T17:53:57Z
0xean marked the issue as duplicate
#2 - Simon-Busch
2022-12-05T15:25:03Z
Issue marked as satisfactory as requested by 0xean
#3 - c4-judge
2022-12-07T08:14:13Z
Simon-Busch marked the issue as duplicate of #584