decane-connect-kit 2.7.5 → 2.8.0
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.cjs +329 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +747 -669
- package/dist/index.d.ts +747 -669
- package/dist/index.js +329 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,258 +1,569 @@
|
|
|
1
1
|
import * as _simplewebauthn_browser from '@simplewebauthn/browser';
|
|
2
2
|
import React, { ReactNode } from 'react';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
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):
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
readonly
|
|
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
|
-
|
|
77
|
-
|
|
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
|
-
|
|
94
|
+
chains: Chain[];
|
|
95
|
+
authMethods: Array<'google' | 'email' | 'phone' | 'kingschat'>;
|
|
80
96
|
/**
|
|
81
|
-
* Scopes requested from KingsChat. Defaults to
|
|
82
|
-
* is what KingsChat's own SDK asks for and is
|
|
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
|
-
|
|
88
|
-
|
|
105
|
+
sessionDurationMinutes?: number;
|
|
106
|
+
promptPin?: () => Promise<string>;
|
|
89
107
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
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
|
-
*
|
|
94
|
-
*
|
|
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
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
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
|
-
|
|
125
|
+
requireAssertionPerSignature?: boolean;
|
|
126
|
+
rpId?: string;
|
|
127
|
+
rpName?: string;
|
|
128
|
+
rpcUrls?: Record<string, string>;
|
|
129
|
+
teeUrl?: string;
|
|
106
130
|
/**
|
|
107
|
-
*
|
|
108
|
-
*
|
|
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
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
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.
|
|
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).
|
|
126
174
|
*/
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
175
|
+
onRecoveryRotated?: (addresses: {
|
|
176
|
+
evmAddress: string;
|
|
177
|
+
solanaAddress: string;
|
|
178
|
+
}) => Promise<{
|
|
179
|
+
password: string;
|
|
180
|
+
passwordHint?: string;
|
|
181
|
+
}>;
|
|
182
|
+
/** Recovery stage callbacks, for dApp UI. */
|
|
183
|
+
onRecoveryProgress?: (event: RecoveryProgress) => void;
|
|
184
|
+
/**
|
|
185
|
+
* Rotate the whole share set whenever a NEW device recovers via passkey sync
|
|
186
|
+
* (Flow A) — the "every device holds a distinct share" model.
|
|
187
|
+
*
|
|
188
|
+
* Default false, which preserves the historical behaviour: every device
|
|
189
|
+
* synced to the same passkey caches the byte-identical wrapped share, so a
|
|
190
|
+
* device that is later lost keeps a share that stays valid until someone
|
|
191
|
+
* explicitly calls rotateShares(). There is no way to revoke one device
|
|
192
|
+
* without revoking all of them.
|
|
193
|
+
*
|
|
194
|
+
* With this on, joining a new device re-splits the master and issues that
|
|
195
|
+
* device its own share, which invalidates every other device's cached copy
|
|
196
|
+
* at the same instant. That is what makes per-device revocation possible,
|
|
197
|
+
* and it has three consequences worth accepting deliberately:
|
|
198
|
+
*
|
|
199
|
+
* - **Other devices are logged out, not just the lost one.** Each surviving
|
|
200
|
+
* device hits StaleDeviceShareError on its next unlock, clears its share,
|
|
201
|
+
* and silently re-recovers through Flow A (one passkey prompt). Expected,
|
|
202
|
+
* but it means a shared-family-device setup re-prompts more often.
|
|
203
|
+
* - **The user's recovery FILE dies too.** Rotation issues a new recovery
|
|
204
|
+
* share, so `onRecoveryRotated` MUST be configured to capture it —
|
|
205
|
+
* enabling this without that callback throws at connect time rather than
|
|
206
|
+
* stranding the user with a dead file.
|
|
207
|
+
* - **Adding a device costs a TEE round trip.** Recovery is slower.
|
|
208
|
+
*/
|
|
209
|
+
rotateOnNewDeviceRecovery?: boolean;
|
|
142
210
|
}
|
|
143
|
-
interface
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
211
|
+
interface ConnectResult {
|
|
212
|
+
addresses: {
|
|
213
|
+
evm?: string;
|
|
214
|
+
solana?: string;
|
|
215
|
+
tron?: string;
|
|
216
|
+
};
|
|
217
|
+
isNewUser: boolean;
|
|
218
|
+
/** Present when the provider released a profile — see {@link AuthProfile}. */
|
|
219
|
+
profile?: AuthProfile;
|
|
150
220
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
221
|
+
/** A recorded on-chain transaction in the user's history. */
|
|
222
|
+
interface TransactionRecord {
|
|
223
|
+
id: string;
|
|
224
|
+
chain: string;
|
|
225
|
+
kind: string;
|
|
226
|
+
txHash: string | null;
|
|
227
|
+
to: string | null;
|
|
228
|
+
value: string | null;
|
|
229
|
+
status: 'submitted' | 'confirmed' | 'failed' | string;
|
|
230
|
+
metadata: Record<string, unknown> | null;
|
|
231
|
+
createdAt: string;
|
|
232
|
+
updatedAt?: string;
|
|
156
233
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
234
|
+
/** What the dApp passes to record a transaction it broadcast. */
|
|
235
|
+
interface RecordTransactionInput {
|
|
236
|
+
chain: string;
|
|
237
|
+
txHash?: string;
|
|
238
|
+
to?: string;
|
|
239
|
+
value?: string;
|
|
240
|
+
kind?: string;
|
|
241
|
+
status?: string;
|
|
242
|
+
metadata?: Record<string, unknown>;
|
|
161
243
|
}
|
|
162
|
-
interface
|
|
163
|
-
|
|
164
|
-
|
|
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;
|
|
244
|
+
interface SignMessageRequest {
|
|
245
|
+
chain: Chain;
|
|
246
|
+
message: string | Uint8Array;
|
|
169
247
|
}
|
|
170
|
-
interface
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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;
|
|
248
|
+
interface SendTransactionRequest {
|
|
249
|
+
chain: Chain;
|
|
250
|
+
to: string;
|
|
251
|
+
value?: bigint;
|
|
252
|
+
data?: string;
|
|
253
|
+
gasLimit?: bigint;
|
|
254
|
+
gasPrice?: bigint;
|
|
182
255
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
256
|
+
interface SignTypedDataRequest {
|
|
257
|
+
chain: Chain;
|
|
258
|
+
domain: Record<string, unknown>;
|
|
259
|
+
types: Record<string, unknown>;
|
|
260
|
+
message: Record<string, unknown>;
|
|
261
|
+
primaryType?: string;
|
|
262
|
+
}
|
|
263
|
+
type SdkEvent = 'connected' | 'disconnected' | 'session-expired' | 'session-started' | 'wallet-creating' | 'wallet-unlocking'
|
|
264
|
+
/**
|
|
265
|
+
* Emitted when this wallet could not be enrolled for server-assisted
|
|
266
|
+
* recovery. Not fatal — the wallet works — but until it is enrolled the user
|
|
267
|
+
* cannot get a device share from login alone, so a future new device will
|
|
268
|
+
* need a passkey backup or a recovery file. The SDK retries automatically on
|
|
269
|
+
* later unlocks; surface or report this if you want visibility rather than a
|
|
270
|
+
* console line.
|
|
271
|
+
*/
|
|
272
|
+
| 'enrolment-failed';
|
|
273
|
+
interface DecaneConnect {
|
|
274
|
+
connect(): Promise<ConnectResult>;
|
|
275
|
+
connectWithGoogle(): Promise<ConnectResult>;
|
|
195
276
|
/**
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
277
|
+
* Sign in with a Google ID token the app already obtained — native Google
|
|
278
|
+
* Sign-In on mobile, or Google Identity Services on the web.
|
|
279
|
+
*
|
|
280
|
+
* Resolves to the SAME wallet as connectWithGoogle(): both end at the
|
|
281
|
+
* backend's Google identity, so a user who signs in on your website and then
|
|
282
|
+
* on your app lands on one wallet, not two.
|
|
199
283
|
*/
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
284
|
+
connectWithGoogleToken(idToken: string): Promise<ConnectResult>;
|
|
285
|
+
/**
|
|
286
|
+
* Sign in with KingsChat.
|
|
287
|
+
*
|
|
288
|
+
* Opens KingsChat's consent screen in a popup, so this MUST be called
|
|
289
|
+
* directly from a click handler — a popup opened after an await is blocked by
|
|
290
|
+
* every browser. The resulting access token is exchanged server-side for a
|
|
291
|
+
* Decane session; it is never treated as one on its own.
|
|
292
|
+
*/
|
|
293
|
+
connectWithKingsChat(): Promise<ConnectResult>;
|
|
294
|
+
/**
|
|
295
|
+
* Sign in with a KingsChat access token the app already holds — from
|
|
296
|
+
* kingschat-web-sdk, a native SDK, or any other flow. Use when you want to
|
|
297
|
+
* own the consent step yourself; connectWithKingsChat() is the turnkey path.
|
|
298
|
+
*/
|
|
299
|
+
connectWithKingsChatToken(accessToken: string): Promise<ConnectResult>;
|
|
300
|
+
connectWithEmail(email: string): Promise<{
|
|
301
|
+
codeRequired: true;
|
|
205
302
|
}>;
|
|
206
303
|
/**
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
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.
|
|
210
313
|
*/
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
314
|
+
connectWithToken(token: string, opts?: {
|
|
315
|
+
providerId?: string;
|
|
316
|
+
}): Promise<ConnectResult>;
|
|
317
|
+
verifyEmailCode(email: string, code: string): Promise<ConnectResult>;
|
|
318
|
+
/**
|
|
319
|
+
* Send a 6-digit sign-in code by SMS (delivered via Termii).
|
|
320
|
+
*
|
|
321
|
+
* `phone` must be **E.164** — `+2348012345678`. Local formats like
|
|
322
|
+
* `08012345678` are rejected rather than guessed at: the wallet is derived
|
|
323
|
+
* from the number, so guessing a country would either merge two people onto
|
|
324
|
+
* one wallet or split one person across two. Collect the country in your UI.
|
|
325
|
+
*
|
|
326
|
+
* Always resolves, whether or not the number has an account — the backend
|
|
327
|
+
* answers identically either way so this cannot be used to discover who is
|
|
328
|
+
* registered. Follow with {@link verifyPhoneCode}.
|
|
329
|
+
*/
|
|
330
|
+
connectWithPhone(phone: string): Promise<{
|
|
331
|
+
codeRequired: true;
|
|
218
332
|
}>;
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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>;
|
|
333
|
+
/** Verify the SMS code and open the wallet. Pass the same E.164 number. */
|
|
334
|
+
verifyPhoneCode(phone: string, code: string): Promise<ConnectResult>;
|
|
335
|
+
unlock(): Promise<void>;
|
|
336
|
+
isUnlocked(): boolean;
|
|
239
337
|
/**
|
|
240
|
-
*
|
|
241
|
-
* session
|
|
242
|
-
*
|
|
338
|
+
* True when the user's identity is known from a previous visit but there is
|
|
339
|
+
* no live session, so unlock() cannot succeed and they must sign in again.
|
|
340
|
+
* Check this before offering an "Unlock" action.
|
|
341
|
+
*
|
|
342
|
+
* Note this can be true while canSignInWithPasskey() is also true — in which
|
|
343
|
+
* case "sign in again" costs one biometric prompt, not a full auth round
|
|
344
|
+
* trip. Prefer offering that over a Google/email button.
|
|
243
345
|
*/
|
|
244
|
-
|
|
346
|
+
needsReconnect(): boolean;
|
|
245
347
|
/**
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
348
|
+
* True when a returning user can be signed straight back in with the passkey
|
|
349
|
+
* already on this device — no Google redirect, no email code. Requires a
|
|
350
|
+
* remembered identity plus a passkey-wrapped device share in local storage,
|
|
351
|
+
* so it is false on new devices and for PIN-only wallets.
|
|
249
352
|
*/
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
353
|
+
canSignInWithPasskey(): Promise<boolean>;
|
|
354
|
+
/**
|
|
355
|
+
* Sign in using only the device passkey. connect() already tries this first
|
|
356
|
+
* when it's available, so call this directly only when driving the UI
|
|
357
|
+
* yourself. Throws NewDeviceError when this device isn't eligible.
|
|
358
|
+
*/
|
|
359
|
+
signInWithPasskey(): Promise<ConnectResult>;
|
|
360
|
+
sessionExpiresAt(): number | null;
|
|
361
|
+
signMessage(req: SignMessageRequest): Promise<string>;
|
|
362
|
+
sendTransaction(req: SendTransactionRequest): Promise<string>;
|
|
363
|
+
/** Sign EIP-712 typed data (EVM only). Throws on a Solana chain. */
|
|
364
|
+
signTypedData(req: SignTypedDataRequest): Promise<string>;
|
|
365
|
+
/**
|
|
366
|
+
* A standard EIP-1193 Ethereum provider backed by this wallet — drop it into
|
|
367
|
+
* viem (`custom(provider)`), wagmi, or ethers (`BrowserProvider`). Signing
|
|
368
|
+
* goes through the TEE; reads are forwarded to the chain RPC. Memoized, so
|
|
369
|
+
* repeated calls return the same instance (safe for wagmi).
|
|
370
|
+
*/
|
|
371
|
+
getEthereumProvider(opts?: {
|
|
372
|
+
chainId?: number;
|
|
373
|
+
}): Eip1193Provider;
|
|
374
|
+
/**
|
|
375
|
+
* Sign an EIP-7702 authorization, delegating this EOA to a smart-account
|
|
376
|
+
* implementation contract on `chainId`. Enables account-abstraction /
|
|
377
|
+
* sponsored-gas flows. Returns the full viem `SignedAuthorization` tuple, so
|
|
378
|
+
* it drops straight into a userOperation's `authorization` field or a type-4
|
|
379
|
+
* transaction's authorizationList. `address` and `contractAddress` are
|
|
380
|
+
* interchangeable (the latter matches Privy's/older viem's param name).
|
|
381
|
+
*/
|
|
382
|
+
signAuthorization(req: {
|
|
383
|
+
chainId: number;
|
|
384
|
+
address?: string;
|
|
385
|
+
contractAddress?: string;
|
|
386
|
+
nonce: number;
|
|
387
|
+
}): Promise<SignedAuthorization>;
|
|
388
|
+
/**
|
|
389
|
+
* The current session's access token (the Decane JWT). Use it to authenticate
|
|
390
|
+
* the user to your own backend (e.g. a sponsored-bundler proxy); verify it
|
|
391
|
+
* server-side against backend.decane.app/.well-known/jwks.json. Null with no
|
|
392
|
+
* live session.
|
|
393
|
+
*/
|
|
394
|
+
getAccessToken(): string | null;
|
|
395
|
+
signSolanaTransaction(tx: Uint8Array): Promise<Uint8Array>;
|
|
396
|
+
/**
|
|
397
|
+
* Sign a Tron transaction. Pass the transaction's `raw_data_hex` (the value
|
|
398
|
+
* TronWeb puts on a built transaction). The enclave computes the txID
|
|
399
|
+
* (sha256(raw_data)) and signs it with the same secp256k1 key as the EVM
|
|
400
|
+
* wallet; returns the 65-byte recoverable signature as 0x-hex. Attach it to
|
|
401
|
+
* the transaction's `signature` array and broadcast via TronWeb / a full node.
|
|
402
|
+
*/
|
|
403
|
+
signTronTransaction(rawDataHex: string): Promise<string>;
|
|
404
|
+
/**
|
|
405
|
+
* Record a transaction this wallet broadcast, so it appears in the user's
|
|
406
|
+
* history (and the developer dashboard). EVM `sendTransaction` records itself
|
|
407
|
+
* automatically; call this for Solana/Tron transactions your dApp broadcasts,
|
|
408
|
+
* or to attach a `confirmed`/`failed` status update to an existing hash.
|
|
409
|
+
*/
|
|
410
|
+
recordTransaction(tx: RecordTransactionInput): Promise<TransactionRecord>;
|
|
411
|
+
/** The user's transaction history, most recent first. Requires a live session. */
|
|
412
|
+
getTransactionHistory(limit?: number): Promise<TransactionRecord[]>;
|
|
413
|
+
disconnect(): Promise<void>;
|
|
414
|
+
getAddresses(): {
|
|
415
|
+
evm?: string;
|
|
416
|
+
solana?: string;
|
|
417
|
+
tron?: string;
|
|
418
|
+
} | null;
|
|
419
|
+
/**
|
|
420
|
+
* Native-token balance for a chain (defaults to the first configured EVM
|
|
421
|
+
* chain, else Solana). Read-only public data — works even while the wallet
|
|
422
|
+
* is locked. Returns raw (wei/lamports), a human `formatted` string, the
|
|
423
|
+
* native `symbol`, and `decimals`.
|
|
424
|
+
*/
|
|
425
|
+
getBalance(chain?: string): Promise<BalanceResult>;
|
|
426
|
+
/** Native balances across every configured chain the wallet has an address for. */
|
|
427
|
+
getBalances(): Promise<Array<{
|
|
428
|
+
chain: string;
|
|
429
|
+
balance: BalanceResult | null;
|
|
430
|
+
}>>;
|
|
431
|
+
/**
|
|
432
|
+
* Voluntary share rotation — after a suspected device compromise, or just
|
|
433
|
+
* to invalidate an old recovery file. Requires a fresh WebAuthn assertion.
|
|
434
|
+
* Returns the new recovery-share file (the caller still owns delivering it
|
|
435
|
+
* to the user — this does not download anything itself).
|
|
436
|
+
*/
|
|
437
|
+
rotateShares(opts: {
|
|
438
|
+
password: string;
|
|
439
|
+
passwordHint?: string;
|
|
440
|
+
}): Promise<RecoveryShareFile>;
|
|
441
|
+
/**
|
|
442
|
+
* The two-share bearer portable backup (device + recovery). This file plus
|
|
443
|
+
* its password gives complete control of the wallet — see the mandatory
|
|
444
|
+
* warning-screen requirements in MIGRATE_SSS_RECOVERY.md §10. Requires a
|
|
445
|
+
* fresh WebAuthn assertion and rotates the share set on every export.
|
|
446
|
+
*/
|
|
447
|
+
exportPortableBackup(opts: {
|
|
448
|
+
password: string;
|
|
449
|
+
passwordHint?: string;
|
|
450
|
+
}): Promise<PortableBackup>;
|
|
451
|
+
/** Whether the user has ever saved a recovery-share file (UX flag only — Decane never stores the share). */
|
|
452
|
+
hasRecoveryShare(): Promise<boolean>;
|
|
453
|
+
/** True at most once per account: no recovery share saved, never nagged, wallet holds value. */
|
|
454
|
+
shouldPromptRecovery(): Promise<boolean>;
|
|
455
|
+
/** Call regardless of the user's answer — the recovery nag fires once, ever. */
|
|
456
|
+
dismissRecoveryPrompt(): Promise<void>;
|
|
457
|
+
/** Whether a wrapped-share backup exists on the server. */
|
|
458
|
+
hasBackup(): Promise<boolean>;
|
|
459
|
+
/** Delete the server-side backup. Disables Flow A recovery. */
|
|
460
|
+
deleteBackup(): Promise<void>;
|
|
461
|
+
on(event: SdkEvent, handler: (...args: unknown[]) => void): void;
|
|
462
|
+
off(event: SdkEvent, handler: (...args: unknown[]) => void): void;
|
|
463
|
+
}
|
|
464
|
+
interface WrappedShareMeta {
|
|
465
|
+
unlockMethod: 'passkey' | 'pin';
|
|
466
|
+
credentialId?: string;
|
|
467
|
+
prfSalt?: string;
|
|
468
|
+
pinSalt?: string;
|
|
469
|
+
pinIterations?: number;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Who the user is, as the identity provider just described them.
|
|
473
|
+
*
|
|
474
|
+
* Passed through from the provider at sign-in and never stored by Decane — the
|
|
475
|
+
* backend keeps only an HMAC and a masked identifier, so there is no profile to
|
|
476
|
+
* look up later. Cache what you need at sign-in if you want to show a name
|
|
477
|
+
* after a page reload.
|
|
478
|
+
*
|
|
479
|
+
* Every field is optional because providers differ in what they release, and a
|
|
480
|
+
* user can withhold most of it. Email sign-in returns an empty profile.
|
|
481
|
+
*/
|
|
482
|
+
interface AuthProfile {
|
|
483
|
+
name?: string;
|
|
484
|
+
email?: string;
|
|
485
|
+
picture?: string;
|
|
486
|
+
}
|
|
487
|
+
interface AuthResult {
|
|
488
|
+
jwt: string;
|
|
489
|
+
isNewUser: boolean;
|
|
490
|
+
hasShare: boolean;
|
|
491
|
+
userId: string;
|
|
492
|
+
profile?: AuthProfile;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
declare class ApiClient {
|
|
496
|
+
private readonly baseUrl;
|
|
497
|
+
private readonly apiKey;
|
|
498
|
+
private readonly appId;
|
|
499
|
+
constructor(baseUrl: string, apiKey: string, appId: string);
|
|
500
|
+
private _request;
|
|
501
|
+
getGoogleInitUrl(): Promise<{
|
|
502
|
+
url: string;
|
|
503
|
+
}>;
|
|
504
|
+
/**
|
|
505
|
+
* Exchange a Google ID token the app already holds for a Decane session.
|
|
506
|
+
* Resolves to the SAME identity as the redirect flow, so one Google account
|
|
507
|
+
* means one wallet across web and mobile.
|
|
508
|
+
*/
|
|
509
|
+
authGoogleToken(idToken: string): Promise<AuthResult>;
|
|
510
|
+
/** The project's KingsChat clientId, so the dApp never has to hardcode it. */
|
|
511
|
+
getKingsChatConfig(): Promise<{
|
|
512
|
+
clientId: string;
|
|
513
|
+
authorizationUrl: string;
|
|
514
|
+
}>;
|
|
515
|
+
/**
|
|
516
|
+
* Exchange a KingsChat access token for a Decane session. The backend spends
|
|
517
|
+
* the token against KingsChat's own API to establish the identity — it is
|
|
518
|
+
* never trusted on its face, and never stored.
|
|
519
|
+
*/
|
|
520
|
+
authKingsChat(accessToken: string): Promise<AuthResult>;
|
|
521
|
+
authEmailStart(email: string): Promise<void>;
|
|
522
|
+
authEmailVerify(email: string, code: string): Promise<AuthResult>;
|
|
523
|
+
authPhoneStart(phone: string): Promise<void>;
|
|
524
|
+
authPhoneVerify(phone: string, code: string): Promise<AuthResult>;
|
|
525
|
+
getAddresses(jwt: string): Promise<{
|
|
526
|
+
evm: string | null;
|
|
527
|
+
solana: string | null;
|
|
528
|
+
tron: string | null;
|
|
529
|
+
}>;
|
|
530
|
+
putShare(jwt: string, share: {
|
|
531
|
+
ciphertext: Uint8Array;
|
|
532
|
+
iv: Uint8Array;
|
|
533
|
+
wrappedShareMeta: WrappedShareMeta;
|
|
534
|
+
evmAddress: string;
|
|
535
|
+
solanaAddress: string;
|
|
536
|
+
tronAddress: string;
|
|
537
|
+
}): Promise<void>;
|
|
538
|
+
recordTransaction(jwt: string, tx: {
|
|
539
|
+
chain: string;
|
|
540
|
+
txHash?: string;
|
|
541
|
+
to?: string;
|
|
542
|
+
value?: string;
|
|
543
|
+
kind?: string;
|
|
544
|
+
status?: string;
|
|
545
|
+
metadata?: Record<string, unknown>;
|
|
546
|
+
}): Promise<TransactionRecord>;
|
|
547
|
+
getTransactions(jwt: string, limit?: number): Promise<TransactionRecord[]>;
|
|
548
|
+
getWebAuthnRegisterOptions(jwt: string, rpId: string, rpName: string): Promise<_simplewebauthn_browser.PublicKeyCredentialCreationOptionsJSON>;
|
|
549
|
+
postWebAuthnRegister(jwt: string, response: _simplewebauthn_browser.RegistrationResponseJSON, origin: string): Promise<void>;
|
|
550
|
+
/**
|
|
551
|
+
* Exchange a token from the developer's own auth system for a Decane
|
|
552
|
+
* session. `providerId` is only needed when the project has more than one
|
|
553
|
+
* provider configured.
|
|
554
|
+
*/
|
|
555
|
+
authExternal(token: string, providerId?: string): Promise<AuthResult>;
|
|
556
|
+
/**
|
|
557
|
+
* Returns null when this user has no server-registered passkey, which is the
|
|
558
|
+
* expected answer for wallets created before passkey sign-in existed or for
|
|
559
|
+
* PIN-only users — the caller falls back to the normal sign-in flow.
|
|
560
|
+
*/
|
|
561
|
+
getWebAuthnLoginOptions(userId: string, rpId: string): Promise<_simplewebauthn_browser.PublicKeyCredentialRequestOptionsJSON | null>;
|
|
562
|
+
postWebAuthnLogin(userId: string, response: _simplewebauthn_browser.AuthenticationResponseJSON, origin: string): Promise<AuthResult>;
|
|
563
|
+
revoke(jwt: string): Promise<void>;
|
|
564
|
+
hasRecoveryBackup(jwt: string): Promise<boolean>;
|
|
565
|
+
getRecoveryBackup(jwt: string): Promise<{
|
|
566
|
+
wrappedDeviceShare: Uint8Array;
|
|
256
567
|
iv: Uint8Array;
|
|
257
568
|
unlockMethod: 'passkey';
|
|
258
569
|
credentialId: Uint8Array;
|
|
@@ -301,480 +612,234 @@ type RecoveryProgress = {
|
|
|
301
612
|
shareSetVersion: number;
|
|
302
613
|
};
|
|
303
614
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
iv: string;
|
|
315
|
-
ciphertext: string;
|
|
316
|
-
addresses: {
|
|
317
|
-
evm: string;
|
|
318
|
-
solana: string;
|
|
615
|
+
type ChainType = "evm" | "solana" | "tron" | "bitcoin";
|
|
616
|
+
type ChainSupport = ChainType[];
|
|
617
|
+
interface ChainDescriptor {
|
|
618
|
+
id: string;
|
|
619
|
+
type: ChainType;
|
|
620
|
+
name: string;
|
|
621
|
+
nativeCurrency: {
|
|
622
|
+
name: string;
|
|
623
|
+
symbol: string;
|
|
624
|
+
decimals: number;
|
|
319
625
|
};
|
|
320
|
-
|
|
321
|
-
|
|
626
|
+
rpcUrl?: string;
|
|
627
|
+
blockExplorerUrl?: string;
|
|
628
|
+
iconUrl?: string;
|
|
629
|
+
chainId?: number;
|
|
630
|
+
isCustom?: boolean;
|
|
322
631
|
}
|
|
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).
|
|
474
|
-
*/
|
|
475
|
-
onRecoveryRotated?: (addresses: {
|
|
476
|
-
evmAddress: string;
|
|
477
|
-
solanaAddress: string;
|
|
478
|
-
}) => Promise<{
|
|
479
|
-
password: string;
|
|
480
|
-
passwordHint?: string;
|
|
481
|
-
}>;
|
|
482
|
-
/** Recovery stage callbacks, for dApp UI. */
|
|
483
|
-
onRecoveryProgress?: (event: RecoveryProgress) => void;
|
|
484
|
-
/**
|
|
485
|
-
* Rotate the whole share set whenever a NEW device recovers via passkey sync
|
|
486
|
-
* (Flow A) — the "every device holds a distinct share" model.
|
|
487
|
-
*
|
|
488
|
-
* Default false, which preserves the historical behaviour: every device
|
|
489
|
-
* synced to the same passkey caches the byte-identical wrapped share, so a
|
|
490
|
-
* device that is later lost keeps a share that stays valid until someone
|
|
491
|
-
* explicitly calls rotateShares(). There is no way to revoke one device
|
|
492
|
-
* without revoking all of them.
|
|
493
|
-
*
|
|
494
|
-
* With this on, joining a new device re-splits the master and issues that
|
|
495
|
-
* device its own share, which invalidates every other device's cached copy
|
|
496
|
-
* at the same instant. That is what makes per-device revocation possible,
|
|
497
|
-
* and it has three consequences worth accepting deliberately:
|
|
498
|
-
*
|
|
499
|
-
* - **Other devices are logged out, not just the lost one.** Each surviving
|
|
500
|
-
* device hits StaleDeviceShareError on its next unlock, clears its share,
|
|
501
|
-
* and silently re-recovers through Flow A (one passkey prompt). Expected,
|
|
502
|
-
* but it means a shared-family-device setup re-prompts more often.
|
|
503
|
-
* - **The user's recovery FILE dies too.** Rotation issues a new recovery
|
|
504
|
-
* share, so `onRecoveryRotated` MUST be configured to capture it —
|
|
505
|
-
* enabling this without that callback throws at connect time rather than
|
|
506
|
-
* stranding the user with a dead file.
|
|
507
|
-
* - **Adding a device costs a TEE round trip.** Recovery is slower.
|
|
508
|
-
*/
|
|
509
|
-
rotateOnNewDeviceRecovery?: boolean;
|
|
510
|
-
}
|
|
511
|
-
interface ConnectResult {
|
|
512
|
-
addresses: {
|
|
513
|
-
evm?: string;
|
|
514
|
-
solana?: string;
|
|
515
|
-
tron?: string;
|
|
516
|
-
};
|
|
517
|
-
isNewUser: boolean;
|
|
518
|
-
/** Present when the provider released a profile — see {@link AuthProfile}. */
|
|
519
|
-
profile?: AuthProfile;
|
|
520
|
-
}
|
|
521
|
-
/** A recorded on-chain transaction in the user's history. */
|
|
522
|
-
interface TransactionRecord {
|
|
632
|
+
interface UnifiedWalletAccount {
|
|
523
633
|
id: string;
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
updatedAt?: string;
|
|
533
|
-
}
|
|
534
|
-
/** What the dApp passes to record a transaction it broadcast. */
|
|
535
|
-
interface RecordTransactionInput {
|
|
536
|
-
chain: string;
|
|
537
|
-
txHash?: string;
|
|
538
|
-
to?: string;
|
|
539
|
-
value?: string;
|
|
540
|
-
kind?: string;
|
|
541
|
-
status?: string;
|
|
542
|
-
metadata?: Record<string, unknown>;
|
|
543
|
-
}
|
|
544
|
-
interface SignMessageRequest {
|
|
545
|
-
chain: Chain;
|
|
546
|
-
message: string | Uint8Array;
|
|
547
|
-
}
|
|
548
|
-
interface SendTransactionRequest {
|
|
549
|
-
chain: Chain;
|
|
550
|
-
to: string;
|
|
551
|
-
value?: bigint;
|
|
552
|
-
data?: string;
|
|
553
|
-
gasLimit?: bigint;
|
|
554
|
-
gasPrice?: bigint;
|
|
555
|
-
}
|
|
556
|
-
interface SignTypedDataRequest {
|
|
557
|
-
chain: Chain;
|
|
558
|
-
domain: Record<string, unknown>;
|
|
559
|
-
types: Record<string, unknown>;
|
|
560
|
-
message: Record<string, unknown>;
|
|
561
|
-
primaryType?: string;
|
|
634
|
+
name: string;
|
|
635
|
+
icon: string;
|
|
636
|
+
rdns?: string;
|
|
637
|
+
chainSupport: ChainSupport;
|
|
638
|
+
evmProvider?: EIP1193Provider;
|
|
639
|
+
solanaProvider?: StandardWallet;
|
|
640
|
+
tronProvider?: TronProvider;
|
|
641
|
+
bitcoinProvider?: BitcoinProvider;
|
|
562
642
|
}
|
|
563
|
-
type
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
interface
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
* The user's wallet is derived from the configured identity claim, so the
|
|
612
|
-
* same token subject always resolves to the same wallet.
|
|
613
|
-
*/
|
|
614
|
-
connectWithToken(token: string, opts?: {
|
|
615
|
-
providerId?: string;
|
|
616
|
-
}): Promise<ConnectResult>;
|
|
617
|
-
verifyEmailCode(email: string, code: string): Promise<ConnectResult>;
|
|
618
|
-
unlock(): Promise<void>;
|
|
619
|
-
isUnlocked(): boolean;
|
|
620
|
-
/**
|
|
621
|
-
* True when the user's identity is known from a previous visit but there is
|
|
622
|
-
* no live session, so unlock() cannot succeed and they must sign in again.
|
|
623
|
-
* Check this before offering an "Unlock" action.
|
|
624
|
-
*
|
|
625
|
-
* Note this can be true while canSignInWithPasskey() is also true — in which
|
|
626
|
-
* case "sign in again" costs one biometric prompt, not a full auth round
|
|
627
|
-
* trip. Prefer offering that over a Google/email button.
|
|
628
|
-
*/
|
|
629
|
-
needsReconnect(): boolean;
|
|
630
|
-
/**
|
|
631
|
-
* True when a returning user can be signed straight back in with the passkey
|
|
632
|
-
* already on this device — no Google redirect, no email code. Requires a
|
|
633
|
-
* remembered identity plus a passkey-wrapped device share in local storage,
|
|
634
|
-
* so it is false on new devices and for PIN-only wallets.
|
|
635
|
-
*/
|
|
636
|
-
canSignInWithPasskey(): Promise<boolean>;
|
|
637
|
-
/**
|
|
638
|
-
* Sign in using only the device passkey. connect() already tries this first
|
|
639
|
-
* when it's available, so call this directly only when driving the UI
|
|
640
|
-
* yourself. Throws NewDeviceError when this device isn't eligible.
|
|
641
|
-
*/
|
|
642
|
-
signInWithPasskey(): Promise<ConnectResult>;
|
|
643
|
-
sessionExpiresAt(): number | null;
|
|
644
|
-
signMessage(req: SignMessageRequest): Promise<string>;
|
|
645
|
-
sendTransaction(req: SendTransactionRequest): Promise<string>;
|
|
646
|
-
/** Sign EIP-712 typed data (EVM only). Throws on a Solana chain. */
|
|
647
|
-
signTypedData(req: SignTypedDataRequest): Promise<string>;
|
|
648
|
-
/**
|
|
649
|
-
* A standard EIP-1193 Ethereum provider backed by this wallet — drop it into
|
|
650
|
-
* viem (`custom(provider)`), wagmi, or ethers (`BrowserProvider`). Signing
|
|
651
|
-
* goes through the TEE; reads are forwarded to the chain RPC. Memoized, so
|
|
652
|
-
* repeated calls return the same instance (safe for wagmi).
|
|
653
|
-
*/
|
|
654
|
-
getEthereumProvider(opts?: {
|
|
655
|
-
chainId?: number;
|
|
656
|
-
}): Eip1193Provider;
|
|
657
|
-
/**
|
|
658
|
-
* Sign an EIP-7702 authorization, delegating this EOA to a smart-account
|
|
659
|
-
* implementation contract on `chainId`. Enables account-abstraction /
|
|
660
|
-
* sponsored-gas flows. Returns the full viem `SignedAuthorization` tuple, so
|
|
661
|
-
* it drops straight into a userOperation's `authorization` field or a type-4
|
|
662
|
-
* transaction's authorizationList. `address` and `contractAddress` are
|
|
663
|
-
* interchangeable (the latter matches Privy's/older viem's param name).
|
|
664
|
-
*/
|
|
665
|
-
signAuthorization(req: {
|
|
666
|
-
chainId: number;
|
|
667
|
-
address?: string;
|
|
668
|
-
contractAddress?: string;
|
|
669
|
-
nonce: number;
|
|
670
|
-
}): Promise<SignedAuthorization>;
|
|
643
|
+
type CAIP25Method = "wallet_requestPermissions" | "wallet_revokePermissions" | "wallet_getPermissions" | "wallet_sendTransaction" | "wallet_signMessage" | "wallet_signTypedData";
|
|
644
|
+
interface ChainScope {
|
|
645
|
+
methods: CAIP25Method[];
|
|
646
|
+
accounts: string[];
|
|
647
|
+
events?: string[];
|
|
648
|
+
}
|
|
649
|
+
interface CAIP25SessionRequest {
|
|
650
|
+
requiredScopes: Partial<Record<string, ChainScope>>;
|
|
651
|
+
optionalScopes: Partial<Record<string, ChainScope>>;
|
|
652
|
+
}
|
|
653
|
+
interface CAIP25SessionResult {
|
|
654
|
+
sessionId: string;
|
|
655
|
+
grantedScopes: Partial<Record<string, ChainScope>>;
|
|
656
|
+
}
|
|
657
|
+
interface ActiveSession {
|
|
658
|
+
sessionId: string;
|
|
659
|
+
grantedScopes: Partial<Record<string, ChainScope>>;
|
|
660
|
+
walletId: string;
|
|
661
|
+
walletType: ChainType | "multi";
|
|
662
|
+
createdAt: number;
|
|
663
|
+
}
|
|
664
|
+
interface DecaneProvider {
|
|
665
|
+
request(args: {
|
|
666
|
+
method: string;
|
|
667
|
+
params?: unknown[];
|
|
668
|
+
}): Promise<unknown>;
|
|
669
|
+
on(event: string, handler: (...args: unknown[]) => void): void;
|
|
670
|
+
off(event: string, handler: (...args: unknown[]) => void): void;
|
|
671
|
+
readonly wallets: UnifiedWalletAccount[];
|
|
672
|
+
readonly evmWallets: UnifiedWalletAccount[];
|
|
673
|
+
readonly solanaWallets: UnifiedWalletAccount[];
|
|
674
|
+
readonly multiChainWallets: UnifiedWalletAccount[];
|
|
675
|
+
readonly tronWallets: UnifiedWalletAccount[];
|
|
676
|
+
readonly bitcoinWallets: UnifiedWalletAccount[];
|
|
677
|
+
connect(wallet: UnifiedWalletAccount, preferredChain?: ChainType): Promise<void>;
|
|
678
|
+
disconnect(): Promise<void>;
|
|
679
|
+
addChain(chain: ChainDescriptor): void;
|
|
680
|
+
removeChain(chainId: string): void;
|
|
681
|
+
getChain(chainId: string): ChainDescriptor | undefined;
|
|
682
|
+
readonly chains: ChainDescriptor[];
|
|
683
|
+
readonly connectedWallet: UnifiedWalletAccount | null;
|
|
684
|
+
readonly activeSession: ActiveSession | null;
|
|
685
|
+
readonly activeChain: string | null;
|
|
686
|
+
}
|
|
687
|
+
type ConnectMode = "all" | "social" | "wallets" | "evm" | "solana" | "tron" | "evm+social" | "solana+social" | "tron+social" | "evm+tron" | "evm+tron+social";
|
|
688
|
+
interface SocialConfig {
|
|
689
|
+
apiKey: string;
|
|
690
|
+
authMethods?: ("google" | "email" | "phone" | "kingschat")[];
|
|
671
691
|
/**
|
|
672
|
-
*
|
|
673
|
-
*
|
|
674
|
-
* server-side against backend.decane.app/.well-known/jwks.json. Null with no
|
|
675
|
-
* live session.
|
|
692
|
+
* Scopes requested from KingsChat. Defaults to `['send_chat_message']`, which
|
|
693
|
+
* is what KingsChat's own SDK asks for and is enough to establish identity.
|
|
676
694
|
*/
|
|
677
|
-
|
|
678
|
-
|
|
695
|
+
kingsChatScopes?: string[];
|
|
696
|
+
promptPin?: () => Promise<string>;
|
|
697
|
+
apiBaseUrl?: string;
|
|
698
|
+
chains?: string[];
|
|
699
|
+
rpcUrls?: Record<string, string>;
|
|
679
700
|
/**
|
|
680
|
-
*
|
|
681
|
-
*
|
|
682
|
-
*
|
|
683
|
-
*
|
|
684
|
-
*
|
|
701
|
+
* Show the built-in full-screen status overlay while a wallet is being
|
|
702
|
+
* created or unlocked. Default **true**.
|
|
703
|
+
*
|
|
704
|
+
* The overlay exists because those flows can take several seconds and often
|
|
705
|
+
* run right after a Google redirect, when no modal of yours is mounted — so
|
|
706
|
+
* without it the page can look frozen. It is rendered by `<DecaneKit>` itself,
|
|
707
|
+
* as a portal, and injects its own theme tokens.
|
|
708
|
+
*
|
|
709
|
+
* Set false to suppress it and render your own. Everything it displays is
|
|
710
|
+
* already available to you: `useSocialAuth()` exposes `phase`
|
|
711
|
+
* (`"creating" | "unlocking" | null`) plus the `isCreatingWallet` /
|
|
712
|
+
* `isUnlockingWallet` booleans, and the client emits `wallet-creating` and
|
|
713
|
+
* `wallet-unlocking` events. If you suppress it, show something of your own —
|
|
714
|
+
* a silent multi-second pause reads as a broken page.
|
|
685
715
|
*/
|
|
686
|
-
|
|
716
|
+
showStatusOverlay?: boolean;
|
|
687
717
|
/**
|
|
688
|
-
*
|
|
689
|
-
*
|
|
690
|
-
*
|
|
691
|
-
*
|
|
718
|
+
* Offer to save a recovery-share file at signup. **Default false** since
|
|
719
|
+
* 2.7.4 — opt in explicitly with `true`.
|
|
720
|
+
*
|
|
721
|
+
* Server-assisted provisioning now covers the new-device case (wallets are
|
|
722
|
+
* enrolled at creation and repair themselves on unlock), so for most apps
|
|
723
|
+
* this dialog is a password prompt during onboarding that buys nothing. Turn
|
|
724
|
+
* it on if you want users to hold an offline fallback that works when Decane
|
|
725
|
+
* is unreachable.
|
|
692
726
|
*/
|
|
693
|
-
|
|
694
|
-
/**
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
727
|
+
offerRecoveryAtSignup?: boolean;
|
|
728
|
+
/** Ask the user whether they want a recovery share and, if so, a password for it. */
|
|
729
|
+
onRecoveryShareOffer?: (addresses: {
|
|
730
|
+
evmAddress: string;
|
|
731
|
+
solanaAddress: string;
|
|
732
|
+
}) => Promise<{
|
|
733
|
+
wants: boolean;
|
|
734
|
+
password: string;
|
|
735
|
+
passwordHint?: string;
|
|
736
|
+
}>;
|
|
737
|
+
/** Hand the built file + suggested filename to the app, which owns the download. */
|
|
738
|
+
onRecoveryFileReady?: (file: RecoveryShareFile, filename: string) => Promise<void>;
|
|
702
739
|
/**
|
|
703
|
-
*
|
|
704
|
-
*
|
|
705
|
-
*
|
|
706
|
-
*
|
|
740
|
+
* Prompt for a recovery-share file (and its password) during recovery.
|
|
741
|
+
* Return null to cancel. **Without this, a user who needs the recovery-file
|
|
742
|
+
* path fails immediately with NewDeviceError** — there is nowhere to ask them
|
|
743
|
+
* for the file.
|
|
707
744
|
*/
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
balance: BalanceResult | null;
|
|
713
|
-
}>>;
|
|
745
|
+
promptForRecoveryFile?: () => Promise<{
|
|
746
|
+
value: unknown;
|
|
747
|
+
getPassword: () => Promise<string | null>;
|
|
748
|
+
} | null>;
|
|
714
749
|
/**
|
|
715
|
-
*
|
|
716
|
-
*
|
|
717
|
-
*
|
|
718
|
-
* to the user — this does not download anything itself).
|
|
750
|
+
* Every successful recovery/rotation invalidates the previous recovery file,
|
|
751
|
+
* so this must prompt for a new password and capture the replacement.
|
|
752
|
+
* Required before recovery can run at all.
|
|
719
753
|
*/
|
|
720
|
-
|
|
754
|
+
onRecoveryRotated?: (addresses: {
|
|
755
|
+
evmAddress: string;
|
|
756
|
+
solanaAddress: string;
|
|
757
|
+
}) => Promise<{
|
|
721
758
|
password: string;
|
|
722
759
|
passwordHint?: string;
|
|
723
|
-
}
|
|
760
|
+
}>;
|
|
761
|
+
/** Recovery stage callbacks, for your own progress UI. */
|
|
762
|
+
onRecoveryProgress?: (event: RecoveryProgress) => void;
|
|
724
763
|
/**
|
|
725
|
-
*
|
|
726
|
-
*
|
|
727
|
-
*
|
|
728
|
-
*
|
|
764
|
+
* Require a fresh WebAuthn assertion for every signature rather than letting
|
|
765
|
+
* an unlocked session sign freely until it expires. Enforced inside the TEE.
|
|
766
|
+
*
|
|
767
|
+
* Does not change the session lifetime — the session stays warm (no
|
|
768
|
+
* re-attestation, no re-auth) and authority is re-proven per signature
|
|
769
|
+
* instead. Default false. Needs a passkey; PIN-only wallets have no
|
|
770
|
+
* authenticator to assert with. See SocialConfig's counterpart in
|
|
771
|
+
* social/types.ts for the full caveats.
|
|
729
772
|
*/
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
773
|
+
requireAssertionPerSignature?: boolean;
|
|
774
|
+
/**
|
|
775
|
+
* Sign in with a token from your own auth system (Auth0, Clerk, Firebase,
|
|
776
|
+
* Cognito, or your own backend) — surfaced as a button in the built-in
|
|
777
|
+
* WalletSelector / SocialWalletModal UI, alongside Google and email.
|
|
778
|
+
* getToken() runs when the user clicks the button; wire it to however you
|
|
779
|
+
* already obtain a token from your auth system (e.g. after your own login
|
|
780
|
+
* flow completes). Configure the issuer and JWKS URL for this token
|
|
781
|
+
* against your project in the dashboard first. `providerId` is only
|
|
782
|
+
* needed when the project has more than one custom provider configured.
|
|
783
|
+
*/
|
|
784
|
+
customAuth?: {
|
|
785
|
+
label: string;
|
|
786
|
+
providerId?: string;
|
|
787
|
+
getToken: () => Promise<string>;
|
|
788
|
+
};
|
|
746
789
|
}
|
|
747
|
-
interface
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
790
|
+
interface DecaneConfig {
|
|
791
|
+
appId: string;
|
|
792
|
+
chains?: ChainDescriptor[];
|
|
793
|
+
supportedChainIds?: string[];
|
|
794
|
+
networks?: "evm" | "solana" | "tron" | "bitcoin" | "all";
|
|
795
|
+
defaultChain?: string;
|
|
796
|
+
theme?: "light" | "dark" | "auto";
|
|
797
|
+
mode?: ConnectMode;
|
|
798
|
+
social?: SocialConfig;
|
|
753
799
|
}
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
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;
|
|
800
|
+
interface EIP1193Provider {
|
|
801
|
+
request(args: {
|
|
802
|
+
method: string;
|
|
803
|
+
params?: unknown[];
|
|
804
|
+
}): Promise<unknown>;
|
|
805
|
+
on(event: string, handler: (...args: unknown[]) => void): void;
|
|
806
|
+
removeListener(event: string, handler: (...args: unknown[]) => void): void;
|
|
769
807
|
}
|
|
770
|
-
interface
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
808
|
+
interface StandardWallet {
|
|
809
|
+
name: string;
|
|
810
|
+
icon: string;
|
|
811
|
+
features: Record<string, unknown>;
|
|
812
|
+
accounts: StandardWalletAccount[];
|
|
813
|
+
}
|
|
814
|
+
interface StandardWalletAccount {
|
|
815
|
+
address: string;
|
|
816
|
+
publicKey: Uint8Array;
|
|
817
|
+
chains: string[];
|
|
818
|
+
}
|
|
819
|
+
interface TronProvider {
|
|
820
|
+
request(args: {
|
|
821
|
+
method: string;
|
|
822
|
+
params?: unknown[] | Record<string, unknown>;
|
|
823
|
+
}): Promise<unknown>;
|
|
824
|
+
on(event: string, handler: (...args: unknown[]) => void): void;
|
|
825
|
+
removeListener(event: string, handler: (...args: unknown[]) => void): void;
|
|
826
|
+
}
|
|
827
|
+
interface BitcoinProvider {
|
|
828
|
+
requestAccounts(): Promise<string[] | Array<{
|
|
829
|
+
address: string;
|
|
830
|
+
}>>;
|
|
831
|
+
getAccounts?(): Promise<string[] | Array<{
|
|
832
|
+
address: string;
|
|
833
|
+
}>>;
|
|
834
|
+
signMessage(message: string, ...args: unknown[]): Promise<string>;
|
|
835
|
+
sendBitcoin?(toAddress: string, satoshis: number, options?: unknown): Promise<string>;
|
|
836
|
+
send?(params: unknown): Promise<string>;
|
|
837
|
+
signPsbt?(psbtHex: string, options?: unknown): Promise<string>;
|
|
838
|
+
on?(event: string, handler: (...args: unknown[]) => void): void;
|
|
776
839
|
}
|
|
777
840
|
|
|
841
|
+
declare function createDecane(config: DecaneConfig): DecaneProvider;
|
|
842
|
+
|
|
778
843
|
declare function createDecaneConnect(config: DecaneConnectConfig): Promise<DecaneConnect>;
|
|
779
844
|
|
|
780
845
|
/**
|
|
@@ -1006,13 +1071,17 @@ interface WalletSelectorProps {
|
|
|
1006
1071
|
theme?: "light" | "dark" | "auto";
|
|
1007
1072
|
networks?: "evm" | "solana" | "tron" | "bitcoin" | "all";
|
|
1008
1073
|
mode?: ConnectMode;
|
|
1009
|
-
socialAuthMethods?: ("google" | "email" | "kingschat")[];
|
|
1074
|
+
socialAuthMethods?: ("google" | "email" | "phone" | "kingschat")[];
|
|
1010
1075
|
onGoogleSignIn?: () => Promise<void>;
|
|
1011
1076
|
onKingsChatSignIn?: () => Promise<void>;
|
|
1012
1077
|
onEmailStart?: (email: string) => Promise<{
|
|
1013
1078
|
codeRequired: true;
|
|
1014
1079
|
}>;
|
|
1015
1080
|
onEmailVerify?: (email: string, code: string) => Promise<void>;
|
|
1081
|
+
onPhoneStart?: (phone: string) => Promise<{
|
|
1082
|
+
codeRequired: true;
|
|
1083
|
+
}>;
|
|
1084
|
+
onPhoneVerify?: (phone: string, code: string) => Promise<void>;
|
|
1016
1085
|
/** Button text for the developer's own auth system, e.g. "Continue with Acme Inc". Omitted entirely when not configured. */
|
|
1017
1086
|
customAuthLabel?: string;
|
|
1018
1087
|
onCustomAuthSignIn?: () => Promise<void>;
|
|
@@ -1022,7 +1091,7 @@ interface WalletSelectorProps {
|
|
|
1022
1091
|
passkeyAddress?: string | null;
|
|
1023
1092
|
onPasskeySignIn?: () => Promise<unknown>;
|
|
1024
1093
|
}
|
|
1025
|
-
declare function WalletSelector({ provider, open, onClose, onConnect, theme, networks, mode, socialAuthMethods, onGoogleSignIn, onKingsChatSignIn, onEmailStart, onEmailVerify, customAuthLabel, onCustomAuthSignIn, canUsePasskey, passkeyAddress, onPasskeySignIn, }: WalletSelectorProps): React.ReactPortal | null;
|
|
1094
|
+
declare function WalletSelector({ provider, open, onClose, onConnect, theme, networks, mode, socialAuthMethods, onGoogleSignIn, onKingsChatSignIn, onEmailStart, onEmailVerify, onPhoneStart, onPhoneVerify, customAuthLabel, onCustomAuthSignIn, canUsePasskey, passkeyAddress, onPasskeySignIn, }: WalletSelectorProps): React.ReactPortal | null;
|
|
1026
1095
|
|
|
1027
1096
|
type Theme = "light" | "dark" | "auto";
|
|
1028
1097
|
interface SocialAuthState {
|
|
@@ -1085,7 +1154,11 @@ interface SocialWalletModalProps {
|
|
|
1085
1154
|
codeRequired: true;
|
|
1086
1155
|
}>;
|
|
1087
1156
|
onEmailVerify: (email: string, code: string) => Promise<void>;
|
|
1088
|
-
|
|
1157
|
+
onPhoneStart?: (phone: string) => Promise<{
|
|
1158
|
+
codeRequired: true;
|
|
1159
|
+
}>;
|
|
1160
|
+
onPhoneVerify?: (phone: string, code: string) => Promise<void>;
|
|
1161
|
+
authMethods?: Array<"google" | "email" | "phone" | "kingschat">;
|
|
1089
1162
|
customAuthLabel?: string;
|
|
1090
1163
|
onCustomAuthSignIn?: () => Promise<void>;
|
|
1091
1164
|
canUsePasskey?: boolean;
|
|
@@ -1102,7 +1175,7 @@ interface SocialWalletModalProps {
|
|
|
1102
1175
|
rpcUrls?: Record<string, string>;
|
|
1103
1176
|
chains?: string[];
|
|
1104
1177
|
}
|
|
1105
|
-
declare function SocialWalletModal({ open, onClose, theme, socialAuth, onUnlock, onDisconnect, onGoogleSignIn, onKingsChatSignIn, onEmailStart, onEmailVerify, authMethods, customAuthLabel, onCustomAuthSignIn, canUsePasskey, passkeyAddress, onPasskeySignIn, onRotateShares, onExportPortableBackup, rpcUrls, chains, }: SocialWalletModalProps): React.ReactPortal | null;
|
|
1178
|
+
declare function SocialWalletModal({ open, onClose, theme, socialAuth, onUnlock, onDisconnect, onGoogleSignIn, onKingsChatSignIn, onEmailStart, onEmailVerify, onPhoneStart, onPhoneVerify, authMethods, customAuthLabel, onCustomAuthSignIn, canUsePasskey, passkeyAddress, onPasskeySignIn, onRotateShares, onExportPortableBackup, rpcUrls, chains, }: SocialWalletModalProps): React.ReactPortal | null;
|
|
1106
1179
|
|
|
1107
1180
|
declare function DecaneMark({ size, muted }: {
|
|
1108
1181
|
size?: number;
|
|
@@ -1133,6 +1206,7 @@ declare function useSocialAuth(): {
|
|
|
1133
1206
|
needsReconnect: boolean;
|
|
1134
1207
|
googleLoading: boolean;
|
|
1135
1208
|
emailLoading: boolean;
|
|
1209
|
+
phoneLoading: boolean;
|
|
1136
1210
|
tokenLoading: boolean;
|
|
1137
1211
|
customLoading: boolean;
|
|
1138
1212
|
customAuthLabel: string | undefined;
|
|
@@ -1143,6 +1217,10 @@ declare function useSocialAuth(): {
|
|
|
1143
1217
|
codeRequired: true;
|
|
1144
1218
|
}>;
|
|
1145
1219
|
confirmEmailCode: (email: string, code: string) => Promise<void>;
|
|
1220
|
+
sendPhoneCode: (phone: string) => Promise<{
|
|
1221
|
+
codeRequired: true;
|
|
1222
|
+
}>;
|
|
1223
|
+
confirmPhoneCode: (phone: string, code: string) => Promise<void>;
|
|
1146
1224
|
signInWithToken: (token: string, opts?: {
|
|
1147
1225
|
providerId?: string;
|
|
1148
1226
|
}) => Promise<void>;
|