# Incorrect voting power calculation when create_lock and increase_amount are called in the same transaction

VotingEscrow._checkpoint appends a new epoch when create_lock and increase_amount share a timestamp, so balanceOfNFT ignores the added amount and voting power/rewards are understated.

- Payout: Contest reward
- Severity: medium
- Program: Hybra Finance
- Credited: dreamcoder
- Disclosed: 2025-11-20
- Write-up quality: 6/10
- Original: https://code4rena.com/reports/2025-10-hybra-finance#m-08
- Library page: https://bugbountylibrary.com/r/c4%3A2025-10-hybra-finance%3AM-08
- Tags: domain:web3 kind:contest platform:code4rena sev:medium tech:solidity technique:chained technique:source-review vuln:accounting-error vuln:business-logic

**Target:** VotingEscrow.sol _checkpoint / balanceOfNFT
**Root cause:** _checkpoint unconditionally increments user_point_epoch and writes a new history point even when the new lock has the same block.timestamp as the previous point; balanceOfNFT reads only the latest epoch, so the second lock's amount is not reflected.
**Steps:**
1. Call `votingEscrow.create_lock(lockAmount/2, lockEnd)` and `votingEscrow.increase_amount(tokenId, lockAmount/2)` in the same transaction (same block.timestamp).
2. Observe `_checkpoint` writes two epochs both with ts=block.timestamp; balanceOfNFT uses only the last epoch's amount.
3. Compare with a user who locks the full amount in one create_lock call.
4. Vote with both tokenIds and claim bribes; the split-lock user receives less reward despite equal locked balance.
**How found:** Code reading of _checkpoint epoch bookkeeping: noticed epoch increments regardless of identical timestamps, then hypothesized balanceOfNFT would ignore the second lock and confirmed via Foundry PoC comparing split vs single lock rewards.
**Exploitation:** Attacker/user calls create_lock then increase_amount in one tx; voting power is computed from the stale/partial amount, understating weight and reducing bribe/reward share relative to equal-balance single-lock users.
**Insight:** In ve-style checkpointing, always check whether same-block/same-timestamp writes create duplicate epochs; compare multi-call vs single-call paths for identical end state to expose accounting divergence.
