メインコンテンツまでスキップ

lesson-2_Phantom Walletを連携しよう

🤖 ローカル開発環境を設定する

まずはアプリの雛形であるスタータープロジェクトをもとに、ローカル環境を作成します。

まだGitHubのアカウントをお持ちでない方は、こちら の手順に沿ってアカウントを作成してください。

GitHubのアカウントをお持ちの方は、下記の手順に沿ってフロントエンドの基盤となるリポジトリをあなたのGitHubにフォークしましょう。

1. こちらからunchain-tech/Solana-NFT-Dropリポジトリにアクセスをして、ページ右上のForkボタンをクリックします。

2. Create a new forkページが開くので、「Copy the main branch only」という項目にチェックが入っていることを確認します

3. 設定が完了したらCreate forkボタンをクリックします。あなたのGitHubアカウントにSolana-NFT-Dropリポジトリのフォークが作成されたことを確認してください。

それでは、フォークしたリポジトリをローカル環境にクローンしましょう。

CodeボタンをクリックしてSSHを選択し、Gitリンクをコピーしましょう。

ターミナルで任意の作業ディレクトリに移動し、先ほどコピーしたリンクを貼り付け、下記を実行してください。

git clone コピーした_github_リンク

クローンしたプロジェクトに移動し、パッケージのインストールを行いましょう。

cd Solana-NFT-Drop/
yarn install

次に、下記を実行してみましょう。

yarn dev

ターミナルに表示されたURLにアクセスをしましょう。あなたのローカル環境で、Webサイトのフロントエンドが立ち上がりましたか?

ローカル環境で表示されている Web サイト

上記のような形でフロントエンドが確認できれば成功です。

これからフロントエンドの表示を確認したい時は、ターミナルに向かい、Solana-NFT-Dropディレクトリ上で、yarn devを実行します。これからも必要となる作業ですので、よく覚えておいてください。

ターミナルを閉じるときは、以下のコマンドが使えます ✍️

  • Mac: ctrl + c
  • Windows: ctrl + shift + w

✅ テストスクリプトについて

このプロジェクトには、コンポーネントのテストスクリプトが__tests__/コンポーネント名.test.jsとして格納されています。これらは、期待するMVPの機能が実装されているかをテストする内容となっており、テストフレームワークとしてJestを、UIコンポーネントのテストを行うためにTesting Libraryを導入しています。Solanaネットワークとやり取りを行う機能をモック(模擬)しているため、ブラウザ上で実際に動作確認を行うよりもより迅速に機能テストを行うことが可能です。対象コンポーネントの実装が完成したら、テストを実行してみましょう!

ただし、あくまでも模擬的なので、各コンポーネントの実装ができたら実際にSolanaネットワークを使用した動作確認をブラウザ上で行いましょう 🚀

🔌 Phantom Wallet を使用してウォレット接続ボタンを作成する

このプロジェクトでは、 Phantom Wallet という、SolanaのNFT取扱に優れたウォレットを使用します。

まずは拡張機能をダウンロードしてPhantom Walletをセットアップしてください。

Phantom Walletは ChromeBraveFirefox、および Edge をサポートしています。

Chromeの方は こちら からPhantom Walletをインストールすることがきます。

Phantom WalletのネットワークをDevnetに変更してください。今回作成するCandy MachineはDevnet上にあるので、ウォレットもDevnetに変更する必要があります。

Phantom Wallet左上のメニューを開き、「Settings」に進みます。

「Developer Settings」に進み、Testnet Modeをオンにします。Solanaのテストネットが表示されるので、Devnetを選択します。

※ 本プロジェクトではBraveとChromeでのみ動作が確認できます。

👻 Solana オブジェクトを設定する

ユーザーのPhantom Walletを、作成するWebアプリケーションと接続する必要があります。

エディタより、pages/index.tsxファイルを開いてください。これはアプリケーションのメインのエントリポイントになるファイルです。

Phantom Wallet拡張機能がインストールされている場合は、windowオブジェクトにsolanaという名前の特別なオブジェクトが自動的に代入されます。

ミントする前に、solanaが代入されているか確認する必要があります。存在しない場合はダウンロードするようにユーザーに指示しましょう。

index.tsxを下記の通り変更します。

// index.tsx
import Head from "next/head";
import Image from "next/image";
import { useEffect } from "react";

import twitterLogo from "@/twitter-logo.svg";
import styles from "@/styles/Home.module.css";

// 定数の宣言
const TWITTER_HANDLE = "あなたのTwitterハンドル";
const TWITTER_LINK = `https://twitter.com/${TWITTER_HANDLE}`;

