DAO con Gasless Voting: Gobernanza On-Chain sin Pagar Gas
Sistema de gobernanza descentralizada con votación sin gas usando meta-transacciones EIP-712 y ERC-2771, Wagmi y un Relayer interno en Next.js.
El Problema con la Votación On-Chain Tradicional
En una DAO (Organización Autónoma Descentralizada) estándar, cada voto es una transacción on-chain. Eso significa que votar cuesta ETH en gas. Este modelo excluye a participantes que no tienen fondos para pagar el gas, aunque tengan tokens de gobernanza suficientes.
La solución: Gasless Voting mediante meta-transacciones.
¿Cómo Funciona el Gasless Voting?
El concepto se basa en la separación entre quien firma y quien paga el gas:
Usuario firma su voto (EIP-712) → off-chain, gratis
↓
Usuario envía la firma al Relayer (API interna)
↓
Relayer verifica la firma y ejecuta la tx pagando el gas
↓
El contrato valida la firma con ERC-2771 y registra el voto
El Stack de Meta-Transacciones
- EIP-712: Para firmar datos estructurados (el voto) de forma tipada y segura
- ERC-2771: Estándar para contratos que aceptan meta-transacciones (extraen el
msg.senderreal de la calldata) - Relayer Service: API route de Next.js que actúa como intermediario, paga el gas y reenvía las txs firmadas
Arquitectura del Sistema
Smart Contracts (Solidity ^0.8.24)
El sistema tiene tres contratos principales:
- GovernanceToken: ERC-20 con capacidad de delegación de voto
- GovernorDAO: Lógica de propuestas y ciclo de vida
- Forwarder (ERC-2771): Verifica y reenvía meta-transacciones
// Ciclo de vida de una propuesta
Pending → Active → Succeeded / Defeated → Executed
Requisitos para Proponer
Solo cuentas con ≥ 10% del total de tokens pueden crear propuestas. Esto previene spam de gobernanza.
Frontend (Next.js 15 + TypeScript)
- Wagmi + Viem: Hooks de bajo nivel para interacción EVM
- TanStack Query: Cache y sincronización de estado blockchain
- Tailwind CSS + Headless UI: UI accesible y responsive
- Dashboard en tiempo real: Estado de propuestas y conteo de votos live
Lecciones Aprendidas
Meta-transacciones son complejas pero poderosas
El mayor desafío fue implementar correctamente el flujo de verificación en ERC-2771. El contrato necesita saber quién es el verdadero remitente, no el Relayer que paga el gas.
// El contrato extrae el sender real del final de la calldata
function _msgSender() internal view override returns (address sender) {
if (isTrustedForwarder(msg.sender)) {
// El sender real está en los últimos 20 bytes de calldata
assembly {
sender := shr(96, calldataload(sub(calldatasize(), 20)))
}
} else {
return super._msgSender();
}
}
UX de Gobernanza es Difícil
La gobernanza descentralizada exige que el usuario entienda bien qué está firmando. El modelo EIP-712 ayuda porque el wallet (MetaMask, Rabby) muestra la estructura legible del mensaje antes de firmar.
Código fuente: github.com/87maxi/dao