npm package: @notdotmarket/dbc-hooks
Description: React hooks for Dynamic Bonding Curve SDK with React Query integration and Reown AppKit support
Author: notdotmarket
Latest version: 0.7.9

# @notdotmarket/dbc-hooks

React hooks for the Dynamic Bonding Curve SDK, built with React Query and **fully integrated with Reown AppKit** for seamless wallet management.

## Features

- ✅ **Reown AppKit Integration** - Built-in support for Reown AppKit at the provider level
- ✅ **Universal Wallet Support** - Works with any Solana wallet (Phantom, Solflare, Backpack, etc.)
- ✅ **Plug and Play** - No manual wallet adapter management needed
- ✅ **React Query Powered** - Automatic caching, refetching, and state management
- ✅ **Complete SDK Coverage** - 16 hooks covering all Dynamic Bonding Curve operations
- ✅ **TypeScript First** - Full type safety and IntelliSense support
- ✅ **Multiple Swap Modes** - ExactIn, ExactOut, and PartialFill support

## Installation

```bash
# Install the hooks package
npm install @notdotmarket/dbc-hooks @tanstack/react-query

# Install Reown AppKit (recommended)
npm install @reown/appkit @reown/appkit-adapter-solana

# Or use with standard wallet adapter
npm install @solana/wallet-adapter-react @solana/wallet-adapter-wallets
```

## Quick Start with Reown AppKit

### 1. Setup Reown AppKit + DBC Provider

```tsx
import { createAppKit } from '@reown/appkit/react'
import { SolanaAdapter } from '@reown/appkit-adapter-solana'
import { DbcAppKitProvider } from '@notdotmarket/dbc-hooks'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { Connection } from '@solana/web3.js'
import { solanaDevnet } from '@reown/appkit/networks'

// 1. Setup React Query
const queryClient = new QueryClient()

// 2. Setup Reown AppKit
const projectId = 'YOUR_PROJECT_ID' // Get from https://dashboard.reown.com

const solanaAdapter = new SolanaAdapter({
  networks: [solanaDevnet]
})

createAppKit({
  adapters: [solanaAdapter],
  networks: [solanaDevnet],
  projectId,
  metadata: {
    name: 'Your DApp',
    description: 'Your DApp Description',
    url: 'https://yourdapp.com',
    icons: ['https://yourdapp.com/icon.png']
  },
  features: {
    analytics: true
  }
})

// 3. Setup DBC with Solana connection
const connection = new Connection('https://api.devnet.solana.com')

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <DbcAppKitProvider connection={connection}>
        <YourApp />
      </DbcAppKitProvider>
    </QueryClientProvider>
  )
}
```

### 2. Use in Your Components

```tsx
import { useAppKitAccount, useAppKitProvider } from '@reown/appkit/react'
import { useSwap, usePoolInfo } from '@notdotmarket/dbc-hooks'
import { PublicKey } from '@solana/web3.js'
import BN from 'bn.js'

function TradingComponent() {
  // Get wallet state from Reown AppKit
  const { address, isConnected } = useAppKitAccount()
  const { walletProvider } = useAppKitProvider('solana')
  
  // Get pool info
  const { data: poolInfo } = usePoolInfo(poolAddress)
  
  // Setup swap mutation
  const swap = useSwap()
  
  const handleSwap = async () => {
    if (!isConnected || !walletProvider || !address) {
      alert('Please connect your wallet')
      return
    }

    try {
      const result = await swap.mutateAsync({
        wallet: walletProvider, // Pass Reown wallet provider directly
        pool: new PublicKey(poolAddress),
        owner: new PublicKey(address),
        amountIn: new BN(1000000),
        minimumAmountOut: new BN(900000),
        swapBaseForQuote: false,
        referralTokenAccount: null,
      })
      
      console.log('Swap successful!', result.signature)
    } catch (error) {
      console.error('Swap failed:', error)
    }
  }
  
  return (
    <div>
      <appkit-button />
      
      {isConnected && (
        <button onClick={handleSwap} disabled={swap.isPending}>
          {swap.isPending ? 'Swapping...' : 'Swap Tokens'}
        </button>
      )}
    </div>
  )
}
```

## Complete Trading Example

```tsx
import { useAppKitAccount, useAppKitProvider } from '@reown/appkit/react'
import {
  useSwap,
  useSwapQuote,
  usePoolInfo,
  useFeeBreakdown,
  useWithdraw
} from '@notdotmarket/dbc-hooks'
import { PublicKey } from '@solana/web3.js'
import BN from 'bn.js'

function CompleteTrading() {
  const { address, isConnected } = useAppKitAccount()
  const { walletProvider } = useAppKitProvider('solana')
  
  const poolAddress = new PublicKey('YOUR_POOL_ADDRESS')
  
  // Fetch pool data
  const { data: poolInfo, isLoading } = usePoolInfo(poolAddress)
  const { data: fees } = useFeeBreakdown(poolAddress)
  
  // Get swap quote
  const { data: quote } = useSwapQuote({
    poolAddress,
    amountIn: new BN(1000000),
    swapBaseForQuote: false,
    slippageBps: 100,
    hasReferral: false,
  })
  
  // Mutations
  const swap = useSwap()
  const withdraw = useWithdraw()
  
  const handleSwap = async () => {
    if (!walletProvider || !address) return
    
    await swap.mutateAsync({
      wallet: walletProvider,
      pool: poolAddress,
      owner: new PublicKey(address),
      amountIn: new BN(1000000),
      minimumAmountOut: quote?.minimumAmountOut || new BN(0),
      swapBaseForQuote: false,
      referralTokenAccount: null,
    })
  }
  
  const handleWithdrawFees = async () => {
    if (!walletProvider || !address) return
    
    await withdraw.mutateAsync({
      wallet: walletProvider,
      pool: poolAddress,
      owner: new PublicKey(address),
      type: 'creatorTradingFee',
    })
  }
  
  if (isLoading) return <div>Loading pool data...</div>
  
  return (
    <div>
      <h2>Pool Trading</h2>
      
      {/* Wallet Connection */}
      <appkit-button />
      
      {/* Pool Info */}
      {poolInfo && (
        <div>
          <p>Base Reserve: {poolInfo.pool.baseReserve.toString()}</p>
          <p>Quote Reserve: {poolInfo.pool.quoteReserve.toString()}</p>
        </div>
      )}
      
      {/* Swap Quote */}
      {quote && (
        <div>
          <p>You will receive: {quote.outputAmount.toString()}</p>
          <p>Trading fee: {quote.tradingFee.toString()}</p>
        </div>
      )}
      
      {/* Trading Actions */}
      {isConnected && (
        <>
          <button onClick={handleSwap} disabled={swap.isPending}>
            {swap.isPending ? 'Swapping...' : 'Swap'}
          </button>
          
          <button onClick={handleWithdrawFees} disabled={withdraw.isPending}>
            Withdraw Fees
          </button>
        </>
      )}
      
      {/* Fee Breakdown */}
      {fees && (
        <div>
          <h3>Unclaimed Fees</h3>
          <p>Creator Quote: {fees.creator.unclaimedQuoteFee.toString()}</p>
          <p>Partner Quote: {fees.partner.unclaimedQuoteFee.toString()}</p>
        </div>
      )}
    </div>
  )
}
```

## Alternative: Standard Wallet Adapter

If you prefer using standard Solana wallet adapters instead of Reown AppKit:

```tsx
import { WalletProvider, ConnectionProvider } from '@solana/wallet-adapter-react'
import { WalletModalProvider } from '@solana/wallet-adapter-react-ui'
import { PhantomWalletAdapter, SolflareWalletAdapter } from '@solana/wallet-adapter-wallets'
import { DbcProvider } from '@notdotmarket/dbc-hooks'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { Connection } from '@solana/web3.js'

const queryClient = new QueryClient()
const connection = new Connection('https://api.devnet.solana.com')

const wallets = [
  new PhantomWalletAdapter(),
  new SolflareWalletAdapter(),
]

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <ConnectionProvider endpoint="https://api.devnet.solana.com">
        <WalletProvider wallets={wallets} autoConnect>
          <WalletModalProvider>
            <DbcProvider connection={connection}>
              <YourApp />
            </DbcProvider>
          </WalletModalProvider>
        </WalletProvider>
      </ConnectionProvider>
    </QueryClientProvider>
  )
}

// Then use with useWallet hook
import { useWallet } from '@solana/wallet-adapter-react'

function TradingComponent() {
  const wallet = useWallet()
  const swap = useSwap()
  
  const handleSwap = async () => {
    if (!wallet.publicKey) return
    
    await swap.mutateAsync({
      wallet, // Pass wallet adapter directly
      pool: poolAddress,
      owner: wallet.publicKey,
      // ...
    })
  }
}
```

