decane-connect-kit 1.0.2 → 2.0.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.js CHANGED
@@ -1,3 +1,145 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+
11
+ // src/social/errors.ts
12
+ var errors_exports = {};
13
+ __export(errors_exports, {
14
+ AttestationError: () => AttestationError,
15
+ CallSequenceError: () => CallSequenceError,
16
+ DecaneConnectError: () => DecaneConnectError,
17
+ ExportNotAuthorizedError: () => ExportNotAuthorizedError,
18
+ InvalidApiKeyError: () => InvalidApiKeyError,
19
+ InvalidPinError: () => InvalidPinError,
20
+ InvalidRecoveryFileError: () => InvalidRecoveryFileError,
21
+ NetworkError: () => NetworkError,
22
+ NewDeviceError: () => NewDeviceError,
23
+ OriginNotAllowedError: () => OriginNotAllowedError,
24
+ PromptPinNotConfiguredError: () => PromptPinNotConfiguredError,
25
+ RecoveryFailedError: () => RecoveryFailedError,
26
+ RecoveryWalletMismatchError: () => RecoveryWalletMismatchError,
27
+ SessionExpiredError: () => SessionExpiredError,
28
+ ShareCorruptedError: () => ShareCorruptedError,
29
+ UnlockRequiredError: () => UnlockRequiredError,
30
+ UserCancelledError: () => UserCancelledError,
31
+ WebAuthnNotSupportedError: () => WebAuthnNotSupportedError,
32
+ WrongPasswordError: () => WrongPasswordError
33
+ });
34
+ var DecaneConnectError, InvalidApiKeyError, OriginNotAllowedError, SessionExpiredError, UnlockRequiredError, UserCancelledError, WebAuthnNotSupportedError, InvalidPinError, CallSequenceError, NetworkError, ShareCorruptedError, NewDeviceError, PromptPinNotConfiguredError, AttestationError, RecoveryFailedError, InvalidRecoveryFileError, WrongPasswordError, RecoveryWalletMismatchError, ExportNotAuthorizedError;
35
+ var init_errors = __esm({
36
+ "src/social/errors.ts"() {
37
+ "use strict";
38
+ DecaneConnectError = class extends Error {
39
+ constructor(code, message, retryable = false) {
40
+ super(message);
41
+ this.name = "DecaneConnectError";
42
+ this.code = code;
43
+ this.retryable = retryable;
44
+ }
45
+ };
46
+ InvalidApiKeyError = class extends DecaneConnectError {
47
+ constructor() {
48
+ super("INVALID_API_KEY", "API key is invalid or revoked");
49
+ }
50
+ };
51
+ OriginNotAllowedError = class extends DecaneConnectError {
52
+ constructor(origin) {
53
+ super("ORIGIN_NOT_ALLOWED", `Origin ${origin} not in API key allowlist`);
54
+ }
55
+ };
56
+ SessionExpiredError = class extends DecaneConnectError {
57
+ constructor() {
58
+ super("SESSION_EXPIRED", "Session has expired \u2014 call unlock()", true);
59
+ }
60
+ };
61
+ UnlockRequiredError = class extends DecaneConnectError {
62
+ constructor() {
63
+ super("UNLOCK_REQUIRED", "Wallet must be unlocked before signing", true);
64
+ }
65
+ };
66
+ UserCancelledError = class extends DecaneConnectError {
67
+ constructor() {
68
+ super("USER_CANCELLED", "User cancelled the operation", true);
69
+ }
70
+ };
71
+ WebAuthnNotSupportedError = class extends DecaneConnectError {
72
+ constructor() {
73
+ super("WEBAUTHN_UNSUPPORTED", "This browser does not support WebAuthn");
74
+ }
75
+ };
76
+ InvalidPinError = class extends DecaneConnectError {
77
+ constructor() {
78
+ super("INVALID_PIN", "Incorrect PIN", true);
79
+ }
80
+ };
81
+ CallSequenceError = class extends DecaneConnectError {
82
+ constructor(msg) {
83
+ super("CALL_SEQUENCE", msg);
84
+ }
85
+ };
86
+ NetworkError = class extends DecaneConnectError {
87
+ constructor() {
88
+ super("NETWORK", "Network request failed", true);
89
+ }
90
+ };
91
+ ShareCorruptedError = class extends DecaneConnectError {
92
+ constructor() {
93
+ super("SHARE_CORRUPTED", "Stored device share is corrupted");
94
+ }
95
+ };
96
+ NewDeviceError = class extends DecaneConnectError {
97
+ constructor() {
98
+ super("NEW_DEVICE", "No device share found. Re-authenticate to set up this device.");
99
+ }
100
+ };
101
+ PromptPinNotConfiguredError = class extends DecaneConnectError {
102
+ constructor() {
103
+ super("PROMPT_PIN_NOT_CONFIGURED", "Passkey unavailable and no promptPin callback was provided in config");
104
+ }
105
+ };
106
+ AttestationError = class extends DecaneConnectError {
107
+ constructor(msg) {
108
+ super("ATTESTATION_FAILED", msg);
109
+ }
110
+ };
111
+ RecoveryFailedError = class extends DecaneConnectError {
112
+ constructor(reason) {
113
+ super("RECOVERY_FAILED", `Recovery failed: ${reason}`, true);
114
+ }
115
+ };
116
+ InvalidRecoveryFileError = class extends DecaneConnectError {
117
+ constructor() {
118
+ super("INVALID_RECOVERY_FILE", "This does not look like a valid recovery file", false);
119
+ }
120
+ };
121
+ WrongPasswordError = class extends DecaneConnectError {
122
+ constructor() {
123
+ super("WRONG_PASSWORD", "Incorrect password for this recovery file", true);
124
+ }
125
+ };
126
+ RecoveryWalletMismatchError = class extends DecaneConnectError {
127
+ constructor() {
128
+ super(
129
+ "RECOVERY_WALLET_MISMATCH",
130
+ "This recovery file is for a different wallet than the account you are signed into",
131
+ false
132
+ );
133
+ }
134
+ };
135
+ ExportNotAuthorizedError = class extends DecaneConnectError {
136
+ constructor() {
137
+ super("EXPORT_NOT_AUTHORIZED", "Export requires a fresh security check", true);
138
+ }
139
+ };
140
+ }
141
+ });
142
+
1
143
  // src/core/emitter.ts
