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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@notdotmarket/dbc-hooks

v0.7.9

Published

React hooks for Dynamic Bonding Curve SDK with React Query integration and Reown AppKit support

Readme

@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

# 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

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

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

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:

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 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

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:

// 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

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:

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:

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:

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:

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:

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:

// 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:

import { Keypair } from '@solana/web3.js'
const wallet = Keypair.fromSecretKey(...)

After:

// 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).

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:

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

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

📖 Redis Backend Implementation Guide

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