Security Considerations for WASM Contracts on Amadeus: A Complete Technical Guide

Amadeus executes WASM smart contracts inside a sandboxed Wasmer runtime with defense-in-depth validation that enforces binary size limits, structural constraints, gas metering, and a strict two-function import whitelist.

The Amadeus blockchain relies on WebAssembly (WASM) as its smart contract execution format, running inside a carefully hardened environment. Every contract undergoes rigorous static and dynamic checks before and during execution, ensuring that malicious or buggy code cannot compromise the network. This guide examines the security considerations for WASM contracts on Amadeus as implemented in the amadeusprotocol/node repository.

Core Security Architecture

The Amadeus execution model follows a defense-in-depth strategy with three interconnected layers: static validation intercepts malformed modules before instantiation; runtime metering bounds computational cost; and import isolation prevents sandbox escape.

The Wasmer Sandbox Foundation

Amadeus builds its execution environment on the Wasmer WebAssembly runtime. This provides memory-safe execution, but the node adds substantial hardening through custom middleware and validation logic concentrated in ex/native/rdb/src/consensus/bic/wasm.rs.

Static Module Validation

Every WASM binary passes through check_module_limits (lines 648–709 in wasm.rs) before execution. This function implements nine distinct security checks that reject malformed or malicious modules with explicit error strings.

Binary Size and Complexity Limits

Limit Constant Purpose Location
Maximum binary size protocol::WASM_MAX_BINARY_SIZE Prevents DoS from oversized uploads Lines 648–652
Function count Internal limit Stops parser exhaustion attacks Lines 658–665
Global count Internal limit Limits module complexity Lines 665–670
Export count Internal limit Restricts exposed interface surface Lines 670–676
Import count Internal limit Enforces minimal dependency surface Lines 676–682
Code body count Internal limit Prevents excessive function bodies Lines 682–686

Violating any of these triggers immediate rejection with identifiers like wasmparser_function_count_exceeds_limit.

Memory Layout Protections

The data section validator (lines 686–709) enforces four critical constraints:

  • Offset must be i32.const — No dynamic or complex offset calculations
  • Offset must be non-negative — Prevents wrapping to high memory addresses
  • No overlap with reserved memory — Protects the node's internal region
  • First 65 KB reserved — The VM environment occupies this region; contracts cannot write here

These rules block out-of-bounds writes that could corrupt host state or escape the sandbox.

Start Section Prohibition

Amadeus explicitly disallows WASM start sections (line 709). This prevents contracts from defining implicit entry points that execute before the intended call frame, ensuring all execution flows through the contract's declared public interface.

Runtime Security Mechanisms

Once validated, contracts execute under continuous monitoring with hard resource boundaries.

Gas Metering via Wasmer Middleware

Every WASM operation consumes gas through Wasmer's metering middleware. The configuration appears in wasm.rs (lines 22–24, 52–55):

// wasm.rs lines 22-24
use wasmer::Middleware;
use wasmer_compiler_singlepass::Singlepass;
use wasmer_middlewares::Metering;

// Cost per WASM operation
const COST_PER_OP_WASM: u64 = 1;

The metering state synchronizes with the transaction budget through budget_sync_in (lines 52–60). When gas exhausts, the VM aborts immediately—no partial state commits, no resource exhaustion.

Pointer and Return Value Limits

Two additional bounds protect against memory abuse:

  • WASM_MAX_PTR_LEN (~1 MiB): Enforced in import_log_implementation and import_return_implementation (lines 172–194). Prevents oversized copies that could overflow the gas meter or exhaust memory.
  • WASM_MAX_PANIC_MSG_SIZE: Enforced in set_return_value (lines 45–48). Stops contracts from flooding logs with arbitrarily large panic messages.

Exceeding WASM_MAX_PTR_LEN yields the explicit error exec_ptr_term_too_long.

Artifact Cache Protections

Compiled modules are cached with a 4 GiB limit (ARTIFACT_CACHE_MAX_BYTES). The ArtifactCache implementation (lines 45–98) prevents cache-filling attacks that could exhaust node memory through repeated deployments of distinct contracts.

Host Import Isolation

The contract's only bridge to the outside world is a strict whitelist of two functions:

Function Purpose Location
log Emit structured log messages import_log_implementation
return Return execution results import_return_implementation

