Skip to content

Advanced Usage

Advanced patterns and examples for using Levr SDK.

Dynamic Token Switching

Switch between different tokens:

typescript
import { useSetClankerToken, useProject } from 'levr-sdk/client'
import { useState } from 'react'

function TokenSwitcher({ tokens }: { tokens: Array<`0x${string}`> }) {
  const [selectedToken, setSelectedToken] = useState<`0x${string}` | null>(tokens[0])
  const { data: project } = useProject()

  // Automatically updates when selectedToken changes
  useSetClankerToken(selectedToken)

  return (
    <div>
      <h2>Current: {project?.token.name}</h2>
      <select
        value={selectedToken || ''}
        onChange={(e) => setSelectedToken(e.target.value as `0x${string}`)}
      >
        {tokens.map((address) => (
          <option key={address} value={address}>{address}</option>
        ))}
      </select>
    </div>
  )
}

All queries automatically update when you switch tokens.

Manual Refetch Control

Control when queries refetch:

typescript
import { useLevrRefetch } from 'levr-sdk/client'

function RefreshButton() {
  const refetch = useLevrRefetch()

  return (
    <div>
      <button onClick={() => refetch.all()}>Refresh All</button>
      <button onClick={() => refetch.user()}>Refresh User Data</button>
      <button onClick={() => refetch.project()}>Refresh Project</button>
      <button onClick={() => refetch.pool()}>Refresh Pool</button>
      <button onClick={() => refetch.proposals()}>Refresh Proposals</button>

      {/* Action-based smart refetches */}
      <button onClick={() => refetch.afterStake()}>After Stake</button>
      <button onClick={() => refetch.afterTrade()}>After Trade</button>
      <button onClick={() => refetch.afterClaim()}>After Claim</button>
      <button onClick={() => refetch.afterVote()}>After Vote</button>
    </div>
  )
}

Custom Query Invalidation

Invalidate specific queries:

typescript
import { queryKeys } from 'levr-sdk/client'
import { useQueryClient } from '@tanstack/react-query'

function CustomInvalidation() {
  const queryClient = useQueryClient()

  const invalidateProject = () => {
    queryClient.invalidateQueries({
      queryKey: queryKeys.project(clankerToken, chainId),
    })
  }

  return <button onClick={invalidateProject}>Refresh Project</button>
}

Price Impact Warnings

Display warnings for high price impact:

typescript
import { useSwap } from 'levr-sdk/client'

function SwapWithWarning() {
  const { quote, swap } = useSwap({
    quoteParams: { zeroForOne: true, amountIn: '100', amountInDecimals: 18, amountOutDecimals: 18 }
  })

  const priceImpact = quote.data?.priceImpactBps ?? 0
  const isHighImpact = priceImpact > 5

  return (
    <div>
      <p>Price Impact: {priceImpact.toFixed(2)}%</p>
      {isHighImpact && <p className="text-red-500">⚠️ Very high impact!</p>}
      <button onClick={() => swap.mutate(config)} disabled={isHighImpact}>
        Swap
      </button>
    </div>
  )
}

Optimistic Updates

Show pending states:

typescript
import { useStake } from 'levr-sdk/client'
import { useState } from 'react'

function OptimisticStaking() {
  const { stake, stakedBalance } = useStake()
  const [pending, setPending] = useState(false)

  const handleStake = (amount: bigint) => {
    setPending(true)
    stake.mutate(amount, {
      onSettled: () => setPending(false)
    })
  }

  return (
    <div>
      <p>
        Staked: {stakedBalance?.formatted}
        {pending && <span> (updating...)</span>}
      </p>
    </div>
  )
}

Server-Side Integration

Use in API routes:

typescript
// app/api/stats/route.ts
import { getProject, Stake } from 'levr-sdk'
import { createPublicClient, http } from 'viem'
import { base } from 'viem/chains'

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url)
  const token = searchParams.get('token') as `0x${string}`

  const publicClient = createPublicClient({
    chain: base,
    transport: http(),
  })

  const projectData = await getProject({
    publicClient,
    clankerToken: token,
  })

  if (!projectData) {
    return Response.json({ error: 'Project not found' }, { status: 404 })
  }

  return Response.json({
    name: projectData.token.name,
    treasury: projectData.treasuryStats?.balance.formatted,
    currentCycle: projectData.governanceStats?.currentCycleId.toString(),
  })
}

Testing Components

Test with providers:

typescript
import { render } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { LevrProvider } from 'levr-sdk/client'
import { WagmiProvider } from 'wagmi'

function renderWithProviders(ui: React.ReactElement) {
  const queryClient = new QueryClient({
    defaultOptions: { queries: { retry: false } }
  })

  return render(
    <QueryClientProvider client={queryClient}>
      <WagmiProvider config={testConfig}>
        <LevrProvider>
          {ui}
        </LevrProvider>
      </WagmiProvider>
    </QueryClientProvider>
  )
}

test('displays project', async () => {
  renderWithProviders(<ProjectComponent />)
  // ... assertions
})

USD Pricing

Enable USD values by providing oraclePublicClient to LevrProvider:

typescript
import { useProject, useUser } from 'levr-sdk/client'

function PricingDisplay() {
  const { data: project } = useProject()
  const { data: user } = useUser()

  if (!project?.pricing) {
    return <div>Pricing not available. Add oraclePublicClient to LevrProvider.</div>
  }

  return (
    <div>
      <h3>Pricing</h3>
      <p>Token Price: ${project.pricing.tokenUsd}</p>
      <p>WETH Price: ${project.pricing.wethUsd}</p>

      <h3>Your Holdings</h3>
      <p>Token Balance: {user?.balances.token.formatted}</p>
      {user?.balances.token.usd && <p>USD Value: ${user.balances.token.usd}</p>}

      <p>Staked: {user?.staking.stakedBalance.formatted}</p>
      {user?.staking.stakedBalance.usd && <p>USD Value: ${user.staking.stakedBalance.usd}</p>}

      <h3>Pool Stats</h3>
      <p>Total Staked: {project.stakingStats?.totalStaked.formatted}</p>
      {project.stakingStats?.totalStaked.usd && (
        <p>Total USD Value: ${project.stakingStats.totalStaked.usd}</p>
      )}
      <p>Treasury: {project.treasuryStats?.balance.formatted}</p>
      {project.treasuryStats?.balance.usd && (
        <p>Treasury USD: ${project.treasuryStats.balance.usd}</p>
      )}
    </div>
  )
}

Best Practices

  1. Always use LevrProvider - Wrap your app at the root
  2. Set token early - Call useSetClankerToken in route components
  3. Trust automatic refetches - Don't manually refetch after mutations
  4. Use smart refetch methods - afterStake(), afterTrade(), afterClaim(), etc.
  5. Handle loading states - Check isLoading before rendering data
  6. Provide USD pricing - Pass oraclePublicClient for better UX
  7. Memoize calculations - Use useMemo for expensive operations
  8. Test with providers - Always wrap tests with required providers