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 (
<QueryClientProvider client={queryClient}>
<GrazProvider grazOptions={{ chains: [cosmoshub] }}>
<Wallet />
</GrazProvider>
</QueryClientProvider>
);
}
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 (
<button onClick={async () => {
if (!addr) return connect();
await sendTokens({ signingClient: signingClients?.["cosmoshub-4"], senderAddress: addr, recipientAddress: "cosmos1...", amount: [{ denom: "uatom", amount: "1000" }], fee: { amount: [{ denom: "uatom", amount: "500" }], gas: "200000" } });
}}>{addr || "Connect"}</button>
);
}
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 (
<QueryClientProvider client={queryClient}>
<GrazProvider grazOptions={{ chains: [cosmoshub] }}>
<YourApp />
</GrazProvider>
</QueryClientProvider>
);
}
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<string, ChainConfig>; // 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<string, ChainInfo> 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 <url> # 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<WalletType, boolean>
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 });