Contract ABI Specification

Basic Design

コントラクトにおけるアプリケーションバイナリインターフェース(Application Binary Interface, ABI)はイーサリアムエコシステム内でコントラクトに接続するスタンダードなインターフェースです。コントラクトもABIもブロックチェーン外で作成され、ABIはコントラクトとコントラクトを接続するために使用されます。このドキュメントで紹介されているように、データはABIに従う形でエンコーディングされます。このエンコーディングのやり方は自己記述的なものではありません。そのため、デコードを行う際には、あるスキーマで行うことが必要になります。

コントラクトのインターフェースは型付けされていることが強制されます。このことはコンパイル時に静的型付けであることとしてすでに知られていることです。すべてのコントラクトは、コンパイル時に呼び出し可能なすべてのコントラクトのインターフェース定義を内包します。

この仕様は動的なインターフェースやランタイムにのみ認識されるインターフェースを持つコントラクトには対応しません。

Function selector

関数呼び出し時のコールデータの最初の4バイトは呼び出す関数を指定しています。関数シグネチャにおけるKeccak-256(SHA-3)ハッシュの最初の4バイト(左から4バイトでビッグ・エンディアンの順序になっています)のことです。このシグネチャはデータ位置指定子(data location specifier)を含まない基本プロトタイプの正規表現として定義されています。例えば、かっこで囲まれたパラメータ型のリストを含む関数名などが挙げられます。パラメータの型は単一のコンマで区切られ、スペースは使用されません。

注釈

関数の戻り値の型はこのシグニチャの一部ではありません。 Solidityの関数のオーバーロード においては、戻り値の型は考慮されません。これは関数呼び出しを特定のコンテキストに依存しないようにするためです。 しかし、ABIのJSON記述 は入力値と出力値の両方を含むことに注意が必要です。

Argument Encoding

先頭から数えて5バイト目から、エンコーディングされた引数が続きます。このエンコーディングされた引数は、特定の関数を指定する4バイトを除いて、戻り値などの別の場所でも使用されます。また、イベント引数も同やり方でエンコーディングされます。

Types

基本の型は以下です:

  • uint<M>: M ビットのunsigned int型。 0 < M <= 256M % 8 == 0 である必要があります。 uint32uint8uint256 が挙げられます。
  • int<M>: 2の補数がつく M ビットのsigned int型。 0 < M <= 256M % 8 == 0 である必要があります。
  • address: uint160 と同等です。ただしこの場合、uint160 が暗黙的な値や型であるときを除きます。関数セレクタを計算する際に、address は使用されます。
  • uint, int: uint256int256 と同じ意味です。関数セレクタを計算する際に、uint256int256 は必ず使用されなくてはなりません。
  • bool: uint8 と同等です。ただしこの場合、値は0と1のみである必要があります。 関数セレクタを計算する際に、bool が使用されます。
  • fixed<M>x<N>: M ビットの符号付き固定小数点。 8 <= M <= 256M % 8 ==0 、0 < N <= 80 である必要があり、v as v / (10 ** N) であることを示します。
  • ufixed<M>x<N>: fixed<M>x<N> の符号付き変数。
  • fixed, ufixed: fixed128x18ufixed128x18 と同じ意味です。関数セレクタを計算する際に、fixed128x18ufixed128x18 は必ず使用されなくてはなりません。
  • bytes<M>: M バイトのバイナリ型。 0 < M <= 32 です。
  • function: アドレス(20バイト)とそれに続く関数セレクタ(4バイト)。 bytes24 と同じエンコーディングです。

固定長な型は以下です:

  • <type>[M]: M 個要素の固定長配列です。与えられた型の M >= 0 になります。

可変長な型は以下です:

  • bytes: 動的サイズのバイトシーケンス。
  • string: UTF-8でエンコードされている動的サイズのUnicode文字列。
  • <type>[]: 指定されたタイプの要素の可変長配列。

データ型はカンマで区切って括弧で囲むとタプルにまとめることができます:

  • (T1,T2,...,Tn): 同じデータ型 T1 ..., Tn, n >= 0 で構成されたタプル。

