upgrade a Solidity smart contract. Proxy contract using a delegate call

Grupo de mercado de criptomonedas: múltiples formas de actualizar un contrato inteligente de Solidity

Publicado por
Comparte en redes sociales

<div>

Hay situaciones en las que desea actualizar un contrato inteligente de Solidity. Es posible que desee corregir una vulnerabilidad de software, cambiar la lógica del contrato o agregar una nueva función. Como sabe, los contratos inteligentes son inmutables y una vez que se implementan en la cadena de bloques, no se pueden cambiar. Si implementa una nueva versión del contrato, también comienza con un almacenamiento vacío. En este tutorial revisaremos varios métodos que le permitirán actualizar un contrato inteligente de Solidity. Estos métodos funcionarán en Ethereum, Binance Smart Chain, Polygon o cualquier otra cadena de bloques compatible con EVM.

Método de actualización 1: contrato de proxy mediante una llamada de delegado

Para este primer método de actualización, los usuarios interactuarán con un contrato de proxy que no contiene ninguna lógica comercial. El contrato de proxy interactuará con el contrato real para ejecutar todas las llamadas. Para que el contrato de representación interactúe con el contrato real, usaremos una llamada de delegado. Una llamada de delegado le permitirá ejecutar una función en el contexto de otro contrato.

En el caso de una actualización, el contrato de proxy todavía se utiliza. Los usuarios interactuarán con el mismo contrato de proxy y todos los datos permanecerán almacenados en el estado. Para actualizar la lógica empresarial, crearía un nuevo contrato inteligente con el que interactúe el proxy. El contrato de proxy no contiene ninguna lógica comercial. Este método separa los datos almacenados (contrato de proxy) y la lógica empresarial (contrato separado).

actualizar un contrato inteligente de Solidity. Contrato de apoderado mediante una llamada de delegado

Contrato de proxy utilizando un flujo de proceso de llamada de delegado:

  1. Bob implementa smart contractV1 que contiene lógica empresarial
  2. Luego, implementa un contrato de proxy que está diseñado para llamar smart contractV1
  3. Los usuarios interactúan con el contrato de proxy y la función de respaldo llama al contrato inteligente V1
  4. Todos los datos se almacenan en el contrato de proxy.
  5. Bob quiere cambiar la funcionalidad en su contrato inteligente, por lo que implementa smart contractV2
  6. Actualiza su contrato de proxy para apuntar a la nueva dirección del contrato V2

Para probar un contrato de proxy mediante una llamada de delegado, realice los siguientes pasos:

  • Implementar el smartContractV1 contrato a continuación
  • Implemente el contrato de proxy y configure SMARTCONTRACTWITHLOGIC en la dirección smartContractV1
  • Pruebe el contrato V1
  • Luego implemente el smartContractV2 contrato a continuación
  • Uso de la función de actualización de proxy para configurar la dirección del contrato smartContractV2
  • Pruebe el contrato V2

Contrato de apoderado

pragma solidity ^0.8.6;

contract sampleProxy {

  //two assembly memory slots locations
  bytes32 private constant _OWNER_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
  bytes32 private constant _SMARTCONTRACTWITHLOGIC_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

 
  constructor() {
    bytes32 slot = _OWNER_SLOT;
    address _admin = msg.sender;
    assembly {
    //allows you to store a value in storage
      sstore(slot, _admin)
    }
  }

 
  //address of the owner
  function admin() public view returns (address owner) {
    bytes32 slot = _OWNER_SLOT;
    assembly {
      //read a value in storage
      owner := sload(slot)
    }
  }

 
  //address of the contract with business logic
  function SMARTCONTRACTWITHLOGIC() public view returns (address contractwithlogic) {
    bytes32 slot = _SMARTCONTRACTWITHLOGIC_SLOT;
    assembly {
      contractwithlogic := sload(slot)
    }
  }


  //function used to change the address of the contract containing business logic
  function upgrade(address newContract) external {
    //verify the sender is the admin
    require(msg.sender == admin(), 'You must be an owner only');
    bytes32 slot = _SMARTCONTRACTWITHLOGIC_SLOT;
    assembly {
      //store in memory the new address
      sstore(slot, newContract)
    }
  }

 
//user calls a function that does not exist in this contract so the fallback function is called
//assembly is used


  fallback() external payable {
    assembly {
      //get the address of the contract that contains business logic
      //save address in temporary memory
      let _target := sload(_SMARTCONTRACTWITHLOGIC_SLOT)
      //copy the function call in memory
      //first parameter is the memory slot we want to copy the function call to
      //second parameter is the memory slot we want to copy from
      //third parameter is the size we want to copy which is all data
      calldatacopy(0x0, 0x0, calldatasize())
      //forward the call to the smart contract that contains the business logic
      //specify the gas, address of contract, function we want to call and size
      //if the call is successful it will be stored in the bool result
      let result := delegatecall(gas(), _target, 0x0, calldatasize(), 0x0, 0)
      //copy the return data into memory
      returndatacopy(0x0, 0x0, returndatasize())
      //if the result is 0 and failed then revert
      switch result case 0 {revert(0, 0)} default {return (0, returndatasize())}
    }
  }
}

