npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@klever/connect-react

v0.1.2

Published

React hooks and components for Klever Connect SDK

Readme

@klever/connect-react

React hooks and components for Klever Connect SDK

Installation

pnpm add @klever/connect-react

Overview

@klever/connect-react provides React hooks for seamless integration with the Klever blockchain. Built with TypeScript for complete type safety and designed following React best practices.

Available Hooks:

  • useKlever() - Main context hook for wallet connection and network management
  • useTransaction() - Generic transaction sending with full lifecycle control
  • useTransfer() - Convenience hook for KLV/KDA transfers
  • useFreeze() - Freeze (stake) assets to create buckets
  • useUnfreeze() - Unfreeze (unstake) assets and start cooldown
  • useDelegate() - Delegate KLV buckets to validators (via useStaking)
  • useUndelegate() - Undelegate from validators (via useStaking)
  • useClaim() - Claim staking or FPR rewards
  • useStaking() - Complete staking workflow in one hook
  • useBalance() - Monitor token balances with auto-polling
  • useTransactionMonitor() - Real-time transaction status monitoring

Key Features:

  • Type-safe hooks with full TypeScript support
  • Automatic state management (loading, error, data)
  • Built-in polling and real-time updates
  • Extension wallet integration (Klever Web Extension)
  • Network switching without disconnecting
  • Transaction lifecycle callbacks
  • React 18+ compatible

Quick Start

1. Wrap your app with KleverProvider

import { KleverProvider } from '@klever/connect-react'

function App() {
  return (
    <KleverProvider config={{ network: 'testnet' }}>
      <YourApp />
    </KleverProvider>
  )
}

2. Connect wallet and send transactions

import { useKlever, useTransaction } from '@klever/connect-react'
import { parseKLV } from '@klever/connect-core'

function TransferButton() {
  const { connect, isConnected, address } = useKlever()
  const { sendKLV, isLoading } = useTransaction()

  const handleTransfer = async () => {
    if (!isConnected) {
      await connect()
    } else {
      await sendKLV('klv1receiver...', parseKLV('10'))
    }
  }

  return (
    <button onClick={handleTransfer} disabled={isLoading}>
      {!isConnected ? 'Connect Wallet' : 'Send 10 KLV'}
    </button>
  )
}

Hooks

useStaking()

Complete staking workflow hook with freeze, unfreeze, delegate, claim, and withdraw operations.

Staking Flow:

  1. Freeze - Stake assets to create bucket
    • KLV: Creates bucket with 32-byte hash bucketID (max 100 buckets/user)
    • Other KDA: Accumulates frozen amount (no bucket limit)
  2. Delegate - Delegate KLV bucket to validator (KLV only)
  3. Claim - Claim rewards (APR or FPR based)
  4. Undelegate - Undelegate from validator
  5. Unfreeze - Start cooldown period
  6. Withdraw - Withdraw after cooldown
import { useStaking } from '@klever/connect-react'
import { parseReceipt } from '@klever/connect-provider'
import { KLV_ASSET_ID } from '@klever/connect-core'
import { useState } from 'react'

function StakingComponent() {
  const [bucketId, setBucketId] = useState<string>('')
  const { freeze, unfreeze, delegate, claim, withdraw, isLoading, error } = useStaking()

  const handleFreeze = async () => {
    // Returns TransactionSubmitResult with hash, status, transaction, and wait()
    const result = await freeze(100000, KLV_ASSET_ID)
    console.log('Transaction submitted:', result.hash)

    // Wait for confirmation and parse receipt
    const receipt = await result.wait()

    // Type-safe parsing using parseReceipt utility
    const { bucketId, amount, kda } = parseReceipt.freeze(receipt)
    console.log(`Created bucket ${bucketId} with ${amount} ${kda}`)
    setBucketId(bucketId)
  }

  return (
    <div>
      {/* Step 1: Freeze KLV to create bucket */}
      <button onClick={handleFreeze} disabled={isLoading}>
        Stake 100K KLV
      </button>

      {/* Step 2: Delegate to validator (use bucketId from freeze) */}
      <button
        onClick={() => delegate('klv1validator...', bucketId)}
        disabled={isLoading || !bucketId}
      >
        Delegate
      </button>

      {/* Step 3: Claim rewards */}
      <button onClick={() => claim(0)} disabled={isLoading}>
        Claim Rewards
      </button>

      {/* Step 4: Unfreeze KLV (requires both KDA and bucketId) */}
      <button onClick={() => unfreeze(KLV_ASSET_ID, bucketId)} disabled={isLoading || !bucketId}>
        Unfreeze KLV
      </button>

      {/* Step 4b: Unfreeze other KDA (only KDA needed, no bucketId) */}
      <button onClick={() => unfreeze('MY-TOKEN-ID')} disabled={isLoading}>
        Unfreeze MY-TOKEN
      </button>

      {/* Step 5: Withdraw */}
      <button onClick={() => withdraw(0)} disabled={isLoading}>
        Withdraw
      </button>

      {bucketId && <p>Bucket ID: {bucketId}</p>}
      {error && <p>Error: {error.message}</p>}
    </div>
  )
}

Parsing Transaction Receipts

All transaction methods return TransactionSubmitResult with a wait() method. Use the parseReceipt utility from @klever/connect-provider for type-safe parsing:

import { parseReceipt } from '@klever/connect-provider'

// Parse freeze receipt to get bucketId
const result = await freeze(100000, 'KLV')
const receipt = await result.wait()
const { bucketId, amount, kda } = parseReceipt.freeze(receipt)

// Parse claim receipt to get rewards
const claimResult = await claim(0)
const claimReceipt = await claimResult.wait()
const { rewards, totalClaimed, claimType } = parseReceipt.claim(claimReceipt)
console.log(`Claimed ${totalClaimed} in ${rewards.length} assets`)

// Parse delegate receipt
const delegateResult = await delegate('klv1validator...', bucketId)
const delegateReceipt = await delegateResult.wait()
const { validator, bucketId: delegatedBucket } = parseReceipt.delegate(delegateReceipt)

// Handle multiple transfers in one transaction
const transferResult = await sendTransaction(multiTransferContract)
const transferReceipt = await transferResult.wait()
const { sender, receiver, amount, kda, transfers } = parseReceipt.transfer(transferReceipt)
console.log(`Primary transfer: ${sender} → ${receiver}: ${amount} ${kda}`)
if (transfers) {
  console.log(`Total ${transfers.length} transfers:`)
  transfers.forEach((t) => console.log(`  ${t.sender} → ${t.receiver}: ${t.amount} ${t.kda}`))
}

Available parsers (hybrid API - simple fields + optional arrays for multiple operations):

  • parseReceipt.freeze(receipt){ bucketId, amount, kda, freezes? }
  • parseReceipt.unfreeze(receipt){ bucketId, kda, availableAt? }
  • parseReceipt.claim(receipt){ rewards[], totalClaimed, claimType? }
  • parseReceipt.withdraw(receipt){ amount, kda, withdrawType?, withdrawals? }
  • parseReceipt.delegate(receipt){ validator, bucketId }
  • parseReceipt.undelegate(receipt){ bucketId, availableAt? }
  • parseReceipt.transfer(receipt){ sender, receiver, amount, kda, transfers? }

Hybrid approach: Each parser returns the primary/first operation in simple fields, plus optional arrays when multiple receipts exist. This keeps the API simple for common cases while supporting complex multi-operation transactions.

useTransaction()

Generic transaction sending hook with callbacks and state management.

Important: sendKLV and sendKDA expect amounts in smallest units (no automatic precision conversion).

import { useTransaction } from '@klever/connect-react'
import { parseKLV, parseUnits } from '@klever/connect-core'

function TransferComponent() {
  const { sendKLV, sendKDA, isLoading } = useTransaction()

  // Send KLV (use parseKLV for human-readable amounts)
  const handleSendKLV = async () => {
    // Correct: parseKLV converts "100" to 100000000 (smallest units)
    await sendKLV('klv1receiver...', parseKLV('100'))

    // Also correct: using bigint directly
    await sendKLV('klv1receiver...', 100000000n)
  }

  // Send KDA (use parseUnits with correct precision)
  const handleSendKDA = async () => {
    // For token with 6 decimals
    await sendKDA('klv1receiver...', parseUnits('50', 6), 'MY-TOKEN')

    // For token with 18 decimals
    await sendKDA('klv1receiver...', parseUnits('50', 18), 'OTHER-TOKEN')
  }

  return (
    <div>
      <button onClick={handleSendKLV} disabled={isLoading}>
        Send 100 KLV
      </button>
      <button onClick={handleSendKDA} disabled={isLoading}>
        Send 50 Tokens
      </button>
    </div>
  )
}

Why no auto-conversion?

  • Prevents accidental 1000x transfers
  • Consistent with other hooks (freeze, unfreeze, etc.)
  • Explicit is better than implicit
  • Different tokens have different precisions (6, 8, 18, etc.)

useKlever()

Main context hook for accessing Klever blockchain functionality. Provides wallet connection, network management, and blockchain provider access.

Must be used within a KleverProvider component.

import { useKlever } from '@klever/connect-react'

function WalletButton() {
  const {
    connect,
    disconnect,
    isConnected,
    isConnecting,
    address,
    currentNetwork,
    switchNetwork,
    extensionInstalled,
    error,
  } = useKlever()

  if (!extensionInstalled) {
    return (
      <div>
        Please install{' '}
        <a href="https://klever.io/extension" target="_blank">
          Klever Extension
        </a>
      </div>
    )
  }

  return (
    <div>
      {!isConnected ? (
        <button onClick={connect} disabled={isConnecting}>
          {isConnecting ? 'Connecting...' : 'Connect Wallet'}
        </button>
      ) : (
        <div>
          <p>
            Connected: {address?.slice(0, 10)}...{address?.slice(-8)}
          </p>
          <p>Network: {currentNetwork}</p>
          <button onClick={() => switchNetwork('mainnet')}>Switch to Mainnet</button>
          <button onClick={disconnect}>Disconnect</button>
        </div>
      )}
      {error && <p style={{ color: 'red' }}>Error: {error.message}</p>}
    </div>
  )
}

Return value:

  • wallet - Wallet instance (undefined if not connected)
  • provider - KleverProvider for blockchain queries
  • address - Connected wallet address
  • isConnected - Boolean indicating connection status
  • isConnecting - Boolean indicating connection in progress
  • error - Error object if any operation failed
  • extensionInstalled - Boolean indicating extension availability
  • searchingExtension - Boolean indicating extension check in progress
  • currentNetwork - Current network name
  • connect() - Async function to initiate wallet connection
  • disconnect() - Function to disconnect wallet
  • switchNetwork(network) - Async function to switch networks

useTransaction()

Core hook for sending any type of transaction with full lifecycle control.

import { useTransaction } from '@klever/connect-react'
import { parseKLV, parseUnits, TXType } from '@klever/connect-core'

function TransactionDemo() {
  const { sendTransaction, sendKLV, sendKDA, isLoading, error, data, reset } = useTransaction({
    onSuccess: (receipt) => {
      console.log('Transaction successful:', receipt.hash)
    },
    onError: (err) => {
      console.error('Transaction failed:', err.message)
    },
  })

  // Send KLV using convenience method
  const handleSendKLV = async () => {
    const result = await sendKLV('klv1receiver...', parseKLV('100'))
    console.log('Transaction hash:', result.hash)

    // Wait for confirmation
    const receipt = await result.wait()
    console.log('Confirmed in block:', receipt.blockNumber)
  }

  // Send KDA tokens
  const handleSendKDA = async () => {
    await sendKDA('klv1receiver...', parseUnits('50', 6), 'MY-KDA-TOKEN')
  }

  // Send custom transaction
  const handleCustomTx = async () => {
    await sendTransaction({
      contractType: TXType.Transfer,
      receiver: 'klv1receiver...',
      amount: 1000000,
      kda: 'MY-TOKEN',
    })
  }

  return (
    <div>
      <button onClick={handleSendKLV} disabled={isLoading}>
        Send 100 KLV
      </button>
      <button onClick={handleSendKDA} disabled={isLoading}>
        Send 50 KDA
      </button>
      <button onClick={handleCustomTx} disabled={isLoading}>
        Custom Transaction
      </button>

      {isLoading && <p>Transaction in progress...</p>}
      {error && (
        <div>
          <p style={{ color: 'red' }}>Error: {error.message}</p>
          <button onClick={reset}>Try Again</button>
        </div>
      )}
      {data && <p style={{ color: 'green' }}>Success! Hash: {data.hash}</p>}
    </div>
  )
}

Important: Amounts must be in smallest units. Use parseKLV() or parseUnits() for human-readable amounts.

useTransfer()

Convenience hook specifically for KLV/KDA token transfers.

import { useTransfer } from '@klever/connect-react'
import { parseKLV, parseUnits } from '@klever/connect-core'
import { useState } from 'react'

function TransferForm() {
  const [recipient, setRecipient] = useState('')
  const [amount, setAmount] = useState('')
  const { transfer, isLoading, error } = useTransfer({
    onSuccess: () => {
      alert('Transfer successful!')
      setAmount('')
      setRecipient('')
    },
  })

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()
    await transfer({
      receiver: recipient,
      amount: parseKLV(amount),
    })
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={recipient}
        onChange={(e) => setRecipient(e.target.value)}
        placeholder="Recipient address"
        disabled={isLoading}
      />
      <input
        type="number"
        value={amount}
        onChange={(e) => setAmount(e.target.value)}
        placeholder="Amount in KLV"
        disabled={isLoading}
      />
      <button type="submit" disabled={isLoading || !recipient || !amount}>
        {isLoading ? 'Sending...' : 'Send KLV'}
      </button>
      {error && <p style={{ color: 'red' }}>{error.message}</p>}
    </form>
  )
}

useFreeze()

Freeze (stake) assets to create staking buckets.

import { useFreeze } from '@klever/connect-react'
import { parseKLV, KLV_ASSET_ID } from '@klever/connect-core'
import { parseReceipt } from '@klever/connect-provider'
import { useState } from 'react'

function FreezeComponent() {
  const [bucketId, setBucketId] = useState<string>('')
  const { freeze, isLoading, error } = useFreeze({
    onSuccess: async (receipt) => {
      console.log('Freeze successful!', receipt.hash)
    },
  })

  const handleFreeze = async () => {
    const result = await freeze({
      amount: parseKLV('1000'),
      kda: KLV_ASSET_ID,
    })

    // Wait for confirmation and get bucket ID
    const receipt = await result.wait()
    const { bucketId } = parseReceipt.freeze(receipt)
    setBucketId(bucketId)
    console.log('Created bucket:', bucketId)
  }

  return (
    <div>
      <button onClick={handleFreeze} disabled={isLoading}>
        {isLoading ? 'Freezing...' : 'Freeze 1000 KLV'}
      </button>
      {error && <p style={{ color: 'red' }}>Error: {error.message}</p>}
      {bucketId && (
        <p>
          Bucket ID: {bucketId.slice(0, 10)}...{bucketId.slice(-8)}
        </p>
      )}
    </div>
  )
}