また、タプルのタプル、タプルの配列なども形成することができます。ゼロタプル( n == 0 )を作ることもできます。

Mapping Solidity to ABI types

Solidityは上記のすべての型を同じ名前でサポートしています(ただしタプルは例外です)。一方で、SolidityタイプのいくつかはABIにはサポートされていません。次の表は、左列にABIにサポートされていないSolidityのデータ型を、右列にはABI側でのそのデータ型の表記を表しています。

Solidity ABI
address payable address
contract address
enum

smallest uint type that is large enough to hold all values

For example, an enum of 255 values or less is mapped to uint8 and an enum of 256 values is mapped to uint16.

struct tuple

Design Criteria for the Encoding

エンコーディングは次のプロパティを持つように設計されています。これは、引数がネストされた配列の場合に特に便利です。

  1. 値にアクセスするのに必要な読み込みの数は、引数となる配列構造内の値の最大の深さに依ります。すなわち、a_i [k] [l] [r] を取得するには4回の読み込みが必要になるということです。以前のバージョンのABIでは、最悪の場合、読み取り数は動的パラメータの総数に比例して増減しました。
  2. 変数または配列要素のデータは他のデータとインターリーブされません。またこれらのデータは再配置可能です。つまり、相対的な「アドレス」のみを使用します。

Formal Specification of the Encoding

静的型と動的型を区別するようにします。静的型はインプレースでエンコードされ、動的型はカレントブロック後に別々に割り当てられた場所でエンコードされます。

定義: 次の型は「動的型」です:

  • bytes
  • string
  • 任意の T における T[]
  • 任意の動的な T と任意の k >= 0 における T[k]
  • Ti1 <= i <= k に対して動的である場合の (T1,...,Tk)

上記以外のすべてのすべての型は「静的型」です。

定義: len(a) はバイナリ文字列 a のバイト数です。len(a) の型は uint256 であると仮定されます。

実際のエンコーディングである enc は、ABI型の値を以下のようなバイナリ文字列にマッピングするものとして定義します。len(enc(X))X の型が動的である場合に限り、X の値に依存します。

定義: 任意のABI値 X に対して、下記の X の型によって再帰的に enc(X) を定義します。

  • k >= 0 かつ任意の型 T1, ..., Tk における (T1,...,Tk) のときは以下の様になります:

    enc(X) = head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(k))

    Ti が静的型である場合、X = (X(1)), ..., X(k))headtail は以下のように

    head(X(i)) = enc(X(i)) および tail(X(i)) = "" (空の文字列)

    head(X(i)) = enc(len(head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(i-1)) )) tail(X(i)) = enc(X(i))

    となります。

    上記以外の場合は、Ti は動的型です。

    動的な場合、head(X(i)) は ヘッド部分がそのデータ型にのみ依存し、値には依存しません。またその値は enc(X) の先頭から tail(X(i)) の先頭までの範囲でのオフセットです。

  • 任意の Tk における T[k] のときは以下になります:

    enc(X) = enc((X[0], ..., X[k-1]))

    すなわち、同じ型の k 要素を持つタプルであるかのようにエンコードされます。

  • Xk 要素を持つとき( kuint256 型であると仮定されます)の T[] のときは以下になります:

enc(X) = enc(k) enc([X[0], ..., X[k-1]])

