Free Voting in DAOs: Removing Gas Fees So Anyone Can Participate

🎯 Democratic DAOs Shouldn't Require Crypto to Vote

The paradox: A DAO claims to be decentralized and democratic, but only members who hold ETH for gas fees can actually participate in governance. What good is decentralization if 80% of token holders can't vote because they don't know how to buy Ethereum?


DAO Web3 Screenshot

Technical subtitle: EIP-712 meta-transactions with ERC-2771 and Relayer pattern for gasless governance


📊 The Participation Problem That Every DAO Faces

In traditional DAOs, governance participation suffers from a fundamental barrier: gas fees.

Barrier Impact
Need to buy ETH Non-crypto users can't participate at all
Gas volatility During busy periods, voting can cost $50-$200
Wallet complexity Connecting MetaMask and approving transactions confuses users
Transaction fatigue Every vote requires a separate on-chain transaction

The result? Most DAOs have 2-5% participation rates. The vast majority of token holders are silent because the friction is too high.

flowchart TD
    A[DAO Token Holder] --> B{Has ETH for gas?}
    B -- No --> C[Cannot Vote]
    B -- Yes --> D{Wants to buy ETH?}
    D -- No --> C
    D -- Yes --> E[Can Vote - But High Friction]
    
    style C fill:#ffcccc,stroke:#ff0000
    style E fill:#fff3e0,stroke:#ffa000

This isn't just a UX problem — it's a fundamental flaw in DAO design. True governance requires participation, and participation requires accessibility.


💡 The Solution: Web2 UX with Web3 Security

The key insight: separate who signs the vote from who pays the blockchain fee.

sequenceDiagram
    participant U as 👤 User (No ETH needed)
    participant W as 💼 Wallet
    participant R as ⚡ Relayer API
    participant C as 📋 DAO Contract
    
    U->>W: Sign vote EIP-712 (free, no gas)
    W-->>U: Returns Signature
    U->>R: POST /api/relay {signature}
    R->>C: executeMetaTx(signature)
    C-->>R: Vote registered on-chain
    R-->>U: 200 OK {txHash}
    
    note over U,C: The user never pays gas. The Relayer does.

How It Works — In Plain Language

  1. User signs their vote — just like clicking "Like" on social media, no crypto needed
  2. A server relays the vote — pays the gas fee on behalf of the user
  3. Vote is recorded on-chain — immutable and verifiable by anyone

The user gets Web2 simplicity (click and vote). The blockchain gets Web3 security (immutable, verifiable records).


Implementation: The Technical Architecture

The Meta-Transaction Stack

Technology Purpose Why It Matters
EIP-712 Typed structured data signatures Users see what they're signing in readable format
ERC-2771 Standard for meta-transaction contracts Extracts the real msg.sender from calldata
Relayer Next.js API route that pays gas Bridges user intent to on-chain execution

Smart Contracts (Solidity ^0.8.24)

The system has three main contracts working together:

graph TB
    subgraph Smart Contracts
        GT[GovernanceToken<br/>ERC-20 + Delegation]
        GD[GovernorDAO<br/>Proposals & Votes]
        FW[Forwarder<br/>ERC-2771]
    end
    
    subgraph Frontend
        N[Next.js 15<br/>Wagmi + Viem]
    end
    
    subgraph Backend
        R[Relayer API<br/>Meta-transactions]
    end
    
    N -->|Read| GT
    N -->|Read| GD
    N -->|EIP-712 Sign| R
    R -->|executeMetaTx| FW
    FW -->|Verifies| GD
    GD -->|Checks balance| GT
    
    style GT fill:#1a1a2e,stroke:#00f2ff,stroke-width:2px,color:#fff
    style GD fill:#1a1a2e,stroke:#00ff88,stroke-width:2px,color:#fff
    style FW fill:#1a1a2e,stroke:#ff00f2,stroke-width:2px,color:#fff
  1. GovernanceToken.sol — ERC-20 with vote delegation. Holders can delegate voting power to others.
  2. GovernorDAO.sol — Proposal creation, voting lifecycle, and execution.
  3. Forwarder.sol — ERC-2771 implementation that verifies and forwards meta-transactions.