No filesystem access. No networking. No OS syscalls. The HostEnv struct (lines 39–44) and setup_wasm_instance configuration establish this minimal surface.

// Conceptual structure from wasm.rs analysis
struct HostEnv {
    // Only log and return capabilities exposed
    log_fn: fn(*const u8, usize),
    return_fn: fn(*const u8, usize) -> !,
}

This isolation guarantees that contract code cannot escape the sandbox regardless of logic errors or intentional attacks.

Practical Secure Contract Development

The following patterns demonstrate compliance with Amadeus security constraints.

Minimal Compliant Contract

// contract_samples/rust/examples/counter.rs
use amadeus_sdk::wasm::{log, return_};

#[no_mangle]
pub extern "C" fn increment(state: *mut u64) {
    // SAFELY read/write within contract linear memory
    unsafe {
        let value = state.read_unaligned();
        state.write_unaligned(value.wrapping_add(1));
    }
    log(b"counter incremented");
    // Empty payload return—respects ptr/len limits
    return_(b"", 0);
}

Security characteristics:

  • Uses only whitelisted host functions (log, return_)
  • Operates exclusively on contract-owned linear memory
  • No dynamic allocation, keeping binary size minimal
  • No external pointers or host memory references

Safe Logging Pattern

use amadeus_sdk::wasm::log;

#[no_mangle]
pub extern "C" fn operation(state: *mut u8) {
    // Fixed-size message guaranteed under WASM_MAX_PTR_LEN
    let msg = b"operation completed";
    log(msg);  // ~19 bytes, well under ~1 MiB limit
}

Attempting to log a buffer exceeding WASM_MAX_PTR_LEN triggers runtime abortion with exec_ptr_term_too_long.

Deployment Validation Flow


# Compile to WASM target

cargo build --target wasm32-unknown-unknown --release

# Deploy via CLI—triggers full validation pipeline

amadeus-node contract deploy \
  --binary target/wasm32-unknown-unknown/release/counter.wasm \
  --init-function "increment" \
  --init-args ""

The CLI invokes contract_validate (lib.rs line 819), which chains to validate_contract and check_module_limits. Any limit violation aborts deployment with a specific error code before the transaction reaches consensus.

Critical Source Files

Path Responsibility Key Functions
ex/native/rdb/src/consensus/bic/wasm.rs Core WASM security check_module_limits, setup_wasm_instance, budget_sync_in, ArtifactCache
ex/native/rdb/src/consensus/consensus_apply.rs Execution entry point call_wasmvm
ex/native/rdb/src/lib.rs High-level validation contract_validate (line 819)
contract_samples/rust/examples/counter.rs Reference implementation Compliant minimal contract
contract_samples/rust/examples/nft.rs Advanced patterns Storage-safe complex contract

Summary

  • Static validation in check_module_limits rejects malformed modules before execution through nine structural checks
  • Gas metering via Wasmer middleware bounds computation with per-operation costs and budget synchronization
  • Memory safety is enforced through data offset validation, reserved region protection, and pointer length limits
  • Import isolation restricts contracts to exactly two host functions: log and return
  • Cache limits prevent memory exhaustion through the 4 GiB ArtifactCache bound
  • Deployment validation runs the full security pipeline via contract_validate before any contract persists

Frequently Asked Questions

What happens if a WASM contract exceeds the binary size limit?

The check_module_limits function rejects the deployment immediately with error code wasmparser_binary_size_exceeds_limit. The transaction fails before the contract is stored or executed, protecting the network from denial-of-service through oversized uploads.

Can Amadeus WASM contracts perform network I/O or file system operations?

No. The import whitelist in setup_wasm_instance exposes only log and return. No networking, filesystem, or OS interfaces are available. This is enforced at the Wasmer runtime level—attempting to import forbidden functions causes instantiation failure.

How does Amadeus prevent infinite loops in WASM contracts?

Through gas metering. Every WASM operation consumes COST_PER_OP_WASM units from the transaction budget. The Metering middleware tracks consumption synchronously; exhaustion triggers immediate VM termination via budget_sync_in. This bounds execution regardless of control flow structure.

What error occurs when a contract tries to log an oversized message?

The import_log_implementation function checks buffer length against WASM_MAX_PTR_LEN (approximately 1 MiB). Exceeding this limit aborts execution with error exec_ptr_term_too_long, preventing log flooding attacks.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →