# ERC721Votes's delegation disables NFT transfers and burning

ERC721Votes _afterTokenTransfer moves votes from owner instead of delegate, so after delegating away, vote balance is zero and any transfer or burn reverts, freezing NFTs.

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

**Target:** ERC721Votes governance token contract (Solidity)
**Root cause:** _afterTokenTransfer calls _moveDelegateVotes(_from, _to, 1) using the token owner rather than the owner's delegate; once votes are delegated away the owner's checkpoint is zero, so the subtraction underflows and reverts.
**Steps:**
1. Alice mints NFT; _afterTokenTransfer increments Alice's vote checkpoint to balanceOf(Alice).
2. Alice calls delegate(Alice) then delegate(bob) (or delegateBySig); _delegate moves balanceOf(Alice) votes from Alice to bob, setting checkpoints[Alice] to 0.
3. Alice calls transferFrom/safeTransferFrom or burn on her tokenId.
4. _afterTokenTransfer invokes _moveDelegateVotes(Alice, recipient, 1); prevTotalVotes=0, prevTotalVotes - 1 underflows in unchecked block, reverting the transfer.
**How found:** Code review of the delegation/checkpoint accounting: noticed _afterTokenTransfer passes the owner address rather than delegates(_from), and traced that after delegation the owner's checkpoint is zero, making the 1-vote subtraction revert.
**Exploitation:** Self-inflicted DoS: any owner who delegates their votes permanently locks all their tokenIds from transfer or burn, disrupting governance participation and secondary market movement.
**Insight:** When a token contract tracks per-account vote checkpoints, verify every _moveDelegateVotes/_afterTokenTransfer call site uses delegates(account) not the raw account; unchecked subtraction of a fixed 1 vote from a possibly-zero checkpoint is the tell.
