Un ataque de emisión infinita ocurre cuando un contrato de token permite que un atacante cree tokens sin autorización válida, sin hacer cumplir un suministro máximo, o ambas cosas. El atacante acuña un saldo arbitrario, luego vende, intercambia, deposita o colateraliza los tokens recién creados antes de que los mercados y las integraciones puedan reaccionar.
La consecuencia no es meramente inflación. La acuñación infinita puede destruir el valor de intercambio de un token, agotar pools de liquidez y crear garantías no respaldadas en sistemas de préstamo o tesorería. Elrond fue registrado como perdiendo $113.0M el 05-06-2022 en un incidente clasificado como Infinite Mint, lo que muestra cómo una frontera de emisión rota puede convertirse en una pérdida a nivel de ecosistema.
¿Qué es un ataque de emisión infinita y cómo funciona?
Un ataque de emisión infinita aprovecha una ruta de emisión de token que no prueba de manera confiable quién puede acuñar y cuánto puede existir. Una función mint pública o mal protegida permite a un atacante crear saldos que el resto del protocolo trata como tokens ERC-20 legítimos.
Una función de emisión es segura solo cuando la autorización de quien llama, las condiciones de emisión y la contabilidad del suministro coinciden con el diseño económico del protocolo. Si cualquier llamado puede acceder a una lógica equivalente a _mint, o un llamador privilegiado puede sortear un techo de suministro, la escasez del token solo es una suposición.
Una secuencia típica de ataque de emisión infinita sigue estos pasos:
- El atacante identifica una ruta de emisión llamada externamente o una verificación de acceso evitable.
- El atacante llama a la función con un destinatario y cantidad controlados por él.
- El contrato incrementa el saldo del atacante y, a menudo,
totalSupply. - El atacante transfiere los tokens acuñados a un pool DEX, mercado de préstamos, puente o vault.
- El atacante intercambia tokens artificiales por activos reales o los usa como colateral.
- Los proveedores de liquidez, prestamistas y titulares de tokens absorben la pérdida económica resultante.
Las vulnerabilidades de emisión infinita son especialmente peligrosas cuando los saldos de tokens son aceptados por otros contratos sin verificar de manera independiente cómo se crearon esos saldos. Un protocolo de préstamo no distingue entre tokens acuñados mediante emisiones previstas y tokens acuñados mediante una verificación de acceso ausente.
¿Qué patrones en Solidity causan vulnerabilidades de emisión infinita?
Las vulnerabilidades de emisión infinita suelen surgir por omisión de autorización, falta de límites en el suministro, o autorización que en apariencia es correcta pero accesible vía un camino administrativo no intencionado. Revisa cada función que pueda aumentar un saldo, directa o indirectamente.
El patrón más evidente es una función mint pública sin restricciones en el llamador:
function mint(address to, uint256 amount) external {
balanceOf[to] += amount;
totalSupply += amount;
}
El mismo riesgo existe cuando un desarrollador verifica una identidad equivocada. Por ejemplo, tx.origin no debe definir la autoridad para acuñar, porque un atacante puede inducir a una wallet privilegiada a llamar a un contrato intermediario malicioso. Similarmente, verificar si un destinatario es privilegiado no prueba que el llamador esté autorizado para emitir tokens.
Otras causas comunes incluyen:
- Una función
setMinterque puede ser llamada por cualquier cuenta, permitiendo a atacantes autoconcederse derechos de emisión. - Un proxy actualizable cuya autoridad para actualizar puede reemplazar restricciones de emisión.
- Un puente o gestor cross-chain que confía en mensajes arbitrarios como instrucciones de acuñación.
- Un límite impuesto en una función de emisión pero no en otra ruta de emisión, como reclamaciones de recompensas o funciones de migración.
- Errores en cálculos aritméticos o conversiones de unidades que vuelven inefectivo un límite nominal.
- Contabilidad de burn y emisión donde los quemados no se verifican realmente antes de emitir tokens sustitutivos.
Una variable totalSupply sola no es un control de suministro. El contrato debe rechazar toda emisión que suponga que totalSupply + amount excede el máximo definido. Esa invariabilidad debe aplicarse en todas las rutas de emisión, incluyendo recuperación administrativa, emisión mediante puente, recompensas y migraciones.
¿Cómo luce un código Solidity vulnerable y corregido para emisión infinita?
Un contrato vulnerable a emisión infinita expone una función de emisión sin restricciones. Cualquier cuenta externa puede acuñar cualquier cantidad para sí misma, y ningún límite máximo mitiga el daño.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract VulnerableInfiniteMintToken {
string public constant name = "Vulnerable Token";
string public constant symbol = "VULN";
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
event Transfer(address indexed from, address indexed to, uint256 amount);
function mint(address to, uint256 amount) external {
// VULNERABLE: cualquier llamador puede crear tokens arbitrarios.
totalSupply += amount;
balanceOf[to] += amount;
emit Transfer(address(0), to, amount);
}
function transfer(address to, uint256 amount) external returns (bool) {
require(balanceOf[msg.sender] >= amount, "saldo insuficiente");
unchecked {
balanceOf[msg.sender] -= amount;
}
balanceOf[to] += amount;
emit Transfer(msg.sender, to, amount);
return true;
}
}
La versión corregida separa al administrador de los minters autorizados y aplica un máximo de suministro antes de cada incremento de saldo. El límite no sustituye la autorización, y la autorización no sustituye a un límite. Ambos controles abordan distintos modos de fallo.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract CappedRoleMintToken {
string public constant name = "Capped Token";
string public constant symbol = "CAP";
address public immutable admin;
uint256 public immutable maxSupply;
uint256 public totalSupply;
mapping(address => bool) public isMinter;
mapping(address => uint256) public balanceOf;
event Transfer(address indexed from, address indexed to, uint256 amount);
event MinterUpdated(address indexed minter, bool allowed);
modifier onlyAdmin() {
require(msg.sender == admin, "no eres admin");
_;
}
modifier onlyMinter() {
require(isMinter[msg.sender], "no eres minter");
_;
}
constructor(uint256 maxSupply_) {
require(maxSupply_ > 0, "máximo cero");
admin = msg.sender;
maxSupply = maxSupply_;
isMinter[msg.sender] = true;
}
function setMinter(address minter, bool allowed) external onlyAdmin {
isMinter[minter] = allowed;
emit MinterUpdated(minter, allowed);
}
function mint(address to, uint256 amount) external onlyMinter {
// CORRECCIÓN: emisión autorizada que no supera el límite inmutable.
require(totalSupply + amount <= maxSupply, "límite superado");
totalSupply += amount;
balanceOf[to] += amount;
emit Transfer(address(0), to, amount);
}
function transfer(address to, uint256 amount) external returns (bool) {
require(balanceOf[msg.sender] >= amount, "saldo insuficiente");
unchecked {
balanceOf[msg.sender] -= amount;
}
balanceOf[to] += amount;
emit Transfer(msg.sender, to, amount);
return true;
}
}
Si tu protocolo tiene emisión mediante recompensas, puentes, acciones en vaults o migraciones, una auditoría de contrato inteligente y revisión de seguridad puede rastrear cada camino de emisión hasta su autorización y su invariabilidad de suministro antes del despliegue.
¿Cómo prevenir ataques de emisión infinita en Solidity?
Prevenir ataques de emisión infinita requiere un diseño en capas de emisión: autoridad explícita, restricciones de suministro, poderes administrativos restringidos y pruebas que demuestren que ningún otro camino puede crear saldos. El diseño apropiado depende de si el suministro es fijo, limitado, basado en emisiones, respaldado por colateral, o mediante puente.
| Técnica | Lo que detiene | Costo en gas o complejidad | Limitación |
|---|---|---|---|
mint controlada por rol |
Llamadores externos arbitrarios creando tokens | Lectura de almacenamiento para la verificación de rol | Un minter comprometido aún puede acuñar en su autoridad |
| Máximo suministro inmutable | Acuñación más allá del suministro diseñado | Una comprobación aritmética por emisión | No previene que una parte autorizada acuñe prematuramente todo el límite |
| Roles separados de administrador y minter | Un minter autoconcede mayor autoridad | Código adicional de gestión de roles | El admin sigue siendo una frontera crítica de confianza |
| Administración multisignature o con tiempo de bloqueo | Cambios con una sola llave | Retraso operacional y sobrecarga de gobernanza | No corrige lógica de emisión defectuosa ya desplegada |
Ruta interna centralizada _mint |
Omisión en verificaciones de límites en funciones secundarias | Esfuerzo de refactorización | Los revisores aún deben encontrar todos los caminos externos a _mint |
| Monitoreo de eventos para actividad de emisión | Detección retardada de emisiones anormales | Infraestructura de monitoreo off-chain | El monitoreo no puede revertir una acuñación maliciosa ya ocurrida |
Para tokens de suministro fijo, el patrón más fuerte suele ser acuñar toda la asignación en despliegue y eliminar toda capacidad de emisión posterior. Para tokens basados en emisiones, define una programación medible, aplica el límite agregado en la función interna compartida y asegura que cada función externa tenga un propósito de autorización distinto.
La metodología de Soken trata la acuñación como un límite de creación de activos, no como una función de conveniencia. Cada ruta que aumenta los saldos de tokens debería tener un modelo de autoridad documentado, un límite económico explícito y una invariabilidad testeable.
¿Qué incidentes reales se clasificaron como ataques de emisión infinita?
Los incidentes registrados de emisión infinita muestran que las fallas en emisión de tokens afectan a activos establecidos y nuevos protocolos en varias cadenas. Los incidentes a continuación están clasificados como Infinite Mint en la base de datos de hacks de DefiLlama.
| Protocolo | Fecha | Cantidad perdida | Cadena |
|---|---|---|---|
| Elrond | 2022-06-05 | $113.0M | Elrond |
| Paid Network | 2021-03-05 | $27.0M | Ethereum |
| Gala | 2024-05-20 | $22.3M | Ethereum |
| Haven Protocol | 2021-07-03 | $8.2M | Haven |
| OcelotDex | 2024-09-20 | $4.9M | Ethereum |
| TSR | 2026-06-01 | $2.5M | BSC |
| Raydium AMM | 2026-06-10 | $1.3M | Solana |
| Flooring Protocol | 2026-06-08 | $500K | Ethereum |
Estos incidentes comparten una propiedad económica clave: una vez que tokens no respaldados ingresan a un mercado integrado, el ataque se extiende más allá del contrato de token. Los pools de liquidez compran la oferta artificial, los mercados de préstamo pueden aceptarla como colateral, y puentes o vaults pueden propagar el saldo a otros sistemas.
Por lo tanto, la cuestión relevante para revisión es más amplia que, “¿Puede un atacante llamar a mint?” Es, “¿Puede cualquier camino no confiable o excesivamente confiado provocar que el protocolo reconozca tokens no respaldados como activos válidos?”
¿Cómo detectar una vulnerabilidad de emisión infinita antes del despliegue?
Detectar vulnerabilidades de emisión infinita requiere mapear todas las transiciones de estado que aumentan saldos, y luego probar que cada transición está autorizada y conserva la invariabilidad de suministro prevista. Una revisión manual debe comenzar con los contratos de tokens, pero debe continuar en puentes, vaults, distribuidores de recompensas, módulos de actualización y helpers administrativos.
Utiliza esta lista de revisión:
- Encuentra cada escritura en balances, acciones, contadores de suministro y funciones internas de emisión.
- Verifica que cada camino externo de emisión tenga autorización del llamador apropiada a su propósito.
- Confirma que cada camino aplique la misma restricción o límite de emisiones.
- Revisa funciones que agregan, quitan o reemplazan minters y administradores.
- Rastrea rutas de actualización, puente, migración y callbacks que puedan llegar a la lógica de
mint. - Verifica si cuentas privilegiadas pueden cambiar el límite, emisor, o unidad de contabilidad inesperadamente.
- Asegúrate de que eventos de emisión y
totalSupplycoincidan con la creación real de saldos. - Prueba integraciones que aceptan el token como colateral, activos de liquidez o en vaults.
Slither resulta útil para enumerar funciones llamables externamente, cambios privilegiados de estado y patrones sospechosos de control de acceso, pero no puede establecer que el modelo de suministro de un protocolo es económicamente correcto. Es esencial el razonamiento manual para caminos de acuñación indirectos y autoridad cruzada entre contratos.
La fuzzing de Foundry puede probar que ningún llamador arbitrario puede acuñar y que el suministro nunca supera el límite. Las pruebas de invariabilidad son particularmente efectivas porque expresan directamente la propiedad de seguridad:
function invariant_supplyNeverExceedsCap() public view {
require(token.totalSupply() <= token.maxSupply(), "límite de suministro roto");
}
Una segunda invariabilidad debe afirmar que solo actores aprobados pueden aumentar el suministro. En la metodología de revisión de Soken, esa propiedad se prueba en todas las funciones públicas, no solo en la llamada mint. Para mayor investigación sobre control de acceso, contabilidad de tokens y riesgos relacionados, visita el Soken Hub.
Insight de seguridad: La metodología de Soken trata cada unidad de token como un pasivo creado por el protocolo. Si el contrato no puede demostrar quién autorizó ese pasivo y por qué permanece dentro del modelo de emisión, la ruta de emisión es un límite de seguridad que requiere rediseño.
Una verificación de rol y un límite de suministro transforman una emisión no restringida en una operación acotada y responsable, pero solo si cada ruta de acuñación las usa. La tarea clave de ingeniería es centralizar la creación de saldos y hacer imposible saltarse pasos por construcción.
Antes del lanzamiento o actualización, escribe la invariabilidad de suministro prevista en lenguaje simple, impleméntala en la ruta de emisión compartida y aplícalo con pruebas de invariabilidad en Foundry.