@kingdomchain/wallet-client 0.6.3 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -103,7 +103,31 @@ See `clients/kingdom-os-reference/README.md` for env vars and usage.
103
103
  - `importEmbeddedBackup(json)` — restore vault from user-held backup file
104
104
  - `hasEmbeddedWallet()` / `clearEmbeddedWallet()` — lifecycle helpers
105
105
 
106
- KDF selection: WebAuthn PRF when available, PBKDF2-passkey fallback (310k iterations — bump to 600k+ before production).
106
+ #### PIN-only vault (0.8.0, recommended for new wallets)
107
+
108
+ Like Exodus: the user remembers **one 6-digit PIN**. Fingerprint / Face ID is the quick door. The **24 words** are the backup.
109
+
110
+ - `createEmbeddedWallet({ pin })` / `recoverEmbeddedWalletFromMnemonic({ mnemonic, pin })` — new vault with kdf `device-pin`.
111
+ - `unlockEmbeddedWallet({ pin })` and `revealEmbeddedRecoveryPhrase({ pin })` — same 5-try timed lockout as the passcode path. A malformed PIN does not count as a try.
112
+ - `setEmbeddedWalletPin({ pin, currentPin? | passcode? })` — change the PIN, or switch an old passcode vault to a PIN once (the passcode then stops working). Can be called without a secret right after a strong unlock. Fingerprint wrap and backup status are kept.
113
+ - `enableEmbeddedPasskeyUnlock({ pin, ... })` works on PIN vaults.
114
+ - `vaultUsesPinUnlock(vault)` — tells the UI which door to show.
115
+ - No backup file: `exportEncryptedBackup` throws and `acknowledgeEmbeddedBackup` returns `null` for PIN vaults. The 24 words are the backup.
116
+
117
+ Security: the vault is encrypted twice — with a PBKDF2-SHA256 (600k) key from the PIN **and** a non-extractable device key in IndexedDB. Copying the vault to another browser does not open it; a new device recovers with the 24 words. Limit (same as Exodus desktop): someone who copies this browser's files off the disk could brute-force 6 digits offline.
118
+
119
+ #### Face ID (passkey) + quick PIN (vault unlock, 0.7.0)
120
+
121
+ - `enableEmbeddedPasskeyUnlock({ userId, userName, displayName, rpId, passcode? })` — **adds** Face ID / fingerprint unlock to a passcode vault (`vault.passkeyWrap`). The passcode keeps working. `passcode` may be omitted right after an unlock in the same page. Pass the registrable domain as `rpId` (e.g. `theholykingdom.io`) so the passkey works on every subdomain.
122
+ - `unlockEmbeddedWalletWithPasskey()` — Face ID unlock (also opens legacy passkey-only vaults).
123
+ - `disableEmbeddedPasskeyUnlock()` — remove Face ID; passcode stays.
124
+ - `revealEmbeddedRecoveryPhrase()` without `passcode` uses Face ID when the vault has a passkey wrap.
125
+ - `setEmbeddedQuickUnlockPin({ pin })` — 6-digit PIN for this browser; only right after a Face ID or passcode unlock. Trivial PINs (000000, 123456, 121212 …) are rejected.
126
+ - `unlockEmbeddedWalletWithPin({ pin })` / `getEmbeddedQuickUnlockStatus()` / `clearEmbeddedQuickUnlockPin()`.
127
+
128
+ Rules: the PIN works for **7 days** after the last Face ID / passcode unlock (PIN unlocks do not extend it); **5 wrong PINs** delete the PIN record; the record is encrypted with a PIN-derived key **and** a non-extractable device key, so it cannot open the vault on any other browser. The PIN is a convenience door: an attacker who copies this browser's files off the disk could brute-force 6 digits offline within the 7-day window — the passcode and passkey records are not weakened by it.
129
+
130
+ KDF selection: passkey unlock **requires** a WebAuthn PRF output. If the device returns none (at registration or on a follow-up assertion) `EmbeddedWalletPasskeyUnsupportedError` is thrown and the vault stays passcode-only. The old `pbkdf2-passkey` fallback derived its key from per-ceremony public data (it neither protected the vault nor reproduced on the next unlock) and is no longer used for new vaults; Safari is no longer excluded by user-agent sniffing (Safari 18+ supports PRF).
107
131
 
108
132
  **Recovery gap (phase 3):** The encrypted backup file requires the original passkey to decrypt. Option D (BIP39 mnemonic export for passkey loss without iCloud/Google sync) is approved in the ADR but **not implemented in phase 2** — see `docs/architecture/ADR-EMBEDDED-WALLET-ONBOARDING.md` § "Known gap: Option D mnemonic recovery".
109
133
 
package/dist/index.d.ts CHANGED
@@ -241,7 +241,13 @@ declare const EMBEDDED_WALLET_VAULT_VERSION = 1;
241
241
  declare const EMBEDDED_WALLET_KDF_PRF: "webauthn-prf";
242
242
  declare const EMBEDDED_WALLET_KDF_PBKDF2: "pbkdf2-passkey";
243
243
  declare const EMBEDDED_WALLET_KDF_PIN: "pbkdf2-pin";
244
- type EmbeddedWalletKdf = typeof EMBEDDED_WALLET_KDF_PRF | typeof EMBEDDED_WALLET_KDF_PBKDF2 | typeof EMBEDDED_WALLET_KDF_PIN;
244
+ /**
245
+ * 6-digit PIN vault (0.8.0): the payload is encrypted with a PBKDF2 key from the
246
+ * PIN and again with a non-extractable device key that never leaves this browser.
247
+ * The 24 words are the only portable backup.
248
+ */
249
+ declare const EMBEDDED_WALLET_KDF_DEVICE_PIN: "device-pin";
250
+ type EmbeddedWalletKdf = typeof EMBEDDED_WALLET_KDF_PRF | typeof EMBEDDED_WALLET_KDF_PBKDF2 | typeof EMBEDDED_WALLET_KDF_PIN | typeof EMBEDDED_WALLET_KDF_DEVICE_PIN;
245
251
  /** How the wallet key relates to its recovery phrase. */
