
【Solanaコアコンセプト⑦】 Solanaにおけるトークン
この記事は、こちらの公式を翻訳・編集したものです。
0 はじめに
1 トークンとは
トークンは、さまざまな資産の所有権を表すデジタル資産です。
トークン化により、財産権のデジタル化が可能となり、代替可能および非代替可能な資産の管理の基本的な構成要素として機能します。
2 FTとNFT
代替可能トークンは、同じ種類で価値のある交換可能で分割可能な資産を表します(例:USDC)。

非代替可能トークン(NFT)は、分割不可能な資産(例:アート作品)の所有権を表します。
3 SPLトークンについて
このセクションでは、Solanaでのトークンの表現の基本について説明します。
これらはSPL(Solana Program Library)トークンとして参照されます。

3ー1 トークンプログラム
トークンプログラムには、ネットワーク上のトークン(代替可能および非代替可能なもの)との相互作用のための全ての命令ロジックが含まれています。


3ー2 ミントアカウント
ミントアカウントは特定のタイプのトークンを表します。

トークンの総供給量やミント権限(新しい単位のトークンを作成する権限を持つアドレス)などのグローバルメタデータを保存します。
3ー3 トークンアカウント
トークンアカウントは、特定のアドレスが特定のタイプのトークン(ミントアカウント)の何単位を所有しているかを追跡します。

情報 2つのバージョン
現在、トークンプログラムには2つのバージョンがあります。
オリジナルのトークンプログラムとトークン拡張プログラム(Token2022)です。

トークン拡張プログラムは、追加機能と改善を備えながらオリジナルのトークンプログラムと同じように機能します。
新しいトークン(ミントアカウント)の作成にはトークン拡張プログラムの使用が推奨されます。
1 主要ポイント
1ー1 所有権
トークンは、交換可能(代替可能)または非交換可能(ユニーク)な資産の所有権を表します。
1ー2 トークンプログラムに含まれる命令
トークンプログラムには、ネットワーク上の代替可能および非代替可能なトークンとの相互作用のためのすべての命令が含まれています。
1ー3 トークン拡張プログラム(Token2022)
トークン拡張プログラムは、追加機能を含むトークンプログラムの新バージョンで、同じコア機能を維持しています。
1ー4 ミントアカウント
ミントアカウントはネットワーク上のユニークなトークンを表し、総供給量などのグローバルメタデータを保存します。

1ー5 トークンアカウント
トークンアカウントは特定のミントアカウントのトークンの個々の所有権を追跡します。
補足
いくら持っているかなど

1ー6 関連トークンアカウント
関連トークンアカウントは、所有者のアドレスとミントアカウントのアドレスから派生したアドレスで作成されたトークンアカウントです。

2 トークンプログラム
1 概要
トークンプログラムには、ネットワーク上のトークン(代替可能および非代替可能なもの)との相互作用に関する全ての命令ロジックが含まれています。
Solana上の全てのトークンは、実質的にトークンプログラムによって所有されるデータアカウントです。

トークンプログラムの命令の完全なリストはこちらで見ることができます。

2 よく使われる命令
よく使用される命令には以下のものが含まれます。
① InitializeMint
新しいタイプのトークンを表す新しいミントアカウントを作成します。

② InitializeAccount
特定のタイプのトークン(ミント)の単位を保持するための新しいトークンアカウントを作成します。

③MintTo
特定のタイプのトークンの新しい単位を作成し、それをトークンアカウントに追加します。
これにより、トークンの供給が増加します。
ミントアカウントのミント権限を持つアカウントのみが行うことができます。

④Transfer
あるトークンアカウントから別のトークンアカウントへ特定のタイプのトークンの単位を転送します。
3 ミントアカウント
1 概要
Solana上のトークンは、トークンプログラムが所有するミントアカウントのアドレスによって一意に識別されます。

2 保存データ
このアカウントは特定のトークンのグローバルカウンタとして機能し、以下のようなデータを保存します。
①Supply
トークンの総供給量

