Skip to main content

Lyquid Development Kit (LDK)

Lyquor helps developers craft, test, and ship plug-and-play standalone apps and services. The basic unit is called a Lyquid.

Depending on your background, you may understand a Lyquid as:

  • A lightweight, Docker-like app or service image with dedicated network and node-local state, ready to run across Lyquor nodes.
  • A shareable, distributable workflow: code defines control flow, tool calls, and state transitions, while prompts, skills, and model calls are packaged inside the runnable Lyquid.
  • A future form of smart-contract logic without typical smart-contract runtime limits: it can handle on-chain and off-chain transactions and events while running at native speed on the host.

Programming and Data Model

Lyquid code is designed to stay as close to "vanilla" Rust as possible. There is no special DSL to learn: you write simple Rust for your app logic, without learning a separate schema language or framework object model. If Rust's learning curve usually feels steep, Lyquid keeps the app surface small: declare state, write top-level functions, and let the LDK wire them into the runtime.

With shaker, your Lyquid is compiled to WebAssembly (WASM) with Lyquid code generation, packaged as an OCI bundle, and deployed on Lyquor. Most Lyquid code revolves around two primitives:

  • state! defines persistent state variables in your crate root, typically src/lib.rs. It separates:

    • Network state: global, consensus-driven state shared by all nodes hosting the same Lyquid deployment.
    • Instance state: local, node-specific state for private or off-chain data.
  • #[method::*] attribute macros mark top-level functions as callable Lyquid methods. They support:

    • Network functions: deterministic functions that can modify network state and are executed consistently across hosting nodes.
    • Instance functions: node-local functions that can read network state and modify instance state.
    • UPC functions: specialized instance functions for aggregation-style remote procedure calls.

The key split is between network and instance function behavior:

ConceptNetwork functionsInstance functions
ExecutionDeterministic and sequenced; the same state transition runs on every nodeNode-local; results may differ by node
State accessRead/write network stateRead network state, read/write instance state
PersistenceVersioned by LyquidNumberPersistent but not versioned; values may differ by node
Entry pointsSequence backend: replicated logs, chains, etc.External, web, local, UPC, or trigger events
NondeterminismDisallowedAllowed
Closest analogySmart-contract storage and methodsOff-chain worker, bot, or service logic

Use network state and methods for facts, configuration, and metadata that all hosting nodes must share. Use instance state and methods for local computation, caching, external I/O, custom protocols, and work that may diverge by node.

A Lyquid project looks and feels like a Rust WASM project. The provided macros abstract away low-level storage and execution details. When built, it is packaged into an app or service that Lyquor nodes can deploy and run.

The LDK is open source at github.com/lyquor-labs/ldk and published as the lyquid crate. Enable the ldk feature when using it in Lyquid dependencies. See Quick Start to install it.

For example, a basic "Hello World" Lyquid might look like this:

use lyquid::prelude::*;

state! {
network greeting: String = String::new();
network greet_count: u64 = 0;
// Off-chain (instance) state: maintained locally on each node.
instance per_user_count: HashMap<Address, u64> = new_hashmap();
}

// Constructor: invoked atomically during deployment.
#[method::network(export = eth)]
fn constructor(ctx: &mut _, greeting: String) {
*ctx.network.greeting = greeting;
}

// Network function: updates the global greeting.
#[method::network(export = eth)]
fn set_greeting(ctx: &mut _, greeting: String) -> LyquidResult<bool> {
*ctx.network.greeting = greeting;
Ok(true)
}

// Network function: increments the global greeting counter.
#[method::network(export = eth)]
fn greet(ctx: &mut _) -> LyquidResult<bool> {
*ctx.network.greet_count += 1;
Ok(true)
}

// Network (view) function: returns a formatted greeting message.
#[method::network(export = eth)]
fn get_greeting_message(ctx: &_) -> LyquidResult<String> {
Ok(format!(
"{} I've greeted {} times to on-chain users",
ctx.network.greeting, ctx.network.greet_count
))
}

// Instance function: demonstrates off-chain state by tracking per-user counts.
#[method::instance(export = eth)]
fn greet_me(ctx: &mut _) -> LyquidResult<String> {
let mut per_user_count = ctx.instance.per_user_count.write();
let user = per_user_count.entry(ctx.caller).or_default();
*user += 1;
Ok(format!(
"{} I've greeted {} times to on-chain users, and {} times to you",
ctx.network.greeting, ctx.network.greet_count, *user
))
}

This is the code from the tutorial. The macros provide the execution context and state access, so the application logic stays direct.

Project Anatomy

A Lyquid crate is a Rust library crate. The bundled examples use this shape:

[package]
name = "hello"
version = "0.0.1"
edition = "2024"

[dependencies]
lyquid = { version = "0.1", features = ["ldk"] }

[lib]
crate-type = ["cdylib", "rlib"]

[profile.release]
strip = "debuginfo"
debug = false

[workspace]

The project template gives you the correct layout:

cargo generate --git https://github.com/lyquor-labs/ldk.git lyquid-template --name <lyquid_name>

A standard src/lib.rs contains:

  • use lyquid::prelude::*; at the beginning,
  • Exactly one state! block at crate root,
  • Some top-level #[method::network] and #[method::instance] functions,
  • Other ordinary Rust helper functions, structs, enums, and modules.

The attribute-macro-tagged functions must be module-level functions. Do not put Lyquid functions inside impl blocks or traits.

This reference covers the shape of Lyquid code, state, execution, external interfaces, packaging, testing, and operational constraints. It assumes Rust familiarity. For a step-by-step onboarding flow, use the tutorial.

For generated API details, see the LDK Rustdoc.