diff --git a/examples/react-components/src/app/dashboard/page.tsx b/examples/react-components/src/app/dashboard/page.tsx
index c9b5717d..846c42eb 100644
--- a/examples/react-components/src/app/dashboard/page.tsx
+++ b/examples/react-components/src/app/dashboard/page.tsx
@@ -71,6 +71,13 @@ export default function Dashboard() {
const [emailInput, setEmailInput] = useState("");
const [phoneInput, setPhoneInput] = useState("");
+ const handleExportSuccess = async () => {
+ toast.success("Wallet successfully exported");
+ };
+ const handleImportSuccess = async () => {
+ await getWallets();
+ toast.success("Wallet successfully imported");
+ };
const handleResendEmail = async () => {
const initAuthResponse = await authIframeClient?.initOtpAuth({
organizationId: suborgId,
@@ -268,12 +275,11 @@ export default function Dashboard() {
try {
if (turnkey && authIframeClient) {
const session = await turnkey?.getReadWriteSession();
- console.log(session);
- if (!session || Date.now() > session!.sessionExpiry) {
+ if (!session || Date.now() > session!.expiry) {
await handleLogout();
}
await authIframeClient.injectCredentialBundle(
- session.credentialBundle
+ session!.credentialBundle
);
const whoami = await authIframeClient?.getWhoami();
const suborgId = whoami?.organizationId;
@@ -285,7 +291,6 @@ export default function Dashboard() {
});
setUser(userResponse.user);
- console.log(userResponse.user);
const walletsResponse = await authIframeClient!.getWallets({
organizationId: suborgId!,
});
@@ -654,8 +659,16 @@ export default function Dashboard() {
diff --git a/examples/react-components/src/app/layout.tsx b/examples/react-components/src/app/layout.tsx
index f73ac3e7..6ae06089 100644
--- a/examples/react-components/src/app/layout.tsx
+++ b/examples/react-components/src/app/layout.tsx
@@ -18,6 +18,10 @@ interface RootLayoutProps {
function RootLayout({ children }: RootLayoutProps) {
return (
+
+
Turnkey Auth Demo
+
+
{children}
diff --git a/packages/sdk-react/src/components/export/Export.module.css b/packages/sdk-react/src/components/export/Export.module.css
index 497ee6e3..77c03ab8 100644
--- a/packages/sdk-react/src/components/export/Export.module.css
+++ b/packages/sdk-react/src/components/export/Export.module.css
@@ -4,6 +4,7 @@
font-weight: 600;
letter-spacing: -0.01em;
text-align: left;
+ color: var(--text-primary);
}
.exportCard {
@@ -14,10 +15,10 @@
min-width: 375px;
margin: 0 auto;
padding: 20px;
- background: var(--Greyscale-20, #f5f7fb);
- border: 1px solid var(--Greyscale-100, #ebedf2);
- border-radius: 15px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ background: var(--auth-card-bg);
+ border: 1px solid var(--auth-card-border);
+ border-radius: var(--auth-card-radius);
+ box-shadow: var(--auth-card-shadow);
}
.exportCard h2 {
@@ -28,6 +29,7 @@
text-align: center;
font-size: 1.5rem;
margin-bottom: 16px;
+ color: var(--text-primary);
}
.doneButtonContainer {
@@ -40,24 +42,24 @@
justify-content: center;
padding: 10px 16px;
gap: 8px;
- color: var(--Greyscale-900, #2b2f33);
+ color: var(--button-text);
width: 100%;
font-size: 1rem;
- background: #ffffff;
- border: 1px solid var(--Greyscale-400, #a2a7ae);
+ background: var(--button-bg);
+ border: 1px solid var(--button-border);
border-radius: 8px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.exportButton:hover {
- background-color: #f5f5f5;
+ background-color: var(--button-hover-bg);
}
.exportButton:disabled {
- color: var(--Greyscale-700, #a2a7ae);
- background: #ffffff;
- border-color: var(--Greyscale-100, #f5f7fb);
+ color: var(--button-disabled-text);
+ background: var(--button-disabled-bg);
+ border-color: var(--button-disabled-border);
cursor: default;
}
@@ -71,6 +73,7 @@
width: 95%;
margin-right: auto;
margin-bottom: 32px;
+ color: var(--text-primary);
}
.row {
@@ -92,7 +95,7 @@
font-weight: 400;
line-height: 16px;
letter-spacing: -0.01em;
- color: var(--Greyscale-500, #868c95);
+ color: var(--text-secondary);
margin-top: 16px;
cursor: pointer;
}
@@ -100,7 +103,8 @@
.poweredBy span {
position: relative;
}
+
iframe {
box-sizing: border-box;
- border: 0 solid #000;
+ border: 0 solid var(--border-default);
}
diff --git a/packages/sdk-react/src/components/export/Export.tsx b/packages/sdk-react/src/components/export/Export.tsx
index cff366df..1b7b62aa 100644
--- a/packages/sdk-react/src/components/export/Export.tsx
+++ b/packages/sdk-react/src/components/export/Export.tsx
@@ -8,11 +8,18 @@ import eyeIcon from "assets/eye.svg";
import cautionIcon from "assets/caution.svg";
import turnkeyIcon from "assets/turnkey.svg";
import exportIcon from "assets/export.svg";
+
type ExportProps = {
walletId: string;
+ onHandleExportSuccess: () => Promise;
+ onError: (errorMessage: string) => void;
};
-const Export: React.FC = ({ walletId }) => {
+const Export: React.FC = ({
+ walletId,
+ onHandleExportSuccess,
+ onError,
+}) => {
const { authIframeClient, turnkey } = useTurnkey();
const [exportIframeClient, setExportIframeClient] =
useState(null);
@@ -74,17 +81,28 @@ const Export: React.FC = ({ walletId }) => {
};
const exportWallet = async () => {
- const whoami = await authIframeClient!.getWhoami();
- const exportResponse = await authIframeClient?.exportWallet({
- organizationId: whoami.organizationId,
- walletId: walletId!,
- targetPublicKey: exportIframeClient!.iframePublicKey!,
- });
- if (exportResponse?.exportBundle) {
+ try {
+ const whoami = await authIframeClient!.getWhoami();
+
+ const exportResponse = await authIframeClient?.exportWallet({
+ organizationId: whoami.organizationId,
+ walletId: walletId!,
+ targetPublicKey: exportIframeClient!.iframePublicKey!,
+ });
+
+ if (!exportResponse?.exportBundle) {
+ throw new Error("Failed to retrieve export bundle");
+ }
+
await exportIframeClient?.injectWalletExportBundle(
exportResponse.exportBundle,
whoami.organizationId
);
+
+ onHandleExportSuccess();
+ } catch (error) {
+ console.error("Error during wallet export:", error);
+ onError("Failed to export wallet");
}
};
diff --git a/packages/sdk-react/src/components/import/Import.module.css b/packages/sdk-react/src/components/import/Import.module.css
index 33fe1923..38b9a406 100644
--- a/packages/sdk-react/src/components/import/Import.module.css
+++ b/packages/sdk-react/src/components/import/Import.module.css
@@ -4,6 +4,7 @@
font-weight: 600;
letter-spacing: -0.01em;
text-align: left;
+ color: var(--text-primary);
}
.importCard {
@@ -14,10 +15,10 @@
min-width: 375px;
margin: 0 auto;
padding: 20px;
- background: var(--Greyscale-20, #f5f7fb);
- border: 1px solid var(--Greyscale-100, #ebedf2);
- border-radius: 15px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ background: var(--auth-card-bg);
+ border: 1px solid var(--auth-card-border);
+ border-radius: var(--auth-card-radius);
+ box-shadow: var(--auth-card-shadow);
}
.importCard h2 {
@@ -28,6 +29,7 @@
text-align: center;
font-size: 1.5rem;
margin-bottom: 16px;
+ color: var(--text-primary);
}
.importButton {
@@ -36,24 +38,24 @@
justify-content: center;
padding: 10px 16px;
gap: 8px;
- color: var(--Greyscale-900, #2b2f33);
+ color: var(--button-text);
width: 100%;
font-size: 1rem;
- background: #ffffff;
- border: 1px solid var(--Greyscale-400, #a2a7ae);
+ background: var(--button-bg);
+ border: 1px solid var(--button-border);
border-radius: 8px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.importButton:hover {
- background-color: #f5f5f5;
+ background-color: var(--button-hover-bg);
}
.importButton:disabled {
- color: var(--Greyscale-700, #a2a7ae);
- background: #ffffff;
- border-color: var(--Greyscale-100, #f5f7fb);
+ color: var(--button-disabled-text);
+ background: var(--button-disabled-bg);
+ border-color: var(--button-disabled-border);
cursor: default;
}
@@ -67,7 +69,7 @@
font-weight: 400;
line-height: 16px;
letter-spacing: -0.01em;
- color: var(--Greyscale-500, #868c95);
+ color: var(--text-secondary);
margin-top: 16px;
cursor: pointer;
}
@@ -75,9 +77,10 @@
.poweredBy span {
position: relative;
}
+
iframe {
box-sizing: border-box;
- border: 0 solid #000;
+ border: 0 solid var(--border-default);
width: 100%;
height: 100%;
}
diff --git a/packages/sdk-react/src/components/import/Import.tsx b/packages/sdk-react/src/components/import/Import.tsx
index b171e135..b91ace6a 100644
--- a/packages/sdk-react/src/components/import/Import.tsx
+++ b/packages/sdk-react/src/components/import/Import.tsx
@@ -10,10 +10,11 @@ import styles from "./Import.module.css";
import turnkeyIcon from "assets/turnkey.svg";
import importIcon from "assets/import.svg";
type ImportProps = {
- onSuccess?: () => void;
+ onError: (errorMessage: string) => void;
+ onHandleImportSuccess: () => Promise;
};
-const Import: React.FC = ({ onSuccess = () => undefined }) => {
+const Import: React.FC = ({ onHandleImportSuccess, onError }) => {
const { authIframeClient, turnkey } = useTurnkey();
const [importIframeClient, setImportIframeClient] =
useState(null);
@@ -66,44 +67,51 @@ const Import: React.FC = ({ onSuccess = () => undefined }) => {
};
const handleImport = async () => {
- const whoami = await authIframeClient!.getWhoami();
- if (!importIframeClient) {
- console.error("IframeStamper is not initialized.");
- return;
- }
- const initResult = await authIframeClient!.initImportWallet({
- organizationId: whoami.organizationId,
- userId: whoami.userId,
- });
- const injected = await importIframeClient!.injectImportBundle(
- initResult.importBundle,
- whoami.organizationId,
- whoami.userId
- );
- if (!injected) {
- console.error("error injecting import bundle");
- return;
- }
- const encryptedBundle =
- await importIframeClient.extractWalletEncryptedBundle();
- if (!encryptedBundle || encryptedBundle.trim() === "") {
- console.error("failed to retrieve encrypted bundle.");
- return;
- }
- const response = await authIframeClient?.importWallet({
- organizationId: whoami.organizationId,
- userId: whoami.userId,
- walletName: walletName,
- encryptedBundle,
- accounts: [...DEFAULT_ETHEREUM_ACCOUNTS, ...DEFAULT_SOLANA_ACCOUNTS],
- });
+ try {
+ const whoami = await authIframeClient!.getWhoami();
+ if (!importIframeClient) {
+ throw new Error("Import iframe client not initialized");
+ }
- if (response) {
- console.log("Wallet imported successfully!");
- handleCloseModal();
- onSuccess();
- } else {
- console.error("Failed to import wallet");
+ const initResult = await authIframeClient!.initImportWallet({
+ organizationId: whoami.organizationId,
+ userId: whoami.userId,
+ });
+
+ const injected = await importIframeClient!.injectImportBundle(
+ initResult.importBundle,
+ whoami.organizationId,
+ whoami.userId
+ );
+
+ if (!injected) {
+ throw new Error("Failed to inject import bundle");
+ }
+
+ const encryptedBundle =
+ await importIframeClient.extractWalletEncryptedBundle();
+
+ if (!encryptedBundle || encryptedBundle.trim() === "") {
+ throw new Error("Encrypted wallet bundle is empty or invalid");
+ }
+
+ const response = await authIframeClient?.importWallet({
+ organizationId: whoami.organizationId,
+ userId: whoami.userId,
+ walletName: walletName,
+ encryptedBundle,
+ accounts: [...DEFAULT_ETHEREUM_ACCOUNTS, ...DEFAULT_SOLANA_ACCOUNTS],
+ });
+
+ if (response?.walletId) {
+ handleCloseModal();
+ onHandleImportSuccess();
+ } else {
+ throw new Error("Failed to import wallet");
+ }
+ } catch (error) {
+ console.error("Error during wallet import:", error);
+ onError("Failed to import wallet");
}
};
diff --git a/packages/sdk-react/src/components/theme.css b/packages/sdk-react/src/components/theme.css
new file mode 100644
index 00000000..aeae08b1
--- /dev/null
+++ b/packages/sdk-react/src/components/theme.css
@@ -0,0 +1,73 @@
+/* Define the font-face rules */
+@font-face {
+ font-family: "Inter";
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url("./assets/fonts/Inter-Regular.woff2") format("woff2");
+}
+
+@font-face {
+ font-family: "Inter";
+ font-style: normal;
+ font-weight: 600;
+ font-display: swap;
+ src: url("./assets/fonts/Inter-SemiBold.woff2?v=3.19") format("woff2");
+}
+
+:root {
+ /* Button Colors */
+ --button-bg: #ffffff; /* Default button background */
+ --button-text: var(--Greyscale-900, #2b2f33); /* Default button text */
+ --button-border: var(--Greyscale-400, #a2a7ae); /* Default button border */
+ --button-hover-bg: #f5f5f5; /* Hover button background */
+ --button-hover-text: var(--Greyscale-900, #2b2f33); /* Hover button text */
+ --button-disabled-bg: #ffffff; /* Disabled button background */
+ --button-disabled-text: var(
+ --Greyscale-700,
+ #a2a7ae
+ ); /* Disabled button text */
+ --button-disabled-border: var(
+ --Greyscale-100,
+ #f5f7fb
+ ); /* Disabled button border */
+
+ /* Input Field Colors */
+ --input-bg: #ffffff; /* Input field background */
+ --input-text: var(--Greyscale-900, #2b2f33); /* Input text color */
+ --input-border: var(--Greyscale-400, #d0d5dd); /* Input border color */
+ --input-hover-border: var(
+ --Greyscale-500,
+ #868c95
+ ); /* Input border on hover */
+ --input-focus-border: var(
+ --Greyscale-900,
+ #2b2f33
+ ); /* Input border on focus */
+
+ /* Text Colors */
+ --text-primary: var(--Greyscale-900, #2b2f33); /* Primary text */
+ --text-secondary: var(--Greyscale-500, #868c95); /* Secondary text */
+ --text-disabled: var(--Greyscale-700, #a2a7ae); /* Disabled text */
+
+ /* Border Colors */
+ --border-default: var(--Greyscale-400, #a2a7ae); /* Default border */
+ --border-hover: var(--Greyscale-500, #868c95); /* Hover border */
+ --border-disabled: var(--Greyscale-100, #f5f7fb); /* Disabled border */
+
+ /* Card Colors */
+ --auth-card-bg: var(
+ --Greyscale-20,
+ #f5f7fb
+ ); /* Default authCard background */
+ --auth-card-border: var(
+ --Greyscale-100,
+ #ebedf2
+ ); /* Default authCard border */
+ --auth-card-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); /* Default authCard shadow */
+ --auth-card-radius: 15px; /* Default authCard border radius */
+
+ /* Accent Colors */
+ --accent-color: var(--Blue-500, #4c48ff);
+ --error-color: #ff4c4c;
+}
diff --git a/packages/sdk-react/src/index.ts b/packages/sdk-react/src/index.ts
index be9a828b..a9c1b54c 100644
--- a/packages/sdk-react/src/index.ts
+++ b/packages/sdk-react/src/index.ts
@@ -3,7 +3,7 @@ import "./components/auth/OtpVerification.module.css";
import "./components/auth/PhoneInput.css";
import "./components/export/Export.module.css";
import "./components/import/Import.module.css";
-import "./components/auth/theme.css";
+import "./components/theme.css";
import { TurnkeyContext, TurnkeyProvider } from "./contexts/TurnkeyContext";
import { useTurnkey } from "./hooks/use-turnkey";
export * from "./components";