> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cyphers.live/llms.txt
> Use this file to discover all available pages before exploring further.

# Query hooks

> React hooks for fetching markets, positions, and protocol state.

All query hooks are built on TanStack Query v5. They return `{ data, isLoading, isError, error }` and handle caching, deduplication, and background refetching automatically.

## useGlobalState

Fetches the protocol config: fee rates and accepted USDC mint.

```typescript TypeScript theme={null}
import { useGlobalState } from "@cypher-zk/sdk/react";

const { data: globalState } = useGlobalState();

// globalState.protocolFeeRate    - protocol fee in basis points
// globalState.lpFeeRate          - LP (creator) fee in basis points
// globalState.acceptedMint       - PublicKey of the accepted USDC mint
// globalState.protocolTreasury   - PublicKey of the treasury
```

<Note>
  Always read `globalState.acceptedMint` at runtime. Don't hardcode the USDC mint address - it differs between devnet and mainnet.
</Note>

Cache TTL: 30 seconds.

***

## useMarkets

Fetches markets. Filter by creator, state, or a known list of IDs.

```typescript TypeScript theme={null}
import { useMarkets } from "@cypher-zk/sdk/react";
import { PublicKey } from "@solana/web3.js";

// All markets — expensive at scale, prefer `{ ids }` for paginated views
const { data: markets } = useMarkets();

// Only active markets (state = 0)
const { data: active } = useMarkets({ state: 0 });

// Markets by a specific creator
const { data: mine } = useMarkets({ creator: new PublicKey("...") });

// Paginated: fetch just the page you're showing. Routes through
// `client.markets.byIds` — chunks at Solana's 100-key cap with retries.
const ids = Array.from({ length: 50 }, (_, i) => BigInt(start + i));
const { data: page } = useMarkets({ ids });
```

Each item in the array is `{ publicKey: PublicKey, account: MarketAccount }`.

<Note>
  The `{ ids }` filter mode was added in v0.8.8. It's the recommended path once a deployment has more than a few hundred markets — `useMarkets()` with no filter still calls `getProgramAccounts` and returns the full payload.
</Note>

<Tip>
  Memoize the filter object when it's derived in render (e.g. from query results) so TanStack Query sees a stable input:

  ```typescript TypeScript theme={null}
  const filter = useMemo(() => ({ ids: marketPdas }), [marketPdas]);
  const opts = useMemo(
    () => ({ enabled: marketPdas.length > 0 }),
    [marketPdas.length],
  );
  const { data: page } = useMarkets(filter, opts);
  ```
</Tip>

Cache TTL: 10 seconds. Pass `refetchInterval: 4000` for live odds updates.

***

## useMarketQuestions

Batch-fetches the question text for a list of markets (typically the output of `useMarkets`). Routes through the SDK's RPC batching layer, so it scales past Solana's 100-key `getMultipleAccountsInfo` cap.

```typescript TypeScript theme={null}
import { useMarkets, useMarketQuestions } from "@cypher-zk/sdk/react";

const { data: markets } = useMarkets({ ids });
const { data: questions } = useMarketQuestions(markets ?? []);

const text =
  questions?.get(market.publicKey.toBase58()) ??
  market.account.inlineQuestion;
```

Returns `Map<marketPdaBase58, questionString>`. Markets without a `MarketQuestion` PDA (legacy v1/v2 inline-question markets) are omitted from the map - fall back to `account.inlineQuestion` for those.

<Tip>
  Stabilize the input array so TanStack Query keys don't churn every render:

  ```typescript TypeScript theme={null}
  const marketsForQuestions = useMemo(() => rawMarkets ?? [], [rawMarkets]);
  const { data: questions } = useMarketQuestions(marketsForQuestions);
  ```
</Tip>

Cache TTL: 60 seconds. Questions are immutable after market creation. Query is automatically disabled when the markets list is empty. Available since v0.8.8.

***

## useMarket

Fetches a single market by ID.

```typescript TypeScript theme={null}
import { useMarket } from "@cypher-zk/sdk/react";

const { data: market } = useMarket(marketId); // bigint | number
```

Use the `enabled` option to gate on prerequisites:

```typescript TypeScript theme={null}
const { data: market } = useMarket(marketId, {
  enabled: !!marketId,
  refetchInterval: 5000,
});
```

Cache TTL: 10 seconds.

***

## useUserPositions

Fetches all positions (bets) for a wallet across all markets.

```typescript TypeScript theme={null}
import { useUserPositions } from "@cypher-zk/sdk/react";
import { PublicKey } from "@solana/web3.js";

const { data: positions } = useUserPositions(
  new PublicKey(walletAddress),
  { enabled: !!walletAddress }
);

// Each item: { publicKey: PublicKey, account: EncryptedPositionAccount }
// account.betIndex    - which bet this is (0, 1, 2, ...)
// account.netAmount   - stake after fees (plaintext bigint)
// account.entryOdds   - locked-in odds × ODDS_SCALE (1e9)
// account.claimed     - whether payout was already claimed
```

Cache TTL: 5 seconds.

***

## usePosition

Fetches a single position by market, user, and bet index.

```typescript TypeScript theme={null}
import { usePosition } from "@cypher-zk/sdk/react";

const { data: position } = usePosition(
  marketPublicKey,
  userPublicKey,
  betIndex ?? 0n // defaults to 0n
);
```

Cache TTL: 5 seconds.

***

## Cache TTL reference

| Hook                 | Default TTL | Notes                                         |
| -------------------- | ----------- | --------------------------------------------- |
| `useGlobalState`     | 30 s        | Fee rates change rarely                       |
| `useMarkets`         | 10 s        | Pass `refetchInterval` for live odds          |
| `useMarket`          | 10 s        | Same as above                                 |
| `useMarketQuestions` | 60 s        | Questions are immutable after market creation |
| `useUserPositions`   | 5 s         | Frequently invalidated by mutations           |
| `usePosition`        | 5 s         | Invalidated after claim                       |

***

## Manual cache invalidation

If you send raw instructions outside of mutation hooks, invalidate the cache yourself using the query key factories:

```typescript TypeScript theme={null}
import {
  marketKeys,
  marketQuestionKeys,
  positionKeys,
} from "@cypher-zk/sdk/react";
import { useQueryClient } from "@tanstack/react-query";

const qc = useQueryClient();

// Invalidate a specific market
qc.invalidateQueries({ queryKey: marketKeys.detail(marketId) });

// Invalidate a paginated market page
qc.invalidateQueries({ queryKey: marketKeys.byIds(ids) });

// Invalidate batch question fetch for a page of markets
qc.invalidateQueries({
  queryKey: marketQuestionKeys.byMarkets(markets.map((m) => m.publicKey)),
});

// Invalidate all positions for a user
qc.invalidateQueries({ queryKey: positionKeys.byUser(userPublicKey) });
```
