# Safe can be bricked because threshold is updated with validSignerCount instead of newThreshold

HatsSignerGate passes validSignerCount instead of newThreshold to Safe.changeThreshold, letting threshold exceed max and permanently bricking the Safe.

- Payout: Contest reward
- Severity: high
- Program: Hats
- Credited: Allarious
- Disclosed: 2023-02-15
- Write-up quality: 7/10
- Original: https://github.com/sherlock-audit/2023-02-hats-judging/issues/37
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-02-hats%3AH-8
- Tags: domain:web3 impact:dos kind:contest platform:sherlock sev:high tech:solidity technique:source-review vuln:business-logic vuln:dos

**Target:** HatsSignerGateBase.sol reconcileSignerCount / Safe threshold module
**Root cause:** reconcileSignerCount computes newThreshold as min(validSignerCount, targetThreshold) but calls changeThreshold with validSignerCount, so when valid signers exceed target the Safe threshold is set above the intended max.
**Steps:**
1. Deploy HatsSignerGate with targetThreshold T and maxSigners M
2. Ensure validSignerCount V > T (e.g. many eligible hat wearers)
3. Trigger reconcileSignerCount(); it calls `changeThreshold(validSignerCount)` instead of `changeThreshold(newThreshold)`
4. Safe threshold becomes V > T
5. checkAfterExecution() rejects any tx because threshold exceeds max, all safe transactions revert
**How found:** Code review of reconcileSignerCount spotted the mismatch between the computed newThreshold variable and the argument passed to changeThreshold.
**Escalation:** If hat eligibility cannot be revoked and V > maxSigners, targetThreshold cannot be raised enough to unblock, permanently bricking the Safe.
**Insight:** When a contract computes a clamped value into a new variable, verify every downstream call uses the clamped variable, not the raw input; threshold/limit setters are prime spots for this copy-paste bug.
