WEB3DEV

Cover image for Foundry: 2a Geração do Ferramental Ethereum
Paulo Gio
Paulo Gio

Posted on

Foundry: 2a Geração do Ferramental Ethereum

2022 foi e continuará sendo um grande ano para as ferramentas de desenvolvimento de contratos inteligentes da Ethereum. No Eth DevConnect deste ano, houve até um evento formal dedicado a ferramentas de verificação, onde ocorreram alguns brainstormings e desenvolvimentos incríveis. Os desenvolvedores estão descobrindo que podem obter maior garantia de qualidade de software com ferramentas. Vimos o surgimento de várias ferramentas de verificação leves de código aberto, como Solidity SMTChecker e HEVM, que possuem cadeias de ferramentas simples que os desenvolvedores podem aproveitar. Também estamos vendo o surgimento da próxima geração de ferramentas de desenvolvimento e teste. Fortemente inspirado pelo Dapptools, o Foundry está assumindo o cenário de desenvolvimento da Ethereum.

Onde o Dapptools poderia ser considerado de geração 1.5 em ferramentas Ethereum, fazendo todas as escolhas certas, mas muito cedo para o mercado e sem um suporte técnico massivo, Foundry é uma ferramenta de geração 2 para a Ethereum. Ao desenvolver no ecossistema Ethereum, atualmente não há ferramentas disponíveis que possam superar a funcionalidade da cadeia de ferramentas Foundry.

Lançado em dezembro de 2021 por Georgios Konstantopoulos e a equipe do Paradigm, a cadeia de ferramentas Foundry é atualmente uma das melhores plataformas para desenvolver contratos inteligentes, oferecendo aos usuários recursos exclusivos que tornam o desenvolvimento e o teste mais fáceis do que nunca.

Nesta postagem de blog, pretendemos compartilhar como os engenheiros de verificação da Runtime Verification usam as funcionalidades que o Foundry oferece. Esperamos deixá-lo entusiasmado com o Foundry e o que ele pode trazer para sua jornada de desenvolvimento e compartilhar algumas ferramentas e dicas úteis para você começar.

Desenvolvimento de Contratos Inteligentes com a Ferramenta Forge

Foundry é uma cadeia de ferramentas de desenvolvimento de contratos inteligentes que consiste em três ferramentas: Cast, Anvil e Forge. O foco deste post será o Forge, pois é a ferramenta que mais utilizamos em nossas auditorias.

Com o Forge, podemos compilar, testar e implantar contratos inteligentes, complementar com fuzzing e interagir com a rede principal. Tradicionalmente, tem sido necessário usar outros frameworks de desenvolvimento como Truffle ou Hardhat. Essas ferramentas exigiriam que os desenvolvedores usassem linguagens de programação separadas do Solidity para executar os testes, o que pode ser inconveniente. É perturbador escrever programas em uma linguagem e testes em outra. Todo desenvolvedor Python espera ser capaz de escrever seus testes em Python, e todo desenvolvedor Java espera escrever seus testes em Java; por que com o Solidity não deveria ser o mesmo?

Um Exemplo Simples

Vamos considerar o exemplo simples da escrita de um teste para uma função ERC20 transfer(). A seguinte implementação incompleta do ERC20 fornece funcionalidade suficiente:

