WEB3DEV

Cover image for Construa um dapp com Tezos (edição 2023)
Adriano P. Araujo
Adriano P. Araujo

Posted on • Atualizado em

Construa um dapp com Tezos (edição 2023)

Image description

Parte 1 de 4 -> Configurando o dapp

Neste tutorial, você aprenderá como configurar e criar um aplicativo da Web descentralizado no Tezos. Construiremos juntos uma interface para o contrato inteligente Liquidity Baking que nos permitirá interagir com esse DEX e executar operações diferentes, como trocar tokens ou fornecer liquidez. Ao mesmo tempo, você será apresentado aos conceitos principais de construção de uma aplicação descentralizada geral, mas também específica no Tezos.

Como o dapp será construído com TypeScript, é necessário um bom conhecimento dessa linguagem de programação. Usaremos o framework Svelte para desenvolver o aplicativo, nenhum conhecimento prévio é necessário, pois é bastante intuitivo de usar e explicarei como ele funciona ao longo do caminho.

Como 99% dos dapps no ecossistema, esse dapp usará Taquito, uma biblioteca TypeScript que fornecerá uma experiência de desenvolvedor muito melhor para usar a blockchain Tezos.

O contrato Liquidity Baking

Há um contrato especial no Tezos chamado contrato Liquidity Baking. Este contrato é uma troca descentralizada (ou DEX ) que lida com apenas 3 tokens: XTZ ( o token nativo do Tezos ), tzBTC ( um token específico para usar Bitcoin em Tezos ) e SIRS ( para Sirius, o token que representa uma quantidade igual de liquidez em XTZ e tzBTC adicionada ao contrato ).

A particularidade deste contrato é que toda vez que um novo bloco é fabricado no Tezos, 2,5 XTZ são adicionados ao contrato. Espera-se que os usuários tragam o tzBTC para manter a liquidez da DEX equilibrada e o preço do SIRS estável.

O contrato também é totalmente público, o que significa que qualquer pessoa com uma carteira Tezos pode interagir com ela para trocar XTZ por tzBTC e vice-versa, fornecer liquidez ou removê-la, que é o que vamos fazer neste tutorial.

O que vamos construir?

Neste tutorial, criaremos uma interface dapp que interage com o contrato LB (Liquidity Baking), para trocar tokens, adicionar liquidez e removê-los. O dapp lidará com diferentes ações:

  • Exibir informações de usuários como seus balanços XTZ, tzBTC e SIRS e atualizá-las após cada transação

  • Conexão e desconexão da carteira de usuários 

  • Exibir informações da carteira, como o status da conexão e a rede à qual está conectada

  • Exibir interfaces diferentes para trocar tokens, adicionar e remover liquidez

  • Permitir que os usuários troquem XTZ por tzBTC e tzBTC por XTZ

  • Permitir que os usuários adicionem liquidez, fornecendo XTZ e tzBTC e recebendo SIRS em troca

  • Permitir que os usuários removam a liquidez, ou seja, resgatem tokens SIRS e obtenham tokens XTZ e tzBTC em troca.

Que ferramentas usaremos?

Como o aplicativo descentralizado é, em última análise, um aplicativo da Web, usaremos as seguintes ferramentas para construí-lo:

  • Svelte como o framework JavaScript

  • TypeScript para tornar nosso código JavaScript mais seguro e expressivo

  • Sass como um pré-processador CSS

  • Vite para agrupar o aplicativo 

  • Taquito para interagir com a blockchain da Tezos

  • Beacon e a biblioteca fornecida por Taquito para usar uma carteira Tezos

Links úteis

Configurando o projeto

Enquanto estamos construindo um aplicativo da web com o framework  Svelte, as etapas para configurar o projeto serão muito semelhantes às que você seguiria para configurar qualquer outro aplicativo da web.

Neste tutorial, faremos um Svelte SPA, para não precisarmos do SvelteKit, o que também facilitará nossa vida.

A primeira coisa a fazer é instalar o Svelte com TypeScript e Vite:


npm create vite@latest lb-dex -- --template svelte-ts

cd lb-dex

npm install

Enter fullscreen mode Exit fullscreen mode

Em seguida, instalaremos todas as dependências necessárias para o dapp:


npm install --save-dev sass

npm install @taquito/taquito @taquito/beacon-wallet

Enter fullscreen mode Exit fullscreen mode

Sass é uma dependência apenas de desenvolvimento, @taquito/taquito é o pacote NPM da biblioteca Taquito e @taquito/beacon-wallet é o pacote NPM que contém o Beacon com alguma pequena configuração para facilitar a conexão com o Taquito.

