WASM Runtime Environment in Amadeus Protocol: How Smart Contracts Execute on Wasmer 7.1.0
The Amadeus Protocol WASM runtime environment is a Wasmer 7.1.0-based execution engine that uses single-pass compilation, deterministic metering, and an LRU artifact cache to run WebAssembly smart contracts with gas-bounded execution.
The Amadeus node executes smart-contract logic by embedding a purpose-built WebAssembly runtime. Built on Wasmer 7.1.0, the system combines fast compilation, execution metering, and host-function injection to provide deterministic, metered contract execution. This article examines the runtime's architecture, tracing actual implementation paths in the amadeusprotocol/node repository.
Core Architecture of the WASM Runtime
The runtime environment consists of six integrated components that handle everything from initial validation to gas accounting during execution.
Wasmer Engine with Single-Pass Compilation
At the heart of the system is the Wasmer Engine, configured with Wasmer's single-pass compiler for predictable compilation times. In ex/native/rdb/src/consensus/bic/wasm.rs, the engine initializes via EngineBuilder with Singlepass as the backend:
// ENGINE_VERSION_TAG identifies the runtime version for cache invalidation
const ENGINE_VERSION_TAG: &str = "wasmer-7.1.0-singlepass";
The compile_and_cache_module function transforms raw Wasm bytes into an executable Module, then serializes it for storage in the ArtifactCache. This design prioritizes fast compilation over optimization, ensuring consistent latency across contract deployments.
Artifact Cache for Compiled Modules
The runtime maintains an LRU cache with a 4 GiB maximum size to eliminate redundant compilation. The cache keys modules by SHA-256(ENGINE_VERSION_TAG || wasm_bytes), automatically invalidating entries when the runtime version changes.
This caching layer proves critical for throughput: after first compilation, subsequent contract invocations reuse the serialized artifact without re-invoking the compiler.
Metering Middleware for Gas Accounting
Deterministic execution cost requires operation-level metering. The runtime injects wasmer_middlewares::metering during module compilation, with COST_PER_OP_WASM set to 1 execution point per Wasm operation in protocol.rs:
pub const COST_PER_OP_WASM: i128 = 1;
Two functions in wasm.rs synchronize the metered state with the transaction's budget:
budget_sync_in— initializes metering points fromapplyenv.exec_leftset_remaining_points— writes updated gas back after host function calls
When gas exhaustion occurs, the VM panics with "exec_invalid_module" or "exec_return_value_too_large" depending on the failure context.
Host Environment and Imported Functions
The HostEnv struct exposes controlled interfaces for contracts to interact with the Amadeus state machine. Only two host functions are currently available:
| Function | Implementation | Purpose |
|---|---|---|
log |
import_log_implementation |
Emit structured logs from contract execution |
return |
import_return_implementation |
Return a byte vector to the caller |
These imports are injected during setup_wasm_instance, which prepares a new Instance with accessible linear memory and the metered execution context.
ApplyEnv: Transaction Context Container
The ApplyEnv struct (defined in consensus_apply.rs) carries mutable execution state across the contract lifecycle:
// Fields tracked during contract execution
- exec_left: remaining execution budget
- storage_left: remaining storage budget
- logs: accumulated contract logs
- caller: transaction initiator address
The runtime updates these fields throughout execution, ensuring atomic budget consumption across storage operations and Wasm opcodes.
Validation Layer
Before any compilation occurs, validate_contract enforces structural constraints using wasmparser:
// Size limits enforced via protocol constants
protocol::WASM_MAX_BINARY_SIZE // maximum contract binary size
This validation prevents resource exhaustion attacks through malformed Wasm and guarantees that only well-formed modules reach the compiler.
Contract Execution Flow: From Bytes to Result
The Amadeus Protocol WASM runtime follows a six-stage pipeline for every contract interaction:
-
Contract submission — Wasm binary stored via the
contractBIC (binary interface contract) -
Validation —
validate_contractparses withwasmparser, enforcesprotocol::WASM_MAX_BINARY_SIZE -
Caching — compiled module serialized and cached; cache key derived from version-tagged hash
-
Instance creation —
setup_wasm_instancecreatesInstance, injectsHostEnv, syncs metering -
Function call —
call_contract(exposed ascall_wasmvm) executes entry point with provided arguments -
Budget accounting — remaining gas read from middleware and written to
applyenv.exec_left
The entire pipeline executes within the native RDB crate, which serves as the Amadeus node's execution layer.
Runtime Configuration and Constants
Protocol-level parameters governing execution are centralized in ex/native/rdb/src/consensus/bic/protocol.rs:
pub const COST_PER_OP_WASM: i128 = 1; // execution cost per Wasm op
pub const WASM_MAX_BINARY_SIZE: usize = /* ... */; // maximum allowed Wasm size
pub const WASM_MAX_PTR_LEN: usize = /* ... */; // max host-pointer buffer length
pub const WASM_MAX_PANIC_MSG_SIZE: usize = /* ... */; // max panic message size
These constants ensure deterministic fees across all nodes in the network.
Code Examples: Deployment and Execution
Validating a New Contract Deployment
use rdb::consensus::bic::{wasm, protocol};
// Initialize execution environment with caller and budgets
let mut env = ApplyEnv::new(caller_address, exec_budget, storage_budget);
// Extract Wasm payload from deployment transaction
let wasm_bytes: Vec<u8> = tx.payload.clone();
// Validate before storing (parses, checks limits, tests compilation)
wasm::validate_contract(&mut env, &wasm_bytes)?;
Calling a Contract Function
use rdb::consensus::bic::wasm;
// Execute contract entry point with arguments
let result: Vec<u8> = wasm::call_contract(
&mut env, // mutable ApplyEnv for budget/log tracking
&wasm_bytes, // compiled or raw Wasm bytes
"transfer".to_string(), // exported function name
vec![ // function arguments
b"sender_addr".to_vec(),
b"recipient_addr".to_vec(),
b"100".to_vec(),
],
)?;
println!("Contract returned: {:?}", result);
These entry points — validate_contract and call_contract — constitute the primary interface between the node's consensus layer and the Wasm runtime.
Dependencies and Build Configuration
The runtime's capabilities are declared in ex/native/rdb/Cargo.toml:
[dependencies]
wasmer = "7.1.0"
wasmer-compiler-singlepass = "7.1.0"
wasmer-middlewares = "7.1.0"
wasmparser = "0.x"
This dependency set pins the Wasmer 7.1.0 stack with single-pass compilation and metering support.
Summary
- The Amadeus Protocol WASM runtime embeds Wasmer 7.1.0 with single-pass compilation for predictable performance
- Metering middleware charges
COST_PER_OP_WASM = 1per operation, synchronized viabudget_sync_inandset_remaining_points - A 4 GiB LRU artifact cache eliminates redundant compilation using SHA-256 keyed by engine version and Wasm bytes
- HostEnv exposes minimal host functions (
log,return) with linear memory access - ApplyEnv tracks mutable execution state including gas budgets, storage limits, and emitted logs
- All execution paths reside in
ex/native/rdb/src/consensus/bic/wasm.rsandconsensus_apply.rs
Frequently Asked Questions
What compiler does the Amadeus WASM runtime use?
The runtime uses Wasmer's single-pass compiler via wasmer-compiler-singlepass version 7.1.0. This compiler prioritizes fast, predictable compilation times over optimization depth, ensuring consistent latency for contract deployment and execution across the network.
How does Amadeus Protocol prevent infinite loops in smart contracts?
The runtime injects metering middleware that decrements execution points for every Wasm operation. When applyenv.exec_left reaches zero, the VM panics with "exec_invalid_module". This deterministic gas model guarantees that all executions terminate within bounded resource consumption.
Where are compiled Wasm modules cached in the Amadeus node?
Compiled modules are stored in an LRU artifact cache with a 4 GiB maximum size, implemented in wasm.rs. The cache uses keys derived from SHA-256(ENGINE_VERSION_TAG || wasm_bytes), automatically invalidating entries when the runtime version changes to prevent stale code execution.
What host functions are available to Amadeus smart contracts?
Contracts can access exactly two host functions: log for emitting structured logs and return for returning a byte vector to the caller. These are implemented as import_log_implementation and import_return_implementation in wasm.rs, with all other system interactions prohibited for determinism and security.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →