decane-connect-kit 2.7.5 → 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,476 +1,176 @@
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
- * Show the built-in full-screen status overlay while a wallet is being
91
- * created or unlocked. Default **true**.
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
- * The overlay exists because those flows can take several seconds and often
94
- * run right after a Google redirect, when no modal of yours is mounted — so
95
- * without it the page can look frozen. It is rendered by `<DecaneKit>` itself,
96
- * as a portal, and injects its own theme tokens.
113
+ * Default false, which preserves the session-signing model: unlock once, sign
114
+ * for the session's lifetime with no further prompts.
97
115
  *
98
- * Set false to suppress it and render your own. Everything it displays is
99
- * already available to you: `useSocialAuth()` exposes `phase`
100
- * (`"creating" | "unlocking" | null`) plus the `isCreatingWallet` /
101
- * `isUnlockingWallet` booleans, and the client emits `wallet-creating` and
102
- * `wallet-unlocking` events. If you suppress it, show something of your own —
103
- * a silent multi-second pause reads as a broken page.
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.
104
124
  */
105
- showStatusOverlay?: boolean;
125
+ requireAssertionPerSignature?: boolean;
126
+ rpId?: string;
127
+ rpName?: string;
128
+ rpcUrls?: Record<string, string>;
129
+ teeUrl?: string;
106
130
  /**
107
- * Require a fresh WebAuthn assertion for every signature rather than letting
108
- * an unlocked session sign freely until it expires. Enforced inside the TEE.
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).
135
+ */
136
+ persistSession?: boolean;
137
+ /**
138
+ * Offer to save a recovery-share file at signup. **Default false** since
139
+ * 2.7.4 — opt in explicitly with `true`.
109
140
  *
110
- * Does not change the session lifetime — the session stays warm (no
111
- * re-attestation, no re-auth) and authority is re-proven per signature
112
- * instead. Default false. Needs a passkey; PIN-only wallets have no
113
- * authenticator to assert with. See SocialConfig's counterpart in
114
- * social/types.ts for the full caveats.
141
+ * The offer existed because a new device could not otherwise get a device
142
+ * share. Server-assisted provisioning now covers that case: every wallet is
143
+ * enrolled at creation, un-enrolled wallets repair themselves on the next
144
+ * unlock, and a returning user is provisioned from their sign-in alone. For
145
+ * almost every app the dialog is now a password prompt during onboarding that
146
+ * buys nothing, so it is off unless asked for.
147
+ *
148
+ * Recovery files themselves are unchanged and still work — `rotateShares()`
149
+ * issues one on demand, and `promptForRecoveryFile` still accepts one during
150
+ * recovery. Turn this back on if you want users to hold an offline fallback
151
+ * that does not depend on Decane being reachable.
115
152
  */
