> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://www.openfort.io/api/mcp` to find what you need.
>
> **Have feedback?** Use `submit_feedback` on the same MCP server.

# `useWalletAuth`

:::warning
**External wallet (SIWE) connection is Ethereum-only.** Solana has no external wallet support via the wagmi bridge. For Solana, use only Openfort embedded wallets.
:::

`useWalletAuth` is the recommended, high-level hook for connecting external wallets (MetaMask, WalletConnect, Coinbase Wallet, etc.) with Sign-In with Ethereum. It connects the wagmi wallet **and** runs SIWE in a single call, and also exposes the list of available wallets plus loading/error state.

This page is also the setup hub for [@openfort/react/wagmi](https://www.openfort.io/docs/products/embedded-wallet/react) — the provider stack, transactions, gas sponsorship, and Ethereum-only UI options all live in the steps below.

## Choosing a SIWE hook

Openfort ships two hooks for connecting external wallets with SIWE. Both are Ethereum-only and live in `@openfort/react/wagmi`.

| Hook | What it does | Reach for it when |
|------|--------------|-------------------|
| `useWalletAuth` **(recommended)** | Connects the wagmi wallet **and** runs SIWE in one call. Also returns the list of available wallets and loading/error state. | You want a standard "connect wallet" list or button. |
| [`useConnectWithSiwe`](https://www.openfort.io/docs/products/embedded-wallet/react/hooks/useConnectWithSiwe) | Runs **only** the SIWE step on a wallet you already connected via wagmi's `useConnect`. | You manage the wagmi connection yourself in a fully custom flow. |

Internally `useWalletAuth` connects the wallet for you and then calls the same SIWE routine as `useConnectWithSiwe`. Start here; drop down to `useConnectWithSiwe` only when you need control over the connection step.

## Setup

::::steps
## Install dependencies

```sh
pnpm add @openfort/react @tanstack/react-query viem wagmi
```

## Provider setup

`OpenfortWagmiBridge` (from `@openfort/react/wagmi`) must sit inside `WagmiProvider` and wrap `OpenfortProvider`. Wagmi uses TanStack Query for caching, so wrap with `QueryClientProvider`. Nesting order:

`QueryClientProvider` → `WagmiProvider` → `OpenfortWagmiBridge` → `OpenfortProvider` → your app

```tsx [Providers.tsx]
import { AuthProvider, OpenfortProvider, RecoveryMethod } from "@openfort/react"
import { getDefaultConfig, OpenfortWagmiBridge } from "@openfort/react/wagmi"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import { WagmiProvider, createConfig } from "wagmi"
import { baseSepolia } from "viem/chains"

const config = createConfig(
  getDefaultConfig({
    appName: "Openfort Demo App",
    chains: [baseSepolia],
    walletConnectProjectId: "YOUR_WALLETCONNECT_PROJECT_ID",
  })
)
const queryClient = new QueryClient()

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <QueryClientProvider client={queryClient}>
      <WagmiProvider config={config}>
        <OpenfortWagmiBridge>
          <OpenfortProvider
            publishableKey="YOUR_OPENFORT_PUBLISHABLE_KEY"
            walletConfig={{
              shieldPublishableKey: "YOUR_SHIELD_PUBLISHABLE_KEY",
              ethereum: { chainId: 84532 },
              createEncryptedSessionEndpoint: "YOUR_BACKEND_ENDPOINT",
            }}
            uiConfig={{
              authProviders: [AuthProvider.EMAIL_OTP, AuthProvider.GUEST, AuthProvider.WALLET],
              walletRecovery: { defaultMethod: RecoveryMethod.AUTOMATIC },
            }}
          >
            {children}
          </OpenfortProvider>
        </OpenfortWagmiBridge>
      </WagmiProvider>
    </QueryClientProvider>
  )
}
```

<details>
  <summary style={{ margin: '20px 0 12px 0', fontSize: 16, fontWeight: 500 }}>WalletConnect support</summary>

  If using WalletConnect, add a project ID from the [WalletConnect dashboard](https://cloud.walletconnect.com):

  ```tsx
  const config = createConfig(
    getDefaultConfig({
      appName: "Openfort demo",
      chains: [baseSepolia],
      ssr: true,
      walletConnectProjectId: "YOUR_WALLET_CONNECT_PROJECT_ID",
    })
  )
  ```
</details>

## @openfort/react/wagmi API reference

**OpenfortWagmiBridge** — React component that bridges wagmi wallet state into Openfort. Must be inside `WagmiProvider` and wrap `OpenfortProvider`. No props except `children`.

:::info[How the bridge works]
`getDefaultConfig` and `getDefaultConnectors` include the Openfort embedded wallet as a native wagmi connector, and `OpenfortProvider` connects it once the wallet is ready. If you pass your own `connectors`, add `embeddedWalletConnector()` from `@openfort/react/wagmi`. Once the bridge is in the tree, the embedded wallet and any connected external wallet (MetaMask, WalletConnect, etc.) both work through the same wagmi hooks — `useSendTransaction`, `useWriteContract`, `useBalance`, `useSignMessage`, `useWalletClient`. One API, regardless of wallet type. No Openfort-specific wrappers needed.
:::

**`getDefaultConfig(opts)`** — Returns a wagmi `CreateConfigParameters` with `ssr: true`. Any other `CreateConfigParameters` field (e.g. `transports`, `ssr`) is passed through, and connectors are built with `getDefaultConnectors` when you don't supply your own.

| Option | Type | Description |
|--------|------|-------------|
| `appName` | `string` | App name shown in wallet prompts. |
| `chains` | `Chain[]` | Chains to support. Defaults to mainnet, polygon, optimism, arbitrum. |
| `walletConnectProjectId?` | `string` | Enables the WalletConnect connector. |
| `coinbaseWalletPreference?` | `CoinbaseWalletPreference` | Coinbase Wallet connector preference. |
| `appIcon?` / `appDescription?` / `appUrl?` | `string` | App metadata shown to wallets. |

:::note
With `ssr: true`, wagmi reconnects after hydration. Treat `useAccount().status === 'reconnecting'` as loading rather than signed out.
:::

**`getDefaultConnectors(opts)`** — Returns the connector list (`CreateConnectorFn[]`): the Openfort embedded wallet, Safe (in iframes), Injected (MetaMask, Phantom, Rabby), Coinbase Wallet, and WalletConnect (when a project ID is set).

| Option | Type | Description |
|--------|------|-------------|
| `app` | `{ name, icon?, description?, url? }` | App metadata shown to wallets. |
| `walletConnectProjectId?` | `string` | Adds the WalletConnect connector. |
| `coinbaseWalletPreference?` | `CoinbaseWalletPreference` | Coinbase Wallet connector preference. |

**`useWalletAuth(hookOptions?)`** — The hook itself. See its full reference in [SIWE connection flow](#usewalletauth-siwe-connection-flow) below.

<h3 id="usewalletauth-siwe-connection-flow">useWalletAuth — SIWE connection flow</h3>

`useWalletAuth` from `@openfort/react/wagmi` is the recommended way to connect external wallets with Sign-In with Ethereum. It handles connect + SIWE sign-in in one flow.

```tsx
import { useWalletAuth } from "@openfort/react/wagmi"

function WalletConnectList() {
  const {
    availableWallets,
    connectWallet,
    linkWallet,
    walletConnectingTo,
    isLoading,
    isError,
    error,
  } = useWalletAuth({
    onSuccess: () => console.log("Connected"),
    onError: (err) => console.error(err),
  })

  return (
    <div>
      {walletConnectingTo && <p>Connecting to {walletConnectingTo}…</p>}
      {availableWallets.map((w) => (
        <button
          key={w.id}
          onClick={() =>
            connectWallet(w.id, {
              onConnect: () => console.log("Connected"),
              onError: (msg, openfortError) => console.error(msg, openfortError),
            })
          }
          disabled={isLoading}
        >
          {w.name}
        </button>
      ))}
    </div>
  )
}
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `hookOptions` | `OpenfortHookOptions?` | Optional `onSuccess` and `onError` callbacks. Default `{}`. |

| Return | Type | Description |
|--------|------|-------------|
| `availableWallets` | `AvailableWallet[]` | List of connectable wallets (excludes Openfort embedded). See [AvailableWallet](#availablewallet-type) below. |
| `connectWallet(connectorId, callbacks?)` | `(string, WalletAuthCallbacks?) => Promise<void>` | Connect + SIWE sign-in (new session) |
| `linkWallet(connectorId, callbacks?)` | `(string, WalletAuthCallbacks?) => Promise<void>` | Connect + SIWE link to existing account |
| `walletConnectingTo` | `string \| null` | Connector id currently in progress, or `null` when idle. Use for “Connecting to MetaMask…”-style UI. |
| `isLoading` | `boolean` | `true` while a connect/link is in progress. |
| `isError` | `boolean` | `true` when the last connect/link failed. |
| `isSuccess` | `boolean` | `true` when the last connect/link succeeded. |
| `error` | `OpenfortError \| null \| undefined` | Error from the last failed connect/link; `undefined` when not in an error state. |

**`WalletAuthCallbacks`** — passed per `connectWallet` / `linkWallet` call, and run *in addition to* the hook-level `hookOptions`:

```ts
type WalletAuthCallbacks = {
  onConnect?: () => void
  onError?: (error: string, openfortError?: OpenfortError) => void
}
```

Callback order:

* **On success** — status becomes `'success'`, then `hookOptions.onSuccess` runs, then the per-call `onConnect`.
* **On error** — status becomes `'error'`, then `hookOptions.onError` runs, then the per-call `onError(message, openfortError)`. String errors are normalized to `OpenfortError` for both `hookOptions.onError` and `error`.

:::note
If the app is already connected and the bridge fails to disconnect before connecting the new wallet, state is reset and both the hook-level and per-call error callbacks fire.
:::

:::info[API compatibility]
Existing code that only uses `availableWallets`, `connectWallet`, and `linkWallet` continues to work. New code can optionally use `hookOptions`, `walletConnectingTo`, and `isLoading` / `isError` / `isSuccess` / `error` for loading and error UI.
:::

<h4 id="availablewallet-type">AvailableWallet type</h4>

```ts
type AvailableWallet = {
  id: string
  name: string
  icon?: string
  connector: OpenfortEthereumBridgeConnector  // Openfort bridge connector for the external wallet
}
```

`connectWallet` creates a new session; `linkWallet` links the external wallet to the current user's account.

## Using wagmi hooks for transactions

When using `OpenfortWagmiBridge`, wagmi's native hooks work with the Openfort embedded wallet. Example with `useWriteContract` for an ERC-20 transfer:

```tsx
import { useWriteContract } from "wagmi"
import { parseUnits } from "viem"

const usdcAbi = [
  { name: "transfer", type: "function", stateMutability: "nonpayable", inputs: [{ name: "to", type: "address" }, { name: "amount", type: "uint256" }], outputs: [{ name: "success", type: "bool" }] },
] as const

function TransferButton() {
  const { writeContract, data: hash, isPending } = useWriteContract()

  return (
    <button
      onClick={() =>
        writeContract({
          address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
          abi: usdcAbi,
          functionName: "transfer",
          args: ["0x742d35Cc6634C0532925a3b844Bc454e4438f44e", parseUnits("10", 6)],
        })
      }
      disabled={isPending}
    >
      Send USDC
    </button>
  )
}
```

`useSendTransaction`, `useAccount`, `useBalance`, `useSignMessage`, and `useWalletClient` also work through the bridge.

## Using wallet\_sendCalls directly

Use `useWalletClient` from wagmi for direct `wallet_sendCalls` RPC:

```tsx
import { useWalletClient } from "wagmi"
import { encodeFunctionData, parseUnits } from "viem"

const usdcAddress = "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
const usdcAbi = [
  { name: "transfer", type: "function", stateMutability: "nonpayable", inputs: [{ name: "to", type: "address" }, { name: "amount", type: "uint256" }], outputs: [{ name: "success", type: "bool" }] },
] as const

function useSendUsdc() {
  const { data: walletClient } = useWalletClient()

  return async (to: `0x${string}`, amount: string) => {
    if (!walletClient?.chain || !walletClient.account) throw new Error("Wallet client not ready")
    const data = encodeFunctionData({ abi: usdcAbi, functionName: "transfer", args: [to, parseUnits(amount, 6)] })
    return walletClient.request({
      method: "wallet_sendCalls",
      params: [{
        version: "1.0",
        chainId: `0x${walletClient.chain.id.toString(16)}`,
        from: walletClient.account.address,
        calls: [{ to: usdcAddress, value: "0x0", data }],
      }],
    })
  }
}
```

## Gas sponsorship

Wire `ethereumFeeSponsorshipId` into `walletConfig.ethereum`. Create policies at [Dashboard → Gas sponsorship](https://dashboard.openfort.io/policies):

```tsx
walletConfig={{
  shieldPublishableKey: "YOUR_SHIELD_PUBLISHABLE_KEY",
  ethereum: {
    chainId: 84532,
    ethereumFeeSponsorshipId: "pol_...",
  },
}}
```

## EOA wallets on a custom chain

For EOA wallets on a custom chain, define the chain with `defineChain` and pass it to `getDefaultConfig`. Set `accountType: AccountTypeEnum.EOA` in `walletConfig.ethereum` and `enforceSupportedChains: false` in `uiConfig`:

```tsx [Providers.tsx]
import { defineChain } from "viem"
import { WagmiProvider, createConfig, http } from "wagmi"
import { AccountTypeEnum, AuthProvider, OpenfortProvider, RecoveryMethod } from "@openfort/react"
import { getDefaultConfig, OpenfortWagmiBridge } from "@openfort/react/wagmi"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"

const customMainnet = defineChain({
  id: 12345,
  name: "Custom Mainnet",
  network: "custom",
  nativeCurrency: { name: "Cust", symbol: "CUST", decimals: 18 },
  rpcUrls: {
    default: { http: ["https://rpc.custom.xyz"] },
  },
  blockExplorers: {
    default: { name: "customscan", url: "https://customscan.io" },
  },
  testnet: true,
})

const wagmiConfig = createConfig(
  getDefaultConfig({
    appName: "Your App Name",
    chains: [customMainnet],
    transports: { [customMainnet.id]: http() },
    walletConnectProjectId: "YOUR_WALLETCONNECT_PROJECT_ID",
  })
)

const queryClient = new QueryClient()

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <QueryClientProvider client={queryClient}>
      <WagmiProvider config={wagmiConfig}>
        <OpenfortWagmiBridge>
          <OpenfortProvider
            publishableKey="YOUR_OPENFORT_PUBLISHABLE_KEY"
            walletConfig={{
              shieldPublishableKey: "YOUR_SHIELD_PUBLISHABLE_KEY",
              ethereum: { chainId: customMainnet.id, rpcUrls: { [customMainnet.id]: "https://rpc.custom.xyz" }, accountType: AccountTypeEnum.EOA },
            }}
            uiConfig={{
              enforceSupportedChains: false,
            }}
          >
            {children}
          </OpenfortProvider>
        </OpenfortWagmiBridge>
      </WagmiProvider>
    </QueryClientProvider>
  )
}
```

## Ethereum-only UI options that require wagmi

* `AuthProvider.WALLET` — external wallet auth via SIWE
* `enforceSupportedChains` — enforces wagmi's chain list
* `walletConnectCTA` / `walletConnectName` — WalletConnect UI options
* `linkWalletOnSignUp` — link external wallet on signup
::::

## Related

* [Quickstart](https://www.openfort.io/docs/products/embedded-wallet/react) — Provider setup with wagmi
* [useConnectWithSiwe](https://www.openfort.io/docs/products/embedded-wallet/react/hooks/useConnectWithSiwe) — Lower-level SIWE hook
* [Wallet actions](https://www.openfort.io/docs/products/embedded-wallet/react/wallet/actions) — Send transactions with wagmi hooks
