decane-connect-kit 1.0.3 → 2.0.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/dist/index.d.cts CHANGED
@@ -1,3 +1,4 @@
1
+ import * as _simplewebauthn_browser from '@simplewebauthn/browser';
1
2
  import React, { ReactNode } from 'react';
2
3
 
3
4
  type ChainType = "evm" | "solana" | "tron" | "bitcoin";
@@ -133,14 +134,6 @@ interface BitcoinProvider {
133
134
 
134
135
  declare function createDecane(config?: DecaneConfig): DecaneProvider;
135
136
 
136
- interface PhraseGenerationResult {
137
- phrase: string;
138
- challenge: Array<{
139
- position: number;
140
- expected: string;
141
- }>;
142
- }
143
-
144
137
  declare class ApiClient {
145
138
  private readonly baseUrl;
146
139
  private readonly apiKey;
@@ -162,6 +155,8 @@ declare class ApiClient {
162
155
  evmAddress: string;
163
156
  solanaAddress: string;
164
157
  }): Promise<void>;
158
+ getWebAuthnRegisterOptions(jwt: string, rpId: string, rpName: string): Promise<_simplewebauthn_browser.PublicKeyCredentialCreationOptionsJSON>;
159
+ postWebAuthnRegister(jwt: string, response: _simplewebauthn_browser.RegistrationResponseJSON, origin: string): Promise<void>;
165
160
  revoke(jwt: string): Promise<void>;
166
161
  hasRecoveryBackup(jwt: string): Promise<boolean>;
167
162
  getRecoveryBackup(jwt: string): Promise<{
@@ -180,7 +175,14 @@ declare class ApiClient {
180
175
  prfSalt: Uint8Array;
181
176
  }): Promise<void>;
182
177
  deleteRecoveryBackup(jwt: string): Promise<void>;
183
- markPhraseSaved(jwt: string): Promise<void>;
178
+ markRecoveryShareSaved(jwt: string): Promise<void>;
179
+ getRecoveryStatus(jwt: string): Promise<{
180
+ hasRecoveryShare: boolean;
181
+ savedAt: string | null;
182
+ nagged: boolean;
183
+ shareSetVersion: number;
184
+ }>;
185
+ dismissRecoveryNag(jwt: string): Promise<void>;
184
186
  }
185
187
 
