# Calling StandardFunding.screeningVote function and ExtraordinaryFunding.voteExtraordinary function when block.number equals respective start block and when block.number is bigger than respective start block can result in different available votes for same voter

Screening/extraordinary vote snapshot uses block.number-1 when voting at startBlock, letting a voter pick the block with higher voting power.

- Payout: Contest reward
- Severity: medium
- Program: Ajna Protocol
- Credited: rbserver
- Disclosed: 2023-06-29
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2023-05-ajna-findings/issues/288
- Library page: https://bugbountylibrary.com/r/c4%3A2023-05-ajna%3AM-07
- Tags: domain:web3 kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:race-window technique:source-review vuln:business-logic vuln:governance

**Target:** StandardFunding.screeningVote / ExtraordinaryFunding.voteExtraordinary snapshot logic
**Root cause:** _getVotesAtSnapshotBlocks substitutes voteStartBlock_ with block.number-1 when voteStartBlock_ == block.number, so voting exactly at startBlock reads a different snapshot than voting one block later, yielding different available votes.
**Steps:**
1. Observe screeningVote allows block.number == currentDistribution.startBlock while fundingVote requires block.number > screeningStageEndBlock.
2. Note _getVotesScreening passes startBlock as voteStartBlock_; _getVotesAtSnapshotBlocks rewrites it to block.number-1 only when equal.
3. At startBlock+32 transfer all AJNA away so balance is 0 at startBlock-1.
4. At startBlock+33 (startBlock) transfer tokens back; voting power at startBlock reads 0.
5. At startBlock+34/35 voting power reads 25_000_000e18, so voter waits and casts screeningVote with the higher snapshot.
6. Same applies to voteExtraordinary at proposal.startBlock.
**How found:** Code reading of the boundary conditions in screeningVote/voteExtraordinary versus fundingVote, spotting the ternary in _getVotesAtSnapshotBlocks that swaps voteStartBlock_ for block.number-1 only at equality.
**Exploitation:** Voter backruns startNewDistributionPeriod, mints/receives AJNA at startBlock, then calls screeningVote at a later block in the screening phase to use votes counted at startBlock rather than the startBlock-1 snapshot, or votes at startBlock using the earlier snapshot then dumps tokens.
**Insight:** When a snapshot helper special-cases voteStartBlock_ == block.number, check every caller's boundary check: any entrypoint permitting block.number == startBlock while others require strictly greater creates a selectable snapshot window.