すなわち、要素数を前に付けた静的サイズの k の配列であるかのようにエンコードされます。

  • 長さが k (これは uint256 型であると仮定されます)の bytes のときは以下になります:

    enc(X) = enc(k) pad_right(X)。すなわち、バイト数は uint256 の後にバイトシーケンスとしての X の実際の値が続き、その後に len(enc(X)) が32の倍数になるような最小ゼロバイト数が続きます。

  • string のときは以下になります:

    enc(X) = enc(enc_utf8(X))。すなわち、X はUTF-8でエンコードされており、この値は bytes 型として解釈され、さらにエンコードされます。この後のエンコーディングで使用される長さは、その文字数ではなく、utf-8でエンコードされた文字列のバイト数です。

  • uint<M>: enc(X)X のビッグエンディアンエンコーディングで、長さが32バイトになるように高位(左側)にゼロバイトが埋め込まれます。

  • address: uint160 の場合と同じです。

  • int<M>: enc(X) は、X のビッグエンディアンの2の補数エンコーディングで、負の X の場合は高位(左側)に 0xff がパディングされ、長さが32バイトになるような正の X の場合はゼロバイトがパディングされます。

  • bool: uint8 の場合のように、1true が使われ、0false が使われます。

  • fixed<M>x<N>: enc(X)enc(X * 10**N) です。ここで X * 10**Nint256 として解釈されます。

  • fixed: fixed128x18 の場合と同じです。

  • ufixed<M>x<N>: enc(X)enc(X * 10**N) です。ここで X * 10**Nuint256 として解釈されます。

  • ufixed: fixed128x18 の場合と同じです。

  • bytes<M>: enc(X) は末尾の0バイトを32バイトの長さまで埋め込んだ X 内のバイトの並びです。

任意の X において、len(enc(X)) は32の倍数であることを念頭に入れてください。

Function Selector and Argument Encoding

たいていの場合、a_1, ..., a_n をパラメータとして関数 f をコールするときには次のようにエンコーディングされます。

function_selector(f) enc((a_1, ..., a_n))

また、f の返り値である v_1, ..., v_k は次のようにエンコーディングされます。

enc((v_1, ..., v_k))

すなわち、値はタプルに組み合わされてエンコードされます。

Examples

次のコントラクトがあります:

pragma solidity >=0.4.16 <0.6.0;

contract Foo {
  function bar(bytes3[2] memory) public pure {}
  function baz(uint32 x, bool y) public pure returns (bool r) { r = x > 32 || y; }
  function sam(bytes memory, bool, uint[] memory) public pure {}
}

このように Foo の例では、もしパラメータに「69」と「true」を指定して「baz」を呼び出すなら、合計68バイトを渡します。このことは次のように分割できます。

  • 0xcdcd77c0: 指定のメソッドID。これは baz(uint32,bool) の署名のASCII形式のKeccakハッシュにおける最初の4バイトとして導出されます。
  • 0x0000000000000000000000000000000000000000000000000000000000000045: 32バイトにパディングされた最初のパラメータ 69 のuint32値。
  • 0x0000000000000000000000000000000000000000000000000000000000000001: 32バイトにパディングされた2番目のパラメータ true のブール値。

これらを連結すると:

0xcdcd77c000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001

一つの bool を返します。たとえば、false を返すことになっていたら、その出力はシングルバイト配列 0x000000000000000000000000000000000000000000000000000000000000、シングルブールになります。

これは1つの bool 値を返します。 もしここで例えばこの値が false を返すなら、その出力はバイト配列の 0x0000000000000000000000000000000000000000000000000000000000000000 となるでしょう。

引数 ["abc"、"def"] と共に bar を呼び出したい場合は、合計68バイトを渡します。これは次のように分割されます:

  • 0xfce353f6: 指定のメソッドID。これは bar(bytes3[2]) の署名から導出されます。
  • 0x6162630000000000000000000000000000000000000000000000000000000000: 最初の引数の最初のパート、bytes3 の値 "abc" (左寄せ)。
  • 0x6465660000000000000000000000000000000000000000000000000000000000: 最初の引数の2番目のパート、bytes3 の値 "def" (左寄せ)。

これらを連結すると:

0xfce353f661626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000

