Este tutorial está diseñado para guiar a quienes desean aprender más acerca de OpenZeppelin. Es ideal para un nivel intermedio.
OpenZeppelin Contracts es la librería de referencia para desarrollar contratos inteligentes seguros en Solidity sobre redes EVM. Ofrece implementaciones auditadas y probadas en producción de los estándares ERC-20, ERC-721, ERC-1155 y ERC-4626, módulos de control de acceso (Ownable, AccessControl), utilidades de seguridad (ReentrancyGuard, Pausable), criptografía (ECDSA, MerkleProof, EIP-712) y gobernanza on-chain (Governor, TimelockController). Su versión 5 introdujo errores personalizados, el hook unificado _update y un Ownable con propietario inicial explícito. Junto con los plugins de Upgrades para Hardhat y Foundry, permite desplegar contratos actualizables con proxies UUPS y Transparent validando automáticamente la compatibilidad del storage. Esta guía cubre la creación de tokens, el control de acceso y los patrones de actualización.
Token ERC-20 con suministro inicial y acuñación restringida al propietario, usando OpenZeppelin Contracts v5.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
contract MiToken is ERC20, Ownable {
// En v5, Ownable exige indicar el propietario inicial de forma explícita
constructor(address initialOwner)
ERC20("Mi Token", "MTK")
Ownable(initialOwner)
{
_mint(initialOwner, 1_000_000 * 10 ** decimals());
}
/// @notice Acuña nuevos tokens; solo el propietario puede llamarla
function mint(address to, uint256 amount) external onlyOwner {
_mint(to, amount);
}
}
Resultado:
Contrato desplegado: se acuñan 1.000.000 MTK (18 decimales) al propietario. Si una cuenta distinta llama a mint(), la transacción revierte con el error personalizado OwnableUnauthorizedAccount(address).
Familiarizarse con estos comandos es esencial para interactuar eficientemente con OpenZeppelin:
npm install @openzeppelin/contracts
forge install OpenZeppelin/openzeppelin-contracts
npm install --save-dev @openzeppelin/hardhat-upgrades @openzeppelin/contracts-upgradeable
const box = await upgrades.deployProxy(Box, [42], { initializer: 'initialize', kind: 'uups' });
await upgrades.upgradeProxy(proxyAddress, BoxV2);
token.grantRole(MINTER_ROLE, minterAddress);
Comprender estos conceptos fundamentales te ayudará a dominar OpenZeppelin de forma más organizada y eficiente:
Herencia y composición de módulos:
Los contratos se construyen heredando módulos auditados (ERC20, Ownable, Pausable) y sobrescribiendo funciones virtuales, en lugar de reimplementar estándares desde cero.
Ownable vs AccessControl:
Ownable define un único propietario con privilegios; AccessControl permite múltiples roles (bytes32) con administradores propios, ideal para sistemas con permisos granulares.
ReentrancyGuard:
El modificador nonReentrant bloquea llamadas reentrantes a funciones protegidas, mitigando ataques como el de The DAO. Se complementa con el patrón Checks-Effects-Interactions.
Hook _update (v5):
En la versión 5, las transferencias, acuñaciones y quemas de ERC-20 y ERC-721 pasan por la función interna _update, que reemplaza a los antiguos hooks _beforeTokenTransfer y _afterTokenTransfer.
Proxies UUPS y Transparent:
Permiten actualizar la lógica de un contrato conservando su dirección y estado. En UUPS la función de actualización vive en la implementación y se protege con _authorizeUpgrade.
Inicializadores en lugar de constructores:
Los contratos actualizables usan funciones initialize protegidas con el modificador initializer, y deben llamar a _disableInitializers() en el constructor de la implementación.
Algunos ejemplos de aplicaciones prácticas donde se utiliza OpenZeppelin:
Emisión de tokens fungibles ERC-20:
Creación de tokens de utilidad, gobernanza o stablecoins con extensiones de quema, pausa, permisos (ERC-2612 Permit) y votación.
Colecciones NFT ERC-721 y ERC-1155:
Despliegue de colecciones de NFTs con metadatos, regalías ERC-2981 y acuñación controlada por roles.
Bóvedas tokenizadas ERC-4626:
Implementación de vaults de rendimiento estandarizados que facilitan la integración con protocolos DeFi.
Gobernanza on-chain para DAOs:
Sistemas de propuestas y votación con Governor y ejecución diferida mediante TimelockController.
Protocolos actualizables en producción:
Contratos con proxies UUPS que permiten corregir errores o agregar funcionalidades sin migrar fondos ni cambiar de dirección.
Listas blancas y airdrops con Merkle Proofs:
Verificación eficiente en gas de pertenencia a listas de direcciones mediante MerkleProof para preventas y distribuciones de tokens.
Aquí tienes algunas recomendaciones para facilitar tus inicios en OpenZeppelin:
Empieza con Contracts Wizard:
Genera la base de tu token o contrato de gobernanza en wizard.openzeppelin.com seleccionando las extensiones que necesitas, y luego adáptalo.
Fija la versión de la librería:
Usa una versión exacta en package.json o en la dependencia de Foundry; mezclar contratos de v4 y v5 provoca errores de compilación por cambios en rutas e interfaces.
No modifiques los archivos de la librería:
Extiende los contratos mediante herencia y overrides; editar el código de node_modules o lib/ invalida las garantías de las auditorías.
Respeta el layout de storage al actualizar:
En contratos actualizables nunca reordenes ni elimines variables de estado existentes; agrega las nuevas al final y deja que el plugin de Upgrades lo valide.
Prefiere AccessControl en sistemas con varios actores:
Separa responsabilidades (minter, pauser, upgrader) en roles distintos en lugar de concentrar todo el poder en un único propietario.
Practica seguridad con Ethernaut:
Resuelve los niveles de Ethernaut para entender en la práctica las vulnerabilidades que los módulos de OpenZeppelin ayudan a prevenir.
Si te interesa OpenZeppelin, también podrías explorar estas herramientas:
Amplía tus conocimientos con estos enlaces y materiales:
Continúa aprendiendo con estos contenidos relacionados: