Nesse artigo iremos aprender o que é um Proxy e para que ele server na Blockchain, como implantá-lo e suas aplicações.
O que é um Proxy e para que ele serve na Blockchain?
Um Proxy, resumidamente, é uma ponte entre versões de um contrato implantado na blockchain.
Tá ok, mas para que serve? O proxy serve para fazer uma atualização de um contrato já implantado, ou seja, mesmo após implantar seu contrato inteligente, você poderá atualizá-lo com novas funcionalidades, corrigir possíveis bugs no contrato, alterar lógicas já implantadas, resumindo, o proxy é como se fosse um versionador de código para os contratos da blockchain.
Se você implantar um novo contrato sem ter implantando um Proxy junto, você não poderá realizar atualizações, por isso, antes de implementar um contrato, analise se você precisará atualizar o contrato ou ele será um contrato já 100% planejado e estático, sem necessidade de alterações.
Exemplo de Proxy básico (MinimalProxy)
Se você tiver um contrato que será implantado várias vezes, use o contrato de proxy mínimo para implantá-los de forma econômica.
// SPDX-License-Identifier: MITpragma solidity ^0.8.13;// Código original// https://github.com/optionality/clone-factory/blob/master/contracts/CloneFactory.solcontract MinimalProxy {function clone(address target) external returns (address result) {// converte o endereço para 20 bytesbytes20 targetBytes = bytes20(target);// código atual //// 3d602d80600a3d3981f3363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe5af43d82803e903d91602b57fd5bf3// código de criação //// copie o código de tempo de execução na memória e retorne-o// 3d602d80600a3d3981f3// código de tempo de execução //// código da delegatecall para endereço// 363d3d373d3d3d363d73 address 5af43d82803e903d91602b57fd5bf3assembly {/*lê os 32 bytes de memória começando no ponteiro armazenado em 0x40Em solidity, o slot 0x40 na memória é especial: contém o "ponteiro de memória livre"que aponta para o final da memória atualmente alocada.*/let clone := mload(0x40)// armazenar 32 bytes na memória começando em "clone"mstore(clone,0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)/*| 20 bytes |0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000^ponteiro*/// armazenar 32 bytes na memória começando em "clone" + 20 bytes// 0x14 = 20mstore(add(clone, 0x14), targetBytes)/*| 20 bytes | 20 bytes |0x3d602d80600a3d3981f3363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe^ponteiro*/// armazenar 32 bytes na memória começando em "clone" + 40 bytes// 0x28 = 40mstore(add(clone, 0x28),0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)/*| 20 bytes | 20 bytes | 15 bytes |0x3d602d80600a3d3981f3363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe5af43d82803e903d91602b57fd5bf3*/// criar novo contrato// enviar 0 Ether// o código começa no ponteiro armazenado em "clone"// tamanho do código 0x37 (55 bytes)result := create(0, clone, 0x37)}}}
Exemplo de Proxy atualizável (Upgradeable Proxy)
Exemplo de contrato de proxy atualizável. Nunca use isso em produção, pois você deve desenvolver seu próprio Proxy.
Este exemplo mostra como usar delegatecalle
para retornar dados quando fallback
é chamado.
// SPDX-License-Identifier: MITpragma solidity ^0.8.13;contract Proxy {address public implementation;function setImplementation(address _imp) external {implementation = _imp;}function _delegate(address _imp) internal virtual {assembly {// calldatacopy(t, f, s)// copiar s bytes de calldata na posição f para mem na posição tcalldatacopy(0, 0, calldatasize())// delegatecall(g, a, in, insize, out, outsize)// - chama o contrato no endereço a// - com entrada mem[in…(in+insize))// - fornecendo gas g// - e retorna mem[out…(out+outsize))// - retornando 0 em caso de erro e 1 em caso de sucessolet result := delegatecall(gas(), _imp, 0, calldatasize(), 0, 0)// returndatacopy(t, f, s)// copia s bytes de returndata na posição f para mem na posição treturndatacopy(0, 0, returndatasize())switch resultcase 0 {// revert(p, s)// terminar a execução, reverte as alterações de estado, return data mem[p…(p+s))revert(0, returndatasize())}default {// return(p, s)// fim da execução, return data mem[p…(p+s))return(0, returndatasize())}}}fallback() external payable {_delegate(implementation);}}// Primeira versão do contratocontract V1 {address public implementation;uint public x;function inc() external {x += 1;}}// Segunda versão do contratocontract V2 {address public implementation;uint public x;function inc() external {x += 1;}function dec() external {x -= 1;}}