WEB3DEV

Cover image for Interação de Contrato Inteligente com o Mundo Externo: Chainlink vs Foundry.
Panegali
Panegali

Posted on

Interação de Contrato Inteligente com o Mundo Externo: Chainlink vs Foundry.

Na medida em que desejamos tornar nossas aplicações o mais descentralizadas possível, no sentido de não haver "forças externas" atuando sobre ela (por exemplo, uma organização centralizada tentando impor todo o sistema a seu favor), logo percebemos que, com frequência, muitas das ações em nossos contratos inteligentes ainda precisam interagir com o mundo exterior e, essencialmente, com entidades centralizadas.

Você pode estar se perguntando, no entanto, em que casos nossos contratos precisariam dessas interações?

Muitas das aplicações do mundo real que acabamos construindo, na verdade, exigem interações com o mundo exterior. Por exemplo, seu contrato pode precisar converter alguma quantia de ethers para USD em tempo real, ou talvez você precise de algum tipo de automação para acionar certas coisas em seu contrato, obter um valor aleatório, etc. Tudo isso requer interações externas com nossos contratos.

A randomização na blockchain é um pouco complicada de ser alcançada, razão pela qual, muitas vezes, precisamos de ferramentas externas para obter valores aleatórios.

Onde a Chainlink entra em cena?

De acordo com seu site oficial chain.link, a_ Chainlink é a plataforma de computação descentralizada que alimenta a web verificável_. Todos os problemas mencionados acima e mais são os que a Chainlink espera resolver. Portanto, em vez de depender de organizações centralizadas e confiar que elas sempre se comportem adequadamente, a Chainlink traz a interação de que precisamos do mundo externo para um ecossistema mais descentralizado.

Neste artigo, veremos como podemos gerar números aleatórios usando o Chainlink VRF (função de aleatoriedade verificável) e faremos isso com nossa ferramenta recém-descoberta, o "Foundry". Vamos começar.

Iniciando

Para acompanhar, você pode visitar a documentação da Chainlink no link abaixo:

https://docs.chain.link/?source=post_page-----73dafe4f653e--------------------------------

Para começar, inicializaremos um novo projeto Foundry com:


forge init

Enter fullscreen mode Exit fullscreen mode

Em seguida, excluiremos todos os contratos fictícios relacionados ao contador nas pastas src, test e script. Eu criei um contrato ChainlinkDemo.sol na pasta src, como mostrado abaixo. Vou usar o VSCode para este artigo.

Chainlink VRF

A coisa mais importante sobre o VRF é que você está financiando uma assinatura, que é como uma conta para manter o saldo de vários outros contratos. Para criar sua própria assinatura, vá para a seção de geração de números aleatórios na documentação aqui e siga as etapas na seção "Criar e financiar assinatura".

Você precisará ter algum ETH de teste e LINK na rede de teste escolhida. No momento da redação deste artigo, a documentação usava a rede de testes Sepolia e é isso que vamos usar aqui. Você pode obter alguns ETH de teste e LINK em faucets.chain.link. Depois disso, siga os processos envolvidos na criação de uma assinatura e financie-a com ETH e LINK.

Depois de configurar tudo, você deverá chegar a uma página de adição de consumidor como mostrado abaixo. Mantenha isso aberto para mais tarde.

Obter um número aleatório com o VRF envolve duas funções de transação. A primeira solicita o gerador de números aleatórios e a segunda obtém o número aleatório. Ao rolar para baixo na documentação para obter um número aleatório, veremos uma seção de código para o contrato VRFv2Consumer e usaremos esse código para obter nosso número aleatório. Em particular, estamos interessados no bloco de código destacado em verde abaixo.

Então, vamos copiar isso para o nosso contrato:

É claro que teremos muitos erros, mas vamos resolvê-los em breve. Primeiro, vamos entender o que esse bloco de código está fazendo.

  • O COORDINATOR é o endereço do coordenador VRF da Chainlink para o qual faremos a solicitação. Isso retorna um requestId quando chamamos a função requestRandomWords.
  • O keyHash é sua pista de gás. Ele especifica se você deseja gastar muito gás ou não (o mesmo com o callbackGasLimit).
  • O s_subscriptionId é o ID da assinatura que financiamos com LINK ao seguir o processo de criação de assinatura mencionado acima.
  • O requestConfirmations é o número de confirmações de blocos para que nosso número aleatório seja considerado bom.
  • O numWords especifica quantos números aleatórios queremos gerar.

