以太坊 - 如何使用QuickNode SDK构建ERC20代币余额应用程序 - Quicknode

  • QuickNode
  • 发布于 2025-02-12 23:13
  • 阅读 34

本文介绍了如何使用QuickNode SDK构建一个展示ERC20代币余额的React应用程序。通过使用QuickNode SDK,开发者可以简化与区块链的交互,无需直接处理智能合约,可方便地获取代币余额和其他信息。文章详细描述了设置环境、创建必要组件以及实现应用的步骤,并包含代码示例,适合有一定前端开发经验的读者。

概述

创建一个应用程序来显示 ERC20 代币余额可以简单或复杂,具体取决于你的方法。传统的方法是使用像 ethers.js 这样的库直接与以太坊区块链进行交互并管理智能合约 ABI,这既费时又技术要求高。幸运的是,QuickNode SDK 简化了这个过程。它是一个强大的工具包,使得与 QuickNode 基础设施的工作变得简单,只需要一个函数就能检索代币余额。

本指南将说明如何使用 QuickNode SDK 构建一个 ERC20 代币余额应用程序。

你需要的东西

你将要做的

  • 了解获取 ERC20 代币数据的传统方法
  • 学习如何利用 QuickNode SDK 获取以太坊上的代币余额
  • 构建一个使用 QuickNode SDK 显示 ERC20 代币余额的 React 应用

传统方式

传统上,要获取 ERC20 代币的余额,你需要使用像 ethers.js 的库直接与区块链进行交互,需要对智能合约 ABI 有了解并手动处理多个合约交互。你可以在我们的 综合指南 中了解更多关于这种方法的信息。

QuickNode SDK 通过提供一个简单的接口来与代币余额和其他区块链数据进行交互,从而简化了这个过程,而无需直接与合约交互。

什么是 QuickNode SDK?

QuickNode SDK 是一个工具,简化了与 EVM 区块链(如以太坊、Polygon 和 Arbitrum)的交互。它提供易于使用的函数来获取代币元数据、余额和转账历史,而不需要手动与区块链或智能合约进行交互。

到目前为止,SDK 主要围绕其核心模块展开。

核心 组件负责通过你的端点启用 RPC 功能,支持通过标准和扩展的 RPC 操作进行交互。在本文撰写时,核心模块支持 20 个 EVM 兼容链。支持的链和更多信息可以在 核心概述页面 中找到。

QuickNode SDK 的一些函数具有分页和筛选支持,这在构建去中心化应用程序时可能会很有用。在本指南中,我们将使用分页规范。

设置 QuickNode SDK

要有效利用 QuickNode SDK 中的 Token 和 NFT API v2 功能,我们需要设置一个具有特定捆绑附加功能的 QuickNode 端点。此设置可无缝访问 QuickNode 提供的各种功能,允许与区块链数据进行广泛的交互。如果你还没有,首先在 此处 创建一个免费的 QuickNode 账户。

一旦拥有账户,你便可以在 QuickNode 端点上免费启用 Token 和 NFT API v2 并进行配置信息如下:

  1. 登录到你的 QuickNode 账户,导航到仪表板。
  2. 选择或创建一个新的以太坊节点。
  3. 在节点仪表板上,寻找附加功能部分并找到 Token 和 NFT API v2 捆绑套餐。
  4. 为所选节点启用这个免费的附加功能。

QuickNode 端点附加功能

启用附加功能后,你的 QuickNode 端点即可使用 Token 和 NFT API v2 的增强功能与区块链进行交互。复制并保存你的端点,因为你将在下一部分中需要它。

ERC20 代币余额应用开发

在开始编码之前,让我们进一步了解应用的主要组件和功能,并查看完成后应用的外观。

  • QuickNode SDK: 用于与区块链数据交互,从指定钱包地址提取 ERC20 代币余额。

  • SearchBar: 允许用户输入他们想查询的钱包地址的组件。

  • TokenList: 显示与该钱包相关的 ERC20 代币及其余额的展示组件。

  • Pagination: 实现以分页方式浏览代币列表,每页显示有限数量的代币(默认为每页最多 5 个代币)。

当用户输入钱包地址并发起搜索时,应用使用 QuickNode SDK 获取代币余额,更新代币列表并提供分页控制以浏览代币数据页面。

ERC20 代币余额 UI 概述

现在,我们准备开始构建了!让我们从设置一个新的 React 项目开始,并安装必要的依赖。

