@zerodev/wallet-react
v0.0.1-alpha.26
Published
React hooks for ZeroDev Wallet SDK
Readme
@zerodev/wallet-react
React hooks and Wagmi connector for ZeroDev Wallet SDK with EIP-7702 gasless transactions.
Features
- Wagmi Integration - Works seamlessly with the Wagmi ecosystem
- Multiple Auth Methods - Passkey (WebAuthn), Email OTP, OAuth (Google)
- Gasless Transactions - All transactions are gasless via EIP-7702
- Session Management - Auto-refresh with configurable thresholds
- Multi-Chain - Lazy kernel account creation per chain
- TypeScript - Full type safety with proper generics
- React Query - Built on TanStack Query for optimal UX
Installation
npm install @zerodev/wallet-react @zerodev/wallet-core wagmi viem
# or
yarn add @zerodev/wallet-react @zerodev/wallet-core wagmi viem
# or
pnpm add @zerodev/wallet-react @zerodev/wallet-core wagmi viemQuick Start
1. Configure Wagmi
import { createConfig, http } from 'wagmi'
import { sepolia } from 'wagmi/chains'
import { zeroDevWallet } from '@zerodev/wallet-react'
const config = createConfig({
chains: [sepolia],
connectors: [
zeroDevWallet({
projectId: 'YOUR_PROJECT_ID',
chains: [sepolia],
})
],
transports: {
[sepolia.id]: http('YOUR_RPC_URL'),
},
})2. Wrap Your App
import { WagmiProvider } from 'wagmi'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
const queryClient = new QueryClient()
function App() {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<YourApp />
</QueryClientProvider>
</WagmiProvider>
)
}3. Authenticate Users
import { useRegisterPasskey, useAuthenticateOAuth, OAUTH_PROVIDERS } from '@zerodev/wallet-react'
function LoginPage() {
const registerPasskey = useRegisterPasskey()
const authenticateOAuth = useAuthenticateOAuth()
return (
<>
<button
onClick={() => registerPasskey.mutate()}
disabled={registerPasskey.isPending}
>
{registerPasskey.isPending ? 'Registering...' : 'Register with Passkey'}
</button>
<button
onClick={() => authenticateOAuth.mutate({ provider: OAUTH_PROVIDERS.GOOGLE })}
disabled={authenticateOAuth.isPending}
>
{authenticateOAuth.isPending ? 'Authenticating...' : 'Login with Google'}
</button>
</>
)
}4. Use Standard Wagmi Hooks
import { useConnection, useSendTransaction, useDisconnect } from 'wagmi'
import { parseEther } from 'viem'
function Dashboard() {
const { address } = useConnection()
const { sendTransaction } = useSendTransaction()
const { disconnect } = useDisconnect()
return (
<>
<p>Address: {address}</p>
<button onClick={() =>
sendTransaction({
to: '0x...',
value: parseEther('0.01')
})
}>
Send Gasless Transaction
</button>
<button onClick={() => disconnect()}>
Logout
</button>
</>
)
}Authentication Methods
Passkey (WebAuthn)
const registerPasskey = useRegisterPasskey()
const loginPasskey = useLoginPasskey()
// Register new passkey
await registerPasskey.mutateAsync()
// Login with existing passkey
await loginPasskey.mutateAsync()OAuth (Google)
const authenticateOAuth = useAuthenticateOAuth()
// Opens popup, backend handles PKCE and token exchange
// No callback page or OAuth library needed - SDK handles everything
await authenticateOAuth.mutateAsync({
provider: OAUTH_PROVIDERS.GOOGLE
})Email Magic Link
const sendMagicLink = useSendMagicLink()
const verifyMagicLink = useVerifyMagicLink()
// Send magic link
const { otpId } = await sendMagicLink.mutateAsync({
email: '[email protected]',
redirectURL: 'https://yourapp.com/verify',
})
// With custom OTP code settings
const { otpId } = await sendMagicLink.mutateAsync({
email: '[email protected]',
redirectURL: 'https://yourapp.com/verify',
otpCodeCustomization: { length: 8, alphanumeric: false },
})
// Verify (on /verify page, extract code from URL)
const code = new URLSearchParams(window.location.search).get('code')
await verifyMagicLink.mutateAsync({ otpId, code })Email OTP
const sendOTP = useSendOTP()
const verifyOTP = useVerifyOTP()
// Send OTP code
const { otpId } = await sendOTP.mutateAsync({
email: '[email protected]'
})
// With custom OTP code settings
const { otpId } = await sendOTP.mutateAsync({
email: '[email protected]',
otpCodeCustomization: { length: 8, alphanumeric: false },
})
// Verify OTP code
await verifyOTP.mutateAsync({
code: '12345678',
otpId,
})OTP Code Customization
Both useSendOTP and useSendMagicLink accept an optional otpCodeCustomization parameter:
| Field | Type | Description |
|---|---|---|
| length | 6 \| 7 \| 8 \| 9 | Code length (default: 6) |
| alphanumeric | boolean | Use alphanumeric characters instead of digits only (default: false) |
Configuration Options
type ZeroDevWalletConnectorParams = {
projectId: string // Required: Your ZeroDev project ID
organizationId?: string // Optional: Turnkey organization ID
proxyBaseUrl?: string // Optional: KMS proxy URL
chains: readonly Chain[] // Required: Supported chains
rpId?: string // Optional: WebAuthn RP ID
sessionStorage?: StorageAdapter // Optional: Custom session storage
autoRefreshSession?: boolean // Optional: Auto-refresh (default: true)
sessionWarningThreshold?: number // Optional: Refresh threshold in ms (default: 60000)
}React Native
The connector works in React Native (Expo) with the same Wagmi setup, but the
key-storage and session-storage adapters that the web build defaults have no
browser equivalent — so on native apiKeyStamper, sessionStorage, and
rpId are required (TypeScript enforces it). passkeyStamper is optional:
omit it (and drop @turnkey/react-native-passkey-stamper from your install) if
your app doesn't use passkey auth. Calling useRegisterPasskey /
useLoginPasskey against a wallet configured without one throws an actionable
error.
npm install @zerodev/wallet-react @zerodev/wallet-core wagmi viem \
expo-secure-store \
@turnkey/api-key-stamper @turnkey/crypto \
@react-native-async-storage/async-storage \
react-native-get-random-values uuid
# Add @turnkey/react-native-passkey-stamper only if you want passkey auth.
# or
yarn add @zerodev/wallet-react @zerodev/wallet-core wagmi viem \
expo-secure-store \
@turnkey/api-key-stamper @turnkey/crypto \
@react-native-async-storage/async-storage \
react-native-get-random-values uuid
# or
pnpm add @zerodev/wallet-react @zerodev/wallet-core wagmi viem \
expo-secure-store \
@turnkey/api-key-stamper @turnkey/crypto \
@react-native-async-storage/async-storage \
react-native-get-random-values uuidImport the crypto polyfill once at your app's entry point, before any SDK call:
import 'react-native-get-random-values'import { zeroDevWallet } from '@zerodev/wallet-react'
import { createSecureStoreStamper } from '@zerodev/wallet-core/react-native/stampers/secure-store'
import { createReactNativePasskeyStamper } from '@zerodev/wallet-core/react-native/stampers/passkey'
import { asyncStorageAdapter } from '@zerodev/wallet-core/react-native/storage/async-storage'
import { createConfig, createStorage, http } from 'wagmi'
import { sepolia } from 'wagmi/chains'
const RP_ID = 'your-app.example.com' // must match your assetlinks/AASA domain
const config = createConfig({
chains: [sepolia],
connectors: [
zeroDevWallet({
projectId: 'YOUR_PROJECT_ID',
chains: [sepolia],
rpId: RP_ID,
apiKeyStamper: createSecureStoreStamper(),
passkeyStamper: createReactNativePasskeyStamper({ rpId: RP_ID }), // optional
sessionStorage: asyncStorageAdapter,
persistStorage: asyncStorageAdapter, // persists the wagmi connection across restarts
}),
],
transports: { [sepolia.id]: http() },
storage: createStorage({ storage: asyncStorageAdapter }),
})OAuth (deep link)
The web popup flow isn't available on native. Use the Expo deep-link hook,
which wires expo-web-browser + expo-linking for you — the consumer supplies
only redirectUri (your app's deep link):
npm install expo-web-browser expo-linking
# or
yarn add expo-web-browser expo-linking
# or
pnpm add expo-web-browser expo-linkingimport { useAuthenticateOAuthWithExpoWebBrowser } from '@zerodev/wallet-react/react-native/oauth/with-expo-web-browser'
import { OAUTH_PROVIDERS } from '@zerodev/wallet-react'
const authenticateOAuth = useAuthenticateOAuthWithExpoWebBrowser({
redirectUri: 'https://your-app.example.com/oauth-callback',
})
authenticateOAuth.mutate({ provider: OAUTH_PROVIDERS.GOOGLE })Using a different OAuth library? Import the generic useAuthenticateOAuth from
@zerodev/wallet-react/react-native and supply your own getSessionId — that
path doesn't pull in expo-web-browser / expo-linking.
Export wallet / private key
The iframe-based useExportWallet / useExportPrivateKey hooks are web-only.
On native, render the ZeroDevExportWebView component — it loads Turnkey's
export iframe inside a hardened react-native-webview, so the plaintext never
touches the RN bundle. Mount it to start the export; unmount to dismiss:
npm install react-native-webview uuid
# or
yarn add react-native-webview uuid
# or
pnpm add react-native-webview uuidimport { ZeroDevExportWebView } from '@zerodev/wallet-react/react-native/export/webview'
<ZeroDevExportWebView
kind="wallet" // or "privateKey"
onReady={() => { /* secret is on screen */ }}
onError={(message) => { /* fetch / iframe failure */ }}
/>Advanced Usage
Custom Callbacks
const registerPasskey = useRegisterPasskey({
mutation: {
onSuccess: () => {
console.log('Registration successful!')
router.push('/dashboard')
},
onError: (error) => {
console.error('Registration failed:', error)
analytics.track('auth_failed', { method: 'passkey' })
}
}
})Manual Session Refresh
const refreshSession = useRefreshSession()
await refreshSession.mutateAsync({})Export Wallet (Seed Phrase)
const exportWallet = useExportWallet()
// Container element must exist: <div id="export-container" />
await exportWallet.mutateAsync({
iframeContainerId: 'export-container'
})Export Private Key
const exportPrivateKey = useExportPrivateKey()
// Container element must exist: <div id="export-container" />
await exportPrivateKey.mutateAsync({
iframeContainerId: 'export-container'
})Get Authenticators
const { data: authenticators, isLoading } = useAuthenticators()
// `authenticators` contains all authenticators linked to the current user:
// { oauths, passkeys, emailContacts, apiKeys }
console.log(authenticators?.oauths)
console.log(authenticators?.passkeys)
console.log(authenticators?.emailContacts)
console.log(authenticators?.apiKeys)API Reference
Hooks
All hooks follow the TanStack Query mutation pattern:
useRegisterPasskey()- Register with passkeyuseLoginPasskey()- Login with passkeyuseAuthenticateOAuth()- OAuth (Google popup)useSendMagicLink()- Send magic link via emailuseVerifyMagicLink()- Verify magic link codeuseSendOTP()- Send OTP via emailuseVerifyOTP()- Verify OTP codeuseRefreshSession()- Manually refresh sessionuseExportWallet()- Export wallet seed phraseuseExportPrivateKey()- Export wallet private keyuseAuthenticators()- Fetch all authenticators (oauths, passkeys, emailContacts, apiKeys) for the user
Connector
zeroDevWallet(params)- Wagmi connector factory
Constants
OAUTH_PROVIDERS.GOOGLE- Google OAuth provider constant
Types
OAuthProvider- OAuth provider typeZeroDevWalletConnectorParams- Connector parametersZeroDevWalletState- Store state typeZeroDevProvider- EIP-1193 provider type
License
MIT
