WEB3DEV

Cover image for Construindo um Dashboard DeFi: Um Guia para o Desenvolvedor
Fatima Lima
Fatima Lima

Posted on

Construindo um Dashboard DeFi: Um Guia para o Desenvolvedor

Image description

A descentralização está lentamente, mas de forma estável, ganhando impulso no mundo da tecnologia como um caso de uso popular para a blockchain. Devido a sua acessibilidade e durabilidade, os aplicativos estão gradualmente se voltando para a descentralização. A tecnologia financeira (FinTech) também começou a migrar para a blockchain, resultando no conceito de DeFi, ou finanças descentralizadas.

Um dashboard DeFi é uma plataforma que permite aos usuários acessar e monitorar fluxos dos ativos DeFi em tempo real, e analisar várias métricas. Estas plataformas têm o objetivo de tornar a interação com sistemas financeiros baseados em blockchain tão simples quanto possível.

Por exemplo, moedas criptográficas e tokens não fungíveis (NFTs) são agora as formas mais populares de troca na blockchain. Os painéis DeFi são projetados para ajudar alguém com experiência mínima de programação no acesso a ativos e execução de atividades autorizadas com esses ativos.

Aqui estão alguns exemplos de painéis de controle DeFi existentes, Zapper, Apeboard, and Zerion.

Neste artigo, aprenderemos sobre os painéis DeFi, por que eles são feitos e como fazer um de nossos próprios painéis.

A fim de acompanhar este tutorial, certifique-se de ter o seguinte:

  • Um editor de texto
  • Node.js instalado localmente
  • Conhecimento prático do React
  • Conhecimento prático de como uma blockchain funciona
  • Uma conta no Moralis

Conteúdos

  • Quais são as vantagens dos dashboards de DeFi?
  • Construindo um dashboard de DeFi
  • Configurando o Moralis no seu aplicativo
  • Autenticando usuários no dashboard DeFi
  • Transmitindo dados em tempo real
  • Implantando seu aplicativo

Quais são as vantagens dos painéis de controle DeFi?

Os painéis DeFi são particularmente importantes para os usuários de blockchain e criptomoeda porque permitem o fácil rastreamento de investimentos através de numerosas plataformas financeiras descentralizadas. Os painéis DeFi fornecem o seguinte para seus usuários:

Primeiro, eles são ótimos para um acesso fácil e rápido aos ativos e serviços de DeFi. O uso da maioria dos aplicativos típicos de blockchain tem sido considerado um desafio para os novatos e acessar os ativos DeFi e usar seus serviços individualmente envolve muito tempo e energia. Painéis de controle DeFi de cadeia múltipla ou cruzada, por exemplo, aceleram o processo de conversão de ativos através das blockchains.

O segundo é o crescimento da comunidade DeFi. Como os painéis DeFi são mais fáceis de usar pelos novatos, eles podem ser usados pelas comunidades DeFi para incorporar novas pessoas em seus projetos.

O terceiro é o rastreamento e relatório de atividades DeFi. A maioria dos projetos DeFi participa de uma variedade de atividades como pooling, fornecimento de liquidez, farming etc. O rastreamento de todas essas ações torna-se um desafio, mas os painéis de controle DeFi podem ajudar a manter o controle e a elaboração de relatórios sobre essas atividades.

Finalmente, há a visualização das atividades DeFi. Devido à complexidade das blockchains, as atividades DeFi podem ser ambíguas e exigir confirmação para certas transações que são executadas em segundo plano. Estas atividades podem ser visualizadas usando os painéis de controle DeFi.

Construindo um dashboard DeFi

Saber projetar websites não é suficiente para um desenvolvedor web. Para construir um bom dashboard DeFi, você precisará aprender mais alguns truques.

Construir painéis DeFi requer conhecimento de como funcionam as blockchains, como os valores são manipulados ou convertidos, criptografia de blockchains, e assim por diante.

Nas seções seguintes, construiremos um dashboard básico DeFi usando o servidor testnet Moralis e o React.

Configurando o servidor testnet Moralis

Você precisará configurar um novo servidor Moralis para acessar os recursos do servidor testnet Moralis em seu aplicativo.

