WEB3DEV

Cover image for Como Conectar Usuários ao Seu dApp com o Adaptador de Carteiras da Solana e o Scaffold
Paulo Gio
Paulo Gio

Posted on

Como Conectar Usuários ao Seu dApp com o Adaptador de Carteiras da Solana e o Scaffold

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*3rv-yBKeCJBZJoF8CcIcpw.jpeg

Visão geral

Está construindo na Solana e pronto para trazer seu dApp para a web? Você vai precisar de uma maneira de conectar suas ferramentas às carteiras dos usuários. Embora existam algumas maneiras de conectar seu dApp às carteiras dos usuários, a Solana criou algumas ferramentas úteis que facilitam o início: o Adaptador de Carteiras da Solana e o Scaffold de dApps da Solana.

O que você fará

  • Aprender o que é o Adaptador de Carteiras da Solana
  • Implantar o Scaffold de dApps da Solana
  • Personalizar seu ambiente
  • Criar um dApp simples para consultar uma Carteira Conectada

O que você precisa

  • Nodejs (versão 16.15 ou superior)
  • Uma carteira Solana (por exemplo, Phantom)
  • Experiência com o SDK Web3 da Solana e a Biblioteca de Token SPL da Solana
  • Experiência com Typescript e o pacote ts-node instalado
  • Conhecimento básico de HTML/CSS
  • Experiência em desenvolvimento web em frontend será útil, mas não necessária para seguir este tutorial. Vamos criar um projeto do Next.js usando o React. A comunidade Solana também suporta Vue, Angular e Svelte. Visite a página da comunidade para mais informações.

O que é o Adaptador de Carteiras

Já percebeu que muitos dApps da Solana têm uma interface de usuário (IU) semelhante para conectar carteiras?

https://miro.medium.com/v2/resize:fit:1100/format:webp/0*1utilKW43nLbNItf.png

Esse é o Adaptador de Carteiras da Solana (Solana Wallet Adapter, ou "SWA"), e você o vê em muitos lugares porque é fácil de usar, incluso em todas as carteiras mais comuns da Solana e regularmente mantido pela Comunidade Solana. Então, o que é isso? O SWA é um conjunto de adaptadores e componentes modulares de carteira em TypeScript para aplicativos Solana que permitem conectar facilmente seu dApp à carteira de escolha de seus usuários (mais de uma dúzia de carteiras da Solana são suportadas automaticamente!).

Aqui estão algumas razões para considerar o uso do SWA:

  • Código aberto, suportado pelo Solana Labs
  • Suporte a várias carteiras: permita que seus usuários usem a carteira de sua escolha sem criar muitos problemas para você ao adicionar novas carteiras
  • Inclui uma IU pronta e personalizável
  • Inclui suporte ao Anchor
  • Funcionalidades principais incorporadas: Conectar, desconectar, autoconectar
  • Suporta vários frameworks de frontend

Vamos experimentar!

Configure o ambiente

O SWA inclui suporte para vários frameworks de frontend:

  • React
  • Material-UI,
  • Ant Design
  • Angular Material UI
  • Vue (suportado pela comunidade)
  • Angular (suportado pela comunidade)
  • Svelte (suportado pela comunidade)

Você pode adicionar esses pacotes aos seus projetos existentes usando as instruções do npm aqui. Para este exemplo, no entanto, vamos criar um novo projeto usando o Scaffold de dApps da Solana. O Scaffold de dApps inclui o SWA e alguns componentes pré-construídos para que você comece a usar tudo rapidamente!

Observação: se você estiver adicionando o adaptador a um projeto existente, o SWA atual, no momento em que este texto foi escrito, não suporta o React 18. Use o React 17.

Crie um novo diretório de projeto em seu terminal com:

mkdir my-solana-dapp
cd my-solana-dapp
Enter fullscreen mode Exit fullscreen mode

Clone o Scaffold de dApps:

git clone https://github.com/solana-labs/dapp-scaffold.git .
Enter fullscreen mode Exit fullscreen mode

O . irá clonar o Scaffold no diretório do seu projeto sem criar um novo diretório dentro dele.

