Skip to main content

✍️ Writing the Code

If you just want to try out the code immediately, jump to Build and Deploy.

Defining State

Lyquid explicitly separates on-chain ("network") and off-chain ("instance") state.

use lyquid::prelude::*;

state! {
network greeting: String = String::new();
network greet_count: u64 = 0;
// Off-chain state
instance per_user_count: HashMap<Address, u64> = new_hashmap();
}
  • network greeting and greet_count closely match Solidity's public storage variables.
  • instance per_user_count introduces a completely new capability: node-local persistent state, which Solidity does not support.

A Simple, Familiar Constructor

Just like Solidity, our Lyquid constructor initializes global state:

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

This closely mirrors Solidity’s constructor:

constructor(string memory _greeting) {
greeting = _greeting;
}

Updating the Greeting (set_greeting)

Next, we implement a simple function to update our greeting message:

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

Solidity equivalent:

function setGreeting(string calldata _greeting) external {
greeting = _greeting;
}

Both versions are concise. Lyquid’s explicit state model clearly indicates that this function modifies global on-chain state (network), enhancing clarity and correctness.

Tracking Greetings (greet)

Every greeting increments a global counter:

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

Solidity equivalent:

function greet() external {
greetCount += 1;
}

Again, the logic aligns directly, but Lyquid explicitly scopes this change to global state, reinforcing clear ownership and intention.

Efficiently Formatting Messages (get_greeting_message)

The Lyquid version simplifies formatted message generation:

// This network function only reads network state (`&ctx`), so it's like a "view" function in
// Solidity. You can also write `instance fn` instead, but since we don't use any instance
// state here it's good to be conservative, so you can't touch upon instance state accidentally.
#[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
))
}

Solidity’s version is verbose, requiring external libraries (OpenZeppelin) or manual conversions:

function getGreetingMessage() external view returns (string memory) {
return string(
abi.encodePacked(
greeting,
"I've greeted",
greetCount.toString(),
" times to on-chain users"
)
);
}

Lyquid leverages Rust’s native format! macro, providing concise, efficient, and highly readable code.

Extending Solidity with Off-Chain Power (greet_me)

Now comes the most exciting feature unique to Lyquid. We'd like to also keep some per-user, off-chain state at each node so the computation knows how many times the user calls this method to the node.

// The off-chain computation below CANNOT be done by Solidity/EVM.
#[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
))
}

Solidity Equivalent: Not Possible!

This function shows Lyquid's off-chain power: node-local, persistent state per user. In this example it's used for personalized interactions (per-user counters) and off-chain analytics. But with off-chain network requests and also UPC, one can build any distributed system, and enjoy the on-chain state management to programmably control the nodes' off-chain behavior seamlessly.

Lyquid keeps the simple, readable style of Solidity, but adds off-chain power, enabling richer and more efficient decentralized services, without extra complexity.

You write less, clearly see state management, and achieve far more.