@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
JavaScript
'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