Digite ls no seu terminal para garantir que tudo foi copiado corretamente. Seu terminal deve se parecer com isto:

https://miro.medium.com/v2/resize:fit:1100/format:webp/0*HbDYVaEEiC3Gc95e.png

Instale as dependências:

npm install
# or
yarn install
Enter fullscreen mode Exit fullscreen mode

Também vá em frente e adicione a biblioteca SPL-token, que usaremos posteriormente neste exercício:

npm i @solana/spl-token
# or
yarn add @solana/spl-token
Enter fullscreen mode Exit fullscreen mode

Adicionando um Ponto de Extremidade RPC personalizado do QuickNode

Para construir na Solana, você precisará de um ponto de extremidade (endpoint) de API para se conectar à rede. Você pode usar nós públicos (que já estão integrados ao Scaffold) ou implantar e gerenciar sua própria infraestrutura; no entanto, se você quiser tempos de resposta 8x mais rápidos, pode deixar o trabalho pesado conosco. Veja por que mais de 50% dos projetos na Solana escolhem o QuickNode e inscreva-se para uma avaliação gratuita de 7 dias aqui.

Vamos lançar nosso nó na rede de desenvolvimento (devnet) da Solana, mas você pode lançar o nó que atenda às suas necessidades. Copie o link do provedor HTTP:

https://miro.medium.com/v2/resize:fit:1100/format:webp/0*KnphwcDioSUooqIr.png

Em seguida, navegue de volta ao seu terminal e crie um arquivo .env com o seguinte comando no diretório my-solana-dapp:

echo > .env
Enter fullscreen mode Exit fullscreen mode

Recomendamos o uso de um arquivo .env para proteger sua chave ao criar um site de produção.

Abra o seu projeto em um editor de código de sua escolha e navegue até o arquivo .env recém-criado. Declare duas variáveis, REACT_APP_SOLANA_RPC_HOST e REACT_APP_NETWORK. Cole seu URL de RPC na variável REACT_APP_SOLANA_RPC_HOST e defina sua variável REACT_APP_NETWORK para a rede da Solana que você estará usando, ou seja, rede de desenvolvimento, rede principal beta (mainnet-beta) ou rede de testes (testnet). Isso deve ser a mesma rede que você selecionou ao escolher o seu ponto de extremidade RPC. Seu arquivo deve ficar parecido com isso:

REACT_APP_SOLANA_RPC_HOST=https://example.solana-devnet.quiknode.pro/00000000000/
REACT_APP_NETWORK=devnet
Enter fullscreen mode Exit fullscreen mode

Precisamos também atualizar o arquivo next.config.js para usar essas variáveis de ambiente em nosso aplicativo Next.js. Abra o arquivo na raiz do seu projeto e substitua o conteúdo por este:

/** @type {import('next').NextConfig} */
const nextConfig = {
 reactStrictMode: true,
}

module.exports = {
 nextConfig,  
 env: {
   REACT_APP_SOLANA_RPC_HOST: process.env.REACT_APP_SOLANA_RPC_HOST,
   REACT_APP_NETWORK: process.env.REACT_APP_NETWORK
 }
}
Enter fullscreen mode Exit fullscreen mode

Agora atualize o ponto de extremidade do Scaffold abrindo ./src/contexts/ContextProvider.tsx e substituindo as linhas 20–21:

const network = WalletAdapterNetwork.Devnet;
   const endpoint = useMemo(() => clusterApiUrl(network), [network]);
Enter fullscreen mode Exit fullscreen mode

com

   const network = process.env.REACT_APP_NETWORK as WalletAdapterNetwork;
   const endpoint = process.env.REACT_APP_SOLANA_RPC_HOST;
Enter fullscreen mode Exit fullscreen mode

Estamos prontos! Vamos lançar nosso aplicativo!

npm run dev
# or
yarn dev
Enter fullscreen mode Exit fullscreen mode

Se você seguiu todas as etapas até este ponto, poderá acessar http://localhost:3000/ e ver seu Scaffold. Bom trabalho! Você deve ver algo assim:

https://miro.medium.com/v2/resize:fit:640/format:webp/0*Ylra3YH8AhhxHtQH.png