Note: For KLV, creates a bucket (max 100 per user). For KDA, accumulates frozen amount without bucket limit.

useUnfreeze()

Unfreeze (unstake) assets and start the cooldown period.

import { useUnfreeze } from '@klever/connect-react'
import { KLV_ASSET_ID } from '@klever/connect-core'
import { parseReceipt } from '@klever/connect-provider'

function UnfreezeComponent({ bucketId }: { bucketId: string }) {
  const { unfreeze, isLoading, error, data } = useUnfreeze({
    onSuccess: async (receipt) => {
      const result = await receipt.wait?.()
      if (result) {
        const { availableAt } = parseReceipt.unfreeze(result)
        if (availableAt) {
          const date = new Date(availableAt)
          console.log('Available for withdrawal at:', date.toLocaleString())
        }
      }
    },
  })

  const handleUnfreeze = async () => {
    await unfreeze({
      kda: KLV_ASSET_ID,
      bucketId: bucketId,
    })
  }

  return (
    <div>
      <button onClick={handleUnfreeze} disabled={isLoading || !bucketId}>
        {isLoading ? 'Unfreezing...' : 'Unfreeze KLV'}
      </button>
      {error && <p style={{ color: 'red' }}>Error: {error.message}</p>}
      {data && <p style={{ color: 'green' }}>Cooldown started! Hash: {data.hash}</p>}
    </div>
  )
}

Note: Must wait for cooldown period before withdrawal. For KLV, bucketId is required. For KDA, bucketId is optional.

useClaim()

Claim staking rewards (APR) or FPR rewards.

import { useClaim } from '@klever/connect-react'
import { parseReceipt } from '@klever/connect-provider'
import { useState } from 'react'

function ClaimRewards() {
  const [totalClaimed, setTotalClaimed] = useState('')
  const { claim, isLoading, error } = useClaim({
    onSuccess: async (receipt) => {
      const result = await receipt.wait?.()
      if (result) {
        const { rewards, totalClaimed } = parseReceipt.claim(result)
        console.log(`Claimed ${totalClaimed} in ${rewards.length} assets`)
        setTotalClaimed(totalClaimed)
      }
    },
  })

  const handleClaimStaking = async () => {
    await claim({
      claimType: 0, // 0 = Staking (APR rewards)
      id: 'KLV',
    })
  }

  const handleClaimFPR = async () => {
    await claim({
      claimType: 3, // 3 = FPR rewards
      id: 'MY-KDA-TOKEN',
    })
  }

  return (
    <div>
      <button onClick={handleClaimStaking} disabled={isLoading}>
        {isLoading ? 'Claiming...' : 'Claim Staking Rewards'}
      </button>
      <button onClick={handleClaimFPR} disabled={isLoading}>
        Claim FPR Rewards
      </button>
      {error && <p style={{ color: 'red' }}>Error: {error.message}</p>}
      {totalClaimed && <p style={{ color: 'green' }}>Total claimed: {totalClaimed}</p>}
    </div>
  )
}

Claim Types:

  • 0 - Staking (APR-based rewards)
  • 1 - Market (marketplace rewards)
  • 2 - Allowance (allowance-based claims)
  • 3 - FPR (dynamic rewards from KDA staking)

useBalance()

Monitor token balances with automatic polling every 10 seconds.

import { useBalance } from '@klever/connect-react'
import { useState } from 'react'

function BalanceDisplay() {
  const [token, setToken] = useState('KLV')
  const { balance, isLoading, error, refetch } = useBalance(token)

  return (
    <div>
      <select value={token} onChange={(e) => setToken(e.target.value)}>
        <option value="KLV">KLV</option>
        <option value="KDA-TOKEN-1">Token 1</option>
        <option value="KDA-TOKEN-2">Token 2</option>
      </select>

      {isLoading && <p>Loading balance...</p>}
      {error && <p style={{ color: 'red' }}>Error: {error.message}</p>}
      {balance && (
        <div>
          <h3>
            {balance.formatted} {balance.token}
          </h3>
          <p>Raw amount: {balance.amount.toString()}</p>
          <p>Precision: {balance.precision} decimals</p>
          <button onClick={refetch}>Refresh</button>
        </div>
      )}
    </div>
  )
}

// Check balance of any address
function AddressBalanceChecker() {
  const [address, setAddress] = useState('')
  const { balance, isLoading } = useBalance('KLV', address)

  return (
    <div>
      <input
        type="text"
        value={address}
        onChange={(e) => setAddress(e.target.value)}
        placeholder="Enter Klever address..."
      />
      {isLoading && <p>Loading...</p>}
      {balance && <p>Balance: {balance.formatted} KLV</p>}
    </div>
  )
}

Features:

  • Auto-polls every 10 seconds
  • Manual refresh with refetch()
  • Check any address (not just connected wallet)
  • Returns formatted and raw amounts
  • Network change triggers automatic refetch

useTransactionMonitor()

Real-time transaction monitoring with automatic polling and status updates.

import { useTransactionMonitor, useTransaction } from '@klever/connect-react'
import { useKlever } from '@klever/connect-react'
import { useState } from 'react'

function TransactionTracker() {
  const { provider } = useKlever()
  const { sendKLV } = useTransaction()
  const [txHash, setTxHash] = useState('')

  const { monitor, cancel, activeMonitors, isMonitoring, getStatus } = useTransactionMonitor({
    provider,
    initialDelay: 1500, // Wait 1.5s before first check
    pollInterval: 2000, // Poll every 2 seconds
    timeout: 60000, // 60 second timeout
    onStatusUpdate: (status) => {
      console.log(`Attempt ${status.attempts}: ${status.status}`)
      console.log(`Time elapsed: ${status.elapsedTime}ms`)
    },
    onComplete: (result) => {
      console.log('Final status:', result.status)
      if (result.transaction) {
        console.log('Block number:', result.transaction.blockNumber)
      }
    },
    onError: (error) => {
      console.error('Monitoring error:', error.message)
    },
  })

  const handleSendAndMonitor = async () => {
    try {
      // Send transaction
      const result = await sendKLV('klv1receiver...', 1000000n)
      setTxHash(result.hash)

      // Monitor the transaction
      const finalResult = await monitor(result.hash)
      console.log('Transaction confirmed:', finalResult.status)
    } catch (err) {
      console.error('Failed:', err)
    }
  }

  return (
    <div>
      <button onClick={handleSendAndMonitor}>Send & Monitor</button>

      <p>Active monitors: {activeMonitors.length}</p>
      {isMonitoring && <p>Monitoring transactions...</p>}

      {activeMonitors.map((hash) => {
        const status = getStatus(hash)
        return (
          <div key={hash} style={{ border: '1px solid #ccc', padding: '10px', margin: '5px' }}>
            <p>
              Hash: {hash.slice(0, 10)}...{hash.slice(-8)}
            </p>
            <p>Status: {status?.status}</p>
            <p>Attempts: {status?.attempts}</p>
            <p>Elapsed: {status?.elapsedTime}ms</p>
            <button onClick={() => cancel(hash)}>Cancel</button>
          </div>
        )
      })}

      {txHash && (
        <p>
          Latest TX: {txHash.slice(0, 10)}...{txHash.slice(-8)}
        </p>
      )}
    </div>
  )
}

Features:

  • Monitor up to 10 concurrent transactions
  • Exponential backoff to reduce API load
  • Real-time status updates via callbacks
  • Cancellable monitoring
  • Custom polling intervals and timeouts
  • Automatic cleanup on unmount

Complete Examples

Full Wallet Connection Component

