Back to Lessons
Lesson 13

ERC20Votes: On-Chain Governance Power

+25EFFORT
5 sections
balanceOf()
01
text

Governance tokens must answer a tricky question: how many votes did an address have at a PAST point in time? Reading the current balance is exploitable — someone could borrow tokens via a flash loan, vote, and return them. OpenZeppelin's ERC20Votes solves this with checkpointed historical balances and a delegation system.

02
code
solidity
1// Voting power must be DELEGATED to activate (even to yourself):
2function delegate(address delegatee) external;
3
4// Read current and historical voting power:
5function getVotes(address account) external view returns (uint256);
6function getPastVotes(address account, uint256 timepoint)
7 external view returns (uint256);
8
9// Internally, each balance/delegation change appends a checkpoint:
10// struct Checkpoint { uint48 fromBlock; uint208 votes; }
03
text

Two ideas make this work. First, DELEGATION: holding tokens gives you zero votes until you delegate them (to yourself or someone else), which separates economic ownership from voting power. Second, CHECKPOINTS: every transfer or delegation writes a (block, votes) checkpoint, so getPastVotes() can look up power at a proposal's start block — immune to last-minute or flash-loan manipulation.

04
note
Key Insight

Costs and gotchas: writing checkpoints adds gas to every transfer and delegation. And because power must be delegated to count, a huge fraction of governance tokens often sit with zero active voting power simply because holders never delegated. Many DAOs nudge users to 'activate' their votes for this reason.

05
code
javascript
1// Activate your own voting power, then read it
2await token.write.delegate([myAddress]);
3const power = await token.read.getVotes([myAddress]);
4// A Governor contract later calls getPastVotes(voter, proposalSnapshot)
Complete

Connect your wallet to mark this lesson as complete

Earn 25 EFFORT tokens