Sinta-se à vontade para clicar onde quiser e explorar o Scaffold. Você pode conectar uma carteira e solicitar um airdrop (rede de desenvolvimento e rede de testes), assinar uma mensagem ou enviar SOL para uma carteira aleatória.

Orientando-se com o Adaptador de Carteiras da Solana e o Scaffold

Antes de criarmos nosso componente, vamos dar uma olhada em nossa área de trabalho para entender melhor como tudo está funcionando. Sem entrar muito profundamente no que está acontecendo com o React aqui, vamos cobrir algumas peças essenciais relevantes para os dApps da Solana.

Botão de Conexão da Carteira

Verifique o arquivo /my-solana-dapp/src/components/AppBar.tsx. Você deve ver que importamos o WalletMultiButton de solana/wallet-adapter-react-ui. Esse botão, <WalletMultiButton className="btn btn-ghost mr-4" />, é como o usuário interage com nosso adaptador de carteira. Aqui também é possível notar o uso do método setAutoConnect para definir a opção de alternância de autoconexão do usuário. Não vamos alterar nada aqui, mas se você quiser definir ou desabilitar o autoConnect, pode usar esse recurso.

Contexto de Conexão

Agora, confira o arquivo /my-solana-dapp/src/contexts/ContextProvider.tsx. Este arquivo é onde atualizamos nosso ponto de extremidade e rede anteriormente e é a casa do Provedor de Contexto de Carteira (Wallet Context Provider), onde podemos configurar o adaptador de carteiras.

  • Primeiro, observe a variável wallets. Você notará uma lista de carteiras Solana comuns listadas. Tente comentar em uma ou mais carteiras com // ou remover os comentários em new SlopeWalletAdapter() (nas importações, linhas 4–11 E nas declarações, linhas 22–35). Atualize seu site e conecte sua carteira. Você deve notar que a lista de carteiras disponíveis mudou. Efetivamente, a variável wallets é passada para o adaptador de carteira por meio do componente do provedor de carteira (Wallet Provider Component) para definir quais carteiras serão permitidas em nosso dApp.
  • autoconnect determina como a carteira do usuário interage com o site no carregamento. Como discutimos anteriormente, nosso botão de alternância do AppBar permite que o usuário controle isso na IU do site.
  • onError informa ao nosso programa como lidar com erros
  • endpoint e network definem como seu aplicativo se conectará à rede Solana
  • O Scaffold já está configurado para nós, mas se você estiver configurando seu projeto sem o Scaffold, certifique-se de que este contexto esteja envolvido em seu aplicativo. Você pode ver onde isso é feito em /my-solana-dapp/src/pages/_app.tsx: <ContextProvider>, hierarquicamente, é o pai do nosso AppBar e ContentContainer.

Componentes

Antes de prosseguir, confira a pasta Components, em /my-solana-dapp/src/components. Você verá componentes associados a cada uma das ferramentas / botões preexistentes do nosso aplicativo (por exemplo, botão para fazer o Airdrop, para assinar mensagens, para enviar transações). Sinta-se à vontade para explorar alguns desses arquivos para ver como eles funcionam - nós vamos criar nossos próprios componentes muito em breve! Se você não está familiarizado com o React, tudo bem. Apenas saiba que cada um desses arquivos, ou componentes, é basicamente um bloco de construção para nosso site. Nós colocamos esses blocos de construção nos locais apropriados (por exemplo, você pode ver o RequestAirdrop chamado na página de visualização principal: /my-solana-dapp/src/views/home/index.tsx).

Crie um componente personalizado para o seu dApp​

Agora que temos nosso ambiente do dApp estabelecido e sabemos como ele funciona, vamos adicionar nosso componente! Para este exercício, vamos usar um script que desenvolvemos em outro tutorial para obter todas as contas de token associadas à carteira do proprietário (acessível aqui).

Crie um modelo de componente reutilizável​

Vamos criar um modelo de componente que podemos usar para este exercício e que você também pode usar para criar facilmente novos componentes no futuro.