引数として "dave"true および [1,2,3] を付けて sam を呼び出したい場合、合計292バイトを渡します。

  • 0xa5643bf2: 指定のメソッドID。 これは sam(bytes,bool,uint256[]) の署名から導出されます。 uint はその正規表現 uint256 に置き換えられていることを念頭に入れてください。
  • 0x0000000000000000000000000000000000000000000000000000000000000060: 引数ブロックの先頭からのバイト単位で測定された、最初のパラメータ(動的型)のデータ部分の位置。この場合は 0x60 です。
  • 0x0000000000000000000000000000000000000000000000000000000000000001: 2番目のパラメータ: true のブール値。
  • 0x00000000000000000000000000000000000000000000000000000000000000a0: 3番目のパラメータ(動的型)のデータ部分の位置(バイト単位)。この場合、0xa0 です。
  • 0x0000000000000000000000000000000000000000000000000000000000000004: 最初の引数のデータ部分。要素のバイト配列の長さで始まります。この場合は4です。
  • 0x6461766500000000000000000000000000000000000000000000000000000000: 最初の引数の内容:32バイトまで右側にパディングしたUTF-8(この場合はASCIIに等しくなります)エンコーディング。
  • 0x0000000000000000000000000000000000000000000000000000000000000003: 3番目の引数のデータ部分。要素の配列の長さから始まります。この場合は3です。
  • 0x0000000000000000000000000000000000000000000000000000000000000001: 3番目のパラメータの最初のデータ
  • 0x0000000000000000000000000000000000000000000000000000000000000002: 3番目のパラメータの2番目のデータ
  • 0x0000000000000000000000000000000000000000000000000000000000000003: 3番目のパラメータの3番目のデータ

これらを連結すると:

0xa5643bf20000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003

Use of Dynamic Types

(0x123、[0x456、0x789]、"1234567890"、"Hello、world!") の値を持つシグネチャ f(uint、uint32[]、bytes10、bytes) を持つ関数コールは次のようにエンコードされます:

sha3("f(uint256,uint32[],bytes10,bytes)") の最初の4バイトを取ります。すなわち、0x8be65246 です。

それから、4つの引数すべての先頭部分をエンコードします。静的型である uint256bytes10 の場合、これらは直接渡したい値ではありますが、動的型の uint32[]bytes の場合は、値エンコーディングの開始から測定された、データ領域の開始までのバイト単位のオフセットを使用します(つまり、関数シグネチャのハッシュを含む最初の4バイトはカウントしません)。これらは:

  • 0x0000000000000000000000000000000000000000000000000000000000000123 (32バイトにパディングされた 0x123 の値)
  • 0x0000000000000000000000000000000000000000000000000000000000000080 (2番目のパラメータのデータ部の先頭までのオフセット。4 * 32バイト。正確には先頭部分のサイズ)
  • 0x3132333435363738393000000000000000000000000000000000000000000000 (32バイトに右詰めにパディングされた "1234567890" の値)
  • 0x00000000000000000000000000000000000000000000000000000000000000e0 (4番目のパラメータのデータ部の先頭までのオフセット = 最初の動的パラメータのデータ部の先頭までのオフセット + 最初の動的パラメータのデータ部のサイズ = 4 * 32 + 3 * 32(下記を参照してください)))

このあと、最初の動的引数のデータ部分である [0x456, 0x789] は次のようになります:

  • 0x0000000000000000000000000000000000000000000000000000000000000002 (配列の要素数、2)
  • 0x0000000000000000000000000000000000000000000000000000000000000456 (最初の要素)
  • 0x0000000000000000000000000000000000000000000000000000000000000789 (2番目の要素)

最後に、2番目の動的引数のデータ部分、"Hello, world!" をエンコードします:

  • 0x000000000000000000000000000000000000000000000000000000000000000d (要素数(この場合バイトになります): 13)
  • 0x48656c6c6f2c20776f726c642100000000000000000000000000000000000000 (32バイトになるように右側にパディングした "Hello, world!" )

こうして、エンコーディングは次のとおりです(関数セレクタの後に改行、わかりやすくするために各32バイト):

0x8be65246
  0000000000000000000000000000000000000000000000000000000000000123
  0000000000000000000000000000000000000000000000000000000000000080
  3132333435363738393000000000000000000000000000000000000000000000
  00000000000000000000000000000000000000000000000000000000000000e0
  0000000000000000000000000000000000000000000000000000000000000002
  0000000000000000000000000000000000000000000000000000000000000456
  0000000000000000000000000000000000000000000000000000000000000789
  000000000000000000000000000000000000000000000000000000000000000d
  48656c6c6f2c20776f726c642100000000000000000000000000000000000000

([[1、2]、[3]]、["one"、"two"、"three"]) の値で署名付きの関数 g(uint[][],string[]) に同じ原則を適用します。しかし、エンコーディングの最も基本的な部分から始めましょう:

