# Users staking via the SurplusGuildMinter can be immediately slashed when staking into a gauge that had previously incurred a loss

SurplusGuildMinter.getRewards compares lastGaugeLoss against a freshly zeroed memory struct, so any gauge with historical loss marks new stakers as slashed, forfeiting their Credit.

- Payout: Contest reward
- Severity: high
- Program: Ethereum Credit Guild
- Credited: JCN
- Disclosed: 2024-02-22
- Write-up quality: 8/10
- Original: https://github.com/code-423n4/2023-12-ethereumcreditguild-findings/issues/473
- Library page: https://bugbountylibrary.com/r/c4%3A2023-12-ethereumcreditguild%3AH-04
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:business-logic vuln:reward-distribution

**Target:** SurplusGuildMinter staking contract (Solidity)
**Root cause:** getRewards reads lastGaugeLoss before loading the stored userStake, so it compares against a zero-initialized memory struct; any gauge with a nonzero historical lastGaugeLoss sets slashed=true for every new staker.
**Steps:**
1. Gauge incurs loss via `profitManager.notifyPnL(term, -50e18)`, setting `lastGaugeLoss[term]`.
2. Gauge is off-boarded (`guild.removeGauge(term)`) then re-onboarded later (`guild.addGauge(1, term)`).
3. User stakes via `sgm.stake(term, 10e18)`; `getRewards` returns `lastGaugeLoss` from storage but `userStake` still zeroed, so `slashed=true` is computed but stake is initialized with `lastGaugeLoss` set.
4. Anyone calls `sgm.getRewards(user, term)`; `lastGaugeLoss > userStake.lastGaugeLoss` check uses zeroed struct, slashed=true, stake wiped.
5. User calls `sgm.unstake(term, 10e18)`; `getRewards` again returns slashed=true and unstake returns early, Credit not refunded.
**How found:** Code-reading: noticed getRewards checks `lastGaugeLoss > userStake.lastGaugeLoss` before assigning `userStake = _stakes[user][term]`, meaning the comparison always uses a zeroed memory struct; contrasted with GuildToken.incrementGauge which correctly initializes lastGaugeLossApplied to block.timestamp.
**Exploitation:** Attacker monitors for a user staking via SurplusGuildMinter into a gauge that ever had a loss, then calls the permissionless `getRewards(user, term)` to zero the victim's stake; victim's staked Credit is permanently locked and guild rewards forfeited.
**Insight:** When auditing state-tracking structs, check the ordering of storage reads vs. comparisons against freshly declared memory structs — a zero-initialized struct silently makes any nonzero historical value trigger the 'slashed/lost' branch.
