Usage
const {
migrateWalletToPasskey,
migrateWalletToPasskeyAsync,
data,
isLoading,
isError,
isSuccess,
error,
reset,
} = useMigrateWalletToPasskey();
Parameters
The mutation accepts an object with:
| Parameter | Type | Required | Description |
|---|
wallet | WalletData | Yes | The current wallet object (publicKey + encryptedPrivateKey) |
oldEncryptKey | string | Yes | The user’s current PIN used to decrypt the wallet |
externalUserId | string | Yes | Your app’s user ID — used as the passkey username |
bearerToken | string | Yes | Auth token from your provider |
Return Value
| Property | Type | Description |
|---|
migrateWalletToPasskey | (input) => void | Fire-and-forget migration |
migrateWalletToPasskeyAsync | (input) => Promise<MigrateWalletToPasskeyResult> | Promise-based migration |
data | MigrateWalletToPasskeyResult | undefined | Migration result |
isLoading | boolean | True while migrating |
isError | boolean | True if migration failed |
isSuccess | boolean | True if migration succeeded |
error | Error | null | Error details |
reset | () => void | Reset mutation state |
MigrateWalletToPasskeyResult
| Property | Type | Description |
|---|
success | boolean | Whether migration succeeded |
wallet | WalletData | Updated wallet with new passkey-encrypted private key |
credentialId | string | The passkey credential ID (store this for future auth) |
How It Works
- A new passkey is created in the browser/device (triggers biometric prompt)
- The wallet’s private key is decrypted using
oldEncryptKey (the PIN)
- The private key is re-encrypted using the passkey-derived key
- 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)