React Hooks

The useCredDAO hook library provides type-safe, cached data fetching and mutations.

Installation

Hooks are included in the frontend package. Import from:

import { 
  useFairScore, 
  useVotingPower, 
  useProposals,
  useTransaction,
  useToast 
} from '@/hooks/useCredDAO'

Query Hooks

useFairScore

Fetch FairScore data for a wallet:

const { data, loading, error, refetch } = useFairScore(walletAddress)
 
// Auto-uses connected wallet if no address provided
const { data } = useFairScore()
 
// data structure
interface FairScoreData {
  wallet: string
  fairscore_base: number
  social_score: number
  fairscore: number
  tier: 'bronze' | 'silver' | 'gold' | 'platinum'
  badges: Array<{
    id: string
    label: string
    description: string
    tier: string
  }>
  features?: {
    active_days?: number
    tx_count?: number
    lst_percentile_score?: number
    native_sol_percentile?: number
    wallet_age_days?: number
  }
}

useVotingPower

Calculate voting power breakdown:

const { votingPower, quadraticBase, reputationMultiplier, loading, error } = 
  useVotingPower(tokenBalance)
 
// votingPower = quadraticBase * reputationMultiplier
// quadraticBase = √(tokenBalance)
// reputationMultiplier = 1 + fairscore/50

useProposals

Fetch proposals with optimistic updates:

const { data, loading, error, optimisticProposals, refetch } = useProposals({
  state: 'active',
  limit: 20
})
 
// optimisticProposals includes pending local changes

useProposal

Fetch single proposal with pending vote:

const { data, pendingVote, loading, error } = useProposal(proposalId)
 
// pendingVote shows user's pending vote before confirmation
if (pendingVote) {
  console.log(`Pending: ${pendingVote.vote} with ${pendingVote.votingPower} power`)
}

useDelegates

Fetch delegate leaderboard:

const { data, loading, error } = useDelegates(20)
 
// data structure
interface Delegate {
  wallet: string
  fairscore: number
  tier: string
  participationRate: number
  delegationEfficiency: number
  delegatedPower: number
  proposalsVoted: number
}

useAnalytics

Fetch governance analytics:

const { data, loading, error } = useAnalytics()
 
// Auto-refreshes every 5 minutes

Mutation Hooks

useCreateProposal

Create proposal with optimistic update:

const { mutate, loading, error, data } = useCreateProposal()
 
await mutate({
  title: 'Treasury Transfer',
  description: 'Transfer 1000 USDC...',
  type: 'standard',
  proposerWallet: wallet.publicKey.toBase58()
})

useCastVote

Cast vote with optimistic feedback:

const { mutate, loading, error } = useCastVote()
 
await mutate({
  proposalId: 'prop_xxx',
  voterWallet: wallet.publicKey.toBase58(),
  vote: 'for',
  votingPower: 150
})

Transaction Hook

useTransaction

Handle Solana transactions with status tracking:

const { status, signature, error, execute, reset } = useTransaction()
 
// status: 'idle' | 'preparing' | 'signing' | 'confirming' | 'confirmed' | 'failed'
 
const handleVote = async () => {
  const sig = await execute(async () => {
    const tx = await createVoteTransaction()
    await wallet.sendTransaction(tx, connection)
    return { signature: tx.signature }
  })
  
  if (sig) {
    toast.success('Vote confirmed!')
  }
}

Utility Functions

withRetry

Exponential backoff retry wrapper:

import { withRetry } from '@/hooks/useCredDAO'
 
const result = await withRetry(
  () => fetchFromAPI(),
  { maxRetries: 3, baseDelayMs: 200 }
)

globalCache

Stale-while-revalidate cache:

import { globalCache } from '@/hooks/useCredDAO'
 
// Get cached value
const cached = globalCache.get<FairScoreData>('fairscore:wallet')
 
// Set with TTL
globalCache.set('fairscore:wallet', data, 5 * 60 * 1000)
 
// Invalidate pattern
globalCache.invalidate('proposals:')

Optimistic Updates

OptimisticProvider

Wrap your app to enable optimistic updates:

import { OptimisticProvider } from '@/hooks/useCredDAO'
 
<OptimisticProvider>
  <App />
</OptimisticProvider>

useOptimistic

Access optimistic state directly:

const { 
  state, 
  addPendingVote, 
  confirmVote, 
  rollbackVote,
  addPendingProposal,
  confirmProposal,
  rollbackProposal 
} = useOptimistic()
 
// state contains pending votes and proposals
state.pendingVotes
state.pendingProposals
state.pendingDelegations

Custom Query Hook

useQuery

Build custom queries with caching:

const { data, loading, error, refetch, isValidating } = useQuery(
  'custom-key',
  () => fetchCustomData(),
  {
    ttl: 5 * 60 * 1000,        // 5 minute cache
    refreshInterval: 60000,     // Auto-refresh every minute
    enabled: !!walletAddress    // Conditional fetching
  }
)

Admin Hooks

useAdminAuth

Admin wallet signature authentication:

import { useAdminAuth } from '@/hooks/useAdminAuth'
 
const { 
  isAuthenticated, 
  isAdmin, 
  adminRole, 
  token, 
  authenticate,
  logout 
} = useAdminAuth()
 
// Call authenticate to sign message
await authenticate()
 
// Use token for admin requests
fetch('/api/admin/stats', {
  headers: { Authorization: `Bearer ${token}` }
})

useInfiniteScroll

Paginated infinite scroll:

import { useInfiniteScroll, usePaginatedQuery } from '@/hooks/useInfiniteScroll'
 
const { items, hasMore, loading, fetchMore } = usePaginatedQuery(
  async (offset, limit) => {
    const res = await fetch(`/api/proposals?offset=${offset}&limit=${limit}`)
    const data = await res.json()
    return { items: data.proposals, hasMore: data.hasMore, total: data.total }
  },
  20
)
 
const { loadMoreRef, isFetching } = useInfiniteScroll(fetchMore)
 
// Add ref to load more trigger
<div ref={loadMoreRef} />