# `Staking.unstake()` doesn't decrease the original voting power that was used in `Staking.stake()`.

Staking.unstake recomputes voting power from mutable admin settings instead of the value stored at stake time, causing underflow and locking NFTs.

- Payout: Contest reward
- Severity: high
- Program: Frankendao
- Credited: hansfriese
- Disclosed: 2022-10-31
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2022-11-frankendao-judging/issues/70
- Library page: https://bugbountylibrary.com/r/sherlock%3A2022-11-frankendao%3AH-2
- Tags: domain:web3 impact:dos kind:contest platform:sherlock sev:high tech:solidity technique:source-review vuln:accounting-error vuln:dos

**Target:** Staking.sol staking/unstaking voting power accounting
**Root cause:** getTokenVotingPower() derives power from mutable globals (baseVotes, monsterMultiplier, stakedTimeBonus, evilBonus) at both stake and unstake time, so the unstake value can differ from the stored stake value.
**Steps:**
1. Admin sets baseVotes=20, monsterMultiplier=50; user stakes FrankenMonster, votesFromOwnedTokens += 20*50/100 = 10.
2. Admin changes monsterMultiplier=60.
3. User calls unstake(); getTokenVotingPower returns 20*60/100 = 12.
4. votesFromOwnedTokens -= 12 underflows (only 10 recorded), reverting; NFT cannot be unstaked.
**How found:** Code reading of _stake/_unstake: noticed both independently call getTokenVotingPower() which reads admin-mutable parameters, hypothesizing the delta would not net to zero after a settings change.
**Insight:** When stake and unstake both recompute a value from mutable config, check that the recomputation is idempotent; store the credited amount at stake time and decrement that exact value on unstake.
