ARC-28: Structured event logging¶
ARC-28 provides a methodology for structured logging by Algorand smart contracts. It introduces the concept of Events, where data contained in logs may be categorized and structured.
Each Event is identified by a unique 4-byte identifier derived from its Event Signature
. The Event Signature is a UTF-8 string comprised of the event’s name, followed by the names of the ARC-4 data types contained in the event, all enclosed in parentheses (EventName(type1,type2,...)
) e.g.:
Swapped(uint64,uint64)
Events are emitting by including them in the log output. The metadata that identifies the event should then be included in the ARC-4 contract output so that a calling client can parse the logs to parse the structured data out. This part of the ARC-28 spec isn’t yet implemented in Algorand Python, but it’s on the roadmap.
Emitting Events¶
To emit an ARC-28 event in Algorand Python you can use the emit
function, which appears in the algopy.arc4
namespace for convenience since it heavily uses ARC-4 types and is essentially an extension of the ARC-4 specification. This function takes care of encoding the event payload to conform to the ARC-28 specification and there are 3 overloads:
An ARC-4 struct, from what the name of the struct will be used as a the event name and the struct parameters will be used as the event fields -
arc4.emit(Swapped(a, b))
An event signature as a string literal (or module variable), followed by the values -
arc4.emit("Swapped(uint64,uint64)", a, b)
An event name as a string literal (or module variable), followed by the values -
arc4.emit("Swapped", a, b)
Here’s an example contract that emits events:
from algopy import ARC4Contract, arc4
class Swapped(arc4.Struct):
a: arc4.UInt64
b: arc4.UInt64
class EventEmitter(ARC4Contract):
@arc4.abimethod
def emit_swapped(self, a: arc4.UInt64, b: arc4.UInt64) -> None:
arc4.emit(Swapped(b, a))
arc4.emit("Swapped(uint64,uint64)", b, a)
arc4.emit("Swapped", b, a)
It’s worth noting that the ARC-28 event signature needs to be known at compile time so the event name can’t be a dynamic type and must be a static string literal or string module constant. If you want to emit dynamic events you can do so using the log
method, but you’d need to manually construct the correct series of bytes and the compiler won’t be able to emit the ARC-28 metadata so you’ll need to also manually parse the logs in your client.
Examples of manually constructing an event:
# This is essentially what the `emit` method is doing, noting that a,b need to be encoded
# as a tuple so below (simple concat) only works for static ARC-4 types
log(arc4.arc4_signature("Swapped(uint64,uint64)"), a, b)
# or, if you wanted it to be truly dynamic for some reason,
# (noting this has a non-trivial opcode cost) and assuming in this case
# that `event_suffix` is already defined as a `String`:
event_name = String("Event") + event_suffix
event_selector = op.sha512_256((event_name + "(uint64)").bytes)[:4]
log(event_selector, UInt64(6))