Pruebatelo Remezclar

Leer también  Bitkraft recauda $220,6 millones para su segundo fondo de tokens

smartContractV1 y smartContractV2

pragma solidity ^0.8.6;


contract smartContractV1 {
  uint public age;

//set
    function setAge(uint newAge) external {
        age = newAge;
    }
}



pragma solidity ^0.8.6;

//in the upgraded contract you need to keep all existing state variables in the same order
//add new state variables below existing state variables or you will overwrite data 

contract smartContractV2 {
  uint public age1;
  uint public age2;

//set
    function setAge1(uint newAge1) external {
        age1 = newAge1;
    }

//set
    function setAge2(uint newAge2) external {
        age2 = newAge2;  
    }
}

Pruebatelo Remezclar

Método de actualización 2: el patrón de interfaz

Para el segundo método de actualización, un contrato inteligente utilizará una interfaz para llamar a una función en otro contrato. El objetivo es abstraer la implementación de un contrato detrás de una interfaz que solo define sus firmas de funciones. Este es un patrón bien conocido en la programación orientada a objetos, por lo que algunos desarrolladores estarán familiarizados con este concepto de abstracción.

Este contrato principal contiene la mayor parte de la lógica empresarial, pero interactúa con uno o varios contratos para realizar más funciones. Por ejemplo, un contrato inteligente que realiza un arbitraje de préstamo rápido en Uniswap y SushiSwap. Algunas de las funciones principales de los contratos dependen de otros contratos satélite para su ejecución. En cualquier momento puede implementar un nuevo contrato de satélite y actualizar su dirección en el contrato principal.

Los usuarios llaman a funciones en el contrato principal que pueden ejecutar la función en sí o interactuar con otro contrato para su ejecución. Un contrato puede interactuar con uno o varios contratos y puede utilizar una combinación de sus funciones. Esto es diferente de un contrato de proxy que utiliza una llamada de delegado porque la lógica empresarial existe en el contrato principal.

Para actualizar el contrato, no puede cambiar ninguna funcionalidad en el contrato principal, pero puede cambiar la funcionalidad en un contrato satélite. siempre que respete la interfaz. Luego actualice la dirección del contrato de satélite en el contrato principal. Este es un patrón muy popular a seguir.

actualizar un contrato inteligente de Solidity. Usando un patrón de interfaz

El flujo de proceso del patrón de interfaz de contrato inteligente:

  1. Bob implementa el contrato principal que importa una interfaz definida
  2. El contrato principal es responsable de la lógica empresarial y el almacenamiento.
  3. Luego despliega un contrato satelliteV1
  4. Configura la dirección satelliteV1 en el contrato principal
  5. Los usuarios interactúan con el contrato principal.
  6. El contrato principal tiene funciones implementadas y llama a funciones adicionales en el contrato satelliteV1
  7. Bob quiere cambiar la lógica empresarial en su contrato inteligente, por lo que implementa el contrato satelliteV2
  8. Actualiza su contrato principal con la dirección del contrato satelliteV2

Para probar el patrón de interfaz, realice los siguientes pasos:

  • Implemente el contrato principal a continuación
  • Implementar el contrato satelliteV1
  • Llama a mejora función del contacto principal utilizando la dirección del contrato satélite V1
  • Pruebe el contrato principal llamando al getAge función
  • Luego, implemente el contrato satelliteV2 a continuación
  • Llama a mejora función del contacto principal utilizando la dirección del contrato satélite V2
  • Pruebe el contrato principal llamando al getAge función

