← Protocols
ERC-5805 — Voting with Delegation
Standard / EIP·EVM

ERC-5805 — Voting with Delegation

01Description

Stagnant ERC formalizing the delegated-voting interface popularized by Compound's `Comp` token and OpenZeppelin's `ERC20Votes` / `ERC721Votes`. Defines `delegate`, `delegateBySig`, `getVotes`, and historical `getPastVotes` checkpoints used by every Governor-style DAO. Status: Stagnant (despite stagnation, the OpenZeppelin Votes implementation is the de facto standard in production).

02Best for
  • 01DAO voting tokens
  • 02Governor Bravo / OZ Governor compatibility
  • 03checkpointed historical voting power
  • 04gasless delegation via EIP-712
03Install
  • pnpm add @openzeppelin/contracts
  • # OZ ERC20Votes / ERC721Votes / Votes are the canonical implementations
05Prompt snippet
Inherit OpenZeppelin's `ERC20Votes` (or `ERC721Votes`, or the abstract `Votes` mixin for custom assets) which implements ERC-5805. Required surface: `delegate(address delegatee)`, `delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s)`, `delegates(address account) returns (address)`, `getVotes(address account) returns (uint256)`, `getPastVotes(address account, uint256 timepoint) returns (uint256)`, `getPastTotalSupply(uint256 timepoint) returns (uint256)`, and `clock()` + `CLOCK_MODE()` to declare whether checkpoints use `block.number` or `block.timestamp` (ERC-6372). Emit `DelegateChanged` and `DelegateVotesChanged`. Voting power transfers automatically on `_update`/`_afterTokenTransfer`. Pair with OZ `Governor` for proposal/quorum/timelock logic.
06Gotchas
  • Holders MUST call `delegate(self)` (or `delegate(otherAddr)`) at least once to activate voting power — fresh transfers to a never-delegated wallet have ZERO votes. Surface this in your dApp UI with a one-click self-delegate button or DAOs lose quorum.
  • Checkpoint storage costs ~20k gas on first write per block per holder — a high-frequency token (DEX LP, rebasing) will explode in gas. Use ERC-20Wrapper or a dedicated voting wrapper instead of voting directly on the underlying.
  • ERC-6372 `clock()` MODE matters: legacy tokens use `block.number`, newer ones use `block.timestamp`. Mismatched clock modes between token and Governor cause `getPastVotes` to revert or return wrong values mid-proposal — pin both to the same mode.
  • `delegateBySig` nonces are SEPARATE from ERC-2612 permit nonces in some implementations — using the wrong nonce silently invalidates the signature.
  • Flash-loan vote-buying: an attacker can borrow tokens, delegate, vote, and repay in a single block. Mitigate via `Governor.proposalThreshold` snapshots taken at proposal-creation timepoint, NOT at vote-cast time, AND set `votingDelay > 0` blocks.
07Alternatives