---
title: "Your First Stack"
editUrl: true
head: []
template: "doc"
sidebar: {"order":5,"hidden":false,"attrs":{}}
pagefind: true
draft: false
---

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.

## What You'll Build

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.

:::note[Demo Version]
This tutorial uses a simplified version of the ORE stack. For the full production-ready version with all features (computed fields, snapshots, round results, etc.), run `a4 create` and select the `ore` template.
:::

---

## Part 1: Create the Stack (Rust)

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.

### 1. Create a new Rust project

```bash
cargo new ore-stack --lib
cd ore-stack
```

### 2. Add dependencies

Add `arete` to your `Cargo.toml`:

```toml
[dependencies]
arete = "{{VERSION}}"
serde = { version = "1.0", features = ["derive"] }
```

### 3. Get the ORE IDL

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

```bash
mkdir -p idl
curl -o idl/ore.json https://docs.arete.run/ore.json
```

:::tip[What is an IDL?]
The IDL (Interface Definition Language) describes the Solana program's account structures and instructions. Arete uses it to generate type-safe code for accessing on-chain data.
:::

:::tip[Generated SDK Naming]
When you point to an IDL (e.g., `ore.json`), Arete reads the program name from the IDL metadata and generates a typed SDK module named `{name}_sdk` — so `ore.json` produces `ore_sdk::accounts::*` and `ore_sdk::instructions::*`. All `#[map]` and `#[aggregate]` paths use this prefix.
:::

### 4. Write your stack definition

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

```rust
// 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>,
    }
}
```

### 5. Build to generate the stack file

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

```bash
cargo build
```

:::tip[Checkpoint]
Verify that the file `.arete/OreStream.stack.json` exists in your project root. This JSON file contains the compiled specification for your stack, including all entities and their field mappings.
:::

---

## Part 2: Deploy to Arete Cloud

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.

:::caution[Closed Beta]
Arete is currently in closed beta. You'll need an API key to deploy. [Contact us on X](https://x.com/usearete) to request access.
:::

### 1. Initialize your project

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

```bash
a4 init
```

### 2. Push and Deploy

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

```bash
a4 up
```

**Expected Output:**

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

:::tip[Checkpoint]
Copy the **WebSocket URL** from the output. You'll need it for the React app.
:::

---

## Part 3: Generate SDK and Connect

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

:::tip[What is the SDK?]
The generated SDK contains the typed interface to your stack — it tells the Arete client what entities exist, what views are available, and the TypeScript types for each. You can share this SDK with your team or publish it.
:::

### 1. Generate the SDK

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

```bash
a4 sdk create typescript ore-stack --output ./sdk
```

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

---

### React: Set up your React app

```bash
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:

```bash
cp -r ../ore-stack/sdk ./src/ore-sdk
```

### React: Configure the Provider

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

```tsx
// 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>,
);
```

### React: Use the Generated Stack

```tsx
// 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>
  );
}
```

### TypeScript: Set up your project

```bash
mkdir my-ore-app && cd my-ore-app
npm init -y
npm install arete-typescript typescript
```

Copy the generated SDK:

```bash
cp -r ../ore-stack/sdk ./src/ore-sdk
```

### TypeScript: Connect and stream data

```typescript
// src/index.ts
import { Arete } from "arete-typescript";
import { OreStack, type OreRound } from "./ore-sdk";

async function main() {
  // Connect to your deployed stack using the generated SDK
  const a4 = await Arete.connect(
    "wss://<your-stack-name>.stack.arete.run",
    { stack: OreStack },
  );

  console.log("Connected! Streaming ORE rounds...\n");

  // Stream updates with full type safety
  for await (const update of a4.views.OreRound.latest.watch()) {
    if (update.type === "upsert") {
      const round = update.data;
      console.log(`Round #${round.id.round_id}:`);
      console.log(`  Address: ${round.id.round_address}`);
      console.log(
        `  Total Deployed: ${round.state.total_deployed?.toLocaleString()} lamports`,
      );
      console.log(
        `  Total Winnings: ${round.state.total_winnings?.toLocaleString()} lamports`,
      );
      console.log(`  Deploy Count: ${round.metrics.deploy_count || 0}`);
      console.log("");
    }
  }
}

main().catch(console.error);
```

### TypeScript: Run your app

```bash
npx tsx src/index.ts
```

---

## Troubleshooting

### "Stack file not found"

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

### WebSocket connection fails

1. Check that the URL matches the output of `a4 up`.
2. Ensure your stack is successfully deployed by running `a4 stack list`.

### Data is not appearing

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`).

## Next Steps

- Learn more about [Stack Macros](/building-stacks/rust-dsl/macros) to build complex pipelines.
- Explore [Population Strategies](/building-stacks/rust-dsl/strategies) for different data access patterns.
- Check the [CLI Reference](/cli/commands) for advanced deployment options.