②Decimals
トークンの小数精度

③Mint authority
新しいトークン単位の作成を許可されたアカウント。これにより供給が増加します。

④Freeze authority
トークンアカウント」からトークンの転送を凍結する権限を持つアカウント

3 保存データの詳細
各ミントアカウントに保存されている完全な詳細は以下の通りです。

補足
「mint_authority」はなんでオプションなんだろうと思ったのですが、ない場合は、ミント供給量がそれ以上増えないことを確定できるのですね。

4 参考ケース(USDC)
参考のために、こちらはUSDCミントアカウントのSolana Explorerリンクです。
補足
下のように、全て入っていますね。
Decimalsが「6」なのが個人的な発見でした。

4 トークンアカウント
1 概要
特定のトークンの各単位の個々の所有権を追跡するために、トークンプログラムが所有する別のタイプのデータアカウントを作成する必要があります。

このアカウントはトークンアカウントと呼ばれます。
2 保存データ
トークンアカウントに保存されている最も一般的に参照されるデータは以下の通りです。
①Mint
トークンアカウントが保持しているトークンのタイプ

②Owner
トークンアカウントからトークンを転送する権限を持つアカウント
補足
「owner」が2つ存在しているので混乱しやすいと思います。
ここは定義で抑えるのが良いと思います。

③Amount
トークンアカウントが現在保持しているトークンの単位

3 保存データの詳細
各トークンアカウントに保存されている完全な詳細は以下の通りです。

4 トークンの取得について
特定のトークン(ミント)の単位を所有するためには、ウォレットがそのトークンアカウントを作成し、ウォレットをトークンアカウントの所有者として指定する必要があります。

ウォレットは同じタイプのトークンに対して複数のトークンアカウントを作成することができますが、

各トークンアカウントは1つのウォレットのみが所有し、1種類のトークンの単位のみを保持することができます。

5 所有者について
各トークンアカウントのデータには、その特定のトークンアカウントを管理する権限がある人を識別するためのownerフィールドが含まれています。

これはAccountInfoで指定されたプログラムオーナー(すべてのトークンアカウントのためのトークンプログラム)とは別です。

5 関連トークンアカウント
1 概要
特定のミントと所有者のトークンアカウントのアドレスを簡単に見つけるために、関連トークンアカウントをよく使用します。

関連トークンアカウントは、所有者のアドレスとミントアカウントのアドレスを使用して決定的に導出されるトークンアカウントです。

補足
ちなみに、中身はこのようになっていました。
オーナーとProgramIDとミントアカウントのアドレスから一意に作っているのですね。

関連トークンアカウントは、特定のミントと所有者のための「デフォルト」のトークンアカウントと考えることができます。
補足
トークンアカウントは複数作れましたもんね。
関連トークンアカウントが異なるタイプのトークンアカウントではないことを理解することが重要です。
これは特定のアドレスを持つただのトークンアカウントです。
補足
言い方がわかりにくいですが、関連トークンアカウントはただのデフォルトの「トークンアカウント」であるということが重要です。
「トークンアカウント」自体はいくつでも作れますが、各々が好きに作ってしまうと、そのウォレットの、特定のミントアカウントのトークンアカウントを特定するのが困難です。
そのため、デフォルトのトークンアカウントとして指定されているのが「関連トークンアカウント」です。
2 PDA(プログラム派生アドレス)について
この部分は、Solana開発における重要な概念であるプログラム派生アドレス(PDA)を紹介しています。
概念的に、PDAは事前に定義された入力を使用してアドレスを決定的に生成する方法を提供します。

これにより、後でアカウントのアドレスを簡単に見つけることができます。
補足
PDA作成時に使用したシードを入れることで、いつでも導けるからです。
3 USDCの例
こちらはSolana Playgroundの例で、USDC関連トークンアカウントのアドレスと所有者から導出しています。
これは、同じミントと所有者に対して常に同じアドレスを生成します。

具体的には、関連トークンアカウントのアドレスは以下の入力を使用して導出されます。

