本文介绍了如何使用Solana Wallet Adapter和dApp Scaffold构建与用户钱包连接的dApp。内容包括环境设置、组件创建、API集成等步骤,并提供了详细的代码示例。通过本指南,开发者能够快速上手Solana生态并构建自己的去中心化应用。
正在构建 Solana 并准备将你的 dApp 推向网络吗?你需要一种方法将你的工具与用户的钱包连接起来。虽然有几种方法可以将你的 dApp 连接到用户的钱包,Solana 创建了一些方便的工具,让入门变得简单: Solana Wallet Adapter 和 Solana dApp Scaffold。
有没有注意到很多 Solana dApp 在连接钱包时都有类似的用户界面?
这就是 Solana Wallet Adapter(“SWA”),你能在很多地方看到它,因为它易于使用,涵盖了所有最常见的 Solana 钱包,并且由 Solana 社区定期维护。那么它是什么呢?SWA 是一组模块化的 TypeScript 钱包适配器和组件,用于 Solana 应用程序,允许你轻松将 dApp 连接到用户选择的钱包(开箱即用支持十几个 Solana 钱包!)。
考虑使用 SWA 的一些理由:
开源,由 Solana-labs 支持
多钱包支持:允许用户使用他们选择的钱包,而不必让你为支持新钱包而头疼
包含现成的可定制用户界面
包含 Anchor 支持
内置关键功能:连接、断开连接、自动连接
支持多个前端框架
让我们尝试一下吧!
SWA 支持多种前端框架:
React
Material-UI
Ant Design
Angular Material UI
Vue(社区支持)
Angular(社区支持)
Svelte(社区支持)
你可以使用 npm 指令将这些包添加到现有项目中,具体可以在 这里。然而,此示例将创建一个使用 Solana dApp Scaffold 的新项目。dApp Scaffold 包括 SWA 和一些预构建的组件,让你快速启动!
注意:如果你要将适配器添加到现有项目中,在写作本文时当前的 SWA 不支持 React 18。请使用 React 17。
在终端中创建一个新的项目目录:
mkdir my-solana-dapp
cd my-solana-dapp
克隆 dApp Scaffold:
git clone https://github.com/solana-labs/dapp-scaffold.git .
代码中的 **.**
将在你的项目目录中克隆该脚手架,而不会在其中创建新目录。
在终端中输入 ls 以确保一切都正确复制。你的终端应该看起来像这样:
安装依赖项:
npm install
## 或
yarn install
去添加 SPL-token 库,我们将在本练习中后面使用:
npm i @solana/spl-token
## 或
yarn add @solana/spl-token
要在 Solana 上构建,你需要一个 API 端点来连接网络。你可以使用公共节点(这些节点已经集成到脚手架中),或者部署和管理自己的基础设施;然而,如果你想要 8 倍更快的响应时间,你可以让我们来完成繁重的工作。
我们将要在 Solana Devnet 下启动节点,但你可以启动适合你需求的节点。复制 HTTP 提供者链接:
然后,回到你的终端,从你的 my-solana-dapp 目录中使用以下命令创建 .env 文件:
echo > .env
在创建生产站点时,我们建议使用 .env 来保护你的密钥。
在你选择的代码编辑器中打开你的项目,并导航到新创建的 .env 文件。声明两个变量 REACT_APP_SOLANA_RPC_HOST 和 REACT_APP_NETWORK。将你的 RPC URL 粘贴到 REACT_APP_SOLANA_RPC_HOST 变量中,并将 REACT_APP_NETWORK 设置为你将使用的 Solana 集群(devnet、mainnet-beta 或 testnet)。这应该与选择 RPC 端点时相同的网络。你的文件应该类似于:
REACT_APP_SOLANA_RPC_HOST=https://example.solana-devnet.quiknode.pro/00000000000/
REACT_APP_NETWORK=devnet
我们还必须更新 next.config.js 以在我们的 Next.js 应用程序中使用这些环境变量。在你的项目根目录中打开文件,并用以下内容替换:
/** @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
}
}
现在通过打开 ./src/contexts/ContextProvider.tsx 来更新脚手架的端点,并替换第 20-21 行:
const network = WalletAdapterNetwork.Devnet;
const endpoint = useMemo(() => clusterApiUrl(network), [network]);
改为:
const network = process.env.REACT_APP_NETWORK as WalletAdapterNetwork;
const endpoint = process.env.REACT_APP_SOLANA_RPC_HOST;
一切准备就绪!让我们启动我们的应用程序!
npm run dev
## 或
yarn dev
如果你遵循了所有步骤,现在应该能够访问 http://localhost:3000/ 并看到你的脚手架。干得好!你应该看到类似于下图的内容:
随意点击并探索脚手架。你可以连接钱包并请求空投(devnet 和 testnet),签名消息,或者向随机钱包发送 SOL。
在创建我们的组件之前,让我们看看工作区周围,以便更好地理解一切是如何运作的。在这里不深入 React 的什么内容之前,让我们涵盖与 Solana dApps 相关的一些基本部分。
查看 /my-solana-dapp/src/components/AppBar.tsx。你应该看到我们从 solana/wallet-adapter-react-ui 导入了 WalletMultiButton。这个按钮,<WalletMultiButton className="btn btn-ghost mr-4" />
是用户与我们钱包适配器交互的方式。你还会在这里注意到 setAutoConnect 方法的使用,设置用户切换。我们不会在这里进行更改,但如果你想设置或禁用 autoConnect,可以使用这种形式。
接下来查看 /my-solana-dapp/src/contexts/ContextProvider.tsx。这个文件是我们先前更新端点和网络的地方,也是 Wallet Context Provider 的主控,这里我们可以配置钱包适配器。
首先,注意我们的 wallets 变量。你会注意到列出了常见的 Solana 钱包列表。尝试注释掉一个或多个钱包,用 // 或者取消 new SlopeWalletAdapter() 的注释(在导入中,第 4-11 行,以及在声明中,第 22-35 行)。刷新你的网站并连接你的钱包。你会注意到可用钱包的列表已经更改。实际上,wallets 变量通过 Wallet Provider 组件传递给钱包适配器,以确定哪些钱包将被允许在我们的 dApp 上使用。
autoconnect 确定用户的钱包在加载时如何与站点互动。正如我们前面讨论的,我们的 AppBar 切换允许用户控制这部分内容在站点的用户界面上。
onError 告诉我们的程序如何处理错误
endpoint 和 network 设置你的应用将如何连接到 Solana 网络
脚手架已为我们配置,但如果你在不使用脚手架的情况下设置项目,你需要确保这个上下文环绕你的应用。你可以在 /my-solana-dapp/src/pages/_app.tsx 看到这是如何完成的:<ContextProvider>
是我们 AppBar 和 ContentContainer 的父组件。
在继续之前,查看组件文件夹 /my-solana-dapp/src/components。你将看到与我们应用的每个预先存在的工具/按钮相关的组件(例如,Airdrop、Sign Message、Send Transaction)。随意探索其中一些文件以查看它们是如何工作的——我们很快将创建自己的组件!如果你不熟悉 React,那也没关系。只需知道这些文件,或组件,基本上是我们网站的构建块。我们将这些构建块放在适当的地方(例如,你可以在主页查看 RequestAirdrop 被调用:/my-solana-dapp/src/views/home/index.tsx)。
现在我们已经建立了 dApp 环境并了解了它的工作原理,让我们添加我们的组件!在这个练习中,我们将使用我们在另一个教程中开发的一段脚本,获取与一个所有者的钱包关联的所有令牌账户(可在这里访问)。
让我们制作一个组件模板,以便在这个练习中使用,并且你也可以在将来轻松创建新组件。
在组件目录中创建一个名为 template.tsx 的新文件。在终端中,按 CTRL+C 停止服务器,并输入以下命令:
echo > ./src/components/template.tsx
打开 template.tsx 并粘贴以下代码,然后保存。
import { useConnection, useWallet } from '@solana/wallet-adapter-react';
import { FC, useState } from 'react';
import { notify } from "../utils/notifications";
// 在这里添加导入依赖项
export const Template: FC = () => {
const { connection } = useConnection();
const { publicKey } = useWallet();
// 状态变量在这里
// dApp 脚本在这里
return(<div>
{/* 在这里渲染结果 */}
</div>)
}
这创建了一个我们可以用来添加自己功能的 React 组件的框架!复制模板并保存为一个名为 GetTokens.tsx 的新文件。
cp ./src/components/template.tsx ./src/components/GetTokens.tsx
在 GetTokens.tsx 中,通过将 export const Template 更改为:
export const GetTokens
对于我们查看钱包令牌账户的工具,我们需要向 SPL Token Library 添加一些额外的依赖项以帮助我们进行交互。在 Add import dependencies here 下添加这些:
import { GetProgramAccountsFilter } from '@solana/web3.js';
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
让我们使用 setState 设置一个新的状态变量 tokenTable(如果你不熟悉 React 那也没关系——这是一点魔法,将帮助我们的结果在找到目标后渲染到页面)。在模板中的 State Variables here 注释后添加以下代码:
const [tokenTable, setTokenTable] = useState(null);
我们稍微修改了现有的 QuickNode 指南:如何获取钱包持有的所有令牌 的代码,以便更加兼容 React。特别是我们希望将结果写入一个可以在我们的网站上渲染的表格(作为一个 JSX 元素),而不是使用 console.log 来显示结果。
在 //dApp Scripts Here 下,并在 return() 之前添加这段代码:
async function getTokenAccounts(wallet: string) {
const filters:GetProgramAccountsFilter[] = [\
{\
dataSize: 165, // 字节数\
},\
{\
memcmp: {\
offset: 32, // 字节数\
bytes: wallet, // base58 编码字符串\
},\
}];
const accounts = await connection.getParsedProgramAccounts(
TOKEN_PROGRAM_ID, // new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
{
filters: filters,
}
);
console.log(`找到了 ${accounts.length} 个钱包 ${wallet} 的令牌账户:`);
if(accounts.length === 0) {
return(<div>未找到令牌账户</div>)
}
else{
const rows = accounts.map((account,i)=>{
// 解析账户数据
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>令牌编号</th>
<th>铸造地址</th>
<th>数量</th>
</tr>)
setTokenTable(<table>{header}{rows}</table>)
}
}
你会看到我们的 .map 方法为找到的每个账户返回了一个 <tr>
行。当被调用时,getTokenAccounts 方法应该从链上获取所有令牌账户,创建一个包含结果的表格元素,并使用 React 将表格元素添加到状态中。我们还添加了一些逻辑,让用户知道是否找不到 SPL 令牌账户。
为了调用 getTokenAccounts,创建一个名为 onClick 的函数,我们可以将其绑定到页面上的一个按钮。按钮将:
检查钱包是否已连接。我们可以通过检查 publicKey 是否在 useWallet() 方法中找到来做到这一点。
尝试获取已连接钱包的 getTokenAccounts(请注意,我们需要将公钥转换为字符串才能在我们的过滤参数中使用,使用 .toString())。
处理错误。
const onClick = async () => {
if (!publicKey) {
console.log('错误', '钱包未连接!');
notify({ type: 'error', message: '错误', description: '钱包未连接!' });
return;
}
try {
await getTokenAccounts(publicKey.toString());
} catch (error: any) {
notify({ type: 'error', message: `未能找到令牌账户!`, description: error?.message });
console.log('错误', `寻找令牌账户时出错! ${error?.message}`);
}
};
在组件的 return() 中,你会看到一个 <div>
。在其中创建一个按钮,调用我们的 onClick 函数。我们将使用与模板中其他部分相同的 CSS。随意自定义!
<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>获取令牌账户</span>
</button>
</div>
现在通过调用我们使用 getTokenAccounts 设置的 tokenTable 状态变量来显示结果。在你的 div 中,在 Render Results Here 注释之后添加:
<div>{tokenTable}</div>
由于我们在令牌查询之前没有定义一个变量,所以在程序找到内容之前,这将是空白的。
这是我们完成时组件的样子:
import { useConnection, useWallet } from '@solana/wallet-adapter-react';
import { FC, useState } from 'react';
import { notify } from "../utils/notifications";
//添加导入依赖项
import { GetProgramAccountsFilter } from '@solana/web3.js';
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
export const GetTokens: FC = () => {
const { connection } = useConnection();
const { publicKey } = useWallet();
// 状态变量在这里
const [tokenTable, setTokenTable] = useState(null);
// dApp 脚本在这里
async function getTokenAccounts(wallet: string) {
const filters:GetProgramAccountsFilter[] = [\
{\
dataSize: 165, // 字节数\
},\
{\
memcmp: {\
offset: 32, // 字节数\
bytes: wallet, // base58 编码字符串\
},\
}];
const accounts = await connection.getParsedProgramAccounts(
TOKEN_PROGRAM_ID, // new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
{
filters: filters,
}
);
console.log(`找到了 ${accounts.length} 个钱包 ${wallet} 的令牌账户:`);
if(accounts.length === 0) {
return(<div>未找到令牌账户</div>)
}
else{
const rows = accounts.map((account,i)=>{
// 解析账户数据
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>令牌编号</th>
<th>铸造地址</th>
<th>数量</th>
</tr>)
setTokenTable(<table>{header}{rows}</table>)
}
}
const onClick = async () => {
if (!publicKey) {
console.log('错误', '钱包未连接!');
notify({ type: 'error', message: '错误', description: '钱包未连接!' });
return;
}
try {
await getTokenAccounts(publicKey.toString());
} catch (error: any) {
notify({ type: 'error', message: `未能找到令牌账户!`, description: error?.message });
console.log('错误', `寻找令牌账户时出错! ${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>获取令牌账户</span>
</button>
</div>
{/* 在这里渲染结果 */}
<div>{tokenTable}</div>
</div>)
}
太棒了!你已经使用 Solana 脚手架构建了你的第一个 dApp 组件。现在是将其添加到网站的时机。
现在让我们把我们的组件添加到主页。打开主页视图 /my-solana-dapp/src/views/home/index.tsx。既然我们创建了一个新组件,就必须先导入它。在第 14 行添加这个导入:
import { GetTokens } from 'components/GetTokens';
让我们将我们的组件添加到现有的空投按钮和钱包余额显示的下面:
<div>
<RequestAirdrop />
{/* ... */}
</div>
{/* 在这里添加 👇 */}
<div>
<GetTokens/>
</div>
我不知道你怎么想,但我准备好看到这个东西运作了。让我们运行一下!
npm run dev
## 或
yarn dev
我们的美丽按钮出现了!
如果你像我一样有点急于探索,你可能会收到错误消息,因为你还没有连接钱包(记得我们设置了那个错误?):
点击右上角的 选择钱包 以连接你的钱包。选择你喜欢的钱包。连接后,你应该能看到你的 Solana 余额。
好了,现在 你可以点击“获取令牌账户”... 嘣!你看到这样的内容吗?
如果你钱包中还没有任何代币,可以查看我们的 使用 Candy Machine 铸造 NFT 的指南 来开始。
觉得好玩吗?随意继续在此基础上构建,甚至加入一些自定义 CSS,使表格看起来符合你想要的样子!
恭喜你!你完成了!我们覆盖了很多内容。完成本练习后,你现在可以使用 Solana dApp Scaffold 和 Wallet Adapter 构建自己的 dApp。这是跳入各种事物的绝佳基础,因此我们将在未来从这个基础上构建更多组件!
觉得有用吗?查看我们其他的一些 Solana 教程 这里。订阅我们的 通讯,获取更多关于 Solana 的文章和指南。随时通过 Twitter 与我们联系。如有反馈,也欢迎随时与我们交流,我们在 Discord 的社区服务器上,你将遇到一些你见过的最酷的开发者 😎
- 原文链接: quicknode.com/guides/sol...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!