Data Hooks

The frontend uses React Query hooks to fetch and cache data from Algorand. This page documents each hook and its configuration.

usePoolState

import { usePoolState } from '@/hooks/usePoolState';

function TradeForm({ appId }: { appId: number }) {
  const { data: poolState, isLoading, error } = usePoolState(appId);

  if (isLoading) return <div>Loading pool...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return <div>Reserves: {poolState.reserves}</div>;
}

Configuration:

useQuery({
  queryKey: ['poolState', appId],
  queryFn: () => readPoolState(algodClient, appId),
  staleTime: 30_000,        // Data is fresh for 30s
  refetchInterval: 30_000   // Poll every 30s
});

useSwapQuote

import { useSwapQuote } from '@/hooks/useSwapQuote';

function QuoteDisplay({ poolState, tradeParams }) {
  const { data: quote, isLoading } = useSwapQuote(
    poolState,
    tradeParams,
    { enabled: !!poolState }  // Only fetch when poolState exists
  );

  return <div>Output: {quote?.amountOut}</div>;
}

Configuration:

useQuery({
  queryKey: ['swapQuote', poolState, tradeParams],
  queryFn: () => getSwapQuote(poolState, tradeParams),
  staleTime: 5_000,         // Quotes expire fast
  enabled: !!poolState      // Don't fetch until poolState loaded
});

useAllPositions

import { useAllPositions } from '@/hooks/useAllPositions';

function PortfolioView({ address, appId }: { address: string; appId: number }) {
  const { data: positions } = useAllPositions(address, appId);

  return (
    <div>
      {positions?.map((pos) => (
        <PositionCard key={pos.tickId} position={pos} />
      ))}
    </div>
  );
}

Configuration:

useQuery({
  queryKey: ['positions', address, appId],
  queryFn: () => readAllPositions(algodClient, appId, address),
  staleTime: 60_000,        // 1 minute
  refetchInterval: 60_000
});

useTokenBalances

import { useTokenBalances } from '@/hooks/useTokenBalances';

function TokenBalance({ address, asaId }) {
  const { data: balance } = useTokenBalances(address, [asaId]);

  return <div>Balance: {balance?.[asaId]}</div>;
}

Configuration:

useQuery({
  queryKey: ['tokenBalances', address, asaIds],
  queryFn: () => fetchTokenBalances(algodClient, address, asaIds),
  staleTime: 10_000,        // 10 seconds
  refetchInterval: 10_000   // Live updates
});

useLivePoolMetrics

import { useLivePoolMetrics } from '@/hooks/useLivePoolMetrics';

function PoolStats({ appId }) {
  const { data: metrics } = useLivePoolMetrics(appId);

  return (
    <div>
      <div>TVL: ${metrics.tvl}</div>
      <div>24h Volume: ${metrics.volume24h}</div>
    </div>
  );
}

Configuration:

useQuery({
  queryKey: ['poolMetrics', appId],
  queryFn: () => fetchPoolMetrics(algodClient, appId),
  staleTime: 60_000,
  refetchInterval: 60_000
});

usePendingTransactions

import { usePendingTransactions } from '@/hooks/usePendingTransactions';

function TxStatus({ txId }) {
  const { data: status } = usePendingTransactions(txId);

  return <div>Status: {status}</div>;
}

Configuration:

useQuery({
  queryKey: ['txStatus', txId],
  queryFn: async () => {
    const pending = await algodClient
      .pendingTransactionInformation(txId)
      .do();
    if (pending['confirmed-round']) return 'confirmed';
    if (pending['pool-error']) return 'failed';
    return 'pending';
  },
  refetchInterval: 1000,    // Check every second
  retry: false               // Don't retry on failure
});

Stale Time vs Refetch Interval

QueryStale TimeRefetch IntervalWhy
usePoolState30s30sPool state changes with every swap
useSwapQuote5s-Quotes are ephemeral, recomputed on input change
useAllPositions60s60sLP positions change less frequently
useTokenBalances10s10sLive balance updates for trading UX
usePendingTransactions01sAlways fresh, polling until confirmed

Custom Query Client

// app/Providers.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: 3,
      retryDelay: (attempt) => Math.min(1000 * 2 ** attempt, 30000),
      networkMode: 'online',  // Only fetch when online
    },
  },
});

export default function Providers({ children }) {
  return (
    <QueryClientProvider client={queryClient}>
      {children}
    </QueryClientProvider>
  );
}
Note: All hooks are in the hooks/ directory. Import and use them in your components for consistent data fetching.