import { useKlever } from '@klever/connect-react'
import { useState } from 'react'

function WalletConnector() {
  const {
    connect,
    disconnect,
    isConnected,
    isConnecting,
    address,
    currentNetwork,
    switchNetwork,
    extensionInstalled,
    searchingExtension,
    error,
  } = useKlever()

  const [showNetworks, setShowNetworks] = useState(false)

  if (searchingExtension) {
    return <div>Checking for Klever Extension...</div>
  }

  if (!extensionInstalled) {
    return (
      <div style={{ padding: '20px', border: '2px solid red', borderRadius: '8px' }}>
        <h3>Klever Extension Not Found</h3>
        <p>Please install the Klever Web Extension to continue.</p>
        <a
          href="https://klever.io/extension"
          target="_blank"
          rel="noopener noreferrer"
          style={{ color: 'blue', textDecoration: 'underline' }}
        >
          Download Extension
        </a>
      </div>
    )
  }

  return (
    <div style={{ padding: '20px', border: '1px solid #ccc', borderRadius: '8px' }}>
      {!isConnected ? (
        <div>
          <h3>Connect Your Wallet</h3>
          <button
            onClick={connect}
            disabled={isConnecting}
            style={{
              padding: '10px 20px',
              fontSize: '16px',
              cursor: isConnecting ? 'not-allowed' : 'pointer',
            }}
          >
            {isConnecting ? 'Connecting...' : 'Connect Wallet'}
          </button>
        </div>
      ) : (
        <div>
          <h3>Wallet Connected</h3>
          <p>
            <strong>Address:</strong> {address?.slice(0, 10)}...{address?.slice(-8)}
          </p>
          <p>
            <strong>Network:</strong> {currentNetwork}
          </p>

          <button
            onClick={() => setShowNetworks(!showNetworks)}
            style={{ marginRight: '10px', padding: '8px 16px' }}
          >
            {showNetworks ? 'Hide Networks' : 'Switch Network'}
          </button>

          {showNetworks && (
            <div style={{ marginTop: '10px' }}>
              <button onClick={() => switchNetwork('mainnet')} style={{ margin: '5px' }}>
                Mainnet
              </button>
              <button onClick={() => switchNetwork('testnet')} style={{ margin: '5px' }}>
                Testnet
              </button>
              <button onClick={() => switchNetwork('devnet')} style={{ margin: '5px' }}>
                Devnet
              </button>
            </div>
          )}

          <button
            onClick={disconnect}
            style={{
              padding: '8px 16px',
              backgroundColor: '#dc3545',
              color: 'white',
              border: 'none',
              borderRadius: '4px',
              cursor: 'pointer',
            }}
          >
            Disconnect
          </button>
        </div>
      )}

      {error && (
        <div
          style={{
            marginTop: '10px',
            padding: '10px',
            backgroundColor: '#f8d7da',
            color: '#721c24',
          }}
        >
          <strong>Error:</strong> {error.message}
        </div>
      )}
    </div>
  )
}

Token Transfer Component

import { useTransfer, useBalance, useKlever } from '@klever/connect-react'
import { parseKLV, formatUnits } from '@klever/connect-core'
import { useState } from 'react'

function TokenTransfer() {
  const { isConnected, address } = useKlever()
  const { balance } = useBalance('KLV')
  const { transfer, isLoading, error, data, reset } = useTransfer()

  const [recipient, setRecipient] = useState('')
  const [amount, setAmount] = useState('')

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()
    reset()

    try {
      const result = await transfer({
        receiver: recipient,
        amount: parseKLV(amount),
      })

      console.log('Transaction submitted:', result.hash)
      const receipt = await result.wait()
      console.log('Transaction confirmed:', receipt.blockNumber)

      // Clear form
      setRecipient('')
      setAmount('')
    } catch (err) {
      console.error('Transfer failed:', err)
    }
  }

  if (!isConnected) {
    return <div>Please connect your wallet first</div>
  }

  return (
    <div style={{ maxWidth: '500px', padding: '20px' }}>
      <h2>Transfer KLV</h2>

      {balance && (
        <div style={{ padding: '10px', backgroundColor: '#f0f0f0', marginBottom: '20px' }}>
          <p>
            <strong>Your Balance:</strong> {balance.formatted} KLV
          </p>
          <p style={{ fontSize: '12px', color: '#666' }}>
            Address: {address?.slice(0, 10)}...{address?.slice(-8)}
          </p>
        </div>
      )}

      <form onSubmit={handleSubmit}>
        <div style={{ marginBottom: '15px' }}>
          <label style={{ display: 'block', marginBottom: '5px' }}>Recipient Address</label>
          <input
            type="text"
            value={recipient}
            onChange={(e) => setRecipient(e.target.value)}
            placeholder="klv1..."
            disabled={isLoading}
            style={{
              width: '100%',
              padding: '8px',
              fontSize: '14px',
              border: '1px solid #ccc',
              borderRadius: '4px',
            }}
          />
        </div>

        <div style={{ marginBottom: '15px' }}>
          <label style={{ display: 'block', marginBottom: '5px' }}>Amount (KLV)</label>
          <input
            type="number"
            value={amount}
            onChange={(e) => setAmount(e.target.value)}
            placeholder="0.00"
            step="0.000001"
            disabled={isLoading}
            style={{
              width: '100%',
              padding: '8px',
              fontSize: '14px',
              border: '1px solid #ccc',
              borderRadius: '4px',
            }}
          />
        </div>

        <button
          type="submit"
          disabled={isLoading || !recipient || !amount}
          style={{
            width: '100%',
            padding: '12px',
            fontSize: '16px',
            backgroundColor: isLoading ? '#ccc' : '#007bff',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            cursor: isLoading ? 'not-allowed' : 'pointer',
          }}
        >
          {isLoading ? 'Sending...' : 'Send KLV'}
        </button>
      </form>

      {error && (
        <div
          style={{
            marginTop: '15px',
            padding: '10px',
            backgroundColor: '#f8d7da',
            color: '#721c24',
            borderRadius: '4px',
          }}
        >
          <strong>Error:</strong> {error.message}
        </div>
      )}

      {data && (
        <div
          style={{
            marginTop: '15px',
            padding: '10px',
            backgroundColor: '#d4edda',
            color: '#155724',
            borderRadius: '4px',
          }}
        >
          <strong>Success!</strong>
          <p style={{ fontSize: '12px', marginTop: '5px', wordBreak: 'break-all' }}>
            Transaction Hash: {data.hash}
          </p>
        </div>
      )}
    </div>
  )
}

Staking Dashboard Component

import { useStaking, useBalance, useKlever } from '@klever/connect-react'
import { parseKLV, KLV_ASSET_ID, formatUnits } from '@klever/connect-core'
import { parseReceipt } from '@klever/connect-provider'
import { useState } from 'react'

