
プラグ&プレイ Token Extensions
はじめに
Token Extensions を使うと、Solana 上でプログラム可能なトークンを簡単に作成できます。開発者は、新しい Token Extensions プログラムを使用して、十数種類の Mint Extension と Account Extension を組み合わせられます。このチュートリアルを完了すると、代替可能トークンと非代替性トークン、利息付きトークン、凍結トークン、完全な権限を持つトークン(自分が所有していないアカウントから移動する操作を含む)を作成して操作できるようになります。また、ミントアカウントを閉じて資金を回収する方法も学べます。
プラグ&プレイ Token Extensions は、インタラクティブな Solana Playground のサンプルを使い、TypeScript で次のトークンを作成しながら Token Extensions を学べるチュートリアルです。
このチュートリアルで使用するコードは、Solana 開発者ガイドを基に調整したものです。Token Extensions を作成するスクリプトは、その他のリソースセクションにあります。
Token Extensions アカウント
新しい命令を導入する Token Extensions は、Token Program を基盤として構築されています。両方のプログラムで作成されたアカウントは、Token Program の命令が終了して Token Extensions の命令が始まる位置まで、先頭部分の長さと内容が同じです。アカウントデータに馴染みがない場合は、Anchor 入門でアカウント領域について確認できます。要するに、Solana 上でアカウントを作成する際は、オンチェーンにデータを保存する領域を明示的に割り当てる必要があります。その領域から、アカウントをレント免除にするために必要な lamports の量を計算できます。
Extension を含むミントアカウントは、次のように作成します。Token Extensions 用の領域を割り当てるには、getMintLen 関数を使って、Extension を含むミントアカウントのサイズを取得します。NonFungibleToken.ts の例では、MetadataPointer と MintCloseAuthority の Extension タイプを含むミントの長さを取得します。
NonFungibleToken.ts 54 行目:
// Size of Mint Account with extension
const mintLen = getMintLen([
ExtensionType.MetadataPointer,
ExtensionType.MintCloseAuthority,
]);Metadata Extension と Metadata Pointer Extension を使うと、サードパーティのプロトコルに依存せずにメタデータを保存し、参照できます。TokenMetadata は @solana/spl-token-metadata のインターフェースで、updateAuthority、mint、name、symbol、uri、additionalMetadata などのフィールドがあります。メタデータの長さは、メタデータオブジェクトに対して pack 関数を使用して取得します。
NonFungibleToken.ts 60 行目:
// Metadata to store in Mint Account
const metaData: TokenMetadata = {
updateAuthority: updateAuthority,
mint: mint,
name: "Solana Soldier",
symbol: "SOLD",
uri: "https://shdw-drive.genesysgo.net/G1Tzt42SDqCV3x9vPY5X826foA8fEk8BR4bB5wARh75d/OIG2.jpg",
additionalMetadata: [["armor", null]],
};
// Size of metadata
const metadataLen = pack(metaData).length;
// Size of MetadataExtension 2 bytes for type, 2 bytes for length
const metadataExtension = TYPE_SIZE + LENGTH_SIZE;ミントの長さ、Metadata Extension、メタデータの長さを合計し、Mint Account に必要な最小 lamports を求めます。
NonFungibleToken.ts 75 行目:
// Minimum lamports required for Mint Account
const lamports = await connection.getMinimumBalanceForRentExemption(
mintLen + metadataExtension + metadataLen
);Token Extensions の命令
@solana/spl-token や @solana/spl-token-metadata などの Solana ライブラリは、Token Extensions プログラムの TypeScript サポートを提供します。まず、@solana/web3.js の SystemProgram を使用し、適切な領域とレント免除に必要な lamports を持つアカウントを作成します。
NonFungibleToken.ts 80 行目:
// Instruction to invoke System Program to create new account
const createAccountInstruction = SystemProgram.createAccount({
fromPubkey: payer.publicKey, // Account that will transfer lamports to created account
newAccountPubkey: mint, // Address of the account to create
space: mintLen, // Amount of bytes to allocate to the created account
lamports, // Amount of lamports transferred to created account
programId: TOKEN_2022_PROGRAM_ID, // Program assigned as owner of created account
});次に、ミントを指定する命令を作成します。以下の命令は、initializeMint を呼び出す前に実行する必要があります。プラグ&プレイで使用するミント前の命令は次のとおりです。
- Metadata Pointer を初期化: createInitializeMetadataPointerInstruction
- Mint Close Authority を初期化: createInitializeMintCloseAuthorityInstruction
- Non-Transferable Mint を初期化: createInitializeNonTransferableMintInstruction
- Permanent Delegate を初期化: createInitializePermanentDelegateInstruction
- Interest Bearing Token Mint を初期化: createInitializeInterestBearingMintInstruction
メタデータアカウントは、initializeMint とは独立して初期化および更新できます。ただし、Metadata Pointer はミント前に初期化する必要があります。トランザクション内の命令の順序によって、実行順序が決まります。ミントアカウントを作成し、Metadata Pointer を初期化してから、ミントの公開鍵を使って Mint Close Authority を初期化します。その後、ミントアカウントとメタデータアカウントを初期化し、追加のメタデータを更新します。
NonFungibleToken.ts 135 行目:
transaction = new Transaction().add(
createAccountInstruction,
initializeMetadataPointerInstruction,
initializeMintCloseAuthorityInstruction,
initializeMintInstruction,
initializeMetadataInstruction,
updateFieldInstruction
);nonFungibleToken.ts、soulboundToken.ts、interestBearingToken.ts、delegateToken.ts の各ファイルは、使用する Extension と命令が異なるものの、トークン作成について同じ構造に従っています。
まず、必要な関数を該当する Solana ライブラリからすべてインポートし、支払者を定義して、ミントアドレスを生成します。次に、目的の Token Extensions を持つミントアドレスに必要な領域と lamports を求めます。@solana/spl-token と @solana/spl-token-metadata ライブラリを使って命令を作成し、トランザクションに追加してネットワークへ送信し、結果を検証します。
転送やバーンなどのトークン操作は、さまざまな Token Extensions で使用できます。Metadata Extension を持つトークンでは、createUpdateFieldInstruction と createRemoveKeyInstruction の命令を使用して、それぞれメタデータを更新または削除できます。
プラグ&プレイ
Solana Playground のクライアントには、Token Extensions を操作するために必要な関数が含まれています。たとえば、ソウルバウンドトークンでは、Non-Transferable Extension と Metadata Extension を使ってソウルバウンド NFT を作成します。トークンを作成および操作する関数は同じファイル内にあり、インポートできます(Solana Playground の場合は、インポートではなくコードをコピー&ペーストする必要があります)。
quest.ts ファイルを使用すると、Solana Soldier をプレイできます。または、Token Extensions を含む各ファイルを実行して、それぞれの機能を確認できます。クエストを実行するには、Playground Wallet に Devnet SOL が必要です。Solana Devnet Faucet から Devnet SOL をエアドロップすることもできます。
クエストは、Solana Soldier の作成から始まります。これは、支払者(pg.wallet.keypair)が Mint Close Authority を持つ NFT です。これにより、支払者はミントアカウントを閉じて SOL を回収できます。この仕組みは、使用されなくなったアカウントから SOL をアプリケーションに戻す際に役立ちます。
Solana Soldier を作成したら、そのミントアドレスを使って NFT のメタデータを更新します。兵士の防具を iron に更新してみましょう。getMetadata、updateMetadata、removeMetadata の各関数は、NonFungibleToken.ts ファイルの 167 行目以降にあります。これらの関数は、Metadata Extension を使用するミントアカウントに対して呼び出せます。簡潔にするため、メタデータ関数は NonFungibleToken.ts ファイルにのみ含めています。
次はレベルアップです。利息付きトークンで Solana Soldier のレベルを表します。InterestBearingToken.ts から createInterestBearingToken をインポートし、その命令を使ってトークンを作成します。アカウントの利息は、InterestBearingToken.ts の 43 行目で設定された利率に従って増加します。amountToUiAmount 関数を使うと、発生した利息を含むトークン量を確認できます。利息は時間の経過とともに蓄積されるため、amountToUiAmount を呼び出して Solana Soldier のレベルを確認できます。
兵士がレベルアップを始めたら、任務を与える必要があります。任務はソウルバウンドトークンで表されます。兵士はこのトークンを転送できず(転送関数を呼び出してエラーを捕捉してみてください)、任務を破棄する唯一の方法はバーンすることです。任務を放棄せず、まず内容を確認してみましょう。ヒント:メタデータを確認してください。
任務を読んだ後、プラグ&プレイチュートリアルでは、トークンアドレスに軍隊を配置します。トークンの配置後、Solana Soldier は軍隊を支払者のもとへ呼び戻せなければなりません。Token Extensions では、デリゲート権限を導入することでこれを実現できます。デリゲート権限は、トークンの所有者に関係なく、トークンを転送およびバーンできます。Permanent Delegate には、自分がアクセスできるキーペアを設定してください。トークンをバーンおよび転送する際は、このキーペアによる署名が必要です。
デリゲートトークンで Solana Army を表します。DelegateToken.ts の 33 行目にある createDelegateTokens を使い、敵のトークンアカウントにデリゲートトークンを作成します。トークンがミントされると、Solana Army と敵が仮想的な戦闘を開始します。戦闘後、DelegateTokens.ts の 176 行目にある transferDelegateTokens 関数を使って、敵のアカウントから兵士を呼び戻します。
すべての兵士が敵のアドレスから呼び戻されたわけではない点に注目してください。残った兵士は戦死したものとします。@solana/spl-token の burn 関数を使い、敵のトークンアカウント(sourceTokenAccount)に残っているトークンをバーンします。
戦闘の成功により、Solana Soldier のクエストは終了します。@solana/spl-token の closeAccount 関数を使ってアカウントを閉じ、レントを返却しました。この記事で取り上げていない Token Extensions には、機密転送、転送手数料、転送フックがあります。転送フックと譲渡不可トークンのような一部の例外を除き、ほぼすべての Token Extensions を自由に組み合わせられます。このチュートリアルで扱った Token Extensions の参考用 Solana Playground は、その他のリソースにリンクされています。
まとめ
CryptoZombies に着想を得たこのプラグ&プレイチュートリアルでは、複数の Extension を組み合わせて、よく使われる Token Extensions を作成するための基礎を取り上げました。このチュートリアルでは、実践的なゲーム体験を通じて Token Extensions のコンポーザビリティを紹介しています。サンプルはそのままプラグ&プレイで利用できるほか、独自のトークンを作成する開発者向けのガイドとしても活用できます。アカウントサイズの算出から命令の順序付けまで、Token Extensions を持つアカウントを効率よく作成するプロセスをサンプルで示しています。 ゲーム形式の学習体験が役に立った場合は、Discord でフィードバックを共有するか、X で @helius をタグ付けしてください。
その他のリソース
関連記事
Heliusを購読
Solana開発の最新情報や新しい記事の公開通知を受け取れます


