Platform: Code4rena
Start Date: 24/10/2023
Pot Size: $36,500 USDC
Total HM: 4
Participants: 147
Period: 6 days
Judge: 0xDjango
Id: 299
League: ETH
Rank: 78/147
Findings: 2
Award: $10.98
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xmystery
Also found by: 0x11singh99, 0xAadi, 0xAlix2, 0xG0P1, 0xStalin, 0xWaitress, 0x_Scar, 0xhacksmithh, 0xhunter, 0xpiken, Al-Qa-qa, Arz, Avci, Bauchibred, BeliSesir, Breeje, Bughunter101, DarkTower, Eeyore, Fitro, HChang26, Imlazy0ne, J4X, JCK, Kaysoft, Kral01, Madalad, Mike_Bello90, Noro, PASCAL, PENGUN, Proxy, Rickard, Shubham, SovaSlava, Strausses, Team_Rocket, ThreeSigma, Topmark, Udsen, Walter, Yanchuan, Zach_166, ZanyBonzy, adam-idarrha, adeolu, almurhasan, arjun16, ast3ros, asui, ayden, btk, cartlex_, castle_chain, cccz, chainsnake, codynhat, critical-or-high, cryptonue, csanuragjain, deepkin, degensec, dirk_y, erebus, foxb868, ge6a, hunter_w3b, jasonxiale, kkkmmmsk, lanrebayode77, lsaudit, marchev, matrix_0wl, max10afternoon, nuthan2x, oakcobalt, oxchsyston, pavankv, peanuts, pep7siup, pipidu83, pontifex, ptsanev, qpzm, radev_sw, rokinot, rotcivegaf, rvierdiiev, sorrynotsorry, squeaky_cactus, supersizer0x, tnquanghuy0512, twcctop, twicek, young, zhaojie, ziyou-
4.5226 USDC - $4.52
In the contract StakedUSDe.sol
the functions addToBlacklist()
and removeFromBlacklist()
both say in the NatSpec comments that the owner and the blacklist manager can call these functions. However the code only allows the blacklist manager to call these functions.
In SingleAdminAccessControl.sol
the name of the modifier notAdmin()
should be more specific (i.e. roleNotAdmin()
). The current name is too generic, leading to confusion and could be interpreted as what is being check is if the msg.sender
is the admin
(as often seen in other protocols).
The same issue appears in the StakedUSDe.sol
contract, where a modifier with a similar name is used here (notOwner()
), but now what is being checked is if the blacklist target is not the owner. Thus, a more clear modifier name could be blacklistTargetNotOwner()
.
In SingelAdminAccessControl.sol
, in line 18, the error message does not match what is happening in the modifier. This modifier checks if a given role
is the DEFAULT_ADMIN_ROLE
, and thus the error InvalidAdminChange
should be replaced with a more suitable error such as RoleIsAdmin
.
In USDeSilo.sol
when establishing the USDe
contract here, the IERC20
interface is used. This is fine since for now the only function called in the contract is the transfer function that is in the IERC20
interface. However, since there is a IUSDe
interface already written that extends IERC20
it should be used here for clarity purposes and for future uses.
#0 - c4-pre-sort
2023-11-02T02:17:11Z
raymondfam marked the issue as sufficient quality report
#1 - c4-judge
2023-11-14T17:06:51Z
fatherGoose1 marked the issue as grade-b
🌟 Selected for report: 0xVolcano
Also found by: 0x11singh99, 0xAadi, 0xAnah, 0xgrbr, 0xhacksmithh, 0xhex, 0xpiken, 0xta, J4X, JCK, K42, Raihan, Rolezn, SAQ, SM3_SS, Sathish9098, SovaSlava, ThreeSigma, Udsen, arjun16, aslanbek, brakelessak, castle_chain, evmboi32, hunter_w3b, lsaudit, naman1778, niser93, nuthan2x, oakcobalt, pavankv, petrichor, phenom80, radev_sw, shamsulhaq123, tabriz, thekmj, unique, yashgoel72, ybansal2403
6.4563 USDC - $6.46
In the constructor of EthenaMinting.sol
the role of DEFAULT_ADMIN_ROLE
is first given to msg.sender
and then to _admin
. This is done in the constructor so the functions addSupportedAsset()
and addCustodianAddress()
can be called since they have the condition onlyRole(DEFAULT_ADMIN_ROLE)
.
The contract can be optimized by creating internal functions for adding the assets and custodian addresses that do not have the modifier, which will be called in the constructor and in the public add functions which wil keep the modifier. This way in the constructor you only need to call _grantRole()
once for the _admin
without the if
condition saving on gas.
return
value since it will always be the same.In the contract EthenaMinting.sol
the functions VerifyOrder()
and verifyNonce()
return a boolean along other variables. This boolean will always be true so it is not needed. If the call doesn't revert we know that the verification worked so the boolean is unnecessary.
In the contract EthenaMinting.sol
the function VerifyRoute()
receives the orderType
and proceeds to check if it is a REDEEM
if so it returns true. This function is only called internally from the mint()
function, and if someone calls the function publicly they wouldn't say it is for a REDEEM
as the route is only needed for the mint. Therefore there is no need to receive the orderType
in the function nor is its verification.
VerifyNonce()
In the contract EthenaMinting.sol
the function VerifyNonce()
creates the variable invalidatorStorage
unnecessarily. It would be less gas to simply replace lines 381 and 382 with uint256 invalidator = _orderBitmaps[sender][invalidatorSlot];
_deduplicateOrder()
In the contract EthenaMinting.sol
the function _deduplicateOrder()
creates the variable invalidatorStorage
unnecessarily. It would be less gas to simply replace the lines 393 and 394 with _orderBitmaps[sender][invalidatorSlot] = invalidator | invalidatorBit;
.
In the contract StakedUSDe.sol
the function transferInRewards()
checks that getUnvestedAmount()
returns 0. In the next line it calls getUnvestedAmount()
again to add it to a variable, but we already know it will return 0. Remove the second call to getUnvestedAmount()
.
#0 - c4-pre-sort
2023-11-01T18:51:10Z
raymondfam marked the issue as sufficient quality report
#1 - c4-judge
2023-11-10T20:28:18Z
fatherGoose1 marked the issue as grade-b