Platform: Code4rena
Start Date: 24/07/2023
Pot Size: $100,000 USDC
Total HM: 18
Participants: 73
Period: 7 days
Judge: alcueca
Total Solo HM: 8
Id: 267
League: ETH
Rank: 52/73
Findings: 1
Award: $44.88
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: immeas
Also found by: 0x70C9, 0xAnah, 0xArcturus, 0xComfyCat, 0xWaitress, 0xackermann, 0xkazim, 2997ms, 33audits, Arz, Aymen0909, ChrisTina, JP_Courses, John_Femi, Jorgect, Kaysoft, LosPollosHermanos, MohammedRizwan, Nyx, Rolezn, Sathish9098, Stormreckson, T1MOH, Tendency, Topmark, Udsen, Vagner, albertwh1te, ast3ros, banpaleo5, berlin-101, catellatech, cats, codetilda, cryptonue, eeshenggoh, fatherOfBlocks, hals, jamshed, jaraxxus, josephdara, kankodu, kodyvim, kutugu, lanrebayode77, mert_eren, nadin, naman1778, niki, petrichor, ravikiranweb3, said, solsaver, souilos, twcctop, wahedtalash77
44.8793 USDC - $44.88
MultiRewardDistributor._rescueFounds
is an easy way to rug pull and could make the project less thrustworthy.MultiRewardDistributor._rescueFunds
allows admin
to withdraw any amount of funds at any time. This might repell potential investors.
Consider adding a third part multi-sig which receives funds rather then the admin
.
MultiRewardDistributor.sendReward
could send remainings even if it does not have enough foundsThe MultiRewardDistributor.sendReward
function could process remaining rewards even if _amount > currentTokenHoldings
. This approach prevents contract from freezing funds and could be benefitial for users, since they would get a part of their reward immidiately.
In MultiRewardDistributor.sendReward
in else statement consider doing token.safeTransfer(_user, token.balanceOf(address(this))
and return _amount - previousBalance
where previousBalance
is token.balanceOf(address(this))
before safeTransfer
Comptroller._setMarketBorrowCaps
and Comptroller.setMarketSupplyCaps
could accidentaly be called with a mtoken
without a market.Comptroller._setMarketBorrowCaps
and Comptroller.setMarketSupplyCaps
do not make any check that a mtoken
has a registered market.
In both functions check that markets for supplied mtokens
exist by requireing markets[mTokens[i]].isListed
for each token.
_setMarketBorrowCaps
_setMarketSupplyCaps
Comptroller._rescueFunds
does not use safeTransfer
Using safeTransfer
instead of transfer
is considered a better security practise. Even thought _rescueFounds
is used only in case of emergency it is advised to use safeTransfer
Replace transfer
with safeTransfer
in Comptroller._rescueFunds
mTokens
to a market can cause transactions to failAdding too many mTokens
to a market can cause a unbounded for loop which will force transactiosn to fail due to high gas usage.
Add a limit for the number of mTokens
supported.
_endTime
check in MultiRewardDistributor._addEmissionConfig
MultiRewardDistributor._addEmissionConfig()
checks that _endTime
is in future by asserting that _endTime > block.timestamp + 1
. This assertion should be modified to _endTime > block.timestamp
.
Modify a require statement in MultiRewardDistributor._addEmissionConfig()
from _endTime > block.timestamp + 1
to _endTime > block.timestamp
marketSupplySpeed <= emissionCap
invariant can be broken.emissionCap
in MultiRewardDistrubitor
can be decreased and therefore break the invariant of marketSupplySpeed <= emissionCap
Add a line in MultiRewardDistributor.calculateNewIndex()
so that if supply speed is bigger than no reawrds are accrued.
Comptroller.seizeAllowed()
does not check that mTokens
have correct comptroller
addressIn seizeAllowed()
, there is a check that the comptroller
address of both the collateral and borrow tokens match. However, there is no check that this address is not an arbitrary address. This will function to revert with no error code.
Consider adding the following code to seizeAllowed()
:
if (MToken(mTokenCollateral).comptroller() != address(this)) { return uint(Error.COMPTROLLER_MISMATCH); }
MultiRewardDistributor.sendReward
is inconsistent with the project's naming conventionIn this project, internal functions' names end with Internal
(eg. disburseBorrowerRewardsInternal
, updateMarketBorrowIndexInternal
). sendReward
is an internal function, but does not match this convention.
Consider renaming sendReward
to sendRewardInternal
JumpRateModel
and WhitePaperIntrestRateModel
could be extractedWhitePaperIntrestRateModel & JumpRateModel vary only at getBorrowRate
function.
Consider moving common logic to another contract and inherit from it.
WhitePaperIntrestRateModel
JumpRateModel
Comptroller
is missleading.Assertions made with enums and integers make code less readable.
Consider changing all instances of allowed != 0
to allowed != Error.NO_ERROR
ERROR.NO_ERROR
is missleadingName ERROR
makes reader think that the code is not doing something that it should; however, ERROR.NO_ERROR
is used when the code behaves properly.
At first ERROR.NO_ERROR
resembles any error that does not have a corresponding enum value yet.
Consider renaming ERROR
to STATUS
, and begin any error value with ERROR_
MarketRewardDistributor.calcualteNewIndex()
will fail in the futureMarketRewardDistributor.calcualteNewIndex()
will fail after the year 2106 due to casting of block.timestamp
Consider casting block.timestamp
to a bigger integer type.
MarketRewardDistributor.updateMarketSupplyIndexInternal()
MToken
already is a MTokenInterface
so casting is unneccessary.
Remove casting to MTokenInterface
from MarketRewardDistributor.updateMarketSupplyIndexInternal()
#0 - c4-judge
2023-08-12T17:30:38Z
alcueca marked the issue as grade-a
#1 - c4-sponsor
2023-08-15T18:33:42Z
ElliotFriedman marked the issue as sponsor confirmed