Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/hypertekorg/hyperstack/llms.txt

Use this file to discover all available pages before exploring further.

Ore Mining Dashboard

A real-time dashboard showing Ore mining rounds from the Ore program on Solana.

Complete Application

App.tsx
import { OreDashboard } from './components';
import { HyperstackProvider } from 'hyperstack-react';

function App() {
  return (
    <HyperstackProvider autoConnect={true}>
      <OreDashboard />
    </HyperstackProvider>
  );
}

export default App;

Main Dashboard Component

OreDashboard.tsx
import { useHyperstack } from 'hyperstack-react';
import { ORE_STREAM_STACK } from 'hyperstack-stacks/ore';
import { ValidatedOreRoundSchema } from '../schemas/ore-round-validated';

export function OreDashboard() {
  const { views, isConnected } = useHyperstack(ORE_STREAM_STACK);
  
  // Fetch latest round with schema validation
  const { data: latestRound } = views.OreRound.latest.useOne({ 
    schema: ValidatedOreRoundSchema 
  });
  
  // Fetch treasury data
  const { data: treasuryData } = views.OreTreasury.list.useOne();

  return (
    <div className="dashboard">
      <header>
        <h1>Ore Mining</h1>
        <p>
          Live ORE rounds powered by{' '}
          <a href="https://docs.usehyperstack.com">
            Hyperstack
          </a>
        </p>
      </header>

      <div className="content">
        <BlockGrid round={latestRound} />
        
        <StatsPanel
          round={latestRound}
          treasuryMotherlode={treasuryData?.state?.motherlode}
          isConnected={isConnected}
        />
      </div>

      <ConnectionBadge isConnected={isConnected} />
    </div>
  );
}

Stats Panel with Live Updates

StatsPanel.tsx
import { useState, useEffect } from 'react';
import type { ValidatedOreRound } from '../schemas/ore-round-validated';

interface StatsPanelProps {
  round: ValidatedOreRound | undefined;
  treasuryMotherlode: number | null | undefined;
  isConnected: boolean;
}

export function StatsPanel({ 
  round, 
  treasuryMotherlode, 
  isConnected 
}: StatsPanelProps) {
  const [timeRemaining, setTimeRemaining] = useState<string>('00:00');

  // Live countdown timer
  useEffect(() => {
    const expiresAtUnix = round?.state.estimated_expires_at_unix;
    if (!expiresAtUnix) {
      setTimeRemaining('00:00');
      return;
    }

    const updateTimer = () => {
      const now = Math.floor(Date.now() / 1000);
      const remaining = Math.max(0, expiresAtUnix - now);

      if (remaining > 300) {
        setTimeRemaining('00:00');
        return;
      }

      const minutes = Math.floor(remaining / 60);
      const seconds = remaining % 60;
      setTimeRemaining(
        `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`
      );
    };

    updateTimer();
    const interval = setInterval(updateTimer, 1000);
    return () => clearInterval(interval);
  }, [round?.state.estimated_expires_at_unix]);

  return (
    <div className="stats-panel">
      {/* Motherlode */}
      <div className="stat-card">
        <div className="stat-value">
          <OreIcon />
          <span>{treasuryMotherlode ?? '–'}</span>
        </div>
        <div className="stat-label">Motherlode</div>
      </div>

      {/* Time Remaining */}
      <div className="stat-card">
        <div className="stat-value">{timeRemaining}</div>
        <div className="stat-label">Time remaining</div>
      </div>

      {/* Total Deployed */}
      <div className="stat-card">
        <div className="stat-value">
          <SolanaIcon />
          <span>
            {round ? round.state.total_deployed.toFixed(4) : '0.0000'}
          </span>
        </div>
        <div className="stat-label">Total deployed</div>
      </div>

      {/* Round Info */}
      <div className="round-info">
        <span>Round {round?.id.round_id ?? '–'}</span>
        {round && (
          <>
            <span>·</span>
            <span>{round.state.total_miners} miners</span>
          </>
        )}
      </div>

      {/* Connection Status */}
      {!isConnected && (
        <div className="connecting-banner">
          Connecting...
        </div>
      )}
    </div>
  );
}

Schema Validation

schemas/ore-round-validated.ts
import { z } from 'zod';