Existem algumas outras bibliotecas que precisamos instalar:


npm install --save-dev buffer events vite-compatible-readable-stream



Enter fullscreen mode Exit fullscreen mode

Essas bibliotecas devem poder executar o Beacon em um aplicativo Svelte. Veremos abaixo como usá-los.

Depois que tudo estiver instalado, precisamos definir a configuração correta.

Na sua pasta app, você observará o arquivo vite.config.js, que é o arquivo que contém a configuração que o Vite precisa executar e agrupar em seu aplicativo. Efetue as seguintes alterações:




import { defineConfig, mergeConfig } from "vite";

import path from "path";

import { svelte } from "@sveltejs/vite-plugin-svelte";



export default ({ command }) => {

  const isBuild = command === "build";

  return defineConfig({

    plugins: [svelte()],

    define: {

      global: {}

    },

    build: {

      target: "esnext",

      commonjsOptions: {

        transformMixedEsModules: true

      }

    },

    server: {

      port: 4000

    },

    resolve: {

      alias: {

        "@airgap/beacon-sdk": path.resolve(

          path.resolve(),

          `./node_modules/@airgap/beacon-sdk/dist/${

            isBuild ? "esm" : "cjs"

          }/index.js`

        ),

        // polyfills

        "readable-stream": "vite-compatible-readable-stream",

        stream: "vite-compatible-readable-stream"

      }

    }

  });

};

Enter fullscreen mode Exit fullscreen mode

Aqui estão algumas alterações feitas na configuração do modelo fornecida pelo Vite:

  • Definimos global para {} e depois forneceremos o objetoglobal em nosso arquivo HTML

  • Fornecemos um caminho para o Beacon SDK

  • Fornecemos polyfills para readable-stream e stream

Depois que essas alterações forem feitas, há um último passo para concluir a configuração do projeto: precisamos atualizar o arquivo HTML em que o código JavaScript será injetado.

Aqui está o que você deve ter:


<!DOCTYPE html>

<html lang="en">

  <head>

    <meta charset="UTF-8" />

    <link rel="icon" href="/favicon.ico" />

    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

    <script>

      const global = globalThis;

    </script>

    <script type="module">

      import { Buffer } from "buffer";

      window.Buffer = Buffer;

    </script>

    <title>Liquidity Baking DEX</title>

  </head>

  <body>

    <script type="module" src="/src/main.ts"></script>

  </body>

</html>

Enter fullscreen mode Exit fullscreen mode

No primeiro tag do script, definimos a variável global para globalThis. Então, na segunda tag  script, com um tipo module, nós importamos Buffer da biblioteca buffer e adicionamos ao objeto global window.

Nota: essa configuração é necessária para executar o Beacon SDK com um aplicativo Vite. Taquito funciona completamente fora da caixa e não requer nenhuma configuração.

Após atualizarmos a configuração no arquivo vite.config.js e no arquivo index.html, nosso projeto está configurado com sucesso! Você pode executar npm run dev no seu terminal na raiz do projeto para verificar se tudo funciona corretamente, o dapp deve estar em execução na porta http://localhost:4000

Agora, comecemos a escrever algum código e configurar o dapp!

Configurando o dapp

Se você chegou até aqui e seu aplicativo está em execução http://localhost:4000, parabéns!

Agora, temos que configurar o dapp para usar Taquito e Beacon.

Estrutura do arquivo

O ponto de entrada de cada aplicativo Svelte é um arquivo chamado App.svelte, é aqui que você importará todos os seus componentes para serem agrupados no seu aplicativo final. A estrutura de arquivos do nosso projeto se parece com isso:

- src

- assets

- svelte.png

- lib

- AddLiquidityView.svelte

- Interface.svelte

- RemoveLiquidity.svelte

- Sidebar.svelte

- SirsStats.svelte

- SwapView.svelte

- Toast.svelte

- UserInput.svelte

- UserStats.svelte

- Wallet.svelte

- styles

- index.scss

- settings.scss

- App.svelte

- config.ts

- lbUtils.ts

- main.ts

- store.ts

- types.ts

- utils.ts

- index.html

- svelte.config.js

- tsconfig.json

- vite.config.js


Enter fullscreen mode Exit fullscreen mode