こちらは前の例と同じアドレスを生成するSolana Playgroundの例です。
4 ミントアカウントごとの関連トークンアカウント
同じタイプのトークンの単位を保持するためには、各ウォレットが特定のミントアカウント用の自身のトークンアカウントを必要とします。
下の画像は、このアカウントの関係を示しています。
補足
この辺りは、普通のトークンアカウントの話と同じですね。

6 トークンのサンプルの準備
1 Solana Playgroundの使用
spl-token CLIは、SPLトークンを試すために使用することができます。

以下の例では、ブラウザ内でCLIコマンドを直接実行するためにSolana Playgroundのターミナルを使用します。
これにより、CLIをローカルにインストールする必要がありません。
2 テストトークンの取得
トークンやアカウントを作成するためには、アカウントのレントの預け入れおよび取引手数料のためにSOLが必要です。
Solana Playgroundを初めて使用する場合は、Playgroundウォレットを作成し、Playgroundターミナルでsolana airdropコマンドを実行します。

また、公開ウェブフォーセットを使用してdevnet SOLを入手することもできます。

3 コマンドの確認について
spl-token --helpを実行すると、使用可能なコマンドの完全な説明が表示されます。

spl-token --help
4 ローカル環境での実施
または、次のコマンドを使用してspl-token CLIをローカルにインストールすることができます。
これには最初にRustをインストールする必要があります。
補足
次のコマンドというのがなかったので、参考に私のノートを貼っておきます。
+α 実行時の補足
以下のセクションでは、CLIコマンドを実行したときに表示されるアカウントアドレスが、以下に示される例の出力と異なる場合があります。
進行する際には、Playgroundターミナルに表示されるアドレスを使用してください。
例えば、create-tokenからのアドレス出力は、あなたのPlaygroundウォレットがミント権限として設定されているミントアカウントです。
補足
次の章から、具体的な処理に移ります。
7 ミントアカウントの作成
1 ミントアカウントの作成
新しいトークン(ミントアカウント)を作成するには、Solana Playgroundのターミナルで以下のコマンドを実行します。
spl-token create-token
以下のような出力が表示されるはずです。

アドレスとシグネチャを使用して、Solana Explorerでトークンとトランザクションの詳細を確認できます。

今回の例の出力では、新しいトークンの一意の識別子(アドレス)は9dn…jdNでした。
補足
もちろん、皆さんの結果と異なると思います。
こんな状態になりました。

2 供給量の確認
新しいトークンは初期には供給量がありません。
次のコマンドを使用してトークンの現在の供給量を確認できます。
spl-token supply <TOKEN_ADDRESS>
新しく作成されたトークンの供給コマンドを実行すると、0の値が返されます。

3 ミントアカウント作成の内部処理について
内部的には、新しいミントアカウントを作成するためには、次の2つの命令でトランザクションを送信する必要があります。
① アカウントの作成
システムプログラムを呼び出して、ミントアカウントのデータに十分なスペースがある新しいアカウントを作成する

その際、所有権をトークンプログラムに割り当てる

補足
所有権の割り当てについてはこちらをご参照ください。

② ミントアカウントの初期化
トークンプログラムを呼び出して、新しいアカウントのデータをミントアカウントとして初期化します。

8 トークンアカウントの作成
1 デフォルトの場合
特定のトークンの単位を保持するためには、最初にトークンアカウントを作成する必要があります。
新しいトークンアカウントを作成するには、次のコマンドを使用します。
spl-token create-account [OPTIONS] <TOKEN_ADDRESS>
以下の出力が返されます。

こんな状態になりました。

GuE…U9aは、create-accountコマンドで指定されたトークンの単位を保持するために作成されたトークンアカウントのアドレスです。
デフォルトでは、create-accountコマンドは、あなたのウォレットアドレスをトークンアカウントの所有者として関連トークンアカウントを作成します。
補足
上の部分は大事だと思います。
デフォルトで作られるのは、関連トークンアカウントなのですね。
2 異なる所有者のトークンアカウントの作成
spl-token create-account --owner <OWNER_ADDRESS> <TOKEN_ADDRESS>
例えば、以下のコマンドを実行します:
spl-token create-account --owner 2i3KvjDCZWxBsqcxBHpdEaZYQwQSYE6LXUMx5VjY5XrR <TOKEN_ADDRESS>
以下の出力が返されます。

8it…n3Aは、create-accountコマンドで指定されたトークンの単位を保持するために作成されたトークンアカウントのアドレスです。
--ownerフラグの後に指定されたアドレス(2i3…XrR)が所有者です。
これは、別のユーザーのためにトークンアカウントを作成する必要がある場合に便利です。
補足
あくまでも自分でない、他者の『関連トークンアカウント』を作っています。
3 関連トークンアカウント作成の内部処理について
内部的には、関連トークンアカウントを作成するには、関連トークンプログラムを呼び出す単一の指示が必要です。

関連トークンプログラムは、CPI(Cross Program Invocation)を使用して次の操作を処理します。
提供されたPDAを使用して新しいアカウントを作成するためにシステムプログラムを呼び出します。

そして、新しいアカウントのトークンアカウントデータを初期化するためにトークンプログラムを呼び出します。

4 関連トークンアカウントでないトークンアカウントの作成
または、ランダムに生成されたキーペアを使用して、関連トークンアカウントではない新しいトークンアカウントを作成するには、2つの指示でトランザクションを送信する必要があります。
① アカウントの作成
トークンアカウントデータのために十分なスペースで新しいアカウントを作成します。

その所有権をトークンプログラムに移譲するためにシステムプログラムを呼び出します。

新しいアカウントのデータをトークンアカウントとして初期化するためにトークンプログラムを呼び出します。

9 トークンのミント
新しいトークンの単位を作成するには、次のコマンドを使用します。
補足
<TOKEN_ADDRESS>の部分はミントアカウントのアドレスが入ります。
spl-token mint [OPTIONS] <TOKEN_ADDRESS> <TOKEN_AMOUNT> [--] [RECIPIENT_TOKEN_ACCOUNT_ADDRESS]
1 オーナーへのミント
例えば、以下のコマンドを実行します。
spl-token mint 9dnrPasJZf4NDdLMb2nYmGHyAaA1Kfy4VtcUPJwERjdN 100
以下の出力が返されます。

9dn…jdNは、トークンがミントされているミントアカウントのアドレスです。(総供給量を増加)
GuE…U9aは、トークンの単位がミントされているあなたのウォレットのトークンアカウントのアドレスです。(数量を増加)


2 異なるトークンアカウントへのミント
異なるトークンアカウントにトークンをミントするには、対象の受取人トークンアカウントのアドレスを指定します。
spl-token mint 99zqUzQGohamfYxyo8ykTEbi91iom3CLmwCA75FK5zTg 100 -- 8itwot3oqVss9BPW9Y22zqhB8rBK7iUr2bJUjYYzhn3A
次のような出力が返されます。

9dn…jdNは、トークンがミントされているミントアカウントのアドレスです。(総供給量が増加)
8it…n3Aは、トークンがミントされているトークンアカウントのアドレスです(数量が増加)。
補足
下のように、「0x3」というエラーが出ているという方もいるのではないでしょうか。

それは、下の「MintMismatch」というエラーです。
書かれているように、アカウントがミントアカウントに紐づいていないためです。

この辺りの話です。

そのため、第8章第2項で作成したアカウントを設定する必要があります。
そのアカウントにすれば、エラーはなくなると思います。
3 ミントの内部的な処理
内部的には、新しいトークンの単位を作成するには、トークンプログラム上でMintTo命令を呼び出す必要があります。

この命令はミント権限を持つ者によって署名されなければなりません。

この命令は新しいトークンの単位をトークンアカウントにミントし、ミントアカウントの総供給量を増加させます。
10 トークンの転送
1 トークンの転送
二つのトークンアカウント間でトークンの単位を転送するには、以下のコマンドを使用します。
spl-token transfer [OPTIONS] <TOKEN_ADDRESS> <TOKEN_AMOUNT> <RECIPIENT_ADDRESS or RECIPIENT_TOKEN_ACCOUNT_ADDRESS>
例えば、以下のコマンドを実行します。
spl-token transfer 9dnrPasJZf4NDdLMb2nYmGHyAaA1Kfy4VtcUPJwERjdN 15 8itwot3oqVss9BPW9Y22zqhB8rBK7iUr2bJUjYYzhn3A
次のような出力が返されます。

GuE…U9aは、トークンの転送元のトークンアカウントのアドレスです。
これは指定されたトークンが転送されるあなたのトークンアカウントのアドレスです。
8it…n3Aは、トークンの転送先のトークンアカウントのアドレスです。

2 転送の内部的な処理
内部的には、トークンを転送するには、トークンプログラム上でTransfer命令を呼び出す必要があります。

この命令は送信者のトークンアカウントの所有者によって署名される必要があります。
補足
第4章第2項で出てきましたね。

この命令は一つのトークンアカウントから別のトークンアカウントへトークンの単位を転送します。
3 重要なポイント
重要なのは、送信者と受信者の両方が転送される特定のタイプのトークンの既存のトークンアカウントを持っている必要があることです。
送信者は取引に追加の指示を含めることができ、通常は受信者のトークンアカウントを作成します。

補足
この辺りは何度も出てきている重要なところですね。
ミントアカウントに対応する、トークンアカウントを作る必要があります。
そのため、上のように関連トークンアカウントを取得し、なければ作るようにしています。
これは一般的に関連トークンアカウントです。
11 トークンメタデータの作成
0 始める前の補足
この章はSolana Playgroundでは実行できないと思われます。
下のように、バージョンが3.4.0以下だからです。

実行する場合は、こちらの記事を参考にして、ローカルで環境を作ってください。
その上で、トークン拡張CLIフラグを使用するには、バージョン3.4.0以降のCLIをローカルにインストールしてください。
cargo install --version 3.4.0 spl-token-cli
ただ、実際に行うと、このようになりました。
このままでもできました。

1 メタデータの保存
トークン拡張プログラム(Token2022)を使用すると、追加のカスタマイズ可能なメタデータ(名前、シンボル、画像へのリンクなど)をミントアカウントに直接保存することができます。
2 メタデータが有効なミントアカウントの作成
メタデータ拡張を有効にして新しいトークンを作成するには、以下のコマンドを使用します。
spl-token create-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb --enable-metadata
このコマンドは以下の出力を返します。

FMP…t6Eは、メタデータ拡張が有効にされた新しいトークンのアドレスです。(ミントアカウント)
3 メタデータの初期化
新しいトークンがメタデータ拡張を有効にして作成されたら、次のコマンドを使用してメタデータを初期化します。
spl-token initialize-metadata <TOKEN_MINT_ADDRESS> <YOUR_TOKEN_NAME> <YOUR_TOKEN_SYMBOL> <YOUR_TOKEN_URI>
トークンURIは通常、トークンに関連付けたいオフチェーンのメタデータへのリンクです。
ここでJSONフォーマットの例を見ることができます。
補足
JSONファイルの中身はこのようになっていました。

例えば、以下のコマンドを実行すると、指定されたミントアカウントに直接追加のメタデータが保存されます。
spl-token initialize-metadata FMPhCWtSxiShsewypA39NkScj6SS8jdwTw16axNQ3t6E "TokenName" "TokenSymbol" "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/DeveloperPortal/metadata.json"

4 メタデータの確認
その後、ミントアカウントのアドレスをエクスプローラーで検索して、メタデータを確認することができます。
例えば、メタデータ拡張を有効にして作成されたトークンは、SolanaFmエクスプローラーで以下のように表示されます。


メタデータ拡張ガイドでさらに詳しい情報を学ぶことができます。
トークン拡張に関する詳細はこちらをご確認ください。
いいなと思ったら応援しよう!