まずはじめに、最初のルート配列 [[1, 2], [3]] の最初の動的な要素配列 [1, 2] の長さとデータをエンコードします。

  • 0x0000000000000000000000000000000000000000000000000000000000000002 (最初の配列の要素数である2。ただし要素自体は1と2です)
  • 0x0000000000000000000000000000000000000000000000000000000000000001 (最初の要素)
  • 0x0000000000000000000000000000000000000000000000000000000000000002 (2番目の要素)

次に、最初のルート配列 [[1、2]、[3]] の2番目の動的な要素配列 [3] の長さとデータをエンコードします。

  • 0x0000000000000000000000000000000000000000000000000000000000000001 (2番目の配列の要素数である1。要素は3です)
  • 0x0000000000000000000000000000000000000000000000000000000000000003 (最初の要素)

それから、それぞれの動的配列 [1、2][3] に対するオフセット ab を見つける必要があります。オフセットを計算するために、エンコーディングの各行を列挙している最初のルート配列 [[1、2]、[3]] のエンコードされたデータを見ることができます。

0 - a                                                                - [1, 2]のオフセット
1 - b                                                                - [3]のオフセット
2 - 0000000000000000000000000000000000000000000000000000000000000002 - [1, 2]の要素数
3 - 0000000000000000000000000000000000000000000000000000000000000001 - 1のエンコーディング
4 - 0000000000000000000000000000000000000000000000000000000000000002 - 2のエンコーディング
5 - 0000000000000000000000000000000000000000000000000000000000000001 - [3]の要素数
6 - 0000000000000000000000000000000000000000000000000000000000000003 - 3のエンコーディング

オフセット a は、2行目(64バイト)の配列 [1、2] の始まりを指しています。したがって、a = 0x0000000000000000000000000000000000000000000000000000000000000040 です。

オフセット b は、配列 [3] の5行目(160バイト)の先頭を指します。したがって、b = 0x00000000000000000000000000000000000000000000000000000000000000a0 です。

そして、2番目のルート配列の埋め込み文字列をエンコードします:

  • 0x0000000000000000000000000000000000000000000000000000000000000003 (単語 "one" の中の文字数)
  • 0x6f6e650000000000000000000000000000000000000000000000000000000000 (単語 "one" のutf8での表現)
  • 0x0000000000000000000000000000000000000000000000000000000000000003 (単語 "two" の中の文字数)
  • 0x74776f0000000000000000000000000000000000000000000000000000000000 (単語 "two" のutf8での表現)
  • 0x0000000000000000000000000000000000000000000000000000000000000005 (単語 "three" の中の文字数)
  • 0x7468726565000000000000000000000000000000000000000000000000000000 (単語 "three" のutf8での表現)

最初のルート配列と並行して、文字列は動的要素なので、それらのオフセット cd および e を見つける必要があります:

0 - c                                                                - "one"のオフセット
1 - d                                                                - "two"のオフセット
2 - e                                                                - "three"のオフセット
3 - 0000000000000000000000000000000000000000000000000000000000000003 - "one"の文字列数
4 - 6f6e650000000000000000000000000000000000000000000000000000000000 - "one"のエンコーディング
5 - 0000000000000000000000000000000000000000000000000000000000000003 - "two"の文字列数
6 - 74776f0000000000000000000000000000000000000000000000000000000000 - "two"のエンコーディング
7 - 0000000000000000000000000000000000000000000000000000000000000005 - "three"の文字列数
8 - 7468726565000000000000000000000000000000000000000000000000000000 - "three"のエンコーディング

オフセット c は、文字列 "one" の3行目(96バイト)の先頭を指します。したがって、c = 0x0000000000000000000000000000000000000000000000000000000000000060 です。

オフセット d は、文字列 "two" の先頭を指します。これは5行目(160バイト)です。したがって、d = 0x00000000000000000000000000000000000000000000000000000000000000a0 です。

オフセット e は、文字列 "three" の7行目(224バイト)の先頭を指します。したがって、e = 0x00000000000000000000000000000000000000000000000000000000000000e0 です。

