Trigger Events
Smart contracts operate in a confined environment but need a mechanism to interact with users. A solution to this is triggering events, which is facilitated through smart contracts.
ISC Sandbox Interface
The ScFuncContext
Call Context in ISC SandboxA controlled environment where programs can be tested safely. has an event()
function to support event triggering.
This function takes a text string parameter, requiring creators and users to maintain and understand the chosen format.
However, this setup is prone to errors and inconsistency due to the arbitrary nature of the text strings used.
Structured Events with Schema Tool
To mitigate issues stemming from the rudimentary interface, use the Schema Tool to define structured events, making event creation and handling more consistent and less error-prone.
This tool allows you to establish structured events that are integrated into all Func function contexts. Note that events:
- Can only be triggered within a Func
- Become part of the smart contract's state
- Are logged in the core
blocklog
contract - Cannot be triggered within a View
Event Structure
Define each event in the events
section of the schema definition file.
This setup ensures that events are encoded consistently,
utilizing a function that automatically formats the event string with the event name, timestamp, and parameter fields,
delimited by vertical bars.
This structured approach facilitates streamlined, error-resistant event triggering.
Example
Here is the events
section that can be found in the demo fairroulette
smart contract:
- YAML
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:
- Go
- Rust
- Typescript
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()
}
use wasmlib::*;
pub struct FairRouletteEvents {
}
impl FairRouletteEvents {
pub fn bet(&self,
// address of better
address: &ScAddress,
// amount of tokens to bet
amount: u64,
// number to bet on
number: u16,
) {
let mut evt = EventEncoder::new("fairroulette.bet");
evt.encode(&address_to_string(&address));
evt.encode(&uint64_to_string(amount));
evt.encode(&uint16_to_string(number));
evt.emit();
}
pub fn payout(&self,
// address of winner
address: &ScAddress,
// amount of tokens won
amount: u64,
) {
let mut evt = EventEncoder::new("fairroulette.payout");
evt.encode(&address_to_string(&address));
evt.encode(&uint64_to_string(amount));
evt.emit();
}
pub fn round(&self,
// current betting round number
number: u32,
) {
let mut evt = EventEncoder::new("fairroulette.round");
evt.encode(&uint32_to_string(number));
evt.emit();
}
pub fn start(&self) {
let mut evt = EventEncoder::new("fairroulette.start");
evt.emit();
}
pub fn stop(&self) {
let mut evt = EventEncoder::new("fairroulette.stop");
evt.emit();
}
pub fn winner(&self,
// the winning number
number: u16,
) {
let mut evt = EventEncoder::new("fairroulette.winner");
evt.encode(&uint16_to_string(number));
evt.emit();
}
}
import * as wasmlib from 'wasmlib';
import * as wasmtypes from 'wasmlib/wasmtypes';
export class FairRouletteEvents {
bet(
// address of better
address: wasmtypes.ScAddress,
// amount of tokens to bet
amount: u64,
// number to bet on
number: u16,
): void {
const evt = new wasmlib.EventEncoder('fairroulette.bet');
evt.encode(wasmtypes.addressToString(address));
evt.encode(wasmtypes.uint64ToString(amount));
evt.encode(wasmtypes.uint16ToString(number));
evt.emit();
}
payout(
// address of winner
address: wasmtypes.ScAddress,
// amount of tokens won
amount: u64,
): void {
const evt = new wasmlib.EventEncoder('fairroulette.payout');
evt.encode(wasmtypes.addressToString(address));
evt.encode(wasmtypes.uint64ToString(amount));
evt.emit();
}
round(
// current betting round number
number: u32,
): void {
const evt = new wasmlib.EventEncoder('fairroulette.round');
evt.encode(wasmtypes.uint32ToString(number));
evt.emit();
}
start(): void {
const evt = new wasmlib.EventEncoder('fairroulette.start');
evt.emit();
}
stop(): void {
const evt = new wasmlib.EventEncoder('fairroulette.stop');
evt.emit();
}
winner(
// the winning number
number: u16,
): void {
const evt = new wasmlib.EventEncoder('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:
- Go
- Rust
- Typescript
f.Events.Bet(bet.Better.Address(), bet.Amount, bet.Number)
f.events.bet(&bet.better.address(), bet.amount, bet.number);
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.