Mobile Factory Tech Blog

技術好きな方へ!モバイルファクトリーのエンジニアたちが楽しい技術話をお届けします!

型安全に TypeScript からスマートコントラクトを扱う

この記事は モバイルファクトリー Advent Calendar 2019の4日目の記事です。

こんにちは、ブロックチェーンチームでエンジニアをしている id:odan3240 です。

今回は Ethereum のスマートコントラクト (以下コントラクト) を TypeScript から型安全に扱う方法について紹介します。

前提

この記事は以下のバージョンを元に執筆されています。

  • NodeJS: 10.16.3
  • TypeScript: 3.7.2
  • web3: 1.2.4
  • typechain: 1.0.3
  • typechain-target-web3-v1: 1.0.3

JavaScript からコントラクトを扱う方法

JavaScript からコントラクトを扱う場合に用いるライブラリとして web3.js がよく知られています。 web3.js を用いてコントラクトのメソッドを呼び出すコードは以下の通りです。

import Web3 from "web3";

async function main() {
  // abi は設定されている前提
  const abi = {};
  const web3 = new Web3();
  const contract = new web3.eth.Contract(abi);

  await contract.methods.isOwner().call();
}

main();

このコードにおいて contract.methods.isOwner().call() の部分は実行時まで正常に実行されるかがわかりません。なぜなら、コントラクトの実装の内容に応じて生えているメソッドは異なるためです。

実際に VSCode でマウスオーバーして型を表示すると any 型になってしまっています。 f:id:odan3240:20191204135920p:plain

型安全に扱う方法

コントラクトの abi ファイルには、そのコントラクトにはどんな引数のどんなメソッドが実装されているかの情報が格納されています。この abi ファイルの情報から TypeScript の型定義ファイルを生成すれば、型安全にコントラクトを扱うことができます。

これを行うモジュールとして、@0x/abi-genTypeChain があります。 今回は実際にプロダクトで採用している TypeChain の使い方を紹介します。

TypeChain の使い方

TypeChain はコアパッケージの typechain と、コントラクトを扱う各ライブラリ向けのモジュールから構成されています。今回は web3.js 向けのコードを出力してほしいので typechain-target-web3-v1 を導入します。

$ yarn add -D typechain typechain-target-web3-v1

コントラクトのコンパイル結果を build ディレクトリに格納している場合、以下のコマンドを実行すると generated-abi/typechain/web3 ディレクトリに型定義ファイルが生成されます。

$ yarn typechain --target=web3-v1 'build/contracts/**/*.json' --outDir generated-abi/typechain/web3

使い方は簡単で、型を import して Contract クラスを new するところで as するだけです。

import Web3 from "web3";
import { AbiItem } from "web3-utils";
import { MyContract } from "../generated-abi/typechain/web3/MyContract";

async function main(): Promise<void> {
  const abi = ({} as any) as AbiItem;
  const web3 = new Web3();
  const contract = new web3.eth.Contract(abi) as MyContract;

  await contract.methods.isOwner().call();
}

main();

再びマウスオーバーしてみると型が表示されているのがわかります。 f:id:odan3240:20191204135951p:plain

注意点

web3.js と TypeChain を使うときには web3typechain-target-web3-v1 のバージョンの組み合わせに注意する必要があります。

具体的には以下の通りです。

  • 1.2.1 以前の web3 を使用している場合
    • 1.0.2 以前typechain-target-web3-v1 を使用する必要があります
  • 1.2.2 以降の web3 を使用している場合
    • 1.0.3 以降typechain-target-web3-v1 を使用する必要があります

この問題が発生している理由は、1.2.1 以前の web3 は型定義ファイルがモジュールに含まれていないため @types/web3 を利用する必要があったのに対して、1.2.2 からモジュール自体に型定義ファイルが含まれるようになり、これらの型定義ファイルに互換性がないためです。

github.com

サンプルリポジトリ

今回紹介したコードの断片が含まれているサンプルリポジトリです。 github.com

終わりに

型安全に TypeScript からコントラクトを扱う方法として、TypeChain を用いて abi ファイルから型定義ファイルを生成するアプローチを紹介しました。 エディタの補完機能によってコントラクトの実装を参照することなく、コントラクト周りのコードを実装できる体験を是非お試しください。