ルート配列の埋め込み要素のエンコーディングは互いに依存関係がなく、シグネチャが g(string[],uint[][]) の関数に対して同じエンコーディングを持つことに注意してください。

次に、最初のルート配列の長さをエンコードします:

  • 0x0000000000000000000000000000000000000000000000000000000000000002 (最初のルート配列の要素数である2。要素自体は [1, 2][3] です)

次に、2番目のルート配列の長さをエンコードします:

  • 0x0000000000000000000000000000000000000000000000000000000000000003 (2番目のルート配列の文字列数である3。文字列自体は "one""two" 及び "three" です)

最後に、それぞれのルート動的配列 [[1, 2], [3]]["one", "two", "three"] に対するオフセット fg を見つけます。そして、正しい順序でこれらのパーツを組み立てます。

0x2289b18c                                                            - 関数シグニチャ
 0 - f                                                                - [[1, 2], [3]]のオフセット
 1 - g                                                                - ["one", "two", "three"]のオフセット
 2 - 0000000000000000000000000000000000000000000000000000000000000002 - [[1, 2], [3]]の要素数
 3 - 0000000000000000000000000000000000000000000000000000000000000040 - [1, 2]のオフセット
 4 - 00000000000000000000000000000000000000000000000000000000000000a0 - [3]のオフセット
 5 - 0000000000000000000000000000000000000000000000000000000000000002 - [1, 2]の要素数
 6 - 0000000000000000000000000000000000000000000000000000000000000001 - 1のエンコーディング
 7 - 0000000000000000000000000000000000000000000000000000000000000002 - 2のエンコーディング
 8 - 0000000000000000000000000000000000000000000000000000000000000001 - [3]の要素数
 9 - 0000000000000000000000000000000000000000000000000000000000000003 - 3のエンコーディング
10 - 0000000000000000000000000000000000000000000000000000000000000003 - ["one", "two", "three"]の要素数
11 - 0000000000000000000000000000000000000000000000000000000000000060 - "one"のエンコーディング
12 - 00000000000000000000000000000000000000000000000000000000000000a0 - "two"のオフセット
13 - 00000000000000000000000000000000000000000000000000000000000000e0 - "three"のオフセット
14 - 0000000000000000000000000000000000000000000000000000000000000003 - "one"の文字列数
15 - 6f6e650000000000000000000000000000000000000000000000000000000000 - "one"のエンコーディング
16 - 0000000000000000000000000000000000000000000000000000000000000003 - "two"の文字列数
17 - 74776f0000000000000000000000000000000000000000000000000000000000 - "two"のエンコーディング
18 - 0000000000000000000000000000000000000000000000000000000000000005 - "three"の文字列数
19 - 7468726565000000000000000000000000000000000000000000000000000000 - "three"のエンコーディング

オフセット f は、配列 [[1、2]、[3]] の2行目(64バイト)の先頭を指します。したがって、f = 0x0000000000000000000000000000000000000000000000000000000000000040 です。

オフセット g は、配列 ["one"、"two"、"three"] の先頭を指します。これは10行目(320バイト)です。したがって、g = 0x0000000000000000000000000000000000000000000000000000000000000140 です。

Events

イベントはEthereumロギング/イベント監視プロトコルを抽象化したものです。ログエントリは、コントラクトアドレス、最大4つまでの一連のトピック、及び任意の長さのバイナリデータを提供します。イベントは既存の関数ABIを(インターフェース仕様とともに)正しく型付けされた構造体として解釈するために利用します。

イベント名と一連のイベントパラメータが与えられたとき、それらを2つのサブシリーズに分けます。それはインデックスされているものとそうでないものです。インデックス付けされたもの(最大3まで)は、イベント署名のKeccakハッシュと一緒に使用されて、ログエントリのトピックを形成します。インデックスが付けられていないものはイベントのバイト配列を形成します。

