Skip to content

Add Rust ABI support for WASM subgraphs#6462

Draft
cargopete wants to merge 6 commits intographprotocol:masterfrom
cargopete:rust-abi-support
Draft

Add Rust ABI support for WASM subgraphs#6462
cargopete wants to merge 6 commits intographprotocol:masterfrom
cargopete:rust-abi-support

Conversation

@cargopete
Copy link
Copy Markdown
Contributor

Summary

Adds support for Rust-compiled WASM subgraphs as an alternative to AssemblyScript. This introduces a new rust_abi/ module parallel to the existing asc_abi/, with a clean ptr+len FFI protocol and TLV entity serialization.

  • rust_abi/ module (~1,450 LOC) — ToRustWasm/FromRustWasm traits, TLV entity serialization, trigger serialization (RustLogTrigger/RustCallTrigger/RustBlockTrigger), and all host function linker wrappers (store, crypto, logging, data sources, IPFS, ethereum calls) with async + gas metering
  • Language detectionis_rust_module() detects "graphite" namespace imports; MappingLanguage enum stored on ValidModule
  • Dispatchbuild_linker() branches on language; handle_trigger_rust() uses Rust calling convention (allocate → write bytes → call handler(ptr, len) → reset_arena)
  • Compatibility — skips parity_wasm gas injection for Rust modules (parity_wasm can't parse modern WASM features like bulk-memory); skips AS-specific exports (id_of_type, _start)
  • Ethereum triggersToRustBytes impl for all 3 trigger types (Log, Call, Block)
  • 14 unit tests passing for entity roundtrips, trigger serialization, type roundtrips

Live-tested

Deployed an ERC20 subgraph (compiled with Graphite SDK) to this fork, indexing real USDC Transfer events from Ethereum mainnet. Full pipeline verified: block ingestion → event scanning → Rust WASM handler → entity storage → correct GraphQL query results.

Architecture

                    ┌─────────────────────────┐
                    │    HostExports<C>       │  ← Language-agnostic (unchanged)
                    │  (store, crypto, ipfs)  │
                    └────────────┬────────────┘
                                 │
              ┌──────────────────┼──────────────────┐
              │                                     │
    ┌─────────┴─────────┐             ┌─────────────┴────────────┐
    │  AscAbiHost       │             │  RustAbiHost             │
    │  (existing code)  │             │  (new, this PR)          │
    │  AscPtr<T>        │             │  ptr+len, TLV serde      │
    └───────────────────┘             └──────────────────────────┘

Known gaps (not blocking review)

  • Gas metering — parity_wasm gas injection is bypassed for Rust modules. Needs wasmtime fuel metering or a WASM-2.0-compatible alternative.
  • NEAR triggers — stubbed as unimplemented! (Ethereum-only for now)
  • Offchain/subgraph triggers — stubbed as empty bytes

Test plan

  • Existing graph-node tests pass (14 rust_abi + 17 NEAR chain tests confirmed locally)
  • WASM integration test: load Rust WASM, serialize trigger, call handler, verify entity
  • Live test: deploy Rust subgraph to running fork, index mainnet events, query via GraphQL
  • Verify AS subgraphs are completely unaffected (no changes to AS code paths)

- Add Ethereum ToRustBytes impl for Log/Call/Block triggers
- Add NEAR ToRustBytes stub (unimplemented, Ethereum-only for now)
- Propagate ToRustBytes trait bounds through instance_manager
- Skip parity_wasm gas injection for Rust modules (can't parse bulk-memory opcodes)
- Skip AS-specific exports (id_of_type, _start) for Rust modules
- Add handle_trigger_rust and invoke_handler_rust calling convention
- Add Rust host function wrappers in module/context.rs
@lutter
Copy link
Copy Markdown
Collaborator

lutter commented Mar 30, 2026

Can you explain a bit what the motivation for this is? Another ABI is a huge commitment in terms of maintenance etc. If another ABI is called for, it would be good to also see if we can avoid some of the mistakes the current ASC ABI makes.

@cargopete
Copy link
Copy Markdown
Contributor Author

@lutter ▎ Thanks for the feedback!

Motivation
The core issue is that AssemblyScript's pain points aren't cosmetic — they're structural. Broken nullable handling (type narrowing only works on locals, not property accesses), no closures (.map()/.filter() crash the compiler), opaque compiler errors, and a debugging story that amounts to "comment everything out and uncomment line by line." These aren't fixable in the SDK layer; they're baked into the language.

Meanwhile The Graph has already validated Rust→WASM as a first-class path with Substreams. A Rust mapping language is the natural extension of that investment to the subgraph layer — same WASM runtime, same graph-node infrastructure, just a different serialization boundary.

Maintenance argument
The maintenance surface is smaller than it might look. HostExports is already fully language-agnostic — it operates on native Rust types (String, HashMap<String, Value>, Vec). The AS coupling lives entirely in the serialization layer. rust_abi/ is a parallel serialization layer (~1,450 LOC), not a parallel runtime. Adding a new host function means implementing it once in HostExports and adding two thin wrappers — the existing AS one and the new Rust one. That's the full maintenance delta.

What we tried to learn from the ASC ABI's mistakes
The ASC ABI has a few known rough edges: AscPtr is stringly-typed in places, the managed heap requires graph-node to reach into AS memory and allocate on its behalf, BigInt endianness has been a source of bugs, and there's no clean versioning story.
The Rust ABI was designed to avoid these:

  • Simple ptr+len calling convention — no managed heap, no allocate-on-behalf-of-the-runtime. The Rust module owns a bump allocator; graph-node just writes into memory the module already allocated and resets the arena after each handler call.
  • Explicit TLV serialization — tagged binary format with a fixed, documented value tag table. No implicit type coercions, no silent endianness bugs (the BigInt LE fix we hit during live testing is already baked into the spec).
  • Language detection via manifest — language: wasm/rust in subgraph.yaml. Clean dispatch, no heuristics.
  • Versioned from day one — apiVersion: 0.0.1 in the manifest gives a migration path if the ABI needs to evolve.

Happy to discuss any of these design choices further, or to write a formal docs/rust-abi-spec.md or a forum post if that would help the review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants