8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

本記事では、Next.js で Arweave AO ベースの dApps を構築する際に役立つ npm ライブラリを紹介します。NFT の Mint、トークンの Claim、AO プロセスとの通信、ウォレット連携など、実用的な機能に直結するライブラリを中心に取り上げます。

ワンコマンドで全て入れるなら。

npm install @permaweb/aoconnect arconnect @ar.io/sdk @permaweb/libs arweave @redstone-finance/sdk

※Arweave AO や web3 以外の技術スタックはこちらに記載

対象読者

  • Arweave AO の dApps を開発するエンジニア・PdM
  • Arweave AO のビルダーやハッカソン参加者
  • MVP 開発を最短で進めたい Web3 スタートアップ関係者

要件

  • 目的: ハッカソンや迅速なプロトタイプ開発における MVP 開発
  • 開発スタイル: Claude Code を使った効率的な開発
  • 背景: Arweave AO エコシステムでの新規開発
  • 焦点: NFT ミント、トークンクレーム、AO プロセス連携、ウォレット統合

AO dApps における Web3 ライブラリの役割

AO dApps における Web3 ライブラリは主にウォレット接続管理と AO Process の CRUD 操作、特にトークン(FT/NFT)操作を担当します。

  • ウォレット接続と署名管理
  • AO Process 通信(EVM におけるスマートコントラクト相当)
  • トークン(FT/NFT)のミント、クレーム、表示
  • Arweave からの分散ストレージデータ取得

推奨のAOライブラリ一覧

@permaweb/aoconnect

AO Process(EVM におけるスマートコントラクト相当)との通信のための主要ライブラリ。

URL: @permaweb/aoconnect

npm install @permaweb/aoconnect
目的 機能 使用理由
AO Process 連携 メッセージ送信、結果評価、プロセス生成 TypeScript サポート、AO 操作の高レベル抽象化
NFT/トークン操作 ミント、転送、残高クエリ ブロックチェーンネイティブなコア機能
メッセージ処理 非同期メッセージ処理 AO のメッセージパッシングアーキテクチャの組み込み対応

The aoconnect library provides an abstraction for spawning, evaluating, and interacting with AO Processes.
This module will run in a browser or server environment.
Send and evaluate messages via AO’s compute and message units.

arconnect

Arweave ウォレット接続と管理のための標準ライブラリ。

URL: arconnect

npm install arconnect
目的 機能 使用理由
ウォレット接続 ArConnect 拡張機能統合 セキュア、成熟、Arweave dApps の標準ツール
トランザクション署名 メッセージとデータアイテム署名 ユーザー操作に不可欠
権限管理 細かい権限要求 セキュリティベストプラクティス

This package helps TypeScript users develop for ArConnect.

@ar.io/sdk

直接的な Arweave アクセスの代わりに、高速なデータ取得のための最適化されたゲートウェイアクセス。

URL: @ar.io/sdk

npm install @ar.io/sdk
目的 機能 使用理由
ゲートウェイ管理 複数ゲートウェイフォールバック 高速で安定したデータアクセス
NFT メタデータ 画像とメタデータ取得 UX とローディング速度の向上
分散 CDN 分散コンテンツ配信 信頼性とパフォーマンス

SDK for interacting with the ar.io ecosystem (e.g. gateways, observers).
Works in both Node.js and Web environments.

@permaweb/libs

一般的な Arweave 操作のための高レベル抽象化ライブラリ、迅速なプロトタイピングに最適。

URL: @permaweb/libs

npm install @permaweb/libs
目的 機能 使用理由
迅速なプロトタイピング 事前構築された Profile、Atomic Assets、Collections arweave-js より学習コストが低い
トークン操作 包括的な FT/NFT 管理 MVP 開発の高速化
ユーザー管理 プロファイルとソーシャル機能 組み合わせ可能な Web 構築ブロック

※注意: これは新しいライブラリなので、本番使用では安定性を考慮してください。
GitHub

