Mejora de los contratos inteligentes
Última actualización de la página: 21 de octubre de 2025
Los contratos inteligentes en Ethereum son programas que se ejecutan solos y que funcionan en la máquina virtual de Ethereum (EVM). Estos programas están diseñados para ser inmutables, lo que impide cualquier actualización en la lógica empresarial una vez que el contrato fue implementado.
Si bien la inmutabilidad es necesaria para no requerir de confianza, la descentralización y la seguridad de los contratos inteligentes puede ser una desventaja en ciertos casos. Por ejemplo, el código inmutable puede hacer que sea imposible para los desarrolladores corregir contratos vulnerables.
Sin embargo, investigación más profunda en la mejora de los contratos inteligentes llevó a la introducción de diversos patrones de actualización. Estos patrones de actualización permiten a los desarrolladores actualizar los contratos inteligentes (manteniendo la inmutabilidad) colocando lógica empresarial en diferentes contratos.
Requisitos previos
Debe tener un buen conocimiento de los contratos inteligentes, la anatomía de los contratos inteligentes y la Máquina Virtual de Ethereum (EVM). Esta guía también asume que los lectores tengan conocimiento sobre programación de contratos inteligentes.
¿Qué es la actualización de un contrato inteligente?
La actualización de un contrato inteligente implica cambiar la lógica empresarial de un contrato inteligente a la vez que se preserva el estado del contrato. Es importante aclarar que la capacidad de actualización y la mutabilidad no son lo mismo, especialmente en el contexto de contratos inteligentes.
Todavía no puede cambiar un programa implementado en una dirección en la red Ethereum. Pero puede cambiar el código que se ejecuta cuando los usuarios interactúan con un contrato inteligente.
Esto se puede llevar a cabo a través de los siguientes métodos:
-
Creando múltiples versiones de un contrato inteligente y migrando el estado (es decir, los datos) del contrato antiguo a una nueva instancia del contrato.
-
Creando contratos separados para almacenar la lógica empresarial y el estado.
-
Utilizando patrones de proxy para delegar llamadas de funciones de un contrato proxy inmutable a un contrato de lógica modificable.
-
Creando un contrato principal inmutable que se comunique con, y dependa de, contratos satélite flexibles para ejecutar funciones específicas.
-
Utilizando el patrón de diamante para delegar llamadas de funciones de un contrato proxy a contratos de lógica.
Mecanismo de actualización #1: migración del contrato
La migración de contratos se basa en el uso de versiones, que consiste en crear y gestionar estados únicos del mismo software. La migración de un contrato implica implementar una nueva instancia de un contrato inteligente existente y la transferencia del almacenamiento y los saldos al nuevo contrato.
El nuevo contrato tendrá un almacenamiento vacío, lo que le permitirá recuperar los datos del contrato anterior y escribirlos en la nueva implementación. Luego va a tener que actualizar todos los contratos que interactuaron con el contrato antiguo para reflejar la nueva dirección.
El último paso en la migración de contratos es convencer a los usuarios para que cambien y usen el nuevo contrato. La nueva versión del contrato va a mantener los saldos y direcciones de los usuarios, lo cual preserva la inmutabilidad. Si se trata de un contrato basado en tokens, también tendrá que ponerse en contacto con los exchanges para que descarten el contrato anterior y comiencen a usar el nuevo contrato.
La migración de contratos es una medida relativamente sencilla y segura para actualizar contratos inteligentes sin interrumpir las interacciones de los usuarios. Sin embargo, la migración manual de almacenamiento y saldos de usuarios al nuevo contrato consume mucho tiempo y puede generar altos costes de gas.
Más sobre la migración de contratos.opens in a new tab
Mecanismo de actualización #2: Separación de datos
Otro método para actualizar contratos inteligentes es separar la lógica empresarial y el almacenamiento de datos en contratos diferentes. Esto significa que los usuarios interactúan con el contrato de lógica, mientras que los datos se almacenan en el contrato de almacenamiento.
El contrato de lógica contiene el código que se ejecuta cuando los usuarios interactúan con la aplicación. También conserva la dirección del contrato de almacenamiento e interactúa con él para obtener y establecer datos.
Mientras tanto, el contrato de almacenamiento contiene el estado asociado con el contrato inteligente, como los saldos y direcciones de los usuarios. Tenga en cuenta que el contrato de almacenamiento es propiedad del contrato de lógica y está configurado con la dirección de este último en la implementación. Esto evita que contratos no autorizados llamen al contrato de almacenamiento o actualicen sus datos.
Por defecto, el contrato de almacenamiento es inmutable, pero se puede reemplazar el contrato de lógica al que apunta con una nueva implementación. Esto va a cambiar el código que se ejecuta en la EVM, manteniendo el almacenamiento y los saldos intactos.
Utilizar este método de actualización requiere actualizar la dirección del contrato de lógica en el contrato de almacenamiento. También hay que configurar el nuevo contrato de lógica con la dirección del contrato de almacenamiento por las razones explicadas anteriormente.
El patrón de separación de datos es posiblemente más fácil de implementar en comparación con la migración de contratos. Sin embargo, hay que gestionar varios contratos e implementar esquemas de autorización complejos para proteger los contratos inteligentes de actualizaciones maliciosas.
Mecanismo de actualización #3: Patrones de proxy
El patrón de proxy también utiliza la separación de datos para mantener la lógica empresarial y los datos en contratos diferentes. Sin embargo, en un patrón de proxy, el contrato de almacenamiento (llamado proxy) llama al contrato de lógica durante la ejecución del código. Esto es una inversión del método de separación de datos, donde el contrato de lógica llama al contrato de almacenamiento.
Esto es lo que sucede en un patrón de proxy:
-
Los usuarios interactúan con el contrato proxy, que almacena datos, pero que no contiene la lógica empresarial.
-
El contrato de proxy almacena la dirección del contrato de lógica y delega todas las llamadas a funciones al contrato de lógica (que contiene la lógica de negocio) utilizando la función
delegatecall. -
Luego de que la llamada se reenvía al contrato de lógica, los datos devueltos por el contrato de lógica se recuperan y se devuelven al usuario.
El uso de los patrones de proxy requiere una comprensión de la función delegatecall. Básicamente, delegatecall es un código de operación (opcode) que permite a un contrato llamar a otro, mientras que la ejecución real del código tiene lugar en el contexto del contrato que llama. Una implicación de usar delegatecall en los patrones de proxy es que el contrato de proxy lee y escribe en su almacenamiento y ejecuta la lógica almacenada en el contrato de lógica como si llamara a una función interna.
De la documentación de Solidityopens in a new tab:
Existe una variante especial de una llamada de mensaje, llamada delegatecall, que es idéntica a una llamada de mensaje, aparte del hecho de que el código en la dirección de destino se ejecuta en el contexto (es decir, en la dirección) del contrato que llama y que
msg.senderymsg.valueno cambian sus valores. Esto significa que un contrato puede cargar dinámicamente código de una dirección diferente en tiempo de ejecución. El almacenamiento, la dirección actual y el saldo todavía se refieren al contrato de llamada, solo se toma el código de la dirección llamada._
El contrato de proxy sabe cómo invocar a delegatecall cada vez que un usuario llama a una función, porque tiene una función fallback integrada. En la programación de Solidity, la función de fallbackopens in a new tab se ejecuta cuando una llamada de función no coincide con las funciones especificadas en un contrato.
Hacer que el patrón de proxy funcione requiere escribir una función fallback personalizada que especifique cómo el contrato de proxy debe manejar las llamadas de función que no admite. En este caso, la función fallback del proxy está programada para iniciar una delegatecall y redirigir la solicitud del usuario a la implementación del contrato de lógica actual.
El contrato de proxy es inmutable de forma predeterminada, pero se pueden crear nuevos contratos de lógica con lógica empresarial actualizada. Realizar la actualización es cuestión de cambiar la dirección del contrato de lógica al que se hace referencia en el contrato proxy.
Al apuntar el contrato de proxy a un nuevo contrato de lógica, el código que se ejecuta cuando los usuarios llaman a la función de contrato de proxy cambia. Esto nos permite actualizar la lógica de un contrato sin pedir a los usuarios que interactúen con un nuevo contrato.
Los patrones de proxy son un método popular para actualizar los contratos inteligentes porque eliminan las dificultades asociadas con la migración de contratos. Sin embargo, los patrones de proxy son más complicados de usar y pueden introducir fallos críticos, como las colisiones de selector de funciónopens in a new tab, si se utilizan de forma incorrecta.
Más sobre los patrones de proxyopens in a new tab.
Mecanismo de actualización #4: Patrón de estrategia
Esta técnica está influenciada por el patrón de estrategiaopens in a new tab, que fomenta la creación de programas de software que se interconectan con otros programas para implementar características específicas. Aplicar el patrón de estrategia al desarrollo de Ethereum significaría crear un contrato inteligente que llame a las funciones de otros contratos.
El contrato principal en este caso contiene la lógica empresarial central, pero interactúa con otros contratos inteligentes ("contratos satélite") para ejecutar ciertas funciones. Este contrato principal también almacena la dirección de cada contrato satélite y puede cambiar entre diferentes implementaciones del contrato satélite.
Puede crear un nuevo contrato satélite y configurar el contrato principal con la nueva dirección. Esto le permite cambiar estrategias (es decir, implementar una nueva lógica) para un contrato inteligente.
Aunque es similar al patrón de proxy explicado anteriormente, el patrón de estrategia es diferente porque el contrato principal, con el que interactúan los usuarios, mantiene la lógica empresarial. El uso de este patrón le brinda la oportunidad de introducir cambios limitados en un contrato inteligente sin afectar a la infraestructura central.
El principal inconveniente es que este patrón es principalmente útil para implementar actualizaciones menores. Además, si el contrato principal se ve comprometido (por ejemplo, a través de un hackeo), no podrá usar este método de actualización.
Mecanismo de actualización #5: Patrón de diamante
El patrón de diamante puede considerarse una mejora en el patrón de proxy. Los patrones de diamante difieren de los patrones de proxy porque el contrato de proxy de diamante puede delegar llamadas de función a más de un contrato de lógica.
Los contratos de lógica en el patrón de diamante se conocen como facetas. Para que el patrón de diamante funcione, necesita crear un mapeo en el contrato de proxy que asigne los selectores de funciónopens in a new tab a las diferentes direcciones de las facetas.
Cuando un usuario realiza una llamada a una función, el contrato de proxy comprueba el mapeo para encontrar el facet responsable de ejecutar esa función. Luego, invoca a delegatecall (utilizando la función de fallback) y redirige la llamada al contrato de lógica apropiado.
El patrón de actualización de diamante tiene algunas ventajas sobre los patrones tradicionales de actualización de proxy:
-
Le permite actualizar una pequeña parte del contrato sin cambiar todo el código. El uso del patrón de proxy para las actualizaciones requiere la creación de un contrato de lógica completamente nuevo, incluso para actualizaciones menores.
-
Todos los contratos inteligentes (incluidos los contratos de lógica utilizados en los patrones de proxy) tienen un límite de tamaño de 24 Kb, lo que puede ser una limitación, especialmente para contratos complejos que requieren más funciones. El patrón de diamante facilita la solución de este problema dividiendo las funciones en múltiples contratos de lógica.
-
Los patrones de proxy adoptan un enfoque general para acceder a los controles. Una entidad con acceso a las funciones de actualización puede cambiar el contrato entero. Pero el patrón de diamante permite un enfoque de permisos modular, en el que puede restringir a las entidades la actualización de ciertas funciones dentro de un contrato inteligente.
Más sobre el patrón de diamanteopens in a new tab.
Ventajas y desventajas de actualizar los contratos inteligentes
| Pros | Contras |
|---|---|
| Una actualización de contrato inteligente puede facilitar la corrección de las vulnerabilidades descubiertas en la fase posterior a la implementación. | La actualización de los contratos inteligentes es contraria a la idea de la inmutabilidad del código, lo que tiene implicaciones para la descentralización y la seguridad. |
| Los desarrolladores pueden usar actualizaciones de lógica para añadir nuevas funciones a las aplicaciones descentralizadas. | Los usuarios deben confiar en que los desarrolladores no modifiquen los contratos inteligentes arbitrariamente. |
| Las actualizaciones de los contratos inteligentes pueden mejorar la seguridad de los usuarios finales, ya que los errores se pueden corregir rápidamente. | La programación de la funcionalidad de actualización en contratos inteligentes añade mayor complejidad y aumenta la posibilidad de fallas críticas. |
| Las actualizaciones de contratos dan a los desarrolladores más espacio para experimentar con diferentes funciones y mejorar las dapps con el tiempo. | La oportunidad de actualizar los contratos inteligentes puede animar a los desarrolladores a lanzar proyectos más rápido sin hacer la debida diligencia durante la fase de desarrollo. |
| El control de acceso inseguro o la centralización en los contratos inteligentes pueden facilitar que los actores maliciosos realicen actualizaciones no autorizadas. |
Consideraciones para actualizar los contratos inteligentes
-
Utilice mecanismos seguros de control de acceso/autorización para evitar actualizaciones no autorizadas de contratos inteligentes, especialmente si se utilizan patrones de proxy, patrones de estrategia o separación de datos. Un ejemplo es restringir el acceso a la función de actualización, de modo que solo el propietario del contrato pueda invocarlo.
-
La actualización de contratos inteligentes es una actividad compleja y requiere un alto nivel de diligencia para evitar la introducción de vulnerabilidades.
-
Reduzca los supuestos de confianza descentralizando el proceso de implementación de actualizaciones. Las posibles estrategias incluyen el uso de un contrato de billetera multifirma para controlar las actualizaciones, o requerir que los miembros de una DAO voten para aprobar la actualización.
-
Tenga en cuenta los costos involucrados en la actualización de contratos. Por ejemplo, copiar el estado (por ejemplo, saldos de los usuarios) de un contrato antiguo a un nuevo contrato durante la migración de un contrato puede requerir más de una transacción, lo que significa más tarifas de gas.
-
Considere implementar bloqueos de tiempo (timelocks) para proteger a los usuarios. Un timelock se refiere a un retraso impuesto en los cambios en un sistema. Los timelocks se pueden combinar con un sistema de gobernanza multifirma para controlar las actualizaciones: si una acción propuesta alcanza el umbral de aprobación requerido, no se ejecuta hasta que transcurre el período de retraso predefinido.
Los timelocks dan a los usuarios algo de tiempo para salir del sistema si no están de acuerdo con un cambio propuesto (por ejemplo, una actualización de lógica o nuevos esquemas de tarifas). Sin timelocks, los usuarios deben confiar en que los desarrolladores no implementen cambios arbitrarios en un contrato inteligente sin previo aviso. La desventaja de los timelocks es que restringen la capacidad de parchear rápidamente las vulnerabilidades.
Recursos
OpenZeppelin Upgrades Plugins - Un conjunto de herramientas para desplegar y proteger contratos inteligentes actualizables.
Tutoriales
- Actualización de sus contratos inteligentes | Tutorial de YouTubeopens in a new tab de Patrick Collins
- Tutorial de migración de contratos inteligentes de Ethereumopens in a new tab de Austin Griffith
- Uso del patrón de proxy UUPS para actualizar contratos inteligentesopens in a new tab de Pranesh A.S
- Tutorial de Web3: Escribir un contrato inteligente actualizable (proxy) con OpenZeppelinopens in a new tab de fangjun.eth
Lecturas adicionales
- El estado de las actualizaciones de los contratos inteligentesopens in a new tab de Santiago Palladino
- Múltiples formas de actualizar un contrato inteligente de Solidityopens in a new tab - blog de Crypto Market Pool
- Aprenda: cómo actualizar contratos inteligentesopens in a new tab - Documentación de OpenZeppelin
- Patrones de proxy para la capacidad de actualización de los contratos de Solidity: proxies transparentes frente a proxies UUPSopens in a new tab de Naveen Sahu
- Cómo funcionan las actualizaciones de diamanteopens in a new tab de Nick Mudge