実際には、このABIを使用するログエントリは次のように記述されています:

  • address: コントラクトアドレス(Ethereumから提供されるものです)
  • topics[0]: keccak(EVENT_NAME+"("+EVENT_ARGS.map(canonical_type_of).join(",")+")") ( canonical_type_of は与えられた引数の標準型を単純に返す関数です。 uint indexed foo の場合は、uint256 を返します。) イベントが 匿名 として宣言されている場合、topics[0] は生成されません。
  • topics[n]: EVENT_INDEXED_ARGS[n - 1] ( EVENT_INDEXED_ARGS はインデックスが付けられた一連の EVENT_ARGS です)
  • data: abi_serialise(EVENT_NON_INDEXED_ARGS) ( EVENT_NON_INDEXED_ARGS はインデックスされていない一連の EVENT_ARGSabi_serialise は上述のように関数から一連の型付き値を返すために使用されるABIシリアライゼーション関数です)

すべての固定長のSolidityの型において、EVENT_INDEXED_ARGS 配列は32バイトでエンコードされた値を直接含みます。しかし、stringbytes、配列を含む 動的長 型の場合、EVENT_INDEXED_ARGS はパックされたエンコード値の Keccakハッシュ を含みます( Strict Encoding Mode を参照ください)。これにより、アプリケーションは(エンコードされた値のハッシュをトピックとして設定することによって)動的長の型の値を効率的に照会できますが、照会していない索引付きの値をデコードできません。動的長型の場合、アプリケーション開発者は、事前定義された値の高速検索(引数がインデックス付けされている場合)と任意の値の読みやすさ(引数にインデックスが付けられていないことが必要)との間のトレードオフに直面します。開発者はこのトレードオフを克服し、同じ値を保持することを目的とした2つの引数(1つはインデックス付き、1つはない)でイベントを定義することによって、効率的な検索と任意の読みやすさの両方を実現できます。

JSON

コントラクトインターフェースのJSONフォーマットは、関数やイベントのデスクリプションの配列によって指定されます。

関数の説明はフィールドを持つJSONオブジェクトです:

  • type: "function""constructor"、及び "fallback" ( unnamed "default" function)
  • name: 関数の名前
  • inputs: それぞれが含むオブジェクトの配列:
    • name: パラメータの名前
    • type: パラメータの標準型(詳細は後述)
    • components: タプル型に使用されます(詳細は後述)
  • outputs: functionが何も返さない場合は、input に似たオブジェクトの配列を省略することができます。
  • stateMutability: 以下のいずれかの値を持つ文字列: pure (specified to not read blockchain state), view (specified to not modify the blockchain state), nonpayable (function does not accept Ether) and payable (function accepts Ether);
  • payable: もし関数がEtherを受け付けるなら、true。そうでないなら、false
  • constant: もし関数が pureview のいづれかなら、true。そうでないなら、false

type は省略することができ、"function" にデフォルト設定され、同様に payableconstant は省略することができ、両方とも false にデフォルト設定されます。

コンストラクタとフォールバック関数は、nameoutputs などを持つことはありません。フォールバック関数も inputs を持つこともありません。

警告

constantpayable は今後廃止予定であり、今後取り除かれます。代わりに、stateMutability 領域が今後は同じプロパティを決定するために使うことができます。

注釈

ゼロ以外の額のイーサリアムをnon-payable functionに送金するときは、トランザクションにリバートが発生します。

イベントの詳細は似たフィールドを持つJSONオブジェクトです:

  • type: 常に "event" です。
  • name: イベントの名前;
  • inputs: 以下の値を含むオブジェクトの配列:
    • name: パラメータの名前
    • type: 標準的なパラメータの型 (詳細は以下を参照してください)
    • components: タプルに使用する(詳細は以下を参照してください)
    • indexed: もしフィールドがログトピックスの一部であるなら、true。もしログデータセグメントであるなら false
  • anonymous: もしイベントが anonymous と宣言されていたなら true

例えば、

pragma solidity ^0.5.0;

contract Test {
  constructor() public { b = hex"12345678901234567890123456789012"; }
  event Event(uint indexed a, bytes32 b);
  event Event2(uint indexed a, bytes32 b);
  function foo(uint a) public { emit Event(a, b); }
  bytes32 b;
}

これはJSONで表現すると以下のようになります:

[{
"type":"event",
"inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}],
"name":"Event"
}, {
"type":"event",
"inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}],
"name":"Event2"
}, {
"type":"function",
"inputs": [{"name":"a","type":"uint256"}],
"name":"foo",
"outputs": []
}]

