Padrão Proxy Transparente
O modelo transparente separa claramente as responsabilidades: o proxy gerencia o acesso e as atualizações, enquanto o contrato de implementação contém a lógica de negócio.
contract ProxyTransparente {
address private enderecoLogica;
address private enderecoGestor;
fallback() external payable {
require(msg.sender != enderecoGestor, "Gestor nao pode chamar");
(bool sucesso, ) = enderecoLogica.delegatecall{value: msg.value}(msg.data);
if (!sucesso) {
assembly {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
}
function migrar(address novaLogica) external {
if (msg.sender != enderecoGestor) {
// Redireciona para fallback se nao for gestor
(bool s, ) = enderecoLogica.delegatecall(msg.data);
require(s);
}
enderecoLogica = novaLogica;
}
}
Padrão UUPS (Universal Upgradeable Proxy Standard)
Aqui o proxy é minimalista, contendo apenas o endereço da implementação. A lógica de upgrade vive no próprio contrato de implementação, que através do delegatecall modifica o estado do proxy.
// Proxy UUPS - Estrutura simplificada
contract ProxyUUPS {
address public implementacao;
fallback() external payable {
address alvo = implementacao;
require(alvo != address(0), "Implementacao invalida");
assembly {
let ptr := mload(0x40)
calldatacopy(ptr, 0, calldatasize())
let resultado := delegatecall(gas(), alvo, ptr, calldatasize(), 0, 0)
let tamanho := returndatasize()
returndatacopy(ptr, 0, tamanho)
switch resultado
case 0 { revert(ptr, tamanho) }
default { return(ptr, tamanho) }
}
}
}
// Mixin para contratos UUPS
abstract contract ModuloUpgradeUUPS {
address internal contaAdmin;
address internal slotImplementacao;
function _atualizar(address proximaVersao) internal {
require(msg.sender == contaAdmin, "Sem permissao");
// Modifica o slot do proxy (delegatecall context)
slotImplementacao = proximaVersao;
}
}
Padrão Diamante
Esta arquitetura avançada permite que um único proxy delegue para múltiplos contratos de implementação (facetas) com base nos seletores de função, superando limites de tamanho e permitindo modularidade extrema.
Comparação de Características
- Proxy Transparente: Maior simplicidade conceitual, mas custo de gas elevado em cada chamada devido à verificação de permissões.
- UUPS: Gas otimizado para operações, porém exige cuidado para não perder a capacidade de upgrade.
- Diamante: Escalabilidade máxima e flexibilidade, ao custo de complexidade significativa.
Soluções da OpenZeppelin
A biblioteca fornece implementações prontas:
TransparentUpgradeableProxy- Proxy transparente com segurança aprimoradaUUPSUpgradeable- Mixin para contratos seguindo o padrão UUPS
Detalhe Crucial: Inicialização
Em contratos upgradeáveis, evite constructors. Utilize funções initialize com modificadores initializer. O constructor executa apenas no contexto do contrato de implmeentação, enquanto o estado efetivo pertence ao proxy. Inicializar via constructor consome gas desnecessariamente e não afeta o estado usado pelos usuários.