const Home = () => {
// Actions

/*
* 関数を宣言します
*/
const checkIfWalletIsConnected = async () => {
try {
const { solana } = window;

if (solana && solana.isPhantom) {
console.log("Phantom wallet found!");
} else {
alert("Solana object not found! Get a Phantom Wallet 👻");
}
} catch (error) {
console.error(error);
}
};

/*
* コンポーネントが最初にマウントされたら、Phantom Walletが
* 接続されているかどうかを確認しましょう。
*/
useEffect(() => {
const onLoad = async () => {
await checkIfWalletIsConnected();
};
onLoad();
}, []);

return (
<>
<Head>
<title>Candy Drop</title>
<meta name="description" content="Generated by create next app" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<div className={styles.container}>
<div>
<p className={styles.header}>🍭 Candy Drop</p>
<p className={styles.subText}>NFT drop machine with fair mint</p>
</div>
<div className={styles.footerContainer}>
<Image
alt="Twitter Logo"
className={styles.twitterLogo}
src={twitterLogo}
/>
<a
className={styles.footerText}
href={TWITTER_LINK}
target="_blank"
rel="noreferrer"
>{`built on @${TWITTER_HANDLE}`}</a>
</div>
</div>
</main>
</>
);
};

export default Home;

index.tsxを分解して説明します。

// index.tsx
const checkIfWalletIsConnected = async () => {
try {
const { solana } = window;

if (solana && solana.isPhantom) {
console.log("Phantom wallet found!");
} else {
alert("Solana object not found! Get a Phantom Wallet 👻");
}
} catch (error) {
console.error(error);
}
};

関数checkIfWalletIsConnectedは、DOMのwindowオブジェクトをチェックして 、Phantom Walletがsolanaオブジェクトを挿入したかどうかを確認します。

solanaオブジェクトが存在しているか、またそれがPhantom Walletであるかどうかを確認しています。

// index.tsx
useEffect(() => {
const onLoad = async () => {
await checkIfWalletIsConnected();
};
onLoad();
}, []);

最後に、これを呼び出す必要があります。

Reactでは、2番目のパラメータ( [] )が空の場合、コンポーネントをマウント時にuseEffect hookが1回呼び出されます。

これで、私たちのWebアプリケーションにアクセスするとすぐに、Phantom Walletがインストールされているかどうかを確認できます。これは 非常に重要な機能です。

最後に、あなたのTwitterハンドルを以下に貼り付けるのをお忘れなく!

// index.tsx
const TWITTER_HANDLE = "あなたのTwitterハンドル";

🔒 ユーザーのアカウントにアクセスする

一度、ブラウザでインタフェースを確認してみましょう。

WebアプリケーションのコンソールにPhantom Wallet found!という行が表示されるはずです。

無題

次に、ユーザーのウォレットにアクセスすることが許可されているか確認する必要があります。アクセスが許可されていると、Solanaプログラムの関数にアクセスできます。

Phantom Walletは、すべてのWebアプリケーションにウォレット情報を提供する訳ではなく、許可したWebアプリケーションだけに許可します。

Webアプリケーションで最初に行う必要があるのは、ユーザーがWebアプリケーションでウォレットを使用する許可を与えているか確認することです。

これはユーザーが「ログイン」しているかどうかを確認するようなものです。

ここでcheckIfWalletIsConnected関数にもう1行追加する必要があります。以下のコードを修正してください。

// index.tsx
const checkIfWalletIsConnected = async () => {
try {
const { solana } = window;

if (solana && solana.isPhantom) {
console.log("Phantom wallet found!");

/*
* "solana"オブジェクトは、ユーザーのウォレットに直接
* 接続できる機能を提供しています。
* 下記からコードを修正してください。
*/
const response = await solana.connect({ onlyIfTrusted: true });
console.log("Connected with Public Key:", response.publicKey.toString());
} else {
alert("Solana object not found! Get a Phantom Wallet 👻");
}
} catch (error) {
console.error(error);
}
};

connectを呼び出すだけで、Webアプリケーションがそのウォレットに関する情報へのアクセスを許可されていることをPhantom Walletに通知できます。

onlyIfTrustedプロパティは、ユーザーがすでにウォレットをアプリケーションに接続している場合、trueになります。

別の接続ポップアップを表示せずに、すぐにデータをpullします。詳細は Phantom の公式ドキュメントをご覧ください。

以上です!

現時点では、コンソールのログにPhantom wallet found!と表示されるだけです。

もしコンソールにUserRejectedRequestエラーが表示されても心配しないでください。

現段階のみの問題で、connectメソッド内にonlyIfTrusted:trueパラメータを追加したためです。

onlyIfTrustedパラメータがtrueに設定されたconnectメソッドは 、ユーザーがウォレットとWebアプリケーション間の接続をすでに承認している場合にのみ実行されます。次のレッスンで修正します。

🙋‍♂️ 質問する

ここまでの作業で何かわからないことがある場合は、Discordの#solanaで質問をしてください。

ヘルプをするときのフローが円滑になるので、エラーレポートには下記の3点を記載してください ✨

1. 質問が関連しているセクション番号とレッスン番号
2. 何をしようとしていたか
3. エラー文をコピー&ペースト
4. エラー画面のスクリーンショット

次のレッスンに進んで、開発を進めましょう 🎉