Skip to main content

Starstream WIT worlds

Each Starstream contract is described by its own bespoke WIT world, importing builtins, Utxos, and so on used by the contract and exporting Utxos and coordination scripts defined in the contract.

The shape of those imports and exports are described here and define the interface between the Starstream runtime (host) and Starstream contract (guest).

Imports from other contracts use @external-id nodes containing the digest of the other contract in a form similar to "sha256:$HEXDIGEST", followed by : and extra data if needed.

Global builtins​

package starstream:std;

/// Core builtins that the Starstream compiler depends on and that (almost) all
/// Starstream programs require.
interface builtin {
/// A generic handle to any Utxo.
resource utxo {
has-method: func(hash: tuple<u64, u64, u64, u64>) -> bool;
}

/// A generic handle to any Token.
resource token {}
}

Exporting Utxos​

Contracts which export Utxos use types from starstream:std/utxo-context:

package starstream:std;

/// Builtins needed by contracts which declare Utxos.
interface utxo-context {
/// A context through which a Utxo can tell the runtime information about
/// its own execution status.
resource utxo-context {
/// The `resume;` statement calls this to indicate the Utxo is resuming.
/// This resets the `implements-method` list. If the Utxo then returns
/// without calling `implements-method` again, the Utxo is considered
/// consumed.
resume: func();

/// The `yield(...);` statement in a Utxo `main fn` calls this to mark
/// a method hash as implemented at the current yield point.
implements-method: func(hash: tuple<u64, u64, u64, u64>);
}
}

Each Utxo declared in the contract is represented as a distinct exported interface with an arbitrary name. An exported interface represents a Utxo if it declares resource utxo. Each Utxo also has a record storage describing its memory and get-storage (save) and set-storage (load) functions.

main fns are static methods on the resource utxo, and also receive an owned utxo-context supplied by the runtime.

For example:

world root {
import starstream:std/utxo-context;

export my-utxo: interface {
resource utxo {
my-main-fn: static func(ctx: own<utxo-context>) -> utxo;
}

record storage {
my-field: s32,
}

get-storage: func(self: borrow<utxo>) -> storage;
set-storage: func(ctx: own<utxo-context>, storage: storage) -> utxo;
}
}

Importing Utxos​

Contracts which import Utxos do so by importing interfaces from starstream:utxo with the Utxo's name containing a resource utxo. If needed, the import may also include downcast and upcast methods, which the runtime will provide.

For example:

world root {
import starstream:utxo/my-utxo;
}

package starstream:utxo {
@external-id("0123456789abcdef")
interface my-utxo {
use starstream:std/builtin.{utxo as builtin-utxo};

resource utxo {
my-main-fn: static func() -> utxo;
}

downcast: func(self: borrow<builtin-utxo>) -> option<utxo>;
upcast: func(self: borrow<utxo>) -> builtin-utxo;
}
}

A contract which defines a Utxo and also uses it in its own coordination script will both export and import the Utxo interface.

Using a Utxo from a coordination script in the same contract imports starstream:self/<utxo-name>.

Importing a Utxo from another contract imports starstream:utxo/<utxo-name> and supplies @external-id on the interface with that contract's digest.

Exporting tokens​

Exporting tokens is similar to exporting Utxos, but the exported resource is named token, and there is no context resource.

Token mint fns work like UTXO main fns and have the signature static func(...) -> token.

Token burn fns have the signature static func(this: token) -> ....

Importing tokens​

Importing tokens is similar to importing Utxos, but the imported resource is named token.

Token mint fns work like UTXO main fns and have the signature static func(...) -> token.

Token burn fns have the signature static func(this: token) -> ....

Importing ABIs​

ABIs can contain events, effects, and methods.

Importing events​

To emit events, import and call starstream:events/<abi-name>#<event-name>. For example:

interface starstream:events/my-abi {
my-event: func(number: s32);
}

world root {
import starstream:events/my-abi;
}

Importing effects​

To raise effects, import and call starstream:effects/<abi-name>#<effect-name>. The effect function may have parameters and a result. For example:

interface starstream:effects/my-abi {
my-effect: func(number: s32) -> s32;
}

world root {
import starstream:effects/my-abi;
}

Exporting effect handlers​

To catch effects, export starstream:effect-handler/<abi-name>#<effect-name>. When the effect is raised, the module stack will be searched for matching handlers. The effect handler function must return one of two types:

  • If the effect has no result type, the handler returns a u8 status code.
  • If the effect has a result type T, the handler returns a tuple<u8, T>.

The status code must be one of:

  • 0 - the effect was not handled and the search for a handler should continue.
  • 1 - the effect was handled and ended in a resume, so execution should continue.
interface starstream:effect-handler/my-abi {
my-effect: func(number: s32) -> tuple<u8, s32>;
}

world root {
export starstream:effect-handler/my-abi;
}

ABI methods​

To call methods on a Utxo without knowing its concrete type, import the methods from starstream:contract/dynamic-utxo:

interface starstream:contract/dynamic-utxo {
use starstream:std/builtin.utxo;

my-method: func(this: borrow<utxo>, parameter: s32);
}

world root {
import starstream:contract/dynamic-utxo;
}

ABI casting​

To test whether methods exist before calling them, call has-method on the starstream:std/builtin#utxo resource.

Exporting coordination scripts​

Coordination scripts are top-level function exports:

world root {
export my-coordination-script: func();
}

Importing coordination scripts​

Cross-contract coordination scripts are imported from an interface named starstream:contract/scripts with all coordination script functions needed. Each function has an @external-id with the digest of the contract it belongs to. For example:

interface starstream:contract/scripts {
@external-id("0123456789abcdef")
foobar: func() -> s32;
}
world root {
import starstream:contract/scripts;
}

Cardano support​

Starstream contracts may explicitly import the cardano interface to access Cardano-specific information. Such contracts become limited to the Cardano chain. For example:

import { blockHeight } from starstream:std/cardano;

script fn myCoordinationScript() -> s64 {
runtime blockHeight()
}

The functions available are:

package starstream:std;

/// Cardano blockchain context functions.
interface cardano {
block-height: func() -> s64;
current-slot: func() -> s64;
}

Arbitrary WIT​

Starstream contracts may import an arbitrary namespace:package/interface if the WIT is available at compile time. Such contracts must be linked with an implementation in some way before being passed to a standard Starstream runtime, which will not have an implementation of those imports.