Vejamos o que cada um desses elementos faz:

  • assets - > contém o favicon ( aqui, este é o favicon Svelte padrão, mas você pode escolher outro )

  • lib - > contém os diferentes componentes que compõem nossa interface, eis o que cada um faz:

    • SwapView.svelte: a interface para trocar tokens XTZ e tzBTC
    • AddLiquidityView.svelte: a interface para adicionar liquidez ao LB DEX
    • RemoveLiquidity.svelte: a interface para remover a liquidez do LB DEX
    • Interface.svelte: o componente de ordem superior para armazenar as diferentes visualizações para interagir com o LB DEX
    • Sidebar.svelte: o componente para navegar entre as diferentes interfaces e conectar ou desconectar a carteira
    • SirsStats.svelte: o componente para exibir a quantidade de XTZ, tzBTC e SIRS presentes no contrato
    • Toast.svelte: um componente simples para exibir a progressão das transações e outras mensagens ao interagir com o contrato
    • UserInput.svelte: um componente utilitário para facilitar a interação e o controle dos campos de entrada
    • UserStats.svelte: o componente para exibir o saldo do usuário em XTZ, tzBTC e SIRS
    • Wallet.svelte: o componente para gerenciar interações da carteira
  • styles - > contém os arquivos SASS para criar diferentes elementos de nossa interface

  • App.svelte - > o ponto de entrada do pedido

  • config.ts - > valores imutáveis diferentes necessários para o aplicativo e salvos em um arquivo separado por conveniência

  • lbUtils.ts - > métodos diferentes para calcular os valores necessários para interagir com o contrato de Liquidity Baking 

  •  main.ts - > é aqui que o JavaScript do aplicativo é empacotado antes de ser injetado no arquivo HTML

  • store.ts - > um arquivo com uma Gerenciador de Estados Svelte para lidar com o estado dapp

  • types.ts - > tipos personalizados de TypeScript

  • utils.ts - > diferentes métodos de utilidade

A primeira coisa a fazer é importar nossos estilos para o main.ts Arquivo:

import App from './App.svelte'

import "./styles/index.scss";

const app = new App({

 target: document.body

});

export default app;

Enter fullscreen mode Exit fullscreen mode

O Svelte usa o SASS por padrão, portanto não há configuração para isso.

Nota: Eu também gosto de segmentar a tag body para injetar o HTML produzido por JavaScript em vez de uma div dentro do body, mas essa é uma escolha pessoal e você é livre para usar uma div em vez disso

Antes de continuar, é assim que um arquivo Svelte se parece:


<script lang="ts">

  ... your TypeScript code

</script>

<style lang="scss">

   ... your SASS code

</style>

... your HTML code

Enter fullscreen mode Exit fullscreen mode

Os componentes Svelte estão totalmente contidos, o que significa que o estilo que você aplica dentro de um componente não vaza para os outros componentes do seu aplicativo. O estilo que queremos compartilhar entre os diferentes componentes será escrito no arquivo index.scss.

Há uma tag script com um atributo lang definido para ts para TypeScript, uma tagstyle com um atributo lang definido para scss para SASS e o restante do código no arquivo será interpretado como HTML.

Configurando o dapp

Agora, vamos configurar coisas diferentes em nosso arquivo App.svelte.

A parte HTML apenas reunirá todos os componentes de ordem superior:

