Skip to main content
Full working code: The complete source code for this example is available in example-contracts/hello-world/
This example walks you through creating your first Sigil contract—a simple “Hello World” that demonstrates the fundamental structure and workflow.

Project Structure

hello-world/
├── Cargo.toml          # Workspace configuration
├── contract/
│   ├── Cargo.toml      # Contract dependencies
│   ├── wit/
│   │   └── contract.wit
│   └── src/
│       └── lib.rs
└── test/
    ├── Cargo.toml      # Test dependencies
    └── src/
        └── lib.rs

Step 1: Workspace Configuration

Root Cargo.toml:
[workspace]
members = ["contract", "test"]
resolver = "2"

[workspace.dependencies]
stdlib = { path = "../../Kontor/core/stdlib" }
testlib = { path = "../../Kontor/core/testlib" }
The paths shown assume the Documentation and Kontor repos are siblings. If you cloned them to different locations, adjust these paths to point to your Kontor installation. See Getting Started for setup details.

Step 2: Contract Configuration

contract/Cargo.toml:
[package]
name = "hello-world"
version = "0.1.0"
edition = "2024"

[dependencies]
stdlib = { workspace = true }

[lib]
crate-type = ["cdylib"]  # Required for WASM compilation
Key points:
  • crate-type = ["cdylib"] - Builds a dynamic library for WASM
  • stdlib provides the contract! macro and runtime primitives

Step 3: Define the Contract Interface (WIT)

Create contract/wit/contract.wit:
package root:component;

world root {
    include kontor:built-in/built-in;
    use kontor:built-in/context.{view-context, proc-context};

    export init: func(ctx: borrow<proc-context>);

    export hello-world: func(ctx: borrow<view-context>) -> string;
}
What this defines:
  • init - Called once when contract is deployed
  • hello-world - Returns a greeting string (read-only)
The kontor:built-in types are available via a symlink at wit/deps/built-in.wit.

Step 4: Implement the Contract

Create contract/src/lib.rs:
contract/src/lib.rs
use stdlib::*;

contract!(name = "hello-world");

impl Guest for HelloWorld {
    fn init(_ctx: &ProcContext) {}

    fn hello_world(_ctx: &ViewContext) -> String {
        "Hello, World!".to_string()
    }
}
How it works:
  • contract!(name = "hello-world") - Generates the Guest trait and WASM bindings
  • impl Guest for HelloWorld - The contract name is converted to PascalCase
  • init() - Empty because we don’t need storage
  • hello_world() - Returns a static string

Step 5: Build and Test

The contract is automatically built when you run tests. The test directory’s build.rs handles compilation, optimization, and compression:
cd test
cargo test
The build.rs automatically:
  1. Compiles the contract to WASM
  2. Optimizes with wasm-opt -Oz --enable-bulk-memory --enable-sign-ext
  3. Compresses with brotli to create .wasm.br
To build manually without running tests:
cd contract
cargo build --release --target wasm32-unknown-unknown
wasm-opt -Oz --enable-bulk-memory --enable-sign-ext \
  target/wasm32-unknown-unknown/release/hello_world.wasm \
  -o hello_world_opt.wasm
brotli -Zf hello_world_opt.wasm -o hello_world.wasm.br

Step 6: Write Tests

Create test/src/lib.rs:
test/src/lib.rs
#[cfg(test)]
mod tests {
    use testlib::*;

    interface!(name = "hello-world", path = "contract/wit");

    #[testlib::test]
    async fn test_contract() -> Result<()> {
        let alice = runtime.identity().await?;
        let hello_world = runtime.publish(&alice, "hello-world").await?;
        let result = hello_world::hello_world(runtime, &hello_world).await?;
        assert_eq!(result, "Hello, World!");
        Ok(())
    }
}
What this does:
  • interface! - Generates type-safe bindings from the WIT file
  • #[testlib::test] - Auto-injects the runtime variable
  • runtime.identity() - Creates a test user with gas funding
  • runtime.publish() - Deploys the contract and returns its address
  • hello_world::hello_world() - Calls the contract function
Run tests:
cd test
cargo test

Common First-Time Issues

contract! macro not found
error: cannot find macro `contract` in this scope
Solution: Add use stdlib::*; at the top of your file Missing Guest trait implementation
error: not all trait items implemented, missing: `hello_world`
Solution: Ensure you’ve implemented all functions listed in your WIT file. Function names with hyphens in WIT become underscores in Rust. WIT file parse error
error: expected `include`, `use`, `import`, `export`, or `type`
Solution: Ensure you have include kontor:built-in/built-in; in your WIT world Build target not installed
error: can't find crate for `core`
Solution: Install the WASM target: rustup target add wasm32-unknown-unknown