## Available Hooks

### Query Hooks (Read-only)

#### Pool Information
- `usePoolInfo(poolAddress)` - Fetch pool and config data
- `usePoolConfig(configAddress)` - Fetch pool configuration
- `usePools()` - Fetch all pools
- `usePoolsByConfig(configAddress)` - Pools by config
- `usePoolsByCreator(creatorAddress)` - Pools by creator
- `usePoolByBaseMint(mintAddress)` - Pool by base token

#### Pool State & Metrics
- `usePoolCurveProgress(poolAddress)` - Bonding curve progress (0-1)
- `usePoolMetadata(poolAddress)` - Pool metadata
- `usePoolExpiryDays(poolAddress)` - Days remaining until curve expires (null if no expiry)
- `usePoolStats(poolAddress)` - Pool statistics (tokens sold, amount raised, percent sold)
- `useTokensSold(poolAddress)` - Number of tokens sold
- `useAmountRaised(poolAddress)` - Amount of quote tokens (SOL) raised
- `usePoolMigrationQuoteThreshold(poolAddress)` - Migration threshold

#### Pool Configurations
- `usePoolConfigs()` - All pool configurations
- `usePoolConfigsByOwner(ownerAddress)` - Configs by owner

#### Fee Information
- `useFeeBreakdown(poolAddress)` - Detailed fee breakdown
- `usePoolFeeMetrics(poolAddress)` - Current fee metrics
- `usePoolsFeesByConfig(configAddress)` - Fees for config pools
- `usePoolsFeesByCreator(creatorAddress)` - Fees for creator pools
- `useSaleTax(params)` - Check if sale tax is enabled and calculate tax amount

#### Partner & Migration
- `usePartnerMetadata(partnerAddress)` - Partner metadata
- `useDammV1MigrationMetadata(poolAddress)` - DAMM V1 migration data
- `useDammV1LockEscrow(poolAddress)` - DAMM V1 lock escrow

#### Token Information
- `useTokenDetails(tokenMint, fetchMetadata?)` - Comprehensive token details including metadata, decimals, supply, and logo

#### Trade Event Monitoring (Real-time)
- `useTradeEventListener(params)` - Listen for real-time trade events and store in Redis for charts
- `useTradeHistory(params)` - Fetch historical trade events from Redis

**Note:** Trade event monitoring requires backend API implementation. See [TRADE_EVENT_LISTENER.md](./TRADE_EVENT_LISTENER.md) for setup guide.

#### Swap Quotes
- `useSwapQuote(params)` - Calculate swap quote (v1, exact input)
- `useSwapQuote2(params)` - Calculate swap quote (v2, multiple modes)

**Note:** Both swap quote hooks return a `price` field (number) which is the actual token price calculated from the sqrtPrice. This makes it easier to display human-readable prices without manual conversion.

### Mutation Hooks (Write operations)
- `useSwap()` - Execute swap transactions
- `useWithdraw()` - Withdraw fees and surplus

## Swap Modes

```typescript
enum SwapMode {
  ExactIn = 0,      // Specify exact input amount
  PartialFill = 1,  // Allow partial fills if liquidity insufficient
  ExactOut = 2,     // Specify exact output amount (calculates required input)
}
```

**Example usage:**

```tsx
// ExactIn: Specify how much to swap in
const { data } = useSwapQuote2({
  poolAddress,
  swapBaseForQuote: false,
  swapMode: SwapMode.ExactIn,
  amountIn: new BN(1000000),
  slippageBps: 100,
})

// Access the actual price
if (data) {
  console.log('Token Price:', data.price) // Human-readable price
  console.log('Output Amount:', data.outputAmount.toString())
  console.log('Trading Fee:', data.tradingFee.toString())
}

// ExactOut: Specify desired output
const { data } = useSwapQuote2({
  poolAddress,
  swapBaseForQuote: false,
  swapMode: SwapMode.ExactOut,
  amountOut: new BN(900000),
  slippageBps: 100,
})
```

## Withdrawal Types

