Skip to main content

Triggering Events

Smart contracts do not live in a vacuum. Even though they run in a very limited sandbox, from a larger perspective there will have to be a way for users to interact with them. Since smart contracts are essentially event-driven, and requests run asynchronously from the user's perspective, there is a need for triggering events by the smart contracts themselves. Of course, it would be possible for users to periodically call a view function to retrieve the latest state of the smart contract, but this burdens the nodes unnecessarily. A better way is to have the smart contracts trigger events that the user can subscribe to and that convey changes to its state.

To support events the ISC sandbox provides only a very rudimentary interface. The function call context exposes this interface through its event() function, which takes a completely arbitrary text string as parameter. It is up to the smart contract creator to format this text string in a meaningful way, and it's up to the user to interpret this text string correctly. This is error-prone, inconsistent, and means that a lot of code needs to be written both on the smart contract side that generates these events, and on the client side that handles these events. And with any change to the formatting of these events both ends need to be modified to stay in sync.

This is why the schema tool allows you to define your own structured events. The schema tool will generate a structure that will become part of all Func call contexts. Events can only be triggered from within a func. They will become part of the state of the smart contract because every event is logged in the core blocklog contract. Therefore, events cannot be triggered from within a View.

For each event defined in the events section of the schema definition file, this events structure will contain a member function that takes the defined types of parameters and will automatically encode the event as a consistently formatted string and pass it to the event() function. The string consists of the name of the event, a timestamp, and string representations of each field, all separated by vertical bars.

Here is the events section that can be found in the demo fairroulette smart contract:

events:
bet:
address: Address # address of better
amount: Uint64 # amount of tokens to bet
number: Uint16 # number to bet on
payout:
address: Address # address of winner
amount: Uint64 # amount of tokens won
round:
number: Uint32 # current betting round number
start:
stop:
winner:
number: Uint16 # the winning number

The schema tool will generate events.xx which contains the following code for the FairRouletteEvents struct:

package fairroulette

import "github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib"
import "github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib/wasmtypes"

type FairRouletteEvents struct {
}

func (e FairRouletteEvents) Bet(
// address of better
address wasmtypes.ScAddress,
// amount of tokens to bet
amount uint64,
// number to bet on
number uint16,
) {
evt := wasmlib.NewEventEncoder("fairroulette.bet")
evt.Encode(wasmtypes.AddressToString(address))
evt.Encode(wasmtypes.Uint64ToString(amount))
evt.Encode(wasmtypes.Uint16ToString(number))
evt.Emit()
}

func (e FairRouletteEvents) Payout(
// address of winner
address wasmtypes.ScAddress,
// amount of tokens won
amount uint64,
) {
evt := wasmlib.NewEventEncoder("fairroulette.payout")
evt.Encode(wasmtypes.AddressToString(address))
evt.Encode(wasmtypes.Uint64ToString(amount))
evt.Emit()
}

func (e FairRouletteEvents) Round(
// current betting round number
number uint32,
) {
evt := wasmlib.NewEventEncoder("fairroulette.round")
evt.Encode(wasmtypes.Uint32ToString(number))
evt.Emit()
}

func (e FairRouletteEvents) Start() {
evt := wasmlib.NewEventEncoder("fairroulette.start")
evt.Emit()
}

func (e FairRouletteEvents) Stop() {
evt := wasmlib.NewEventEncoder("fairroulette.stop")
evt.Emit()
}

func (e FairRouletteEvents) Winner(
// the winning number
number uint16,
) {
evt := wasmlib.NewEventEncoder("fairroulette.winner")
evt.Encode(wasmtypes.Uint16ToString(number))
evt.Emit()
}

Notice how the generated functions use the WasmLib EventEncoder to encode the parameters into a single string before emitting it. Here is the way in which fairroulette emits the bet event in its smart contract code:

    f.Events.Bet(bet.Better.Address(), bet.Amount, bet.Number)

The smart contract client code can define handler functions to listen in to the event stream and respond to any events it deems noteworthy. The schema tool will automatically generate the necessary client side code that properly listens for the events, parses the event strings into a type-safe structure, and passes this structure to the corresponding handler function.

In the next section we will explore how the schema tool helps to simplify smart contract function definitions.