UNPKG

@turnkey/react-wallet-kit

Version:

The easiest and most powerful way to integrate Turnkey's Embedded Wallets into your React applications.

278 lines (274 loc) 13 kB
'use strict'; var jsxRuntime = require('react/jsx-runtime'); var react = require('react'); var Hook$1 = require('../../providers/modal/Hook.js'); var Hook = require('../../providers/client/Hook.js'); var iframeStamper = require('@turnkey/iframe-stamper'); var sdkTypes = require('@turnkey/sdk-types'); var Buttons = require('../design/Buttons.js'); var react$1 = require('@headlessui/react'); var core = require('@turnkey/core'); var Success = require('../design/Success.js'); var clsx = require('clsx'); var base = require('../../types/base.js'); const TurnkeyImportIframeContainerId = "turnkey-import-iframe-container-id"; const TurnkeyIframeElementId = "turnkey-default-iframe-element-id"; const TurnkeyIframeClassNames = "w-full h-full overflow-hidden border-none !text-base bg-icon-background-light dark:bg-icon-background-dark"; // IMPORTANT: These colors need to match --icon-text-light, --icon-background-light, --icon-background-dark and --icon-text-dark in index.css const iconBackgroundLight = "#e5e7eb"; const iconBackgroundDark = "#333336"; const iconTextLight = "#828282"; const iconTextDark = "#a3a3a5"; function ImportComponent(props) { const { importType, curve = "CURVE_SECP256K1", addressFormats = ["ADDRESS_FORMAT_ETHEREUM"], onSuccess, onError, defaultWalletAccounts, successPageDuration, clearClipboardOnPaste, stampWith, name, keyFormat } = props; const { config, session, importWallet, importPrivateKey, httpClient } = Hook.useTurnkey(); if (!config) { throw new sdkTypes.TurnkeyError("Turnkey SDK is not properly configured. Please check your configuration.", sdkTypes.TurnkeyErrorCodes.CONFIG_NOT_INITIALIZED); } const organizationId = props.organizationId || session?.organizationId; if (!organizationId) { throw new sdkTypes.TurnkeyError("Organization ID or a valid session is required for importing. Please pass in an organizationId or log in.", sdkTypes.TurnkeyErrorCodes.IMPORT_WALLET_ERROR); } const userId = props.userId || session?.userId; if (!userId) { throw new sdkTypes.TurnkeyError("User ID or a valid session is required for importing. Please pass in a userId or log in.", sdkTypes.TurnkeyErrorCodes.IMPORT_WALLET_ERROR); } const [walletName, setWalletName] = react.useState(name || ""); const [isLoading, setIsLoading] = react.useState(false); const [error, setError] = react.useState(null); const [shaking, setShaking] = react.useState(false); const shakeInput = () => { setShaking(true); setTimeout(() => setShaking(false), 250); }; const importIframeUrl = config?.importIframeUrl; const { closeModal, pushPage, isMobile } = Hook$1.useModal(); const [importIframeClient, setImportIframeClient] = react.useState(null); const subtitle = importType === base.ImportType.Wallet ? "Enter your seed phrase. Seed phrases are typically 12-24 words." : importType === base.ImportType.PrivateKey ? "Enter your private key." : ""; const placeholder = importType === base.ImportType.Wallet ? "Enter your wallet name" : importType === base.ImportType.PrivateKey ? "Enter your private key name" : ""; react.useEffect(() => { const initIframe = async () => { try { const newImportIframeClient = new iframeStamper.IframeStamper({ iframeUrl: importIframeUrl, iframeElementId: TurnkeyIframeElementId, iframeContainer: document.getElementById(TurnkeyImportIframeContainerId), clearClipboardOnPaste }); await newImportIframeClient.init(); await newImportIframeClient.applySettings({ styles: { fontSize: "16px", // IMPORTANT: These colors need to match --icon-text-light and --icon-text-dark in index.css backgroundColor: config?.ui?.darkMode ? config?.ui?.colors?.dark?.iconBackground || iconBackgroundDark : config?.ui?.colors?.light?.iconBackground || iconBackgroundLight, color: config?.ui?.darkMode ? config?.ui?.colors?.dark?.iconText || iconTextDark : config?.ui?.colors?.light?.iconText || iconTextLight } }); setImportIframeClient(newImportIframeClient); } catch (error) { throw new sdkTypes.TurnkeyError(`Error initializing IframeStamper`, sdkTypes.TurnkeyErrorCodes.INTERNAL_ERROR, error); } }; const existingIframe = document.getElementById(TurnkeyIframeElementId); if (!existingIframe) { initIframe(); } const iframeElement = document.getElementById(TurnkeyIframeElementId); if (iframeElement) { iframeElement.className = TurnkeyIframeClassNames; } return () => { handleImportModalClose(); }; }, []); react.useEffect(() => { const reapplyIframeStyles = async () => { await importIframeClient?.applySettings({ styles: { fontSize: "16px", backgroundColor: config?.ui?.darkMode ? config?.ui?.colors?.dark?.iconBackground || iconBackgroundDark : config?.ui?.colors?.light?.iconBackground || iconBackgroundLight, color: config?.ui?.darkMode ? config?.ui?.colors?.dark?.iconText || iconTextDark : config?.ui?.colors?.light?.iconText || iconTextLight } }); }; reapplyIframeStyles(); }, [config.ui]); function handleImportModalClose() { if (importIframeClient) { setImportIframeClient(null); const existingIframe = document.getElementById(TurnkeyIframeElementId); if (existingIframe) { existingIframe.remove(); } } } async function handleImport() { setIsLoading(true); try { if (!importIframeClient) { throw new sdkTypes.TurnkeyError("Import iframe client not initialized", sdkTypes.TurnkeyErrorCodes.INTERNAL_ERROR); } let response; switch (importType) { case base.ImportType.Wallet: const initWalletResult = await httpClient?.initImportWallet({ organizationId: organizationId, userId: userId }, stampWith); if (!initWalletResult || !initWalletResult.importBundle) { throw new sdkTypes.TurnkeyError("Failed to retrieve import bundle", sdkTypes.TurnkeyErrorCodes.IMPORT_WALLET_ERROR); } const injectedWallet = await importIframeClient.injectImportBundle(initWalletResult.importBundle, organizationId, userId); if (!injectedWallet) { throw new sdkTypes.TurnkeyError("Failed to inject import bundle", sdkTypes.TurnkeyErrorCodes.IMPORT_WALLET_ERROR); } const encryptedWalletBundle = await importIframeClient.extractWalletEncryptedBundle(); if (!encryptedWalletBundle || encryptedWalletBundle.trim() === "") { throw new sdkTypes.TurnkeyError("Encrypted bundle is empty", sdkTypes.TurnkeyErrorCodes.IMPORT_WALLET_ERROR); } let accounts = []; if (Array.isArray(defaultWalletAccounts) && defaultWalletAccounts.length > 0 && defaultWalletAccounts[0]?.addressFormat === undefined) { accounts = core.generateWalletAccountsFromAddressFormat({ addresses: defaultWalletAccounts }); } else if (Array.isArray(defaultWalletAccounts)) { accounts = defaultWalletAccounts; } response = await importWallet({ walletName: walletName, accounts, encryptedBundle: encryptedWalletBundle, stampWith, organizationId: organizationId, userId: userId }); break; case base.ImportType.PrivateKey: const initPrivateKeyResult = await httpClient?.initImportPrivateKey({ organizationId: organizationId, userId: userId }, stampWith); if (!initPrivateKeyResult || !initPrivateKeyResult.importBundle) { throw new sdkTypes.TurnkeyError("Failed to retrieve import bundle", sdkTypes.TurnkeyErrorCodes.IMPORT_WALLET_ERROR); } const injectedKey = await importIframeClient.injectImportBundle(initPrivateKeyResult.importBundle, organizationId, userId); if (!injectedKey) { throw new sdkTypes.TurnkeyError("Failed to inject import bundle", sdkTypes.TurnkeyErrorCodes.IMPORT_WALLET_ERROR); } const encryptedKeyBundle = await importIframeClient.extractKeyEncryptedBundle(keyFormat); if (!encryptedKeyBundle || encryptedKeyBundle.trim() === "") { throw new sdkTypes.TurnkeyError("Encrypted bundle is empty", sdkTypes.TurnkeyErrorCodes.IMPORT_WALLET_ERROR); } response = await importPrivateKey({ addressFormats, curve, privateKeyName: walletName, encryptedBundle: encryptedKeyBundle, stampWith, organizationId: organizationId, userId: userId }); break; default: throw new sdkTypes.TurnkeyError("Invalid import type", sdkTypes.TurnkeyErrorCodes.IMPORT_WALLET_ERROR); } if (response) { onSuccess(response); if (successPageDuration && successPageDuration !== 0) { pushPage({ key: "success", content: jsxRuntime.jsx(Success.SuccessPage, { text: importType === base.ImportType.Wallet ? "Wallet imported successfully!" : importType === base.ImportType.PrivateKey ? "Private key imported successfully!" : "Success!", duration: successPageDuration, onComplete: () => { handleImportModalClose(); closeModal(); } }), preventBack: true, showTitle: false }); } else { handleImportModalClose(); closeModal(); } handleImportModalClose(); } else { await importIframeClient.clear(); throw new sdkTypes.TurnkeyError("Failed to import wallet", sdkTypes.TurnkeyErrorCodes.IMPORT_WALLET_ERROR); } } catch (error) { shakeInput(); setError(error instanceof sdkTypes.TurnkeyError ? error : new sdkTypes.TurnkeyError(`Error importing wallet`, sdkTypes.TurnkeyErrorCodes.IMPORT_WALLET_ERROR, error)); if (error instanceof sdkTypes.TurnkeyError) onError(error); throw new sdkTypes.TurnkeyError(`Error importing wallet`, sdkTypes.TurnkeyErrorCodes.IMPORT_WALLET_ERROR, error); } finally { setIsLoading(false); } } return jsxRuntime.jsxs("div", { className: clsx("flex flex-col items-center pt-4", isMobile ? "w-full" : "w-[21rem]"), children: [jsxRuntime.jsx("p", { className: "text-sm text-icon-text-light dark:text-icon-text-dark", children: subtitle }), jsxRuntime.jsx("div", { id: TurnkeyImportIframeContainerId, style: { height: "100%", overflow: "hidden", display: "block", backgroundColor: config?.ui?.darkMode ? config?.ui?.colors?.dark?.iconBackground || iconBackgroundDark : config?.ui?.colors?.light?.iconBackground || iconBackgroundLight, width: "100%", boxSizing: "border-box", padding: "5px", borderStyle: "solid", borderWidth: "1px", borderRadius: "8px", borderColor: config?.ui?.darkMode ? config?.ui?.colors?.dark?.iconText || iconTextDark : config?.ui?.colors?.light?.iconText || iconTextLight }, className: `transition-all ${shaking ? "animate-shake" : ""}` }), !name && jsxRuntime.jsx(react$1.Input, { type: "text", "data-testid": "import-wallet-name-input", placeholder: placeholder, value: walletName, onChange: e => setWalletName(e.target.value), className: "placeholder:text-icon-text-light dark:placeholder:text-icon-text-dark w-full mt-2 py-3 px-3 rounded-md text-inherit bg-icon-background-light dark:bg-icon-background-dark border border-modal-background-dark/20 dark:border-modal-background-light/20 focus:outline-primary-light focus:dark:outline-primary-dark focus:outline-[1px] focus:outline-offset-0 box-border" }), jsxRuntime.jsx(Buttons.ActionButton, { name: "import-button", loading: isLoading, spinnerClassName: "text-primary-text-light dark:text-primary-text-dark", onClick: handleImport, className: "bg-primary-light mt-2 dark:bg-primary-dark text-primary-text-light dark:text-primary-text-dark", children: "Import" }), jsxRuntime.jsxs("p", { "data-testid": "import-error-message", className: clsx("text-sm text-red-500 transition-opacity delay-75 line-clamp-2 w-full", error ? "opacity-100 pointer-events-auto mt-2" : "opacity-0 pointer-events-none absolute"), children: [error?.message, ":", " ", error?.cause instanceof sdkTypes.TurnkeyError ? error?.cause.message : error?.cause?.toString() || "Unknown error"] })] }); } exports.ImportComponent = ImportComponent; //# sourceMappingURL=Import.js.map