246
252
  declare const EMBEDDED_WALLET_KEY_HD_DERIVED: "hd-derived";
247
253
  declare const EMBEDDED_WALLET_KEY_DIRECT_ENTROPY: "direct-entropy";
@@ -270,6 +276,25 @@ interface EmbeddedWalletRecoveryPhraseReveal {
270
276
  address: string;
271
277
  warnings: EmbeddedWalletRecoveryPhraseWarning[];
272
278
  }
279
+ /**
280
+ * Optional second unlock path for a passcode vault: the same wallet payload,
281
+ * encrypted again under a KEK derived from a WebAuthn PRF output (Face ID /
282
+ * fingerprint passkey). The passcode wrap stays the primary record, so a device
283
+ * or browser without PRF can always fall back to the passcode.
284
+ */
285
+ interface EmbeddedWalletPasskeyWrap {
286
+ /** WebAuthn credential id (base64). */
287
+ credentialId: string;
288
+ /** PRF eval salt (base64). */
289
+ prfSalt: string;
290
+ /** Relying party id the passkey was registered for (e.g. "theholykingdom.io"). */
291
+ rpId?: string;
292
+ /** AES-GCM IV (base64). */
293
+ iv: string;
294
+ /** AES-GCM ciphertext of the wallet payload under the PRF KEK (base64). */
295
+ ciphertext: string;
296
+ createdAt: string;
297
+ }
273
298
  /** Encrypted vault stored in IndexedDB and exported backup files. */
