UNPKG

@avalabs/avacloud-waas-react

Version:
1,431 lines (1,408 loc) 204 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { AvaCloudWalletProvider: () => AvaCloudWalletProvider, ExportView: () => ExportView, GaslessProvider: () => GaslessProvider, LoginButton: () => LoginButton, ReceiveView: () => ReceiveView, SendView: () => SendView, ThemeProvider: () => ThemeProvider, TokensView: () => TokensView, UserProfile: () => UserProfile, VM: () => VM, WalletButton: () => WalletButton, WalletCard: () => WalletCard, WalletDisplay: () => WalletDisplay, avaCloudWallet: () => avaCloudWallet, useAuth: () => useAuth, useAvaCloudWallet: () => useAvaCloudWallet, useBlockchain: () => useBlockchain, useChainId: () => useChainId, useGaslessConfig: () => useGaslessConfig, useGaslessTransaction: () => useGaslessTransaction, useGlacier: () => useGlacier, usePostMessage: () => usePostMessage, useSignMessage: () => useSignMessage, useSignTransaction: () => useSignTransaction, useThemeMode: () => useThemeMode, useTransferTokens: () => useTransferTokens, useUserWallets: () => useUserWallets }); module.exports = __toCommonJS(index_exports); // src/AvaCloudWalletProvider.tsx var import_react9 = require("react"); var import_react_query2 = require("@tanstack/react-query"); var import_cubesigner_sdk = require("@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/usePenpalAuth.ts var import_react = require("react"); var import_penpal = require("penpal"); // ../avacloud-waas-common/dist/constants/storage.js 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/usePenpalAuth.ts function usePenpalAuth({ authServiceUrl, orgId, environment, iframe, onAuthSuccess, onAuthError, onOidcReceived, onOrgConfigUpdate, setIsAuthenticated, setUser, setIsLoading, setIsIframeReady }) { const connectionRef = (0, import_react.useRef)(null); const [authService, setAuthService] = (0, import_react.useState)(null); const [isConnected, setIsConnected] = (0, import_react.useState)(false); const isConnectedRef = (0, import_react.useRef)(false); const hasCheckedAuthRef = (0, import_react.useRef)(false); const hasLoadedCacheRef = (0, import_react.useRef)(false); (0, import_react.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 parentMethods = { onReady: () => { console.log("[Penpal Parent] Iframe is ready"); setIsIframeReady(true); }, onAuthStatusChange: (status) => { var _a; console.log("[Penpal Parent] Auth status changed:", status); setIsAuthenticated(status.isAuthenticated); if (status.user) { const userInfo = { email: status.user.email, sub: status.user.sub, configured_mfa: [], displayName: status.user.nickname || status.user.name || ((_a = status.user.email) == null ? void 0 : _a.split("@")[0]) || "User", rawUserData: status.user }; setUser(userInfo); } else { setUser(null); } if (status.orgConfig) { onOrgConfigUpdate == null ? void 0 : onOrgConfigUpdate(status.orgConfig); if (orgId) { try { const cachedConfigKey = `${ORG_CONFIG_CACHE_KEY}-${orgId}-${environment}`; const configWithTimestamp = { ...status.orgConfig, _timestamp: Date.now() }; localStorage.setItem(cachedConfigKey, JSON.stringify(configWithTimestamp)); } catch (error) { } } } if (status.isAuthenticated) { onAuthSuccess == null ? void 0 : onAuthSuccess(); } setIsLoading(false); }, onError: (error) => { console.error("[Penpal Parent] Error from iframe:", error); if (error.message === "User not authenticated in iframe" || error.message === "Unknown error" || error.message.includes("OIDC user not found")) { return; } onAuthError == null ? void 0 : onAuthError(new Error(error.message)); setIsLoading(false); }, onWalletUpdate: (wallet) => { console.log("[Penpal Parent] Wallet update:", wallet); } }; (0, import_react.useEffect)(() => { if (!(iframe == null ? void 0 : iframe.contentWindow)) { return; } let isCancelled = false; const setupConnection = async (attempt = 1, maxAttempts = 3) => { if (isCancelled) return; if (isConnectedRef.current && connectionRef.current) { console.log("[Penpal Parent] Already connected, skipping setup"); return; } console.log(`[Penpal Parent] Setting up connection to iframe (attempt ${attempt}/${maxAttempts})...`); if (connectionRef.current) { connectionRef.current.destroy(); connectionRef.current = null; setAuthService(null); setIsConnected(false); isConnectedRef.current = false; } try { const authServiceOrigin = new URL(authServiceUrl).origin; const messenger = new import_penpal.WindowMessenger({ remoteWindow: iframe.contentWindow, allowedOrigins: [authServiceOrigin] }); const connection = (0, import_penpal.connect)({ messenger, methods: parentMethods, timeout: 1e4 // 10 second connection timeout per attempt }); connectionRef.current = connection; const remote = await connection.promise; if (isCancelled) { connection.destroy(); return; } console.log("[Penpal Parent] Connected to auth service"); isConnectedRef.current = true; setAuthService(remote); setIsConnected(true); setIsIframeReady(true); } catch (error) { if (isCancelled) return; console.error(`[Penpal Parent] Failed to connect (attempt ${attempt}):`, error); if (attempt < maxAttempts) { console.log(`[Penpal Parent] Retrying in 1 second...`); await new Promise((resolve) => setTimeout(resolve, 1e3)); if (!isCancelled) { await setupConnection(attempt + 1, maxAttempts); } } else { onAuthError == null ? void 0 : onAuthError(error instanceof Error ? error : new Error("Failed to connect to auth service")); } } }; setupConnection(); return () => { isCancelled = true; }; }, [iframe, authServiceUrl]); (0, import_react.useEffect)(() => { if (isConnected && authService && !hasCheckedAuthRef.current) { hasCheckedAuthRef.current = true; authService.checkAuthStatus(orgId, environment).then((status) => { console.log("[Penpal Parent] Initial auth status:", status); parentMethods.onAuthStatusChange(status); if (status.isAuthenticated) { return authService.getOidc(); } return null; }).then((oidcResult) => { if (oidcResult == null ? void 0 : oidcResult.idToken) { onOidcReceived == null ? void 0 : onOidcReceived(oidcResult.idToken); } }).catch((error) => { console.error("[Penpal Parent] Error checking auth status:", error); setIsLoading(false); }); } }, [isConnected, authService, orgId, environment]); const login = (0, import_react.useCallback)(async (params) => { var _a; if (!authService) { console.warn("[Penpal] Cannot login - not connected"); return; } try { const result = await authService.login(params || {}); setIsAuthenticated(true); if (result.user) { const userInfo = { email: result.user.email, sub: result.user.sub, configured_mfa: [], displayName: result.user.nickname || result.user.name || ((_a = result.user.email) == null ? void 0 : _a.split("@")[0]) || "User", rawUserData: result.user }; setUser(userInfo); } if (result.accessToken) { onOidcReceived == null ? void 0 : onOidcReceived(result.accessToken); } onAuthSuccess == null ? void 0 : onAuthSuccess(); return result; } catch (error) { onAuthError == null ? void 0 : onAuthError(error instanceof Error ? error : new Error("Login failed")); throw error; } }, [authService, setIsAuthenticated, setUser, onOidcReceived, onAuthSuccess, onAuthError]); const signup = (0, import_react.useCallback)(async (email, password) => { var _a; if (!authService) { console.warn("[Penpal] Cannot signup - not connected"); return; } try { const result = await authService.signup({ email, password }); setIsAuthenticated(true); if (result.user) { const userInfo = { email: result.user.email, sub: result.user.sub, configured_mfa: [], displayName: result.user.nickname || result.user.name || ((_a = result.user.email) == null ? void 0 : _a.split("@")[0]) || "User", rawUserData: result.user }; setUser(userInfo); } if (result.accessToken) { onOidcReceived == null ? void 0 : onOidcReceived(result.accessToken); } onAuthSuccess == null ? void 0 : onAuthSuccess(); return result; } catch (error) { onAuthError == null ? void 0 : onAuthError(error instanceof Error ? error : new Error("Signup failed")); throw error; } }, [authService, setIsAuthenticated, setUser, onOidcReceived, onAuthSuccess, onAuthError]); const logout = (0, import_react.useCallback)(async () => { if (!authService) { console.warn("[Penpal] Cannot logout - not connected"); return; } try { await authService.logout(); setIsAuthenticated(false); setUser(null); localStorage.removeItem(AUTH_TOKENS_KEY); localStorage.removeItem(AUTH0_STORAGE_KEYS.IS_AUTHENTICATED); localStorage.removeItem(AUTH0_STORAGE_KEYS.ACCESS_TOKEN); localStorage.removeItem(AUTH0_STORAGE_KEYS.ID_TOKEN); localStorage.removeItem(AUTH0_STORAGE_KEYS.EXPIRES_AT); localStorage.removeItem(CUBIST_USER_ID_KEY); sessionStorage.removeItem(OIDC_TOKEN_KEY); } catch (error) { onAuthError == null ? void 0 : onAuthError(error instanceof Error ? error : new Error("Logout failed")); throw error; } }, [authService, setIsAuthenticated, setUser, onAuthError]); const getOidc = (0, import_react.useCallback)(async () => { if (!authService) { console.warn("[Penpal] Cannot get OIDC - not connected"); return; } try { return await authService.getOidc(); } catch (error) { onAuthError == null ? void 0 : onAuthError(error instanceof Error ? error : new Error("Failed to get OIDC token")); throw error; } }, [authService, onAuthError]); const checkAuthStatus = (0, import_react.useCallback)(async () => { if (!authService) { console.warn("[Penpal] Cannot check auth status - not connected"); return; } try { return await authService.checkAuthStatus(orgId, environment); } catch (error) { onAuthError == null ? void 0 : onAuthError(error instanceof Error ? error : new Error("Failed to check auth status")); throw error; } }, [authService, orgId, environment, onAuthError]); return { authService, isConnected, login, signup, logout, getOidc, checkAuthStatus }; } // src/AuthModalContext.tsx var import_react4 = require("react"); var import_core_k2_components4 = require("@avalabs/core-k2-components"); // src/components/Modal.tsx var import_react3 = require("react"); var import_core_k2_components3 = require("@avalabs/core-k2-components"); // src/components/SignInContent.tsx var import_react2 = require("react"); var import_core_k2_components2 = require("@avalabs/core-k2-components"); // src/components/PoweredByAvaCloud.tsx var import_core_k2_components = require("@avalabs/core-k2-components"); var import_jsx_runtime = require("react/jsx-runtime"); function PoweredByAvaCloud() { return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_core_k2_components.Stack, { direction: "row", alignItems: "center", justifyContent: "center", spacing: 1, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_core_k2_components.Typography, { variant: "body2", children: "Powered by" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_core_k2_components.Stack, { direction: "row", alignItems: "center", spacing: 0.5, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_core_k2_components.AvaCloudConnectIcon, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_core_k2_components.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 var import_jsx_runtime2 = require("react/jsx-runtime"); function SignInContent({ onEmailLogin, onEmailSignup, onProviderLogin, error, isSubmitting, socialLogins = ["google", "x", "apple"] }) { const [email, setEmail] = (0, import_react2.useState)(""); const [password, setPassword] = (0, import_react2.useState)(""); const [isPasswordStep, setIsPasswordStep] = (0, import_react2.useState)(false); const [isSignupMode, setIsSignupMode] = (0, import_react2.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 = import_core_k2_components2.GoogleIcon; break; case "x": case "twitter": LoginIcon = import_core_k2_components2.XTwitterIcon; break; case "facebook": LoginIcon = import_core_k2_components2.FacebookIcon; break; case "apple": LoginIcon = import_core_k2_components2.AppleIcon; break; default: return null; } return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_core_k2_components2.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__ */ (0, import_jsx_runtime2.jsx)(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__ */ (0, import_jsx_runtime2.jsxs)(import_core_k2_components2.Stack, { gap: 3, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_core_k2_components2.Typography, { variant: "h6", sx: titleTypography, children: "Sign up with" }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_core_k2_components2.Stack, { direction: "row", spacing: 3, sx: { justifyContent: "center" }, children: socialLogins.map((loginType) => renderSocialLoginButton(loginType)) }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_core_k2_components2.Divider, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_core_k2_components2.Typography, { variant: "body2", color: "text.secondary", children: "or" }) }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("form", { onSubmit: handleSignupSubmit, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_core_k2_components2.Stack, { gap: 2, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_core_k2_components2.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__ */ (0, import_jsx_runtime2.jsx)( import_core_k2_components2.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__ */ (0, import_jsx_runtime2.jsx)( import_core_k2_components2.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__ */ (0, import_jsx_runtime2.jsxs)( import_core_k2_components2.Typography, { variant: "body2", sx: { textAlign: "center", "& a": { color: "#3A65FF", textDecoration: "none", cursor: "pointer", "&:hover": { textDecoration: "underline" } } }, children: [ "Already have an account?", " ", /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_core_k2_components2.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__ */ (0, import_jsx_runtime2.jsx)(PoweredByAvaCloud, {}), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( import_core_k2_components2.Typography, { 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__ */ (0, import_jsx_runtime2.jsx)( import_core_k2_components2.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__ */ (0, import_jsx_runtime2.jsx)( import_core_k2_components2.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__ */ (0, import_jsx_runtime2.jsxs)(import_core_k2_components2.Stack, { gap: 3, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_core_k2_components2.Typography, { variant: "h6", sx: titleTypography, children: "Sign in with" }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("form", { onSubmit: handlePasswordSubmit, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_core_k2_components2.Stack, { gap: 2, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_core_k2_components2.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__ */ (0, import_jsx_runtime2.jsx)( import_core_k2_components2.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__ */ (0, import_jsx_runtime2.jsx)( import_core_k2_components2.Typography, { variant: "body2", sx: { textAlign: "center", "& a": { color: "#3A65FF", textDecoration: "none", cursor: "pointer", "&:hover": { textDecoration: "underline" } } }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_core_k2_components2.Button, { variant: "text", onClick: handleForgotPassword, sx: { color: "#3A65FF", textTransform: "none", "&:hover": { textDecoration: "underline", backgroundColor: "transparent" } }, children: "Forget password?" } ) } ) ] }) }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PoweredByAvaCloud, {}), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( import_core_k2_components2.Typography, { 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__ */ (0, import_jsx_runtime2.jsx)( import_core_k2_components2.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__ */ (0, import_jsx_runtime2.jsx)( import_core_k2_components2.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__ */ (0, import_jsx_runtime2.jsxs)(import_core_k2_components2.Stack, { gap: 3, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_core_k2_components2.Typography, { variant: "h6", sx: titleTypography, children: "Sign in with" }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_core_k2_components2.Stack, { direction: "row", spacing: 3, sx: { justifyContent: "center" }, children: socialLogins.map((loginType) => renderSocialLoginButton(loginType)) }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_core_k2_components2.Divider, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_core_k2_components2.Typography, { variant: "body2", color: "text.secondary", children: "or" }) }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("form", { onSubmit: handleEmailSubmit, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_core_k2_components2.Stack, { gap: 2, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_core_k2_components2.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__ */ (0, import_jsx_runtime2.jsx)( import_core_k2_components2.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__ */ (0, import_jsx_runtime2.jsxs)( import_core_k2_components2.Typography, { variant: "body2", sx: { textAlign: "center", "& a": { color: "#3A65FF", textDecoration: "none", cursor: "pointer", "&:hover": { textDecoration: "underline" } } }, children: [ "Don't have an account?", " ", /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_core_k2_components2.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__ */ (0, import_jsx_runtime2.jsx)(PoweredByAvaCloud, {}), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( import_core_k2_components2.Typography, { 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__ */ (0, import_jsx_runtime2.jsx)( import_core_k2_components2.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__ */ (0, import_jsx_runtime2.jsx)( import_core_k2_components2.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 var import_jsx_runtime3 = require("react/jsx-runtime"); function LoginModal({ open, onClose }) { var _a; const { orgConfig, signup, login, isAuthenticated } = useAvaCloudWallet(); const [isSubmitting, setIsSubmitting] = (0, import_react3.useState)(false); const [error, setError] = (0, import_react3.useState)(""); const timeoutIdRef = (0, import_react3.useRef)(null); const socialLogins = (_a = orgConfig == null ? void 0 : orgConfig.adminPortalSettings) == null ? void 0 : _a.socialLogins; (0, import_react3.useEffect)(() => { if (isAuthenticated && open) { setIsSubmitting(false); if (timeoutIdRef.current) { clearTimeout(timeoutIdRef.current); timeoutIdRef.current = null; } onClose(); } }, [isAuthenticated, open, onClose]); (0, import_react3.useEffect)(() => { if (!open) { setIsSubmitting(false); setError(""); if (timeoutIdRef.current) { clearTimeout(timeoutIdRef.current); timeoutIdRef.current = null; } } }, [open]); (0, import_react3.useEffect)(() => { return () => { if (timeoutIdRef.current) { clearTimeout(timeoutIdRef.current); timeoutIdRef.current = null; } }; }, []); const handleProviderLogin = (0, import_react3.useCallback)((provider) => { setError(""); setIsSubmitting(true); login({ connection: provider }); }, [login]); const handleEmailLogin = (0, import_react3.useCallback)((email, password) => { setError(""); setIsSubmitting(true); if (timeoutIdRef.current) { clearTimeout(timeoutIdRef.current); timeoutIdRef.current = null; } login({ email, password }); timeoutIdRef.current = setTimeout(() => { setError("Authentication service timed out"); setIsSubmitting(false); timeoutIdRef.current = null; }, 1e4); }, [login]); const handleEmailSignup = (0, import_react3.useCallback)((email, password) => { setError(""); setIsSubmitting(true); if (timeoutIdRef.current) { clearTimeout(timeoutIdRef.current); timeoutIdRef.current = null; } signup(email, password); timeoutIdRef.current = setTimeout(() => { setError("Authentication service timed out"); setIsSubmitting(false); timeoutIdRef.current = null; }, 1e4); }, [signup]); return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)( import_core_k2_components3.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__ */ (0, import_jsx_runtime3.jsxs)( import_core_k2_components3.Stack, { direction: "row", sx: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", alignSelf: "stretch", p: 3, pb: 0 }, children: [ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "img", { src: "https://images.ctfassets.net/9bazykntljf6/58QaXZf2yQ7MqI9A8MrKiX/d8f986355c6e321e1dee79f6e91575ec/avacloud.png", alt: "AvaCloud", style: { height: 24, objectFit: "contain" } } ), /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( import_core_k2_components3.IconButton, { 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__ */ (0, import_jsx_runtime3.jsx)(import_core_k2_components3.XIcon, { sx: { fontSize: 20 } }) } ) ] } ), /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_core_k2_components3.DialogContent, { sx: { p: 3 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( SignInContent, { onEmailLogin: handleEmailLogin, onEmailSignup: handleEmailSignup, onProviderLogin: handleProviderLogin, error, isSubmitting, socialLogins } ) }) ] } ); } // src/AuthModalContext.tsx var import_jsx_runtime4 = require("react/jsx-runtime"); var AuthModalContext = (0, import_react4.createContext)(void 0); function AuthModalProvider({ children }) { const [isModalOpen, setIsModalOpen] = (0, import_react4.useState)(false); const openLoginModal = (0, import_react4.useCallback)(() => setIsModalOpen(true), []); const closeLoginModal = (0, import_react4.useCallback)(() => setIsModalOpen(false), []); return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(AuthModalContext.Provider, { value: { openLoginModal, closeLoginModal, isModalOpen }, children: [ children, /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)( import_core_k2_components4.Dialog, { open: isModalOpen, onClose: closeLoginModal, maxWidth: "xs", children: [ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_core_k2_components4.DialogTitle, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { src: "", alt: "AvaCloud Connect", style: { height: "32px" } }) }), /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_core_k2_components4.DialogContent, { sx: { padding: 4 }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)( LoginModal, { open: isModalOpen, onClose: closeLoginModal } ) }) ] } ) ] }); } function useAuthModal() { const context = (0, import_react4.useContext)(AuthModalContext); if (context === void 0) { throw new Error("useAuthModal must be used within an AuthModalProvider"); } return context; } // src/AvaCloudWalletProvider.tsx var import_cubesigner_sdk2 = require("@cubist-labs/cubesigner-sdk"); // src/providers/ViemContext.tsx var import_react6 = require("react"); var import_viem = require("viem"); // src/hooks/useAuth.ts var import_react5 = require("react"); function useAuth() { const { isAuthenticated, isLoading, user, wallet, logout, loginWithCubist, cubistClient, cubistError } = useAvaCloudWallet(); const { openLoginModal } = useAuthModal(); const login = (0, import_react5.useCallback)(() => { openLoginModal(); }, [openLoginModal]); return { isAuthenticated, isLoading, user, wallet, login, logout, loginWithCubist, cubistClient, cubistError }; } // src/providers/ViemContext.tsx var import_jsx_runtime5 = require("react/jsx-runtime"); var ViemContext = (0, import_react6.createContext)(null); function ViemProvider({ children, rpcUrl, chainId, explorerUrl }) { var _a, _b; const [publicClient, setPublicClient] = (0, import_react6.useState)(null); const [walletClient, setWalletClient] = (0, import_react6.useState)(null); const [isConnected, setIsConnected] = (0, import_react6.useState)(false); const [error, setError] = (0, import_react6.useState)(null); const { cubistClient, wallet: authWallet } = useAuth(); (0, import_react6.useEffect)(() => { const initClient = async () => { var _a2; try { const transport = (0, import_viem.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 = (0, import_viem.createPublicClient)({ transport, chain: customChain }); if ((_a2 = authWallet == null ? void 0 : authWallet.cubistWallet) == null ? void 0 : _a2.address) { const walletInstance = (0, import_viem.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]); (0, import_react6.useEffect)(() => { 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__ */ (0, import_jsx_runtime5.jsx)( ViemContext.Provider, { value: { publicClient, walletClient, setPublicClient, setWalletClient, chainId, explorerUrl, isConnected, error, clearError }, children } ); } function useViem() { const context = (0, import_react6.useContext)(ViemContext); if (!context) { throw new Error("useViem must be used within a ViemProvider"); } return context; } // src/hooks/useGlacier.ts var import_react_query = require("@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 var import_react7 = require("react"); var CHAIN_ID_STORAGE_KEY = "avalanche-chain-id"; var DEFAULT_CHAIN_ID = 43113; function useChainId() { const [chainId, setChainIdState] = (0, import_react7.useState)(() => { 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.blockchain