Platform: Code4rena
Start Date: 03/05/2022
Pot Size: $50,000 USDC
Total HM: 4
Participants: 46
Period: 5 days
Judge: gzeon
Total Solo HM: 2
Id: 117
League: ETH
Rank: 29/46
Findings: 1
Award: $114.33
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: BowTiedWardens
Also found by: 0x1337, 0x1f8b, 0x4non, 0xDjango, David_, Funen, GimelSec, IllIllI, Picodes, TerrierLover, WatchPug, bobi, cryptphi, csanuragjain, delfin454000, dirk_y, ellahi, fatherOfBlocks, hyh, ilan, jayjonah8, kebabsec, leastwood, oyc_109, robee, samruna, simon135, sorrynotsorry, throttle
114.326 USDC - $114.33
Core collateral valuation logic depends on a valid oracle, functionality of the system will be unavailable if the oracle is set to zero (there will be low level reverts).
nftOracle value isn't controlled to be valid:
_setNftPriceOracle:
function _setNftPriceOracle(NftPriceOracle newOracle) public returns (uint) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PRICE_ORACLE_OWNER_CHECK); } // Set comptroller's nft oracle to newOracle nftOracle = newOracle;
_initializeNftCollateral:
nftOracle = _nftOracle;
Both functions are admin only, but nftOracle can be omitted by mistake.
Consider adding zero address checks
One step process offers no protection for the cases when admin rights transfer is performed mistakenly or with any malicious intent.
Adding a modest complexity of an additional step and a delay is a low price to pay for having time to evaluate the change.
function _changeAdmin(address newAdmin) external { require(msg.sender == admin, "Only admin can change the admin"); admin = newAdmin; }
Consider utilizing two-step admin rights transferring process (proposition and acceptance in the separate actions) with a noticeable delay between the steps to enforce the transparency and stability of the system.
As different compiler versions have critical behavior specifics if the contracts get accidentally deployed using another compiler version compared to the one they were tested with, various types of undesired behavior can be introduced.
Old contracts use pragma solidity ^0.5.16
:
New ones use pragma solidity ^0.8.0
:
As this Compound fork was developed and tested with some stable version of compiler it's adviced to directly fixing it for the whole set of system contracts.
As the tokens here are IERC1155 and IERC721 that have transfer hook, this would be immediately exploitable with the whole contract balance as the target in absence of nonReentrant modifier.
Now suggesting to move it after transfers as a best practice.
totalBalance is increased before assets have changed hands:
totalBalance[msg.sender] += totalAmount;
Move balance increase to happen after fund transfer:
totalBalance[msg.sender] += totalAmount; _mintBatch(msg.sender, tokenIds, amounts, "");
Should be 'instead':
// We call the internal function instad of the public one because in liquidation, we
Assert will consume all the available gas, providing no additional benefits when being used instead of require, which both returns gas and allows for error message.
assert(assetIndex < len);
assert(markets[cToken].accountMembership[borrower]);
Using assert in production isn't recommended, consider substituting it with require in all the cases.