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: 140/147
Findings: 1
Award: $4.52
🌟 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
For users to be able withdraw their staked tokens, they will have to wait for the cooldown
period to end then be able to get their funds. Now, the cooldownAssets ()
and the cooldownShares ()
functions are used to start the cooldown
period https://github.com/code-423n4/2023-10-ethena/blob/ee67d9b542642c9757a6b826c82d0cae60256509/contracts/StakedUSDeV2.sol#L95-L124.
Therefore, when a user calls the cooldownShares ()
or/and cooldownAssets ()
, their respective shares or assets is converted then the cooldownEnd
for the user is set as cooldowns[owner].cooldownEnd = uint104(block.timestamp) + cooldownDuration;
After this period has expired, the user can now call unstake ()
function which would effectively call the silo
withdraw
function and tokens would be sent to the user.
Now cooldownEnd
is updated when every time the two functions cooldownShares
and coolDownAssets
is called meaning if these functions are called more than once the cooldownEnd
period would be updated more than once. Consider the following scenario:
cooldownDuration
is set to 20 days and the cooldown
period is on.coolDownAssets
wanting to withdraw 400 Tokens.coolDownAssets
updates here cooldownEnd
as follows cooldowns[owner].cooldownEnd = uint104(block.timestamp) + cooldownDuration;
meaning she can now unstake
her tokens on day 23.coolDownAssets
function again which effectively updates her cooldownEnd
as follows cooldowns[owner].cooldownEnd = uint104(block.timestamp) + cooldownDuration;
subsequently setting here cooldownEnd
period to day 30.cooldown
period and calls unstake ()
function. This call would fail as the coolDownEnd
period was last updated on her last call to coolDownAssets
pushing forward her coolDownEnd
.With this it is clear that earlier user's cooldowns
is overwritten by subsequent calls to coolDownAssets
or/and coolDownShares
leading to temporary freezing of earlier withdrawn amounts as the contract only uses the last updated cooldown
period.
The following PoC, was created and added in the StakedUSDeV2.cooldownEnabled.t.sol
file.
The forge command run as forge test --match-test testTemporaryFreezeonRedeem -vvv
to print to console and specifically that test.
function testTemporaryFreezeonRedeem() public { uint256 balBefore = usdeToken.balanceOf(alice); uint256 amount = 100 ether; _mintApproveDeposit(alice, amount); vm.startPrank(alice); uint256 halfShare = amount /2 ; stakedUSDe.cooldownShares(halfShare, alice); (uint104 cooldownEnd, uint256 usdeAmount) = stakedUSDe.cooldowns(alice); console.log("The cooldownEnd Period is [+]", cooldownEnd); vm.warp(50); // Another cooldownShares call to push the cooldownEnd further stakedUSDe.cooldownShares(halfShare, alice); (uint104 newcooldownEnd, uint256 newusdeAmount) = stakedUSDe.cooldowns(alice); // console to see the updated cooldown End period console.log("The cooldownEnd Period is [+]", newcooldownEnd); console.log("The old cooldownEnd is [--] %s and The new cooldownEnd is [++] %s", cooldownEnd, newcooldownEnd); vm.warp(cooldownEnd + 1); vm.expectRevert(); stakedUSDe.unstake(alice); }
Other
#0 - c4-pre-sort
2023-10-30T23:25:58Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-10-30T23:26:11Z
raymondfam marked the issue as duplicate of #4
#2 - c4-pre-sort
2023-11-01T19:36:17Z
raymondfam marked the issue as duplicate of #514
#3 - c4-judge
2023-11-10T21:27:01Z
fatherGoose1 marked the issue as unsatisfactory: Invalid
#4 - c4-judge
2023-11-17T17:04:09Z
fatherGoose1 changed the severity to QA (Quality Assurance)
#5 - c4-judge
2023-11-20T20:18:36Z
fatherGoose1 marked the issue as grade-b