Infuraを使ってEthereumのトランザクションを監視してみた

特定のアドレスのトランザクションだけ監視したい場合はEtherscanを使うのが便利かと思います。
詳しくは以下の記事が参考になると思います。
https://qiita.com/sot528/items/2044cea2cf2a2c79b14c

しかし、監視したいアドレスが沢山有る場合などには登録も面倒なので自前でやってみたいと思います。

# はじめに
まずは[Infura](https://infura.io/)に登録してみましょう。
登録しなくても利用は可能なのですが、API問合せの量が多い時はAPIキーを利用した方が確実です。
登録の方法に関しては以下の記事を参考にしてください。
https://qiita.com/SAengineer/items/4369e6d5cf843d449430

# セットアップ
無事登録が完了したら環境構築をしていきましょう。
今回は Amazon Linux 2 を使っています。

```
$ cat /etc/system-release
Amazon Linux release 2 (Karoo)
```

あとは Node.js を使います。
今回は以下の手順でインストールしました。

```
# curl -sL https://rpm.nodesource.com/setup_10.x | bash -
# yum install gcc-c++ make
# yum install nodejs
# node --version
v10.15.3
# npm --version
6.4.1
```

# コーディング
作業ディレクトリの作成とnpmパッケージのインストールから行います。

```
$ mkdir ethereum_watcher
$ npm install web3
```

続いて、以下の感じでコードを書きました。
InfuraにはWebScoketで接続し、ブロックが掘られる度に中のトランザクションを読み込んでいく感じです。

```watcher.js
const Web3 = require('web3');
// 監視したいアドレスのリスト
const addresses = require('./addresses.json');

// WebSocket で接続する
// xxx... の部分はAPIキーを入れる
Web3.providers.WebsocketProvider('wss://ropsten.infura.io/ws/v3/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'));
//const web3 = new Web3(new Web3.providers.WebsocketProvider('wss://mainnet.infura.io/ws/v3/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'));

// 直近読み込んだブロック番号を保存する
let blocknumber = 0;

// 最新のブロックが掘られるたびに処理を受け取る
const subscription = web3.eth.subscribe('newBlockHeaders', function(error, result) {
  if (error) console.error(error)
})
.on("data", function(blockHeader) {
  // 最新のブロック情報は取得出来ないことが有るのでここでは5個前のブロックを取得
  web3.eth.getBlock(blockHeader.number - 5)
  .then(function(block) {
    if (blocknumber < block.number) {
      if (block.transactions.length > 0) {
        // トランザクションの内容を取得していく
        for (let i = 0; i < block.transactions.length; i++) {
          let txid = block.transactions[i];
          web3.eth.getTransaction(txid, function(error, tx) {
            if (error) {
              console.error(error);
            } else if (tx.to && addresses[tx.to.toLowerCase()]) {
              // 監視したいアドレス宛てだった場合に何か処理を行う
              console.log("hash: "   + tx.hash);
              console.log("to : "    + tx.to);
              console.log("value : " + web3.utils.fromWei(tx.value, 'ether'));
            }
          });
        }
      }
      blocknumber = block.number;
    } else {
      // 同じブロックの通知が来たときは無視する
      console.log("Duplicated Block : " + block.number);
    }
  });
})
.on("error", function(error) {
  console.error(error);
});

// 接続が解除された場合
process.on('unhandledRejection', function() {
  subscription.unsubscribe(function(error, success) {
    if (success) {
      console.log('Unsubscribed');
    } else {
      console.error(error);
    }
  });
  process.exit(1);
});
```

注意点としては、最新のブロック情報を取りに行かないようにすることです。
Ethereumの場合、同タイミングで複数の最新ブロックが掘られる場合がちょこちょこ有ります。
なので、ブロックが掘られたタイミングで幾つか前のブロック情報を取得することをオススメします。

また、アドレスに関してもユーザが入力したままのアドレスになっているので、大文字小文字が混在しているケースが有るので``toLowerCase()``などで統一すると良いと思います。

そしていきなり``addresses.json``というものが登場しましたが、内容は単純です。

```addresses.json
{
  "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1": 1,
  "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2": 1,
  "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa3": 1,
  "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4": 1,
  "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa5": 1,
  "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa6": 1,
  "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa7": 1,
  "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa8": 1,
  "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9": 1,
}
```

ここに監視したいアドレス一覧を記述しておけば、それに従いさっきのスクリプトで処理が可能となります。

# まとめ
やってみると結構簡単にトランザクションが取得出来ました。
実際は連続稼動させていると接続が切断されることもあったりするので、実際に運用する際はもうちょっと手を加える必要が出てきます。
それはまたの機会に書きたいと思います。