Crie um novo arquivo no diretório components chamado template.tsx. No terminal, pare o servidor com um CTRL+C e digite o seguinte comando:

echo > ./src/components/template.tsx
Enter fullscreen mode Exit fullscreen mode

Abra template.tsx, cole este código e salve.

import { useConnection, useWallet } from '@solana/wallet-adapter-react';
import { FC, useState } from 'react';
import { notify } from "../utils/notifications";
//Adicione dependências de importação aqui

export const Template: FC = () => {
   const { connection } = useConnection();
   const { publicKey } = useWallet();

   //Variáveis de estado aqui

   //Scripts do dApp aqui

   return(<div>

   {/* Renderize resultados aqui */}

   </div>)   
}
Enter fullscreen mode Exit fullscreen mode

Configure seu componente

Isso cria um shell para um componente React que podemos usar para adicionar nossa própria funcionalidade! Copie o modelo e salve como um novo arquivo chamado GetTokens.tsx.

cp ./src/components/template.tsx ./src/components/GetTokens.tsx
Enter fullscreen mode Exit fullscreen mode

Em GetTokens.tsx, renomeie o componente alterando export const Template para:

export const GetTokens
Enter fullscreen mode Exit fullscreen mode

Para nossa ferramenta que analisa as contas de token de uma carteira, precisaremos adicionar algumas dependências adicionais para nos ajudar a interagir com a biblioteca de tokens SPL. Adicione-os abaixo de //Adicione dependências de importação aqui:

import { GetProgramAccountsFilter } from '@solana/web3.js';
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
Enter fullscreen mode Exit fullscreen mode

Vamos configurar uma nova variável de estado, tokenTable usando setState (se você não estiver familiarizado com o React, tudo bem — essa é uma das mágicas que ajudará nossos resultados a serem renderizados na página depois de encontrarmos o que procuramos). No modelo, adicione o seguinte código logo após o comentário //Variáveis de estado aqui:

   const [tokenTable, setTokenTable] = useState(null);
Enter fullscreen mode Exit fullscreen mode

Crie sua consulta

Já modificamos o código do nosso guia existente do QuickNode sobre Como Obter Todos os Tokens Mantidos por uma Carteira na Solana para que seja compatível com o React. Especificamente, em vez de usar console.log para exibir os resultados, queremos escrevê-los em uma tabela (como um elemento JSX) que poderá ser renderizada em nosso site.

Adicione este código abaixo da sua variável de estado //Scripts do dApp aqui e antes de return():

async function getTokenAccounts(wallet: string) {
       const filters:GetProgramAccountsFilter[] = [
           {
             dataSize: 165, // número de bytes
           },
           {
             memcmp: {
               offset: 32, // número de bytes
               bytes: wallet, // string codificada em base58
             },           
           }];
       const accounts = await connection.getParsedProgramAccounts(
           TOKEN_PROGRAM_ID, // nova chave pública -  PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
           {
             filters: filters,
           }
         );
       console.log(`Found ${accounts.length} token account(s) for wallet ${wallet}: `);
       if(accounts.length === 0) {
           return(<div>No Token Accounts Found</div>)
       }
       else{
           const rows = accounts.map((account,i)=>{
               //Analise os dados da conta
               const parsedAccountInfo:any = account.account.data;
               const mintAddress:string = parsedAccountInfo["parsed"]["info"]["mint"];
               const tokenBalance: number = parsedAccountInfo["parsed"]["info"]["tokenAmount"]["uiAmount"];
               return (
               <tr key={i+1}>
                   <td key={'index'}>{i+1}</td>
                   <td key={'mint address'}>{mintAddress}</td>
                   <td key={'balance'}>{tokenBalance}</td>
               </tr>)
           })
           const header = (<tr>
               <th>Token No.</th>
               <th>Mint Address</th>
               <th>Qty</th>
           </tr>)
           setTokenTable(<table>{header}{rows}</table>)
       }
   }
Enter fullscreen mode Exit fullscreen mode

Você verá que nosso método .map retorna uma linha <tr> para cada conta encontrada. Quando invocado, o método getTokenAccounts deve buscar todas as contas de token da cadeia, criar um elemento de tabela com os resultados e usar o React para adicionar o elemento de tabela ao nosso estado. Também adicionamos alguma lógica para informar ao usuário quando nenhuma conta de token SPL for encontrada.

