新消息:Helius 收购 Light Protocol
如何使用 Next.js 和 Helius 的 DAS API 构建 Solana 资产组合查看器
博客/开发

如何使用 Next.js 和 Helius 的 DAS API 构建 Solana 资产组合查看器

开发者体验工程师X 上的 Owen VenterLinkedIn 上的 Owen Venter
阅读需 6 分钟

目标

在本教程中,我们将使用 Helius 的 DAS API 构建一个前端应用,用于显示 Solana 钱包中的资产。我们将展示多种代币类型及其详细信息,包括元数据、系列信息和同质化代币价格。

本教程将拆解必要步骤,指导你构建一个类似 Galleria 的应用。

前置条件

  • Helius API 密钥。
  • 熟悉 React。
  • 具备 Next.js 使用经验。

所用技术概览

  • Next.js:用于构建服务端渲染应用的 React 框架。
  • Helius 的 DAS API:用于访问 Solana 代币数据的综合 API。
  • 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

使用以下配置:

这样,我们既可以使用 Tailwind CSS 设置样式,也可以使用新的 App router。

代码
cd helius-portfolio

‍第 2 步:创建组件

在 app 目录中创建一个 components 文件夹,以保持项目结构清晰。

SearchBar 组件(components/SearchBar.js):

此组件负责处理用户输入的钱包地址。它使用 useState hook 管理地址状态,并使用 useRouter 在表单提交后进行导航。表单使用 Tailwind CSS 类设置样式,界面简洁美观。

TokenCard 组件(components/TokenCard.js):

此组件用于显示代币信息。它会根据同质化代币和非同质化代币有条件地渲染不同布局,并使用来自 ../types/Token 的 Token 和 Attribute 类型。

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 (
    
       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"
      />
      
        Search
      
    
  );
}

TokenCard 组件:

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

在下面的示例中,我们会显示每种同质化代币的图片、符号、数量和价值等基本信息。对于非同质化代币,我们会显示图片、名称、描述和属性。你可以提取 API 提供的任意信息,并将其显示在卡片上。

例如,可以显示 cNFT 的压缩相关信息,或铭刻资产的铭文信息。要进一步了解可以使用的具体信息,请查看 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 (
    
      {/* Other token information */}
      {tokenType === "fungible" ? (
        
          
          
            
              {token.content.metadata.symbol}
            
            {/* {token.content.metadata.description} */}
            Amount:{token.token_info.balance}
            {token.token_info.price_info?.total_price ? (
              Value: ${token.token_info.price_info.total_price}
            ) : null}
          
        
      ) : (
        
          
          
            
              {token.content.metadata.name}
            
            {token.content.metadata.description}
            
              {token.content.metadata?.attributes?.map(
                (attribute: Attribute, index: number) => (
                  
                    {attribute.trait_type}: {attribute.value}
                  
                )
              )}
            
          
        
      )}
    
  );
}

export default TokenCard;

第 3 步:从 DAS 获取数据

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

创建 lib 文件夹:

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

API 调用函数(lib/searchAssets.ts):

  • 此函数根据钱包地址获取代币。
  • 它会处理潜在错误并记录响应,以便调试
代码
interface Tokens {
  items: any[];
}

const fetchTokens = async (walletAddress: string): Promise => {
  const url = `https://mainnet.helius-rpc.com/?api-key=`;
  console.log(
    `Starting search for tokens for wallet address: ${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(
      `Data returned for wallet address ${walletAddress}:`,
      data.result
    );
    return { items: data.result };
  } catch (error) {
    console.error("Error fetching tokens:", 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[]; // or any[]
  royalty: Royalty;
  creators: Creator[]; // or 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;
  links: Record;
}

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 (
    
      
        
          Solana Portfolio Viewer
        
        
          {"Built with Helius's DAS API"}
        
        
          
        
      
    
  );
}

设置代币页面

我们将使用 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, Attribute } from "../types/Token";

interface Tokens {
  items: Token[];
}

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

export default function Page({ params }: PageProps) {
  const [tokens, setTokens] = useState(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 (
    
      
        Portfolio Viewer
      
      {params.wallet}
      
         setTokenType("fungible")}
        >
          Fungible Tokens
        
         setTokenType("nonFungible")}
        >
          Non-Fungible Tokens
        
      
      {displayedTokens.length > 0 ? (
        
          {displayedTokens.map((token) => (
            
          ))}
        
      ) : (
        Loading...
      )}
    
  );
}

完成所有设置后,你的项目目录应如下所示:

第 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

及时了解 Solana 开发的最新动态,并在我们发布新内容时收到更新

放大图片