Platform: Code4rena
Start Date: 11/12/2023
Pot Size: $90,500 USDC
Total HM: 29
Participants: 127
Period: 17 days
Judge: TrungOre
Total Solo HM: 4
Id: 310
League: ETH
Rank: 127/127
Findings: 1
Award: $3.05
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: JCN
Also found by: 0xadrii, 0xaltego, 0xdice91, 0xivas, 0xpiken, Akali, AlexCzm, Chinmay, DanielArmstrong, HighDuty, Infect3d, Inference, KupiaSec, PENGUN, SECURITISE, Stormreckson, SweetDream, TheSchnilch, Timeless, Varun_05, XDZIBECX, alexzoid, asui, beber89, btk, carrotsmuggler, cats, cccz, developerjordy, ether_sky, grearlake, imare, jasonxiale, kaden, klau5, santipu_, serial-coder, sl1, smiling_heretic, stackachu, wangxx2026, whitehat-boys
3.0466 USDC - $3.05
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/loan/SurplusGuildMinter.sol#L216-L290 https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/loan/SurplusGuildMinter.sol#L228-L231 https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/loan/SurplusGuildMinter.sol#L274-L284
In SurplusGuildMinter.sol
getRewards()
is used to get rewards from a staking position, the variable userStake
is first declared to reserve memory to hold a UserStake
struct when the function is called, but the variable has not been initialised yet.
The variable is only initialised further down in the function
userStake = _stakes[user][term];
The issue here is that the variable userStake
is used to determine if the user has been slashed or not before it has been initialised
leading to its components being in their default values
lastGaugeLoss = GuildToken(guild).lastGaugeLoss(term); if (lastGaugeLoss > uint256(userStake.lastGaugeLoss)) { slashed = true; }
As userStake.lastGaugeLoss
still has its default value of 0, if the term
has ever suffered a loss in the past even before the user staked, the check will always return true (that is slashed will be set to true).
As a result of this error, Users guildReward
and their staked first loss capital (credit token)
will be unfairly slashed and lost forever whenever getRewards()
is called.
if (slashed) { guildReward = 0; }
if (slashed) { emit Unstake(block.timestamp, term, uint256(userStake.credit)); userStake = UserStake({ stakeTime: uint48(0), lastGaugeLoss: uint48(0), profitIndex: uint160(0), credit: uint128(0), guild: uint128(0) }); updateState = true; }
Manual Review
The UserStake memory userStake
variable should simply be initialised
first with the user values before the check for if the user has been slashed is done.
function getRewards( address user, address term ) public returns ( uint256 lastGaugeLoss, // GuildToken.lastGaugeLoss(term) UserStake memory userStake, // stake state after execution of getRewards() bool slashed // true if the user has been slashed ) { bool updateState; userStake = _stakes[user][term]; // The initialisation should be moved there before the check for if slashed lastGaugeLoss = GuildToken(guild).lastGaugeLoss(term); if (lastGaugeLoss > uint256(userStake.lastGaugeLoss)) { slashed = true; } // if the user is not staking, do nothing if (userStake.stakeTime == 0) return (lastGaugeLoss, userStake, slashed); //More code }
Invalid Validation
#0 - c4-pre-sort
2023-12-29T14:38:12Z
0xSorryNotSorry marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-12-29T14:38:18Z
0xSorryNotSorry marked the issue as duplicate of #1164
#2 - c4-judge
2024-01-28T20:13:37Z
Trumpero marked the issue as satisfactory