新着:HeliusがLight Protocolを買収
Next.jsとHeliusのDAS APIでSolanaポートフォリオビューアを構築する方法
ブログ/開発

Next.jsとHeliusのDAS APIでSolanaポートフォリオビューアを構築する方法

Developer Experienceエンジニア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 Developer Portalにアクセスします。開始手順は次のとおりです。

  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フックを使用し、フォーム送信後のナビゲーションには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. API呼び出し用に、appディレクトリ内に**lib**フォルダーを作成します。
  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コンポーネントを使用して表示します。切り替えボタンを使うことで、2種類のトークン表示を切り替えられます。

検索ページへのコンポーネントの統合

  • 検索ページ(pages/index.ts)
  • ランディングページには、ユーザーがウォレットアドレスを入力できるSearch Barを配置します。
  • 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開発の最新情報や新しい記事の公開通知を受け取れます

拡大画像