Platform: Code4rena
Start Date: 03/02/2022
Pot Size: $75,000 USDC
Total HM: 42
Participants: 52
Period: 7 days
Judge: GalloDaSballo
Total Solo HM: 21
Id: 83
League: ETH
Rank: 13/52
Findings: 4
Award: $1,515.05
🌟 Selected for report: 1
🚀 Solo Findings: 1
🌟 Selected for report: mtz
Also found by: 0x1f8b, Czar102, GalloDaSballo, GeekyLumberjack, Randyyy, Rhynorater, Ruhum, ShadowyNoobDev, bitbopper, cccz, cmichel, csanuragjain, danb, gzeon, hickuphh3, hyh, leastwood
31.0722 USDC - $31.07
The user can drain the contract.
The withdraw
method does not check that the user has called this method before, there is also no tracking of how many tokens the user donates, so the calls client.shareOf(_token, msg.sender) / client.totalShare(_token )
are not related to how many tokens this user deposited, it is about how much balance was deposited.
This method does not subtract transferred tokens from savedTokens[_token]
, and also does not use the flag claimed[_token][_to] = true;
so there is no restriction on a user calling this method multiple times until drain the contract.
Steps:
withdraw
multiple timesclaimed[_token][_to]
to avoid duplicate calls to withdraw
#0 - GalloDaSballo
2022-04-12T23:29:01Z
Duplicate of #246
🌟 Selected for report: 0x1f8b
Shelter contract can steal user tokens.
Shelter client
can call activate
on an already activated token, this will reset its start time, so if the client activate a token when it GRACE_PERIOD
is almost finished, it will reset this time.
This will prevent the user to call withdraw
because the condition activated[_token] + GRACE_PERIOD < block.timestamp
but will allow the client to call deactivate
and receive all funds from the users because it will satisfy the condition activated[_token] + GRACE_PERIOD > block.timestamp
.
Steps:
activate
tokenA.donate
.activate
tokenA again until they has enough tokens.donate
.activate
twice for the same tokendonate
only after the GRACE_PERIOD
#0 - GalloDaSballo
2022-04-15T22:58:26Z
I believe the finding to be valid, the warden has shown how the Shelter design allows the client to repeatedly call activate
to prevent anyone from withdrawing the tokens.
Because this is contingent on a malicious admin, I believe Medium Severity to be more appropraite
🌟 Selected for report: hickuphh3
Also found by: 0x1f8b, 0xw4rd3n, BouSalman, CertoraInc, Czar102, Dravee, IllIllI, Randyyy, Rhynorater, Ruhum, ShadowyNoobDev, Sleepy, SolidityScan, WatchPug, bitbopper, cccz, cryptphi, csanuragjain, defsec, gzeon, harleythedog, hubble, hyh, kenta, kirk-baird, leastwood, mtz, pauliax, peritoflores, rfa, robee, samruna, throttle, wuwe1, ye0lde
163.9289 USDC - $163.93
The method ConcurRewardPool.pushReward should emit an event in order to be detectable by the _recipient
.
The method ConcurRewardPool.claimRewards allow the reentrancy, it seems that it's not vulnerable but it should be protected in order to be resilient.
The following contracts are Ownable
and Pausable
, so the owner could resign while the contract is paused, causing a Denial of Service. Owner resignation while paused should be avoided:
transfer
, approve
or transferFrom
without checking the boolean result, ERC20 standard specify that the token can return false if this call was not made, so it's mandatory to check the result of these methods.As following you can see the affected locations:
_amount=0
it should be denied in order to avoid possible errors.#0 - GalloDaSballo
2022-04-21T16:54:25Z
Events -> Informational
Reentrancy -> Valid (no POC so low)
Contract management risks Withdrawal are not paused so technically it should be fine, but interesting finding
Lack of check Valid
0 check in requestWithdraw Personally don't think it would make any differnce
#1 - GalloDaSballo
2022-04-27T14:54:38Z
2+++
🌟 Selected for report: WatchPug
Also found by: 0x0x0x, 0x1f8b, 0x510c, 0xNot0rious, 0xngndev, BouSalman, CertoraInc, Dravee, Heartless, IllIllI, Jujic, Randyyy, Ruhum, ShadowyNoobDev, Sleepy, SolidityScan, Tomio, bitbopper, csanuragjain, defsec, gzeon, hickuphh3, kenta, mtz, pauliax, peritoflores, rfa, robee, sabtikw, throttle, wuwe1, ye0lde
140.0476 USDC - $140.05
i++
to ++i
in order to save some opcodes:false
or 0)There was an empty pool in lines L63-L70, pushed during the constructor that never is used.
The following struct could be optimized moving depositFeeBP
close to depositToken
in order to save one storage slot:
struct PoolInfo { IERC20 depositToken; // <- Move here uint allocPoint; uint lastRewardBlock; uint accConcurPerShare; uint16 depositFeeBP; // <- Move this }
transferSuccess
variable is init false, it is better to create the variable with the return to avoid this initialization.== true
in order to save some opcodes.startLiquidity
is never used and can be removed or changed to immutable.step
coud be constant.usdm.balanceOf(address(this))
was called twice without any change of balance, it's cheaper to cache it.deposits[_pid][msg.sender].amount
inside the if if (_amount > 0) {
deposits[_pid][msg.sender].amount -= uint192(_amount); if (_amount > 0) {
#0 - GalloDaSballo
2022-03-25T17:23:35Z
MastChef.sol
VoteProxy.sol
USDMPegRecovery.sol
ConvexStakingWrapper.sol
18812 Gas Saved by implementing these findings