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, typicallysrc/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:
| Concept | Network functions | Instance functions |
|---|---|---|
| Execution | Deterministic and sequenced; the same state transition runs on every node | Node-local; results may differ by node |
| State access | Read/write network state | Read network state, read/write instance state |
| Persistence | Versioned by LyquidNumber | Persistent but not versioned; values may differ by node |
| Entry points | Sequence backend: replicated logs, chains, etc. | External, web, local, UPC, or trigger events |
| Nondeterminism | Disallowed | Allowed |
| Closest analogy | Smart-contract storage and methods | Off-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.
- State Model
- Functions
- Groups and UPC
- Host APIs
- External Access
- Sequence Backend & Certified Calls
- Memory and Persistence
- Programming Guidelines
- Build, Deploy, and Test (WIP)
For generated API details, see the LDK Rustdoc.