如何使用 Next.js 构建 Solana 投资组合查看器

  • Helius
  • 发布于 2024-02-04 21:48
  • 阅读 6

本文是一个关于如何使用Helius的DAS API构建Solana钱包前端应用的教程,详细介绍了各个步骤,包括设置项目、创建组件、通过API获取数据等。文章提供了示例代码和额外资源,旨在帮助开发者掌握使用Next.js框架的实际应用。

6分钟阅读

2024年2月2日

目标

在本教程中,我们将构建一个前端应用程序,以使用 Helios 的 DAS API 显示 Solana 钱包的内容。我们将展示各种代币类型及其详细信息,包括元数据、集合信息和可替代代币价格。

本教程将通过分解所需的步骤,指导你构建类似于 Galleria 的应用程序。

先决条件

  • Helius API 密钥。
  • 熟悉 React。
  • 有 Next.js 的经验。

使用的技术概述

  • Next.js: 用于构建服务器端渲染应用程序的 React 框架。
  • Helius 的 DAS API: 一个全面的 API,用于访问 Solana 代币数据。
  • Vercel: 用于托管 Next.js 应用程序的平台。
  • Tailwind CSS: 一个实用优先的 CSS 框架,用于样式设计。

项目设置

DAS API

DAS API 是 Helius 提供的开源规范和系统。该 API 提供一个简单的接口,用于与 Solana 区块链上的数字资产进行交互。它支持各种代币类型,包括可替代代币、常规 NFT 和压缩 NFT,并提供像索引链外元数据等功能。

API 密钥设置

要获取 Helius API 密钥,请访问 Helius 开发者门户。以下是获取入门的方法:

  1. 使用 Solana 钱包、Google 帐户或 GitHub 帐户创建一个帐户。
  2. 登录后,系统会提示你创建一个 API 密钥。
  3. 安全存储此密钥,因为我们将使用它进行 API 调用。

构建 Next.js 应用程序

步骤 1: 设置你的 Next.js 项目

使用 Tailwind 创建一个新的 Next.js 应用

代码

npx create-next-app@latest helius-portfolio --typescript --eslint

使用以下配置:

solana wallet portfolio tutorial

这将允许我们使用 Tailwind CSS 进行样式设计,以及使用新的 App 路由

代码

cd helius-portfolio

步骤 2: 创建组件

在应用程序目录中创建一个 components 文件夹,以保持结构的组织性。

SearchBar 组件(components/SearchBar.js:

该组件处理用户输入的钱包地址。它利用 useState Hook管理地址状态,并在表单提交时为空间使用 useRouter 进行导航。该表单使用 Tailwind CSS 类样式设计,以实现流畅的外观。

TokenCard 组件(components/TokenCard.js:

此组件旨在显示代币信息。它会根据代币类型有条件地渲染不同的布局,并利用来自 ../types/TokenTokenAttribute 类型。

SearchBar 组件

components/searchBar.ts

代码

import { useRouter } from "next/navigation";
import React, { useState } from "react";

export default function SearchBar() {
  const router = useRouter();
  const [address, setAddress] = useState("");

  function handleSubmit(event: React.FormEvent) {
    event.preventDefault();
    router.push(`/${address}`);
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        onChange={(e) => setAddress(e.target.value)}
        className="border-2 border-gray-300 rounded-md w-full text-center px-4 py-2 mb-4 flex-grow text-black"
      />
      <button type="submit">Search</button>
    </form>
  );
}

TokenCard 组件:

此组件解析来自 API 的代币数据,并在每个代币卡上显示相关信息。

在下面的例子中,我们展示了一些基本信息,例如每个可替代代币的图像、符号、数量和价值。对于非可替代代币,我们展示图像、名称、描述和属性。你可以提取 API 提供的任何信息并在卡片上显示。

一个例子是显示 cNFTs 的压缩特定信息或者显示铭刻资产的铭刻信息。要了解你可以使用的具体信息,你可以查看 DAS 的 文档

代码

"use client";

import React from "react";
import { Token, Attribute } from "../types/Token";

interface TokenCardProps {
  token: Token;
  tokenType: string;
}

const TokenCard = ({ token, tokenType }: TokenCardProps) => {
  return (
    <div>
      {/* 其他代币信息 */}
      {tokenType === "fungible" ? (
        <div>
          <p>{token.content.metadata.symbol}</p>
          {/*  {token.content.metadata.description} */}
          <p>Amount: {token.token_info.balance}</p>
          {token.token_info.price_info?.total_price ? (
            <p>Value: ${token.token_info.price_info.total_price}</p>
          ) : null}
        </div>
      ) : (
        <div>
          <p>{token.content.metadata.name}</p>
          <p>{token.content.metadata.description}</p>
          {token.content.metadata?.attributes?.map(
            (attribute: Attribute, index: number) => (
              <p key={index}>{attribute.trait_type}: {attribute.value}</p>
            )
          )}
        </div>
      )}
    </div>
  );
}

export default TokenCard;

步骤 3: 从 DAS 获取数据

我们将使用 DAS API 中的 searchAssets 方法来获取代币数据。此方法灵活,允许我们指定钱包地址和代币类型等标准。

创建一个 lib 文件夹

  1. 在应用程序目录中制作一个 lib 文件夹,用于 API 调用。
  2. 在其中创建一个 searchAssets.ts 文件。

API 调用函数(lib/searchAssets.ts

  • 该函数基于钱包地址获取代币。
  • 它处理潜在的错误,并记录响应以供调试。

代码

interface Tokens {
  items: any[];
}

const fetchTokens = async (walletAddress: string): Promise<Tokens> => {
  const url = `https://mainnet.helius-rpc.com/?api-key=`;
  console.log(
    `开始为钱包地址 ${walletAddress} 搜索代币`
  );
  try {
    const response = await fetch(url, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        jsonrpc: "2.0",
        id: "my-id",
        method: "searchAssets",
        params: {
          ownerAddress: walletAddress,
          tokenType: "all",
          displayOptions: {
            showCollectionMetadata: true,
          },
        },
      }),
    });
    const data = await response.json();
    console.log(
      `钱包地址 ${walletAddress} 返回的数据:`,
      data.result
    );
    return { items: data.result };
  } catch (error) {
    console.error("获取代币时出错:", error);
    return { items: [] };
  }
};