在你的终端中运行以下代码。这将设置一个新的 React 应用程序,导航到项目的文件夹,并安装 QuickNode SDK。

npx create-react-app token-balance-app
cd token-balance-app
npm install @quicknode/sdk

组件

构建此应用所需的两个组件是:SearchBarTokenList。运行以下命令以创建这些组件文件。

echo > src/SearchBar.js
echo > src/TokenList.js

SearchBar 组件

SearchBar 组件包含一个输入字段,用于输入钱包地址,以及一个搜索按钮。SearchBar 使用 viemisAddress 方法验证输入的地址,只有当地址有效时,搜索按钮才会启用。当用户在输入框中输入时,onInputChange 函数会更新状态为当前值。当搜索按钮被点击时,将触发 onSearch 函数。

打开 src/SearchBar.js 文件并添加以下代码。随时查阅注释以获取详细信息。

import React, { useState, useEffect } from 'react'
import { viem } from '@quicknode/sdk'

function SearchBar({ onSearch, inputValue, onInputChange }) {
    const [notAddress, setNotAddress] = useState(false) // 状态监控输入值是否为有效地址

    // useEffect 钩子在每次 inputValue 改变时验证地址
    useEffect(() => {
        // 如果 inputValue 是有效地址,将 notAddress 状态设为 false,否则设为 true
        viem.isAddress(inputValue) ? setNotAddress(false) : setNotAddress(true)
    }, [inputValue]) // 依赖数组包含 inputValue,因此当 inputValue 改变时该效果运行

    return (
        <div>
            <input
                className="search-bar"
                type="text"
                placeholder="请输入钱包地址" // 输入框的占位符文本
                value={inputValue} // 受控输入框,其值设为 inputValue 状态
                onChange={e => onInputChange(e.target.value)} // 在每次按键时更新 inputValue 状态
            />
            <button onClick={onSearch} disabled={notAddress}>
                {' '}
                {/*  // 发起搜索的按钮,如果 notAddress 为 true,则禁用 */}
                {inputValue && notAddress ? '无效地址' : '搜索'}
            </button>
        </div>
    )
}

export default SearchBar

TokenList 组件

TokenList 组件接收钱包地址和代币数组作为 props,并显示与该钱包相关的 ERC20 代币的详细信息列表。它还为每个代币的地址提供复制到剪贴板的功能,并在成功复制地址时出现简要通知。

打开 src/TokenList.js 文件并将其修改如下。随时查阅注释以获取详细信息。

import React, { useState } from 'react'
import { viem } from '@quicknode/sdk'

function TokenList({ walletAddress, tokens }) {
    const [notification, setNotification] = useState('') // 状态处理通知消息

    const handleCopyAddress = address => {
        navigator.clipboard.writeText(address).then(() => {
            // 异步将地址复制到剪贴板
            // 一旦地址复制后显示通知消息
            setNotification({ message: '地址已复制到剪贴板!' })

            // 1秒后(1000毫秒)隐藏通知
            setTimeout(() => {
                setNotification('')
            }, 1000)
        })
    }

    return (
        <div className="token-list">
            {notification && (
                // 如果有通知消息,则显示
                <div className="notification">{notification.message}</div>
            )}

            <h2>钱包 {walletAddress} 的 ERC20 代币</h2>

            {/* 遍历 tokens 数组并渲染每个代币的详细信息 */}
            {tokens.length > 0 ? (
                tokens.map(token => (
                    <div key={token.address} className="token-item">
                        <h2>
                            {token.name} ({token.symbol})
                        </h2>
                        {/* 使用 viem.formatUnits 格式化并显示代币余额 */}
                        <p>
                            余额:{' '}
                            {parseFloat(
                                viem.formatUnits(token.totalBalance, token.decimals)
                            ).toFixed(2)}
                        </p>
                        <p>合约地址: {token.address}</p>
                        {/* 复制代币合约地址的按钮 */}
                        <button onClick={() => handleCopyAddress(token.address)}>
                            复制
                        </button>
                    </div>
                ))
            ) : (
                <p>未找到 ERC-20 代币。</p>
            )}
        </div>
    )
}

export default TokenList

应用

组件准备就绪,现在是时候将它们整合在一起,构建主文件。

src/App.js 是一个 React 应用程序的主结构,它显示给定钱包地址的 ERC20 代币余额。它包括搜索和从 Token 和 NFT API v2 捆绑包 中检索代币余额的功能,进行结果导航的分页以及指示数据正在获取的加载状态。

