Skip to content

How It Works

Hyperstack is a declarative data layer for Solana. You define the data shape you need, and Hyperstack handles all the infrastructure to stream it to your app.


Building data pipelines for Solana apps is painful:

  1. Manual parsing - You write custom code to decode accounts and instructions
  2. ETL complexity - You build pipelines to transform, aggregate, and store data
  3. RPC overhead - You manage websocket connections, retries, and state sync
  4. Type mismatches - On-chain types don’t match your app types

Most teams spend weeks on infrastructure before shipping features.


Instead of building infrastructure, you declare what data you need:

#[entity(name = "Token")]
pub struct Token {
#[from_instruction(Create::mint, primary_key)]
pub mint: String,
#[map(BondingCurve::virtual_sol_reserves)]
pub sol_reserves: u64,
#[aggregate(from = Buy, field = amount, strategy = Sum)]
pub total_volume: u64,
}

Hyperstack then:

  • Subscribes to the relevant on-chain events
  • Transforms raw data into your entity shape
  • Streams updates to your app as they happen on-chain
  • Generates type-safe SDKs for your frontend

┌──────────────────────────────────────────────────────────────┐
│ YOUR APP │
│ React hooks, TypeScript streams, or raw WebSocket │
└──────────────────────────────────────────────────────────────┘
│ WebSocket (live updates)
┌──────────────────────────────────────────────────────────────┐
│ HYPERSTACK CLOUD │
│ │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ Compiler │ -> │ VM │ -> │ Streamer │ │
│ └────────────┘ └────────────┘ └────────────┘ │
│ ^ ^ │ │
│ AST Bytecode State Tables WebSocket │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Yellowstone gRPC │ │
│ │ (Live Solana data feed) │ │
│ └────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
│ Yellowstone gRPC (raw transactions)
┌──────────────────────────────────────────────────────────────┐
│ SOLANA │
│ On-chain programs emitting transactions and state changes │
└──────────────────────────────────────────────────────────────┘

A Stack is a deployed data pipeline that:

  1. Declares one or more Solana programs it cares about — each with its own IDL
  2. Subscribes to on-chain events across all of those programs
  3. Transforms raw transaction data into structured entities
  4. Aggregates values over time (sums, counts, etc.)
  5. Streams updates to connected clients as they happen

A stack isn’t limited to a single program. You can pull data from multiple programs into a single unified set of entities — as long as you provide an IDL for each. For example, a DeFi stack might combine instructions from a DEX program, a token program, and a rewards program into one coherent stream.

Think of it as a “live query” across Solana — you declare the programs and data shape you need, and Hyperstack keeps it updated.

An Entity is a structured object representing on-chain state. A stack can contain multiple entities — for example, a DeFi stack might have separate entities for pools, positions, and trades. Each entity has:

  • Primary key - Unique identifier (usually a pubkey)
  • Fields - Data attributes with population strategies
  • Sections - Logical groupings of fields
#[entity(name = "Token")]
pub struct Token {
pub id: TokenId, // Primary key section
pub info: TokenInfo, // Metadata section
pub trading: Trading, // Metrics section
}

Mappings define how on-chain data flows into entity fields:

Mapping TypeSourceExample
#[map]Account field#[map(BondingCurve::reserves)]
#[from_instruction]Instruction arg/account#[from_instruction(Create::mint)]
#[snapshot]Account snapshot#[snapshot(from = Account)]
#[derive_from]Derive from instruction#[derive_from(from = Buy, field = user)]
#[aggregate]Computed from events#[aggregate(from = Buy, strategy = Sum)]
#[event]Captured instruction#[event(strategy = Append)]
#[computed]Derived from fields#[computed(field_a + field_b)]

Strategies control how field values are updated:

StrategyBehavior
SetOnceSet once, never overwrite
LastWriteAlways use latest value
AppendCollect into array
SumRunning total
CountEvent counter
UniqueCountCount unique values
Max / MinTrack extremes

Views are how you access data from a stack. By default, every entity gets two built-in views:

ModePathReturnsUse Case
listEntity/listArray of entitiesAll entities
stateEntity/stateSingle entityGet by key
// List - all tokens as array
const { data: tokens } = stack.views.tokens.list.use();
// State - single token by key
const { data: token } = stack.views.tokens.state.use({ key: mintAddress });

Custom Views: If you need different sorting, filtering, or aggregation logic, you can define custom views using the #[view] macro in your stack specification. This lets you create specialized access patterns beyond the default list and state views.

A Stack SDK is a generated package that tells the Hyperstack client how to interact with a specific feed. It contains:

  • Entity types — The shape of your data
  • View definitions — How to access entities (list, state, etc.)
  • Helpers — Shared formatting and transformation logic

You can share generated SDKs with your team or publish them to npm/crates.io.


  1. Write stream definition using Rust macros
  2. Build generates stack spec (.hyperstack/*.stack.json)
  3. Deploy with hs up
  4. Cloud compiles spec to bytecode
  1. Connect to WebSocket
  2. Subscribe to views
  3. Receive live updates
  4. React components re-render automatically

Unlike traditional APIs where you poll for changes, Hyperstack pushes updates to you.

TypeMeaning
upsertEntity was created or updated
deleteEntity was removed
for await (const update of hs.views.token.list.watch()) {
if (update.type === "upsert") {
console.log("Token changed:", update.data);
} else if (update.type === "delete") {
console.log("Token removed:", update.key);
}
}

disconnected → connecting → connected → (reconnecting) → connected
↘ error
StateMeaning
disconnectedNot connected
connectingEstablishing WebSocket connection
connectedActive and receiving updates
reconnectingConnection lost, attempting to restore
errorFailed to connect (check URL, network)

The SDK handles reconnection automatically. Your subscriptions resume when the connection is restored.


Hyperstack provides end-to-end type safety:

  1. Spec types - Rust ensures valid mappings at compile time
  2. Generated types - SDK types match your entity definitions
  3. Runtime types - Data arrives pre-shaped, no parsing needed
// TypeScript knows token.mint is string, token.volume is bigint
const { data: token } = stack.views.tokens.state.use({ key });
console.log(token.mint); // string
console.log(token.volume); // bigint