Proposal Lifecycle

stateDiagram-v2
    [*] --> Pending: Creation (≥10% tokens)
    Pending --> Active: Voting starts
    Active --> Succeeded: Majority for
    Active --> Defeated: Majority against
    Succeeded --> Executed: On-chain execution
    Defeated --> [*]
    Executed --> [*]

Proposal requirements: Only accounts with ≥ 10% of total tokens can create proposals. This prevents governance spam.

Frontend (Next.js 15 + TypeScript)

The frontend in packages/web/src/ uses:

  • Wagmi + Viem: Type-safe EVM interaction hooks
  • TanStack Query: Blockchain state cache with auto-revalidation
  • Tailwind CSS + Headless UI: Accessible, responsive interface
  • Real-time dashboard: Live proposal status and vote counts

🧠 Deep Dive: The ERC-2771 Magic

Extracting the Real Sender

The most complex part: the contract must know who actually voted, not who paid the gas.

// The contract extracts the real sender from calldata function _msgSender() internal view override returns (address sender) { if (isTrustedForwarder(msg.sender)) { // Real sender is in the last 20 bytes of calldata assembly { sender := shr(96, calldataload(sub(calldatasize(), 20))) } } else { return super._msgSender(); } }

This pattern in Forwarder.sol is the critical piece — it allows the contract to distinguish between a direct call and a forwarded meta-transaction.

Why EIP-712 Matters for UX

When users sign with EIP-712, MetaMask shows readable structured data instead of hex strings:

Vote Proposal #3 ───────────────── Proposal: Update treasury allocation Vote: FOR Nonce: 42 Domain: GovernanceDAO v1 Chain: 1

This prevents phishing and ensures users understand what they're signing.


📈 Impact: What Gasless Voting Enables

Metric Traditional DAO Gasless DAO
Participation rate 2-5% Potentially 30-50%
Barrier to entry Must buy ETH, learn wallets Just connect wallet
Cost per vote $0.50 - $50+ $0 (user pays nothing)
Non-crypto users Cannot participate Can participate

🤔 Why This Matters Beyond DAOs

The meta-transaction pattern applies to any decentralized system that needs user participation:

  • DeFi voting on protocol upgrades — make it accessible to token holders who aren't traders
  • NFT community governance — let holders vote without requiring ETH
  • Decentralized social platforms — enable content moderation votes without gas barriers
  • Token-gated applications — access control without transaction friction

✅ Lessons Learned

  1. Meta-transactions are powerful but complex — correctly implementing ERC-2771 verification was the biggest technical challenge
  2. EIP-712 transforms UX — showing structured data instead of hex dramatically improves user confidence
  3. The Relayer is a trust assumption — the relayer could censor votes; future iterations should support multiple relayers


🔗 Explore the Code

Full source code: github.com/87maxi/dao

Key files:

Try it: Clone the repo, run anvil locally, and deploy the contracts. Connect your wallet, delegate tokens, and cast a gasless vote — you'll sign with EIP-712 and the local relayer handles the rest.


Project from the Blockchain and Web3 Master — CodeCrypto Academy

💡 The Solution: Separating Signature from Payment

The concept is based on the separation between who signs and who pays the gas:

sequenceDiagram
    participant U as 👤 User
    participant W as 💼 Wallet
    participant R as ⚡ Relayer API
    participant C as 📋 DAO Contract
    
    U->>W: Sign vote EIP-712 (free)
    W-->>U: Returns Signature
    U->>R: POST /api/relay {signature}
    R->>C: executeMetaTx(signature)
    C-->>R: Vote registered on-chain
    R-->>U: 200 OK {txHash}
    
    note over U,C: The user never pays gas

