
Solana Frames:FarcasterでcNFTをミントする
この記事で解説する内容
分散型ソーシャルメディアは、オンラインデータに対するプライバシーとコントロールをユーザーにより多く委ねるという、大きな転換をもたらします。従来のソーシャルメディアプラットフォームとは異なり、分散型ネットワークでは個人が自身のソーシャルグラフとデジタルアイデンティティを直接管理できます。Farcasterのようなプラットフォームはその最前線に立ち、さまざまなプラットフォーム間でオープンかつ相互運用可能なコミュニケーションを促進する、検閲耐性のあるネットワークを構築しています。Farcasterにおける重要なイノベーションの1つがFramesです。通常のソーシャルメディア投稿を本格的なアプリケーションへと変え、オンラインでのソーシャル体験を豊かにします。
この記事では、Farcasterと、最近追加されたSolanaサポートについて解説します。Farcasterとは何か、Framesとは何か、Solanaアドレスを認証する方法を取り上げます。その後、Helius Mint APIを使用して、ユーザーが認証したSolanaアドレスにcNFTをミントするFrameを作成する短いチュートリアルを紹介します。このチュートリアルでは、TypeScriptとNext.jsの使用経験があることを前提とします。
Farcasterとは?
Farcasterは、Ethereumネットワークを活用した十分に分散化されたソーシャルネットワークです。TwitterやRedditなどの人気ソーシャルネットワークに似た公開プラットフォームを提供し、ユーザーはプロフィールを作成し、投稿(castと呼ばれます)を共有し、ほかのユーザーをフォローできます。Farcasterの特長は、ユーザーの所有権とポータビリティを重視している点です。ユーザーは自身のアカウントや他者との関係を所有します。デジタルアイデンティティとソーシャルグラフを維持したまま、異なるアプリケーション間を移動できます。
Framesとは?
Framesを使用すると、開発者は任意のcastをインタラクティブなアプリケーションに変換できます。OpenGraph標準を拡張することで、静的な埋め込みを動的でインタラクティブな体験へと変えます。これにより、ユーザーはWarpcasterを含む任意のFarcasterクライアント内で、投票、ライブフィード、インタラクティブギャラリーなどの機能を作成できます。Frameの中核となるのは、HTMLページの<head>とともに返される一連の<meta>タグです。ページに必要なFrameプロパティがすべて含まれている場合、FarcasterアプリはそのページをFrameとしてレンダリングします。Frame仕様には、これらの必須プロパティが記載されています。たとえば、初期Frameは、次の要件を満たす必要があります。
- HTMLの
<head>セクションで有効なFrameを返す - ユーザーがブラウザでFrameをクリックして開いた場合に備え、有効なHTMLの
<body>を返す - Farcasterクライアントによってキャッシュされるため、初期Frameに動的コンテンツを含めない
fc:frame:stateタグを含めない
FarcasterでSolanaアドレスを認証する
Farcasterは最近、Solanaアドレスのサポートを追加しました。これにより、開発者はユーザーのSolanaウォレットと連携するFrameを構築できます。サポート開始から24時間も経たないうちに、9,328を超える一意のFarcaster FIDがSolanaアドレスを認証しました。
ユーザーは次の手順でSolanaアドレスを認証できます。
- Settingsに移動します
- Verified Addressesをクリックし、「Verify an address」ボタンをクリックします
- Solanaウォレットを接続し、所有権を証明するメッセージに署名します
現在サポートされているウォレットはPhantomのみです。
チュートリアル:cNFTをミントするFrameを作成する
この記事では、認証済みのSolanaアドレスを持つユーザーが圧縮NFT(cNFT)をミントできるFrameを作成します。Framesの構築とデバッグに使用できるNext.jsテンプレート、frames.jsを使ってプロジェクトを立ち上げます。CoinbaseのOnchainKit、特にFrame Kitを使用し、FarcasterユーザーがFrameを操作した際にそのユーザー情報を取得します。その後、このデータをHelius Mint APIに渡し、ユーザーのSolanaウォレットに次のcNFTをミントします。
Frameをセットアップする
frame.jsでFrameを作成するのは、スターターテンプレートをクローンするだけなので簡単です。次のコマンドを実行し、<name>を、テンプレートのクローン先として作成する新しいフォルダー名に置き換えます。
npx degit github:framesjs/frames.js/examples/framesjs-starter#main <name>ここではディレクトリ名をhelius-frameとしたため、<name>を<helius-frame>に置き換えました。次に、新しいディレクトリ(例:cd helius-frame)へ移動し、yarn installコマンドで必要な依存関係をインストールします。ダウンロードが完了したら、yarn devを実行して開発サーバーを起動します。ブラウザでhttp://localhost:3000にアクセスすると、次の画面が表示されます。
Debugハイパーリンクをクリックすると、http://localhost:3000/debugに移動します。
デプロイせずにFrameをローカルでテストできるため便利です。たとえば、サインインしてボタンをテストしたり、ほかのユーザーになりすましたり、ハブの状態をモックしたりできます。Warpcasterには、Frameを検証できる優れたFrame Validatorツールもあります。以降は、個人的な好みからFrame Validatorツールを使用してテストします。
次に、yarn installを実行して、テンプレートに必要な依存関係をインストールします。さらに、yarn install @coinbase/onchainkitを実行してOnchainKitをインストールします。
プロジェクトのフォルダーを次のように再構成します。
- examplesフォルダーを削除します。サンプルからはFramesの構築方法について有益な知見を得られますが、今回の目的には不要です。このフォルダーを削除すると、プロジェクトの肥大化を抑えられます。ただし、一般的なFramesを構築する別の方法を知るために、内容を確認することを強くおすすめします
- appディレクトリにapiフォルダーを作成します。この手順により、アプリケーションのフロントエンドとバックエンドが明確に分かれ、APIルートに関するNext.jsの規約に沿った構成になります。次に、framesフォルダーを、その内容であるroute.tsとともに新しいapiフォルダーへ移動します。
- appディレクトリにutilsフォルダーを作成します。この新しいフォルダー内にmint.tsファイルを作成します。ここにcNFTのミント処理を記述します
これで、いよいよFrameの構築を始められます!
Frameを編集する
layout.tsx
まず、layout.tsxファイルから始めます。内容をすべて削除し、次のコードに置き換えます。
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
{children}
);
}RootLayoutコンポーネントは、アプリケーション全体を一貫したレイアウト構造でラップする、ミニマルなアプローチを採用しています。柔軟性があり、再利用できるように設計されています。このミニマルなレイアウトを出発点として、開発者はこのファイルをテンプレートにし、必要に応じて独自のFramesを構築、カスタマイズできます。
layout.tsxファイルを変更すると、ローカルでデバッガーを使用する際に問題が発生する点に注意してください。layout.tsxの編集は必須ではありません。ここで編集するのは、アプリケーションのメタデータをpage.tsxに導入するためです。回避策として、このファイルにメタデータを導入することも可能です。
page.tsx
page.tsxの内容を削除し、次のコードに置き換えます。
import type { Metadata } from "next";
import { getFrameMetadata } from "@coinbase/onchainkit";
const baseURL = "https://helius-frame.vercel.app/";
const frameMetadata = getFrameMetadata({
image: {
src: `${baseURL}/default.jpg`,
aspectRatio: "1:1",
},
buttons: [
{
label: "Click to mint a cNFT on Solana!",
action: "post",
},
],
postUrl: `${baseURL}api/frames`,
});
export const metadata: Metadata = {
title: "Mint a cNFT on Solana",
description: "Mint a cNFT to your verified Solana address using the Helius Mint API",
openGraph: {
title: "Mint a cNFT on Solana",
description: "Mint a cNFT to your verified Solana address using the Helius Mint API",
images: ["https://helius-frame.vercel.app/default.jpg"],
},
other: {
...frameMetadata,
},
};
export default function Page() {
return (
Mint a cNFT on Farcaster using the Helius Mint API
Built by 0xIchigo
GitHub Repo
);
}上記のコードでは、Webサーバー上のURLで公開する初期Frameをセットアップしています。ここでは、初期FrameをVercel上のhttps://helius-frame.vercel.app/でホストしています。このチュートリアルの後半で、Vercelを使ったデプロイ方法を解説します。
まず、このファイルのmetadataエクスポートに型を付けるため、Next.jsからMetadata型をインポートします。また、@coinbase/onchainkitからgetFrameMetadataをインポートします。次に、コードの重複を減らすため、アプリケーションのベースURLを定義します。
続いてgetFrameMetadataを呼び出し、初期Frameのメタデータを設定します。このメタデータには、次の項目が含まれます。
- アスペクト比1:1で、Frameの視覚要素となる画像(つまりsrc)
- 「Click to mint a cNFT on Solana!」というラベルのボタン。クリックすると、指定された
postUrl(つまり${baseURL}api/frames)にPOSTリクエストを送信します。この操作によってミント処理が開始されます
次に、ページのタイトルと説明、OpenGraphメタデータ(つまりopenGraph)、およびotherプロパティに展開された、先ほど定義したframeMetadataを含むmetadataオブジェクトをエクスポートします。
最後に、pageでデフォルトのページコンポーネントを定義してエクスポートします。cNFTをミントするよう促す基本的なh1を返します。これは、Farcasterでは何らかのHTMLを返す必要があるためです。主な操作はサイト自体へ直接アクセスするのではなく、Framesを通じて行われます。ただし、ユーザーがクリックしてサイトを開いた場合に備え、サイト上にも何らかの内容が必要です。
mint.ts
mint.tsに次のコードを貼り付けます。
const url = `Get a URL at dev.helius.xyz`;
export const mintCompressedNFT = async (address: string) => {
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
jsonrpc: "2.0",
id: "frames-sol-mint",
method: "mintCompressedNft",
params: {
name: "Solana Soldier",
symbol: "HELIUS",
owner: address,
description: "A decentralized warrior backed by Solana's most loved RPC Nodes, APIs, Webhooks, and Developer Tooling",
attributes: [
{
trait_type: "RPCs",
value: "Fast",
},
{
trait_type: "Company",
value: "Helius"
},
],
imageUrl: "https://shdw-drive.genesysgo.net/HmvVwYAy7cxWECLbu2cjWBSHysVoriTqgdugyePVg3rY/cnftmint.jpg",
externalUrl: "https://www.helius.dev/",
sellerFeeBasisPoints: 6900,
},
}),
});
const data = await response.json();
console.log(`data: ${JSON.stringify(data, null, 2)}`);
if (!response.ok) {
throw new Error(`The response was not okay - ${response.status}`);
}
if (!data.result || !data.result.assetId) {
throw new Error(`The response did not include the expected data.result.assetId field`);
}
return data.result;
};mintCompressedNFTユーティリティ関数は、指定したアドレスに事前定義済みのcNFTをミントするため、アプリケーションにとって重要です。Helius Mint APIを使用し、cNFTのミント処理をカプセル化した、シンプルで再利用可能な非同期関数を作成しています。urlとして定義するHelius General RPC URLが必要です。まだ取得していない場合は、Heliusダッシュボードにアクセスし、urlで使用する無料のRPC URLを生成してください。
このリポジトリではSecureRPC URLを使用しています。APIキーが漏洩しないよう注意し、プロキシの運用を検討してください。リクエスト自体については、リクエストスキーマの詳細な説明をこちらで確認できます。
また、執筆時点ではFarcasterがトランザクションを完全にはサポートしていないため、cNFTの画像はすでにShdwDriveへアップロードしてあります。署名者を指定すれば、Helius Mint APIがオフチェーンメタデータのArweaveへのアップロードを処理します。
この関数には堅牢なエラー処理も含まれており、ミント処理中に発生した問題を確実に捕捉して報告します。また、デバッグ用にレスポンスデータをログへ出力します。
route.ts
api/frames/route.tsに次のコードを貼り付けます。
import { NextRequest, NextResponse} from "next/server";
import { getFrameMessage, getFrameHtmlResponse } from "@coinbase/onchainkit/frame";
import { mintCompressedNFT } from "../../utils/mint";
const baseURL = "https://helius-frame.vercel.app/";
export async function POST(req: NextRequest): Promise {
const body = await req.json();
// Validate the frame message and extract the user ID
const { isValid, message } = await getFrameMessage(body, {
neynarApiKey: "NEYNAR_ONCHAIN_KIT",
});
console.log(JSON.stringify(message, null, 2));
// Make sure they're following us before they can mint the cNFT
if (isValid && !message.following) {
return new NextResponse(getFrameHtmlResponse({
image: {
src: `${baseURL}/error.jpg`,
aspectRatio: "1:1",
},
buttons: [
{
label: "You need to be following to mint! Click to retry",
action: "post",
},
],
postUrl: `${baseURL}api/frames`,
}));
// Make sure they have a verified Solana address
} else if (isValid && message.interactor.verified_addresses.sol_addresses) {
const solanaAddresses = message.interactor.verified_addresses.sol_addresses;
console.log(`SOLANA ADDRESSES: ${solanaAddresses}`);
if (solanaAddresses.length !== 0) {
try {
console.log(`MINTING to ${solanaAddresses[0]}`);
// We take the first address address, in case there's more than one
const mintResult = await mintCompressedNFT(solanaAddresses[0]!);
return new NextResponse(getFrameHtmlResponse({
image: {
src: `${baseURL}/success.jpg`,
aspectRatio: "1:1",
},
buttons: [
{
label: "Success! View your cNFT on XRAY",
action: "link",
target: `https://xray.helius.xyz/token/${mintResult.assetId}?network=mainnet`
},
],
}));
} catch (e: any) {
console.log(`Minting failed: ${e}`);
return new NextResponse(getFrameHtmlResponse({
image: {
src: `${baseURL}/error.jpg`,
aspectRatio: "1:1",
},
buttons: [
{
label: "Minting failed. Click to retry",
action: "post",
},
],
postUrl: `${baseURL}api/frames`,
}));
}
} else {
console.log(`No Solana address found`);
return new NextResponse(getFrameHtmlResponse({
image: {
src: `${baseURL}/error.jpg`,
aspectRatio: "1:1",
},
buttons: [
{
label: "No Solana address found. Click to retry",
action: "post",
},
],
postUrl: `${baseURL}api/frames`
}));
}
} else {
return new NextResponse(getFrameHtmlResponse({
image: {
src: `${baseURL}/error.jpg`,
aspectRatio: "1:1",
},
buttons: [
{
label: "Invalid frame message. Click to retry",
action: "post",
},
],
postUrl: `${baseURL}api/frames`,
}));
}
}このファイルは、FrameへのすべてのPOSTリクエストを処理するNext.js APIルートです。まず、受信したリクエストの処理とレスポンスの送信に使用するNextRequestとNextResponseをnext/serverからインポートします。また、Frameメッセージの検証とFrame操作用HTMLレスポンスの生成に使用するgetFrameMessageとgetFrameHtmlResponseを**@coinbase/onchainkit/frame**からインポートします。さらに、mintCompressedNFTユーティリティ関数もインポートします。
コードの重複を減らすため、baseURLを定義しています。これは、リソースやAPIエンドポイントへのパスを構築するために使用します。
このファイルはPOST関数をエクスポートします。この関数は、受信したリクエスト本文を解析してFrameメッセージを抽出します。getFrameMessageを使用し、受信したFrame操作とメッセージのペイロードが有効かどうかを確認します。そのために、リクエスト本文をJSONとして渡し、デフォルトのNeynar APIキー(つまり**「NEYNAR_ONCHAIN_KIT」**)を渡します。Neynarは、Farcaster開発の簡素化を目的とした開発者向けプラットフォームです。getFrameMessageは、FrameValidationResponseに解決されるPromiseを返します。定義は次のとおりです。
export type FrameValidationResponse =
| { isValid: true; message: FrameValidationData }
| { isValid: false; message: undefined };したがって、Frame操作が有効な場合は、FrameValidationData型のメッセージを返します。定義は次のとおりです。
export interface FrameValidationData {
button: number; // Number of the button clicked
following: boolean; // Indicates if the viewer clicking the frame follows the cast author
input: string; // Text input from the viewer typing in the frame
interactor: {
fid: number; // Viewer Farcaster ID
custody_address: string; // Viewer custody address
verified_accounts: string[]; // Viewer account addresses
verified_addresses: {
eth_addresses: string[] | null;
sol_addresses: string[] | null;
};
};
liked: boolean; // Indicates if the viewer clicking the frame liked the cast
raw: NeynarFrameValidationInternalModel;
recasted: boolean; // Indicates if the viewer clicking the frame recasted the cast
valid: boolean; // Indicates if the frame is valid
}これには、ユーザーのFarcaster ID、認証済みのEthereumまたはSolanaアドレスを持っているかどうか、castに「いいね」したかどうかなど、関連データが含まれるため非常に有用です。このチュートリアルでは、ミントを試みているユーザーが私のプロフィールをフォローしているか確認します。フォローしている場合は、cNFTのミントを許可します。また、cNFTのミント先となる認証済みSolanaアドレスを持っていることも確認します。
まず、操作が有効か、ユーザーが私のアカウントをフォローしているかを確認します。条件を満たさない場合は、エラーメッセージと再試行を促すボタンを表示します。次に、ユーザーが認証済みのSolanaアドレスを持っているか確認します。1つ以上のアドレスが存在する場合は、ミント処理へ進みます。最初の認証済みSolanaアドレスを取得し、mintCompressedNFTに渡します。成功すると、成功を示す画像と、OrbでcNFTを表示するためのリンクを返します。失敗した場合はエラーをログに記録し、再試行を促すメッセージを返します。認証済みのSolanaアドレスが見つからない場合は、その旨を示すエラーメッセージを返し、再試行を促します。最後に、Frameメッセージが無効な場合は、Frameが無効であることを示すエラーメッセージを返し、再試行を促します。
このコードでは、getFrameHtmlResponseを使用して、ミント処理や検証チェックの結果に応じたHTMLレスポンスを生成します。これには、特定のユーザー操作に応じたレスポンス画像、アスペクト比、ボタンの設定が含まれます。
本番環境へプッシュする
コードが完成したら、新しいGitHubリポジトリを作成し、そこへコードをプッシュします。コマンドラインで次のコマンドを実行し、コードをGitHubへプッシュしてください。
git add .
git commit -m "Initial commit"
git remote add origin https://github.com/<username>/<new repo name>.git
git branch -M main
git push -u origin mainコードをGitHubへプッシュしたら、次はアプリケーションをデプロイします。VercelはGitHubとシームレスに統合でき、デプロイプロセス全体を簡素化できるため、今回はVercelを使用します。次の手順で進めます。
- VercelのWebサイトでアカウントにサインインします。アカウントがない場合は作成してください。十分に利用できるHobbyプラン(つまり無料)があり、今回の目的にはそれで対応できます
- ログインしたらダッシュボードへ移動し、New Projectボタンをクリックします。VercelからGitHubプロジェクトのインポートを求められます。前の手順で作成したリポジトリを選択します
- Vercelは使用しているフレームワークを自動検出し、ビルド設定を提案します。VercelはNext.jsの開発元によって作られており、最上位のサポートを提供しているため、複雑な設定をあまり心配する必要はありません
- プロジェクトの設定が完了したら、Deployボタンをクリックします。Vercelが自動的にデプロイプロセスを開始し、プロジェクトをビルドして一意のVercel URLへデプロイします。
- デプロイが完了すると、稼働中のアプリケーションへアクセスするための一意のURLがVercelから提供されます。このプロジェクトはhttps://helius-frame.vercel.app/にビルドされました。GitHubリポジトリ名の後に**.vercel.app**が続く形式です。Vercelでは、プロジェクト設定からカスタムドメインも構成できます
以上です!これらの手順により、Farcaster FrameをGitHubへプッシュし、Vercelでホストできました。前述したFrames validatorなどのテストツールを使用して、Frameの機能をテストできます。あとはFrameのURLを含むcastを作成するだけです!
まとめ
ブロックチェーンとソーシャルメディアの融合は、デジタル空間の捉え方や、その中での交流方法を大きく進化させます。FarcasterとFramesの人気により、従来のソーシャルメディアプラットフォーム上で静的だった投稿が、インタラクティブな体験へと変わります。この記事では、FarcasterとFramesについて解説しました。チュートリアルでは、Farcasterに最近追加されたSolanaサポートを活用し、ボタンを1回クリックするだけでユーザーにcNFTをミントする方法を紹介しました。Farcaster上で開発したい方にも、新しく刺激的な方法でソーシャルメディアのフィードを閲覧したい方にも、可能性は無限に広がっています。
ここまでお読みいただき、ありがとうございます!Farcasterに参加したい方は、次の招待リンクをご利用ください!
Solanaの最新情報を見逃さないよう、以下にメールアドレスを入力してください。さらに深く学ぶ準備はできましたか?Heliusブログで最新記事を読み、今日からSolanaの旅を続けましょう。
その他のリソース
関連記事
Heliusを購読
Solana開発の最新情報や新しい記事の公開通知を受け取れます


