A Rust SDK for building subgraphs on The Graph.
Status: Early development — requires graph-node modifications (see Architecture).
AssemblyScript subgraphs suffer from broken nullable handling, missing closures, opaque compiler crashes, and a hostile debugging experience. Graphite provides a proper Rust alternative with:
- Type safety —
Option<T>instead of runtime null crashes - Ergonomic APIs —
#[derive(Entity)]and#[handler]macros - Native testing —
cargo testwith mock host functions, no PostgreSQL required - Rust ecosystem — iterators, closures, and crates that actually work
- ~2× performance — Rust WASM is faster than AssemblyScript
Graphite does not try to conform to AssemblyScript's memory layout. Instead, it defines a clean Rust-native ABI that requires modifications to graph-node:
┌─────────────────────────┐
│ HostExports<C> │ ← Language-agnostic (unchanged)
│ (store, crypto, ipfs) │
└────────────┬────────────┘
│
┌──────────────────┼──────────────────┐
│ │
┌─────────┴─────────┐ ┌─────────────┴────────────┐
│ AscAbiHost │ │ RustAbiHost │
│ (current code) │ │ (new, in graph-node) │
│ AscPtr<T> │ │ ptr+len, simple serde │
└───────────────────┘ └──────────────────────────┘
Graph-node changes required:
- Detect
language: wasm/rustin subgraph.yaml manifest - Add
RustAbiHostwith ptr+len FFI protocol - Use simple serialization (not AS TypedMap)
See rfc-rust-subgraph.md for the full design.
graphite/
├── graphite/ # Core SDK crate
├── graphite-macros/ # Proc macros (#[derive(Entity)], #[handler])
└── graphite-cli/ # CLI tooling (graphite init, codegen, build, deploy)
use graphite::prelude::*;
#[derive(Entity)]
pub struct Transfer {
#[id]
id: String,
from: Address,
to: Address,
value: BigInt,
}
#[handler]
pub fn handle_transfer(host: &mut impl HostFunctions, event: &TransferEvent) {
let mut transfer = Transfer::new(&event.id());
transfer.from = event.from;
transfer.to = event.to;
transfer.value = event.value.clone();
transfer.save(host);
}Handlers run natively with MockHost — no WASM, no graph-node needed:
#[test]
fn transfer_creates_entity() {
let mut host = MockHost::default();
let event = TransferEvent { /* ... */ };
handle_transfer(&mut host, &event);
assert_eq!(host.store.entity_count("Transfer"), 1);
}graphite init my-subgraph --network mainnet
graphite codegen # Generate Rust types from ABI + schema
graphite build # Compile to WASM
graphite test # Run tests (delegates to cargo test)Create a graphite.toml in your project root:
output_dir = "src/generated"
schema = "schema.graphql"
[[contracts]]
name = "ERC20"
abi = "abis/ERC20.json"- Core primitives (BigInt, Address, Bytes)
-
HostFunctionstrait +MockHostfor native testing -
#[derive(Entity)]macro with load/save/remove -
#[handler]macro with WASM wrapper generation -
FromWasmBytestrait for TLV event deserialization - ABI → Rust event struct codegen with
EventDecode+FromWasmBytes - Schema.graphql → Entity struct codegen
- CLI:
init,codegen,build,test - WASM ABI layer (Rust-native ptr+len protocol)
- no_std WASM support with alloc
See IMPLEMENTATION_PLAN.md for the detailed plan.
Files created in graph-node fork (runtime/wasm/src/rust_abi/):
-
mod.rs— MappingLanguage enum -
types.rs—ToRustWasm/FromRustWasmtraits + primitives -
entity.rs— Entity TLV serialization -
host.rs— Linker function stubs (needs async integration)
Remaining work:
- Parse
language: wasm/rustin manifest - Wire up dispatch based on MappingLanguage
- Complete async store operation integration
- Gas metering integration
- Integration testing with real subgraph
MIT OR Apache-2.0