Events
Events are operations that allow smart contracts to send information to off-chain applications. Smart contracts emit events and off-chain applications can listen for events to know when things happen on the chain.
Event data
An event includes data about the call to the smart contract that triggered the event, including the hash of that operation and the level of the block that included the operation.
The event can also include these optional fields:
- A tag that can identify the type of event or help clients filter the stream of events.
- A payload of data in Michelson format
- The Michelson data type of the payload
Emitting events
Each high-level language has its own way of creating events.
The compiled Michelson code uses the EMIT
command to emit the event.
For example, this SmartPy contract stores a number and emits events when that amount changes:
import smartpy as sp
@sp.module
def main():
class Events(sp.Contract):
def __init__(self, value):
self.data.storedValue = value
@sp.entrypoint
def add(self, addAmount):
sp.emit(sp.record(
source=sp.source,
addAmount=addAmount
), tag="add", with_type=True)
self.data.storedValue += addAmount
@sp.entrypoint
def reset(self):
sp.emit(sp.record(
source=sp.source,
previousValue=self.data.storedValue
), tag="reset", with_type=True)
self.data.storedValue = 0
if "templates" not in __name__:
@sp.add_test(name="Events")
def test():
c1 = main.Events(12)
scenario = sp.test_scenario(main)
scenario.h1("Add")
scenario += c1
c1.add(2).run(
sender = sp.test_account("Alice")
)
scenario.verify(c1.data.storedValue == 14)
When a client calls the reset
entrypoint, it emits an event that is tagged with "reset" and includes the address that called the entrypoint and the amount that the storage was before it was reset to 0.
Responding to events
Smart contracts cannot respond to events.
Taquito includes tools to listen for and respond to events.
For example, this code listens for events from the contract with the address contractAddress
and the tag tagName
:
const Tezos = new TezosToolkit(rpcUrl);
Tezos.setStreamProvider(
Tezos.getFactory(PollingSubscribeProvider)({
shouldObservableSubscriptionRetry: true,
pollingIntervalMilliseconds: 1500,
}),
);
try {
const sub = Tezos.stream.subscribeEvent({
tag: tagName,
address: contractAddress,
});
sub.on("data", console.log);
} catch (e) {
console.log(e);
}
Both the tag
and address
parameters are optional in the subscribeEvent
function, but most clients are interested in events from a specific contract address or tag.
The event data is in Michelson format, so an event from the reset
entrypoint of the previous example contract might look like this:
{
"opHash": "onw8EwWVnZbx2yBHhL72ECRdCPBbw7z1d5hVCJxp7vzihVELM2m",
"blockHash": "BM1avumf2rXSFYKf4JS7YJePAL3gutRJwmazvqcSAoaqVBPAmTf",
"level": 4908983,
"kind": "event",
"source": "KT1AJ6EjaJHmH6WiExCGc3PgHo3JB5hBMhEx",
"nonce": 0,
"type": {
"prim": "pair",
"args": [
{
"prim": "int",
"annots": [
"%previousValue"
]
},
{
"prim": "address",
"annots": [
"%source"
]
}
]
},
"tag": "reset",
"payload": {
"prim": "Pair",
"args": [
{
"int": "17"
},
{
"bytes": "000032041dca76bac940b478aae673e362bd15847ed8"
}
]
},
"result": {
"status": "applied",
"consumed_milligas": "100000"
}
}
Note that the address field is returned as a byte value.
To convert the bytes to an address, use the encodePubKey
function in @taquito/utils
.
Implementation details
- Michelson: Contract events
- LIGO: Events
- SmartPy:
sp.emit
- Archetype: Events
- Taquito: Contract Events