Introduction
Note: This document is a work in progress. Its structure is not finalized, and parts of the content may still change.
Fluent is a blended execution network: an Ethereum L2 and framework that blends Wasm, EVM, and (roadmap) SVM-style smart-contract execution into one shared execution environment.
Smart contracts from different VM targets can directly call each other on Fluent. Fluent is in public devnet and currently supports apps composed of Solidity, Vyper, and Rust contracts.
Reading this book safely
This tech-book combines architecture intent and implementation examples. When you need exact current behavior, always cross-check with:
fluentlabs-xyz/fluentbasedocs,- the active
fluentlabs-xyz/rethpatched release branch.
Glossary
IR (Intermediary Representation) — An intermediary representation of some bytecode, that is usually used to represent and optimized and execution-ready application state.
ZK (Zero Knowledge) — A cryptographic method where one party (the prover) can prove to another party (the verifier) that they know a value without conveying any information apart from the fact that they know the value.
STF (State Transition Function) — A function used to ensure the correctness of a system’s state transition.
ISA (Instruction Set Architecture) — The part of the computer architecture related to programming, which includes the instruction set, word size, memory address modes, processor registers, and address and data formats.
AOT (Ahead Of Time) — A type of compilation that converts high-level code to a lower-level code or machine code before execution.
JIT (Just In Time) — A type of compilation that converts high-level code to intermediate or machine code at runtime, typically to enhance performance during execution.
EVM (Ethereum Virtual Machine) — The runtime environment for smart contracts in Ethereum, allowing code to be executed exactly as intended.
DA (Data Availability) — Refers to the accessibility and correctness of the data required to reconstruct the state of a blockchain.
Our Vision
While developing a zkVM for an EVM-compatible, Wasm based rollup on Ethereum, Fluent explored methods to optimize the proving process of smart contracts. A major challenge lies in the fact that using a zkVM to support multiple VMs only allows for the proof of the root STF, while the remaining nested execution of other VMs requires emulation.
To address this issue, Fluent proposes a VM capable of performing nested execution without incurring additional emulation overhead. This is achieved through a hardware acceleration process similar to the translation of smart contracts, applications, and precompiled contracts into a specialized low-level intermediary representation (IR) binary structure known as rWasm. This structure can be proven much more efficient compared to running emulator software.
With its unique interruption system, rWasm emerged as an innovative solution for handling nested calls and compiling diverse smart contracts, revolutionizing the landscape of efficient zk-Wasm based applications on Ethereum.
Meet Blended Execution
With blended execution on Fluent, developers can create applications using languages and tools from various VMs yet coexisting in the same execution space. This is possible because all smart contracts share the same account space. As a result, native composability is achieved for anything within Fluent that can be represented as rWasm (which relies on Wasm and LLVM, making 17+ languages available for development).
Different VMs can be supported using an AOT translation process (directly into Rwasm) or an emulation process. Fluent can enable execution environments like the EVM, bringing Solidity and Vyper support. Similarly, the new language Sway from Fuel can be integrated by running an FVM execution precompile.
Both users and developers obtain major benefits from blended execution because it enhances the experience of developers who are ready to expand the Fluent ecosystem by bringing more development languages and execution environments on board. Additionally, it bridges the gap between Web2 and Web3 users by providing a shared set of tools and new ways to interact with applications through account abstraction backed by EIP-7702 and modules such as OAuth 2.0 and WebAuthn.
Product Vision
The Fluent Blended Execution Layer will revolutionize the way developers create, deploy, and prove smart contracts by unifying multiple VMs into a single, highly efficient execution environment. At the heart of this vision is rWasm, a unique IR language designed to bridge the gap between various VMs and EEs such as the EVM, SVM, and Wasm. By eliminating the emulation overhead typically associated with nested execution, Fluent enables faster, more secure, and more scalable smart contract execution.
Key features:
- Unified Execution Environment: Support for multiple VMs and EEs through a single execution environment, enabling native composability between different smart contracts, tools, and languages.
- rWasm Integration: Full compatibility with standard Wasm and support for over 17+ traditional programming languages, allowing developers to use familiar tools such as Rust, C, Solidity, TinyGo, and AssemblyScript without sacrificing performance.
- Optimized Proving Infrastructure: Efficient proving through rWasm that drastically reduces overhead and complexity, providing optimal performance for ZK circuits.
- Seamless Expansion: Advanced JIT/AOT compilers to integrate new VMs and EEs, ensuring extensibility for future-proofing and scalability.
- Security and Extensibility: BlendedVM unifies the execution space, providing enhanced security and preventing vulnerabilities that arise from managing multiple VMs while enabling new VMs to be added.
The Fluent Blended Execution Layer empowers developers to build diverse applications without managing disparate systems, optimizing their experience and fostering rapid growth in the Ethereum ecosystem.
Technical Vision
Extracting traces is crucial for proving. It involves obtaining snapshots of stack and memory operations to feed into zk circuits. Aligning the IR language with the trace structure and ensuring its compatibility with zk is essential to minimize circuit size, ultimately improving proving speed and reducing code complexity. rWasm, as an IR binary language, combines execution concepts and maps its execution trace to the zkVM circuit structure. This approach aims to achieve optimal performance, minimizing both proving and execution overhead.
Blended execution natively supports multiple VMs and EEs within a single execution environment, and within Fluent, is enabled by employing a single IR known as rWasm, which serves as its primary VM. This IR enables the verification of all state transitions occurring within the system, encompassing various VMs and EEs. In the case of the Fluent L2, the EVM, SVM, and Wasm are supported. As a result, developers familiar with any of these primitives can leverage circuits designed for rWasm and effortlessly obtain the necessary optimal proving infrastructure for their applications. In essence, blended execution acts as a state verification function responsible for representing every operation within the Fluent execution layer.
Given that rWasm serves as the IR language for Fluent, it has the potential to represent not only Wasm or EVM but also other VMs and EEs. This is facilitated by providing dedicated compilation or emulation software for these platforms or in some cases utilizing emulation software.
rWasm is a derivative of the Wasm assembly language that keeps 100% backward compatibility with original Wasm standards. Leveraging the extensive adoption and support of Wasm, rWasm enables blockchain developers to effortlessly create new applications in traditional languages like Rust and C. Fluent’s advanced AOT compilers handle all IR compilation tasks, simplifying the development and deployment process of Wasm-based blockchain applications in the Ethereum ecosystem.
Blended Execution
Blended Execution is Fluent’s approach to executing smart contracts from diverse VMs inside one shared state machine.
It does this by using a unified execution representation (rWasm) and a host-governed control model, so different runtime styles can compose natively without being split into isolated domains.
This system enables verification of state transitions in one execution environment, reducing the emulation overhead that usually appears in nested multi-VM designs.
Because applications share one execution space, Fluent supports native composability across supported runtime families. Compilation/translation tooling is used to bring different bytecode formats into Fluent’s execution model while preserving developer ergonomics.
Ultimately, blended execution acts as a unified state-transition representation model, streamlining development and expanding what builders can compose on Ethereum L2.
BlendedVM vs MultiVM
MultiVM is an execution layer that provides multiple VM engines side-by-side. It gives flexibility, but also introduces operational complexity:
- separate VM assumptions,
- different ISA-level constraints,
- increased surface for metering and security mistakes.
BlendedVM takes a different path. Instead of keeping isolated VM silos, it uses one execution/control model to represent multiple runtime behaviors through translation/runtime routing.
This architecture improves composability and makes security boundaries easier to centralize, while still allowing extension to additional VMs/EEs over time.
Architecture
Fluent is an Ethereum Layer L2 rollup designed to natively execute EVM, SVM and Wasm-based programs. Fluent exists as a unified state machine, where all contracts can call each other, regardless of which VM they were originally built for.
As a rollup, Fluent supports scalable and efficient execution by committing state changes to Ethereum L1. This process involves compressing the state changes using ZK proofs, specifically SNARKs.
The base architecture of Fluent
Fluent currently operates on a modified Reth stack, with Fluent-specific execution/runtime integration. It maintains compatibility with core Ethereum data formats (for example transactions and blocks), while extending runtime behavior for blended execution.
Furthermore, Fluent is designed around a fork-less runtime upgrade model by keeping critical execution logic in upgradable runtime paths.
Implementation note: exact fork-level compatibility policy (for example Prague-era assumptions) is release-dependent and should be validated against the active runtime/release branch rather than treated as a permanent invariant in this chapter.
Execution Environment
The Fluent EE is designed to be universal, supporting various VMs and EEs. This universality is achieved through the rWasm VM, which executes and simulates different EEs. By using rWasm, Fluent translates all applications into a single execution language, enabling different EEs to share the same state trie. This shared state trie facilitates seamless interoperability among applications.
Given that each EE/VM can introduce unique execution standards, various integration challenges may arise. Let’s explore the most significant ones.
-
Incompatible Cryptography: EEs can consume an entire transaction as an input, requiring the use of system bindings to verify signature correctness. Fluent supports pre-compiled contracts for a range of commonly used cryptographic functions (e.g., secp256k1, ed25519, bn254, bls384). Any missing cryptographic functions can be implemented as custom precompiled contracts.
-
Different Address Format: Fluent uses an Ethereum-compatible 20-byte address format. If an address uses a different derivation format or has more bytes than a contract can store, there needs to be a way to handle this. Such addresses can be stored as so-called projected addresses, which are calculated using any hashing function.
-
Varying Gas Calculation Policies: EEs/VMs may use different gas calculation policies compared to Fluent. To address this, Fluent has two distinct gas calculation policies:
- Standard Gas Calculation: Applies to typical operations, assigning a specific gas cost to each rWasm opcode.
- Manual Gas Calculation: Available exclusively for genesis precompiled smart contracts on Fluent.
This approach ensures flexibility and maintains the integrity of operations across different environments.
-
Custom Bytecode Storage: There is a need to store immutable data like custom bytecode or some EE-specific context information. Fluent provides special metadata accounts that can be used to store immutable data, including custom bytecode or other data.
-
Variable Storage Size: Some EEs implement variable storage key/value lengths. For instance, Solana uses 10kB chunks, while Cosmos utilizes variable key/value lengths with certain constraints. Storing these datasets within Ethereum’s standard 32-byte storage format may lead to significant performance issues and increased gas consumption. To address this, Fluent offers a metadata API that allows developers to use custom storage solutions for varying data lengths efficiently.
Account Ownership
Fluent extends the original EVM account structure to support account ownership. Account ownership is a special modification of an EIP-7702 account. Instead of delegating an account to a contract, you assign it an owner.
At first glance the idea looks similar, but it enables managing metadata storage directly within the account. This metadata can store arbitrary data as linear storage and is used by both the EVM and SVM runtimes to keep track of information such as bytecode, code hashes, and other account metadata.
Ownable accounts begin with a special 0xEF44 prefix and follow the structure below:
#![allow(unused)]
fn main() {
/// Ownable account bytecode representation
///
/// Format:
/// `0xEF44` (MAGIC) + `0x00` (VERSION) + 20 bytes of owner address + metadata.
pub struct OwnableAccountBytecode {
/// The owner of this account.
pub owner_address: Address,
/// Account version.
pub version: u8,
/// Extra bytes stored by the runtime.
pub metadata: Bytes,
}
}
The concept is simple but unlocks powerful new features for EVM extensibility.
Account Delegation
This mechanism is similar to EIP-7702, but with one key difference: ownership cannot be revoked by the runtime, since the metadata structure is strictly bound to runtime logic.
Once a smart contract is deployed, its ownership is permanently assigned—every contract has an owner.
A simplified runtime resolution sketch is shown below (illustrative, not a frozen implementation contract):
#![allow(unused)]
fn main() {
pub fn resolve_precompiled_runtime_from_input(input: &[u8]) -> Address {
if input.len() > WASM_MAGIC_BYTES.len()
&& input[..WASM_MAGIC_BYTES.len()] == WASM_MAGIC_BYTES
{
PRECOMPILE_WASM_RUNTIME
} else if input.len() > SVM_ELF_MAGIC_BYTES.len()
&& input[..SVM_ELF_MAGIC_BYTES.len()] == SVM_ELF_MAGIC_BYTES
{
PRECOMPILE_SVM_RUNTIME
} else if input.len() > ERC20_MAGIC_BYTES.len()
&& input[..ERC20_MAGIC_BYTES.len()] == ERC20_MAGIC_BYTES
{
PRECOMPILE_ERC20_RUNTIME
} else {
PRECOMPILE_EVM_RUNTIME
}
}
}
Runtime families and routing labels are implementation-dependent and can evolve by release. The conceptual model remains the same: runtime ownership determines execution path.
Typical families discussed in Fluent architecture include EVM/default, Wasm-oriented paths, and additional runtime-specific routes where enabled by the active release.
Account Derivation
Ownable accounts can also derive new accounts within the same runtime.
This process is similar to CREATE2, but it differs in two key ways:
- It does not use bytecode as input (the runtime is fixed).
- It allows runtimes themselves to spawn new accounts.
This mechanism resembles Program Derived Addresses (PDA) in Solana, where subaccounts can be deterministically created.
To avoid collisions with the CREATE2 scheme, Fluent uses a custom hashing function for deriving ownable accounts:
0x44 || account_owner || salt
Composability
Blended EEs within Fluent operating as execution proxies. Since Fluent only supports rWasm bytecode, then every operation inside Fluent must be represented using rWasm ISA.
Architecture design of Blended EE
There are two main options for this approach:
-
Translation: A translation from one binary format into rWasm. This concept is used for Wasm application deployment where runtime is a Wasm → rWasm compiler.
-
Runtime: An precompiled execution runtime developed using Wasm and compiled into machine code. This concept is used by EVM, SVM, ERC20 runtimes.
For example, Fluent architecture incorporates EVM and SVM-style integration using the account ownership method (aka runtime proxy). A proxy with a delegate call forwards execution to a dedicated runtime loader smart contract.
Status note: exact runtime-family enablement can vary by release/network. Treat this section as architecture pattern guidance, and verify active runtime support in current release docs. This setup eliminates the need for address mapping or transaction verification. ABI encoding/decoding format can be used, and contracts can be managed using default EVM/SVM-compatible data structures, such as storage, block/transaction structures.
To achieve native composability on Fluent, an EE must coexist within the same trie space as the default EVM+Wasm EEs. It should adhere to established EVM standards, including address formatting and derivation strategies (for example, CREATE/CREATE2 for smart contract deployment). The EE must not execute arbitrary account or state modifications and can only manage basic Ethereum-compatible accounts.
As a result, apps built with the proxy model can natively interoperate with other native-compatible EEs. Since they share the same address space, isolation isn’t required. Consequently, Wasm apps can directly interact with EVM apps and vice versa.
State Access
Fluent operates with state tries through a pure functional approach, where every smart contract and root-STF can be represented as a function. In this model, the input provides a list of dependencies, and the output yields an execution result along with a number of logs.
However, this isn’t entirely feasible due to cold storage reads and external storage dependencies, such as CODEHASH-like EVM opcodes. To address this, Fluent employs an interruption system to “request” missing information from the root-STF. This is particularly useful for operations involving cold storage or invalidated warm storage.
Interruption System
Fluent interoperability relies heavily on its interruption system. Smart contracts on Fluent are limited to pure functions, preventing system calls from accessing external resources such as bytecodes, cold or invalidated storage slots, or performing nested calls. Including system calls imposes additional proving overhead, as proving gadgets must be developed for each call, complicating system development. This also impacts the flexibility in managing rights or extending contracts, making the system less sustainable for the fork-less concept. In such a scenario, system contracts cannot be upgraded without updating the circuits.
Fluent solves this problem by enabling an interruption system that helps manage context switching between nested apps and the so-called STF that stands for context management.
For simplicity, let’s assume that STF, smart contracts and EEs are all functions (since they are essentially state transition functions or a part of STF). Functions can be categorized as either root or non-root. A root function is defined as a function where the depth level is equal to 0. The root function is pivotal because it handles all context switching and cross-contract accesses, serving as a security layer.
The root function has the ultimate authority over all state transitions within the blockchain. Additionally, the root function is in charge of managing and executing system calls. The root function cannot be interrupted, but it is capable of handling interruptions.
System Bindings
The system bindings manage context switching in a Fluent interruption system. Functions signatures are provided below. These functions maintain the entire flow of an interruption system.
#![allow(unused)]
fn main() {
/// Low-level function that terminates the execution of the program and exits with the specified
/// exit code.
///
/// This function is typically used to perform an immediate and final exit of a program,
/// bypassing Rust's standard teardown mechanisms.
/// It effectively stops execution and prevents further operations, including cleanup or
/// unwinding.
///
/// # Parameters
/// - `code` (i32): The non-positive exit code indicating the reason for termination.
///
/// # Notes
/// - This function is generally invoked in specialized environments, such as WebAssembly
/// runtimes, or through higher-level abstractions.
/// - Consider alternatives in standard applications, such as returning control to the caller or
/// using Rust's standard exit mechanisms, for safer options.
pub fn _exit(code: i32) -> !;
/// Executes a nested call with specified bytecode poseidon hash.
///
/// # Parameters
/// - `hash32_ptr`: A pointer to a 254-bit poseidon hash of a contract to be called.
/// - `input_ptr`: A pointer to the input data (const u8).
/// - `input_len`: The length of the input data (u32).
/// - `fuel16_ptr`: A 16 byte array of elements where [fuel_limit/fuel_used, fuel_refunded]
/// - `state`: A state value (u32), used internally to maintain function state.
///
/// Fuel ptr can be set to zero if you want to delegate all remaining gas.
/// In this case sender won't get the consumed gas result.
///
/// # Returns
/// - An `i32` value indicating the result of the execution, negative or zero result stands for
/// terminated execution, but positive code stands for interrupted execution (works only for
/// root execution level)
pub fn _exec(
hash32_ptr: *const u8,
input_ptr: *const u8,
input_len: u32,
fuel16_ptr: *mut [i64; 2],
state: u32,
) -> i32;
/// Resumes the execution of a previously suspended function call.
///
/// This function is designed to handle the resumption of a function call
/// that was previously paused.
/// It takes several parameters that provide
/// the necessary context and data for resuming the call.
///
/// # Parameters
///
/// * `call_id` - A unique identifier for the call that needs to be resumed.
/// * `return_data_ptr` - A pointer to the return data that needs to be passed back to the resuming function. This should point to a byte array.
/// * `return_data_len` - The length of the return data in bytes.
/// * `exit_code` - An integer code that represents the exit status of the resuming function. Typically, this might be 0 for success or an error code for failure.
/// * `fuel_limit` - A fuel used representing the fuel need to be charged, also it puts a consumed fuel result into the same pointer
pub fn _resume(
call_id: u32,
return_data_ptr: *const u8,
return_data_len: u32,
exit_code: i32,
fuel16_ptr: *mut [i64; 2],
) -> i32;
}
During execution, once shared resources are requested, it pauses the execution and forwards params into the STF. Once the interruption is over, it resumes the previous frame.
Here is the basic flow of an interruption:

