ABI.Event (hieroglyph v1.4.0)

Copy Markdown View Source

Decodes Ethereum event log data into Solidity-typed arguments.

Splits the topic list (indexed parameters) from the data blob (non-indexed parameters) per the ABI specification, and optionally verifies that topics[0] matches the keccak256 hash of the event signature.

API Functions

FunctionArityDescriptionParam Kinds
canonical2Render the canonical signature string of an event for hashing or display, optionally including indexed and parameter-name annotations.function_selector: value, opts: value
event_signature1Compute the keccak-256 hash of the event's canonical signature, used as topics[0] in event logs.function_selector: value
decode_event4Decode an Ethereum event log, splitting indexed parameters from topics and non-indexed parameters from the data blob, optionally verifying topics[0] against the event signature.data: exchange_data, topics: exchange_data, function_selector: value, opts: value

Summary

Types

Closed error set returned by decode_event/4.

Functions

Returns the canonical form of this event topic. Pass in indexed: true to include "indexed" keywords.

Decodes an event, including handling parsing out data from topics.

Returns the signature of an event, i.e. the first item that appears in an Ethereum log for this event.

Types

decode_error()

@type decode_error() ::
  {:event_signature_mismatch, %{expected: binary(), got: binary()}}
  | {:topics_length_mismatch, length_pair()}
  | {:malformed_data, String.t()}

Closed error set returned by decode_event/4.

  • :event_signature_mismatchtopics[0] did not match keccak256(canonical_signature).
  • :topics_length_mismatch — number of topics did not match the indexed-parameter count (plus the implicit topics[0] slot when check_event_signature: true).
  • :malformed_data — non-indexed payload failed to decode (truncated, wrong types, or otherwise inconsistent with function_selector.types).

Functions

canonical(function_selector, opts \\ [])

@spec canonical(
  ABI.FunctionSelector.t(),
  keyword()
) :: String.t()

Returns the canonical form of this event topic. Pass in indexed: true to include "indexed" keywords.

Examples

iex> ABI.Event.canonical(
...>   %ABI.FunctionSelector{
...>     function: "Transfer",
...>     types: [
...>       %{type: :address, name: "from", indexed: true},
...>       %{type: :address, name: "to", indexed: true},
...>       %{type: {:uint, 256}, name: "amount"},
...>     ]
...>   }
...> )
"Transfer(address,address,uint256)"

iex> ABI.Event.canonical(
...>   %ABI.FunctionSelector{
...>     function: "Transfer",
...>     types: [
...>       %{type: :address, name: "from", indexed: true},
...>       %{type: :address, name: "to", indexed: true},
...>       %{type: {:uint, 256}, name: "amount"},
...>     ]
...>   },
...>   names: true
...> )
"Transfer(address from,address to,uint256 amount)"

iex> ABI.Event.canonical(
...>   %ABI.FunctionSelector{
...>     function: "Transfer",
...>     types: [
...>       %{type: :address, name: "from", indexed: true},
...>       %{type: :address, name: "to", indexed: true},
...>       %{type: {:uint, 256}, name: "amount"},
...>     ]
...>   },
...>   indexed: true
...> )
"Transfer(address indexed,address indexed,uint256)"

iex> ABI.Event.canonical(
...>   %ABI.FunctionSelector{
...>     function: "Transfer",
...>     types: [
...>       %{type: :address, name: "from", indexed: true},
...>       %{type: :address, name: "to", indexed: true},
...>       %{type: {:uint, 256}, name: "amount"},
...>     ]
...>   },
...>   indexed: true,
...>   names: true
...> )
"Transfer(address indexed from,address indexed to,uint256 amount)"

decode_event(data, topics, function_selector, opts \\ [])

@spec decode_event(binary(), [binary()], ABI.FunctionSelector.t(), keyword()) ::
  {:ok, String.t() | nil, map()} | {:error, decode_error()}

Decodes an event, including handling parsing out data from topics.

Returns {:ok, function_name, args_map} on success, or {:error, reason} where reason is one of the variants in decode_error/0.

Examples

