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?
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
Clone o Scaffold de dApps:
git clone https://github.com/solana-labs/dapp-scaffold.git .
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:
Instale as dependências:
npm install
# or
yarn install
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
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:
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
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
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
}
}
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]);
com
const network = process.env.REACT_APP_NETWORK as WalletAdapterNetwork;
const endpoint = process.env.REACT_APP_SOLANA_RPC_HOST;
Estamos prontos! Vamos lançar nosso aplicativo!
npm run dev
# or
yarn dev
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:
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 emnew 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ávelwallets
é 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
enetwork
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
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>)
}
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
Em GetTokens.tsx
, renomeie o componente alterando export const Template
para:
export const GetTokens
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";
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);
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>)
}
}
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á:
- Verificar se uma carteira está conectada. Podemos fazer isso verificando se
publicKey
é encontrado a partir do nosso métodouseWallet()
do adaptador de carteira. - Usar o
try
comgetTokenAccounts
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()
). - 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}`);
}
};
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>
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>
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>)
}
Ó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';
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>
Não sei você, mas estou pronto para ver isso em ação. Vamos executar!
npm run dev
# or
yarn dev
Aí está o nosso lindo botão!
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?):
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.
Oldest comments (0)