フロントエンドのベースを作成しよう
🚅 フロントエンドのベースを実装しましょう
コード量が多く貼り付ける作業が大変になってしまうのを防ぐため、本プロジェクトのpackages/client
ディレクトリからファイルやディレクトリごとコピーしてフロントエンドを構築していきます。
本レポジトリ自体をローカルにクローンしてからコピーしたほうが作業が楽かもしれません。
📁 styles
ディレクトリ
styles
ディレクトリにはcssのコードが入っています。
全てのページに適用されるよう用意されたglobal.css
と、ホームページ用のHome.module.css
があります。
今回はtailwindを使用するので、Home.module.css
を削除してください。
client
└── styles
└── globals.css
📁 public
ディレクトリ
Next.js
はルートディレクトリ直下のpublic
ディレクトリを静的なリソース(画像やテキストデータなど)の配置場所と認識します。
そのためソースコード内で画像のURLを/image.png
と指定した場合、
Next.js
は自動的にpublic
ディレクトリをルートとしたプロジェクトルート/image.png
を参照してくれます。
このディレクトリを本プロジェクトのpackages/client/public
ディレクトリと置き換えてください。
favicon.ico
: ファビコンになります。お好きな画像で設定したい場合は同じファイル名で保存するとフ ァビコンに設定できます。
あなたのアプリのファビコンをお好きな画像で設定したい場合は、favicon.ico
という名前でpublic/favicon.ico
と置き換えてください。
background.png
: ホームページの背景画像になります。
tailwindで画像を扱えるようにtailwind.config.js
を以下の内容に書き換えてください。
vscodeがエラーを出す可能性もありますが、気にせず進めて問題ありません。
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./pages/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {
backgroundImage: {
home: "url('../background.png')",
},
},
},
plugins: [],
};
client
└── public
├── background.png
└── favicon.ico
📁 types
ディレクトリ
types
ディレクトリを本プロジェクトのpackages/client
ディレクトリからコピーして貼り付けてください。
中身に関しては次のセクションで触れます。
client
└── types
└── ...
📁 artifacts
ディレクトリ
artifacts
ディレクトリを本プロジェクトのpackages/client
ディレクトリからコピーして貼り付けてください。
中身に関しては次のセクションで触れます。
client
└── artifacts
├── Bank.json
└── IAllowList.json
📁 utils
ディレクトリ
utils
ディレクトリを本プロジェクトのpackages/client
ディレクトリからコピーして貼り付けてください。
🐬 ethereum.ts
型定義に厳格なtypescriptでwindow.ethereum
を使用するためには、window
にethereum
オブジェクトがあるということを明示する必要があります。
MetaMaskInpageProvider
は環境設定時にインストールした@metamask/providers
から取得したethereum
の型定義です。
📓
window.ethereum
とは Web アプリケーション上でユーザーがブロックチェーンネットワークと通信するためには、Web アプリケーションはユーザーのウォレット情報を取得する必要があります。
window.ethereum
は MetaMask がwindow
(JavaScript にデフォルトで存在するグローバル変数)の直下に用意するオブジェクトであり API です。 この API を使用して、ウェブサイトはユーザーのイーサリアムアカウントを要求し、ユーザーが接続しているブロックチェーンからデータを読み取り、ユーザーがメッセージや取引に署名するよう求めることができます。
また、getEthereum
関数を呼び出すとwindow
から取り出したethereum
オブジェクトを取得できるようにしています。
🐬 formatter.ts
weiToAvax
(or avaxToWei
)はwei
とAVAX
の単位変換を行なっています。
※ APIでは「1 AVAX = 10^18 wei」で単位変換がされているため、formatEther
(or parseEther
)を使用できます。
また、blockTimeStampToDate
はsolidity内のblock.timestamp
から、フロントエンドで使用するDate
への変換を行なっています。
block.timestamp
は単位がミリ秒で、Date
は秒単位の時間を元に作成するので* 1000
を行なっています。
🐬 compare.ts
ここでは2つのアドレスを比較する関数を用意しています。
client
└── utils
├── compare.ts
├── ethereum.ts
└── formatter.ts
📁 hooks
ディレクトリ
hooks
ディレクトリを本プロジェクトのpackages/client
ディレクトリからコピーして貼り付けてください。
ウォレットやコントラクトの状態を扱うようなカスタムフック(独自で作ったフック)を実装したファイルを保存します。
🐬 useWallet.ts
ここでは、ユーザがMetamaskを持っていることの確認とウォレットへの接続機能を実装します。
connectWallet
はWebアプリがユーザのウォレットにアクセスすることを求める関数です。
この後の実装でUIにユーザのウォレット接続ボタンを用意し、そのボタンとこの関数を連携します。
そのため外部で使用できるように返り値の中に含めています。
checkIfWalletIsConnected
は既にユーザのウォレットとWebアプリが接続しているかを確認する関数です。
また、それぞれの関数内で使用しているeth_requestAccounts
とeth_accounts
は,空の配列または単一のアカウントアドレスを含む配列を返す特別なメソッドです。
ユーザーがウォレットに複数のアカウントを持っている場合を考慮して、プログラムはユーザーの1つ目のアカウントアドレスを取得することにしています。
🐬 useWallet.ts
ユーザがMetamaskを持っていることの確認とウォレットへの接続機能を実装しています。
connectWallet
はWebアプリがユーザのウォレットにアクセス することを求める関数です。
この後の実装でUIにユーザのウォレット接続ボタンを用意し、そのボタンとこの関数を連携します。
そのため外部で使用できるように返り値の中に含めています。
checkIfWalletIsConnected
は既にユーザのウォレットとWebアプリが接続しているかを確認する関数です。
また、それぞれの関数内で使用しているeth_requestAccounts
とeth_accounts
は,空の配列または単一のアカウントアドレスを含む配列を返す特別なメソッドです。
ユーザーがウォレットに複数のアカウントを持っている場合を考慮して、プログラムはユーザーの1つ目のアカウントアドレスを取得することにしています。
🐬 useContract.ts
コントラクトとの接続を行います。
ファイル冒頭には、今回フロントエンドから接続する2つのコントラクトのアドレスBankAddress
とTxAllowListAddress
が記載されています。
BankAddress
はBank
コントラクトのアドレスに後ほど変更します。
TxAllowListAddress
は(section-1のLesson_3で設定したPreCompileの)TransactionAllowList
のアドレスになります。
このアドレス値は固定です。
その下には型の定義があり、その下にuseContract
が定義されています。
主な関数はgetContract
で、接続しているcurrentAccount
やコントラクトのアドレス・ABIを元にコントラクトに接続し、stateに保存します。
getBills
関数は、接続したBank
コントラクトから全てのBillデータを取得しています。
取得したBillはフロントエンド内で扱うBillType
に変換して、bills
というstateに保存しています。
client
└── hooks
├── useContract.ts
└── useWallet.ts
📁 context
ディレクトリ
context
ディレクトリを本プロジェクトのpackages/client
ディレクトリからコピーして貼り付けてください。
🐬 CurrentAccountProvider.tsx
コンテキストを用意しています。 コンテキストは複数のコンポーネント間を跨いで値を渡せるもので、値にはstateも渡せます。
はじめに以下の部分で先ほど作成したuseWallet()
を使用してウォレット接続に関わるオブジェクトを取得しています。
const { currentAccount, connectWallet } = useWallet();
重要なのは以下です。 valueに、取得したオブジェクトを渡します。
<CurrentAccountContext.Provider value={[currentAccount, connectWallet]}>
{children}
</CurrentAccountContext.Provider>
するとこのコンポーネントの子となるコンポーネント(children
)以下ではどのコンポーネントでもコンテキストからvalueを取得することができます。
複数のコンポーネント間でstateを共有できます。
後ほどこのコンテキストを使用する実装を他のファイルで行います。
client
└── context
└── CurrentAccountProvider.tsx