From 3fa3039eccb108460e1b71d32144cf6407cef138 Mon Sep 17 00:00:00 2001 From: srinjoyc Date: Tue, 16 Jun 2026 07:26:34 -0700 Subject: [PATCH] Refresh wallet docs --- docs/pages/wallets/api-reference.mdx | 232 ++++++++++++++++++ docs/pages/wallets/auth/email-otp.mdx | 22 +- docs/pages/wallets/auth/google-oauth.mdx | 16 +- docs/pages/wallets/auth/index.mdx | 65 +++++ docs/pages/wallets/auth/magic-link.mdx | 29 ++- docs/pages/wallets/auth/passkeys.mdx | 5 + docs/pages/wallets/export.mdx | 78 ++++-- .../wallets/hooks/use-authenticate-oauth.mdx | 12 +- .../wallets/hooks/use-authenticators.mdx | 70 ++++++ .../wallets/hooks/use-get-user-email.mdx | 48 +--- .../wallets/hooks/use-send-magic-link.mdx | 20 +- docs/pages/wallets/hooks/use-send-otp.mdx | 16 +- .../wallets/hooks/use-verify-magic-link.mdx | 23 +- docs/pages/wallets/hooks/use-verify-otp.mdx | 26 +- docs/pages/wallets/index.mdx | 31 ++- docs/pages/wallets/overview.mdx | 75 ++++++ docs/pages/wallets/quickstart.mdx | 135 +++++++++- docs/pages/wallets/wallet-api/index.mdx | 42 ++++ .../wallets/wallet-api/send-transaction.mdx | 24 +- .../pages/wallets/wallet-api/sign-message.mdx | 12 +- .../wallets/wallet-api/sign-typed-message.mdx | 15 +- vocs.config.tsx | 99 +++++--- 22 files changed, 937 insertions(+), 158 deletions(-) create mode 100644 docs/pages/wallets/api-reference.mdx create mode 100644 docs/pages/wallets/auth/index.mdx create mode 100644 docs/pages/wallets/hooks/use-authenticators.mdx create mode 100644 docs/pages/wallets/overview.mdx create mode 100644 docs/pages/wallets/wallet-api/index.mdx diff --git a/docs/pages/wallets/api-reference.mdx b/docs/pages/wallets/api-reference.mdx new file mode 100644 index 0000000..d75a28f --- /dev/null +++ b/docs/pages/wallets/api-reference.mdx @@ -0,0 +1,232 @@ +# Public API Reference [Current package exports] + +:::danger[IMPORTANT] +**USE FOR INTERNAL TESTING PURPOSES ONLY.** You may use these features solely for internal evaluation purposes on supported testnets. DO NOT use for production use or share with your users. Wallets created during this preview ("Alpha Wallets") will be discontinued. Any tokens remaining within Alpha Wallets will be permanently lost upon discontinuance. Any mainnet tokens sent to an Alpha Wallet will not be deposited and will be permanently lost when discontinued. We are unable to help recover any lost funds from Alpha Wallets. We provide all previews on an "as is" basis without warranty of any kind, and we may terminate or suspend the availability of any preview at any time. +::: + +This page tracks the public exports from the current ZeroDev Wallet SDK packages. + +## @zerodev/wallet-react + +### Dashboard configuration + +The SDK enforces project security settings from the ZeroDev dashboard: + +- Allowlist every app origin in the access control list, including local development ports. +- Allowlist OAuth and magic-link redirect URLs before using those auth methods. +- Disable any auth method you do not want to support. Email, Google, and passkey auth methods are enabled by default for new projects. + +### Wallet operation credits + +ZeroDev Wallet operations consume the Credits included in your existing ZeroDev plan. + +| Wallet operation | Credit cost | Notes | +|------------------|-------------|-------| +| Signature | 10 credits | Any wallet signature, including `personal_sign`, EIP-712 typed data, and UserOperation signatures. | +| Bundler broadcast | 20 credits | Same pricing as the core ZeroDev Account Abstraction service for sending a UserOperation through the bundler. | + +A standalone message or typed-data signature costs **10 credits**. A transaction sent in `7702` or `4337` mode costs **30 credits total**: **10 credits** for the wallet signature plus **20 credits** for the bundler broadcast that sends the UserOperation to the network. + +### zeroDevWallet + +```ts +import { zeroDevWallet } from '@zerodev/wallet-react' +``` + +Wagmi connector factory for ZeroDev Wallet. + +```ts +zeroDevWallet({ + projectId: 'YOUR_ZERODEV_PROJECT_ID', + chains: [sepolia], + mode: '7702', +}) +``` + +| Option | Type | Description | +|--------|------|-------------| +| `projectId` | `string` | Required. ZeroDev project ID. | +| `chains` | `readonly Chain[]` | Required. Chains supported by this connector. | +| `organizationId` | `string` | Optional Turnkey organization ID override. | +| `proxyBaseUrl` | `string` | Optional KMS proxy URL. | +| `aaUrl` | `string` | Optional bundler/paymaster URL override. | +| `rpId` | `string` | Optional WebAuthn relying party ID. Defaults to the current hostname on web. | +| `sessionStorage` | `StorageAdapter` | Optional storage for wallet sessions. | +| `persistStorage` | `StateStorage` | Optional Zustand persistence storage for connector state. | +| `apiKeyStamper` | `ApiKeyStamper \| Promise` | Optional API-key stamper override. | +| `passkeyStamper` | `PasskeyStamper \| Promise` | Optional passkey stamper override. | +| `autoRefreshSession` | `boolean` | Defaults to `true`. Set to `false` to disable automatic refresh. | +| `sessionWarningThreshold` | `number` | Milliseconds before expiry to refresh. Defaults to `60000`. | +| `mode` | `'7702' \| '4337'` | Account mode. Defaults to `'7702'`. | +| `autoInitialize` | `boolean \| (() => boolean)` | Controls whether connector setup initializes immediately. | + +`fetchOptions` is available on the lower-level `zeroDevWalletCore`, but intentionally omitted from the web `zeroDevWallet` export because browsers ignore custom `Origin` overrides. + +### Account modes + +| Mode | Address exposed to Wagmi | Transaction path | +|------|--------------------------|------------------| +| `'7702'` | Wallet address | Kernel account via EIP-7702 UserOperations submitted through a bundler. | +| `'4337'` | Counterfactual Kernel smart account address | ERC-4337 UserOperations submitted through a bundler. | + +### Auth hooks + +| Export | Purpose | +|--------|---------| +| `useRegisterPasskey` | Register a new passkey and connect the wallet. | +| `useLoginPasskey` | Login with an existing passkey and connect the wallet. | +| `useAuthenticateOAuth` | Authenticate with Google OAuth. The web export opens a popup and accepts optional `timeoutMs`. | +| `useSendOTP` | Send an email OTP. Returns `otpId` and `otpEncryptionTargetBundle`. | +| `useVerifyOTP` | Verify an email OTP. Requires `code`, `otpId`, and `otpEncryptionTargetBundle`. | +| `useSendMagicLink` | Send a magic link. Returns `otpId` and `otpEncryptionTargetBundle`. | +| `useVerifyMagicLink` | Verify a magic link. Requires `code`, `otpId`, and `otpEncryptionTargetBundle`. | + +### Session and account hooks + +| Export | Purpose | +|--------|---------| +| `useRefreshSession` | Manually refresh the active session. | +| `useAuthenticators` | Query OAuth providers, passkeys, email contacts, and API keys for the current user. | +| `useExportWallet` | Render a secure Turnkey iframe for wallet seed phrase export. | +| `useExportPrivateKey` | Render a secure Turnkey iframe for private key export. | + +### Actions and helpers + +| Export | Purpose | +|--------|---------| +| `getZeroDevConnector(config)` | Find the ZeroDev connector in a Wagmi config. Throws if it is not configured. | +| `getZeroDevStore(connector)` | Get the connector's ZeroDev store. | +| `getZeroDevWallet(store)` | Get the initialized core wallet SDK from the store. Throws if initialization has not completed. | +| `createZeroDevWalletStore(options?)` | Create the connector state store. Advanced usage only. | +| `NotAuthenticatedError` | Error thrown when wallet operations require an authenticated session. | +| `OAUTH_PROVIDERS` | OAuth provider constants. Currently `{ GOOGLE: 'google' }`. | +| `generateOAuthNonce(publicKey)` | Compute the Google OIDC nonce bound to the wallet public key. | +| `verifyGoogleLoginUrl(loginUrl, publicKey)` | Validate Google OAuth login URL host and nonce before starting OAuth. | + +## Wallet provider methods + +The connector exposes an EIP-1193 provider. Supported `request` methods are: + +| Method | Description | +|--------|-------------| +| `eth_accounts` | Returns the active wallet address. | +| `eth_requestAccounts` | Returns the active wallet address or throws `NotAuthenticatedError`. | +| `eth_chainId` | Returns the active chain ID as hex. | +| `eth_sendTransaction` | Sends a transaction. Uses UserOperations in `7702` and `4337` mode. | +| `wallet_sendTransaction` | Same transaction path as `eth_sendTransaction`. | +| `personal_sign` | Signs a message with the wallet signer for the configured mode. | +| `eth_signTypedData_v4` | Signs EIP-712 typed data with the wallet signer for the configured mode. | +| `wallet_switchEthereumChain` | Updates the active chain and emits `chainChanged`. | + +## @zerodev/wallet-core + +Use `@zerodev/wallet-core` directly when you need lower-level control without Wagmi. + +### createZeroDevWallet + +```ts +import { createZeroDevWallet } from '@zerodev/wallet-core' + +const wallet = await createZeroDevWallet({ + projectId: 'YOUR_ZERODEV_PROJECT_ID', +}) +``` + +On web, `rpId`, `sessionStorage`, `apiKeyStamper`, and `passkeyStamper` have browser defaults. React Native consumers should import from the React Native subpath and provide native storage/stamper adapters. + +### ZeroDevWalletSDK + +```ts +type ZeroDevWalletSDK = { + client: ZeroDevWalletClient + auth: (params: AuthParams) => Promise + getPublicKey: () => Promise + getSession: () => Promise + getAllSessions: () => Promise> + switchSession: (sessionId: string) => Promise + clearSession: (sessionId: string) => Promise + clearAllSessions: () => Promise + refreshSession: (sessionId?: string) => Promise + logout: () => Promise + toAccount: () => Promise +} +``` + +`auth` supports `oauth`, passkey `register`/`login`, OTP `sendOtp`/`verifyOtp`, and magic-link `send`/`verify`. OTP and magic-link verify calls require the `otpEncryptionTargetBundle` returned by the matching send call. + +### Core utilities + +| Export | Purpose | +|--------|---------| +| `toViemAccount` | Convert an authenticated wallet client/session into a viem `LocalAccount`. | +| `createClient`, `createBaseClient`, `zeroDevWalletTransport` | Low-level client/transport primitives. | +| `zeroDevWalletActions` | Client decorator for wallet actions. | +| `createIframeStamper` | Create a Turnkey iframe stamper for secure export flows. | +| `exportWallet` | Build a wallet export bundle for iframe injection. | +| `exportPrivateKey` | Build a private-key export bundle for iframe injection. | +| `normalizeTimestamp` | Normalize session expiry timestamps. | +| `KMS_SERVER_URL` | Default KMS server URL. | + +### Root action exports + +These actions are exported from `@zerodev/wallet-core` and are also available through the decorated `wallet.client`. + +| Export | Purpose | +|--------|---------| +| `authenticateWithEmail` | Authenticate with an email-based session. | +| `authenticateWithOAuth` | Authenticate with an OAuth session ID. | +| `getAuthenticators` | Fetch OAuth authenticators, passkeys, email contacts, and API keys. | +| `getUserWallet` | Fetch wallet addresses for the authenticated user. | +| `getWhoami` | Fetch identity/session metadata for the current token. | +| `loginWithOTP` | Login with an OTP attempt. | +| `registerWithOTP` | Register an OTP contact and send an OTP. | +| `sign7702Authorization` | Sign an EIP-7702 authorization. | +| `signMessage` | Sign a message with a wallet key. | +| `signTransaction` | Sign a serialized transaction. | +| `signTypedDataV4` | Sign EIP-712 typed data. | +| `signUserOperation` | Sign an ERC-4337 UserOperation. | + +### Actions subpath + +```ts +import { getOAuthLoginUrl } from '@zerodev/wallet-core/actions' +``` + +The `@zerodev/wallet-core/actions` subpath exports the root actions above plus lower-level actions used by the React SDK: + +| Export | Purpose | +|--------|---------| +| `getAuthProxyConfigId` | Fetch the auth proxy config ID. | +| `getOAuthLoginUrl` | Build the OAuth login URL for a provider and return URL. | +| `loginWithStamp` | Login using an existing stamper. | +| `registerWithPasskey` | Register a passkey credential. | + +## @zerodev/wallet-react-kit + +Use `@zerodev/wallet-react-kit` if you want prebuilt auth and signing UI. + +| Export | Purpose | +|--------|---------| +| `zeroDevWallet` | Enhanced connector wrapping `@zerodev/wallet-react` with optional auth/signing UI. | +| `AuthFlow` | Prebuilt authentication flow component. | +| `useAuth` | Access the kit auth state and navigation actions. | +| `SignatureRequest` | Prebuilt request confirmation UI. | +| `usePendingRequest` | Get the current pending signing request. | +| `usePendingRequests` | Get all pending signing requests. | + +The kit connector accepts the same connector options as `@zerodev/wallet-react`, plus: + +```ts +type ZeroDevKitConfig = { + signing?: { mode: 'background' } | { mode: 'prompt'; methods?: RequestMethod[] } + auth?: { + magicLinkBaseUrl: string + enabledMethods: ('email' | 'google' | 'passkey')[] + emailAuthMethod?: 'magicLink' | 'otp' + termsAndConditionsUrl?: string + privacyPolicyUrl?: string + onSuccess?: () => void + onError?: (error: unknown) => void + } +} +``` diff --git a/docs/pages/wallets/auth/email-otp.mdx b/docs/pages/wallets/auth/email-otp.mdx index c7aaf37..3d99ab6 100644 --- a/docs/pages/wallets/auth/email-otp.mdx +++ b/docs/pages/wallets/auth/email-otp.mdx @@ -6,6 +6,11 @@ Email OTP is a two-step flow: send a verification code to the user's email, then verify the code they enter. +## Dashboard requirements + +- Allowlist your app origin in the dashboard access control list, including the local development port. +- Email auth is enabled by default for new projects. If you do not want users to authenticate with email, explicitly disable email auth in the dashboard. + ## Hooks - [`useSendOTP`](/wallets/hooks/use-send-otp) — Send a one-time code to an email address @@ -22,6 +27,8 @@ function EmailOTPAuth() { const [email, setEmail] = useState('') const [code, setCode] = useState('') const [otpId, setOtpId] = useState(null) + const [otpEncryptionTargetBundle, setOtpEncryptionTargetBundle] = + useState(null) const { address, isConnected } = useAccount() const { disconnectAsync } = useDisconnect() @@ -50,6 +57,7 @@ function EmailOTPAuth() { onClick={async () => { const result = await sendOTP.mutateAsync({ email }) setOtpId(result.otpId) + setOtpEncryptionTargetBundle(result.otpEncryptionTargetBundle) }} disabled={sendOTP.isPending || !email} > @@ -60,6 +68,8 @@ function EmailOTPAuth() { ) } + if (!otpEncryptionTargetBundle) return null + return (