This SDK provides a set of libraries designed as foundational building blocks for developers to create and interact with applications on Arweave's permaweb. These libraries aim to contribute building on the composable web, promoting interoperability and reusability across decentralized applications. With libraries for managing profiles, atomic assets, collections, and more, this SDK simplifies the development of decentralized, permanent applications.

arweave (arweave-js)

低レベル操作で少し難解なため、無理に使う必要はない。

URL: arweave

npm i arweave
目的 使用時期 MVP で避ける理由
ネイティブ Arweave 操作 カスタムファイルアップロード、ブロッククエリ 迅速な開発には冗長すぎる
直接ブロックチェーンアクセス 他の SDK が必要な機能を提供しない場合 複雑性が高い

Arweave JS is the JavaScript/TypeScript SDK for interacting with the Arweave network and uploading data to the Permaweb.

@redstone-finance/sdk

リアルタイムなトークン価格フィードと市場データのための分散オラクルネットワーク。

URL: @redstone-finance/sdk

npm install @redstone-finance/sdk
目的 機能 使用理由
トークン価格フィード リアルタイム暗号通貨価格 信頼性の高い分散価格データ
市場データ 取引量、時価総額、価格履歴 DeFi アプリケーションに不可欠
オラクル統合 オンチェーン価格検証 スマートコントラクト向けトラストレス価格フィード

Typescript/Javascript SDK for interacting with the RedStone ecosystem. Below a couple of basic use cases.

Sample Codes

基本的な AO Process 連携 (aoconnect)

// lib/ao.ts
import { connect } from "@permaweb/aoconnect";

const ao = connect();

// Send message to AO Process
export async function sendMessage(
  processId: string,
  data: any,
  tags: any[] = []
) {
  try {
    const messageId = await ao.message({
      process: processId,
      data: JSON.stringify(data),
      tags,
      signer: createDataItemSigner(window.arweaveWallet),
    });

    return await ao.result({
      message: messageId,
      process: processId,
    });
  } catch (error) {
    console.error("AO message failed:", error);
    throw error;
  }
}

// Read from AO Process
export async function readProcessState(processId: string, query: string) {
  try {
    const result = await ao.dryrun({
      process: processId,
      data: query,
    });

    return JSON.parse(result.Messages[0]?.Data || "{}");
  } catch (error) {
    console.error("AO read failed:", error);
    throw error;
  }
}

