この記事は モバイルファクトリー 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 型になってしまっています。
型安全に扱う方法
コントラクトの abi ファイルには、そのコントラクトにはどんな引数のどんなメソッドが実装されているかの情報が格納されています。この abi ファイルの情報から TypeScript の型定義ファイルを生成すれば、型安全にコントラクトを扱うことができます。
これを行うモジュールとして、@0x/abi-gen や TypeChain があります。 今回は実際にプロダクトで採用している 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();
再びマウスオーバーしてみると型が表示されているのがわかります。
注意点
web3.js と TypeChain を使うときには web3
と typechain-target-web3-v1
のバージョンの組み合わせに注意する必要があります。
具体的には以下の通りです。
- 1.2.1 以前の
web3
を使用している場合- 1.0.2 以前の
typechain-target-web3-v1
を使用する必要があります
- 1.0.2 以前の
- 1.2.2 以降の
web3
を使用している場合- 1.0.3 以降の
typechain-target-web3-v1
を使用する必要があります
- 1.0.3 以降の
この問題が発生している理由は、1.2.1 以前の web3
は型定義ファイルがモジュールに含まれていないため @types/web3
を利用する必要があったのに対して、1.2.2 からモジュール自体に型定義ファイルが含まれるようになり、これらの型定義ファイルに互換性がないためです。
サンプルリポジトリ
今回紹介したコードの断片が含まれているサンプルリポジトリです。 github.com
終わりに
型安全に TypeScript からコントラクトを扱う方法として、TypeChain を用いて abi ファイルから型定義ファイルを生成するアプローチを紹介しました。 エディタの補完機能によってコントラクトの実装を参照することなく、コントラクト周りのコードを実装できる体験を是非お試しください。