@avalabs/avacloud-waas-react
Version:
React SDK for AvaCloud Wallet as a Service
1,431 lines (1,408 loc) • 204 kB
JavaScript
"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