VanillaJS library for Ethereum blockchain interactions with reactive primitives and utilities for building Ethereum applications
—
First-class TanStack Query support with query options, mutation options, and caching strategies.
@wagmi/core provides comprehensive TanStack Query integration through the /query export. This integration offers optimized caching, background refetching, and reactive data management for all blockchain operations.
Generate TanStack Query options for read operations that automatically handle caching and refetching.
/**
* Query options for balance queries
* @param config - Wagmi configuration
* @param options - Balance query options
* @returns TanStack Query options
*/
function getBalanceQueryOptions<config extends Config>(
config: config,
options: GetBalanceOptions<config>
): QueryOptions<GetBalanceData, GetBalanceErrorType>;
interface GetBalanceOptions<config extends Config> {
/** Account address */
address: Address;
/** Token contract address (optional) */
token?: Address;
/** Chain ID */
chainId?: config['chains'][number]['id'];
/** Query configuration */
query?: {
enabled?: boolean;
staleTime?: number;
gcTime?: number;
};
}
type GetBalanceData = {
decimals: number;
formatted: string;
symbol: string;
value: bigint;
};
/**
* Query key for balance queries
* @param parameters - Balance parameters
* @returns Query key array
*/
function getBalanceQueryKey(parameters?: GetBalanceParameters): GetBalanceQueryKey;
type GetBalanceQueryKey = readonly ['balance', GetBalanceParameters];
type GetBalanceQueryFnData = GetBalanceReturnType;Usage Example:
import { useQuery } from '@tanstack/react-query'
import { getBalanceQueryOptions } from '@wagmi/core/query'
// In React component
function BalanceDisplay({ address }: { address: Address }) {
const { data: balance, isLoading, error } = useQuery(
getBalanceQueryOptions(config, {
address,
query: {
staleTime: 1000 * 60, // 1 minute
enabled: !!address,
},
})
)
if (isLoading) return <div>Loading balance...</div>
if (error) return <div>Error loading balance</div>
if (!balance) return null
return (
<div>
Balance: {balance.formatted} {balance.symbol}
</div>
)
}
// In vanilla JavaScript
import { QueryClient } from '@tanstack/query-core'
const queryClient = new QueryClient()
const balance = await queryClient.fetchQuery(
getBalanceQueryOptions(config, {
address: '0x742d35Cc6601C2F3Ac5e5c7A9d16e4e6Be4e6e9e',
})
)
console.log('Balance:', balance.formatted)Query options for contract read operations.
/**
* Query options for contract reads
* @param config - Wagmi configuration
* @param options - Contract read options
* @returns TanStack Query options
*/
function readContractQueryOptions<config extends Config>(
config: config,
options: ReadContractOptions<config>
): QueryOptions<ReadContractData, ReadContractErrorType>;
interface ReadContractOptions<config extends Config> {
/** Contract address */
address: Address;
/** Contract ABI */
abi: Abi;
/** Function name */
functionName: string;
/** Function arguments */
args?: readonly unknown[];
/** Chain ID */
chainId?: config['chains'][number]['id'];
/** Query configuration */
query?: {
enabled?: boolean;
staleTime?: number;
gcTime?: number;
};
}
type ReadContractData = any; // Depends on contract function
/**
* Query key for contract reads
* @param parameters - Contract read parameters
* @returns Query key array
*/
function readContractQueryKey(parameters?: ReadContractParameters): ReadContractQueryKey;
type ReadContractQueryKey = readonly ['readContract', ReadContractParameters];
/**
* Query options for multiple contract reads
* @param config - Wagmi configuration
* @param options - Multiple contract read options
* @returns TanStack Query options
*/
function readContractsQueryOptions<config extends Config>(
config: config,
options: ReadContractsOptions<config>
): QueryOptions<ReadContractsData, ReadContractsErrorType>;
interface ReadContractsOptions<config extends Config> {
/** Array of contract calls */
contracts: readonly {
address: Address;
abi: Abi;
functionName: string;
args?: readonly unknown[];
}[];
/** Chain ID */
chainId?: config['chains'][number]['id'];
/** Allow failures */
allowFailure?: boolean;
/** Query configuration */
query?: {
enabled?: boolean;
staleTime?: number;
gcTime?: number;
};
}
type ReadContractsData = ReadContractsReturnType;Usage Example:
import { useQuery } from '@tanstack/react-query'
import { readContractQueryOptions } from '@wagmi/core/query'
// Single contract read
function TokenName({ address }: { address: Address }) {
const { data: name } = useQuery(
readContractQueryOptions(config, {
address,
abi: erc20Abi,
functionName: 'name',
query: {
staleTime: 1000 * 60 * 60, // 1 hour (name rarely changes)
},
})
)
return <div>Token: {name}</div>
}
// Multiple contract reads
function TokenInfo({ address }: { address: Address }) {
const { data: tokenData } = useQuery(
readContractsQueryOptions(config, {
contracts: [
{ address, abi: erc20Abi, functionName: 'name' },
{ address, abi: erc20Abi, functionName: 'symbol' },
{ address, abi: erc20Abi, functionName: 'decimals' },
{ address, abi: erc20Abi, functionName: 'totalSupply' },
],
})
)
if (!tokenData) return null
return (
<div>
<div>Name: {tokenData[0].result}</div>
<div>Symbol: {tokenData[1].result}</div>
<div>Decimals: {tokenData[2].result}</div>
<div>Total Supply: {tokenData[3].result?.toString()}</div>
</div>
)
}Query options for blockchain data like blocks, transactions, and network information.
/**
* Query options for block data
* @param config - Wagmi configuration
* @param options - Block query options
* @returns TanStack Query options
*/
function getBlockQueryOptions<config extends Config>(
config: config,
options: GetBlockOptions<config>
): QueryOptions<GetBlockData, GetBlockErrorType>;
interface GetBlockOptions<config extends Config> {
/** Block number */
blockNumber?: bigint;
/** Block hash */
blockHash?: Hash;
/** Block tag */
blockTag?: 'latest' | 'earliest' | 'pending' | 'safe' | 'finalized';
/** Include transactions */
includeTransactions?: boolean;
/** Chain ID */
chainId?: config['chains'][number]['id'];
/** Query configuration */
query?: {
enabled?: boolean;
staleTime?: number;
gcTime?: number;
};
}
type GetBlockData = GetBlockReturnType;
/**
* Query options for block number
* @param config - Wagmi configuration
* @param options - Block number options
* @returns TanStack Query options
*/
function getBlockNumberQueryOptions<config extends Config>(
config: config,
options: GetBlockNumberOptions<config>
): QueryOptions<GetBlockNumberData, GetBlockNumberErrorType>;
interface GetBlockNumberOptions<config extends Config> {
/** Chain ID */
chainId?: config['chains'][number]['id'];
/** Query configuration */
query?: {
enabled?: boolean;
staleTime?: number;
gcTime?: number;
refetchInterval?: number; // Auto-refetch for latest block
};
}
type GetBlockNumberData = bigint;
/**
* Query options for transaction data
* @param config - Wagmi configuration
* @param options - Transaction query options
* @returns TanStack Query options
*/
function getTransactionQueryOptions<config extends Config>(
config: config,
options: GetTransactionOptions<config>
): QueryOptions<GetTransactionData, GetTransactionErrorType>;
interface GetTransactionOptions<config extends Config> {
/** Transaction hash */
hash: Hash;
/** Chain ID */
chainId?: config['chains'][number]['id'];
/** Query configuration */
query?: {
enabled?: boolean;
staleTime?: number;
gcTime?: number;
};
}
type GetTransactionData = GetTransactionReturnType;
/**
* Query options for transaction receipt
* @param config - Wagmi configuration
* @param options - Transaction receipt options
* @returns TanStack Query options
*/
function getTransactionReceiptQueryOptions<config extends Config>(
config: config,
options: GetTransactionReceiptOptions<config>
): QueryOptions<GetTransactionReceiptData, GetTransactionReceiptErrorType>;
interface GetTransactionReceiptOptions<config extends Config> {
/** Transaction hash */
hash: Hash;
/** Chain ID */
chainId?: config['chains'][number]['id'];
/** Query configuration */
query?: {
enabled?: boolean;
staleTime?: number;
gcTime?: number;
};
}
type GetTransactionReceiptData = GetTransactionReceiptReturnType;Additional blockchain operations and client query options.
/**
* Query options for arbitrary contract calls
* @param config - Wagmi configuration
* @param options - Call options
* @returns TanStack Query options
*/
function callQueryOptions<config extends Config>(
config: config,
options: CallOptions<config>
): QueryOptions<CallData, CallErrorType>;
/**
* Query options for Merkle proofs
* @param config - Wagmi configuration
* @param options - Proof options
* @returns TanStack Query options
*/
function getProofQueryOptions<config extends Config>(
config: config,
options: GetProofOptions<config>
): QueryOptions<GetProofData, GetProofErrorType>;
/**
* Query options for connector clients
* @param config - Wagmi configuration
* @param options - Connector client options
* @returns TanStack Query options
*/
function getConnectorClientQueryOptions<config extends Config>(
config: config,
options: GetConnectorClientOptions<config>
): QueryOptions<GetConnectorClientData, GetConnectorClientErrorType>;
/**
* Query options for wallet clients
* @param config - Wagmi configuration
* @param options - Wallet client options
* @returns TanStack Query options
*/
function getWalletClientQueryOptions<config extends Config>(
config: config,
options: GetWalletClientOptions<config>
): QueryOptions<GetWalletClientData, GetWalletClientErrorType>;Generate TanStack Query mutation options for write operations like transactions and contract interactions.
/**
* Mutation options for sending transactions
* @param config - Wagmi configuration
* @returns TanStack Query mutation options
*/
function sendTransactionMutationOptions<config extends Config>(
config: config
): MutationOptions<SendTransactionData, SendTransactionErrorType, SendTransactionVariables>;
interface SendTransactionVariables {
/** Target address */
to?: Address;
/** Transaction data */
data?: Hex;
/** Gas limit */
gas?: bigint;
/** Gas price */
gasPrice?: bigint;
/** Max fee per gas */
maxFeePerGas?: bigint;
/** Max priority fee per gas */
maxPriorityFeePerGas?: bigint;
/** Nonce */
nonce?: number;
/** Value to send */
value?: bigint;
/** Chain ID */
chainId?: number;
}
type SendTransactionData = Hash;
type SendTransactionMutate = (variables: SendTransactionVariables) => void;
type SendTransactionMutateAsync = (variables: SendTransactionVariables) => Promise<SendTransactionData>;
/**
* Mutation options for contract writes
* @param config - Wagmi configuration
* @returns TanStack Query mutation options
*/
function writeContractMutationOptions<config extends Config>(
config: config
): MutationOptions<WriteContractData, WriteContractErrorType, WriteContractVariables>;
interface WriteContractVariables {
/** Contract address */
address: Address;
/** Contract ABI */
abi: Abi;
/** Function name */
functionName: string;
/** Function arguments */
args?: readonly unknown[];
/** Chain ID */
chainId?: number;
/** Gas options */
gas?: bigint;
gasPrice?: bigint;
maxFeePerGas?: bigint;
maxPriorityFeePerGas?: bigint;
/** Transaction value */
value?: bigint;
}
type WriteContractData = Hash;
type WriteContractMutate = (variables: WriteContractVariables) => void;
type WriteContractMutateAsync = (variables: WriteContractVariables) => Promise<WriteContractData>;
/**
* Mutation options for wallet connection
* @param config - Wagmi configuration
* @returns TanStack Query mutation options
*/
function connectMutationOptions<config extends Config>(
config: config
): MutationOptions<ConnectData, ConnectErrorType, ConnectVariables>;
interface ConnectVariables {
/** Connector to connect with */
connector: Connector | CreateConnectorFn;
/** Chain ID to connect to */
chainId?: number;
}
type ConnectData = {
accounts: readonly [Address, ...Address[]];
chainId: number;
};
type ConnectMutate = (variables: ConnectVariables) => void;
type ConnectMutateAsync = (variables: ConnectVariables) => Promise<ConnectData>;Usage Example:
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { sendTransactionMutationOptions, getBalanceQueryKey } from '@wagmi/core/query'
function SendTransactionButton() {
const queryClient = useQueryClient()
const {
mutate: sendTransaction,
mutateAsync: sendTransactionAsync,
isPending,
error,
} = useMutation({
...sendTransactionMutationOptions(config),
onSuccess: (hash) => {
console.log('Transaction sent:', hash)
// Invalidate balance queries to refetch
queryClient.invalidateQueries({
queryKey: getBalanceQueryKey(),
})
},
})
const handleSend = () => {
sendTransaction({
to: '0x742d35Cc6601C2F3Ac5e5c7A9d16e4e6Be4e6e9e',
value: parseEther('0.1'),
})
}
const handleSendAsync = async () => {
try {
const hash = await sendTransactionAsync({
to: '0x742d35Cc6601C2F3Ac5e5c7A9d16e4e6Be4e6e9e',
value: parseEther('0.1'),
})
console.log('Transaction sent:', hash)
} catch (error) {
console.error('Transaction failed:', error)
}
}
return (
<div>
<button onClick={handleSend} disabled={isPending}>
{isPending ? 'Sending...' : 'Send Transaction'}
</button>
<button onClick={handleSendAsync} disabled={isPending}>
Send Async
</button>
{error && <div>Error: {error.message}</div>}
</div>
)
}For paginated data like contract events or transaction lists.
/**
* Infinite query options for contract reads
* @param config - Wagmi configuration
* @param options - Infinite query options
* @returns TanStack Query infinite options
*/
function infiniteReadContractsQueryOptions<config extends Config>(
config: config,
options: InfiniteReadContractsOptions<config>
): InfiniteQueryOptions<InfiniteReadContractsData, InfiniteReadContractsErrorType>;
interface InfiniteReadContractsOptions<config extends Config> {
/** Array of contract calls with pagination */
contracts: (pageParam: unknown) => readonly {
address: Address;
abi: Abi;
functionName: string;
args?: readonly unknown[];
}[];
/** Chain ID */
chainId?: config['chains'][number]['id'];
/** Query configuration */
query?: {
enabled?: boolean;
staleTime?: number;
gcTime?: number;
getNextPageParam?: (lastPage: any, allPages: any[]) => unknown;
getPreviousPageParam?: (firstPage: any, allPages: any[]) => unknown;
};
}
type InfiniteReadContractsData = {
pages: ReadContractsReturnType[];
pageParams: unknown[];
};
type InfiniteReadContractsQueryKey = readonly ['infiniteReadContracts', InfiniteReadContractsOptions];
type InfiniteReadContractsQueryFnData = ReadContractsReturnType;Utility functions for working with query keys and caching.
/**
* Hash function for query keys
* @param queryKey - Query key to hash
* @returns Hash string
*/
function hashFn(queryKey: QueryKey): string;
/**
* Structural sharing for query data
* @param oldData - Previous data
* @param newData - New data
* @returns Optimized data with structural sharing
*/
function structuralSharing<T>(oldData: T | undefined, newData: T): T;
type QueryKey = readonly unknown[];Usage Example:
import { hashFn, structuralSharing } from '@wagmi/core/query'
// Custom query with optimized caching
const customQueryOptions = {
queryKey: ['custom', 'data'],
queryFn: async () => {
// Fetch data
return await fetchCustomData()
},
select: (data: any) => structuralSharing(undefined, data),
queryKeyHashFn: hashFn,
}
// Use with QueryClient
const queryClient = new QueryClient({
defaultOptions: {
queries: {
queryKeyHashFn: hashFn,
structuralSharing,
},
},
})Here's a comprehensive example showing how to use TanStack Query with wagmi:
import { QueryClient, useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import {
getBalanceQueryOptions,
readContractQueryOptions,
writeContractMutationOptions,
getBalanceQueryKey,
} from '@wagmi/core/query'
// Set up QueryClient with wagmi optimizations
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60, // 1 minute
gcTime: 1000 * 60 * 60 * 24, // 24 hours
},
},
})
// React component example
function TokenManager({ tokenAddress, userAddress }: {
tokenAddress: Address
userAddress: Address
}) {
const queryClient = useQueryClient()
// Query user's token balance
const { data: balance, isLoading: balanceLoading } = useQuery(
getBalanceQueryOptions(config, {
address: userAddress,
token: tokenAddress,
query: {
enabled: !!userAddress && !!tokenAddress,
staleTime: 1000 * 30, // 30 seconds for balance
},
})
)
// Query token metadata
const { data: tokenName } = useQuery(
readContractQueryOptions(config, {
address: tokenAddress,
abi: erc20Abi,
functionName: 'name',
query: {
enabled: !!tokenAddress,
staleTime: 1000 * 60 * 60, // 1 hour for token name
},
})
)
// Mutation for token transfer
const {
mutate: transfer,
isPending: transferPending,
error: transferError,
} = useMutation({
...writeContractMutationOptions(config),
onSuccess: (hash) => {
console.log('Transfer successful:', hash)
// Invalidate and refetch balance
queryClient.invalidateQueries({
queryKey: getBalanceQueryKey({ address: userAddress, token: tokenAddress }),
})
// Optimistically update balance (optional)
queryClient.setQueryData(
getBalanceQueryKey({ address: userAddress, token: tokenAddress }),
(oldBalance: any) => {
if (!oldBalance) return oldBalance
return {
...oldBalance,
value: oldBalance.value - transferAmount, // Subtract transferred amount
formatted: formatUnits(oldBalance.value - transferAmount, oldBalance.decimals),
}
}
)
},
onError: (error) => {
console.error('Transfer failed:', error)
},
})
const handleTransfer = (to: Address, amount: bigint) => {
transfer({
address: tokenAddress,
abi: erc20Abi,
functionName: 'transfer',
args: [to, amount],
})
}
if (balanceLoading) return <div>Loading...</div>
return (
<div>
<h3>{tokenName}</h3>
<p>Balance: {balance?.formatted} {balance?.symbol}</p>
<button
onClick={() => handleTransfer('0xRecipient', parseUnits('10', balance?.decimals || 18))}
disabled={transferPending}
>
{transferPending ? 'Transferring...' : 'Transfer 10 tokens'}
</button>
{transferError && (
<div>Error: {transferError.message}</div>
)}
</div>
)
}
// App setup
function App() {
return (
<QueryClientProvider client={queryClient}>
<TokenManager
tokenAddress="0xA0b86a33E6411c0B7f8C4b5d3e1B9d3e8b4a4e6f"
userAddress="0x742d35Cc6601C2F3Ac5e5c7A9d16e4e6Be4e6e9e"
/>
</QueryClientProvider>
)
}This integration provides:
Install with Tessl CLI
npx tessl i tessl/npm-wagmi--core