ProCat Solutions
Desvío por Web3: lo que enseñan los contratos que no se pueden corregir
Preventa de tokens con Solidity y OpenZeppelin, whitelist con árbol de Merkle, referidos, gas en Polygon y Arbitrum, backend con Ethers.js, auditoría y lo que perdura.
Un smart contract desplegado no se puede corregir. Si tiene un error, el error se queda, y lo pagan los usuarios con su dinero. En los últimos dos años hemos trabajado en varios proyectos Web3: sistemas de preventa de tokens, lógica de whitelist y referidos, integración de pagos en cripto en redes compatibles con EVM. Para un equipo que, por lo demás, construye sistemas backend, esa sola restricción cambió todas las decisiones. Lo que sigue es lo que supuso en la práctica desarrollar y operar smart contracts, y lo que nos trajimos de vuelta a nuestro trabajo habitual.

No escribas el tuyo
La primera regla que aprendimos con Solidity: los componentes estándar no los escribimos nosotros. ERC-20, ERC-721, el control de acceso y la protección contra reentrada ya están en la librería OpenZeppelin, auditados y ampliamente usados. Cualquier implementación propia solo añade riesgo.
El código propio empieza donde empieza la lógica de negocio: en el contrato de preventa, que decide quién puede comprar, cuándo, a qué precio y cuánto. Ahí seguimos cuatro principios:
- el estado del contrato es mínimo; lo que no hace falta para una decisión dentro de la cadena se queda fuera de ella,
- cada función que modifica estado está ligada a un permiso explícito (
OwnableoAccessControl), - el contrato se puede pausar (
Pausable), porque ante un error es el único freno de emergencia, - el movimiento de fondos sigue el patrón “checks-effects-interactions” y lo protege
ReentrancyGuard.
Como nada se puede parchear después, el manejo de errores, las pruebas y los permisos son más estrictos que en cualquier backend que hubiéramos escrito antes.
Un árbol de Merkle en lugar de una lista
Un sistema de preventa típico tiene tres partes. El contrato gestiona la compra, el backend mantiene la whitelist y los códigos de referido, y en el frontend el usuario conecta su wallet.
La whitelist ingenua, una lista de direcciones guardada en el contrato, es cara: cada dirección nueva es una transacción. Nosotros usamos un árbol de Merkle. El backend calcula la raíz del árbol de direcciones autorizadas, solo esa raíz va al contrato, y al comprar el usuario envía una prueba (Merkle proof) que el contrato verifica. Actualizar la whitelist cuesta una única transacción.
La lógica de referidos la repartimos a propósito entre la cadena y el backend. El contrato solo registra un evento: qué dirección compró y con qué identificador de referido. El cálculo de comisiones, la comprobación de elegibilidad y la programación de pagos ocurren en el backend. Las reglas pueden cambiar sin redesplegar el contrato, y la cadena no guarda datos que no deberían ser públicos.

Polygon, Arbitrum y el gas
Las comisiones de la red principal de Ethereum son inasumibles para la mayoría de las preventas, así que los proyectos corren en Polygon o Arbitrum. Ambas son compatibles con EVM y el contrato se despliega sin cambios. La operación es otra historia:
- el precio del gas y el tiempo de bloque son distintos, así que la espera de confirmaciones se configura por red,
- la comisión de Arbitrum como L2 incluye el coste de datos en L1, que depende del tamaño de la transacción; un calldata grande, como una Merkle proof larga, sale más caro de lo que uno pensaría,
- Polygon sufre de vez en cuando congestión y bloques reorganizados (reorg), así que el procesamiento de eventos solo da por definitiva una transacción tras varias confirmaciones.
Optimizar el gas en el código del contrato vale dinero real: empaquetar variables de almacenamiento, evitar escrituras innecesarias, emitir eventos en lugar de almacenar. El informe de gas de Hardhat avisa en cada ejecución de tests si algo se ha encarecido.
El websocket se corta sin avisar
Nuestro backend, en Node.js, habla con la cadena mediante Ethers.js. Tiene dos tareas: enviar transacciones (actualizar la raíz de la whitelist, pagos) y observar eventos (compras).
La observación de eventos causó la mayoría de los quebraderos de cabeza. La escucha por websocket es poco fiable: la conexión se cae en silencio. Pasamos a consultas periódicas (polling). El backend registra el último bloque procesado y pide con regularidad los eventos generados desde entonces, siempre unos bloques por detrás de la cabeza de la cadena por los reorgs. El procesamiento es idempotente, con el hash de la transacción y el índice del log como clave.
El envío necesita su propia lógica para el nonce y para sustituir transacciones atascadas. Una transacción enviada con un precio de gas bajo puede quedarse colgada horas cuando la red se encarece, y mientras sigue ahí, el siguiente nonce tampoco pasa.
La auditoría encuentra lo que nadie escribió
Los smart contracts los probamos con mucho más rigor que el código backend: tests unitarios bajo Hardhat para cada rama, tests sobre un fork con el estado real de la red, fuzzing en las funciones de cálculo. Antes del despliegue encargamos una auditoría independiente. Es obligatoria, porque el coste de un error es el dinero de los usuarios.
La auditoría rara vez encuentra un error crítico. Lo que saca a la luz son los supuestos que el desarrollador nunca dejó por escrito: qué pasa si se pierde la clave del propietario, qué ocurre con el saldo que queda en el contrato al terminar la preventa, quién puede activar la parada de emergencia.
Lo que se quedó
Web3 fue un desvío para nosotros y nunca se convirtió en línea principal. Algunas costumbres, sin embargo, se quedaron:
- la mentalidad de “esto no se puede corregir”: diseñamos migraciones, flujos de pago e integraciones externas como si no se pudieran deshacer,
- el procesamiento de eventos idempotente y reproducible pasó a ser el patrón por defecto para webhooks y colas de mensajes,
- el modelo de permisos se describe de forma explícita y se prueba para cada operación que modifica estado,
- las partes críticas del código pasan por una revisión independiente, aunque no haya auditoría formal.
Los mismos patrones han funcionado en nuestros siguientes proyectos, los sistemas basados en voz, donde el procesamiento de los eventos de llamada plantea exactamente los mismos problemas.