Mainnet, testnet e servidores locais devchain são os servidores suportados pelo Moralis. A Mainnet (cadeia principal) é utilizada para a produção; e as atividades que são realizadas nela são genuínas e repercutem nos ativos reais. Os servidores Testnet são utilizados para fins de desenvolvimento; este servidor permite testar e experimentar sem colocar em risco os ativos reais. Os servidores locais devchain são similares aos servidores testnet, com a exceção de serem hospedados em uma máquina local, tornando-os disponíveis exclusivamente para desenvolvimento e uso local.

Utilizaremos o servidor de testes Moralis. Dê os seguintes passos para criar um:

No canto superior direito da página de servidores, clique no botão verde + Create a new Server e selecione Testnet Server no menu suspenso.

Image description

Um modal será aberto, mostrando as configurações do servidor. Preencha as informações solicitadas e selecione as cadeias que você deseja que o servidor utilize.

Image description

Clique no botão Add Instance depois de selecionar as cadeias que você deseja usar. Isto cria uma instância de servidor que aparece na página de servidores.

O botão View Information no final da seção do servidor pode ser usado para visualizar os detalhes de configuração do servidor. Como as informações neste modal são privadas, não as mantenha abertas enquanto você estiver fora de seu computador e restrinja quem tem acesso a elas.

Image description

Quando você dispensa o modal e seleciona o item do servidor no menu suspenso, aparece um botão Dashboard, que o leva ao dashboard do tipo banco de dados como mostrado abaixo.

Este dashboard acompanha todas as ações que acontecem no servidor atual. Como o servidor ainda é novo, ele ficará vazio por enquanto.
Image description

Configurando uma interface do usuário no React

Vamos usar o React para criar nosso template DeFi. Já criei um modelo para os objetivos deste artigo, mas você está livre para construir um front end para seu projeto e aplicar a lógica a ele.

Siga estes passos para usar o modelo que eu já criei.

Primeiro, execute o comando abaixo em um terminal em um diretório específico para clonar o repositório Github:

git clone https://github.com/wolz-CODElife/DeFidash-template.git
Enter fullscreen mode Exit fullscreen mode

Em seguida, execute este comando para mudar o diretório do terminal para o diretório do projeto:

cd DeFidash-template
Enter fullscreen mode Exit fullscreen mode

É fundamental instalar as dependências listadas no arquivo package.json antes de iniciar o servidor do aplicativo.

Instalando as dependências

Execute o seguinte comando em um terminal no diretório do projeto para instalar as dependências do aplicativo:

npm install
Enter fullscreen mode Exit fullscreen mode

Quando a instalação estiver concluída, verifique se os seguintes pacotes estão disponíveis:

Image description

Execute o seguinte comando no terminal para obter esta lista:

npm list
Enter fullscreen mode Exit fullscreen mode

Quando você inicia o servidor do aplicativo, você deve ver algo semelhante a isto em seu navegador.

Image description

Configurando o Moralis no seu aplicativo

Vamos adicionar alguma funcionalidade à nossa interface de usuário agora que ela está pronta e está funcionando. Para começar, devemos fornecer a URL do servidor Moralis e o ID do app da conta Moralis que criamos para nosso aplicativo.

Há duas constantes identificadas SERVER_URL e APP_ID em /src/services/MoralisConfig.js:

export const APP_ID = "YOUR_MORALIS_APP_ID" 
export const SERVER_URL = "YOUR_MORALIS_SERVER_URL"
Enter fullscreen mode Exit fullscreen mode

Substitua os valores pelos valores requeridos da conta Moralis. Queremos empacotar nosso aplicativo com MoralisProvider agora que temos essas credenciais. Isto ajudará na inicialização da Moralis assim que o pedido for enviado.

Importe MoralisProvider e o acrescente no arquivo index.js como um componente, como se vê abaixo:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { APP_ID, SERVER_URL } from './services/MoralisConfig';
ReactDOM.render(
  <React.StrictMode>
    <MoralisProvider serverUrl={SERVER_URL} appId={APP_ID}>
      <App />
    </MoralisProvider>
  </React.StrictMode>,
  document.getElementById('root')
);
Enter fullscreen mode Exit fullscreen mode