```typescript
type WithdrawType = 
  | 'creatorSurplusQuote'   // Creator withdraws surplus quote tokens
  | 'partnerSurplusQuote'   // Partner withdraws surplus quote tokens  
  | 'partnerSurplusBase'    // Partner withdraws surplus base tokens (NoMigration pools)
  | 'creatorTradingFee'     // Creator withdraws trading fees
  | 'partnerTradingFee'     // Partner withdraws trading fees
```

**Example usage:**

```tsx
const { address } = useAppKitAccount()
const { walletProvider } = useAppKitProvider('solana')
const withdraw = useWithdraw()

await withdraw.mutateAsync({
  wallet: walletProvider,
  pool: poolAddress,
  owner: new PublicKey(address),
  type: 'creatorTradingFee',
})
```

## Pool Expiry Tracking

Track when a bonding curve expires with `usePoolExpiryDays`:

```tsx
import { usePoolExpiryDays } from '@notdotmarket/dbc-hooks'

function PoolExpiryDisplay({ poolAddress }) {
  const { data: daysLeft, isLoading } = usePoolExpiryDays(poolAddress)
  
  if (isLoading) return <div>Loading...</div>
  
  // null = no expiry set
  if (daysLeft === null) {
    return <div>⏰ No expiration</div>
  }
  
  // Expired
  if (daysLeft <= 0) {
    return <div className="text-red-500">🔒 Curve Expired</div>
  }
  
  // Active with expiry
  const isExpiringSoon = daysLeft < 7
  return (
    <div className={isExpiringSoon ? 'text-yellow-500' : 'text-green-500'}>
      ⏱️ Expires in {daysLeft.toFixed(1)} days
      {isExpiringSoon && ' ⚠️'}
    </div>
  )
}
```

## Token Details & Metadata

Fetch comprehensive token information including on-chain metadata, logo, symbol, decimals, and supply:

```tsx
import { useTokenDetails } from '@notdotmarket/dbc-hooks'
import { PublicKey } from '@solana/web3.js'

function TokenDisplay({ tokenMint }) {
  // fetchMetadata=true (default) will also fetch the JSON metadata from URI
  const { data: token, isLoading } = useTokenDetails(tokenMint, true)
  
  if (isLoading) return <div>Loading token info...</div>
  if (!token) return <div>Token not found</div>
  
  return (
    <div className="token-card">
      {/* Token Logo */}
      {token.metadata?.logo && (
        <img 
          src={token.metadata.logo} 
          alt={token.metadata.name}
          className="token-logo"
        />
      )}
      
      {/* Token Identity */}
      <div className="token-info">
        <h3>{token.metadata?.name || 'Unknown Token'}</h3>
        <p className="symbol">{token.metadata?.symbol || 'N/A'}</p>
        {token.metadata?.description && (
          <p className="description">{token.metadata.description}</p>
        )}
      </div>
      
      {/* Token Stats */}
      <div className="token-stats">
        <div>
          <label>Mint Address:</label>
          <p>{token.mint.toString()}</p>
        </div>
        
        <div>
          <label>Decimals:</label>
          <p>{token.decimals}</p>
        </div>
        
        <div>
          <label>Total Supply:</label>
          <p>{(Number(token.supply) / Math.pow(10, token.decimals)).toLocaleString()}</p>
        </div>
        
        <div>
          <label>Token Type:</label>
          <p>{token.isToken2022 ? 'Token-2022' : 'SPL Token'}</p>
        </div>
        
        <div>
          <label>Mint Authority:</label>
          <p>{token.mintAuthority ? token.mintAuthority.toString() : 'Revoked ✓'}</p>
        </div>
        
        <div>
          <label>Freeze Authority:</label>
          <p>{token.freezeAuthority ? token.freezeAuthority.toString() : 'None'}</p>
        </div>
        
        {/* Metadata URI */}
        {token.metadata?.uri && (
          <div>
            <label>Metadata:</label>
            <a href={token.metadata.uri} target="_blank" rel="noopener noreferrer">
              View Metadata
            </a>
          </div>
        )}
      </div>
    </div>
  )
}

// Example: Fetch metadata but skip JSON fetch for faster loading
function QuickTokenInfo({ tokenMint }) {
  const { data: token } = useTokenDetails(tokenMint, false)
  
  return (
    <div>
      <p>{token?.metadata?.symbol || 'Loading...'}</p>
      <p>{token?.decimals} decimals</p>
    </div>
  )
}

// Example: Display token in a pool interface
function PoolTokenInfo({ poolAddress }) {
  const { data: poolInfo } = usePoolInfo(poolAddress)
  const { data: baseToken } = useTokenDetails(poolInfo?.baseMint)
  const { data: quoteToken } = useTokenDetails(poolInfo?.quoteMint)
  
  return (
    <div className="pool-tokens">
      <div className="base-token">
        {baseToken?.metadata?.logo && (
          <img src={baseToken.metadata.logo} alt="Base token" />
        )}
        <span>{baseToken?.metadata?.symbol}</span>
      </div>
      
      <span className="pair-separator">/</span>
      
      <div className="quote-token">
        {quoteToken?.metadata?.logo && (
          <img src={quoteToken.metadata.logo} alt="Quote token" />
        )}
        <span>{quoteToken?.metadata?.symbol}</span>
      </div>
    </div>
  )
}
```