Crie um manipulador de clique

Para invocar getTokenAccounts, crie uma função chamada onClick que podemos vincular a um botão em nossa página. O botão irá:

  1. Verificar se uma carteira está conectada. Podemos fazer isso verificando se publicKey é encontrado a partir do nosso método useWallet() do adaptador de carteira.
  2. Usar o try com getTokenAccounts para a carteira conectada (observe que precisamos converter nossa chave pública em uma string para usá-la nos parâmetros do filtro, usando .toString()).
  3. Lidar com erros.
const onClick = async () => {
       if (!publicKey) {
           console.log('error', 'Wallet not connected!');
           notify({ type: 'error', message: 'error', description: 'Wallet not connected!' });
           return;
       }
       try {
           await getTokenAccounts(publicKey.toString());

       } catch (error: any) {
           notify({ type: 'error', message: `Couldn't Find Token Accounts!`, description: error?.message });
           console.log('error', `Error finding Token Accounts! ${error?.message}`);
       }
   };
Enter fullscreen mode Exit fullscreen mode

Crie um botão

Dentro do return() de seu componente, você verá uma <div>. Crie um botão dentro dela que chamará nossa função onClick. Estamos usando o mesmo CSS utilizado em outras partes do modelo. Sinta-se à vontade para personalizar!

<div className="text-center">
       <button
               className="px-8 m-2 btn animate-pulse bg-gradient-to-r from-[#9945FF] to-[#14F195] hover:from-pink-500 hover:to-yellow-500"
               onClick={onClick}
           >
               <span>Get Token Accounts</span>
       </button>
       </div>
Enter fullscreen mode Exit fullscreen mode

Resultados da renderização

Agora exiba seus resultados chamando a variável de estado tokenTable que definimos com getTokenAccounts. Dentro da sua div e após o comentário /* Renderize resultados aqui */, adicione:

           <div>{tokenTable}</div>
Enter fullscreen mode Exit fullscreen mode

Como não definimos uma variável até depois da nossa consulta de tokens, isso ficará em branco até que o programa encontre algo.

E é assim que fica nosso componente agora que terminamos:

import { useConnection, useWallet } from '@solana/wallet-adapter-react';
import { FC, useState } from 'react';
import { notify } from "../utils/notifications";
//Adicione dependências de importação aqui
import { GetProgramAccountsFilter } from '@solana/web3.js';
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";

export const GetTokens: FC = () => {
   const { connection } = useConnection();
   const { publicKey } = useWallet();

   //Variáveis de estado aqui
   const [tokenTable, setTokenTable] = useState(null);

   //Scripts do dApp aqui
   async function getTokenAccounts(wallet: string) {
       const filters:GetProgramAccountsFilter[] = [
           {
             dataSize: 165, // número de bytes
           },
           {
             memcmp: {
               offset: 32, // número de bytes
               bytes: wallet, // string codificada em base58
             },           
           }];
       const accounts = await connection.getParsedProgramAccounts(
           TOKEN_PROGRAM_ID, // nova chave pública -  PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
           {
             filters: filters,
           }
         );
       console.log(`Found ${accounts.length} token account(s) for wallet ${wallet}: `);
       if(accounts.length === 0) {
           return(<div>No Token Accounts Found</div>)
       }
       else{
           const rows = accounts.map((account,i)=>{
               //Analise os dados da conta
               const parsedAccountInfo:any = account.account.data;
               const mintAddress:string = parsedAccountInfo["parsed"]["info"]["mint"];
               const tokenBalance: number = parsedAccountInfo["parsed"]["info"]["tokenAmount"]["uiAmount"];
               return (
               <tr key={i+1}>
                   <td key={'index'}>{i+1}</td>
                   <td key={'mint address'}>{mintAddress}</td>
                   <td key={'balance'}>{tokenBalance}</td>
               </tr>)
           })
           const header = (<tr>
               <th>Token No.</th>
               <th>Mint Address</th>
               <th>Qty</th>
           </tr>)
           setTokenTable(<table>{header}{rows}</table>)
       }

   }

   const onClick = async () => {
       if (!publicKey) {
           console.log('error', 'Wallet not connected!');
           notify({ type: 'error', message: 'error', description: 'Wallet not connected!' });
           return;
       }
       try {
           await getTokenAccounts(publicKey.toString());

       } catch (error: any) {
           notify({ type: 'error', message: `Couldn't Find Token Accounts!`, description: error?.message });
           console.log('error', `Error finding Token Accounts! ${error?.message}`);
       }
   };

   return(<div>
       <div className="text-center">
       <button
               className="px-8 m-2 btn animate-pulse bg-gradient-to-r from-[#9945FF] to-[#14F195] hover:from-pink-500 hover:to-yellow-500"
               onClick={onClick}
           >
               <span>Get Token Accounts</span>
       </button>
       </div>
   {/* Render Results Here */}
       <div>{tokenTable}</div>
   </div>)
}
Enter fullscreen mode Exit fullscreen mode

