# _transferFrom() can be used to indefinitely increase voting power

Nouns Builder ERC721Votes moves votes from token sender instead of their delegate on transfer, letting a user delegate then transfer to inflate voting power indefinitely.

- Payout: Contest reward
- Severity: high
- Program: Nouns Builder
- Credited: Soosh
- Disclosed: 2022-11-03
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2022-09-nouns-builder-findings/issues/224
- Library page: https://bugbountylibrary.com/r/c4%3A2022-09-nouns-builder%3AH-05
- Tags: domain:web3 impact:integrity kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:governance

**Target:** ERC721Votes governance token contract
**Root cause:** _transferFrom calls _moveDelegateVotes(_from, _to, 1) using token owner addresses rather than their delegates, so votes are credited to the recipient while the sender's delegated votes remain counted.
**Steps:**
1. Deploy mock; voter1 acquires 1 NFT via auction bid and settle
2. voter1 calls `token.delegate(voter2)` — voter1 still has 1 vote, voter2 gains 1
3. voter1 calls `token.transferFrom(voter1, voter2, 2)` — voter1 drops to 0, voter2 rises to 2
4. voter2 calls `token.delegate(voter2)` — voter2 now has 3 votes
5. Repeat transfer/delegate cycle to grow voting power indefinitely
**How found:** Code review of ERC721Votes transfer logic against OpenZeppelin's ERC721Votes reference, noticing votes are moved between owner addresses instead of delegates; confirmed with a Foundry PoC.
**Exploitation:** Attacker with one NFT delegates to a fresh address, transfers the NFT to that address (votes credited to recipient without debiting the delegate), then re-delegates to self, accumulating votes each cycle for governance dominance.
**Escalation:** Repeat the delegate/transfer loop across many addresses to reach unbounded voting power in governance.
**Insight:** When auditing vote-escrow/delegation tokens, check that every balance-moving path (transfer, mint, burn, hook) moves votes between the *delegates* of the parties, not the parties themselves; a single mismatched argument in _moveDelegateVotes lets votes be duplicated.
