ネイティブトークンの収入と支出を監視

# はじめに（Introduction）

Ethereumなど、EVM互換ブロックチェーンのネイティブトークンに対して、秘密鍵をもつアカウント（以下、EOA）のネイティブトークンに対する収入と支出を監視しようとした場合、案外複雑な手順が必要なので説明したいと思います。

# 輸入と支出トークン（In Out Token）

ネイティブトークンの収入と支出には以下の２つが考えられます。

- EOA ⇒ EOA
- EOA ⇒ コントラクトアカウント（CA） ⇒ EOA

トランザクションをノードに送付するには秘密鍵が必要な為、EOAからの送付となります。

## 支出トークン（Out Token）

ネイティブトークンの支出を見てみます。

手順として、まずはトランザクションデータを取得します。
トランザクションデータの取得には、単体であれば`eth_getTransactionByHash`、`eth_getTransactionByBlockNumberAndIndex`、`eth_getTransactionByBlockHashAndIndex`などから、複数であれば`eth_getBlockByHash`、`eth_getBlockByNumber`の `hydrated`オプションを `true` にすることで取得できます。
以下がトランザクションデータのデータ構造となります。
※：トランザクションの形式により項目が増える場合もあります。

|field|type|description|
|:--|:--|:--|
|blockHash|DATA<br>32バイト|このトランザクションが組み込まれていたブロックのハッシュ<br>保留中の場合はnull|
|blockNumber|QUANTITY|このトランザクションが組み込まれていたブロックの番号<br>保留中の場合はnull|
|from|DATA<br>20バイト|送信者のアドレス|
|gas|QUANTITY|送信者が提供するガス|
|gasPrice|QUANTITY|送信者が指定したガス価格(wei単位)|
|hash|DATA、32バイト|トランザクションのハッシュ|
|input|DATA|トランザクションと共に送信されるデータ|
|nonce|QUANTITY|送信者がこのトランザクションより前に送信したトランザクションの数|
|to|DATA<br>20バイト|受信者のアドレス<br>コントラクト作成時のトランザクションはnull|
|transactionIndex|QUANTITY|ブロック内のトランザクションのインデックスの位置(整数)<br>保留中の場合はnull|
|value|QUANTITY|送金された価値(wei単位)|
|v|QUANTITY|ECDSAのリカバリID|
|r|QUANTITY|ECDSA署名 `r`|
|s|QUANTITY|ECDSA署名 `s`|

**`from`が対象のEOAであった場合、支出していることになります。**

支出を計算する前に、このトランザクションレシートを取得します。
トランザクションレシートの取得方法は、`eth_getTransactionReceipt` を利用します。
以下がトランザクションレシートのデータ構造となります。

|field|type|description|
|:--|:--|:--|
|transactionHash|DATA<br>32バイト|トランザクションのハッシュ|
|transactionIndex|QUANTITY|ブロック内のトランザクションのインデックスの位置(整数)|
|blockHash|DATA<br>32バイト|このトランザクションが組み込まれていたブロックのハッシュ|
|blockNumber|QUANTITY|このトランザクションが組み込まれていたブロックの番号|
|from|DATA<br>20バイト|送信者のアドレス|
|to|DATA<br>20バイト|受信者のアドレス<br>コントラクト作成時のトランザクションはnull|
|cumulativeGasUsed|QUANTITY|ブロック内でこのトランザクションの実行時に使用されたガスの総量|
|effectiveGasPrice|QUANTITY|ガスユニットごとに支払われるベースフィーとチップの合計|
|gasUsed|QUANTITY|この特定のトランザクションのみで使用されたガスの量|
|contractAddress|DATA<br>20バイト|コントラクト作成のトランザクションの場合は、作成されたコントラクト|
|logs|Array|このトランザクションが生成したログオブジェクトの配列|
|logsBloom|DATA<br>256バイト|関連ログを迅速に取得するためのライトクライアント用のブルームフィルター|
|type|QUANTITY|トランザクションタイプの整数、0x0でレガシートランザクション、 0x1でアクセスリストタイプ、0x2で動的手数料|
|status|QUANTITY| `1`(成功)、または `0`(失敗)|

