Como monitorar preços de criptomoedas na Uniswap V3

Cripto

Como monitorar preços de criptomoedas na Uniswap V3

Luiz Duarte
Escrito por Luiz Duarte em 12/09/2023
Junte-se a mais de 34 mil devs

Entre para minha lista e receba conteúdos exclusivos e com prioridade

Uma das primeiras barreiras que encontramos quando queremos iniciar um trabalho de monitorar uma dex qualquer, como a Uniswap, é que como ela roda inteiramente na blockchain (web3 baby!), não há APIs REST para usarmos do modo que faríamos com corretoras centralizadas como Binance, por exemplo. Ao invés disso, precisamos interagir com smart contracts na blockchain usando bibliotecas com Web3.js ou EthersJS, que já mostrei como algumas vezes aqui no blog (clique nos links para conhecer os tutoriais).

No entanto, as dex não costumam fornecer em seus smart contracts funções que exibam os preços dos ativos e aí mora nosso primeiro e maior problema. Isso não é possível de maneira tão simples quanto em exchanges centralizadas porque não existe um book de ofertas para se consultar o preço atual em 99% das dex, pois elas operam de maneira diferente, baseadas na Constant Product Formula e outras “matemáticas” derivadas dela. Assim, com base na oferta de um par de moedas em um pool de liquidez vs o tamanho do lote a ser negociado, um “preço” é estipulado para aquela operação, na própria transação do swap em si.

Ou seja, não é algo impossível de ser calculado, apenas um pouco mais complexo do que gostaríamos e é justamente isso que vou te mostrar como fazer neste tutorial. É importante entender que para conseguir acompanhar este tutorial você deve ter conhecimentos básicos de Node.js e de funcionamento da Uniswap, como usuário. Ajuda um pouco se você conhecer o básico de smart contracts em Solidity também, mas não é algo obrigatório.

Se preferir, você pode assistir ao vídeo abaixo ao invés de ler este tutorial.

Vamos lá!

Estruturando o Projeto

Para fazer chamadas aos smart contracts da Uniswap você vai precisar ter acesso a um nó da blockchain que desejará monitorar. Você pode obter um nó gratuitamente com a Infura, um dos maiores provedores de Blockchain as a Service do mundo. Crie uma conta gratuita no site deles e depois crie um node da rede que deseja (eu vou usar Ethereum) para você assim que conseguir entrar no painel. Guarde a API Key que vai receber, vamos precisar dela mais tarde.

Agora vamos criar nosso projeto Node.js, começando pela criação de uma pasta uniswap-monitor e inicialização de um projeto Node.js nela.

Depois, vamos instalar as dependências que vamos precisar.

A saber:

  • DotEnv: pacote para carregamento das variáveis de ambiente;
  • Ethers: pacote para comunicação com a blockchain;
  • @uniswap: pacotes utilitários para comunicação com os smart contracts da UniSwap;

Agora crie um arquivo .env na raiz do seu projeto e coloque nele as seguintes variáveis:

  • INFURA_API_KEY: informe sua API Key da Infura;
  • INTERVAL: informe o intervalo em que o bot vai coletar o preço atualizado. Ex: 300000 (5min);
  • NETWORK: o nome da rede que vai monitorar, em minúsculas. Ex: mainnet
  • FACTORY_ADDRESS: o endereço do contrato de pool factory da Uniswap V3 na rede que deseja monitorar. Lista completa de endereços neste link.
  • QUOTER_ADDRESS: o endereço do contrato de quoting (cotação) da Uniswap V3 na rede que deseja monitorar. Lista completa de endereços neste link.
  • TOKEN_IN_ADDRESS: o endereço do contrato ERC20 do token in na rede que vai monitorar. Ex: 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 (WETH na Ethereum Mainnet)
  • TOKEN_OUT_ADDRESS: o endereço do contrato ERC20 do token out na rede que vai monitorar. Ex: 0xdac17f958d2ee523a2206206994597c13d831ec7 (USDT na Ethereum Mainnet)

Muita atenção aqui à variável INTERVAL, pois os nós gratuitos da Infura possuem uma limitação de chamadas por dia de 100k. É bastante, mas não custa nada tomar cuidado né. Outro ponto de atenção são os endereços dos tokens in e out, sendo que aqui vamos ter como resultado a cotação em dólares do Ether, através do par WETH/USDT (Wrapped Ether, pareado com Ether e Tether, pareado com dólar).

Por fim, crie um arquivo index.js na raiz do seu projeto e configure no package.json para que ele seja executado no comando de start, já que é nele que irá toda a lógica do nosso bot.

E com isso temos toda a “infraestrutura” necessária para desenvolver o nosso robô de monitoramento de preços na Uniswap, vamos em frente.

Curso Node.js e MongoDB

Preparando o Monitoramento

O primeiro passo agora que estamos com a estrutura inicial do projeto pronta é obter as informações da exchange relativas ao pool de liquidez que vamos monitorar. Para isso precisaremos configurar uma série de variáveis e codificar uma função. Mas vamos por partes.

Primeiro, carregamos as variáveis de ambiente em constantes locais no index.js.

Em seguida, vamos criar objetos com as configurações das duas moedas que formam o par de monitoramento. Para isso usarei o enumerador ChainId e a classe Token, provenientes do SDK da Uniswap. Repare que você terá de mudar estas configurações de tokens conforme o par que decidir monitorar, sendo que aqui estou conduzindo tudo para monitorar WETH em USDT.

A última etapa de configuração da preparação do monitoramento é configurarmos o provedor de acesso à blockchain, usando a classe InfuraProvider disponível na biblioteca Ethers v6 em conjunto com algumas das variáveis de ambiente.

Agora que temos todas variáveis monitoradas, vamos escrever a função que carrega todas as informações do pool que precisamos para obter as cotações. O primeiro passo desta função é descobrir o endereço do pool a ser monitorado. Se você já possui esse endereço, pode ignorar esta etapa e colocar ele em uma constante chamada currentPoolAddress.

O endereço do pool é resultado de um cálculo em cima de algumas informações que passamos para a função computePoolAddress, presente no Uniswap SDK. Repare que WETH_TOKEN e USDT_TOKEN são objetos que pré-configuramos antes (não confundir com os endereços deles). Além disso, repare também que estarei considerando para nossas cotações a taxa média (fee) do pool e que os preços podem variar para mais ou para menos conforme a liquidez do pool com taxas menores e maiores. Resumindo: teremos um preço médio, mas não absoluto/universal.

O próximo passo é carregar o ABI do contrato de pool e carregar um objeto com o referido contrato (usando o provider criado anteriormente). A chamada que precisamos fazer à exchange exige que a gente passe um token0 e um token1, sendo que não necessariamente é tokenIn e tokenOut, respectivamente. Sendo assim, precisamos ao final do código a seguir, descobrir quem é token0 e quem é token1, sendo que para isso eu chamo uma nova função que vai nos entregar a ordem certa.

Segue a função getTokenOrder, que pode colocar em qualquer lugar do arquivo. Ela faz basicamente uma comparação da versão decimal dos endereços dos tokens para determinar quem vem antes (token0), assumindo automaticamente que token1 será o outro token.

Com esse objeto poolContract podemos fazer uma chamada ao contrato de pool para saber a última informação que ainda não temos: o valor nominal da taxa de swap ou simplesmente: a fee. Novamente: se você já sabe o fee exato do pool que vai monitorar, você pode apenas colocar ele no código.

Com estas três informações do pool a ser monitorado (token0, token1 e fee), estamos com tudo pronto para finalizar nosso bot.

Curso Beholder
Curso Beholder

Programando o Monitoramento

Repare que até esta etapa já tivemos de fazer uma chamada à blockchain e, além disto gastar créditos na Infura, é algo que toma tempo e que você não vai querer fazer toda hora. Por isso que essa etapa de preparação está em uma função exclusiva. Função essa que será chamada logo mais.

Mas agora é hora de começarmos a implementar a função de monitoramento de fato.

Nesta função nós vamos receber as informações que obtivemos anteriormente na etapa de preparação, usando-as ao longo da função. A primeira etapa é carregar o ABI do contrato de Quoter, contrato este que configuramos na linha seguinte usando o mesmo provider que configuramos lá atrás.

Na sequência, chamamos a função quoteExactInputSingle do contrato de Quoter que espera os dados dos tokens, da fee, a quantidade de token que vamos gastar e o mínimo que esperamos receber.

Repare que após a chamada da função eu usei o recurso staticCall. Esse recurso é crucial para nosso robô pois a função quoteExactInputSingle é uma transaction, ou seja: escreve na blockchain e consequentemente gera custos na sua execução. No entanto, ao usarmos uma static call nós dizemos ao nó da blockchain que ele deve executar a transação mas não deve persisti-la no disco, nos livrando dos custos envolvidos. Como é apenas uma informação que queremos, esse comportamento de transação “efêmera” é perfeitamente aceitável.

Agora que finalizamos nossa função de execução do ciclo de monitoramento, vamos implementar o monitoramento em si.

Aqui estou usando um IIFE para conseguir fazer chamadas com Async/Await na raiz do index.js. Começo fazendo a única chamada que teremos à função preparationCycle, pego estas informações para serem passadas adiante e configuro o timer que rodará a cada x tempo refazendo o monitoramento. Como não quero ter de esperar pela execução do primeiro ciclo, já chamo a executionCycle logo na sequência.

Internamente, a executionCycle já vai imprimir pra gente o preço obtido na cotação, então com isso programado e rodando o projeto com npm start, você terá o preço impresso no seu console a cada 5 minutos.

Existem algumas empresas que fizeram APIs com códigos semelhantes a esse, como a Moralis. Ensino esta outra abordagem usando as APIs da Moralis neste outro tutorial. A própria Uniswap criou uma Graph API que faz exatamente o que mostrei neste tutorial, mas expõe de maneira mais simples, saiba mais aqui.

Também possuo tutoriais que podem lhe ajudar a transformar esse conhecimento em um bot de sinais, como esse de Telegram, de email, de SMS e até de Whatsapp ou esse aqui onde ensino a fazer um bot trader para Uniswap.

Está tendo erros? Consulte no meu guia de erros comuns de integração com Uniswap, neste link.

E com isso finalizamos mais este tutorial. Até o próximo!

Olá, tudo bem?

O que você achou deste conteúdo? Conte nos comentários.

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *