gill
Hooks

Data Fetching Hooks

Hooks for fetching data from the Solana blockchain

These hooks provide easy access to common Solana blockchain data with automatic caching, refetching, and error handling.

useAccount

Get the account info for an address using the Solana RPC method getAccountInfo.

Usage

"use client";
 
import { useAccount } from "@gillsdk/react";
 
export function AccountInfo() {
  const { account, isLoading, isError, error } = useAccount({
    address: "nicktrLHhYzLmoVbuZQzHUTicd2sfP571orwo9jfc8c",
  });
 
  if (isLoading) return <div>Loading...</div>;
  if (isError) return <div>Error: {error?.message}</div>;
 
  return (
    <div>
      <pre>{JSON.stringify(account, null, 2)}</pre>
    </div>
  );
}

With Decoder

Fetch and decode an account's data into a typed object using an appropriate decoder function:

"use client";
 
import { useAccount } from "@gillsdk/react";
import { getMintDecoder } from "gill/programs";
 
export function MintInfo() {
  const { account, isLoading } = useAccount({
    // USDC mint account on mainnet
    address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    decoder: getMintDecoder(),
  });
 
  if (isLoading) return <div>Loading...</div>;
 
  return (
    <div>
      <p>Supply: {account?.data?.supply.toString()}</p>
      <p>Decimals: {account?.data?.decimals}</p>
    </div>
  );
}

Parameters

interface UseAccountOptions {
  address: Address | string;
  decoder?: Decoder<T>;
  commitment?: Commitment;
  minContextSlot?: number;
  enabled?: boolean;
  // ... other TanStack Query options
}

useBalance

Get an account's balance in lamports using getBalance.

Usage

"use client";
 
import { lamportsToSol } from "gill";
import { useBalance } from "@gillsdk/react";
 
export function WalletBalance() {
  const { balance, isLoading, isError, error } = useBalance({
    address: "nicktrLHhYzLmoVbuZQzHUTicd2sfP571orwo9jfc8c",
  });
 
  if (isLoading) return <div>Loading...</div>;
  if (isError) return <div>Error: {error?.message}</div>;
 
  return (
    <div>
      <p>Balance: {lamportsToSol(balance)} SOL</p>
    </div>
  );
}

With Automatic Refetch

const { balance } = useBalance({
  address: walletAddress,
  refetchInterval: 5000, // Refetch every 5 seconds
  refetchOnWindowFocus: true,
});

Parameters

interface UseBalanceOptions {
  address: Address | string;
  commitment?: Commitment;
  minContextSlot?: number;
  enabled?: boolean;
  // ... other TanStack Query options
}

useLatestBlockhash

Get the latest blockhash using getLatestBlockhash.

Usage

"use client";
 
import { useLatestBlockhash } from "@gillsdk/react";
 
export function BlockhashDisplay() {
  const { latestBlockhash, isLoading } = useLatestBlockhash();
 
  if (isLoading) return <div>Loading...</div>;
 
  return (
    <div>
      <p>Blockhash: {latestBlockhash?.blockhash}</p>
      <p>Last Valid Height: {latestBlockhash?.lastValidBlockHeight}</p>
    </div>
  );
}

For Transactions

const { latestBlockhash } = useLatestBlockhash();
 
// Use in transaction
const transaction = createTransaction({
  blockhash: latestBlockhash.blockhash,
  // ... other transaction params
});

Parameters

interface UseLatestBlockhashOptions {
  commitment?: Commitment;
  minContextSlot?: number;
  enabled?: boolean;
  // ... other TanStack Query options
}

useSignatureStatuses

Get the statuses of signatures using getSignatureStatuses.

Usage

"use client";
 
import { useSignatureStatuses } from "@gillsdk/react";
 
export function TransactionStatus({ signature }) {
  const { statuses, isLoading } = useSignatureStatuses({
    signatures: [signature],
  });
 
  if (isLoading) return <div>Checking status...</div>;
 
  const status = statuses?.[0];
  return (
    <div>
      <p>Confirmed: {status?.confirmationStatus}</p>
      <p>Confirmations: {status?.confirmations}</p>
      {status?.err && <p>Error: {JSON.stringify(status.err)}</p>}
    </div>
  );
}

Multiple Signatures

const { statuses } = useSignatureStatuses({
  signatures: [sig1, sig2, sig3],
  searchTransactionHistory: true,
});
 
statuses?.forEach((status, index) => {
  console.log(`Signature ${index}:`, status);
});

Parameters

interface UseSignatureStatusesOptions {
  signatures: string[];
  searchTransactionHistory?: boolean;
  enabled?: boolean;
  // ... other TanStack Query options
}

useSignaturesForAddress

Get signatures for confirmed transactions using getSignaturesForAddress.

Usage

"use client";
 
import { useSignaturesForAddress } from "@gillsdk/react";
 
export function TransactionHistory({ address }) {
  const { signatures, isLoading } = useSignaturesForAddress({
    address,
    config: {
      limit: 10,
    },
  });
 
  if (isLoading) return <div>Loading history...</div>;
 
  return (
    <ul>
      {signatures?.map((sig) => (
        <li key={sig.signature}>
          <p>Signature: {sig.signature}</p>
          <p>Slot: {sig.slot}</p>
          <p>Time: {new Date(sig.blockTime * 1000).toLocaleString()}</p>
        </li>
      ))}
    </ul>
  );
}

