Skip to content

WIT Reference

package chain:contract@1.0.0;

/// Host functions available to every contract. Each contract runs in an
/// isolated sandbox with no filesystem, network, clock, or randomness —
/// these imports are the only way to interact with the outside world.
interface host {

    // ── Storage ──────────────────────────────────────────────────────
    // Each contract has its own isolated key-value namespace. Writes
    // accumulate in a transactional overlay during execution and are
    // committed atomically on success or discarded entirely on failure.

    /// Read a value from this contract's storage.
    /// Returns `none` if the key has never been set.
    kv-get: func(key: string) -> option<list<u8>>;

    /// Write a value to this contract's storage.
    /// Traps if called during a read-only query.
    kv-set: func(key: string, value: list<u8>);

    /// Delete a key from this contract's storage.
    /// Traps if called during a read-only query.
    kv-delete: func(key: string);

    /// Check whether a key exists without reading its value.
    /// More efficient than `kv-get` when you only need an existence check.
    kv-has: func(key: string) -> bool;

    // ── Events ───────────────────────────────────────────────────────

    /// Emit a typed event that is included in the transaction receipt.
    /// `kind` is an application-defined tag (e.g. "transfer") and `data`
    /// is an arbitrary byte payload. Each byte of data costs fuel.
    /// Traps if called during a read-only query.
    emit-event: func(kind: string, data: list<u8>);

    // ── Identity ─────────────────────────────────────────────────────

    /// Get the immediate caller's address (32 bytes). For a user-submitted
    /// transaction this is the signer's Ed25519 public key. In a
    /// cross-contract sub-message from contract A → B, B's `get-sender`
    /// returns A's contract address. Use this for access-control checks.
    get-sender: func() -> list<u8>;

    /// Get this contract instance's own address (32 bytes). The address is
    /// deterministically derived from the creator, code ID, and
    /// instantiation salt, so it can be pre-computed off-chain.
    get-self-address: func() -> list<u8>;

    /// Get the original transaction signer (32 bytes). Unlike `get-sender`,
    /// this value never changes across sub-message depths — it always
    /// identifies the account that signed the root transaction.
    get-origin: func() -> list<u8>;

    // ── Block context ────────────────────────────────────────────────

    /// Get the height of the block currently being executed.
    get-block-height: func() -> u64;

    // ── Debugging ────────────────────────────────────────────────────

    /// Write a debug message to the node's log output. These messages are
    /// visible to node operators but are **not** stored on-chain.
    log: func(msg: string);

    // ── Cross-contract calls ─────────────────────────────────────────

    /// Controls whether (and when) the `reply` export is called after a
    /// sub-message dispatched with `dispatch` finishes executing.
    enum reply-on {
        /// Fire and forget. If the sub-message fails the entire
        /// transaction is reverted — no reply handler is called.
        never,
        /// Call `reply` only on success. Failure still reverts the
        /// entire transaction.
        success,
        /// Call `reply` only on failure. This lets the parent contract
        /// handle errors gracefully without reverting.
        error,
        /// Call `reply` on both success and failure.
        always,
    }

    /// Queue a state-changing sub-message for deferred execution. The
    /// target contract's `exec` runs **after** this contract returns,
    /// eliminating reentrancy by construction. Returns a locally-unique
    /// `msg-id` that is later passed to `reply` so you can match
    /// responses. `funds` is reserved for future use (pass 0).
    dispatch: func(target: list<u8>, msg: list<u8>, reply-on: reply-on, funds: u64) -> result<u64, string>;

    /// Synchronously call another contract's `query` export and return
    /// the result inline. Because queries are read-only, this is safe
    /// from reentrancy and does not require the dispatch/reply pattern.
    query-contract: func(target: list<u8>, msg: list<u8>) -> result<list<u8>, string>;
}

world contract-world {
    import host;

    /// Constructor — called exactly once when the contract is instantiated.
    /// `msg` contains application-defined initialization parameters.
    export init: func(msg: list<u8>) -> result<list<u8>, string>;

    /// Handle a state-changing transaction. May call `dispatch` to send
    /// sub-messages, `emit-event` to log events, and read/write storage.
    export exec: func(msg: list<u8>) -> result<list<u8>, string>;

    /// Handle a read-only query. Storage writes, event emission, and
    /// dispatch are all forbidden (they will trap). Can be called
    /// synchronously by other contracts via `query-contract`.
    export query: func(msg: list<u8>) -> result<list<u8>, string>;

    /// Derive a deterministic consensus key for a service payload. Services
    /// using the `contract-query` consensus strategy call this export on the
    /// target contract and compare the returned bytes across validators.
    /// Often this just returns `payload` unchanged.
    export service-consensus-query: func(payload: list<u8>) -> list<u8>;

    /// Called when a dispatched sub-message completes, if the `reply-on`
    /// mode matches the outcome. `msg-id` is the ID returned by
    /// `dispatch`. On success `data` contains the sub-message's return
    /// value; on failure it contains the error message.
    export reply: func(msg-id: u64, success: bool, data: list<u8>) -> result<list<u8>, string>;
}