Open source / MIT licensed

Prediction market
components for React.

Drop-in shadcn components and hooks for building on Hyperliquid HIP-4. Wallet agnostic. Builder fee ready. Install individually.

Install a component
npx shadcn@latest add https://ui.purrdict.xyz/r/market-card.json
7 components 8 hooks wagmi + shadcn/ui

Components

UI primitives for prediction market interfaces. Each installs independently with its dependencies.

Countdown Timer

component

Live countdown to a prediction market expiry. Ticks every second, shows 'Settled' when expired.

npx shadcn@latest add https://ui.purrdict.xyz/r/countdown-timer.json
Preview
Active market
02hrs
:
15min
:
30sec
After expiry
Settled
Usage
import { CountdownTimer } from "@/components/hip4/countdown-timer"

<CountdownTimer
  expiry={market.expiry}
  onExpire={() => refetchMarket()}
/>
Props
Prop Type Description
expiry Date UTC expiry date
className string Additional CSS classes
onExpire () => void Called when timer reaches zero

Market Card

component

Display card for a HIP-4 prediction market showing underlying asset, Yes/No prices with color coding, target price, and expiry countdown.

npx shadcn@latest add https://ui.purrdict.xyz/r/market-card.json
Preview
Bitcoin Up or Down — 15 Min
Target: 87,450 USDT
65%
Live
2h 15m 30s
Usage
import { MarketCard } from "@/components/hip4/market-card"

<MarketCard
  market={market}
  yesMid={0.65}
  noMid={0.35}
  volume={4200}
  onClick={() => router.push(`/market/${market.yesCoin}`)}
/>
Props
Prop Type Description
market Market Market object from useMarkets()
yesMid number | string Yes mid price (0-1)
noMid number | string No mid price (0-1). Defaults to 1 - yesMid
volume number Optional 24h volume in USDH
onClick () => void Click handler
className string Additional CSS classes
Dependencies: @purrdict/hip4

Market Grid

component

Filterable, searchable grid of market cards. Filter by underlying asset (BTC, ETH, SOL). Responsive layout with search.

npx shadcn@latest add https://ui.purrdict.xyz/r/market-grid.json
Preview
BTC 15m
65¢35¢
BTC 1h
42¢58¢
ΞETH 15m
71¢29¢
ΞETH 1h
55¢45¢
SOL 15m
48¢52¢
SOL 1h
60¢40¢
Usage
import { MarketGrid } from "@/components/hip4/market-grid"

const { markets, mids } = useMarkets(client)

<MarketGrid
  markets={markets}
  mids={mids}
  onMarketClick={(m) => router.push(`/market/${m.yesCoin}`)}
/>
Props
Prop Type Description
markets Market[] Array of market objects
mids Record<string, string> Live mid prices from useMarkets()
onMarketClick (market: Market) => void Click handler for a market card
initialFilter "all" | "btc" | "eth" | "sol" Default filter. Default: "all"
className string Additional CSS classes
Dependencies: @purrdict/hip4

Orderbook

component

Dual-pane L2 orderbook for prediction markets with color-coded depth bars, spread display, and click-to-set-price interaction.

npx shadcn@latest add https://ui.purrdict.xyz/r/orderbook.json
Preview
#9860 OrderbookDepth: 5
PriceSizeTotal
0.68209564.8
0.67906544.1
0.676012081.1
0.67308557.2
0.67004026.8
Spread0.0050(0.75%)
0.66505536.6
0.6620200132.4
0.660015099.0
0.65708857.8
0.654012078.5
Usage
import { Orderbook } from "@/components/hip4/orderbook"

const { bids, asks } = useOrderbook(client, "#9860")

<Orderbook
  coin="#9860"
  bids={bids}
  asks={asks}
  depth={10}
  onPriceClick={(px) => setLimitPrice(px)}
/>
Props
Prop Type Description
coin string Coin name for display (e.g. "#9860")
bids BookLevel[] Bid levels sorted by price descending
asks BookLevel[] Ask levels sorted by price ascending
depth number Number of levels to display. Default: 10
onPriceClick (price: number) => void Called when user clicks a price level
className string Additional CSS classes
Dependencies: @purrdict/hip4

Position Card

component

Displays a user's position in a prediction market with shares, current value, average entry, and unrealized P&L.

npx shadcn@latest add https://ui.purrdict.xyz/r/position-card.json
Preview
#9860Yes
100 shares
Avg Entry
45¢
Current Price
65¢
Value
$65.00
P&L
+$20.00 (+44.4%)
entry: 45¢100¢
Usage
import { PositionCard } from "@/components/hip4/position-card"

<PositionCard
  coin="#9860"
  shares={100}
  avgEntry={0.45}
  currentPrice={0.65}
  onSell={(coin) => openSellDialog(coin)}
/>
Props
Prop Type Description
coin string Coin name (e.g. "#9860")
shares number Number of shares held
avgEntry number Average entry price
currentPrice number Current mark/mid price
onSell (coin: string) => void Sell button handler
className string Additional CSS classes

Settlement Banner

component

Banner showing a resolved market result with winning side highlight and oracle settle price.