function StakingDashboard() {
  const { isConnected } = useKlever()
  const { balance } = useBalance(KLV_ASSET_ID)
  const { freeze, unfreeze, delegate, claim, isLoading, error } = useStaking()

  const [bucketId, setBucketId] = useState('')
  const [validatorAddress, setValidatorAddress] = useState('')
  const [stakeAmount, setStakeAmount] = useState('1000')

  const handleFreeze = async () => {
    try {
      const result = await freeze(parseKLV(stakeAmount), KLV_ASSET_ID)
      console.log('Freeze submitted:', result.hash)

      const receipt = await result.wait()
      const { bucketId: newBucketId } = parseReceipt.freeze(receipt)
      setBucketId(newBucketId)
      alert(`Bucket created: ${newBucketId}`)
    } catch (err) {
      console.error('Freeze failed:', err)
    }
  }

  const handleDelegate = async () => {
    if (!bucketId || !validatorAddress) {
      alert('Please provide bucket ID and validator address')
      return
    }

    try {
      const result = await delegate(validatorAddress, bucketId)
      const receipt = await result.wait()
      alert('Successfully delegated to validator!')
    } catch (err) {
      console.error('Delegate failed:', err)
    }
  }

  const handleClaim = async () => {
    try {
      const result = await claim(0) // 0 = Staking rewards
      const receipt = await result.wait()
      const { totalClaimed, rewards } = parseReceipt.claim(receipt)
      alert(`Claimed ${totalClaimed} in ${rewards.length} assets!`)
    } catch (err) {
      console.error('Claim failed:', err)
    }
  }

  const handleUnfreeze = async () => {
    if (!bucketId) {
      alert('Please provide bucket ID')
      return
    }

    try {
      const result = await unfreeze(KLV_ASSET_ID, bucketId)
      const receipt = await result.wait()
      const { availableAt } = parseReceipt.unfreeze(receipt)
      if (availableAt) {
        alert(`Cooldown started! Available at: ${new Date(availableAt).toLocaleString()}`)
      }
    } catch (err) {
      console.error('Unfreeze failed:', err)
    }
  }

  if (!isConnected) {
    return <div>Please connect your wallet to access staking features</div>
  }

  return (
    <div style={{ maxWidth: '600px', padding: '20px' }}>
      <h2>Staking Dashboard</h2>

      {balance && (
        <div style={{ padding: '15px', backgroundColor: '#f0f0f0', marginBottom: '20px' }}>
          <h3>Available Balance</h3>
          <p style={{ fontSize: '24px', margin: '10px 0' }}>
            <strong>{balance.formatted} KLV</strong>
          </p>
        </div>
      )}

      {/* Freeze Section */}
      <div style={{ marginBottom: '20px', padding: '15px', border: '1px solid #ccc' }}>
        <h3>1. Freeze (Stake) KLV</h3>
        <input
          type="number"
          value={stakeAmount}
          onChange={(e) => setStakeAmount(e.target.value)}
          placeholder="Amount to stake"
          style={{ width: '100%', padding: '8px', marginBottom: '10px' }}
        />
        <button
          onClick={handleFreeze}
          disabled={isLoading}
          style={{ width: '100%', padding: '10px' }}
        >
          {isLoading ? 'Processing...' : `Freeze ${stakeAmount} KLV`}
        </button>
        {bucketId && (
          <p style={{ marginTop: '10px', fontSize: '12px' }}>
            Bucket ID: {bucketId.slice(0, 10)}...{bucketId.slice(-8)}
          </p>
        )}
      </div>

      {/* Delegate Section */}
      <div style={{ marginBottom: '20px', padding: '15px', border: '1px solid #ccc' }}>
        <h3>2. Delegate to Validator</h3>
        <input
          type="text"
          value={validatorAddress}
          onChange={(e) => setValidatorAddress(e.target.value)}
          placeholder="Validator address"
          style={{ width: '100%', padding: '8px', marginBottom: '10px' }}
        />
        <input
          type="text"
          value={bucketId}
          onChange={(e) => setBucketId(e.target.value)}
          placeholder="Bucket ID from freeze"
          style={{ width: '100%', padding: '8px', marginBottom: '10px' }}
        />
        <button
          onClick={handleDelegate}
          disabled={isLoading}
          style={{ width: '100%', padding: '10px' }}
        >
          {isLoading ? 'Processing...' : 'Delegate'}
        </button>
      </div>

      {/* Claim Section */}
      <div style={{ marginBottom: '20px', padding: '15px', border: '1px solid #ccc' }}>
        <h3>3. Claim Rewards</h3>
        <button
          onClick={handleClaim}
          disabled={isLoading}
          style={{ width: '100%', padding: '10px', backgroundColor: '#28a745', color: 'white' }}
        >
          {isLoading ? 'Processing...' : 'Claim Staking Rewards'}
        </button>
      </div>

      {/* Unfreeze Section */}
      <div style={{ marginBottom: '20px', padding: '15px', border: '1px solid #ccc' }}>
        <h3>4. Unfreeze (Unstake)</h3>
        <p style={{ fontSize: '12px', marginBottom: '10px' }}>
          Note: Starts cooldown period before withdrawal
        </p>
        <button
          onClick={handleUnfreeze}
          disabled={isLoading}
          style={{ width: '100%', padding: '10px', backgroundColor: '#dc3545', color: 'white' }}
        >
          {isLoading ? 'Processing...' : 'Unfreeze KLV'}
        </button>
      </div>

      {error && (
        <div style={{ padding: '10px', backgroundColor: '#f8d7da', color: '#721c24' }}>
          <strong>Error:</strong> {error.message}
        </div>
      )}
    </div>
  )
}

Transaction History Component

import { useTransactionMonitor, useTransaction, useKlever } from '@klever/connect-react'
import { parseKLV } from '@klever/connect-core'
import { useState } from 'react'

interface TransactionHistoryItem {
  hash: string
  status: 'pending' | 'confirmed' | 'failed'
  timestamp: number
  attempts: number
}

function TransactionHistory() {
  const { provider } = useKlever()
  const { sendKLV } = useTransaction()
  const [history, setHistory] = useState<TransactionHistoryItem[]>([])

  const { monitor, activeMonitors, getStatus } = useTransactionMonitor({
    provider,
    onStatusUpdate: (status) => {
      setHistory((prev) =>
        prev.map((item) =>
          item.hash === status.hash
            ? { ...item, status: status.status, attempts: status.attempts }
            : item,
        ),
      )
    },
    onComplete: (result) => {
      setHistory((prev) =>
        prev.map((item) => (item.hash === result.hash ? { ...item, status: result.status } : item)),
      )
    },
  })

  const handleSendTransaction = async () => {
    try {
      const result = await sendKLV('klv1receiver...', parseKLV('10'))

      // Add to history
      setHistory((prev) => [
        {
          hash: result.hash,
          status: 'pending',
          timestamp: Date.now(),
          attempts: 0,
        },
        ...prev,
      ])

      // Monitor transaction
      await monitor(result.hash)
    } catch (err) {
      console.error('Transaction failed:', err)
    }
  }

  const getStatusColor = (status: string) => {
    switch (status) {
      case 'confirmed':
        return '#28a745'
      case 'pending':
        return '#ffc107'
      case 'failed':
        return '#dc3545'
      default:
        return '#6c757d'
    }
  }

  return (
    <div style={{ maxWidth: '800px', padding: '20px' }}>
      <h2>Transaction History</h2>

      <button
        onClick={handleSendTransaction}
        style={{
          padding: '10px 20px',
          marginBottom: '20px',
          backgroundColor: '#007bff',
          color: 'white',
          border: 'none',
          borderRadius: '4px',
        }}
      >
        Send Test Transaction
      </button>

      <div style={{ marginBottom: '20px' }}>
        <strong>Active Monitors:</strong> {activeMonitors.length}
      </div>

      <div>
        {history.length === 0 ? (
          <p>No transactions yet</p>
        ) : (
          history.map((item) => {
            const liveStatus = getStatus(item.hash)
            const currentStatus = liveStatus?.status || item.status

            return (
              <div
                key={item.hash}
                style={{
                  padding: '15px',
                  marginBottom: '10px',
                  border: '1px solid #ccc',
                  borderRadius: '4px',
                  backgroundColor: '#f9f9f9',
                }}
              >
                <div
                  style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}
                >
                  <div style={{ flex: 1 }}>
                    <p style={{ margin: '5px 0', fontSize: '14px' }}>
                      <strong>Hash:</strong> {item.hash.slice(0, 12)}...{item.hash.slice(-10)}
                    </p>
                    <p style={{ margin: '5px 0', fontSize: '12px', color: '#666' }}>
                      {new Date(item.timestamp).toLocaleString()}
                    </p>
                    {liveStatus && (
                      <p style={{ margin: '5px 0', fontSize: '12px', color: '#666' }}>
                        Attempts: {liveStatus.attempts} | Elapsed: {liveStatus.elapsedTime}ms
                      </p>
                    )}
                  </div>
                  <div>
                    <span
                      style={{
                        padding: '6px 12px',
                        borderRadius: '4px',
                        backgroundColor: getStatusColor(currentStatus),
                        color: 'white',
                        fontSize: '12px',
                        fontWeight: 'bold',
                      }}
                    >
                      {currentStatus.toUpperCase()}
                    </span>
                  </div>
                </div>
              </div>
            )
          })
        )}
      </div>
    </div>
  )
}