O endereço do coordenador e o keyHash são diferentes para cada cadeia que estivermos usando.

Criamos um construtor que aceita alguns parâmetros e, como eles não serão alterados, os definimos como variáveis imutáveis ou constantes. Após configurar todas as nossas variáveis e passá-las para a função requestRandomWords, recebemos o erro de que nosso i_vrfCoordinator, que é um endereço, não possui a função requestRandomWords. Para resolver isso, teremos que importar a VRFCoordinateV2Interface da Chainlink, conforme mostrado na documentação.

Em seguida, precisamos executar o comando de instalação abaixo para obter o que precisamos da Chainlink.


forge install smartcontractkit/chainlink --no-commit

Enter fullscreen mode Exit fullscreen mode

No entanto, o problema ao instalar diretamente do repositório da Chainlink é que descobri que é um pull muito grande (quase 300 MB). Como alternativa, poderíamos instalar uma versão mais simplificada usando o seguinte comando:


forge install smartcontractkit/[email protected] --no-commit

Enter fullscreen mode Exit fullscreen mode

Eu optarei pelo primeiro comando de instalação.

Em nosso arquivo foundry.toml, escrevemos as seguintes linhas para tornar o pacote instalado acessível e visível para o nosso contrato.

`remappings = ['@chainlink/contracts/=lib/chainlink/contracts']

// se estiver usando a versão reduzida, use
remappings = ['@chainlink/contracts/=lib/chainlink-brownie-contracts/contracts']
Enter fullscreen mode Exit fullscreen mode

Se tudo estiver configurado corretamente, então o erro não deverá aparecer novamente.

Nós alteramos o tipo do nosso i_vrfCoordinator para agora ser a interface importada e, em seguida, realizamos a conversão de tipo (type cast) do endereço vrfCoordinator passado como parâmetro no nosso construtor, também para a interface importada.

A próxima coisa que precisamos fazer é copiar outro trecho de código da documentação que o nó da Chainlink vai chamar para nos fornecer nosso número aleatório.

A palavra-chave override ali significa simplesmente que queremos substituir a função fulfillRandomWords que nosso contrato está herdando, mas como não estamos fazendo herança atualmente em nosso contrato, recebemos o erro mencionado anteriormente. Vamos fazer nosso contrato herdar do VRFConsumerBaseV2, como na documentação.

Observe como isso nos dá uma série de erros? Isso ocorre porque precisamos chamar o construtor do VRFConsumerBaseV2 do qual estamos herdando.

Sempre que herdamos de um contrato que possui um construtor, temos que passar esse construtor para o nosso próprio construtor também.

Observe como passamos o construtor de VRFConsumerBaseV2 para o nosso próprio construtor chamando-o e passando o endereço do vrfCoordinator. Agora todos os erros devem desaparecer. Para acompanhar as solicitações que fizemos para obter um número aleatório, criamos as variáveis de armazenamento conforme visto abaixo:

Em seguida, as utilizamos na nossa função randomNumber:

Como mencionado anteriormente, o fulfillRandomWords é o que será chamado pelo nó da Chainlink, passando o requestId e os números que foram gerados. Em seguida, armazenamos o número na nossa variável s_requests e fazemos algumas atualizações.

Finalmente, criaremos algumas funções de acesso para obter alguns valores nos quais podemos estar interessados.

Podemos definitivamente testar isso localmente usando as ferramentas fornecidas pelo Foundry, mas apenas para testar rapidamente, usarei o Remix. Então, copie seu código para o Remix e, na seção "Deploy and Run Transactions", selecione "Injected Provider - Metamask", pois estaremos implantando na rede de testes SEPOLIA, ao contrário da máquina virtual do Remix. Em seguida, preencha os parâmetros necessários para implantar seu contrato como indicado abaixo.

O subscriptionId é o ID da assinatura que você criou anteriormente. Para obter o endereço do vrfCoordinator e o hash da chave para a rede de testes, neste caso, a Sepolia, vá para a seção de rede suportada na documentação e você verá os diferentes detalhes para sua rede de testes, se ela for suportada.

Depois que o contrato for implantado, copie o endereço do contrato implantado e adicione-o como um consumidor na seção de adição de consumidor mencionada anteriormente.

Após adicionar o consumidor, volte para o Remix e interaja com o contrato implantado, clicando primeiro no botão randomNumber, em seguida, clique no botão getLastRequestId para obter o seu requestId e depois verifique se o pedido foi atendido clicando no botão getRequestStatus. Quando isso retornar verdadeiro, clique no botão getNumber e isso retornará o Número Aleatório.

Você pode encontrar o código completo abaixo.

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.21;

import {VRFCoordinatorV2Interface} from "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import {VRFConsumerBaseV2} from "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2.sol";

contract ChainlinkDemo is VRFConsumerBaseV2 {
   uint16 private constant REQUEST_CONFIRMATIONS = 3;
   uint32 private constant NUM_WORDS = 1;

   VRFCoordinatorV2Interface private immutable i_vrfCoordinator;
   bytes32 private immutable i_keyHash;
   uint64 private immutable i_subscriptionId;
   uint32 private immutable i_callbackGasLimit;

   struct RequestStatus {
       bool fulfilled;
       bool exists;
       uint256[] randomWords;
   }
   mapping(uint256 => RequestStatus) private s_requests;
   uint256[] private s_requestIds;
   uint256 private s_lastRequestId;

   constructor(
       uint64 subscriptionId,
       address vrfCoordinator,
       bytes32 keyHash,
       uint32 callbackGasLimit
   ) VRFConsumerBaseV2(vrfCoordinator) {
       i_subscriptionId = subscriptionId;
       i_vrfCoordinator = VRFCoordinatorV2Interface(vrfCoordinator);
       i_keyHash = keyHash;
       i_callbackGasLimit = callbackGasLimit;
   }

   function randomNumber() external {
       uint256 requestId = i_vrfCoordinator.requestRandomWords(
           i_keyHash,
           i_subscriptionId,
           REQUEST_CONFIRMATIONS,
           i_callbackGasLimit,
           NUM_WORDS
       );
       s_requests[requestId] = RequestStatus({
           fulfilled: false,
           exists: true,
           randomWords: new uint256[](0)
       });
       s_requestIds.push(requestId);
       s_lastRequestId = requestId;
   }

   function fulfillRandomWords(
       uint256 _requestId,
       uint256[] memory _randomWords
   ) internal override {
       require(s_requests[_requestId].exists, "request not found");
       s_requests[_requestId].fulfilled = true;
       s_requests[_requestId].randomWords = _randomWords;
   }

   function getLastRequestId() public view returns (uint256) {
       return s_lastRequestId;
   }

   function getRequestStatus(uint256 requestId) public view returns (bool) {
       return s_requests[requestId].fulfilled;
   }

   function getNumber(
       uint256 requestId
   ) public view returns (uint256[] memory) {
       return s_requests[requestId].randomWords;
   }

   function getRequestIds() public view returns (uint256[] memory) {
       return s_requestIds;
   }
}
Enter fullscreen mode Exit fullscreen mode

Certo, então, neste artigo, discutimos o fato de que gerar números aleatórios na blockchain pode ser muito complicado e podemos aproveitar o Chainlink VRF para nos ajudar a gerar um número aleatório seguindo as etapas descritas na documentação. Sinta-se à vontade para explorar mais a documentação para entender melhor o que está acontecendo e construir sobre o que fizemos neste artigo.

No próximo artigo, também estaremos examinando outra ferramenta maravilhosa da Chainlink que nos ajuda a obter feeds de dados em tempo real, especificamente o equivalente em tempo real de ethers para USD.

Espero que tenha achado este artigo útil. Por favor, aplauda, siga e comente. Nos vemos no próximo.


Artigo escrito por Ifeoluwaolubo. Traduzido por Marcelo Panegali.

Latest comments (0)