WEB3DEV

Cover image for Uma Introdução Completa ao Desenvolvimento de Contratos Inteligentes na Scroll zkEVM
Adriano P. Araujo
Adriano P. Araujo

Posted on

Uma Introdução Completa ao Desenvolvimento de Contratos Inteligentes na Scroll zkEVM

### Uma introdução técnica à Scroll zkEVM para desenvolvedores de blockchain

Escrevi este artigo como uma introdução técnica à Scroll zkEVM para desenvolvedores de contratos inteligentes que desejam experimentar a rede.

Neste artigo, abordaremos o seguinte:

  1. Aprender sobre a Scroll zkEVM e como começar com Alpha testnet.

  2. Configurar um ambiente de desenvolvimento com o Foundry e escrever um contrato inteligente que distribui ETH na Alpha testnet com base em uma função de geração de números pseudoaleatórios.

  3. Ajustar o contrato inteligente para se adequar às especificações exatas necessárias para a Alpha testnet.

  4. Implantar nosso contrato inteligente com a ajuda de um script Solidity.

  5. Verificar nosso contrato inteligente na Alpha testnet diretamente a partir da linha de comando do Foundry.

Ao final deste artigo, você aprenderá como implantar seus contratos inteligentes na Alpha testnet da Scroll com o Foundry.

O que é a Scroll zkEVM?

A Scroll zkEVM é uma solução de blockchain de camada 2 recém lançada, projetada especificamente para aumentar a escalabilidade do Ethereum.

A característica distintiva da Scroll e de plataformas semelhantes, como a Polygon zkEVM, é o uso de provas de conhecimento zero (ZK proofs) que funcionam agrupando grandes grupos de transações e as gravando na Ethereum de uma só vez, reduzindo significativamente as taxas de transação em comparação com o processamento de cada transação individualmente na Ethereum.

A melhor coisa sobre as zkEVMs, como a Scroll, é que um desenvolvedor de contratos inteligentes não precisa entender a tecnologia subjacente de ZK para implantar contratos inteligentes compatíveis com a EVM em uma solução muito mais barata e escalável.

Pré-requisitos

  • Certifique-se de ter algum ETH da rede Goerli em sua carteira. Sim, eu sei que a Goerli está descontinuada, mas você precisará dela por enquanto.

  • Um conhecimento básico de blockchains e alguma experiência com Solidity.

  • Se você nunca trabalhou com o Foundry antes, recomendo que verifique o README deste repositório no GitHub, que criei para um workshop há algum tempo.

  • Não é necessário assistir ao vídeo inteiro; basta dar uma olhada rápida no arquivo README se você nunca trabalhou com o Foundry antes.

Primeiras coisas primeiro: a Ponte!

Sem entrar em muitos detalhes específicos de ZK, saiba que o ETH na rede Scroll espelha o ETH na rede principal da Ethereum. O espelhamento significa que a equipe da Scroll implantou contratos inteligentes de ponte na rede Scroll (Alpha testnet por enquanto) e na rede ETH correspondente (Goerli testnet por enquanto).  

Portanto, para obter ETH para pagar pelas taxas de gás na Alpha testnet, você precisa depositar algum ETH Goerli no contrato de ponte na Goerli.

O espelhamento também funciona no sentido oposto.

Para começar:

  1. Acesse a interface de usuário (UI) da Scroll que permite interagir com o contrato da ponte.

  2. Conecte sua carteira Metamask à página da web.

  3. Certifique-se de estar enviando ETH para a rede Scroll, não o contrário. Siga a UI (é realmente intuitiva) e confirme a ponte.

Após a confirmação, talvez seja necessário aguardar de 30 a 45 minutos antes de receber ETH na Alpha testnet. Portanto, você precisa ter paciência por um tempo.

Você está pronto para começar assim que tiver algum ETH na Alpha testnet!

Inicializando um Projeto Foundry

O Foundry é um dos frameworks de desenvolvimento de contratos inteligentes mais recentes a entrar no cenário e está se tornando cada vez mais popular.

Para instalar o Foundry, siga os comandos abaixo ou consulte o Foundry-book.

Execute este comando para baixar o foundryup


curl -L https://foundry.paradigm.xyz | bash

Enter fullscreen mode Exit fullscreen mode

Em seguida, reinicie seu terminal e, em seguida, instale o Foundry executando:


foundryup

Enter fullscreen mode Exit fullscreen mode

Após ter instalado tudo, abra um novo terminal em um novo diretório. Você pode inicializar um novo projeto Foundry usando o seguinte comando:


forge init

Enter fullscreen mode Exit fullscreen mode

