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: 37/147
Findings: 2
Award: $166.32
🌟 Selected for report: 0
🚀 Solo Findings: 0
161.7958 USDC - $161.80
https://github.com/code-423n4/2023-10-ethena/blob/ee67d9b542642c9757a6b826c82d0cae60256509/contracts/StakedUSDe.sol#L225-L238 https://github.com/code-423n4/2023-10-ethena/blob/ee67d9b542642c9757a6b826c82d0cae60256509/contracts/StakedUSDeV2.sol#L92-L122 https://github.com/code-423n4/2023-10-ethena/blob/ee67d9b542642c9757a6b826c82d0cae60256509/contracts/StakedUSDeV2.sol#L52-L73
Due to the provided Ethena documentation on C4 and Gitbook SOFT_RESTRICTED_STAKER_ROLE should have restrictions in scope of StakedUSDeV2 for deposit and withdraw.
Due to legal requirements, there's a SOFT_RESTRICTED_STAKER_ROLE and FULL_RESTRICTED_STAKER_ROLE. The former is for addresses based in countries we are not allowed to provide yield to, for example USA. Addresses under this category will be soft restricted. They cannot deposit USDe to get stUSDe or withdraw stUSDe for USDe. However they can participate in earning yield by buying and selling stUSDe on the open market.
This issue marked as high because it violates main principles of provided contract and Ethena documentation. Without proper restrictions, SOFT_RESTRICTED_STAKER_ROLE can still withdraw stUSDe in exchange for USDe that breaks whole sense of this limitation.
In the StakedUSDe(parent of StakedUSDeV2 and implementation of SingleAdminAccessControl) contract we can see that SOFT_RESTRICTED_STAKER_ROLE checks are only present in _deposit() function. Therefore, the soft restricted role cannot deposit.
While in _withdraw() there are only checks for FULL_RESTRICTED_STAKER_ROLE, with no checks related to SOFT_RESTRICTED_STAKER_ROLE
So SOFT_RESTRICTED_STAKER_ROLE can continues to use all withdraw-based operations from StakedUSDeV2 like withdraw(), redeem(), cooldownAssets(), cooldownShares().
Manual check
Update _withdraw(). Change FULL_RESTRICTED_STAKER_ROLE(which will be anyway checked during _beforeTokenTransfer()) with SOFT_RESTRICTED_STAKER_ROLE to block this role from withdrawals.
Access Control
#0 - c4-pre-sort
2023-11-01T02:15:14Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-11-01T02:15:26Z
raymondfam marked the issue as duplicate of #52
#2 - c4-judge
2023-11-10T21:44:18Z
fatherGoose1 marked the issue as satisfactory
#3 - c4-judge
2023-11-14T17:21:26Z
fatherGoose1 changed the severity to 2 (Med Risk)
🌟 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
https://github.com/code-423n4/2023-10-ethena/blob/ee67d9b542642c9757a6b826c82d0cae60256509/contracts/EthenaMinting.sol#L124 https://github.com/code-423n4/2023-10-ethena/blob/ee67d9b542642c9757a6b826c82d0cae60256509/contracts/EthenaMinting.sol#L138-L140
Make no sense because contract inherited from SingleAdminAccessControl, that will have only one. And it's better for readability to set Admin in one "if" block.
Move in scope of one "if-else" block.
https://github.com/code-423n4/2023-10-ethena/blob/ee67d9b542642c9757a6b826c82d0cae60256509/contracts/EthenaMinting.sol#L290-L295 https://github.com/code-423n4/2023-10-ethena/blob/ee67d9b542642c9757a6b826c82d0cae60256509/contracts/EthenaMinting.sol#L126-L128
It's possible to add StackedUSDe(V2) asset for EthenaMinting._supportedAssets. This can lead to endless minting of USDe and StackedUSDe(V2) tokens.
Add check for StackedUSDe(V2).
There is no information about how contract and mint logic should work with native Eth, but both receive() and EthenaMinting._transferToBeneficiary() support it. Currently there is no options to mint() with native Eth, but contract still can burn() during EthenaMinting.redeem() https://github.com/code-423n4/2023-10-ethena/blob/ee67d9b542642c9757a6b826c82d0cae60256509/contracts/EthenaMinting.sol#L194-L216
Make flows consistent to be sure that end logic wouldn't be broken.
With current implementation getUnvestedAmount() always will be null, so there is no reason for "amount + getUnvestedAmount()".
Remove unnecessary call.
https://github.com/code-423n4/2023-10-ethena/blob/ee67d9b542642c9757a6b826c82d0cae60256509/contracts/StakedUSDe.sol#L101-L113 https://github.com/code-423n4/2023-10-ethena/blob/ee67d9b542642c9757a6b826c82d0cae60256509/contracts/StakedUSDe.sol#L115-L127
Both addToBlacklist() and removeFromBlacklist() have code documentation with similar context: Allows the owner (DEFAULT_ADMIN_ROLE) and blacklist managers to ... But with current implementation only blacklist manager can call both functions. I report it as QA because from what I saw there is no additional documentation on that in Gitbook or C4 audit description.
Fix code documentation.
Marked as low because it shouldn't really break anything, but this flow still can be a risk in case if Ethena pending Admin will be a sender or receiver in assets transfer flow. https://github.com/code-423n4/2023-10-ethena/blob/ee67d9b542642c9757a6b826c82d0cae60256509/contracts/StakedUSDe.sol#L106-L109 In addToBlacklist() function we can see check notOwner(target). So contract have protection to not block an Admin of that contract. But because the fact that SingleAdminAccessControl use Transfer/Accept pattern it's still possible to make a mistake and block future(pending) Admin. There is no check during Admin acceptance.
And even more important because of a similar notOwner(target) check in removeFromBlacklist() function. That literally blocks any way to unblock blocked admin. https://github.com/code-423n4/2023-10-ethena/blob/ee67d9b542642c9757a6b826c82d0cae60256509/contracts/StakedUSDe.sol#L120C3-L123
Recheck if the blocking or unblocking of the Admin leads to any errors that are outside the scope of this audit. Fix removeFromBlacklist() function.
Value of current cooldown isn't checked in unstake(). In case when we disable cooldown with setCooldownDuration(0) users will still need to wait for previous cooldown if request for withdraw have been submitted(cooldowns[owner]). https://github.com/code-423n4/2023-10-ethena/blob/ee67d9b542642c9757a6b826c82d0cae60256509/contracts/StakedUSDeV2.sol#L78-L90
Extend unstake() logic with checks for disabled cooldowns.
Only single silo(USDeSilo) entity is created in StakedUSDeV2 constructor. In case if it will be restricted with FULL_RESTRICTED_STAKER_ROLE role that will block cooldownAssets() and cooldownShares() functions. Both will be blocked because FULL_RESTRICTED_STAKER_ROLE blocks every token interaction via _beforeTokenTransfer() https://github.com/code-423n4/2023-10-ethena/blob/ee67d9b542642c9757a6b826c82d0cae60256509/contracts/StakedUSDe.sol#L245-L252
Add additional logic to check that such case is not possible.
public MAX_COOLDOWN_DURATION = 90 days
should be constant
Library added but not used. https://github.com/code-423n4/2023-10-ethena/blob/ee67d9b542642c9757a6b826c82d0cae60256509/contracts/USDeSilo.sol#L13
Deprecated since 4.9.0 https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v4.9.0, use the proper one ERC20Permit. Similar dependency used in StackedUSDe.sol
#0 - raymondfam
2023-11-02T02:08:00Z
Two Admin _grantRole in one constructor: False positive USDeSilo can be assigned to FULL_RESTRICTED_STAKER_ROLE role: False positive Unused SafeERC20 library: bot deprecated-ERC20Permit import: bot
#1 - c4-pre-sort
2023-11-02T02:08:06Z
raymondfam marked the issue as sufficient quality report
#2 - c4-judge
2023-11-14T17:07:51Z
fatherGoose1 marked the issue as grade-b