Docs
SDK

React Integration

This guide shows you how to integrate the PerpCity SDK with Wagmi in a React application.

Installation

Install required dependencies:

pnpm add @strobelabs/perpcity-sdk wagmi viem @tanstack/react-query

Wagmi Configuration

Set up your Wagmi config:

// wagmi.config.ts
import { http, createConfig } from 'wagmi'
import { base } from 'wagmi/chains'
import { injected, walletConnect } from 'wagmi/connectors'

export const config = createConfig({
  chains: [base],
  connectors: [
    injected(),
    walletConnect({ projectId: 'YOUR_PROJECT_ID' })
  ],
  transports: {
    [base.id]: http(process.env.NEXT_PUBLIC_RPC_URL)
  }
})

App Setup

Wrap your app with Wagmi providers:

// app.tsx
import { WagmiProvider } from 'wagmi'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { config } from './wagmi.config'

const queryClient = new QueryClient()

function App() {
  return (
    <WagmiProvider config={config}>
      <QueryClientProvider client={queryClient}>
        <YourApp />
      </QueryClientProvider>
    </WagmiProvider>
  )
}

export default App

Create a PerpCity Hook

Create a custom hook to manage the PerpCity context:

// hooks/usePerpCity.ts
import { useMemo } from 'react'
import { useWalletClient } from 'wagmi'
import { PerpCityContext } from '@strobelabs/perpcity-sdk'

const PERP_MANAGER_ADDRESS = '0x...' as const
const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' as const

export function usePerpCity(): PerpCityContext | null {
  const { data: walletClient } = useWalletClient()

  const context = useMemo(() => {
    if (!walletClient) return null

    return new PerpCityContext({
      walletClient,
      rpcUrl: process.env.NEXT_PUBLIC_RPC_URL!,
      deployments: {
        perpManager: PERP_MANAGER_ADDRESS,
        usdc: USDC_ADDRESS
      }
    })
  }, [walletClient])

  return context
}

Using the Hook in Components

Display User Balance

// components/Balance.tsx
import { useEffect, useState } from 'react'
import { useAccount } from 'wagmi'
import { usePerpCity } from '../hooks/usePerpCity'
import { getUserUsdcBalance } from '@strobelabs/perpcity-sdk'

export function Balance() {
  const context = usePerpCity()
  const { address } = useAccount()
  const [balance, setBalance] = useState<number | null>(null)

  useEffect(() => {
    if (!context || !address) return

    const fetchBalance = async () => {
      // Two-step pattern: fetch user data, then extract balance
      const userData = await context.getUserData(address, [])
      const bal = getUserUsdcBalance(userData)
      setBalance(bal)
    }

    fetchBalance()

    // Refresh every 10 seconds
    const interval = setInterval(fetchBalance, 10_000)
    return () => clearInterval(interval)
  }, [context, address])

  if (!context) return <div>Connect wallet</div>
  if (balance === null) return <div>Loading...</div>

  return (
    <div>
      <h3>USDC Balance</h3>
      <p>${balance.toFixed(2)}</p>
    </div>
  )
}

React Query Integration

For better state management, use React Query:

// hooks/usePositionData.ts
import { useQuery } from '@tanstack/react-query'
import { usePerpCity } from './usePerpCity'
import type { Hex } from 'viem'

export function usePositionData(
  perpId: Hex,
  positionId: bigint,
  isLong: boolean,
  isMaker: boolean
) {
  const context = usePerpCity()

  return useQuery({
    queryKey: ['position', perpId, positionId.toString(), isLong, isMaker],
    queryFn: async () => {
      if (!context) throw new Error('No context')
      return await context.getOpenPositionData(perpId, positionId, isLong, isMaker)
    },
    enabled: !!context,
    refetchInterval: 10_000 // Refresh every 10 seconds
  })
}

Use in components:

// components/Position.tsx
import { usePositionData } from '../hooks/usePositionData'
import type { Hex } from 'viem'

export function Position({
  perpId,
  positionId
}: {
  perpId: Hex
  positionId: bigint
}) {
  const { data, isLoading, error } = usePositionData(perpId, positionId, true, false)

  if (isLoading) return <div>Loading...</div>
  if (error) return <div>Error: {error.message}</div>
  if (!data) return null

  const pnl = data.liveDetails.pnl

  return (
    <div>
      <h4>Position {positionId.toString()}</h4>
      <p>PnL: ${pnl.toFixed(2)}</p>
      <p>{data.liveDetails.isLiquidatable ? '⚠️ At risk' : '✅ Healthy'}</p>
    </div>
  )
}

Complete Example App

// App.tsx
import { WagmiProvider } from 'wagmi'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { ConnectKitProvider, ConnectKitButton } from 'connectkit'
import { config } from './wagmi.config'
import { Balance } from './components/Balance'
import { OpenPosition } from './components/OpenPosition'
import { PositionList } from './components/PositionList'

const queryClient = new QueryClient()

function App() {
  return (
    <WagmiProvider config={config}>
      <QueryClientProvider client={queryClient}>
        <ConnectKitProvider>
          <div style={{ padding: '20px' }}>
            <ConnectKitButton />

            <div style={{ marginTop: '20px' }}>
              <Balance />
              <OpenPosition />
              <PositionList />
            </div>
          </div>
        </ConnectKitProvider>
      </QueryClientProvider>
    </WagmiProvider>
  )
}

export default App

Next Steps