npx shadcn@latest add https://ui.purrdict.xyz/r/settlement-banner.json
Preview
Market Resolved — Yes wins
Oracle settle price: $87,450
Usage
import { SettlementBanner } from "@/components/hip4/settlement-banner"

<SettlementBanner
  result="Yes"
  settlePrice="87450"
/>
Props
Prop Type Description
result "Yes" | "No" The winning side
settlePrice string | number Oracle price at settlement
className string Additional CSS classes

Trade Form

component

Complete prediction market trade form with market/limit modes, tick-size validation, min-shares enforcement, builder fee support, and order summary.

npx shadcn@latest add https://ui.purrdict.xyz/r/trade-form.json
Preview
Cost65.00 USDH
Fee (0.1%)0.065 USDH
Total65.065 USDH
Usage
import { TradeForm } from "@/components/hip4/trade-form"

<TradeForm
  market={market}
  side="Yes"
  currentPrice={0.55}
  minShares={20}
  builder={{ address: "0x...", fee: 100 }}
  onTrade={async (params) => {
    const result = await buy(params)
    return result
  }}
/>
Props
Prop Type Description
market Market Market object
side "Yes" | "No" Active trade side
onSideChange (side) => void Called when user switches side
currentPrice number Current mark/mid price (0-1)
minShares number Minimum shares for a valid order
builder { address, fee } Optional builder fee config
usdhBalance number User's USDH balance for validation
onTrade (params) => Promise Trade execution callback
Dependencies: @purrdict/hip4

Hooks

React hooks for market data, trading, and wallet interaction. All hooks depend on the HIP-4 SDK.

use-hip4-client

hook

Creates and caches an @purrdict/hip4 client with WebSocket subscription support. Manages lifecycle and cleanup on unmount.

useHIP4Client(opts?: { testnet?: boolean }) HIP4Client
const client = useHIP4Client({ testnet: true })
const mids = await client.info.allMids()

use-hip4-signer

hook

Adapts any wagmi wallet (MetaMask, WalletConnect, Privy, etc.) to the HIP-4 SDK signer interface. Returns null when disconnected.

useHIP4Signer() HIP4Signer | null
const signer = useHIP4Signer()
// null when wallet disconnected

use-markets

hook

Discovers active HIP-4 markets from outcomeMeta and subscribes to live mid prices via allMids WebSocket. Returns markets array and real-time mids map.

useMarkets(client: HIP4Client) { markets, mids, isLoading, error }
const { markets, mids, isLoading } = useMarkets(client)
// markets: Market[], mids: Record<string, string>

use-orderbook

hook

Subscribes to the L2 orderbook for a prediction market coin. Returns bids, asks, spread, and mid price with live WebSocket updates.

useOrderbook(client: HIP4Client, coin: string) { bids, asks, spread, midPrice }
const { bids, asks, spread } = useOrderbook(client, "#9860")

use-portfolio

hook

Fetches USDH balance, outcome token positions, and open orders for a wallet address. Includes manual refresh support.

usePortfolio(client: HIP4Client, address: string) { balance, positions, orders, refresh }
const { balance, positions } = usePortfolio(client, address)

use-trade

hook

Place and cancel HIP-4 prediction market orders. Handles builder fee attachment, tick alignment, and order signing via the SDK.

useTrade(client: HIP4Client, signer: HIP4Signer) { buy, sell, cancel, isSubmitting, error }
const { buy, sell, cancel } = useTrade(client, signer)
await buy({ coin: "#9860", shares: 20, price: 0.55 })

use-settlement

hook

Detects when a prediction market outcome has settled by subscribing to user fills. Returns winner, settle price, and closed PnL.

useSettlement(client: HIP4Client, outcomeId: number) { settled, winner, settlePrice, closedPnl }
const { settled, winner } = useSettlement(client, 234)

use-min-shares

hook

Computes the minimum order size for a prediction market coin based on mark price from spotMetaAndAssetCtxs.

useMinShares(client: HIP4Client, coin: string) { minShares, markPrice, isLoading }
const { minShares } = useMinShares(client, "#9860")
// minShares: 20 (typical for ~0.5 mark price)

Utilities

Shared formatting and helper functions used by components.

hip4-format

lib

Price, USDH, countdown, and period formatters for HIP-4 prediction market data.

npx shadcn@latest add https://ui.purrdict.xyz/r/hip4-format.json

Quick Start

Three steps to add prediction market components to your Next.js app.

1

Install the SDK

bun add @purrdict/hip4 viem wagmi
2

Add a component

npx shadcn@latest add https://ui.purrdict.xyz/r/market-card.json

This installs the component, its dependencies (countdown-timer, format utils), and adds the npm package.

3

Use it

import { MarketCard } from "@/components/hip4/market-card"
import { useMarkets } from "@/hooks/hip4/use-markets"
import { useHIP4Client } from "@/hooks/hip4/use-hip4-client"

export default function Markets() {
  const client = useHIP4Client({ testnet: true })
  const { markets, mids } = useMarkets(client)

  return (
    <div className="grid grid-cols-3 gap-4">
      {markets.map((m) => (
        <MarketCard
          key={m.yesCoin}
          market={m}
          yesMid={mids[m.yesCoin]}
        />
      ))}
    </div>
  )
}