Code sent to {email}

@@ -70,7 +80,13 @@ function EmailOTPAuth() { onChange={(e) => setCode(e.target.value)} /> -
@@ -81,7 +109,7 @@ function ExportModal() { ## How it works 1. You provide a container element ID where the secure iframe will be rendered. -2. The SDK initiates a secure export session. +2. The SDK initiates a secure export session with Turnkey. 3. The seed phrase or private key is rendered inside a secure iframe. 4. The key material is displayed directly to the user — it never passes through your application. diff --git a/docs/pages/wallets/hooks/use-authenticate-oauth.mdx b/docs/pages/wallets/hooks/use-authenticate-oauth.mdx index a09ccc6..e0397de 100644 --- a/docs/pages/wallets/hooks/use-authenticate-oauth.mdx +++ b/docs/pages/wallets/hooks/use-authenticate-oauth.mdx @@ -22,7 +22,9 @@ import { import { useAccount } from 'wagmi' function OAuthLogin() { - const authenticateOAuth = useAuthenticateOAuth() + const authenticateOAuth = useAuthenticateOAuth({ + timeoutMs: 5 * 60 * 1000, + }) const { address, isConnected } = useAccount() if (isConnected) { @@ -46,6 +48,14 @@ function OAuthLogin() { ## Parameters +The web hook takes optional hook-level parameters, then `provider` as the mutation variable. + +### timeoutMs + +`number | undefined` + +Optional popup timeout in milliseconds. Defaults to 5 minutes. + ### provider `OAuthProvider` diff --git a/docs/pages/wallets/hooks/use-authenticators.mdx b/docs/pages/wallets/hooks/use-authenticators.mdx new file mode 100644 index 0000000..f05f80d --- /dev/null +++ b/docs/pages/wallets/hooks/use-authenticators.mdx @@ -0,0 +1,70 @@ +import QueryResult from '../shared/query-result.mdx' + +# useAuthenticators [Hook for listing the user's authenticators] + +:::danger[IMPORTANT] +**USE FOR INTERNAL TESTING PURPOSES ONLY.** You may use these features solely for internal evaluation purposes on supported testnets. DO NOT use for production use or share with your users. Wallets created during this preview ("Alpha Wallets") will be discontinued. Any tokens remaining within Alpha Wallets will be permanently lost upon discontinuance. Any mainnet tokens sent to an Alpha Wallet will not be deposited and will be permanently lost when discontinued. We are unable to help recover any lost funds from Alpha Wallets. We provide all previews on an "as is" basis without warranty of any kind, and we may terminate or suspend the availability of any preview at any time. +::: + +## Import + +```tsx +import { useAuthenticators } from '@zerodev/wallet-react' +``` + +## Usage + +```tsx +import { useAuthenticators } from '@zerodev/wallet-react' +import { useAccount } from 'wagmi' + +function Authenticators() { + const { isConnected } = useAccount() + const authenticators = useAuthenticators({ + query: { + enabled: isConnected, + }, + }) + + if (!isConnected) return null + if (authenticators.isLoading) return

Loading...

+ if (authenticators.isError) { + return

Error: {authenticators.error.message}

+ } + + return ( +
+      {JSON.stringify(authenticators.data, null, 2)}
+    
+ ) +} +``` + +## Parameters + +### query + +`Omit, 'queryKey' | 'queryFn'> | undefined` + +Optional TanStack Query options. The hook owns the `queryKey` and `queryFn`. + +## Return Types + +### data + +`GetAuthenticatorsReturnType | undefined` + +The authenticators linked to the current user in the connected project and sub-organization. + +```ts +type GetAuthenticatorsReturnType = { + oauths: OAuthAuthenticator[] | null + passkeys: PasskeyAuthenticator[] | null + emailContacts: EmailContact[] | null + apiKeys: ApiKeyAuthenticator[] | null +} +``` + +`emailContacts` includes email contacts linked by OTP or magic-link auth. `oauths` includes linked OAuth providers such as Google. `passkeys` includes WebAuthn credentials. `apiKeys` includes session/API-key authenticators. + + diff --git a/docs/pages/wallets/hooks/use-get-user-email.mdx b/docs/pages/wallets/hooks/use-get-user-email.mdx index 6e92cc7..e62c658 100644 --- a/docs/pages/wallets/hooks/use-get-user-email.mdx +++ b/docs/pages/wallets/hooks/use-get-user-email.mdx @@ -1,56 +1,24 @@ import QueryResult from '../shared/query-result.mdx' -# useGetUserEmail [Hook for getting the authenticated user's email] +# useGetUserEmail [Removed hook] :::danger[IMPORTANT] **USE FOR INTERNAL TESTING PURPOSES ONLY.** You may use these features solely for internal evaluation purposes on supported testnets. DO NOT use for production use or share with your users. Wallets created during this preview ("Alpha Wallets") will be discontinued. Any tokens remaining within Alpha Wallets will be permanently lost upon discontinuance. Any mainnet tokens sent to an Alpha Wallet will not be deposited and will be permanently lost when discontinued. We are unable to help recover any lost funds from Alpha Wallets. We provide all previews on an "as is" basis without warranty of any kind, and we may terminate or suspend the availability of any preview at any time. ::: -## Import +`useGetUserEmail` is not exported by the current `@zerodev/wallet-react` package. -```tsx -import { useGetUserEmail } from '@zerodev/wallet-react' -``` - -## Usage +Use [`useAuthenticators`](/wallets/hooks/use-authenticators) instead and read `data.emailContacts` for email contacts linked to the authenticated user. ```tsx -import { useGetUserEmail } from '@zerodev/wallet-react' -import { useAccount } from 'wagmi' - -function UserProfile() { - const { isConnected } = useAccount() - const { data: userEmail, isLoading } = useGetUserEmail({}) - - if (!isConnected) return null +import { useAuthenticators } from '@zerodev/wallet-react' - if (isLoading) return

Loading email...

+function UserEmail() { + const authenticators = useAuthenticators() + const email = authenticators.data?.emailContacts?.[0]?.email - return

Email: {userEmail?.email ?? 'Not available'}

+ return

Email: {email ?? 'Not available'}

} ``` -## Parameters - -```tsx -import { type UseGetUserEmailParameters } from '@zerodev/wallet-react' -``` - -This hook takes an empty object `{}` as its argument. No additional parameters are required. - -## Return Types - -### data - -`{ email: string } | undefined` - -- Defaults to `undefined` -- The authenticated user's email address, if available. - -#### email - -`string` - -The user's email address. This is available when the user authenticated with Email OTP, Magic Link, or Google OAuth. It may not be available for passkey-only authentication. - diff --git a/docs/pages/wallets/hooks/use-send-magic-link.mdx b/docs/pages/wallets/hooks/use-send-magic-link.mdx index 1d2690b..8b08ab4 100644 --- a/docs/pages/wallets/hooks/use-send-magic-link.mdx +++ b/docs/pages/wallets/hooks/use-send-magic-link.mdx @@ -36,8 +36,12 @@ function SendMagicLink() { email, redirectURL: `${window.location.origin}/verify`, }) - // Store otpId for the verify page + // Store both values for the verify page sessionStorage.setItem('magicLinkOtpId', result.otpId) + sessionStorage.setItem( + 'magicLinkOtpEncryptionTargetBundle', + result.otpEncryptionTargetBundle, + ) }} disabled={sendMagicLink.isPending || !email} > @@ -66,7 +70,7 @@ function SendMagicLink() { ### otpCodeCustomization -`{ length?: 6 | 7 | 8 | 9; alphanumeric?: boolean } | undefined` +`{ length: 6 | 7 | 8 | 9; alphanumeric: boolean } | undefined` Optional customization for the generated verification code. @@ -88,19 +92,19 @@ Whether to use alphanumeric characters instead of digits only. Defaults to `fals ### mutate -`(variables: { email: string; redirectURL: string }) => void` +`(variables: { email: string; redirectURL: string; otpCodeCustomization?: { length: 6 | 7 | 8 | 9; alphanumeric: boolean }; connector?: Connector }) => void` The mutation function to send the magic link. ### mutateAsync -`(variables: { email: string; redirectURL: string }) => Promise<{ otpId: string }>` +`(variables: { email: string; redirectURL: string; otpCodeCustomization?: { length: 6 | 7 | 8 | 9; alphanumeric: boolean }; connector?: Connector }) => Promise<{ otpId: string; otpEncryptionTargetBundle: string }>` Similar to [`mutate`](#mutate) but returns a promise. ### data -`{ otpId: string } | undefined` +`{ otpId: string; otpEncryptionTargetBundle: string } | undefined` - Defaults to `undefined` - The data returned from the mutation on success. @@ -111,4 +115,10 @@ Similar to [`mutate`](#mutate) but returns a promise. The identifier for this magic link verification. Store this value and pass it to [`useVerifyMagicLink`](/wallets/hooks/use-verify-magic-link) on the redirect page. +#### otpEncryptionTargetBundle + +`string` + +The encryption target bundle for this magic link attempt. Store it with the matching `otpId` and pass it to [`useVerifyMagicLink`](/wallets/hooks/use-verify-magic-link) on the redirect page. + diff --git a/docs/pages/wallets/hooks/use-send-otp.mdx b/docs/pages/wallets/hooks/use-send-otp.mdx index ffb2fb1..bcae717 100644 --- a/docs/pages/wallets/hooks/use-send-otp.mdx +++ b/docs/pages/wallets/hooks/use-send-otp.mdx @@ -34,7 +34,7 @@ function SendCode() { onClick={async () => { const result = await sendOTP.mutateAsync({ email }) console.log('OTP ID:', result.otpId) - // Pass otpId to useVerifyOTP + // Pass otpId and otpEncryptionTargetBundle to useVerifyOTP }} disabled={sendOTP.isPending || !email} > @@ -67,7 +67,7 @@ Custom template for the email content. ### otpCodeCustomization -`{ length?: 6 | 7 | 8 | 9; alphanumeric?: boolean } | undefined` +`{ length: 6 | 7 | 8 | 9; alphanumeric: boolean } | undefined` Optional customization for the generated OTP code. @@ -89,19 +89,19 @@ Whether to use alphanumeric characters instead of digits only. Defaults to `fals ### mutate -`(variables: { email: string; emailCustomization?: { magicLinkTemplate?: string } }) => void` +`(variables: { email: string; emailCustomization?: { magicLinkTemplate?: string }; otpCodeCustomization?: { length: 6 | 7 | 8 | 9; alphanumeric: boolean }; connector?: Connector }) => void` The mutation function to send the OTP. ### mutateAsync -`(variables: { email: string; emailCustomization?: { magicLinkTemplate?: string } }) => Promise<{ otpId: string }>` +`(variables: { email: string; emailCustomization?: { magicLinkTemplate?: string }; otpCodeCustomization?: { length: 6 | 7 | 8 | 9; alphanumeric: boolean }; connector?: Connector }) => Promise<{ otpId: string; otpEncryptionTargetBundle: string }>` Similar to [`mutate`](#mutate) but returns a promise. ### data -`{ otpId: string } | undefined` +`{ otpId: string; otpEncryptionTargetBundle: string } | undefined` - Defaults to `undefined` - The data returned from the mutation on success. @@ -112,4 +112,10 @@ Similar to [`mutate`](#mutate) but returns a promise. The identifier for this OTP verification attempt. Pass this to [`useVerifyOTP`](/wallets/hooks/use-verify-otp) to complete authentication. +#### otpEncryptionTargetBundle + +`string` + +The encryption target bundle for this OTP attempt. Store it with the matching `otpId` and pass it to [`useVerifyOTP`](/wallets/hooks/use-verify-otp). + diff --git a/docs/pages/wallets/hooks/use-verify-magic-link.mdx b/docs/pages/wallets/hooks/use-verify-magic-link.mdx index 5810d1b..9dd7bf3 100644 --- a/docs/pages/wallets/hooks/use-verify-magic-link.mdx +++ b/docs/pages/wallets/hooks/use-verify-magic-link.mdx @@ -28,9 +28,16 @@ function VerifyPage() { useEffect(() => { const code = searchParams.get('code') const otpId = sessionStorage.getItem('magicLinkOtpId') - - if (code && otpId && !isConnected) { - verifyMagicLink.mutateAsync({ otpId, code }) + const otpEncryptionTargetBundle = sessionStorage.getItem( + 'magicLinkOtpEncryptionTargetBundle', + ) + + if (code && otpId && otpEncryptionTargetBundle && !isConnected) { + verifyMagicLink.mutateAsync({ + otpId, + code, + otpEncryptionTargetBundle, + }) } }, [searchParams]) @@ -56,19 +63,25 @@ function VerifyPage() { **Required.** The verification code from the magic link URL query parameters. +### otpEncryptionTargetBundle + +`string` + +**Required.** The encryption target bundle returned by the matching [`useSendMagicLink`](/wallets/hooks/use-send-magic-link) call. + ## Return Types [TanStack Query mutation docs](https://tanstack.com/query/v5/docs/react/reference/useMutation) ### mutate -`(variables: { otpId: string; code: string }) => void` +`(variables: { otpId: string; code: string; otpEncryptionTargetBundle: string; connector?: Connector }) => void` The mutation function to verify the magic link. ### mutateAsync -`(variables: { otpId: string; code: string }) => Promise` +`(variables: { otpId: string; code: string; otpEncryptionTargetBundle: string; connector?: Connector }) => Promise` Similar to [`mutate`](#mutate) but returns a promise. Resolves when the magic link is verified and the wallet is connected. diff --git a/docs/pages/wallets/hooks/use-verify-otp.mdx b/docs/pages/wallets/hooks/use-verify-otp.mdx index 4ee8832..4e3f08d 100644 --- a/docs/pages/wallets/hooks/use-verify-otp.mdx +++ b/docs/pages/wallets/hooks/use-verify-otp.mdx @@ -19,7 +19,13 @@ import { useState } from 'react' import { useVerifyOTP } from '@zerodev/wallet-react' import { useAccount } from 'wagmi' -function VerifyCode({ otpId }: { otpId: string }) { +function VerifyCode({ + otpId, + otpEncryptionTargetBundle, +}: { + otpId: string + otpEncryptionTargetBundle: string +}) { const [code, setCode] = useState('') const verifyOTP = useVerifyOTP() const { address, isConnected } = useAccount() @@ -37,7 +43,13 @@ function VerifyCode({ otpId }: { otpId: string }) { placeholder="Enter verification code" /> + )} + + ) +} +``` + +For magic link auth, render `AuthFlow` on the URL you configured as `magicLinkBaseUrl`: + +```tsx +import { AuthFlow } from '@zerodev/wallet-react-kit' + +export default function VerifyPage() { + return +} +``` + +After authentication, the user's wallet is available through standard Wagmi hooks like `useAccount`, `useSendTransaction`, `useWriteContract`, `useSignMessage`, and `useSignTypedData`. + +## 5. Optional: build a custom auth UI + +Use the lower-level React hooks when you want to control every auth screen yourself. Import the connector from `@zerodev/wallet-react` instead of `@zerodev/wallet-react-kit`, and keep UI configuration in your own components instead of `config.auth`. + +```tsx +import { zeroDevWallet } from '@zerodev/wallet-react' + +zeroDevWallet({ + projectId: 'YOUR_ZERODEV_PROJECT_ID', + chains: [sepolia], + mode: '7702', +}) +``` Here's a basic example with passkey registration and login: @@ -118,7 +210,26 @@ function AuthPage() { } ``` -After authentication, the user's wallet is available through standard Wagmi hooks like `useAccount`, `useSendTransaction`, and `useSignMessage`. +The email OTP and magic-link hooks require the `otpEncryptionTargetBundle` returned by the matching send call. See [Email OTP](/wallets/auth/email-otp) and [Magic Link](/wallets/auth/magic-link) for complete examples. + +## 6. Optional: request confirmation UI + +Mount `SignatureRequest` when you want the React Kit to ask users to review signing and transaction requests: + +```tsx +import { SignatureRequest } from '@zerodev/wallet-react-kit' + +function AppShell() { + return ( + <> + + {/* your app */} + + ) +} +``` + +For background signing, omit `SignatureRequest` or configure the kit connector's signing mode. See the [public API reference](/wallets/api-reference) for the kit config type. ## Next Steps @@ -127,4 +238,6 @@ After authentication, the user's wallet is available through standard Wagmi hook - [Google OAuth](/wallets/auth/google-oauth) — Social login with Google - [Send Transactions](/wallets/wallet-api/send-transaction) — Send gasless transactions - [Sign Messages](/wallets/wallet-api/sign-message) — Sign messages with the wallet +- [Export Wallet](/wallets/export) — Let users export seed phrases or private keys +- [Public API Reference](/wallets/api-reference) — Current package exports - [Session Management](/wallets/session-management) — Session lifecycle and refresh diff --git a/docs/pages/wallets/wallet-api/index.mdx b/docs/pages/wallets/wallet-api/index.mdx new file mode 100644 index 0000000..487a434 --- /dev/null +++ b/docs/pages/wallets/wallet-api/index.mdx @@ -0,0 +1,42 @@ +# Wallets [Transactions, signing, and export] + +:::danger[IMPORTANT] +**USE FOR INTERNAL TESTING PURPOSES ONLY.** You may use these features solely for internal evaluation purposes on supported testnets. DO NOT use for production use or share with your users. Wallets created during this preview ("Alpha Wallets") will be discontinued. Any tokens remaining within Alpha Wallets will be permanently lost upon discontinuance. Any mainnet tokens sent to an Alpha Wallet will not be deposited and will be permanently lost when discontinued. We are unable to help recover any lost funds from Alpha Wallets. We provide all previews on an "as is" basis without warranty of any kind, and we may terminate or suspend the availability of any preview at any time. +::: + +After authentication, ZeroDev Wallet exposes a Wagmi-compatible account. Your app can use normal Wagmi hooks for transactions, contract writes, message signatures, and typed-data signatures. + +The important implementation detail is the account mode: + +| Mode | Transaction path | +|------|------------------| +| `7702` | Wagmi send/write calls are routed through a Kernel smart account and broadcast by the bundler as UserOperations. | +| `4337` | Wagmi send/write calls are routed through a counterfactual Kernel smart account and broadcast by the bundler as UserOperations. | + +## Common wallet operations + +| Task | Guide | Credits | +|------|-------|---------| +| Send ETH, call contracts, or use Wagmi writes | [Send a transaction](/wallets/wallet-api/send-transaction) | 30 credits total in `7702` or `4337`: 10 for the signature and 20 for the bundler broadcast. | +| Sign `personal_sign` messages | [Sign a message](/wallets/wallet-api/sign-message) | 10 credits per wallet signature. | +| Sign EIP-712 typed data | [Sign typed data](/wallets/wallet-api/sign-typed-message) | 10 credits per wallet signature. | +| Let users export wallet material | [Export wallet](/wallets/export) | Uses secure Turnkey iframe flows. | + +## Request confirmation UI + +If you use `@zerodev/wallet-react-kit`, mount `SignatureRequest` to show prebuilt approval UI for signing and transaction requests: + +```tsx +import { SignatureRequest } from '@zerodev/wallet-react-kit' + +export function AppShell({ children }: { children: React.ReactNode }) { + return ( + <> + + {children} + + ) +} +``` + +If you omit `SignatureRequest`, make sure your own product flow still gives users appropriate context before signing messages or sending transactions. diff --git a/docs/pages/wallets/wallet-api/send-transaction.mdx b/docs/pages/wallets/wallet-api/send-transaction.mdx index e77368a..c278fbc 100644 --- a/docs/pages/wallets/wallet-api/send-transaction.mdx +++ b/docs/pages/wallets/wallet-api/send-transaction.mdx @@ -4,25 +4,27 @@ **USE FOR INTERNAL TESTING PURPOSES ONLY.** You may use these features solely for internal evaluation purposes on supported testnets. DO NOT use for production use or share with your users. Wallets created during this preview ("Alpha Wallets") will be discontinued. Any tokens remaining within Alpha Wallets will be permanently lost upon discontinuance. Any mainnet tokens sent to an Alpha Wallet will not be deposited and will be permanently lost when discontinued. We are unable to help recover any lost funds from Alpha Wallets. We provide all previews on an "as is" basis without warranty of any kind, and we may terminate or suspend the availability of any preview at any time. ::: -Use standard Wagmi hooks to send transactions. The ZeroDev connector turns transactions into user operations (AA transactions) behind the scenes, achieving gas abstraction automatically so long as you have enabled it on the ZeroDev dashboard. +Use standard Wagmi hooks to send transactions after the user authenticates. In `7702` and `4337` mode, the ZeroDev connector turns transaction requests into UserOperations behind the scenes and submits them through a bundler. Sponsorship applies automatically when it is enabled on the ZeroDev dashboard. + +The [demo app](https://github.com/zerodevapp/zerodev-wallet-sdk/tree/main/apps/zerodev-signer-demo) uses this same pattern with `useSendTransaction` for ETH transfers and `useWriteContract` for NFT minting. ## `useSendTransaction` ```tsx import { useState } from "react"; -import { parseEther } from "viem"; +import { isAddress, parseEther, type Address } from "viem"; import { useAccount, useSendTransaction } from "wagmi"; export function SendEth() { const { address, isConnected } = useAccount(); - const { sendTransaction, data, isPending } = useSendTransaction(); + const { sendTransaction, data, isPending, error } = useSendTransaction(); const [to, setTo] = useState("0x..."); const [amount, setAmount] = useState("0.001"); const handleSend = () => { - if (!isConnected) return; + if (!isConnected || !isAddress(to)) return; sendTransaction({ - to, + to: to as Address, value: parseEther(amount), }); }; @@ -36,6 +38,7 @@ export function SendEth() { {isPending ? "Sending..." : "Send"} {data &&

Tx hash: {data}

} + {error &&

Transaction failed: {error.message}

} ); } @@ -45,13 +48,13 @@ export function SendEth() { ```tsx import { useWriteContract } from "wagmi"; -import { parseAbi } from "viem"; +import { parseAbi, type Address } from "viem"; const NFT_ABI = parseAbi([ "function mint(address _to) public", ]); -export function MintNftGasless({ recipient }: { recipient: `0x${string}` }) { +export function MintNftGasless({ recipient }: { recipient: Address }) { const { writeContract, data, isPending, error } = useWriteContract(); const mint = () => @@ -73,3 +76,10 @@ export function MintNftGasless({ recipient }: { recipient: `0x${string}` }) { ); } ``` + +## Notes + +- `useSendTransaction` and `useWriteContract` return the submitted transaction hash. +- In `7702` and `4337` mode, your app is interacting with bundler-backed smart-account infrastructure by default. +- In `7702` and `4337` mode, sending a transaction costs 30 credits total: 10 credits for the wallet signature and 20 credits for the bundler broadcast. +- If you mount `SignatureRequest` from `@zerodev/wallet-react-kit`, the kit can prompt the user to review supported signing and transaction requests. diff --git a/docs/pages/wallets/wallet-api/sign-message.mdx b/docs/pages/wallets/wallet-api/sign-message.mdx index c053948..db48834 100644 --- a/docs/pages/wallets/wallet-api/sign-message.mdx +++ b/docs/pages/wallets/wallet-api/sign-message.mdx @@ -4,12 +4,13 @@ **USE FOR INTERNAL TESTING PURPOSES ONLY.** You may use these features solely for internal evaluation purposes on supported testnets. DO NOT use for production use or share with your users. Wallets created during this preview ("Alpha Wallets") will be discontinued. Any tokens remaining within Alpha Wallets will be permanently lost upon discontinuance. Any mainnet tokens sent to an Alpha Wallet will not be deposited and will be permanently lost when discontinued. We are unable to help recover any lost funds from Alpha Wallets. We provide all previews on an "as is" basis without warranty of any kind, and we may terminate or suspend the availability of any preview at any time. ::: -To sign a message, use Wagmi's `useSignMessage` hook: +To sign a message, use Wagmi's `useSignMessage` hook after the user authenticates. The connector routes the signing request to the correct signer for the configured account mode. ```tsx -import { useSignMessage } from "wagmi"; +import { useAccount, useSignMessage } from "wagmi"; export function SignMessage() { + const { address } = useAccount(); const { signMessage, data, isPending, error } = useSignMessage(); const handleSign = () => @@ -19,7 +20,8 @@ export function SignMessage() { return (
- @@ -29,3 +31,7 @@ export function SignMessage() { ); } ``` + +Mount `SignatureRequest` from `@zerodev/wallet-react-kit` if you want the user to review signing requests in a prebuilt confirmation UI. Otherwise, signing can run in the background. + +Each wallet signature costs 10 credits from your existing ZeroDev plan credits. diff --git a/docs/pages/wallets/wallet-api/sign-typed-message.mdx b/docs/pages/wallets/wallet-api/sign-typed-message.mdx index 75af00f..b1d5308 100644 --- a/docs/pages/wallets/wallet-api/sign-typed-message.mdx +++ b/docs/pages/wallets/wallet-api/sign-typed-message.mdx @@ -4,16 +4,17 @@ **USE FOR INTERNAL TESTING PURPOSES ONLY.** You may use these features solely for internal evaluation purposes on supported testnets. DO NOT use for production use or share with your users. Wallets created during this preview ("Alpha Wallets") will be discontinued. Any tokens remaining within Alpha Wallets will be permanently lost upon discontinuance. Any mainnet tokens sent to an Alpha Wallet will not be deposited and will be permanently lost when discontinued. We are unable to help recover any lost funds from Alpha Wallets. We provide all previews on an "as is" basis without warranty of any kind, and we may terminate or suspend the availability of any preview at any time. ::: -EIP-712 typed data signing works through Wagmi's `useSignTypedData`. +EIP-712 typed data signing works through Wagmi's `useSignTypedData`. The connector signs with the wallet signer required by the configured account mode. ```tsx -import { useSignTypedData } from "wagmi"; +import { useAccount, useSignTypedData } from "wagmi"; +import { sepolia } from "wagmi/chains"; const typedData = { domain: { name: "Ether Mail", version: "1", - chainId: 1, + chainId: sepolia.id, verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", }, types: { @@ -42,13 +43,15 @@ const typedData = { } as const; export function SignTypedData() { + const { address } = useAccount(); const { signTypedData, data, isPending, error } = useSignTypedData(); const handleSign = () => signTypedData(typedData); return (
- @@ -58,3 +61,7 @@ export function SignTypedData() { ); } ``` + +If users should approve typed-data signatures in your UI, mount `SignatureRequest` from `@zerodev/wallet-react-kit`. + +Each wallet signature costs 10 credits from your existing ZeroDev plan credits. diff --git a/vocs.config.tsx b/vocs.config.tsx index d1d155a..657050b 100644 --- a/vocs.config.tsx +++ b/vocs.config.tsx @@ -142,6 +142,7 @@ const REDIRECTS: Record = { "/global-address": "/onramp/smart-routing-address", // Embedded Wallet docs live at /wallets/* (alpha — unlisted in sidebars). + "/wallets/evaluate": "/wallets/overview", // React WaaS hooks → Advanced › React Hooks (legacy) "/react/getting-started": "/advanced/react-hooks/getting-started", @@ -741,13 +742,17 @@ export default defineConfig({ // Lives at /wallets/* and is only navigable for users who know the URL. "/wallets": [ { - text: "Embedded Wallet", + text: "Embedded Wallets", collapsed: false, items: [ { text: "Introduction", link: "/wallets", }, + { + text: "Overview", + link: "/wallets/overview", + }, { text: "Quickstart", link: "/wallets/quickstart", @@ -758,62 +763,86 @@ export default defineConfig({ }, { text: "Authentication", - collapsed: true, + collapsed: false, items: [ { - text: "Passkeys", - link: "/wallets/auth/passkeys", - }, - { - text: "Email OTP", - link: "/wallets/auth/email-otp", - }, - { - text: "Magic Link", - link: "/wallets/auth/magic-link", + text: "Overview", + link: "/wallets/auth", }, { - text: "Google OAuth", - link: "/wallets/auth/google-oauth", + text: "Login", + collapsed: true, + items: [ + { + text: "Passkeys", + link: "/wallets/auth/passkeys", + }, + { + text: "Email OTP", + link: "/wallets/auth/email-otp", + }, + { + text: "Magic Link", + link: "/wallets/auth/magic-link", + }, + { + text: "Google OAuth", + link: "/wallets/auth/google-oauth", + }, + ], }, - ], - }, - { - text: "Features", - collapsed: true, - items: [ { text: "Session Management", link: "/wallets/session-management", }, - { - text: "Export Wallet", - link: "/wallets/export", - }, ], }, { - text: "Wallet API", - collapsed: true, + text: "Wallets", + collapsed: false, items: [ { - text: "Send a Transaction", - link: "/wallets/wallet-api/send-transaction", + text: "Overview", + link: "/wallets/wallet-api", }, { - text: "Sign a Message", - link: "/wallets/wallet-api/sign-message", + text: "Features", + collapsed: true, + items: [ + { + text: "Send a Transaction", + link: "/wallets/wallet-api/send-transaction", + }, + { + text: "Sign a Message", + link: "/wallets/wallet-api/sign-message", + }, + { + text: "Sign a Typed Message", + link: "/wallets/wallet-api/sign-typed-message", + }, + ], }, { - text: "Sign a Typed Message", - link: "/wallets/wallet-api/sign-typed-message", + text: "Security", + collapsed: true, + items: [ + { + text: "Export Wallet", + link: "/wallets/export", + }, + ], }, ], }, { - text: "Hooks", + text: "Reference", collapsed: true, items: [ + { + text: "Public API Reference", + link: "/wallets/api-reference", + }, { text: "useRegisterPasskey", link: "/wallets/hooks/use-register-passkey", @@ -843,8 +872,8 @@ export default defineConfig({ link: "/wallets/hooks/use-verify-magic-link", }, { - text: "useGetUserEmail", - link: "/wallets/hooks/use-get-user-email", + text: "useAuthenticators", + link: "/wallets/hooks/use-authenticators", }, { text: "useRefreshSession",