
Explore a importância dos contratos inteligentes atualizáveis em aplicações descentralizadas. Saiba como eles permitem atualizações, correções de bugs e maior segurança por meio do padrão proxy.

Smart contracts mudaram a forma como pensamos sobre aplicações descentralizadas ao introduzir programas autoexecutáveis que eliminam a necessidade de intermediários. Eles são executados de forma transparente em redes blockchain descentralizadas, oferecendo benefícios como ausência de necessidade de confiança e maior segurança. No entanto, sua imutabilidade, antes considerada um ponto forte, representa um desafio significativo: uma vez que um smart contract é implantado, ele não pode ser alterado. Essa rigidez dificulta a implementação de atualizações necessárias ou a correção de bugs.
Neste post, vamos explorar o conceito de smart contracts atualizáveis, explicar por que eles são essenciais e detalhar como funcionam, com foco especial em sua implementação na Linea zkEVM.
Smart contracts atualizáveis resolvem as limitações dos smart contracts tradicionais ao permitir modificações e melhorias após a implantação. Enquanto os smart contracts tradicionais são conhecidos por sua imutabilidade, os contratos atualizáveis combinam a segurança da imutabilidade com a flexibilidade de adaptação. Isso permite que os desenvolvedores implementem atualizações sem interromper os usuários ou exigir que eles migrem para um novo contrato.
Nos contratos tradicionais, bugs ou falhas de segurança são permanentes após a implantação. Os contratos atualizáveis resolvem isso ao permitir atualizações após a implantação, solucionando problemas importantes como:
1. Correção de bugs e vulnerabilidades de segurança: Os desenvolvedores podem corrigir vulnerabilidades após a implantação, protegendo os ativos dos usuários.
2. Adaptabilidade: Contratos atualizáveis permitem integração fluida com novos protocolos, mantendo as aplicações em dia com os avanços tecnológicos.
3. Melhorias de segurança: Com as ameaças cibernéticas em constante evolução, esses contratos facilitam a implementação de novas medidas de segurança sem a necessidade de reimplantar o contrato inteiro.
4. Flexibilidade de governança: Organizações Autônomas Descentralizadas (DAOs) podem usar votação comunitária para gerenciar o processo de atualização, garantindo transparência e participação das partes interessadas.
5. Atualizações simplificadas: Ao contrário dos contratos tradicionais, que exigem reimplantação complexa e migração de dados, os contratos atualizáveis simplificam as atualizações para uma experiência mais eficiente e amigável ao usuário.
Para criar um smart contract atualizável, o método mais comum é o padrão proxy. Essa abordagem separa a lógica do contrato de seus dados, permitindo que os desenvolvedores atualizem a lógica enquanto mantêm o mesmo armazenamento de dados.
Veja como funciona:
Contrato Proxy: Este contrato mantém o armazenamento (variáveis de estado) e atua como interface para os usuários.
Contrato de Implementação: Este contrato contém o código executável (lógica de negócios).
Delegação: O contrato proxy delega chamadas de função ao contrato de implementação usando o delegatecall do Ethereum. Isso permite que o proxy execute o código da implementação enquanto mantém o mesmo armazenamento.
Quando uma atualização é necessária, o contrato proxy é direcionado a um novo contrato de implementação, permitindo atualizações de lógica sem alterar o armazenamento do contrato ou interromper os usuários. Veja um exemplo usando um contrato proxy de DAO para ilustrar esse processo:
Dois aspectos importantes a observar são o slot de armazenamento para o endereço do contrato de implementação e o slot de armazenamento para o endereço do administrador. Esses são identificadores deliberados e únicos usados para calcular um slot de armazenamento para o endereço de implementação dentro do contrato proxy.
Ao usar o padrão proxy com delegatecall (vamos analisar essa função mais de perto em breve), o contrato de implementação opera no contexto do armazenamento do proxy. Isso significa:
Espaço de armazenamento compartilhado: Ambos os contratos compartilham o mesmo layout de armazenamento.
Risco de sobrescrita: Sem um gerenciamento cuidadoso, variáveis no contrato proxy podem ser sobrescritas pelo contrato de implementação.
Ao usar um identificador único com hash:
Isolamento: Garante que os slots de armazenamento usados pelo proxy dificilmente entrarão em conflito com quaisquer variáveis no contrato de implementação.
Consistência: O mesmo slot de armazenamento é usado de forma consistente em todo o contrato onde o endereço de implementação for necessário.
Outro aspecto importante é o construtor do contrato proxy. Ele inclui um parâmetro _implementation, o que significa que o contrato de implementação deve ser implantado primeiro, e seu endereço é passado como argumento ao implantar o contrato proxy.
Agora temos as funções fallback e receive, que são funções do Solidity e desempenham papéis fundamentais no encaminhamento de chamadas e transferências de Ether para o contrato de implementação. Elas lidam com situações em que um contrato recebe chamadas ou fundos que não correspondem a nenhuma de suas funções explicitamente definidas. Veja uma explicação detalhada do que essas funções fazem:
Mecanismo de fallback: A função fallback no Solidity é uma função especial que é executada em duas condições principais:- Quando uma função que não existe no contrato é chamada.
Quando dados são enviados ao contrato sem corresponder a nenhuma assinatura de função.
Delegação: No contexto do contrato DAOProxy, a função fallback delega todas essas chamadas ao contrato de implementação atual.
Como funciona no DAOProxy:
Tratamento de chamadas de função:- Quando um usuário chama uma função não definida no contrato proxy, a função fallback é acionada.
Isso é comum porque o contrato proxy contém principalmente funções administrativas como upgradeTo e changeAdmin, em vez das funções de lógica de negócios do contrato de implementação.
Delegação para a implementação:- A função fallback chama _delegate(implementation()), encaminhando a chamada para o contrato de implementação.
_delegate é uma função interna que usa delegatecall para executar o código da implementação enquanto opera no contexto de armazenamento do proxy.
Contexto de execução:- Todas as mudanças de estado ocorrem no armazenamento do proxy.
msg.sender e msg.value permanecem inalterados, preservando o contexto do chamador original.
Recepção de Ether: A função receive, introduzida no Solidity 0.6.0, é executada quando um contrato recebe Ether sem nenhum dado adicional (ou seja, quando msg.data está vazio).
Delegação de transferências de Ether: No contrato DAOProxy, transferências simples de Ether são delegadas ao contrato de implementação.
Como funciona no DAOProxy:
Tratamento de transferências de Ether:- Quando alguém envia Ether para o endereço do contrato proxy sem dados, a função receive é acionada.
Isso ocorre por meio de uma transação que especifica apenas o endereço to e o valor (quantidade de Ether), sem chamar nenhuma função específica.
Delegação para a implementação:- Assim como a função fallback, receive chama _delegate(implementation()), encaminhando a transferência de Ether para o contrato de implementação.
O contrato de implementação então define como lidar com o Ether recebido (por exemplo, atualizando saldos, emitindo eventos).
Agora temos a função _delegate, que permite ao proxy encaminhar chamadas de função ao contrato de implementação enquanto preserva o contexto da chamada original. Isso permite que a lógica do contrato seja atualizada ao longo do tempo sem alterar o endereço do contrato ou interromper seu estado.
As demais funções são getters e setters que nos ajudam a gerenciar as variáveis de estado do nosso contrato proxy. Para isso, usamos a palavra-chave assembly, que permite aos desenvolvedores incluir código assembly de baixo nível diretamente em seus contratos. Esse recurso fornece acesso direto às instruções da Ethereum Virtual Machine (EVM), permitindo um controle mais preciso sobre o comportamento do contrato, otimização do consumo de gas e execução de operações que não estão diretamente disponíveis ou são menos eficientes no código de alto nível do Solidity.
Agora, a grande questão é: como interagimos com nosso smart contract de implementação usando esse contrato proxy?
Para isso, precisamos da ABI do smart contract de implementação. Como o proxy delega as chamadas ao contrato de implementação, você precisa da ABI dele para saber quais funções pode chamar. Além disso, usaremos o endereço do contrato proxy ao interagir com a blockchain, e não o contrato de implementação. O proxy mantém o estado e delega chamadas à implementação, portanto, todas as interações devem passar pelo proxy.
Vamos imaginar que temos um projeto JavaScript e estamos usando Ethers.js para interagir com a blockchain. Veja como ficaria:
Dessa forma, estamos chamando a função imaginária setValue em nosso contrato de implementação usando nosso contrato proxy.
Agora que estamos discutindo contratos atualizáveis, outra pergunta importante surge: como faço para atualizar meus contratos?
Primeiro, apenas o administrador (a pessoa que implantou o contrato proxy) pode atualizar o contrato de implementação. Para isso, o administrador deve primeiro implantar uma nova versão do contrato de implementação na blockchain. Depois disso, ele deve chamar a função upgradeTo no contrato proxy, passando o novo endereço do contrato como parâmetro. Ficaria assim:
await proxyContract.connect(adminSigner).upgradeTo(newImplementationAddress);Smart contracts atualizáveis são importantes no desenvolvimento blockchain porque permitem mudanças e melhorias após a implantação. Ao aproveitar o padrão proxy, os desenvolvedores podem introduzir novos recursos de forma fluida, corrigir bugs e responder a requisitos em constante mudança sem interromper a experiência do usuário ou migrar ativos.
No entanto, grandes poderes trazem grandes responsabilidades. É fundamental abordar contratos atualizáveis com um entendimento profundo de sua mecânica, implicações de segurança e melhores práticas. Garantir a consistência do layout de armazenamento, implementar controles de acesso robustos e testar as atualizações de forma abrangente são etapas essenciais para se proteger contra riscos potenciais.
Ao se aventurar na construção de contratos atualizáveis, considere utilizar ferramentas consolidadas como o OpenZeppelin's Upgrades Plugin para simplificar o processo e aumentar a segurança. Bons códigos!
Traduzido por IA. Pode conter erros. Por favor, verifique sempre as informações.