# getVotingPower Is Not Equipped To Handle On-Chain Voting

sNOTE.getVotingPower computes live voting power without block snapshots, letting a staker vote then transfer sNOTE to another account and vote again.

- Payout: Audit contest
- Severity: medium
- Program: Notional
- Found by: leastwood
- Disclosed: 2022-03-10
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2022-01-notional-findings/issues/165
- Library page: https://bugbountylibrary.com/r/c4%3A2022-01-notional%3AM-06
- Tags: domain:web3 impact:integrity kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:governance

**Target:** sNOTE governance voting power function (Solidity contract)
**Root cause:** getVotingPower derives voting power from current totalSupply and live oracle prices rather than a checkpointed balance at a specific block, so power is not fixed at vote time.
**Steps:**
1. Stake NOTE for sNOTE and call the governance vote using getVotingPower-derived weight.
2. Transfer sNOTE to a second account after voting.
3. Second account calls getVotingPower and votes again with the same underlying NOTE.
4. Repeat to accumulate voting weight beyond actual stake.
**How found:** Code review of getVotingPower noticed it reads live totalSupply/oracle prices with no blockNumber checkpoint, and reasoned that on-chain voting needs a per-block snapshot to prevent re-voting after transfers.
**Exploitation:** A staker casts an on-chain vote, then moves sNOTE to another address which recomputes voting power from current balances and votes again, multiplying effective governance weight without additional capital.
**Insight:** When auditing governance tokens, check whether voting power is derived from live balances/supply instead of a checkpointed snapshot at a fixed block; any transferable voting token without checkpoints allows vote-then-transfer double voting.
