
Как создать обозреватель портфеля Solana с помощью Next.js и DAS API от Helius
Цели
В этом руководстве мы создадим фронтенд-приложение, которое отображает содержимое кошелька Solana с помощью DAS API от Helius. Мы покажем разные типы токенов и подробную информацию о них, включая метаданные, сведения о коллекциях и цены взаимозаменяемых токенов.
В этом руководстве мы пошагово разберём процесс создания приложения, похожего на Galleria.
Предварительные требования
- Ключ Helius API.
- Знание React.
- Опыт работы с Next.js.
Обзор используемых технологий
- Next.js: React-фреймворк для создания приложений с серверным рендерингом.
- DAS API от Helius: Комплексный API для доступа к данным токенов Solana.
- Vercel: Платформа для хостинга приложений Next.js.
- Tailwind CSS: CSS-фреймворк для стилизации, основанный на служебных классах.
Настройка проекта
DAS API
DAS API — это спецификация и система с открытым исходным кодом от Helius. API предоставляет простой интерфейс для взаимодействия с цифровыми активами в блокчейне Solana. Он поддерживает разные типы токенов, включая взаимозаменяемые токены, обычные NFT и сжатые NFT, а также предлагает такие возможности, как индексирование метаданных вне блокчейна.
Настройка ключа API
Чтобы получить ключ Helius API, откройте портал Helius для разработчиков. Вот как начать работу:
- Создайте аккаунт с помощью кошелька Solana, аккаунта Google или GitHub.
- После входа вам будет предложено создать ключ API.
- Храните этот ключ в безопасном месте: он понадобится для выполнения вызовов API.
Создание приложения Next.js
Шаг 1. Настройка проекта Next.js
Создайте новое приложение Next.js с Tailwind
npx create-next-app@latest helius-portfolio --typescript --eslintИспользуйте следующие параметры конфигурации:
Это позволит использовать Tailwind CSS для стилизации, а также новый App router.
cd helius-portfolioШаг 2. Создание компонентов
Создайте папку components в каталоге app, чтобы сохранить упорядоченную структуру.
Компонент SearchBar (components/SearchBar.js):
Этот компонент обрабатывает ввод адресов кошельков. Он использует хук useState для управления состоянием адреса и useRouter для навигации после отправки формы. Форма стилизована с помощью классов Tailwind CSS и выглядит лаконично.
Компонент TokenCard (components/TokenCard.js):
Этот компонент предназначен для отображения информации о токене. Он условно отображает разные макеты для взаимозаменяемых и невзаимозаменяемых токенов и использует типы Token и Attribute из ../types/Token.
Компонент 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, и показать её на карточке.
Например, для 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 (
{/* 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
Для получения данных токенов мы используем метод searchAssets из DAS API. Этот метод позволяет задавать такие критерии, как адреса кошельков и типы токенов.
Создание папки lib:
- Создайте папку
libв каталоге app для вызова API. - Создайте в ней файл
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 и взаимодействия с блокчейном Solana через DAS API.
Похожие статьи
Подпишитесь на Helius
Следите за новостями разработки на Solana и получайте уведомления о наших публикациях