**Return Type:**

```typescript
interface TokenDetails {
  mint: PublicKey                    // Token mint address
  decimals: number                   // Number of decimals
  supply: string                     // Total supply in smallest units
  mintAuthority: PublicKey | null    // Null if revoked (fixed supply)
  freezeAuthority: PublicKey | null  // Null if no freeze authority
  metadata?: TokenMetadata           // On-chain metadata (if exists)
  isToken2022: boolean               // True for Token-2022, false for SPL
}

interface TokenMetadata {
  name: string          // Token name from on-chain metadata
  symbol: string        // Token symbol from on-chain metadata
  uri: string           // Metadata JSON URI
  logo?: string         // Logo/image URL from JSON metadata
  image?: string        // Image URL from JSON metadata
  description?: string  // Description from JSON metadata
}
```

## Pool Statistics (Tokens Sold & Amount Raised)

Track pool performance metrics:

```tsx
import { usePoolStats, useTokensSold, useAmountRaised } from '@notdotmarket/dbc-hooks'
import { LAMPORTS_PER_SOL } from '@solana/web3.js'

// Option 1: Get all stats at once (recommended)
function PoolStatsDisplay({ poolAddress }) {
  const { data: stats, isLoading } = usePoolStats(poolAddress)
  
  if (isLoading) return <div>Loading stats...</div>
  if (!stats) return null
  
  return (
    <div>
      <h3>Pool Performance</h3>
      
      {/* Tokens Sold */}
      <p>Tokens Sold: {(stats.tokensSold.toNumber() / 1e6).toFixed(2)}</p>
      <p>Remaining: {(stats.baseReserve.toNumber() / 1e6).toFixed(2)}</p>
      
      {/* Amount Raised */}
      <p>SOL Raised: {(stats.amountRaised.toNumber() / LAMPORTS_PER_SOL).toFixed(4)} SOL</p>
      
      {/* Progress */}
      <p>Progress: {stats.percentSold.toFixed(2)}%</p>
      
      {/* Progress Bar */}
      <div className="progress-bar">
        <div style={{ width: `${stats.percentSold}%` }} />
      </div>
    </div>
  )
}

// Option 2: Get individual metrics
function SimplePoolMetrics({ poolAddress }) {
  const { data: tokensSold } = useTokensSold(poolAddress)
  const { data: amountRaised } = useAmountRaised(poolAddress)
  
  return (
    <div>
      {tokensSold && <p>Sold: {tokensSold.toString()}</p>}
      {amountRaised && <p>Raised: {amountRaised.toString()} lamports</p>}
    </div>
  )
}

// Real-world example with formatting
function TokenSaleProgress({ poolAddress }) {
  const { data: stats } = usePoolStats(poolAddress, {
    refetchInterval: 10000, // Update every 10 seconds
  })
  
  if (!stats) return null
  
  const tokenDecimals = 6 // Adjust based on your token
  const tokensSoldFormatted = stats.tokensSold.toNumber() / Math.pow(10, tokenDecimals)
  const solRaised = stats.amountRaised.toNumber() / LAMPORTS_PER_SOL
  
  return (
    <div className="sale-stats">
      <div className="stat-card">
        <h4>Tokens Sold</h4>
        <p className="big-number">{tokensSoldFormatted.toLocaleString()}</p>
        <p className="subtitle">{stats.percentSold.toFixed(1)}% of supply</p>
      </div>
      
      <div className="stat-card">
        <h4>Amount Raised</h4>
        <p className="big-number">{solRaised.toFixed(2)} SOL</p>
        <p className="subtitle">${(solRaised * 200).toFixed(2)} USD</p>
      </div>
      
      <div className="stat-card">
        <h4>Remaining</h4>
        <p className="big-number">
          {(stats.baseReserve.toNumber() / Math.pow(10, tokenDecimals)).toLocaleString()}
        </p>
        <p className="subtitle">{(100 - stats.percentSold).toFixed(1)}% left</p>
      </div>
    </div>
  )
}
```
```

## TypeScript Support

Full TypeScript support with exported types:

```typescript
import type {
  WalletAdapter,
  ReownWalletProvider,
  AnyWalletProvider,
  SwapMutationParams,
  WithdrawMutationParams,
  SwapParams,
  SwapQuoteParams,
  SwapQuote2Params,
  WithdrawParams,
  PoolInfoResult,
  FeeBreakdownResult,
  DbcContextValue,
} from '@notdotmarket/dbc-hooks'
```

## Migration Guide

### From 0.2.0 to 0.3.0

**What's New:**
- ✅ Reown AppKit integration at provider level
- ✅ `DbcAppKitProvider` for simplified setup
- ✅ Automatic wallet adapter detection (Reown vs. standard)
- ✅ Support for Reown's `sendTransaction` method

**No Breaking Changes!** Version 0.3.0 is fully backward compatible with 0.2.0. You can continue using standard wallet adapters or upgrade to Reown AppKit.

**To use Reown AppKit:**

```tsx
// Old way (still works)
import { DbcProvider } from '@notdotmarket/dbc-hooks'
import { useWallet } from '@solana/wallet-adapter-react'

