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
- Always use LevrProvider - Wrap your app at the root
- Set token early - Call
useSetClankerTokenin route components - Trust automatic refetches - Don't manually refetch after mutations
- Use smart refetch methods -
afterStake(),afterTrade(),afterClaim(), etc. - Handle loading states - Check
isLoadingbefore rendering data - Provide USD pricing - Pass
oraclePublicClientfor better UX - Memoize calculations - Use
useMemofor expensive operations - Test with providers - Always wrap tests with required providers