contract ERC20 { 

   address immutable owner; 
   mapping(address => uint256) public balanceOf; 
   constructor() { 
   owner = msg.sender; } 
       function mint(address user, uint256 amount) external { 
   require(msg.sender == owner, "Somente o proprietario pode cunhar"); 
   balanceOf[user] += amount; 
} 
   function transfer(address to, uint amount) external { 
        balanceOf[msg.sender] -= amount; 
        balanceOf[to] += amount; } // Outras funcoes... }
Enter fullscreen mode Exit fullscreen mode

Usando a estrutura de teste do Forge, podemos escrever um teste da seguinte forma:

contract ERC20Test is Test { 
   ERC20 token; // Contrato em teste address Alice = makeAddr("Alice"); 
   address Bob = makeAddr("Bob"); 
   address Eve = makeAddr("Eve"); 
   function setUp() public { 
   token = new ERC20(); 
   token.mint(Alice, 10 ether); 
   token.mint(Bob, 20 ether); 
   token.mint(Eve, 30 ether); } 
   function testTransfer(address from, address to, uint256 amount) 
   public { vm.assume(token.balanceOf(from) >= amount); 
   uint256 preBalanceFrom = token.balanceOf(from); 
   uint256 preBalanceTo = token.balanceOf(to); 
   vm.prank(from); 
   token.transfer(to, amount); 
if(from == to) { 
   assertEq(token.balanceOf(from), preBalanceFrom); 
   assertEq(token.balanceOf(to), preBalanceTo); } else { 
   assertEq(token.balanceOf(from), preBalanceFrom - amount); 
   assertEq(token.balanceOf(to), preBalanceTo + amount); } } }
Enter fullscreen mode Exit fullscreen mode

Qualquer contrato derivado de Test é automaticamente reconhecido pelo Forge como um contrato de teste. Quando executamos forge test na linha de comando, o Forge passa por todos os contratos de teste, chama a função setUp() e, em seguida, qualquer função cujo nome comece com test do estado obtido após chamar a função setUp().

No exemplo acima, queremos testar a função transfer() do contrato de token ERC20. Para isso, criamos uma nova instância do contrato ERC20 na função setUp() e cunhamos alguns tokens para os três usuários, Alice, Bob e Eve. Seus endereços são gerados usando a função makeAddr() fornecida pelo Forge.

Em seguida, a função testTransfer() contém a lógica de teste real. Observe que são necessários três argumentos, from, to e amount, tornando-o um teste de propriedade. O Forge chamará essa função de teste com valores aleatórios para esses argumentos (chamados de teste aleatório ou fuzzing), o que ajuda a detectar casos extremos que o desenvolvedor pode ter perdido de outra forma. No entanto, em muitos casos, o código que queremos testar tem certas expectativas e usar valores completamente aleatórios nem sempre é útil. Por exemplo, transfer() é revertido se o saldo de from for menor do que amount. Assim, para testar se transfer() transfere corretamente a quantidade certa de tokens, precisamos garantir que o remetente tenha fundos suficientes, o que fazemos com a função especial. Em seguida, para fazer a transferência real, precisamos representar a conta de envio, o que podemos fazer com vm.prank(from). Depois, verificamos se a quantidade correta de tokens foi transferida.

Se tivermos o contrato acima, podemos executar o teste da seguinte forma:

https://miro.medium.com/max/1100/0*0zqaIEsfzX1hWa80.gif

Observe como na saída vemos runs: 256. Esta é a indicação do Forge de que ele executou este teste de propriedade 256 vezes, cada vez adivinhando diferentes valores aleatórios para usar para os parâmetros from, to e amount.

Se você deseja executar esses testes, criamos um repositório para que você execute os exemplos deste artigo rapidamente!

Executando testes na Mainnet

O Forge também permite que os usuários façam uma bifurcação (fork) da Rede Principal (Mainnet) da Ethereum para executar testes em um ambiente realista.

Muitas vezes, os contratos inteligentes se comunicam com contratos externos em sua operação. Essas interações podem ser difíceis de replicar localmente sem muito trabalho. Além disso, usar dados realistas ao testar é importante para aumentar a confiança na correção do código. Portanto, testar programas isoladamente em uma máquina local ou em uma testnet é insuficiente.

Por exemplo, suponha que estamos desenvolvendo uma exchange descentralizada. Não basta presumir que nosso programa funcionará corretamente e com segurança com todos os ERC20 apenas porque testamos o sistema em um contrato simulado do ERC20. Devemos testar todas as implementações do ERC20 para as quais queremos oferecer suporte — sem exceções. Com a funcionalidade “Fork Mainnet”, não é necessário ir manualmente ao GitHub, buscar os códigos-fonte, recompilá-los e integrar o artefato de compilação em nosso conjunto de testes. Em vez disso, podemos testar nossas chamadas externas de contrato quase sem sobrecarga de configuração. Tudo o que precisamos fazer é adicionar o parâmetro --fork-url <URL> à nossa chamada do Forge. Podemos configurá-lo para qualquer ponto de extremidade Ethereum JSON-RPC. Em seguida, estamos prontos para ir ao Etherscan para buscar o endereço do contrato de terceiros e simular chamadas externas para dados reais de nível de produção, o que nos poupa o incômodo de escrever contratos simulados e aumenta nossa confiança no conjunto de testes. Também podemos especificar o --fork-block-number <BLOCK> para executar testes em um estado passado da rede.

Essa técnica também pode ser usada como uma solução de monitoramento leve. Quer fazer um bot de arbitragem simples entre SushiSwap e UniSwap? Faça um teste de propriedade descrevendo a arbitragem com parâmetros para o valor a ser negociado e use o Foundry para executar várias simulações na rede principal quando novos blocos forem produzidos! Deseja observar estados potencialmente ruins que podem ser alcançados pelo seu contrato na produção? Faça um teste do Foundry descrevendo o estado ruim e execute-o continuamente em novos blocos usando o Forge.

Por que você deveria usar o Foundry?

O Forge, e o Foundry como um todo, é uma nova ferramenta em um espaço em rápida mudança, e as atualizações do próprio Foundry mudam constantemente a forma como os usuários interagem com as ferramentas. Mas há uma razão pela qual os desenvolvedores estão correndo para adotá-lo. O Foundry oferece a seus usuários uma estrutura de teste nativa em Solidity e velocidades de processamento rápidas, o que proporciona uma facilidade de uso incrível e uma grande adoção pelos desenvolvedores.

O uso do Foundry permite a geração de artefatos de compilação independentes e detalhados para cada contrato, facilitando a inspeção do bytecode gerado e a integração de ferramentas de terceiros. O Foundry também é compatível com ferramentas existentes, como o Hardhat, tornando mais viável a transição de projetos existentes. Tudo isso junto torna as extensões de construção e as ferramentas de análise muito mais fáceis no Foundry.

Os usuários obtêm acesso a uma primeira abordagem de especificação de software para testes: o Foundry expõe e melhora a linguagem de testes de propriedade dapptools diretamente para os usuários. Ele incentiva os usuários a aprender como declarar as propriedades que esperam manter para suas ferramentas e permite que eles verifiquem se essas propriedades são válidas com o fuzzing integrado do Foundry!

O Foundry é uma ferramenta valiosa em qualquer estágio do processo de desenvolvimento. Começar com as ferramentas no início do desenvolvimento de software pode colocar seu projeto em uma posição melhor para ter sucesso. A implantação antecipada das ferramentas do Foundry pode tornar o processo de desenvolvimento mais gerenciável e permitir uma auditoria mais simplificada e completa no futuro. Entrar em contato com especialistas da Runtime Verification para obter assistência na configuração das ferramentas do Foundry pode ser um ótimo ponto de partida.

Alchemix e Ferramental do Foundry

A Alchemix é uma plataforma de ativos sintéticos lastreada em rendimentos futuros e uma comunidade DAO. A plataforma oferece adiantamentos em Yield Farming (cultivo de rendimento) por meio de um token sintético que representa uma reivindicação fungível em qualquer garantia subjacente no protocolo Alchemix.

Em suma, os usuários que possuem qualquer ativo aceito (digamos, 1000 DAI) podem colocar seu ativo como garantia para obter um empréstimo do ativo sintético Alchemix correspondente (500 alDAI neste caso, pois os usuários podem pegar emprestado até metade de suas garantias), representado como um CDP (posição de dívida colateralizada, ou cofre). O ingrediente-chave aqui é que a dívida do usuário está sendo paga ao longo do tempo pelo rendimento obtido pela garantia do CDP.

A Runtime Verification começou a trabalhar com a Alchemix no verão de 2021, auxiliando a equipe com questões de design e segurança nos primeiros dias do desenvolvimento da versão 2. Nos meses seguintes, ambas as equipes trabalharam para melhorar o design e o código do contrato. Ao longo do caminho, nossas equipes discutiram auditorias e estratégias gerais de segurança.

A Alchemix iniciou sua jornada com o Foundry em março de 2022, após a conclusão da auditoria da versão 2. Antes de usar o Foundry, a Alchemix usava o Hardhat para suas ferramentas. A equipe da Alchemix descobriu que o Foundry é mais fácil de usar do que o Hardhat devido à capacidade de escrever código em Solidity, recursos integrados de fuzzing e utilitários de teste integrados e de velocidade que facilitam a compilação/teste/depuração.

A Runtime Verification ajudou a equipe da Alchemix a usar o Foundry, identificando e escrevendo o código para invariantes no contrato inteligente, permitindo testes de propriedade precisos com as ferramentas do Foundry. Os leitores interessados em uma lista completa dessas propriedades podem encontrá-la no relatório de auditoria.

Uma Palavra do Time da Alchemix

P. Por que vocês decidiram usar o Foundry?

R. É mais rápido que o hardhat e podemos escrever apenas em solidity (ao invés de ter que escrever js/ts). Ele possui recursos de fuzzing integrados e muitos outros ótimos utilitários de teste que facilitam a compilação/teste/depuração.

P. Qual é a sua experiência com a ferramenta Foundry?

R. Nós adoramos. Houve peculiaridades, mas nenhum problema que impedisse sua performance. Estamos ansiosos para ver o ecossistema se desenvolver e para que mais ferramentas sejam construídas com/em torno dele.

P. Que conselho vocês dariam a uma pessoa nova no Foundry? Vocês podem oferecer alguma dica para evitar armadilhas que vocês possam ter encontrado?

R. Não há necessidade de apostar tudo no Foundry desde o primeiro dia. Se você estiver migrando do Hardhat, poderá adicionar o Foundry ao seu repositório e fluxo de trabalho e fazer a transição lenta para usar o Foundry cada vez mais ao longo do tempo, enquanto ainda obtém os benefícios do Hardhat (script, implantações, etc.).

P. O Foundry ajudou vocês a descobrir algum bug ou erro?

R. Sim, o fuzzing nos ajudou a identificar alguns casos de arestas a arredondar. O teste é muito fácil e nos ajudou a encontrar muitos erros em nosso código (embora eu não possa dizer com certeza que não conseguiríamos encontrá-los apenas com nosso env do Hardhat, certamente foi mais fácil no Foundry).

P. Quais são seus planos para o futuro do Foundry?

R. Planejamos usá-lo para todo o código Solidity daqui para frente e esperamos adotar qualquer nova ferramenta que ele suporte à medida que for lançada.

P. Como é a experiência de trabalhar com a equipe da Runtime Verification para configurar/codificar os testes invariantes da v2 no Foundry?

R. Os testes invariantes nos deram uma imensa tranquilidade em nosso conjunto de testes. Acho que daqui para frente, para qualquer novo código, devemos sempre começar com testes invariantes para garantir que estamos testando os aspectos mais críticos do sistema.

Conclusão

Com o lançamento das ferramentas Foundry, fica claro que o campo de contratos inteligentes da Ethereum está amadurecendo. O Foundry é uma ferramenta versátil com recursos avançados e tem a vantagem de ser altamente adaptável. Muitos programadores, auditores e pesquisadores já usam o Foundry de uma forma ou de outra em seu trabalho diário, como os auditores da Runtime Verification.

O teste de propriedade é crucial para o desenvolvimento de contratos inteligentes e é uma ótima maneira de qualquer equipe aumentar sua garantia de qualidade de software. Com a implementação antecipada do Foundry e parcerias de segurança com auditores, como os da Runtime Verification, os desenvolvedores podem aproveitar ao máximo os benefícios que essas ferramentas têm a oferecer agora e no futuro.

Publicado originalmente em https://runtimeverification.com. Artigo original aqui. Traduzido por Paulinho Giovannini.

Latest comments (0)