UNPKG

@avalabs/avacloud-waas-react

Version:
1,539 lines (1,517 loc) 173 kB
// src/AvaCloudWalletProvider.tsx import { createContext as createContext4, useContext as useContext4, useEffect as useEffect5, useState as useState7, useCallback as useCallback5, useRef as useRef3 } from "react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { envs } from "@cubist-labs/cubesigner-sdk"; // src/types/vm.ts var VM = /* @__PURE__ */ ((VM2) => { VM2["EVM"] = "EVM"; VM2["AVM"] = "AVM"; VM2["PVM"] = "PVM"; return VM2; })(VM || {}); // src/utils/derivationPath.ts var getCoinIndexForVm = (vm) => { switch (vm) { case "EVM" /* EVM */: return 60; case "AVM" /* AVM */: case "PVM" /* PVM */: return 9e3; default: throw new Error(`Unknown coin index for vm: ${vm}`); } }; function getDerivationPath(vm, accountIndex) { if (accountIndex < 0 || Math.round(accountIndex) !== accountIndex) { throw new Error("Account index must be a non-negative integer"); } const coinIndex = getCoinIndexForVm(vm); return `m/44'/${coinIndex}'/0'/0/${accountIndex}`; } // src/hooks/usePostMessage.ts import { useCallback, useEffect, useRef } from "react"; // src/constants/storage.ts var OIDC_TOKEN_KEY = "avacloud-auth-oidc-token"; var AUTH_TOKENS_KEY = "auth_tokens"; var AUTH0_STORAGE_KEYS = { IS_AUTHENTICATED: "auth0.is.authenticated", ACCESS_TOKEN: "auth0.access_token", ID_TOKEN: "auth0.id_token", EXPIRES_AT: "auth0.expires_at" }; var CUBIST_USER_ID_KEY = "cubist_user_id"; var ORG_CONFIG_CACHE_KEY = "avacloud-org-config-cache"; // src/hooks/usePostMessage.ts var globalAuthState = { signupInProgress: false, loginInProgress: false, lastSignupTimestamp: 0, lastLoginTimestamp: 0, processingMessageIds: /* @__PURE__ */ new Set() }; function cleanupProcessedMessageIds() { if (globalAuthState.processingMessageIds.size > 100) { const messageIds = Array.from(globalAuthState.processingMessageIds); globalAuthState.processingMessageIds = new Set(messageIds.slice(-50)); } } function usePostMessage({ authServiceUrl, orgId, environment, iframe, isIframeReady, onAuthSuccess, onAuthError, onOidcReceived, onOrgConfigUpdate, setIsAuthenticated, setUser, setIsLoading, setIsIframeReady, cubistClient }) { const lastAuthStateRef = useRef({ isAuthenticated: false, userId: null, lastUpdateTime: 0 }); const hasRequestedOidcRef = useRef(false); const isHandlingMessageRef = useRef(false); const hasLoadedCacheRef = useRef(false); useEffect(() => { if (orgId && onOrgConfigUpdate && !hasLoadedCacheRef.current) { hasLoadedCacheRef.current = true; try { const cachedConfigKey = `${ORG_CONFIG_CACHE_KEY}-${orgId}-${environment}`; const cachedConfigJson = localStorage.getItem(cachedConfigKey); if (cachedConfigJson) { const cachedConfig = JSON.parse(cachedConfigJson); const timestamp = cachedConfig._timestamp || 0; const isExpired = Date.now() - timestamp > 30 * 60 * 1e3; if (!isExpired) { const { _timestamp, ...configWithoutTimestamp } = cachedConfig; onOrgConfigUpdate(configWithoutTimestamp); } } } catch (error) { } } }, [orgId, environment, onOrgConfigUpdate]); const sendMessage = useCallback((message) => { if (iframe == null ? void 0 : iframe.contentWindow) { try { if (message.type === "SIGNUP_REQUEST") { if (globalAuthState.signupInProgress) { return; } const now = Date.now(); if (now - globalAuthState.lastSignupTimestamp < 3e3) { return; } globalAuthState.signupInProgress = true; globalAuthState.lastSignupTimestamp = now; let finalMessage = message; if (!message.requestId) { finalMessage = { ...message, requestId: `signup-${now}` }; } let messageToSend = finalMessage; if (finalMessage.payload && typeof finalMessage.payload.email === "string" && typeof finalMessage.payload.password === "string") { messageToSend = { type: "SIGNUP_REQUEST", email: finalMessage.payload.email, password: finalMessage.payload.password, requestId: finalMessage.requestId || `signup-${now}` }; } if ("requestId" in messageToSend && messageToSend.requestId) { globalAuthState.processingMessageIds.add(messageToSend.requestId); } iframe.contentWindow.postMessage(messageToSend, authServiceUrl); return; } if (message.type === "LOGIN_REQUEST") { if (globalAuthState.loginInProgress) { return; } const now = Date.now(); if (now - globalAuthState.lastLoginTimestamp < 3e3) { return; } globalAuthState.loginInProgress = true; globalAuthState.lastLoginTimestamp = now; let finalMessage = message; if (!message.requestId) { finalMessage = { ...message, requestId: `login-${now}` }; } if (finalMessage.requestId) { globalAuthState.processingMessageIds.add(finalMessage.requestId); } iframe.contentWindow.postMessage(finalMessage, authServiceUrl); return; } iframe.contentWindow.postMessage(message, authServiceUrl); } catch (error) { if (message.type === "SIGNUP_REQUEST") { globalAuthState.signupInProgress = false; } if (message.type === "LOGIN_REQUEST") { globalAuthState.loginInProgress = false; } } } }, [iframe, authServiceUrl]); useEffect(() => { const handleMessage = async (event) => { var _a, _b, _c, _d, _e, _f, _g; if (isHandlingMessageRef.current) { return; } if (event.origin !== new URL(authServiceUrl).origin) { return; } if (typeof ((_a = event.data) == null ? void 0 : _a.type) !== "string") { return; } if (event.data.requestId && globalAuthState.processingMessageIds.has(event.data.requestId)) { return; } try { isHandlingMessageRef.current = true; if (event.data.type === "ERROR") { globalAuthState.signupInProgress = false; globalAuthState.loginInProgress = false; } else if (event.data.type === "AUTH_STATUS" && event.data.isAuthenticated) { globalAuthState.signupInProgress = false; globalAuthState.loginInProgress = false; cleanupProcessedMessageIds(); } else if (event.data.type === "RECEIVE_OIDC") { globalAuthState.signupInProgress = false; globalAuthState.loginInProgress = false; } switch (event.data.type) { case "IFRAME_READY": setIsIframeReady(true); break; case "RECEIVE_OIDC": if (typeof ((_b = event.data.payload) == null ? void 0 : _b.idToken) === "string") { const oidcToken = event.data.payload.idToken; hasRequestedOidcRef.current = false; onOidcReceived == null ? void 0 : onOidcReceived(oidcToken); } else { hasRequestedOidcRef.current = false; } break; case "AUTH_STATUS": { const newIsAuthenticated = !!event.data.isAuthenticated; const newUserId = (_d = (_c = event.data.user) == null ? void 0 : _c.sub) != null ? _d : null; const now = Date.now(); if (now - lastAuthStateRef.current.lastUpdateTime < 500) { return; } const authStateChanged = lastAuthStateRef.current.isAuthenticated !== newIsAuthenticated || lastAuthStateRef.current.userId !== newUserId; if (authStateChanged) { setIsAuthenticated(newIsAuthenticated); if (event.data.user) { const userInfo = { email: event.data.user.email, sub: event.data.user.sub, configured_mfa: [], displayName: event.data.user.nickname || event.data.user.name || ((_e = event.data.user.email) == null ? void 0 : _e.split("@")[0]) || "User", rawUserData: event.data.user }; setUser(userInfo); if (newIsAuthenticated && !cubistClient && !hasRequestedOidcRef.current) { hasRequestedOidcRef.current = true; sendMessage({ type: "GET_OIDC" }); return; } } else { setUser(null); localStorage.removeItem(AUTH_TOKENS_KEY); localStorage.removeItem(AUTH0_STORAGE_KEYS.IS_AUTHENTICATED); sessionStorage.removeItem(OIDC_TOKEN_KEY); } lastAuthStateRef.current = { isAuthenticated: newIsAuthenticated, userId: newUserId, lastUpdateTime: now }; if (newIsAuthenticated && event.data.user) { if (!hasRequestedOidcRef.current) { if (event.data.tokens) { localStorage.setItem(AUTH_TOKENS_KEY, JSON.stringify(event.data.tokens)); } onAuthSuccess == null ? void 0 : onAuthSuccess(); } } } if (event.data.orgConfig && onOrgConfigUpdate) { onOrgConfigUpdate(event.data.orgConfig); if (orgId) { try { const cachedConfigKey = `${ORG_CONFIG_CACHE_KEY}-${orgId}-${environment}`; const configWithTimestamp = { ...event.data.orgConfig, _timestamp: Date.now() }; localStorage.setItem(cachedConfigKey, JSON.stringify(configWithTimestamp)); } catch (error) { } } } if (!hasRequestedOidcRef.current) { setIsLoading(false); } break; } case "REGISTER_SUCCESS": { const userId = (_f = event.data.payload) == null ? void 0 : _f.userId; if (userId) { localStorage.setItem(CUBIST_USER_ID_KEY, userId); if (!hasRequestedOidcRef.current) { hasRequestedOidcRef.current = true; sendMessage({ type: "GET_OIDC" }); } } break; } case "ERROR": if (event.data.error === "User not authenticated in iframe") { isHandlingMessageRef.current = false; return; } onAuthError == null ? void 0 : onAuthError(new Error((_g = event.data.error) != null ? _g : "Unknown error")); setIsLoading(false); break; } } finally { isHandlingMessageRef.current = false; } }; window.addEventListener("message", handleMessage); return () => window.removeEventListener("message", handleMessage); }, [ authServiceUrl, onAuthError, onAuthSuccess, onOidcReceived, onOrgConfigUpdate, setIsAuthenticated, setIsIframeReady, setIsLoading, setUser, sendMessage, cubistClient, orgId, environment ]); useEffect(() => { if (isIframeReady && (iframe == null ? void 0 : iframe.contentWindow)) { sendMessage({ type: "CHECK_AUTH_STATUS", payload: { orgId, environment } }); } }, [isIframeReady, iframe, sendMessage, orgId, environment]); return { sendMessage }; } // src/AuthModalContext.tsx import { createContext, useContext, useState as useState3, useCallback as useCallback2 } from "react"; import { Dialog as Dialog2, DialogContent as DialogContent2, DialogTitle } from "@avalabs/core-k2-components"; // src/components/Modal.tsx import { useState as useState2, useEffect as useEffect2, useRef as useRef2 } from "react"; import { Dialog, DialogContent, IconButton as IconButton2, Stack as Stack3, XIcon } from "@avalabs/core-k2-components"; // src/components/SignInContent.tsx import { useState } from "react"; import { Button, TextField, Typography as Typography2, Stack as Stack2, IconButton, Divider, GoogleIcon, AppleIcon, XTwitterIcon, FacebookIcon } from "@avalabs/core-k2-components"; // src/components/PoweredByAvaCloud.tsx import { Stack, Typography, AvaCloudConnectIcon } from "@avalabs/core-k2-components"; import { jsx, jsxs } from "react/jsx-runtime"; function PoweredByAvaCloud() { return /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", justifyContent: "center", spacing: 1, children: [ /* @__PURE__ */ jsx(Typography, { variant: "body2", children: "Powered by" }), /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", spacing: 0.5, children: [ /* @__PURE__ */ jsx(AvaCloudConnectIcon, {}), /* @__PURE__ */ jsx(Typography, { variant: "body2", children: "AvaCloud" }) ] }) ] }); } // src/constants/legal.ts var TERMS_OF_SERVICE_URL = "https://app.avacloud.io/legal/waas/user-terms"; var PRIVACY_POLICY_URL = "https://www.avalabs.org/privacy-policy"; // src/components/SignInContent.tsx import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime"; function SignInContent({ onEmailLogin, onEmailSignup, onProviderLogin, error, isSubmitting, socialLogins = ["google", "x", "apple"] }) { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [isPasswordStep, setIsPasswordStep] = useState(false); const [isSignupMode, setIsSignupMode] = useState(false); const providerConnectionMap = { "google": "google-oauth2", "x": "twitter", "twitter": "twitter", "facebook": "facebook", "apple": "apple" }; const renderSocialLoginButton = (loginType) => { if (!providerConnectionMap[loginType]) return null; const connectionName = providerConnectionMap[loginType]; let LoginIcon; switch (loginType) { case "google": LoginIcon = GoogleIcon; break; case "x": case "twitter": LoginIcon = XTwitterIcon; break; case "facebook": LoginIcon = FacebookIcon; break; case "apple": LoginIcon = AppleIcon; break; default: return null; } return /* @__PURE__ */ jsx2( IconButton, { color: "default", variant: "contained", onClick: () => onProviderLogin(connectionName), disabled: isSubmitting, sx: { display: "flex", padding: "16px", alignItems: "center", gap: "8px", borderRadius: "8px", background: "rgba(0, 0, 0, 0.02)", boxShadow: "2px 1px 4px 0px rgba(0, 0, 0, 0.20)", width: 72, height: 72, "&:hover": { background: "rgba(0, 0, 0, 0.04)" } }, children: /* @__PURE__ */ jsx2(LoginIcon, { sx: { width: 24, height: 24 } }) }, loginType ); }; const handleEmailSubmit = (e) => { e.preventDefault(); if (!email) return; setIsPasswordStep(true); }; const handlePasswordSubmit = (e) => { e.preventDefault(); if (!password) return; onEmailLogin(email, password); }; const handleSignupSubmit = (e) => { e.preventDefault(); if (!email || !password) return; onEmailSignup(email, password); }; const handleForgotPassword = () => { }; const handleToggleSignup = () => { setIsSignupMode(!isSignupMode); setIsPasswordStep(false); }; const titleTypography = { alignSelf: "stretch", color: (theme) => theme.palette.mode === "dark" ? "#FFFFFF" : "rgba(0, 0, 0, 0.80)", fontFamily: "Inter", fontSize: "16px", fontStyle: "normal", fontWeight: 700, lineHeight: "150%" }; if (isSignupMode) { return /* @__PURE__ */ jsxs2(Stack2, { gap: 3, children: [ /* @__PURE__ */ jsx2(Typography2, { variant: "h6", sx: titleTypography, children: "Sign up with" }), /* @__PURE__ */ jsx2(Stack2, { direction: "row", spacing: 3, sx: { justifyContent: "center" }, children: socialLogins.map((loginType) => renderSocialLoginButton(loginType)) }), /* @__PURE__ */ jsx2(Divider, { children: /* @__PURE__ */ jsx2(Typography2, { variant: "body2", color: "text.secondary", children: "or" }) }), /* @__PURE__ */ jsx2("form", { onSubmit: handleSignupSubmit, children: /* @__PURE__ */ jsxs2(Stack2, { gap: 2, children: [ /* @__PURE__ */ jsx2( TextField, { placeholder: "Enter email", type: "email", value: email, onChange: (e) => setEmail(e.target.value), fullWidth: true, required: true, disabled: isSubmitting, error: !!error, helperText: error, sx: { "& .MuiOutlinedInput-root": { backgroundColor: (theme) => theme.palette.mode === "dark" ? "rgba(255, 255, 255, 0.05)" : "grey.50" } } } ), /* @__PURE__ */ jsx2( TextField, { placeholder: "Create password", type: "password", value: password, onChange: (e) => setPassword(e.target.value), fullWidth: true, required: true, disabled: isSubmitting, sx: { "& .MuiOutlinedInput-root": { backgroundColor: (theme) => theme.palette.mode === "dark" ? "rgba(255, 255, 255, 0.05)" : "grey.50" } } } ), /* @__PURE__ */ jsx2( Button, { type: "submit", variant: "contained", fullWidth: true, disabled: isSubmitting || !email || !password, sx: { height: 48, backgroundColor: "#3A65FF", borderRadius: 24, textTransform: "none", "&:hover": { backgroundColor: "#2952E6" } }, children: "Sign up" } ), /* @__PURE__ */ jsxs2( Typography2, { variant: "body2", sx: { textAlign: "center", "& a": { color: "#3A65FF", textDecoration: "none", cursor: "pointer", "&:hover": { textDecoration: "underline" } } }, children: [ "Already have an account?", " ", /* @__PURE__ */ jsx2( Button, { variant: "text", onClick: handleToggleSignup, sx: { color: "#3A65FF", textTransform: "none", padding: "0 4px", minWidth: "auto", fontWeight: 600, "&:hover": { textDecoration: "underline", backgroundColor: "transparent" } }, children: "Sign in" } ) ] } ) ] }) }), /* @__PURE__ */ jsx2(PoweredByAvaCloud, {}), /* @__PURE__ */ jsxs2( Typography2, { variant: "body2", sx: { textAlign: "center", color: "text.secondary", fontSize: "0.75rem", "& a": { color: "inherit", textDecoration: "none", "&:hover": { textDecoration: "underline" } } }, children: [ "By connecting, you agree to the", " ", /* @__PURE__ */ jsx2( Button, { variant: "text", component: "a", href: TERMS_OF_SERVICE_URL, target: "_blank", rel: "noopener noreferrer", sx: { color: "inherit", p: 0, minWidth: "auto", textTransform: "none", fontSize: "inherit", fontWeight: "inherit", "&:hover": { textDecoration: "underline", backgroundColor: "transparent" } }, children: "Terms of Service" } ), " ", "and", " ", /* @__PURE__ */ jsx2( Button, { variant: "text", component: "a", href: PRIVACY_POLICY_URL, target: "_blank", rel: "noopener noreferrer", sx: { color: "inherit", p: 0, minWidth: "auto", textTransform: "none", fontSize: "inherit", fontWeight: "inherit", "&:hover": { textDecoration: "underline", backgroundColor: "transparent" } }, children: "Privacy Policy" } ) ] } ) ] }); } if (isPasswordStep) { return /* @__PURE__ */ jsxs2(Stack2, { gap: 3, children: [ /* @__PURE__ */ jsx2(Typography2, { variant: "h6", sx: titleTypography, children: "Sign in with" }), /* @__PURE__ */ jsx2("form", { onSubmit: handlePasswordSubmit, children: /* @__PURE__ */ jsxs2(Stack2, { gap: 2, children: [ /* @__PURE__ */ jsx2( TextField, { placeholder: "Enter password", type: "password", value: password, onChange: (e) => setPassword(e.target.value), fullWidth: true, required: true, disabled: isSubmitting, error: !!error, helperText: error, sx: { "& .MuiOutlinedInput-root": { backgroundColor: (theme) => theme.palette.mode === "dark" ? "rgba(255, 255, 255, 0.05)" : "grey.50" } } } ), /* @__PURE__ */ jsx2( Button, { type: "submit", variant: "contained", fullWidth: true, disabled: isSubmitting || !password, sx: { height: 48, backgroundColor: "#3A65FF", borderRadius: 24, textTransform: "none", "&:hover": { backgroundColor: "#2952E6" } }, children: "Continue" } ), /* @__PURE__ */ jsx2( Typography2, { variant: "body2", sx: { textAlign: "center", "& a": { color: "#3A65FF", textDecoration: "none", cursor: "pointer", "&:hover": { textDecoration: "underline" } } }, children: /* @__PURE__ */ jsx2( Button, { variant: "text", onClick: handleForgotPassword, sx: { color: "#3A65FF", textTransform: "none", "&:hover": { textDecoration: "underline", backgroundColor: "transparent" } }, children: "Forget password?" } ) } ) ] }) }), /* @__PURE__ */ jsx2(PoweredByAvaCloud, {}), /* @__PURE__ */ jsxs2( Typography2, { variant: "body2", sx: { textAlign: "center", color: "text.secondary", fontSize: "0.75rem", "& a": { color: "inherit", textDecoration: "none", "&:hover": { textDecoration: "underline" } } }, children: [ "By continuing, you agree to our", " ", /* @__PURE__ */ jsx2( Button, { variant: "text", component: "a", href: TERMS_OF_SERVICE_URL, target: "_blank", rel: "noopener noreferrer", sx: { color: "inherit", p: 0, minWidth: "auto", textTransform: "none", fontSize: "inherit", fontWeight: "inherit", "&:hover": { textDecoration: "underline", backgroundColor: "transparent" } }, children: "Terms of Use" } ), " ", "and", " ", /* @__PURE__ */ jsx2( Button, { variant: "text", component: "a", href: PRIVACY_POLICY_URL, target: "_blank", rel: "noopener noreferrer", sx: { color: "inherit", p: 0, minWidth: "auto", textTransform: "none", fontSize: "inherit", fontWeight: "inherit", "&:hover": { textDecoration: "underline", backgroundColor: "transparent" } }, children: "Privacy Policy" } ) ] } ) ] }); } return /* @__PURE__ */ jsxs2(Stack2, { gap: 3, children: [ /* @__PURE__ */ jsx2(Typography2, { variant: "h6", sx: titleTypography, children: "Sign in with" }), /* @__PURE__ */ jsx2(Stack2, { direction: "row", spacing: 3, sx: { justifyContent: "center" }, children: socialLogins.map((loginType) => renderSocialLoginButton(loginType)) }), /* @__PURE__ */ jsx2(Divider, { children: /* @__PURE__ */ jsx2(Typography2, { variant: "body2", color: "text.secondary", children: "or" }) }), /* @__PURE__ */ jsx2("form", { onSubmit: handleEmailSubmit, children: /* @__PURE__ */ jsxs2(Stack2, { gap: 2, children: [ /* @__PURE__ */ jsx2( TextField, { placeholder: "Enter email", type: "email", value: email, onChange: (e) => setEmail(e.target.value), fullWidth: true, required: true, disabled: isSubmitting, error: !!error, helperText: error, sx: { "& .MuiOutlinedInput-root": { backgroundColor: (theme) => theme.palette.mode === "dark" ? "rgba(255, 255, 255, 0.05)" : "grey.50" } } } ), /* @__PURE__ */ jsx2( Button, { type: "submit", variant: "contained", fullWidth: true, disabled: isSubmitting || !email, sx: { height: 48, backgroundColor: "#3A65FF", borderRadius: 24, textTransform: "none", "&:hover": { backgroundColor: "#2952E6" } }, children: "Continue" } ), /* @__PURE__ */ jsxs2( Typography2, { variant: "body2", sx: { textAlign: "center", "& a": { color: "#3A65FF", textDecoration: "none", cursor: "pointer", "&:hover": { textDecoration: "underline" } } }, children: [ "Don't have an account?", " ", /* @__PURE__ */ jsx2( Button, { variant: "text", onClick: handleToggleSignup, sx: { color: "#3A65FF", textTransform: "none", padding: "0 4px", minWidth: "auto", fontWeight: 600, "&:hover": { textDecoration: "underline", backgroundColor: "transparent" } }, children: "Sign up" } ) ] } ) ] }) }), /* @__PURE__ */ jsx2(PoweredByAvaCloud, {}), /* @__PURE__ */ jsxs2( Typography2, { variant: "body2", sx: { textAlign: "center", color: "text.secondary", fontSize: "0.75rem", "& a": { color: "inherit", textDecoration: "none", "&:hover": { textDecoration: "underline" } } }, children: [ "By connecting, you agree to the", " ", /* @__PURE__ */ jsx2( Button, { variant: "text", component: "a", href: TERMS_OF_SERVICE_URL, target: "_blank", rel: "noopener noreferrer", sx: { color: "inherit", p: 0, minWidth: "auto", textTransform: "none", fontSize: "inherit", fontWeight: "inherit", "&:hover": { textDecoration: "underline", backgroundColor: "transparent" } }, children: "Terms of Service" } ), " ", "and", " ", /* @__PURE__ */ jsx2( Button, { variant: "text", component: "a", href: PRIVACY_POLICY_URL, target: "_blank", rel: "noopener noreferrer", sx: { color: "inherit", p: 0, minWidth: "auto", textTransform: "none", fontSize: "inherit", fontWeight: "inherit", "&:hover": { textDecoration: "underline", backgroundColor: "transparent" } }, children: "Privacy Policy" } ) ] } ) ] }); } // src/components/Modal.tsx import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime"; function LoginModal({ open, onClose }) { var _a; const { iframe, orgConfig, signup } = useAvaCloudWallet(); const [isSubmitting, setIsSubmitting] = useState2(false); const [error, setError] = useState2(""); const timeoutIdRef = useRef2(null); const signupInProgressRef = useRef2(false); const lastSignupAttemptRef = useRef2(0); const socialLogins = (_a = orgConfig == null ? void 0 : orgConfig.adminPortalSettings) == null ? void 0 : _a.socialLogins; useEffect2(() => { let requestInProgress = false; const handleMessage = (event) => { if (!event.data || typeof event.data.type !== "string") { return; } if (event.data.type === "ERROR") { if (event.data.payload === "User not authenticated in iframe") { return; } setError(event.data.payload); setIsSubmitting(false); signupInProgressRef.current = false; if (timeoutIdRef.current) { clearTimeout(timeoutIdRef.current); timeoutIdRef.current = null; } } else if (event.data.type === "AUTH_STATUS" && event.data.isAuthenticated) { setIsSubmitting(false); signupInProgressRef.current = false; requestInProgress = false; if (timeoutIdRef.current) { clearTimeout(timeoutIdRef.current); timeoutIdRef.current = null; } onClose(); } else if (event.data.type === "RECEIVE_OIDC") { signupInProgressRef.current = false; requestInProgress = false; } }; window.addEventListener("message", handleMessage); return () => { window.removeEventListener("message", handleMessage); if (timeoutIdRef.current) { clearTimeout(timeoutIdRef.current); timeoutIdRef.current = null; } }; }, [onClose]); const handleProviderLogin = (provider) => { var _a2; setError(""); if (!iframe) { setError("Authentication service not available"); return; } (_a2 = iframe.contentWindow) == null ? void 0 : _a2.postMessage({ type: "LOGIN_REQUEST", connection: provider, requestId: `login-${Date.now()}` }, "*"); onClose(); }; const handleEmailLogin = (email, password) => { var _a2; setError(""); setIsSubmitting(true); if (!iframe) { setError("Authentication service not available"); setIsSubmitting(false); return; } if (timeoutIdRef.current) { clearTimeout(timeoutIdRef.current); timeoutIdRef.current = null; } (_a2 = iframe.contentWindow) == null ? void 0 : _a2.postMessage({ type: "LOGIN_REQUEST", email, password, requestId: `login-${Date.now()}` }, "*"); timeoutIdRef.current = setTimeout(() => { setError("Authentication service timed out"); setIsSubmitting(false); timeoutIdRef.current = null; }, 1e4); }; const handleEmailSignup = (email, password) => { if (signupInProgressRef.current) { return; } const now = Date.now(); if (now - lastSignupAttemptRef.current < 2e3) { return; } lastSignupAttemptRef.current = now; setError(""); setIsSubmitting(true); signupInProgressRef.current = true; if (timeoutIdRef.current) { clearTimeout(timeoutIdRef.current); timeoutIdRef.current = null; } try { signup(email, password); } catch (error2) { setError("Failed to initiate signup"); setIsSubmitting(false); signupInProgressRef.current = false; return; } timeoutIdRef.current = setTimeout(() => { setError("Authentication service timed out"); setIsSubmitting(false); signupInProgressRef.current = false; timeoutIdRef.current = null; }, 1e4); }; return /* @__PURE__ */ jsxs3( Dialog, { open, maxWidth: "xs", PaperProps: { sx: { borderRadius: "8px", width: "100%", maxWidth: 400, maxHeight: "calc(100% - 64px)", m: 2, overflow: "visible", bgcolor: (theme) => theme.palette.mode === "dark" ? "#1A1A1A" : "#ffffff", backgroundImage: "none", display: "flex", flexDirection: "column", position: "relative", boxShadow: (theme) => theme.palette.mode === "dark" ? "0px 4px 6px 0px rgba(0, 0, 0, 0.3), 0px 15px 15px 0px rgba(0, 0, 0, 0.25)" : "0px 4px 6px 0px rgba(0, 0, 0, 0.16), 0px 15px 15px 0px rgba(0, 0, 0, 0.15)", transition: "all 0.2s ease-in-out" } }, children: [ /* @__PURE__ */ jsxs3( Stack3, { direction: "row", sx: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", alignSelf: "stretch", p: 3, pb: 0 }, children: [ /* @__PURE__ */ jsx3( "img", { src: "https://images.ctfassets.net/9bazykntljf6/58QaXZf2yQ7MqI9A8MrKiX/d8f986355c6e321e1dee79f6e91575ec/avacloud.png", alt: "AvaCloud", style: { height: 24, objectFit: "contain" } } ), /* @__PURE__ */ jsx3( IconButton2, { onClick: onClose, size: "small", sx: { color: (theme) => theme.palette.mode === "dark" ? "rgba(255, 255, 255, 0.54)" : "rgba(0, 0, 0, 0.54)", padding: "4px", marginTop: "-4px", marginRight: "-4px", "&:hover": { backgroundColor: (theme) => theme.palette.mode === "dark" ? "rgba(255, 255, 255, 0.04)" : "rgba(0, 0, 0, 0.04)" } }, children: /* @__PURE__ */ jsx3(XIcon, { sx: { fontSize: 20 } }) } ) ] } ), /* @__PURE__ */ jsx3(DialogContent, { sx: { p: 3 }, children: /* @__PURE__ */ jsx3( SignInContent, { onEmailLogin: handleEmailLogin, onEmailSignup: handleEmailSignup, onProviderLogin: handleProviderLogin, error, isSubmitting, socialLogins } ) }) ] } ); } // src/AuthModalContext.tsx import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime"; var AuthModalContext = createContext(void 0); function AuthModalProvider({ children }) { const [isModalOpen, setIsModalOpen] = useState3(false); const openLoginModal = useCallback2(() => setIsModalOpen(true), []); const closeLoginModal = useCallback2(() => setIsModalOpen(false), []); return /* @__PURE__ */ jsxs4(AuthModalContext.Provider, { value: { openLoginModal, closeLoginModal, isModalOpen }, children: [ children, /* @__PURE__ */ jsxs4( Dialog2, { open: isModalOpen, onClose: closeLoginModal, maxWidth: "xs", children: [ /* @__PURE__ */ jsx4(DialogTitle, { children: /* @__PURE__ */ jsx4("img", { src: "", alt: "AvaCloud Connect", style: { height: "32px" } }) }), /* @__PURE__ */ jsx4(DialogContent2, { sx: { padding: 4 }, children: /* @__PURE__ */ jsx4( LoginModal, { open: isModalOpen, onClose: closeLoginModal } ) }) ] } ) ] }); } function useAuthModal() { const context = useContext(AuthModalContext); if (context === void 0) { throw new Error("useAuthModal must be used within an AuthModalProvider"); } return context; } // src/AvaCloudWalletProvider.tsx import { CubeSignerClient } from "@cubist-labs/cubesigner-sdk"; // src/providers/ViemContext.tsx import { createContext as createContext2, useContext as useContext2, useState as useState4, useEffect as useEffect3 } from "react"; import { createPublicClient, createWalletClient, http } from "viem"; // src/hooks/useAuth.ts import { useCallback as useCallback3 } from "react"; function useAuth() { const { isAuthenticated, isLoading, user, wallet, logout, loginWithCubist, cubistClient, cubistError } = useAvaCloudWallet(); const { openLoginModal } = useAuthModal(); const login = useCallback3(() => { openLoginModal(); }, [openLoginModal]); return { isAuthenticated, isLoading, user, wallet, login, logout, loginWithCubist, cubistClient, cubistError }; } // src/providers/ViemContext.tsx import { jsx as jsx5 } from "react/jsx-runtime"; var ViemContext = createContext2(null); function ViemProvider({ children, rpcUrl, chainId, explorerUrl }) { var _a, _b; const [publicClient, setPublicClient] = useState4(null); const [walletClient, setWalletClient] = useState4(null); const [isConnected, setIsConnected] = useState4(false); const [error, setError] = useState4(null); const { cubistClient, wallet: authWallet } = useAuth(); useEffect3(() => { const initClient = async () => { var _a2; try { const transport = http(rpcUrl); const customChain = { id: chainId, name: `Chain ${chainId}`, nativeCurrency: { name: "AVAX", symbol: "AVAX", decimals: 18 }, rpcUrls: { default: { http: [rpcUrl] }, public: { http: [rpcUrl] } }, blockExplorers: explorerUrl ? { default: { name: "Explorer", url: explorerUrl } } : void 0 }; const client = createPublicClient({ transport, chain: customChain }); if ((_a2 = authWallet == null ? void 0 : authWallet.cubistWallet) == null ? void 0 : _a2.address) { const walletInstance = createWalletClient({ transport, chain: customChain, account: authWallet.cubistWallet.address }); setWalletClient(walletInstance); } await client.getBlockNumber(); setPublicClient(client); setIsConnected(true); setError(null); } catch (err) { setError(err instanceof Error ? err : new Error("Failed to connect")); setIsConnected(false); } }; initClient(); }, [rpcUrl, chainId, explorerUrl, (_a = authWallet == null ? void 0 : authWallet.cubistWallet) == null ? void 0 : _a.address]); useEffect3(() => { var _a2; if (((_a2 = authWallet == null ? void 0 : authWallet.cubistWallet) == null ? void 0 : _a2.address) && publicClient && walletClient) { setIsConnected(true); } else { setIsConnected(false); } }, [(_b = authWallet == null ? void 0 : authWallet.cubistWallet) == null ? void 0 : _b.address, publicClient, walletClient]); const clearError = () => setError(null); return /* @__PURE__ */ jsx5( ViemContext.Provider, { value: { publicClient, walletClient, setPublicClient, setWalletClient, chainId, explorerUrl, isConnected, error, clearError }, children } ); } function useViem() { const context = useContext2(ViemContext); if (!context) { throw new Error("useViem must be used within a ViemProvider"); } return context; } // src/hooks/useGlacier.ts import { useQuery } from "@tanstack/react-query"; // src/services/glacier/client.ts var GLACIER_API_BASE_URL = "https://glacier-api.avax.network"; var GlacierApiClient = class { constructor() { this.baseUrl = GLACIER_API_BASE_URL; } async getBlockchains() { const response = await fetch(`${this.baseUrl}/v1/chains`); if (!response.ok) { throw new Error("Failed to fetch blockchains"); } return response.json(); } async getValidators(subnetId) { const response = await fetch(`${this.baseUrl}/v1/subnets/${subnetId}/validators`); if (!response.ok) { throw new Error("Failed to fetch validators"); } return response.json(); } async getSubnets() { const response = await fetch(`${this.baseUrl}/v1/subnets`); if (!response.ok) { throw new Error("Failed to fetch subnets"); } return response.json(); } async getBlockchain(chainId) { const response = await fetch(`${this.baseUrl}/v1/chains/${chainId}`); if (!response.ok) { throw new Error("Failed to fetch blockchain"); } return response.json(); } async getBalance(address, chainId) { const chain = await this.getBlockchain(chainId); const response = await fetch(chain.rpcUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "eth_getBalance", params: [address, "latest"] }) }); if (!response.ok) { throw new Error("Failed to fetch balance"); } const data = await response.json(); return data.result; } async getERC20Balances(address, chainId) { const response = await fetch(`${this.baseUrl}/v1/chains/${chainId}/addresses/${address}/balances:listErc20`); if (!response.ok) { throw new Error("Failed to fetch ERC20 balances"); } return response.json(); } async getERC721Balances(address, chainId) { const response = await fetch(`${this.baseUrl}/v1/chains/${chainId}/addresses/${address}/balances:listErc721`); if (!response.ok) { throw new Error("Failed to fetch ERC721 balances"); } return response.json(); } async getERC1155Balances(address, chainId) { const response = await fetch(`${this.baseUrl}/v1/chains/${chainId}/addresses/${address}/balances:listErc1155`); if (!response.ok) { throw new Error("Failed to fetch ERC1155 balances"); } return response.json(); } }; var glacierApi = new GlacierApiClient(); // src/hooks/useChainId.ts import { useState as useState5 } from "react"; var CHAIN_ID_STORAGE_KEY = "avalanche-chain-id"; var DEFAULT_CHAIN_ID = 43113; function useChainId() { const [chainId, setChainIdState] = useState5(() => { const storedChainId = localStorage.getItem(CHAIN_ID_STORAGE_KEY); return storedChainId ? Number.parseInt(storedChainId, 10) : DEFAULT_CHAIN_ID; }); const setChainId = (newChainId) => { localStorage.setItem(CHAIN_ID_STORAGE_KEY, newChainId.toString()); setChainIdState(newChainId); }; return { chainId, setChainId }; } // src/hooks/useGlacier.ts var glacierKeys = { all: ["glacier"], blockchains: () => [...glacierKeys.all, "blockchains"], blockchain: (chainId) => [...glacierKeys.blockchains(), chainId], validators: (subnetId) => [...glacierKeys.all, "validators", subnetId], subnets: () => [...glacierKeys.all, "subnets"], balance: (address, chainId) => [...glacierKeys.all, "balance", address, chainId], erc20Balances: (address, chainId) => [...glacierKeys.all, "erc20Balances", address, chainId], erc721Balances: (address, chainId) => [...glacierKeys.all, "erc721Balances", address, chainId], erc1155Balances: (address, chainId) => [...glacierKeys.all, "erc1155Balances", address, chainId] }; function useGlacier() { var _a; const { wallet } = useAvaCloudWallet(); const { chainId } = useChainId(); const { data: blockchain } = useBlockchain(chainId.toString()); const { data: balance, isLoading: isLoadingBalance } = useQuery({ queryKey: glacierKeys.balance(wallet.address || "", chainId.toString()), queryFn: () => glacierApi.getBalance(wallet.address || "", chainId.toString()), enabled: !!wallet.address, refetchInterval: 3e3 // Refetch every 3 seconds }); return { balance: balance ? (Number.parseInt(balance, 16) / 1e18).toString() : "0", isLoadingBalance, currencySymbol: ((_a = blockchain == null ? void 0 : blockchain.networkToken) == null ? void 0 : _a.symbol) || "AVAX", blockchain }; } function useBlockchain(chainId) { return useQuery({ queryKey: glacierKeys.blockchain(chainId), queryFn: () => glacierApi.getBlockchain(chainId), enabled: !!chainId, staleTime: Number.POSITIVE_INFINITY }); } function useERC20Balances(address, chainId) { return useQuery({ queryKey: glacierKeys.erc20Balances(address || "", chainId || ""), queryFn: () => glacierApi.getERC20Balances(address || "", chainId || ""), enabled: !!address && !!chainId, refetchInterval: 3e3 // Refetch every 3 seconds }); } function useERC721Balances(address, chainId) { return useQuery({ queryKey: glacierKeys.erc721Balances(address || "", chainId || ""), queryFn: () => glacierApi.getERC721Balances(address || "", chainId || ""), enabled: !!address && !!chainId, refetchInterval: 3e3 // Refetch every 3 seconds }); } function useERC1155Balances(address, chainId) { return useQuery({ queryKey: glacierKeys.erc1155Balances(address || "", chainId || ""), queryFn: () => glacierApi.getERC1155Balances(address || "", chainId || ""), enabled: !!address && !!chainId, refetchInterval: 3e3 // Refetch every 3 seconds }); } // src/providers/ThemeProvider.tsx import { ThemeProvider as K2ThemeProvider, createTheme } from "@avalabs/core-k2-components"; import { createContext as createContext3, useContext as useContext3, useState as useState6, useCallback as useCallback4, useEffect as useEffect4 } from "react"; import { jsx as jsx6 } from "react/jsx-runtime"; var ThemeContext = createContext3({ isDarkMode: false, toggleTheme: () => { } }); var useThemeMode = () => useContext3(ThemeContext); var lightTheme = createTheme({ typography: { fontFamily: "Inter, sans-serif" } }); var darkTheme = createTheme({ typography: { fontFamily: "Inter, sans-serif" }, palette: { mode: "dark", background: { default: "#1A1A1A", paper: "#1A1A1A" }, text: { primary: "#FFFFFF", secondary: "rgba(255, 255, 255, 0.7)" }, primary: { main: "#4B9FFF", light: "#73B5FF", dark: "#3B7FCC", contrastText: "#FFFFFF" }, divider: "rgba(255, 255, 255, 0.12)" }, components: { MuiDialog: { styleOverrides: { paper: { backgroundColor: "#1A1A1A" } } } } }); function ThemeProvider({ children, darkMode, onDarkModeChange }) { const [isDarkMode, setIsDarkMode] = useState6(darkMode != null ? darkMode : false); useEffect4(() => { if (darkMode !== void 0 && darkMode !== isDarkMode) { setIsDarkMode(darkMode); } }, [darkMode]); const toggleTheme = useCallback4(() => { const newDarkMode = !isDarkMode; setIsDarkMode(newDarkMode); onDarkModeChange == null ? void 0