Hardhat Chai Matchers / Reference

# はじめに（Introduction）

以下を [Google翻訳](https://translate.google.co.jp/) などで翻訳します。

https://hardhat.org/hardhat-chai-matchers/docs/reference

# リファレンス（Reference）

これは Hardhat Chai Matchers プラグインのリファレンスです。
これは、スマート コントラクトをテストするための新しいアサーション機能を追加する [Chai](https://www.chaijs.com/) プラグインです。

## 数値（Numbers）

`@nomicfoundation/hardhat-chai-matchers` を使用すると、数値が異なる型で表されている場合でも、数値の等価比較が機能します。
これは、次のようなアサーションを意味します。

```javascript
expect(await token.totalSupply()).to.equal(1_000_000);
```

これで動きます。
`totalSupply()` によって返される値は [bigint](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) であり、bigint 値は常に単純な数値とは異なるため、これらのアサーションは通常は機能しません。

サポートされているタイプは次のとおりです。

- プレーンな JavaScript 数値
- [BigInts](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)
- [`bn.js`](https://github.com/indutny/bn.js/) インスタンス
- [`bignumber.js`](https://github.com/MikeMcl/bignumber.js/) インスタンス

これは、配列またはオブジェクトの完全等価比較が実行される場合にも機能します。

```javascript
expect(await contract.getRatio()).to.deep.equal([100, 55]);
```

## 取り消されたトランザクション（Reverted transactions）

トランザクションが取り消されたことと、取り消された理由をアサートするために、いくつかのマッチャーが用意されています。

### `.reverted`

取り消しの原因を確認せずに、何らかの理由でトランザクションが取り消されたことをアサートします。

```javascript
await expect(token.transfer(address, 0)).to.be.reverted;
```

### `.revertedWith`

トランザクションが特定の理由文字列で元に戻されたことをアサートします。

```javascript
await expect(token.transfer(address, 0)).to.be.revertedWith(
  "transfer value must be positive"
);
```

正規表現も使用できます。

```javascript
await expect(token.transfer(address, 0)).to.be.revertedWith(
  /AccessControl: account .* is missing role .*/
);
```

### `.revertedWithCustomError`

トランザクションが特定のカスタム エラーで元に戻ったことをアサートします。

```javascript
await expect(token.transfer(address, 0)).to.be.revertedWithCustomError(
  token,
  "InvalidTransferValue"
);
```

最初の引数は、エラーを定義するコントラクトでなければなりません。

エラーに引数がある場合は、`.withArgs` マッチャーを追加できます。

```javascript
await expect(token.transfer(address, 0))
  .to.be.revertedWithCustomError(token, "InvalidTransferValue")
  .withArgs(0);
```

詳細については、[`.withArgs`](https://hardhat.org/hardhat-chai-matchers/docs/reference#.withargs) マッチャー エントリを参照してください。

### `.revertedWithPanic`

トランザクションが[パニック コード](https://docs.soliditylang.org/en/v0.8.14/control-structures.html#panic-via-assert-and-error-via-require)で元に戻ったことをアサートします。

```javascript
await expect(token.transfer(address, 0)).to.be.revertedWithPanic();
```

オプションの引数を渡すと、特定のパニック コードがスローされたことをアサートできます。

```javascript
await expect(token.transfer(address, 0)).to.be.revertedWithPanic(0x12);
```

`PANIC_CODES` 辞書をインポートして使用することもできます。

```javascript
import { PANIC_CODES } from "@nomicfoundation/hardhat-chai-matchers/panic";

await expect(token.transfer(address, 0)).to.be.revertedWithPanic(
  PANIC_CODES.DIVISION_BY_ZERO
);
```

### `.revertedWithoutReason`

理由を返さずにトランザクションが元に戻ったことをアサートします。

```javascript
await expect(token.transfer(address, 0)).to.be.revertedWithoutReason();
```

このマッチャーは、トランザクションが理由文字列、カスタム エラー、またはパニック コードで元に戻された場合に失敗するという点で `.reverted` とは異なります。
理由なしに元に戻る Solidity 式の例は、Solidity v0.8.0 より前の `require(false)` (理由文字列なし) および `assert(false)` です。
これはガス欠エラー(out-of-gas errors)でも発生します。


## イベント（Events）

### `.emit`

トランザクションが特定のイベントを発行することをアサートします。

```javascript
await expect(token.transfer(address, 100)).to.emit(token, "Transfer");
```

最初の引数は、イベントを発行するコントラクトである必要があります。

イベントに引数がある場合は、`.withArgs` マッチャーを追加できます。

```javascript
await expect(token.transfer(address, 0))
  .to.be.emit(token, "Transfer")
  .withArgs(100);
```

詳細については、[`.withArgs`](https://hardhat.org/hardhat-chai-matchers/docs/reference#.withargs) マッチャー エントリを参照してください。

## 残高変更（Balance change）

これらのマッチャーを使用すると、特定のトランザクションが特定のアドレスのイーサ残高または ERC20 トークン残高にどのような影響を与えるかをアサートできます。

これらのマッチャーはすべて、指定されたトランザクションがそのブロック内でマイニングされた唯一のトランザクションであると想定しています。

### `.changeEtherBalance`

アドレスのイーサ残高が特定の量だけ変化したことをアサートします。

```javascript
await expect(
  sender.sendTransaction({ to: receiver, value: 1000 })
).to.changeEtherBalance(sender, -1000);
```

このマッチャーはトランザクションの手数料を無視しますが、 `includeFee` オプションを使用してトランザクションの手数料を含めることができます。

```javascript
await expect(
  sender.sendTransaction({ to: receiver, value: 1000 })
).to.changeEtherBalance(sender, -22000, { includeFee: true });
```

### `.changeTokenBalance`

アドレスの ERC20 トークン残高が特定の量だけ変更されたことをアサートします。

```javascript
await expect(token.transfer(receiver, 1000)).to.changeTokenBalance(
  token,
  sender,
  -1000
);
```

最初の引数はトークンのコントラクトでなければなりません。

### `.changeEtherBalances`

`.changeEtherBalance` と似ていますが、複数のアドレスを同時にチェックできます。

```javascript
await expect(
  sender.sendTransaction({ to: receiver, value: 1000 })
).to.changeEtherBalances([sender, receiver], [-1000, 1000]);
```

### `.changeTokenBalances`

`.changeTokenBalance` と似ていますが、複数のアドレスを同時にチェックできます。

```javascript
await expect(token.transfer(receiver, 1000)).to.changeTokenBalances(
  token,
  [sender, receiver],
  [-1000, 1000]
);
```

## その他マッチャー（Other matchers）

### `.withArgs`

`.emit` または `.revertedWithCustomError` マッチャーの後に使用して、イベント/エラーの引数の値をアサートできます。

```javascript
// events
await expect(token.transfer(address, 0))
  .to.be.emit(token, "Transfer")
  .withArgs(100);

// errors
await expect(token.transfer(address, 0))
  .to.be.revertedWithCustomError(token, "InvalidTransferValue")
  .withArgs(0);
```

If you don't care about the value of one of the arguments, you can use the anyValue predicate:
いずれかの引数の値を気にしない場合は、`anyValue` 述語を使用できます。


```javascript
import { anyValue } from "@nomicfoundation/hardhat-chai-matchers/withArgs";

await expect(factory.create(9999))
  .to.emit(factory, "Created")
  .withArgs(anyValue, 9999);
```

述語は、値が正しい場合は true を返し、そうでない場合は false を返す単なる関数であるため、独自の述語を作成できます。

```javascript
function isEven(x: bigint): boolean {
  return x % 2n === 0n;
}

await expect(token.transfer(100)).to.emit(token, "Transfer").withArgs(isEven);
```

### `.properAddress`

指定された文字列が適切なアドレスであることをアサートします。

```javascript
expect("0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266").to.be.properAddress;
```

### `.properPrivateKey`

指定された文字列が適切な秘密キーであることをアサートします。

```javascript
expect("0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80").to
  .be.properPrivateKey;
```

### `.properHex`

指定された文字列が特定の長さの適切な 16 進文字列であることをアサートします。

```javascript
expect("0x1234").to.be.properHex(4);
```

### `.hexEqual`

指定された文字列の 16 進文字列が同じ数値に対応することをアサートします。

```javascript
expect("0x00012AB").to.hexEqual("0x12ab");
```

# まとめ（Conclusion）

スマートコントラクトに特化した機能があるので有効活用していきたいところです。

※：サンプルコードで明らかな間違いを修正しています。