274
299
  interface EmbeddedWalletVaultV1 {
275
300
  version: typeof EMBEDDED_WALLET_VAULT_VERSION;
@@ -288,6 +313,12 @@ interface EmbeddedWalletVaultV1 {
288
313
  backupAcknowledged: boolean;
289
314
  backupAcknowledgedAt?: string;
290
315
  createdAt: string;
316
+ /** Present when Face ID / passkey unlock was added on top of the passcode. */
317
+ passkeyWrap?: EmbeddedWalletPasskeyWrap;
318
+ /** kdf=device-pin only: non-extractable AES-GCM key bound to this browser. */
319
+ deviceKey?: CryptoKey;
320
+ /** kdf=device-pin only: IV (base64) of the outer device-key layer. */
321
+ deviceIv?: string;
291
322
  }
292
323
  interface EmbeddedWalletBackupFileV1 {
293
324
  format: "kingdom-os-embedded-wallet-backup";
@@ -318,6 +349,25 @@ declare class EmbeddedWalletWeakPasscodeError extends Error {
318
349
  readonly reason: PasscodeBlockReason;
319
350
  constructor(reason: PasscodeBlockReason);
320
351
  }
352
+ /**
353
+ * Thrown when this device/browser cannot produce a WebAuthn PRF output, so a
354
+ * passkey cannot protect the vault. Callers keep the passcode path instead.
355
+ */
356
+ declare class EmbeddedWalletPasskeyUnsupportedError extends EmbeddedWalletVaultError {
357
+ readonly code: "passkey_prf_unsupported";
358
+ constructor(message?: string);
359
+ }
360
+ type EmbeddedWalletQuickUnlockErrorCode = "not_set" | "expired" | "wrong_pin" | "wiped" | "invalid_pin" | "no_session";
361
+ /**
362
+ * Quick-unlock (6-digit PIN) failures. `attemptsLeft` is set for `wrong_pin`.
363
+ * `wiped` means too many wrong PINs: the PIN record was deleted and a strong
364
+ * unlock (passkey or passcode) is required.
365
+ */
366
+ declare class EmbeddedWalletQuickUnlockError extends Error {
367
+ readonly code: EmbeddedWalletQuickUnlockErrorCode;
368
+ readonly attemptsLeft?: number;
369
+ constructor(code: EmbeddedWalletQuickUnlockErrorCode, message: string, attemptsLeft?: number);
370
+ }
321
371
 
322
372
  interface EncryptWalletOptions {
323
373
  kdf: EmbeddedWalletKdf;
@@ -332,6 +382,8 @@ interface EncryptWalletOptions {
332
382
  kdfIterations?: number;
333
383
  backupAcknowledged?: boolean;
334
384
  }
385
+ declare function serializePlaintext(payload: EmbeddedWalletPlaintext): Uint8Array;
386
+ declare function deserializePlaintext(bytes: Uint8Array): EmbeddedWalletPlaintext;
335
387
  declare function deriveKekFromPrf(prfOutput: Uint8Array): Promise<Uint8Array>;
336
388
  /** PBKDF2-SHA256 fallback when WebAuthn PRF is unavailable (Safari cross-device QR, etc.). */
337
389
  declare function deriveKekFromPbkdf2(passkeyMaterial: Uint8Array, salt: Uint8Array, iterations?: number): Promise<Uint8Array>;
@@ -341,12 +393,22 @@ declare function encryptWalletPayload(payload: EmbeddedWalletPlaintext, options:
341
393
  declare function decryptWalletPayload(vault: EmbeddedWalletVaultV1, kek: Uint8Array): Promise<EmbeddedWalletPlaintext>;
342
394
  declare function markVaultBackupAcknowledged(vault: EmbeddedWalletVaultV1): EmbeddedWalletVaultV1;
343
395
  declare function assertVaultPermitsTransaction(vault: EmbeddedWalletVaultV1): void;
396
+ /** Encrypt a wallet payload under a raw 32-byte KEK (used for the passkey wrap). */
397
+ declare function encryptPlaintextWithKek(payload: EmbeddedWalletPlaintext, kek: Uint8Array): Promise<{
398
+ iv: string;
399
+ ciphertext: string;
400
+ }>;
401
+ /** Decrypt a payload produced by {@link encryptPlaintextWithKek}. */
402
+ declare function decryptPlaintextWithKek(iv: string, ciphertext: string, kek: Uint8Array): Promise<EmbeddedWalletPlaintext>;
344
403
 
345
404
  /**
346
405
  * WebAuthn PRF capability detection for embedded wallet KEK derivation.
347
406
  *
348
- * Safari does not expose PRF during cross-device QR-code WebAuthn flows — only
349
- * direct iCloud-synced passkeys. Callers must fall back to PBKDF2 in that case.
407
+ * A passkey only protects the vault when the authenticator returns a PRF
408
+ * output (a secret it never reveals otherwise). Without PRF there is no secret
409
+ * to derive a key from, so the wallet keeps the passcode instead — the old
410
+ * "pbkdf2-passkey" mode built its key from per-assertion public data and is no
411
+ * longer used for new vaults.
350
412
  */
351
413
  interface WebAuthnPrfCapabilities {
352
414
  /** Browser exposes PublicKeyCredential. */
@@ -356,7 +418,7 @@ interface WebAuthnPrfCapabilities {
356
418
  /** Human-readable reason when prfLikelySupported is false. */
357
419
  prfFallbackReason?: string;
358
420
  }
359
- /** Best-effort PRF support probe — PBKDF2 fallback remains mandatory. */
421
+ /** Best-effort PRF support probe; the real check is a PRF output at registration. */
360
422
  declare function detectWebAuthnPrfCapabilities(): WebAuthnPrfCapabilities;
361
423
  /** Extract 32-byte PRF output from assertion clientExtensionResults. */
362
424
  declare function readPrfOutput(clientExtensionResults: AuthenticationExtensionsClientOutputs | undefined): Uint8Array | null;
@@ -425,6 +487,23 @@ interface EmbeddedWalletSessionCacheRecord {
425
487
  ciphertext: string;
426
488
  expiresAt: number;
427
489
  }
490
+ /**
491
+ * 6-digit PIN quick-unlock record (see {@link file://./quickUnlock.ts}). The
492
+ * wallet payload is encrypted twice: inside with a PBKDF2 key from the PIN,
493
+ * outside with a non-extractable device key that lives only in this browser.
494
+ */
495
+ interface EmbeddedWalletQuickUnlockRecord {
496
+ deviceKey: CryptoKey;
497
+ publicAddress: string;
498
+ salt: string;
499
+ iterations: number;
500
+ innerIv: string;
501
+ outerIv: string;
502
+ ciphertext: string;
503
+ /** Epoch millis; refreshed only by a passkey or passcode unlock. */
504
+ expiresAt: number;
505
+ failedAttempts: number;
506
+ }
428
507
  interface PasscodeLockoutRecord {
429
508
  failedAttempts: number;
430
509
  lockoutTier: number;
@@ -440,6 +519,9 @@ declare function clearPasscodeLockoutRecord(publicAddress: string): Promise<void
440
519
  declare function saveSessionCacheRecord(record: EmbeddedWalletSessionCacheRecord, options?: VaultStorageOptions): Promise<void>;
441
520
  declare function loadSessionCacheRecord(options?: VaultStorageOptions): Promise<EmbeddedWalletSessionCacheRecord | null>;
442
521
  declare function clearSessionCacheRecord(options?: VaultStorageOptions): Promise<void>;
522
+ declare function saveQuickUnlockRecord(record: EmbeddedWalletQuickUnlockRecord, options?: VaultStorageOptions): Promise<void>;
523
+ declare function loadQuickUnlockRecord(options?: VaultStorageOptions): Promise<EmbeddedWalletQuickUnlockRecord | null>;
524
+ declare function clearQuickUnlockRecord(options?: VaultStorageOptions): Promise<void>;
443
525
 
444
526
  /**
445
527
  * How long a passcode/passkey unlock stays valid across page reloads and new
@@ -520,6 +602,107 @@ declare function normalizePasscode(passcode: string): string;
520
602
  declare function assertValidPasscode(passcode: string): void;
521
603
  declare function deriveKekFromPasscode(passcode: string, salt: Uint8Array, iterations?: number): Promise<Uint8Array>;
522
604
 
605
+ /**
606
+ * 6-digit PIN quick unlock ("Exodus feel").
607
+ *
608
+ * Security model, stated plainly:
609
+ * - The PIN is a convenience door, never the real key. It can only be set right
610
+ * after a strong unlock (passkey/Face ID or the long passcode), and only works
611
+ * on this browser: the payload is also encrypted under a non-extractable
612
+ * device key that no JavaScript can export.
613
+ * - It expires {@link QUICK_UNLOCK_TTL_MS} after the last strong unlock. A PIN
614
+ * unlock does NOT extend it, so Face ID / passcode is needed at least weekly.
615
+ * - {@link QUICK_UNLOCK_MAX_ATTEMPTS} wrong PINs delete the record entirely.
616
+ * - A 6-digit PIN has only 1,000,000 values, so an attacker who copies this
617
+ * browser's storage files off the disk (malware, stolen unlocked laptop) could
618
+ * brute-force it offline. The short expiry bounds that window; the passkey
619
+ * and passcode vault records are not weakened by the PIN.
620
+ */
621
+ declare const QUICK_UNLOCK_PIN_LENGTH = 6;
622
+ /** PIN stays usable for 7 days after the last passkey / passcode unlock. */
623
+ declare const QUICK_UNLOCK_TTL_MS: number;
624
+ /** Wrong PINs allowed before the quick-unlock record is wiped. */
625
+ declare const QUICK_UNLOCK_MAX_ATTEMPTS = 5;
626
+ /** PBKDF2-SHA256 iterations for the PIN-derived inner key. */
627
+ declare const QUICK_UNLOCK_PBKDF2_ITERATIONS = 600000;
628
+ interface EmbeddedQuickUnlockStatus {
629
+ /** A PIN record exists for this scope. */
630
+ enabled: boolean;
631
+ /** The record exists but is past its expiry (strong unlock needed). */
632
+ expired: boolean;
633
+ /** Epoch millis, or null when not enabled. */
634
+ expiresAt: number | null;
635
+ /** Wrong PINs left before the record is wiped. */
636
+ attemptsLeft: number;
637
+ /** Address the PIN opens, or null when not enabled. */
638
+ publicAddress: string | null;
639
+ }
640
+ /** Rejects anything but 6 digits, and PINs that are trivially guessable. */
641
+ declare function assertQuickUnlockPinAllowed(pin: string): void;
642
+ /** Same digit, straight runs (123456 / 654321) or short repeats (121212, 123123). */
643
+ declare function isTrivialPin(pin: string): boolean;
644
+ declare function derivePinKey(pin: string, salt: Uint8Array, iterations: number): Promise<CryptoKey>;
645
+ /**
646
+ * Store a PIN record for the given decrypted payload. Callers must only invoke
647
+ * this right after a strong unlock — wallet.ts enforces that.
648
+ */
649
+ declare function createQuickUnlockRecord(options: VaultStorageOptions & {
650
+ plaintext: EmbeddedWalletPlaintext;
651
+ publicAddress: string;
652
+ pin: string;
653
+ ttlMs?: number;
654
+ iterations?: number;
655
+ }): Promise<EmbeddedWalletQuickUnlockRecord>;
656
+ /**
657
+ * Open the PIN record. The failed-attempt counter is written BEFORE the PIN is
658
+ * checked, so killing the tab mid-check cannot be used to get free guesses.
659
+ */
660
+ declare function openQuickUnlockRecord(options: VaultStorageOptions & {
661
+ pin: string;
662
+ }): Promise<{
663
+ plaintext: EmbeddedWalletPlaintext;
664
+ publicAddress: string;
665
+ }>;
666
+ /** Status for the UI (show PIN pad vs Face ID / passcode). Never throws for "none". */
667
+ declare function getQuickUnlockStatus(options?: VaultStorageOptions): Promise<EmbeddedQuickUnlockStatus>;
668
+ /**
669
+ * Restart the 7-day window after a strong unlock. Only for the same address;
670
+ * a record for another wallet is wiped instead.
671
+ */
672
+ declare function refreshQuickUnlockExpiry(options: VaultStorageOptions & {
673
+ publicAddress: string;
674
+ ttlMs?: number;
675
+ }): Promise<void>;
676
+ declare function clearQuickUnlock(options?: VaultStorageOptions): Promise<void>;
677
+
678
+ /**
679
+ * PIN-only vault (0.8.0) — the one secret a citizen remembers.
680
+ *
681
+ * Two layers, both AES-GCM:
682
+ * inner: key from PBKDF2-SHA256(PIN, salt, 600k)
683
+ * outer: a fresh non-extractable device key stored with the vault in IndexedDB
684
+ * So the vault opens only in this browser AND only with the PIN. Wrong PINs go
685
+ * through the existing timed lockout (5 tries, then 30 s doubling to 15 min).
686
+ *
687
+ * Honest limit: someone who copies this browser's storage files off the disk
688
+ * (malware, stolen unlocked computer) could try all 1,000,000 PINs offline. The
689
+ * 24-word phrase stays the real, portable backup; Face ID / fingerprint
690
+ * (passkey) can be added on top.
691
+ */
692
+ declare const DEVICE_PIN_PBKDF2_ITERATIONS = 600000;
693
+ declare function vaultUsesPinUnlock(vault: EmbeddedWalletVaultV1): boolean;
694
+ declare function encryptPlaintextWithDevicePin(plaintext: EmbeddedWalletPlaintext, pin: string, options: {
695
+ publicAddress: string;
696
+ backupAcknowledged?: boolean;
697
+ iterations?: number;
698
+ }): Promise<EmbeddedWalletVaultV1>;
699
+ /**
700
+ * Decrypt a PIN vault. A wrong PIN throws {@link EmbeddedWalletDecryptError}
701
+ * (counted by the lockout); a malformed PIN throws `invalid_pin` (not counted);
702
+ * a missing / broken device layer throws {@link EmbeddedWalletVaultError}.
703
+ */
704
+ declare function decryptDevicePinVault(vault: EmbeddedWalletVaultV1, pin: string): Promise<EmbeddedWalletPlaintext>;
705
+
523
706
  /** Normalize user input: trim, lowercase, collapse whitespace. */
524
707
  declare function normalizeRecoveryMnemonic(mnemonic: string): string;
525
708
  declare function assertValidRecoveryMnemonic(mnemonic: string): void;
@@ -544,18 +727,27 @@ interface EmbeddedWalletScopeOptions {
544
727
  scopeId?: string;
545
728
  }
546
729
  interface CreateEmbeddedWalletOptions extends EmbeddedWalletScopeOptions {
547
- passcode: string;
730
+ /** Legacy long passcode. Pass `pin` instead for the PIN-only vault (0.8.0). */
731
+ passcode?: string;
732
+ /** 6-digit PIN — creates a device-bound PIN vault (no passcode). */
733
+ pin?: string;
548
734
  /** When true, replaces an existing vault on this account (e.g. an intentional reset). */
549
735
  replaceExisting?: boolean;
550
736
  }
551
737
  interface UnlockEmbeddedWalletOptions extends EmbeddedWalletScopeOptions {
552
738
  passcode?: string;
739
+ /** 6-digit PIN for PIN vaults. */
740
+ pin?: string;
553
741
  }
554
742
  interface RevealEmbeddedRecoveryPhraseOptions extends EmbeddedWalletScopeOptions {
555
743
  passcode?: string;
744
+ pin?: string;
556
745
  }
557
746
  interface RecoverEmbeddedWalletOptions extends EmbeddedWalletScopeOptions {
558
- passcode: string;
747
+ /** Legacy long passcode. Pass `pin` instead for the PIN-only vault. */
748
+ passcode?: string;
749
+ /** 6-digit PIN — the recovered wallet is stored as a PIN vault. */
750
+ pin?: string;
559
751
  mnemonic: string;
560
752
  /** When omitted, inferred from expectedAddress or defaults to hd-derived. */
561
753
  keyType?: EmbeddedWalletKeyType;
@@ -565,8 +757,22 @@ interface RecoverEmbeddedWalletOptions extends EmbeddedWalletScopeOptions {
565
757
  /** When true, replaces an existing vault on this device (e.g. after device loss). */
566
758
  replaceExisting?: boolean;
567
759
  }
760
+ interface SetEmbeddedWalletPinOptions extends EmbeddedWalletScopeOptions {
761
+ /** The new 6-digit PIN. */
762
+ pin: string;
763
+ /** Current PIN (PIN vaults). Optional right after an unlock in this page. */
764
+ currentPin?: string;
765
+ /** Current passcode (switching an old passcode vault to a PIN). */
766
+ passcode?: string;
767
+ }
568
768
  interface EnableEmbeddedPasskeyUnlockOptions extends EmbeddedWalletScopeOptions {
569
- passcode: string;
769
+ /**
770
+ * Passcode to decrypt the vault. May be omitted right after an unlock in this
771
+ * page (the decrypted payload is still in memory for the same scope).
772
+ */
773
+ passcode?: string;
774
+ /** PIN to decrypt a PIN vault (same rule as passcode). */
775
+ pin?: string;
570
776
  userId: Uint8Array;
571
777
  userName: string;
572
778
  displayName: string;
@@ -582,6 +788,12 @@ interface ImportEmbeddedBackupOptions extends EmbeddedWalletScopeOptions {
582
788
  }
583
789
  interface ClearEmbeddedWalletOptions extends EmbeddedWalletScopeOptions {
584
790
  }
791
+ interface SetEmbeddedQuickUnlockPinOptions extends EmbeddedWalletScopeOptions {
792
+ pin: string;
793
+ }
794
+ interface UnlockEmbeddedWalletWithPinOptions extends EmbeddedWalletScopeOptions {
795
+ pin: string;
796
+ }
585
797
  interface AcknowledgeEmbeddedBackupOptions extends EmbeddedWalletScopeOptions {
586
798
  }
587
799
  interface EmbeddedWalletSession {
@@ -591,6 +803,9 @@ interface EmbeddedWalletSession {
591
803
  }
592
804
  declare function vaultUsesPasskeyUnlock(vault: EmbeddedWalletVaultV1): boolean;
593
805
  declare function vaultUsesPasscodeUnlock(vault: EmbeddedWalletVaultV1): boolean;
806
+
807
+ /** True when Face ID / passkey can open this vault (added wrap or legacy passkey vault). */
808
+ declare function vaultHasPasskeyUnlock(vault: EmbeddedWalletVaultV1): boolean;
594
809
  /** Returns true if an encrypted vault exists under the given scope (not the legacy key). */
595
810
  declare function hasEmbeddedWallet(options?: EmbeddedWalletScopeOptions): Promise<boolean>;
596
811
  /** Returns true if an unscoped legacy vault still exists under the "primary" key. */
@@ -621,13 +836,46 @@ declare function recoverEmbeddedWalletFromMnemonic(options: RecoverEmbeddedWalle
621
836
  keyType: EmbeddedWalletKeyType;
622
837
  }>;
623
838
  /**
624
- * Opt-in upgrade: re-wrap an existing passcode vault with passkey unlock.
625
- * Does not change the underlying private key or address.
839
+ * Add Face ID / fingerprint (passkey) unlock to a passcode vault. The passcode
840
+ * keeps working; the same payload is additionally encrypted under a KEK from the
841
+ * passkey's PRF output. Does not change the private key or address.
842
+ *
843
+ * Throws {@link EmbeddedWalletPasskeyUnsupportedError} when the device returns no
844
+ * PRF output (the vault is left unchanged — keep using the passcode).
626
845
  */
627
846
  declare function enableEmbeddedPasskeyUnlock(options: EnableEmbeddedPasskeyUnlockOptions): Promise<{
628
847
  address: string;
629
848
  vault: EmbeddedWalletVaultV1;
630
849
  }>;
850
+ /**
851
+ * Set or change the 6-digit PIN. Turns an old passcode vault into a PIN vault
852
+ * (the passcode stops working), or re-keys an existing PIN vault. Needs the
853
+ * current secret, or an unlock in this page. Keeps Face ID / fingerprint, the
854
+ * backup flag and the address unchanged.
855
+ */
856
+ declare function setEmbeddedWalletPin(options: SetEmbeddedWalletPinOptions): Promise<{
857
+ address: string;
858
+ vault: EmbeddedWalletVaultV1;
859
+ }>;
860
+ /** Remove Face ID / passkey unlock; the passcode keeps working. */
861
+ declare function disableEmbeddedPasskeyUnlock(options?: EmbeddedWalletScopeOptions): Promise<EmbeddedWalletVaultV1>;
862
+ /**
863
+ * Unlock with Face ID / fingerprint. Works for vaults with an added passkey wrap
864
+ * and for legacy passkey-only vaults.
865
+ */
866
+ declare function unlockEmbeddedWalletWithPasskey(options?: EmbeddedWalletScopeOptions): Promise<EmbeddedWalletSession>;
867
+ /**
868
+ * Set (or replace) the 6-digit PIN for quick unlock on this browser. Allowed only
869
+ * while the wallet is open from a passkey or passcode unlock in this page — not
870
+ * from a restored session or a PIN unlock.
871
+ */
872
+ declare function setEmbeddedQuickUnlockPin(options: SetEmbeddedQuickUnlockPinOptions): Promise<EmbeddedQuickUnlockStatus>;
873
+ /** Unlock with the 6-digit PIN. Does not extend the 7-day PIN window. */
874
+ declare function unlockEmbeddedWalletWithPin(options: UnlockEmbeddedWalletWithPinOptions): Promise<EmbeddedWalletSession>;
875
+ /** PIN status for the UI: show the PIN pad, or ask for Face ID / passcode. */
876
+ declare function getEmbeddedQuickUnlockStatus(options?: EmbeddedWalletScopeOptions): Promise<EmbeddedQuickUnlockStatus>;
877
+ /** Remove the PIN from this browser. */
878
+ declare function clearEmbeddedQuickUnlockPin(options?: EmbeddedWalletScopeOptions): Promise<void>;
631
879
  /** Unlock existing vault with passcode (default) or passkey (opt-in upgrade path). */
632
880
  declare function unlockEmbeddedWallet(options?: UnlockEmbeddedWalletOptions): Promise<EmbeddedWalletSession>;
633
881
  /**
@@ -649,7 +897,7 @@ declare function getEmbeddedWalletSession(): EmbeddedWalletSession | null;
649
897
  /** Offline signer for CosmJS; enforces backup gate before signing. */
650
898
  declare function getEmbeddedOfflineSigner(): OfflineDirectSigner;
651
899
  /** Mark backup downloaded/acknowledged and persist. Returns backup file payload. */
652
- declare function acknowledgeEmbeddedBackup(options?: AcknowledgeEmbeddedBackupOptions): Promise<EmbeddedWalletBackupFileV1>;
900
+ declare function acknowledgeEmbeddedBackup(options?: AcknowledgeEmbeddedBackupOptions): Promise<EmbeddedWalletBackupFileV1 | null>;
653
901
  declare function downloadEmbeddedBackupFile(backup: EmbeddedWalletBackupFileV1, filename?: string): void;
654
902
  /** Restore vault from user-held backup file (requires passcode or passkey to decrypt). */
655
903
  declare function importEmbeddedBackup(json: string, options?: ImportEmbeddedBackupOptions): Promise<EmbeddedWalletVaultV1>;
@@ -804,4 +1052,4 @@ interface MessageFns<T> {
804
1052
  fromPartial(object: DeepPartial<T>): T;
805
1053
  }
806
1054
 
807
- export { ATTESTATION_TYPE_CITIZENSHIP_APPLICATION, ATTESTATION_TYPE_RECOVERY, type AcknowledgeEmbeddedBackupOptions, ApplicationStatus, type AttestationPayloadInput, BECH32_PREFIX, type BankBalanceResponse, type BroadcastConfirmOptions, type BroadcastConfirmResult, type BrowserWalletApi, CHAIN_COIN_TYPE, type CacheUnlockedSessionOptions, type ChainCurrency, type ChainInfo, CitizenTxError, type CitizenTxErrorCode, type ClearEmbeddedWalletOptions, type CosmostationWindowApi, type CreateEmbeddedWalletOptions, DEFAULT_CHAIN_ID, DEFAULT_REST_URL, DEFAULT_RPC_URL, EMBEDDED_WALLET_KDF_PBKDF2, EMBEDDED_WALLET_KDF_PIN, EMBEDDED_WALLET_KDF_PRF, EMBEDDED_WALLET_KEY_DIRECT_ENTROPY, EMBEDDED_WALLET_KEY_HD_DERIVED, EMBEDDED_WALLET_PASSCODE_MAX_LENGTH, EMBEDDED_WALLET_PASSCODE_MIN_LENGTH, EMBEDDED_WALLET_PASSKEY_PBKDF2_ITERATIONS, EMBEDDED_WALLET_PIN_PBKDF2_ITERATIONS, EMBEDDED_WALLET_RECOVERY_PHRASE_DIRECT_ENTROPY_WARNING, EMBEDDED_WALLET_RECOVERY_PHRASE_GENERAL_WARNING, EMBEDDED_WALLET_SESSION_TTL_MS, EMBEDDED_WALLET_VAULT_VERSION, type EmbeddedWalletBackupFileV1, EmbeddedWalletBackupRequiredError, EmbeddedWalletDecryptError, type EmbeddedWalletKdf, type EmbeddedWalletKeyType, EmbeddedWalletLockoutError, type EmbeddedWalletPlaintext, type EmbeddedWalletRecoveryPhraseReveal, type EmbeddedWalletRecoveryPhraseWarning, type EmbeddedWalletScopeOptions, type EmbeddedWalletSession, type EmbeddedWalletSessionCacheRecord, EmbeddedWalletVaultError, type EmbeddedWalletVaultV1, EmbeddedWalletWeakPasscodeError, type EnableEmbeddedPasskeyUnlockOptions, type EncryptWalletOptions, type FeeGrantAllowancesResponse, FinalizedReward, type ImportEmbeddedBackupOptions, InvalidChainConfigError, type MigrateLegacyEmbeddedWalletOptions, MsgClaimReward, MsgSubmitApplication, MsgSubmitRecoveryAttestation, PASSCODE_LOCKOUT_FAILURE_THRESHOLD, PASSCODE_LOCKOUT_INITIAL_MS, PASSCODE_LOCKOUT_MAX_MS, type PasscodeBlockReason, type PasscodeLockoutRecord, type PasscodeStrengthLabel, type PasscodeStrengthResult, type PasskeyAssertionResult, type PasskeyRegistrationResult, type RecoverEmbeddedWalletOptions, RecoveryAttestationStatus, RestQueryError, type RevealEmbeddedRecoveryPhraseOptions, RpcConnectionError, type SubmitApplicationParams, UKDR_DECIMALS, UKDR_DISPLAY_DENOM, UKDR_MINIMAL_DENOM, type UnclaimedRewardsResponse, type UnlockEmbeddedWalletOptions, VAULT_LEGACY_KEY, type VaultStorageOptions, WALLETCONNECT_COSMOS_METHODS, WALLETCONNECT_COSMOS_NAMESPACE, WEBAUTHN_RP_NAME, WalletAccountError, type WalletConnectChainConfig, type WalletConnectConnectResult, type WalletConnectMetadata, WalletConnectNotInitializedError, WalletConnectSessionError, WalletConnectSigningError, WalletConnectionError, WalletConnectionRejectedError, WalletNotInstalledError, type WebAuthnPrfCapabilities, WebAuthnUnavailableError, WebAuthnUserCancelledError, acknowledgeEmbeddedBackup, addressFromHdMnemonic, addressFromPrivateKey, assertPasscodeAllowedForCreation, assertPasscodeUnlockAllowed, assertPasskey, assertValidPasscode, assertValidRecoveryMnemonic, assertVaultPermitsTransaction, attestationSignPayload, broadcastAndConfirmTx, buildClaimRewardMsg, buildKingdomChainInfo, buildRequiredCosmosNamespaces, buildSendUkdrMsg, buildSubmitApplicationMsg, cacheUnlockedSession, citizenTxErrorMessage, clearEmbeddedWallet, clearPasscodeLockoutRecord, clearSessionCacheRecord, clearUnlockedSession, clearVaultFromIndexedDb, computePasscodeLockoutDurationMs, connectCosmostation, connectKeplr, connectLeap, connectWalletConnect, createBrowserSigningClient, createEmbeddedWallet, createHdDerivedPlaintext, createKingdomChainRegistry, createPasskeyMaterialFromAssertion, createPbkdf2Salt, createWalletConnectOfflineSigner, decodeCredentialId, decodeDirectEntropyMnemonic, decryptWalletPayload, defaultEmbeddedHdPath, defaultUkdrTxFee, deriveKekFromPasscode, deriveKekFromPbkdf2, deriveKekFromPrf, derivePrivateKeyFromHdMnemonic, detectRecoveryMnemonicKeyType, detectWebAuthnPrfCapabilities, disconnectWalletConnect, downloadEmbeddedBackupFile, embeddedPlaintextToSigner, enableEmbeddedPasskeyUnlock, encodeCredentialId, encodeDirectEntropyMnemonic, encryptWalletPayload, evaluatePasscodeStrength, exportEncryptedBackup, fetchFeeGrantGranter, fetchUkdrBalance, fetchUnclaimedFinalizedRewards, formatUkdrAsKdr, getCosmostationKeplrApi, getEmbeddedOfflineSigner, getEmbeddedWalletSession, getPasscodeLockoutState, hasEmbeddedWallet, hasLegacyEmbeddedWallet, hasMixedCharacterClasses, importEmbeddedBackup, initWalletConnectClient, isCosmostationAvailable, isKeplrAvailable, isLeapAvailable, loadPasscodeLockoutRecord, loadSessionCacheRecord, loadVaultFromIndexedDb, lockEmbeddedWallet, markVaultBackupAcknowledged, migrateLegacyEmbeddedWallet, normalizePasscode, normalizeRecoveryMnemonic, parseEncryptedBackup, parseKdrToUkdr, parseWalletConnectAccount, plaintextFromRecoveryMnemonic, providerSeedFromHex, publicKeyFromPrivateSeed, readPrfOutput, recordPasscodeUnlockFailure, recordPasscodeUnlockSuccess, recoverEmbeddedWalletFromMnemonic, recoveryMnemonicAddress, recoveryMnemonicFromPlaintext, registerPasskey, resolvePlaintextKeyType, resolveVaultStorageKey, restoreCachedSessionPlaintext, restoreWalletConnectSession, revealEmbeddedRecoveryPhrase, savePasscodeLockoutRecord, saveSessionCacheRecord, saveVaultToIndexedDb, selectEmbeddedWalletKdf, serializeBackupFile, signAttestationPayload, signCitizenshipAttestation, suggestAndConnect, toWalletConnectCosmosChainId, tryRestoreUnlockedSession, ukdrCoin, unlockEmbeddedWallet, validateChainConfig, vaultUsesPasscodeUnlock, vaultUsesPasskeyUnlock };
1055
+ export { ATTESTATION_TYPE_CITIZENSHIP_APPLICATION, ATTESTATION_TYPE_RECOVERY, type AcknowledgeEmbeddedBackupOptions, ApplicationStatus, type AttestationPayloadInput, BECH32_PREFIX, type BankBalanceResponse, type BroadcastConfirmOptions, type BroadcastConfirmResult, type BrowserWalletApi, CHAIN_COIN_TYPE, type CacheUnlockedSessionOptions, type ChainCurrency, type ChainInfo, CitizenTxError, type CitizenTxErrorCode, type ClearEmbeddedWalletOptions, type CosmostationWindowApi, type CreateEmbeddedWalletOptions, DEFAULT_CHAIN_ID, DEFAULT_REST_URL, DEFAULT_RPC_URL, DEVICE_PIN_PBKDF2_ITERATIONS, EMBEDDED_WALLET_KDF_DEVICE_PIN, EMBEDDED_WALLET_KDF_PBKDF2, EMBEDDED_WALLET_KDF_PIN, EMBEDDED_WALLET_KDF_PRF, EMBEDDED_WALLET_KEY_DIRECT_ENTROPY, EMBEDDED_WALLET_KEY_HD_DERIVED, EMBEDDED_WALLET_PASSCODE_MAX_LENGTH, EMBEDDED_WALLET_PASSCODE_MIN_LENGTH, EMBEDDED_WALLET_PASSKEY_PBKDF2_ITERATIONS, EMBEDDED_WALLET_PIN_PBKDF2_ITERATIONS, EMBEDDED_WALLET_RECOVERY_PHRASE_DIRECT_ENTROPY_WARNING, EMBEDDED_WALLET_RECOVERY_PHRASE_GENERAL_WARNING, EMBEDDED_WALLET_SESSION_TTL_MS, EMBEDDED_WALLET_VAULT_VERSION, type EmbeddedQuickUnlockStatus, type EmbeddedWalletBackupFileV1, EmbeddedWalletBackupRequiredError, EmbeddedWalletDecryptError, type EmbeddedWalletKdf, type EmbeddedWalletKeyType, EmbeddedWalletLockoutError, EmbeddedWalletPasskeyUnsupportedError, type EmbeddedWalletPasskeyWrap, type EmbeddedWalletPlaintext, EmbeddedWalletQuickUnlockError, type EmbeddedWalletQuickUnlockErrorCode, type EmbeddedWalletQuickUnlockRecord, type EmbeddedWalletRecoveryPhraseReveal, type EmbeddedWalletRecoveryPhraseWarning, type EmbeddedWalletScopeOptions, type EmbeddedWalletSession, type EmbeddedWalletSessionCacheRecord, EmbeddedWalletVaultError, type EmbeddedWalletVaultV1, EmbeddedWalletWeakPasscodeError, type EnableEmbeddedPasskeyUnlockOptions, type EncryptWalletOptions, type FeeGrantAllowancesResponse, FinalizedReward, type ImportEmbeddedBackupOptions, InvalidChainConfigError, type MigrateLegacyEmbeddedWalletOptions, MsgClaimReward, MsgSubmitApplication, MsgSubmitRecoveryAttestation, PASSCODE_LOCKOUT_FAILURE_THRESHOLD, PASSCODE_LOCKOUT_INITIAL_MS, PASSCODE_LOCKOUT_MAX_MS, type PasscodeBlockReason, type PasscodeLockoutRecord, type PasscodeStrengthLabel, type PasscodeStrengthResult, type PasskeyAssertionResult, type PasskeyRegistrationResult, QUICK_UNLOCK_MAX_ATTEMPTS, QUICK_UNLOCK_PBKDF2_ITERATIONS, QUICK_UNLOCK_PIN_LENGTH, QUICK_UNLOCK_TTL_MS, type RecoverEmbeddedWalletOptions, RecoveryAttestationStatus, RestQueryError, type RevealEmbeddedRecoveryPhraseOptions, RpcConnectionError, type SetEmbeddedQuickUnlockPinOptions, type SetEmbeddedWalletPinOptions, type SubmitApplicationParams, UKDR_DECIMALS, UKDR_DISPLAY_DENOM, UKDR_MINIMAL_DENOM, type UnclaimedRewardsResponse, type UnlockEmbeddedWalletOptions, type UnlockEmbeddedWalletWithPinOptions, VAULT_LEGACY_KEY, type VaultStorageOptions, WALLETCONNECT_COSMOS_METHODS, WALLETCONNECT_COSMOS_NAMESPACE, WEBAUTHN_RP_NAME, WalletAccountError, type WalletConnectChainConfig, type WalletConnectConnectResult, type WalletConnectMetadata, WalletConnectNotInitializedError, WalletConnectSessionError, WalletConnectSigningError, WalletConnectionError, WalletConnectionRejectedError, WalletNotInstalledError, type WebAuthnPrfCapabilities, WebAuthnUnavailableError, WebAuthnUserCancelledError, acknowledgeEmbeddedBackup, addressFromHdMnemonic, addressFromPrivateKey, assertPasscodeAllowedForCreation, assertPasscodeUnlockAllowed, assertPasskey, assertQuickUnlockPinAllowed, assertValidPasscode, assertValidRecoveryMnemonic, assertVaultPermitsTransaction, attestationSignPayload, broadcastAndConfirmTx, buildClaimRewardMsg, buildKingdomChainInfo, buildRequiredCosmosNamespaces, buildSendUkdrMsg, buildSubmitApplicationMsg, cacheUnlockedSession, citizenTxErrorMessage, clearEmbeddedQuickUnlockPin, clearEmbeddedWallet, clearPasscodeLockoutRecord, clearQuickUnlock, clearQuickUnlockRecord, clearSessionCacheRecord, clearUnlockedSession, clearVaultFromIndexedDb, computePasscodeLockoutDurationMs, connectCosmostation, connectKeplr, connectLeap, connectWalletConnect, createBrowserSigningClient, createEmbeddedWallet, createHdDerivedPlaintext, createKingdomChainRegistry, createPasskeyMaterialFromAssertion, createPbkdf2Salt, createQuickUnlockRecord, createWalletConnectOfflineSigner, decodeCredentialId, decodeDirectEntropyMnemonic, decryptDevicePinVault, decryptPlaintextWithKek, decryptWalletPayload, defaultEmbeddedHdPath, defaultUkdrTxFee, deriveKekFromPasscode, deriveKekFromPbkdf2, deriveKekFromPrf, derivePinKey, derivePrivateKeyFromHdMnemonic, deserializePlaintext, detectRecoveryMnemonicKeyType, detectWebAuthnPrfCapabilities, disableEmbeddedPasskeyUnlock, disconnectWalletConnect, downloadEmbeddedBackupFile, embeddedPlaintextToSigner, enableEmbeddedPasskeyUnlock, encodeCredentialId, encodeDirectEntropyMnemonic, encryptPlaintextWithDevicePin, encryptPlaintextWithKek, encryptWalletPayload, evaluatePasscodeStrength, exportEncryptedBackup, fetchFeeGrantGranter, fetchUkdrBalance, fetchUnclaimedFinalizedRewards, formatUkdrAsKdr, getCosmostationKeplrApi, getEmbeddedOfflineSigner, getEmbeddedQuickUnlockStatus, getEmbeddedWalletSession, getPasscodeLockoutState, getQuickUnlockStatus, hasEmbeddedWallet, hasLegacyEmbeddedWallet, hasMixedCharacterClasses, importEmbeddedBackup, initWalletConnectClient, isCosmostationAvailable, isKeplrAvailable, isLeapAvailable, isTrivialPin, loadPasscodeLockoutRecord, loadQuickUnlockRecord, loadSessionCacheRecord, loadVaultFromIndexedDb, lockEmbeddedWallet, markVaultBackupAcknowledged, migrateLegacyEmbeddedWallet, normalizePasscode, normalizeRecoveryMnemonic, openQuickUnlockRecord, parseEncryptedBackup, parseKdrToUkdr, parseWalletConnectAccount, plaintextFromRecoveryMnemonic, providerSeedFromHex, publicKeyFromPrivateSeed, readPrfOutput, recordPasscodeUnlockFailure, recordPasscodeUnlockSuccess, recoverEmbeddedWalletFromMnemonic, recoveryMnemonicAddress, recoveryMnemonicFromPlaintext, refreshQuickUnlockExpiry, registerPasskey, resolvePlaintextKeyType, resolveVaultStorageKey, restoreCachedSessionPlaintext, restoreWalletConnectSession, revealEmbeddedRecoveryPhrase, savePasscodeLockoutRecord, saveQuickUnlockRecord, saveSessionCacheRecord, saveVaultToIndexedDb, selectEmbeddedWalletKdf, serializeBackupFile, serializePlaintext, setEmbeddedQuickUnlockPin, setEmbeddedWalletPin, signAttestationPayload, signCitizenshipAttestation, suggestAndConnect, toWalletConnectCosmosChainId, tryRestoreUnlockedSession, ukdrCoin, unlockEmbeddedWallet, unlockEmbeddedWalletWithPasskey, unlockEmbeddedWalletWithPin, validateChainConfig, vaultHasPasskeyUnlock, vaultUsesPasscodeUnlock, vaultUsesPasskeyUnlock, vaultUsesPinUnlock };