Hardhatとは何か
HardhatはEthereumの開発環境で、スマートコントラクトの開発、テスト、デバッグを簡単にするツールです。まずHardhatっていう名前がユニークですよね。アイコン?も黄色いヘルメットですし。

Hardhatはまるで建築現場のハードハット(ヘルメット)のような役割を果たします。建築現場では、ハードハットは労働者を危険から保護し、効率的に作業を進めるためのツールを提供します。同様に、Hardhatは開発者がスマートコントラクトを安全に、効率的に構築できるように支援します。
今回は、Hardhatを使用してシンプルなトークンを実装するスマートコントラクトを作成するチュートリアルを読み解きます。
具体的には、Hardhatはスマートコントラクトのコンパイル、テスト、デバッグを容易にします。また、Ethereumのローカルネットワークを立ち上げて、開発中のスマートコントラクトをデプロイしたり、トランザクションを送信したりすることも可能です。これはまるで、自分だけのプライベートな建築現場を持つようなもので、自由に試行錯誤を繰り返しながら最適な設計を見つけることができます。
Hardhatのインストール
それでは、Hardhatをインストールしてみましょう。まずはNode.jsとnpm(Node.jsのパッケージマネージャ)がインストールされていることを確認します。ターミナルで以下のコマンドを実行してバージョンを確認します。
node --version
npm --version
これらのコマンドがバージョン情報を表示すれば、Node.jsとnpmは正しくインストールされています。表示されない場合は、Node.jsの公式サイトからダウンロードしてインストールしてください。
次に、Hardhatをインストールします。以下のコマンドをターミナルで実行します。
npm install --save-dev hardhat
これで、Hardhatがプロジェクトの開発依存関係としてインストールされます。
最後に、新しいHardhatプロジェクトを作成します。以下のコマンドを実行し、表示される指示に従ってください。
npx hardhat
以上で、Hardhatのインストールとプロジェクトの作成が完了しました。これであなたのEthereumの建築現場が整いました。
トークンスマートコントラクトの作成
まず、トークンスマートコントラクトの基本的な機能を理解しましょう。「トークンって何?」という方はEther, ERC20トークンを理解する1を参照してください。
今回のHardhatの公式ドキュメントのチュートリアルでは以下のような特性を持つトークンを作成します。
- トークンの総供給量は固定で、変更することはできません。
- すべての供給量は、コントラクトをデプロイするアドレスに割り当てられます。
- 任意のアドレスがトークンを受け取ることができます。
- 少なくとも1つのトークンを持つ任意のアドレスがトークンを転送することができます。
- トークンは分割不可能です。つまり、1, 2, 3, 37などの整数のトークンを転送することはできますが、2.5などの分数のトークンを転送することはできません。
スマートコントラクトの記述
まず、新しいディレクトリcontractsを作成し、その中にToken.solという名前のファイルを作成します。以下のコードをそのファイルに貼り付けます。