ウォレット統合例(arconnect

// hooks/useArweaveWallet.ts
import { useEffect, useState } from "react";

interface WalletState {
  address: string | null;
  isConnected: boolean;
  isConnecting: boolean;
}

export function useArweaveWallet() {
  const [wallet, setWallet] = useState<WalletState>({
    address: null,
    isConnected: false,
    isConnecting: false,
  });

  const connect = async () => {
    if (!window.arweaveWallet) {
      throw new Error("ArConnect wallet not found");
    }

    setWallet((prev) => ({ ...prev, isConnecting: true }));

    try {
      await window.arweaveWallet.connect([
        "ACCESS_ADDRESS",
        "ACCESS_PUBLIC_KEY",
        "SIGN_TRANSACTION",
        "DISPATCH",
      ]);

      const address = await window.arweaveWallet.getActiveAddress();

      setWallet({
        address,
        isConnected: true,
        isConnecting: false,
      });
    } catch (error) {
      setWallet((prev) => ({
        ...prev,
        isConnecting: false,
      }));
      throw error;
    }
  };

  const disconnect = async () => {
    if (window.arweaveWallet) {
      await window.arweaveWallet.disconnect();
      setWallet({
        address: null,
        isConnected: false,
        isConnecting: false,
      });
    }
  };

  useEffect(() => {
    const checkConnection = async () => {
      if (window.arweaveWallet) {
        try {
          const address = await window.arweaveWallet.getActiveAddress();
          if (address) {
            setWallet({
              address,
              isConnected: true,
              isConnecting: false,
            });
          }
        } catch {
          // Wallet not connected
        }
      }
    };

    checkConnection();
  }, []);

  return { ...wallet, connect, disconnect };
}

データ取得例 (@ar.io/sdk)

// lib/arweave-data.ts
import { ArIO } from "@ar.io/sdk";

const arIO = new ArIO();

// Get NFT metadata with fallback gateways
export async function getNFTMetadata(transactionId: string) {
  try {
    const gateways = await arIO.getGateways();
    const gatewayUrls = Object.values(gateways)
      .map((gateway) => gateway.settings.fqdn)
      .slice(0, 3); // Use top 3 gateways

    for (const gatewayUrl of gatewayUrls) {
      try {
        const response = await fetch(`https://${gatewayUrl}/${transactionId}`);
        if (response.ok) {
          return await response.json();
        }
      } catch {
        continue; // Try next gateway
      }
    }

    throw new Error("All gateways failed");
  } catch (error) {
    console.error("Failed to fetch NFT metadata:", error);
    throw error;
  }
}

// Optimized image loading
export function getOptimizedImageUrl(
  transactionId: string,
  width?: number
): string {
  const baseUrl = "https://arweave.net"; // or preferred gateway
  const params = width ? `?width=${width}&quality=80` : "";
  return `${baseUrl}/${transactionId}${params}`;
}

プロファイルとアセット管理(@permaweb/libs

// lib/permaweb.ts
import { Profile, AtomicAsset } from "@permaweb/libs";

// User profile management
export async function getUserProfile(address: string) {
  try {
    const profile = new Profile({ address });
    return await profile.get();
  } catch (error) {
    console.error("Failed to get profile:", error);
    return null;
  }
}

// NFT asset operations
export async function mintNFT(metadata: any, wallet: any) {
  try {
    const asset = new AtomicAsset({
      title: metadata.title,
      description: metadata.description,
      image: metadata.image,
      // ... other metadata
    });

    const result = await asset.mint({ wallet });
    return result;
  } catch (error) {
    console.error("NFT minting failed:", error);
    throw error;
  }
}

トークン価格フィード(@redstone-finance/sdk

// lib/price-feeds.ts
import { WrapperBuilder } from "@redstone-finance/sdk";

// Get real-time token prices
export async function getTokenPrice(symbol: string): Promise<number> {
  try {
    const wrappedContract = WrapperBuilder.wrap({} as any) // Can be used without contract for price queries
      .usingDataService({
        dataServiceId: "redstone-main-demo",
        uniqueSignersCount: 1,
      });

    const price = await wrappedContract.getValueForDataFeed(symbol);
    return price / Math.pow(10, 8); // RedStone uses 8 decimals
  } catch (error) {
    console.error(`Failed to fetch price for ${symbol}:`, error);
    throw error;
  }
}

// Get multiple token prices
export async function getMultipleTokenPrices(
  symbols: string[]
): Promise<Record<string, number>> {
  try {
    const prices: Record<string, number> = {};

    await Promise.all(
      symbols.map(async (symbol) => {
        try {
          prices[symbol] = await getTokenPrice(symbol);
        } catch (error) {
          console.warn(`Failed to fetch price for ${symbol}:`, error);
          prices[symbol] = 0;
        }
      })
    );

    return prices;
  } catch (error) {
    console.error("Failed to fetch multiple token prices:", error);
    throw error;
  }
}

// Price feed hook for React components
export function useTokenPrice(symbol: string, refreshInterval: number = 30000) {
  const [price, setPrice] = useState<number | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const fetchPrice = async () => {
      try {
        setLoading(true);
        const currentPrice = await getTokenPrice(symbol);
        setPrice(currentPrice);
        setError(null);
      } catch (err) {
        setError(err instanceof Error ? err.message : "Failed to fetch price");
      } finally {
        setLoading(false);
      }
    };

    fetchPrice();
    const interval = setInterval(fetchPrice, refreshInterval);

    return () => clearInterval(interval);
  }, [symbol, refreshInterval]);

  return { price, loading, error };
}

// hooks/useAOMint.ts
import { useState } from "react";
import { connect, createDataItemSigner } from "@permaweb/aoconnect";
import { useArweaveWallet } from "./useArweaveWallet";

const ao = connect();

interface MintState {
  isPending: boolean;
  isConfirming: boolean;
  isSuccess: boolean;
  error: string | null;
  transactionId: string | null;
}

export function useAOMint(processId: string) {
  const { address, isConnected } = useArweaveWallet();
  const [mintState, setMintState] = useState<MintState>({
    isPending: false,
    isConfirming: false,
    isSuccess: false,
    error: null,
    transactionId: null,
  });

  const mintNFT = async (metadata: {
    title: string;
    description: string;
    image: string;
    [key: string]: any;
  }) => {
    if (!isConnected || !address) {
      throw new Error("Wallet not connected");
    }

    setMintState({
      isPending: true,
      isConfirming: false,
      isSuccess: false,
      error: null,
      transactionId: null,
    });

    try {
      // Send mint message to AO Process
      const messageId = await ao.message({
        process: processId,
        data: JSON.stringify({
          action: "Mint",
          recipient: address,
          metadata,
        }),
        tags: [
          { name: "Action", value: "Mint" },
          { name: "Recipient", value: address },
        ],
        signer: createDataItemSigner(window.arweaveWallet),
      });

      setMintState((prev) => ({
        ...prev,
        isPending: false,
        isConfirming: true,
        transactionId: messageId,
      }));

      // Wait for result
      const result = await ao.result({
        message: messageId,
        process: processId,
      });

      if (result.Messages?.[0]?.Data) {
        const response = JSON.parse(result.Messages[0].Data);
        if (response.success) {
          setMintState((prev) => ({
            ...prev,
            isConfirming: false,
            isSuccess: true,
          }));
        } else {
          throw new Error(response.error || "Minting failed");
        }
      }
    } catch (error) {
      setMintState((prev) => ({
        ...prev,
        isPending: false,
        isConfirming: false,
        error: error instanceof Error ? error.message : "Unknown error",
      }));
      throw error;
    }
  };

  const resetMint = () => {
    setMintState({
      isPending: false,
      isConfirming: false,
      isSuccess: false,
      error: null,
      transactionId: null,
    });
  };

  return {
    mintNFT,
    resetMint,
    ...mintState,
  };
}

参考: シリーズ記事

Claude Codeを使用した開発フロー(特にdApps)に最適なテンプレートを構築中で、各項目についてメモを残しています。

  1. Claude Code x MVP開発に最適なdApps要求定義
  2. Claude Code x MVP開発に最適なNext.jsディレクトリ構成
  3. Claude Code x MVP開発に最適なUXデザインガイド
  4. Claude Code x MVP開発に最適なNext.jsの状態管理パターン [Zustand, React Hook Form, TanStack Query]
  5. Claude Code x MVP開発の作業フロー(のメモ)
  6. AO dApps × Next.jsのnpmライブラリ選定 [2025年]
  7. EVM dApps x Next.jsのnpmライブラリ選定 [2025 年]
  8. Solana dApps × Next.jsのnpmライブラリ選定 [2025年]


【Arweave Japan とは】
Arweave Japan は Arweave / AO の日本語ビルダーエコシステム構築を目的とした分散型組織です。

【​Arweave / AO とは?】
​Arweave は無制限にスケール可能な分散型ストレージであり、AO は Arweave 上に構築された無制限にスケール可能な分散型スーパーコンピュータです。Arweave と AO を使って既存のブロックチェーンでは実現不可能であった実用的なプロダクトが開発できます。

イーサリアム L1 のリステーキングによってセキュリティが担保され、TVL はローンチ数ヶ月で 1000 億円近くまで上がり、今後数兆円規模の市場が期待されます。完全フェアローンチされた AO のトークン設計によって、この流動性は AO 上のプロジェクトが活用することができ、ビットコインと同じ半減スケジュールでミントされる AO トークンは開発者やプロジェクトが受け取れる仕組みとなっています。

​Web2 を置き換えるレベルで実用的なプロジェクトが構築できる唯一無二の分散型プロトコル AO で開発することはグローバルの第一線で活躍する非常に大きなチャンスとなっています。

【Social Links】

【Blog】

1080x360.jpeg

8
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?