Skip to main content

Usage

const {
  migrateWalletToPasskey,
  migrateWalletToPasskeyAsync,
  data,
  isLoading,
  isError,
  isSuccess,
  error,
  reset,
} = useMigrateWalletToPasskey();

Parameters

The mutation accepts an object with:
ParameterTypeRequiredDescription
walletWalletDataYesThe current wallet object (publicKey + encryptedPrivateKey)
oldEncryptKeystringYesThe user’s current PIN used to decrypt the wallet
externalUserIdstringYesYour app’s user ID — used as the passkey username
bearerTokenstringYesAuth token from your provider

Return Value

PropertyTypeDescription
migrateWalletToPasskey(input) => voidFire-and-forget migration
migrateWalletToPasskeyAsync(input) => Promise<MigrateWalletToPasskeyResult>Promise-based migration
dataMigrateWalletToPasskeyResult | undefinedMigration result
isLoadingbooleanTrue while migrating
isErrorbooleanTrue if migration failed
isSuccessbooleanTrue if migration succeeded
errorError | nullError details
reset() => voidReset mutation state

MigrateWalletToPasskeyResult

PropertyTypeDescription
successbooleanWhether migration succeeded
walletWalletDataUpdated wallet with new passkey-encrypted private key
credentialIdstringThe passkey credential ID (store this for future auth)

How It Works

  1. A new passkey is created in the browser/device (triggers biometric prompt)
  2. The wallet’s private key is decrypted using oldEncryptKey (the PIN)
  3. The private key is re-encrypted using the passkey-derived key
  4. The updated wallet is returned — persist the new wallet object and credentialId
After migration, the old PIN (oldEncryptKey) will no longer work. Store the updated wallet object and credentialId securely.

Example Implementation

import { useMigrateWalletToPasskey } from "@chipi-stack/chipi-react";
import { useAuth } from "@clerk/nextjs";
import { useState } from "react";

export function MigrateToPasskey({ currentWallet, userId }: {
  currentWallet: WalletData;
  userId: string;
}) {
  const { getToken } = useAuth();
  const [currentPin, setCurrentPin] = useState("");
  const { migrateWalletToPasskeyAsync, isLoading, error } = useMigrateWalletToPasskey();

  const handleMigrate = async () => {
    const bearerToken = await getToken();
    if (!bearerToken) return;

    try {
      const result = await migrateWalletToPasskeyAsync({
        wallet: currentWallet,
        oldEncryptKey: currentPin,
        externalUserId: userId,
        bearerToken,
      });

      // Persist the updated wallet and credentialId
      localStorage.setItem("wallet", JSON.stringify(result.wallet));
      localStorage.setItem("passkeyCredentialId", result.credentialId);

      alert("Successfully migrated to passkey!");
    } catch (err) {
      console.error("Migration failed:", err);
    }
  };

  return (
    <div>
      <input
        type="password"
        placeholder="Current PIN"
        value={currentPin}
        onChange={(e) => setCurrentPin(e.target.value)}
      />
      <button onClick={handleMigrate} disabled={isLoading}>
        {isLoading ? "Migrating..." : "Migrate to Passkey"}
      </button>
      {error && <p>Error: {error.message}</p>}
    </div>
  );
}
  • Use Passkeys Guide — Full passkey setup walkthrough
  • useCreateWallet — Create a wallet with passkey from the start (set usePasskey: true)