# AbstractRewardMine - Re-entrancy attack during withdrawal

AbstractRewardMine._withdraw transfers reward tokens before updating _userWithdrawn accounting, letting a hook token re-enter and drain the pool.

- Payout: Audit contest
- Severity: medium
- Program: Malt Finance
- Found by: ScopeLift
- Disclosed: 2022-02-14
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2021-11-malt-findings/issues/333
- Library page: https://bugbountylibrary.com/r/c4%3A2021-11-malt%3AM-06
- Tags: area:web3 domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:solidity technique:source-review vuln:accounting-error vuln:reentrancy

**Target:** AbstractRewardMine reward staking contract (_withdraw/withdraw)
**Root cause:** _withdraw performs rewardToken.safeTransfer before updating _userWithdrawn and _globalWithdrawn, violating checks-effects-interactions; earned() reads stale _userWithdrawn during the transfer hook.
**Steps:**
1. Deploy/point rewardToken to an ERC777/ERC677 hook token.
2. Stake and accrue rewards so earned(account) > 0.
3. Call `withdraw()`; during safeTransfer the token hook re-enters `withdraw()`/`_withdraw`.
4. Because `_userWithdrawn[account]` is not yet incremented, earned() still reports full balance; repeat re-entry to drain.
**How found:** Code review of _withdraw ordering: noticed state updates occur after the external safeTransfer call, and that earned() depends on the not-yet-updated _userWithdrawn mapping.
**Exploitation:** Attacker uses a hook-capable reward token; on each safeTransfer callback re-enters withdraw before accounting updates, extracting rewards repeatedly until the contract balance is drained.
**Insight:** In staking/reward contracts, check whether earned()/pending-reward views read mappings updated only after the payout transfer; a hook token re-entering the public withdraw re-reads stale accounting.
