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: 35/147
Findings: 2
Award: $166.32
🌟 Selected for report: 0
🚀 Solo Findings: 0
161.7958 USDC - $161.80
The current _withdraw
function implementation allows soft-restricted stakers to withdraw stUSDe in exchange for USDe, effectively earning yield. This is a legal violation.
Legally, a soft-restricted staker is prohibited from certain activities, including depositing USDe to obtain stUSDe or withdrawing stUSDe for USDe.
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.
Upon examining the _withdraw
function in the StakedUSDe
contract, it appears that only the FULL_RESTRICTED_STAKER_ROLE
is blocked from withdrawal, leaving soft-restricted stakers unchecked. There can be a case when a user is being soft restricted after he is deposited into the StakedUSDe (his ccountry is included in the blocked list), he can still withdraw and earn yield.
This creates a legal vulnerability for the Ethena protocol, which is obligated to restrict yield payments to users from certain jurisdictions. Currently, there's no on-chain mechanism to enforce this requirement.
function _withdraw(address caller, address receiver, address _owner, uint256 assets, uint256 shares) internal override nonReentrant notZero(assets) notZero(shares) { if (hasRole(FULL_RESTRICTED_STAKER_ROLE, caller) || hasRole(FULL_RESTRICTED_STAKER_ROLE, receiver)) { revert OperationNotAllowed(); } super._withdraw(caller, receiver, _owner, assets, shares); _checkMinShares(); }
Manual
When withdrawing, check if user is soft restricted:
Other
#0 - c4-pre-sort
2023-10-31T06:02:16Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-10-31T06:02:41Z
raymondfam marked the issue as duplicate of #52
#2 - c4-judge
2023-11-10T21:39:53Z
fatherGoose1 marked the issue as satisfactory
🌟 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
The _custodians array is not checked for zero length in constructor. If there is no custodian set, functionality of the EthenalMinting such as mint
can be blocked.
if (address(_usde) == address(0)) revert InvalidUSDeAddress(); if (_assets.length == 0) revert NoAssetsProvided(); if (_admin == address(0)) revert InvalidZeroAddress(); usde = _usde; _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); for (uint256 i = 0; i < _assets.length; i++) { addSupportedAsset(_assets[i]); } for (uint256 j = 0; j < _custodians.length; j++) { addCustodianAddress(_custodians[j]); }
To address this issue, add checking length for _custodians array.
if (_custodians.length == 0) revert NoAssetsProvided();
The getUnvestedAmount is unnecessary in calculating newVestingAmount
since getUnvestedAmount is equal 0.
if (getUnvestedAmount() > 0) revert StillVesting(); uint256 newVestingAmount = amount + getUnvestedAmount();
#0 - c4-pre-sort
2023-11-02T03:23:10Z
raymondfam marked the issue as sufficient quality report
#1 - c4-judge
2023-11-14T16:49:45Z
fatherGoose1 marked the issue as grade-b