Application Exit
The application exit binding terminates function execution with the specified exit code. The function is designed to exit from any smart contract or application. It immediately halts the contract execution and forwards all execution results, including the exit code and return data, to the caller contract.
Constraints:
The exit code must always be a negative 32-bit integer.
Supplying a positive exit code will result in a NonNegativeExitCode execution error.
Positive exit codes indicate interrupted execution and are exclusive to the _exec or _resume functions.
Exec & Resume
During execution, once shared resources are requested, it pauses the execution and forwards params into the STF.
Interrupted params contain the following items:
hash32_ptr: A pointer to a 32-byte code hash of a contract to be called (bytecode hash for STF or syscall code hash).input: An input parameter for smart contract or input for interruption.fuel_ptr: A mutable pointer to a fuel value. The consumed and refunded fuel is stored in the same pointer after execution.state: A state value (u32), used internally to maintain function state (main or deploy).
This binding executes a nested call or sends an interruption to the parent execution call though context switching. If the depth level is greater than 0 (non STF), then an interruption occurs; otherwise, bytecode is executed.
Once the interruption is resolved, it resumes the execution of a previously suspended function call by specifying return data, resulting exit code and fuel consumed.
The resume function operates similarly to exec, but it requires an interrupted call ID and the interruption result (including return data and exit code). Interruption events may also occur during the resume process, requiring an execution loop capable of handling and correctly processing these interruptions.
System Calls
System calls use the same approach as an interruption system. Since the root-STF function is responsible for all state transitions, including cold/warm storage reads, then a syscall can be represented as an interruption to the ephemeral smart contract.
For accessing state data, Fluent uses special ephemeral smart contracts to access information located outside a smart contract. For example, in case of storage cache invalidation, the contract must request the newest info from the root call instead of reading invalidated cache. Also, nested calls to other contracts require ACL checks that must be checked and verified by the root-STF.
Here is an example of what the system call looks like for Rust contracts.
#![allow(unused)]
fn main() {
fn syscall_storage_read<SDK: NativeAPI>(native_sdk: &mut SDK, slot: &U256) -> U256 {
// do a call to the root-STF to request some storage slot
let (_, exit_code) = native_sdk.exec(
&SYSCALL_ID_STORAGE_READ, // an unique storage read code hash
slot.as_le_slice(), // a requesting slice with data (aka call-input)
GAS_LIMIT_SYSCALL_STORAGE_READ, // a gas limit for this call (max threshold)
STATE_MAIN, // state of the call (must always be 0, except some special tricky cases)
);
// make sure returning result is zero (Ok)
assert_eq!(exit_code, 0);
// read output from the return data (storage slot value is always 32 bytes)
let mut output: [u8; 32] = [0u8; 32];
native_sdk.read_output(&mut output, 0);
// convert return data to the U256 value
U256::from_le_bytes(output)
}
}
For example, if smart contract A needs to send a message to smart contract B, it can trigger a special system call interruption. Upon interruption, the STF processes the interruption, performs ACL checks, executes the target application, and then resumes the previous context with the appropriate exit code and return data.
Blended VM
Blended VM implements Blended Execution concepts inside Fluent. It works on the top of rWasm VM, the runtime system bindings and interruption system. rWasm VM represents all state transitions within the VM, including Wasm instructions. The interruption system efficiently manages interruptions during system calls or cross-contract calls.
rWasm
rWasm is Fluent’s Wasm-derived execution substrate inside the blended VM architecture.
Its role is to make heterogeneous execution more deterministic and proving-friendly while preserving a practical Wasm developer path.
Architectural role in Fluent
In Fluent’s blended model, rWasm is the execution representation that helps unify runtime behavior across different integration paths.
At system level, this supports:
- one shared state-transition model,
- host-governed interruption/syscall boundaries,
- deterministic commit semantics.
Why rWasm instead of plain Wasm modules everywhere
Plain Wasm is developer-friendly but includes representation/runtime assumptions that are not always ideal for protocol-grade deterministic/proving workflows.
rWasm addresses this by using:
- a constrained translation/execution model,
- explicit module/runtime boundaries,
- a dedicated opcode/runtime surface designed for predictable behavior.
Current implementation shape (from fluentlabs-xyz/rwasm)
1) Translation pipeline
Wasm input is validated and translated into a compact rWasm module consumed by the runtime.
2) Module encoding contract
rWasm modules use explicit header/versioning and ordered sections. This is treated as compatibility-sensitive wire format.
3) Runtime VM + strategy layer
The runtime includes a native VM executor and a strategy abstraction for optional compatibility/comparison execution backends.
4) Fuel-first metering
Fuel limits and consumption are explicit runtime controls, including deterministic out-of-fuel behavior.
5) Optional tracing
Tracing can capture execution/memory/table events for debugging and analysis pipelines.
Determinism and security boundary
rWasm determinism depends on both:
- VM semantics, and
- host import/syscall behavior.
So runtime correctness requires deterministic host integration, not only deterministic opcode dispatch.
Status notes for readers
- rWasm docs evolve with implementation; treat exact opcode and module-format details as version-sensitive.
- For current production behavior, consult
fluentlabs-xyz/rwasm/docs/*plus active Fluentbase/reth release docs.
EVM
Fluent integrates with the EVM by leveraging special EVM precompiled contracts. These contracts facilitate the execution of EVM bytecode, enabling the deployment and operation of smart contracts designed for the EVM ecosystem. This allows developers to deploy applications built for EVM platforms using languages like Solidity or Vyper.
The EVM executor, a Rust-based smart contract, provides two key functions:
deploy: Deploys the EVM application and stores the bytecode state.main: Executes the already deployed EVM application.
During deployment, a specialized rWasm proxy is deployed under the smart contract address. This proxy redirects all deployment and execution calls to the EVM executor.
The deployment and interaction model is intended to stay close to Ethereum-compatible expectations. In practice, this provides a smooth migration path for most EVM apps.
Status note: Fluent still applies runtime-routing and host-governed execution boundaries, so implementation details can differ in some edge cases versus vanilla upstream EVM nodes.
WebAssembly
Fluent offers near-native support for Wasm, with the primary distinction being that, during deployment, it’s compiled into rWasm. A Wasm application can use Fluent system-call interfaces, subject to the same host-governed execution and metering rules as other runtime paths.
During the deployment process, Fluent enhances the rWasm codebase with additional checks for gas measurement and modifies certain instructions or segment structures when necessary. For more details, refer to the rWasm section.
Solana Integration
Fluent’s Solana-oriented architecture targets composability with EVM and Wasm applications by mapping Solana-style applications into Fluent’s account space.
Status note: Solana-family runtime availability and feature completeness are release-dependent. Validate current support level against active release docs before relying on this path in production. To achieve SVM support, a special rPBF executor is employed, which defines the execution of Solana binaries and specifies a list of mapped system bindings and calls. Native support for rPBF bytecode is achieved by mapping each operation into the Fluent EE space.
Address Format
Solana uses a 32-byte address format, while Fluent operates with a 20-byte format. Instead of storing a mapping from the 32-byte to the 20-byte format, Fluent employs a special address convention to convert addresses between formats. A unique magic prefix is attached to Solana addresses to make them convertible into the same binary representation within the 20-byte Fluent account trie.
The first 12 bytes of the address are used to route transfers and calls between SVM and EVM+Wasm accounts. This routing is necessary to achieve full EE compatibility and perform additional containment checks when required. For example, while a simple transfer without invoking callee bytecode is not allowed in EVM, it is possible in SVM. Therefore, differentiating between SVM and EVM accounts is essential.
Transaction Type
Fluent does not support Solana transactions directly; instead, EIP-1559 transactions must be used. During deployment, Fluent automatically detects Solana applications (EFL+rPBF) and specifies a special proxy that refers to the SVM executor system’s precompiled contract.
Currently, Fluent does not support an additional transaction type for Solana transactions. However, this feature could be added in the future if there is enough demand.
PDA (Program-Derivable Address)
Program Derived Addresses (PDA) are a core feature of Solana, allowing for the management of nested contracts with
dedicated storage. Fluent leverages various derivation schemes for Solana programs, ensuring that the migration process
remains seamless for developers. The CREATE2 derivation scheme is used to replicate the PDA functionality.
To maintain an identical storage layout, Fluent offers supplementary storage interfaces. These interfaces enable Solana applications to efficiently manage data chunks without incurring extra overhead.
Accounts
Fluent employs account projection, also known as EE simulation, to integrate Solana accounts into the unified Fluent account space. This approach ensures that all EVM, Wasm, and SVM accounts are treated uniformly, leveraging the same rWasm VM. Consequently, this enables seamless interoperability and balance transfers between various account types.
System Builtins
A collection of system functions provides access to low-level operations, serving both our VM runtime and our circuit definitions. Each system function is replaced with a specialized ZK-gadget to speed up the proving process. These functions can include hashing algorithms, I/O operations, and nested call functions.
WARNING: The system functions API/ABI evolve over time. The signatures/IDs shown below are historical examples from one implementation snapshot. For current production development, always verify against the latest
fluentbaseSDK/runtime docs and release branch.
#![allow(unused)]
fn main() {
#[link(wasm_import_module = "fluentbase_v1preview")]
extern "C" {
/// Functions that provide access to crypto elements, right now we support following:
/// - Keccak256
/// - Poseidon (two modes, message hash and two elements hash)
/// - Ecrecover
pub fn _keccak256(data_offset: *const u8, data_len: u32, output32_offset: *mut u8);
pub fn _poseidon(data_offset: *const u8, data_len: u32, output32_offset: *mut u8);
pub fn _poseidon_hash(
fa32_offset: *const u8,
fb32_offset: *const u8,
fd32_offset: *const u8,
output32_offset: *mut u8,
);
pub fn _ecrecover(
digest32_offset: *const u8,
sig64_offset: *const u8,
output65_offset: *mut u8,
rec_id: u32,
);
/// Basic system methods that are available for every app (shared and sovereign)
pub fn _exit(code: i32) -> !;
pub fn _write(offset: *const u8, length: u32);
pub fn _input_size() -> u32;
pub fn _read(target: *mut u8, offset: u32, length: u32);
pub fn _output_size() -> u32;
pub fn _read_output(target: *mut u8, offset: u32, length: u32);
pub fn _forward_output(offset: u32, len: u32);
pub fn _state() -> u32;
pub fn _read_context(target_ptr: *mut u8, offset: u32, length: u32);
/// Executes a nested call with specified bytecode poseidon hash.
///
/// # Parameters
/// - `hash32_ptr`: A pointer to a 254-bit poseidon hash of a contract to be called.
/// - `input_ptr`: A pointer to the input data (const u8).
/// - `input_len`: The length of the input data (u32).
/// - `fuel_ptr`: A mutable pointer to a fuel value (u64), consumed fuel is stored in the same
/// pointer after execution.
/// - `state`: A state value (u32), used internally to maintain function state.
///
/// Fuel ptr can be set to zero if you want to delegate all remaining gas.
/// In this case sender won't get consumed gas result.
///
/// # Returns
/// - An `i32` value indicating the result of the execution,
/// negative or zero result stands for terminated execution,
/// but positive code stands for interrupted execution (works only for root execution level)
pub fn _exec(
hash32_ptr: *const u8,
input_ptr: *const u8,
input_len: u32,
fuel_ptr: *mut u64,
state: u32,
) -> i32;
/// Resumes the execution of a previously suspended function call.
///
/// This function is designed to handle the resumption of a function call
/// that was previously paused.
/// It takes several parameters that provide
/// the necessary context and data for resuming the call.
///
/// # Parameters
///
/// * `call_id` - A unique identifier for the call that needs to be resumed.
/// * `return_data_ptr` - A pointer to the return data that needs to be passed back to the
/// resuming function.
/// This should point to a byte array.
/// * `return_data_len` - The length of the return data in bytes.
/// * `exit_code` - An integer code that represents the exit status of the resuming function.
/// Typically, this might be 0 for success or an error code for failure.
/// * `fuel_ptr` - A mutable pointer to a 64-bit unsigned integer representing the fuel need to
/// be charged, also it puts a consumed fuel result into the same pointer
pub fn _resume(
call_id: u32,
return_data_ptr: *const u8,
return_data_len: u32,
exit_code: i32,
fuel_ptr: *mut u64,
) -> i32;
pub fn _charge_fuel(delta: u64) -> u64;
pub fn _fuel() -> u64;
/// Journaled ZK Trie methods to work with blockchain state
pub fn _preimage_size(hash32_ptr: *const u8) -> u32;
pub fn _preimage_copy(hash32_ptr: *const u8, preimage_ptr: *mut u8);
pub fn _debug_log(msg_ptr: *const u8, msg_len: u32);
}
}
For each system function, a unique identifier is assigned.
During the rWASM translation, every function call is replaced with a Call(SysCallIdx) instruction.
This approach significantly enhances the efficiency and simplicity of the proving process.
#![allow(unused)]
fn main() {
#[repr(u32)]
#[allow(non_camel_case_types)]
pub enum SysFuncIdx {
#[default]
UNKNOWN = 0x0000,
// crypto
KECCAK256 = 0x0101,
POSEIDON = 0x0102,
POSEIDON_HASH = 0x0103,
ECRECOVER = 0x0104,
// SYS host
EXIT = 0x0001,
STATE = 0x0002,
READ = 0x0003,
INPUT_SIZE = 0x0004,
WRITE = 0x0005,
OUTPUT_SIZE = 0x0006,
READ_OUTPUT = 0x0007,
EXEC = 0x0009,
RESUME = 0x000a,
FORWARD_OUTPUT = 0x000b,
CHARGE_FUEL = 0x000c,
FUEL = 0x000d,
READ_CONTEXT = 0x000e,
// preimage
PREIMAGE_SIZE = 0x070D,
PREIMAGE_COPY = 0x070E,
DEBUG_LOG = 0x0901,
}
}
ABI
In the realm of EVM applications, the Solidity ABI has become the predominant encoding format, effectively establishing itself as a primary standard for on-chain interactions today. This encoding and decoding schema, primarily driven by the Solidity language, is widely used across Ethereum and other EVM-compatible platforms.
However, in the Web2 domain, developers opt for ABI encoding/decoding schemes that best fit their specific needs and tasks. Notably, Ethereum includes several system precompiles that do not conform to a Solidity-compatible ABI schema.
Blended VM Fluent distinguishes itself by supporting a variety of execution environments, such as EVM and Solana’s VM, using distinct ABI schemes tailored to each environment. In Solana, for instance, there isn’t a standardized ABI format, granting developers the flexibility to choose any format that suits their requirements.
This flexibility is a hallmark of Fluent, as it does not mandate a single encoding/decoding standard for applications. Instead, it empowers developers to select the most suitable option. The Fluentbase SDK accommodates this by implementing the necessary ABI encoding/decoding standards, allowing developers to freely use any ABI format they prefer.
Fluentbase Codec
Fluent employs a custom codec for ABI encoding/decoding tailored to various ABIs. This Codec includes compatibility modes such as Solidity ABI, and is designed to efficiently encode and decode parameters across different VMs.
Fluent Codec is a lightweight library, compatible with no-std environments, and optimized for random reads. Although it shares similarities with Solidity ABI encoding, it incorporates several optimizations and features to enhance efficient data access and handle nested structures more effectively.
The Codec leverages a header/body encoding mode:
- Header: Contains all static information.
- Body: Contains all dynamic information.
Key Features:
- No-std Compatible: Operates in environments without the standard library.
- Configurable Byte Order and Alignment: Allows customization according to requirements.
- Solidity ABI Compatibility Mode: Seamlessly integrates with Solidity ABI.
- Random Access: Accesses first-level information without requiring full decoding.
- Support for Nested Structures: Encodes nested structures recursively.
- Derive Macro Support: Facilitates custom type encoding/decoding via derive macros.
Encoding Modes
The library supports two primary encoding modes:
- SolidityABI: Applied for external cross-contract calls to handle input parameters and decode outputs effectively.
- FluentABI: Utilized for internal cross-system calls, benefiting from 4-byte stack alignment for efficiency without compromising the developer experience.
SolidityABI Mode
Parameters:
- Big-endian byte order
- 32-byte alignment (Solidity compatible)
- Dynamic structure encoding:
- Header
- offset (u256) - a position in the structure
- Body
- length (u256) - a number of elements
- recursively encoded elements
- Header
Usage example:
#![allow(unused)]
fn main() {
use fluentbase_codec::SolidityABI;
SolidityABI::encode(&value, &mut buf, 0)
}
FluentABI Mode
Parameters:
- Little-endian byte order
- 4-byte alignment
- Dynamic structure encoding:
- Header
- length (u32) - a total number of elements in the dynamic data
- offset (u32) - a position in the buffer
- size (u32) - a total number of encoded elements bytes
- Body
- recursively encoded elements
- Header
Usage example:
#![allow(unused)]
fn main() {
use fluentbase_codec::FluentABI;
FluentABI::encode(&value, &mut buf, 0)
}
Type System
Primitive Types
Primitive types are encoded directly without any additional metadata, offering zero-cost encoding when their alignment matches the stack size.
TODO: add all types
- Integer types:
u8,i8,u16,i16,u32,i32,u64,i64 - Static arrays:
[T; N]
Non-Primitive Types
These types require additional metadata for encoding:
Vec<T>: Dynamic array of encodable elementsHashMap<K,V>: Hash map with encodable keys and valuesHashSet<T>: Hash set with encodable elements
For dynamic types, the codec stores metadata that enables partial reading. For example:
- Vectors store offset and length information
- HashMaps store separate metadata for keys and values, allowing independent access
Important Notes
Determinism
The encoded binary is not deterministic and should only be used for parameter passing. The encoding order of non-primitive fields affects the data layout after the header, though decoding will produce the same result regardless of encoding order.
Order Sensitivity
The order of encoding operations is significant, especially for non-primitive types, as it affects the final binary layout. For non-ordered set/map data structures, ordering by key is applied.
Usage Examples
Basic Structure
#![allow(unused)]
fn main() {
use fluentbase_codec::{Codec, FluentABI};
use bytes::BytesMut;
#[derive(Codec)]
struct Point {
x: u32,
y: u32,
}
// Encoding
let point = Point { x: 10, y: 20 };
let mut buf = BytesMut::new();
FluentABI::encode(&point, &mut buf, 0).unwrap();
// Decoding
let decoded: Point = FluentABI::decode(&buf, 0).unwrap();
}
Dynamic Array Example
#![allow(unused)]
fn main() {
// Vector encoding with metadata
let numbers = vec![1, 2, 3];
// FluentABI encoding (with full metadata)
let mut fluent_buf = BytesMut::new();
FluentABI::encode(&numbers, &mut fluent_buf, 0).unwrap();
// Format: [length:3][offset:12][size:12][1][2][3]
// SolidityABI encoding
let mut solidity_buf = BytesMut::new();
SolidityABI::encode(&numbers, &mut solidity_buf, 0).unwrap();
// Format: [offset:32][length:3][1][2][3]
}
Account Trie
The account structure in rWASM-oriented execution is designed to be as simple as possible, containing only the most essential fields. It is aligned with Ethereum account fundamentals, while Fluent can apply additional runtime metadata/routing semantics where needed.
#![allow(unused)]
fn main() {
pub struct Account {
pub address: Address,
pub balance: U256,
pub nonce: u64,
pub code_hash: B256,
pub code_size: u64,
}
}
Fields description:
address: This transient field holds the account address. Currently, a 20-byte address is used to ensure compatibility with the EVM account structure. However, there is a possibility of extending this to 32 bytes in the future to align with the state trie account path, thereby enhancing interoperability.balance: Represents the account balance as a 256-bit element. This is consistently expressed as a 256-bit Big Endian value, although future changes may be considered.nonce: Indicates the number of transactions initiated by this account. It increments with each transaction, call, or contract creation, regardless of the operation’s success.code_hash: Represents the account code hash in the active execution/account representation. In routed/runtime-managed flows this may involve Fluent-specific transformations, while EVM-facing compatibility paths may expose Ethereum-expected behavior.code_size: Denotes size information for the code representation used by the active runtime/account path.
State Trie
Fluent operates with state tries through a pure functional approach, where every smart contract and root-STF can be represented as a function. In this model, the input provides a list of dependencies, and the output yields an execution result along with a number of logs.
However, this isn’t entirely feasible due to cold storage reads and external storage dependencies, such as CODEHASH-like EVM opcodes. To address this, Fluent employs an interruption system to “request” missing information from the root-STF. This is particularly useful for operations involving cold storage or invalidated warm storage.
The same concept is also used to handle nested calls without incurring additional simulation overhead.
Genesis
The Genesis block, numbered 0, serves as the initial block in the blockchain. It defines fundamental blockchain parameters, the genesis hash, and stores the initial blockchain state, including system contracts.
Fluent Precompiled Contracts
Fluent provides system genesis/runtime contracts.
Status note: exact enabled runtime set and addresses are release-dependent. The list below is illustrative for this documentation revision and should be validated against current genesis assets.
- EVM:
0x0000000000000000000000000000000000005210 - WASM:
0x0000000000000000000000000000000000005220 - SVM:
0x0000000000000000000000000000000000005230
EVM
EVM precompiled contract is responsible for managing EVM deployment and execution. It embeds EVM virtual machine with ISA.
The contract defines two methods:
deploy- for deploying EVM smart contract using EVM constructor (aka init code)main- for executing already deployed EVM contract
Keccak256 compatible code hash is stored inside special constant storage slot
calculated using function keccak256("_evm_bytecode_hash").
The EVM bytecode is stored in the special preimage storage that uses CREATE2 for storing custom immutable data.
- EVM_BYTECODE_HASH_SLOT:
0xfd8a2cf66e0f80fe20ebc0e96c0e08e69c883c792a0409d4f4f92413fb66e980
WASM
The contract is disabled for now, WASM support is achieved natively
SVM
TBD
EVM-compatible Precompiled Contracts
EVM precompiled contracts are fully compatible with the original EVM contracts and are maintained with the following addresses:
- SECP256K1_ECRECOVER (
0x0000000000000000000000000000000000000001): Used for recovering the public key from a given signature. - SHA256 (
0x0000000000000000000000000000000000000002): Computes the SHA-256 hash of the input data. - RIPEMD160 (
0x0000000000000000000000000000000000000003): Computes the RIPEMD-160 hash of the input data. - IDENTITY (
0x0000000000000000000000000000000000000004): Returns the input as the output without any modifications. - MODEXP (
0x0000000000000000000000000000000000000005): Performs modular exponentiation. - BN128_ADD (
0x0000000000000000000000000000000000000006): Adds two points on the BN128 elliptic curve. - BN128_MUL (
0x0000000000000000000000000000000000000007): Multiplies a point on the BN128 elliptic curve by a scalar. - BN128_PAIR (
0x0000000000000000000000000000000000000008): Performs a pairing check on the BN128 elliptic curve. - BLAKE2 (
0x0000000000000000000000000000000000000009): Computes the BLAKE2 cryptographic hash of the input data. - KZG_POINT_EVALUATION (
0x000000000000000000000000000000000000000a): Evaluates a KZG commitment at a given point. - BLS12_381_G1_ADD (
0x000000000000000000000000000000000000000b): Adds two points in the G1 group on the BLS12-381 elliptic curve. - BLS12_381_G1_MUL (
0x000000000000000000000000000000000000000c): Multiplies a point in the G1 group on the BLS12-381 elliptic curve by a scalar. - BLS12_381_G1_MSM (
0x000000000000000000000000000000000000000d): Performs a multi-scalar multiplication in the G1 group on the BLS12-381 elliptic curve. - BLS12_381_G2_ADD (
0x000000000000000000000000000000000000000e): Adds two points in the G2 group on the BLS12-381 elliptic curve. - BLS12_381_G2_MUL (
0x000000000000000000000000000000000000000f): Multiplies a point in the G2 group on the BLS12-381 elliptic curve by a scalar. - BLS12_381_G2_MSM (
0x0000000000000000000000000000000000000010): Performs a multi-scalar multiplication in the G2 group on the BLS12-381 elliptic curve. - BLS12_381_PAIRING (
0x0000000000000000000000000000000000000011): Performs a pairing check on the BLS12-381 elliptic curve. - BLS12_381_MAP_FP_TO_G1 (
0x0000000000000000000000000000000000000012): Maps an element of the base field to a point in the G1 group on the BLS12-381 elliptic curve. - BLS12_381_MAP_FP2_TO_G2 (
0x0000000000000000000000000000000000000013): Maps an element of the quadratic extension field to a point in the G2 group on the BLS12-381 elliptic curve. - SECP256R1_VERIFY (
0x0000000000000000000000000000000000000100): Verifies an ECDSA signature on the SECP256R1 elliptic curve.
L2 Rollup
Fluent is built as an Ethereum L2 execution layer with a blended execution architecture.
At the rollup level, the system combines:
- execution inside Fluent’s runtime model,
- deterministic state transition outputs,
- proof/data pipelines anchored to Ethereum.
Why this matters for architecture
Many design choices in Fluent’s execution layer are shaped by rollup constraints:
- deterministic execution for reproducible proofs,
- explicit host/runtime boundaries for verifiable state transitions,
- predictable metering and side-effect ordering.
So rollup concerns are not “outside” architecture—they are one of its core inputs.
Scope
The chapters below describe the high-level context for:
- proof systems,
- data availability.
They are intentionally architecture-oriented and may evolve as proving/DA implementation details evolve.
ZK Proof System
Fluent’s blended execution architecture is designed with proving in mind from the start.
Core objective
Represent state transitions from heterogeneous execution environments in one coherent model, instead of proving many isolated execution domains with heavy emulation boundaries.
Architectural consequences
This objective impacts multiple layers:
- rWasm execution representation,
- interruption/resume control flow,
- syscall determinism,
- gas/fuel accounting consistency,
- deterministic commit semantics.
In other words, proving is a first-class architecture constraint, not an optional optimization.
Practical note
Exact proving pipeline details (circuits, proving backends, recursion strategy, batching) may change over time. This chapter focuses on architecture intent and constraints, not a frozen implementation spec.
Data Availability
Data availability (DA) is a fundamental part of rollup correctness.
Why it matters
Even if execution is correct, verification requires access to the data needed to reconstruct and validate state transitions.
For Fluent, DA constraints influence:
- what transition artifacts must be committed,
- how verifiers can reconstruct state updates,
- how proving and execution pipelines stay auditably connected.
Architecture relationship
DA should be considered together with execution and proving design:
- execution defines what data is produced,
- proving defines what must be attested,
- DA defines what must remain accessible for independent validation.
Scope note
This chapter is intentionally high-level. For concrete DA pipeline implementation and current deployment settings, use current network/runtime documentation.
Fluentbase Framework
Fluentbase is a framework offering an SDK and a proving system for Fluent STF. Developers can leverage this framework to create shared applications (smart contracts), dedicated applications, system precompile contracts, or custom STFs.
WARNING: Don’t use in production!
Fluentbase is under experimental development and remains a work in progress. The bindings, methods, and naming conventions within the codebase are not yet standardized and may undergo significant changes. Furthermore, the codebase has not been audited or thoroughly tested, which could result in vulnerabilities or crashes.
Modules
bin: Binary tools used in build/runtime workflows.crates: Houses Fluentbase modules. The exact set evolves, but major crate families include:codec/codec-derive: ABI codec and derive support.contracts: system/runtime contracts.crypto: cryptographic helpers.evm/revm: EVM execution and Fluent-specific REVM integration.genesis: genesis-generation utilities/assets.runtime: core runtime execution layer.sdk/sdk-derive: developer SDK, macros, and runtime bindings.types: shared primitive/protocol types.node: node integration components.- Additional runtime-family and testing support crates (for example SVM-related and harness crates).
e2e: end-to-end tests for execution behavior and compatibility paths.examples: contract/application examples built with Fluentbase SDK.
Note: older references to crate names such as
core,poseidon, orzktriemay appear in historical discussions. Always treat the currentcrates/*tree in the repository as the source of truth.
Build and Testing
To build Fluentbase, a Makefile is available in the root folder that builds all required dependencies and examples.
Run the make command to build all contracts, examples, and genesis files.
The resulting files can be found in:
crates/contracts/assets: WASM and rWASM binaries for all precompiled contracts and system contracts.crates/genesis/assets: Reth/geth compatible genesis files with injected rWASM binaries (used by reth).examples/*: Each folder containslib.wasmandlib.watfiles matching the compiled example bytecode.
For testing, the complete EVM official testing suite, which consumes significant resources, is included. Increase the Rust stack size to 20 MB for testing:
RUST_MIN_STACK=20000000 cargo test --no-fail-fast
Note: test status evolves continuously. Check current CI and repository test reports for authoritative pass/fail state.
Examples
Fluentbase SDK can be used to develop various applications, generally using the same interface. Below is a simple application developed using Fluentbase:
#![cfg_attr(target_arch = "wasm32", no_std)]
extern crate fluentbase_sdk;
use fluentbase_sdk::{basic_entrypoint, derive::Contract, SharedAPI};
#[derive(Contract)]
struct GREETING<SDK> {
sdk: SDK,
}
impl<SDK: SharedAPI> GREETING<SDK> {
fn deploy(&mut self) {
// any custom deployment logic here
}
fn main(&mut self) {
// write "Hello, World" message into output
self.sdk.write("Hello, World".as_bytes());
}
}
basic_entrypoint!(GREETING);
Supported Languages
Fluentbase SDK currently supports writing smart contracts in:
- Rust
- Solidity
- Vyper
Fluentbase Operation
Fluentbase operates around Fluent’s rWASM VM (reduced WebAssembly) and runtime integration layers. rWASM is Wasm-derived and optimized for Zero-Knowledge (ZK)-oriented execution/proving constraints, with a reduced/reshaped representation compared with unrestricted host Wasm binaries.
Limitations and Future Enhancements
As of now, Fluentbase does not support floating-point operations. However, this feature is on the roadmap for future enhancements.
rWASM
rWASM (reduced WebAssembly) is Fluent’s deterministic Wasm-derived execution representation and runtime stack.
It is designed for environments that care about:
- predictable execution behavior,
- explicit metering controls,
- proving-friendly structure.
What rWASM is (and is not)
rWASM is not just a documentation alias for plain WebAssembly binaries. It is a constrained execution model with:
- translation from Wasm inputs,
- a compact module format,
- a dedicated opcode/runtime model,
- host import/syscall integration boundaries.
At the same time, it keeps a Wasm-oriented developer path so existing tooling ecosystems remain practical.
Current source of truth
For implementation-level details, use fluentlabs-xyz/rwasm docs first:
- architecture,
- pipeline,
- module format,
- VM/fuel behavior,
- opcode specification,
- security considerations.
This tech-book chapter summarizes architecture intent.
Key architecture properties
-
Deterministic execution model
- runtime semantics are designed for reproducible transitions.
-
Compact module representation
- rWASM module encoding is structured for predictable runtime consumption.
-
Host boundary clarity
- imports/syscalls are explicit integration points, not implicit ambient powers.
-
Fuel-aware execution
- metering is a first-class runtime concern.
-
Strategy abstraction
- native rWASM VM path and optional compatibility/backtesting strategy paths can coexist.
Reading map
- Motivation: why Fluent uses a reduced Wasm representation.
- Technology: architecture-level mechanics (pipeline, module format, VM/fuel, feature gates).
Motivation
WebAssembly gives Fluent a strong developer bridge: mature language support, broad tooling, and well-understood execution semantics.
But plain Wasm module/runtime assumptions are not automatically ideal for deterministic, proving-oriented blockchain execution.
Problem Fluent is solving
If heterogeneous execution is handled as many isolated VM silos, proving and control-plane complexity grows quickly.
Fluent’s approach is to keep execution in one blended architecture and use rWASM as the Wasm-derived execution representation aligned with that model.
Why Wasm-derived instead of machine ISA-first
Compared with low-level machine ISAs, Wasm offers:
- better developer ecosystem reach,
- cleaner portability story,
- structured semantics that can be translated/constrained for deterministic execution.
rWASM keeps these advantages while reshaping representation where needed for runtime determinism and proof ergonomics.
What rWASM changes conceptually
At architecture level, rWASM aims to reduce execution-representation friction by:
- flattening/normalizing control-flow and metadata patterns where useful,
- reducing dependence on runtime-heavy mapping layers,
- making module/runtime boundaries explicit.
The objective is not “invent a new language,” but to make Wasm-family execution easier to govern and verify in Fluent’s protocol context.
Expected outcome
rWASM helps Fluent combine:
- Wasm-friendly developer onboarding,
- deterministic host/runtime coordination,
- proving-oriented execution structure.
That combination is why rWASM is central to Fluent’s blended execution design.
Technology
This chapter summarizes the current rWASM technology model as implemented in fluentlabs-xyz/rwasm.
High-level pipeline
rWASM uses a clear execution pipeline:
- Input Wasm module
- Parser + validator
- rWASM translator/compiler
- Compact rWASM module artifact
- Execution strategy (native rWASM VM, plus optional compatibility strategy)
Core subsystems
Compiler / translator
The compiler layer parses and validates Wasm, then rewrites execution into the rWASM-oriented opcode stream and metadata layout.
Module model
rWASM modules are serialized as explicit runtime artifacts consumed by the VM. Current format includes a fixed header/magic + ordered sections (code/data/element/hints and source metadata).
Opcode model
The opcode surface is explicitly defined and version-sensitive.
Important operational rule:
- opcode ordering and module encoding are wire-compatibility concerns,
- changing them is a format-level change, not a cosmetic refactor.
Runtime VM
The native VM is a stack-machine executor with:
- value/call stack transitions,
- memory/table/global handling,
- trap model,
- host import linkage,
- resumable context hooks.
Strategy abstraction
rWASM supports a strategy layer so native execution and compatibility/comparison backends can be selected by feature/runtime setup.
Fuel and metering
Fuel is a first-class runtime primitive:
- bounded mode: deterministic out-of-fuel traps,
- unbounded mode: for controlled/testing contexts,
- reset/remaining behavior exposed by runtime store interfaces.
Fuel is consumed by both instruction paths and host-mediated operations depending on integration policy.
Tracing and observability
With tracing enabled, runtime emits instruction/memory/table-oriented execution traces for debugging and analysis.
This is useful for:
- differential checks,
- profiling,
- proving/debug instrumentation.
Feature-gated runtime surface
rWASM behavior depends on enabled features (std, wasmtime, fpu, tracing, etc.).
Feature combinations are part of the effective runtime surface and should be pinned in production builds.
Security posture (architecture level)
Current rWASM docs emphasize:
- validation-first execution,
- fail-safe traps and bounds checks,
- host boundary determinism requirements,
- DoS controls via size/fuel/resource policies.
In practice, VM determinism and host determinism are both required. A deterministic VM with nondeterministic host imports is still nondeterministic at system level.
Implementation references
For exact, current behavior see:
rwasm/docs/architecture.mdrwasm/docs/pipeline.mdrwasm/docs/module-format.mdrwasm/docs/vm-and-fuel.mdrwasm/docs/opcodes.mdrwasm/docs/security-considerations.md
Specifications
Address Format
Address format is used to compute paths inside the trie, which makes interoperability between different EEs difficult without additional adapters, routing rules, or precompiled/runtime support.
| Chain | Address Format | Curve | Size |
|---|---|---|---|
| EVM | keccak256(uncompressed(G^x)[1:])[12..] | secp256k1 | 160 bits |
| SVM | G^SHA512(x)[..32] | ed25519 | 256 bits |
| FVM | SHA256(uncompressed(G^x)) | secp256k1 | 256 bits |
Table 1. Different blockchains use different address schemes and elliptic curves.
Naive address mapping is usually insufficient, because proving and security assumptions differ across curves and derivation schemes. Practical interoperability therefore needs protocol-level design (projection/routing/compatibility layers), not only client-side address translation.
Our account/state system leverages a Sparse Merkle Binary Trie (SMBT) powered by the Poseidon hashing function. This implementation utilizes 254-bit elliptic curve points to compute paths within the trie by hashing addresses with Poseidon.
Let’s examine how trie keys are calculated for Ethereum and Solana blockchains:
- For an Ethereum address, it pads to 20 bytes with zeros to achieve a 32-byte size, then splits into two elements to calculate the binary path.
- For a Solana address, it calculates the path using a 32-byte address, which is longer than the Ethereum’s 20-byte address.
The current function cannot handle Ethereum and Solana addresses simultaneously because they occupy different trie spaces due to Ethereum’s 20-byte padding. This issue also affects Fuel address formats and other blockchain systems, such as Polkadot, which employs the Ristretto curve. Consequently, native interoperability across these platforms is not feasible without altering blockchain address formats and cryptographic methods.
Solution
Fully Isolated EE
For Fully Isolated Execution Environments (EE), address projection (also known as account emulation) is employed. In this model, an account is stored within a special EE smart contract (or executor) responsible for managing all balance operations. This setup ensures that all balances of the isolated EE coexist in the same account space and can interact with external EEs only through specific system calls.
Partially Compatible EE
For Partially Compatible Execution Environments (EE), the same address format and derivation standards as used by Ethereum must be followed. The address derivation is as follows:
CREATE(contract deployment):h(address || nonce)CREATE2:h(deployer, salt, init_code_hash)