转到代码编辑器中的 src/App.js 并粘贴以下代码。随时查阅注释以获取详细解释。

不要忘记将 YOUR_QUICKNODE_ENDPOINT_URL 替换为你在 设置 QuickNode SDK 部分获得的端点 URL。

import './App.css' // 导入样式的 CSS

import React, { useState } from 'react' // 导入 React 和 useState 钩子
import SearchBar from './SearchBar' // 导入 SearchBar 组件
import TokenList from './TokenList' // 导入 TokenList 组件
import QuickNode from '@quicknode/sdk' // 导入 QuickNode SDK

// 主应用组件
function App() {
  // 应用的状态变量
  const [walletAddress, setWalletAddress] = useState(""); // 状态存储钱包地址
  const [tokens, setTokens] = useState([]); // 状态存储代币列表
  const [currentPage, setCurrentPage] = useState(1); // 状态存储当前页码
  const [pageInfo, setPageInfo] = useState(null); // 状态存储分页信息
  const [loading, setLoading] = useState(false); // 状态处理加载状态
  const [blockchain, setBlockchain] = useState("ethereum"); // 状态存储选择的区块链,默认为以太坊

  // 每页显示的结果数量
  const resultsPerPage = 5;

  // 使用 QuickNode Endpoint 初始化 QuickNode Core
  const core = new QuickNode.Core({
    endpointUrl:
      "YOUR_QUICKNODE_ENDPOINT_URL",
    config: {
      addOns: { nftTokenV2: true },
    },
  });

  // 根据地址搜索代币余额的函数
  const searchAddress = async (
    pagination = "firstPage" // 默认分页设置
  ) => {
    setLoading(true); // 开始加载
    try {
      let data;

      // 根据参数处理分页
      switch (pagination) {
        case "firstPage":
          // 获取第一页的代币余额
          data = await core.client.qn_getWalletTokenBalance({
            wallet: walletAddress,
            perPage: resultsPerPage,
          });

          break;

        case "nextPage":
          // 获取下一页的代币余额
          data = await core.client.qn_getWalletTokenBalance({
            wallet: walletAddress,
            perPage: resultsPerPage,
            page: pageInfo.pageNumber + 1,
          });
          break;

        case "previousPage":
          // 获取上一页的代币余额
          data = await core.client.qn_getWalletTokenBalance({
            wallet: walletAddress,
            perPage: resultsPerPage,
            page: pageInfo.pageNumber - 1,
          });
          break;

        default:
          // 默认情况下处理其他情况
          data = await core.client.qn_getWalletTokenBalance({
            wallet: walletAddress,
            perPage: resultsPerPage,
            page: pageInfo.pageNumber,
          });
      }

      setTokens(data.result); // 使用获取的数据更新 tokens 状态
      setPageInfo({ pageNumber: data.pageNumber, totalPages: data.totalPages }); // 使用获取的数据更新 pageInfo 状态
    } catch (error) {
      console.error("获取代币余额时出错:", error); // 记录任何错误
    }
    setLoading(false); // 结束加载
  };

  // 导航到下一页的函数
  const goToNextPage = () => {
    if (pageInfo && pageInfo.pageNumber < pageInfo.totalPages) {
      setCurrentPage(currentPage + 1); // 当前页码加 1
      searchAddress("nextPage"); // 获取下一页的结果
    }
  };

  // 导航到上一页的函数
  const goToPreviousPage = () => {
    if (pageInfo && pageInfo.pageNumber !== 1) {
      setCurrentPage(currentPage - 1); // 当前页码减 1
      searchAddress("previousPage"); // 获取上一页的结果
    }
  };

  return (
    <div className="App">
      <header className="App-header">
        <h1>ERC20 代币余额</h1>
      </header>
      {/* 下拉框选择区块链 */}
      <select
        value={blockchain}
        onChange={(e) => setBlockchain(e.target.value)}
        className="select-box"
      >
        <option value="ethereum">以太坊</option>
      </select>
      {/* 搜索框以输入和搜索钱包地址 */}
      <SearchBar
        onSearch={() => searchAddress("firstPage")}
        inputValue={walletAddress}
        onInputChange={setWalletAddress}
      />
      {/* 条件渲染显示加载状态或代币列表 */}
      {loading ? (
        <p>加载中...</p> // 显示加载文本
      ) : (
        <TokenList
          walletAddress={walletAddress}
          tokens={tokens}
          page={currentPage}
          pageSize={resultsPerPage}
        />
      )}
      {/* 分页控件以在页面之间导航 */}
      <div className="pagination">
        <button
          onClick={goToPreviousPage}
          disabled={!pageInfo || currentPage === 1} // 如果没有上一页,则禁用按钮
        >
          上一页
        </button>
        <span>{currentPage}</span>
        <button
          onClick={goToNextPage}
          disabled={!pageInfo || currentPage === pageInfo.totalPages} // 如果没有下一页,则禁用按钮
        >
          下一页
        </button>
      </div>
    </div>
  );
}

