SwiftHTML & CSSSolidityDesenvolvimento de JogosSolana/Rust
21.11.2024

Lição 091: Contratos Proxy

No mundo do Ethereum e dos contratos inteligentes, é crucial manter a flexibilidade e a capacidade de upgrade de seus contratos. Um dos padrões mais poderosos para alcançar isso é o padrão Proxy. Esta lição fornecerá uma visão geral dos contratos proxy, seus benefícios e como implementá-los usando Solidity.

O que é um Contrato Proxy?

Um contrato proxy atua como um intermediário que encaminha chamadas para outro contrato (o contrato de implementação). Ao usar contratos proxy, podemos separar a lógica do nosso contrato de seu estado. Isso significa que podemos atualizar nossa implementação sem perder nenhum dado armazenado.

Benefícios de Usar Contratos Proxy

  1. Capacidade de Upgrade: Atualize a lógica do contrato sem perder o estado (por exemplo, saldo dos usuários).
  2. Separação de Responsabilidades: Mantenha o armazenamento e a lógica separados, facilitando a gestão.
  3. Eficiência de Gás: Reduza os custos de implantação ao implantar a lógica apenas uma vez.

Como Funcionam os Contratos Proxy

O contrato proxy mantém as variáveis de estado e delega as chamadas para um contrato de implementação real. Ele interage com o contrato de implementação através de um mecanismo chamado delegatecall. Isso permite que o proxy mantenha o contexto (armazenamento) do contrato chamador enquanto executa a lógica da implementação.

Implementação Simples de um Contrato Proxy

Nesta seção, implementaremos uma versão muito básica de um contrato proxy e um contrato de implementação.

Passo 1: Criar o Contrato de Implementação

Vamos criar um contrato simples de armazenamento para manter uma mensagem.

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

contract ArmazenamentoMensagem {
    string private mensagem;

    function setMensagem(string memory novaMensagem) public {
        mensagem = novaMensagem;
    }

    function getMensagem() public view returns (string memory) {
        return mensagem;
    }
}

Passo 2: Criar o Contrato Proxy

Agora, criaremos um contrato proxy que encaminha chamadas para o contrato ArmazenamentoMensagem.

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

contract Proxy {
    address public implementacao;

    constructor(address _implementacao) {
        implementacao = _implementacao;
    }

    function atualizarImplementacao(address _implementacao) public {
        implementacao = _implementacao;
    }

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

        assembly {
            // Encaminhar a chamada para o contrato de implementação
            let result := delegatecall(gas(), _impl, add(msg.data, 0x20), mload(msg.data), 0, 0)
            // Obter o tamanho dos dados retornados
            let size := returndatasize()
            // Copiar os dados retornados para a memória
            return(0, size)
        }
    }
}

Passo 3: Implantar e Usar Seus Contratos

  1. Implemente o contrato ArmazenamentoMensagem.
  2. Implemente o contrato Proxy, passando o endereço do contrato ArmazenamentoMensagem como parâmetro.
  3. Interaja com o proxy para definir e obter a mensagem.

Exemplo Usando Hardhat

Você pode usar o Hardhat para implantar e testar esses contratos. Aqui está um exemplo de script de implantação.

const { ethers } = require("hardhat");

async function main() {
    const ArmazenamentoMensagem = await ethers.getContractFactory("ArmazenamentoMensagem");
    const armazenamentoMensagem = await ArmazenamentoMensagem.deploy();
    await armazenamentoMensagem.deployed();
    console.log("ArmazenamentoMensagem implantado em:", armazenamentoMensagem.address);

    const Proxy = await ethers.getContractFactory("Proxy");
    const proxy = await Proxy.deploy(armazenamentoMensagem.address);
    await proxy.deployed();
    console.log("Proxy implantado em:", proxy.address);

    const proxyComoArmazenamento = await ethers.getContractAt("ArmazenamentoMensagem", proxy.address);
    await proxyComoArmazenamento.setMensagem("Olá, Proxy!");
    const mensagem = await proxyComoArmazenamento.getMensagem();
    console.log("Mensagem do Proxy:", mensagem);
}

main().catch((error) => {
    console.error(error);
    process.exitCode = 1;
});

Conclusão

Nesta lição, exploramos os contratos proxy e como eles permitem a capacidade de upgrade para aplicações Ethereum. A implementação de um contrato proxy básico demonstrou como separar lógica e estado enquanto mantém o estado entre as atualizações. Esse padrão é amplamente utilizado em aplicações descentralizadas para garantir que possam evoluir sem perder os dados de seus usuários.

Em aplicações do mundo real, é crucial combinar esse padrão com as melhores práticas de segurança, como controle de acesso e gerenciamento cuidadoso dos processos de atualização. Sempre tenha cautela e teste minuciosamente seus contratos antes de implantá-los na rede principal do Ethereum.

Video

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

Thank you for voting!