Governance tokens
Token Voting Plugin draws voting power from an IVotes token, a token with the checkpointed, delegatable balances OpenZeppelin's ERC20Votes provides. Choosing that token is the main decision when you install the plugin, and TokenVotingSetup handles three paths automatically based on the token settings you pass:
- Mint a new token. Pass no token address and the setup deploys a fresh
GovernanceERC20and mints an initial supply to the receivers you specify. The DAO gets mint rights. Best for a new DAO issuing its own governance token. - Reuse an existing
IVotestoken. Pass a token that already speaksIVotesand it's used as-is, no new deployment. Best when your community already has a governance token. - Wrap a plain ERC-20. Pass a token that isn't
IVotesand the setup deploys aGovernanceWrappedERC20around it. Holders lock the original token into the wrapper to receive voting power. The fallback for governing with a token that was never built for it.
(The setup duck-types these: it probes for IVotes functions rather than trusting an ERC-165 flag, since many IVotes tokens don't advertise support formally.)
GovernanceERC20: a mintable governance token
A full ERC20Votes + ERC20Permit token, DAO-managed:
- Minting is DAO-gated.
mintis behindMINT_PERMISSION_ID, granted to the DAO at install, so new supply is issued through governance, not by an EOA. - Supply can be frozen.
freezeMintingis a one-way switch a DAO can flip to credibly commit to a fixed supply, visible right on the token.
GovernanceWrappedERC20: retrofitting votes onto an existing token
Wraps an existing ERC-20 to add IVotes. It has no mint of its own, supply is entirely a function of how much of the underlying token has been deposited:
depositFor(account, amount)locks the underlying token and mints an equal amount of wrapped, voting-enabled token.withdrawTo(account, amount)reverses it 1:1.
This works, but it adds real friction: holders must actively wrap (lock) their tokens to participate, so turnout suffers. Prefer a native GovernanceERC20 for a new token; reach for wrapping only when you're committed to an existing non-governance ERC-20.
Delegation
Voting power is delegated, not merely held, the rule that catches every token-voting DAO out. The two tokens differ in when that first delegation happens, and whether it's automatic:
GovernanceERC20delegates only on mint, never on a plain transfer, and only when itsensureDelegationOnMintoption is set (it then self-delegates a recipient who has no delegate yet). Leave the option off and freshly-minted holders have balance but no power until they manuallydelegate(), the classic onboarding foot-gun.GovernanceWrappedERC20delegates on receive: it auto-self-delegates whenever a receiver has no delegate yet (no flag to disable it, and it won't override an existing delegation), so wrapping grants power without a separate step (it compensates for the friction of wrapping).
The zkSync variant of the setup (
TokenVotingSetupZkSync) differs only in how it deploys these token contracts (fresh deployment instead of minimal-proxy clones, for zkEVM compatibility); the governance behavior is identical.
Keep in mind
- Delegate, or holders have no power. Unless
ensureDelegationOnMintis on (or you use the wrapper), a token holder must delegate before they can vote, expect to guide users through it. - Wrapping means locking. Voting with
GovernanceWrappedERC20requires depositing the underlying token; that friction depresses turnout. Don't wrap a token you could instead issue natively.
See also
- Token Voting Plugin — how the plugin reads voting power from the token.
- Plugin setup — the setup mechanism that deploys and wires all this.