Skip to main content

Host APIs

Lyquid code runs inside the Lyquor VM's sandboxed environment, but it sometimes needs capabilities that live outside the guest program: randomness, cryptography primitives, HTTP egress, UPC, sequencing, and other node-side services. Here, the host is the Lyquor VM that runs the program, whereas the guest is the Lyquid code running inside that sandbox.

lyquor_api is the bridge the guest uses to call those host-provided services. Every host function returns LyquidResult<T>, so handle the error or call them with ?. Some host functions are wrapped by user-friendly macros; prefer those wrappers when they exist, and see Macro-Wrapped APIs for the mapping. Several APIs are called directly, including signature primitives, random generation, time, HTTP, identity lookups, and sequencer/oracle lookup helpers.

Function Category Restrictions

Many host APIs are restricted to one function category, and the restriction is enforced at runtime: calling a restricted API from the wrong category returns LyquidError::LyquorRuntime(...). This protects determinism for network functions: they cannot read the clock, draw randomness, sign with the node key, or make an HTTP request.

In the tables below, the Category column is one of:

  • instance: only available in instance functions,
  • network: only available in network functions,
  • any: allowed in both categories and deterministic.

Directly Called APIs

APISignatureCategoryDescription
signsign(msg: Bytes, cipher: Cipher) -> BytesinstanceSign a message with the node's key for the given Cipher
verifyverify(msg: Bytes, cipher: Cipher, sig: Bytes, pubkey: Bytes) -> boolanyVerify a signature (deterministic)
random_bytesrandom_bytes(length: usize) -> Vec<u8>instanceNode-local randomness
systimesystime() -> u64instanceCurrent node time as Unix milliseconds
http_requesthttp_request(request: http::Request, options: Option<http::RequestOptions>) -> http::ResponseinstancePerform an outbound HTTP(S) request
eth_contracteth_contract() -> Option<Address>anyThe EVM contract address bound to this Lyquid, if any
sequence_backend_idsequence_backend_id() -> SequenceBackendIDanyIdentifier of the active sequence backend
fetch_oracle_infofetch_oracle_info(topic: String, target: OracleTarget, full_config: bool) -> Option<OracleEpochInfo>instanceRead oracle epoch and committee info for a topic and target
versionversion() -> LyquidNumberanyCurrent network state version
chain_poschain_pos() -> ChainPosanyCurrent chain position
get_ed25519_qxyget_ed25519_qxy(pubkey: [u8; 32]) -> (U256, U256)anyDecompress an ed25519 public key to curve coordinates
get_address_by_ed25519get_address_by_ed25519(pubkey: [u8; 32]) -> Option<Address>anyResolve the bartender-registered address for an ed25519 node ID at the current chain position
get_ed25519_by_addressget_ed25519_by_address(address: Address) -> Option<NodeID>anyResolve the bartender-registered ed25519 node ID for an address at the current chain position

Cipher is Ed25519 or Secp256k1. Note the asymmetry between sign (instance only, uses the node key) and verify (deterministic, allowed in network functions).

Macro-Wrapped APIs

MacroHost functionCategoryDescription
call!inter_lyquid_callnetworkAtomic inter-Lyquid network call; aborts the slot on failure
upc!universal_procedural_callinstanceAggregation-style inter-Lyquid call across hosting nodes
submit_certified_call!submit_callinstanceSubmit a certified call to the sequence backend
trigger!triggeranySchedule a function; TriggerMode::Commit mode is network only
log!lognetworkEmit a Lyte log (EVM-event-like)
print! / println! / eprint! / eprintln!console_outputanyLyquid console output

state_set and state_get also exist in host APIs but are emitted by the generated state! code. Do NOT call them directly; use declared state variables instead.

HTTP Request Example

An instance function can fetch external data over HTTP, and decode the response body with serde_json or any other parser in Rust.

use lyquid::prelude::*;
use lyquid::http::{Method, Request, RequestOptions};

#[method::instance]
fn fetch_price(_ctx: &mut _, symbol: String) -> LyquidResult<U256> {
let req = Request {
method: Method::Get,
url: format!("https://api.example.com/ticker?symbol={symbol}"),
headers: vec![],
body: None,
};

let resp = lyquor_api::http_request(req, Some(RequestOptions { timeout_ms: Some(1000) }))?;
if resp.status != 200 {
return Err(LyquidError::LyquorRuntime(format!("HTTP {}", resp.status)));
}

// ... (parse resp.body, store into instance state, or return a value)
Ok(U256::ZERO)
}

http::Request carries method, url, headers: Vec<Header>, and body: Option<Vec<u8>>. http::Response carries status: u16, headers: Vec<Header>, and body: Vec<u8>. http_request is instance-only, so results that need to reach network state must go through a certified call; see Sequence Backend & Certified Calls.