Contrato principal con interfaz definida


pragma solidity ^0.8.6;

//defined interface needed to interact with other contract
interface Ibusinesslogic {
  function getAge() external pure returns(uint);
}

contract MainContract {
  //set an admin address
  address public admin;
  //interface contract address
  Ibusinesslogic public businesslogic;
  //the admin is the owner
  constructor() {
    admin = msg.sender;
  }

 
  //function to upgrade the contract to point to execute function
  function upgrade(address _businesslogic) external {
    require(msg.sender == admin, 'only admin');
    businesslogic = Ibusinesslogic(_businesslogic);
  }


  //call the getAge function using the businesslogic function
  function getAge() external view returns(uint) {
    return businesslogic.getAge();
  }
}

Pruebatelo Remezclar

Leer también  Los ejecutivos de League of Legends recaudan $ 55 millones para el juego de mundo abierto de próxima generación

Contratos satélite con interfaz definida

pragma solidity ^0.8.6;

//defined interface needed to interact with other contract
interface Ibusinesslogic {
  function getAge() external pure returns(uint);
}


pragma solidity ^0.8.6;

//satelliteV1 uses the Ibusinesslogic interface
contract satelliteV1 is Ibusinesslogic {
  function getAge() override external pure returns(uint) {
    return 25;
  }
}


pragma solidity ^0.8.6;

//satelliteV2 uses the Ibusinesslogic interface
contract satelliteV2 is Ibusinesslogic {
  function getAge() override external pure returns(uint) {
    return 32;
  }
}

Pruebatelo Remezclar

Método de actualización 3: almacene todos los datos en un contrato de almacenamiento

El tercer método de actualización es utilizar un contrato para toda la lógica empresarial y un segundo contrato para almacenar datos. Este caso de uso es valioso cuando desea actualizar un contrato inteligente y no le importa que la dirección cambie, pero desea conservar todos los datos. Los usuarios interactúan con el contrato de lógica empresarial y los datos se guardan en el contrato de almacenamiento.

Cuando actualiza, no puede cambiar ninguna de las funciones del contrato de almacenamiento, pero puede reemplazar el contrato de lógica empresarial. El contrato de lógica empresarial es responsable de toda la lógica y de interactuar con el contrato de almacenamiento obteniendo y configurando datos.

actualizar un contrato inteligente de Solidity. almacenar datos en un contrato de almacenamiento y un contrato para la lógica empresarial

El flujo de proceso de patrón de uso de dos contratos (lógica empresarial y almacenamiento de datos):

  1. Bob implementa un contrato de almacenamiento de usuario
  2. El propósito de los contratos de almacenamiento del usuario es guardar datos
  3. Bob implementa el userContract
  4. El contrato de usuario es responsable de la lógica empresarial y envía solicitudes al contrato de almacenamiento del usuario.
  5. Configura la dirección userStorage en el userContract
  6. Configura la dirección userContract en el contrato userStorage. Esto agrega un nivel de seguridad para que solo los contratos autorizados puedan actualizar los datos.
  7. Los usuarios interactúan con el contrato de usuario que llama a funciones y almacena datos en el contrato de almacenamiento del usuario
  8. Bob quiere cambiar la funcionalidad en su userContract, por lo que implementa un nuevo usercontractV2
  9. Actualiza su dirección userContract en el contrato userStorage y la dirección userStorage en el userContract

Para probar este patrón utilizando dos contratos (lógica empresarial y almacenamiento de datos), realice los siguientes pasos:

  • Implemente el contrato de userStorage a continuación
  • Implementar el contrato de usuario a continuación
  • En el contrato userStorage, llame al permitir el acceso función utilizando la dirección userContract. Esto le dará al userContract permiso para escribir en el contrato userStorage.
  • En el userContract llame al setStorageContract función utilizando la dirección userStorage. Esto indica al contrato dónde obtener y configurar los datos.
  • Pruebe el userContract estableciendo la variable de edad y luego obteniendo la variable de edad.

Contrato de almacenamiento de usuario

pragma solidity ^0.8.6;

//this contract is used to store data
 
