Skip to main content

Events and Errors

Events

Declare events using SkittlesEvent<T> as a class property:

import { address, SkittlesEvent, Indexed } from "skittles";

class Token {
Transfer: SkittlesEvent<{
from: Indexed<address>;
to: Indexed<address>;
value: number;
}>;
}
Generated Solidity
event Transfer(address indexed from, address indexed to, uint256 value);

Indexed Parameters

Wrap parameter types with Indexed<T> to mark them as indexed in the event. Indexed parameters are stored in the event's topics and can be filtered by off chain indexers. Up to 3 parameters per event can be indexed.

Transfer: SkittlesEvent<{
from: Indexed<address>; // indexed
to: Indexed<address>; // indexed
value: number; // not indexed (stored in data)
}>;

Emitting Events

Emit events using this.EventName.emit(...). There are two syntaxes:

Object literal syntax:

this.Transfer.emit({ from: msg.sender, to, value: amount });

Positional argument syntax:

this.Transfer.emit(msg.sender, to, amount);

Both produce the same Solidity:

emit Transfer(msg.sender, to, amount);

Event Alias

You can also use Event as a shorter alias for SkittlesEvent:

import { Event } from "skittles";

class Token {
Transfer: Event<{ from: address; to: address; value: number }>;
}

Custom Errors

Custom errors provide gas efficient revert reasons. There are two ways to declare them.

SkittlesError Property

Declare errors as class properties using SkittlesError<T>:

import { address, SkittlesError } from "skittles";

class Token {
InsufficientBalance: SkittlesError<{
sender: address;
balance: number;
required: number;
}>;

transfer(to: address, amount: number): void {
if (this.balances[msg.sender] < amount) {
throw this.InsufficientBalance(
msg.sender,
this.balances[msg.sender],
amount,
);
}
// ...
}
}
Generated Solidity
error InsufficientBalance(address sender, uint256 balance, uint256 required);

function transfer(address to, uint256 amount) public virtual {
if (balances[msg.sender] < amount) {
revert InsufficientBalance(msg.sender, balances[msg.sender], amount);
}
// ...
}

Throw custom errors with throw this.ErrorName(args...).

Error Class Pattern

Alternatively, declare errors as classes that extend Error:

class InsufficientBalance extends Error {
constructor(sender: address, balance: number, required: number) {}
}

class Token {
transfer(to: address, amount: number): void {
if (this.balances[msg.sender] < amount) {
throw new InsufficientBalance(
msg.sender,
this.balances[msg.sender],
amount,
);
}
}
}

Both approaches generate the same Solidity error declaration and revert statement.

Simple Reverts

For simple string reverts, use throw new Error("message"):

if (amount == 0) {
throw new Error("Amount must be greater than zero");
}

When this is the only statement in an if block with no else, it is automatically optimized to require():

require(amount != 0, "Amount must be greater than zero");

A bare throw without arguments generates revert().