delegatecall y Proxies Transparentes: Claves para la Actualización de Contratos Inteligentes

En el ecositsema blockchain, una vez desplegado, un contrato inteligente es inmutable. Sin embargo, la necesidad de corregir errores o añadir funcionalidades es común. Por ello, la capacidad de actualizar contratos inteligentes es fundamental.

¿Qué es delegatecall?

delegatecall es una llamada a función de bajo nivel en Solidity que permite ejecutar código de otro contrato dentro del contexto del contrato que realiza la llamada. Esto significa que el código del contrato invocado opera sobre el almacenamiento y el estado del contrato invocador.

Analogía

Imagina a un actor (Contrato A) actuando bajo las indicaciones del guion de otro actor (Contrato B). Aunque el Contrato A ejecuta el código del Contrato B, utiliza los accesorios y el escenario del Contrato A (su propio almacenamiento y contexto).

Sintaxis de delegatecall

(bool exito, bytes memory datos) = objetivo.delegatecall(datos);

Donde objetivo es la dirección del contrato a invocar y datos son los datos de la llamada.

Casos de Uso

  1. Patrón de Contrato Proxy (Proxy Pattern): En este patrón, un contrato (el proxy) ejecuta código en nombre de otro contrato (el contrato lógico). Mediante delegatecall, el proxy transfiere la ejecución al contrato lógico, permitiendo la actualización del contrato sin cambiar su dirección.

¿Qué es un Proxy Transparente?

El administrader se convierte en una figura central, capaz de invocar funciones de actualización en el contrato proxy para modificar el contrato lógico. Otros usuarios interactúan directamente con las funcionalidades del contrato lógico a través del proxy, pero no pueden ejecutar las funciones de actualización. Se evita así la colisión de selectores de función.

Estructura del Patrón Proxy Transparente

  1. Contrato Proxy: Gestiona la dirección del contrato lógico y utiliza delegatecall para redirigir las llamadas.
  2. Contrato Lógico: Contiene la lógica de negocio. Puede ser reemplazado por una nueva versión para implementar actualizaciones.

Analogía

Piensa en un teatro (contrato proxy) con una dirección fija. Los espectadores (usuarios) siempre acuden a este teatro. La obra represantada (contrato lógico) puede cambiar, y es el director del teatro (administrador) quien decide cuándo y qué obra se pone en escena. Esencialmente, es una implementación del patrón Proxy.

Ventajas del Patrón Proxy Transparente

  1. Actualización: Permite cambiar el contrato lógico para corregir errores o añadir características, sin alterar la dirección del proxy.
  2. Transparencia: Los usuarios interactúan con el proxy sin necesidad de conocer el contrato lógico subyacente, facilitando transiciones fluidas.
  3. Flexibilidad: La capacidad de actualizar el contrato lógico permite adaptarse a nuevos requisitos de negocio.

Ejemplo

Contrato Proxy


contract Proxy {
    address public implementation; // Dirección del contrato lógico
    address public owner;          // Administrador del proxy
    string public message;          // Variable de estado compartida

    constructor(address _implementation) {
        owner = msg.sender;
        implementation = _implementation;
    }

    // Fallback para delegar llamadas al contrato lógico
    // Se evita que el owner llame directamente para prevenir conflictos
    fallback() external payable {
        require(msg.sender != owner, "Owner cannot call logic directly");
        (bool success, ) = implementation.delegatecall(msg.data);
        require(success, "Delegate call failed");
    }

    // Función para actualizar el contrato lógico
    function updateImplementation(address newImplementation) external {
        require(msg.sender == owner, "Only owner can upgrade");
        implementation = newImplementation;
    }
}

Contratos Lógicos


// Versión anterior del contrato lógico
contract LogicV1 {
    // Las variables de estado deben coincidir con las del proxy para evitar colisiones de slots
    address public implementation; 
    address public owner;          
    string public message;          // Mensaje que se modificará

    // Función para modificar la variable 'message'
    // Selector: 0x171c992a (aproximado, varía según la compilación)
    function updateMessage(string memory newMessage) public {
        message = newMessage;
    }
}

// Nueva versión del contrato lógico
contract LogicV2 {
    // Las variables de estado deben coincidir con las del proxy para evitar colisiones de slots
    address public implementation; 
    address public owner;          
    string public message;          // Mensaje que se modificará

    // Función para modificar la variable 'message'
    // Selector: 0x171c992a (aproximado, debe ser el mismo que LogicV1)
    function updateMessage(string memory newMessage) public {
        message = newMessage;
    }
}

Etiquetas: Solidity delegatecall Contratos Inteligentes Proxy Pattern actualización de contratos

Publicado el 7-21 08:18