contract UserStorage {

    //a mapping to determine which contract has access to write data to this contract
    //used in the modifier below
    mapping(address =&gt; bool) accessAllowed;
    uint private age;


    //a basic mapping that allows one to set an address and a bool value
    //for example - is this address registered on the platform?
     mapping(address =&gt; bool) addressSet;

    //function modifier checks to see if an address has permission to update data
    //bool has to be true
    modifier isAllowed() {
        require(accessAllowed[msg.sender] == true);
        _;
    }

    //access is allowed to the person that deployed the contract
    function UserStorageAccess() public {
        accessAllowed[msg.sender] = true;
    }


    //set an address to the accessAllowed map and set bool to true
    //uses the isAllowed function modifier to determine if user can change data
    //this function controls which addresses can write data to the contract
    //if you update the UserContract you would add the new address here
    function allowAccess (address _address) isAllowed public {
         accessAllowed[_address] = true;
    }

     
    //set an address to the accessAllowed map and set bool to false
    //uses the isAllowed function modifier to determine if user can change data
    //this function controls which addresses need to have thier write access removed from the contract
    //if you update the UserContract you would set the old contract address to false
    function denyAccess (address _address) isAllowed public {
         accessAllowed[_address] = false;
     }

     
    //gets an address from the addressSet map and displays true or false
    function getAddressSet (address _address) public view returns(bool) {
         return addressSet[_address];
    }

     
    //sets an address to the addressSet map and sets the bool true or false
    //uses the isAllowed function modifier to determine if user can change data
    function setAddressSet (address _address, bool _bool) isAllowed public {
         addressSet[_address] = _bool;
    }

     
    //get the age from the age variable
    function getAge () public view returns (uint) {
        return age;
    } 

    
    //set an age to the age variable
    //uses the isAllowed function modifier to determine if user can change data
    function setAge(uint newAge) isAllowed public {
        age = newAge;
      
    }
}

Pruebatelo Remezclar

Contrato de usuario que contiene lógica empresarial

pragma solidity ^0.8.6;

//logic is in the UserContract and data storage is in the UserStorage contract
//if we want to upgrade the usercontract we can and will not loose any data

contract UserContract {

    UserStorage userStorage;


    //set the address of the storage contract that this contract should user
    //all functions will read and write data to this contract
    function setStorageContract(address _userStorageAddress) public {
        userStorage = UserStorage(_userStorageAddress);    
    }


    //reads the addressSet map in the UserStorage contract
    function isMyUserNameRegistered() public view returns(bool) {
        return userStorage.getAddressSet(msg.sender);   
    }

    
    //writes to the addressSet map in the UserStorage contract
    function registerMe() public {
        userStorage.setAddressSet(msg.sender, true);
    }

    
    //set the age in the storage contract
    function setAge(uint newAge) public {
        userStorage.setAge(newAge);
    }

    
    //get the age in the storage contract
    function getAge() public view returns (uint){
        return userStorage.getAge();
    }   
}

Pruebatelo Remezclar

Aunque el código de Solidity es inmutable, se puede implementar un método para evitar este concepto y tener código en múltiples contratos para tener mutabilidad. Esto permite actualizar un contrato inteligente de Solidity.

Leer también  iShares Bitcoin Trust (IBIT) de BlackRock cierra la brecha con el Bitcoin Trust (GBTC) de Grayscale con una diferencia de 50.000 BTC

 

Si quiere puede hacernos una donación por el trabajo que hacemos, lo apreciaremos mucho.

Direcciones de Billetera:

- BTC: 14xsuQRtT3Abek4zgDWZxJXs9VRdwxyPUS 

- USDT: TQmV9FyrcpeaZMro3M1yeEHnNjv7xKZDNe 

- BNB: 0x2fdb9034507b6d505d351a6f59d877040d0edb0f

- DOGE: D5SZesmFQGYVkE5trYYLF8hNPBgXgYcmrx 

También puede seguirnos en nuestras Redes sociales para mantenerse al tanto de los últimos post de la web:

-Twitter

- Telegram

Disclaimer: En Cryptoshitcompra.com no nos hacemos responsables de ninguna inversión de ningún visitante, nosotros simplemente damos información sobre Tokens, juegos NFT y criptomonedas, no recomendamos inversiones