With Pagination

const { signatures, refetch } = useSignaturesForAddress({
  address,
  config: {
    limit: 20,
    before: lastSignature, // For pagination
    until: firstSignature,
  },
});

Parameters

interface UseSignaturesForAddressOptions {
  address: Address | string;
  config?: {
    limit?: number;
    before?: string;
    until?: string;
    commitment?: Commitment;
  };
  enabled?: boolean;
  // ... other TanStack Query options
}

useProgramAccounts

Get all accounts owned by a program using the getProgramAccounts RPC method.

Important getProgramAccounts (GPA) Limitations

  • getProgramAccounts can return large amounts of data and is resource-intensive
  • Many RPC providers return errors or have rate limits due to the system load
  • Public RPC endpoints often have getProgramAccounts disabled entirely
  • Consider using filters to reduce the data returned and always use a paid RPC provider for production
  • For large programs, consider alternative approaches like indexers or specialized APIs

Usage

"use client";
 
import { useProgramAccounts } from "@gillsdk/react";
 
export function ProgramAccounts() {
  const { accounts, isLoading } = useProgramAccounts({
    program: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
    config: {
      encoding: "base64",
      filters: [
        { dataSize: 165n }, // Token account size
      ],
    },
  });
 
  if (isLoading) return <div>Loading accounts...</div>;
 
  return (
    <div>
      <p>Found {accounts?.length} accounts</p>
    </div>
  );
}

With Memcmp Filter

const { accounts } = useProgramAccounts({
  program: programId,
  config: {
    filters: [
      {
        memcmp: {
          offset: 0n,
          bytes: base58.encode(ownerPubkey),
          encoding: "base58",
        },
      },
    ],
  },
});

Parameters

interface UseProgramAccountsOptions {
  program: Address | string;
  config?: {
    encoding?: "base64" | "jsonParsed";
    filters?: Filter[];
    commitment?: Commitment;
    minContextSlot?: number;
    withContext?: boolean;
  };
  decoder?: Decoder<T>;
  enabled?: boolean;
  // ... other TanStack Query options
}

useTokenMint

Get a decoded Mint account for a token.

Usage

"use client";
 
import { useTokenMint } from "@gillsdk/react";
 
export function TokenInfo() {
  const { account, isLoading } = useTokenMint({
    // USDC mint on mainnet
    mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
  });
 
  if (isLoading) return <div>Loading...</div>;
 
  const mintData = account?.data;
  return (
    <div>
      <p>Supply: {mintData?.supply.toString()}</p>
      <p>Decimals: {mintData?.decimals}</p>
      <p>Mint Authority: {mintData?.mintAuthority?.toString()}</p>
    </div>
  );
}

Parameters

interface UseTokenMintOptions {
  mint: Address | string;
  commitment?: Commitment;
  enabled?: boolean;
  // ... other TanStack Query options
}

useTokenAccount

Get the decoded Token Account for a given mint and owner, automatically deriving the Associated Token Account (ATA) address:

Usage

"use client";
 
import { useTokenAccount } from "@gillsdk/react";
 
export function TokenBalance() {
  const { account, isLoading } = useTokenAccount({
    mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    owner: "nicktrLHhYzLmoVbuZQzHUTicd2sfP571orwo9jfc8c",
  });
 
  if (isLoading) return <div>Loading...</div>;
 
  const tokenData = account?.data;
  return (
    <div>
      <p>Balance: {tokenData?.amount.toString()}</p>
      <p>Delegated: {tokenData?.delegatedAmount.toString()}</p>
    </div>
  );
}

With ATA Address

If you need to fetch and decode a specific Token Account's address (like for ancillary token accounts), you can manually derive the address and provide it to the hook:

const { account } = useTokenAccount({
  ata: "CCMCWh4FudPEmY6Q1AVi5o8mQMXkHYkJUmZfzRGdcJ9P",
});

Parameters

interface UseTokenAccountOptions {
  // Option 1: Provide mint and owner
  mint?: Address | string;
  owner?: Address | string;
 
  // Option 2: Provide ATA address directly
  ata?: Address | string;
 
  commitment?: Commitment;
  enabled?: boolean;
  // ... other TanStack Query options
}

Performance Optimization

Conditional Fetching

const { balance } = useBalance({
  address: walletAddress,
  enabled: !!walletAddress, // Only fetch if address exists
});

Stale Time Configuration

const { account } = useAccount({
  address,
  staleTime: 30000, // Consider data fresh for 30 seconds
  cacheTime: 300000, // Keep in cache for 5 minutes
});

Query Invalidation

import { useQueryClient } from "@tanstack/react-query";
 
function RefreshButton() {
  const queryClient = useQueryClient();
 
  const refreshAll = () => {
    // Invalidate all `@gillsdk/react` queries
    queryClient.invalidateQueries({ queryKey: ["gill"] });
  };
 
  return <button onClick={refreshAll}>Refresh All</button>;
}