Starknet Events

A contract may emit events throughout its execution. Each event contains the following fields:

  • from_address: address of the contract emitting the events

  • keys: a list of field elements

  • data: a list of field elements

The keys can be used for indexing the events, while the data may contain any information that we wish to log (note that we are dealing with two separate lists of possibly varying size, rather than a list of key-value pairs).

Emitting events

Events can be defined in a contract using the @event decorator. Once an event E has been defined, the compiler automatically adds the function E.emit(). The following example illustrates how an event is defined and emitted:

  • Cairo 0

  • Cairo 1.0

@event
func message_received(a : felt, b: felt):
end
#[event]
fn Transfer(from: ContractAddress, to: ContractAddress, value: u256) {}
  • Cairo 0

  • Cairo 1.0

message_received.emit(1, 2);
Transfer(12345, 12345, 1)

The emit function emits an event with a single key, which is an identifier of the event, given by \(\text{sn_keccak(event_name)}\), where \(\text{event_name}\) is the ASCII encoding of the event’s name and \(\text{sn_keccak}\) is defined here.

To emit custom keys, one should use the low level emit_event system call:

  • Cairo 0

  • Cairo 1.0

from starkware.starknet.common.syscalls import emit_event

# ...

let (keys : felt*) = alloc()
assert keys[0] = 'status'
assert keys[1] = 'deposit'
let (data : felt*) = alloc()
assert data[0] = 1
assert data[1] = 2
assert data[2] = 3
emit_event(2, keys, 3, data)
use starknet::syscalls::emit_event_syscall;

let keys = ArrayTrait::new();
keys.append('key');
keys.append('deposit');
let values = ArrayTrait::new();
values.append(1);
values.append(2);
values.append(3);
emit_event_syscall(keys, values).unwrap_syscall();

The above code emits an event with two keys, the strings "status" and "deposit" (think of those as identifiers of the event that can be used for indexing) and three data elments 1, 2, 3.

When using the higher level emit syntax, the event’s data may be of complex types, for example:

struct Point:
    member x : felt
    member y : felt
end

@event
func message_received(arr_len : felt, arr: felt*, p: Point):
end

# ...

let (data : felt*) = alloc()
assert data[0] = 1
assert data[1] = 2
let p = Point(3,4)
message_received.emit(2, data, p)

The emitted events are part of the transaction receipt.

Event ABI

The event definition appears in the contract’s ABI. It contains the list of data fields (name and type) and the list of the custom keys (that is, all keys except the event identifier discussed above). Below is an example of an event inside the ABI:

  • Cairo 0

  • Cairo 1.0

{
  "data": [
    {
      "name": "a",
      "type": "felt"
    },
    {
      "name": "b",
      "type": "felt"
    }
  ],
  "keys": [],
  "name": "message_received",
  "type": "event"
}
{
  "type": "event",
  "name": "Transfer",
  "inputs": [
    {
      "name": "from",
      "type": "core::starknet::contract_address::ContractAddress"
    },
    {
      "name": "to",
      "type": "core::starknet::contract_address::ContractAddress"
    },
    {
      "name": "value",
      "type": "core::integer::u256"
    }
  ]
}

Event hash

The event hash is given by:

\[h(h(h(h(0,\text{from_address}),\text{keys_hash}),\text{data_hash}),3)\]

Where:

  • \(\text{keys_hash}\), \(\text{data_hash}\) are the hashes of the keys list and data list correspondingly (see array hashing).

  • \(h\) is the pedersen hash function

The event hashes are included in the event_commitment field of a block.