// New way (recommended)
import { DbcAppKitProvider } from '@notdotmarket/dbc-hooks'
import { useAppKitAccount, useAppKitProvider } from '@reown/appkit/react'
```

### From 0.1.x to 0.2.0+

**Breaking Change:** Mutation hooks now require a wallet adapter instead of Keypair.

**Before:**
```tsx
import { Keypair } from '@solana/web3.js'
const wallet = Keypair.fromSecretKey(...)
```

**After:**
```tsx
// With Reown AppKit
import { useAppKitProvider } from '@reown/appkit/react'
const { walletProvider } = useAppKitProvider('solana')

// Or with standard adapter
import { useWallet } from '@solana/wallet-adapter-react'
const wallet = useWallet()
```

## Sale Tax Hook

The `useSaleTax` hook checks if a pool has sale tax enabled and calculates the tax amount for selling transactions.

**When enabled, a 10% tax is applied when selling base tokens (swapBaseForQuote = true).**

```tsx
import { useSaleTax } from '@notdotmarket/dbc-hooks'
import { PublicKey } from '@solana/web3.js'
import BN from 'bn.js'

function SwapWithSaleTax() {
  const poolAddress = new PublicKey('YOUR_POOL_ADDRESS')
  const amountIn = new BN(1000000) // 1 TOKEN (6 decimals)
  
  // Check sale tax info
  const { data: saleTaxInfo, isLoading } = useSaleTax({
    poolAddress,
    amountIn,
    swapBaseForQuote: true, // true when selling, false when buying
  })
  
  if (isLoading) return <div>Loading...</div>
  
  return (
    <div>
      <h3>Sale Tax Information</h3>
      <p>Sale Tax Enabled: {saleTaxInfo?.isSaleTaxEnabled ? 'Yes' : 'No'}</p>
      
      {saleTaxInfo?.isSaleTaxEnabled && (
        <>
          <p>Tax Rate: {saleTaxInfo.saleTaxPercentage}%</p>
          {saleTaxInfo.saleTaxFee && (
            <p>Tax Amount: {saleTaxInfo.saleTaxFee.toString()} lamports</p>
          )}
          <p className="warning">
            ⚠️ Selling will incur a {saleTaxInfo.saleTaxPercentage}% tax
          </p>
        </>
      )}
    </div>
  )
}
```

**Integration with Swap Quote:**

```tsx
import { useSwapQuote2, useSaleTax } from '@notdotmarket/dbc-hooks'