このデータから、`status`を参照します。

**`1`(成功) の場合**

支出は、トランザクションデータから以下の式で求めることができます。

$ \mathrm{Out} = \mathrm{value} + \mathrm{gas} \times \mathrm{gasPrice} $

**`0`(失敗) の場合**

支出の値は、トランザクションデータから以下の式で求めることができます。

$ \mathrm{Out} = \mathrm{gas} \times \mathrm{gasPrice} $

## 収入トークン（In Token）

支出と同等に、トランザクションデータとトランザクションレシートを取得します。

:::note warn
以下の２つの場合でも収入の可能性はあります。
- トランザクションデータの `to` が対象のEOAでない
- トランザクションデータの `value` が `0x0` 
:::

トランザクションレシートの `status`を参照します。

- **`0`(失敗) の場合**
ネイティブトークンの収入はありません。

- **`1`(成功) の場合**
ここからまた分岐します。
トランザクションデータの `to` を参照します。
    - **`to` が対象のEOAだった場合**
トランザクションデータの `value` が収入の値となります。
    - **`to` が対象のEOAでない場合**
インターナルトランザクションを調べる必要があります。
Gethの場合、`debug_traceTransaction` を利用してインターナルトランザクションを取得します。
パラメータは、トランザクションハッシュと callTracer（`{ tracer: 'callTracer' }`） になります。
        - **各`call`（サブ`call`が存在）を検索**
            - **`to`が対象のEOAだった場合**
`call` の `value` が収入の値となります。

以下が`call`のデータ構造となります。

|field|type|description|
|:--|:--|:--|
|type|string|CALL または CREATE|
|from|string|アドレス|
|to|string|アドレス|
|value|string|16進数でエンコードされた価値転送量（value transfer）|
|gas|string|`call` 用に提供された 16 進数でエンコードされたガス（gas）|
|gasUsed|string|`call` 中に使用された 16 進数でエンコードされたガス|
|input|string|`call` データ|
|output|string|リターンデータ|
|error|string|エラー（ある場合）|
|revertReason|string|Solidity の「revert」理由（ある場合）|
|calls|[]callframe|サブ`call`のリスト|

## インターナルトランザクション（Internal Transaction）

インターナルトランザクションの仕様を探したところ、イーサリアム仮想マシン（EVM）のドキュメントに記載されていました。

> The calldata
calldata 領域は、スマート コントラクト トランザクションの一部としてトランザクションに送信されるデータです。
たとえば、コントラクトを作成する場合、calldata は新しいコントラクトのコンストラクター コードになります。
calldata は不変であり、CALLDATALOAD、CALLDATASIZE、および CALLDATACOPY 命令を使用して読み取ることができます。
コントラクトが xCALL 命令を実行すると、内部トランザクション（internal transaction）も作成されることに注意してください。
その結果、xCALL を実行すると、新しいコンテキストに calldata 領域が存在します。

https://www.evm.codes/about#calldata

# まとめ（Conclusion）

EOAからの支出に関しては比較的簡単に見つけることが出来ます。
EOAへの収入に関しては、かなり複雑な手順が必要となります。
これは、コントラクトからEOAへの支出がEOAからの場合とコントラクトからの場合がある為です。
コントラクト内での呼び出しはインターナルトランザクションとして作成される為、これらをトレースする必要があります。


# 参考（References）

- トランザクションのデータ
https://ethereum.org/ja/developers/docs/apis/json-rpc/#eth_gettransactionbyhash
- レシピのデータ
https://ethereum.org/ja/developers/docs/apis/json-rpc/#eth_gettransactionreceipt
- インターナルトランザクション
https://www.evm.codes/about#calldata
- debug_traceTransaction
https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug#debugtracetransaction
- callTracer
https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers#call-tracer