Solo con fines educativos; no constituye asesoramiento de inversión. Invertir puede ocasionar pérdidas.
Respuesta directa
delegatecall ejecuta el código de un contrato de destino en el contexto del contrato que llama. El llamador conserva su propio almacenamiento, saldo y address(this), mientras que msg.sender y msg.value mantienen los valores de la llamada original.
Este comportamiento hace posibles los proxies, las bibliotecas y los módulos de billeteras inteligentes, pero también otorga al código delegado la autoridad efectiva del llamador. Toda escritura en el almacenamiento, transferencia de activos, aprobación o llamada externa se realiza como el llamador, no como el destino que proporcionó el código.
Trate cada destino accesible mediante delegatecall como código privilegiado. La seguridad depende de las reglas de selección del destino, la compatibilidad de los diseños de almacenamiento, el estado de inicialización, los controles de actualización y la implementación exacta que esté activa para la transacción.
Cómo funciona
Con una llamada externa ordinaria, el contrato llamado lee y escribe en su propio almacenamiento. Con delegatecall, el bytecode del destino se ejecuta sobre el almacenamiento del llamador: una instrucción SSTORE modifica un slot que pertenece al llamador. Los nombres de las variables del destino no importan durante la ejecución; solo importan las posiciones calculadas de los slots.
Esto crea cuatro límites que deben auditarse:
- Control del destino: determine si el destino es fijo, lo selecciona un usuario, se resuelve mediante un registro o puede modificarlo un administrador.
- Compatibilidad del almacenamiento: compare el orden y los tipos de las variables, la herencia, los espacios reservados y los slots con espacio de nombres o estandarizados entre todas las versiones de la implementación.
- Inicialización y autorización: confirme que los inicializadores no puedan volver a ejecutarse y que las funciones de actualización o gestión de módulos exijan el llamador previsto y la demora de gobernanza establecida.
- Gestión de retornos: verifique que los fallos se propaguen y que los datos devueltos se decodifiquen como el tipo esperado; las llamadas de bajo nivel no incluyen las comprobaciones habituales de Solidity sobre el tipo de contrato.
ERC-1967 reduce las colisiones en proxies al ubicar las direcciones de implementación, beacon y administrador en slots estandarizados fuera de la asignación normal del compilador. No demuestra que una implementación sea segura ni que una actualización autorizada sea inocua.
Ejemplo
Suponga que una billetera almacena owner en slot 0. Un complemento compilado con counter en slot 0 incrementa el contador cuando la billetera lo invoca mediante delegatecall.
La escritura cambia el valor owner de la billetera porque el almacenamiento pertenece a la billetera. Si la palabra resultante codifica una dirección controlada por un atacante, las comprobaciones de autorización posteriores pueden reconocer al atacante como propietario aunque el complemento nunca haya custodiado los activos de la billetera.
Un recibo exitoso no distingue entre cambios de estado intencionados y perjudiciales. Por ello, la simulación de la transacción debe examinar las diferencias de almacenamiento, los cambios en activos y aprobaciones, los eventos emitidos y las llamadas posteriores con las direcciones exactas del proxy y de la implementación.
Riesgos
- Ejecución en destinos arbitrarios: los destinos controlados por usuarios o validados de forma insuficiente pueden ejecutar código malicioso con los permisos del llamador.
- Colisión de almacenamiento: una implementación puede sobrescribir la propiedad, los saldos, el estado de pausa o incluso el slot que selecciona la siguiente implementación.
- Actualización insegura: un administrador o proceso de gobernanza comprometido puede sustituir código revisado previamente después de que los usuarios hayan depositado activos o concedido aprobaciones.
- Fallo de inicialización: un proxy o una implementación sin inicializar puede permitir que otra cuenta reclame funciones privilegiadas o configure dependencias peligrosas.
- Inspección engañosa: verificar únicamente el código fuente del proxy, la implementación actual o la interfaz puede pasar por alto un beacon, una actualización pendiente, un registro de módulos o una ruta de ejecución alternativa.
Antes de firmar, determine la implementación en un bloque reciente, verifique quién puede cambiarla y con qué demora, inspeccione el bytecode verificado y el diseño de almacenamiento del destino, simule todos los calldata y compare el almacenamiento sensible y las aprobaciones de tokens antes y después de la ejecución. En el caso de una billetera inteligente, revise también cómo se habilitan y deshabilitan los módulos y cómo se les permite elegir destinos.
Errores comunes
- «El destino no puede tocar los activos del llamador». El código delegado se ejecuta como el llamador y puede invocar contratos externos, transferir activos o crear aprobaciones si el llamador dispone de esas capacidades.
- «Usar los mismos nombres de variables evita las colisiones». La EVM utiliza slots de almacenamiento, no nombres del código fuente. El orden del diseño, la herencia y los tipos deben seguir siendo compatibles.
- «Que el código del proxy esté verificado significa que el sistema está verificado». La implementación activa, el beacon, el administrador de actualizaciones, el estado de inicialización y los permisos de los módulos son partes independientes del límite de confianza.
Temas relacionados
- Seguridad criptoeconómica
- RPC de transacciones privadas
- Contrato proxy
- Contrato inteligente
- Simulación de transacciones
Fuentes
- Introduction to Smart Contracts - Solidity Documentation (consultado: 2026-08-20)
- Units and Globally Available Variables - Solidity Documentation (consultado: 2026-08-20)
- ERC-1967: Proxy Storage Slots - Ethereum Improvement Proposals (consultado: 2026-08-20)