Usage
const {
updateWalletEncryption,
updateWalletEncryptionAsync,
data,
isLoading,
isError,
error,
isSuccess,
reset,
} = useUpdateWalletEncryption();
Parameters
The mutation accepts an object with:
| Parameter | Type | Required | Description |
|---|
externalUserId | string | Yes | Your app’s user ID |
newEncryptedPrivateKey | string | Yes | The wallet private key re-encrypted with the new passkey or PIN |
publicKey | string | No | Wallet public key; required when the user has multiple wallets |
bearerToken | string | Yes | Auth token from your provider |
Return Value
| Property | Type | Description |
|---|
updateWalletEncryption | (input) => void | Fire-and-forget update |
updateWalletEncryptionAsync | (input) => Promise<UpdateWalletEncryptionResponse> | Promise-based update |
data | UpdateWalletEncryptionResponse | undefined | Response from the API |
isLoading | boolean | True while the update is in progress |
isError | boolean | True if the update failed |
isSuccess | boolean | True if the update succeeded |
error | Error | null | Error details |
reset | () => void | Reset mutation state |
UpdateWalletEncryptionResponse
| Property | Type | Description |
|---|
success | boolean | Whether the update succeeded |
How It Works
- The client re-encrypts the wallet private key with a new passkey or PIN
updateWalletEncryption sends the new ciphertext to the Chipi backend
- The backend replaces the stored encrypted private key
- The wallet query cache is automatically invalidated so subsequent reads return fresh data
After calling this hook, any previous passkey or PIN used to encrypt the
private key will no longer work. Make sure the new encryption is persisted
before discarding the old key material.
Example Implementation
import { useUpdateWalletEncryption } from "@chipi-stack/chipi-react";
import { usePasskeySetup } from "@chipi-stack/chipi-passkey/hooks";
import { useAuth } from "@clerk/nextjs";
export function RotatePasskey({ wallet, userId }: {
wallet: WalletData;
userId: string;
}) {
const { getToken } = useAuth();
const { setupPasskey } = usePasskeySetup();
const {
updateWalletEncryptionAsync,
isLoading,
isSuccess,
error,
} = useUpdateWalletEncryption();
const handleRotate = async () => {
const bearerToken = await getToken();
if (!bearerToken) return;
try {
// Create a new passkey (triggers biometric prompt)
const newEncryptKey = await setupPasskey();
// Re-encrypt the private key client-side with the new key.
// Keep plaintext key material in-memory only — never log or persist it.
const newEncryptedPrivateKey = encrypt(
wallet.privateKey,
newEncryptKey
);
await updateWalletEncryptionAsync({
externalUserId: userId,
newEncryptedPrivateKey,
publicKey: wallet.publicKey,
bearerToken,
});
alert("Passkey rotated successfully!");
} catch (err) {
console.error("Failed to rotate passkey:", err);
}
};
return (
<div>
<button onClick={handleRotate} disabled={isLoading}>
{isLoading ? "Updating..." : "Rotate Passkey"}
</button>
{isSuccess && <p>Encryption updated.</p>}
{error && <p>Error: {error.message}</p>}
</div>
);
}