Skip to content
Get Started

WebAssembly Plugins — ABI Reference

Files

File Runs on What
nanoconda.h (native, repo root) native only The real header, untouched.
nanoconda.h (wasm directory) guest The derived guest SDK — #include this directly from your strategy.
nanoconda_wasm_abi.h both The wire contract: struct mirrors and every function crossing the host/guest boundary. Plain C, so any language targeting wasm32 can implement it.
nanoconda_wasm_host.h host Header-only wasmtime glue: imports, the admin sequence, callback forwarding.
nacowasmplugin.cpp host The CLI loader, matching nacoplugin::runPlugin. Has its own main() — drop it (-DNC_NO_STANDALONE_MAIN) when wiring into nanoconda-cli.cpp.
example_client.cpp guest Minimal strategy example (reproduced in full in Building & Running).

The guest SDK (nanoconda.h) is a minimal-diff derivative of the real, native header: every enum, POD struct, and the listener interface are unconditional, identical source. Only dmasession, logger, and a few free functions carry small #if defined(__wasm__) sections where the implementation differs (real libnanoconda calls vs. ABI imports); all wasm-specific method bodies live together in one section at the bottom of the file, as out-of-class definitions. To see exactly what's wasm-specific: diff nanoconda.h wasm/nanoconda.h.

For the full type reference (struct fields, enum values) — since these are byte-identical between native and wasm — see the native Order Entry API, Market Data API, and Algo Control API pages. This page covers only what's specific to the wasm boundary itself.

ABI versioning

NC_WASM_ABI_VERSION in nanoconda_wasm_abi.h is checked by HostRuntime::load() against the guest's own nc_abi_version() export at load time. A guest built against a different ABI version than the host expects fails to load outright, rather than running with a silently mismatched struct layout. Bump this constant on any struct or signature change to the ABI.

Every mirrored struct's size is additionally static_assert-checked against the real, native nanoconda.h at host build time — a struct-layout drift between the native and wasm headers is a compile-time failure on the host side, not a runtime data-corruption bug.

Linkage

Wasm import/export binding is controlled by __attribute__((import_name/export_name)), not by C++ symbol linkage — so plain C++ linkage is enough everywhere in guest code, with exactly one exception: init_nanoconda_plugin is declared extern "C" purely so it has the same language linkage as the native .so entry point of the same name, which is what lets unmodified native client code build for wasm too without any changes. Nothing else in the ABI or in guest code needs extern "C" — declare nanoconda::requestShutdown() and everything else with plain C++ linkage.

Logging

logM/logL/setLevel expand to logMessage/logMessageL/setLevelL, exactly as they do natively — your strategy class needs one of these visible, typically by inheriting nanoconda::logger. They format locally in the guest (vsnprintf) and send the formatted result to the host's real nanoconda::logger; there is no separate stdio/printf path a guest can use instead.

nanoconda::logger() (no-arg constructor) routes to the host's default logger — handle 0, whichever file nacowasmplugin -l/--log named — so host-side and guest-side log messages land in the same file. Pass a filename to the constructor instead for a second, independent log file.

State persistence

nanoconda::saveState/loadState read and write the file given by nacowasmplugin's -f/--statefile argument. This is the supported way for a guest to persist data across restarts — the wasm sandbox doesn't give a guest direct filesystem access, so this ABI call is the only path to durable state. See the example strategy for the calling convention (loadState in init_nanoconda_plugin, saveState in plugin_stop).

Admin-sequence calls: declared but no-op on the guest

registerApplication, dmasession::init, subscribe, subscribeUnderlying, start, stop, and replayTrace are all declared on the guest-side dmasession, matching native exactly (no #ifdef needed in shared client code), but their wasm-side bodies are no-ops — the host already performed the equivalent action from its own CLI arguments before your guest ran. See Admin sequence is host-driven for the full explanation of why and when this matters. listSymbols is the one exception in this group: it's genuinely host-routed and callable at runtime, for entitlements discovered after the initial call.

Host-routed calls

These guest-side calls are thin proxies to the real implementation running on the host, designed to avoid an extra copy of the data where possible — see What's different from native for the complete table. In every case the call shape (arguments, return type) matches native exactly; only where the actual work happens differs.