Algumas observações:

  • Todos os contratos inteligentes são criados dentro do diretório src por padrão.

  • O comando forge install pode instalar novos pacotes como submódulos Git dentro do diretório lib.

  • Por padrão, todos os contratos de teste são definidos no diretório de test  e geralmente têm a extensão .t.sol, o que significa que um arquivo de contrato chamado hello.sol terá um arquivo chamado hello.t.sol, conforme a convenção usual. Você pode executar todos os arquivos de teste com o comando forge test.

  • Você pode controlar o comportamento do Foundry configurando o arquivo foundry.toml. Esta página no Github fornece uma referência completa para o arquivo toml. Também compilei um gist que contém uma lista de valores padrão.

  • O diretório de scripts contém todos os scripts de implantação e execução para o seu projeto Foundry.

Sempre que você fizer alterações no código, pode compilar todos os contratos inteligentes usando:


forge build

Enter fullscreen mode Exit fullscreen mode

Opcodes e Bytecode: Um Curso Rápido

Antes de escrever o contrato inteligente em si, vamos examinar alguns conceitos que serão úteis para entender o panorama.

Portanto, para aqueles que não sabem, a EVM não executa o Solidity nem qualquer outra linguagem de desenvolvimento de contratos inteligentes. A EVM nem mesmo tem conhecimento da existência dessas linguagens, nem se importa. Isso se aplica à Scroll zkEVM também.

O que acontece é que qualquer código de contrato inteligente é decomposto em um conjunto de instruções executáveis pela EVM conhecido como Bytecode, que é o que a EVM realmente executa.

Ok, mas o que são opcodes, e por que nos importamos? Lembra-se de quando eu disse que o bytecode é um 'conjunto de instruções'?

Bem, cada opcode é uma instrução que, combinada com outros opcodes, compõe o bytecode de qualquer contrato inteligente. Os opcodes são o nível mais baixo de computação que você precisa lidar como desenvolvedor de blockchain. Uma linguagem de contrato inteligente é uma abstração construída sobre esses opcodes.

Por exemplo, se você já usou block.number em Solidity, você invoca o opcode NUMBER. Uma referência completa para todos os opcodes suportados pela EVM (para não ser confundido com a zkEVM da Scroll) pode ser encontrada no site da Ethereum Foundation.

Permita-me enfatizar o significado disso com um exemplo claro.

Dê uma olhada neste contrato inteligente simples:


// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;



contract Adder {

    function add(uint256 a, uint256 b) public pure returns (uint256) {

        uint256 sum = a + b;

        return sum;

    }

}

Enter fullscreen mode Exit fullscreen mode

Nada mais do que uma função que retorna a soma de dois números. Como se parecerão as instruções de opcode equivalentes? Basta dar uma olhada neste Gist que eu fiz. Você também pode verificar este contrato no Etherscan para verificar o Bytecode e os opcodes por conta própria.

Esta seção pode parecer um desvio indesejado, mas acredite em mim, fará muito mais sentido nas seções seguintes.

Escrevendo o Contrato Inteligente

Finalmente, vamos concluir isso.

Vá para o diretório src e crie um novo arquivo chamado ScrollTutorial.sol.

Cole o código a seguir nele:


// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;



contract Dispenser {

    // Mapeamento para rastrear os endereços que já fizeram saques

    mapping(address => bool) public hasWithdrawn;



    function withdraw() public {

        require(

            !hasWithdrawn[msg.sender],

            "Você já fez um saque, desculpe!. Tente novamente de um endereço diferente!"

        );



        require(

            address(this).balance > 0.5 ether,

            "Não há fundos suficientes no contrato no momento"

        );



        uint256 randomNumber = uint256(

            keccak256(

                abi.encodePacked(

                    blockhash(block.number - 100),

                    block.prevrandao,

                    block.coinbase

                )

            )

        ) % 2;



        // Verifique se o número aleatório é par

        require(

            randomNumber == 0,

            "Desculpe, mas o hash gerado foi um número ímpar. Tente novamente de um endereço diferente!"

        );



        // Defina a sinalização hasWithdrawn como verdadeira para este endereço

        hasWithdrawn[msg.sender] = true;



        // Transfira 0,5 ether para o endereço

        payable(msg.sender).transfer(0,5 ether);

    }



    function deposit() public payable {}

}

Enter fullscreen mode Exit fullscreen mode

Este é um contrato inteligente direto. Vamos entender o código rapidamente:

  1.  Queremos que cada endereço possa sacar apenas 0,5 ETH uma vez; o mapeamento hasWithdrawn rastreia todos os endereços que sacaram ETH com sucesso.

  2.  A função deposit é uma função simples que aceita pagamentos e permite que nosso contrato receba ETH.

  3. A função withdraw é onde toda a mágica acontece.

 Vamos examinar a função withdraw em mais detalhes.

  • As duas primeiras declarações require no início da função garantem que cada endereço possa sacar apenas uma vez e que haja ETH suficiente no contrato para a transação.

  • A variável randomNumber é um número pseudoaleatório que geramos usando alguns valores em tempo real. Vamos examiná-los com mais detalhes em breve.

  • A função abi.encodePacked pega todos os argumentos que ela recebe e os concatena em uma única sequência de bytes.

  • A função keccak256 gera o hash Keccak-256 de qualquer entrada que ela recebe. Fazemos o módulo do valor que esta função gera com 2 para verificar se o hash gerado é par ou não.

  • Em seguida, pagamos o ETH se o hash for par e atualizamos o mapeamento.

 Para compilar o contrato inteligente, execute o seguinte comando no seu terminal:


forge build

Enter fullscreen mode Exit fullscreen mode

Dica #1: PREVRENDAO é um opcode relativamente novo que substitui o opcode DIFFICULTY e é suportado apenas pelas versões do Solidity 0.8.18 e superiores. Certifique-se de configurar a versão do Solidity de acordo.

Problemas com Nosso Código

Nossa, há muito o que explorar aqui!

Este código está longe de ser adequado para produção, mas vamos discutir um problema mais fundamental primeiro.

Lembre-se que eu linquei uma lista de opcodes que a EVM suporta.

Infelizmente, até o momento, a zkEVM da Scroll NÃO suporta todos esses opcodes.

Ela suporta a maioria deles, mas se você está desenvolvendo um contrato inteligente para uma zkEVM (qualquer zkEVM), você deve entender as diferenças no suporte a opcodes entre a rede e a EVM.

Reveja o código e veja os valores que usamos para gerar nosso número aleatório. Vamos dar uma olhada no que não funcionará em nosso contrato inteligente:

Opcode Equivalente em Solidity Diferença comportamental
BLOCKHASH blockHash(BlockNum) Usamos o hash de um bloco 100 atrás do mais recente como parte do hash do nosso número aleatório. No entanto, a Scroll zkEVM NÃO oferece suporte para buscar o hash de um bloco tão antigo. Se o número do bloco exceder a faixa suportada, um 0 será retornado. Suporte da ethereum: [LATEST-1] - [LATEST-256] Suporte da Scroll zkEVM: [LATEST-1]
PREVRANDAO block.prevrendao Pense no PREVRENDAO como um número de origem pseudo-aleatório, você pode ler mais sobre isso aqui.Comportamento do Ethereum: Retorna um número pseudoaleatório<br><br>Comportamento do zkEVM Scroll: Retorna 0, sem aleatoriedade<br><br>
COINBASE block.coinbase Comportamento da EVM: Retorna o endereço do validador do bloco.. Este número é pseudo-aleatório, uma vez que os validadores de ETH são escolhidos aleatoriamente.Comportamento do Scroll: Retorna apenas um único endereço por enquanto, sem aleatoriedade.

Então, essencialmente, todos os três valores que concatenamos para obter nosso randomNumber têm um comportamento pseudoaleatório na EVM. No entanto, eles falham em funcionar ou retornam um valor constante, o que os torna inúteis para nossos fins.

Mas honestamente, mesmo que a Scroll suportasse todos esses opcodes, este contrato ainda não seria digno de produção. Nenhum contrato inteligente sério depende da combinação de valores de bloco para gerar um número aleatório. Para implantar um contrato inteligente que faça uso de aleatoriedade, considere o serviço VRF da Chainlink.

Ajustando o Contrato Inteligente

Já que a Chainlink não é compatível com a Scroll, recorreremos à pseudoaleatoriedade.

Desta vez, faremos uma pequena alteração na função withdraw de duas maneiras:

  1. Agora permitimos que o usuário passe um argumento ao chamar a função de saque. Usaremos esse argumento como parte do hash do randomNumber, para que o usuário tenha algum controle sobre o hash gerado.

  2. Substituímos os outros dois valores pelo hash do bloco anterior e o carimbo de data/hora do bloco atual. Não é uma boa fonte de aleatoriedade, mas por enquanto, esta é a melhor solução que conheço.

Sinta-se à vontade para me esclarecer se tiver uma solução melhor.


// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;



contract Dispenser {

    // Mapeamento para rastrear os endereços que já fizeram saques

    mapping(address => bool) public hasWithdrawn;



    function withdraw(uint seedValue) public {

        require(

            !hasWithdrawn[msg.sender],

            "Você já fez um saque, desculpe!. Tente novamente de um endereço diferente!"

        );



        require(

            address(this).balance > 0.5 ether,

            "Não há fundos suficientes no contrato no momento"

        );



        uint256 randomNumber = uint256(

            keccak256(

                abi.encodePacked(

                    blockhash(block.number - 1),

                    block.timestamp,

                    seedValue

                )

            )

        ) % 2;



        // Verifique se o número aleatório é par

        require(

            randomNumber == 0,

            "Desculpe, mas o hash gerado foi um número ímpar."

        );



        // Defina a sinalização hasWithdrawn como verdadeira para este endereço

        hasWithdrawn[msg.sender] = true;



        // Transfira 0,5 ether para o endereço

        payable(msg.sender).transfer(0.5 ether);

    }



    function deposit() public payable {}

}

Enter fullscreen mode Exit fullscreen mode

Implantação e Verificação do Nosso Contrato Inteligente

Precisaremos passar alguns valores para o Foundry a fim de implantar nosso contrato inteligente. Poderíamos fazer isso diretamente na linha de comando durante a implantação do contrato, mas é mais conveniente fazê-lo em um arquivo dotenv. Crie um novo arquivo .env no diretório em que você tem seu projeto.

Precisamos adicionar dois valores ao arquivo .env:

  1. RPC_URL: O Foundry precisará de um URL RPC para se conectar à rede de teste Alpha. Podemos obter um URL RPC público para o teste Alpha na documentação da Scroll.

  2. PRIVATE_KEY: Precisaremos da chave privada de uma carteira com ETH na rede de teste Alpha para assinar transações. Seu arquivo env deve ficar assim:


RPC_URL=https://alpha-rpc.scroll.io/l2

PRIVATE_KEY=1dh12j1XXXXXXXXXXXXXh1pqdfjnma91k

Enter fullscreen mode Exit fullscreen mode

Salve o arquivo env. Execute este comando para carregar essas variáveis no seu terminal:


source .env

Enter fullscreen mode Exit fullscreen mode

Agora que configuramos com segurança todas as informações sensíveis, vamos escrever um script para implantar nosso contrato. Você também pode usar o comando forge create para implantar diretamente a partir da linha de comando, mas considero que usar um script seja mais elegante.

Crie um arquivo ScrollTutorial.s.sol dentro do diretório de scripts. No arquivo, cole o seguinte código:


// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.13;



import "forge-std/Script.sol";

import {Dispenser} from "../src/ScrollTutorial.sol";



contract MyScript is Script {

    function run() external {



        uint256 PrivateKey = vm.envUint("PRIVATE_KEY");

        vm.startBroadcast(PrivateKey);



        Dispenser dispenser = new Dispenser();



        vm.stopBroadcast();

    }

}

Enter fullscreen mode Exit fullscreen mode

Vamos passar pelo que está acontecendo aqui:

  1. Nas duas primeiras linhas, importamos as utilidades de script da biblioteca padrão do forge e nosso contrato inteligente.

  2. A biblioteca forge-std nos fornece a interface vm.sol para usar "códigos de trapaça" valiosos. A função envUint pode ser usada para acessar nossas chaves privadas no arquivo .env.

  3. Qualquer transação entre startBroadcast e stopBroadcast pode ser enviada na cadeia. No nosso caso, precisamos criar uma nova instância do nosso contrato inteligente.

Salve o arquivo de script. Agora estamos prontos para implantar. No seu terminal, execute o seguinte comando:


forge script script/ScrollTutorial.s.sol:MyScript --rpc-url $RPC_URL --broadcast --legacy -vvvv

Enter fullscreen mode Exit fullscreen mode

Dica #2: O Foundry nos permite configurar a verbosidade da linha de comando usando a bandeira '-v'. Eu prefiro usar a máxima verbosidade na maioria das vezes. Você pode ler mais sobre isso aqui.
Dica #3: Para zkEVMs como a Scroll e a zkSync, pode ser necessário passar a flag '--legacy' para as implantações de contratos pelo Foundry, pois geralmente eles não suportam o EIP-1559._

A documentação da Scroll nos fornece um URL da API para o explorador Blockscout, que nos permite verificar nossos contratos. Execute este comando no terminal para verificar seu contrato:


forge verify-contract <CONTRACT_ADDRESS> src/ScrollTutorial.sol:Dispenser --chain-id 534353 --verifier-url https://blockscout.scroll.io/api/ --verifier blockscout

Enter fullscreen mode Exit fullscreen mode

Observação: A API do Blockscout parece estar inativa, e seu comportamento foi reconhecido como inconsistente pela documentação da Scroll. Não consegui verificar meu contrato na linha de comando, e você também pode enfrentar o mesmo problema. No entanto, você pode verificá-lo na interface do Blockscout.

Depois que seu contrato for verificado, você poderá interagir com ele na interface do Blockscout.

Conclusão

Neste artigo, falamos sobre a Scroll zkEVM e como você pode implantar um contrato inteligente nesta rede com uma configuração mínima como desenvolvedor de contratos inteligentes. O lançamento da rede principal da Scroll está prestes a acontecer; este é o melhor momento para começar com essa incrível nova tecnologia.


Este artigo foi escrito por Priyank Gupta e traduzido por Adriano P. de Araujo. O original em inglês pode ser encontrado aqui.

Latest comments (0)