export default App;

样式

应用程序现在已经准备好了,只有样式还需要调整。由于使用 CSS 进行样式设计并不是本指南的重点,因此我们不会深入探讨每个样式设置。然而,请随意尝试代码并查看本地运行应用后效果。

打开 src/App.css 并用以下代码替换文件中的现有代码。

/* 通用样式 */
body {
    font-family: 'Arial', sans-serif;
    background-color: #f4f4f4;
    margin: 0;
    padding: 20px;
}

.App {
    max-width: 600px;
    margin: auto;
    background: white;
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

.select-box {
    margin-bottom: 1rem;
    padding: 0.5rem 1rem;
    border: 1px solid #ccc;
    border-radius: 4px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
    font-size: 1rem;
    cursor: pointer;
}

/* 搜索框样式 */
.search-bar {
    padding: 10px;
    margin-bottom: 20px;
    width: 100%;
    box-sizing: border-box;
    border-radius: 4px;
    border: 1px solid #ccc;
}

/* 代币列表样式 */
.token-list {
    list-style: none;
    padding: 0;
}

.token-item {
    padding: 15px;
    background-color: #f9f9f9;
    border: 1px solid #e1e1e1;
    margin-bottom: 10px;
    border-radius: 4px;
}

.token-item h2 {
    margin-top: 0;
    color: #333;
}

.token-item p {
    margin: 5px 0;
}

.token-item button {
    padding: 5px 10px;
    background-color: #4caf50;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    transition: background-color 0.3s;
}

.token-item button:hover {
    background-color: #45a049;
}

/* 搜索按钮样式 */
.search-bar + button {
    padding: 10px 15px;
    background-color: #008cba;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    transition: background-color 0.3s;
}

.search-bar + button:hover {
    background-color: #007b9a;
}

.notification {
    padding: 10px;
    margin-bottom: 10px;
    background-color: #4caf50;
    color: white;
    text-align: center;
    border-radius: 4px;
    position: fixed;
    top: 20px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1000;
}

/* 分页样式 */
.pagination {
    display: flex;
    justify-content: center;
    margin-top: 20px;
}

.pagination button {
    padding: 10px 15px;
    margin: 0 5px;
    background-color: #007bff;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    transition: background-color 0.3s;
}

.pagination button:disabled {
    background-color: #ccc;
}

.pagination button:not(:disabled):hover {
    background-color: #0056b3;
}

.pagination span {
    line-height: 30px;
    margin: 0 10px;
}

运行应用

编码部分现在已经完成!让我们通过运行以下命令来启动应用程序。

npm start

如果一切顺利,输出控制台应类似于下方文字。

编译成功!

你可以在浏览器中查看 token-balance-app。

  本地:            http://localhost:3000
  在你的网络上:  http://192.168.1.11:3000

注意,开发构建并未优化。
要创建生产构建,请使用 npm run build。

webpack 编译成功

然后,在浏览器中访问 http://localhost:3000。它的外观应该与下图的左侧相似。粘贴一个 EVM 地址并搜索后,前端应像右侧的图像。

替代文本

检查所有功能,例如尝试不同的地址,以及通过点击分页按钮查看更多结果。

结论

祝贺你完成了本指南!你已经学习了如何使用 QuickNode SDK 构建一个 React 应用程序,可以显示 ERC20 代币余额。如所示,QuickNode SDK 通过处理复杂的区块链交互简化了你的工作,让你可以将精力集中在创建应用的核心功能上。

要探索 QuickNode SDK 的更多功能,或有任何问题,请查看 QuickNode SDK 文档,在 Discord 加入 QuickNode 社区,或通过 Twitter 联系我们。

我们 ❤️ 反馈!

让我们知道 如果你有任何反馈或对新主题的请求。我们很想听听你的想法。

其他资源

  • 原文链接: quicknode.com/guides/qui...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
QuickNode
QuickNode
江湖只有他的大名,没有他的介绍。