@kingdomchain/wallet-client 0.2.0 → 0.4.3

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
@@ -1,130 +1,130 @@
1
- # Kingdom Chain TypeScript / CosmJS client
2
-
3
- Self-contained TypeScript package for constructing, signing, and broadcasting Kingdom Chain transactions via [CosmJS](https://github.com/cosmos/cosm-js). Includes a Keplr / Leap / Cosmostation browser wallet connector for non-custodial signing.
4
-
5
- ## Scope (current)
6
-
7
- - **MsgSubmitApplication** (citizenship application)
8
- - **MsgSubmitRecoveryAttestation** (wallet recovery attestation)
9
- - **Browser wallet connector** (`src/browserSigner.ts`) — Keplr / Leap / Cosmostation via `experimentalSuggestChain` + `OfflineDirectSigner`
10
- - **WalletConnect mobile signer** (`src/walletConnectSigner.ts`) — WalletConnect v2 `OfflineDirectSigner` adapter
11
- - **Embedded passkey wallet** (`src/embeddedWallet/`) — WebAuthn PRF + PBKDF2 fallback, IndexedDB vault, mandatory backup gate
12
- - Ed25519 attestation signing ported from `x/compliance/types/attestation.go`
13
- - Proto codegen via `ts-proto` + `buf`
14
-
15
- ## Setup
16
-
17
- ```bash
18
- cd clients/ts
19
- npm install
20
- npm run codegen # regenerate from ../../proto (requires buf + repo root)
21
- ```
22
-
23
- ## Testing
24
-
25
- ```bash
26
- cd clients/ts
27
- npm test # unit + property fuzz (1500 cross-lang iterations)
28
- npm run test:mutate # Stryker mutation testing on attestation.ts + registry.ts
29
- ```
30
-
31
- Cross-language fuzz compares TS `attestationSignPayload` against a compiled Go helper (`payload_attestation.go`) for 1000 citizenship + 500 recovery inputs.
32
-
33
- ## Live E2E (Ona devnet)
34
-
35
- Requires a running node at `~/.kingdomchain-e2e` (see `scripts/citizen-compliance-e2e/run.sh`).
36
-
37
- ```bash
38
- # Build CLI if needed
39
- go build -o /tmp/kingdomchaind ./cmd/kingdomchaind
40
-
41
- cd clients/ts
42
- npm run e2e:submit-application
43
- npm run e2e:submit-recovery
44
- ```
45
-
46
- Environment overrides:
47
-
48
- | Variable | Default |
49
- |----------|---------|
50
- | `CHAIN_ID` | `kingdomchain-e2e` |
51
- | `RPC_URL` | `http://127.0.0.1:26657` |
52
- | `REST_URL` | `http://127.0.0.1:1317` |
53
- | `HOME_DIR` | `~/.kingdomchain-e2e` |
54
- | `KINGDOMCHAIND` | `/tmp/kingdomchaind` |
55
- | `PROVIDER_SEED_HEX` | devnet provider-alpha seed from e2e script |
56
-
57
- ## Browser wallet demo (Keplr / Leap)
58
-
59
- Manual click-through demo for Isa — **not automated in CI**. Unit tests mock `window.keplr` / `window.leap`; the demo requires a real extension in your browser.
60
-
61
- 1. Start the Ona devnet (`scripts/citizen-compliance-e2e/run.sh` or your existing node).
62
- 2. Fund the Keplr/Leap account you will connect with (`ukdr` on `kingdomchain-e2e`).
63
- 3. From `clients/ts`:
64
-
65
- ```bash
66
- npm run demo
67
- ```
68
-
69
- 4. Open the printed URL (default `http://localhost:5173`) in a browser with Keplr or Leap installed.
70
- 5. Click **Connect Keplr** or **Connect Leap**, approve the chain suggestion + connection, then **Submit test application**.
71
-
72
- Optional Vite env overrides (prefix `VITE_`):
73
-
74
- | Variable | Default |
75
- |----------|---------|
76
- | `VITE_CHAIN_ID` | `kingdomchain-e2e` |
77
- | `VITE_RPC_URL` | `http://127.0.0.1:26657` |
78
- | `VITE_REST_URL` | `http://127.0.0.1:1317` |
79
- | `VITE_PROVIDER_ID` | `provider-alpha` |
80
- | `VITE_POLICY_VERSION` | `policy-v1` |
81
- | `VITE_PROVIDER_SEED_HEX` | devnet provider-alpha seed |
82
-
83
- The demo signs the provider attestation off-chain (devnet seed) and broadcasts `MsgSubmitApplication` using your connected wallet as the on-chain tx signer.
84
-
85
- ## Kingdom OS / Base44 reference wiring
86
-
87
- Copy-paste React integration lives in `clients/kingdom-os-reference/`:
88
-
89
- - `useKingdomWallet.js` — unified hook for Keplr, Leap, Cosmostation, WalletConnect, embedded passkey
90
- - `WalletStep.jsx` — onboarding UI with side-by-side connect buttons
91
- - `WalletConnectQrModal.jsx` — QR pairing modal (`VITE_WC_PROJECT_ID` required)
92
- - `EmbeddedWalletPanel.jsx` — create / unlock / mandatory backup gate
93
-
94
- See `clients/kingdom-os-reference/README.md` for env vars and usage.
95
-
96
- ### Embedded wallet API (`src/embeddedWallet/`)
97
-
98
- - `createEmbeddedWallet({ userId, userName, displayName, rpId? })` — register passkey + encrypt vault in IndexedDB
99
- - `unlockEmbeddedWallet()` — passkey assertion → in-memory session
100
- - `getEmbeddedOfflineSigner()` — CosmJS signer; throws `EmbeddedWalletBackupRequiredError` until backup acknowledged
101
- - `acknowledgeEmbeddedBackup()` / `downloadEmbeddedBackupFile()` — mandatory backup gate
102
- - `importEmbeddedBackup(json)` — restore vault from user-held backup file
103
- - `hasEmbeddedWallet()` / `clearEmbeddedWallet()` — lifecycle helpers
104
-
105
- KDF selection: WebAuthn PRF when available, PBKDF2-passkey fallback (310k iterations — bump to 600k+ before production).
106
-
107
- **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".
108
-
109
- ### WalletConnect API (`src/walletConnectSigner.ts`)
110
-
111
- - `initWalletConnectClient({ chainId, projectId, metadata })`
112
- - `connectWalletConnect(client, chainId)` → `{ uri, session, signer }` (display `uri` as QR)
113
- - `restoreWalletConnectSession(client, chainId)` — reconnect on page load
114
- - `createWalletConnectOfflineSigner(client, session, chainId)`
115
- - `disconnectWalletConnect(client, session)`
116
-
117
- ### Browser wallet API (`src/browserSigner.ts`)
118
-
119
- - `isKeplrAvailable()` / `isLeapAvailable()`
120
- - `buildKingdomChainInfo(chainId, rpcUrl, restUrl)` — `ukdr` / KDR with **6 decimals** (`x/supply/types/constants.go`, `TOKENOMICS_COSMOS_SPEC.md`), bech32 prefix `king`, coin type 118
121
- - `connectKeplr(chainInfo)` / `connectLeap(chainInfo)` — suggest chain + enable + offline signer
122
- - `createBrowserSigningClient(rpcUrl, signer)` — `SigningStargateClient` + `createKingdomChainRegistry()`
123
-
124
- ## Encoding notes (for Base44 frontend)
125
-
126
- 1. **Custom protobuf registry** — CosmJS `SigningStargateClient` must be constructed with a `Registry` that includes Kingdom Chain message type URLs (e.g. `/isatheword.kingdomchain.compliance.v1.MsgSubmitApplication`) alongside `defaultRegistryTypes`.
127
- 2. **Attestation signing is off-chain** — providers sign a canonical SHA256-payload (not the tx bytes). The TS port in `src/attestation.ts` must match Go `AttestationSignPayload` exactly; the E2E script cross-checks against `scripts/citizen-compliance-e2e/sign_attestation.go`.
128
- 3. **Timestamps** — proto `google.protobuf.Timestamp` fields (`issued_at`, `expires_at`) are `Date` in generated TS; encoding uses `@bufbuild/protobuf` wire format.
129
- 4. **Uint64 fields** — `provider_key_version` is `bigint` in generated types.
130
- 5. **Tx signer** — `MsgSubmitApplication` signer is `applicant_wallet`; the fee payer / signer must be the applicant's secp256k1 account (`king` bech32 prefix, coin type 118).
1
+ # Kingdom Chain TypeScript / CosmJS client
2
+
3
+ Self-contained TypeScript package for constructing, signing, and broadcasting Kingdom Chain transactions via [CosmJS](https://github.com/cosmos/cosm-js). Includes a Keplr / Leap / Cosmostation browser wallet connector for non-custodial signing.
4
+
5
+ ## Scope (current)
6
+
7
+ - **MsgSubmitApplication** (citizenship application)
8
+ - **MsgSubmitRecoveryAttestation** (wallet recovery attestation)
9
+ - **Browser wallet connector** (`src/browserSigner.ts`) — Keplr / Leap / Cosmostation via `experimentalSuggestChain` + `OfflineDirectSigner`
10
+ - **WalletConnect mobile signer** (`src/walletConnectSigner.ts`) — WalletConnect v2 `OfflineDirectSigner` adapter
11
+ - **Embedded passkey wallet** (`src/embeddedWallet/`) — WebAuthn PRF + PBKDF2 fallback, IndexedDB vault, mandatory backup gate
12
+ - Ed25519 attestation signing ported from `x/compliance/types/attestation.go`
13
+ - Proto codegen via `ts-proto` + `buf`
14
+
15
+ ## Setup
16
+
17
+ ```bash
18
+ cd clients/ts
19
+ npm install
20
+ npm run codegen # regenerate from ../../proto (requires buf + repo root)
21
+ ```
22
+
23
+ ## Testing
24
+
25
+ ```bash
26
+ cd clients/ts
27
+ npm test # unit + property fuzz (1500 cross-lang iterations)
28
+ npm run test:mutate # Stryker mutation testing on attestation.ts + registry.ts
29
+ ```
30
+
31
+ Cross-language fuzz compares TS `attestationSignPayload` against a compiled Go helper (`payload_attestation.go`) for 1000 citizenship + 500 recovery inputs.
32
+
33
+ ## Live E2E (Ona devnet)
34
+
35
+ Requires a running node at `~/.kingdomchain-e2e` (see `scripts/citizen-compliance-e2e/run.sh`).
36
+
37
+ ```bash
38
+ # Build CLI if needed
39
+ go build -o /tmp/kingdomchaind ./cmd/kingdomchaind
40
+
41
+ cd clients/ts
42
+ npm run e2e:submit-application
43
+ npm run e2e:submit-recovery
44
+ ```
45
+
46
+ Environment overrides:
47
+
48
+ | Variable | Default |
49
+ |----------|---------|
50
+ | `CHAIN_ID` | `kingdomchain-e2e` |
51
+ | `RPC_URL` | `http://127.0.0.1:26657` |
52
+ | `REST_URL` | `http://127.0.0.1:1317` |
53
+ | `HOME_DIR` | `~/.kingdomchain-e2e` |
54
+ | `KINGDOMCHAIND` | `/tmp/kingdomchaind` |
55
+ | `PROVIDER_SEED_HEX` | devnet provider-alpha seed from e2e script |
56
+
57
+ ## Browser wallet demo (Keplr / Leap)
58
+
59
+ Manual click-through demo for Isa — **not automated in CI**. Unit tests mock `window.keplr` / `window.leap`; the demo requires a real extension in your browser.
60
+
61
+ 1. Start the Ona devnet (`scripts/citizen-compliance-e2e/run.sh` or your existing node).
62
+ 2. Fund the Keplr/Leap account you will connect with (`ukdr` on `kingdomchain-e2e`).
63
+ 3. From `clients/ts`:
64
+
65
+ ```bash
66
+ npm run demo
67
+ ```
68
+
69
+ 4. Open the printed URL (default `http://localhost:5173`) in a browser with Keplr or Leap installed.
70
+ 5. Click **Connect Keplr** or **Connect Leap**, approve the chain suggestion + connection, then **Submit test application**.
71
+
72
+ Optional Vite env overrides (prefix `VITE_`):
73
+
74
+ | Variable | Default |
75
+ |----------|---------|
76
+ | `VITE_CHAIN_ID` | `kingdomchain-e2e` |
77
+ | `VITE_RPC_URL` | `http://127.0.0.1:26657` |
78
+ | `VITE_REST_URL` | `http://127.0.0.1:1317` |
79
+ | `VITE_PROVIDER_ID` | `provider-alpha` |
80
+ | `VITE_POLICY_VERSION` | `policy-v1` |
81
+ | `VITE_PROVIDER_SEED_HEX` | devnet provider-alpha seed |
82
+
83
+ The demo signs the provider attestation off-chain (devnet seed) and broadcasts `MsgSubmitApplication` using your connected wallet as the on-chain tx signer.
84
+
85
+ ## Kingdom OS / Base44 reference wiring
86
+
87
+ Copy-paste React integration lives in `clients/kingdom-os-reference/`:
88
+
89
+ - `useKingdomWallet.js` — unified hook for Keplr, Leap, Cosmostation, WalletConnect, embedded passkey
90
+ - `WalletStep.jsx` — onboarding UI with side-by-side connect buttons
91
+ - `WalletConnectQrModal.jsx` — QR pairing modal (`VITE_WC_PROJECT_ID` required)
92
+ - `EmbeddedWalletPanel.jsx` — create / unlock / mandatory backup gate
93
+
94
+ See `clients/kingdom-os-reference/README.md` for env vars and usage.
95
+
96
+ ### Embedded wallet API (`src/embeddedWallet/`)
97
+
98
+ - `createEmbeddedWallet({ userId, userName, displayName, rpId? })` — register passkey + encrypt vault in IndexedDB
99
+ - `unlockEmbeddedWallet()` — passkey assertion → in-memory session
100
+ - `getEmbeddedOfflineSigner()` — CosmJS signer; throws `EmbeddedWalletBackupRequiredError` until backup acknowledged
101
+ - `acknowledgeEmbeddedBackup()` / `downloadEmbeddedBackupFile()` — mandatory backup gate
102
+ - `importEmbeddedBackup(json)` — restore vault from user-held backup file
103
+ - `hasEmbeddedWallet()` / `clearEmbeddedWallet()` — lifecycle helpers
104
+
105
+ KDF selection: WebAuthn PRF when available, PBKDF2-passkey fallback (310k iterations — bump to 600k+ before production).
106
+
107
+ **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".
108
+
109
+ ### WalletConnect API (`src/walletConnectSigner.ts`)
110
+
111
+ - `initWalletConnectClient({ chainId, projectId, metadata })`
112
+ - `connectWalletConnect(client, chainId)` → `{ uri, session, signer }` (display `uri` as QR)
113
+ - `restoreWalletConnectSession(client, chainId)` — reconnect on page load
114
+ - `createWalletConnectOfflineSigner(client, session, chainId)`
115
+ - `disconnectWalletConnect(client, session)`
116
+
117
+ ### Browser wallet API (`src/browserSigner.ts`)
118
+
119
+ - `isKeplrAvailable()` / `isLeapAvailable()`
120
+ - `buildKingdomChainInfo(chainId, rpcUrl, restUrl)` — `ukdr` / KDR with **6 decimals** (`x/supply/types/constants.go`, `TOKENOMICS_COSMOS_SPEC.md`), bech32 prefix `king`, coin type 118
121
+ - `connectKeplr(chainInfo)` / `connectLeap(chainInfo)` — suggest chain + enable + offline signer
122
+ - `createBrowserSigningClient(rpcUrl, signer)` — `SigningStargateClient` + `createKingdomChainRegistry()`
123
+
124
+ ## Encoding notes (for Base44 frontend)
125
+
126
+ 1. **Custom protobuf registry** — CosmJS `SigningStargateClient` must be constructed with a `Registry` that includes Kingdom Chain message type URLs (e.g. `/isatheword.kingdomchain.compliance.v1.MsgSubmitApplication`) alongside `defaultRegistryTypes`.
127
+ 2. **Attestation signing is off-chain** — providers sign a canonical SHA256-payload (not the tx bytes). The TS port in `src/attestation.ts` must match Go `AttestationSignPayload` exactly; the E2E script cross-checks against `scripts/citizen-compliance-e2e/sign_attestation.go`.
128
+ 3. **Timestamps** — proto `google.protobuf.Timestamp` fields (`issued_at`, `expires_at`) are `Date` in generated TS; encoding uses `@bufbuild/protobuf` wire format.
129
+ 4. **Uint64 fields** — `provider_key_version` is `bigint` in generated types.
130
+ 5. **Tx signer** — `MsgSubmitApplication` signer is `applicant_wallet`; the fee payer / signer must be the applicant's secp256k1 account (`king` bech32 prefix, coin type 118).
package/dist/index.d.ts CHANGED
@@ -240,12 +240,35 @@ declare function restoreWalletConnectSession(client: SignClient, chainId: string
240
240
  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
- type EmbeddedWalletKdf = typeof EMBEDDED_WALLET_KDF_PRF | typeof EMBEDDED_WALLET_KDF_PBKDF2;
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;
245
+ /** How the wallet key relates to its recovery phrase. */
246
+ declare const EMBEDDED_WALLET_KEY_HD_DERIVED: "hd-derived";
247
+ declare const EMBEDDED_WALLET_KEY_DIRECT_ENTROPY: "direct-entropy";
248
+ type EmbeddedWalletKeyType = typeof EMBEDDED_WALLET_KEY_HD_DERIVED | typeof EMBEDDED_WALLET_KEY_DIRECT_ENTROPY;
244
249
  /** Plaintext wallet payload — never persisted unencrypted. */
245
250
  interface EmbeddedWalletPlaintext {
246
251
  privateKey: Uint8Array;
247
252
  hdPath: string;
248
253
  createdAt: string;
254
+ /** Omitted in pre-phase-3 vaults; treated as direct-entropy when absent. */
255
+ keyType?: EmbeddedWalletKeyType;
256
+ /** Stored for hd-derived wallets so the phrase can be revealed later. */
257
+ mnemonic?: string;
258
+ }
259
+ /** Warnings shown before revealing a recovery phrase. */
260
+ interface EmbeddedWalletRecoveryPhraseWarning {
261
+ id: "general" | "direct-entropy-portability";
262
+ title: string;
263
+ body: string;
264
+ }
265
+ declare const EMBEDDED_WALLET_RECOVERY_PHRASE_GENERAL_WARNING: EmbeddedWalletRecoveryPhraseWarning;
266
+ declare const EMBEDDED_WALLET_RECOVERY_PHRASE_DIRECT_ENTROPY_WARNING: EmbeddedWalletRecoveryPhraseWarning;
267
+ interface EmbeddedWalletRecoveryPhraseReveal {
268
+ mnemonic: string;
269
+ keyType: EmbeddedWalletKeyType;
270
+ address: string;
271
+ warnings: EmbeddedWalletRecoveryPhraseWarning[];
249
272
  }
250
273
  /** Encrypted vault stored in IndexedDB and exported backup files. */
251
274
  interface EmbeddedWalletVaultV1 {
@@ -278,16 +301,35 @@ declare class EmbeddedWalletBackupRequiredError extends Error {
278
301
  declare class EmbeddedWalletVaultError extends Error {
279
302
  constructor(message: string, cause?: unknown);
280
303
  }
304
+ /** Thrown when AES-GCM decryption fails (wrong KEK or tampered ciphertext). */
305
+ declare class EmbeddedWalletDecryptError extends EmbeddedWalletVaultError {
306
+ readonly code: "decrypt_failed";
307
+ constructor(cause?: unknown);
308
+ }
309
+ type PasscodeStrengthLabel = "weak" | "fair" | "good" | "strong";
310
+ type PasscodeBlockReason = "denylist" | "too_weak" | "numeric_short";
311
+ /** Thrown when too many failed passcode unlock attempts trigger a timed lockout. */
312
+ declare class EmbeddedWalletLockoutError extends Error {
313
+ readonly retryAfterMs: number;
314
+ constructor(retryAfterMs: number);
315
+ }
316
+ /** Thrown when a passcode is too weak or on the common-password denylist. */
317
+ declare class EmbeddedWalletWeakPasscodeError extends Error {
318
+ readonly reason: PasscodeBlockReason;
319
+ constructor(reason: PasscodeBlockReason);
320
+ }
281
321
 
282
322
  interface EncryptWalletOptions {
283
323
  kdf: EmbeddedWalletKdf;
284
324
  credentialId: Uint8Array;
285
325
  publicAddress: string;
286
326
  kek: Uint8Array;
287
- /** Required when kdf=pbkdf2-passkey */
327
+ /** Required when kdf=pbkdf2-passkey or kdf=pbkdf2-pin */
288
328
  kdfSalt?: Uint8Array;
289
329
  /** Required when kdf=webauthn-prf */
290
330
  prfSalt?: Uint8Array;
331
+ /** Override PBKDF2 iterations (defaults vary by kdf type). */
332
+ kdfIterations?: number;
291
333
  backupAcknowledged?: boolean;
292
334
  }
293
335
  declare function deriveKekFromPrf(prfOutput: Uint8Array): Promise<Uint8Array>;
@@ -365,41 +407,190 @@ declare function decodeCredentialId(encoded: string): Uint8Array;
365
407
  declare function embeddedPlaintextToSigner(plaintext: EmbeddedWalletPlaintext, prefix?: string): Promise<DirectSecp256k1Wallet>;
366
408
  declare function defaultEmbeddedHdPath(): string;
367
409
 
368
- declare function saveVaultToIndexedDb(vault: EmbeddedWalletVaultV1): Promise<void>;
369
- declare function loadVaultFromIndexedDb(): Promise<EmbeddedWalletVaultV1 | null>;
370
- declare function clearVaultFromIndexedDb(): Promise<void>;
410
+ /** Legacy unscoped vault key used before per-account scoping shipped. */
411
+ declare const VAULT_LEGACY_KEY = "primary";
412
+ interface VaultStorageOptions {
413
+ /** When set, vault is stored under this account-scoped key instead of {@link VAULT_LEGACY_KEY}. */
414
+ scopeId?: string;
415
+ }
416
+ interface PasscodeLockoutRecord {
417
+ failedAttempts: number;
418
+ lockoutTier: number;
419
+ lockedUntil: string | null;
420
+ }
421
+ declare function resolveVaultStorageKey(scopeId?: string): string;
422
+ declare function saveVaultToIndexedDb(vault: EmbeddedWalletVaultV1, options?: VaultStorageOptions): Promise<void>;
423
+ declare function loadVaultFromIndexedDb(options?: VaultStorageOptions): Promise<EmbeddedWalletVaultV1 | null>;
424
+ declare function clearVaultFromIndexedDb(options?: VaultStorageOptions): Promise<void>;
425
+ declare function loadPasscodeLockoutRecord(publicAddress: string): Promise<PasscodeLockoutRecord | null>;
426
+ declare function savePasscodeLockoutRecord(publicAddress: string, record: PasscodeLockoutRecord): Promise<void>;
427
+ declare function clearPasscodeLockoutRecord(publicAddress: string): Promise<void>;
371
428
 
372
- interface CreateEmbeddedWalletOptions {
429
+ /** Consecutive wrong passcodes before lockout triggers. */
430
+ declare const PASSCODE_LOCKOUT_FAILURE_THRESHOLD = 5;
431
+ /** First lockout duration (milliseconds). */
432
+ declare const PASSCODE_LOCKOUT_INITIAL_MS = 30000;
433
+ /** Maximum lockout duration (15 minutes). */
434
+ declare const PASSCODE_LOCKOUT_MAX_MS = 900000;
435
+ /** Pure lockout duration for a tier (exported for property tests). */
436
+ declare function computePasscodeLockoutDurationMs(tier: number): number;
437
+ /** Throws when the vault is temporarily locked after too many failed unlock attempts. */
438
+ declare function assertPasscodeUnlockAllowed(publicAddress: string): Promise<void>;
439
+ /** Record a failed passcode unlock attempt; triggers lockout at threshold. */
440
+ declare function recordPasscodeUnlockFailure(publicAddress: string): Promise<void>;
441
+ /** Clear lockout state after a successful passcode unlock. */
442
+ declare function recordPasscodeUnlockSuccess(publicAddress: string): Promise<void>;
443
+ declare function getPasscodeLockoutState(publicAddress: string): Promise<PasscodeLockoutRecord & {
444
+ retryAfterMs: number;
445
+ }>;
446
+
447
+ interface PasscodeStrengthResult {
448
+ score: 0 | 1 | 2 | 3 | 4;
449
+ label: PasscodeStrengthLabel;
450
+ blocked: boolean;
451
+ blockReason?: "denylist" | "too_weak" | "numeric_short";
452
+ }
453
+ declare function hasMixedCharacterClasses(passcode: string): boolean;
454
+ /** Score passcode strength for creation UI (denylist + zxcvbn). */
455
+ declare function evaluatePasscodeStrength(passcode: string): PasscodeStrengthResult;
456
+ /** Hard reject weak or common passcodes at wallet creation / recovery. */
457
+ declare function assertPasscodeAllowedForCreation(passcode: string): void;
458
+
459
+ /** Minimum passcode length — alphanumeric passcode, not a 4–6 digit PIN. */
460
+ declare const EMBEDDED_WALLET_PASSCODE_MIN_LENGTH = 8;
461
+ /** Maximum passcode length (DoS guard for PBKDF2 input). */
462
+ declare const EMBEDDED_WALLET_PASSCODE_MAX_LENGTH = 128;
463
+ /**
464
+ * OWASP-recommended PBKDF2-SHA256 iteration count (2023 guidance).
465
+ * Uses native Web Crypto — no WASM bundle required.
466
+ */
467
+ declare const EMBEDDED_WALLET_PIN_PBKDF2_ITERATIONS = 600000;
468
+ /** Passkey PBKDF2 fallback iteration count (matches PIN path; OWASP 2023 guidance). */
469
+ declare const EMBEDDED_WALLET_PASSKEY_PBKDF2_ITERATIONS = 600000;
470
+ declare function normalizePasscode(passcode: string): string;
471
+ /** Validate citizen-chosen passcode before KDF. */
472
+ declare function assertValidPasscode(passcode: string): void;
473
+ declare function deriveKekFromPasscode(passcode: string, salt: Uint8Array, iterations?: number): Promise<Uint8Array>;
474
+
475
+ /** Normalize user input: trim, lowercase, collapse whitespace. */
476
+ declare function normalizeRecoveryMnemonic(mnemonic: string): string;
477
+ declare function assertValidRecoveryMnemonic(mnemonic: string): void;
478
+ declare function resolvePlaintextKeyType(plaintext: EmbeddedWalletPlaintext): EmbeddedWalletKeyType;
479
+ /** Encode a legacy raw 32-byte key as a 24-word BIP-39 mnemonic (entropy encoding, no HD). */
480
+ declare function encodeDirectEntropyMnemonic(privateKey: Uint8Array): string;
481
+ /** Decode a direct-entropy mnemonic back to the original 32-byte key. */
482
+ declare function decodeDirectEntropyMnemonic(mnemonic: string): Uint8Array;
483
+ declare function derivePrivateKeyFromHdMnemonic(mnemonic: string, hdPath?: string): Promise<Uint8Array>;
484
+ declare function addressFromPrivateKey(privateKey: Uint8Array, prefix?: string): Promise<string>;
485
+ declare function addressFromHdMnemonic(mnemonic: string, hdPath?: string, prefix?: string): Promise<string>;
486
+ declare function createHdDerivedPlaintext(createdAt: string, hdPath?: string): Promise<EmbeddedWalletPlaintext>;
487
+ declare function plaintextFromRecoveryMnemonic(mnemonic: string, keyType: EmbeddedWalletKeyType, createdAt: string, hdPath?: string): Promise<EmbeddedWalletPlaintext>;
488
+ /** Infer key type by matching an expected on-chain address, or default to hd-derived. */
489
+ declare function detectRecoveryMnemonicKeyType(mnemonic: string, expectedAddress: string, hdPath?: string, prefix?: string): Promise<EmbeddedWalletKeyType>;
490
+ declare function recoveryMnemonicAddress(mnemonic: string, keyType: EmbeddedWalletKeyType, hdPath?: string, prefix?: string): Promise<string>;
491
+ /** Build the recovery phrase for an unlocked plaintext payload. */
492
+ declare function recoveryMnemonicFromPlaintext(plaintext: EmbeddedWalletPlaintext): string;
493
+
494
+ interface EmbeddedWalletScopeOptions {
495
+ /** Supabase user id or other account key — scopes IndexedDB vault storage per account. */
496
+ scopeId?: string;
497
+ }
498
+ interface CreateEmbeddedWalletOptions extends EmbeddedWalletScopeOptions {
499
+ passcode: string;
500
+ }
501
+ interface UnlockEmbeddedWalletOptions extends EmbeddedWalletScopeOptions {
502
+ passcode?: string;
503
+ }
504
+ interface RevealEmbeddedRecoveryPhraseOptions extends EmbeddedWalletScopeOptions {
505
+ passcode?: string;
506
+ }
507
+ interface RecoverEmbeddedWalletOptions extends EmbeddedWalletScopeOptions {
508
+ passcode: string;
509
+ mnemonic: string;
510
+ /** When omitted, inferred from expectedAddress or defaults to hd-derived. */
511
+ keyType?: EmbeddedWalletKeyType;
512
+ /** Used to auto-detect legacy direct-entropy vs standard HD phrases. */
513
+ expectedAddress?: string;
514
+ hdPath?: string;
515
+ /** When true, replaces an existing vault on this device (e.g. after device loss). */
516
+ replaceExisting?: boolean;
517
+ }
518
+ interface EnableEmbeddedPasskeyUnlockOptions extends EmbeddedWalletScopeOptions {
519
+ passcode: string;
373
520
  userId: Uint8Array;
374
521
  userName: string;
375
522
  displayName: string;
376
523
  rpId?: string;
377
524
  }
525
+ interface MigrateLegacyEmbeddedWalletOptions extends EmbeddedWalletScopeOptions {
526
+ scopeId: string;
527
+ passcode?: string;
528
+ }
529
+ interface ImportEmbeddedBackupOptions extends EmbeddedWalletScopeOptions {
530
+ /** When true, replaces an existing vault on this device (e.g. intentional restore). */
531
+ replaceExisting?: boolean;
532
+ }
533
+ interface ClearEmbeddedWalletOptions extends EmbeddedWalletScopeOptions {
534
+ }
535
+ interface AcknowledgeEmbeddedBackupOptions extends EmbeddedWalletScopeOptions {
536
+ }
378
537
  interface EmbeddedWalletSession {
379
538
  address: string;
380
539
  vault: EmbeddedWalletVaultV1;
381
540
  signer: OfflineDirectSigner;
382
541
  }
383
- /** Returns true if an encrypted vault exists in IndexedDB. */
384
- declare function hasEmbeddedWallet(): Promise<boolean>;
385
- /** Create passkey + encrypted vault; does not unlock signer until backup is acknowledged. */
542
+ declare function vaultUsesPasskeyUnlock(vault: EmbeddedWalletVaultV1): boolean;
543
+ declare function vaultUsesPasscodeUnlock(vault: EmbeddedWalletVaultV1): boolean;
544
+ /** Returns true if an encrypted vault exists under the given scope (not the legacy key). */
545
+ declare function hasEmbeddedWallet(options?: EmbeddedWalletScopeOptions): Promise<boolean>;
546
+ /** Returns true if an unscoped legacy vault still exists under the "primary" key. */
547
+ declare function hasLegacyEmbeddedWallet(): Promise<boolean>;
548
+ /**
549
+ * Verify ownership of a legacy "primary" vault, copy it to a scoped key, and remove the legacy entry.
550
+ * Does not run automatically — callers must confirm with the user first.
551
+ */
552
+ declare function migrateLegacyEmbeddedWallet(options: MigrateLegacyEmbeddedWalletOptions): Promise<EmbeddedWalletSession>;
553
+ /** Create passcode-protected encrypted vault; does not unlock signer until backup is acknowledged. */
386
554
  declare function createEmbeddedWallet(options: CreateEmbeddedWalletOptions): Promise<{
387
555
  address: string;
388
556
  vault: EmbeddedWalletVaultV1;
389
557
  }>;
390
- /** Unlock existing vault with passkey; requires backup acknowledged for signing. */
391
- declare function unlockEmbeddedWallet(): Promise<EmbeddedWalletSession>;
558
+ /**
559
+ * Re-authenticate and return the recovery phrase plus UI warnings.
560
+ * Call only after showing {@link EMBEDDED_WALLET_RECOVERY_PHRASE_GENERAL_WARNING} (and the
561
+ * direct-entropy portability warning when applicable).
562
+ */
563
+ declare function revealEmbeddedRecoveryPhrase(options?: RevealEmbeddedRecoveryPhraseOptions): Promise<EmbeddedWalletRecoveryPhraseReveal>;
564
+ /**
565
+ * Restore an embedded wallet on a new device from a recovery phrase.
566
+ * Sets a new passcode and stores an encrypted vault in IndexedDB.
567
+ */
568
+ declare function recoverEmbeddedWalletFromMnemonic(options: RecoverEmbeddedWalletOptions): Promise<{
569
+ address: string;
570
+ vault: EmbeddedWalletVaultV1;
571
+ keyType: EmbeddedWalletKeyType;
572
+ }>;
573
+ /**
574
+ * Opt-in upgrade: re-wrap an existing passcode vault with passkey unlock.
575
+ * Does not change the underlying private key or address.
576
+ */
577
+ declare function enableEmbeddedPasskeyUnlock(options: EnableEmbeddedPasskeyUnlockOptions): Promise<{
578
+ address: string;
579
+ vault: EmbeddedWalletVaultV1;
580
+ }>;
581
+ /** Unlock existing vault with passcode (default) or passkey (opt-in upgrade path). */
582
+ declare function unlockEmbeddedWallet(options?: UnlockEmbeddedWalletOptions): Promise<EmbeddedWalletSession>;
392
583
  /** Clear in-memory signer. */
393
584
  declare function lockEmbeddedWallet(): void;
394
585
  declare function getEmbeddedWalletSession(): EmbeddedWalletSession | null;
395
586
  /** Offline signer for CosmJS; enforces backup gate before signing. */
396
587
  declare function getEmbeddedOfflineSigner(): OfflineDirectSigner;
397
588
  /** Mark backup downloaded/acknowledged and persist. Returns backup file payload. */
398
- declare function acknowledgeEmbeddedBackup(): Promise<EmbeddedWalletBackupFileV1>;
589
+ declare function acknowledgeEmbeddedBackup(options?: AcknowledgeEmbeddedBackupOptions): Promise<EmbeddedWalletBackupFileV1>;
399
590
  declare function downloadEmbeddedBackupFile(backup: EmbeddedWalletBackupFileV1, filename?: string): void;
400
- /** Restore vault from user-held backup file (still requires passkey to decrypt). */
401
- declare function importEmbeddedBackup(json: string): Promise<EmbeddedWalletVaultV1>;
402
- declare function clearEmbeddedWallet(): Promise<void>;
591
+ /** Restore vault from user-held backup file (requires passcode or passkey to decrypt). */
592
+ declare function importEmbeddedBackup(json: string, options?: ImportEmbeddedBackupOptions): Promise<EmbeddedWalletVaultV1>;
593
+ declare function clearEmbeddedWallet(options?: ClearEmbeddedWalletOptions): Promise<void>;
403
594
 
404
595
  /**
405
596
  * Kingdom Chain on-chain denom and display metadata.
@@ -443,4 +634,4 @@ declare function signCitizenshipAttestation(params: SubmitApplicationParams): Pr
443
634
  input: AttestationPayloadInput;
444
635
  }>;
445
636
 
446
- export { ATTESTATION_TYPE_CITIZENSHIP_APPLICATION, ATTESTATION_TYPE_RECOVERY, ApplicationStatus, type AttestationPayloadInput, BECH32_PREFIX, type BrowserWalletApi, CHAIN_COIN_TYPE, type ChainCurrency, type ChainInfo, type CosmostationWindowApi, type CreateEmbeddedWalletOptions, DEFAULT_CHAIN_ID, DEFAULT_REST_URL, DEFAULT_RPC_URL, EMBEDDED_WALLET_KDF_PBKDF2, EMBEDDED_WALLET_KDF_PRF, EMBEDDED_WALLET_VAULT_VERSION, type EmbeddedWalletBackupFileV1, EmbeddedWalletBackupRequiredError, type EmbeddedWalletKdf, type EmbeddedWalletPlaintext, type EmbeddedWalletSession, EmbeddedWalletVaultError, type EmbeddedWalletVaultV1, type EncryptWalletOptions, InvalidChainConfigError, MsgSubmitApplication, MsgSubmitRecoveryAttestation, type PasskeyAssertionResult, type PasskeyRegistrationResult, RecoveryAttestationStatus, RpcConnectionError, type SubmitApplicationParams, UKDR_DECIMALS, UKDR_DISPLAY_DENOM, UKDR_MINIMAL_DENOM, 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, assertPasskey, assertVaultPermitsTransaction, attestationSignPayload, buildKingdomChainInfo, buildRequiredCosmosNamespaces, buildSubmitApplicationMsg, clearEmbeddedWallet, clearVaultFromIndexedDb, connectCosmostation, connectKeplr, connectLeap, connectWalletConnect, createBrowserSigningClient, createEmbeddedWallet, createKingdomChainRegistry, createPasskeyMaterialFromAssertion, createPbkdf2Salt, createWalletConnectOfflineSigner, decodeCredentialId, decryptWalletPayload, defaultEmbeddedHdPath, deriveKekFromPbkdf2, deriveKekFromPrf, detectWebAuthnPrfCapabilities, disconnectWalletConnect, downloadEmbeddedBackupFile, embeddedPlaintextToSigner, encodeCredentialId, encryptWalletPayload, exportEncryptedBackup, getCosmostationKeplrApi, getEmbeddedOfflineSigner, getEmbeddedWalletSession, hasEmbeddedWallet, importEmbeddedBackup, initWalletConnectClient, isCosmostationAvailable, isKeplrAvailable, isLeapAvailable, loadVaultFromIndexedDb, lockEmbeddedWallet, markVaultBackupAcknowledged, parseEncryptedBackup, parseWalletConnectAccount, providerSeedFromHex, publicKeyFromPrivateSeed, readPrfOutput, registerPasskey, restoreWalletConnectSession, saveVaultToIndexedDb, selectEmbeddedWalletKdf, serializeBackupFile, signAttestationPayload, signCitizenshipAttestation, suggestAndConnect, toWalletConnectCosmosChainId, unlockEmbeddedWallet, validateChainConfig };
637
+ export { ATTESTATION_TYPE_CITIZENSHIP_APPLICATION, ATTESTATION_TYPE_RECOVERY, type AcknowledgeEmbeddedBackupOptions, ApplicationStatus, type AttestationPayloadInput, BECH32_PREFIX, type BrowserWalletApi, CHAIN_COIN_TYPE, type ChainCurrency, type ChainInfo, 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_VAULT_VERSION, type EmbeddedWalletBackupFileV1, EmbeddedWalletBackupRequiredError, EmbeddedWalletDecryptError, type EmbeddedWalletKdf, type EmbeddedWalletKeyType, EmbeddedWalletLockoutError, type EmbeddedWalletPlaintext, type EmbeddedWalletRecoveryPhraseReveal, type EmbeddedWalletRecoveryPhraseWarning, type EmbeddedWalletScopeOptions, type EmbeddedWalletSession, EmbeddedWalletVaultError, type EmbeddedWalletVaultV1, EmbeddedWalletWeakPasscodeError, type EnableEmbeddedPasskeyUnlockOptions, type EncryptWalletOptions, type ImportEmbeddedBackupOptions, InvalidChainConfigError, type MigrateLegacyEmbeddedWalletOptions, 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, type RevealEmbeddedRecoveryPhraseOptions, RpcConnectionError, type SubmitApplicationParams, UKDR_DECIMALS, UKDR_DISPLAY_DENOM, UKDR_MINIMAL_DENOM, 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, buildKingdomChainInfo, buildRequiredCosmosNamespaces, buildSubmitApplicationMsg, clearEmbeddedWallet, clearPasscodeLockoutRecord, clearVaultFromIndexedDb, computePasscodeLockoutDurationMs, connectCosmostation, connectKeplr, connectLeap, connectWalletConnect, createBrowserSigningClient, createEmbeddedWallet, createHdDerivedPlaintext, createKingdomChainRegistry, createPasskeyMaterialFromAssertion, createPbkdf2Salt, createWalletConnectOfflineSigner, decodeCredentialId, decodeDirectEntropyMnemonic, decryptWalletPayload, defaultEmbeddedHdPath, deriveKekFromPasscode, deriveKekFromPbkdf2, deriveKekFromPrf, derivePrivateKeyFromHdMnemonic, detectRecoveryMnemonicKeyType, detectWebAuthnPrfCapabilities, disconnectWalletConnect, downloadEmbeddedBackupFile, embeddedPlaintextToSigner, enableEmbeddedPasskeyUnlock, encodeCredentialId, encodeDirectEntropyMnemonic, encryptWalletPayload, evaluatePasscodeStrength, exportEncryptedBackup, getCosmostationKeplrApi, getEmbeddedOfflineSigner, getEmbeddedWalletSession, getPasscodeLockoutState, hasEmbeddedWallet, hasLegacyEmbeddedWallet, hasMixedCharacterClasses, importEmbeddedBackup, initWalletConnectClient, isCosmostationAvailable, isKeplrAvailable, isLeapAvailable, loadPasscodeLockoutRecord, loadVaultFromIndexedDb, lockEmbeddedWallet, markVaultBackupAcknowledged, migrateLegacyEmbeddedWallet, normalizePasscode, normalizeRecoveryMnemonic, parseEncryptedBackup, parseWalletConnectAccount, plaintextFromRecoveryMnemonic, providerSeedFromHex, publicKeyFromPrivateSeed, readPrfOutput, recordPasscodeUnlockFailure, recordPasscodeUnlockSuccess, recoverEmbeddedWalletFromMnemonic, recoveryMnemonicAddress, recoveryMnemonicFromPlaintext, registerPasskey, resolvePlaintextKeyType, resolveVaultStorageKey, restoreWalletConnectSession, revealEmbeddedRecoveryPhrase, savePasscodeLockoutRecord, saveVaultToIndexedDb, selectEmbeddedWalletKdf, serializeBackupFile, signAttestationPayload, signCitizenshipAttestation, suggestAndConnect, toWalletConnectCosmosChainId, unlockEmbeddedWallet, validateChainConfig, vaultUsesPasscodeUnlock, vaultUsesPasskeyUnlock };