Nós importamos SERVER_URL e APP_ID do arquivo MoralisConfig.js no trecho de código acima; isto pode não ser um problema porque estamos desenvolvendo um aplicativo de teste.

É recomendado armazenar o URL do servidor e a ID do aplicativo como variáveis de ambiente para uso público ou aplicativos que lidam com dados reais. Engenheiros reversos podem ser capazes de acessar códigos fonte, e se forem encontradas credenciais particulares, elas podem ser usadas para comprometer a plataforma.

Autenticando usuários no dashboard DeFi

Se tudo acontecer conforme o planejado, devemos ser capazes de interagir e nos comunicar com o servidor Moralis a partir do nosso aplicativo neste momento. O primeiro recurso que queremos construir é a autenticação Moralis, pois precisamos vincular as carteiras dos usuários ao aplicativo, para que ele possa acessar seus ativos e transações.

Para implementar a autenticação, altere o seguinte código em

/src/pages/ConnectWallet.js:

import React, { useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
import styled from 'styled-components'
import { useMoralis } from "react-moralis";
const ConnectWallet = ({connected, setConnected}) => {
    const navigate = useNavigate()
    const { authenticate, isAuthenticated } = useMoralis();
    useEffect(() => {
        if(connected){
            navigate("/dashboard")        
        }
    }, [connected, navigate])
    const HandleMetaMask = () => {
        if (!isAuthenticated) {
            authenticate({signingMessage: "Log in using Moralis" })
            .then((user) => {
                if(user){
                    setConnected(true)
                }
            })
            .catch(error => {
                console.log(error);
            });
        }
    }
    const HandleWalletConnect = () => {
        if (!isAuthenticated) {
            authenticate({signingMessage: "Log in using Moralis", provider: "walletconnect", chainId: 56 })
            .then((user) => {
                if(user) {
                    setConnected(true)
                }
            })
            .catch(error => {
                console.log(error);
            });
        }
    }
  return (
    <SigninForm>
        <div className="title">
            <h1>Connect your wallet</h1>
            <p>Connect your wallet and know everything about NFT


<p id="gdcalert8" ><span style="color: red; font-weight: bold">>>>>>  gd2md-html alert: error handling inline image </span><br>(<a href="#">Back to top</a>)(<a href="#gdcalert9">Next alert</a>)<br><span style="color: red; font-weight: bold">>>>>> </span></p>

</p>
        </div>
        <div className="wallets">
            <button onClick={HandleMetaMask}>
                <img src="https://i.postimg.cc/C1v3V2Zp/image.png" alt="Meta Mask" />
                <h2>MetaMask</h2>
            </button>
            <button onClick={HandleWalletConnect}>
                <img src="https://i.postimg.cc/tChHs2wW/image.png" alt="Meta Mask" />
                <h2>Wallet Connect</h2>
            </button>
            <a href="https://metamask.io/download" target="_blank" rel="noopener noreferrer">I don't have a wallet</a>
        </div>
    </SigninForm>
  )
}
export default ConnectWallet

const SigninForm = styled.div`
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 100px;
    @media screen and (max-width: 648px) {
        padding: 100px 50px;
    }
    @media screen and (max-width: 420px) {
        padding: 100px 25px;
    }
    .title {
        text-align: center;
        p {
            font-size: 18px;
        }
    }
    .wallets {
        display: flex;
        flex-direction: column;
        margin-top: 56px;
        button {
            height: 72px;
            width: 452px;
            padding: 24px;
            border: 1px solid #D7D9F2;
            display: flex;
            flex-direction: row;
            align-items: center;
            background: none;
            margin-bottom: 16px;
            border-radius: 14px;
            cursor: pointer;
            transition: all ease 0.4s;
            @media screen and (max-width: 550px) {
                width: 350px;
                padding: 15px;
            }
            @media screen and (max-width: 410px) {
                width: 250px;
                h2 {
                    font-size: 15px;
                }
            }
            img {
                width: 48px;
                height: 48px;
                margin: 0px 48px;
                @media screen and (max-width: 550px) {
                    margin: 0px;
                    margin-right: 20px;
                }
            }
            &:hover {
                border: 1px solid #5A66F9;
                background: #F7FAFA;
            }
        }
        a {
            text-decoration: none;
            color: #111119;
            flex: 1;
            text-align: center;
            font-size: 18px;
            margin: 24px 0px;
            &:hover {
                color: #8A92FF;
            }
        }
    }
`
Enter fullscreen mode Exit fullscreen mode

O trecho de código inclui um componente do React chamado ConnectWallet que requer duas_ props_: connected e setConnected, que são processadores de estado que detectam se um usuário vinculou ou não sua carteira ao aplicativo.

Nós definimos uma constante navigate como uma função do Hook useNavigate() no componente ConnectWallet e usamos a função para redirecionar o usuário se ele ou ela não estiver conectado.

Também definimos duas constantes no Hook useMoralis(): authenticated, que é uma função para autenticação de usuários e isAuthenticated, que é um booleano que testa se o usuário está autenticado.

Então, toda vez que o componente é montado, o useEffect verifica se o usuário ainda está conectado. O programa navega para a rota /dashboard se o usuário estiver conectado.

Também temos dois métodos para lidar com conexões de carteiras: o HandleMetaMask, que processa a autenticação usando o provedor Moralis MetaMask e o HandleWalletConnect, que processa a autenticação utilizando o provedor Moralis walletconnect.

O componente produz então um componente estilizado chamado SigninForm que contém a estrutura HTML do componente ConnectWallet.

Transmitindo dados em tempo real em seu dashboard DeFi

Queremos obter as informações do usuário do servidor Moralis depois que elas tiverem sido autorizadas. Para isso, altere o seguinte código em /src/pages/Dashboard.js:

import React, { useEffect, useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import styled from 'styled-components'
import History from '../Layouts/History'
import Portfolio from '../Layouts/Portfolio'
import { IconCopy, LogosEthereum } from '../services/icons'
import { useMoralisWeb3Api, useMoralis } from "react-moralis";
import { APP_ID, SERVER_URL } from '../services/MoralisConfig'

const Dashboard = ({connected, setConnected}) => {
  const [address, setAddress] = useState("")
  const addressInput = useRef(null)
  const [nativeBal, setNativeBal] = useState("")
  const [activeTab, setActiveTab] = useState('Portfolio')
  const tabs = ["Portfolio", "History"]
  const navigate = useNavigate()
  const Web3Api = useMoralisWeb3Api();
  const { user, logout, Moralis } = useMoralis();

  useEffect(() => {
    Moralis.start({serverUrl: SERVER_URL, appId: APP_ID})
    if(!connected){
        navigate("/connectwallet")        
    }
    else{
      setAddress(user.get("ethAddress"))
      fetchNativeBalances()
    }
    // eslint-disable-next-line
}, [connected, navigate, user])
  const handleCopy = () => {
    let text = addressInput.current.value
    navigator.clipboard.writeText(text).then(() => {
      alert('Copied address to clipboard!');
    }, function(err) {
      console.error('Async: Could not copy text: ', err);
    });
  }
  const fetchNativeBalances = async () => {
    let options = {
      chain: "ropsten"
    }
    Web3Api.account.getNativeBalance(options).then(balance => {
      console.log(balance);
      let newNativeBalance = parseFloat(Moralis.Units.FromWei(balance.balance)).toFixed(2)
      setNativeBal(newNativeBalance)
    })
  };
  const HandleDisconnectWallet = () => {
    logout().then(() => {
      setConnected(false)
    })
  }
  return (
    <DashContainer>
      <h1 className='overview'>Overview <button onClick={HandleDisconnectWallet}>Disconnect Wallet</button></h1>
      <div className="header">
        <img src="https://i.postimg.cc/VsGFzCqn/image.png" alt="fakeqr" />
        <span>{address.slice(0, 5) + "..." + address.slice(-4)}</span>
        <button onClick={handleCopy}><IconCopy /></button>
        <input type="hidden" defaultValue={address} ref={addressInput} />
      </div>
      <div className="sub_header">
        <LogosEthereum />
        <h1>{nativeBal || 0} ETH</h1>
      </div>
      <div className="tabs">
        <div className="head">
          {tabs.map(menu => (
            <button key={menu} className={activeTab === menu? "active" : ""} onClick={() => setActiveTab(menu)}>{menu}</button>
          ))}
        </div>
        <div className="body">
          {activeTab === tabs[0] ?
            <Portfolio />
            :
            <History />
          }
        </div>
      </div>
    </DashContainer>
  )
}
export default Dashboard

const DashContainer = styled.div`
    padding: 100px;
    @media screen and (max-width: 768px) {
      padding: 100px 50px;
    }
    @media screen and (max-width: 498px) {
      padding: 100px 25px;
    }

    .overview {
      display: flex;
      align-items: center;
      justify-content: space-between;

      button {
        background: none;
        border: 1px solid #FF0000A6;
        padding: 8px 15px;
        border-radius: 6px;
        cursor: pointer;

        @media screen and (max-width: 498px) {
          transform: scale(0.8);  
        }
        &:hover {
          background: #FF0000A6;
          color: #FFFFFF;
          border: none;
        }
      }
    }
    .header {
      display: flex;
      align-items: center;
      img {
        border-radius: 50%;
        width: 40px;
        height: 40px;
        margin-right: 10px;
      }
      span {
        color: #8a8a8a;
        font-size: 15px;
      }
      button {
        color: #8a8a8a;
        background: none;
        border: none;
        outline: none;
        cursor: pointer;
        svg {
          width: 15px;
          height: 15px;
        }
      }
    }
    .sub_header {
      display: flex;
      flex-direction: column;
      align-items: center;
      margin: 20px 0px;
    }
    .tabs {
      margin: 30px 0px;
      box-shadow: 2px 2px 20px #D7D9F2C0;
      border-radius: 10px;

      .head {
        background: #FFFFFF;
        border-radius: 10px 10px 0px 0px;

        button {
          border: none;
          outline: none;
          cursor: pointer;
          padding: 10px 15px;
          background: none;
          &:hover {
            border-bottom: 2px solid #5A66F960;
          }
          &.active {
            border-bottom: 2px solid #5A66F9;
          }
        }
      }

      .body {
        background: #FFFFFFA5;
        padding: 20px;
        border-radius: 0px 0px 10px 10px;
      }
    }
`
Enter fullscreen mode Exit fullscreen mode

O trecho de código acima inclui um componente do React chamado Dashboard, que, como o componente ConnectWallet, recebe as duas props connected e setConnected.

Em seguida, definimos um conjunto de estados para o tratamento de dados. Também criamos uma lista de abas chamada Portfolio e History, que será usada para alternar entre o portfólio do usuário e as prévias do histórico de transações.

Também definimos certas utilidades Moralis; construímos um novo objeto de métodos chamado Web3Api do useMoralisWeb3Api() Hook e definimos user, logout e Moralis do useMoralis() Hook.

Após configurarmos os recursos, executamos um useEffect para ver se o usuário está conectado e então usamos o método fetchNativeBalances() para obter o saldo nativo do usuário. Se o usuário não estiver conectado, vai navegar para a rota /connectwallet.

Nós também implementamos os métodos handleCopy para copiar o endereço Ethereum do usuário, fetchNativeBalances para obter o saldo nativo atual do usuário na testnet ropsten (você pode mudar a cadeia para ver o saldo nativo dessa cadeia) e HandleDisconnectWallet para fazer o logout do usuário.

Finalmente, o componente Dashboard retorna um componente DashContainer estilizado que mostra o saldo nativo do usuário, bem como duas abas para exibir o portfólio do usuário e o histórico de transações.

O componente Portfolio é a primeira aba no dashboard que é mostrada por padrão. Este componente contém uma lista tabular de saldos de tokens na cadeia ETH da carteira do usuário.

Para implementar a aba do portfólio, altere o seguinte código em /src/Layouts/Portfolio.js:

import React, { useEffect, useState } from 'react'
import styled from 'styled-components'
import { useMoralisWeb3Api, useMoralis } from "react-moralis";
import { LogosEthereum } from '../services/icons'
import { APP_ID, SERVER_URL } from '../services/MoralisConfig';
const Portfolio = () => {
  const Web3Api = useMoralisWeb3Api();
  const { Moralis } = useMoralis()
  const [tokenBal, setTokenBal] = useState([])
  useEffect(() => {
    Moralis.start({serverUrl: SERVER_URL, appId: APP_ID})
    fetchTokenBalances()

    // eslint-disable-next-line
  }, [])
  const fetchTokenBalances = () => {
    Web3Api.account.getTokenBalances().then(balances => {
    let newBalances = []
    balances.map(bal => (
      newBalances.push({
        token_name: bal.symbol,
        token_logo: bal.logo,
        balance: parseFloat(Moralis.Units.FromWei(bal.balance)).toFixed(2)
      })
      ))
      setTokenBal(newBalances)
    })
  }

  return (
    <PortfolioContainer>
      <div className="table_responsive">
        <table>
          <thead>
            <tr>
              <th>Token</th>
              <th>Balance</th>
            </tr>
          </thead>
          {/* Table body */}
          <tbody>
            {tokenBal.length > 0?
              tokenBal.map(bal => (
                <tr key={bal.token_name}>
                  <td className='token_name'>{bal.token_logo && <img src={bal.token_logo} alt={bal.token_name} />} {bal.token_name}</td>
                  <td>{bal.balance} ETH</td>
                </tr>
              ))
            :
            <tr>
              <td colSpan="2">No token found</td>
            </tr>
            }
          </tbody>
        </table>
      </div>
    </PortfolioContainer>
  )
}
export default Portfolio
const PortfolioContainer = styled.div`
  .table_responsive {
    width: 100%;
    overflow-x: auto;
    .header {
      display: flex;
      align-items: center;
      svg {
        margin-right: 10px;
        width: 40px;
        height: 40px;
      }
    }
    table {
      border-collapse: collapse;
      width: 100%;
      margin: 20px 0px;
    }
    td, th {
      border: 1px solid #5A66F965;
      text-align: left;
      padding: 8px;
    }
    .token_name {
      display: flex;
      img {
        width: 20px;
        height: 20px;
        border-radius: 50%;
        margin-right: 15px;
      }
    }
    tr:nth-child(even) {
      background-color: #5A66F940;
    }
  }
`
Enter fullscreen mode Exit fullscreen mode

O componente Portfolio na amostra de código acima tem uma aba que condicionalmente apresenta linhas de saldos de tokens para o usuário atual. Uma linha com "Nenhum token encontrado" será apresentada se o usuário não tiver nenhum token na cadeia ropsten.

Quando o componente é montado, ele inicia um useEffect que executa o método fetchTokenBalances(). fetchTokenBalances() utiliza o Web3Api para recuperar os saldos atuais de tokens dos usuários e adiciona cada um deles a uma lista de newBalances antes de atualizar o estado do tokenBal.

Após configurarmos a aba do portfólio, precisaremos configurar uma aba para mostrar o histórico de transações do usuário.

Para adicionar a aba de histórico, altere o código em /src/Layouts/History.js para:

import React, { useEffect, useState } from 'react'
import styled from 'styled-components'
import { useMoralisWeb3Api, useMoralis } from "react-moralis";
import { APP_ID, SERVER_URL } from '../services/MoralisConfig';
import { IconCopy } from '../services/icons';
const History = () => {
  const Web3Api = useMoralisWeb3Api();
  const { Moralis } = useMoralis()
  const [transactions, setTransactions] = useState([])
  useEffect(() => {
    Moralis.start({serverUrl: SERVER_URL, appId: APP_ID})
    fetchTransactionHistory()
    // eslint-disable-next-line
  }, [])
  const fetchTransactionHistory = async () => {
    let options = {
      chain: "testnet",
    }
    Web3Api.account.getTransactions(options).then(transacts => {
      if(transacts.result.length > 0) {
        let newTransactions = []
        transacts.result.map(transaction => {
          newTransactions.push({
            hash: transaction.hash,
            sender: transaction.from_address,
            receiver: transaction.to_address,
            value: parseFloat(Moralis.Units.FromWei(transaction.value)).toFixed(3),
            status: transaction.receipt_status,
            timestamp: transaction.block_timestamp
          })  
        })
        setTransactions(newTransactions)
      }
    })
  };
  const handleCopy = (address) => {
    navigator.clipboard.writeText(address).then(() => {
      alert('Copied address to clipboard!');
    }, function(err) {
      console.error('Async: Could not copy text: ', err);
    });
  }

  return (
    <HistoryContainer>
      <div className="table_responsive">
        <table>
          <thead>
            <tr>
              <th>Transaction Hash</th>
              <th>Sender Address</th>
              <th>Receiver Address</th>
              <th>Amount</th>
              <th>Status</th>
              <th>Timestamp</th>
            </tr>
          </thead>
          {/* Table body */}
          <tbody>
            {transactions.length > 0?
              transactions.map(transaction => (
                <tr key={transaction.hash + transaction.timestamp}>
                  <td>
                    <div className='trans_address'>
                      {transaction.hash.slice(0, 6) + "..." + transaction.hash.slice(-4)} 
                      <button onClick={() => handleCopy(transaction.hash)}><IconCopy /></button>
                    </div>
                  </td>
                  <td>
                    <div className='trans_address'>
                      {transaction.sender.slice(0, 6) + "..." + transaction.sender.slice(-4)} 
                      <button onClick={() => handleCopy(transaction.sender)}><IconCopy /></button>
                    </div>
                  </td>
                  <td>
                    <div className='trans_address'>
                      {transaction.receiver.slice(0, 6) + "..." + transaction.receiver.slice(-4)} 
                      <button onClick={() => handleCopy(transaction.receiver)}><IconCopy /></button>
                    </div>
                  </td>
                  <td>{transaction.value} ETH</td>
                  <td>{transaction.status === "1"? <span className='success'>Successful</span> : <span className='pending'>Pending</span> }</td>
                  <td>{new Date(transaction.timestamp).toUTCString()}</td>
                </tr>
              ))
            :
            <tr>
              <td colSpan="6">No transactions found</td>
            </tr>
            }
          </tbody>
        </table>
      </div>
    </HistoryContainer>
  )
}
export default History
const HistoryContainer = styled.div`
  .table_responsive {
    width: 100%;
    overflow-x: auto;
    table {
      border-collapse: collapse;
      width: 100%;
      margin: 20px 0px;
    }
    td, th {
      border: 1px solid #5A66F965;
      text-align: left;
      padding: 8px;
      min-width: 150px;
    }

    .trans_address {
      display: flex;
      button {
        color: #8a8a8a;
        background: none;
        border: none;
        outline: none;
        cursor: pointer;
        svg {
          width: 15px;
          height: 15px;
        }
      }
    }

    .success {
      font-size: 10px;
      background: #00aa0060;
      border-radius: 20px;
      padding: 3px 7px;
    }

    .pending {
      font-size: 10px;
      background: #aaaa0060;
      border-radius: 20px;
      padding: 3px 7px;
    }
    tr:nth-child(even) {
      background-color: #5A66F940;
    }
  }
Enter fullscreen mode Exit fullscreen mode

No código acima, o componente History retorna um componente estilizado chamado HistoryContainer. O componente também chama o método fetchTransactionHistory(), que utiliza o utilitário Web3Api para coletar transações na cadeia testnet da carteira do usuário, assim que ela é montada.

O aplicativo deve estar funcionando perfeitamente e pronto para ser implantado após a edição dos arquivos necessários para os trechos de código acima mencionados.

Implantando o aplicativo

Para acesso público, você pode optar por hospedar o aplicativo em qualquer plataforma de hospedagem. Eu escolhi implantar o meu na Firebase.

Você pode ver o aplicativo implantado aqui.

Conclusão

Depois de ler este artigo, os leitores devem entender o que é um dashboard de DeFi, por que eles são feitos, e como construir um a partir do zero.

Você pode projetar um clone Zapper para colocar à prova suas habilidades de construção do painel de controle DeFi. Na área de comentários abaixo, sinta-se à vontade para fazer perguntas ou fazer sugestões.

Este artigo foi escrito por Joel Adewole e traduzido por Fátima Lima. Seu original pode ser lido aqui.

Top comments (0)