--- name: graz description: React hooks for Cosmos blockchain interactions — connect wallets, query balances, send tokens, sign messages. Use when building Cosmos dApps with graz hooks, setting up GrazProvider, connecting wallets, or performing transactions. --- # Using the graz Library React hooks for Cosmos blockchain interactions. ## Quick start ```tsx import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { GrazProvider, useConnect, useAccount, useSendTokens, useStargateSigningClient } from "graz"; import { cosmoshub } from "graz/chains"; const queryClient = new QueryClient(); export function App() { return ( ); } function Wallet() { const { connect } = useConnect(); const { data: accounts, isConnected } = useAccount({ chainId: ["cosmoshub-4"] as const }); const { data: signingClients } = useStargateSigningClient({ chainId: ["cosmoshub-4"] as const, enabled: isConnected }); const { sendTokens } = useSendTokens(); const addr = accounts?.["cosmoshub-4"]?.bech32Address; return ( ); } ``` ## Provider Setup ```tsx import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { GrazProvider } from "graz"; import { cosmoshub } from "graz/chains"; const queryClient = new QueryClient(); function App() { return ( ); } ``` `GrazProvider` must be inside `QueryClientProvider`. The `chains` prop is required — pass `ChainInfo[]` (from `graz/chains`, `defineChainInfo()`, or manually). ## Configure Options ```ts interface ConfigureGrazArgs { chains: ChainInfo[]; defaultWallet?: WalletType; // Default: Keplr chainsConfig?: Record; // Per-chain: { rpcHeaders, gas: { price, denom } } autoReconnect?: boolean; // Default: true walletConnect?: { // WalletConnect config options: { projectId: string }; modalOptions?: ModalConfig; }; walletDefaultOptions?: Keplr["defaultOptions"]; multiChainFetchConcurrency?: number; // Default: 3 onNotFound?: () => void; onReconnectFailed?: () => void; iframeOptions?: IframeOptions; // Cosmiframe config pingInteval?: number; // MS between pings, default: 1hr logger?: { enabled?: boolean; level?: LogLevel; categories?: (keyof typeof LogCategory)[] }; paraConfig?: ParaGrazConfig; // Para wallet config } ``` ## Chain Info ```ts import { defineChainInfo, defineChains } from "graz"; // Type-safe helpers (identity functions) const myChain = defineChainInfo({ chainId: "mychain-1", currencies: [{ coinDenom: "TOKEN", coinMinimalDenom: "utoken", coinDecimals: 6 }], rpc: "https://rpc.example.com", rest: "https://rest.example.com", bech32Config: { bech32PrefixAccAddr: "mychain", bech32PrefixAccPub: "mychainpub", bech32PrefixValAddr: "mychainvaloper", bech32PrefixValPub: "mychainvaloperpub", bech32PrefixConsAddr: "mychainvalcons", bech32PrefixConsPub: "mychainvalconspub", }, chainName: "My Chain", feeCurrencies: [{ coinDenom: "TOKEN", coinMinimalDenom: "utoken", coinDecimals: 6 }], stakeCurrency: { coinDenom: "TOKEN", coinMinimalDenom: "utoken", coinDecimals: 6 }, bip44: { coinType: 118 }, }); defineChains({ cosmoshub, osmosis }); // Wraps Record for type safety ``` ### CLI Generate ```bash pnpm graz cli --generate # Generate all chains pnpm graz cli --generate --mainnet cosmoshub,osmosis --testnet osmosistestnet pnpm graz cli --generate --endpoint # Custom registry endpoint pnpm graz cli --generate --best # Use lowest-latency endpoints pnpm graz cli --generate --authz # Authz-compatible chains only ``` Output goes to `packages/graz/chains/index.{js,mjs,ts}` (gitignored). ```ts import { cosmoshub, osmosis, mainnetChains, testnetChains } from "graz/chains"; ``` ## Wallet Connection ### Connect ```ts import { useConnect, useAccount, useDisconnect, useActiveWalletType, useCheckWallet } from "graz"; import { WalletType } from "graz"; // Mutation hook const { connect, connectAsync, isLoading, isSuccess, isSupported, error } = useConnect(); connect(); // Default wallet connect({ walletType: WalletType.KEPLR }); // Specific wallet connect({ chainId: ["cosmoshub-4", "osmosis-1"] }); // Specific chains connect({ walletType: WalletType.LEAP, chainId: ["cosmoshub-4"] }); connect({ autoReconnect: false }); // Actions (no hook) import { connect, disconnect, getAvailableWallets, checkWallet, clearSession } from "graz"; await connect({ walletType: WalletType.KEPLR }); ``` ### Reactive Account State ```ts // Single chain (generic Record) const { data: accounts, isConnected, isConnecting, isDisconnected, status, reconnect } = useAccount(); const address = Object.values(accounts || {})[0]?.bech32Address; // Multi-chain with typed chain IDs (use `as const`) const { data: accounts } = useAccount({ chainId: ["cosmoshub-4", "osmosis-1"] as const }); // accounts?.["cosmoshub-4"] is Key | undefined — fully typed // accounts?.["osmosis-1"] is Key | undefined const atomAddress = accounts?.["cosmoshub-4"]?.bech32Address; ``` ### Check Wallet Availability ```ts // Reactive hook const { data: isKeplrAvailable } = useCheckWallet(WalletType.KEPLR); const { data: isCosmostationAvailable } = useCheckWallet(WalletType.COSMOSTATION); // Imperative const available = getAvailableWallets(); // Record const supported = checkWallet(); // Default wallet const cosmostationSupported = checkWallet(WalletType.COSMOSTATION); // Active wallet info const { walletType, isKeplr, isCosmostation, isWalletConnect } = useActiveWalletType(); ``` ### Disconnect ```ts const { disconnect, disconnectAsync, isLoading } = useDisconnect(); disconnect(); // All chains disconnect({ chainId: ["cosmoshub-4"] }); // Specific chain only // Actions import { clearSession } from "graz"; clearSession(); // Reset session + clear storage ``` ### Wallet Events Use `useWalletEvents` to react to committed wallet state changes. The hook owns its subscription and automatically cleans it up when the component unmounts. ```tsx import { useWalletEvents } from "graz"; function WalletEventObserver() { useWalletEvents({ onAccountChange: ({ accounts, previousAccounts, changedChainIds, walletType }) => { console.log({ accounts, previousAccounts, changedChainIds, walletType }); }, onActiveChainsChange: ({ activeChainIds, previousActiveChainIds, walletType }) => { console.log({ activeChainIds, previousActiveChainIds, walletType }); }, onDisconnect: ({ chainIds, reason, walletType }) => { console.log({ chainIds, reason, walletType }); }, }); return null; } ``` `onActiveChainsChange` reports the complete set of connected `activeChainIds`. It does not represent an EVM-style switch to one active chain. Initial connection does not emit account or active-chain events. For non-React consumers, subscribe imperatively and retain the cleanup function: ```ts import { subscribeWalletEvents } from "graz"; const unsubscribe = subscribeWalletEvents({ onDisconnect: ({ reason }) => console.log(reason), }); unsubscribe(); ``` Balance changes are not wallet events. Use `useBalance` or `useBalances` and an explicit refetch strategy when balance monitoring is required. ## Offline Signers ```ts const { data: signers } = useOfflineSigners(); const { data: signers } = useOfflineSigners({ chainId: ["cosmoshub-4"] as const }); // signers?.["cosmoshub-4"] is OfflineSigners const { offlineSigner, offlineSignerAmino, offlineSignerAuto } = signers?.["cosmoshub-4"] || {}; // Imperative import { getOfflineSigners } from "graz"; const signers = await getOfflineSigners({ chainId: "cosmoshub-4", walletType: WalletType.KEPLR }); ``` ## Balance Queries ```ts // All balances const { data: balances } = useBalances({ chainId: "cosmoshub-4", bech32Address: "cosmos1...", enabled: Boolean(address), }); // balances is Coin[] | undefined // Single denom const { data: atomBalance } = useBalance({ chainId: "cosmoshub-4", bech32Address: "cosmos1...", denom: "uatom", enabled: Boolean(address), }); // atomBalance is Coin | undefined // Staked balance const { data: staked } = useBalanceStaked({ chainId: "cosmoshub-4", bech32Address: "cosmos1...", enabled: Boolean(address), }); ``` ## Clients ### Read Clients ```ts const { data: stargateClients } = useStargateClient({ chainId: ["cosmoshub-4"] as const, enabled: Boolean(connected), }); // stargateClients?.["cosmoshub-4"] is StargateClient | undefined const { data: cosmwasmClients } = useCosmWasmClient({ chainId: ["neutron-1"] as const, enabled: Boolean(connected), }); ``` ### Signing Clients (requires wallet connected) ```ts const { data: signingClients, isLoading } = useStargateSigningClient({ chainId: ["cosmoshub-4"] as const, enabled: isConnected, offlineSigner: "offlineSignerAuto", // "offlineSigner" | "offlineSignerAuto" | "offlineSignerOnlyAmino" }); const { data: cosmwasmSigningClients } = useCosmWasmSigningClient({ chainId: ["neutron-1"] as const, enabled: isConnected, }); // Use client await signingClients?.["cosmoshub-4"]?.sendTokens( senderAddress, recipientAddress, [{ denom: "uatom", amount: "1000" }], { amount: [{ denom: "uatom", amount: "500" }], gas: "200000" } ); ``` ## Transaction Hooks ```ts // Send tokens const { sendTokens, sendTokensAsync, isPending, isSuccess, isError, data, reset } = useSendTokens(); sendTokens({ signingClient: signingClients?.["cosmoshub-4"], senderAddress: account?.bech32Address, recipientAddress: "cosmos1...", amount: [{ denom: "uatom", amount: "1000" }], fee: { amount: [{ denom: "uatom", amount: "500" }], gas: "200000" }, memo: "optional memo", }); // IBC transfer const { sendIbcTokens } = useSendIbcTokens(); sendIbcTokens({ signingClient, senderAddress, recipientAddress: "osmo1...", transferAmount: { denom: "uatom", amount: "1000" }, sourcePort: "transfer", sourceChannel: "channel-0", timeoutTimestamp: (Date.now() + 600_000) * 1_000_000, // 10 min in nanos fee: { amount: [{ denom: "uatom", amount: "500" }], gas: "200000" }, }); // Execute smart contract const { executeContract } = useExecuteContract(); executeContract({ signingClient, senderAddress: account?.bech32Address, contractAddress: "cosmos1contract...", msg: { transfer: { recipient: "cosmos1...", amount: "100" } }, fee: { amount: [{ denom: "uatom", amount: "500" }], gas: "300000" }, funds: [{ denom: "uatom", amount: "100" }], }); // Instantiate contract const { instantiateContract } = useInstantiateContract(); instantiateContract({ codeId: 1, signingClient, senderAddress: account?.bech32Address, msg: { name: "My Contract", symbol: "MYC" }, label: "my-contract", fee: { amount: [{ denom: "uatom", amount: "5000" }], gas: "500000" }, }); // Query smart contract const { data: queryResult } = useQuerySmart({ address: "cosmos1contract...", queryMsg: { get_info: {} }, }); ``` ## Chain Management ```ts // Suggest chain to wallet (no auto-connect) const { suggest } = useSuggestChain(); await suggest({ chainInfo: cosmoshub, walletType: WalletType.KEPLR }); // Suggest + auto-connect const { suggestAndConnect } = useSuggestChainAndConnect(); await suggestAndConnect({ chainInfo: osmosistestnet }); // Add chain (add to GrazProvider config at runtime) const { addChain } = useAddChain(); await addChain({ chainInfo: myNewChain }); // Chain info hooks const { data: chainInfo } = useChainInfo({ chainId: "cosmoshub-4" }); // Single const { data: chainInfos } = useChainInfos({ chainId: ["cosmoshub-4", "osmosis-1"] }); // Filtered const { data: activeChains } = useActiveChains(); // Currently connected const { data: activeChainIds } = useActiveChainIds(); // Connected chain IDs // Recent chains const { data: recentChainIds, clear: clearRecentIds } = useRecentChainIds(); const { data: recentChains, clear: clearRecentChains } = useRecentChains(); // Actions import { addChain, suggestChain, suggestChainAndConnect, getChainInfo, getChainInfos } from "graz"; ``` ## Multi-Chain API Pattern **All hooks that accept `chainId` support three overloads:** ```ts // 1. No chainId — returns generic Record useAccount(); // Record // 2. Single element tuple — typed Record with one key useAccount({ chainId: ["cosmoshub-4"] as const }); // ChainIdToRecord<["cosmoshub-4"], Key | undefined> // 3. Multi-element tuple — typed Record with multiple keys useAccount({ chainId: ["cosmoshub-4", "osmosis-1"] as const }); // ChainIdToRecord<["cosmoshub-4", "osmosis-1"], Key | undefined> ``` This applies to: `useAccount`, `useOfflineSigners`, `useStargateClient`, `useCosmWasmClient`, `useStargateSigningClient`, `useCosmWasmSigningClient`. The `UseMultiChainQueryResult` return type gives you: ```ts TypeUseMultiChainQueryResult = UseQueryResult< TChainIds extends readonly string[] ? ChainIdToRecord : Record >; ``` ## Logger ```ts import { LOG_CATEGORIES, LOG_FUNCTIONS, LOG_HOOKS, LogLevel, LogCategory } from "graz"; // In GrazProvider ; ``` ## Key Types ```ts import { WalletType, Key, Wallet, OfflineSigners, ConnectResult, UseMultiChainQueryResult, ChainConfig } from "graz"; // WalletType — enum for all supported wallets WalletType.KEPLR, WalletType.COSMOSTATION, WalletType.VECTIS, WalletType.WALLETCONNECT, WalletType.OKX, WalletType.PARA, WalletType.INITIA, WalletType.CACTUSCOSMOS, WalletType.COMPASS, WalletType.STATION, WalletType.XDEFI, WalletType.COSMIFRAME, WalletType.METAMASK_SNAP_COSMOS, WalletType.WC_KEPLR_MOBILE, WalletType.WC_COSMOSTATION_MOBILE, WalletType.WC_CLOT_MOBILE // Key — wallet account key interface Key { name: string; algo: string; pubKey: Uint8Array; address: Uint8Array; bech32Address: string; isNanoLedger: boolean; } // OfflineSigners interface OfflineSigners { offlineSigner: OfflineSigner; offlineSignerAmino: OfflineSignerAmino; offlineSignerAuto: OfflineSigner; } // Query config interface QueryConfig { enabled?: boolean; } interface MutationEventArgs { onError?: (error: unknown, data: T) => unknown; onLoading?: (data: T) => unknown; onSuccess?: (data: S) => unknown; } ``` ## Quick Reference | Task | Hook/Action | |---|---| | Connect wallet | `useConnect()` → `connect()` | | Disconnect | `useDisconnect()` → `disconnect()` | | Account state | `useAccount({ chainId })` | | Wallet events | `useWalletEvents(handlers)`, `subscribeWalletEvents(handlers)` | | Check wallet availability | `useCheckWallet(type)`, `getAvailableWallets()` | | Active wallet info | `useActiveWalletType()` | | Offline signers | `useOfflineSigners({ chainId })` | | Read client | `useStargateClient({ chainId })`, `useCosmWasmClient({ chainId })` | | Signing client | `useStargateSigningClient({ chainId })`, `useCosmWasmSigningClient({ chainId })` | | Balances | `useBalances({ chainId, bech32Address })` | | Single balance | `useBalance({ chainId, bech32Address, denom })` | | Staked balance | `useBalanceStaked({ chainId, bech32Address })` | | Send tokens | `useSendTokens()` | | IBC transfer | `useSendIbcTokens()` | | Execute contract | `useExecuteContract()` | | Instantiate contract | `useInstantiateContract()` | | Query contract | `useQuerySmart({ address, queryMsg })` | | Query raw | `useQueryRaw({ address, key })` | | Suggest chain | `useSuggestChain()`, `useSuggestChainAndConnect()` | | Add chain | `useAddChain()` | | Chain info | `useChainInfo({ chainId })`, `useChainInfos({ chainId })` | | Active chains | `useActiveChains()`, `useActiveChainIds()` | | Validators | `useQueryClientValidators({ queryClient })` |