//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract Token {
string public name = "My Hardhat Token";
string public symbol = "MHT";
uint256 public totalSupply = 1000000;
address public owner;
mapping(address => uint256) balances;
event Transfer(address indexed _from, address indexed _to, uint256 _value);
constructor() {
balances[msg.sender] = totalSupply;
owner = msg.sender;
}
function transfer(address to, uint256 amount) external {
require(balances[msg.sender] >= amount, "Not enough tokens");
balances[msg.sender] -= amount;
balances[to] += amount;
emit Transfer(msg.sender, to, amount);
}
function balanceOf(address account) external view returns (uint256) {
return balances[account];
}
}
このコードは、トークンの名前、シンボル、総供給量を設定し、トークンの所有者を記録します。また、transfer関数を使用してトークンを転送し、balanceOf関数を使用してアカウントのトークン残高を取得します。
詳細の解説
以下に、コードの各行の詳細な説明を提供します。
//SPDX-License-Identifier: UNLICENSED
これはSPDXライセンス識別子で、ソフトウェアパッケージのライセンスを明示します。ここでは、ライセンスが指定されていないことを示しています。詳しくはこちら2を参照。
pragma solidity ^0.8.0;
この行は、Solidityのバージョンを指定します。^記号は、0.8.0以上のバージョンを意味しますが、1.0.0未満を意味します。(まだバージョン1すら出ていないという!
contract Token {
これは新しいスマートコントラクトを定義する行で、その名前はTokenです。
string public name = "My Hardhat Token";
string public symbol = "MHT";
これらの行は、トークンの名前とシンボルを定義します。これらは公開変数なので、コントラクトの外部からアクセスできます。
uint256 public totalSupply = 1000000;
これは、トークンの総供給量を定義する行です。この場合、総供給量は1000000と設定されています。
address public owner;
これは、トークンの所有者を格納するためのアドレス型の変数を定義します。
mapping(address => uint256) balances;
これは、各アドレスのトークン残高を格納するためのマッピングを定義します。キーはアドレスで、値はそのアドレスのトークン残高です。
event Transfer(address indexed _from, address indexed _to, uint256 _value);
これは、トークンが転送されたときに発生するイベントを定義します。このイベントは、トークンの転送を監視する外部のアプリケーションに役立ちます。
constructor() {
balances[msg.sender] = totalSupply;
owner = msg.sender;
}
これは、コントラクトのコンストラクタ関数です。コントラクトがデプロイされるときに一度だけ実行されます。ここでは、全トークン供給をデプロイヤー(msg.sender)に割り当て、所有者(Owner)をmsg.senderに設定しています。
function transfer(address to, uint256 amount) external {
require(balances[msg.sender] >= amount, "Not enough tokens");
balances[msg.sender] -= amount;
balances[to] += amount;
emit Transfer(msg.sender, to, amount);
}
このtransfer関数は、トークンの送金を実行する役割を果たしています。関数の内部ではトークンの送金を行うための様々な処理が実行されています。
それぞれの行について具体的に見ていきましょう。
-
function transfer(address to, uint256 amount) external {: この行はtransfer関数の宣言部分です。この関数は2つの引数を取ります。address toは送金先のアドレスを指定し、uint256 amountは送金するトークンの量を指定します。また、externalキーワードはこの関数がコントラクトの外部から呼び出されることを示しています。 -
require(balances[msg.sender] >= amount, "Not enough tokens");: この行は送金の前提条件をチェックしています。具体的には、送金元のアドレス(msg.sender)が送金するトークンの量(amount)以上のトークンを保有しているかを確認しています。もし保有量が不足している場合は、エラーメッセージ "Not enough tokens" と共にトランザクションがリバート(失敗)します。 -
balances[msg.sender] -= amount;: この行は送金元のトークン残高から指定された量を減算しています。 -
balances[to] += amount;: この行は送金先のトークン残高に指定された量を加算しています。 -
emit Transfer(msg.sender, to, amount);: 最後に、Transferイベントを発行(emit)しています。このイベントは送金元のアドレス(msg.sender)、送金先のアドレス(to)、そして送金したトークンの量(amount)を含む情報をログに記録します。
このemitの行は、以下のような図でビジュアル化できます:
Event: Transfer
-------------------------------------
| From Address | To Address | Amount |
-------------------------------------
| msg.sender | to | amount |
-------------------------------------
このように、Transferイベントは「誰が(msg.sender)、誰に(to)、どれだけのトークンを(amount)」送金したかという情報をブロックチェーン上に記録します。これにより、後からこのトランザクションを追跡したり分析したりすることが可能になります。
function balanceOf(address account) external view returns (uint256) {
return balances[account];
}
}
このbalanceOf関数は指定されたアカウント(アドレス)のトークン残高を取得するための関数です。この関数は「読み取り専用」で、view修飾子が付いているため、コントラクトの状態を変更することなく呼び出すことができます。そのため、ガス費用を消費せずに呼び出すことが可能です。(おお!!!)
それぞれの部分について見ていきましょう:
-
function balanceOf(address account) external view returns (uint256) {: これはbalanceOf関数の宣言です。この関数は一つの引数、address accountを取り、そのアドレスのトークン残高を返します(returns (uint256))。externalキーワードは、この関数がコントラクトの外部から呼び出されることを示しています。また、viewキーワードは、この関数がコントラクトの状態を変更せずにデータを閲覧するのみであることを示しています。 -
return balances[account];: この行は指定されたアカウントのトークン残高を返します。balancesは、アドレス(アカウント)をキーとし、そのアドレスが保有するトークンの量を値とするマッピングです。ここでは指定されたaccountのキーに対応する値、つまりそのアカウントのトークン残高を返しています。
これにより、特定のアドレスが保有するトークンの量を簡単に取得することができます。この関数は、例えばウェブサイト上でユーザーのトークン残高を表示する際などに利用できます。
以上が、このスマートコントラクトの各行の詳細な説明です。
コントラクトのコンパイル
コントラクトをコンパイルするには、ターミナルでnpx hardhat compileを実行します。これにより、コントラクトが正常にコンパイルされ、使用する準備が整います。
以上がHardhatの公式ドキュメントの読み解きです。このチュートリアルを通じて、シンプルなトークンを実装するスマートコントラクトの作成とコンパイル方法を学びました。