Multi-Step Workflow Component

import { useStaking, useKlever, useBalance } from '@klever/connect-react'
import { parseKLV, KLV_ASSET_ID } from '@klever/connect-core'
import { parseReceipt } from '@klever/connect-provider'
import { useState } from 'react'

type Step = 'freeze' | 'delegate' | 'claim' | 'unfreeze' | 'complete'

function StakingWorkflow() {
  const { isConnected } = useKlever()
  const { balance, refetch } = useBalance(KLV_ASSET_ID)
  const { freeze, delegate, claim, unfreeze, isLoading } = useStaking()

  const [currentStep, setCurrentStep] = useState<Step>('freeze')
  const [bucketId, setBucketId] = useState('')
  const [validatorAddress] = useState('klv1fpwjz234gy8aaae3gx0e8q9f52vymzzn3z5q0s5h7d5zjv5x9h4y') // Example
  const [logs, setLogs] = useState<string[]>([])

  const addLog = (message: string) => {
    setLogs((prev) => [`[${new Date().toLocaleTimeString()}] ${message}`, ...prev])
  }

  const handleFreeze = async () => {
    try {
      addLog('Starting freeze transaction...')
      const result = await freeze(parseKLV('1000'), KLV_ASSET_ID)
      addLog(`Transaction submitted: ${result.hash}`)

      const receipt = await result.wait()
      const { bucketId: newBucketId } = parseReceipt.freeze(receipt)
      setBucketId(newBucketId)

      addLog(`Bucket created: ${newBucketId}`)
      addLog('Freeze completed! Moving to delegation step.')
      setCurrentStep('delegate')
      refetch()
    } catch (err) {
      addLog(`Freeze failed: ${err instanceof Error ? err.message : 'Unknown error'}`)
    }
  }

  const handleDelegate = async () => {
    try {
      addLog('Starting delegation...')
      const result = await delegate(validatorAddress, bucketId)
      addLog(`Transaction submitted: ${result.hash}`)

      await result.wait()
      addLog('Successfully delegated to validator!')
      addLog('You can now claim rewards. Moving to claim step.')
      setCurrentStep('claim')
    } catch (err) {
      addLog(`Delegation failed: ${err instanceof Error ? err.message : 'Unknown error'}`)
    }
  }

  const handleClaim = async () => {
    try {
      addLog('Claiming rewards...')
      const result = await claim(0)
      addLog(`Transaction submitted: ${result.hash}`)

      const receipt = await result.wait()
      const { totalClaimed, rewards } = parseReceipt.claim(receipt)
      addLog(`Claimed ${totalClaimed} in ${rewards.length} assets!`)
      addLog('Rewards claimed! You can unfreeze now.')
      setCurrentStep('unfreeze')
      refetch()
    } catch (err) {
      addLog(`Claim failed: ${err instanceof Error ? err.message : 'Unknown error'}`)
    }
  }

  const handleUnfreeze = async () => {
    try {
      addLog('Starting unfreeze...')
      const result = await unfreeze(KLV_ASSET_ID, bucketId)
      addLog(`Transaction submitted: ${result.hash}`)

      const receipt = await result.wait()
      const { availableAt } = parseReceipt.unfreeze(receipt)

      if (availableAt) {
        addLog(`Cooldown started! Available at: ${new Date(availableAt).toLocaleString()}`)
      }
      addLog('Workflow complete!')
      setCurrentStep('complete')
      refetch()
    } catch (err) {
      addLog(`Unfreeze failed: ${err instanceof Error ? err.message : 'Unknown error'}`)
    }
  }

  const renderStep = () => {
    switch (currentStep) {
      case 'freeze':
        return (
          <div>
            <h3>Step 1: Freeze KLV</h3>
            <p>Stake 1000 KLV to create a staking bucket.</p>
            {balance && <p>Available: {balance.formatted} KLV</p>}
            <button onClick={handleFreeze} disabled={isLoading}>
              {isLoading ? 'Processing...' : 'Freeze 1000 KLV'}
            </button>
          </div>
        )

      case 'delegate':
        return (
          <div>
            <h3>Step 2: Delegate to Validator</h3>
            <p>Delegate your bucket to a validator to earn rewards.</p>
            <p style={{ fontSize: '12px' }}>Bucket ID: {bucketId}</p>
            <p style={{ fontSize: '12px' }}>Validator: {validatorAddress}</p>
            <button onClick={handleDelegate} disabled={isLoading}>
              {isLoading ? 'Processing...' : 'Delegate'}
            </button>
          </div>
        )

      case 'claim':
        return (
          <div>
            <h3>Step 3: Claim Rewards</h3>
            <p>Claim your staking rewards.</p>
            <button onClick={handleClaim} disabled={isLoading}>
              {isLoading ? 'Processing...' : 'Claim Rewards'}
            </button>
          </div>
        )

      case 'unfreeze':
        return (
          <div>
            <h3>Step 4: Unfreeze</h3>
            <p>Unfreeze your KLV to start the cooldown period.</p>
            <button onClick={handleUnfreeze} disabled={isLoading}>
              {isLoading ? 'Processing...' : 'Unfreeze KLV'}
            </button>
          </div>
        )

      case 'complete':
        return (
          <div>
            <h3>Workflow Complete!</h3>
            <p>You have successfully completed the full staking workflow.</p>
            <button onClick={() => window.location.reload()}>Start Over</button>
          </div>
        )
    }
  }

  if (!isConnected) {
    return <div>Please connect your wallet to start the workflow</div>
  }

  return (
    <div style={{ maxWidth: '600px', padding: '20px' }}>
      <h2>Complete Staking Workflow</h2>

      {/* Progress Indicator */}
      <div style={{ display: 'flex', marginBottom: '30px', justifyContent: 'space-between' }}>
        {['freeze', 'delegate', 'claim', 'unfreeze', 'complete'].map((step, index) => {
          const stepOrder = ['freeze', 'delegate', 'claim', 'unfreeze', 'complete']
          const currentIndex = stepOrder.indexOf(currentStep)
          const isActive = index === currentIndex
          const isComplete = index < currentIndex

          return (
            <div
              key={step}
              style={{
                flex: 1,
                textAlign: 'center',
                padding: '10px',
                backgroundColor: isActive ? '#007bff' : isComplete ? '#28a745' : '#e9ecef',
                color: isActive || isComplete ? 'white' : '#6c757d',
                borderRadius: '4px',
                margin: '0 5px',
                fontSize: '12px',
              }}
            >
              {step.toUpperCase()}
            </div>
          )
        })}
      </div>

      {/* Current Step */}
      <div
        style={{
          padding: '20px',
          border: '1px solid #ccc',
          borderRadius: '8px',
          marginBottom: '20px',
        }}
      >
        {renderStep()}
      </div>

      {/* Logs */}
      <div>
        <h4>Activity Log</h4>
        <div
          style={{
            maxHeight: '200px',
            overflowY: 'auto',
            padding: '10px',
            backgroundColor: '#f5f5f5',
            borderRadius: '4px',
            fontSize: '12px',
            fontFamily: 'monospace',
          }}
        >
          {logs.length === 0 ? (
            <p>No activity yet</p>
          ) : (
            logs.map((log, index) => (
              <div key={index} style={{ marginBottom: '5px' }}>
                {log}
              </div>
            ))
          )}
        </div>
      </div>
    </div>
  )
}

