Solana

PreviousNext

Solana network integration with network definitions and utilities.

Documentation

The Solana integration provides network definitions and utilities for working with Solana blockchains.

Overview

This module provides:

  • Network factory functions for Solana networks (mainnet, devnet, testnet, localnet)
  • SOL amount formatting utility (formatSol)
  • Type-safe network definitions
  • Solana client integration via SolanaWalletProvider plugin

Installation

pnpm dlx shadcn@latest add @wallet-kit/solana

Note on @tanstack/react-query: This package is required for the Solana adapter to work. However, SolanaWalletProvider handles all the configuration automatically. You only need to wrap your app with QueryClientProvider - no additional setup is needed unless you want custom query options.

Network Definitions

createSolanaMainnet

Creates a Solana mainnet network definition.

import { createSolanaMainnet } from "@/lib/chains/solana"
 
const mainnet = createSolanaMainnet()
// { id: "solana:mainnet", label: "Mainnet", url: "mainnet" }

createSolanaDevnet

Creates a Solana devnet network definition.

import { createSolanaDevnet } from "@/lib/chains/solana"
 
const devnet = createSolanaDevnet()
// { id: "solana:devnet", label: "Devnet", url: "devnet" }

createSolanaTestnet

Creates a Solana testnet network definition.

import { createSolanaTestnet } from "@/lib/chains/solana"
 
const testnet = createSolanaTestnet()
// { id: "solana:testnet", label: "Testnet", url: "testnet" }

createSolanaLocalnet

Creates a Solana localnet network definition.

import { createSolanaLocalnet } from "@/lib/chains/solana"
 
const localnet = createSolanaLocalnet()
// { id: "solana:localnet", label: "Localnet", url: "localnet" }

Usage

Basic Setup (Default)

The simplest setup - SolanaWalletProvider automatically uses the QueryClient from context if QueryClientProvider is present, or creates one internally.

import { QueryClientProvider } from "@tanstack/react-query"
import {
  createWalletConfig,
  WalletProvider,
} from "@/components/wallet/provider"
import {
  createSolanaMainnet,
  createSolanaDevnet,
  createSolanaTestnet,
} from "@/lib/chains/solana"
import { SolanaWalletProvider } from "@/lib/chains/solana/adapter"
 
const queryClient = new QueryClient()
 
export default function App({ children }) {
  const config = createWalletConfig({
    networks: [
      createSolanaMainnet(),
      createSolanaDevnet(),
      createSolanaTestnet(),
    ],
  })
 
  return (
    <QueryClientProvider client={queryClient}>
      <WalletProvider config={config}>
        <SolanaWalletProvider>
          {children}
        </SolanaWalletProvider>
      </WalletProvider>
    </QueryClientProvider>
  )
}

Advanced Setup (Explicit QueryClient)

For more control, you can pass a QueryClient instance explicitly to SolanaWalletProvider. This is useful when you need to share the same QueryClient across multiple providers or configure it with custom options.

import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import {
  createWalletConfig,
  WalletProvider,
} from "@/components/wallet/provider"
import {
  createSolanaMainnet,
  createSolanaDevnet,
  createSolanaTestnet,
} from "@/lib/chains/solana"
import { SolanaWalletProvider } from "@/lib/chains/solana/adapter"
 
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5, // 5 minutes
      refetchOnWindowFocus: false,
    },
  },
})
 
export default function App({ children }) {
  const config = createWalletConfig({
    networks: [
      createSolanaMainnet(),
      createSolanaDevnet(),
      createSolanaTestnet(),
    ],
  })
 
  return (
    <QueryClientProvider client={queryClient}>
      <WalletProvider config={config}>
        <SolanaWalletProvider queryClient={queryClient}>
          {children}
        </SolanaWalletProvider>
      </WalletProvider>
    </QueryClientProvider>
  )
}

Custom Network URLs

You can override the network URL when creating networks:

import { createSolanaMainnet } from "@/lib/chains/solana"
 
const customMainnet = createSolanaMainnet({
  url: "https://api.mainnet-beta.solana.com"
})

Utilities

formatSol

Formats a SOL balance (in lamports) to a human-readable string.

function formatSol({
  balance: bigint;
  locale?: Intl.LocalesArgument;
  options?: Intl.NumberFormatOptions;
}): string

Parameters

ParameterTypeDefaultDescription
balancebigint-Required. Balance in lamports.
localestring"en-US"Locale for number formatting.
optionsIntl.NumberFormatOptionsSee belowNumber formatting options.

Default options:

{
  style: "decimal",
  minimumFractionDigits: 2,
  maximumFractionDigits: 5,
}

Examples

import { formatSol } from "@/lib/chains/solana"
 
// Basic usage
const balance = formatSol({ balance: 1000000000n }) // "1.00"
 
// With custom locale
const balanceFR = formatSol({ 
  balance: 1000000000n,
  locale: "fr-FR" 
}) // "1,00"
 
// With custom options
const balanceCustom = formatSol({
  balance: 1000000000n,
  options: {
    minimumFractionDigits: 0,
    maximumFractionDigits: 2,
  }
}) // "1"

Solana Client Integration

Solana Client Demo

Connect a wallet to use Solana RPC client

The Solana adapter provides a complete integration with @gillsdk/react, offering React hooks for fetching Solana blockchain data with automatic caching, refetching, and error handling.

SolanaWalletProvider

Optional plugin provider that injects a Solana client into the wallet context and enables @gillsdk/react hooks. It automatically configures the React Query client with the necessary defaults.

Default Behavior: If QueryClientProvider is present in your component tree, SolanaWalletProvider will automatically use its QueryClient. You don't need to pass queryClient explicitly unless you need custom configuration.

Basic Usage (Default)

import { QueryClientProvider } from "@tanstack/react-query"
import { SolanaWalletProvider } from "@/lib/chains/solana/adapter"
import { WalletProvider } from "@/components/wallet/provider"
 
const queryClient = new QueryClient()
 
export default function App({ children }) {
  return (
    <QueryClientProvider client={queryClient}>
      <WalletProvider config={walletConfig}>
        <SolanaWalletProvider>
          {children}
        </SolanaWalletProvider>
      </WalletProvider>
    </QueryClientProvider>
  )
}

Advanced Usage (Explicit QueryClient)

import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import { SolanaWalletProvider } from "@/lib/chains/solana/adapter"
import { WalletProvider } from "@/components/wallet/provider"
 
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5,
    },
  },
})
 
export default function App({ children }) {
  return (
    <QueryClientProvider client={queryClient}>
      <WalletProvider config={walletConfig}>
        <SolanaWalletProvider queryClient={queryClient}>
          {children}
        </SolanaWalletProvider>
      </WalletProvider>
    </QueryClientProvider>
  )
}

Props

PropTypeDefaultDescription
childrenReactNode-Required. Child components.
urlSolanaClientUrlOrMonikerNetwork URLOptional custom RPC URL or moniker (e.g., "mainnet", "devnet").
queryClientQueryClientFrom QueryClientProvider contextOptional React Query client. If not provided, automatically uses the client from QueryClientProvider context via useQueryClient(). The provider handles all necessary configuration internally.

With Custom RPC URL

<SolanaWalletProvider url="https://api.mainnet-beta.solana.com">
  {children}
</SolanaWalletProvider>

Hooks

The Solana adapter provides hooks that wrap @gillsdk/react hooks with automatic network detection and enabled state management.

useSolanaBalance

Fetch an account's balance in lamports. Automatically uses the connected account's address if no address is provided.

import { useSolanaBalance } from "@/lib/chains/solana/adapter"
import { formatSol } from "@/lib/chains/solana"
 
function BalanceDisplay() {
  const { balance, isLoading, isError, error } = useSolanaBalance()
  
  if (isLoading) return <div>Loading...</div>
  if (isError) return <div>Error: {error?.message}</div>
  
  return <div>{formatSol({ balance: balance ?? 0n })} SOL</div>
}

useSolanaAccount

Fetch account information for an address.

import { useSolanaAccount } from "@/lib/chains/solana/adapter"
 
function AccountInfo({ address }: { address: string }) {
  const { account, isLoading } = useSolanaAccount(address)
  
  if (isLoading) return <div>Loading...</div>
  if (!account) return <div>Account not found</div>
  
  return <div>Owner: {account.owner}</div>
}

useSolanaTokenMint

Fetch and decode a token mint account.

import { useSolanaTokenMint } from "@/lib/chains/solana/adapter"
 
function TokenInfo({ mint }: { mint: string }) {
  const { account, isLoading } = useSolanaTokenMint(mint)
  
  if (isLoading) return <div>Loading...</div>
  
  return (
    <div>
      <p>Decimals: {account?.data.decimals}</p>
      <p>Supply: {account?.data.supply.toString()}</p>
    </div>
  )
}

useSolanaTokenAccount

Fetch and decode a token account for a given mint and owner.

import { useSolanaTokenAccount } from "@/lib/chains/solana/adapter"
 
function TokenBalance({ mint, owner }: { mint: string; owner: string }) {
  const { account, isLoading } = useSolanaTokenAccount(mint, owner)
  
  if (isLoading) return <div>Loading...</div>
  
  return <div>Amount: {account?.data.amount.toString()}</div>
}

useSolanaProgramAccounts

Fetch all accounts owned by a program.

import { useSolanaProgramAccounts } from "@/lib/chains/solana/adapter"
 
function ProgramAccounts({ program }: { program: string }) {
  const { accounts, isLoading } = useSolanaProgramAccounts(program, {
    commitment: "confirmed",
  })
  
  if (isLoading) return <div>Loading...</div>
  
  return (
    <div>
      {accounts?.map((acc, i) => (
        <div key={i}>Account: {acc.pubkey}</div>
      ))}
    </div>
  )
}

