# Quickstart (/wallets/quickstart)

> For the complete documentation index, see [llms.txt](/llms.txt)



Add ZeroDev Wallet to any React app that can render client-side React components. Authenticate users with ZeroDev hooks, then use standard Wagmi hooks for wallet actions.

Want a faster starting point? Try the optional [Wallet UI Kit](/wallets/auth/wallet-ui-kit/getting-started) for a prebuilt login flow.

For a complete working reference, see the [ZeroDev Wallet SDK demo app](https://smart-wallet-demo.zerodev.app/).

<div className="fd-steps">
  <div className="fd-step">
    ## Prerequisites [#prerequisites]

    You will need:

    * A ZeroDev account in the [ZeroDev Dashboard](https://dashboard.zerodev.app/).
    * A project with at least one enabled network.
    * Your project ID.
    * Your app origin added to the project's ACL allowlist.
  </div>

  <div className="fd-step">
    ## Install packages [#install-packages]

    <Tabs items="[&#x22;npm&#x22;,&#x22;yarn&#x22;,&#x22;pnpm&#x22;,&#x22;bun&#x22;]">
      <Tab value="npm">
        ```bash
        npm i @zerodev/wallet-react @zerodev/wallet-core wagmi viem @wagmi/core @tanstack/react-query
        ```
      </Tab>

      <Tab value="yarn">
        ```bash
        yarn add @zerodev/wallet-react @zerodev/wallet-core wagmi viem @wagmi/core @tanstack/react-query
        ```
      </Tab>

      <Tab value="pnpm">
        ```bash
        pnpm add @zerodev/wallet-react @zerodev/wallet-core wagmi viem @wagmi/core @tanstack/react-query
        ```
      </Tab>

      <Tab value="bun">
        ```bash
        bun add @zerodev/wallet-react @zerodev/wallet-core wagmi viem @wagmi/core @tanstack/react-query
        ```
      </Tab>
    </Tabs>
  </div>

  <div className="fd-step">
    ## Allowlist your app origin [#allowlist-your-app-origin]

    Before testing authentication, add your app's origin to the project's ACL allowlist in the ZeroDev dashboard.

    For example, if you run your app at `http://localhost:3000` during development, add `http://localhost:3000` to the allowlist.
  </div>

  <div className="fd-step">
    ## Add configuration values [#add-configuration-values]

    In the ZeroDev dashboard, open your project and copy its project ID. Make sure the network you want to use is enabled for the project.

    Add these values to your app's configuration. The exact environment variable prefix depends on your framework:

    ```bash
    ZERODEV_PROJECT_ID="your-project-id"
    ZERODEV_AA_HOST="https://rpc.zerodev.app"
    ARBITRUM_SEPOLIA_RPC_URL="https://sepolia-rollup.arbitrum.io/rpc"
    ```

    * `ZERODEV_PROJECT_ID` is your ZeroDev project ID.
    * `ZERODEV_AA_HOST` is the ZeroDev bundler and paymaster host. It is optional and defaults to `https://rpc.zerodev.app`; set it only when you target staging or self-hosted infrastructure. The SDK derives the per-chain URL from this host, so the same value works across every chain.
    * `ARBITRUM_SEPOLIA_RPC_URL` is the chain RPC used by Wagmi's transport.

    This example uses Arbitrum Sepolia. Replace the chain and RPC URLs if your project uses a different chain.
  </div>

  <div className="fd-step">
    ## Configure Wagmi [#configure-wagmi]

    Create `src/wagmi.ts`:

    ```tsx
    import { zeroDevWallet } from '@zerodev/wallet-react'
    import { createConfig, http } from 'wagmi'
    import { arbitrumSepolia } from 'wagmi/chains'

    const projectId = '<your-project-id>'
    const aaHost = 'https://rpc.zerodev.app'
    const chainRpcUrl = 'https://sepolia-rollup.arbitrum.io/rpc'

    export const config = createConfig({
      chains: [arbitrumSepolia],
      connectors: [
        zeroDevWallet({
          projectId,
          aaHost,
          chains: [arbitrumSepolia],
          mode: '7702',
        }),
      ],
      transports: {
        [arbitrumSepolia.id]: http(chainRpcUrl),
      },
    })
    ```

    In your app, replace the inline constants with your framework's public runtime config or client-safe environment variable access.

    `7702` is the SDK default, but setting it explicitly makes the recommended mode clear.

    The SDK builds the bundler and paymaster URL from your project ID, the active chain, and `aaHost`. Pass only the host origin with `aaHost`, for example `https://rpc.zerodev.app`, not a full `/api/v3/.../chain/...` URL.
  </div>

  <div className="fd-step">
    ## Add providers [#add-providers]

    Wrap your app with Wagmi and TanStack Query providers:

    ```tsx
    import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
    import type { ReactNode } from 'react'
    import { WagmiProvider } from 'wagmi'
    import { config } from './wagmi'

    const queryClient = new QueryClient()

    export function WalletProviders({ children }: { children: ReactNode }) {
      return (
        <WagmiProvider config={config}>
          <QueryClientProvider client={queryClient}>
            {children}
          </QueryClientProvider>
        </WagmiProvider>
      )
    }
    ```
  </div>

  <div className="fd-step">
    ## Authenticate users [#authenticate-users]

    Start with passkeys for a minimal passwordless flow:

    ```tsx
    import { useLoginPasskey, useRegisterPasskey } from '@zerodev/wallet-react'
    import { useAccount, useDisconnect } from 'wagmi'

    export function WalletLogin() {
      const { address, isConnected } = useAccount()
      const { disconnect } = useDisconnect()
      const registerPasskey = useRegisterPasskey()
      const loginPasskey = useLoginPasskey()

      if (isConnected) {
        return (
          <div>
            <p>Connected: {address}</p>
            <button type="button" onClick={() => disconnect()}>
              Disconnect
            </button>
          </div>
        )
      }

      return (
        <div>
          <button
            type="button"
            disabled={registerPasskey.isPending}
            onClick={() => registerPasskey.mutate()}
          >
            {registerPasskey.isPending ? 'Registering...' : 'Register passkey'}
          </button>

          <button
            type="button"
            disabled={loginPasskey.isPending}
            onClick={() => loginPasskey.mutate()}
          >
            {loginPasskey.isPending ? 'Logging in...' : 'Login with passkey'}
          </button>
        </div>
      )
    }
    ```

    The auth hooks authenticate the user and connect the ZeroDev Wagmi connector. After auth succeeds, use normal Wagmi hooks such as `useAccount`, `useSignMessage`, and `useSendTransaction`.
  </div>

  <div className="fd-step">
    ## Sign a message [#sign-a-message]

    Message signing is offchain and does not require gas.

    ```tsx
    import { useSignMessage } from 'wagmi'

    export function SignMessageButton() {
      const signMessage = useSignMessage()

      return (
        <div>
          <button
            type="button"
            disabled={signMessage.isPending}
            onClick={() =>
              signMessage.signMessage({
                message: 'Hello from ZeroDev Wallet',
              })
            }
          >
            {signMessage.isPending ? 'Waiting for signature...' : 'Sign message'}
          </button>

          {signMessage.data ? <p>Signature: {signMessage.data}</p> : null}
          {signMessage.error ? <p>{signMessage.error.message}</p> : null}
        </div>
      )
    }
    ```
  </div>

  <div className="fd-step">
    ## Send a gasless transaction [#send-a-gasless-transaction]

    To test gas sponsorship, configure a gas policy for your project and chain in the ZeroDev dashboard first. See [Gas Policies](/api-and-toolings/infrastructure/gas-policies) for setup details.

    This example sends a 0 ETH self-transfer. It exercises the full account abstraction transaction path without requiring the user to hold native gas.

    ```tsx
    import {
      useAccount,
      useSendTransaction,
      useWaitForTransactionReceipt,
    } from 'wagmi'

    export function SendTransactionButton() {
      const { address } = useAccount()
      const sendTransaction = useSendTransaction()
      const receipt = useWaitForTransactionReceipt({
        hash: sendTransaction.data,
      })

      const isPending =
        sendTransaction.isPending ||
        (Boolean(sendTransaction.data) && receipt.isLoading)

      return (
        <div>
          <button
            type="button"
            disabled={!address || isPending}
            onClick={() =>
              address &&
              sendTransaction.sendTransaction({
                to: address,
                value: 0n,
              })
            }
          >
            {isPending ? 'Sending transaction...' : 'Send gasless transaction'}
          </button>

          {sendTransaction.data ? <p>Hash: {sendTransaction.data}</p> : null}
          {receipt.isSuccess ? <p>Transaction confirmed</p> : null}
          {sendTransaction.error ? <p>{sendTransaction.error.message}</p> : null}
          {receipt.error ? <p>{receipt.error.message}</p> : null}
        </div>
      )
    }
    ```
  </div>
</div>

## Account modes [#account-modes]

ZeroDev Wallet supports two account modes:

| Mode   | Use when                                        | Notes                                                                                       |
| ------ | ----------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `7702` | You want the recommended default                | Exposes the user's wallet address while enabling smart wallet features such as sponsorship. |
| `4337` | You need a counterfactual smart account address | Exposes the Kernel smart account address. The first transaction can deploy the account.     |

For most apps, use `7702`.

## Other auth methods [#other-auth-methods]

Passkeys are only one option. The hook-based SDK also supports:

* [Email OTP](/wallets/auth/email-otp)
* [Google OAuth](/wallets/auth/google-oauth)
* [Magic Link](/wallets/auth/magic-link)

Each auth method connects the same ZeroDev Wagmi connector after successful authentication.

## Optional Wallet UI Kit [#optional-wallet-ui-kit]

The examples above use `@zerodev/wallet-react` so you can build your own UI. If you want a prebuilt login flow, use the optional [Wallet UI Kit](/wallets/auth/wallet-ui-kit/getting-started). Keep the hook-based path if you want full control over layout, styling, and bundle size.

## Troubleshooting [#troubleshooting]

* **Allowlist errors**: confirm that the dashboard allowlists the exact origin you are opening in the browser.
* **Sponsored transaction fails**: confirm that a gas policy exists for the project and chain.
* **Wrong chain**: make sure the chain in `createConfig` and the gas policy refer to the same chain, and that the chain is enabled for the project. `aaHost` is host-only, so the SDK derives the correct per-chain URL automatically.

## Next steps [#next-steps]

* [Passkey authentication](/wallets/auth/passkeys)
* [Email OTP](/wallets/auth/email-otp)
* [Google OAuth](/wallets/auth/google-oauth)
* [Send a transaction](/wallets/wallet-api/send-transaction)
* [Batch transactions](/wallets/wallet-api/batch-transactions)
* [Sign a message](/wallets/wallet-api/sign-message)
* [Session management](/wallets/session-management)
