SwiftHTML & CSSSolidityDesenvolvimento de JogosSolana/Rust
26.11.2024

Lição: 146: Contratos Proxy e Proxies Mínimos

No desenvolvimento de contratos inteligentes, um desafio comum surge quando precisamos atualizar contratos sem perder estado ou dados. Para alcançar isso, podemos utilizar contratos proxy e proxies mínimos (às vezes referidos como “contratos clone”). Esta aula explicará os conceitos de contratos proxy, como eles funcionam e fornecerá exemplos em Solidity.

Compreendendo Contratos Proxy

Um contrato proxy atua como um intermediário entre os usuários e a lógica do contrato subjacente. Ele delega as chamadas a um contrato de implementação separado enquanto mantém o estado no proxy em si. Essa abordagem nos permite atualizar a lógica do nosso contrato simplesmente mudando o endereço do contrato de implementação, sem alterar o endereço do proxy.

Estrutura de um Contrato Proxy

Um contrato proxy básico geralmente é composto por:

  • Um ponteiro para o contrato de lógica (implementação).
  • Armazenamento para as variáveis de estado.

Aqui está um exemplo simples de um contrato proxy:

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

contract Proxy {
    address public implementation;

    constructor(address _implementation) {
        implementation = _implementation;
    }

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

        assembly {
            // Encaminha a chamada para o contrato de implementação
            calldatacopy(0, 0, calldatasize())
            let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)
            let size := returndatasize()
            returndatacopy(0, 0, size)
            switch result
            case 0 { revert(0, size) }
            default { return(0, size) }
        }
    }
}

Como Funciona

  • A variável implementation armazena o endereço do contrato de lógica que contém a lógica de negócio real.
  • A função fallback() é acionada quando o proxy recebe uma chamada que não corresponde a nenhuma de suas funções. Ela usa delegatecall para encaminhar a chamada ao contrato de implementação.
  • A chamada mantém o contexto (ou seja, msg.sender e msg.value) porque delegatecall é utilizado, permitindo a manipulação direta do estado do proxy.

Atualizando a Implementação

Para atualizar o contrato, precisamos apenas mudar o endereço da implementação no proxy. Veja como você pode adicionar uma função de atualização:

function upgradeTo(address newImplementation) external {
    // Para segurança, você pode querer adicionar controle de acesso aqui
    implementation = newImplementation;
}

Compreendendo Proxies Mínimos

Proxies mínimos são uma versão mais eficiente em termos de gás de contratos proxy. Eles utilizam a opcode CREATE2 e um modelo de bytecode para implantar novos contratos proxy que apontam para a mesma implementação. Isso ajuda a economizar custos de implantação quando a mesma lógica precisa ser reutilizada.

Aqui está como implementar um proxy mínimo:

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

contract MinimalProxy {
    address public implementation;

    constructor(address _implementation) {
        implementation = _implementation;
    }

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

        assembly {
            // Encaminha a chamada para o contrato de implementação
            calldatacopy(0, 0, calldatasize())
            let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)
            let size := returndatasize()
            returndatacopy(0, 0, size)
            switch result
            case 0 { revert(0, size) }
            default { return(0, size) }
        }
    }
}

Para implantar proxies mínimos de forma eficiente, você pode usar um contrato de fábrica que gera múltiplos proxies apontando para a mesma implementação.

Fábrica de Proxies Mínimos

Aqui está um exemplo de uma fábrica que cria proxies mínimos:

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

contract MinimalProxyFactory {
    function createMinimalProxy(address implementation) external returns (address) {
        bytes20 targetBytes = bytes20(implementation);

        // Cria o proxy mínimo usando o padrão EIP-1167
        bytes memory bytecode = abi.encodePacked(
            hex"3d602d80600a3d393df3602052",
            targetBytes,
            hex"5af43d82803e903d91602b57fd5bf3"
        );

        address proxy;
        assembly {
            proxy := create(0, add(bytecode, 32), mload(bytecode))
            if iszero(proxy) {
                revert(0, 0)
            }
        }

        return proxy;
    }
}

Partes Principais da Fábrica:

  • A função createMinimalProxy gera o bytecode para o proxy mínimo utilizando assembly inline.
  • Ela utiliza a instrução create para implantar o contrato proxy mínimo.

Conclusão

Contratos proxy e proxies mínimos são ferramentas poderosas no ecossistema de desenvolvimento de contratos inteligentes Ethereum. Eles permitem que os desenvolvedores mantenham e atualizem contratos com facilidade.

Entender como implementar e gerenciar esses contratos ajudará você a construir aplicações descentralizadas mais flexíveis e robustas. Sempre lembre-se de incorporar mecanismos adequados de controle de acesso em suas funções de atualização para garantir a segurança de seus contratos!

Video

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

Thank you for voting!