useSolanaSignaturesForAddress

Fetch transaction signatures for an address.

import { useSolanaSignaturesForAddress } from "@/lib/chains/solana/adapter"
 
function TransactionHistory({ address }: { address: string }) {
  const { signatures, isLoading } = useSolanaSignaturesForAddress(address, {
    limit: 10,
  })
  
  if (isLoading) return <div>Loading...</div>
  
  return (
    <div>
      {signatures?.map((sig) => (
        <div key={sig.signature}>{sig.signature}</div>
      ))}
    </div>
  )
}

useSolanaSignatureStatuses

Check the status of transaction signatures.

import { useSolanaSignatureStatuses } from "@/lib/chains/solana/adapter"
 
function SignatureStatus({ signatures }: { signatures: string[] }) {
  const { statuses, isLoading } = useSolanaSignatureStatuses(signatures)
  
  if (isLoading) return <div>Loading...</div>
  
  return (
    <div>
      {statuses?.map((status, i) => (
        <div key={i}>
          {status?.confirmationStatus ?? "Unknown"}
        </div>
      ))}
    </div>
  )
}

useSolanaLatestBlockhash

Fetch the latest blockhash. Useful for transaction building.

import { useSolanaLatestBlockhash } from "@/lib/chains/solana/adapter"
 
function BlockhashDisplay() {
  const { latestBlockhash, isLoading } = useSolanaLatestBlockhash()
  
  if (isLoading) return <div>Loading...</div>
  
  return <div>Blockhash: {latestBlockhash?.blockhash}</div>
}

useSolanaClient

Get the current Solana client instance.

import { useSolanaClient } from "@/lib/chains/solana/adapter"
 
function MyComponent() {
  const { rpc, rpcSubscriptions } = useSolanaClient()
  
  // Use rpc for direct RPC calls
  const balance = await rpc.getBalance(address).send()
}

useSolanaWallet

Get the Solana client from wallet context or create a fallback.

import { useSolanaWallet } from "@/lib/chains/solana/adapter"
 
function MyComponent() {
  const client = useSolanaWallet()
  // Use client for direct operations
}

useUpdateSolanaClient

Update the Solana client instance. Useful for switching RPC endpoints dynamically.

import { useUpdateSolanaClient, useSolanaNetwork } from "@/lib/chains/solana/adapter"
import { createSolanaClient } from "gill"
 
function NetworkSwitcher() {
  const { mutate: updateClient } = useUpdateSolanaClient()
  const network = useSolanaNetwork()
  
  const handleSwitchRPC = () => {
    const newClient = createSolanaClient({
      urlOrMoniker: "https://custom-rpc.com"
    })
    updateClient(newClient)
  }
  
  return <button onClick={handleSwitchRPC}>Switch RPC</button>
}

useSolanaNetwork

Get the current Solana network information.

import { useSolanaNetwork } from "@/lib/chains/solana/adapter"
 
function NetworkInfo() {
  const network = useSolanaNetwork()
  
  if (!network) return <div>Not on Solana network</div>
  
  return (
    <div>
      <p>Network: {network.networkId}</p>
      <p>Cluster: {network.cluster}</p>
    </div>
  )
}

Hook Behavior

All hooks work out of the box with zero configuration:

  • Automatic network detection: Hooks automatically detect if you're on a Solana network and disable queries when not
  • Smart defaults: Use the connected account's address automatically when no address is provided
  • Safe fallbacks: Return safe defaults (null values, no loading/error states) when conditions aren't met
  • React Query integration: Full integration with React Query for automatic caching, background refetching, and error handling
  • No configuration needed: Works with the default QueryClient from QueryClientProvider - no additional setup required

Integration with @gillsdk/react

The Solana adapter is built on top of @gillsdk/react, which provides:

  • Automatic caching with React Query
  • Background refetching
  • Error handling and retry logic
  • Optimistic updates support
  • Type-safe RPC calls

All hooks from the adapter are wrappers around @gillsdk/react hooks, ensuring full compatibility and feature parity.

Types

SolanaNetwork

Extended network type for Solana networks.

interface SolanaNetwork extends Network {
  id: SolanaNetworkId;
  url: ClusterUrl;
}

SolanaNetworkId

Type for Solana network IDs.

type SolanaNetworkId = `solana:${string}`

Network IDs

Solana network IDs follow the format: solana:{network}

  • solana:mainnet - Mainnet
  • solana:devnet - Devnet
  • solana:testnet - Testnet
  • solana:localnet - Localnet
  • Network URLs use ClusterUrl type from gill package
  • The formatSol function uses LAMPORTS_PER_SOL (1,000,000,000) for conversion
  • All network definitions are type-safe and can be extended
  • Networks can be customized by passing partial network objects to factory functions
  • The SolanaWalletProvider automatically configures React Query with required defaults
  • All hooks integrate seamlessly with @gillsdk/react for optimal developer experience
  • The adapter handles network detection and query enabling/disabling automatically

Last updated 11/21/2025