decane-connect-kit 2.4.0 → 2.6.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 +425 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +182 -7
- package/dist/index.d.ts +182 -7
- package/dist/index.js +423 -5
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -27,12 +27,13 @@ __export(errors_exports, {
|
|
|
27
27
|
RecoveryWalletMismatchError: () => RecoveryWalletMismatchError,
|
|
28
28
|
SessionExpiredError: () => SessionExpiredError,
|
|
29
29
|
ShareCorruptedError: () => ShareCorruptedError,
|
|
30
|
+
StaleDeviceShareError: () => StaleDeviceShareError,
|
|
30
31
|
UnlockRequiredError: () => UnlockRequiredError,
|
|
31
32
|
UserCancelledError: () => UserCancelledError,
|
|
32
33
|
WebAuthnNotSupportedError: () => WebAuthnNotSupportedError,
|
|
33
34
|
WrongPasswordError: () => WrongPasswordError
|
|
34
35
|
});
|
|
35
|
-
var DecaneConnectError, InvalidApiKeyError, OriginNotAllowedError, SessionExpiredError, UnlockRequiredError, ReconnectRequiredError, UserCancelledError, WebAuthnNotSupportedError, InvalidPinError, CallSequenceError, NetworkError, ShareCorruptedError, NewDeviceError, PromptPinNotConfiguredError, AttestationError, RecoveryFailedError, InvalidRecoveryFileError, WrongPasswordError, RecoveryWalletMismatchError, ExportNotAuthorizedError;
|
|
36
|
+
var DecaneConnectError, InvalidApiKeyError, OriginNotAllowedError, SessionExpiredError, UnlockRequiredError, ReconnectRequiredError, UserCancelledError, WebAuthnNotSupportedError, InvalidPinError, CallSequenceError, NetworkError, ShareCorruptedError, NewDeviceError, StaleDeviceShareError, PromptPinNotConfiguredError, AttestationError, RecoveryFailedError, InvalidRecoveryFileError, WrongPasswordError, RecoveryWalletMismatchError, ExportNotAuthorizedError;
|
|
36
37
|
var init_errors = __esm({
|
|
37
38
|
"src/social/errors.ts"() {
|
|
38
39
|
"use strict";
|
|
@@ -108,6 +109,15 @@ var init_errors = __esm({
|
|
|
108
109
|
super("NEW_DEVICE", "No device share found. Re-authenticate to set up this device.");
|
|
109
110
|
}
|
|
110
111
|
};
|
|
112
|
+
StaleDeviceShareError = class extends DecaneConnectError {
|
|
113
|
+
constructor() {
|
|
114
|
+
super(
|
|
115
|
+
"STALE_DEVICE_SHARE",
|
|
116
|
+
"This device\u2019s wallet share is from a superseded share set (likely rotated from another device) and no longer matches this account. Reconnect to recover a fresh share.",
|
|
117
|
+
true
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
111
121
|
PromptPinNotConfiguredError = class extends DecaneConnectError {
|
|
112
122
|
constructor() {
|
|
113
123
|
super("PROMPT_PIN_NOT_CONFIGURED", "Passkey unavailable and no promptPin callback was provided in config");
|
|
@@ -1957,6 +1967,32 @@ var ApiClient = class {
|
|
|
1957
1967
|
async getGoogleInitUrl() {
|
|
1958
1968
|
return this._request("/auth/google/init", { method: "GET" });
|
|
1959
1969
|
}
|
|
1970
|
+
/**
|
|
1971
|
+
* Exchange a Google ID token the app already holds for a Decane session.
|
|
1972
|
+
* Resolves to the SAME identity as the redirect flow, so one Google account
|
|
1973
|
+
* means one wallet across web and mobile.
|
|
1974
|
+
*/
|
|
1975
|
+
async authGoogleToken(idToken) {
|
|
1976
|
+
return this._request("/auth/google/token", {
|
|
1977
|
+
method: "POST",
|
|
1978
|
+
body: JSON.stringify({ idToken })
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1981
|
+
/** The project's KingsChat clientId, so the dApp never has to hardcode it. */
|
|
1982
|
+
async getKingsChatConfig() {
|
|
1983
|
+
return this._request("/auth/kingschat/config", { method: "GET" });
|
|
1984
|
+
}
|
|
1985
|
+
/**
|
|
1986
|
+
* Exchange a KingsChat access token for a Decane session. The backend spends
|
|
1987
|
+
* the token against KingsChat's own API to establish the identity — it is
|
|
1988
|
+
* never trusted on its face, and never stored.
|
|
1989
|
+
*/
|
|
1990
|
+
async authKingsChat(accessToken) {
|
|
1991
|
+
return this._request("/auth/kingschat", {
|
|
1992
|
+
method: "POST",
|
|
1993
|
+
body: JSON.stringify({ accessToken })
|
|
1994
|
+
});
|
|
1995
|
+
}
|
|
1960
1996
|
async authEmailStart(email) {
|
|
1961
1997
|
await this._request("/auth/email/start", {
|
|
1962
1998
|
method: "POST",
|
|
@@ -1991,6 +2027,21 @@ var ApiClient = class {
|
|
|
1991
2027
|
})
|
|
1992
2028
|
});
|
|
1993
2029
|
}
|
|
2030
|
+
// ── Transaction history ───────────────────────────────────────────────────
|
|
2031
|
+
async recordTransaction(jwt, tx) {
|
|
2032
|
+
const body = await this._request("/transactions", {
|
|
2033
|
+
method: "POST",
|
|
2034
|
+
headers: { Authorization: `Bearer ${jwt}` },
|
|
2035
|
+
body: JSON.stringify(tx)
|
|
2036
|
+
});
|
|
2037
|
+
return body.transaction;
|
|
2038
|
+
}
|
|
2039
|
+
async getTransactions(jwt, limit = 50) {
|
|
2040
|
+
const body = await this._request(`/transactions?limit=${encodeURIComponent(String(limit))}`, {
|
|
2041
|
+
headers: { Authorization: `Bearer ${jwt}` }
|
|
2042
|
+
});
|
|
2043
|
+
return body.transactions;
|
|
2044
|
+
}
|
|
1994
2045
|
// ── WebAuthn registration (additive to the PRF-only passkey flow) ─────────
|
|
1995
2046
|
async getWebAuthnRegisterOptions(jwt, rpId, rpName) {
|
|
1996
2047
|
return this._request(`/webauthn/register-options?rpId=${encodeURIComponent(rpId)}&rpName=${encodeURIComponent(rpName)}`, {
|
|
@@ -2266,6 +2317,13 @@ var TeeClient = class {
|
|
|
2266
2317
|
requireAssertionPerSignature
|
|
2267
2318
|
})
|
|
2268
2319
|
});
|
|
2320
|
+
if (res.status === 403) {
|
|
2321
|
+
const body2 = await res.json().catch(() => ({}));
|
|
2322
|
+
if (body2?.error === "share_mismatch") {
|
|
2323
|
+
const { StaleDeviceShareError: StaleDeviceShareError2 } = await Promise.resolve().then(() => (init_errors(), errors_exports));
|
|
2324
|
+
throw new StaleDeviceShareError2();
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2269
2327
|
if (!res.ok) throw new Error("session open failed");
|
|
2270
2328
|
const body = await res.json();
|
|
2271
2329
|
this.handle = body.handle;
|
|
@@ -2325,6 +2383,45 @@ var TeeClient = class {
|
|
|
2325
2383
|
}
|
|
2326
2384
|
return { challengeId, assertion };
|
|
2327
2385
|
}
|
|
2386
|
+
/**
|
|
2387
|
+
* Enrol this wallet for server-assisted provisioning by handing the enclave
|
|
2388
|
+
* the recovery share to seal.
|
|
2389
|
+
*
|
|
2390
|
+
* After this, a new device gets a working share from login alone — no passkey
|
|
2391
|
+
* sync, no recovery file. The enclave seals the share under a key only it can
|
|
2392
|
+
* derive, so the backend stores ciphertext it cannot read; but the enclave
|
|
2393
|
+
* then holds two of three shares and can reconstruct on its own. That is the
|
|
2394
|
+
* trade this call makes, which is why it is opt-out rather than implicit.
|
|
2395
|
+
*/
|
|
2396
|
+
async enrolAuthShare(jwt, recoveryShare) {
|
|
2397
|
+
await this.ensureAttested();
|
|
2398
|
+
const res = await fetch(`${this.teeUrl}/signer/auth-share`, {
|
|
2399
|
+
method: "POST",
|
|
2400
|
+
headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
|
|
2401
|
+
body: JSON.stringify({ share: bytesToBase64(recoveryShare) })
|
|
2402
|
+
});
|
|
2403
|
+
if (!res.ok) throw new Error("auth share enrolment failed");
|
|
2404
|
+
}
|
|
2405
|
+
/**
|
|
2406
|
+
* Ask the enclave for a fresh device share for this authenticated user.
|
|
2407
|
+
* Returns null when the wallet was never enrolled — the signal to fall back
|
|
2408
|
+
* to passkey sync or the recovery file rather than fail.
|
|
2409
|
+
*
|
|
2410
|
+
* Every successful call re-splits the set, so any share this device or any
|
|
2411
|
+
* other held is now dead. Intentional: a leaked device share stops working the
|
|
2412
|
+
* next time the user signs in anywhere.
|
|
2413
|
+
*/
|
|
2414
|
+
async provisionDeviceShare(jwt) {
|
|
2415
|
+
await this.ensureAttested();
|
|
2416
|
+
const res = await fetch(`${this.teeUrl}/signer/provision`, {
|
|
2417
|
+
method: "POST",
|
|
2418
|
+
headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" }
|
|
2419
|
+
});
|
|
2420
|
+
if (res.status === 409) return null;
|
|
2421
|
+
if (!res.ok) throw new Error("device share provisioning failed");
|
|
2422
|
+
const body = await res.json();
|
|
2423
|
+
return base64ToBytes(body.deviceShare);
|
|
2424
|
+
}
|
|
2328
2425
|
/** Voluntary rotation — e.g. after a suspected device compromise, or to invalidate an old recovery file. */
|
|
2329
2426
|
async rotateShares(jwt) {
|
|
2330
2427
|
if (!this.handle) throw new Error("no session");
|
|
@@ -2711,6 +2808,79 @@ async function verifyEmailCode(api, email, code) {
|
|
|
2711
2808
|
return api.authEmailVerify(email, code);
|
|
2712
2809
|
}
|
|
2713
2810
|
|
|
2811
|
+
// src/social/auth/kingschat.ts
|
|
2812
|
+
init_errors();
|
|
2813
|
+
var KINGSCHAT_AUTH_ORIGIN = "https://accounts.kingsch.at";
|
|
2814
|
+
function readTokens(data) {
|
|
2815
|
+
if (!data || typeof data !== "object") return null;
|
|
2816
|
+
const d = data;
|
|
2817
|
+
const accessToken = d.accessToken ?? d.access_token;
|
|
2818
|
+
if (typeof accessToken !== "string" || !accessToken) return null;
|
|
2819
|
+
const refreshToken = d.refreshToken ?? d.refresh_token;
|
|
2820
|
+
const expires = d.expiresInMillis ?? d.expires_in_millis;
|
|
2821
|
+
return {
|
|
2822
|
+
accessToken,
|
|
2823
|
+
refreshToken: typeof refreshToken === "string" ? refreshToken : void 0,
|
|
2824
|
+
expiresInMillis: typeof expires === "number" ? expires : void 0
|
|
2825
|
+
};
|
|
2826
|
+
}
|
|
2827
|
+
function buildAuthUrl(clientId, scopes) {
|
|
2828
|
+
const url = new URL(KINGSCHAT_AUTH_ORIGIN);
|
|
2829
|
+
url.searchParams.set("client_id", clientId);
|
|
2830
|
+
url.searchParams.set("scopes", JSON.stringify(scopes));
|
|
2831
|
+
url.searchParams.set("redirect_uri", window.location.origin);
|
|
2832
|
+
url.searchParams.set("post_message", "1");
|
|
2833
|
+
return url.toString();
|
|
2834
|
+
}
|
|
2835
|
+
function loginWithKingsChatPopup(opts) {
|
|
2836
|
+
const scopes = opts.scopes ?? ["send_chat_message"];
|
|
2837
|
+
const popup = window.open(
|
|
2838
|
+
buildAuthUrl(opts.clientId, scopes),
|
|
2839
|
+
"decane-kingschat",
|
|
2840
|
+
"width=480,height=720,menubar=no,toolbar=no"
|
|
2841
|
+
);
|
|
2842
|
+
if (!popup) {
|
|
2843
|
+
throw new CallSequenceError(
|
|
2844
|
+
"The KingsChat sign-in popup was blocked. Call connectWithKingsChat() directly from a click handler."
|
|
2845
|
+
);
|
|
2846
|
+
}
|
|
2847
|
+
return new Promise((resolve, reject) => {
|
|
2848
|
+
let settled = false;
|
|
2849
|
+
const finish = (fn) => {
|
|
2850
|
+
if (settled) return;
|
|
2851
|
+
settled = true;
|
|
2852
|
+
window.removeEventListener("message", onMessage);
|
|
2853
|
+
clearInterval(closedPoll);
|
|
2854
|
+
clearTimeout(timeout);
|
|
2855
|
+
try {
|
|
2856
|
+
popup.close();
|
|
2857
|
+
} catch {
|
|
2858
|
+
}
|
|
2859
|
+
fn();
|
|
2860
|
+
};
|
|
2861
|
+
const onMessage = (event) => {
|
|
2862
|
+
if (event.origin !== KINGSCHAT_AUTH_ORIGIN) return;
|
|
2863
|
+
const data = event.data;
|
|
2864
|
+
if (data && typeof data === "object" && data.error) {
|
|
2865
|
+
finish(() => reject(new CallSequenceError(`KingsChat sign-in failed: ${String(data.error)}`)));
|
|
2866
|
+
return;
|
|
2867
|
+
}
|
|
2868
|
+
const tokens2 = readTokens(event.data);
|
|
2869
|
+
if (tokens2) finish(() => resolve(tokens2));
|
|
2870
|
+
};
|
|
2871
|
+
window.addEventListener("message", onMessage);
|
|
2872
|
+
const closedPoll = setInterval(() => {
|
|
2873
|
+
if (popup.closed) finish(() => reject(new UserCancelledError()));
|
|
2874
|
+
}, 400);
|
|
2875
|
+
const timeout = setTimeout(
|
|
2876
|
+
() => finish(
|
|
2877
|
+
() => reject(new CallSequenceError("KingsChat sign-in timed out before returning a token."))
|
|
2878
|
+
),
|
|
2879
|
+
opts.timeoutMs ?? 5 * 6e4
|
|
2880
|
+
);
|
|
2881
|
+
});
|
|
2882
|
+
}
|
|
2883
|
+
|
|
2714
2884
|
// src/social/signers/evm.ts
|
|
2715
2885
|
import { keccak256, Transaction, resolveProperties } from "ethers";
|
|
2716
2886
|
async function signEvmTransaction(tee, chain, tx, jwt) {
|
|
@@ -3073,7 +3243,7 @@ async function recoverDeviceShare(input) {
|
|
|
3073
3243
|
prfSalt: backup.prfSalt,
|
|
3074
3244
|
version: 1
|
|
3075
3245
|
});
|
|
3076
|
-
return deviceShare;
|
|
3246
|
+
return { deviceShare, flow: "passkey-sync" };
|
|
3077
3247
|
} catch (e) {
|
|
3078
3248
|
input.onProgress?.({
|
|
3079
3249
|
type: "passkey-failed",
|
|
@@ -3101,7 +3271,7 @@ async function recoverDeviceShare(input) {
|
|
|
3101
3271
|
input.onProgress?.({ type: "rotated", shareSetVersion: result.shareSetVersion });
|
|
3102
3272
|
await input.onNewRecoveryShare?.(result.recoveryShare, result.shareSetVersion);
|
|
3103
3273
|
zeroize(result.recoveryShare);
|
|
3104
|
-
return result.deviceShare;
|
|
3274
|
+
return { deviceShare: result.deviceShare, flow: "recovery-file" };
|
|
3105
3275
|
}
|
|
3106
3276
|
|
|
3107
3277
|
// src/social/recovery/portableBackup.ts
|
|
@@ -3219,6 +3389,11 @@ async function createDecaneConnect(config) {
|
|
|
3219
3389
|
if (!config.apiKey) throw new Error("decane-connect-kit: apiKey is required");
|
|
3220
3390
|
if (!config.chains?.length)
|
|
3221
3391
|
throw new Error("decane-connect-kit: at least one chain is required");
|
|
3392
|
+
if (config.rotateOnNewDeviceRecovery && !config.onRecoveryRotated) {
|
|
3393
|
+
throw new Error(
|
|
3394
|
+
"decane-connect-kit: rotateOnNewDeviceRecovery requires an onRecoveryRotated callback \u2014 rotating on device join invalidates the user's existing recovery file, and the replacement must be saved."
|
|
3395
|
+
);
|
|
3396
|
+
}
|
|
3222
3397
|
const baseUrl = config.apiBaseUrl ?? DEFAULT_API_BASE;
|
|
3223
3398
|
const durationMs = (config.sessionDurationMinutes ?? DEFAULT_SESSION_MINUTES) * 6e4;
|
|
3224
3399
|
const rpId = config.rpId ?? (typeof window !== "undefined" ? window.location.hostname : "localhost");
|
|
@@ -3238,6 +3413,7 @@ async function createDecaneConnect(config) {
|
|
|
3238
3413
|
let persisted = loadPersisted(PERSIST_KEY);
|
|
3239
3414
|
let ethProvider = null;
|
|
3240
3415
|
const persistSession = config.persistSession !== false;
|
|
3416
|
+
const serverAssistedRecovery = config.serverAssistedRecovery !== false;
|
|
3241
3417
|
const SESSION_KEY = sessionPersistKey(config.apiKey);
|
|
3242
3418
|
function persistLiveSession() {
|
|
3243
3419
|
if (!persistSession) return;
|
|
@@ -3463,6 +3639,17 @@ async function createDecaneConnect(config) {
|
|
|
3463
3639
|
console.warn("decane-connect-kit: recovery share offer failed or was skipped.", e);
|
|
3464
3640
|
}
|
|
3465
3641
|
}
|
|
3642
|
+
if (serverAssistedRecovery) {
|
|
3643
|
+
try {
|
|
3644
|
+
const tee = new TeeClient(teeUrl);
|
|
3645
|
+
await tee.enrolAuthShare(jwt, shareSet.recovery);
|
|
3646
|
+
} catch (e) {
|
|
3647
|
+
console.warn(
|
|
3648
|
+
"decane-connect-kit: could not enrol this wallet for server-assisted recovery; a recovery file will be required on a new device.",
|
|
3649
|
+
e
|
|
3650
|
+
);
|
|
3651
|
+
}
|
|
3652
|
+
}
|
|
3466
3653
|
zeroize(shareSet.recovery);
|
|
3467
3654
|
return addresses;
|
|
3468
3655
|
}
|
|
@@ -3471,13 +3658,36 @@ async function createDecaneConnect(config) {
|
|
|
3471
3658
|
const recordedAddresses = rawAddresses.evm && rawAddresses.solana ? { evm: rawAddresses.evm, solana: rawAddresses.solana, tron: rawAddresses.tron ?? evmToTronAddress2(rawAddresses.evm) } : null;
|
|
3472
3659
|
const stored = await storage.getShare(userId);
|
|
3473
3660
|
let deviceShare;
|
|
3661
|
+
let joinedViaPasskeySync = false;
|
|
3474
3662
|
if (!stored) {
|
|
3663
|
+
if (serverAssistedRecovery) {
|
|
3664
|
+
try {
|
|
3665
|
+
const tee = new TeeClient(teeUrl);
|
|
3666
|
+
const provisioned = await tee.provisionDeviceShare(jwt);
|
|
3667
|
+
if (provisioned) {
|
|
3668
|
+
await registerPasskeyAndBackup(userId, jwt, provisioned).catch(() => {
|
|
3669
|
+
});
|
|
3670
|
+
let addrs;
|
|
3671
|
+
try {
|
|
3672
|
+
addrs = await session.start(provisioned, jwt, recordedAddresses);
|
|
3673
|
+
} finally {
|
|
3674
|
+
zeroize(provisioned);
|
|
3675
|
+
}
|
|
3676
|
+
return addrs;
|
|
3677
|
+
}
|
|
3678
|
+
} catch (e) {
|
|
3679
|
+
console.warn(
|
|
3680
|
+
"decane-connect-kit: server-assisted provisioning unavailable; falling back to passkey/recovery-file recovery.",
|
|
3681
|
+
e
|
|
3682
|
+
);
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3475
3685
|
if (!config.onRecoveryRotated) {
|
|
3476
3686
|
throw new CallSequenceError(
|
|
3477
3687
|
"Recovery rotates the share set and issues a new recovery file, but no onRecoveryRotated callback is configured to save it. Configure onRecoveryRotated before allowing recovery."
|
|
3478
3688
|
);
|
|
3479
3689
|
}
|
|
3480
|
-
|
|
3690
|
+
const recovered = await recoverDeviceShare({
|
|
3481
3691
|
userId,
|
|
3482
3692
|
jwt,
|
|
3483
3693
|
rpId,
|
|
@@ -3495,6 +3705,8 @@ async function createDecaneConnect(config) {
|
|
|
3495
3705
|
await api.markRecoveryShareSaved(jwt);
|
|
3496
3706
|
}
|
|
3497
3707
|
});
|
|
3708
|
+
deviceShare = recovered.deviceShare;
|
|
3709
|
+
joinedViaPasskeySync = recovered.flow === "passkey-sync";
|
|
3498
3710
|
if (!await storage.getShare(userId) && isPasskeySupported()) {
|
|
3499
3711
|
await registerPasskeyAndBackup(userId, jwt, deviceShare);
|
|
3500
3712
|
}
|
|
@@ -3510,15 +3722,66 @@ async function createDecaneConnect(config) {
|
|
|
3510
3722
|
throw stored.unlockMethod === "pin" ? new InvalidPinError() : new ShareCorruptedError();
|
|
3511
3723
|
}
|
|
3512
3724
|
}
|
|
3513
|
-
|
|
3725
|
+
let addresses;
|
|
3726
|
+
try {
|
|
3727
|
+
addresses = await session.start(deviceShare, jwt, recordedAddresses);
|
|
3728
|
+
} catch (e) {
|
|
3729
|
+
if (e instanceof StaleDeviceShareError) {
|
|
3730
|
+
zeroize(deviceShare);
|
|
3731
|
+
await storage.deleteShare(userId);
|
|
3732
|
+
if (stored) return returningUserUnlock(userId, jwt);
|
|
3733
|
+
} else {
|
|
3734
|
+
zeroize(deviceShare);
|
|
3735
|
+
}
|
|
3736
|
+
throw e;
|
|
3737
|
+
}
|
|
3514
3738
|
zeroize(deviceShare);
|
|
3515
3739
|
if (recordedAddresses && recordedAddresses.evm.toLowerCase() !== addresses.evm.toLowerCase()) {
|
|
3516
3740
|
console.warn(
|
|
3517
3741
|
`decane-connect-kit: recorded EVM address (${recordedAddresses.evm}) does not match the signing key (${addresses.evm}). Using the signing key's address; the record has been corrected.`
|
|
3518
3742
|
);
|
|
3519
3743
|
}
|
|
3744
|
+
if (joinedViaPasskeySync && config.rotateOnNewDeviceRecovery) {
|
|
3745
|
+
await rotateForNewDevice(userId, jwt);
|
|
3746
|
+
}
|
|
3520
3747
|
return addresses;
|
|
3521
3748
|
}
|
|
3749
|
+
async function rotateForNewDevice(userId, jwt) {
|
|
3750
|
+
const tee = session.requireActive();
|
|
3751
|
+
const result = await tee.rotateShares(jwt);
|
|
3752
|
+
try {
|
|
3753
|
+
await persistRotatedDeviceShare(userId, result.deviceShare);
|
|
3754
|
+
const refreshed = await storage.getShare(userId);
|
|
3755
|
+
if (refreshed?.unlockMethod === "passkey" && refreshed.credentialId && refreshed.prfSalt) {
|
|
3756
|
+
await api.putRecoveryBackup(jwt, {
|
|
3757
|
+
wrappedDeviceShare: refreshed.wrappedDeviceShare,
|
|
3758
|
+
iv: refreshed.iv,
|
|
3759
|
+
unlockMethod: "passkey",
|
|
3760
|
+
credentialId: refreshed.credentialId,
|
|
3761
|
+
prfSalt: refreshed.prfSalt
|
|
3762
|
+
}).catch((e) => {
|
|
3763
|
+
console.warn("decane-connect-kit: backup refresh after device rotation failed; the next new device will need the recovery file.", e);
|
|
3764
|
+
});
|
|
3765
|
+
}
|
|
3766
|
+
const addrs = await deriveAddressesFromShares(result.deviceShare, result.recoveryShare);
|
|
3767
|
+
const { password, passwordHint } = await config.onRecoveryRotated({
|
|
3768
|
+
evmAddress: addrs.evm,
|
|
3769
|
+
solanaAddress: addrs.solana
|
|
3770
|
+
});
|
|
3771
|
+
const file = await buildRecoveryShareFile(
|
|
3772
|
+
result.recoveryShare,
|
|
3773
|
+
password,
|
|
3774
|
+
addrs,
|
|
3775
|
+
result.shareSetVersion,
|
|
3776
|
+
passwordHint
|
|
3777
|
+
);
|
|
3778
|
+
await config.onRecoveryFileReady?.(file, recoveryShareFileName(addrs.evm));
|
|
3779
|
+
await api.markRecoveryShareSaved(jwt);
|
|
3780
|
+
} finally {
|
|
3781
|
+
zeroize(result.deviceShare);
|
|
3782
|
+
zeroize(result.recoveryShare);
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3522
3785
|
async function passkeyLoginCandidate() {
|
|
3523
3786
|
if (!persisted?.userId || !isPasskeySupported()) return null;
|
|
3524
3787
|
const stored = await storage.getShare(persisted.userId);
|
|
@@ -3593,6 +3856,7 @@ async function createDecaneConnect(config) {
|
|
|
3593
3856
|
const method = config.authMethods[0];
|
|
3594
3857
|
if (!method) throw new CallSequenceError("No authMethods configured");
|
|
3595
3858
|
if (method === "google") return client.connectWithGoogle();
|
|
3859
|
+
if (method === "kingschat") return client.connectWithKingsChat();
|
|
3596
3860
|
if (method === "email")
|
|
3597
3861
|
throw new CallSequenceError(
|
|
3598
3862
|
"Use connectWithEmail(email) for email auth"
|
|
@@ -3622,6 +3886,25 @@ async function createDecaneConnect(config) {
|
|
|
3622
3886
|
return new Promise(() => {
|
|
3623
3887
|
});
|
|
3624
3888
|
},
|
|
3889
|
+
async connectWithGoogleToken(idToken) {
|
|
3890
|
+
if (!idToken) throw new CallSequenceError("connectWithGoogleToken requires an ID token");
|
|
3891
|
+
return handleAuthResult(await api.authGoogleToken(idToken));
|
|
3892
|
+
},
|
|
3893
|
+
async connectWithKingsChat() {
|
|
3894
|
+
const { clientId } = await api.getKingsChatConfig();
|
|
3895
|
+
const tokens2 = await loginWithKingsChatPopup({
|
|
3896
|
+
clientId,
|
|
3897
|
+
scopes: config.kingsChatScopes
|
|
3898
|
+
});
|
|
3899
|
+
return client.connectWithKingsChatToken(tokens2.accessToken);
|
|
3900
|
+
},
|
|
3901
|
+
async connectWithKingsChatToken(accessToken) {
|
|
3902
|
+
if (!accessToken) {
|
|
3903
|
+
throw new CallSequenceError("connectWithKingsChatToken requires an access token");
|
|
3904
|
+
}
|
|
3905
|
+
const result = await api.authKingsChat(accessToken);
|
|
3906
|
+
return handleAuthResult(result);
|
|
3907
|
+
},
|
|
3625
3908
|
async connectWithToken(token, opts) {
|
|
3626
3909
|
if (!token) throw new CallSequenceError("connectWithToken requires a token");
|
|
3627
3910
|
const result = await api.authExternal(token, opts?.providerId);
|
|
@@ -3733,6 +4016,16 @@ async function createDecaneConnect(config) {
|
|
|
3733
4016
|
};
|
|
3734
4017
|
const signedTx = await signEvmTransaction(tee, req.chain, tx, auth?.jwt);
|
|
3735
4018
|
const txResponse = await provider.broadcastTransaction(signedTx);
|
|
4019
|
+
if (auth?.jwt) {
|
|
4020
|
+
void api.recordTransaction(auth.jwt, {
|
|
4021
|
+
chain: req.chain,
|
|
4022
|
+
txHash: txResponse.hash,
|
|
4023
|
+
to: req.to,
|
|
4024
|
+
value: (req.value ?? 0n).toString(),
|
|
4025
|
+
kind: "send"
|
|
4026
|
+
}).catch(() => {
|
|
4027
|
+
});
|
|
4028
|
+
}
|
|
3736
4029
|
return txResponse.hash;
|
|
3737
4030
|
},
|
|
3738
4031
|
async signTypedData(req) {
|
|
@@ -3765,6 +4058,14 @@ async function createDecaneConnect(config) {
|
|
|
3765
4058
|
getAccessToken() {
|
|
3766
4059
|
return auth?.jwt ?? null;
|
|
3767
4060
|
},
|
|
4061
|
+
async recordTransaction(tx) {
|
|
4062
|
+
if (!auth?.jwt) throw new CallSequenceError("Not connected \u2014 call connect() first");
|
|
4063
|
+
return api.recordTransaction(auth.jwt, tx);
|
|
4064
|
+
},
|
|
4065
|
+
async getTransactionHistory(limit = 50) {
|
|
4066
|
+
if (!auth?.jwt) throw new CallSequenceError("Not connected \u2014 call connect() first");
|
|
4067
|
+
return api.getTransactions(auth.jwt, limit);
|
|
4068
|
+
},
|
|
3768
4069
|
getEthereumProvider(opts) {
|
|
3769
4070
|
if (!ethProvider) {
|
|
3770
4071
|
const firstEvm = config.chains.find((c) => c.startsWith("evm:"));
|
|
@@ -4376,6 +4677,7 @@ function WalletSelector({
|
|
|
4376
4677
|
mode = "wallets",
|
|
4377
4678
|
socialAuthMethods = ["google", "email"],
|
|
4378
4679
|
onGoogleSignIn,
|
|
4680
|
+
onKingsChatSignIn,
|
|
4379
4681
|
onEmailStart,
|
|
4380
4682
|
onEmailVerify,
|
|
4381
4683
|
customAuthLabel,
|
|
@@ -4453,6 +4755,18 @@ function WalletSelector({
|
|
|
4453
4755
|
setSocialLoad(false);
|
|
4454
4756
|
}
|
|
4455
4757
|
}
|
|
4758
|
+
async function handleKingsChatClick() {
|
|
4759
|
+
if (!onKingsChatSignIn) return;
|
|
4760
|
+
setSocialLoad(true);
|
|
4761
|
+
setSocialErr("");
|
|
4762
|
+
try {
|
|
4763
|
+
await onKingsChatSignIn();
|
|
4764
|
+
} catch (e) {
|
|
4765
|
+
setSocialErr(e.message);
|
|
4766
|
+
} finally {
|
|
4767
|
+
setSocialLoad(false);
|
|
4768
|
+
}
|
|
4769
|
+
}
|
|
4456
4770
|
async function handleCustomAuthClick() {
|
|
4457
4771
|
if (!onCustomAuthSignIn) return;
|
|
4458
4772
|
setSocialLoad(true);
|
|
@@ -4875,6 +5189,38 @@ function WalletSelector({
|
|
|
4875
5189
|
},
|
|
4876
5190
|
/* @__PURE__ */ React2.createElement("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none" }, /* @__PURE__ */ React2.createElement("path", { d: "M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844c-.209 1.125-.843 2.078-1.796 2.717v2.258h2.908c1.702-1.567 2.684-3.875 2.684-6.615z", fill: "#4285F4" }), /* @__PURE__ */ React2.createElement("path", { d: "M9 18c2.43 0 4.467-.806 5.956-2.18l-2.908-2.259c-.806.54-1.837.86-3.048.86-2.344 0-4.328-1.584-5.036-3.711H.957v2.332A8.997 8.997 0 0 0 9 18z", fill: "#34A853" }), /* @__PURE__ */ React2.createElement("path", { d: "M3.964 10.71A5.41 5.41 0 0 1 3.682 9c0-.593.102-1.17.282-1.71V4.958H.957A8.996 8.996 0 0 0 0 9c0 1.452.348 2.827.957 4.042l3.007-2.332z", fill: "#FBBC05" }), /* @__PURE__ */ React2.createElement("path", { d: "M9 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.463.891 11.426 0 9 0A8.997 8.997 0 0 0 .957 4.958L3.964 6.29C4.672 4.163 6.656 3.58 9 3.58z", fill: "#EA4335" })),
|
|
4877
5191
|
socialLoad ? "Redirecting\u2026" : "Continue with Google"
|
|
5192
|
+
), !passkeyFirst && socialAuthMethods.includes("kingschat") && onKingsChatSignIn && /* @__PURE__ */ React2.createElement(
|
|
5193
|
+
"button",
|
|
5194
|
+
{
|
|
5195
|
+
onClick: handleKingsChatClick,
|
|
5196
|
+
disabled: socialLoad,
|
|
5197
|
+
style: {
|
|
5198
|
+
display: "flex",
|
|
5199
|
+
alignItems: "center",
|
|
5200
|
+
justifyContent: "center",
|
|
5201
|
+
gap: 10,
|
|
5202
|
+
width: "100%",
|
|
5203
|
+
padding: "11px 16px",
|
|
5204
|
+
background: "var(--dc-surface)",
|
|
5205
|
+
border: "1px solid var(--dc-border)",
|
|
5206
|
+
borderRadius: 10,
|
|
5207
|
+
cursor: socialLoad ? "not-allowed" : "pointer",
|
|
5208
|
+
fontSize: 14,
|
|
5209
|
+
fontWeight: 600,
|
|
5210
|
+
color: "var(--dc-text)",
|
|
5211
|
+
fontFamily: "inherit",
|
|
5212
|
+
transition: "border-color 0.12s, background 0.12s",
|
|
5213
|
+
opacity: socialLoad ? 0.6 : 1
|
|
5214
|
+
},
|
|
5215
|
+
onMouseEnter: (e) => {
|
|
5216
|
+
if (!socialLoad) e.currentTarget.style.borderColor = "var(--dc-border-accent)";
|
|
5217
|
+
},
|
|
5218
|
+
onMouseLeave: (e) => {
|
|
5219
|
+
e.currentTarget.style.borderColor = "var(--dc-border)";
|
|
5220
|
+
}
|
|
5221
|
+
},
|
|
5222
|
+
/* @__PURE__ */ React2.createElement("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", "aria-hidden": "true" }, /* @__PURE__ */ React2.createElement("circle", { cx: "9", cy: "9", r: "9", fill: "var(--dc-accent)" }), /* @__PURE__ */ React2.createElement("path", { d: "M5.5 4.5h1.9v3.6l3.1-3.6h2.3l-3.3 3.8 3.5 5.2h-2.3l-2.5-3.8-.8.9v2.9H5.5V4.5z", fill: "var(--dc-accent-text)" })),
|
|
5223
|
+
socialLoad ? "Waiting for KingsChat\u2026" : "Continue with KingsChat"
|
|
4878
5224
|
), !passkeyFirst && socialAuthMethods.includes("google") && socialAuthMethods.includes("email") && /* @__PURE__ */ React2.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10 } }, /* @__PURE__ */ React2.createElement("div", { style: { flex: 1, height: 1, background: "var(--dc-border)" } }), /* @__PURE__ */ React2.createElement("span", { style: { fontSize: 11, color: "var(--dc-text-muted)", whiteSpace: "nowrap" } }, "or continue with email"), /* @__PURE__ */ React2.createElement("div", { style: { flex: 1, height: 1, background: "var(--dc-border)" } })), !passkeyFirst && socialAuthMethods.includes("email") && (emailStep === "input" ? /* @__PURE__ */ React2.createElement("form", { onSubmit: handleEmailStart, style: { display: "flex", flexDirection: "column", gap: 8 } }, /* @__PURE__ */ React2.createElement(
|
|
4879
5225
|
"input",
|
|
4880
5226
|
{
|
|
@@ -5121,6 +5467,7 @@ function AddressRow({
|
|
|
5121
5467
|
}
|
|
5122
5468
|
function SignInView({
|
|
5123
5469
|
onGoogleSignIn,
|
|
5470
|
+
onKingsChatSignIn,
|
|
5124
5471
|
onEmailStart,
|
|
5125
5472
|
onEmailVerify,
|
|
5126
5473
|
authMethods,
|
|
@@ -5132,6 +5479,7 @@ function SignInView({
|
|
|
5132
5479
|
onPasskeySignIn
|
|
5133
5480
|
}) {
|
|
5134
5481
|
const [googleLoading, setGoogleLoading] = useState2(false);
|
|
5482
|
+
const [kingsChatLoading, setKingsChatLoading] = useState2(false);
|
|
5135
5483
|
const [email, setEmail] = useState2("");
|
|
5136
5484
|
const [code, setCode] = useState2("");
|
|
5137
5485
|
const [emailSent, setEmailSent] = useState2(false);
|
|
@@ -5166,6 +5514,18 @@ function SignInView({
|
|
|
5166
5514
|
setGoogleLoading(false);
|
|
5167
5515
|
}
|
|
5168
5516
|
}
|
|
5517
|
+
async function handleKingsChat() {
|
|
5518
|
+
if (!onKingsChatSignIn) return;
|
|
5519
|
+
setError("");
|
|
5520
|
+
setKingsChatLoading(true);
|
|
5521
|
+
try {
|
|
5522
|
+
await onKingsChatSignIn();
|
|
5523
|
+
} catch (e) {
|
|
5524
|
+
setError(e.message);
|
|
5525
|
+
} finally {
|
|
5526
|
+
setKingsChatLoading(false);
|
|
5527
|
+
}
|
|
5528
|
+
}
|
|
5169
5529
|
async function handleEmailStart(e) {
|
|
5170
5530
|
e.preventDefault();
|
|
5171
5531
|
setError("");
|
|
@@ -5276,6 +5636,27 @@ function SignInView({
|
|
|
5276
5636
|
}
|
|
5277
5637
|
},
|
|
5278
5638
|
googleLoading ? "Redirecting\u2026" : "Continue with Google"
|
|
5639
|
+
), !passkeyFirst && authMethods.includes("kingschat") && onKingsChatSignIn && /* @__PURE__ */ React3.createElement(
|
|
5640
|
+
"button",
|
|
5641
|
+
{
|
|
5642
|
+
onClick: handleKingsChat,
|
|
5643
|
+
disabled: kingsChatLoading,
|
|
5644
|
+
style: {
|
|
5645
|
+
width: "100%",
|
|
5646
|
+
padding: "10px 0",
|
|
5647
|
+
marginBottom: 10,
|
|
5648
|
+
background: "var(--dc-surface)",
|
|
5649
|
+
color: "var(--dc-text)",
|
|
5650
|
+
border: "1px solid var(--dc-border)",
|
|
5651
|
+
borderRadius: 10,
|
|
5652
|
+
fontSize: 13,
|
|
5653
|
+
fontWeight: 700,
|
|
5654
|
+
cursor: kingsChatLoading ? "not-allowed" : "pointer",
|
|
5655
|
+
opacity: kingsChatLoading ? 0.7 : 1,
|
|
5656
|
+
fontFamily: "inherit"
|
|
5657
|
+
}
|
|
5658
|
+
},
|
|
5659
|
+
kingsChatLoading ? "Waiting for KingsChat\u2026" : "Continue with KingsChat"
|
|
5279
5660
|
), !passkeyFirst && authMethods.includes("email") && /* @__PURE__ */ React3.createElement(React3.Fragment, null, authMethods.includes("google") && /* @__PURE__ */ React3.createElement("div", { style: { textAlign: "center", color: "var(--dc-text-muted)", fontSize: 12, margin: "10px 0" } }, "\u2014 or \u2014"), !emailSent ? /* @__PURE__ */ React3.createElement("form", { onSubmit: handleEmailStart, style: { display: "flex", gap: 8 } }, /* @__PURE__ */ React3.createElement(
|
|
5280
5661
|
"input",
|
|
5281
5662
|
{
|
|
@@ -5449,6 +5830,7 @@ function SocialWalletModal({
|
|
|
5449
5830
|
onUnlock,
|
|
5450
5831
|
onDisconnect,
|
|
5451
5832
|
onGoogleSignIn,
|
|
5833
|
+
onKingsChatSignIn,
|
|
5452
5834
|
onEmailStart,
|
|
5453
5835
|
onEmailVerify,
|
|
5454
5836
|
authMethods = ["google", "email"],
|
|
@@ -5582,6 +5964,7 @@ function SocialWalletModal({
|
|
|
5582
5964
|
SignInView,
|
|
5583
5965
|
{
|
|
5584
5966
|
onGoogleSignIn,
|
|
5967
|
+
onKingsChatSignIn,
|
|
5585
5968
|
onEmailStart,
|
|
5586
5969
|
onEmailVerify,
|
|
5587
5970
|
authMethods,
|
|
@@ -5910,6 +6293,12 @@ function DecaneKit({ config, children }) {
|
|
|
5910
6293
|
if (!sdk) throw new Error("Social sign-in not configured");
|
|
5911
6294
|
await sdk.connectWithGoogle();
|
|
5912
6295
|
}, []);
|
|
6296
|
+
const connectWithKingsChat = useCallback3(async () => {
|
|
6297
|
+
const sdk = socialRef.current;
|
|
6298
|
+
if (!sdk) throw new Error("Social sign-in not configured");
|
|
6299
|
+
const result = await sdk.connectWithKingsChat();
|
|
6300
|
+
applyResult(result);
|
|
6301
|
+
}, [applyResult]);
|
|
5913
6302
|
const connectWithEmail = useCallback3(async (email) => {
|
|
5914
6303
|
const sdk = socialRef.current;
|
|
5915
6304
|
if (!sdk) throw new Error("Social sign-in not configured");
|
|
@@ -5989,6 +6378,16 @@ function DecaneKit({ config, children }) {
|
|
|
5989
6378
|
if (!sdk) throw new Error("Social wallet not connected");
|
|
5990
6379
|
return sdk.signTronTransaction(rawDataHex);
|
|
5991
6380
|
}, []);
|
|
6381
|
+
const socialRecordTransaction = useCallback3(async (tx) => {
|
|
6382
|
+
const sdk = socialRef.current;
|
|
6383
|
+
if (!sdk) throw new Error("Social wallet not connected");
|
|
6384
|
+
return sdk.recordTransaction(tx);
|
|
6385
|
+
}, []);
|
|
6386
|
+
const socialGetTransactionHistory = useCallback3(async (limit) => {
|
|
6387
|
+
const sdk = socialRef.current;
|
|
6388
|
+
if (!sdk) throw new Error("Social wallet not connected");
|
|
6389
|
+
return sdk.getTransactionHistory(limit);
|
|
6390
|
+
}, []);
|
|
5992
6391
|
const socialSignTypedData = useCallback3(async (req) => {
|
|
5993
6392
|
const sdk = socialRef.current;
|
|
5994
6393
|
if (!sdk) throw new Error("Social wallet not connected");
|
|
@@ -6066,6 +6465,7 @@ function DecaneKit({ config, children }) {
|
|
|
6066
6465
|
socialAuth,
|
|
6067
6466
|
signInWithPasskey,
|
|
6068
6467
|
connectWithGoogle,
|
|
6468
|
+
connectWithKingsChat,
|
|
6069
6469
|
connectWithEmail,
|
|
6070
6470
|
verifyEmailCode: verifyEmailCode2,
|
|
6071
6471
|
connectWithToken,
|
|
@@ -6080,6 +6480,8 @@ function DecaneKit({ config, children }) {
|
|
|
6080
6480
|
socialGetEthereumProvider,
|
|
6081
6481
|
socialSignAuthorization,
|
|
6082
6482
|
socialSignTronTransaction,
|
|
6483
|
+
socialRecordTransaction,
|
|
6484
|
+
socialGetTransactionHistory,
|
|
6083
6485
|
socialGetAccessToken,
|
|
6084
6486
|
socialSignSolanaTransaction,
|
|
6085
6487
|
socialSignTypedData,
|
|
@@ -6106,6 +6508,7 @@ function DecaneKit({ config, children }) {
|
|
|
6106
6508
|
mode,
|
|
6107
6509
|
socialAuthMethods: config?.social?.authMethods ?? ["google", "email"],
|
|
6108
6510
|
onGoogleSignIn: connectWithGoogle,
|
|
6511
|
+
onKingsChatSignIn: connectWithKingsChat,
|
|
6109
6512
|
onEmailStart: connectWithEmail,
|
|
6110
6513
|
onEmailVerify: verifyEmailCode2,
|
|
6111
6514
|
canUsePasskey: socialAuth.canUsePasskey,
|
|
@@ -6127,6 +6530,7 @@ function DecaneKit({ config, children }) {
|
|
|
6127
6530
|
customAuthLabel: customAuthConfig?.label,
|
|
6128
6531
|
onCustomAuthSignIn: customAuthConfig ? connectWithCustomAuth : void 0,
|
|
6129
6532
|
onGoogleSignIn: connectWithGoogle,
|
|
6533
|
+
onKingsChatSignIn: connectWithKingsChat,
|
|
6130
6534
|
onEmailStart: connectWithEmail,
|
|
6131
6535
|
onEmailVerify: verifyEmailCode2,
|
|
6132
6536
|
authMethods: config?.social?.authMethods ?? ["google", "email"],
|
|
@@ -6301,6 +6705,8 @@ function useSocialWallet() {
|
|
|
6301
6705
|
socialSendTransaction,
|
|
6302
6706
|
socialSignSolanaTransaction,
|
|
6303
6707
|
socialSignTronTransaction,
|
|
6708
|
+
socialRecordTransaction,
|
|
6709
|
+
socialGetTransactionHistory,
|
|
6304
6710
|
socialSignTypedData,
|
|
6305
6711
|
socialGetBalance,
|
|
6306
6712
|
socialGetBalances,
|
|
@@ -6347,6 +6753,14 @@ function useSocialWallet() {
|
|
|
6347
6753
|
(rawDataHex) => run(() => socialSignTronTransaction(rawDataHex)),
|
|
6348
6754
|
[socialSignTronTransaction]
|
|
6349
6755
|
);
|
|
6756
|
+
const recordTransaction = useCallback4(
|
|
6757
|
+
(tx) => run(() => socialRecordTransaction(tx)),
|
|
6758
|
+
[socialRecordTransaction]
|
|
6759
|
+
);
|
|
6760
|
+
const getTransactionHistory = useCallback4(
|
|
6761
|
+
(limit) => socialGetTransactionHistory(limit),
|
|
6762
|
+
[socialGetTransactionHistory]
|
|
6763
|
+
);
|
|
6350
6764
|
const signTypedData = useCallback4(
|
|
6351
6765
|
(req) => run(() => socialSignTypedData(req)),
|
|
6352
6766
|
[socialSignTypedData]
|
|
@@ -6429,6 +6843,8 @@ function useSocialWallet() {
|
|
|
6429
6843
|
sendTransaction,
|
|
6430
6844
|
signSolanaTransaction: signSolanaTransaction2,
|
|
6431
6845
|
signTronTransaction,
|
|
6846
|
+
recordTransaction,
|
|
6847
|
+
getTransactionHistory,
|
|
6432
6848
|
signTypedData,
|
|
6433
6849
|
// Balances (native token; read-only, works while locked)
|
|
6434
6850
|
getBalance,
|
|
@@ -6758,6 +7174,7 @@ export {
|
|
|
6758
7174
|
SessionExpiredError,
|
|
6759
7175
|
ShareCorruptedError,
|
|
6760
7176
|
SocialWalletModal,
|
|
7177
|
+
StaleDeviceShareError,
|
|
6761
7178
|
UnlockRequiredError,
|
|
6762
7179
|
UserCancelledError,
|
|
6763
7180
|
WalletSelector,
|
|
@@ -6766,6 +7183,7 @@ export {
|
|
|
6766
7183
|
createDecane,
|
|
6767
7184
|
createDecaneConnect,
|
|
6768
7185
|
createEthereumProvider,
|
|
7186
|
+
loginWithKingsChatPopup,
|
|
6769
7187
|
startEmailAuth,
|
|
6770
7188
|
useChainSwitch,
|
|
6771
7189
|
useDecane,
|