Skip to content
For the complete documentation index optimized for AI agents, see llms.txt or llms-full.txt. A markdown version of this page is available by appending .md to the URL or sending Accept: text/markdown.

Your First Stack

For AI agents: the documentation index is at llms.txt (full corpus: llms-full.txt). A markdown source for this page is /building-stacks/your-first-stack.md.

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 Arete 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 Arete 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 arete to your Cargo.toml:

[dependencies]
arete = "0.1.1"
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.arete.run/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 arete::prelude::*;
#[arete(idl = "idl/ore.json")]
pub mod ore_stream {
use arete::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, Arete macros automatically generate a .arete/OreStream.stack.json file containing all entities in your stack.

Terminal window
cargo build

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

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

Terminal window
a4 init

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

Terminal window
a4 up

Expected Output:

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

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 a4 up):

Terminal window
a4 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 arete-react zustand

Copy the generated SDK into your React app:

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

Wrap your app in AreteProvider using the URL from a4 up.

src/index.tsx
import { AreteProvider } from "arete-react";
const root = ReactDOM.createRoot(document.getElementById("root")!);
root.render(
<AreteProvider websocketUrl="wss://<your-stack-name>.stack.arete.run">
<App />
</AreteProvider>,
);
src/App.tsx
import { useArete } from "arete-react";
import { OreStack } from "./ore-sdk";
export default function App() {
const stack = useArete(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 #[arete] macro generates the .stack.json file during compilation.

  1. Check that the URL matches the output of a4 up.
  2. Ensure your stack is successfully deployed by running a4 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).