SwiftHTML & CSSSolidityDesenvolvimento de JogosSolana/Rust
21.11.2024

Lição: 090: Atualização de Contratos

No mundo dos contratos inteligentes na blockchain Ethereum, a atualização é um tema crucial. Contratos inteligentes são imutáveis, o que significa que, uma vez implantados, seu código não pode ser alterado. Isso apresenta desafios quando bugs são encontrados ou novas funcionalidades precisam ser adicionadas. Nesta aula, exploraremos o conceito de contratos atualizáveis e como implementá-los efetivamente.

Por que Atualização?

Contratos atualizáveis permitem que os desenvolvedores modifiquem o comportamento de um contrato após sua implantação. Isso pode ser necessário para:

  • Corrigir bugs
  • Adicionar novas funcionalidades
  • Melhorar o desempenho
  • Modificar a lógica do contrato em resposta a requisitos em mudança

Padrões para Contratos Atualizáveis

Existem vários padrões que podem ser usados para alcançar a atualização de contratos, mas nos concentraremos em dois populares: Padrão Proxy e Padrão de Armazenamento Eterno.

Padrão Proxy

O Padrão Proxy envolve implantar dois contratos: um contrato proxy e um contrato de implementação. O proxy encaminha chamadas para a implementação, permitindo que você atualize a implementação sem alterar o endereço do proxy.

Código de Exemplo

Aqui está um exemplo de como implementar o Padrão Proxy em Solidity:

Contrato de Implementação:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Implementation {
    uint256 public value;

    function setValue(uint256 _value) public {
        value = _value;
    }

    function increment() public {
        value += 1;
    }
}

Contrato Proxy:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Proxy {
    address implementation;

    constructor(address _implementation) {
        implementation = _implementation;
    }

    fallback() external {
        address _impl = implementation;
        require(_impl != address(0), "Implementação não definida");

        assembly {
            // Copiar dados da chamada da função para a memória
            calldatacopy(0, 0, calldatasize())
            // Encaminhar a chamada para a implementação
            let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)
            // Obter o tamanho dos dados retornados
            let size := returndatasize()
            // Copiar os dados retornados para a memória
            returndatacopy(0, 0, size)
            // Retornar os dados para o chamador
            switch result
            case 0 { revert(0, size) } // Reverter se o delegatecall falhar
            default { return(0, size) } // Retornar dados se bem-sucedido
        }
    }

    function updateImplementation(address _implementation) public {
        implementation = _implementation;
    }
}

Uso

Para implantar e usar os contratos:

  1. Implemente o contrato Implementation.
  2. Implemente o contrato Proxy com o endereço da Implementation.
  3. Interaja com o contrato Proxy como se fosse a Implementation.

Quando você quiser atualizar, implemente um novo contrato Implementation e, em seguida, chame updateImplementation no Proxy. O proxy agora irá encaminhar chamadas para a nova implementação.

Padrão de Armazenamento Eterno

Outra abordagem para a atualização de contratos é o Padrão de Armazenamento Eterno, onde o contrato mantém uma camada de armazenamento (como um mapeamento) que separa os dados da lógica. A lógica pode ser atualizada independentemente dos dados.

Código de Exemplo

Contrato de Armazenamento:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Storage {
    mapping(bytes32 => uint256) public data;

    function setValue(bytes32 key, uint256 value) external {
        data[key] = value;
    }

    function getValue(bytes32 key) external view returns (uint256) {
        return data[key];
    }
}

Contrato de Lógica:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Logic {
    Storage storageContract;

    constructor(Storage _storageContract) {
        storageContract = _storageContract;
    }

    function incrementValue(bytes32 key) external {
        uint256 currentValue = storageContract.getValue(key);
        storageContract.setValue(key, currentValue + 1);
    }
}

Uso

  1. Implemente o contrato Storage.
  2. Implemente o contrato Logic com o endereço do Storage.
  3. Para atualizar a lógica, você pode implementar um novo contrato Logic e apontá-lo para o mesmo contrato Storage.

Conclusão

A atualização de contratos inteligentes é essencial para manter e evoluir aplicações descentralizadas. Ao empregar padrões de design como o Padrão Proxy e o Padrão de Armazenamento Eterno, os desenvolvedores podem garantir que seus contratos permaneçam funcionais e atualizados mesmo após a implantação. À medida que você constrói aplicações mais complexas, integrar esses padrões no seu fluxo de trabalho fornecerá a flexibilidade necessária para se adaptar ao longo do tempo.

Video

Did you like this article? Rate it from 1 to 5:

Thank you for voting!