export default fetchTokens;

设置代币类型

  • 我们需要为来自 DAS API 的响应设置一个类型。这是在使用 Typescript 时的好习惯。你可以创建一个名为 types 的文件夹,并创建一个名为 token.ts 的文件。

代码

export interface ApiResponse {
  total: number;
  limit: number;
  cursor?: string;
  items: Token[];
}

export interface Token {
  interface: string;
  id: string;
  content: Content;
  authorities: Authority[];
  compression: Compression;
  grouping: Grouping[]; // 或 any[]
  royalty: Royalty;
  creators: Creator[]; // 或 any[]
  ownership: Ownership;
  supply: Supply | null | number;
  mutable: boolean;
  burnt: boolean;
  token_info: TokenInfo;
  mint_extensions: MintExtensions;
  inscription: Inscription;
  spl20?: Spl20;
}

export interface Content {
  $schema: string;
  json_uri: string;
  files: any[];
  metadata: Record<string, any>;
  links: Record<string, any>;
}

export interface Authority {
  address: string;
  scopes: string[];
}

export interface Compression {
  eligible: boolean;
  compressed: boolean;
  data_hash: string;
  creator_hash: string;
  asset_hash: string;
  tree: string;
  seq: number;
  leaf_id: number;
}

export interface Grouping {
  group_key: string;
  group_value: string;
  collection_metadata: CollectionMetadata;
}

export interface Royalty {
  royalty_model: string;
  target: string | null;
  percent: number;
  basis_points: number;
  primary_sale_happened: boolean;
  locked: boolean;
}

export interface Creator {
  address: string;
  share: number;
  verified: boolean;
}

export interface Ownership {
  frozen: boolean;
  delegated: boolean;
  delegate: null | string;
  ownership_model: string;
  owner: string;
}

export interface Supply {
  print_max_supply: number;
  print_current_supply: number;
  edition_nonce: number;
}

export interface TokenInfo {
  symbol: string;
  balance: number;
  supply: number;
  decimals: number;
  token_program: string;
  associated_token_address: string;
  price_info: PriceInfo;
}

export interface Inscription {
  order: number;
  size: number;
  contentType: string;
  encoding: string;
  validationHash: string;
  inscriptionDataAccount: string;
}

export interface Spl20 {
  p: string;
  op: string;
  tick: string;
  amt: string;
}

export interface File {
  uri: string;
  cdn_uri: string;
  mime: string;
}

export interface Metadata {
  attributes: Attribute[];
  description: string;
  name: string;
  symbol: string;
}

export interface Attribute {
  value: string;
  trait_type: string;
}

export interface CollectionMetadata {
  name: string;
  symbol: string;
  image: string;
  description: string;
  external_url: string;
}

export interface PriceInfo {
  price_per_token: number;
  total_price: number;
  currency: string;
}