Ótimo trabalho! Você agora criou seu primeiro componente do dApp usando o Solana Scaffold. Hora de adicioná-lo ao site.

Implante seu componente

Agora vamos adicionar nosso componente à nossa página inicial. Abra a visualização inicial, /my-solana-dapp/src/views/home/index.tsx. Como criamos um novo componente, devemos importá-lo antes de usá-lo. Adicione esta importação à linha 14:

import { GetTokens } from 'components/GetTokens';
Enter fullscreen mode Exit fullscreen mode

Vamos adicionar nosso componente abaixo do botão do Airdrop existente e exibir o saldo da carteira:

       <div className="text-center">
         <RequestAirdrop />
         {/* {wallet.publicKey && <p>Public Key: {wallet.publicKey.toBase58()}</p>} */}
         {wallet && <p>SOL Balance: {(balance || 0).toLocaleString()}</p>}
       </div>
       {/* ADICIONE ISSO 👇 */}
       <div>
         <GetTokens/>
       </div>
Enter fullscreen mode Exit fullscreen mode

Não sei você, mas estou pronto para ver isso em ação. Vamos executar!

npm run dev
# or
yarn dev
Enter fullscreen mode Exit fullscreen mode

Aí está o nosso lindo botão!

https://miro.medium.com/v2/resize:fit:640/format:webp/0*OKxwJkY1RVO7kLXn.png

Se você for como eu e clicar várias vezes, provavelmente receberá uma mensagem de erro porque ainda não conectou uma carteira (lembra quando configuramos aquele erro?):

https://miro.medium.com/v2/resize:fit:640/format:webp/0*z2k6QmVEaiP8U4Bf.png

Conecte sua carteira clicando em "Selecionar Carteira" (Select Wallet) no canto superior direito. Selecione a carteira desejada. Você deve ver seu saldo de SOL assim que sua carteira estiver conectada.

Agora, você pode clicar em "Obter Contas de Token" (Get Token Accounts)... BOOM! Você está vendo algo parecido com isso?

Se você ainda não tem nenhum token em sua carteira, confira nosso guia sobre a criação de NFTs com a Candy Machine para começar.

Está se divertindo? Sinta-se à vontade para continuar construindo e até mesmo adicionar algum CSS personalizado para deixar a tabela com a aparência que desejar!

Encerramento

Parabéns! Você conseguiu! E cobrimos muita coisa. Depois de concluir este exercício, você agora pode construir seus próprios dApps com o Scaffold para dapps da Solana e o adaptador de carteiras. Esta é uma excelente base para mergulhar em tudo o que há por aí, então iremos construir mais componentes a partir dessa base no futuro!

Inscreva-se em nossa newsletter para obter mais artigos e guias sobre a Solana. Se você tiver algum feedback, sinta-se à vontade para nos contatar via Twitter. Você sempre pode conversar conosco em nosso servidor da comunidade no Discord, com alguns dos desenvolvedores mais legais que você já conheceu. :)

Artigo original publicado por Arpan Mondal. Traduzido por Paulinho Giovannini.

Top comments (0)