Sigil supports smart contracts inside organisation zones using the WebAssembly Component Model under WASI. Contracts are deterministic, gas-metered, and storage-isolated.

Languages

Any language that compiles to a WASI Component can target Sigil. The first-party SDK is Rust; community SDKs exist for AssemblyScript and TinyGo.
LanguageSDKNotes
Rustsigil-contract-sdkReference implementation. Procedural macros generate the ABI.
AssemblyScript@sigil/contract-asCommunity-maintained.
TinyGosigil-contract-goCommunity-maintained.

Contract structure

use sigil_contract_sdk::{contract, storage, Caller, Result};

#[contract]
pub mod token {
    use super::*;

    pub fn balance_of(_ctx: &Caller, who: String) -> Result<u128> {
        Ok(storage::get(&format!("bal/{who}")).unwrap_or(0))
    }

    pub fn transfer(ctx: &Caller, to: String, amount: u128) -> Result<()> {
        let from = ctx.caller_did();
        let from_bal: u128 = storage::get(&format!("bal/{from}")).unwrap_or(0);
        let from_bal = from_bal.checked_sub(amount).ok_or("insufficient")?;
        storage::set(&format!("bal/{from}"), &from_bal)?;

        let to_bal: u128 = storage::get(&format!("bal/{to}")).unwrap_or(0);
        let to_bal = to_bal.checked_add(amount).ok_or("overflow")?;
        storage::set(&format!("bal/{to}"), &to_bal)?;
        Ok(())
    }
}
The #[contract] macro exposes the public functions as ABI methods, generates the JCS argument codecs, and registers the contract in the zone’s namespace.

Lifecycle

PhaseDescription
BuildCompile to wasm32-wasi with --release. Output must be ≤ 2 MiB.
DeploySubmit DeployContract with the WASM bytes. The chain pins by BLAKE3 hash.
CallSubmit CallContract with the contract id, method, args, and gas budget.
ReadUse sigil_callContract for read-only views; no gas, no state change.
UpgradeDeploy a new version under a new contract id. The old id stays addressable.
There is no admin upgrade path. Contracts are immutable once deployed. To migrate users, deploy v2 and let users move state explicitly.

Gas

Wasmtime gas is metered per instruction plus per syscall:
OperationCost
Bytecode instruction1 gas
Storage read100 gas
Storage write (32 bytes)1,000 gas
Inter-contract call1,000 gas + callee cost
Cryptographic primitive (BLAKE3, Ed25519 verify)200 gas
JCS serialise / deserialise50 gas per byte
The per-block gas limit is 100,000,000. Per-call gas budget is set by the caller; the executor refunds unused gas. See programmability for the full table.

State isolation

A contract has read and write access only to its own state partition. Cross-contract calls go through the explicit call interface and consume their own gas budget. There is no shared memory. State is stored under the contract id in the zone’s AkashaKV partition. Keys are arbitrary bytes; values are JCS-encoded.

Determinism

Wasmtime is configured with non-deterministic features disabled:
  • No floating-point operations that vary across CPUs.
  • No threads, no SIMD that depends on runtime feature detection.
  • No NaN payload propagation.
  • No wall-clock reads.
The randomness syscall returns the on-chain randomness beacon for the current block. The clock syscall returns the block timestamp.

Limits

LimitValue
Max contract size2 MiB
Max stack depth512 frames
Max storage writes per call1,024
Max inter-contract call depth16
Max bytes returned to caller32 KiB

Implementation

  • Contract executor: node/sigil-contract/src/executor.rs.
  • Zone integration: node/sigil-zone/src/contract_namespace.rs.
  • Rust SDK: sdks/rust/sigil-contract-sdk/.

See also