2
144
  function createEmitter() {
3
145
  const listeners = /* @__PURE__ */ new Map();
@@ -1670,6 +1812,7 @@ function startSolanaDiscovery(registry) {
1670
1812
  var TRONLINK_RDNS = "link.tron.tronlink";
1671
1813
  var TRONLINK_ICON = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiI+PGNpcmNsZSBjeD0iMTYiIGN5PSIxNiIgcj0iMTYiIGZpbGw9IiNGRjAwMTMiLz48cG9seWdvbiBwb2ludHM9IjEyLDkgMjMsMTYgMTIsMjMiIGZpbGw9IndoaXRlIi8+PC9zdmc+";
1672
1814
  function startTronDiscovery(registry) {
1815
+ if (typeof window === "undefined") return;
1673
1816
  let registered = false;
1674
1817
  function tryRegister() {
1675
1818
  if (registered || !window.tronLink) return;
@@ -1694,6 +1837,7 @@ function startTronDiscovery(registry) {
1694
1837
  // src/bitcoin/discovery.ts
1695
1838
  var BTC_ICON = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiI+PGNpcmNsZSBjeD0iMTYiIGN5PSIxNiIgcj0iMTYiIGZpbGw9IiNGNzkzMUEiLz48dGV4dCB4PSIxNi41IiB5PSIyMiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxNiIgZm9udC13ZWlnaHQ9IjcwMCIgZmlsbD0id2hpdGUiIGZvbnQtZmFtaWx5PSJBcmlhbCxzeXN0ZW0tdWkiPuKmojwvdGV4dD48L3N2Zz4=";
1696
1839
  function startBitcoinDiscovery(registry) {
1840
+ if (typeof window === "undefined") return;
1697
1841
  setTimeout(() => {
1698
1842
  const candidates = [
1699
1843
  { name: "Unisat", rdns: "io.unisat.wallet", provider: window.unisat },
@@ -1731,160 +1875,1560 @@ function createDecane(config = {}) {
1731
1875
  return createProvider({ config, registry, session, chainStore, router, emitter });
1732
1876
  }
1733
1877
 
1734
- // src/ui/WalletSelector.tsx
1735
- import React2, { useEffect, useCallback, useState } from "react";
1736
- import { createPortal } from "react-dom";
1878
+ // src/social/client.ts
1879
+ init_errors();
1880
+ import { Wallet } from "ethers";
1881
+ import { Transaction as Transaction2, VersionedTransaction, Keypair } from "@solana/web3.js";
1737
1882
 
1738
- // src/ui/styles.ts
1739
- var tokens = {
1740
- // ── Gold palette ──────────────────────────────────────────────────────────
1741
- gold: {
1742
- 50: "#FFFDF0",
1743
- 100: "#FFF8D6",
1744
- 200: "#FFEF99",
1745
- 300: "#FFE04D",
1746
- 400: "#F5C800",
1747
- // primary gold — buttons, accents
1748
- 500: "#D4A800",
1749
- // darker gold — hover states
1750
- 600: "#A37F00",
1751
- // deep gold — borders, active
1752
- 700: "#7A5E00",
1753
- 800: "#4D3B00",
1754
- 900: "#261D00"
1755
- },
1756
- // ── Black / dark palette ──────────────────────────────────────────────────
1757
- black: {
1758
- 0: "#FFFFFF",
1759
- 50: "#F9F9F7",
1760
- 100: "#F0EFEB",
1761
- 200: "#DDDCDA",
1762
- 300: "#BDBBB8",
1763
- 400: "#8A8884",
1764
- 500: "#5C5A57",
1765
- 600: "#3D3C39",
1766
- 700: "#2A2927",
1767
- 800: "#1A1917",
1768
- 900: "#0D0D0B",
1769
- 950: "#070706"
1770
- },
1771
- // ── Semantic tokens — light mode ──────────────────────────────────────────
1772
- light: {
1773
- bg: "#FFFFFF",
1774
- bgOverlay: "rgba(0, 0, 0, 0.55)",
1775
- surface: "#F9F9F7",
1776
- surfaceHover: "#F0EFEB",
1777
- border: "#DDDCDA",
1778
- borderAccent: "#F5C800",
1779
- text: "#0D0D0B",
1780
- textSecondary: "#5C5A57",
1781
- textMuted: "#8A8884",
1782
- accent: "#F5C800",
1783
- accentHover: "#D4A800",
1784
- accentText: "#0D0D0B",
1785
- badgeEvm: "#1A1917",
1786
- badgeEvmText: "#F5C800",
1787
- badgeSolana: "#F5C800",
1788
- badgeSolanaText: "#0D0D0B",
1789
- danger: "#CC3300"
1790
- },
1791
- // ── Semantic tokens — dark mode ───────────────────────────────────────────
1792
- dark: {
1793
- bg: "#0D0D0B",
1794
- bgOverlay: "rgba(0, 0, 0, 0.75)",
1795
- surface: "#1A1917",
1796
- surfaceHover: "#2A2927",
1797
- border: "#3D3C39",
1798
- borderAccent: "#F5C800",
1799
- text: "#F9F9F7",
1800
- textSecondary: "#BDBBB8",
1801
- textMuted: "#8A8884",
1802
- accent: "#F5C800",
1803
- accentHover: "#FFE04D",
1804
- accentText: "#0D0D0B",
1805
- badgeEvm: "#F5C800",
1806
- badgeEvmText: "#0D0D0B",
1807
- badgeSolana: "#1A1917",
1808
- badgeSolanaText: "#F5C800",
1809
- danger: "#FF5533"
1883
+ // src/social/events.ts
1884
+ var EventEmitter = class {
1885
+ constructor() {
1886
+ this._listeners = /* @__PURE__ */ new Map();
1887
+ }
1888
+ emit(event, ...args) {
1889
+ this._listeners.get(event)?.forEach((h) => h(...args));
1890
+ }
1891
+ on(event, handler) {
1892
+ if (!this._listeners.has(event)) this._listeners.set(event, /* @__PURE__ */ new Set());
1893
+ this._listeners.get(event).add(handler);
1894
+ }
1895
+ off(event, handler) {
1896
+ this._listeners.get(event)?.delete(handler);
1810
1897
  }
1811
1898
  };
1812
- var FONT_SANS = "'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif";
1813
- var FONT_MONO = "'JetBrains Mono', 'Fira Code', ui-monospace, monospace";
1814
- var CSS_VARS = {
1815
- light: `
1816
- --dc-font-sans: ${FONT_SANS};
1817
- --dc-font-mono: ${FONT_MONO};
1818
- --dc-bg: ${tokens.light.bg};
1819
- --dc-bg-overlay: ${tokens.light.bgOverlay};
1820
- --dc-surface: ${tokens.light.surface};
1821
- --dc-surface-hover: ${tokens.light.surfaceHover};
1822
- --dc-border: ${tokens.light.border};
1823
- --dc-border-accent: ${tokens.light.borderAccent};
1824
- --dc-text: ${tokens.light.text};
1825
- --dc-text-secondary: ${tokens.light.textSecondary};
1826
- --dc-text-muted: ${tokens.light.textMuted};
1827
- --dc-accent: ${tokens.light.accent};
1828
- --dc-accent-hover: ${tokens.light.accentHover};
1829
- --dc-accent-text: ${tokens.light.accentText};
1830
- --dc-badge-evm: ${tokens.light.badgeEvm};
1831
- --dc-badge-evm-text: ${tokens.light.badgeEvmText};
1832
- --dc-badge-sol: ${tokens.light.badgeSolana};
1833
- --dc-badge-sol-text: ${tokens.light.badgeSolanaText};
1834
- --dc-danger: ${tokens.light.danger};
1835
- `,
1836
- dark: `
1837
- --dc-font-sans: ${FONT_SANS};
1838
- --dc-font-mono: ${FONT_MONO};
1839
- --dc-bg: ${tokens.dark.bg};
1840
- --dc-bg-overlay: ${tokens.dark.bgOverlay};
1841
- --dc-surface: ${tokens.dark.surface};
1842
- --dc-surface-hover: ${tokens.dark.surfaceHover};
1843
- --dc-border: ${tokens.dark.border};
1844
- --dc-border-accent: ${tokens.dark.borderAccent};
1845
- --dc-text: ${tokens.dark.text};
1846
- --dc-text-secondary: ${tokens.dark.textSecondary};
1847
- --dc-text-muted: ${tokens.dark.textMuted};
1848
- --dc-accent: ${tokens.dark.accent};
1849
- --dc-accent-hover: ${tokens.dark.accentHover};
1850
- --dc-accent-text: ${tokens.dark.accentText};
1851
- --dc-badge-evm: ${tokens.dark.badgeEvm};
1852
- --dc-badge-evm-text: ${tokens.dark.badgeEvmText};
1853
- --dc-badge-sol: ${tokens.dark.badgeSolana};
1854
- --dc-badge-sol-text: ${tokens.dark.badgeSolanaText};
1855
- --dc-danger: ${tokens.dark.danger};
1856
- `
1899
+
1900
+ // src/social/api.ts
1901
+ init_errors();
1902
+ var ERROR_MAP = {
1903
+ INVALID_API_KEY: InvalidApiKeyError,
1904
+ ORIGIN_NOT_ALLOWED: OriginNotAllowedError
1905
+ };
1906
+ var ApiClient = class {
1907
+ constructor(baseUrl, apiKey) {
1908
+ this.baseUrl = baseUrl;
1909
+ this.apiKey = apiKey;
1910
+ }
1911
+ async _request(path, init) {
1912
+ let res;
1913
+ try {
1914
+ res = await fetch(`${this.baseUrl}${path}`, {
1915
+ ...init,
1916
+ headers: {
1917
+ "Content-Type": "application/json",
1918
+ "X-API-Key": this.apiKey,
1919
+ ...init?.headers
1920
+ }
1921
+ });
1922
+ } catch {
1923
+ throw new NetworkError();
1924
+ }
1925
+ if (res.status === 404 && init?.allow404) return null;
1926
+ if (!res.ok) {
1927
+ const body = await res.json().catch(() => ({}));
1928
+ const code = body?.error?.code ?? "UNKNOWN";
1929
+ const msg = body?.error?.message ?? `Request failed: ${res.status}`;
1930
+ const Ctor = ERROR_MAP[code];
1931
+ if (Ctor) throw new Ctor(msg);
1932
+ throw new Error(msg);
1933
+ }
1934
+ return res.json();
1935
+ }
1936
+ async getGoogleInitUrl() {
1937
+ return this._request("/auth/google/init", { method: "GET" });
1938
+ }
1939
+ async authEmailStart(email) {
1940
+ await this._request("/auth/email/start", {
1941
+ method: "POST",
1942
+ body: JSON.stringify({ email })
1943
+ });
1944
+ }
1945
+ async authEmailVerify(email, code) {
1946
+ return this._request("/auth/email/verify", {
1947
+ method: "POST",
1948
+ body: JSON.stringify({ email, code })
1949
+ });
1950
+ }
1951
+ // GET /share (the server share itself) was removed as part of the TEE
1952
+ // migration — the browser must never reconstruct the private key anymore.
1953
+ // Only public addresses are fetched from the backend now.
1954
+ async getAddresses(jwt) {
1955
+ return this._request("/share/addresses", {
1956
+ headers: { Authorization: `Bearer ${jwt}` }
1957
+ });
1958
+ }
1959
+ async putShare(jwt, share) {
1960
+ await this._request("/share", {
1961
+ method: "POST",
1962
+ headers: { Authorization: `Bearer ${jwt}` },
1963
+ body: JSON.stringify({
1964
+ ciphertext: bytesToB64(share.ciphertext),
1965
+ iv: bytesToB64(share.iv),
1966
+ wrappedShareMeta: btoa(JSON.stringify(share.wrappedShareMeta)),
1967
+ evmAddress: share.evmAddress,
1968
+ solanaAddress: share.solanaAddress
1969
+ })
1970
+ });
1971
+ }
1972
+ // ── WebAuthn registration (additive to the PRF-only passkey flow) ─────────
1973
+ async getWebAuthnRegisterOptions(jwt, rpId, rpName) {
1974
+ return this._request(`/webauthn/register-options?rpId=${encodeURIComponent(rpId)}&rpName=${encodeURIComponent(rpName)}`, {
1975
+ headers: { Authorization: `Bearer ${jwt}` }
1976
+ });
1977
+ }
1978
+ async postWebAuthnRegister(jwt, response, origin) {
1979
+ await this._request("/webauthn/register", {
1980
+ method: "POST",
1981
+ headers: { Authorization: `Bearer ${jwt}` },
1982
+ body: JSON.stringify({ response, origin })
1983
+ });
1984
+ }
1985
+ async revoke(jwt) {
1986
+ await this._request("/auth/revoke", {
1987
+ method: "POST",
1988
+ headers: { Authorization: `Bearer ${jwt}` }
1989
+ });
1990
+ }
1991
+ // ── Recovery (passkey-sync backup + recovery-share-saved flag) ────────────
1992
+ async hasRecoveryBackup(jwt) {
1993
+ const data = await this._request("/recovery/backup/exists", {
1994
+ headers: { Authorization: `Bearer ${jwt}` }
1995
+ });
1996
+ return data.exists;
1997
+ }
1998
+ async getRecoveryBackup(jwt) {
1999
+ const data = await this._request("/recovery/backup", {
2000
+ headers: { Authorization: `Bearer ${jwt}` },
2001
+ allow404: true
2002
+ });
2003
+ if (!data) return null;
2004
+ return {
2005
+ wrappedDeviceShare: b64ToBytes(data.wrappedDeviceShare),
2006
+ iv: b64ToBytes(data.iv),
2007
+ unlockMethod: data.unlockMethod,
2008
+ credentialId: b64ToBytes(data.credentialId),
2009
+ prfSalt: b64ToBytes(data.prfSalt),
2010
+ formatVersion: data.formatVersion
2011
+ };
2012
+ }
2013
+ async putRecoveryBackup(jwt, backup) {
2014
+ await this._request("/recovery/backup", {
2015
+ method: "PUT",
2016
+ headers: { Authorization: `Bearer ${jwt}` },
2017
+ body: JSON.stringify({
2018
+ wrappedDeviceShare: bytesToB64(backup.wrappedDeviceShare),
2019
+ iv: bytesToB64(backup.iv),
2020
+ unlockMethod: backup.unlockMethod,
2021
+ credentialId: bytesToB64(backup.credentialId),
2022
+ prfSalt: bytesToB64(backup.prfSalt)
2023
+ })
2024
+ });
2025
+ }
2026
+ async deleteRecoveryBackup(jwt) {
2027
+ await this._request("/recovery/backup", {
2028
+ method: "DELETE",
2029
+ headers: { Authorization: `Bearer ${jwt}` }
2030
+ });
2031
+ }
2032
+ async markRecoveryShareSaved(jwt) {
2033
+ await this._request("/recovery/share-saved", {
2034
+ method: "POST",
2035
+ headers: { Authorization: `Bearer ${jwt}` }
2036
+ });
2037
+ }
2038
+ async getRecoveryStatus(jwt) {
2039
+ return this._request("/recovery/status", {
2040
+ headers: { Authorization: `Bearer ${jwt}` }
2041
+ });
2042
+ }
2043
+ async dismissRecoveryNag(jwt) {
2044
+ await this._request("/recovery/nag-dismissed", {
2045
+ method: "POST",
2046
+ headers: { Authorization: `Bearer ${jwt}` }
2047
+ });
2048
+ }
1857
2049
  };
2050
+ function bytesToB64(bytes) {
2051
+ return btoa(String.fromCharCode(...bytes));
2052
+ }
2053
+ function b64ToBytes(b644) {
2054
+ return Uint8Array.from(atob(b644), (c) => c.charCodeAt(0));
2055
+ }
1858
2056
 
1859
- // src/ui/icons.tsx
1860
- import React from "react";
1861
- function WalletIcon({ src, name, size = 36 }) {
1862
- if (src) {
1863
- return /* @__PURE__ */ React.createElement(
1864
- "img",
1865
- {
1866
- src,
1867
- alt: name,
1868
- width: size,
1869
- height: size,
1870
- style: { borderRadius: size * 0.25, objectFit: "cover", flexShrink: 0 }
2057
+ // src/social/storage.ts
2058
+ import { openDB } from "idb";
2059
+ var DB_NAME = "decane-connect-kit";
2060
+ var STORE = "shares";
2061
+ var Storage = class {
2062
+ constructor() {
2063
+ this._db = openDB(DB_NAME, 1, {
2064
+ upgrade(db) {
2065
+ db.createObjectStore(STORE, { keyPath: "userId" });
1871
2066
  }
2067
+ });
2068
+ }
2069
+ async getShare(userId) {
2070
+ return (await this._db).get(STORE, userId) ?? null;
2071
+ }
2072
+ async putShare(share) {
2073
+ await (await this._db).put(STORE, share);
2074
+ }
2075
+ async deleteShare(userId) {
2076
+ await (await this._db).delete(STORE, userId);
2077
+ }
2078
+ };
2079
+
2080
+ // src/social/teeClient.ts
2081
+ import { startAuthentication } from "@simplewebauthn/browser";
2082
+
2083
+ // src/social/attestation.ts
2084
+ init_errors();
2085
+ import { getCollateralAndVerify } from "@phala/dcap-qvl";
2086
+
2087
+ // ../../infra/tee-measurement.json
2088
+ var tee_measurement_default = {
2089
+ signerVersion: "0.1.3",
2090
+ acceptedMrtd: [
2091
+ '["68102e7b524af310f7b7d426ce75481e36c40f5d513a9009c046e9d37e31551f0134d954b496a3357fd61d03f07ffe96","07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878c","a2a58c9a959a4fa44bd6da0c97a2270c051faf12084cfe91ae900e4fdff6cdd4f69a82005e04ee920f231497894d677f","a76a81147b7f733cb400914c8791ec149bb3a31f3727b1c1e12ec22e9c1a183b6e617e78636dca37075f5a695afd46c2"]'
2092
+ ],
2093
+ primaryMrtd: '["68102e7b524af310f7b7d426ce75481e36c40f5d513a9009c046e9d37e31551f0134d954b496a3357fd61d03f07ffe96","07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878c","a2a58c9a959a4fa44bd6da0c97a2270c051faf12084cfe91ae900e4fdff6cdd4f69a82005e04ee920f231497894d677f","a76a81147b7f733cb400914c8791ec149bb3a31f3727b1c1e12ec22e9c1a183b6e617e78636dca37075f5a695afd46c2"]',
2094
+ capturedAt: "2026-07-20T10:08:58.717Z",
2095
+ tcbStatus: "UpToDate",
2096
+ _note: "Captured from a live, signature-verified TDX quote (getCollateralAndVerify -> UpToDate) against decane-tee-signer v0.1.3 running on Phala Cloud CVM 786e714d-5042-4bf1-8060-0bc358f6a72f. measurement is the JSON.stringify(replayRtmrs()) output, compared as an opaque string (not parsed per-RTMR), matching the actual comparison logic in social/attestation.ts."
2097
+ };
2098
+
2099
+ // src/social/attestation.ts
2100
+ function hexToBytes(hex) {
2101
+ const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
2102
+ const bytes = new Uint8Array(Math.floor(clean.length / 2));
2103
+ for (let i = 0; i < bytes.length; i++) {
2104
+ bytes[i] = parseInt(clean.substring(i * 2, i * 2 + 2), 16);
2105
+ }
2106
+ return bytes;
2107
+ }
2108
+ function bytesToHex(bytes) {
2109
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
2110
+ }
2111
+ async function verifyTeeAttestation(teeUrl, nonce) {
2112
+ const res = await fetch(`${teeUrl}/attestation`, {
2113
+ method: "POST",
2114
+ headers: { "Content-Type": "application/json" },
2115
+ body: JSON.stringify({ nonce })
2116
+ });
2117
+ if (!res.ok) throw new AttestationError("quote fetch failed");
2118
+ const { quote, measurement } = await res.json();
2119
+ let verified;
2120
+ try {
2121
+ verified = await getCollateralAndVerify(hexToBytes(quote));
2122
+ } catch {
2123
+ throw new AttestationError("quote signature invalid");
2124
+ }
2125
+ if (verified.status === "Revoked") {
2126
+ throw new AttestationError("TEE platform TCB status is Revoked");
2127
+ }
2128
+ const td = verified.report.asTd10() ?? verified.report.asTd15()?.base;
2129
+ if (!td) throw new AttestationError("quote is not a TDX report");
2130
+ const reportDataHex = bytesToHex(td.reportData);
2131
+ if (!reportDataHex.toLowerCase().startsWith(nonce.toLowerCase())) {
2132
+ throw new AttestationError("quote nonce mismatch");
2133
+ }
2134
+ const accepted = tee_measurement_default.acceptedMrtd.map((m) => m.toLowerCase());
2135
+ if (!accepted.includes(String(measurement).toLowerCase())) {
2136
+ throw new AttestationError(
2137
+ `TEE measurement not accepted. Got ${measurement}, expected one of ${accepted.join(", ") || "(none configured yet)"}`
1872
2138
  );
1873
2139
  }
1874
- const initial = name.charAt(0).toUpperCase();
1875
- return /* @__PURE__ */ React.createElement(
1876
- "div",
1877
- {
1878
- style: {
1879
- width: size,
1880
- height: size,
1881
- borderRadius: size * 0.25,
1882
- background: "linear-gradient(135deg, var(--dc-surface-hover), var(--dc-surface-active, var(--dc-surface-hover)))",
1883
- border: "1px solid var(--dc-border)",
1884
- display: "flex",
1885
- alignItems: "center",
1886
- justifyContent: "center",
1887
- fontSize: size * 0.38,
2140
+ }
2141
+
2142
+ // src/social/teeClient.ts
2143
+ init_errors();
2144
+ function bytesToBase64(b) {
2145
+ let s = "";
2146
+ for (const byte of b) s += String.fromCharCode(byte);
2147
+ return btoa(s);
2148
+ }
2149
+ function base64ToBytes(s) {
2150
+ return Uint8Array.from(atob(s), (c) => c.charCodeAt(0));
2151
+ }
2152
+ var TeeClient = class {
2153
+ constructor(teeUrl) {
2154
+ this.teeUrl = teeUrl;
2155
+ this.handle = null;
2156
+ this.attested = false;
2157
+ }
2158
+ async ensureAttested() {
2159
+ if (this.attested) return;
2160
+ const nonce = crypto.getRandomValues(new Uint8Array(16));
2161
+ const nonceHex = Array.from(nonce).map((b) => b.toString(16).padStart(2, "0")).join("");
2162
+ await verifyTeeAttestation(this.teeUrl, nonceHex);
2163
+ this.attested = true;
2164
+ }
2165
+ async openSession(deviceShare, jwt) {
2166
+ await this.ensureAttested();
2167
+ const res = await fetch(`${this.teeUrl}/signer/session`, {
2168
+ method: "POST",
2169
+ headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
2170
+ body: JSON.stringify({ deviceShare: bytesToBase64(deviceShare) })
2171
+ });
2172
+ if (!res.ok) throw new Error("session open failed");
2173
+ this.handle = (await res.json()).handle;
2174
+ }
2175
+ /**
2176
+ * Flow B: exchange a recovery share (from a recovery-share file) for a
2177
+ * fresh device share. The server verifies the reconstructed master
2178
+ * actually matches this account's addresses before returning anything —
2179
+ * see the security note in the TEE's /signer/recover handler. Rotates the
2180
+ * whole share set: the caller MUST persist the returned recoveryShare as a
2181
+ * new recovery-share file, since the old one stops working immediately.
2182
+ */
2183
+ async recoverWithShare(jwt, recoveryShare) {
2184
+ await this.ensureAttested();
2185
+ const res = await fetch(`${this.teeUrl}/signer/recover`, {
2186
+ method: "POST",
2187
+ headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
2188
+ body: JSON.stringify({ recoveryShare: bytesToBase64(recoveryShare) })
2189
+ });
2190
+ if (res.status === 403) {
2191
+ const { RecoveryWalletMismatchError: RecoveryWalletMismatchError2 } = await Promise.resolve().then(() => (init_errors(), errors_exports));
2192
+ throw new RecoveryWalletMismatchError2();
2193
+ }
2194
+ if (!res.ok) throw new Error("recovery failed");
2195
+ const body = await res.json();
2196
+ return {
2197
+ deviceShare: base64ToBytes(body.deviceShare),
2198
+ recoveryShare: base64ToBytes(body.recoveryShare),
2199
+ shareSetVersion: body.shareSetVersion
2200
+ };
2201
+ }
2202
+ /**
2203
+ * Request a fresh WebAuthn assertion challenge and complete it in the
2204
+ * browser via @simplewebauthn/browser's startAuthentication(), which
2205
+ * handles all the base64url (de)serialization the raw navigator.credentials
2206
+ * API requires. Shared by rotateShares() and exportPortableBackup() — both
2207
+ * require proof of fresh authenticator possession, not just an active session.
2208
+ */
2209
+ async completeAssertion(jwt) {
2210
+ const res = await fetch(`${this.teeUrl}/signer/assertion-challenge`, {
2211
+ method: "POST",
2212
+ headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
2213
+ body: JSON.stringify({ rpId: window.location.hostname })
2214
+ });
2215
+ if (!res.ok) throw new ExportNotAuthorizedError();
2216
+ const { challengeId, options } = await res.json();
2217
+ let assertion;
2218
+ try {
2219
+ assertion = await startAuthentication({ optionsJSON: options });
2220
+ } catch {
2221
+ const { UserCancelledError: UserCancelledError2 } = await Promise.resolve().then(() => (init_errors(), errors_exports));
2222
+ throw new UserCancelledError2();
2223
+ }
2224
+ return { challengeId, assertion };
2225
+ }
2226
+ /** Voluntary rotation — e.g. after a suspected device compromise, or to invalidate an old recovery file. */
2227
+ async rotateShares(jwt) {
2228
+ if (!this.handle) throw new Error("no session");
2229
+ const { challengeId, assertion } = await this.completeAssertion(jwt);
2230
+ const res = await fetch(`${this.teeUrl}/signer/rotate`, {
2231
+ method: "POST",
2232
+ headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
2233
+ body: JSON.stringify({ handle: this.handle, challengeId, assertion, origin: window.location.origin })
2234
+ });
2235
+ if (!res.ok) throw new ExportNotAuthorizedError();
2236
+ const body = await res.json();
2237
+ return {
2238
+ deviceShare: base64ToBytes(body.deviceShare),
2239
+ recoveryShare: base64ToBytes(body.recoveryShare),
2240
+ shareSetVersion: body.shareSetVersion
2241
+ };
2242
+ }
2243
+ /** The two-share bearer portable backup — see recovery/portableBackup.ts. Rotates the set on export. */
2244
+ async exportPortableBackup(jwt) {
2245
+ if (!this.handle) throw new Error("no session");
2246
+ const { challengeId, assertion } = await this.completeAssertion(jwt);
2247
+ const res = await fetch(`${this.teeUrl}/signer/export-portable`, {
2248
+ method: "POST",
2249
+ headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
2250
+ body: JSON.stringify({ handle: this.handle, challengeId, assertion, origin: window.location.origin })
2251
+ });
2252
+ if (!res.ok) throw new ExportNotAuthorizedError();
2253
+ const body = await res.json();
2254
+ return {
2255
+ deviceShare: base64ToBytes(body.deviceShare),
2256
+ recoveryShare: base64ToBytes(body.recoveryShare),
2257
+ shareSetVersion: body.shareSetVersion
2258
+ };
2259
+ }
2260
+ hasSession() {
2261
+ return this.handle !== null;
2262
+ }
2263
+ async sign(req) {
2264
+ if (!this.handle) throw new Error("no session");
2265
+ const res = await fetch(`${this.teeUrl}/signer/sign`, {
2266
+ method: "POST",
2267
+ headers: { "Content-Type": "application/json" },
2268
+ body: JSON.stringify({ handle: this.handle, ...req })
2269
+ });
2270
+ if (res.status === 401) {
2271
+ this.handle = null;
2272
+ throw new SessionExpiredError();
2273
+ }
2274
+ if (!res.ok) throw new Error("sign failed");
2275
+ return (await res.json()).signature;
2276
+ }
2277
+ async closeSession() {
2278
+ if (!this.handle) return;
2279
+ const handle = this.handle;
2280
+ this.handle = null;
2281
+ void fetch(`${this.teeUrl}/signer/close`, {
2282
+ method: "POST",
2283
+ headers: { "Content-Type": "application/json" },
2284
+ body: JSON.stringify({ handle })
2285
+ }).catch(() => {
2286
+ });
2287
+ }
2288
+ };
2289
+
2290
+ // src/social/session.ts
2291
+ init_errors();
2292
+ var SessionManager = class {
2293
+ constructor(events, durationMs, teeUrl) {
2294
+ this.events = events;
2295
+ this.durationMs = durationMs;
2296
+ this.teeUrl = teeUrl;
2297
+ this.tee = null;
2298
+ this.evmAddress = null;
2299
+ this.solanaAddress = null;
2300
+ this.expiresAt = null;
2301
+ this.timeoutId = null;
2302
+ if (typeof window !== "undefined") {
2303
+ window.addEventListener("pagehide", () => this.end());
2304
+ }
2305
+ }
2306
+ async start(deviceShare, jwt, addresses) {
2307
+ this.tee = new TeeClient(this.teeUrl);
2308
+ await this.tee.openSession(deviceShare, jwt);
2309
+ this.evmAddress = addresses.evm;
2310
+ this.solanaAddress = addresses.solana;
2311
+ this.expiresAt = Date.now() + this.durationMs;
2312
+ if (this.timeoutId) clearTimeout(this.timeoutId);
2313
+ this.timeoutId = setTimeout(() => {
2314
+ this.end();
2315
+ this.events.emit("session-expired");
2316
+ }, this.durationMs);
2317
+ this.events.emit("session-started");
2318
+ }
2319
+ end() {
2320
+ void this.tee?.closeSession();
2321
+ this.tee = null;
2322
+ this.evmAddress = null;
2323
+ this.solanaAddress = null;
2324
+ this.expiresAt = null;
2325
+ if (this.timeoutId) {
2326
+ clearTimeout(this.timeoutId);
2327
+ this.timeoutId = null;
2328
+ }
2329
+ }
2330
+ isActive() {
2331
+ return this.tee !== null && Date.now() < (this.expiresAt ?? 0);
2332
+ }
2333
+ expiresAtTimestamp() {
2334
+ return this.expiresAt;
2335
+ }
2336
+ requireActive() {
2337
+ if (!this.isActive()) throw new UnlockRequiredError();
2338
+ return this.tee;
2339
+ }
2340
+ addresses() {
2341
+ if (!this.evmAddress || !this.solanaAddress) return null;
2342
+ return { evm: this.evmAddress, solana: this.solanaAddress };
2343
+ }
2344
+ };
2345
+
2346
+ // src/social/crypto/shares.ts
2347
+ import { secp256k1 } from "@noble/curves/secp256k1";
2348
+ import { split, combine } from "shamir-secret-sharing";
2349
+ function generateRandomBytes(n) {
2350
+ return crypto.getRandomValues(new Uint8Array(n));
2351
+ }
2352
+ function zeroize(bytes) {
2353
+ bytes.fill(0);
2354
+ }
2355
+ function generateMaster() {
2356
+ let master;
2357
+ do {
2358
+ master = generateRandomBytes(32);
2359
+ } while (!secp256k1.utils.isValidPrivateKey(master));
2360
+ return master;
2361
+ }
2362
+ async function splitMaster(master, version = 1) {
2363
+ if (master.length !== 32) throw new Error("master must be 32 bytes");
2364
+ const parts = await split(master, 3, 2);
2365
+ if (parts.length !== 3) throw new Error("unexpected share count");
2366
+ return { device: parts[0], server: parts[1], recovery: parts[2], version };
2367
+ }
2368
+
2369
+ // src/social/crypto/wrap.ts
2370
+ function buf(u) {
2371
+ return u.buffer.slice(u.byteOffset, u.byteOffset + u.byteLength);
2372
+ }
2373
+ async function importAesKey(rawKey) {
2374
+ return crypto.subtle.importKey(
2375
+ "raw",
2376
+ buf(rawKey),
2377
+ { name: "AES-GCM", length: 256 },
2378
+ false,
2379
+ ["encrypt", "decrypt"]
2380
+ );
2381
+ }
2382
+ async function aesGcmEncrypt(key, plaintext, iv) {
2383
+ const ct = await crypto.subtle.encrypt({ name: "AES-GCM", iv: buf(iv) }, key, buf(plaintext));
2384
+ return new Uint8Array(ct);
2385
+ }
2386
+ async function aesGcmDecrypt(key, ciphertext, iv) {
2387
+ const pt = await crypto.subtle.decrypt({ name: "AES-GCM", iv: buf(iv) }, key, buf(ciphertext));
2388
+ return new Uint8Array(pt);
2389
+ }
2390
+
2391
+ // src/social/crypto/pin.ts
2392
+ var PBKDF2_ITERATIONS = 6e5;
2393
+ function buf2(u) {
2394
+ return u.buffer.slice(u.byteOffset, u.byteOffset + u.byteLength);
2395
+ }
2396
+ async function deriveKeyFromPin(pin, salt) {
2397
+ const pinBytes = new TextEncoder().encode(pin);
2398
+ const base = await crypto.subtle.importKey("raw", buf2(pinBytes), "PBKDF2", false, ["deriveKey"]);
2399
+ return crypto.subtle.deriveKey(
2400
+ { name: "PBKDF2", salt: buf2(salt), iterations: PBKDF2_ITERATIONS, hash: "SHA-256" },
2401
+ base,
2402
+ { name: "AES-GCM", length: 256 },
2403
+ false,
2404
+ ["encrypt", "decrypt"]
2405
+ );
2406
+ }
2407
+ function validatePin(pin) {
2408
+ if (!/^\d{4,8}$/.test(pin)) throw new Error("PIN must be 4\u20138 digits");
2409
+ }
2410
+
2411
+ // src/social/crypto/passkey.ts
2412
+ init_errors();
2413
+ import { startRegistration } from "@simplewebauthn/browser";
2414
+ function buf3(u) {
2415
+ return u.buffer.slice(u.byteOffset, u.byteOffset + u.byteLength);
2416
+ }
2417
+ function isPasskeySupported() {
2418
+ return typeof window !== "undefined" && !!window.PublicKeyCredential && "isUserVerifyingPlatformAuthenticatorAvailable" in window.PublicKeyCredential;
2419
+ }
2420
+ async function registerPasskey(opts) {
2421
+ let response;
2422
+ try {
2423
+ response = await startRegistration({ optionsJSON: opts.optionsJSON });
2424
+ } catch (e) {
2425
+ if (e.name === "NotAllowedError") throw new UserCancelledError();
2426
+ throw e;
2427
+ }
2428
+ const extResults = response.clientExtensionResults;
2429
+ const prfResults = extResults?.prf?.results;
2430
+ let prfOutput;
2431
+ if (prfResults?.first) {
2432
+ prfOutput = typeof prfResults.first === "string" ? Uint8Array.from(atob(prfResults.first.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0)) : new Uint8Array(prfResults.first);
2433
+ }
2434
+ const credentialId = Uint8Array.from(
2435
+ atob(response.id.replace(/-/g, "+").replace(/_/g, "/")),
2436
+ (c) => c.charCodeAt(0)
2437
+ );
2438
+ return { credentialId, prfOutput, response };
2439
+ }
2440
+ async function getPrfOutput(opts) {
2441
+ const challenge = crypto.getRandomValues(new Uint8Array(32));
2442
+ let cred;
2443
+ try {
2444
+ cred = await navigator.credentials.get({
2445
+ publicKey: {
2446
+ challenge: buf3(challenge),
2447
+ rpId: opts.rpId,
2448
+ allowCredentials: [{ id: buf3(opts.credentialId), type: "public-key" }],
2449
+ userVerification: "required",
2450
+ extensions: { prf: { eval: { first: buf3(opts.prfSalt) } } }
2451
+ }
2452
+ });
2453
+ } catch (e) {
2454
+ if (e.name === "NotAllowedError") throw new UserCancelledError();
2455
+ throw e;
2456
+ }
2457
+ const ext = cred.getClientExtensionResults();
2458
+ const prfResults = ext?.prf?.results;
2459
+ if (!prfResults?.first) throw new WebAuthnNotSupportedError();
2460
+ return new Uint8Array(prfResults.first);
2461
+ }
2462
+
2463
+ // src/social/webauthnRegistration.ts
2464
+ function bufFromBytes(u) {
2465
+ return u.buffer.slice(u.byteOffset, u.byteOffset + u.byteLength);
2466
+ }
2467
+ async function registerWebAuthnCredential(opts) {
2468
+ const baseOptions = await opts.api.getWebAuthnRegisterOptions(opts.jwt, opts.rpId, opts.rpName);
2469
+ const optionsJSON = {
2470
+ ...baseOptions,
2471
+ extensions: {
2472
+ ...baseOptions.extensions,
2473
+ prf: { eval: { first: bufFromBytes(opts.prfSalt) } }
2474
+ }
2475
+ };
2476
+ const { credentialId, prfOutput, response } = await registerPasskey({ optionsJSON });
2477
+ try {
2478
+ await opts.api.postWebAuthnRegister(opts.jwt, response, window.location.origin);
2479
+ } catch (e) {
2480
+ console.warn("decane-connect-kit: WebAuthn server-side registration failed; PRF-based unlock still works, but rotateShares()/exportPortableBackup() will not until this is retried.", e);
2481
+ }
2482
+ return { credentialId, prfOutput };
2483
+ }
2484
+
2485
+ // src/social/auth/email.ts
2486
+ async function startEmailAuth(api, email) {
2487
+ await api.authEmailStart(email);
2488
+ }
2489
+ async function verifyEmailCode(api, email, code) {
2490
+ return api.authEmailVerify(email, code);
2491
+ }
2492
+
2493
+ // src/social/signers/evm.ts
2494
+ import { keccak256, Transaction, resolveProperties } from "ethers";
2495
+ async function signEvmTransaction(tee, chain, tx) {
2496
+ const resolved = await resolveProperties(tx);
2497
+ const unsigned = Transaction.from(resolved).unsignedSerialized;
2498
+ const payload = unsigned.startsWith("0x") ? unsigned.slice(2) : unsigned;
2499
+ const txHash = keccak256("0x" + payload);
2500
+ return tee.sign({ chain, kind: "transaction", payload, txHash });
2501
+ }
2502
+ async function signEvmMessage(tee, chain, message) {
2503
+ return tee.sign({ chain, kind: "message", payload: message });
2504
+ }
2505
+
2506
+ // src/social/signers/solana.ts
2507
+ import { VersionedTransaction as VTx } from "@solana/web3.js";
2508
+ function b64(bytes) {
2509
+ let s = "";
2510
+ for (const b of bytes) s += String.fromCharCode(b);
2511
+ return btoa(s);
2512
+ }
2513
+ function unb64(s) {
2514
+ return Uint8Array.from(atob(s), (c) => c.charCodeAt(0));
2515
+ }
2516
+ async function signSolanaTransaction(tee, chain, tx) {
2517
+ const bytes = tx instanceof VTx ? tx.serialize() : tx.serialize({ requireAllSignatures: false });
2518
+ const signed = await tee.sign({ chain, kind: "transaction", payload: b64(bytes) });
2519
+ return unb64(signed);
2520
+ }
2521
+ async function signSolanaMessage(tee, chain, message) {
2522
+ const sig = await tee.sign({ chain, kind: "message", payload: b64(message) });
2523
+ return unb64(sig);
2524
+ }
2525
+
2526
+ // src/social/recovery/wrappedShareBackup.ts
2527
+ async function fetchWrappedShareBackup(api, jwt) {
2528
+ return api.getRecoveryBackup(jwt);
2529
+ }
2530
+
2531
+ // src/social/recovery/recoveryShareFile.ts
2532
+ init_errors();
2533
+ var PBKDF2_ITERATIONS2 = 6e5;
2534
+ var FILE_VERSION = 2;
2535
+ function b642(u) {
2536
+ let s = "";
2537
+ for (const b of u) s += String.fromCharCode(b);
2538
+ return btoa(s);
2539
+ }
2540
+ function unb642(s) {
2541
+ return Uint8Array.from(atob(s), (c) => c.charCodeAt(0));
2542
+ }
2543
+ function buf4(u) {
2544
+ return u.buffer.slice(u.byteOffset, u.byteOffset + u.byteLength);
2545
+ }
2546
+ async function deriveFileKey(password, salt, iterations) {
2547
+ const base = await crypto.subtle.importKey(
2548
+ "raw",
2549
+ new TextEncoder().encode(password),
2550
+ "PBKDF2",
2551
+ false,
2552
+ ["deriveKey"]
2553
+ );
2554
+ return crypto.subtle.deriveKey(
2555
+ { name: "PBKDF2", salt: buf4(salt), iterations, hash: "SHA-256" },
2556
+ base,
2557
+ { name: "AES-GCM", length: 256 },
2558
+ false,
2559
+ ["encrypt", "decrypt"]
2560
+ );
2561
+ }
2562
+ async function buildRecoveryShareFile(share, password, addresses, shareSetVersion, passwordHint) {
2563
+ if (share.length !== 33) throw new Error("share must be 33 bytes");
2564
+ if (password.length < 8) throw new Error("password must be at least 8 characters");
2565
+ if (passwordHint && passwordHint.trim() === password) {
2566
+ throw new Error("password hint must not be the password");
2567
+ }
2568
+ const salt = crypto.getRandomValues(new Uint8Array(16));
2569
+ const iv = crypto.getRandomValues(new Uint8Array(12));
2570
+ const key = await deriveFileKey(password, salt, PBKDF2_ITERATIONS2);
2571
+ const ct = new Uint8Array(
2572
+ await crypto.subtle.encrypt({ name: "AES-GCM", iv: buf4(iv) }, key, buf4(share))
2573
+ );
2574
+ return {
2575
+ version: FILE_VERSION,
2576
+ type: "decane-recovery-share",
2577
+ shareSetVersion,
2578
+ kdf: "pbkdf2-sha256",
2579
+ kdfParams: { iterations: PBKDF2_ITERATIONS2, salt: b642(salt) },
2580
+ cipher: "aes-256-gcm",
2581
+ iv: b642(iv),
2582
+ ciphertext: b642(ct),
2583
+ addresses,
2584
+ passwordHint: passwordHint?.trim() || null,
2585
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2586
+ };
2587
+ }
2588
+ function parseRecoveryShareFile(json) {
2589
+ const raw = json;
2590
+ if (raw?.type === "decane-recovery") {
2591
+ throw new InvalidRecoveryFileError();
2592
+ }
2593
+ const f = json;
2594
+ if (!f || f.type !== "decane-recovery-share" || f.version !== FILE_VERSION || f.kdf !== "pbkdf2-sha256" || f.cipher !== "aes-256-gcm" || typeof f.iv !== "string" || typeof f.ciphertext !== "string" || typeof f.kdfParams?.salt !== "string" || typeof f.kdfParams?.iterations !== "number" || typeof f.shareSetVersion !== "number" || typeof f.addresses?.evm !== "string" || typeof f.addresses?.solana !== "string") {
2595
+ throw new InvalidRecoveryFileError();
2596
+ }
2597
+ if (f.kdfParams.iterations < 1e5 || f.kdfParams.iterations > 5e6) {
2598
+ throw new InvalidRecoveryFileError();
2599
+ }
2600
+ return f;
2601
+ }
2602
+ async function decryptRecoveryShareFile(file, password) {
2603
+ const salt = unb642(file.kdfParams.salt);
2604
+ const iv = unb642(file.iv);
2605
+ const ct = unb642(file.ciphertext);
2606
+ const key = await deriveFileKey(password, salt, file.kdfParams.iterations);
2607
+ let share;
2608
+ try {
2609
+ share = new Uint8Array(
2610
+ await crypto.subtle.decrypt({ name: "AES-GCM", iv: buf4(iv) }, key, buf4(ct))
2611
+ );
2612
+ } catch {
2613
+ throw new WrongPasswordError();
2614
+ }
2615
+ if (share.length !== 33) {
2616
+ share.fill(0);
2617
+ throw new InvalidRecoveryFileError();
2618
+ }
2619
+ return share;
2620
+ }
2621
+ function recoveryShareFileName(evmAddress) {
2622
+ return `decane-recovery-${evmAddress.slice(0, 8)}.json`;
2623
+ }
2624
+
2625
+ // src/social/recovery/recoveryFlow.ts
2626
+ init_errors();
2627
+ async function recoverDeviceShare(input) {
2628
+ const backup = await fetchWrappedShareBackup(input.api, input.jwt);
2629
+ if (backup) {
2630
+ input.onProgress?.({ type: "passkey-attempt" });
2631
+ try {
2632
+ const prfOutput = await getPrfOutput({
2633
+ rpId: input.rpId,
2634
+ credentialId: backup.credentialId,
2635
+ prfSalt: backup.prfSalt
2636
+ });
2637
+ const wrappingKey = await importAesKey(prfOutput);
2638
+ const deviceShare = await aesGcmDecrypt(
2639
+ wrappingKey,
2640
+ backup.wrappedDeviceShare,
2641
+ backup.iv
2642
+ );
2643
+ await input.storage.putShare({
2644
+ userId: input.userId,
2645
+ wrappedDeviceShare: backup.wrappedDeviceShare,
2646
+ iv: backup.iv,
2647
+ unlockMethod: "passkey",
2648
+ credentialId: backup.credentialId,
2649
+ prfSalt: backup.prfSalt,
2650
+ version: 1
2651
+ });
2652
+ return deviceShare;
2653
+ } catch (e) {
2654
+ input.onProgress?.({
2655
+ type: "passkey-failed",
2656
+ reason: e instanceof Error ? e.message : "unknown"
2657
+ });
2658
+ }
2659
+ }
2660
+ if (!input.promptForRecoveryFile) throw new NewDeviceError();
2661
+ input.onProgress?.({ type: "recovery-file-prompt" });
2662
+ const supplied = await input.promptForRecoveryFile();
2663
+ if (!supplied) throw new NewDeviceError();
2664
+ const file = parseRecoveryShareFile(supplied.value);
2665
+ input.onProgress?.({ type: "password-prompt", passwordHint: file.passwordHint });
2666
+ const password = await supplied.getPassword();
2667
+ if (!password) throw new NewDeviceError();
2668
+ const recoveryShare = await decryptRecoveryShareFile(file, password);
2669
+ input.onProgress?.({ type: "recovery-verifying" });
2670
+ const tee = new TeeClient(input.teeUrl);
2671
+ let result;
2672
+ try {
2673
+ result = await tee.recoverWithShare(input.jwt, recoveryShare);
2674
+ } finally {
2675
+ zeroize(recoveryShare);
2676
+ }
2677
+ input.onProgress?.({ type: "rotated", shareSetVersion: result.shareSetVersion });
2678
+ await input.onNewRecoveryShare?.(result.recoveryShare, result.shareSetVersion);
2679
+ zeroize(result.recoveryShare);
2680
+ return result.deviceShare;
2681
+ }
2682
+
2683
+ // src/social/recovery/portableBackup.ts
2684
+ init_errors();
2685
+ var PBKDF2_ITERATIONS3 = 6e5;
2686
+ var FILE_VERSION2 = 1;
2687
+ function b643(u) {
2688
+ let s = "";
2689
+ for (const b of u) s += String.fromCharCode(b);
2690
+ return btoa(s);
2691
+ }
2692
+ function buf5(u) {
2693
+ return u.buffer.slice(u.byteOffset, u.byteOffset + u.byteLength);
2694
+ }
2695
+ async function deriveFileKey2(password, salt, iterations) {
2696
+ const base = await crypto.subtle.importKey(
2697
+ "raw",
2698
+ new TextEncoder().encode(password),
2699
+ "PBKDF2",
2700
+ false,
2701
+ ["deriveKey"]
2702
+ );
2703
+ return crypto.subtle.deriveKey(
2704
+ { name: "PBKDF2", salt: buf5(salt), iterations, hash: "SHA-256" },
2705
+ base,
2706
+ { name: "AES-GCM", length: 256 },
2707
+ false,
2708
+ ["encrypt", "decrypt"]
2709
+ );
2710
+ }
2711
+ async function buildPortableBackup(deviceShare, recoveryShare, password, addresses, shareSetVersion, passwordHint) {
2712
+ if (deviceShare.length !== 33 || recoveryShare.length !== 33) {
2713
+ throw new Error("shares must be 33 bytes");
2714
+ }
2715
+ if (password.length < 8) throw new Error("password must be at least 8 characters");
2716
+ if (passwordHint && passwordHint.trim() === password) {
2717
+ throw new Error("password hint must not be the password");
2718
+ }
2719
+ const plaintext = new TextEncoder().encode(
2720
+ JSON.stringify({ deviceShare: b643(deviceShare), recoveryShare: b643(recoveryShare) })
2721
+ );
2722
+ const salt = crypto.getRandomValues(new Uint8Array(16));
2723
+ const iv = crypto.getRandomValues(new Uint8Array(12));
2724
+ const key = await deriveFileKey2(password, salt, PBKDF2_ITERATIONS3);
2725
+ const ct = new Uint8Array(
2726
+ await crypto.subtle.encrypt({ name: "AES-GCM", iv: buf5(iv) }, key, buf5(plaintext))
2727
+ );
2728
+ return {
2729
+ version: FILE_VERSION2,
2730
+ type: "decane-portable-backup",
2731
+ shareSetVersion,
2732
+ kdf: "pbkdf2-sha256",
2733
+ kdfParams: { iterations: PBKDF2_ITERATIONS3, salt: b643(salt) },
2734
+ cipher: "aes-256-gcm",
2735
+ iv: b643(iv),
2736
+ ciphertext: b643(ct),
2737
+ addresses,
2738
+ passwordHint: passwordHint?.trim() || null,
2739
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2740
+ };
2741
+ }
2742
+
2743
+ // src/social/client.ts
2744
+ var DEFAULT_API_BASE = "https://backend.decane.app";
2745
+ var DEFAULT_TEE_URL = "https://4405742df08d19abc549a05ade2a7154d98bd87f-8443.dstack-pha-prod9.phala.network";
2746
+ var DEFAULT_SESSION_MINUTES = 30;
2747
+ var DEFAULT_RPCS = {
2748
+ "evm:1": "https://ethereum.publicnode.com",
2749
+ "evm:137": "https://polygon.publicnode.com",
2750
+ "evm:8453": "https://base.publicnode.com",
2751
+ "evm:10": "https://optimism.publicnode.com",
2752
+ "evm:42161": "https://arbitrum-one.publicnode.com",
2753
+ "evm:56": "https://bsc.publicnode.com",
2754
+ "evm:11155111": "https://ethereum-sepolia.publicnode.com",
2755
+ "evm:84532": "https://base-sepolia.publicnode.com",
2756
+ "evm:31337": "http://localhost:8545"
2757
+ };
2758
+ function makePersistKey(apiKey) {
2759
+ return `decane:social:${apiKey.slice(0, 20)}`;
2760
+ }
2761
+ function loadPersisted(key) {
2762
+ try {
2763
+ const raw = typeof localStorage !== "undefined" ? localStorage.getItem(key) : null;
2764
+ return raw ? JSON.parse(raw) : null;
2765
+ } catch {
2766
+ return null;
2767
+ }
2768
+ }
2769
+ function savePersisted(key, state) {
2770
+ try {
2771
+ localStorage.setItem(key, JSON.stringify(state));
2772
+ } catch {
2773
+ }
2774
+ }
2775
+ function clearPersisted(key) {
2776
+ try {
2777
+ localStorage.removeItem(key);
2778
+ } catch {
2779
+ }
2780
+ }
2781
+ async function createDecaneConnect(config) {
2782
+ if (!config.apiKey) throw new Error("decane-connect-kit: apiKey is required");
2783
+ if (!config.chains?.length)
2784
+ throw new Error("decane-connect-kit: at least one chain is required");
2785
+ const baseUrl = config.apiBaseUrl ?? DEFAULT_API_BASE;
2786
+ const durationMs = (config.sessionDurationMinutes ?? DEFAULT_SESSION_MINUTES) * 6e4;
2787
+ const rpId = config.rpId ?? (typeof window !== "undefined" ? window.location.hostname : "localhost");
2788
+ const rpName = config.rpName ?? "Decane Connect";
2789
+ const PERSIST_KEY = makePersistKey(config.apiKey);
2790
+ const teeUrl = config.teeUrl ?? DEFAULT_TEE_URL;
2791
+ const events = new EventEmitter();
2792
+ const api = new ApiClient(baseUrl, config.apiKey);
2793
+ const storage = new Storage();
2794
+ const session = new SessionManager(events, durationMs, teeUrl);
2795
+ let auth = null;
2796
+ let persisted = loadPersisted(PERSIST_KEY);
2797
+ async function pinSetup() {
2798
+ if (!config.promptPin) throw new PromptPinNotConfiguredError();
2799
+ const pin = await config.promptPin();
2800
+ validatePin(pin);
2801
+ const pinSalt = generateRandomBytes(32);
2802
+ const wrappingKey = await deriveKeyFromPin(pin, pinSalt);
2803
+ return {
2804
+ wrappingKey,
2805
+ meta: {
2806
+ unlockMethod: "pin",
2807
+ pinSalt: bufferToB64u(pinSalt),
2808
+ pinIterations: 6e5
2809
+ }
2810
+ };
2811
+ }
2812
+ async function getWrappingKey(stored) {
2813
+ if (stored.unlockMethod === "passkey") {
2814
+ const prfOutput = await getPrfOutput({
2815
+ rpId,
2816
+ credentialId: stored.credentialId,
2817
+ prfSalt: stored.prfSalt
2818
+ });
2819
+ return importAesKey(prfOutput);
2820
+ }
2821
+ if (!config.promptPin) throw new PromptPinNotConfiguredError();
2822
+ const pin = await config.promptPin();
2823
+ return deriveKeyFromPin(pin, stored.pinSalt);
2824
+ }
2825
+ async function registerPasskeyAndBackup(userId, jwt, deviceShare) {
2826
+ if (!isPasskeySupported()) return;
2827
+ const prfSalt = generateRandomBytes(32);
2828
+ let credentialId;
2829
+ let prfOutput;
2830
+ try {
2831
+ const result = await registerWebAuthnCredential({ api, jwt, rpId, rpName, prfSalt });
2832
+ credentialId = result.credentialId;
2833
+ prfOutput = result.prfOutput;
2834
+ } catch (e) {
2835
+ console.warn("decane-connect-kit: passkey provisioning after recovery failed; user can set this up later.", e);
2836
+ return;
2837
+ }
2838
+ if (!prfOutput) return;
2839
+ const wrappingKey = await importAesKey(prfOutput);
2840
+ const iv = generateRandomBytes(12);
2841
+ const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
2842
+ await storage.putShare({
2843
+ userId,
2844
+ wrappedDeviceShare,
2845
+ iv,
2846
+ unlockMethod: "passkey",
2847
+ credentialId,
2848
+ prfSalt,
2849
+ version: 1
2850
+ });
2851
+ try {
2852
+ await api.putRecoveryBackup(jwt, {
2853
+ wrappedDeviceShare,
2854
+ iv,
2855
+ unlockMethod: "passkey",
2856
+ credentialId,
2857
+ prfSalt
2858
+ });
2859
+ } catch (e) {
2860
+ console.warn("decane-connect-kit: wrapped share backup upload failed; user can retry from settings.", e);
2861
+ }
2862
+ }
2863
+ async function firstTimeSetup(userId, jwt) {
2864
+ let unlockMethod;
2865
+ let wrappingKey;
2866
+ let meta = {};
2867
+ if (isPasskeySupported()) {
2868
+ const prfSalt = generateRandomBytes(32);
2869
+ try {
2870
+ const { credentialId, prfOutput } = await registerWebAuthnCredential({ api, jwt, rpId, rpName, prfSalt });
2871
+ if (prfOutput) {
2872
+ unlockMethod = "passkey";
2873
+ wrappingKey = await importAesKey(prfOutput);
2874
+ meta = {
2875
+ unlockMethod: "passkey",
2876
+ credentialId: bufferToB64u(credentialId),
2877
+ prfSalt: bufferToB64u(prfSalt)
2878
+ };
2879
+ } else {
2880
+ ({ wrappingKey, meta } = await pinSetup());
2881
+ unlockMethod = "pin";
2882
+ }
2883
+ } catch (e) {
2884
+ if (e.name === "UserCancelledError")
2885
+ throw new UserCancelledError();
2886
+ ({ wrappingKey, meta } = await pinSetup());
2887
+ unlockMethod = "pin";
2888
+ }
2889
+ } else {
2890
+ ({ wrappingKey, meta } = await pinSetup());
2891
+ unlockMethod = "pin";
2892
+ }
2893
+ const master = generateMaster();
2894
+ let shareSet;
2895
+ let evmAddress;
2896
+ let solanaAddress;
2897
+ try {
2898
+ evmAddress = new Wallet(toHex(master)).address;
2899
+ solanaAddress = Keypair.fromSeed(master).publicKey.toBase58();
2900
+ shareSet = await splitMaster(master, 1);
2901
+ } finally {
2902
+ zeroize(master);
2903
+ }
2904
+ const iv = generateRandomBytes(12);
2905
+ const wrappedDeviceShare = await aesGcmEncrypt(
2906
+ wrappingKey,
2907
+ shareSet.device,
2908
+ iv
2909
+ );
2910
+ await storage.putShare({
2911
+ userId,
2912
+ wrappedDeviceShare,
2913
+ iv,
2914
+ unlockMethod,
2915
+ credentialId: meta.credentialId ? b64uToBuffer(meta.credentialId) : void 0,
2916
+ prfSalt: meta.prfSalt ? b64uToBuffer(meta.prfSalt) : void 0,
2917
+ pinSalt: meta.pinSalt ? b64uToBuffer(meta.pinSalt) : void 0,
2918
+ pinIterations: meta.pinIterations,
2919
+ version: 1
2920
+ });
2921
+ const serverIv = generateRandomBytes(12);
2922
+ await api.putShare(jwt, {
2923
+ ciphertext: shareSet.server,
2924
+ iv: serverIv,
2925
+ wrappedShareMeta: {
2926
+ unlockMethod
2927
+ // credential IDs, prfSalt, pinSalt stay on-device — never sent to server
2928
+ },
2929
+ evmAddress,
2930
+ solanaAddress
2931
+ });
2932
+ zeroize(shareSet.server);
2933
+ const addresses = { evm: evmAddress, solana: solanaAddress };
2934
+ if (unlockMethod === "passkey" && meta.credentialId && meta.prfSalt) {
2935
+ try {
2936
+ await api.putRecoveryBackup(jwt, {
2937
+ wrappedDeviceShare,
2938
+ iv,
2939
+ unlockMethod: "passkey",
2940
+ credentialId: b64uToBuffer(meta.credentialId),
2941
+ prfSalt: b64uToBuffer(meta.prfSalt)
2942
+ });
2943
+ } catch (e) {
2944
+ console.warn("decane-connect-kit: wrapped share backup failed; user can retry from settings.", e);
2945
+ }
2946
+ }
2947
+ await session.start(shareSet.device, jwt, addresses);
2948
+ zeroize(shareSet.device);
2949
+ if (config.offerRecoveryAtSignup !== false && config.onRecoveryShareOffer) {
2950
+ try {
2951
+ const choice = await config.onRecoveryShareOffer({ evmAddress, solanaAddress });
2952
+ if (choice.wants) {
2953
+ const file = await buildRecoveryShareFile(
2954
+ shareSet.recovery,
2955
+ choice.password,
2956
+ addresses,
2957
+ shareSet.version,
2958
+ choice.passwordHint
2959
+ );
2960
+ await config.onRecoveryFileReady?.(file, recoveryShareFileName(evmAddress));
2961
+ await api.markRecoveryShareSaved(jwt);
2962
+ }
2963
+ } catch (e) {
2964
+ console.warn("decane-connect-kit: recovery share offer failed or was skipped.", e);
2965
+ }
2966
+ }
2967
+ zeroize(shareSet.recovery);
2968
+ return addresses;
2969
+ }
2970
+ async function returningUserUnlock(userId, jwt) {
2971
+ const rawAddresses = await api.getAddresses(jwt);
2972
+ if (!rawAddresses.evm || !rawAddresses.solana) {
2973
+ throw new CallSequenceError(
2974
+ "No addresses are recorded for this user \u2014 inconsistent server state"
2975
+ );
2976
+ }
2977
+ const addresses = { evm: rawAddresses.evm, solana: rawAddresses.solana };
2978
+ const stored = await storage.getShare(userId);
2979
+ let deviceShare;
2980
+ if (!stored) {
2981
+ deviceShare = await recoverDeviceShare({
2982
+ userId,
2983
+ jwt,
2984
+ rpId,
2985
+ teeUrl,
2986
+ api,
2987
+ storage,
2988
+ onProgress: config.onRecoveryProgress,
2989
+ promptForRecoveryFile: config.promptForRecoveryFile,
2990
+ onNewRecoveryShare: async (recoveryShare, shareSetVersion) => {
2991
+ if (!config.onRecoveryRotated) {
2992
+ console.warn("decane-connect-kit: share set rotated after recovery, but no onRecoveryRotated callback is configured \u2014 the user has no way to save a new recovery-share file.");
2993
+ return;
2994
+ }
2995
+ const { password, passwordHint } = await config.onRecoveryRotated({ evmAddress: addresses.evm, solanaAddress: addresses.solana });
2996
+ const file = await buildRecoveryShareFile(recoveryShare, password, addresses, shareSetVersion, passwordHint);
2997
+ await config.onRecoveryFileReady?.(file, recoveryShareFileName(addresses.evm));
2998
+ await api.markRecoveryShareSaved(jwt);
2999
+ }
3000
+ });
3001
+ if (!await storage.getShare(userId) && isPasskeySupported()) {
3002
+ await registerPasskeyAndBackup(userId, jwt, deviceShare);
3003
+ }
3004
+ } else {
3005
+ const wrappingKey = await getWrappingKey(stored);
3006
+ try {
3007
+ deviceShare = await aesGcmDecrypt(
3008
+ wrappingKey,
3009
+ stored.wrappedDeviceShare,
3010
+ stored.iv
3011
+ );
3012
+ } catch {
3013
+ throw stored.unlockMethod === "pin" ? new InvalidPinError() : new ShareCorruptedError();
3014
+ }
3015
+ }
3016
+ await session.start(deviceShare, jwt, addresses);
3017
+ zeroize(deviceShare);
3018
+ return addresses;
3019
+ }
3020
+ async function handleAuthResult(result) {
3021
+ let addresses;
3022
+ if (!result.hasShare) {
3023
+ addresses = await firstTimeSetup(result.userId, result.jwt);
3024
+ } else {
3025
+ addresses = await returningUserUnlock(result.userId, result.jwt);
3026
+ }
3027
+ auth = { jwt: result.jwt, userId: result.userId, addresses };
3028
+ persisted = { userId: result.userId, addresses };
3029
+ savePersisted(PERSIST_KEY, persisted);
3030
+ events.emit("connected");
3031
+ return { addresses, isNewUser: result.isNewUser };
3032
+ }
3033
+ const client = {
3034
+ async connect() {
3035
+ if (auth && session.isActive()) {
3036
+ return { addresses: auth.addresses, isNewUser: false };
3037
+ }
3038
+ const method = config.authMethods[0];
3039
+ if (!method) throw new CallSequenceError("No authMethods configured");
3040
+ if (method === "google") return client.connectWithGoogle();
3041
+ if (method === "email")
3042
+ throw new CallSequenceError(
3043
+ "Use connectWithEmail(email) for email auth"
3044
+ );
3045
+ throw new CallSequenceError(`Unknown auth method: ${method}`);
3046
+ },
3047
+ async connectWithGoogle() {
3048
+ const params = new URLSearchParams(window.location.search);
3049
+ const decaneJwt = params.get("decane_jwt");
3050
+ const decaneError = params.get("decane_error");
3051
+ if (decaneError) {
3052
+ window.history.replaceState({}, "", window.location.pathname);
3053
+ throw new CallSequenceError(`Google sign-in failed: ${decaneError}`);
3054
+ }
3055
+ if (decaneJwt) {
3056
+ const result = {
3057
+ jwt: decaneJwt,
3058
+ userId: params.get("decane_user_id") ?? "",
3059
+ isNewUser: params.get("decane_is_new_user") === "true",
3060
+ hasShare: params.get("decane_has_share") === "true"
3061
+ };
3062
+ window.history.replaceState({}, "", window.location.pathname);
3063
+ return handleAuthResult(result);
3064
+ }
3065
+ const { url } = await api.getGoogleInitUrl();
3066
+ window.location.href = url;
3067
+ return new Promise(() => {
3068
+ });
3069
+ },
3070
+ async connectWithEmail(email) {
3071
+ await startEmailAuth(api, email);
3072
+ return { codeRequired: true };
3073
+ },
3074
+ async verifyEmailCode(email, code) {
3075
+ const result = await verifyEmailCode(api, email, code);
3076
+ return handleAuthResult(result);
3077
+ },
3078
+ async unlock() {
3079
+ if (!auth)
3080
+ throw new CallSequenceError("Not connected \u2014 call connect() first");
3081
+ if (session.isActive()) return;
3082
+ await returningUserUnlock(auth.userId, auth.jwt);
3083
+ },
3084
+ isUnlocked() {
3085
+ return session.isActive();
3086
+ },
3087
+ sessionExpiresAt() {
3088
+ return session.expiresAtTimestamp();
3089
+ },
3090
+ async signMessage(req) {
3091
+ const tee = session.requireActive();
3092
+ if (req.chain.startsWith("evm:")) {
3093
+ return signEvmMessage(tee, req.chain, req.message);
3094
+ }
3095
+ if (req.chain.startsWith("solana:")) {
3096
+ const msg = typeof req.message === "string" ? new TextEncoder().encode(req.message) : req.message;
3097
+ const sig = await signSolanaMessage(tee, req.chain, msg);
3098
+ return bufferToB64u(sig);
3099
+ }
3100
+ throw new CallSequenceError(`Unsupported chain: ${req.chain}`);
3101
+ },
3102
+ async sendTransaction(req) {
3103
+ const tee = session.requireActive();
3104
+ if (!req.chain.startsWith("evm:")) {
3105
+ throw new CallSequenceError(
3106
+ "sendTransaction is EVM-only. Use signSolanaTransaction for Solana."
3107
+ );
3108
+ }
3109
+ if (!auth?.addresses.evm) {
3110
+ throw new CallSequenceError("Not connected");
3111
+ }
3112
+ const evmAddr = auth.addresses.evm;
3113
+ const rpcUrl = config.rpcUrls?.[req.chain] ?? DEFAULT_RPCS[req.chain];
3114
+ if (!rpcUrl) {
3115
+ throw new CallSequenceError(
3116
+ `No RPC URL for ${req.chain}. Pass rpcUrls: { '${req.chain}': 'https://...' } in social config.`
3117
+ );
3118
+ }
3119
+ const { JsonRpcProvider } = await import("ethers");
3120
+ const provider = new JsonRpcProvider(rpcUrl);
3121
+ const chainId = parseInt(req.chain.slice(4), 10);
3122
+ const [nonce, feeData] = await Promise.all([
3123
+ provider.getTransactionCount(evmAddr, "pending"),
3124
+ provider.getFeeData()
3125
+ ]);
3126
+ const gasLimit = req.gasLimit ?? await provider.estimateGas({
3127
+ from: evmAddr,
3128
+ to: req.to,
3129
+ value: req.value ?? 0n,
3130
+ data: req.data
3131
+ });
3132
+ const tx = {
3133
+ to: req.to,
3134
+ value: req.value ?? 0n,
3135
+ data: req.data,
3136
+ gasLimit,
3137
+ chainId,
3138
+ nonce,
3139
+ // EIP-1559 fees — fall back to legacy gasPrice if unavailable
3140
+ ...feeData.maxFeePerGas != null ? {
3141
+ maxFeePerGas: feeData.maxFeePerGas,
3142
+ maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? 0n
3143
+ } : { gasPrice: req.gasPrice ?? feeData.gasPrice ?? void 0 }
3144
+ };
3145
+ const signedTx = await signEvmTransaction(tee, req.chain, tx);
3146
+ const txResponse = await provider.broadcastTransaction(signedTx);
3147
+ return txResponse.hash;
3148
+ },
3149
+ async signSolanaTransaction(serializedTx) {
3150
+ const tee = session.requireActive();
3151
+ let tx;
3152
+ try {
3153
+ tx = VersionedTransaction.deserialize(serializedTx);
3154
+ } catch {
3155
+ tx = Transaction2.from(serializedTx);
3156
+ }
3157
+ const solChain = config.chains.find((c) => c.startsWith("solana:")) ?? "solana:mainnet";
3158
+ return signSolanaTransaction(tee, solChain, tx);
3159
+ },
3160
+ async disconnect() {
3161
+ session.end();
3162
+ if (auth?.jwt) {
3163
+ await api.revoke(auth.jwt).catch(() => {
3164
+ });
3165
+ }
3166
+ auth = null;
3167
+ persisted = null;
3168
+ clearPersisted(PERSIST_KEY);
3169
+ events.emit("disconnected");
3170
+ },
3171
+ getAddresses() {
3172
+ return auth?.addresses ?? persisted?.addresses ?? null;
3173
+ },
3174
+ async rotateShares(opts) {
3175
+ if (!auth) throw new CallSequenceError("Not connected \u2014 call connect() first");
3176
+ const tee = session.requireActive();
3177
+ const result = await tee.rotateShares(auth.jwt);
3178
+ try {
3179
+ const file = await buildRecoveryShareFile(
3180
+ result.recoveryShare,
3181
+ opts.password,
3182
+ auth.addresses,
3183
+ result.shareSetVersion,
3184
+ opts.passwordHint
3185
+ );
3186
+ await api.markRecoveryShareSaved(auth.jwt);
3187
+ return file;
3188
+ } finally {
3189
+ zeroize(result.deviceShare);
3190
+ zeroize(result.recoveryShare);
3191
+ }
3192
+ },
3193
+ async exportPortableBackup(opts) {
3194
+ if (!auth) throw new CallSequenceError("Not connected \u2014 call connect() first");
3195
+ const tee = session.requireActive();
3196
+ const result = await tee.exportPortableBackup(auth.jwt);
3197
+ try {
3198
+ return await buildPortableBackup(
3199
+ result.deviceShare,
3200
+ result.recoveryShare,
3201
+ opts.password,
3202
+ auth.addresses,
3203
+ result.shareSetVersion,
3204
+ opts.passwordHint
3205
+ );
3206
+ } finally {
3207
+ zeroize(result.deviceShare);
3208
+ zeroize(result.recoveryShare);
3209
+ }
3210
+ },
3211
+ async hasRecoveryShare() {
3212
+ if (!auth) throw new CallSequenceError("Not connected \u2014 call connect() first");
3213
+ const status = await api.getRecoveryStatus(auth.jwt);
3214
+ return status.hasRecoveryShare;
3215
+ },
3216
+ async shouldPromptRecovery() {
3217
+ if (!auth) throw new CallSequenceError("Not connected \u2014 call connect() first");
3218
+ const status = await api.getRecoveryStatus(auth.jwt);
3219
+ if (status.hasRecoveryShare || status.nagged) return false;
3220
+ try {
3221
+ const checks = [];
3222
+ if (auth.addresses.evm) {
3223
+ const { JsonRpcProvider } = await import("ethers");
3224
+ for (const chain of config.chains.filter((c) => c.startsWith("evm:"))) {
3225
+ const rpcUrl = config.rpcUrls?.[chain] ?? DEFAULT_RPCS[chain];
3226
+ if (!rpcUrl) continue;
3227
+ checks.push(
3228
+ new JsonRpcProvider(rpcUrl).getBalance(auth.addresses.evm).then((b) => b > 0n).catch(() => false)
3229
+ );
3230
+ }
3231
+ }
3232
+ if (auth.addresses.solana && config.chains.some((c) => c.startsWith("solana:"))) {
3233
+ const { Connection, PublicKey } = await import("@solana/web3.js");
3234
+ const solChain = config.chains.find((c) => c.startsWith("solana:")) ?? "solana:mainnet";
3235
+ const cluster = solChain.slice("solana:".length);
3236
+ const endpoint = cluster === "devnet" ? "https://api.devnet.solana.com" : "https://api.mainnet-beta.solana.com";
3237
+ checks.push(
3238
+ new Connection(endpoint).getBalance(new PublicKey(auth.addresses.solana)).then((b) => b > 0).catch(() => false)
3239
+ );
3240
+ }
3241
+ const results = await Promise.all(checks);
3242
+ return results.some(Boolean);
3243
+ } catch {
3244
+ return false;
3245
+ }
3246
+ },
3247
+ async dismissRecoveryPrompt() {
3248
+ if (!auth) throw new CallSequenceError("Not connected \u2014 call connect() first");
3249
+ await api.dismissRecoveryNag(auth.jwt);
3250
+ },
3251
+ async hasBackup() {
3252
+ if (!auth) throw new CallSequenceError("Not connected \u2014 call connect() first");
3253
+ return api.hasRecoveryBackup(auth.jwt);
3254
+ },
3255
+ async deleteBackup() {
3256
+ if (!auth) throw new CallSequenceError("Not connected \u2014 call connect() first");
3257
+ await api.deleteRecoveryBackup(auth.jwt);
3258
+ },
3259
+ on: events.on.bind(events),
3260
+ off: events.off.bind(events)
3261
+ };
3262
+ return client;
3263
+ }
3264
+ function bufferToB64u(buf6) {
3265
+ return btoa(String.fromCharCode(...buf6)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
3266
+ }
3267
+ function b64uToBuffer(b64u) {
3268
+ const b644 = b64u.replace(/-/g, "+").replace(/_/g, "/");
3269
+ return Uint8Array.from(atob(b644), (c) => c.charCodeAt(0));
3270
+ }
3271
+ function toHex(bytes) {
3272
+ return "0x" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
3273
+ }
3274
+
3275
+ // src/social/index.ts
3276
+ init_errors();
3277
+
3278
+ // src/ui/WalletSelector.tsx
3279
+ import React2, { useEffect, useCallback, useState } from "react";
3280
+ import { createPortal } from "react-dom";
3281
+
3282
+ // src/ui/styles.ts
3283
+ var tokens = {
3284
+ // ── Gold palette ──────────────────────────────────────────────────────────
3285
+ gold: {
3286
+ 50: "#FFFDF0",
3287
+ 100: "#FFF8D6",
3288
+ 200: "#FFEF99",
3289
+ 300: "#FFE04D",
3290
+ 400: "#F5C800",
3291
+ // primary gold — buttons, accents
3292
+ 500: "#D4A800",
3293
+ // darker gold — hover states
3294
+ 600: "#A37F00",
3295
+ // deep gold — borders, active
3296
+ 700: "#7A5E00",
3297
+ 800: "#4D3B00",
3298
+ 900: "#261D00"
3299
+ },
3300
+ // ── Black / dark palette ──────────────────────────────────────────────────
3301
+ black: {
3302
+ 0: "#FFFFFF",
3303
+ 50: "#F9F9F7",
3304
+ 100: "#F0EFEB",
3305
+ 200: "#DDDCDA",
3306
+ 300: "#BDBBB8",
3307
+ 400: "#8A8884",
3308
+ 500: "#5C5A57",
3309
+ 600: "#3D3C39",
3310
+ 700: "#2A2927",
3311
+ 800: "#1A1917",
3312
+ 900: "#0D0D0B",
3313
+ 950: "#070706"
3314
+ },
3315
+ // ── Semantic tokens — light mode ──────────────────────────────────────────
3316
+ light: {
3317
+ bg: "#FFFFFF",
3318
+ bgOverlay: "rgba(0, 0, 0, 0.55)",
3319
+ surface: "#F9F9F7",
3320
+ surfaceHover: "#F0EFEB",
3321
+ border: "#DDDCDA",
3322
+ borderAccent: "#F5C800",
3323
+ text: "#0D0D0B",
3324
+ textSecondary: "#5C5A57",
3325
+ textMuted: "#8A8884",
3326
+ accent: "#F5C800",
3327
+ accentHover: "#D4A800",
3328
+ accentText: "#0D0D0B",
3329
+ badgeEvm: "#1A1917",
3330
+ badgeEvmText: "#F5C800",
3331
+ badgeSolana: "#F5C800",
3332
+ badgeSolanaText: "#0D0D0B",
3333
+ danger: "#CC3300"
3334
+ },
3335
+ // ── Semantic tokens — dark mode ───────────────────────────────────────────
3336
+ dark: {
3337
+ bg: "#0D0D0B",
3338
+ bgOverlay: "rgba(0, 0, 0, 0.75)",
3339
+ surface: "#1A1917",
3340
+ surfaceHover: "#2A2927",
3341
+ border: "#3D3C39",
3342
+ borderAccent: "#F5C800",
3343
+ text: "#F9F9F7",
3344
+ textSecondary: "#BDBBB8",
3345
+ textMuted: "#8A8884",
3346
+ accent: "#F5C800",
3347
+ accentHover: "#FFE04D",
3348
+ accentText: "#0D0D0B",
3349
+ badgeEvm: "#F5C800",
3350
+ badgeEvmText: "#0D0D0B",
3351
+ badgeSolana: "#1A1917",
3352
+ badgeSolanaText: "#F5C800",
3353
+ danger: "#FF5533"
3354
+ }
3355
+ };
3356
+ var FONT_SANS = "'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif";
3357
+ var FONT_MONO = "'JetBrains Mono', 'Fira Code', ui-monospace, monospace";
3358
+ var CSS_VARS = {
3359
+ light: `
3360
+ --dc-font-sans: ${FONT_SANS};
3361
+ --dc-font-mono: ${FONT_MONO};
3362
+ --dc-bg: ${tokens.light.bg};
3363
+ --dc-bg-overlay: ${tokens.light.bgOverlay};
3364
+ --dc-surface: ${tokens.light.surface};
3365
+ --dc-surface-hover: ${tokens.light.surfaceHover};
3366
+ --dc-border: ${tokens.light.border};
3367
+ --dc-border-accent: ${tokens.light.borderAccent};
3368
+ --dc-text: ${tokens.light.text};
3369
+ --dc-text-secondary: ${tokens.light.textSecondary};
3370
+ --dc-text-muted: ${tokens.light.textMuted};
3371
+ --dc-accent: ${tokens.light.accent};
3372
+ --dc-accent-hover: ${tokens.light.accentHover};
3373
+ --dc-accent-text: ${tokens.light.accentText};
3374
+ --dc-badge-evm: ${tokens.light.badgeEvm};
3375
+ --dc-badge-evm-text: ${tokens.light.badgeEvmText};
3376
+ --dc-badge-sol: ${tokens.light.badgeSolana};
3377
+ --dc-badge-sol-text: ${tokens.light.badgeSolanaText};
3378
+ --dc-danger: ${tokens.light.danger};
3379
+ `,
3380
+ dark: `
3381
+ --dc-font-sans: ${FONT_SANS};
3382
+ --dc-font-mono: ${FONT_MONO};
3383
+ --dc-bg: ${tokens.dark.bg};
3384
+ --dc-bg-overlay: ${tokens.dark.bgOverlay};
3385
+ --dc-surface: ${tokens.dark.surface};
3386
+ --dc-surface-hover: ${tokens.dark.surfaceHover};
3387
+ --dc-border: ${tokens.dark.border};
3388
+ --dc-border-accent: ${tokens.dark.borderAccent};
3389
+ --dc-text: ${tokens.dark.text};
3390
+ --dc-text-secondary: ${tokens.dark.textSecondary};
3391
+ --dc-text-muted: ${tokens.dark.textMuted};
3392
+ --dc-accent: ${tokens.dark.accent};
3393
+ --dc-accent-hover: ${tokens.dark.accentHover};
3394
+ --dc-accent-text: ${tokens.dark.accentText};
3395
+ --dc-badge-evm: ${tokens.dark.badgeEvm};
3396
+ --dc-badge-evm-text: ${tokens.dark.badgeEvmText};
3397
+ --dc-badge-sol: ${tokens.dark.badgeSolana};
3398
+ --dc-badge-sol-text: ${tokens.dark.badgeSolanaText};
3399
+ --dc-danger: ${tokens.dark.danger};
3400
+ `
3401
+ };
3402
+
3403
+ // src/ui/icons.tsx
3404
+ import React from "react";
3405
+ function WalletIcon({ src, name, size = 36 }) {
3406
+ if (src) {
3407
+ return /* @__PURE__ */ React.createElement(
3408
+ "img",
3409
+ {
3410
+ src,
3411
+ alt: name,
3412
+ width: size,
3413
+ height: size,
3414
+ style: { borderRadius: size * 0.25, objectFit: "cover", flexShrink: 0 }
3415
+ }
3416
+ );
3417
+ }
3418
+ const initial = name.charAt(0).toUpperCase();
3419
+ return /* @__PURE__ */ React.createElement(
3420
+ "div",
3421
+ {
3422
+ style: {
3423
+ width: size,
3424
+ height: size,
3425
+ borderRadius: size * 0.25,
3426
+ background: "linear-gradient(135deg, var(--dc-surface-hover), var(--dc-surface-active, var(--dc-surface-hover)))",
3427
+ border: "1px solid var(--dc-border)",
3428
+ display: "flex",
3429
+ alignItems: "center",
3430
+ justifyContent: "center",
3431
+ fontSize: size * 0.38,
1888
3432
  fontWeight: 800,
1889
3433
  color: "var(--dc-accent)",
1890
3434
  flexShrink: 0,
@@ -2083,17 +3627,33 @@ function ChainOption({ label, description, badges, onClick }) {
2083
3627
  /* @__PURE__ */ React2.createElement("span", { style: { fontSize: 11, color: "var(--dc-text-muted)", lineHeight: 1.4 } }, description)
2084
3628
  );
2085
3629
  }
3630
+ function hasSocialMode(mode) {
3631
+ return mode === "all" || mode === "social" || mode === "evm+social" || mode === "solana+social" || mode === "tron+social" || mode === "evm+tron+social";
3632
+ }
2086
3633
  function WalletSelector({
2087
3634
  provider,
2088
3635
  open,
2089
3636
  onClose,
2090
3637
  onConnect,
2091
3638
  theme = "auto",
2092
- networks = "all"
3639
+ networks = "all",
3640
+ mode = "wallets",
3641
+ socialAuthMethods = ["google", "email"],
3642
+ onGoogleSignIn,
3643
+ onEmailStart,
3644
+ onEmailVerify
2093
3645
  }) {
3646
+ const showSocial = hasSocialMode(mode);
3647
+ const showWallets = mode !== "social";
3648
+ const [view, setView] = useState(showWallets ? "wallets" : "social");
2094
3649
  const [search, setSearch] = useState("");
2095
3650
  const [filter, setFilter] = useState("all");
2096
3651
  const [pendingWallet, setPendingWallet] = useState(null);
3652
+ const [emailStep, setEmailStep] = useState("input");
3653
+ const [emailVal, setEmailVal] = useState("");
3654
+ const [codeVal, setCodeVal] = useState("");
3655
+ const [socialLoad, setSocialLoad] = useState(false);
3656
+ const [socialErr, setSocialErr] = useState("");
2097
3657
  useEffect(() => {
2098
3658
  if (!open) return;
2099
3659
  injectFont();
@@ -2109,12 +3669,55 @@ function WalletSelector({
2109
3669
  setSearch("");
2110
3670
  setFilter("all");
2111
3671
  setPendingWallet(null);
3672
+ setView(showWallets ? "wallets" : "social");
3673
+ setEmailStep("input");
3674
+ setEmailVal("");
3675
+ setCodeVal("");
3676
+ setSocialErr("");
2112
3677
  return;
2113
3678
  }
2114
3679
  return () => {
2115
3680
  document.getElementById("decane-tokens")?.remove();
2116
3681
  };
2117
- }, [open]);
3682
+ }, [open, showWallets]);
3683
+ async function handleGoogleClick() {
3684
+ if (!onGoogleSignIn) return;
3685
+ setSocialLoad(true);
3686
+ setSocialErr("");
3687
+ try {
3688
+ await onGoogleSignIn();
3689
+ } catch (e) {
3690
+ setSocialErr(e.message);
3691
+ setSocialLoad(false);
3692
+ }
3693
+ }
3694
+ async function handleEmailStart(e) {
3695
+ e.preventDefault();
3696
+ if (!onEmailStart) return;
3697
+ setSocialLoad(true);
3698
+ setSocialErr("");
3699
+ try {
3700
+ await onEmailStart(emailVal);
3701
+ setEmailStep("code");
3702
+ } catch (e2) {
3703
+ setSocialErr(e2.message);
3704
+ } finally {
3705
+ setSocialLoad(false);
3706
+ }
3707
+ }
3708
+ async function handleEmailVerify(e) {
3709
+ e.preventDefault();
3710
+ if (!onEmailVerify) return;
3711
+ setSocialLoad(true);
3712
+ setSocialErr("");
3713
+ try {
3714
+ await onEmailVerify(emailVal, codeVal);
3715
+ } catch (e2) {
3716
+ setSocialErr(e2.message);
3717
+ } finally {
3718
+ setSocialLoad(false);
3719
+ }
3720
+ }
2118
3721
  const handleKey = useCallback(
2119
3722
  (e) => {
2120
3723
  if (e.key === "Escape") {
@@ -2327,8 +3930,8 @@ function WalletSelector({
2327
3930
  }
2328
3931
  )))
2329
3932
  ) : (
2330
- /* ── Wallet list step ────────────────────────────────────────── */
2331
- /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("div", { style: { padding: "18px 20px 0" } }, /* @__PURE__ */ React2.createElement("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between", marginBottom: 14 } }, /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("h2", { style: { margin: 0, fontSize: 17, fontWeight: 800, color: "var(--dc-text)", letterSpacing: "-0.02em", lineHeight: 1.2 } }, "Connect wallet"), networkWallets.length > 0 && /* @__PURE__ */ React2.createElement("p", { style: { margin: "3px 0 0", fontSize: 12, color: "var(--dc-text-muted)" } }, networkWallets.length, " wallet", networkWallets.length !== 1 ? "s" : "", " detected")), /* @__PURE__ */ React2.createElement(
3933
+ /* ── Wallet list / Social sign-in ────────────────────────────── */
3934
+ /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("div", { style: { padding: "18px 20px 0" } }, /* @__PURE__ */ React2.createElement("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between", marginBottom: 14 } }, /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("h2", { style: { margin: 0, fontSize: 17, fontWeight: 800, color: "var(--dc-text)", letterSpacing: "-0.02em", lineHeight: 1.2 } }, view === "social" ? "Sign in" : "Connect wallet"), view === "wallets" && networkWallets.length > 0 && /* @__PURE__ */ React2.createElement("p", { style: { margin: "3px 0 0", fontSize: 12, color: "var(--dc-text-muted)" } }, networkWallets.length, " wallet", networkWallets.length !== 1 ? "s" : "", " detected")), /* @__PURE__ */ React2.createElement(
2332
3935
  "button",
2333
3936
  {
2334
3937
  onClick: onClose,
@@ -2359,7 +3962,28 @@ function WalletSelector({
2359
3962
  }
2360
3963
  },
2361
3964
  /* @__PURE__ */ React2.createElement("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none" }, /* @__PURE__ */ React2.createElement("path", { d: "M1.5 1.5L10.5 10.5M10.5 1.5L1.5 10.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }))
2362
- )), /* @__PURE__ */ React2.createElement("div", { style: { position: "relative", marginBottom: 10 } }, /* @__PURE__ */ React2.createElement(
3965
+ )), showWallets && showSocial && /* @__PURE__ */ React2.createElement("div", { style: { display: "flex", gap: 4, marginBottom: 14, background: "var(--dc-surface)", borderRadius: 10, padding: 3 } }, ["wallets", "social"].map((v) => /* @__PURE__ */ React2.createElement(
3966
+ "button",
3967
+ {
3968
+ key: v,
3969
+ onClick: () => setView(v),
3970
+ style: {
3971
+ flex: 1,
3972
+ padding: "7px 0",
3973
+ borderRadius: 8,
3974
+ border: "none",
3975
+ background: view === v ? "var(--dc-bg)" : "transparent",
3976
+ color: view === v ? "var(--dc-text)" : "var(--dc-text-muted)",
3977
+ fontSize: 13,
3978
+ fontWeight: view === v ? 700 : 500,
3979
+ cursor: "pointer",
3980
+ fontFamily: "inherit",
3981
+ boxShadow: view === v ? "0 1px 4px rgba(0,0,0,0.2)" : "none",
3982
+ transition: "all 0.15s"
3983
+ }
3984
+ },
3985
+ v === "wallets" ? "Wallet" : "Sign in"
3986
+ ))), view === "wallets" && networks === "all" && /* @__PURE__ */ React2.createElement("div", { style: { display: "flex", gap: 4, marginBottom: 10 } }, /* @__PURE__ */ React2.createElement(FilterTab, { label: "All", active: filter === "all", onClick: () => setFilter("all") }), /* @__PURE__ */ React2.createElement(FilterTab, { label: "EVM", active: filter === "evm", onClick: () => setFilter("evm") }), /* @__PURE__ */ React2.createElement(FilterTab, { label: "Solana", active: filter === "solana", onClick: () => setFilter("solana") }), provider.tronWallets && provider.tronWallets.length > 0 && /* @__PURE__ */ React2.createElement(FilterTab, { label: "Tron", active: filter === "tron", onClick: () => setFilter("tron") }), provider.bitcoinWallets && provider.bitcoinWallets.length > 0 && /* @__PURE__ */ React2.createElement(FilterTab, { label: "Bitcoin", active: filter === "bitcoin", onClick: () => setFilter("bitcoin") })), view === "wallets" && /* @__PURE__ */ React2.createElement("div", { style: { position: "relative", marginBottom: 10 } }, /* @__PURE__ */ React2.createElement(
2363
3987
  "svg",
2364
3988
  {
2365
3989
  width: "14",
@@ -2397,52 +4021,628 @@ function WalletSelector({
2397
4021
  e.currentTarget.style.borderColor = "var(--dc-border)";
2398
4022
  }
2399
4023
  }
2400
- )), networks === "all" && /* @__PURE__ */ React2.createElement("div", { style: { display: "flex", gap: 4, marginBottom: 10 } }, /* @__PURE__ */ React2.createElement(FilterTab, { label: "All", active: filter === "all", onClick: () => setFilter("all") }), /* @__PURE__ */ React2.createElement(FilterTab, { label: "EVM", active: filter === "evm", onClick: () => setFilter("evm") }), /* @__PURE__ */ React2.createElement(FilterTab, { label: "Solana", active: filter === "solana", onClick: () => setFilter("solana") }), provider.tronWallets && provider.tronWallets.length > 0 && /* @__PURE__ */ React2.createElement(FilterTab, { label: "Tron", active: filter === "tron", onClick: () => setFilter("tron") }), provider.bitcoinWallets && provider.bitcoinWallets.length > 0 && /* @__PURE__ */ React2.createElement(FilterTab, { label: "Bitcoin", active: filter === "bitcoin", onClick: () => setFilter("bitcoin") }))), /* @__PURE__ */ React2.createElement("div", { style: { overflowY: "auto", flex: 1, padding: "0 12px 20px" } }, filtered.length > 0 ? /* @__PURE__ */ React2.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 2 } }, filtered.map((w) => /* @__PURE__ */ React2.createElement(WalletRow, { key: w.id, wallet: w, onClick: () => handleWalletClick(w) }))) : /* @__PURE__ */ React2.createElement("div", { style: { textAlign: "center", padding: "32px 16px", color: "var(--dc-text-muted)", fontSize: 13 } }, q ? `No wallets matching "${q}"` : "No wallets detected")), /* @__PURE__ */ React2.createElement("div", { style: { padding: "10px 20px 14px", borderTop: "1px solid var(--dc-border)", textAlign: "center", fontSize: 11, color: "var(--dc-text-muted)" } }, "Don't see your wallet? Make sure it's installed and unlocked"))
4024
+ ))), view === "social" && /* @__PURE__ */ React2.createElement("div", { style: { padding: "4px 20px 24px", display: "flex", flexDirection: "column", gap: 10 } }, socialErr && /* @__PURE__ */ React2.createElement("div", { style: { background: "rgba(204,51,0,0.1)", border: "1px solid var(--dc-danger)", borderRadius: 8, padding: "9px 12px", fontSize: 12, color: "var(--dc-danger)" } }, socialErr), socialAuthMethods.includes("google") && /* @__PURE__ */ React2.createElement(
4025
+ "button",
4026
+ {
4027
+ onClick: handleGoogleClick,
4028
+ disabled: socialLoad,
4029
+ style: {
4030
+ display: "flex",
4031
+ alignItems: "center",
4032
+ justifyContent: "center",
4033
+ gap: 10,
4034
+ width: "100%",
4035
+ padding: "11px 16px",
4036
+ background: "var(--dc-surface)",
4037
+ border: "1px solid var(--dc-border)",
4038
+ borderRadius: 10,
4039
+ cursor: socialLoad ? "not-allowed" : "pointer",
4040
+ fontSize: 14,
4041
+ fontWeight: 600,
4042
+ color: "var(--dc-text)",
4043
+ fontFamily: "inherit",
4044
+ transition: "border-color 0.12s, background 0.12s",
4045
+ opacity: socialLoad ? 0.6 : 1
4046
+ },
4047
+ onMouseEnter: (e) => {
4048
+ if (!socialLoad) e.currentTarget.style.borderColor = "var(--dc-border-accent)";
4049
+ },
4050
+ onMouseLeave: (e) => {
4051
+ e.currentTarget.style.borderColor = "var(--dc-border)";
4052
+ }
4053
+ },
4054
+ /* @__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" })),
4055
+ socialLoad ? "Redirecting\u2026" : "Continue with Google"
4056
+ ), 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)" } })), socialAuthMethods.includes("email") && (emailStep === "input" ? /* @__PURE__ */ React2.createElement("form", { onSubmit: handleEmailStart, style: { display: "flex", flexDirection: "column", gap: 8 } }, /* @__PURE__ */ React2.createElement(
4057
+ "input",
4058
+ {
4059
+ type: "email",
4060
+ placeholder: "your@email.com",
4061
+ value: emailVal,
4062
+ onChange: (e) => setEmailVal(e.target.value),
4063
+ required: true,
4064
+ style: {
4065
+ width: "100%",
4066
+ padding: "10px 12px",
4067
+ background: "var(--dc-surface)",
4068
+ border: "1px solid var(--dc-border)",
4069
+ borderRadius: 10,
4070
+ color: "var(--dc-text)",
4071
+ fontSize: 13,
4072
+ fontFamily: "inherit",
4073
+ outline: "none",
4074
+ boxSizing: "border-box"
4075
+ },
4076
+ onFocus: (e) => {
4077
+ e.currentTarget.style.borderColor = "var(--dc-border-accent)";
4078
+ },
4079
+ onBlur: (e) => {
4080
+ e.currentTarget.style.borderColor = "var(--dc-border)";
4081
+ }
4082
+ }
4083
+ ), /* @__PURE__ */ React2.createElement(
4084
+ "button",
4085
+ {
4086
+ type: "submit",
4087
+ disabled: socialLoad || !emailVal,
4088
+ style: {
4089
+ width: "100%",
4090
+ padding: "10px 16px",
4091
+ background: "var(--dc-accent)",
4092
+ color: "var(--dc-accent-text)",
4093
+ border: "none",
4094
+ borderRadius: 10,
4095
+ fontSize: 14,
4096
+ fontWeight: 700,
4097
+ cursor: socialLoad ? "not-allowed" : "pointer",
4098
+ fontFamily: "inherit",
4099
+ opacity: socialLoad || !emailVal ? 0.6 : 1
4100
+ }
4101
+ },
4102
+ socialLoad ? "Sending\u2026" : "Send code"
4103
+ )) : /* @__PURE__ */ React2.createElement("form", { onSubmit: handleEmailVerify, style: { display: "flex", flexDirection: "column", gap: 8 } }, /* @__PURE__ */ React2.createElement("p", { style: { margin: 0, fontSize: 12, color: "var(--dc-text-muted)", textAlign: "center" } }, "Code sent to ", /* @__PURE__ */ React2.createElement("strong", { style: { color: "var(--dc-text)" } }, emailVal)), /* @__PURE__ */ React2.createElement(
4104
+ "input",
4105
+ {
4106
+ type: "text",
4107
+ placeholder: "6-digit code",
4108
+ value: codeVal,
4109
+ onChange: (e) => setCodeVal(e.target.value.replace(/\D/g, "").slice(0, 6)),
4110
+ autoFocus: true,
4111
+ required: true,
4112
+ style: {
4113
+ width: "100%",
4114
+ padding: "10px 12px",
4115
+ background: "var(--dc-surface)",
4116
+ border: "1px solid var(--dc-border)",
4117
+ borderRadius: 10,
4118
+ color: "var(--dc-text)",
4119
+ fontSize: 20,
4120
+ fontFamily: "monospace",
4121
+ letterSpacing: 8,
4122
+ textAlign: "center",
4123
+ outline: "none",
4124
+ boxSizing: "border-box"
4125
+ },
4126
+ onFocus: (e) => {
4127
+ e.currentTarget.style.borderColor = "var(--dc-border-accent)";
4128
+ },
4129
+ onBlur: (e) => {
4130
+ e.currentTarget.style.borderColor = "var(--dc-border)";
4131
+ }
4132
+ }
4133
+ ), /* @__PURE__ */ React2.createElement("div", { style: { display: "flex", gap: 8 } }, /* @__PURE__ */ React2.createElement(
4134
+ "button",
4135
+ {
4136
+ type: "button",
4137
+ onClick: () => {
4138
+ setEmailStep("input");
4139
+ setCodeVal("");
4140
+ setSocialErr("");
4141
+ },
4142
+ style: {
4143
+ flex: 1,
4144
+ padding: "9px 0",
4145
+ background: "transparent",
4146
+ border: "1px solid var(--dc-border)",
4147
+ borderRadius: 10,
4148
+ color: "var(--dc-text-muted)",
4149
+ fontSize: 13,
4150
+ cursor: "pointer",
4151
+ fontFamily: "inherit"
4152
+ }
4153
+ },
4154
+ "Back"
4155
+ ), /* @__PURE__ */ React2.createElement(
4156
+ "button",
4157
+ {
4158
+ type: "submit",
4159
+ disabled: socialLoad || codeVal.length < 6,
4160
+ style: {
4161
+ flex: 2,
4162
+ padding: "9px 0",
4163
+ background: "var(--dc-accent)",
4164
+ color: "var(--dc-accent-text)",
4165
+ border: "none",
4166
+ borderRadius: 10,
4167
+ fontSize: 13,
4168
+ fontWeight: 700,
4169
+ cursor: socialLoad || codeVal.length < 6 ? "not-allowed" : "pointer",
4170
+ fontFamily: "inherit",
4171
+ opacity: socialLoad || codeVal.length < 6 ? 0.6 : 1
4172
+ }
4173
+ },
4174
+ socialLoad ? "Verifying\u2026" : "Verify"
4175
+ ))))), view === "wallets" && /* @__PURE__ */ React2.createElement("div", { style: { overflowY: "auto", flex: 1, padding: "0 12px 20px" } }, filtered.length > 0 ? /* @__PURE__ */ React2.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 2 } }, filtered.map((w) => /* @__PURE__ */ React2.createElement(WalletRow, { key: w.id, wallet: w, onClick: () => handleWalletClick(w) }))) : /* @__PURE__ */ React2.createElement("div", { style: { textAlign: "center", padding: "32px 16px", color: "var(--dc-text-muted)", fontSize: 13 } }, q ? `No wallets matching "${q}"` : "No wallets detected")), view === "wallets" && /* @__PURE__ */ React2.createElement("div", { style: { padding: "10px 20px 14px", borderTop: "1px solid var(--dc-border)", textAlign: "center", fontSize: 11, color: "var(--dc-text-muted)" } }, "Don't see your wallet? Make sure it's installed and unlocked"))
2401
4176
  )
2402
4177
  )
2403
4178
  );
2404
4179
  return createPortal(modal, document.body);
2405
4180
  }
2406
4181
 
4182
+ // src/ui/SocialWalletModal.tsx
4183
+ import React3, { useCallback as useCallback2, useEffect as useEffect2, useState as useState2 } from "react";
4184
+ import { createPortal as createPortal2 } from "react-dom";
4185
+ function injectTokens2(theme) {
4186
+ const resolved = theme === "auto" ? window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : theme;
4187
+ let el = document.getElementById("decane-tokens");
4188
+ if (!el) {
4189
+ el = document.createElement("style");
4190
+ el.id = "decane-tokens";
4191
+ document.head.appendChild(el);
4192
+ }
4193
+ el.textContent = `:root { ${CSS_VARS[resolved]} }`;
4194
+ }
4195
+ function countdown(expiresAt) {
4196
+ const secs = Math.max(0, Math.round((expiresAt - Date.now()) / 1e3));
4197
+ return `${Math.floor(secs / 60)}m ${String(secs % 60).padStart(2, "0")}s`;
4198
+ }
4199
+ function CopyButton({ value }) {
4200
+ const [copied, setCopied] = useState2(false);
4201
+ async function copy() {
4202
+ await navigator.clipboard.writeText(value).catch(() => {
4203
+ });
4204
+ setCopied(true);
4205
+ setTimeout(() => setCopied(false), 1500);
4206
+ }
4207
+ return /* @__PURE__ */ React3.createElement(
4208
+ "button",
4209
+ {
4210
+ onClick: copy,
4211
+ style: {
4212
+ background: "none",
4213
+ border: "none",
4214
+ cursor: "pointer",
4215
+ color: copied ? "var(--dc-accent)" : "var(--dc-text-muted)",
4216
+ fontSize: 11,
4217
+ fontFamily: "inherit",
4218
+ padding: "2px 6px",
4219
+ borderRadius: 5,
4220
+ transition: "color 0.15s"
4221
+ }
4222
+ },
4223
+ copied ? "\u2713" : "copy"
4224
+ );
4225
+ }
4226
+ function AddressRow({ label, address }) {
4227
+ const short = `${address.slice(0, 8)}\u2026${address.slice(-6)}`;
4228
+ return /* @__PURE__ */ React3.createElement("div", { style: {
4229
+ background: "var(--dc-surface)",
4230
+ border: "1px solid var(--dc-border)",
4231
+ borderRadius: 10,
4232
+ padding: "10px 14px",
4233
+ marginBottom: 8
4234
+ } }, /* @__PURE__ */ React3.createElement("div", { style: { fontSize: 10, fontWeight: 700, color: "var(--dc-text-muted)", letterSpacing: "0.06em", textTransform: "uppercase", marginBottom: 5 } }, label), /* @__PURE__ */ React3.createElement("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8 } }, /* @__PURE__ */ React3.createElement("span", { style: { fontSize: 13, fontFamily: "monospace", color: "var(--dc-text)", letterSpacing: "0.02em" } }, short), /* @__PURE__ */ React3.createElement(CopyButton, { value: address })));
4235
+ }
4236
+ function SocialWalletModal({
4237
+ open,
4238
+ onClose,
4239
+ theme = "auto",
4240
+ socialAuth,
4241
+ onUnlock,
4242
+ onDisconnect
4243
+ }) {
4244
+ const [timer, setTimer] = useState2("");
4245
+ const [unlocking, setUnlocking] = useState2(false);
4246
+ const [disconnecting, setDisconnecting] = useState2(false);
4247
+ useEffect2(() => {
4248
+ if (!open) return;
4249
+ injectTokens2(theme);
4250
+ if (theme !== "auto") return;
4251
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
4252
+ const h = () => injectTokens2("auto");
4253
+ mq.addEventListener("change", h);
4254
+ return () => mq.removeEventListener("change", h);
4255
+ }, [open, theme]);
4256
+ useEffect2(() => {
4257
+ if (!socialAuth.sessionExpiresAt) {
4258
+ setTimer("");
4259
+ return;
4260
+ }
4261
+ setTimer(countdown(socialAuth.sessionExpiresAt));
4262
+ const id = setInterval(() => setTimer(countdown(socialAuth.sessionExpiresAt)), 1e3);
4263
+ return () => clearInterval(id);
4264
+ }, [socialAuth.sessionExpiresAt]);
4265
+ const handleKey = useCallback2((e) => {
4266
+ if (e.key === "Escape") onClose();
4267
+ }, [onClose]);
4268
+ useEffect2(() => {
4269
+ if (!open) return;
4270
+ document.addEventListener("keydown", handleKey);
4271
+ return () => document.removeEventListener("keydown", handleKey);
4272
+ }, [open, handleKey]);
4273
+ if (!open) return null;
4274
+ async function handleUnlock() {
4275
+ setUnlocking(true);
4276
+ try {
4277
+ await onUnlock();
4278
+ } finally {
4279
+ setUnlocking(false);
4280
+ }
4281
+ }
4282
+ async function handleDisconnect() {
4283
+ setDisconnecting(true);
4284
+ try {
4285
+ await onDisconnect();
4286
+ onClose();
4287
+ } finally {
4288
+ setDisconnecting(false);
4289
+ }
4290
+ }
4291
+ const modal = /* @__PURE__ */ React3.createElement(
4292
+ "div",
4293
+ {
4294
+ role: "dialog",
4295
+ "aria-modal": "true",
4296
+ "aria-label": "Social wallet",
4297
+ style: {
4298
+ position: "fixed",
4299
+ inset: 0,
4300
+ zIndex: 9999,
4301
+ display: "flex",
4302
+ alignItems: "center",
4303
+ justifyContent: "center",
4304
+ padding: 16,
4305
+ fontFamily: "var(--dc-font-sans, system-ui, sans-serif)"
4306
+ }
4307
+ },
4308
+ /* @__PURE__ */ React3.createElement(
4309
+ "div",
4310
+ {
4311
+ onClick: onClose,
4312
+ style: {
4313
+ position: "absolute",
4314
+ inset: 0,
4315
+ background: "var(--dc-bg-overlay)",
4316
+ backdropFilter: "blur(8px)",
4317
+ WebkitBackdropFilter: "blur(8px)"
4318
+ }
4319
+ }
4320
+ ),
4321
+ /* @__PURE__ */ React3.createElement("div", { style: {
4322
+ position: "relative",
4323
+ background: "var(--dc-bg)",
4324
+ border: "1px solid var(--dc-border)",
4325
+ borderRadius: 20,
4326
+ width: "100%",
4327
+ maxWidth: 380,
4328
+ boxShadow: "0 24px 64px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.04)",
4329
+ overflow: "hidden"
4330
+ } }, /* @__PURE__ */ React3.createElement("div", { style: {
4331
+ height: 2,
4332
+ background: "linear-gradient(90deg, transparent, var(--dc-accent) 30%, var(--dc-accent) 70%, transparent)"
4333
+ } }), /* @__PURE__ */ React3.createElement("div", { style: { padding: "18px 20px 16px", display: "flex", alignItems: "center", gap: 10 } }, /* @__PURE__ */ React3.createElement("div", { style: {
4334
+ width: 34,
4335
+ height: 34,
4336
+ borderRadius: 10,
4337
+ background: "var(--dc-surface)",
4338
+ border: "1px solid var(--dc-border)",
4339
+ display: "flex",
4340
+ alignItems: "center",
4341
+ justifyContent: "center",
4342
+ flexShrink: 0
4343
+ } }, /* @__PURE__ */ React3.createElement("svg", { width: "16", height: "18", viewBox: "0 0 16 18", fill: "none" }, /* @__PURE__ */ React3.createElement(
4344
+ "path",
4345
+ {
4346
+ d: "M8 1L1.5 3.5V8.5C1.5 12.09 4.35 15.4 8 16.5C11.65 15.4 14.5 12.09 14.5 8.5V3.5L8 1Z",
4347
+ stroke: "var(--dc-accent)",
4348
+ strokeWidth: "1.4",
4349
+ strokeLinejoin: "round"
4350
+ }
4351
+ ), /* @__PURE__ */ React3.createElement(
4352
+ "path",
4353
+ {
4354
+ d: "M5.5 9L7 10.5L10.5 7",
4355
+ stroke: "var(--dc-accent)",
4356
+ strokeWidth: "1.4",
4357
+ strokeLinecap: "round",
4358
+ strokeLinejoin: "round"
4359
+ }
4360
+ ))), /* @__PURE__ */ React3.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React3.createElement("div", { style: { fontSize: 15, fontWeight: 800, color: "var(--dc-text)", letterSpacing: "-0.02em" } }, "Social Wallet"), /* @__PURE__ */ React3.createElement("div", { style: { fontSize: 11, color: "var(--dc-text-muted)", marginTop: 1 } }, "Decane non-custodial \xB7 key split between device & server")), /* @__PURE__ */ React3.createElement(
4361
+ "button",
4362
+ {
4363
+ onClick: onClose,
4364
+ style: {
4365
+ background: "var(--dc-surface)",
4366
+ border: "1px solid var(--dc-border)",
4367
+ borderRadius: 8,
4368
+ width: 28,
4369
+ height: 28,
4370
+ cursor: "pointer",
4371
+ display: "flex",
4372
+ alignItems: "center",
4373
+ justifyContent: "center",
4374
+ color: "var(--dc-text-muted)",
4375
+ flexShrink: 0,
4376
+ padding: 0
4377
+ }
4378
+ },
4379
+ /* @__PURE__ */ React3.createElement("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none" }, /* @__PURE__ */ React3.createElement("path", { d: "M1.5 1.5L10.5 10.5M10.5 1.5L1.5 10.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }))
4380
+ )), /* @__PURE__ */ React3.createElement("div", { style: {
4381
+ margin: "0 16px 14px",
4382
+ padding: "8px 14px",
4383
+ background: socialAuth.isUnlocked ? "rgba(18,183,106,0.08)" : "rgba(245,200,0,0.08)",
4384
+ border: `1px solid ${socialAuth.isUnlocked ? "rgba(18,183,106,0.2)" : "rgba(245,200,0,0.2)"}`,
4385
+ borderRadius: 10,
4386
+ display: "flex",
4387
+ alignItems: "center",
4388
+ justifyContent: "space-between"
4389
+ } }, /* @__PURE__ */ React3.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8 } }, /* @__PURE__ */ React3.createElement("span", { style: {
4390
+ width: 7,
4391
+ height: 7,
4392
+ borderRadius: "50%",
4393
+ flexShrink: 0,
4394
+ background: socialAuth.isUnlocked ? "#12b76a" : "var(--dc-accent)",
4395
+ boxShadow: `0 0 6px ${socialAuth.isUnlocked ? "#12b76a" : "var(--dc-accent)"}`
4396
+ } }), /* @__PURE__ */ React3.createElement("span", { style: {
4397
+ fontSize: 12,
4398
+ fontWeight: 700,
4399
+ color: socialAuth.isUnlocked ? "#12b76a" : "var(--dc-accent)"
4400
+ } }, socialAuth.isUnlocked ? "Unlocked" : "Locked")), socialAuth.isUnlocked && timer && /* @__PURE__ */ React3.createElement("span", { style: { fontSize: 11, color: "var(--dc-text-muted)", fontFamily: "monospace" } }, "expires ", timer)), /* @__PURE__ */ React3.createElement("div", { style: { padding: "0 16px 16px" } }, socialAuth.addresses?.evm && /* @__PURE__ */ React3.createElement(AddressRow, { label: "EVM (Ethereum, Base, \u2026)", address: socialAuth.addresses.evm }), socialAuth.addresses?.solana && /* @__PURE__ */ React3.createElement(AddressRow, { label: "Solana", address: socialAuth.addresses.solana }), !socialAuth.addresses?.evm && !socialAuth.addresses?.solana && /* @__PURE__ */ React3.createElement("div", { style: { textAlign: "center", padding: "16px 0", color: "var(--dc-text-muted)", fontSize: 13 } }, "Unlock wallet to reveal addresses")), /* @__PURE__ */ React3.createElement("div", { style: {
4401
+ padding: "14px 16px",
4402
+ borderTop: "1px solid var(--dc-border)",
4403
+ display: "flex",
4404
+ gap: 8
4405
+ } }, !socialAuth.isUnlocked && /* @__PURE__ */ React3.createElement(
4406
+ "button",
4407
+ {
4408
+ onClick: handleUnlock,
4409
+ disabled: unlocking,
4410
+ style: {
4411
+ flex: 1,
4412
+ padding: "10px 0",
4413
+ background: "var(--dc-accent)",
4414
+ color: "var(--dc-accent-text)",
4415
+ border: "none",
4416
+ borderRadius: 10,
4417
+ fontSize: 13,
4418
+ fontWeight: 700,
4419
+ cursor: unlocking ? "not-allowed" : "pointer",
4420
+ fontFamily: "inherit",
4421
+ opacity: unlocking ? 0.7 : 1
4422
+ }
4423
+ },
4424
+ unlocking ? "Unlocking\u2026" : "\u{1F513} Unlock wallet"
4425
+ ), /* @__PURE__ */ React3.createElement(
4426
+ "button",
4427
+ {
4428
+ onClick: handleDisconnect,
4429
+ disabled: disconnecting,
4430
+ style: {
4431
+ flex: socialAuth.isUnlocked ? 1 : 0,
4432
+ padding: "10px 16px",
4433
+ background: "transparent",
4434
+ border: "1px solid var(--dc-border)",
4435
+ borderRadius: 10,
4436
+ color: "var(--dc-text-muted)",
4437
+ fontSize: 13,
4438
+ cursor: disconnecting ? "not-allowed" : "pointer",
4439
+ fontFamily: "inherit",
4440
+ opacity: disconnecting ? 0.7 : 1,
4441
+ whiteSpace: "nowrap"
4442
+ }
4443
+ },
4444
+ disconnecting ? "Signing out\u2026" : "Sign out"
4445
+ )))
4446
+ );
4447
+ return createPortal2(modal, document.body);
4448
+ }
4449
+
2407
4450
  // src/ui/context.tsx
2408
- import React3, {
4451
+ import React4, {
2409
4452
  createContext,
2410
- useCallback as useCallback2,
4453
+ useCallback as useCallback3,
2411
4454
  useContext,
4455
+ useEffect as useEffect3,
2412
4456
  useRef,
2413
- useState as useState2
4457
+ useState as useState3
2414
4458
  } from "react";
4459
+ var DEFAULT_SOCIAL = {
4460
+ addresses: null,
4461
+ isNewUser: false,
4462
+ isUnlocked: false,
4463
+ sessionExpiresAt: null
4464
+ };
2415
4465
  var DecaneContext = createContext(null);
4466
+ function hasSocial(mode) {
4467
+ return mode === "all" || mode === "social" || mode === "evm+social" || mode === "solana+social" || mode === "tron+social" || mode === "evm+tron+social";
4468
+ }
4469
+ function deriveNetworks(mode, explicit) {
4470
+ if (explicit) return explicit;
4471
+ if (mode === "evm" || mode === "evm+social") return "evm";
4472
+ if (mode === "solana" || mode === "solana+social") return "solana";
4473
+ if (mode === "tron" || mode === "tron+social") return "tron";
4474
+ return "all";
4475
+ }
2416
4476
  function DecaneKit({ config, children }) {
2417
- const ref = useRef(null);
2418
- if (!ref.current) {
2419
- ref.current = createDecane(config);
2420
- }
2421
- const [selectorOpen, setSelectorOpen] = useState2(false);
2422
- const [theme, setTheme] = useState2(config?.theme ?? "auto");
2423
- const openSelector = useCallback2(() => setSelectorOpen(true), []);
2424
- const closeSelector = useCallback2(() => setSelectorOpen(false), []);
2425
- const handleConnect = useCallback2(
4477
+ const mode = config?.mode ?? "wallets";
4478
+ const walletRef = useRef(null);
4479
+ if (!walletRef.current) walletRef.current = createDecane(config);
4480
+ const socialRef = useRef(null);
4481
+ const socialInitialized = useRef(false);
4482
+ const [selectorOpen, setSelectorOpen] = useState3(false);
4483
+ const [socialModalOpen, setSocialModalOpen] = useState3(false);
4484
+ const [theme, setTheme] = useState3(config?.theme ?? "auto");
4485
+ const [socialAuth, setSocialAuth] = useState3(DEFAULT_SOCIAL);
4486
+ const openSelector = useCallback3(() => setSelectorOpen(true), []);
4487
+ const closeSelector = useCallback3(() => setSelectorOpen(false), []);
4488
+ const openSocialModal = useCallback3(() => setSocialModalOpen(true), []);
4489
+ const closeSocialModal = useCallback3(() => setSocialModalOpen(false), []);
4490
+ useEffect3(() => {
4491
+ if (!socialAuth.isUnlocked) return;
4492
+ const id = setInterval(() => {
4493
+ const sdk = socialRef.current;
4494
+ if (!sdk) return;
4495
+ setSocialAuth((p) => ({
4496
+ ...p,
4497
+ isUnlocked: sdk.isUnlocked(),
4498
+ sessionExpiresAt: sdk.sessionExpiresAt()
4499
+ }));
4500
+ }, 1e4);
4501
+ return () => clearInterval(id);
4502
+ }, [socialAuth.isUnlocked]);
4503
+ useEffect3(() => {
4504
+ if (!hasSocial(mode) || !config?.social || socialInitialized.current) return;
4505
+ socialInitialized.current = true;
4506
+ const { apiKey, authMethods, promptPin, apiBaseUrl, chains, rpcUrls } = config.social;
4507
+ createDecaneConnect({
4508
+ apiKey,
4509
+ apiBaseUrl,
4510
+ authMethods: authMethods ?? ["google", "email"],
4511
+ chains: chains ?? ["evm:1", "solana:mainnet"],
4512
+ rpcUrls,
4513
+ promptPin
4514
+ }).then((sdk) => {
4515
+ socialRef.current = sdk;
4516
+ sdk.on(
4517
+ "session-expired",
4518
+ () => setSocialAuth((p) => ({ ...p, isUnlocked: false, sessionExpiresAt: null }))
4519
+ );
4520
+ sdk.on("disconnected", () => setSocialAuth(DEFAULT_SOCIAL));
4521
+ const saved = sdk.getAddresses();
4522
+ if (saved) {
4523
+ setSocialAuth((p) => ({ ...p, addresses: saved, isUnlocked: false }));
4524
+ }
4525
+ const params = new URLSearchParams(window.location.search);
4526
+ if (params.has("decane_jwt") || params.has("decane_error")) {
4527
+ sdk.connectWithGoogle().then(applyResult).catch(console.error);
4528
+ }
4529
+ }).catch(console.error);
4530
+ }, []);
4531
+ function applyResult(result) {
4532
+ const sdk = socialRef.current;
4533
+ setSocialAuth({
4534
+ addresses: result.addresses,
4535
+ isNewUser: result.isNewUser,
4536
+ isUnlocked: sdk?.isUnlocked() ?? false,
4537
+ sessionExpiresAt: sdk?.sessionExpiresAt() ?? null
4538
+ });
4539
+ }
4540
+ const connectWithGoogle = useCallback3(async () => {
4541
+ const sdk = socialRef.current;
4542
+ if (!sdk) throw new Error("Social sign-in not configured");
4543
+ await sdk.connectWithGoogle();
4544
+ }, []);
4545
+ const connectWithEmail = useCallback3(async (email) => {
4546
+ const sdk = socialRef.current;
4547
+ if (!sdk) throw new Error("Social sign-in not configured");
4548
+ return sdk.connectWithEmail(email);
4549
+ }, []);
4550
+ const verifyEmailCode2 = useCallback3(async (email, code) => {
4551
+ const sdk = socialRef.current;
4552
+ if (!sdk) throw new Error("Social sign-in not configured");
4553
+ const result = await sdk.verifyEmailCode(email, code);
4554
+ applyResult(result);
4555
+ setSelectorOpen(false);
4556
+ }, []);
4557
+ const socialUnlock = useCallback3(async () => {
4558
+ const sdk = socialRef.current;
4559
+ if (!sdk) return;
4560
+ await sdk.unlock();
4561
+ setSocialAuth((p) => ({
4562
+ ...p,
4563
+ isUnlocked: sdk.isUnlocked(),
4564
+ sessionExpiresAt: sdk.sessionExpiresAt()
4565
+ }));
4566
+ }, []);
4567
+ const socialDisconnect = useCallback3(async () => {
4568
+ const sdk = socialRef.current;
4569
+ if (!sdk) return;
4570
+ await sdk.disconnect();
4571
+ setSocialAuth(DEFAULT_SOCIAL);
4572
+ }, []);
4573
+ const socialSignMessage = useCallback3(async (chain, message) => {
4574
+ const sdk = socialRef.current;
4575
+ if (!sdk) throw new Error("Social wallet not connected");
4576
+ return sdk.signMessage({ chain, message });
4577
+ }, []);
4578
+ const socialSendTransaction = useCallback3(async (req) => {
4579
+ const sdk = socialRef.current;
4580
+ if (!sdk) throw new Error("Social wallet not connected");
4581
+ return sdk.sendTransaction({ ...req, chain: req.chain });
4582
+ }, []);
4583
+ const socialSignSolanaTransaction = useCallback3(async (tx) => {
4584
+ const sdk = socialRef.current;
4585
+ if (!sdk) throw new Error("Social wallet not connected");
4586
+ return sdk.signSolanaTransaction(tx);
4587
+ }, []);
4588
+ const handleWalletConnect = useCallback3(
2426
4589
  (wallet, preference) => {
2427
- ref.current.connect(wallet, preference).catch(console.error);
4590
+ walletRef.current.connect(wallet, preference).catch(console.error);
2428
4591
  },
2429
4592
  []
2430
4593
  );
2431
- return /* @__PURE__ */ React3.createElement(
4594
+ const networks = deriveNetworks(mode, config?.networks);
4595
+ return /* @__PURE__ */ React4.createElement(
2432
4596
  DecaneContext.Provider,
2433
4597
  {
2434
- value: { provider: ref.current, selectorOpen, openSelector, closeSelector, theme, setTheme }
4598
+ value: {
4599
+ provider: walletRef.current,
4600
+ selectorOpen,
4601
+ openSelector,
4602
+ closeSelector,
4603
+ theme,
4604
+ setTheme,
4605
+ mode,
4606
+ socialAuth,
4607
+ connectWithGoogle,
4608
+ connectWithEmail,
4609
+ verifyEmailCode: verifyEmailCode2,
4610
+ socialUnlock,
4611
+ socialDisconnect,
4612
+ socialSignMessage,
4613
+ socialSendTransaction,
4614
+ socialSignSolanaTransaction,
4615
+ socialModalOpen,
4616
+ openSocialModal,
4617
+ closeSocialModal
4618
+ }
2435
4619
  },
2436
4620
  children,
2437
- /* @__PURE__ */ React3.createElement(
4621
+ /* @__PURE__ */ React4.createElement(
2438
4622
  WalletSelector,
2439
4623
  {
2440
- provider: ref.current,
4624
+ provider: walletRef.current,
2441
4625
  open: selectorOpen,
2442
4626
  onClose: closeSelector,
2443
- onConnect: handleConnect,
4627
+ onConnect: handleWalletConnect,
4628
+ theme,
4629
+ networks,
4630
+ mode,
4631
+ socialAuthMethods: config?.social?.authMethods ?? ["google", "email"],
4632
+ onGoogleSignIn: connectWithGoogle,
4633
+ onEmailStart: connectWithEmail,
4634
+ onEmailVerify: verifyEmailCode2
4635
+ }
4636
+ ),
4637
+ /* @__PURE__ */ React4.createElement(
4638
+ SocialWalletModal,
4639
+ {
4640
+ open: socialModalOpen,
4641
+ onClose: closeSocialModal,
2444
4642
  theme,
2445
- networks: config?.networks ?? "all"
4643
+ socialAuth,
4644
+ onUnlock: socialUnlock,
4645
+ onDisconnect: socialDisconnect
2446
4646
  }
2447
4647
  )
2448
4648
  );
@@ -2458,7 +4658,169 @@ function useDecaneContext() {
2458
4658
  }
2459
4659
 
2460
4660
  // src/ui/hooks.ts
2461
- import { useState as useState3, useEffect as useEffect2, useCallback as useCallback3 } from "react";
4661
+ import { useState as useState4, useEffect as useEffect4, useCallback as useCallback4 } from "react";
4662
+ function useSocialAuth() {
4663
+ const {
4664
+ socialAuth,
4665
+ connectWithGoogle,
4666
+ connectWithEmail,
4667
+ verifyEmailCode: verifyEmailCode2,
4668
+ socialUnlock,
4669
+ socialDisconnect
4670
+ } = useDecaneContext();
4671
+ const [googleLoading, setGoogleLoading] = useState4(false);
4672
+ const [emailLoading, setEmailLoading] = useState4(false);
4673
+ const [error, setError] = useState4(null);
4674
+ const signInWithGoogle = useCallback4(async () => {
4675
+ setGoogleLoading(true);
4676
+ setError(null);
4677
+ try {
4678
+ await connectWithGoogle();
4679
+ } catch (e) {
4680
+ setError(e.message);
4681
+ setGoogleLoading(false);
4682
+ }
4683
+ }, [connectWithGoogle]);
4684
+ const sendEmailCode = useCallback4(async (email) => {
4685
+ setEmailLoading(true);
4686
+ setError(null);
4687
+ try {
4688
+ await connectWithEmail(email);
4689
+ return { codeRequired: true };
4690
+ } catch (e) {
4691
+ setError(e.message);
4692
+ throw e;
4693
+ } finally {
4694
+ setEmailLoading(false);
4695
+ }
4696
+ }, [connectWithEmail]);
4697
+ const confirmEmailCode = useCallback4(async (email, code) => {
4698
+ setEmailLoading(true);
4699
+ setError(null);
4700
+ try {
4701
+ await verifyEmailCode2(email, code);
4702
+ } catch (e) {
4703
+ setError(e.message);
4704
+ throw e;
4705
+ } finally {
4706
+ setEmailLoading(false);
4707
+ }
4708
+ }, [verifyEmailCode2]);
4709
+ const unlock = useCallback4(async () => {
4710
+ setError(null);
4711
+ try {
4712
+ await socialUnlock();
4713
+ } catch (e) {
4714
+ setError(e.message);
4715
+ throw e;
4716
+ }
4717
+ }, [socialUnlock]);
4718
+ const disconnect = useCallback4(async () => {
4719
+ setError(null);
4720
+ try {
4721
+ await socialDisconnect();
4722
+ } catch (e) {
4723
+ setError(e.message);
4724
+ throw e;
4725
+ }
4726
+ }, [socialDisconnect]);
4727
+ return {
4728
+ isConnected: !!socialAuth.addresses,
4729
+ isUnlocked: socialAuth.isUnlocked,
4730
+ isNewUser: socialAuth.isNewUser,
4731
+ addresses: socialAuth.addresses,
4732
+ sessionExpiresAt: socialAuth.sessionExpiresAt,
4733
+ googleLoading,
4734
+ emailLoading,
4735
+ error,
4736
+ clearError: () => setError(null),
4737
+ signInWithGoogle,
4738
+ sendEmailCode,
4739
+ confirmEmailCode,
4740
+ unlock,
4741
+ disconnect
4742
+ };
4743
+ }
4744
+ function useSocialWallet() {
4745
+ const {
4746
+ socialAuth,
4747
+ socialUnlock,
4748
+ socialDisconnect,
4749
+ socialSignMessage,
4750
+ socialSendTransaction,
4751
+ socialSignSolanaTransaction,
4752
+ openSocialModal,
4753
+ closeSocialModal,
4754
+ socialModalOpen
4755
+ } = useDecaneContext();
4756
+ const [loading, setLoading] = useState4(false);
4757
+ const [error, setError] = useState4(null);
4758
+ async function run(fn) {
4759
+ setLoading(true);
4760
+ setError(null);
4761
+ try {
4762
+ return await fn();
4763
+ } catch (e) {
4764
+ const msg = e.message;
4765
+ setError(msg);
4766
+ throw e;
4767
+ } finally {
4768
+ setLoading(false);
4769
+ }
4770
+ }
4771
+ const signMessage = useCallback4(
4772
+ (chain, message) => run(() => socialSignMessage(chain, message)),
4773
+ [socialSignMessage]
4774
+ );
4775
+ const sendTransaction = useCallback4(
4776
+ (req) => run(() => socialSendTransaction(req)),
4777
+ [socialSendTransaction]
4778
+ );
4779
+ const signSolanaTransaction2 = useCallback4(
4780
+ (tx) => run(() => socialSignSolanaTransaction(tx)),
4781
+ [socialSignSolanaTransaction]
4782
+ );
4783
+ const unlock = useCallback4(async () => {
4784
+ setError(null);
4785
+ try {
4786
+ await socialUnlock();
4787
+ } catch (e) {
4788
+ setError(e.message);
4789
+ throw e;
4790
+ }
4791
+ }, [socialUnlock]);
4792
+ const disconnect = useCallback4(async () => {
4793
+ setError(null);
4794
+ try {
4795
+ await socialDisconnect();
4796
+ } catch (e) {
4797
+ setError(e.message);
4798
+ throw e;
4799
+ }
4800
+ }, [socialDisconnect]);
4801
+ return {
4802
+ // State
4803
+ isConnected: !!socialAuth.addresses,
4804
+ isUnlocked: socialAuth.isUnlocked,
4805
+ isNewUser: socialAuth.isNewUser,
4806
+ addresses: socialAuth.addresses,
4807
+ sessionExpiresAt: socialAuth.sessionExpiresAt,
4808
+ // Modal
4809
+ modalOpen: socialModalOpen,
4810
+ openModal: openSocialModal,
4811
+ closeModal: closeSocialModal,
4812
+ // Auth
4813
+ unlock,
4814
+ disconnect,
4815
+ // Signing
4816
+ signMessage,
4817
+ sendTransaction,
4818
+ signSolanaTransaction: signSolanaTransaction2,
4819
+ loading,
4820
+ error,
4821
+ clearError: () => setError(null)
4822
+ };
4823
+ }
2462
4824
  function toDecaneError(err) {
2463
4825
  if (err instanceof DecaneError) return err;
2464
4826
  const msg = err instanceof Error ? err.message : err?.message != null ? String(err.message) : String(err);
@@ -2466,8 +4828,8 @@ function toDecaneError(err) {
2466
4828
  }
2467
4829
  function useDecane() {
2468
4830
  const { provider, theme, setTheme } = useDecaneContext();
2469
- const [, forceUpdate] = useState3(0);
2470
- useEffect2(() => {
4831
+ const [, forceUpdate] = useState4(0);
4832
+ useEffect4(() => {
2471
4833
  const refresh = () => forceUpdate((n) => n + 1);
2472
4834
  const events = [
2473
4835
  "connect",
@@ -2506,10 +4868,10 @@ function useWalletSelector() {
2506
4868
  }
2507
4869
  function useChainSwitch() {
2508
4870
  const { provider } = useDecaneContext();
2509
- const [, forceUpdate] = useState3(0);
2510
- const [loading, setLoading] = useState3(false);
2511
- const [error, setError] = useState3(null);
2512
- useEffect2(() => {
4871
+ const [, forceUpdate] = useState4(0);
4872
+ const [loading, setLoading] = useState4(false);
4873
+ const [error, setError] = useState4(null);
4874
+ useEffect4(() => {
2513
4875
  const refresh = () => forceUpdate((n) => n + 1);
2514
4876
  provider.on("connect", refresh);
2515
4877
  provider.on("disconnect", refresh);
@@ -2518,7 +4880,7 @@ function useChainSwitch() {
2518
4880
  provider.off("disconnect", refresh);
2519
4881
  };
2520
4882
  }, [provider]);
2521
- const switchChain = useCallback3(
4883
+ const switchChain = useCallback4(
2522
4884
  async (chainId) => {
2523
4885
  setLoading(true);
2524
4886
  setError(null);
@@ -2572,10 +4934,10 @@ function resolveScope(provider, chainId, account) {
2572
4934
  }
2573
4935
  function useSignMessage() {
2574
4936
  const { provider } = useDecaneContext();
2575
- const [signature, setSignature] = useState3(null);
2576
- const [loading, setLoading] = useState3(false);
2577
- const [error, setError] = useState3(null);
2578
- const signMessage = useCallback3(
4937
+ const [signature, setSignature] = useState4(null);
4938
+ const [loading, setLoading] = useState4(false);
4939
+ const [error, setError] = useState4(null);
4940
+ const signMessage = useCallback4(
2579
4941
  async (message, options) => {
2580
4942
  const { chain, account } = resolveScope(
2581
4943
  provider,
@@ -2601,7 +4963,7 @@ function useSignMessage() {
2601
4963
  },
2602
4964
  [provider]
2603
4965
  );
2604
- const reset = useCallback3(() => {
4966
+ const reset = useCallback4(() => {
2605
4967
  setSignature(null);
2606
4968
  setError(null);
2607
4969
  }, []);
@@ -2609,10 +4971,10 @@ function useSignMessage() {
2609
4971
  }
2610
4972
  function useSendTransaction() {
2611
4973
  const { provider } = useDecaneContext();
2612
- const [txHash, setTxHash] = useState3(null);
2613
- const [loading, setLoading] = useState3(false);
2614
- const [error, setError] = useState3(null);
2615
- const sendTransaction = useCallback3(
4974
+ const [txHash, setTxHash] = useState4(null);
4975
+ const [loading, setLoading] = useState4(false);
4976
+ const [error, setError] = useState4(null);
4977
+ const sendTransaction = useCallback4(
2616
4978
  async (tx, options) => {
2617
4979
  const { chain, account } = resolveScope(
2618
4980
  provider,
@@ -2639,7 +5001,7 @@ function useSendTransaction() {
2639
5001
  },
2640
5002
  [provider]
2641
5003
  );
2642
- const reset = useCallback3(() => {
5004
+ const reset = useCallback4(() => {
2643
5005
  setTxHash(null);
2644
5006
  setError(null);
2645
5007
  }, []);
@@ -2647,10 +5009,10 @@ function useSendTransaction() {
2647
5009
  }
2648
5010
  function useSignTypedData() {
2649
5011
  const { provider } = useDecaneContext();
2650
- const [signature, setSignature] = useState3(null);
2651
- const [loading, setLoading] = useState3(false);
2652
- const [error, setError] = useState3(null);
2653
- const signTypedData = useCallback3(
5012
+ const [signature, setSignature] = useState4(null);
5013
+ const [loading, setLoading] = useState4(false);
5014
+ const [error, setError] = useState4(null);
5015
+ const signTypedData = useCallback4(
2654
5016
  async (typedData, options) => {
2655
5017
  const { chain, account } = resolveScope(
2656
5018
  provider,
@@ -2676,25 +5038,50 @@ function useSignTypedData() {
2676
5038
  },
2677
5039
  [provider]
2678
5040
  );
2679
- const reset = useCallback3(() => {
5041
+ const reset = useCallback4(() => {
2680
5042
  setSignature(null);
2681
5043
  setError(null);
2682
5044
  }, []);
2683
5045
  return { signTypedData, signature, loading, error, reset };
2684
5046
  }
2685
5047
  export {
5048
+ AttestationError,
2686
5049
  BUILT_IN_CHAINS,
2687
5050
  CHAINS,
5051
+ CallSequenceError,
5052
+ DecaneConnectError,
2688
5053
  DecaneError,
2689
5054
  DecaneErrorCode,
2690
5055
  DecaneKit,
5056
+ ExportNotAuthorizedError,
5057
+ InvalidApiKeyError,
5058
+ InvalidPinError,
5059
+ InvalidRecoveryFileError,
5060
+ NetworkError,
5061
+ NewDeviceError,
5062
+ OriginNotAllowedError,
5063
+ PromptPinNotConfiguredError,
5064
+ RecoveryFailedError,
5065
+ RecoveryWalletMismatchError,
5066
+ SessionExpiredError,
5067
+ ShareCorruptedError,
5068
+ SocialWalletModal,
5069
+ UnlockRequiredError,
5070
+ UserCancelledError,
2691
5071
  WalletSelector,
5072
+ WebAuthnNotSupportedError,
5073
+ WrongPasswordError,
2692
5074
  createDecane,
5075
+ createDecaneConnect,
5076
+ startEmailAuth,
2693
5077
  useChainSwitch,
2694
5078
  useDecane,
2695
5079
  useSendTransaction,
2696
5080
  useSignMessage,
2697
5081
  useSignTypedData,
2698
- useWalletSelector
5082
+ useSocialAuth,
5083
+ useSocialWallet,
5084
+ useWalletSelector,
5085
+ verifyEmailCode
2699
5086
  };
2700
5087
  //# sourceMappingURL=index.js.map