React Best Practices

Handling Re-renders

import { useKlever, useBalance } from '@klever/connect-react'
import { useMemo, useCallback } from 'react'

function OptimizedComponent() {
  const { address, provider } = useKlever()
  const { balance } = useBalance('KLV')

  // Memoize expensive calculations
  const formattedBalance = useMemo(() => {
    if (!balance) return '0'
    return new Intl.NumberFormat('en-US', {
      minimumFractionDigits: 2,
      maximumFractionDigits: 6,
    }).format(Number(balance.formatted))
  }, [balance])

  // Memoize event handlers
  const handleClick = useCallback(() => {
    console.log('Address:', address)
  }, [address])

  return (
    <div>
      <p>Balance: {formattedBalance}</p>
      <button onClick={handleClick}>Show Address</button>
    </div>
  )
}

Error Boundaries

import React, { Component, ReactNode } from 'react'

interface Props {
  children: ReactNode
}

interface State {
  hasError: boolean
  error?: Error
}

class TransactionErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props)
    this.state = { hasError: false }
  }

  static getDerivedStateFromError(error: Error): State {
    return { hasError: true, error }
  }

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    console.error('Transaction error:', error, errorInfo)
  }

  render() {
    if (this.state.hasError) {
      return (
        <div style={{ padding: '20px', backgroundColor: '#f8d7da', color: '#721c24' }}>
          <h3>Transaction Error</h3>
          <p>{this.state.error?.message || 'An error occurred'}</p>
          <button onClick={() => this.setState({ hasError: false })}>Try Again</button>
        </div>
      )
    }

    return this.props.children
  }
}

// Usage
function App() {
  return (
    <TransactionErrorBoundary>
      <YourTransactionComponent />
    </TransactionErrorBoundary>
  )
}

Loading States

import { useTransaction, useKlever } from '@klever/connect-react'
import { useState } from 'react'

function LoadingStates() {
  const { isConnected, isConnecting } = useKlever()
  const { sendKLV, isLoading } = useTransaction()
  const [txStatus, setTxStatus] = useState<'idle' | 'signing' | 'broadcasting' | 'confirming'>(
    'idle',
  )

  const handleSend = async () => {
    try {
      setTxStatus('signing')
      const result = await sendKLV('klv1receiver...', 1000000n)

      setTxStatus('broadcasting')
      // Transaction is now broadcasting

      setTxStatus('confirming')
      await result.wait()

      setTxStatus('idle')
    } catch (err) {
      setTxStatus('idle')
    }
  }

  const getLoadingMessage = () => {
    switch (txStatus) {
      case 'signing':
        return 'Please sign the transaction in your wallet...'
      case 'broadcasting':
        return 'Broadcasting transaction to network...'
      case 'confirming':
        return 'Waiting for confirmation...'
      default:
        return null
    }
  }

  return (
    <div>
      <button onClick={handleSend} disabled={isLoading || isConnecting}>
        {isConnecting ? 'Connecting...' : isLoading ? getLoadingMessage() : 'Send Transaction'}
      </button>

      {isLoading && (
        <div style={{ marginTop: '10px' }}>
          <div
            style={{
              width: '100%',
              height: '4px',
              backgroundColor: '#e0e0e0',
              borderRadius: '2px',
              overflow: 'hidden',
            }}
          >
            <div
              style={{
                width: '100%',
                height: '100%',
                backgroundColor: '#007bff',
                animation: 'progress 1.5s ease-in-out infinite',
              }}
            />
          </div>
          <p style={{ fontSize: '12px', color: '#666', marginTop: '5px' }}>{getLoadingMessage()}</p>
        </div>
      )}
    </div>
  )
}

Optimizing with useMemo/useCallback

import { useBalance, useTransaction, useKlever } from '@klever/connect-react'
import { useMemo, useCallback } from 'react'

function OptimizedTransactions() {
  const { address } = useKlever()
  const { balance } = useBalance('KLV')
  const { sendKLV } = useTransaction()

  // Expensive calculation - memoize it
  const balanceStats = useMemo(() => {
    if (!balance) return null

    const amount = Number(balance.formatted)
    return {
      total: amount,
      canStake: amount >= 1000,
      canTransfer: amount >= 10,
      formatted: new Intl.NumberFormat('en-US').format(amount),
    }
  }, [balance])

  // Callback with dependencies - wrap in useCallback
  const handleMaxTransfer = useCallback(async () => {
    if (!balance) return
    const maxAmount = balance.amount - 1000000n // Keep 1 KLV for fees
    await sendKLV('klv1receiver...', maxAmount)
  }, [balance, sendKLV])

  // Stable callback - no dependencies
  const handleRefresh = useCallback(() => {
    window.location.reload()
  }, [])

  return (
    <div>
      {balanceStats && (
        <div>
          <p>Total: {balanceStats.formatted} KLV</p>
          <p>Can Stake: {balanceStats.canStake ? 'Yes' : 'No'}</p>
          <p>Can Transfer: {balanceStats.canTransfer ? 'Yes' : 'No'}</p>
        </div>
      )}
      <button onClick={handleMaxTransfer}>Transfer Max</button>
      <button onClick={handleRefresh}>Refresh</button>
    </div>
  )
}

Polling Strategies

import { useBalance, useKlever } from '@klever/connect-react'
import { useEffect, useState } from 'react'

// Custom hook with configurable polling
function useCustomBalance(token: string, pollInterval: number = 10000) {
  const { provider, address } = useKlever()
  const [balance, setBalance] = useState<string | null>(null)

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

    let cancelled = false

    const fetchBalance = async () => {
      try {
        const account = await provider.getAccount(address)
        const asset = account.assets?.find((a) => a.assetId === token)
        if (!cancelled) {
          setBalance(asset?.balance || '0')
        }
      } catch (err) {
        console.error('Balance fetch error:', err)
      }
    }

    fetchBalance()
    const interval = setInterval(fetchBalance, pollInterval)

    return () => {
      cancelled = true
      clearInterval(interval)
    }
  }, [provider, address, token, pollInterval])

  return balance
}

// Usage with different poll intervals
function BalanceWithCustomPolling() {
  // Fast polling for critical data
  const klvBalance = useCustomBalance('KLV', 5000) // 5 seconds

  // Slower polling for less critical data
  const kdaBalance = useCustomBalance('MY-KDA-TOKEN', 30000) // 30 seconds

  return (
    <div>
      <p>KLV: {klvBalance || 'Loading...'}</p>
      <p>KDA: {kdaBalance || 'Loading...'}</p>
    </div>
  )
}

