一般

Turboを使用した決定論的メタデータの作成

Last updated October 19, 2024

MPL-Hybridプログラムでメタデータのランダム化機能を利用するには、オフチェーンメタデータのURIが一貫性があり、段階的な構造に従う必要があります。これを達成するために、ArweaveとTurbo SDKのパスマニフェスト機能を使用します。このガイドでは、その設定方法をデモンストレーションします!

Turboとは

Turboは、Arweaveとの間でデータの資金調達、インデックス作成、送信を合理化する超高スループットのPermaweb サービスです。クレジットカードやデビットカードでの法定通貨による支払いオプション、およびETH、SOL、ARなどの暗号通貨のグラフィカルおよびプログラマティックインターフェースを提供します。

前提条件

必要なパッケージ

このガイドに必要なパッケージをインストールしてください。

npm i @ardrive/turbo-sdk

メタデータフォルダ

この例では、決定論的な方法でメタデータをアップロードする方法を示します。そのためには、開始前にすべてのアセットを準備する必要があります。

メタデータを生成するには、これらの方法のいずれかを使用し、0から始まる増分命名規則に従ってメタデータを保存します:

metadata/
├─ 0.json
├─ 1.json
├─ 2.json
├─ ...

注意: メタデータを作成する際は、NFTの適切なJSONスキーマに従うことを確認してください!

Turboのセットアップ

Turboは複数のトークンとチェーンに対応しているため、このガイドではSolanaをトークンとして使用するようにTurboインスタンスを設定する必要があります。これは、TurboFactory.authenticated()メソッドを呼び出し、Solana固有の設定オプションを渡すことで行います。

import { TurboFactory } from '@ardrive/turbo-sdk';
// アップロードの支払いに使用するkeypair.jsonファイルを
// ここでインポートします
import secretKey from "/path/to/your/keypair.json";
const turbo = TurboFactory.authenticated({
privateKey: bs58.encode(Uint8Array.from(secretKey)),
token: 'solana',
gatewayUrl: `https://api.devnet.solana.com`,
paymentServiceConfig: { url: "https://payment.ardrive.dev" },
uploadServiceConfig: { url: "https://upload.ardrive.dev" },
});

注意: この例では、devnetで動作するように環境を設定したいため、gatewayUrlpaymentServiceConfiguploadServiceConfigを明示的に提供しています。メインネットでの使用では、これらのフィールドを空のままにすることができ、Turboはデフォルトでメインネットのエンドポイントを使用します。

メタデータのアップロード

Turboは、TurboAuthenticatedClient.uploadFolder()関数を使用してメタデータフォルダ全体をアップロードするプロセスを簡素化します。この関数はデフォルトでマニフェストをサポートしており、メタデータ作成とエスクローセットアップに使用できるマニフェストIDをmetadataUploadResponse.manifestResponse?.id経由で返します。

プロセスを簡素化するために、このガイドでは全体のワークフローを処理するuploadMetadata()というヘルパー関数を提供します。

const metadataUploadResponse = await uploadMetadata(turbo);

uploadMetadata()ヘルパーのステップ

  1. calculateRequiredLamportsForUpload()を呼び出してアップロードに必要なlamportを決定します。これはアップロードコストをWinc(Turboのトークン)で計算し、TurboAuthenticatedClient.getWincForToken()を使用してlamportに変換します。

  2. ウォレットに十分なWincがない場合、関数はTurboAuthenticatedClient.topUpWithTokens()を使用してlamportをWincに変換することで必要な金額をチャージします。

  3. ウォレットに十分なWincがあれば、TurboAuthenticatedClient.uploadFolder()を使用してメタデータフォルダをアップロードし、メタデータのマニフェストIDを返します。

必要なLamportの計算

const requiredLamportsForMetadata = await calculateRequiredLamportsForUpload(
turbo,
calculateFolderSize(metadataFolderPath)
);

まず、フォルダの総サイズをバイト単位で計算します。次の関数は、フォルダ構造を再帰的に走査してすべてのファイルのサイズを合計します:

function calculateFolderSize(folderPath: string): number {
return fs.readdirSync(folderPath).reduce((totalSize, item) => {
const fullPath = path.join(folderPath, item);
const stats = fs.statSync(fullPath);
return stats.isFile()
? totalSize + stats.size
: totalSize + calculateFolderSize(fullPath);
}, 0);
}

フォルダサイズが決定されたら、次のステップはアップロードに必要なlamportの数を計算することです。これはcalculateRequiredLamportsForUpload()関数を使用して行われ、Wincコストを決定してlamportに変換します:

async function calculateRequiredLamportsForUpload(turbo: TurboAuthenticatedClient, fileSize: number): Promise<number> {
/// ファイルサイズが105 KiB未満の場合、支払う必要はありません
if (fileSize < 107_520) { return 0; }
/// ファイルをアップロードするのにどれくらいのwincがかかるかを確認
const uploadPrice = new BigNumber((await turbo.getUploadCosts({ bytes: [fileSize]}))[0].winc);
/// 現在のWinc残高を確認
const currentBalance = new BigNumber((await turbo.getBalance()).winc);
/// ファイルをアップロードするのに必要なWincを計算
const requiredWinc = uploadPrice.isGreaterThan(currentBalance)
? uploadPrice.minus(currentBalance)
: new BigNumber(0); // 残高が十分な場合、Wincは不要
/// 必要なWincが0の場合、ファイルをアップロードするのに十分な量を既に持っています
if (requiredWinc.isEqualTo(0)) { return 0; }
/// 1 SOLがどれくらいのWincに相当するかを計算(1 SOL = 1_000_000_000 Lamports)
const wincForOneSol = new BigNumber((await turbo.getWincForToken({ tokenAmount: 1_000_000_000 })).winc);
/// ファイルをアップロードするのに必要なSOLを計算(SOLで返す)
const requiredSol = requiredWinc.dividedBy(wincForOneSol).toNumber();
/// 必要なSOLの量をLamportで返す
return Math.floor(requiredSol * 1_000_000_000)
}

ウォレットのチャージとメタデータのアップロード

ウォレットをチャージするために、前のステップで計算されたlamportの金額を指定してTurboAuthenticatedClient.topUpWithTokens()メソッドを使用します。この金額はアップロードプロセスに必要なWinc(Turboのトークン)に変換されます。

注意: チャージプロセスは条件付きです。ウォレットに既に十分なWincがある場合、calculateRequiredLamportsForUpload()関数は0を返し、チャージは不要になります。

// 必要に応じてウォレットをチャージ
await turbo.topUpWithTokens({tokenAmount: lamportToTokenAmount(requiredLamportsForMetadata)});

ウォレットに十分なWincがあることを確認した後、画像フォルダのアップロードを続行できます。これはTurboAuthenticatedClient.uploadFolder()メソッドを使用して行われます。アップロードは、アップロードされたファイルへのアクセスを可能にするマニフェストIDを返し、次のようにフォーマットされます:https://arweave.net/${manifestID}/${nameOfTheFile.extension}

注意: アップロード中に各ファイルに正しいMIMEタイプを設定することが重要です。MIMEタイプが正しく設定されていない場合、URI経由でアクセスした際にファイルが適切に表示されない可能性があります。

// 画像フォルダをアップロード
const metadataUploadResponse = await turbo.uploadFolder({
folderPath: metadataFolderPath,
dataItemOpts: { tags: [{ name: 'Content-Type', value: 'application/json' }] },
});

完全なコード例

簡単に使用するためにコピー&ペーストできる完全なコード例を以下に示します。