function SwapQuoteWithSaleTax() {
  const poolAddress = new PublicKey('YOUR_POOL_ADDRESS')
  const amountIn = new BN(1000000)
  const isSelling = true // swapBaseForQuote
  
  // Get swap quote (includes saleTaxFee in the result)
  const { data: quote } = useSwapQuote2({
    poolAddress,
    swapMode: SwapMode.ExactIn,
    amountIn,
    swapBaseForQuote: isSelling,
    slippageBps: 100,
    hasReferral: false,
  })
  
  // Get sale tax info for display
  const { data: saleTaxInfo } = useSaleTax({
    poolAddress,
    amountIn,
    swapBaseForQuote: isSelling,
  })
  
  return (
    <div>
      <h3>Swap Details</h3>
      <p>Output Amount: {quote?.outputAmount.toString()}</p>
      <p>Trading Fee: {quote?.tradingFee.toString()}</p>
      <p>Protocol Fee: {quote?.protocolFee.toString()}</p>
      {quote?.saleTaxFee && !quote.saleTaxFee.isZero() && (
        <p className="warning">
          Sale Tax Fee: {quote.saleTaxFee.toString()}
          {saleTaxInfo?.isSaleTaxEnabled && 
            ` (${saleTaxInfo.saleTaxPercentage}% tax on sells)`
          }
        </p>
      )}
    </div>
  )
}
```

## Real-Time Trade Monitoring & Chart Integration

The `useTradeEventListener` hook enables real-time monitoring of trades on your bonding curve pools, automatically storing price data in Redis for chart integration.

### Quick Example

```tsx
import { useTradeEventListener } from '@notdotmarket/dbc-hooks'
import { PublicKey } from '@solana/web3.js'

function PriceChart({ poolAddress }) {
  const [trades, setTrades] = useState([])
  
  // Listen for real-time trades
  const { isListening } = useTradeEventListener({
    poolAddress: new PublicKey(poolAddress),
    redisConfig: {
      url: process.env.NEXT_PUBLIC_REDIS_URL!,
      keyPrefix: 'dbc:trades',
      ttl: 2592000, // 30 days
    },
    enabled: true,
    onEvent: (event) => {
      // Add to chart data
      setTrades(prev => [event, ...prev].slice(0, 100))
      
      // Show notification
      console.log('New trade:', {
        direction: event.tradeDirection === 1 ? 'Buy' : 'Sell',
        price: calculatePrice(event.nextSqrtPrice),
        timestamp: new Date(event.timestamp)
      })
    },
    onError: (error) => {
      console.error('Event listener error:', error)
    }
  })
  
  return (
    <div>
      <div>Status: {isListening ? '🟢 Live' : '🔴 Offline'}</div>
      {/* Render your chart with trades data */}
    </div>
  )
}

function calculatePrice(sqrtPrice: string): number {
  const sqrt = BigInt(sqrtPrice)
  const price = (sqrt * sqrt) / (BigInt(2) ** BigInt(64))
  return Number(price) / 1e9
}
```

### Features

- ✅ Real-time event listening for swap transactions
- ✅ Automatic price quote extraction after each trade
- ✅ Redis storage with configurable TTL
- ✅ Timestamp management for time-series data
- ✅ Historical data fetching with `useTradeHistory`
- ✅ Perfect for building live trading charts

### Setup Required

This hook requires backend API endpoints to interact with Redis. See the complete setup guide:

📖 **[Trade Event Listener Documentation](./TRADE_EVENT_LISTENER.md)**

📖 **[Redis Backend Implementation Guide](./REDIS_BACKEND_IMPLEMENTATION.md)**

## Why Reown AppKit?

- **Universal Wallet Support** - One integration for all Solana wallets
- **Social Logins** - Google, Twitter, Discord, etc.
- **Email Authentication** - Passwordless email login
- **Better UX** - Beautiful, customizable modal
- **Multi-Chain Ready** - Easy to add EVM, Bitcoin support later
- **Built-in Wallet Detection** - Automatic wallet discovery
- **Mobile Optimized** - WalletConnect integration

## License

MIT

## Links

- [NPM Package](https://www.npmjs.com/package/@notdotmarket/dbc-hooks)
- [GitHub Repository](https://github.com/notdotmarket/dynamic-bonding-curve-sdk)
- [Reown AppKit Docs](https://docs.reown.com/appkit/react/core/installation)
- [Dynamic Bonding Curve SDK](https://www.npmjs.com/package/@notdotmarket/dynamic-bonding-curve-sdk)