# `ABI.Event`
[🔗](https://github.com/ZenHive/hieroglyph/blob/main/lib/abi/event.ex#L1)

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
| Function | Arity | Description | Param Kinds |
| --- | --- | --- | --- |
| `canonical` | 2 | Render the canonical signature string of an event for hashing or display, optionally including indexed and parameter-name annotations. | `function_selector: value`, `opts: value` |
| `event_signature` | 1 | Compute the keccak-256 hash of the event's canonical signature, used as topics[0] in event logs. | `function_selector: value` |
| `decode_event` | 4 | Decode 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` |

# `decode_error`

```elixir
@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_mismatch` — `topics[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`).

# `canonical`

```elixir
@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`

```elixir
@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 `t: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`

```elixir
@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"

---

*Consult [api-reference.md](api-reference.md) for complete listing*
