Architecture · Explanation
WASM contracts and host capabilities
The WASM environment runs modules in Wasmtime with invocation-local memory, fuel, call metadata, and declared accounts. Useful host services—account access, cryptography, supported cross-VM calls, and HILSA reads—are explicit imports. The module does not receive a general filesystem, network client, or process environment.
The active transaction ABI is Singularity's native WASM ABI. A module compiled for another host, including ordinary CosmWasm, must match the actual imports and exports before it can run.
The sections below explain how module entry, account authority and nested work fit together. Use HILSA interface reference for its exact host-call records and readiness checks before depending on an external route.
Deploy a module, then invoke its native ABI
A deployment targets WASM_PROGRAM_ID, with the new program account first in the instruction. The payload is:
e7 de 91 0d || wasm_bytecodeThe target must be writable, a transaction signer, fresh, system-owned, and empty. It must already hold the rent-exempt lamports for the resulting account; deployment does not mint them. Bytecode must be nonempty and no larger than 512 KiB. The stored representation is 0x01 || wasm_bytecode, owned by WASM_PROGRAM_ID and executable. Deploy format, admission.
Once executable, that account routes to invocation. The current seam does not offer in-place replacement of executable code. Validation at deploy time checks the module, but missing native-ABI exports can still fail on first invocation.
The required exports are:
memory
allocate(i32 message_length) -> i32 message_pointer
entrypoint(i32 message_pointer, i32 message_length) -> i32 exit_codeThe host calls allocate, verifies a non-negative pointer and a bounded memory range, copies instruction bytes, and invokes entrypoint. Zero means success. A nonzero exit, missing/mistyped export, invalid pointer, trap, or exhausted fuel fails the frame. Invocation ABI.
A minimal ABI fixture is:
(module
(memory (export "memory") 1)
(func (export "allocate") (param i32) (result i32)
i32.const 0)
(func (export "entrypoint") (param i32 i32) (result i32)
i32.const 0))This illustrates an empty successful invocation; it neither stores data nor proves a deployment. Real code must provide an allocator appropriate to its own memory usage.
Account access is scoped twice
The transaction wrapper gathers only this instruction's accounts and derives signer/writable sets from the authenticated transaction. The executor intersects delegated authority with that working set. An account elsewhere in the transaction is not automatically available to this WASM instruction.
read_account and get_balance read declared clones. write_account mutates an existing declared writable, non-executable account with permitted WASM ownership. The result is then checked again before merge: executable changes, rent-epoch changes, foreign-owned writes, and individual lamport increases are refused. Scope and publication.
This outer policy is stricter than the names of some helpers suggest. A locally successful transfer that increases a destination balance can still fail the publication guard. Likewise, an agent_trigger helper constructing a foreign-owned funded account does not establish a usable persisted agent-creation path. Use an implemented native transaction route for those effects.
The active executor works on context account clones. The separate WasmStateAdapter library is not automatically the backing store used by every host import.
Host services and their boundaries
| Family | Useful imports | Boundary |
|---|---|---|
| Accounts | read_account, write_account, get_balance, transfer | Declared capabilities and outer publication checks |
| Context | caller, self_address, block_height, block_timestamp | Authenticated call context and deterministic chain time |
| Diagnostics | log, abort | Captured logs; abort fails the frame |
| Cryptography | sha256, keccak256, ed25519_verify, secp256k1_recover | Bounded guest-memory inputs and explicit host fuel |
| Composition | call_evm, call_wasm | Shared dispatcher, attenuated authority, bounded child output |
| SVM compatibility | call_svm | Narrow canonical System transfer path, not arbitrary SVM programs |
| Foreign reads | hilsa_chain_head, hilsa_interop_snapshot, hilsa_deposit_processed | Committed state and minimum verification/finality classes |
| External writes | hilsa_send_message | Authorized caller, payload/fuel bounds, outbox activation and settlement path |
There is no native call_move import. Nested WASM still enters entrypoint; reserved entry-selection parameters do not select arbitrary exports. EVM and WASM child output copied into guest memory is capped at 4,096 bytes. Import-specific negative sentinels and result records must be decoded according to the actual ABI; a successful host call is not always represented by the same integer. Native linker.
HILSA reads use the dispatcher's state when the legacy context state field is unset. The older bridge_balance and bridge_withdraw imports use that context field directly and are not equivalent to the working HILSA read path. Their current live-context behavior must not be advertised as complete bridge support. See HILSA interfaces.
Fuel and nested state move together
Wasmtime meters guest instructions; the host explicitly charges costly native work. Hashes charge a base plus bytes, signature operations have fixed charges, and HILSA reads/writes charge their own costs. An infinite guest loop therefore exhausts fuel, and repeated host work must not escape that budget.
Before a nested call, the parent stages validated account changes and flushes its consumed fuel to the shared transaction context. The child reads the journal, not an obsolete parent snapshot. On return, the parent refreshes declared accounts and clamps its local fuel to the shared remainder. Child work is not charged twice, and a failed child does not refund executed work. Compute barriers, state barriers.
A state/journal/engine defect is latched in the context. Even if the guest ignores the immediate negative sentinel, the enclosing executor cannot accept the frame as successful. Expected guest errors and infrastructure defects have different handling.
The source declares a 256-page/16 MiB memory constant, but the inspected store construction does not install a limiter using it. Do not treat that declaration as an enforced memory cap. Module declarations and Wasmtime's configured behavior still matter.
Outbox emission is a distinct state seam
The HILSA write helper calls outbox::emit_via_state. That is a direct shared-state helper rather than an ordinary returned WASM account diff. Its own atomicity does not, by itself, establish rollback with a later WASM trap.
The ordinary executor's outbox flag is disabled pending deterministic activation. An integration enabling that capability must validate transaction-journal ownership and later-failure behavior; presence of the import is not a release gate being satisfied. Write helper. The complete external path is explained in HILSA writes and settlement.
CosmWasm and caching
The tree contains CosmWasm Region, storage, address, crypto, and message helpers. The production executor registers its native host linker, not the standalone CosmWasm host shim. Normal transactions still expect allocate and entrypoint; parsed CosmWasm response messages do not imply dispatched bank sends or nested Wasm calls.
The query path calls a Region-style query(env, msg) with no writable/signing authority and discards state. It only works for modules that can instantiate against the available imports. A standard contract requiring unregistered db_read or db_write imports fails earlier. Executor and query path, query invocation.
Compiled-module cache entries bind public key and exact bytecode fingerprint. However, the top-level transaction path constructs a fresh executor and cache per call, so it does not establish reuse across transactions. Mutable stores remain isolated regardless of cache lifetime. Transaction construction, cache implementation.
Tests cover the native ABI, missing exports, fuel exhaustion, undeclared writes, canonical System-transfer framing, shared-journal refresh, and HILSA error records. CosmWasm helper tests establish their individual helper behavior, not a complete production CosmWasm environment. These tests were inspected; continue with composition or validation strategy for targeted acceptance work.