ウォレットを作成しよう
👛 HD ウォレットについて
このセクションでは、HDウォレットと呼ばれる種類のウォレットを構築します。HDとは階層的決定性(Hierarchy Deterministic)の略です。
これは簡単に言うと、1つのシードからマスタキーとなる秘密鍵を生成し、そこから木構造のような階層的に複数の派生秘密鍵と派生公開鍵及びアドレスを生成します。
最初の秘密鍵は、シードと呼ばれるランダムな文字列から作ります。その後は作った秘密鍵をシードとして新たな秘密鍵を階層的に作ることができます。
シードは覚えにくい文字列ですので、シードから「シードフレーズ」や「リカバリーフレーズ」などと呼ばれる、12個から24個の単語に変換して記録しておく方法が使われています。
参考: 暗号資産におけるウォレットとは ② 〜HD ウォレット編〜
⏬ BIP39 ライブラリを追加する
ここからは、components/GenerateWallet/index.jsファイルを更新してGenerateWalletコンポーネントを作成していきます。
フレーズを生成するには、決定論的なキーのフレーズ生成の標準を設定したBIP39仕様を満たす外部ライブラリを活用する必要があります。
JavaScriptには BIP39 と呼ばれるライブラリがあるのでこれを利用していきましょう。
BIP39ライブラリは、フレーズを生成し、それをSolanaウォレットキーの生成に必要なシードに変換するために必要な機能を提供してくれます。
BIP39ライブラリをnpm installする
npm install bip39@^3.1.0
- importする
ライブラリのインストールが完了したら、 ファイルの先頭でライブラリを読み込みましょう。
import * as bip39 from "bip39";
🏭 ニーモニックフレーズを生成する
BIP39にはニーモニックフレーズを生成するためのメソッドgenerateMnemonicがあります。これを呼び出し、変数に格納してみましょう。
const generatedMnemonic = bip39.generateMnemonic();
これにより、ユーザーがメモして安全に保管できるように、ニーモニックフレーズを設定し、表示することができます。フレーズそれ自体で、その所持者はそのフレーズに一致するアカウントにアクセスすることが可能になります。
🔐 キーペアとシード
ブロックチェーン上のアカウントに接続する前に、このニーモニックフレーズをブロックチェーンが理解できる形に変換する必要があります。ニーモニックフレーズは、長くて古風な数字を、より人間に近い形に変換する抽象的なものなのです。
@solana/web3.jsライブラリがキーペアオブジェクトを生成するためにこのフレーズを使用できるように、フレーズをバイトに変換する必要があります。キーペアは、データを暗号化できる公開キーとデータを復号化できる秘密キーからなるウォレットアカウントとなります。
@solana/web3.js ドキュメントを確認すると、Keypairクラスが "An account keypair used for signing transactions." として定義されていることがわかります。これはまさに、ニーモニックフレーズを使用して生成する必要があるものです。
またドキュメントを読むと、 Keypairクラスには、32バイトの シードからKeypairを生成するfromSeedメソッドがあることがわかります。そして、シードはUint8Arrayである必要があります。つまり、ニーモニックフレーズをUint8Arrayに変換する方法が必要だということです。
BIP39ライブラリに戻ると、mnemonicToSeedSync(mnemonic)というメソッドがあり、16進数のリストのようなBufferオブジェクトが返されます。このメソッドを実行し、生成したニーモニックを渡すことで、テストすることができます。
const seed = bip39.mnemonicToSeedSync(generatedMnemonic);
console.log(seed);
// > Uint8Array(64)
ゴールまでもう少しです!🥭
Keypairクラスは32バイトのUint8Arrayを必要としますが、現在は64バイトのUint8Arrayを取得しています。シードをsliceして、最初の32バイトだけを保持するようにしましょう。
const seed = bip39.mnemonicToSeedSync(generatedMnemonic).slice(0, 32);
console.log(seed);
// > Uint8Array(32)
正しい形式のシードがあれば、KeypairのfromSeedメソッドを使って、アカウントのキーペアを生成することができます。
const newAccount = Keypair.fromSeed(new Uint8Array(seed));
console.log("newAccount", newAccount.publicKey.toString());
// > ランダムな文字列
👛 ウォレット生成関数を定義する
これまでの説明を踏まえて、ウォレットを生成するための関数generateWalletを定義します。それでは、components/GenerateWallet/index.jsを更新していきましょう。
まずは、下記のインポート文を追加します。
import { Keypair } from "@solana/web3.js";
import { useState } from "react";
次に、export default function GenerateWallet() {の下に下記のコードを追加します。
const generateWallet = () => {
const generatedMnemonic = bip39.generateMnemonic();
// ニーモニックフレーズを使用して、シードを生成します。
const seed = bip39.mnemonicToSeedSync(generatedMnemonic).slice(0, 32);
// シードを使用して、アカウントを生成します。
const newAccount = Keypair.fromSeed(new Uint8Array(seed));
setMnemonic(generatedMnemonic);
setAccount(newAccount);
};
generateWallet関数では、ニーモニックフレーズとアカウントの生成を行ってます。
また、生成したニーモニックフレーズとアカウントは、useStateを用いて値を保持します。ニーモニックフレーズは、GenerateWalletコンポーネント内でのみ表示するため、mnemonicという状態変数に格納します。アカウントは、複数のコンポーネント間で共有したいので、Homeコンポーネント内で状態変数を定義し、各コンポーネントに必要なものを引数で渡す形にしましょう。
GenerateWalletコンポーネントの引数にsetAccountを記述し、export default function GenerateWallet() {の直下に、mnemonicを保持する状態変数を定義しましょう。
// `{ setAccount }`を引数に追加
export default function GenerateWallet({ setAccount }) {
// 下記を追加
const [mnemonic, setMnemonic] = useState(null);