116
- requireAssertionPerSignature?: boolean;
153
+ offerRecoveryAtSignup?: boolean;
154
+ /** Ask the user whether they want a recovery share and, if so, a password for it. */
155
+ onRecoveryShareOffer?: (addresses: {
156
+ evmAddress: string;
157
+ solanaAddress: string;
158
+ }) => Promise<{
159
+ wants: boolean;
160
+ password: string;
161
+ passwordHint?: string;
162
+ }>;
163
+ /** Hand the built file + suggested filename to the dApp, which owns the actual download. */
164
+ onRecoveryFileReady?: (file: RecoveryShareFile, filename: string) => Promise<void>;
165
+ /** Prompt for a recovery-share file (and its password) during Flow B. Return null to cancel. */
166
+ promptForRecoveryFile?: () => Promise<{
167
+ value: unknown;
168
+ getPassword: () => Promise<string | null>;
169
+ } | null>;
117
170
  /**
118
- * Sign in with a token from your own auth system (Auth0, Clerk, Firebase,
119
- * Cognito, or your own backend) — surfaced as a button in the built-in
120
- * WalletSelector / SocialWalletModal UI, alongside Google and email.
121
- * getToken() runs when the user clicks the button; wire it to however you
122
- * already obtain a token from your auth system (e.g. after your own login
123
- * flow completes). Configure the issuer and JWKS URL for this token
124
- * against your project in the dashboard first. `providerId` is only
125
- * needed when the project has more than one custom provider configured.
126
- */
127
- customAuth?: {
128
- label: string;
129
- providerId?: string;
130
- getToken: () => Promise<string>;
131
- };
132
- }
133
- interface DecaneConfig {
134
- appId: string;
135
- chains?: ChainDescriptor[];
136
- supportedChainIds?: string[];
137
- networks?: "evm" | "solana" | "tron" | "bitcoin" | "all";
138
- defaultChain?: string;
139
- theme?: "light" | "dark" | "auto";
140
- mode?: ConnectMode;
141
- social?: SocialConfig;
142
- }
143
- interface EIP1193Provider {
144
- request(args: {
145
- method: string;
146
- params?: unknown[];
147
- }): Promise<unknown>;
148
- on(event: string, handler: (...args: unknown[]) => void): void;
149
- removeListener(event: string, handler: (...args: unknown[]) => void): void;
150
- }
151
- interface StandardWallet {
152
- name: string;
153
- icon: string;
154
- features: Record<string, unknown>;
155
- accounts: StandardWalletAccount[];
156
- }
157
- interface StandardWalletAccount {
158
- address: string;
159
- publicKey: Uint8Array;
160
- chains: string[];
161
- }
162
- interface TronProvider {
163
- request(args: {
164
- method: string;
165
- params?: unknown[] | Record<string, unknown>;
166
- }): Promise<unknown>;
167
- on(event: string, handler: (...args: unknown[]) => void): void;
168
- removeListener(event: string, handler: (...args: unknown[]) => void): void;
169
- }
170
- interface BitcoinProvider {
171
- requestAccounts(): Promise<string[] | Array<{
172
- address: string;
173
- }>>;
174
- getAccounts?(): Promise<string[] | Array<{
175
- address: string;
176
- }>>;
177
- signMessage(message: string, ...args: unknown[]): Promise<string>;
178
- sendBitcoin?(toAddress: string, satoshis: number, options?: unknown): Promise<string>;
179
- send?(params: unknown): Promise<string>;
180
- signPsbt?(psbtHex: string, options?: unknown): Promise<string>;
181
- on?(event: string, handler: (...args: unknown[]) => void): void;
182
- }
183
-
184
- declare function createDecane(config: DecaneConfig): DecaneProvider;
185
-
186
- declare class ApiClient {
187
- private readonly baseUrl;
188
- private readonly apiKey;
189
- private readonly appId;
190
- constructor(baseUrl: string, apiKey: string, appId: string);
191
- private _request;
192
- getGoogleInitUrl(): Promise<{
193
- url: string;
194
- }>;
195
- /**
196
- * Exchange a Google ID token the app already holds for a Decane session.
197
- * Resolves to the SAME identity as the redirect flow, so one Google account
198
- * means one wallet across web and mobile.
199
- */
200
- authGoogleToken(idToken: string): Promise<AuthResult>;
201
- /** The project's KingsChat clientId, so the dApp never has to hardcode it. */
202
- getKingsChatConfig(): Promise<{
203
- clientId: string;
204
- authorizationUrl: string;
205
- }>;
206
- /**
207
- * Exchange a KingsChat access token for a Decane session. The backend spends
208
- * the token against KingsChat's own API to establish the identity — it is
209
- * never trusted on its face, and never stored.
210
- */
211
- authKingsChat(accessToken: string): Promise<AuthResult>;
212
- authEmailStart(email: string): Promise<void>;
213
- authEmailVerify(email: string, code: string): Promise<AuthResult>;
214
- getAddresses(jwt: string): Promise<{
215
- evm: string | null;
216
- solana: string | null;
217
- tron: string | null;
218
- }>;
219
- putShare(jwt: string, share: {
220
- ciphertext: Uint8Array;
221
- iv: Uint8Array;
222
- wrappedShareMeta: WrappedShareMeta;
223
- evmAddress: string;
224
- solanaAddress: string;
225
- tronAddress: string;
226
- }): Promise<void>;
227
- recordTransaction(jwt: string, tx: {
228
- chain: string;
229
- txHash?: string;
230
- to?: string;
231
- value?: string;
232
- kind?: string;
233
- status?: string;
234
- metadata?: Record<string, unknown>;
235
- }): Promise<TransactionRecord>;
236
- getTransactions(jwt: string, limit?: number): Promise<TransactionRecord[]>;
237
- getWebAuthnRegisterOptions(jwt: string, rpId: string, rpName: string): Promise<_simplewebauthn_browser.PublicKeyCredentialCreationOptionsJSON>;
238
- postWebAuthnRegister(jwt: string, response: _simplewebauthn_browser.RegistrationResponseJSON, origin: string): Promise<void>;
239
- /**
240
- * Exchange a token from the developer's own auth system for a Decane
241
- * session. `providerId` is only needed when the project has more than one
242
- * provider configured.
243
- */
244
- authExternal(token: string, providerId?: string): Promise<AuthResult>;
245
- /**
246
- * Returns null when this user has no server-registered passkey, which is the
247
- * expected answer for wallets created before passkey sign-in existed or for
248
- * PIN-only users — the caller falls back to the normal sign-in flow.
249
- */
250
- getWebAuthnLoginOptions(userId: string, rpId: string): Promise<_simplewebauthn_browser.PublicKeyCredentialRequestOptionsJSON | null>;
251
- postWebAuthnLogin(userId: string, response: _simplewebauthn_browser.AuthenticationResponseJSON, origin: string): Promise<AuthResult>;
252
- revoke(jwt: string): Promise<void>;
253
- hasRecoveryBackup(jwt: string): Promise<boolean>;
254
- getRecoveryBackup(jwt: string): Promise<{
255
- wrappedDeviceShare: Uint8Array;
256
- iv: Uint8Array;
257
- unlockMethod: 'passkey';
258
- credentialId: Uint8Array;
259
- prfSalt: Uint8Array;
260
- formatVersion: number;
261
- } | null>;
262
- putRecoveryBackup(jwt: string, backup: {
263
- wrappedDeviceShare: Uint8Array;
264
- iv: Uint8Array;
265
- unlockMethod: 'passkey';
266
- credentialId: Uint8Array;
267
- prfSalt: Uint8Array;
268
- }): Promise<void>;
269
- deleteRecoveryBackup(jwt: string): Promise<void>;
270
- markRecoveryShareSaved(jwt: string): Promise<void>;
271
- getRecoveryStatus(jwt: string): Promise<{
272
- hasRecoveryShare: boolean;
273
- savedAt: string | null;
274
- nagged: boolean;
275
- shareSetVersion: number;
276
- /**
277
- * Whether this wallet is enrolled for server-assisted provisioning, i.e.
278
- * whether a new device can be given a device share from login alone.
279
- * Optional because backends older than this field simply omit it — treat
280
- * `undefined` as "unknown", never as "not enrolled".
281
- */
282
- enrolled?: boolean;
283
- }>;
284
- dismissRecoveryNag(jwt: string): Promise<void>;
285
- }
286
-
287
- type RecoveryProgress = {
288
- type: 'passkey-attempt';
289
- } | {
290
- type: 'passkey-failed';
291
- reason: string;
292
- } | {
293
- type: 'recovery-file-prompt';
294
- } | {
295
- type: 'password-prompt';
296
- passwordHint: string | null;
297
- } | {
298
- type: 'recovery-verifying';
299
- } | {
300
- type: 'rotated';
301
- shareSetVersion: number;
302
- };
303
-
304
- interface RecoveryShareFile {
305
- version: number;
306
- type: 'decane-recovery-share';
307
- shareSetVersion: number;
308
- kdf: 'pbkdf2-sha256';
309
- kdfParams: {
310
- iterations: number;
311
- salt: string;
312
- };
313
- cipher: 'aes-256-gcm';
314
- iv: string;
315
- ciphertext: string;
316
- addresses: {
317
- evm: string;
318
- solana: string;
319
- };
320
- passwordHint: string | null;
321
- createdAt: string;
322
- }
323
-
324
- interface PortableBackup {
325
- version: number;
326
- type: 'decane-portable-backup';
327
- shareSetVersion: number;
328
- kdf: 'pbkdf2-sha256';
329
- kdfParams: {
330
- iterations: number;
331
- salt: string;
332
- };
333
- cipher: 'aes-256-gcm';
334
- iv: string;
335
- ciphertext: string;
336
- addresses: {
337
- evm: string;
338
- solana: string;
339
- };
340
- passwordHint: string | null;
341
- createdAt: string;
342
- }
343
-
344
- interface BalanceResult {
345
- /** Smallest unit — wei for EVM, lamports for Solana. */
346
- raw: bigint;
347
- /** Human-readable amount in whole native units, e.g. "1.2345". */
348
- formatted: string;
349
- /** Native currency symbol, e.g. "ETH", "MATIC", "SOL". */
350
- symbol: string;
351
- /** Decimals of the native currency (18 for EVM, 9 for Solana). */
352
- decimals: number;
353
- }
354
-
355
- interface Eip1193Provider {
356
- request(args: {
357
- method: string;
358
- params?: unknown[] | object;
359
- }): Promise<unknown>;
360
- on(event: string, handler: (...args: unknown[]) => void): this;
361
- removeListener(event: string, handler: (...args: unknown[]) => void): this;
362
- /** Marks this as the Decane provider (parallel to wallets' isMetaMask etc.). */
363
- readonly isDecane: true;
364
- }
365
- interface EthereumProviderOptions {
366
- /** Initial chain id (numeric). Defaults to the first configured EVM chain, else 1. */
367
- chainId?: number;
368
- /** Per-chain RPC overrides ('evm:8453' -> url). Falls back to Decane's defaults. */
369
- rpcUrls?: Record<string, string>;
370
- }
371
- /**
372
- * Build an EIP-1193 provider over a connected Decane social client.
373
- *
374
- * `signAuthorization` (EIP-7702) is wired only when the client exposes it — that
375
- * requires the TEE signer to support the authorization signing path.
376
- */
377
- declare function createEthereumProvider(client: DecaneConnect, opts?: EthereumProviderOptions): Eip1193Provider;
378
-
379
- interface SignedAuthorization {
380
- chainId: number;
381
- address: string;
382
- nonce: number;
383
- yParity: number;
384
- r: string;
385
- s: string;
386
- }
387
-
388
- type Chain = `evm:${number}` | `solana:${'mainnet' | 'devnet'}` | `tron:${'mainnet' | 'shasta' | 'nile'}`;
389
- interface DecaneConnectConfig {
390
- /** Dashboard project ID (public). Sent with every request so the backend can
391
- * confirm the API key below actually belongs to this project. */
392
- appId: string;
393
- apiKey: string;
394
- chains: Chain[];
395
- authMethods: Array<'google' | 'email' | 'kingschat'>;
396
- /**
397
- * Scopes requested from KingsChat at sign-in. Defaults to
398
- * `['send_chat_message']`, which is what KingsChat's own SDK asks for and is
399
- * enough to establish identity. Widen only if the app actually calls
400
- * KingsChat APIs on the user's behalf — the consent screen lists every scope,
401
- * so asking for more than you use costs conversions.
402
- */
403
- kingsChatScopes?: string[];
404
- apiBaseUrl?: string;
405
- sessionDurationMinutes?: number;
406
- promptPin?: () => Promise<string>;
407
- /**
408
- * Require a fresh WebAuthn assertion for every signature, instead of letting
409
- * an unlocked session sign freely until it expires. The TEE session stays
410
- * warm — this costs one authenticator prompt per signature, not a re-auth or
411
- * re-attestation — and the check is enforced inside the enclave, not here.
412
- *
413
- * Default false, which preserves the session-signing model: unlock once, sign
414
- * for the session's lifetime with no further prompts.
415
- *
416
- * Two things worth knowing before turning this on:
417
- * - Multi-step flows prompt per leg. An ERC-20 approve + swap is two
418
- * prompts, not one.
419
- * - The policy is declared by the client at session open, so it hardens a
420
- * leaked session handle (useless without the authenticator) but a fully
421
- * compromised page could open a session without it. Making it
422
- * unbypassable requires the policy to come from the server per user or
423
- * per project; that is not implemented yet.
424
- */
425
- requireAssertionPerSignature?: boolean;
426
- rpId?: string;
427
- rpName?: string;
428
- rpcUrls?: Record<string, string>;
429
- teeUrl?: string;
430
- /**
431
- * Persist the live session (handle + JWT) in sessionStorage so a page RELOAD
432
- * silently resumes it — no repeat passkey/sign-in. Survives reloads, cleared
433
- * on tab close. Default true. Set false to require a fresh unlock on every
434
- * page load (stricter, but the reload friction that motivated this returns).
435
- */
436
- persistSession?: boolean;
437
- /**
438
- * Offer to save a recovery-share file at signup. **Default false** since
439
- * 2.7.4 — opt in explicitly with `true`.
440
- *
441
- * The offer existed because a new device could not otherwise get a device
442
- * share. Server-assisted provisioning now covers that case: every wallet is
443
- * enrolled at creation, un-enrolled wallets repair themselves on the next
444
- * unlock, and a returning user is provisioned from their sign-in alone. For
445
- * almost every app the dialog is now a password prompt during onboarding that
446
- * buys nothing, so it is off unless asked for.
447
- *
448
- * Recovery files themselves are unchanged and still work — `rotateShares()`
449
- * issues one on demand, and `promptForRecoveryFile` still accepts one during
450
- * recovery. Turn this back on if you want users to hold an offline fallback
451
- * that does not depend on Decane being reachable.
452
- */
453
- offerRecoveryAtSignup?: boolean;
454
- /** Ask the user whether they want a recovery share and, if so, a password for it. */
455
- onRecoveryShareOffer?: (addresses: {
456
- evmAddress: string;
457
- solanaAddress: string;
458
- }) => Promise<{
459
- wants: boolean;
460
- password: string;
461
- passwordHint?: string;
462
- }>;
463
- /** Hand the built file + suggested filename to the dApp, which owns the actual download. */
464
- onRecoveryFileReady?: (file: RecoveryShareFile, filename: string) => Promise<void>;
465
- /** Prompt for a recovery-share file (and its password) during Flow B. Return null to cancel. */
466
- promptForRecoveryFile?: () => Promise<{
467
- value: unknown;
468
- getPassword: () => Promise<string | null>;
469
- } | null>;
470
- /**
471
- * Every successful recovery/rotation invalidates the previous recovery
472
- * file — this MUST prompt for a new password and block progress until the
473
- * dApp confirms the user saved the new file (see MIGRATE_SSS_RECOVERY.md §10).
171
+ * Every successful recovery/rotation invalidates the previous recovery
172
+ * file — this MUST prompt for a new password and block progress until the
173
+ * dApp confirms the user saved the new file (see MIGRATE_SSS_RECOVERY.md §10).
474
174
  */
475
175
  onRecoveryRotated?: (addresses: {
476
176
  evmAddress: string;
@@ -669,112 +369,458 @@ interface DecaneConnect {
669
369
  nonce: number;
670
370
  }): Promise<SignedAuthorization>;
671
371
  /**
672
- * The current session's access token (the Decane JWT). Use it to authenticate
673
- * the user to your own backend (e.g. a sponsored-bundler proxy); verify it
674
- * server-side against backend.decane.app/.well-known/jwks.json. Null with no
675
- * live session.
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.
676
675
  */
677
- getAccessToken(): string | null;
678
- signSolanaTransaction(tx: Uint8Array): Promise<Uint8Array>;
676
+ kingsChatScopes?: string[];
677
+ promptPin?: () => Promise<string>;
678
+ apiBaseUrl?: string;
679
+ chains?: string[];
680
+ rpcUrls?: Record<string, string>;
679
681
  /**
680
- * Sign a Tron transaction. Pass the transaction's `raw_data_hex` (the value
681
- * TronWeb puts on a built transaction). The enclave computes the txID
682
- * (sha256(raw_data)) and signs it with the same secp256k1 key as the EVM
683
- * wallet; returns the 65-byte recoverable signature as 0x-hex. Attach it to
684
- * the transaction's `signature` array and broadcast via TronWeb / a full node.
682
+ * Show the built-in full-screen status overlay while a wallet is being
683
+ * created or unlocked. Default **true**.
684
+ *
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.
689
+ *
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.
685
696
  */
686
- signTronTransaction(rawDataHex: string): Promise<string>;
697
+ showStatusOverlay?: boolean;
687
698
  /**
688
- * Record a transaction this wallet broadcast, so it appears in the user's
689
- * history (and the developer dashboard). EVM `sendTransaction` records itself
690
- * automatically; call this for Solana/Tron transactions your dApp broadcasts,
691
- * or to attach a `confirmed`/`failed` status update to an existing hash.
699
+ * Offer to save a recovery-share file at signup. **Default false** since
700
+ * 2.7.4 — opt in explicitly with `true`.
701
+ *
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.
692
707
  */
693
- recordTransaction(tx: RecordTransactionInput): Promise<TransactionRecord>;
694
- /** The user's transaction history, most recent first. Requires a live session. */
695
- getTransactionHistory(limit?: number): Promise<TransactionRecord[]>;
696
- disconnect(): Promise<void>;
697
- getAddresses(): {
698
- evm?: string;
699
- solana?: string;
700
- tron?: string;
701
- } | 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>;
702
720
  /**
703
- * Native-token balance for a chain (defaults to the first configured EVM
704
- * chain, else Solana). Read-only public data — works even while the wallet
705
- * is locked. Returns raw (wei/lamports), a human `formatted` string, the
706
- * 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.
707
725
  */
708
- getBalance(chain?: string): Promise<BalanceResult>;
709
- /** Native balances across every configured chain the wallet has an address for. */
710
- getBalances(): Promise<Array<{
711
- chain: string;
712
- balance: BalanceResult | null;
713
- }>>;
726
+ promptForRecoveryFile?: () => Promise<{
727
+ value: unknown;
728
+ getPassword: () => Promise<string | null>;
729
+ } | null>;
714
730
  /**
715
- * Voluntary share rotation — after a suspected device compromise, or just
716
- * to invalidate an old recovery file. Requires a fresh WebAuthn assertion.
717
- * Returns the new recovery-share file (the caller still owns delivering it
718
- * 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.
719
734
  */
720
- rotateShares(opts: {
735
+ onRecoveryRotated?: (addresses: {
736
+ evmAddress: string;
737
+ solanaAddress: string;
738
+ }) => Promise<{
721
739
  password: string;
722
740
  passwordHint?: string;
723
- }): Promise<RecoveryShareFile>;
741
+ }>;
742
+ /** Recovery stage callbacks, for your own progress UI. */
743
+ onRecoveryProgress?: (event: RecoveryProgress) => void;
724
744
  /**
725
- * The two-share bearer portable backup (device + recovery). This file plus
726
- * its password gives complete control of the wallet — see the mandatory
727
- * warning-screen requirements in MIGRATE_SSS_RECOVERY.md §10. Requires a
728
- * 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.
729
753
  */
730
- exportPortableBackup(opts: {
731
- password: string;
732
- passwordHint?: string;
733
- }): Promise<PortableBackup>;
734
- /** Whether the user has ever saved a recovery-share file (UX flag only — Decane never stores the share). */
735
- hasRecoveryShare(): Promise<boolean>;
736
- /** True at most once per account: no recovery share saved, never nagged, wallet holds value. */
737
- shouldPromptRecovery(): Promise<boolean>;
738
- /** Call regardless of the user's answer — the recovery nag fires once, ever. */
739
- dismissRecoveryPrompt(): Promise<void>;
740
- /** Whether a wrapped-share backup exists on the server. */
741
- hasBackup(): Promise<boolean>;
742
- /** Delete the server-side backup. Disables Flow A recovery. */
743
- deleteBackup(): Promise<void>;
744
- on(event: SdkEvent, handler: (...args: unknown[]) => void): void;
745
- 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
+ };
746
770
  }
747
- interface WrappedShareMeta {
748
- unlockMethod: 'passkey' | 'pin';
749
- credentialId?: string;
750
- prfSalt?: string;
751
- pinSalt?: string;
752
- 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;
753
780
  }
754
- /**
755
- * Who the user is, as the identity provider just described them.
756
- *
757
- * Passed through from the provider at sign-in and never stored by Decane — the
758
- * backend keeps only an HMAC and a masked identifier, so there is no profile to
759
- * look up later. Cache what you need at sign-in if you want to show a name
760
- * after a page reload.
761
- *
762
- * Every field is optional because providers differ in what they release, and a
763
- * user can withhold most of it. Email sign-in returns an empty profile.
764
- */
765
- interface AuthProfile {
766
- name?: string;
767
- email?: string;
768
- 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;
769
788
  }
770
- interface AuthResult {
771
- jwt: string;
772
- isNewUser: boolean;
773
- hasShare: boolean;
774
- userId: string;
775
- 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;
776
820
  }
777
821
 
822
+ declare function createDecane(config: DecaneConfig): DecaneProvider;
823
+
778
824
  declare function createDecaneConnect(config: DecaneConnectConfig): Promise<DecaneConnect>;
779
825
 
780
826
  /**