export interface MintExtensions {}

步骤 4: 显示代币信息

我们将把 API 响应分类为可替代和非可替代代币。使用 Token Card 组件,这些代币将被显示。一个切换按钮允许用户在这两种代币类型之间切换。

在搜索页面集成组件

  • 搜索页面 (pages/index.ts)
  • 主页设有一个搜索栏,用户可以在其中输入钱包地址。
  • 它使用 Tailwind CSS 进行样式设计,以实现干净和响应式的设计。

代码

"use client";

import React from "react";
import SearchBar from "./components/searchBar";

export default function Home() {
  return (
    <>
      <h1>Solana Portfolio Viewer</h1>
      <p>{"Built with Helius's DAS API"}</p>
      <SearchBar />
    </>
  );
}

设置代币页面

我们将利用 Next.js 中的动态路由为单个钱包地址提供服务。这通过允许轻松导航和 URL 分享来增强用户体验。

设置一个名为 [wallet] 的目录,方括号启用动态路由。完成后,创建一个名为 page.tsx 的文件,其中包含所有动态加载页面的业务逻辑。

代币页面设置(pages/[wallet]/page.tsx:

该页面显示与 URL 中的钱包地址相关的代币。它进行 API 调用以获取代币数据,并使用 Token Card 组件显示它。

代码

"use client";

import React, { useState, useEffect } from "react";
import fetchTokens from "../lib/searchAssets";
import TokenCard from "../components/tokenCard";
import { Token } from "../types/Token";

interface Tokens {
  items: Token[];
}

interface PageProps {
  params: {
    wallet: string;
  };
}

export default function Page({ params }: PageProps) {
  const [tokens, setTokens] = useState<Tokens | null>(null);
  const [tokenType, setTokenType] = useState("fungible");

  useEffect(() => {
    fetchTokens(params.wallet).then(setTokens).catch(console.error);
  }, [params.wallet]);

  console.log("tokens", tokens);

  const fungibleTokens = tokens
    ? tokens.items.filter(
        (token) =>
          token.interface === "FungibleToken" ||
          token.interface === "FungibleAsset"
      )
    : [];

  const nonFungibleTokens = tokens
    ? tokens.items.filter(
        (token) =>
          token.interface !== "FungibleToken" &&
          token.interface !== "FungibleAsset"
      )
    : [];

  const displayedTokens =
    tokenType === "fungible" ? fungibleTokens : nonFungibleTokens;

  return (
    <>
      <h2>Portfolio Viewer</h2>
      <p>{params.wallet}</p>
      <button onClick={() => setTokenType("fungible")}>Fungible Tokens</button>
      <button onClick={() => setTokenType("nonFungible")}>Non-Fungible Tokens</button>

      {displayedTokens.length > 0 ? (
        <div>
            {displayedTokens.map((token, index) => (
              <TokenCard key={index} token={token} tokenType={tokenType} />
            ))}
        </div>
      ) : (
        <p>Loading...</p>
      )}
    </>
  );
}

在完成以上所有设置后,你的项目目录结构应如下所示:

Next.js directory

步骤 5: 样式设计应用程序

Tailwind CSS 将是我们的样式框架。其实用优先的方法使我们能够快速对组件进行样式设计,同时确保一致和响应式的设计。你可以使用 Tailwind 根据你的需求自定义你的应用程序。

步骤 6: 测试应用程序

运行你的 Next.js 应用程序以进行本地测试:

代码

npm run dev

通过输入不同的钱包地址进行测试,确保正确的数据被显示。

部署

我们将使用 Vercel 部署我们的应用程序。Vercel 提供免费的托管,并提供有价值的分析和见解,因此是托管 Next.js 应用程序的理想选择。你可以在 https://vercel.com/ 上设置此服务。

结论

祝贺你完成本教程!我们希望你发现它的信息量很大。

DAS API 是一个非常强大且快速的工具,使你能够从链上检索代币数据。

此投资组合查看器是该 API 能力的一个优秀示例。

我们希望你能够将这个基本网站转变为一些伟大的东西。如果你有任何问题,请不要犹豫,加入我们的 Discord 并直接问我们!

你可以在这里找到完整的代码:https://github.com/helius-labs/galleria

进一步学习资源

要深入了解 Next.js、Solana 区块链和 API 集成,可以浏览以下资源:

本指南为使用 Next.js 构建基于区块链的应用程序以及使用 DAS API 与 Solana 区块链进行交互提供了坚实的基础。

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

0 条评论

请先 登录 后评论
Helius
Helius
https://www.helius.dev/