<main>

  <Toast />

  {#if $store.Tezos && $store.dexInfo}

    <Sidebar />

    <Interface />

  {:else}

    <div>Loading</div>

  {/if}

</main>

Enter fullscreen mode Exit fullscreen mode

A interface mudará depois que diferentes elementos estiverem disponíveis para o dapp, principalmente os dados sobre os pools de liquidez do contrato Liquidity Baking.

A parte SASS importará configurações diferentes e aplicará estilo a tag main:

@import "./styles/settings.scss";
main {

 display: grid;

 grid-template-columns: 250px 1fr;

 gap: $padding;

 padding: $padding;

 height: calc(100% - (#{$padding} * 2));

}

@media screen and (max-height: 700px) {

 main {

   padding: 0px;

   height: 100%;

 }

}

Enter fullscreen mode Exit fullscreen mode

Agora, vamos à parte do TypeScript. Primeiro, você importa as bibliotecas e componentes de que precisamos:


import { onMount } from "svelte";

import { TezosToolkit } from "@taquito/taquito";

import store from "./store";

import { rpcUrl, dexAddress } from "./config";

import Sidebar from "./lib/Sidebar.svelte";

import Interface from "./lib/Interface.svelte";

import Toast from "./lib/Toast.svelte";

import type { Storage } from "./types";

import { fetchExchangeRates } from "./utils";

Enter fullscreen mode Exit fullscreen mode
  • onMounté um método exportado pela Svelte que executará algum código quando o componente renderizar( mais sobre isso abaixo )

  • TezosToolkit é a classe que lhe dá acesso a todos os recursos do Taquito

  • store é um recurso Svelte para gerenciar o estado do dapp

  • Do arquivo config.ts, importamos rpcUrl ( o URL do nó RPC Tezos ) e o dexAddress, o endereço do contrato de Liquidity Baking

  • Storage é um tipo personalizado que representa o tipo de assinatura do armazenamento LB DEX

  • fetchExchangeRatesé uma função para buscar as taxas de câmbio de XTZ e tzBTC ( mais sobre isso abaixo )

Em seguida, usamos onMount para configurar o estado do dapp:


onMount(async () => {

  const Tezos = new TezosToolkit(rpcUrl);

  store.updateTezos(Tezos);

  const contract = await Tezos.wallet.at(dexAddress);

  const storage: Storage | undefined = await contract.storage();



  if (storage) {

    store.updateDexInfo({ ...storage });

  }

  // obtém os preços do XTZ  e tzBTC

  const res = await fetchExchangeRates();

  if (res) {

    store.updateExchangeRates([

      { token: "XTZ", exchangeRate: res.xtzPrice },

      { token: "tzBTC", exchangeRate: res.tzbtcPrice }

    ]);

  } else {

    store.updateExchangeRates([

      { token: "XTZ", exchangeRate: null },

      { token: "tzBTC", exchangeRate: null }

    ]);

  }

});

Enter fullscreen mode Exit fullscreen mode

A primeira coisa a se fazer é criar uma instância do TezosToolkit passando o URL do nó RPC com o qual queremos interagir. Em geral, você deseja ter uma única instância do TezosToolkit para manter a mesma configuração em todos os componentes do seu aplicativo. É por isso que a salvamos no store com o método updateTezos.

Depois disso, queremos buscar o armazenamento do LB DEX para obter as quantidades de XTZ, tzBTC e SIRS no contrato. Criamos um ContractAbstraction, uma instância fornecida pela Taquito com diferentes propriedades e métodos úteis para trabalhar com contratos inteligentes da Tezos. Do ContractAbstraction, podemos ligar para o método storage que retorna um objeto JavaScript que representa o armazenamento do contrato especificado. Passamos o armazenamento para o método updateDexInfo presente no store para atualizar esses dados e exibi-los ao usuário.

Para finalizar, precisamos buscar as taxas de câmbio do XTZ e tzBTC para fazer as conversões exigidas por esse tipo de aplicativo. O arquivo utils.ts contém uma função que nos ajudará aqui:


export const fetchExchangeRates = async (): Promise<{

  tzbtcPrice: number;

  xtzPrice: number;

} | null> => {

  const query = `

      query {

        overview { xtzUsdQuote },

        token(id: "KT1PWx2mnDueood7fEmfbBDKx1D9BAnnXitn") { price }

      }

    `;

  const res = await fetch(`https://analytics-api.quipuswap.com/graphql`, {

    method: "POST",

    headers: {

      "Content-Type": "application/json"

    },

    body: JSON.stringify({

      query

    })

  });

  if (res.status === 200) {

    const resData = await res.json();

    let xtzPrice = resData?.data?.overview?.xtzUsdQuote;

    let tzbtcPrice = resData?.data?.token?.price;

    // validação de 2 valores

    if (xtzPrice && tzbtcPrice) {

      xtzPrice = +xtzPrice;

      tzbtcPrice = +tzbtcPrice;

      if (!isNaN(xtzPrice) && !isNaN(tzbtcPrice)) {

        // O preço tzBTC é dado em XTZ pela API

        tzbtcPrice = tzbtcPrice * xtzPrice;

        return { tzbtcPrice, xtzPrice };

      }

    } else {

      return null;

    }

  } else {

    return null;

  }

};

Enter fullscreen mode Exit fullscreen mode

Usamos a API do QuipuSwap GraphQL para buscar essas taxas de câmbio. Após o recebimento das taxas de câmbio, analisamos a resposta da API e validamos o preço fornecido pelo XTZ e tzBTC. Esses preços são retornados pela função e podemos salvá-los no store. As taxas de câmbio são usadas, por exemplo, para calcular o valor total em dólar, bloqueado no contrato.

Na segunda parte, tokens de carteira e usuário = >


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

Top comments (0)