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: 21/39
Findings: 3
Award: $407.82
🌟 Selected for report: 0
🚀 Solo Findings: 0
https://github.com/code-423n4/2022-02-hubble/blob/main/contracts/Oracle.sol#L33 https://github.com/code-423n4/2022-02-hubble/blob/main/contracts/Oracle.sol#L64 https://github.com/code-423n4/2022-02-hubble/blob/main/contracts/Oracle.sol#L115
Oracle returns Chainlink latestRoundData without proper validation, e.g.:
function getUnderlyingPrice(address underlying) ... (,answer,,,) = AggregatorV3Interface(chainLinkAggregatorMap[underlying]).latestRoundData(); answer /= 100; }
And other functions that call latestRoundData are quite similar. The problem is that there are no checks if the return value indicates stale data. This could lead to stale prices according to the Chainlink documentation: “if answeredInRound < roundId could indicate stale data.” “A timestamp with zero value means the round is not complete and should not be used.”
Add missing checks for stale data. See example here: https://github.com/cryptexfinance/contracts/blob/master/contracts/oracles/ChainlinkOracle.sol#L58-L65
#0 - atvanguard
2022-02-24T06:59:23Z
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
require(_maintenanceMargin > 0, "_maintenanceMargin < 0");
There are functions that do not follow the Check-Effects-Interaction pattern, e.g. addMarginFor, processWithdrawals. They have external calls in the middle of execution, e.g. inside the loop, so should have extra protection from re-entrancy just in case, unless you 100% trust these external contracts (e.g. tokens), but nevertheless I think you should always act preventively.
VUSD returns hardcoded 6 decimals:
function decimals() public pure override returns (uint8) { return 6; }
While in practice it should be tied with USDC token that has 6 decimals:
/// @notice vUSD is backed 1:1 with reserveToken (USDC) IERC20 public immutable reserveToken;
There is no restriction of setting another reserveToken, so consider calling .decimals() when setting the reserveToken, and then assign the same value to the VUSD decimals.
// @todo put checks on slippage // sending market orders can fk the trader. @todo put some safe guards around price of liquidations // @todo handle case when totalPosition = 0 @todo consider providing some incentive from insurance fund to execute a liquidation in this scenario.
while (i < withdrawals.length && (i - start) <= maxWithdrawalProcesses)
e.g. when maxWithdrawalProcesses = 3, it will actually execute the loop 4 times.
Consider introducing reasonable upper and lower limits for setMaxWithdrawalProcesses, otherwise an admin can grief by setting it to 0 and thus blocking the withdrawals, unless this may be intended.
Oracle always assumes that the result will have 8 decimals, thus it divides by a hardcoded value of 100. You should verify that by calling .decimals on Chainlink oracle: https://docs.chain.link/docs/price-feeds-api-reference/#decimals
#0 - atvanguard
2022-02-26T06:47:25Z
The last issue is a duplicate of #21. The rest are acknowledged.
87.3994 USDC - $87.40
require(margin[idx][trader] >= amount.toInt256(), "Insufficient balance"); margin[idx][trader] -= amount.toInt256();
i += 1;
while (i < withdrawals.length && (i - start) <= maxWithdrawalProcesses)
#0 - atvanguard
2022-02-26T08:07:58Z
Duplicate of #21