export const ValidatedOreRoundSchema = z.object({
  id: z.object({
    round_id: z.number(),
  }),
  state: z.object({
    estimated_expires_at_unix: z.number(),
    total_deployed: z.number(),
    total_miners: z.number(),
  }),
});

export type ValidatedOreRound = z.infer<typeof ValidatedOreRoundSchema>;

Simple Counter Example

Stack Definition

stack.ts
import { defineStack } from 'hyperstack-typescript';

export const COUNTER_STACK = defineStack({
  name: 'counter',
  url: 'wss://api.example.com/counter',
  views: {
    Counter: {
      current: {
        mode: 'state' as const,
        view: 'counter_current',
      },
      history: {
        mode: 'list' as const,
        view: 'counter_history',
      },
    },
  },
  instructions: {
    increment: defineInstruction({
      name: 'increment',
      accounts: [
        { name: 'counter', category: 'pda', pda: { seeds: ['counter'] } },
        { name: 'user', category: 'signer' },
      ],
    }),
  },
});

Counter Component

Counter.tsx
import { useHyperstack } from 'hyperstack-react';
import { COUNTER_STACK } from './stack';

interface CounterData {
  value: number;
  lastUpdated: number;
}

function Counter() {
  const { views, instructions, isConnected } = useHyperstack(COUNTER_STACK);
  
  // Fetch current counter value
  const { data, isLoading } = views.Counter.current.use<CounterData>();
  
  // Setup mutation for incrementing
  const incrementMutation = instructions.increment.useMutation();
  
  const handleIncrement = async () => {
    try {
      await incrementMutation.submit({});
    } catch (error) {
      console.error('Failed to increment:', error);
    }
  };
  
  if (isLoading) {
    return <div>Loading...</div>;
  }
  
  return (
    <div>
      <h1>Counter: {data?.value ?? 0}</h1>
      
      <button 
        onClick={handleIncrement}
        disabled={!isConnected || incrementMutation.isLoading}
      >
        {incrementMutation.isLoading ? 'Incrementing...' : 'Increment'}
      </button>
      
      {incrementMutation.error && (
        <div className="error">{incrementMutation.error}</div>
      )}
      
      {incrementMutation.signature && (
        <div className="success">
          Transaction: {incrementMutation.signature}
        </div>
      )}
      
      <div className="status">
        {isConnected ? '🟢 Connected' : '🔴 Disconnected'}
      </div>
    </div>
  );
}

Counter History

CounterHistory.tsx
import { useHyperstack } from 'hyperstack-react';
import { COUNTER_STACK } from './stack';

interface HistoryEntry {
  value: number;
  timestamp: number;
  user: string;
}

function CounterHistory() {
  const { views } = useHyperstack(COUNTER_STACK);
  
  // Fetch last 10 updates
  const { data, isLoading, refresh } = views.Counter.history.use<HistoryEntry>({
    limit: 10,
  });
  
  if (isLoading) {
    return <div>Loading history...</div>;
  }
  
  return (
    <div>
      <div className="header">
        <h2>Counter History</h2>
        <button onClick={refresh}>Refresh</button>
      </div>
      
      <ul>
        {data?.map((entry, i) => (
          <li key={i}>
            <span>Value: {entry.value}</span>
            <span>User: {entry.user.slice(0, 8)}...</span>
            <span>{new Date(entry.timestamp * 1000).toLocaleString()}</span>
          </li>
        ))}
      </ul>
      
      {data?.length === 0 && <p>No history yet</p>}
    </div>
  );
}

Todo List Example

TodoList.tsx
import { useState } from 'react';
import { useHyperstack } from 'hyperstack-react';
import { z } from 'zod';

// Define your stack configuration
const TodoStack = {
  name: 'my-todos',
  url: 'wss://your-stack.usehyperstack.com',
};

const TodoSchema = z.object({
  id: z.string(),
  text: z.string(),
  completed: z.boolean(),
  createdAt: z.number(),
});

type Todo = z.infer<typeof TodoSchema>;