186
188
  type RecoveryProgress = {
@@ -189,11 +191,57 @@ type RecoveryProgress = {
189
191
  type: 'passkey-failed';
190
192
  reason: string;
191
193
  } | {
192
- type: 'phrase-prompt';
194
+ type: 'recovery-file-prompt';
195
+ } | {
196
+ type: 'password-prompt';
197
+ passwordHint: string | null;
193
198
  } | {
194
- type: 'phrase-verifying';
199
+ type: 'recovery-verifying';
200
+ } | {
201
+ type: 'rotated';
202
+ shareSetVersion: number;
195
203
  };
196
204
 
205
+ interface RecoveryShareFile {
206
+ version: number;
207
+ type: 'decane-recovery-share';
208
+ shareSetVersion: number;
209
+ kdf: 'pbkdf2-sha256';
210
+ kdfParams: {
211
+ iterations: number;
212
+ salt: string;
213
+ };
214
+ cipher: 'aes-256-gcm';
215
+ iv: string;
216
+ ciphertext: string;
217
+ addresses: {
218
+ evm: string;
219
+ solana: string;
220
+ };
221
+ passwordHint: string | null;
222
+ createdAt: string;
223
+ }
224
+
225
+ interface PortableBackup {
226
+ version: number;
227
+ type: 'decane-portable-backup';
228
+ shareSetVersion: number;
229
+ kdf: 'pbkdf2-sha256';
230
+ kdfParams: {
231
+ iterations: number;
232
+ salt: string;
233
+ };
234
+ cipher: 'aes-256-gcm';
235
+ iv: string;
236
+ ciphertext: string;
237
+ addresses: {
238
+ evm: string;
239
+ solana: string;
240
+ };
241
+ passwordHint: string | null;
242
+ createdAt: string;
243
+ }
244
+
197
245
  type Chain = `evm:${number}` | `solana:${'mainnet' | 'devnet'}`;
198
246
  interface DecaneConnectConfig {
199
247
  apiKey: string;
@@ -206,18 +254,36 @@ interface DecaneConnectConfig {
206
254
  rpName?: string;
207
255
  rpcUrls?: Record<string, string>;
208
256
  teeUrl?: string;
209
- /** Offer phrase generation during signup. Default true. */
210
- offerPhraseAtSignup?: boolean;
211
- /** Show the phrase to the user; return their confirmation answers. */
212
- onPhraseGenerated?: (result: PhraseGenerationResult) => Promise<{
213
- userConfirmed: boolean;
214
- userAnswers: Array<{
215
- position: number;
216
- word: string;
217
- }>;
257
+ /** Offer to save a recovery-share file at signup. Default true. Never blocks onboarding. */
258
+ offerRecoveryAtSignup?: boolean;
259
+ /** Ask the user whether they want a recovery share and, if so, a password for it. */
260
+ onRecoveryShareOffer?: (addresses: {
261
+ evmAddress: string;
262
+ solanaAddress: string;
263
+ }) => Promise<{
264
+ wants: boolean;
265
+ password: string;
266
+ passwordHint?: string;
267
+ }>;
268
+ /** Hand the built file + suggested filename to the dApp, which owns the actual download. */
269
+ onRecoveryFileReady?: (file: RecoveryShareFile, filename: string) => Promise<void>;
270
+ /** Prompt for a recovery-share file (and its password) during Flow B. Return null to cancel. */
271
+ promptForRecoveryFile?: () => Promise<{
272
+ value: unknown;
273
+ getPassword: () => Promise<string | null>;
274
+ } | null>;
275
+ /**
276
+ * Every successful recovery/rotation invalidates the previous recovery
277
+ * file — this MUST prompt for a new password and block progress until the
278
+ * dApp confirms the user saved the new file (see MIGRATE_SSS_RECOVERY.md §10).
279
+ */
280
+ onRecoveryRotated?: (addresses: {
281
+ evmAddress: string;
282
+ solanaAddress: string;
283
+ }) => Promise<{
284
+ password: string;
285
+ passwordHint?: string;
218
286
  }>;
219
- /** Prompt for a phrase during recovery. Return null to cancel. */
220
- promptForRecoveryPhrase?: () => Promise<string | null>;
221
287
  /** Recovery stage callbacks, for dApp UI. */
222
288
  onRecoveryProgress?: (event: RecoveryProgress) => void;
223
289
  }
@@ -250,6 +316,12 @@ interface DecaneConnect {
250
316
  verifyEmailCode(email: string, code: string): Promise<ConnectResult>;
251
317
  unlock(): Promise<void>;
252
318
  isUnlocked(): boolean;
319
+ /**
320
+ * True when the user's identity is known from a previous visit but there is
321
+ * no live session, so unlock() cannot succeed and they must sign in again.
322
+ * Check this before offering an "Unlock" action.
323
+ */
324
+ needsReconnect(): boolean;
253
325
  sessionExpiresAt(): number | null;
254
326
  signMessage(req: SignMessageRequest): Promise<string>;
255
327
  sendTransaction(req: SendTransactionRequest): Promise<string>;
@@ -259,16 +331,32 @@ interface DecaneConnect {
259
331
  evm?: string;
260
332
  solana?: string;
261
333
  } | null;
262
- /** Generate a recovery phrase. Re-prompts for unlock; does not cache the share. */
263
- generateRecoveryPhrase(): Promise<PhraseGenerationResult>;
264
- /** Record that the user confirmed they saved the phrase. */
265
- markPhraseSaved(challenge: Array<{
266
- position: number;
267
- expected: string;
268
- }>, userAnswers: Array<{
269
- position: number;
270
- word: string;
271
- }>): Promise<boolean>;
334
+ /**
335
+ * Voluntary share rotation — after a suspected device compromise, or just
336
+ * to invalidate an old recovery file. Requires a fresh WebAuthn assertion.
337
+ * Returns the new recovery-share file (the caller still owns delivering it
338
+ * to the user — this does not download anything itself).
339
+ */
340
+ rotateShares(opts: {
341
+ password: string;
342
+ passwordHint?: string;
343
+ }): Promise<RecoveryShareFile>;
344
+ /**
345
+ * The two-share bearer portable backup (device + recovery). This file plus
346
+ * its password gives complete control of the wallet — see the mandatory
347
+ * warning-screen requirements in MIGRATE_SSS_RECOVERY.md §10. Requires a
348
+ * fresh WebAuthn assertion and rotates the share set on every export.
349
+ */
350
+ exportPortableBackup(opts: {
351
+ password: string;
352
+ passwordHint?: string;
353
+ }): Promise<PortableBackup>;
354
+ /** Whether the user has ever saved a recovery-share file (UX flag only — Decane never stores the share). */
355
+ hasRecoveryShare(): Promise<boolean>;
356
+ /** True at most once per account: no recovery share saved, never nagged, wallet holds value. */
357
+ shouldPromptRecovery(): Promise<boolean>;
358
+ /** Call regardless of the user's answer — the recovery nag fires once, ever. */
359
+ dismissRecoveryPrompt(): Promise<void>;
272
360
  /** Whether a wrapped-share backup exists on the server. */
273
361
  hasBackup(): Promise<boolean>;
274
362
  /** Delete the server-side backup. Disables Flow A recovery. */
@@ -319,6 +407,22 @@ declare class SessionExpiredError extends DecaneConnectError {
319
407
  declare class UnlockRequiredError extends DecaneConnectError {
320
408
  constructor();
321
409
  }
410
+ /**
411
+ * unlock() was called after a page reload (or any other loss of the in-memory
412
+ * auth state). We still know *who* the user is — their identity and addresses
413
+ * are persisted — but unlock() needs a live JWT to open a TEE session, and
414
+ * JWTs are deliberately never persisted. So there is no unlock() that can
415
+ * succeed here; the user has to re-authenticate.
416
+ *
417
+ * This is its own error type rather than a generic CallSequenceError because
418
+ * it is a *routing* signal, not a programming mistake: the correct response is
419
+ * to show the sign-in UI, which callers can only do reliably if they can tell
420
+ * this case apart from "you never called connect()". Prefer checking
421
+ * needsReconnect() up front so this never has to be caught at all.
422
+ */
423
+ declare class ReconnectRequiredError extends DecaneConnectError {
424
+ constructor();
425
+ }
322
426
  declare class UserCancelledError extends DecaneConnectError {
323
427
  constructor();
324
428
  }
@@ -349,7 +453,16 @@ declare class AttestationError extends DecaneConnectError {
349
453
  declare class RecoveryFailedError extends DecaneConnectError {
350
454
  constructor(reason: string);
351
455
  }
352
- declare class InvalidPhraseError extends DecaneConnectError {
456
+ declare class InvalidRecoveryFileError extends DecaneConnectError {
457
+ constructor();
458
+ }
459
+ declare class WrongPasswordError extends DecaneConnectError {
460
+ constructor();
461
+ }
462
+ declare class RecoveryWalletMismatchError extends DecaneConnectError {
463
+ constructor();
464
+ }
465
+ declare class ExportNotAuthorizedError extends DecaneConnectError {
353
466
  constructor();
354
467
  }
355
468
 
@@ -467,6 +580,16 @@ interface SocialAuthState {
467
580
  isNewUser: boolean;
468
581
  isUnlocked: boolean;
469
582
  sessionExpiresAt: number | null;
583
+ /**
584
+ * True when `addresses` came from persisted (localStorage) identity only,
585
+ * with no live session. This is NOT the same as "locked" — every unlock()
586
+ * requires a fresh, valid JWT to open a TEE session, and JWTs are
587
+ * deliberately never persisted (short-lived, security-sensitive), so
588
+ * there is no legitimate "just unlock" path in this state. A consumer
589
+ * should route this into a full reconnect (connectWithGoogle() /
590
+ * connectWithEmail()), not unlock() — which will always throw here.
591
+ */
592
+ needsReconnect: boolean;
470
593
  }
471
594
  interface DecaneKitProps {
472
595
  config?: DecaneConfig;
@@ -481,8 +604,24 @@ interface SocialWalletModalProps {
481
604
  socialAuth: SocialAuthState;
482
605
  onUnlock: () => Promise<void>;
483
606
  onDisconnect: () => Promise<void>;
607
+ onGoogleSignIn: () => Promise<void>;
608
+ onEmailStart: (email: string) => Promise<{
609
+ codeRequired: true;
610
+ }>;
611
+ onEmailVerify: (email: string, code: string) => Promise<void>;
612
+ authMethods?: Array<"google" | "email">;
613
+ onRotateShares: (opts: {
614
+ password: string;
615
+ passwordHint?: string;
616
+ }) => Promise<RecoveryShareFile>;
617
+ onExportPortableBackup: (opts: {
618
+ password: string;
619
+ passwordHint?: string;
620
+ }) => Promise<PortableBackup>;
621
+ rpcUrls?: Record<string, string>;
622
+ chains?: string[];
484
623
  }
485
- declare function SocialWalletModal({ open, onClose, theme, socialAuth, onUnlock, onDisconnect, }: SocialWalletModalProps): React.ReactPortal | null;
624
+ declare function SocialWalletModal({ open, onClose, theme, socialAuth, onUnlock, onDisconnect, onGoogleSignIn, onEmailStart, onEmailVerify, authMethods, onRotateShares, onExportPortableBackup, rpcUrls, chains, }: SocialWalletModalProps): React.ReactPortal | null;
486
625
 
487
626
  declare function useSocialAuth(): {
488
627
  isConnected: boolean;
@@ -493,6 +632,7 @@ declare function useSocialAuth(): {
493
632
  solana?: string;
494
633
  } | null;
495
634
  sessionExpiresAt: number | null;
635
+ needsReconnect: boolean;
496
636
  googleLoading: boolean;
497
637
  emailLoading: boolean;
498
638
  error: string | null;
@@ -514,6 +654,7 @@ declare function useSocialWallet(): {
514
654
  solana?: string;
515
655
  } | null;
516
656
  sessionExpiresAt: number | null;
657
+ needsReconnect: boolean;
517
658
  modalOpen: boolean;
518
659
  openModal: () => void;
519
660
  closeModal: () => void;
@@ -529,6 +670,17 @@ declare function useSocialWallet(): {
529
670
  gasPrice?: bigint;
530
671
  }) => Promise<string>;
531
672
  signSolanaTransaction: (tx: Uint8Array) => Promise<Uint8Array<ArrayBufferLike>>;
673
+ rotateShares: (opts: {
674
+ password: string;
675
+ passwordHint?: string;
676
+ }) => Promise<RecoveryShareFile>;
677
+ exportPortableBackup: (opts: {
678
+ password: string;
679
+ passwordHint?: string;
680
+ }) => Promise<PortableBackup>;
681
+ hasRecoveryShare: () => Promise<boolean>;
682
+ shouldPromptRecovery: () => Promise<boolean>;
683
+ dismissRecoveryPrompt: () => Promise<void>;
532
684
  loading: boolean;
533
685
  error: string | null;
534
686
  clearError: () => void;
@@ -605,4 +757,4 @@ declare function useSignTypedData(): {
605
757
  reset: () => void;
606
758
  };
607
759
 
608
- export { type ActiveSession, AttestationError, type AuthResult, BUILT_IN_CHAINS, type CAIP25Method, type CAIP25SessionRequest, type CAIP25SessionResult, CHAINS, CallSequenceError, type Chain, type ChainDescriptor, type ChainId, type ChainScope, type ChainSupport, type ChainType, type ConnectMode, type ConnectResult, type DecaneConfig, type DecaneConnect, type DecaneConnectConfig, DecaneConnectError, DecaneError, DecaneErrorCode, DecaneKit, type DecaneProvider, InvalidApiKeyError, InvalidPhraseError, InvalidPinError, NetworkError, NewDeviceError, OriginNotAllowedError, type PhraseGenerationResult, PromptPinNotConfiguredError, RecoveryFailedError, type RecoveryProgress, type SdkEvent, type SendTransactionRequest, SessionExpiredError, ShareCorruptedError, type SignMessageRequest, type SocialAuthState, type SocialConfig, SocialWalletModal, type Theme, type TxParams, type UnifiedWalletAccount, UnlockRequiredError, UserCancelledError, WalletSelector, WebAuthnNotSupportedError, type WrappedShareMeta, createDecane, createDecaneConnect, startEmailAuth, useChainSwitch, useDecane, useSendTransaction, useSignMessage, useSignTypedData, useSocialAuth, useSocialWallet, useWalletSelector, verifyEmailCode };
760
+ export { type ActiveSession, AttestationError, type AuthResult, BUILT_IN_CHAINS, type CAIP25Method, type CAIP25SessionRequest, type CAIP25SessionResult, CHAINS, CallSequenceError, type Chain, type ChainDescriptor, type ChainId, type ChainScope, type ChainSupport, type ChainType, type ConnectMode, type ConnectResult, type DecaneConfig, type DecaneConnect, type DecaneConnectConfig, DecaneConnectError, DecaneError, DecaneErrorCode, DecaneKit, type DecaneProvider, ExportNotAuthorizedError, InvalidApiKeyError, InvalidPinError, InvalidRecoveryFileError, NetworkError, NewDeviceError, OriginNotAllowedError, type PortableBackup, PromptPinNotConfiguredError, ReconnectRequiredError, RecoveryFailedError, type RecoveryProgress, type RecoveryShareFile, RecoveryWalletMismatchError, type SdkEvent, type SendTransactionRequest, SessionExpiredError, ShareCorruptedError, type SignMessageRequest, type SocialAuthState, type SocialConfig, SocialWalletModal, type Theme, type TxParams, type UnifiedWalletAccount, UnlockRequiredError, UserCancelledError, WalletSelector, WebAuthnNotSupportedError, type WrappedShareMeta, WrongPasswordError, createDecane, createDecaneConnect, startEmailAuth, useChainSwitch, useDecane, useSendTransaction, useSignMessage, useSignTypedData, useSocialAuth, useSocialWallet, useWalletSelector, verifyEmailCode };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import * as _simplewebauthn_browser from '@simplewebauthn/browser';
1
2
  import React, { ReactNode } from 'react';
2
3
 
3
4
  type ChainType = "evm" | "solana" | "tron" | "bitcoin";
@@ -133,14 +134,6 @@ interface BitcoinProvider {
133
134
 
134
135
  declare function createDecane(config?: DecaneConfig): DecaneProvider;
135
136
 
136
- interface PhraseGenerationResult {
137
- phrase: string;
138
- challenge: Array<{
139
- position: number;
140
- expected: string;
141
- }>;
142
- }
143
-
144
137
  declare class ApiClient {
145
138
  private readonly baseUrl;
146
139
  private readonly apiKey;
@@ -162,6 +155,8 @@ declare class ApiClient {
162
155
  evmAddress: string;
163
156
  solanaAddress: string;
164
157
  }): Promise<void>;
158
+ getWebAuthnRegisterOptions(jwt: string, rpId: string, rpName: string): Promise<_simplewebauthn_browser.PublicKeyCredentialCreationOptionsJSON>;
159
+ postWebAuthnRegister(jwt: string, response: _simplewebauthn_browser.RegistrationResponseJSON, origin: string): Promise<void>;
165
160
  revoke(jwt: string): Promise<void>;
166
161
  hasRecoveryBackup(jwt: string): Promise<boolean>;
167
162
  getRecoveryBackup(jwt: string): Promise<{
@@ -180,7 +175,14 @@ declare class ApiClient {
180
175
  prfSalt: Uint8Array;
181
176
  }): Promise<void>;
182
177
  deleteRecoveryBackup(jwt: string): Promise<void>;
183
- markPhraseSaved(jwt: string): Promise<void>;
178
+ markRecoveryShareSaved(jwt: string): Promise<void>;
179
+ getRecoveryStatus(jwt: string): Promise<{
180
+ hasRecoveryShare: boolean;
181
+ savedAt: string | null;
182
+ nagged: boolean;
183
+ shareSetVersion: number;
184
+ }>;
185
+ dismissRecoveryNag(jwt: string): Promise<void>;
184
186
  }
185
187
 
186
188
  type RecoveryProgress = {
@@ -189,11 +191,57 @@ type RecoveryProgress = {
189
191
  type: 'passkey-failed';
190
192
  reason: string;
191
193
  } | {
192
- type: 'phrase-prompt';
194
+ type: 'recovery-file-prompt';
195
+ } | {
196
+ type: 'password-prompt';
197
+ passwordHint: string | null;
193
198
  } | {
194
- type: 'phrase-verifying';
199
+ type: 'recovery-verifying';
200
+ } | {
201
+ type: 'rotated';
202
+ shareSetVersion: number;
195
203
  };
196
204
 
205
+ interface RecoveryShareFile {
206
+ version: number;
207
+ type: 'decane-recovery-share';
208
+ shareSetVersion: number;
209
+ kdf: 'pbkdf2-sha256';
210
+ kdfParams: {
211
+ iterations: number;
212
+ salt: string;
213
+ };
214
+ cipher: 'aes-256-gcm';
215
+ iv: string;
216
+ ciphertext: string;
217
+ addresses: {
218
+ evm: string;
219
+ solana: string;
220
+ };
221
+ passwordHint: string | null;
222
+ createdAt: string;
223
+ }
224
+
225
+ interface PortableBackup {
226
+ version: number;
227
+ type: 'decane-portable-backup';
228
+ shareSetVersion: number;
229
+ kdf: 'pbkdf2-sha256';
230
+ kdfParams: {
231
+ iterations: number;
232
+ salt: string;
233
+ };
234
+ cipher: 'aes-256-gcm';
235
+ iv: string;
236
+ ciphertext: string;
237
+ addresses: {
238
+ evm: string;
239
+ solana: string;
240
+ };
241
+ passwordHint: string | null;
242
+ createdAt: string;
243
+ }
244
+
197
245
  type Chain = `evm:${number}` | `solana:${'mainnet' | 'devnet'}`;
198
246
  interface DecaneConnectConfig {
199
247
  apiKey: string;
@@ -206,18 +254,36 @@ interface DecaneConnectConfig {
206
254
  rpName?: string;
207
255
  rpcUrls?: Record<string, string>;
208
256
  teeUrl?: string;
209
- /** Offer phrase generation during signup. Default true. */
210
- offerPhraseAtSignup?: boolean;
211
- /** Show the phrase to the user; return their confirmation answers. */
212
- onPhraseGenerated?: (result: PhraseGenerationResult) => Promise<{
213
- userConfirmed: boolean;
214
- userAnswers: Array<{
215
- position: number;
216
- word: string;
217
- }>;
257
+ /** Offer to save a recovery-share file at signup. Default true. Never blocks onboarding. */
258
+ offerRecoveryAtSignup?: boolean;
259
+ /** Ask the user whether they want a recovery share and, if so, a password for it. */
260
+ onRecoveryShareOffer?: (addresses: {
261
+ evmAddress: string;
262
+ solanaAddress: string;
263
+ }) => Promise<{
264
+ wants: boolean;
265
+ password: string;
266
+ passwordHint?: string;
267
+ }>;
268
+ /** Hand the built file + suggested filename to the dApp, which owns the actual download. */
269
+ onRecoveryFileReady?: (file: RecoveryShareFile, filename: string) => Promise<void>;
270
+ /** Prompt for a recovery-share file (and its password) during Flow B. Return null to cancel. */
271
+ promptForRecoveryFile?: () => Promise<{
272
+ value: unknown;
273
+ getPassword: () => Promise<string | null>;
274
+ } | null>;
275
+ /**
276
+ * Every successful recovery/rotation invalidates the previous recovery
277
+ * file — this MUST prompt for a new password and block progress until the
278
+ * dApp confirms the user saved the new file (see MIGRATE_SSS_RECOVERY.md §10).
279
+ */
280
+ onRecoveryRotated?: (addresses: {
281
+ evmAddress: string;
282
+ solanaAddress: string;
283
+ }) => Promise<{
284
+ password: string;
285
+ passwordHint?: string;
218
286
  }>;
219
- /** Prompt for a phrase during recovery. Return null to cancel. */
220
- promptForRecoveryPhrase?: () => Promise<string | null>;
221
287
  /** Recovery stage callbacks, for dApp UI. */
222
288
  onRecoveryProgress?: (event: RecoveryProgress) => void;
223
289
  }
@@ -250,6 +316,12 @@ interface DecaneConnect {
250
316
  verifyEmailCode(email: string, code: string): Promise<ConnectResult>;
251
317
  unlock(): Promise<void>;
252
318
  isUnlocked(): boolean;
319
+ /**
320
+ * True when the user's identity is known from a previous visit but there is
321
+ * no live session, so unlock() cannot succeed and they must sign in again.
322
+ * Check this before offering an "Unlock" action.
323
+ */
324
+ needsReconnect(): boolean;
253
325
  sessionExpiresAt(): number | null;
254
326
  signMessage(req: SignMessageRequest): Promise<string>;
255
327
  sendTransaction(req: SendTransactionRequest): Promise<string>;
@@ -259,16 +331,32 @@ interface DecaneConnect {
259
331
  evm?: string;
260
332
  solana?: string;
261
333
  } | null;
262
- /** Generate a recovery phrase. Re-prompts for unlock; does not cache the share. */
263
- generateRecoveryPhrase(): Promise<PhraseGenerationResult>;
264
- /** Record that the user confirmed they saved the phrase. */
265
- markPhraseSaved(challenge: Array<{
266
- position: number;
267
- expected: string;
268
- }>, userAnswers: Array<{
269
- position: number;
270
- word: string;
271
- }>): Promise<boolean>;
334
+ /**
335
+ * Voluntary share rotation — after a suspected device compromise, or just
336
+ * to invalidate an old recovery file. Requires a fresh WebAuthn assertion.
337
+ * Returns the new recovery-share file (the caller still owns delivering it
338
+ * to the user — this does not download anything itself).
339
+ */
340
+ rotateShares(opts: {
341
+ password: string;
342
+ passwordHint?: string;
343
+ }): Promise<RecoveryShareFile>;
344
+ /**
345
+ * The two-share bearer portable backup (device + recovery). This file plus
346
+ * its password gives complete control of the wallet — see the mandatory
347
+ * warning-screen requirements in MIGRATE_SSS_RECOVERY.md §10. Requires a
348
+ * fresh WebAuthn assertion and rotates the share set on every export.
349
+ */
350
+ exportPortableBackup(opts: {
351
+ password: string;
352
+ passwordHint?: string;
353
+ }): Promise<PortableBackup>;
354
+ /** Whether the user has ever saved a recovery-share file (UX flag only — Decane never stores the share). */
355
+ hasRecoveryShare(): Promise<boolean>;
356
+ /** True at most once per account: no recovery share saved, never nagged, wallet holds value. */
357
+ shouldPromptRecovery(): Promise<boolean>;
358
+ /** Call regardless of the user's answer — the recovery nag fires once, ever. */
359
+ dismissRecoveryPrompt(): Promise<void>;
272
360
  /** Whether a wrapped-share backup exists on the server. */
273
361
  hasBackup(): Promise<boolean>;
274
362
  /** Delete the server-side backup. Disables Flow A recovery. */
@@ -319,6 +407,22 @@ declare class SessionExpiredError extends DecaneConnectError {
319
407
  declare class UnlockRequiredError extends DecaneConnectError {
320
408
  constructor();
321
409
  }
410
+ /**
411
+ * unlock() was called after a page reload (or any other loss of the in-memory
412
+ * auth state). We still know *who* the user is — their identity and addresses
413
+ * are persisted — but unlock() needs a live JWT to open a TEE session, and
414
+ * JWTs are deliberately never persisted. So there is no unlock() that can
415
+ * succeed here; the user has to re-authenticate.
416
+ *
417
+ * This is its own error type rather than a generic CallSequenceError because
418
+ * it is a *routing* signal, not a programming mistake: the correct response is
419
+ * to show the sign-in UI, which callers can only do reliably if they can tell
420
+ * this case apart from "you never called connect()". Prefer checking
421
+ * needsReconnect() up front so this never has to be caught at all.
422
+ */
423
+ declare class ReconnectRequiredError extends DecaneConnectError {
424
+ constructor();
425
+ }
322
426
  declare class UserCancelledError extends DecaneConnectError {
323
427
  constructor();
324
428
  }
@@ -349,7 +453,16 @@ declare class AttestationError extends DecaneConnectError {
349
453
  declare class RecoveryFailedError extends DecaneConnectError {
350
454
  constructor(reason: string);
351
455
  }
352
- declare class InvalidPhraseError extends DecaneConnectError {
456
+ declare class InvalidRecoveryFileError extends DecaneConnectError {
457
+ constructor();
458
+ }
459
+ declare class WrongPasswordError extends DecaneConnectError {
460
+ constructor();
461
+ }
462
+ declare class RecoveryWalletMismatchError extends DecaneConnectError {
463
+ constructor();
464
+ }
465
+ declare class ExportNotAuthorizedError extends DecaneConnectError {
353
466
  constructor();
354
467
  }
355
468
 
@@ -467,6 +580,16 @@ interface SocialAuthState {
467
580
  isNewUser: boolean;
468
581
  isUnlocked: boolean;
469
582
  sessionExpiresAt: number | null;
583
+ /**
584
+ * True when `addresses` came from persisted (localStorage) identity only,
585
+ * with no live session. This is NOT the same as "locked" — every unlock()
586
+ * requires a fresh, valid JWT to open a TEE session, and JWTs are
587
+ * deliberately never persisted (short-lived, security-sensitive), so
588
+ * there is no legitimate "just unlock" path in this state. A consumer
589
+ * should route this into a full reconnect (connectWithGoogle() /
590
+ * connectWithEmail()), not unlock() — which will always throw here.
591
+ */
592
+ needsReconnect: boolean;
470
593
  }
471
594
  interface DecaneKitProps {
472
595
  config?: DecaneConfig;
@@ -481,8 +604,24 @@ interface SocialWalletModalProps {
481
604
  socialAuth: SocialAuthState;
482
605
  onUnlock: () => Promise<void>;
483
606
  onDisconnect: () => Promise<void>;
607
+ onGoogleSignIn: () => Promise<void>;
608
+ onEmailStart: (email: string) => Promise<{
609
+ codeRequired: true;
610
+ }>;
611
+ onEmailVerify: (email: string, code: string) => Promise<void>;
612
+ authMethods?: Array<"google" | "email">;
613
+ onRotateShares: (opts: {
614
+ password: string;
615
+ passwordHint?: string;
616
+ }) => Promise<RecoveryShareFile>;
617
+ onExportPortableBackup: (opts: {
618
+ password: string;
619
+ passwordHint?: string;
620
+ }) => Promise<PortableBackup>;
621
+ rpcUrls?: Record<string, string>;
622
+ chains?: string[];
484
623
  }
485
- declare function SocialWalletModal({ open, onClose, theme, socialAuth, onUnlock, onDisconnect, }: SocialWalletModalProps): React.ReactPortal | null;
624
+ declare function SocialWalletModal({ open, onClose, theme, socialAuth, onUnlock, onDisconnect, onGoogleSignIn, onEmailStart, onEmailVerify, authMethods, onRotateShares, onExportPortableBackup, rpcUrls, chains, }: SocialWalletModalProps): React.ReactPortal | null;
486
625
 
487
626
  declare function useSocialAuth(): {
488
627
  isConnected: boolean;
@@ -493,6 +632,7 @@ declare function useSocialAuth(): {
493
632
  solana?: string;
494
633
  } | null;
495
634
  sessionExpiresAt: number | null;
635
+ needsReconnect: boolean;
496
636
  googleLoading: boolean;
497
637
  emailLoading: boolean;
498
638
  error: string | null;
@@ -514,6 +654,7 @@ declare function useSocialWallet(): {
514
654
  solana?: string;
515
655
  } | null;
516
656
  sessionExpiresAt: number | null;
657
+ needsReconnect: boolean;
517
658
  modalOpen: boolean;
518
659
  openModal: () => void;
519
660
  closeModal: () => void;
@@ -529,6 +670,17 @@ declare function useSocialWallet(): {
529
670
  gasPrice?: bigint;
530
671
  }) => Promise<string>;
531
672
  signSolanaTransaction: (tx: Uint8Array) => Promise<Uint8Array<ArrayBufferLike>>;
673
+ rotateShares: (opts: {
674
+ password: string;
675
+ passwordHint?: string;
676
+ }) => Promise<RecoveryShareFile>;
677
+ exportPortableBackup: (opts: {
678
+ password: string;
679
+ passwordHint?: string;
680
+ }) => Promise<PortableBackup>;
681
+ hasRecoveryShare: () => Promise<boolean>;
682
+ shouldPromptRecovery: () => Promise<boolean>;
683
+ dismissRecoveryPrompt: () => Promise<void>;
532
684
  loading: boolean;
533
685
  error: string | null;
534
686
  clearError: () => void;
@@ -605,4 +757,4 @@ declare function useSignTypedData(): {
605
757
  reset: () => void;
606
758
  };
607
759
 
608
- export { type ActiveSession, AttestationError, type AuthResult, BUILT_IN_CHAINS, type CAIP25Method, type CAIP25SessionRequest, type CAIP25SessionResult, CHAINS, CallSequenceError, type Chain, type ChainDescriptor, type ChainId, type ChainScope, type ChainSupport, type ChainType, type ConnectMode, type ConnectResult, type DecaneConfig, type DecaneConnect, type DecaneConnectConfig, DecaneConnectError, DecaneError, DecaneErrorCode, DecaneKit, type DecaneProvider, InvalidApiKeyError, InvalidPhraseError, InvalidPinError, NetworkError, NewDeviceError, OriginNotAllowedError, type PhraseGenerationResult, PromptPinNotConfiguredError, RecoveryFailedError, type RecoveryProgress, type SdkEvent, type SendTransactionRequest, SessionExpiredError, ShareCorruptedError, type SignMessageRequest, type SocialAuthState, type SocialConfig, SocialWalletModal, type Theme, type TxParams, type UnifiedWalletAccount, UnlockRequiredError, UserCancelledError, WalletSelector, WebAuthnNotSupportedError, type WrappedShareMeta, createDecane, createDecaneConnect, startEmailAuth, useChainSwitch, useDecane, useSendTransaction, useSignMessage, useSignTypedData, useSocialAuth, useSocialWallet, useWalletSelector, verifyEmailCode };
760
+ export { type ActiveSession, AttestationError, type AuthResult, BUILT_IN_CHAINS, type CAIP25Method, type CAIP25SessionRequest, type CAIP25SessionResult, CHAINS, CallSequenceError, type Chain, type ChainDescriptor, type ChainId, type ChainScope, type ChainSupport, type ChainType, type ConnectMode, type ConnectResult, type DecaneConfig, type DecaneConnect, type DecaneConnectConfig, DecaneConnectError, DecaneError, DecaneErrorCode, DecaneKit, type DecaneProvider, ExportNotAuthorizedError, InvalidApiKeyError, InvalidPinError, InvalidRecoveryFileError, NetworkError, NewDeviceError, OriginNotAllowedError, type PortableBackup, PromptPinNotConfiguredError, ReconnectRequiredError, RecoveryFailedError, type RecoveryProgress, type RecoveryShareFile, RecoveryWalletMismatchError, type SdkEvent, type SendTransactionRequest, SessionExpiredError, ShareCorruptedError, type SignMessageRequest, type SocialAuthState, type SocialConfig, SocialWalletModal, type Theme, type TxParams, type UnifiedWalletAccount, UnlockRequiredError, UserCancelledError, WalletSelector, WebAuthnNotSupportedError, type WrappedShareMeta, WrongPasswordError, createDecane, createDecaneConnect, startEmailAuth, useChainSwitch, useDecane, useSendTransaction, useSignMessage, useSignTypedData, useSocialAuth, useSocialWallet, useWalletSelector, verifyEmailCode };