Skip to content

Comparando Padrões de Capacidade de Atualização do Solidity

Comparando Padrões de Capacidade de Atualização do Solidity

Proxy Transparente, Armazenamento Eterno (Eternal), Armazenamento Diamante (Diamond), Multifacetas.

Pode-se encontrar esses termos ao tentar construir contratos de alta qualidade que possam resistir ao teste do tempo, atualizando conforme necessário.

Este resumo tem como objetivo desmembrá-los, como eles interagem entre si e como você pode começar a usá-los. Ele não tem como objetivo fornecer um tutorial aprofundado sobre cada um deles.

Proxy transparente

https://docs.openzeppelin.com/upgrades-plugins/1.x/

Essa é a abordagem usada pelo plug-in de atualizações do OpenZeppelin. É uma solução simples para capacidade de atualização, onde você pode começar a implantar contratos inteligentes atualizáveis ​​com conhecimento mínimo da estrutura de armazenamento do Solidity ou proxies, pois o plug-in faz um ótimo trabalho ao abstraí-los.

Ao implantar um contrato atualizável usando o plug-in, o que realmente acontece nos bastidores é que um contrato de proxy é implantado, responsável por armazenar todo o estado. O contrato de implementação que você mesmo escreveu está oculto atrás do proxy e não possui nenhum estado.

Você pode estar se perguntando, como a implementação pode não manter nenhum estado? Eu tenho variáveis ​​de armazenamento na minha implementação. Bem, o uso da delegatecall pelo proxy significa que quaisquer variáveis ​​de armazenamento declaradas em um contrato de implementação são indicações para o local de armazenamento real que é o proxy.

Isso significa que você pode alterar o contrato de implementação mantendo o estado, pois o contrato de implementação é apenas um monte de indicações de armazenamento, enquanto o armazenamento em si está no contrato de proxy.

Uma limitação da abordagem de proxy transparente é que deve-se tomar cuidado ao atualizar contratos, pois quaisquer variáveis ​​de armazenamento adicionadas ao contrato de implementação durante uma atualização não devem se sobrepor aos contratos de implementação anteriores.

Isso pode ser difícil, pois a EVM normalmente usa um espaço de armazenamento linear, onde as variáveis ​​declaradas sequencialmente terão locais sequenciais no armazenamento. Felizmente, o plug-in OpenZeppelin evita erros detectando esses erros antecipadamente. Portanto, esta é a melhor abordagem se o seu caso de uso não for extremamente complexo.

Armazenamento Eterno (Eternal)

https://fravoll.github.io/solidity-patterns/eternal_storage.html

Em março de 2020, foi lançada a versão 0.6.4 do Solidity, permitindo que você escolha onde armazenar suas variáveis ​​de armazenamento.

O que isso significa é que você não precisa mais se preocupar com o espaço de armazenamento linear se não quiser. Esse espaço de armazenamento linear é problemático ao fazer atualizações, pois você precisa seguir regras cuidadosas ao adicionar novas variáveis ​​de armazenamento. Em vez disso, você pode usar o armazenamento eterno, que permite armazenar em qualquer lugar.

Geralmente, a abordagem é que o local que você escolhe para armazenar suas variáveis ​​é o hash da 'chave' que você atribui à variável. Por exemplo, se eu quiser armazenar um contador em algum lugar, posso dar a chave 'contador'. Deve-se tomar cuidado ao usar uma chave exclusiva, pois conflitos com outros nomes de chave resultariam em indicações de armazenamento substituindo umas às outras.

Exemplo de configuração e obtenção:

// definir
EternalStorage.setUintValue('MY_SPECIAL_KEY', 10);
// obter
uint256 val = EternalStorage.getUintValue('MY_SPECIAL_KEY');

Armazenamento Diamante (Diamond)

https://eips.ethereum.org/EIPS/eip-2535#diamond-storage

Isso usa um padrão semelhante ao armazenamento Eterno, mas usa estruturas para acessar as variáveis ​​necessárias em vez de 'chaves' baseadas em string.

Por exemplo, você pode fazer algo assim para definir e obter um inteiro:

DiamondStorage.MySpecialStruct storage mySpecialStruct = DiamondStorage.mySpecialStruct();
// definir
mySpecialStruct.myValue = 10;
// obter
uint256 val = mySpecialStruct.myValue;

O benefício do padrão Diamante é que a sua struct pode ter vários valores e só precisa ser buscada uma vez, enquanto no padrão Eterno, cada item que você deseja definir ou obter precisa ser feito explicitamente e não pode ser agrupado facilmente.

Além disso, você precisa ter um setter/getter explícito para cada tipo que deseja suportar ao usar o padrão Eterno. Além disso, o padrão Diamante pode ser estendido para usar o padrão AppStorage, o que reduz a versatilidade de obtenção e configuração.

Diamantes (proxy multifacetado)

https://blog.zkga.me/dark-forest-and-the-diamond-standard

https://eips.ethereum.org/EIPS/eip-2535

Lembre-se que com um proxy transparente, temos um único proxy, com um único contrato de implementação. Com o padrão diamante, você pode ter um único ponto de contrato de proxy para várias implementações. Por que isso é útil?

Bem, vamos imaginar um contrato ERC20 que usa o padrão diamante. Você poderia ter a funcionalidade de cunhar e a funcionalidade de transferência em contratos separados. Eles seriam costurados por um único contrato diamante.

Na realidade, não faríamos isso, porque a cunhagem e a transferência são logicamente semelhantes e, portanto, agrupadas no mesmo contrato.

Mas se, por exemplo, você quisesse toda a funcionalidade de um ERC20 e toda a funcionalidade de algum outro contrato arbitrário sem usar herança, exposta por meio de um único endereço de contrato, você poderia usar um contrato Diamante para fazer isso. Isso é ótimo para grandes projetos em Solidity que têm vários contratos que precisam ser expostos por um único endereço de contrato.


Artigo escrito por Zoraiz Mahmood e traduzido por Marcelo Panegali