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
Svelte = > https://svelte.dev/
TypeScript = > https://www.typescriptlang.org/
Sass = > https://sass-lang.com/
Vite = > https://vitejs.dev/
Taquito = > https://tezostaquito.io/
Farol = > https://docs.walletbeacon.io/
Repo do GitHub com o dapp = > https://github.com/claudebarde/tezos-dev-portal-tutorial
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
Em seguida, instalaremos todas as dependências necessárias para o dapp:
npm install --save-dev sass
npm install @taquito/taquito @taquito/beacon-wallet
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
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"
}
}
});
};
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 HTMLFornecemos um caminho para o Beacon SDK
Fornecemos polyfills para
readable-stream
estream
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>
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
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;
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
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>
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%;
}
}
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";
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 Taquitostore
é um recurso Svelte para gerenciar o estado do dappDo arquivo
config.ts
, importamosrpcUrl
( o URL do nó RPC Tezos ) e odexAddress
, o endereço do contrato de Liquidity BakingStorage
é um tipo personalizado que representa o tipo de assinatura do armazenamento LB DEXfetchExchangeRates
é 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 }
]);
}
});
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;
}
};
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.
Oldest comments (0)