The Meta-Transaction Stack

  • EIP-712: For signing structured data (the vote) in a typed and secure way. The wallet shows the user exactly what they're signing in readable language.
  • ERC-2771: Standard for contracts that accept meta-transactions. Extracts the real msg.sender from calldata, allowing the contract to know who actually voted (not the Relayer).
  • Relayer Service: A Next.js API route acting as intermediary, verifies the signature, pays the gas, and forwards the signed transaction.

🏗️ System Architecture

Smart Contracts (Solidity ^0.8.24)

The system has three main contracts working together:

graph TB
    subgraph Smart Contracts
        GT[GovernanceToken<br/>ERC-20 + Delegation]
        GD[GovernorDAO<br/>Proposals & Votes]
        FW[Forwarder<br/>ERC-2771]
    end
    
    subgraph Frontend
        N[Next.js 15<br/>Wagmi + Viem]
    end
    
    subgraph Backend
        R[Relayer API<br/>Meta-transactions]
    end
    
    N -->|Read| GT
    N -->|Read| GD
    N -->|EIP-712 Sign| R
    R -->|executeMetaTx| FW
    FW -->|Verifies| GD
    GD -->|Checks balance| GT
    
    style GT fill:#1a1a2e,stroke:#00f2ff,stroke-width:2px,color:#fff
    style GD fill:#1a1a2e,stroke:#00ff88,stroke-width:2px,color:#fff
    style FW fill:#1a1a2e,stroke:#ff00f2,stroke-width:2px,color:#fff
  1. GovernanceToken.sol: ERC-20 with vote delegation capability. Holders can delegate their voting power to other addresses.

  2. GovernorDAO.sol: Proposal logic and lifecycle. Manages proposal creation, voting, and execution.

  3. Forwarder.sol: ERC-2771 implementation that verifies and forwards meta-transactions.

Proposal Lifecycle

stateDiagram-v2
    [*] --> Pending: Creation (≥10% tokens)
    Pending --> Active: Voting starts
    Active --> Succeeded: Majority for
    Active --> Defeated: Majority against
    Succeeded --> Executed: On-chain execution
    Defeated --> [*]
    Executed --> [*]

Proposal Requirements

Only accounts with ≥ 10% of total tokens can create proposals. This prevents governance spam and ensures only proposals with significant backing reach voting.

Frontend (Next.js 15 + TypeScript)

The frontend, located in packages/web/src/, uses:

  • Wagmi + Viem: Low-level hooks for type-safe EVM interaction
  • TanStack Query: Blockchain state cache with automatic revalidation
  • Tailwind CSS + Headless UI: Accessible and responsive UI
  • Real-time dashboard: Live proposal status and vote count

🧠 Lessons Learned

Meta-transactions are complex but powerful

The biggest challenge was correctly implementing the verification flow in ERC-2771. The contract needs to know who the true sender is, not the Relayer paying the gas.

// The contract extracts the real sender from the end of calldata function _msgSender() internal view override returns (address sender) { if (isTrustedForwarder(msg.sender)) { // The real sender is in the last 20 bytes of calldata assembly { sender := shr(96, calldataload(sub(calldatasize(), 20))) } } else { return super._msgSender(); } }

This pattern, implemented in Forwarder.sol, is the key piece that allows the system to work: the contract can distinguish between a direct call and a forwarded meta-transaction.

Governance UX is Hard

Decentralized governance demands that the user understands well what they're signing. The EIP-712 model helps because the wallet (MetaMask, Rabby) shows the readable structure of the message before signing:

Vote Proposal #3 Proposal: Update treasury Vote: FOR Nonce: 42


🔗 Source Code

Repository: github.com/87maxi/dao

Key files:


Project from the Blockchain and Web3 Master — CodeCrypto Academy

💬

Comments

Powered by Giscus · GitHub Discussions

🧠 Web3 & Blockchain