Quickstart: React
Wire the sufwan-local-test client, provider, sign-in, and a first trade into a React app
You'll need: an app ID (Base URL, app IDs & API keys), your site's origin in the app's allowed origins, and React 18 or later. Result: a React app with sufwan-local-test data, sign-in, and a buy that is tracked to a final state.
Install the packages you need. See the Packages overview for what each one
does and its peer dependencies. Every layer uses the same client from @agg-build/sdk.
1. Create the client and provider
Mount one AggProvider around your app. Use the hooks export for data hooks, or the UI export
to include the UI kit's toast support; the UI export already includes the hooks provider.
Mount AggAuthProvider inside it for the connect button. The example assumes you have a
Wagmi configuration in ./wagmi-config and your application component in MyApp.
import "@agg-build/ui/styles.css";
import { AggProvider, QueryClient, QueryClientProvider } from "@agg-build/hooks";
import { createAggClient } from "@agg-build/sdk";
import { AggAuthProvider, ConnectButton, createGoogleAuthMethod } from "@agg-build/auth";
import { useSiweAuthMethod } from "@agg-build/auth/siwe";
import { WagmiProvider } from "wagmi";
import { wagmiConfig } from "./wagmi-config";
const client = createAggClient({
baseUrl: "https://api.t.sufwankhalid.com",
appId: "your-app-id",
wsUrl: "wss://ws.t.sufwankhalid.com/ws",
});
const queryClient = new QueryClient();
function AuthButton() {
const siwe = useSiweAuthMethod({ statement: "Sign in" });
return (
<AggAuthProvider methods={[siwe, createGoogleAuthMethod()]}>
<ConnectButton />
</AggAuthProvider>
);
}
function App() {
return (
<WagmiProvider config={wagmiConfig}>
<QueryClientProvider client={queryClient}>
<AggProvider
client={client}
config={{
general: { locale: "en-US", theme: "light" },
features: { enableAnimations: true, enableLiveUpdates: true },
}}
>
<AuthButton />
<MyApp />
</AggProvider>
</QueryClientProvider>
</WagmiProvider>
);
}For the UI kit's toasts, import AggProvider from @agg-build/ui instead of from
@agg-build/hooks. Keep the same client and config props. That component already mounts the
hooks provider.
UI components go inside MyApp:
import { EventMarketPage } from "@agg-build/ui/pages";
import { MarketDetails } from "@agg-build/ui/events";
import { LineChart } from "@agg-build/ui/primitives";
// Full event page: hero chart, market cards, orderbooks, all live
<EventMarketPage eventId="..." />
// Or individual components
<MarketDetails event={event} marketId={marketId} defaultTab="graph" />
<LineChart series={series} chartType="candlestick" height={320} live />See the Event Market Page and Market Details references, and Customize UI for fonts, colors, copy, and slots.
Use outcome IDs for trading, charts, orderbooks, and live trades. Arbitrage and midpoint reads use market IDs. Some hook parameter names retain older naming; the table below lists the ID each one expects.
import { useLiveMarket, useMarketArb, useMarketChart, useSmartRoute } from "@agg-build/hooks";
function MarketView({
venueMarketOutcomeId,
venueMarketId,
}: {
venueMarketOutcomeId: string;
venueMarketId: string;
}) {
const { orderbook } = useLiveMarket(venueMarketOutcomeId);
const { data: chart } = useMarketChart({
marketId: venueMarketOutcomeId,
interval: "5m",
startTs: Date.now() - 86_400_000,
endTs: Date.now(),
});
const { data: route } = useSmartRoute({
venueMarketOutcomeId,
tradeSide: "buy",
maxSpend: 50,
});
const { arbReturn } = useMarketArb(venueMarketId);
const primaryVenue = chart?.primaryVenue;
const candles = primaryVenue ? chart.venues[primaryVenue]?.candles ?? [] : [];
return <YourChart data={candles} orderbook={orderbook} route={route} arbReturn={arbReturn} />;
}No session means a preview quote. See Quotes.
| Hook | Id it actually reads |
|---|---|
useLiveMarket(canonicalMarketId) | venueMarketOutcomeId. Not a venueMarketId. |
useMarketOrderbook({ venueMarketOutcomeId }) | Outcome id. marketId is a deprecated alias for that same outcome id. |
useMarketChart({ marketId, venueMarketIds }) | Both accept outcome IDs: pass VenueMarketOutcome.id values. |
useLiveTrades(canonicalMarketId) | Outcome id, because it calls useLiveMarket. |
useSmartRoute({ venueMarketOutcomeId, tradeSide, maxSpend/sellShares }) | Outcome id. |
useMarketArb(marketId) | venueMarketId. Midpoints and arb use venueMarketId. |
useArbFeed() | Live arbitrage returns for many markets. |
useCatalogFeed({ since, onChange }) | No id. Keeps your copy of events and markets in sync; returns { status, lastChangedAt, error, resync }. See Catalog Sync. |
2. Sign the user in
AuthButton above lets the user sign in through @agg-build/auth. Trading calls use that
user's session through the shared client.
See the Connect Button reference and Authentication & sessions for every provider.
3. Buy and track to a final state
The user needs a balance first. See Funding for deposits. No session means a
preview quote. See Quotes. Then quote, fill, and poll the execution status
until terminal is true.
import { useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { useAggClient, useExecuteManaged, useSmartRoute } from "@agg-build/hooks";
function BuyButton({ venueMarketOutcomeId }: { venueMarketOutcomeId: string }) {
const client = useAggClient();
const [quoteId, setQuoteId] = useState<string | null>(null);
const { data: quote } = useSmartRoute({ venueMarketOutcomeId, tradeSide: "buy", maxSpend: 5 });
const execute = useExecuteManaged({ onSuccess: (fill) => setQuoteId(fill.quoteId) });
const { data: status } = useQuery({
queryKey: ["execution-status", quoteId],
queryFn: () => client.getExecutionStatus({ quoteId: quoteId! }),
enabled: !!quoteId,
// Poll at the interval the API suggests; stop once the trade is terminal.
refetchInterval: (query) =>
query.state.data?.terminal ? false : (query.state.data?.pollAfterMs ?? 1000),
});
if (status?.terminal) return <p>Done: {status.overallState}</p>;
if (quoteId) return <p>Executing: {status?.overallState ?? "created"}</p>;
return (
<button
className="cursor-pointer disabled:cursor-not-allowed"
disabled={quote?.status !== "ok" || execute.isPending}
onClick={() => quote && execute.mutate({ quoteId: quote.quoteId })}
>
Buy $5
</button>
);
}Stop polling when terminal is true. overallState then reads filled, partially_filled,
failed, cancelled, or expired. Read Order lifecycle & statuses
for what each state means.
WebSocket endpoint
The SDK and hooks manage the socket, reconnection, resnapshots, and orderbook integrity checks. If you open raw sockets yourself, connect to:
wss://ws.t.sufwankhalid.com/ws?appId=YOUR_APP_IDSee WebSocket Protocol.