ABI.Event (ex_abi v0.7.1)

Tools for decoding event data and topics given a list of function selectors.

Summary

Functions

Finds the function selector in the ABI, and decodes the event data accordingly.

Types

Link to this type

event_value()

@type event_value() ::
  {name :: String.t(), type :: String.t(), indexed? :: boolean(),
   value :: term()}
@type topic() :: binary() | nil

Functions

Link to this function

find_and_decode(function_selectors, topic1, topic2, topic3, topic4, data)

@spec find_and_decode(
  [ABI.FunctionSelector.t()],
  topic(),
  topic(),
  topic(),
  topic(),
  binary()
) ::
  {ABI.FunctionSelector.t(), [event_value()]} | {:error, any()}

Finds the function selector in the ABI, and decodes the event data accordingly.

Ensure that you included events when parsing the ABI, via include_events?: true

You'll need to have the data separate from the topics, and pass each topic in separately. It isn't possible to properly decode the topics + data of an event without knowing the exact order of the topics, and having those topics separated from the data itself. That is why this function takes each topic (even if that topic value is nil) as a separate explicit argument.

The first topic will be the keccak 256 hash of the function signature of the event. You should not have to calculate this, it should be present as the first topic of the event.

If any of the topics are dynamic types, you will not be able to get the actual value of the string. Indexed arguments are placed in topics, and indexed dynamic types are actually indexed by their keccak 256 hash. The only way for a contract to provide that value and index the argument is to pass the same value into the event as two separate arguments, one that is indexed and one that is not. To signify this, those values are returned in a special tuple: {:dynamic, value}.

Examples:

iex> topic1 = ExKeccak.hash_256("WantsPets(string,uint256,bool)")
# first argument is indexed, so it is a topic
...> topic2 = ExKeccak.hash_256("bob")
# third argument is indexed, so it is also a topic
...> topic3 = "0000000000000000000000000000000000000000000000000000000000000001" |> Base.decode16!()
# there are only two indexed arguments, so the fourth topic is `nil`
...> topic4 = nil
# second argument is not, so it is in data
...> data = "0000000000000000000000000000000000000000000000000000000000000000" |> Base.decode16!()
...> File.read!("priv/dog.abi.json")
...> |> Jason.decode!()
...> |> ABI.parse_specification(include_events?: true)
...> |> ABI.Event.find_and_decode(topic1, topic2, topic3, topic4, data)
{%ABI.FunctionSelector{
    type: :event,
    function: "WantsPets",
    input_names: ["_from_human", "_number", "_belly"],
    inputs_indexed: [true, false, true],
    method_id: <<235, 155, 60, 76, 236, 41, 90, 133, 158, 131, 71, 199, 88, 206, 85, 83, 36, 105, 140, 112, 231, 125, 249, 63, 87, 99, 121, 242, 184, 82, 161, 19>>,
    types: [:string, {:uint, 256}, :bool]
  },
  [
    {"_from_human", "string", true, {:dynamic, <<56, 228, 122, 123, 113, 157, 206, 99, 102, 42, 234, 244, 52, 64, 50, 111, 85, 27, 138, 126, 225, 152, 206, 227, 92, 181, 213, 23, 242, 210, 150, 162>>}},
    {"_number", "uint256", false, 0},
    {"_belly", "bool", true, true}
  ]
}