UNPKG

@dynamic-labs/sdk-react-core

Version:

A React SDK for implementing wallet web3 authentication and authorization to your website.

347 lines (344 loc) 24 kB
'use client' import { __awaiter } from '../../../../../_virtual/_tslib.js'; import { jsxs, jsx, Fragment } from 'react/jsx-runtime'; import { useState, useRef, useEffect, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { DynamicError, AccessBlockedError } from '@dynamic-labs/utils'; import { isSessionKeyCompatibleWalletConnector } from '@dynamic-labs/wallet-connector-core'; import { Badge } from '../../../components/Badge/Badge.js'; import { Checkbox } from '../../../components/Checkbox/Checkbox.js'; import { ErrorContainer } from '../../../components/ErrorContainer/ErrorContainer.js'; import { IconButton } from '../../../components/IconButton/IconButton.js'; import { ModalHeader } from '../../../components/ModalHeader/ModalHeader.js'; import { Typography } from '../../../components/Typography/Typography.js'; import { TypographyButton } from '../../../components/TypographyButton/TypographyButton.js'; import '../../../context/DynamicContext/DynamicContext.js'; import '../../../store/state/loadingAndLifecycle/loadingAndLifecycle.js'; import '@dynamic-labs/sdk-api-core'; import { logger } from '../../../shared/logger.js'; import '@dynamic-labs/iconic'; import { ReactComponent as SvgClose } from '../../../shared/assets/close.js'; import { ReactComponent as SvgExportEmbeddedHero } from '../../../shared/assets/export-embedded-hero.js'; import { ReactComponent as SvgEyeOffIcon } from '../../../shared/assets/eye-off-icon.js'; import { ReactComponent as SvgInfo } from '../../../shared/assets/info.js'; import { ReactComponent as SvgWalletV2 } from '../../../shared/assets/wallet-v2.js'; import { useViewContext } from '../../../context/ViewContext/ViewContext.js'; import '@dynamic-labs/wallet-book'; import '../../../utils/constants/colors.js'; import '../../../utils/constants/values.js'; import '../../../shared/consts/index.js'; import { dynamicEvents } from '../../../events/dynamicEvents.js'; import '../../../context/CaptchaContext/CaptchaContext.js'; import '../../../context/ErrorContext/ErrorContext.js'; import { INVALID_WALLET_DATA } from '../../../utils/constants/errors.js'; import '@dynamic-labs/multi-wallet'; import 'react-international-phone'; import '../../../store/state/nonce/nonce.js'; import '../../../store/state/projectSettings/projectSettings.js'; import '../../../config/ApiEndpoint.js'; import '../../../store/state/user/user.js'; import '../../../locale/locale.js'; import '../../../store/state/dynamicContextProps/dynamicContextProps.js'; import '../../../store/state/primaryWalletId/primaryWalletId.js'; import '../../../store/state/connectedWalletsInfo/connectedWalletsInfo.js'; import '../../../context/AccessDeniedContext/AccessDeniedContext.js'; import '../../../context/AccountExistsContext/AccountExistsContext.js'; import '../../../context/UserWalletsContext/UserWalletsContext.js'; import '../../../store/state/authMode/authMode.js'; import '../../../context/VerificationContext/VerificationContext.js'; import 'react-dom'; import '../../../utils/functions/compareChains/compareChains.js'; import '../../Passkey/utils/findPrimaryEmbeddedChain/findPrimaryEmbeddedChain.js'; import { getPrimaryTurnkeyWalletId } from '../../../utils/functions/getPrimaryTurnkeyWalletId/getPrimaryTurnkeyWalletId.js'; import '../../../context/ThemeContext/ThemeContext.js'; import { useSmartWallets } from '../../../utils/hooks/useSmartWallets/useSmartWallets.js'; import '../../../utils/hooks/useUserUpdateRequest/useUpdateUser/userFieldsSchema.js'; import { useMutation } from '../../../utils/hooks/useMutation/useMutation.js'; import { useIsTurnkeyWallet } from '../../../utils/hooks/useIsTurnkeyWallet/useIsTurnkeyWallet.js'; import 'bs58'; import { usePromise } from '../../../utils/hooks/usePromise/usePromise.js'; import '@dynamic-labs/types'; import '../../../context/SocialRedirectContext/SocialRedirectContext.js'; import '../../../context/LoadingContext/LoadingContext.js'; import '../../../context/WalletContext/WalletContext.js'; import { cleanupExport, exportCredential, initExport } from '../utils/turnkeyExport/turnkeyExport.js'; import '../../../utils/hooks/useEmbeddedWallet/useSecureEnclaveEmbeddedWallet/constants.js'; import 'yup'; import '../../../context/MockContext/MockContext.js'; import '../../CollectUserDataView/useFields.js'; import '../../../context/FieldsStateContext/FieldsStateContext.js'; import '../../../context/UserFieldEditorContext/UserFieldEditorContext.js'; import '@dynamic-labs/rpc-providers'; import '../../../store/state/walletOptions/walletOptions.js'; import '../../../components/Accordion/components/AccordionItem/AccordionItem.js'; import '../../../components/Alert/Alert.js'; import '../../../components/ShadowDOM/ShadowDOM.js'; import '../../../components/InlineWidget/InlineWidget.js'; import '../../../components/Input/Input.js'; import '../../../components/IsBrowser/IsBrowser.js'; import '../../../components/MenuList/Dropdown/Dropdown.js'; import '../../../components/OverlayCard/OverlayCard.js'; import '../../../components/Transition/ZoomTransition/ZoomTransition.js'; import '../../../components/Transition/SlideInUpTransition/SlideInUpTransition.js'; import '../../../components/Transition/OpacityTransition/OpacityTransition.js'; import { NeedHelpSection } from '../../../components/NeedHelpSection/NeedHelpSection.js'; import '../../../components/PasskeyCreatedSuccessBanner/PasskeyCreatedSuccessBanner.js'; import '../../../components/Popper/Popper/Popper.js'; import '../../../components/Popper/PopperContext/PopperContext.js'; import 'react-focus-lock'; import 'qrcode'; import 'formik'; import '../../../utils/hooks/useSubdomainCheck/useSubdomainCheck.js'; import '../../../context/WalletGroupContext/WalletGroupContext.js'; import '../../../context/IpConfigurationContext/IpConfigurationContext.js'; import '../../../context/ConnectWithOtpContext/ConnectWithOtpContext.js'; import '../../../widgets/DynamicBridgeWidget/views/WalletsView/components/SecondaryWallets/SecondaryWallets.js'; import '@hcaptcha/react-hcaptcha'; import { getProperErrorMessage } from '../../../modals/SignMessageConfirmationModal/getProperErrorMessage.js'; import { useWidgetContext } from '../../../widgets/DynamicWidget/context/DynamicWidgetContext.js'; import { iframeContainerId, iframeElementId } from '../constants.js'; import { cleanupExport as cleanupExport$1, exportWaasCredential } from '../utils/waasExport/waasExport.js'; import '../../../context/FooterAnimationContext/index.js'; import '../../../context/ErrorContext/hooks/useErrorText/useErrorText.js'; import '../../../context/PasskeyContext/PasskeyContext.js'; import '../../../widgets/DynamicWidget/helpers/convertExchangeKeyAndProviderEnum.js'; import '../../../store/state/sendBalances.js'; import '../../../store/state/connectorsInitializing/connectorsInitializing.js'; import '../../../components/OverlayCardBase/OverlayCardTarget/OverlayCardTarget.js'; import '../../../widgets/DynamicWidget/components/DynamicWidgetHeader/DynamicWidgetHeader.js'; import '../../TransactionConfirmationView/TransactionConfirmationView.js'; import '../../../widgets/DynamicWidget/views/ManagePasskeysWidgetView/PasskeyCard/PasskeyCard.js'; import '../../../context/OnrampContext/OnrampContext.js'; import '../../../widgets/DynamicWidget/views/ReceiveWalletFunds/ReceiveWalletFunds.js'; import '../../../../index.js'; import '../../../store/state/tokenBalances.js'; import { useSyncEmbeddedWalletFlow } from '../../../utils/hooks/useSyncEmbeddedWalletFlow/useSyncEmbeddedWalletFlow.js'; import '../../../shared/utils/functions/getInitialUrl/getInitialUrl.js'; import { useInternalDynamicContext } from '../../../context/DynamicContext/useDynamicContext/useInternalDynamicContext/useInternalDynamicContext.js'; const EmbeddedRevealView = ({ exportPrivateKey, isPromptForExport = false, }) => { var _a, _b, _c; const { primaryWallet, user, setShowAuthFlow, environmentId } = useInternalDynamicContext(); const { handleAcknowledgeExportPrompt } = useSyncEmbeddedWalletFlow(); const { isTurnkeyWallet } = useIsTurnkeyWallet(); const isWaasWallet = user === null || user === void 0 ? void 0 : user.verifiedCredentials.find((vc) => { var _a; return (_a = vc.walletName) === null || _a === void 0 ? void 0 : _a.startsWith('dynamicwaas'); }); const { setDynamicWidgetView } = useWidgetContext(); const { pushView } = useViewContext(); const { getEOAWallet, isSmartWallet } = useSmartWallets(); const [acknowledgement1, setAcknowledgement1] = useState(false); const iframeContainerRef = useRef(null); if (!primaryWallet) { throw new DynamicError('No primary wallet found', INVALID_WALLET_DATA); } let primaryTurnkeyWalletId; try { primaryTurnkeyWalletId = getPrimaryTurnkeyWalletId(primaryWallet.id, (user === null || user === void 0 ? void 0 : user.verifiedCredentials) || []); } catch (error) { // waas wallet } const walletProperties = (_b = (_a = user === null || user === void 0 ? void 0 : user.verifiedCredentials) === null || _a === void 0 ? void 0 : _a.find(({ walletName, id }) => (walletName === null || walletName === void 0 ? void 0 : walletName.startsWith('turnkey')) && id === primaryTurnkeyWalletId)) === null || _b === void 0 ? void 0 : _b.walletProperties; const isTurnkeyHDWallet = walletProperties === null || walletProperties === void 0 ? void 0 : walletProperties.turnkeyHDWalletId; const wallet = (_c = (primaryWallet && getEOAWallet(primaryWallet))) !== null && _c !== void 0 ? _c : primaryWallet; const [showSkeleton, setShowSkeleton] = useState(false); // eslint-disable-next-line arrow-body-style useEffect(() => { return () => { if (isTurnkeyWallet) { cleanupExport({ wallet: wallet, }); } if (isWaasWallet) { cleanupExport$1({ wallet: wallet, }); } }; // only run once on unmount // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const { isLoading: exportLoading } = usePromise(() => __awaiter(void 0, void 0, void 0, function* () { var _d, _e; const iframeContainerElement = iframeContainerRef.current; if (!iframeContainerElement || ((_d = iframeContainerElement === null || iframeContainerElement === void 0 ? void 0 : iframeContainerElement.children) === null || _d === void 0 ? void 0 : _d.length) > 0) { return; } if (isSessionKeyCompatibleWalletConnector(wallet === null || wallet === void 0 ? void 0 : wallet.connector)) { yield ((_e = wallet === null || wallet === void 0 ? void 0 : wallet.connector) === null || _e === void 0 ? void 0 : _e.createOrRestoreSession()); } if (isTurnkeyWallet) { return initExport({ iframeContainer: iframeContainerElement, iframeElementId, wallet: wallet, }); } return; })); const { mutate: handleExportWallet, isLoading, error, data, } = useMutation(() => __awaiter(void 0, void 0, void 0, function* () { var _f, _g, _h; handleAcknowledgeExportPrompt(); if (isTurnkeyWallet) { try { return yield exportCredential({ address: exportPrivateKey ? wallet === null || wallet === void 0 ? void 0 : wallet.address : undefined, environmentId, user, wallet: wallet, }); } catch (_j) { if (isSessionKeyCompatibleWalletConnector(wallet === null || wallet === void 0 ? void 0 : wallet.connector) && ((_f = wallet === null || wallet === void 0 ? void 0 : wallet.connector) === null || _f === void 0 ? void 0 : _f.removeSessionKeys)) { yield ((_g = wallet === null || wallet === void 0 ? void 0 : wallet.connector) === null || _g === void 0 ? void 0 : _g.removeSessionKeys()); yield ((_h = wallet === null || wallet === void 0 ? void 0 : wallet.connector) === null || _h === void 0 ? void 0 : _h.createOrRestoreSession({ ignoreRestore: true, })); } return exportCredential({ address: exportPrivateKey ? wallet === null || wallet === void 0 ? void 0 : wallet.address : undefined, environmentId, user, wallet: wallet, }); } } if (isWaasWallet) { try { return yield exportWaasCredential({ address: exportPrivateKey ? wallet === null || wallet === void 0 ? void 0 : wallet.address : undefined, iframeContainer: iframeContainerRef.current, user, wallet: wallet, }); } catch (_k) { return undefined; } } return undefined; }), { onFailure: (err) => { logger.error('Failed to export', err); dynamicEvents.emit('embeddedWalletRevealFailed', err); }, }); const errorText = useMemo(() => { if (!error) { return undefined; } if (error instanceof DynamicError) { return error.message; } try { return getProperErrorMessage(error, user); } catch (e) { if (e instanceof AccessBlockedError) { pushView('access-blocked'); } return; } }, [error, pushView, user]); const { t } = useTranslation(); const contentHeader = (jsxs("div", { children: [primaryWallet && isSmartWallet(primaryWallet) && !isLoading && data && !error && (jsxs("div", { className: 'embedded-reveal-view__zerodev-warning', children: [jsxs("div", { className: 'embedded-reveal-view__zerodev-warning__title-row', children: [jsx(SvgInfo, { className: 'embedded-reveal-view__zerodev-warning__icon' }), jsx(Typography, { variant: 'body_normal', weight: 'bold', copykey: 'dyn_embedded_reveal.aa_warning.title', children: t('dyn_embedded_reveal.aa_warning.title') })] }), jsxs(Typography, { variant: 'body_normal', weight: 'regular', copykey: 'dyn_embedded_reveal.aa_warning.subtitle', children: [t('dyn_embedded_reveal.aa_warning.subtitle'), jsx("button", { onClick: () => { setShowAuthFlow(false); setDynamicWidgetView('send-balance'); }, className: 'embedded-reveal-view__zerodev-warning__link-button', children: jsx(Typography, { variant: 'body_normal', weight: 'regular', color: 'primary', className: 'underline', copykey: 'dyn_embedded_reveal.aa_warning.button', children: t('dyn_embedded_reveal.aa_warning.button') }) })] })] })), jsx("div", { className: 'embedded-reveal-view__body__description', children: !(!isLoading && data && !error) && (jsx("div", { className: 'embedded-reveal-view__header', children: jsx("div", { className: 'embedded-reveal-view__header__hero', children: jsx(SvgExportEmbeddedHero, {}) }) })) })] })); useEffect(() => { if (data) { // Show skeleton first setShowSkeleton(true); // After a delay, hide skeleton and show the iframe const timer = setTimeout(() => { setShowSkeleton(false); }, 2000); return () => clearTimeout(timer); } return; }, [data]); const closeButton = (jsx(IconButton, { onClick: () => { setShowAuthFlow(false); }, type: 'button', children: jsx(SvgClose, {}) })); const hasInjectedCredential = !isLoading && data && !error; const credentialTitle = !isTurnkeyHDWallet || exportPrivateKey ? t('dyn_embedded_reveal.private_key_title') : t('dyn_embedded_reveal.recovery_phrase_title'); const getTitle = () => { if (hasInjectedCredential) { return credentialTitle; } if (isPromptForExport) { return t('dyn_embedded_reveal.prompt_for_export_title'); } return t('dyn_embedded_reveal.agreement_title'); }; const title = getTitle(); const handleClickUnlink = () => { pushView('embedded-delete-view'); }; // Extract nested ternaries into separate functions for clarity const getBodyClassName = () => { const baseClass = 'embedded-reveal-view__body'; if (isWaasWallet && hasInjectedCredential && title === credentialTitle) { return `${baseClass} ${baseClass}--waas-credentials`; } return baseClass; }; const getCredentialContainerClassName = () => { const baseClass = 'embedded-reveal-view__body__credential-container'; if (title !== credentialTitle) { return `${baseClass} ${baseClass}--hidden`; } if (isWaasWallet) { return `${baseClass} ${baseClass}--waas`; } return baseClass; }; const getIframeContainerClassName = () => { const baseClass = 'embedded-reveal-view__body__iframe-container'; if (!data) { return `${baseClass} ${baseClass}--hidden`; } if (showSkeleton) { return `${baseClass} ${baseClass}--invisible`; } return baseClass; }; const getSkeletonLines = () => { const lineCount = 5; // Updated for 110px height const lines = []; for (let i = 0; i < lineCount; i++) { const isLastLine = i === lineCount - 1; const className = isLastLine ? 'embedded-reveal-view__body__skeleton-line embedded-reveal-view__body__skeleton-line--short' : 'embedded-reveal-view__body__skeleton-line'; lines.push(jsx("div", { className: className, "data-testid": i === 0 ? 'private-key-skeleton' : undefined }, i)); } return lines; }; return (jsxs(Fragment, { children: [jsx(ModalHeader, { trailing: isPromptForExport ? null : closeButton, children: jsx(Typography, { as: 'h1', variant: 'title', color: 'primary', "data-testid": 'dynamic-auth-modal-heading', className: 'header__typography', children: title }) }), jsx("div", { className: 'embedded-reveal-view', children: jsxs("div", { className: getBodyClassName(), children: [(!isWaasWallet || !hasInjectedCredential || title !== credentialTitle) && contentHeader, isTurnkeyWallet || isWaasWallet ? (jsxs("div", { className: getCredentialContainerClassName(), children: [jsx("div", { id: iframeContainerId, className: getIframeContainerClassName(), ref: iframeContainerRef }), showSkeleton && data && (jsx("div", { className: 'embedded-reveal-view__body__skeleton-container', children: getSkeletonLines() }))] })) : null, hasInjectedCredential ? (jsxs(Fragment, { children: [jsx(TypographyButton, { buttonPadding: 'medium', buttonVariant: 'brand-primary', onClick: () => { setShowAuthFlow(false); }, loading: isLoading, copykey: 'dyn_embedded_reveal.done_button_label', typographyProps: { color: 'inherit', }, children: t('dyn_embedded_reveal.done_button_label') }), !exportPrivateKey && (jsx(TypographyButton, { buttonClassName: 'embedded-reveal-view__body__unlink_button', buttonVariant: 'tertiary', buttonPadding: 'none', copykey: 'dyn_embedded_reveal.unlink', onClick: handleClickUnlink, typographyProps: { color: 'secondary', variant: 'button_tertiary', }, children: t('dyn_embedded_reveal.unlink') }))] })) : (jsxs(Fragment, { children: [jsxs("div", { className: 'embedded-reveal-view__body__description', children: [jsx("div", { className: 'embedded-reveal-view__body__badge-container', children: jsx(Badge, { text: t('dyn_embedded_reveal.badge_label'), className: 'embedded-reveal-view__body__badge' }) }), jsx(Typography, { variant: 'body_normal', color: 'primary', weight: 'regular', copykey: 'dyn_embedded_reveal.prompt_for_export_description', children: t('dyn_embedded_reveal.prompt_for_export_description') })] }), errorText && jsx(ErrorContainer, { children: errorText }), jsxs("div", { className: 'embedded-reveal-view__body__card', children: [jsxs("div", { className: 'embedded-reveal-view__body__card__statement', children: [jsx("div", { className: 'embedded-reveal-view__body__card__icon', children: jsx(SvgWalletV2, { height: 16, width: 16 }) }), jsxs("div", { className: 'embedded-reveal-view__body__card__statement__text', children: [jsxs(Typography, { variant: 'body_small', color: 'primary', copykey: 'dyn_embedded_reveal.statement_1.title', children: [t('dyn_embedded_reveal.statement_1.title'), ":"] }), jsx(Typography, { variant: 'body_small', color: 'primary', copykey: 'dyn_embedded_reveal.statement_1.description', children: t('dyn_embedded_reveal.statement_1.description') })] })] }), jsxs("div", { className: 'embedded-reveal-view__body__card__statement', children: [jsx("div", { className: 'embedded-reveal-view__body__card__icon', children: jsx(SvgEyeOffIcon, { height: 16, width: 16 }) }), jsx("div", { className: 'embedded-reveal-view__body__card__statement__text', children: jsx(Typography, { variant: 'body_small', color: 'primary', copykey: 'dyn_embedded_reveal.statement_2.title', children: t('dyn_embedded_reveal.statement_2.title') }) })] })] }), jsx("div", { className: 'embedded-reveal-view__body__confirm_card', children: jsxs("button", { className: 'embedded-reveal-view__body__card__acknowledgement', onClick: () => setAcknowledgement1(!acknowledgement1), children: [jsx("div", { children: jsx(Checkbox, { checked: acknowledgement1, onChange: () => setAcknowledgement1(!acknowledgement1), className: 'embedded-reveal-view__body__card__statement__checkbox', id: 'embedded-reveal-checkbox-1' }) }), jsx("div", { children: jsx(Typography, { variant: 'body_small', color: 'primary', style: { letterSpacing: '-0.15px' }, copykey: 'dyn_embedded_reveal.checkbox_label', children: t('dyn_embedded_reveal.checkbox_label') }) })] }) }), jsx(NeedHelpSection, { isExport: true }), jsxs("div", { className: 'embedded-reveal-view__body__button_section', children: [isPromptForExport && (jsx(TypographyButton, { buttonPadding: 'medium', buttonVariant: 'primary', onClick: () => { handleAcknowledgeExportPrompt(); setShowAuthFlow(false); }, dataTestId: 'embedded-reveal-button', copykey: 'dyn_embedded_reveal.skip_button_label', expanded: true, buttonClassName: 'embedded-reveal-view__body__button', children: t('dyn_embedded_reveal.skip_button_label') })), jsx(TypographyButton, { buttonPadding: 'medium', buttonVariant: 'brand-primary', typographyProps: { color: 'inherit', }, onClick: () => handleExportWallet(), disabled: !acknowledgement1 || exportLoading, loading: isLoading, dataTestId: 'embedded-reveal-button', copykey: 'dyn_embedded_reveal.reveal_button_label', style: { width: '100%' }, className: 'embedded-reveal-view__body__button', expanded: true, children: isPromptForExport ? t('dyn_embedded_reveal.backup_button_label') : t('dyn_embedded_reveal.reveal_button_label') })] })] }))] }) })] })); }; export { EmbeddedRevealView };