Skip to content

Your First Stack

In this tutorial, you’ll build an end-to-end streaming data pipeline in 15 minutes. We’ll create an “Ore” stack that tracks ORE mining rounds and displays live round data in a React app.

A full-stack streaming application consisting of:

  1. A Rust Definition: Defines how to transform on-chain ORE mining data into a queryable “OreRound” entity.
  2. A Cloud Deployment: A managed Hyperstack instance that processes the stream.
  3. A React Frontend: A live dashboard showing the latest ORE mining rounds that updates instantly as on-chain events occur.

The stack definition is the heart of your application. It tells Hyperstack which on-chain data to watch and how to project it into a useful state.

Terminal window
cargo new ore-stack --lib
cd ore-stack

Add hyperstack to your Cargo.toml:

[dependencies]
hyperstack = "0.5.3"
serde = { version = "1.0", features = ["derive"] }

Download the ORE program IDL file and place it in an idl/ directory:

Terminal window
mkdir -p idl
curl -o idl/ore.json https://docs.usehyperstack.com/ore.json

Open src/lib.rs and define your projection. We’ll create a simplified version of the ORE mining round tracker:

src/lib.rs
use hyperstack::prelude::*;
#[hyperstack(idl = "idl/ore.json")]
pub mod ore_stream {
use hyperstack::macros::Stream;
use serde::{Deserialize, Serialize};
// The main entity representing an ORE mining round
#[entity(name = "OreRound")]
#[view(name = "latest", sort_by = "id.round_id", order = "desc")]
pub struct OreRound {
pub id: RoundId,
pub state: RoundState,
pub metrics: RoundMetrics,
}
// Round identification with primary key
#[derive(Debug, Clone, Serialize, Deserialize, Stream)]
pub struct RoundId {
#[map(ore_sdk::accounts::Round::id, primary_key, strategy = SetOnce)]
pub round_id: u64,
#[map(ore_sdk::accounts::Round::__account_address, lookup_index, strategy = SetOnce)]
pub round_address: String,
}
// Round state fields - updated on every change
#[derive(Debug, Clone, Serialize, Deserialize, Stream)]
pub struct RoundState {
#[map(ore_sdk::accounts::Round::expires_at, strategy = LastWrite)]
pub expires_at: Option<u64>,
#[map(ore_sdk::accounts::Round::total_deployed, strategy = LastWrite)]
pub total_deployed: Option<u64>,
#[map(ore_sdk::accounts::Round::total_winnings, strategy = LastWrite)]
pub total_winnings: Option<u64>,
}
// Aggregated metrics computed from transactions
#[derive(Debug, Clone, Serialize, Deserialize, Stream)]
pub struct RoundMetrics {
// Count of deploy instructions for this round
#[aggregate(from = ore_sdk::instructions::Deploy, strategy = Count, lookup_by = accounts::round)]
pub deploy_count: Option<u64>,
// Sum of all deployed SOL amounts
#[aggregate(from = ore_sdk::instructions::Deploy, field = amount, strategy = Sum, lookup_by = accounts::round)]
pub total_deployed_sol: Option<u64>,
}
}

When you compile your code, Hyperstack macros automatically generate a .hyperstack/OreStream.stack.json file containing all entities in your stack.

Terminal window
cargo build

Now let’s push your stack to the cloud. Hyperstack manages the infrastructure, so you don’t need to worry about scaling or WebSocket servers.

This creates a hyperstack.toml file that links your local stack to the cloud.

Terminal window
hs init

The up command is a shortcut that pushes your stack, builds the container, and deploys it to a global cluster.

Terminal window
hs up

Expected Output:

✔ Stack pushed (v1)
✔ Build created (id: bld_123...)
✔ Build completed
🚀 Deployed to: wss://<your-stack-name>.stack.usehyperstack.com

Finally, let’s build the frontend. First, generate a typed SDK from your deployed stack, then connect using React or TypeScript.

From your stack project directory (where you ran hs up):

Terminal window
hs sdk create typescript ore-stack --output ./sdk

This creates a typed SDK in ./sdk/ with full TypeScript support.


Terminal window
npx create-react-app my-ore-app --template typescript
cd my-ore-app
npm install hyperstack-react zustand

Copy the generated SDK into your React app:

Terminal window
cp -r ../ore-stack/sdk ./src/ore-sdk

Wrap your app in HyperstackProvider using the URL from hs up.

src/index.tsx
import { HyperstackProvider } from "hyperstack-react";
const root = ReactDOM.createRoot(document.getElementById("root")!);
root.render(
<HyperstackProvider websocketUrl="wss://<your-stack-name>.stack.usehyperstack.com">
<App />
</HyperstackProvider>,
);
src/App.tsx
import { useHyperstack } from "hyperstack-react";
import { OreStack } from "./ore-sdk";
export default function App() {
const stack = useHyperstack(OreStack);
const { data: rounds, isLoading } = stack.views.OreRound.latest.use({
take: 5,
});
if (isLoading) return <div>Connecting to stream...</div>;
return (
<div>
<h1>Latest ORE Mining Rounds</h1>
{rounds?.map((round) => (
<div
key={round.id.round_id}
style={{
padding: "1rem",
border: "1px solid #ccc",
margin: "0.5rem 0",
}}
>
<h3>Round #{round.id.round_id}</h3>
<p>Address: {round.id.round_address}</p>
<p>
Total Deployed: {round.state.total_deployed?.toLocaleString()}{" "}
lamports
</p>
<p>
Total Winnings: {round.state.total_winnings?.toLocaleString()}{" "}
lamports
</p>
<p>Deploy Count: {round.metrics.deploy_count || 0}</p>
<p>
Total SOL Deployed:{" "}
{round.metrics.total_deployed_sol?.toLocaleString()} lamports
</p>
</div>
))}
</div>
);
}

Ensure you ran cargo build in your Rust project. The #[hyperstack] macro generates the .stack.json file during compilation.

  1. Check that the URL matches the output of hs up.
  2. Ensure your stack is successfully deployed by running hs stack list.
  1. Verify that the ORE program is emitting events and updating the Round accounts.
  2. Check the browser console for any subscription errors.
  3. Ensure your view path matches the entity name exactly (e.g., OreRound/latest).