function TodoList() {
  const { views, instructions } = useHyperstack(TodoStack);
  const [newTodoText, setNewTodoText] = useState('');
  
  // Fetch all todos
  const { data: todos, isLoading } = views.Todos.list.use<Todo>({
    schema: TodoSchema,
  });
  
  // Fetch only active todos
  const { data: activeTodos } = views.Todos.list.use<Todo>({
    where: { completed: false },
    schema: TodoSchema,
  });
  
  const createMutation = instructions.createTodo.useMutation();
  const toggleMutation = instructions.toggleTodo.useMutation();
  const deleteMutation = instructions.deleteTodo.useMutation();
  
  const handleCreate = async () => {
    if (!newTodoText.trim()) return;
    
    try {
      await createMutation.submit({ text: newTodoText });
      setNewTodoText('');
    } catch (error) {
      console.error('Failed to create todo:', error);
    }
  };
  
  const handleToggle = async (id: string) => {
    try {
      await toggleMutation.submit({ id });
    } catch (error) {
      console.error('Failed to toggle todo:', error);
    }
  };
  
  const handleDelete = async (id: string) => {
    try {
      await deleteMutation.submit({ id });
    } catch (error) {
      console.error('Failed to delete todo:', error);
    }
  };
  
  if (isLoading) {
    return <div>Loading todos...</div>;
  }
  
  return (
    <div>
      <h1>Todo List</h1>
      
      <div className="stats">
        <span>Total: {todos?.length ?? 0}</span>
        <span>Active: {activeTodos?.length ?? 0}</span>
      </div>
      
      <div className="add-todo">
        <input
          type="text"
          value={newTodoText}
          onChange={(e) => setNewTodoText(e.target.value)}
          placeholder="What needs to be done?"
          onKeyPress={(e) => e.key === 'Enter' && handleCreate()}
        />
        <button 
          onClick={handleCreate}
          disabled={createMutation.isLoading}
        >
          Add
        </button>
      </div>
      
      {createMutation.error && (
        <div className="error">{createMutation.error}</div>
      )}
      
      <ul className="todo-list">
        {todos?.map((todo) => (
          <li key={todo.id} className={todo.completed ? 'completed' : ''}>
            <input
              type="checkbox"
              checked={todo.completed}
              onChange={() => handleToggle(todo.id)}
              disabled={toggleMutation.isLoading}
            />
            <span>{todo.text}</span>
            <button 
              onClick={() => handleDelete(todo.id)}
              disabled={deleteMutation.isLoading}
            >
              Delete
            </button>
          </li>
        ))}
      </ul>
      
      {todos?.length === 0 && (
        <p className="empty">No todos yet. Add one above!</p>
      )}
    </div>
  );
}

export default TodoList;

User Profile Example

UserProfile.tsx
import { useHyperstack } from 'hyperstack-react';
import { USER_STACK } from './stack';
import { z } from 'zod';

const ProfileSchema = z.object({
  username: z.string(),
  avatar: z.string().url(),
  bio: z.string(),
  followers: z.number(),
  following: z.number(),
});

type Profile = z.infer<typeof ProfileSchema>;

function UserProfile({ userId }: { userId: string }) {
  const { views, instructions } = useHyperstack(USER_STACK);
  
  // Fetch user profile by ID
  const { data: profile, isLoading } = views.Users.byId.use<Profile>(
    { id: userId },
    { schema: ProfileSchema }
  );
  
  const followMutation = instructions.followUser.useMutation();
  
  const handleFollow = async () => {
    try {
      await followMutation.submit({ userId });
    } catch (error) {
      console.error('Failed to follow user:', error);
    }
  };
  
  if (isLoading) {
    return <div>Loading profile...</div>;
  }
  
  if (!profile) {
    return <div>User not found</div>;
  }
  
  return (
    <div className="profile">
      <img src={profile.avatar} alt={profile.username} />
      <h1>{profile.username}</h1>
      <p>{profile.bio}</p>
      
      <div className="stats">
        <span>{profile.followers} followers</span>
        <span>{profile.following} following</span>
      </div>
      
      <button 
        onClick={handleFollow}
        disabled={followMutation.isLoading}
      >
        {followMutation.isLoading ? 'Following...' : 'Follow'}
      </button>
      
      {followMutation.error && (
        <div className="error">{followMutation.error}</div>
      )}
      
      {followMutation.signature && (
        <div className="success">Now following {profile.username}!</div>
      )}
    </div>
  );
}

Next Steps

Provider Setup

Configure HyperstackProvider

Hooks Reference

Complete hooks documentation

Build docs developers (and LLMs) love