// Stop polling when component is not visible
function SmartPolling() {
  const [isVisible, setIsVisible] = useState(true)
  const { balance, refetch } = useBalance('KLV')

  useEffect(() => {
    const handleVisibilityChange = () => {
      const visible = document.visibilityState === 'visible'
      setIsVisible(visible)

      // Refetch when user returns to tab
      if (visible) {
        refetch()
      }
    }

    document.addEventListener('visibilitychange', handleVisibilityChange)
    return () => document.removeEventListener('visibilitychange', handleVisibilityChange)
  }, [refetch])

  return (
    <div>
      <p>Balance: {balance?.formatted || 'Loading...'}</p>
      <p>Polling: {isVisible ? 'Active' : 'Paused'}</p>
    </div>
  )
}

TypeScript Support

All hooks are fully typed for excellent TypeScript support:

import type {
  KleverContextValue,
  Balance,
  UseTransactionReturn,
  TransactionCallbacks,
} from '@klever/connect-react'
import { useKlever, useBalance, useTransaction } from '@klever/connect-react'

// Type-safe context usage
function TypedWalletInfo() {
  const context: KleverContextValue = useKlever()
  const { address, isConnected } = context

  // address is typed as `string | undefined`
  return <div>{address && <p>Connected: {address}</p>}</div>
}

// Type-safe balance hook
function TypedBalance() {
  const {
    balance,
    isLoading,
    error,
  }: { balance: Balance | null; isLoading: boolean; error: Error | null } = useBalance('KLV')

  return (
    <div>
      {balance && (
        <div>
          <p>Token: {balance.token}</p>
          <p>Amount: {balance.amount.toString()}</p>
          <p>Precision: {balance.precision}</p>
          <p>Formatted: {balance.formatted}</p>
        </div>
      )}
    </div>
  )
}

// Type-safe transaction callbacks
function TypedTransaction() {
  const callbacks: TransactionCallbacks = {
    onSuccess: (receipt) => {
      // receipt is typed as TransactionReceipt
      console.log('Hash:', receipt.hash)
      console.log('Block:', receipt.blockNumber)
    },
    onError: (error) => {
      // error is typed as Error
      console.error('Message:', error.message)
    },
  }

  const { sendKLV, isLoading, error }: UseTransactionReturn = useTransaction(callbacks)

  return (
    <button onClick={() => sendKLV('klv1...', 1000000n)} disabled={isLoading}>
      Send
    </button>
  )
}

// Custom hook with types
function useTypedStaking() {
  const { freeze, unfreeze, isLoading, error } = useStaking()

  return {
    freeze,
    unfreeze,
    isLoading,
    error,
  }
}

Troubleshooting

Extension Not Detected

Problem: extensionInstalled is false even though extension is installed.

Solutions:

  1. Refresh the page after installing extension
  2. Check if extension is enabled in browser
  3. Try in incognito mode to rule out conflicts
  4. Check browser console for errors
function ExtensionDebug() {
  const { extensionInstalled, searchingExtension } = useKlever()

  useEffect(() => {
    // Debug extension detection
    console.log('Extension installed:', extensionInstalled)
    console.log('Searching:', searchingExtension)
    console.log('Window.kleverWeb:', window.kleverWeb)
  }, [extensionInstalled, searchingExtension])

  return <div>Check console for debug info</div>
}

Wallet Not Connecting

Problem: connect() fails or hangs.

Solutions:

  1. Ensure extension is unlocked
  2. Check network connectivity
  3. Verify correct network is selected
  4. Clear localStorage and try again
function ConnectionDebug() {
  const { connect, error, isConnecting } = useKlever()

  const handleConnect = async () => {
    try {
      // Clear stored state
      localStorage.removeItem('klever-connected')
      localStorage.removeItem('klever-network')

      await connect()
    } catch (err) {
      console.error('Connection error:', err)
    }
  }

  return (
    <div>
      <button onClick={handleConnect} disabled={isConnecting}>
        {isConnecting ? 'Connecting...' : 'Connect'}
      </button>
      {error && <p>Error: {error.message}</p>}
    </div>
  )
}

Transaction Fails Silently

Problem: Transaction doesn't complete but no error is shown.

Solutions:

  1. Always await result.wait() for confirmation
  2. Use transaction monitor for real-time status
  3. Check transaction on blockchain explorer
  4. Ensure wallet has sufficient balance
function DebugTransaction() {
  const { sendKLV } = useTransaction()
  const { provider } = useKlever()

  const handleSend = async () => {
    try {
      console.log('Starting transaction...')
      const result = await sendKLV('klv1receiver...', 1000000n)
      console.log('Transaction submitted:', result.hash)

      console.log('Waiting for confirmation...')
      const receipt = await result.wait()
      console.log('Confirmed:', receipt)

      // Verify on chain
      const tx = await provider.getTransaction(result.hash)
      console.log('On-chain status:', tx?.status)
    } catch (err) {
      console.error('Transaction error:', err)
      // Log full error details
      if (err instanceof Error) {
        console.error('Error name:', err.name)
        console.error('Error message:', err.message)
        console.error('Error stack:', err.stack)
      }
    }
  }

  return <button onClick={handleSend}>Send with Debug</button>
}

Balance Not Updating

Problem: Balance doesn't reflect recent transactions.

Solutions:

  1. Use refetch() to manually update
  2. Wait for polling interval (10s default)
  3. Check if transaction is confirmed
  4. Verify correct network
function BalanceDebug() {
  const { balance, isLoading, error, refetch } = useBalance('KLV')
  const { currentNetwork } = useKlever()

  useEffect(() => {
    // Auto-refetch after transaction
    const handleTransactionComplete = () => {
      setTimeout(() => refetch(), 2000)
    }

    window.addEventListener('transactionComplete', handleTransactionComplete)
    return () => window.removeEventListener('transactionComplete', handleTransactionComplete)
  }, [refetch])

  return (
    <div>
      <p>Network: {currentNetwork}</p>
      <p>Balance: {balance?.formatted || 'Loading...'}</p>
      <p>Loading: {isLoading ? 'Yes' : 'No'}</p>
      {error && <p>Error: {error.message}</p>}
      <button onClick={refetch}>Force Refresh</button>
    </div>
  )
}

Network Switching Issues

Problem: Network switch doesn't take effect.

Solutions:

  1. Disconnect and reconnect wallet
  2. Refresh page after network switch
  3. Check localStorage for saved network
  4. Ensure extension supports the network
function NetworkSwitchDebug() {
  const { switchNetwork, currentNetwork, disconnect, connect } = useKlever()

  const handleSwitchNetwork = async (network: 'mainnet' | 'testnet') => {
    try {
      console.log('Current network:', currentNetwork)
      await switchNetwork(network)
      console.log('Switched to:', network)

      // Reconnect if needed
      await disconnect()
      await connect()
    } catch (err) {
      console.error('Network switch error:', err)
    }
  }

  return (
    <div>
      <p>Current: {currentNetwork}</p>
      <button onClick={() => handleSwitchNetwork('mainnet')}>Mainnet</button>
      <button onClick={() => handleSwitchNetwork('testnet')}>Testnet</button>
    </div>
  )
}

Memory Leaks

Problem: App slows down over time.

Solutions:

  1. Cancel polling on unmount
  2. Clean up event listeners
  3. Use AbortController for async operations
  4. Avoid creating new objects in render
function MemoryLeakPrevention() {
  const { provider } = useKlever()
  const [data, setData] = useState(null)

  useEffect(() => {
    let cancelled = false
    const controller = new AbortController()

    const fetchData = async () => {
      try {
        const result = await provider.getAccount('klv1...')
        if (!cancelled) {
          setData(result)
        }
      } catch (err) {
        if (!cancelled) {
          console.error(err)
        }
      }
    }

    fetchData()

    return () => {
      // Cleanup
      cancelled = true
      controller.abort()
    }
  }, [provider])

  return <div>Data: {JSON.stringify(data)}</div>
}

License

MIT