iex> ABI.Event.decode_event(
...>   ~h[0x00000000000000000000000000000000000000000000000000000004a817c800],
...>   [
...>     ~h[0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef],
...>     ~h[0x000000000000000000000000b2b7c1795f19fbc28fda77a95e59edbb8b3709c8],
...>     ~h[0x0000000000000000000000007795126b3ae468f44c901287de98594198ce38ea]
...>   ],
...>   %ABI.FunctionSelector{
...>     function: "Transfer",
...>     types: [
...>       %{type: :address, name: "from", indexed: true},
...>       %{type: :address, name: "to", indexed: true},
...>       %{type: {:uint, 256}, name: "amount"},
...>     ]
...>   })
{:ok,
  "Transfer", %{
    "amount" => 20000000000,
    "from" => ~h[0xb2b7c1795f19fbc28fda77a95e59edbb8b3709c8],
    "to" => ~h[0x7795126b3ae468f44c901287de98594198ce38ea]
}}

iex> ABI.Event.decode_event(
...>   ~h[0x00000000000000000000000000000000000000000000000000000004a817c800],
...>   [
...>     ~h[0x0000000000000000000000000000000000000000000000000000000000000001],
...>     ~h[0x000000000000000000000000b2b7c1795f19fbc28fda77a95e59edbb8b3709c8],
...>     ~h[0x0000000000000000000000007795126b3ae468f44c901287de98594198ce38ea]
...>   ],
...>   %ABI.FunctionSelector{
...>     function: "Transfer",
...>     types: [
...>       %{type: :address, name: "from", indexed: true},
...>       %{type: :address, name: "to", indexed: true},
...>       %{type: {:uint, 256}, name: "amount"},
...>     ]
...>   })
{:error,
  {:event_signature_mismatch,
   %{
     expected: ~h[0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef],
     got: ~h[0x0000000000000000000000000000000000000000000000000000000000000001]
   }}}

iex> ABI.Event.decode_event(
...>   ~h[0x00000000000000000000000000000000000000000000000000000004a817c800],
...>   [
...>     ~h[0x000000000000000000000000b2b7c1795f19fbc28fda77a95e59edbb8b3709c8],
...>     ~h[0x0000000000000000000000007795126b3ae468f44c901287de98594198ce38ea]
...>   ],
...>   %ABI.FunctionSelector{
...>     function: "Transfer",
...>     types: [
...>       %{type: :address, name: "from", indexed: true},
...>       %{type: :address, name: "to", indexed: true},
...>       %{type: {:uint, 256}, name: "amount"},
...>     ]
...>   })
{:error, {:topics_length_mismatch, %{got: 2, expected: 3}}}

iex> ABI.Event.decode_event(
...>   ~h[0x00000000000000000000000000000000000000000000000000000004a817c800],
...>   [
...>     ~h[0x000000000000000000000000b2b7c1795f19fbc28fda77a95e59edbb8b3709c8],
...>     ~h[0x0000000000000000000000007795126b3ae468f44c901287de98594198ce38ea]
...>   ],
...>   %ABI.FunctionSelector{
...>     function: "Transfer",
...>     types: [
...>       %{type: :address, name: "from", indexed: true},
...>       %{type: :address, name: "to", indexed: true},
...>       %{type: {:uint, 256}, name: "amount"},
...>     ]
...>   },
...>   check_event_signature: false
...> )
{:ok,
  "Transfer", %{
    "amount" => 20000000000,
    "from" => ~h[0xb2b7c1795f19fbc28fda77a95e59edbb8b3709c8],
    "to" => ~h[0x7795126b3ae468f44c901287de98594198ce38ea]
}}

When the non-indexed payload bytes are truncated or wrongly typed, the underlying decoder previously raised; the function now wraps that path and returns {:error, {:malformed_data, msg}} with a human-readable description.

event_signature(function_selector)

@spec event_signature(ABI.FunctionSelector.t()) :: binary()

Returns the signature of an event, i.e. the first item that appears in an Ethereum log for this event.

Examples

iex> ABI.Event.event_signature(
...>   %ABI.FunctionSelector{
...>     function: "Transfer",
...>     types: [
...>       %{type: :address, name: "from", indexed: true},
...>       %{type: :address, name: "to", indexed: true},
...>       %{type: {:uint, 256}, name: "amount"},
...>     ]
...>   }
...> )
...> |> to_hex()
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"