decane-connect-kit 2.7.4 → 2.7.6

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,420 +1,137 @@
1
1
  import * as _simplewebauthn_browser from '@simplewebauthn/browser';
2
2
  import React, { ReactNode } from 'react';
3
3
 
4
- type ChainType = "evm" | "solana" | "tron" | "bitcoin";
5
- type ChainSupport = ChainType[];
6
- interface ChainDescriptor {
7
- id: string;
8
- type: ChainType;
9
- name: string;
10
- nativeCurrency: {
11
- name: string;
12
- symbol: string;
13
- decimals: number;
4
+ interface RecoveryShareFile {
5
+ version: number;
6
+ type: 'decane-recovery-share';
7
+ shareSetVersion: number;
8
+ kdf: 'pbkdf2-sha256';
9
+ kdfParams: {
10
+ iterations: number;
11
+ salt: string;
14
12
  };
15
- rpcUrl?: string;
16
- blockExplorerUrl?: string;
17
- iconUrl?: string;
18
- chainId?: number;
19
- isCustom?: boolean;
20
- }
21
- interface UnifiedWalletAccount {
22
- id: string;
23
- name: string;
24
- icon: string;
25
- rdns?: string;
26
- chainSupport: ChainSupport;
27
- evmProvider?: EIP1193Provider;
28
- solanaProvider?: StandardWallet;
29
- tronProvider?: TronProvider;
30
- bitcoinProvider?: BitcoinProvider;
31
- }
32
- type CAIP25Method = "wallet_requestPermissions" | "wallet_revokePermissions" | "wallet_getPermissions" | "wallet_sendTransaction" | "wallet_signMessage" | "wallet_signTypedData";
33
- interface ChainScope {
34
- methods: CAIP25Method[];
35
- accounts: string[];
36
- events?: string[];
37
- }
38
- interface CAIP25SessionRequest {
39
- requiredScopes: Partial<Record<string, ChainScope>>;
40
- optionalScopes: Partial<Record<string, ChainScope>>;
13
+ cipher: 'aes-256-gcm';
14
+ iv: string;
15
+ ciphertext: string;
16
+ addresses: {
17
+ evm: string;
18
+ solana: string;
19
+ };
20
+ passwordHint: string | null;
21
+ createdAt: string;
41
22
  }
42
- interface CAIP25SessionResult {
43
- sessionId: string;
44
- grantedScopes: Partial<Record<string, ChainScope>>;
23
+
24
+ interface PortableBackup {
25
+ version: number;
26
+ type: 'decane-portable-backup';
27
+ shareSetVersion: number;
28
+ kdf: 'pbkdf2-sha256';
29
+ kdfParams: {
30
+ iterations: number;
31
+ salt: string;
32
+ };
33
+ cipher: 'aes-256-gcm';
34
+ iv: string;
35
+ ciphertext: string;
36
+ addresses: {
37
+ evm: string;
38
+ solana: string;
39
+ };
40
+ passwordHint: string | null;
41
+ createdAt: string;
45
42
  }
46
- interface ActiveSession {
47
- sessionId: string;
48
- grantedScopes: Partial<Record<string, ChainScope>>;
49
- walletId: string;
50
- walletType: ChainType | "multi";
51
- createdAt: number;
43
+
44
+ interface BalanceResult {
45
+ /** Smallest unit — wei for EVM, lamports for Solana. */
46
+ raw: bigint;
47
+ /** Human-readable amount in whole native units, e.g. "1.2345". */
48
+ formatted: string;
49
+ /** Native currency symbol, e.g. "ETH", "MATIC", "SOL". */
50
+ symbol: string;
51
+ /** Decimals of the native currency (18 for EVM, 9 for Solana). */
52
+ decimals: number;
52
53
  }
53
- interface DecaneProvider {
54
+
55
+ interface Eip1193Provider {
54
56
  request(args: {
55
57
  method: string;
56
- params?: unknown[];
58
+ params?: unknown[] | object;
57
59
  }): Promise<unknown>;
58
- on(event: string, handler: (...args: unknown[]) => void): void;
59
- off(event: string, handler: (...args: unknown[]) => void): void;
60
- readonly wallets: UnifiedWalletAccount[];
61
- readonly evmWallets: UnifiedWalletAccount[];
62
- readonly solanaWallets: UnifiedWalletAccount[];
63
- readonly multiChainWallets: UnifiedWalletAccount[];
64
- readonly tronWallets: UnifiedWalletAccount[];
65
- readonly bitcoinWallets: UnifiedWalletAccount[];
66
- connect(wallet: UnifiedWalletAccount, preferredChain?: ChainType): Promise<void>;
67
- disconnect(): Promise<void>;
68
- addChain(chain: ChainDescriptor): void;
69
- removeChain(chainId: string): void;
70
- getChain(chainId: string): ChainDescriptor | undefined;
71
- readonly chains: ChainDescriptor[];
72
- readonly connectedWallet: UnifiedWalletAccount | null;
73
- readonly activeSession: ActiveSession | null;
74
- readonly activeChain: string | null;
60
+ on(event: string, handler: (...args: unknown[]) => void): this;
61
+ removeListener(event: string, handler: (...args: unknown[]) => void): this;
62
+ /** Marks this as the Decane provider (parallel to wallets' isMetaMask etc.). */
63
+ readonly isDecane: true;
75
64
  }
76
- type ConnectMode = "all" | "social" | "wallets" | "evm" | "solana" | "tron" | "evm+social" | "solana+social" | "tron+social" | "evm+tron" | "evm+tron+social";
77
- interface SocialConfig {
65
+ interface EthereumProviderOptions {
66
+ /** Initial chain id (numeric). Defaults to the first configured EVM chain, else 1. */
67
+ chainId?: number;
68
+ /** Per-chain RPC overrides ('evm:8453' -> url). Falls back to Decane's defaults. */
69
+ rpcUrls?: Record<string, string>;
70
+ }
71
+ /**
72
+ * Build an EIP-1193 provider over a connected Decane social client.
73
+ *
74
+ * `signAuthorization` (EIP-7702) is wired only when the client exposes it — that
75
+ * requires the TEE signer to support the authorization signing path.
76
+ */
77
+ declare function createEthereumProvider(client: DecaneConnect, opts?: EthereumProviderOptions): Eip1193Provider;
78
+
79
+ interface SignedAuthorization {
80
+ chainId: number;
81
+ address: string;
82
+ nonce: number;
83
+ yParity: number;
84
+ r: string;
85
+ s: string;
86
+ }
87
+
88
+ type Chain = `evm:${number}` | `solana:${'mainnet' | 'devnet'}` | `tron:${'mainnet' | 'shasta' | 'nile'}`;
89
+ interface DecaneConnectConfig {
90
+ /** Dashboard project ID (public). Sent with every request so the backend can
91
+ * confirm the API key below actually belongs to this project. */
92
+ appId: string;
78
93
  apiKey: string;
79
- authMethods?: ("google" | "email" | "kingschat")[];
94
+ chains: Chain[];
95
+ authMethods: Array<'google' | 'email' | 'kingschat'>;
80
96
  /**
81
- * Scopes requested from KingsChat. Defaults to `['send_chat_message']`, which
82
- * is what KingsChat's own SDK asks for and is enough to establish identity.
97
+ * Scopes requested from KingsChat at sign-in. Defaults to
98
+ * `['send_chat_message']`, which is what KingsChat's own SDK asks for and is
99
+ * enough to establish identity. Widen only if the app actually calls
100
+ * KingsChat APIs on the user's behalf — the consent screen lists every scope,
101
+ * so asking for more than you use costs conversions.
83
102
  */
84
103
  kingsChatScopes?: string[];
85
- promptPin?: () => Promise<string>;
86
104
  apiBaseUrl?: string;
87
- chains?: string[];
88
- rpcUrls?: Record<string, string>;
105
+ sessionDurationMinutes?: number;
106
+ promptPin?: () => Promise<string>;
89
107
  /**
90
- * Require a fresh WebAuthn assertion for every signature rather than letting
91
- * an unlocked session sign freely until it expires. Enforced inside the TEE.
108
+ * Require a fresh WebAuthn assertion for every signature, instead of letting
109
+ * an unlocked session sign freely until it expires. The TEE session stays
110
+ * warm — this costs one authenticator prompt per signature, not a re-auth or
111
+ * re-attestation — and the check is enforced inside the enclave, not here.
92
112
  *
93
- * Does not change the session lifetime — the session stays warm (no
94
- * re-attestation, no re-auth) and authority is re-proven per signature
95
- * instead. Default false. Needs a passkey; PIN-only wallets have no
96
- * authenticator to assert with. See SocialConfig's counterpart in
97
- * social/types.ts for the full caveats.
113
+ * Default false, which preserves the session-signing model: unlock once, sign
114
+ * for the session's lifetime with no further prompts.
115
+ *
116
+ * Two things worth knowing before turning this on:
117
+ * - Multi-step flows prompt per leg. An ERC-20 approve + swap is two
118
+ * prompts, not one.
119
+ * - The policy is declared by the client at session open, so it hardens a
120
+ * leaked session handle (useless without the authenticator) but a fully
121
+ * compromised page could open a session without it. Making it
122
+ * unbypassable requires the policy to come from the server per user or
123
+ * per project; that is not implemented yet.
98
124
  */
99
125
  requireAssertionPerSignature?: boolean;
126
+ rpId?: string;
127
+ rpName?: string;
128
+ rpcUrls?: Record<string, string>;
129
+ teeUrl?: string;
100
130
  /**
101
- * Sign in with a token from your own auth system (Auth0, Clerk, Firebase,
102
- * Cognito, or your own backend) — surfaced as a button in the built-in
103
- * WalletSelector / SocialWalletModal UI, alongside Google and email.
104
- * getToken() runs when the user clicks the button; wire it to however you
105
- * already obtain a token from your auth system (e.g. after your own login
106
- * flow completes). Configure the issuer and JWKS URL for this token
107
- * against your project in the dashboard first. `providerId` is only
108
- * needed when the project has more than one custom provider configured.
109
- */
110
- customAuth?: {
111
- label: string;
112
- providerId?: string;
113
- getToken: () => Promise<string>;
114
- };
115
- }
116
- interface DecaneConfig {
117
- appId: string;
118
- chains?: ChainDescriptor[];
119
- supportedChainIds?: string[];
120
- networks?: "evm" | "solana" | "tron" | "bitcoin" | "all";
121
- defaultChain?: string;
122
- theme?: "light" | "dark" | "auto";
123
- mode?: ConnectMode;
124
- social?: SocialConfig;
125
- }
126
- interface EIP1193Provider {
127
- request(args: {
128
- method: string;
129
- params?: unknown[];
130
- }): Promise<unknown>;
131
- on(event: string, handler: (...args: unknown[]) => void): void;
132
- removeListener(event: string, handler: (...args: unknown[]) => void): void;
133
- }
134
- interface StandardWallet {
135
- name: string;
136
- icon: string;
137
- features: Record<string, unknown>;
138
- accounts: StandardWalletAccount[];
139
- }
140
- interface StandardWalletAccount {
141
- address: string;
142
- publicKey: Uint8Array;
143
- chains: string[];
144
- }
145
- interface TronProvider {
146
- request(args: {
147
- method: string;
148
- params?: unknown[] | Record<string, unknown>;
149
- }): Promise<unknown>;
150
- on(event: string, handler: (...args: unknown[]) => void): void;
151
- removeListener(event: string, handler: (...args: unknown[]) => void): void;
152
- }
153
- interface BitcoinProvider {
154
- requestAccounts(): Promise<string[] | Array<{
155
- address: string;
156
- }>>;
157
- getAccounts?(): Promise<string[] | Array<{
158
- address: string;
159
- }>>;
160
- signMessage(message: string, ...args: unknown[]): Promise<string>;
161
- sendBitcoin?(toAddress: string, satoshis: number, options?: unknown): Promise<string>;
162
- send?(params: unknown): Promise<string>;
163
- signPsbt?(psbtHex: string, options?: unknown): Promise<string>;
164
- on?(event: string, handler: (...args: unknown[]) => void): void;
165
- }
166
-
167
- declare function createDecane(config: DecaneConfig): DecaneProvider;
168
-
169
- declare class ApiClient {
170
- private readonly baseUrl;
171
- private readonly apiKey;
172
- private readonly appId;
173
- constructor(baseUrl: string, apiKey: string, appId: string);
174
- private _request;
175
- getGoogleInitUrl(): Promise<{
176
- url: string;
177
- }>;
178
- /**
179
- * Exchange a Google ID token the app already holds for a Decane session.
180
- * Resolves to the SAME identity as the redirect flow, so one Google account
181
- * means one wallet across web and mobile.
182
- */
183
- authGoogleToken(idToken: string): Promise<AuthResult>;
184
- /** The project's KingsChat clientId, so the dApp never has to hardcode it. */
185
- getKingsChatConfig(): Promise<{
186
- clientId: string;
187
- authorizationUrl: string;
188
- }>;
189
- /**
190
- * Exchange a KingsChat access token for a Decane session. The backend spends
191
- * the token against KingsChat's own API to establish the identity — it is
192
- * never trusted on its face, and never stored.
193
- */
194
- authKingsChat(accessToken: string): Promise<AuthResult>;
195
- authEmailStart(email: string): Promise<void>;
196
- authEmailVerify(email: string, code: string): Promise<AuthResult>;
197
- getAddresses(jwt: string): Promise<{
198
- evm: string | null;
199
- solana: string | null;
200
- tron: string | null;
201
- }>;
202
- putShare(jwt: string, share: {
203
- ciphertext: Uint8Array;
204
- iv: Uint8Array;
205
- wrappedShareMeta: WrappedShareMeta;
206
- evmAddress: string;
207
- solanaAddress: string;
208
- tronAddress: string;
209
- }): Promise<void>;
210
- recordTransaction(jwt: string, tx: {
211
- chain: string;
212
- txHash?: string;
213
- to?: string;
214
- value?: string;
215
- kind?: string;
216
- status?: string;
217
- metadata?: Record<string, unknown>;
218
- }): Promise<TransactionRecord>;
219
- getTransactions(jwt: string, limit?: number): Promise<TransactionRecord[]>;
220
- getWebAuthnRegisterOptions(jwt: string, rpId: string, rpName: string): Promise<_simplewebauthn_browser.PublicKeyCredentialCreationOptionsJSON>;
221
- postWebAuthnRegister(jwt: string, response: _simplewebauthn_browser.RegistrationResponseJSON, origin: string): Promise<void>;
222
- /**
223
- * Exchange a token from the developer's own auth system for a Decane
224
- * session. `providerId` is only needed when the project has more than one
225
- * provider configured.
226
- */
227
- authExternal(token: string, providerId?: string): Promise<AuthResult>;
228
- /**
229
- * Returns null when this user has no server-registered passkey, which is the
230
- * expected answer for wallets created before passkey sign-in existed or for
231
- * PIN-only users — the caller falls back to the normal sign-in flow.
232
- */
233
- getWebAuthnLoginOptions(userId: string, rpId: string): Promise<_simplewebauthn_browser.PublicKeyCredentialRequestOptionsJSON | null>;
234
- postWebAuthnLogin(userId: string, response: _simplewebauthn_browser.AuthenticationResponseJSON, origin: string): Promise<AuthResult>;
235
- revoke(jwt: string): Promise<void>;
236
- hasRecoveryBackup(jwt: string): Promise<boolean>;
237
- getRecoveryBackup(jwt: string): Promise<{
238
- wrappedDeviceShare: Uint8Array;
239
- iv: Uint8Array;
240
- unlockMethod: 'passkey';
241
- credentialId: Uint8Array;
242
- prfSalt: Uint8Array;
243
- formatVersion: number;
244
- } | null>;
245
- putRecoveryBackup(jwt: string, backup: {
246
- wrappedDeviceShare: Uint8Array;
247
- iv: Uint8Array;
248
- unlockMethod: 'passkey';
249
- credentialId: Uint8Array;
250
- prfSalt: Uint8Array;
251
- }): Promise<void>;
252
- deleteRecoveryBackup(jwt: string): Promise<void>;
253
- markRecoveryShareSaved(jwt: string): Promise<void>;
254
- getRecoveryStatus(jwt: string): Promise<{
255
- hasRecoveryShare: boolean;
256
- savedAt: string | null;
257
- nagged: boolean;
258
- shareSetVersion: number;
259
- /**
260
- * Whether this wallet is enrolled for server-assisted provisioning, i.e.
261
- * whether a new device can be given a device share from login alone.
262
- * Optional because backends older than this field simply omit it — treat
263
- * `undefined` as "unknown", never as "not enrolled".
264
- */
265
- enrolled?: boolean;
266
- }>;
267
- dismissRecoveryNag(jwt: string): Promise<void>;
268
- }
269
-
270
- type RecoveryProgress = {
271
- type: 'passkey-attempt';
272
- } | {
273
- type: 'passkey-failed';
274
- reason: string;
275
- } | {
276
- type: 'recovery-file-prompt';
277
- } | {
278
- type: 'password-prompt';
279
- passwordHint: string | null;
280
- } | {
281
- type: 'recovery-verifying';
282
- } | {
283
- type: 'rotated';
284
- shareSetVersion: number;
285
- };
286
-
287
- interface RecoveryShareFile {
288
- version: number;
289
- type: 'decane-recovery-share';
290
- shareSetVersion: number;
291
- kdf: 'pbkdf2-sha256';
292
- kdfParams: {
293
- iterations: number;
294
- salt: string;
295
- };
296
- cipher: 'aes-256-gcm';
297
- iv: string;
298
- ciphertext: string;
299
- addresses: {
300
- evm: string;
301
- solana: string;
302
- };
303
- passwordHint: string | null;
304
- createdAt: string;
305
- }
306
-
307
- interface PortableBackup {
308
- version: number;
309
- type: 'decane-portable-backup';
310
- shareSetVersion: number;
311
- kdf: 'pbkdf2-sha256';
312
- kdfParams: {
313
- iterations: number;
314
- salt: string;
315
- };
316
- cipher: 'aes-256-gcm';
317
- iv: string;
318
- ciphertext: string;
319
- addresses: {
320
- evm: string;
321
- solana: string;
322
- };
323
- passwordHint: string | null;
324
- createdAt: string;
325
- }
326
-
327
- interface BalanceResult {
328
- /** Smallest unit — wei for EVM, lamports for Solana. */
329
- raw: bigint;
330
- /** Human-readable amount in whole native units, e.g. "1.2345". */
331
- formatted: string;
332
- /** Native currency symbol, e.g. "ETH", "MATIC", "SOL". */
333
- symbol: string;
334
- /** Decimals of the native currency (18 for EVM, 9 for Solana). */
335
- decimals: number;
336
- }
337
-
338
- interface Eip1193Provider {
339
- request(args: {
340
- method: string;
341
- params?: unknown[] | object;
342
- }): Promise<unknown>;
343
- on(event: string, handler: (...args: unknown[]) => void): this;
344
- removeListener(event: string, handler: (...args: unknown[]) => void): this;
345
- /** Marks this as the Decane provider (parallel to wallets' isMetaMask etc.). */
346
- readonly isDecane: true;
347
- }
348
- interface EthereumProviderOptions {
349
- /** Initial chain id (numeric). Defaults to the first configured EVM chain, else 1. */
350
- chainId?: number;
351
- /** Per-chain RPC overrides ('evm:8453' -> url). Falls back to Decane's defaults. */
352
- rpcUrls?: Record<string, string>;
353
- }
354
- /**
355
- * Build an EIP-1193 provider over a connected Decane social client.
356
- *
357
- * `signAuthorization` (EIP-7702) is wired only when the client exposes it — that
358
- * requires the TEE signer to support the authorization signing path.
359
- */
360
- declare function createEthereumProvider(client: DecaneConnect, opts?: EthereumProviderOptions): Eip1193Provider;
361
-
362
- interface SignedAuthorization {
363
- chainId: number;
364
- address: string;
365
- nonce: number;
366
- yParity: number;
367
- r: string;
368
- s: string;
369
- }
370
-
371
- type Chain = `evm:${number}` | `solana:${'mainnet' | 'devnet'}` | `tron:${'mainnet' | 'shasta' | 'nile'}`;
372
- interface DecaneConnectConfig {
373
- /** Dashboard project ID (public). Sent with every request so the backend can
374
- * confirm the API key below actually belongs to this project. */
375
- appId: string;
376
- apiKey: string;
377
- chains: Chain[];
378
- authMethods: Array<'google' | 'email' | 'kingschat'>;
379
- /**
380
- * Scopes requested from KingsChat at sign-in. Defaults to
381
- * `['send_chat_message']`, which is what KingsChat's own SDK asks for and is
382
- * enough to establish identity. Widen only if the app actually calls
383
- * KingsChat APIs on the user's behalf — the consent screen lists every scope,
384
- * so asking for more than you use costs conversions.
385
- */
386
- kingsChatScopes?: string[];
387
- apiBaseUrl?: string;
388
- sessionDurationMinutes?: number;
389
- promptPin?: () => Promise<string>;
390
- /**
391
- * Require a fresh WebAuthn assertion for every signature, instead of letting
392
- * an unlocked session sign freely until it expires. The TEE session stays
393
- * warm — this costs one authenticator prompt per signature, not a re-auth or
394
- * re-attestation — and the check is enforced inside the enclave, not here.
395
- *
396
- * Default false, which preserves the session-signing model: unlock once, sign
397
- * for the session's lifetime with no further prompts.
398
- *
399
- * Two things worth knowing before turning this on:
400
- * - Multi-step flows prompt per leg. An ERC-20 approve + swap is two
401
- * prompts, not one.
402
- * - The policy is declared by the client at session open, so it hardens a
403
- * leaked session handle (useless without the authenticator) but a fully
404
- * compromised page could open a session without it. Making it
405
- * unbypassable requires the policy to come from the server per user or
406
- * per project; that is not implemented yet.
407
- */
408
- requireAssertionPerSignature?: boolean;
409
- rpId?: string;
410
- rpName?: string;
411
- rpcUrls?: Record<string, string>;
412
- teeUrl?: string;
413
- /**
414
- * Persist the live session (handle + JWT) in sessionStorage so a page RELOAD
415
- * silently resumes it — no repeat passkey/sign-in. Survives reloads, cleared
416
- * on tab close. Default true. Set false to require a fresh unlock on every
417
- * page load (stricter, but the reload friction that motivated this returns).
131
+ * Persist the live session (handle + JWT) in sessionStorage so a page RELOAD
132
+ * silently resumes it — no repeat passkey/sign-in. Survives reloads, cleared
133
+ * on tab close. Default true. Set false to require a fresh unlock on every
134
+ * page load (stricter, but the reload friction that motivated this returns).
418
135
  */
419
136
  persistSession?: boolean;
420
137
  /**
@@ -584,180 +301,526 @@ interface DecaneConnect {
584
301
  codeRequired: true;
585
302
  }>;
586
303
  /**
587
- * Sign in with a JWT from your own auth system (Auth0, Clerk, Firebase,
588
- * Cognito, your own backend — anything that publishes a JWKS endpoint).
304
+ * Sign in with a JWT from your own auth system (Auth0, Clerk, Firebase,
305
+ * Cognito, your own backend — anything that publishes a JWKS endpoint).
306
+ *
307
+ * Configure the issuer and JWKS URL against your project in the dashboard;
308
+ * no client-side setup and no secret in your dApp. `providerId` is only
309
+ * required if the project has more than one provider configured.
310
+ *
311
+ * The user's wallet is derived from the configured identity claim, so the
312
+ * same token subject always resolves to the same wallet.
313
+ */
314
+ connectWithToken(token: string, opts?: {
315
+ providerId?: string;
316
+ }): Promise<ConnectResult>;
317
+ verifyEmailCode(email: string, code: string): Promise<ConnectResult>;
318
+ unlock(): Promise<void>;
319
+ isUnlocked(): boolean;
320
+ /**
321
+ * True when the user's identity is known from a previous visit but there is
322
+ * no live session, so unlock() cannot succeed and they must sign in again.
323
+ * Check this before offering an "Unlock" action.
324
+ *
325
+ * Note this can be true while canSignInWithPasskey() is also true — in which
326
+ * case "sign in again" costs one biometric prompt, not a full auth round
327
+ * trip. Prefer offering that over a Google/email button.
328
+ */
329
+ needsReconnect(): boolean;
330
+ /**
331
+ * True when a returning user can be signed straight back in with the passkey
332
+ * already on this device — no Google redirect, no email code. Requires a
333
+ * remembered identity plus a passkey-wrapped device share in local storage,
334
+ * so it is false on new devices and for PIN-only wallets.
335
+ */
336
+ canSignInWithPasskey(): Promise<boolean>;
337
+ /**
338
+ * Sign in using only the device passkey. connect() already tries this first
339
+ * when it's available, so call this directly only when driving the UI
340
+ * yourself. Throws NewDeviceError when this device isn't eligible.
341
+ */
342
+ signInWithPasskey(): Promise<ConnectResult>;
343
+ sessionExpiresAt(): number | null;
344
+ signMessage(req: SignMessageRequest): Promise<string>;
345
+ sendTransaction(req: SendTransactionRequest): Promise<string>;
346
+ /** Sign EIP-712 typed data (EVM only). Throws on a Solana chain. */
347
+ signTypedData(req: SignTypedDataRequest): Promise<string>;
348
+ /**
349
+ * A standard EIP-1193 Ethereum provider backed by this wallet — drop it into
350
+ * viem (`custom(provider)`), wagmi, or ethers (`BrowserProvider`). Signing
351
+ * goes through the TEE; reads are forwarded to the chain RPC. Memoized, so
352
+ * repeated calls return the same instance (safe for wagmi).
353
+ */
354
+ getEthereumProvider(opts?: {
355
+ chainId?: number;
356
+ }): Eip1193Provider;
357
+ /**
358
+ * Sign an EIP-7702 authorization, delegating this EOA to a smart-account
359
+ * implementation contract on `chainId`. Enables account-abstraction /
360
+ * sponsored-gas flows. Returns the full viem `SignedAuthorization` tuple, so
361
+ * it drops straight into a userOperation's `authorization` field or a type-4
362
+ * transaction's authorizationList. `address` and `contractAddress` are
363
+ * interchangeable (the latter matches Privy's/older viem's param name).
364
+ */
365
+ signAuthorization(req: {
366
+ chainId: number;
367
+ address?: string;
368
+ contractAddress?: string;
369
+ nonce: number;
370
+ }): Promise<SignedAuthorization>;
371
+ /**
372
+ * The current session's access token (the Decane JWT). Use it to authenticate
373
+ * the user to your own backend (e.g. a sponsored-bundler proxy); verify it
374
+ * server-side against backend.decane.app/.well-known/jwks.json. Null with no
375
+ * live session.
376
+ */
377
+ getAccessToken(): string | null;
378
+ signSolanaTransaction(tx: Uint8Array): Promise<Uint8Array>;
379
+ /**
380
+ * Sign a Tron transaction. Pass the transaction's `raw_data_hex` (the value
381
+ * TronWeb puts on a built transaction). The enclave computes the txID
382
+ * (sha256(raw_data)) and signs it with the same secp256k1 key as the EVM
383
+ * wallet; returns the 65-byte recoverable signature as 0x-hex. Attach it to
384
+ * the transaction's `signature` array and broadcast via TronWeb / a full node.
385
+ */
386
+ signTronTransaction(rawDataHex: string): Promise<string>;
387
+ /**
388
+ * Record a transaction this wallet broadcast, so it appears in the user's
389
+ * history (and the developer dashboard). EVM `sendTransaction` records itself
390
+ * automatically; call this for Solana/Tron transactions your dApp broadcasts,
391
+ * or to attach a `confirmed`/`failed` status update to an existing hash.
392
+ */
393
+ recordTransaction(tx: RecordTransactionInput): Promise<TransactionRecord>;
394
+ /** The user's transaction history, most recent first. Requires a live session. */
395
+ getTransactionHistory(limit?: number): Promise<TransactionRecord[]>;
396
+ disconnect(): Promise<void>;
397
+ getAddresses(): {
398
+ evm?: string;
399
+ solana?: string;
400
+ tron?: string;
401
+ } | null;
402
+ /**
403
+ * Native-token balance for a chain (defaults to the first configured EVM
404
+ * chain, else Solana). Read-only public data — works even while the wallet
405
+ * is locked. Returns raw (wei/lamports), a human `formatted` string, the
406
+ * native `symbol`, and `decimals`.
407
+ */
408
+ getBalance(chain?: string): Promise<BalanceResult>;
409
+ /** Native balances across every configured chain the wallet has an address for. */
410
+ getBalances(): Promise<Array<{
411
+ chain: string;
412
+ balance: BalanceResult | null;
413
+ }>>;
414
+ /**
415
+ * Voluntary share rotation — after a suspected device compromise, or just
416
+ * to invalidate an old recovery file. Requires a fresh WebAuthn assertion.
417
+ * Returns the new recovery-share file (the caller still owns delivering it
418
+ * to the user — this does not download anything itself).
419
+ */
420
+ rotateShares(opts: {
421
+ password: string;
422
+ passwordHint?: string;
423
+ }): Promise<RecoveryShareFile>;
424
+ /**
425
+ * The two-share bearer portable backup (device + recovery). This file plus
426
+ * its password gives complete control of the wallet — see the mandatory
427
+ * warning-screen requirements in MIGRATE_SSS_RECOVERY.md §10. Requires a
428
+ * fresh WebAuthn assertion and rotates the share set on every export.
429
+ */
430
+ exportPortableBackup(opts: {
431
+ password: string;
432
+ passwordHint?: string;
433
+ }): Promise<PortableBackup>;
434
+ /** Whether the user has ever saved a recovery-share file (UX flag only — Decane never stores the share). */
435
+ hasRecoveryShare(): Promise<boolean>;
436
+ /** True at most once per account: no recovery share saved, never nagged, wallet holds value. */
437
+ shouldPromptRecovery(): Promise<boolean>;
438
+ /** Call regardless of the user's answer — the recovery nag fires once, ever. */
439
+ dismissRecoveryPrompt(): Promise<void>;
440
+ /** Whether a wrapped-share backup exists on the server. */
441
+ hasBackup(): Promise<boolean>;
442
+ /** Delete the server-side backup. Disables Flow A recovery. */
443
+ deleteBackup(): Promise<void>;
444
+ on(event: SdkEvent, handler: (...args: unknown[]) => void): void;
445
+ off(event: SdkEvent, handler: (...args: unknown[]) => void): void;
446
+ }
447
+ interface WrappedShareMeta {
448
+ unlockMethod: 'passkey' | 'pin';
449
+ credentialId?: string;
450
+ prfSalt?: string;
451
+ pinSalt?: string;
452
+ pinIterations?: number;
453
+ }
454
+ /**
455
+ * Who the user is, as the identity provider just described them.
456
+ *
457
+ * Passed through from the provider at sign-in and never stored by Decane — the
458
+ * backend keeps only an HMAC and a masked identifier, so there is no profile to
459
+ * look up later. Cache what you need at sign-in if you want to show a name
460
+ * after a page reload.
461
+ *
462
+ * Every field is optional because providers differ in what they release, and a
463
+ * user can withhold most of it. Email sign-in returns an empty profile.
464
+ */
465
+ interface AuthProfile {
466
+ name?: string;
467
+ email?: string;
468
+ picture?: string;
469
+ }
470
+ interface AuthResult {
471
+ jwt: string;
472
+ isNewUser: boolean;
473
+ hasShare: boolean;
474
+ userId: string;
475
+ profile?: AuthProfile;
476
+ }
477
+
478
+ declare class ApiClient {
479
+ private readonly baseUrl;
480
+ private readonly apiKey;
481
+ private readonly appId;
482
+ constructor(baseUrl: string, apiKey: string, appId: string);
483
+ private _request;
484
+ getGoogleInitUrl(): Promise<{
485
+ url: string;
486
+ }>;
487
+ /**
488
+ * Exchange a Google ID token the app already holds for a Decane session.
489
+ * Resolves to the SAME identity as the redirect flow, so one Google account
490
+ * means one wallet across web and mobile.
491
+ */
492
+ authGoogleToken(idToken: string): Promise<AuthResult>;
493
+ /** The project's KingsChat clientId, so the dApp never has to hardcode it. */
494
+ getKingsChatConfig(): Promise<{
495
+ clientId: string;
496
+ authorizationUrl: string;
497
+ }>;
498
+ /**
499
+ * Exchange a KingsChat access token for a Decane session. The backend spends
500
+ * the token against KingsChat's own API to establish the identity — it is
501
+ * never trusted on its face, and never stored.
502
+ */
503
+ authKingsChat(accessToken: string): Promise<AuthResult>;
504
+ authEmailStart(email: string): Promise<void>;
505
+ authEmailVerify(email: string, code: string): Promise<AuthResult>;
506
+ getAddresses(jwt: string): Promise<{
507
+ evm: string | null;
508
+ solana: string | null;
509
+ tron: string | null;
510
+ }>;
511
+ putShare(jwt: string, share: {
512
+ ciphertext: Uint8Array;
513
+ iv: Uint8Array;
514
+ wrappedShareMeta: WrappedShareMeta;
515
+ evmAddress: string;
516
+ solanaAddress: string;
517
+ tronAddress: string;
518
+ }): Promise<void>;
519
+ recordTransaction(jwt: string, tx: {
520
+ chain: string;
521
+ txHash?: string;
522
+ to?: string;
523
+ value?: string;
524
+ kind?: string;
525
+ status?: string;
526
+ metadata?: Record<string, unknown>;
527
+ }): Promise<TransactionRecord>;
528
+ getTransactions(jwt: string, limit?: number): Promise<TransactionRecord[]>;
529
+ getWebAuthnRegisterOptions(jwt: string, rpId: string, rpName: string): Promise<_simplewebauthn_browser.PublicKeyCredentialCreationOptionsJSON>;
530
+ postWebAuthnRegister(jwt: string, response: _simplewebauthn_browser.RegistrationResponseJSON, origin: string): Promise<void>;
531
+ /**
532
+ * Exchange a token from the developer's own auth system for a Decane
533
+ * session. `providerId` is only needed when the project has more than one
534
+ * provider configured.
535
+ */
536
+ authExternal(token: string, providerId?: string): Promise<AuthResult>;
537
+ /**
538
+ * Returns null when this user has no server-registered passkey, which is the
539
+ * expected answer for wallets created before passkey sign-in existed or for
540
+ * PIN-only users — the caller falls back to the normal sign-in flow.
541
+ */
542
+ getWebAuthnLoginOptions(userId: string, rpId: string): Promise<_simplewebauthn_browser.PublicKeyCredentialRequestOptionsJSON | null>;
543
+ postWebAuthnLogin(userId: string, response: _simplewebauthn_browser.AuthenticationResponseJSON, origin: string): Promise<AuthResult>;
544
+ revoke(jwt: string): Promise<void>;
545
+ hasRecoveryBackup(jwt: string): Promise<boolean>;
546
+ getRecoveryBackup(jwt: string): Promise<{
547
+ wrappedDeviceShare: Uint8Array;
548
+ iv: Uint8Array;
549
+ unlockMethod: 'passkey';
550
+ credentialId: Uint8Array;
551
+ prfSalt: Uint8Array;
552
+ formatVersion: number;
553
+ } | null>;
554
+ putRecoveryBackup(jwt: string, backup: {
555
+ wrappedDeviceShare: Uint8Array;
556
+ iv: Uint8Array;
557
+ unlockMethod: 'passkey';
558
+ credentialId: Uint8Array;
559
+ prfSalt: Uint8Array;
560
+ }): Promise<void>;
561
+ deleteRecoveryBackup(jwt: string): Promise<void>;
562
+ markRecoveryShareSaved(jwt: string): Promise<void>;
563
+ getRecoveryStatus(jwt: string): Promise<{
564
+ hasRecoveryShare: boolean;
565
+ savedAt: string | null;
566
+ nagged: boolean;
567
+ shareSetVersion: number;
568
+ /**
569
+ * Whether this wallet is enrolled for server-assisted provisioning, i.e.
570
+ * whether a new device can be given a device share from login alone.
571
+ * Optional because backends older than this field simply omit it — treat
572
+ * `undefined` as "unknown", never as "not enrolled".
573
+ */
574
+ enrolled?: boolean;
575
+ }>;
576
+ dismissRecoveryNag(jwt: string): Promise<void>;
577
+ }
578
+
579
+ type RecoveryProgress = {
580
+ type: 'passkey-attempt';
581
+ } | {
582
+ type: 'passkey-failed';
583
+ reason: string;
584
+ } | {
585
+ type: 'recovery-file-prompt';
586
+ } | {
587
+ type: 'password-prompt';
588
+ passwordHint: string | null;
589
+ } | {
590
+ type: 'recovery-verifying';
591
+ } | {
592
+ type: 'rotated';
593
+ shareSetVersion: number;
594
+ };
595
+
596
+ type ChainType = "evm" | "solana" | "tron" | "bitcoin";
597
+ type ChainSupport = ChainType[];
598
+ interface ChainDescriptor {
599
+ id: string;
600
+ type: ChainType;
601
+ name: string;
602
+ nativeCurrency: {
603
+ name: string;
604
+ symbol: string;
605
+ decimals: number;
606
+ };
607
+ rpcUrl?: string;
608
+ blockExplorerUrl?: string;
609
+ iconUrl?: string;
610
+ chainId?: number;
611
+ isCustom?: boolean;
612
+ }
613
+ interface UnifiedWalletAccount {
614
+ id: string;
615
+ name: string;
616
+ icon: string;
617
+ rdns?: string;
618
+ chainSupport: ChainSupport;
619
+ evmProvider?: EIP1193Provider;
620
+ solanaProvider?: StandardWallet;
621
+ tronProvider?: TronProvider;
622
+ bitcoinProvider?: BitcoinProvider;
623
+ }
624
+ type CAIP25Method = "wallet_requestPermissions" | "wallet_revokePermissions" | "wallet_getPermissions" | "wallet_sendTransaction" | "wallet_signMessage" | "wallet_signTypedData";
625
+ interface ChainScope {
626
+ methods: CAIP25Method[];
627
+ accounts: string[];
628
+ events?: string[];
629
+ }
630
+ interface CAIP25SessionRequest {
631
+ requiredScopes: Partial<Record<string, ChainScope>>;
632
+ optionalScopes: Partial<Record<string, ChainScope>>;
633
+ }
634
+ interface CAIP25SessionResult {
635
+ sessionId: string;
636
+ grantedScopes: Partial<Record<string, ChainScope>>;
637
+ }
638
+ interface ActiveSession {
639
+ sessionId: string;
640
+ grantedScopes: Partial<Record<string, ChainScope>>;
641
+ walletId: string;
642
+ walletType: ChainType | "multi";
643
+ createdAt: number;
644
+ }
645
+ interface DecaneProvider {
646
+ request(args: {
647
+ method: string;
648
+ params?: unknown[];
649
+ }): Promise<unknown>;
650
+ on(event: string, handler: (...args: unknown[]) => void): void;
651
+ off(event: string, handler: (...args: unknown[]) => void): void;
652
+ readonly wallets: UnifiedWalletAccount[];
653
+ readonly evmWallets: UnifiedWalletAccount[];
654
+ readonly solanaWallets: UnifiedWalletAccount[];
655
+ readonly multiChainWallets: UnifiedWalletAccount[];
656
+ readonly tronWallets: UnifiedWalletAccount[];
657
+ readonly bitcoinWallets: UnifiedWalletAccount[];
658
+ connect(wallet: UnifiedWalletAccount, preferredChain?: ChainType): Promise<void>;
659
+ disconnect(): Promise<void>;
660
+ addChain(chain: ChainDescriptor): void;
661
+ removeChain(chainId: string): void;
662
+ getChain(chainId: string): ChainDescriptor | undefined;
663
+ readonly chains: ChainDescriptor[];
664
+ readonly connectedWallet: UnifiedWalletAccount | null;
665
+ readonly activeSession: ActiveSession | null;
666
+ readonly activeChain: string | null;
667
+ }
668
+ type ConnectMode = "all" | "social" | "wallets" | "evm" | "solana" | "tron" | "evm+social" | "solana+social" | "tron+social" | "evm+tron" | "evm+tron+social";
669
+ interface SocialConfig {
670
+ apiKey: string;
671
+ authMethods?: ("google" | "email" | "kingschat")[];
672
+ /**
673
+ * Scopes requested from KingsChat. Defaults to `['send_chat_message']`, which
674
+ * is what KingsChat's own SDK asks for and is enough to establish identity.
675
+ */
676
+ kingsChatScopes?: string[];
677
+ promptPin?: () => Promise<string>;
678
+ apiBaseUrl?: string;
679
+ chains?: string[];
680
+ rpcUrls?: Record<string, string>;
681
+ /**
682
+ * Show the built-in full-screen status overlay while a wallet is being
683
+ * created or unlocked. Default **true**.
589
684
  *
590
- * Configure the issuer and JWKS URL against your project in the dashboard;
591
- * no client-side setup and no secret in your dApp. `providerId` is only
592
- * required if the project has more than one provider configured.
685
+ * The overlay exists because those flows can take several seconds and often
686
+ * run right after a Google redirect, when no modal of yours is mounted — so
687
+ * without it the page can look frozen. It is rendered by `<DecaneKit>` itself,
688
+ * as a portal, and injects its own theme tokens.
593
689
  *
594
- * The user's wallet is derived from the configured identity claim, so the
595
- * same token subject always resolves to the same wallet.
690
+ * Set false to suppress it and render your own. Everything it displays is
691
+ * already available to you: `useSocialAuth()` exposes `phase`
692
+ * (`"creating" | "unlocking" | null`) plus the `isCreatingWallet` /
693
+ * `isUnlockingWallet` booleans, and the client emits `wallet-creating` and
694
+ * `wallet-unlocking` events. If you suppress it, show something of your own —
695
+ * a silent multi-second pause reads as a broken page.
596
696
  */
597
- connectWithToken(token: string, opts?: {
598
- providerId?: string;
599
- }): Promise<ConnectResult>;
600
- verifyEmailCode(email: string, code: string): Promise<ConnectResult>;
601
- unlock(): Promise<void>;
602
- isUnlocked(): boolean;
697
+ showStatusOverlay?: boolean;
603
698
  /**
604
- * True when the user's identity is known from a previous visit but there is
605
- * no live session, so unlock() cannot succeed and they must sign in again.
606
- * Check this before offering an "Unlock" action.
699
+ * Offer to save a recovery-share file at signup. **Default false** since
700
+ * 2.7.4 — opt in explicitly with `true`.
607
701
  *
608
- * Note this can be true while canSignInWithPasskey() is also true — in which
609
- * case "sign in again" costs one biometric prompt, not a full auth round
610
- * trip. Prefer offering that over a Google/email button.
611
- */
612
- needsReconnect(): boolean;
613
- /**
614
- * True when a returning user can be signed straight back in with the passkey
615
- * already on this device — no Google redirect, no email code. Requires a
616
- * remembered identity plus a passkey-wrapped device share in local storage,
617
- * so it is false on new devices and for PIN-only wallets.
618
- */
619
- canSignInWithPasskey(): Promise<boolean>;
620
- /**
621
- * Sign in using only the device passkey. connect() already tries this first
622
- * when it's available, so call this directly only when driving the UI
623
- * yourself. Throws NewDeviceError when this device isn't eligible.
624
- */
625
- signInWithPasskey(): Promise<ConnectResult>;
626
- sessionExpiresAt(): number | null;
627
- signMessage(req: SignMessageRequest): Promise<string>;
628
- sendTransaction(req: SendTransactionRequest): Promise<string>;
629
- /** Sign EIP-712 typed data (EVM only). Throws on a Solana chain. */
630
- signTypedData(req: SignTypedDataRequest): Promise<string>;
631
- /**
632
- * A standard EIP-1193 Ethereum provider backed by this wallet — drop it into
633
- * viem (`custom(provider)`), wagmi, or ethers (`BrowserProvider`). Signing
634
- * goes through the TEE; reads are forwarded to the chain RPC. Memoized, so
635
- * repeated calls return the same instance (safe for wagmi).
636
- */
637
- getEthereumProvider(opts?: {
638
- chainId?: number;
639
- }): Eip1193Provider;
640
- /**
641
- * Sign an EIP-7702 authorization, delegating this EOA to a smart-account
642
- * implementation contract on `chainId`. Enables account-abstraction /
643
- * sponsored-gas flows. Returns the full viem `SignedAuthorization` tuple, so
644
- * it drops straight into a userOperation's `authorization` field or a type-4
645
- * transaction's authorizationList. `address` and `contractAddress` are
646
- * interchangeable (the latter matches Privy's/older viem's param name).
647
- */
648
- signAuthorization(req: {
649
- chainId: number;
650
- address?: string;
651
- contractAddress?: string;
652
- nonce: number;
653
- }): Promise<SignedAuthorization>;
654
- /**
655
- * The current session's access token (the Decane JWT). Use it to authenticate
656
- * the user to your own backend (e.g. a sponsored-bundler proxy); verify it
657
- * server-side against backend.decane.app/.well-known/jwks.json. Null with no
658
- * live session.
659
- */
660
- getAccessToken(): string | null;
661
- signSolanaTransaction(tx: Uint8Array): Promise<Uint8Array>;
662
- /**
663
- * Sign a Tron transaction. Pass the transaction's `raw_data_hex` (the value
664
- * TronWeb puts on a built transaction). The enclave computes the txID
665
- * (sha256(raw_data)) and signs it with the same secp256k1 key as the EVM
666
- * wallet; returns the 65-byte recoverable signature as 0x-hex. Attach it to
667
- * the transaction's `signature` array and broadcast via TronWeb / a full node.
668
- */
669
- signTronTransaction(rawDataHex: string): Promise<string>;
670
- /**
671
- * Record a transaction this wallet broadcast, so it appears in the user's
672
- * history (and the developer dashboard). EVM `sendTransaction` records itself
673
- * automatically; call this for Solana/Tron transactions your dApp broadcasts,
674
- * or to attach a `confirmed`/`failed` status update to an existing hash.
702
+ * Server-assisted provisioning now covers the new-device case (wallets are
703
+ * enrolled at creation and repair themselves on unlock), so for most apps
704
+ * this dialog is a password prompt during onboarding that buys nothing. Turn
705
+ * it on if you want users to hold an offline fallback that works when Decane
706
+ * is unreachable.
675
707
  */
676
- recordTransaction(tx: RecordTransactionInput): Promise<TransactionRecord>;
677
- /** The user's transaction history, most recent first. Requires a live session. */
678
- getTransactionHistory(limit?: number): Promise<TransactionRecord[]>;
679
- disconnect(): Promise<void>;
680
- getAddresses(): {
681
- evm?: string;
682
- solana?: string;
683
- tron?: string;
684
- } | null;
708
+ offerRecoveryAtSignup?: boolean;
709
+ /** Ask the user whether they want a recovery share and, if so, a password for it. */
710
+ onRecoveryShareOffer?: (addresses: {
711
+ evmAddress: string;
712
+ solanaAddress: string;
713
+ }) => Promise<{
714
+ wants: boolean;
715
+ password: string;
716
+ passwordHint?: string;
717
+ }>;
718
+ /** Hand the built file + suggested filename to the app, which owns the download. */
719
+ onRecoveryFileReady?: (file: RecoveryShareFile, filename: string) => Promise<void>;
685
720
  /**
686
- * Native-token balance for a chain (defaults to the first configured EVM
687
- * chain, else Solana). Read-only public data — works even while the wallet
688
- * is locked. Returns raw (wei/lamports), a human `formatted` string, the
689
- * native `symbol`, and `decimals`.
721
+ * Prompt for a recovery-share file (and its password) during recovery.
722
+ * Return null to cancel. **Without this, a user who needs the recovery-file
723
+ * path fails immediately with NewDeviceError** — there is nowhere to ask them
724
+ * for the file.
690
725
  */
691
- getBalance(chain?: string): Promise<BalanceResult>;
692
- /** Native balances across every configured chain the wallet has an address for. */
693
- getBalances(): Promise<Array<{
694
- chain: string;
695
- balance: BalanceResult | null;
696
- }>>;
726
+ promptForRecoveryFile?: () => Promise<{
727
+ value: unknown;
728
+ getPassword: () => Promise<string | null>;
729
+ } | null>;
697
730
  /**
698
- * Voluntary share rotation — after a suspected device compromise, or just
699
- * to invalidate an old recovery file. Requires a fresh WebAuthn assertion.
700
- * Returns the new recovery-share file (the caller still owns delivering it
701
- * to the user — this does not download anything itself).
731
+ * Every successful recovery/rotation invalidates the previous recovery file,
732
+ * so this must prompt for a new password and capture the replacement.
733
+ * Required before recovery can run at all.
702
734
  */
703
- rotateShares(opts: {
735
+ onRecoveryRotated?: (addresses: {
736
+ evmAddress: string;
737
+ solanaAddress: string;
738
+ }) => Promise<{
704
739
  password: string;
705
740
  passwordHint?: string;
706
- }): Promise<RecoveryShareFile>;
741
+ }>;
742
+ /** Recovery stage callbacks, for your own progress UI. */
743
+ onRecoveryProgress?: (event: RecoveryProgress) => void;
707
744
  /**
708
- * The two-share bearer portable backup (device + recovery). This file plus
709
- * its password gives complete control of the wallet — see the mandatory
710
- * warning-screen requirements in MIGRATE_SSS_RECOVERY.md §10. Requires a
711
- * fresh WebAuthn assertion and rotates the share set on every export.
745
+ * Require a fresh WebAuthn assertion for every signature rather than letting
746
+ * an unlocked session sign freely until it expires. Enforced inside the TEE.
747
+ *
748
+ * Does not change the session lifetime — the session stays warm (no
749
+ * re-attestation, no re-auth) and authority is re-proven per signature
750
+ * instead. Default false. Needs a passkey; PIN-only wallets have no
751
+ * authenticator to assert with. See SocialConfig's counterpart in
752
+ * social/types.ts for the full caveats.
712
753
  */
713
- exportPortableBackup(opts: {
714
- password: string;
715
- passwordHint?: string;
716
- }): Promise<PortableBackup>;
717
- /** Whether the user has ever saved a recovery-share file (UX flag only — Decane never stores the share). */
718
- hasRecoveryShare(): Promise<boolean>;
719
- /** True at most once per account: no recovery share saved, never nagged, wallet holds value. */
720
- shouldPromptRecovery(): Promise<boolean>;
721
- /** Call regardless of the user's answer — the recovery nag fires once, ever. */
722
- dismissRecoveryPrompt(): Promise<void>;
723
- /** Whether a wrapped-share backup exists on the server. */
724
- hasBackup(): Promise<boolean>;
725
- /** Delete the server-side backup. Disables Flow A recovery. */
726
- deleteBackup(): Promise<void>;
727
- on(event: SdkEvent, handler: (...args: unknown[]) => void): void;
728
- off(event: SdkEvent, handler: (...args: unknown[]) => void): void;
754
+ requireAssertionPerSignature?: boolean;
755
+ /**
756
+ * Sign in with a token from your own auth system (Auth0, Clerk, Firebase,
757
+ * Cognito, or your own backend) — surfaced as a button in the built-in
758
+ * WalletSelector / SocialWalletModal UI, alongside Google and email.
759
+ * getToken() runs when the user clicks the button; wire it to however you
760
+ * already obtain a token from your auth system (e.g. after your own login
761
+ * flow completes). Configure the issuer and JWKS URL for this token
762
+ * against your project in the dashboard first. `providerId` is only
763
+ * needed when the project has more than one custom provider configured.
764
+ */
765
+ customAuth?: {
766
+ label: string;
767
+ providerId?: string;
768
+ getToken: () => Promise<string>;
769
+ };
729
770
  }
730
- interface WrappedShareMeta {
731
- unlockMethod: 'passkey' | 'pin';
732
- credentialId?: string;
733
- prfSalt?: string;
734
- pinSalt?: string;
735
- pinIterations?: number;
771
+ interface DecaneConfig {
772
+ appId: string;
773
+ chains?: ChainDescriptor[];
774
+ supportedChainIds?: string[];
775
+ networks?: "evm" | "solana" | "tron" | "bitcoin" | "all";
776
+ defaultChain?: string;
777
+ theme?: "light" | "dark" | "auto";
778
+ mode?: ConnectMode;
779
+ social?: SocialConfig;
736
780
  }
737
- /**
738
- * Who the user is, as the identity provider just described them.
739
- *
740
- * Passed through from the provider at sign-in and never stored by Decane — the
741
- * backend keeps only an HMAC and a masked identifier, so there is no profile to
742
- * look up later. Cache what you need at sign-in if you want to show a name
743
- * after a page reload.
744
- *
745
- * Every field is optional because providers differ in what they release, and a
746
- * user can withhold most of it. Email sign-in returns an empty profile.
747
- */
748
- interface AuthProfile {
749
- name?: string;
750
- email?: string;
751
- picture?: string;
781
+ interface EIP1193Provider {
782
+ request(args: {
783
+ method: string;
784
+ params?: unknown[];
785
+ }): Promise<unknown>;
786
+ on(event: string, handler: (...args: unknown[]) => void): void;
787
+ removeListener(event: string, handler: (...args: unknown[]) => void): void;
752
788
  }
753
- interface AuthResult {
754
- jwt: string;
755
- isNewUser: boolean;
756
- hasShare: boolean;
757
- userId: string;
758
- profile?: AuthProfile;
789
+ interface StandardWallet {
790
+ name: string;
791
+ icon: string;
792
+ features: Record<string, unknown>;
793
+ accounts: StandardWalletAccount[];
794
+ }
795
+ interface StandardWalletAccount {
796
+ address: string;
797
+ publicKey: Uint8Array;
798
+ chains: string[];
799
+ }
800
+ interface TronProvider {
801
+ request(args: {
802
+ method: string;
803
+ params?: unknown[] | Record<string, unknown>;
804
+ }): Promise<unknown>;
805
+ on(event: string, handler: (...args: unknown[]) => void): void;
806
+ removeListener(event: string, handler: (...args: unknown[]) => void): void;
807
+ }
808
+ interface BitcoinProvider {
809
+ requestAccounts(): Promise<string[] | Array<{
810
+ address: string;
811
+ }>>;
812
+ getAccounts?(): Promise<string[] | Array<{
813
+ address: string;
814
+ }>>;
815
+ signMessage(message: string, ...args: unknown[]): Promise<string>;
816
+ sendBitcoin?(toAddress: string, satoshis: number, options?: unknown): Promise<string>;
817
+ send?(params: unknown): Promise<string>;
818
+ signPsbt?(psbtHex: string, options?: unknown): Promise<string>;
819
+ on?(event: string, handler: (...args: unknown[]) => void): void;
759
820
  }
760
821
 
822
+ declare function createDecane(config: DecaneConfig): DecaneProvider;
823
+
761
824
  declare function createDecaneConnect(config: DecaneConnectConfig): Promise<DecaneConnect>;
762
825
 
763
826
  /**