Handling tuple types

名前(names)はABIエンコーディングの一部にあたるわけではないですが、ABIに含めることには意味があります。これはJSON形式でエンドユーザーに表示できるという点にあります。 その構造は次のようにネストされています:

nametype、潜在的には components などと共にオブジェクトは型付きの変数を記述します。

正規型はタプル型に達するまで決定され、それまでの文字列の説明は type プレフィックスに tuple という語で格納されます。つまり、tuple の後に整数の k を持つ [][k] のシーケンスが続きます。 タプルの構成要素はメンバの構成要素に格納されます。これは配列型で、インデックスが許可されていないことを除いて最上位オブジェクトと同じ構造を持ちます。

例として、次のコードは

pragma solidity >=0.4.19 <0.6.0;
pragma experimental ABIEncoderV2;

contract Test {
  struct S { uint a; uint[] b; T[] c; }
  struct T { uint x; uint y; }
  function f(S memory s, T memory t, uint a) public;
  function g() public returns (S memory s, T memory t, uint a);
}

以下のようなJSONとなります:

[
  {
    "name": "f",
    "type": "function",
    "inputs": [
      {
        "name": "s",
        "type": "tuple",
        "components": [
          {
            "name": "a",
            "type": "uint256"
          },
          {
            "name": "b",
            "type": "uint256[]"
          },
          {
            "name": "c",
            "type": "tuple[]",
            "components": [
              {
                "name": "x",
                "type": "uint256"
              },
              {
                "name": "y",
                "type": "uint256"
              }
            ]
          }
        ]
      },
      {
        "name": "t",
        "type": "tuple",
        "components": [
          {
            "name": "x",
            "type": "uint256"
          },
          {
            "name": "y",
            "type": "uint256"
          }
        ]
      },
      {
        "name": "a",
        "type": "uint256"
      }
    ],
    "outputs": []
  }
]

Strict Encoding Mode

Strict encoding modeは上記の仕様で定義されているものとまったく同じ符号化につながるモードです。 これは、データ領域にオーバーラップが発生しないようにしながらオフセットをできるだけ小さくする必要があるため、あらゆるギャップが許容されないことを意味します。

大抵の場合、ABIデコーダはオフセットポインタの直後に書かれていますが、Strict encoding modeを強制するデコーダもあります。 SolidityのABIデコーダは現在Strict encoding modeを強制しませんが、エンコーダは常にStrict encoding modeでデータを作成します。

Non-standard Packed Mode

abi.encodePacked() を通じて、SolidityはNon-standard Packed Modeをサポートしています:
  • 32バイトより短い型はゼロパディングも符号拡張もされず、
  • 動的型はその場でlengthを持たずにエンコードされます

Non-standard Packed Modeは主にインデックス付きイベントパラメータに使用されます。

例として、int16(-1), bytes1(0x42), uint16(0x03), string("Hello, world!") のエンコーディングは次のようになります:

0xffff42000348656c6c6f2c20776f726c6421
  ^^^^                                 int16(-1)
      ^^                               bytes1(0x42)
        ^^^^                           uint16(0x03)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^ string("Hello, world!") without a length field
より詳細にいうと:
  • 各値型は、その範囲と同じ数のバイトを取ります。
  • 構造体または固定長配列のエンコーディングは、そのメンバー/要素のエンコーディングを連結したもので、区切り文字やパディングはありません。
  • 構造体のマッピングメンバーは通常どおり認識されません。
  • stringbytesuint [] のような動的型はそれらのlengthフィールドなしでエンコードされます。

一般に、lengthフィールドが欠落しているため、2つの動的な要素があるとすぐに、エンコードはあいまいなものになってしまいます。

もしパディングが必要であれば、次のような明示的な型変換が使えるでしょう: abi.encodePacked(uint16(0x12)) == hex"0012"

パックエンコーディングは関数呼び出しの際に使われるものではないため、関数セレクタを準備するためのサポートがあるわけではありません。 また、エンコーディングが曖昧であるため、復号化のための関数は存在しません。