# getVotes inside the BlackGovernor incorrectly provides block.number instead of block.timestamp, leading to complete DOS of proposal functionality

BlackGovernor.propose passes block.number to getVotes which expects a timestamp, so the threshold check reads pre-launch voting power and always reverts, blocking all proposals.

- Payout: Contest reward
- Severity: medium
- Program: Blackhole
- Credited: hakunamatata
- Disclosed: 2025-07-03
- Write-up quality: 6/10
- Original: https://code4rena.com/reports/2025-05-blackhole#m-16
- Library page: https://bugbountylibrary.com/r/c4%3A2025-05-blackhole%3AM-16
- Tags: domain:web3 impact:dos kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:dos vuln:governance

**Target:** BlackGovernor governance contract (Solidity/EVM)
**Root cause:** _proposal calls getVotes(_msgSender(), block.number - 1) but getVotes/getsmNFTPastVotes expect a timestamp; block.number is far smaller than block.timestamp so the checkpoint lookup resolves to a pre-launch time with zero voting power.
**Steps:**
1. Deploy Blackhole stack per PoC setUp (Black, VotingEscrow, Minter, BlackGovernor).
2. As userA, approve and create_lock(1_000_000e18, 4*365 days) to exceed proposalThreshold.
3. skip(3600) to advance time.
4. Call `blackGovernor.propose(targets, values, calldatas, "Nudge proposal")` with minter.nudge calldata.
5. Observe revert `Governor: proposer votes below proposal threshold`; replacing block.number with block.timestamp makes it succeed.
**How found:** Code review of BlackGovernor._proposal noticed the copied ThenaFi check passing block.number-1 into getVotes, then traced into getsmNFTPastVotes to confirm it interprets the argument as a timestamp checkpoint.
**Exploitation:** No attacker action needed: any legitimate proposer meeting the threshold is blocked because the timestamp argument equals block.number-1, which precedes deployment, so getVotes returns 0 and propose reverts, fully disabling governance proposal creation.
**Insight:** When governance code is forked, verify the units of every time argument passed into checkpoint-based vote lookups (block.number vs block.timestamp); a mismatched unit silently resolves to a pre-deployment checkpoint and zeroes voting power.
