Skip to main content
This page has been updated for Wallets SDK V1. If you are using the previous version, see the previous version docs or the V1 migration guide.
The device signer is the default signer for client-side wallets. It generates a P256 keypair inside the device’s secure hardware — the iOS Secure Enclave, Android Keystore, or the browser’s Web Crypto API — so the private key never leaves the device. Transactions are signed silently, with no OTP or user confirmation required.
Device signers are not currently available for Solana. For Solana wallets, the recovery signer is used as a fallback for signing transactions.

How It Works

When a client-side wallet is created, the SDK automatically generates a device signer unless you explicitly configure a different signer. The flow:
  1. The SDK initializes secure storage on the device (Secure Enclave, Keystore, or a hidden iframe in the browser)
  2. A P256 keypair is generated — the private key is non-extractable and never leaves the hardware boundary
  3. The public key is sent to the Crossmint backend, which registers the device signer on the wallet
  4. Subsequent transactions are signed locally on the device — no OTP, no network round-trips for signing

Browser Architecture

In browser environments, the device signer key lives inside a hidden iframe hosted at crossmint-signer.io — a separate origin from your application. Key generation and signing happen inside the iframe via postMessage. Keys are stored as non-extractable CryptoKey objects in the iframe’s IndexedDB, isolated from the parent page by the browser’s same-origin policy.

Mobile Architecture

On iOS, keys are stored in the Secure Enclave. On Android, keys are stored in the Android Keystore. Both provide hardware-level isolation for the private key material.

Setup

No explicit configuration is needed. When you create a wallet on the client side, a device signer is added automatically.
Using createOnLogin on the provider (recommended for most apps):
import {
    CrossmintProvider,
    CrossmintAuthProvider,
    CrossmintWalletProvider,
} from "@crossmint/client-sdk-react-ui";

function App({ children }) {
    return (
        <CrossmintProvider apiKey="YOUR_CLIENT_API_KEY">
            <CrossmintAuthProvider loginMethods={["email", "google"]}>
                <CrossmintWalletProvider
                    createOnLogin={{
                        chain: "base-sepolia",
                        recovery: { type: "email" },
                        // Device signer is added automatically
                    }}
                >
                    {children}
                </CrossmintWalletProvider>
            </CrossmintAuthProvider>
        </CrossmintProvider>
    );
}
Or using createWallet directly:
import { useWallet } from "@crossmint/client-sdk-react-ui";

const { createWallet } = useWallet();

const wallet = await createWallet({
    chain: "base-sepolia",
    recovery: { type: "email", email: "user@example.com" },
    // signers defaults to [{ type: "device" }] if omitted
});

Signing Transactions

With a device signer, useSigner() is not needed — the device signer is auto-selected as the default. Transactions are signed without any user-facing prompts:
import { useWallet } from "@crossmint/client-sdk-react-ui";

const { wallet } = useWallet();

const { hash, explorerLink } = await wallet.send(
    "0xRecipientAddress",
    "usdc",
    "10"
);

New Device Recovery

Device signers are per-device — they do not sync across devices. When a user accesses their wallet from a new device:
  1. The user authenticates via your app (JWT) and the SDK retrieves the wallet
  2. No local device signer exists on the new device, so wallet.needsRecovery() returns true
  3. On the first transaction, the SDK automatically triggers recovery: the user verifies via their recovery signer (e.g., email OTP) to authorize a new device signer for this device
  4. After recovery, all subsequent transactions on the new device are frictionless again
The previous device’s signer remains valid — each device maintains its own independent signer.

Pre-emptive Recovery

Recovery runs automatically before the first signing operation on a new device. If you want to trigger it earlier (e.g., on app startup to avoid an OTP prompt mid-transaction), call recover() explicitly:
import { useWallet } from "@crossmint/client-sdk-react-ui";

const { wallet } = useWallet();

if (wallet.needsRecovery()) {
    await wallet.recover();
}

Chain Support

ChainDevice Signer Support
EVM (Base, Ethereum, Polygon, etc.)Supported
StellarSupported
SolanaNot currently available
For Solana wallets, use an email or phone recovery signer — it serves as the signing fallback. See Configure Wallet Recovery for setup instructions.

Next Steps

Configure Recovery

Set up email or phone recovery for new device flows

Add Signers

Add passkeys or external wallets as additional signers

Transfer Tokens

Send tokens from your wallet