Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jordan/import export react components #275

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/sdk-browser/src/__types__/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,9 @@ export type commandOverrideParams = {
organizationId?: string;
timestampMs?: string;
};

export interface IframeClientParams {
iframeContainer: HTMLElement | null | undefined;
iframeUrl: string;
iframeElementId?: string;
}
54 changes: 45 additions & 9 deletions packages/sdk-browser/src/sdk-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
GrpcStatus,
TurnkeySDKClientConfig,
TurnkeySDKBrowserConfig,
IframeClientParams
} from "./__types__/base";

import { TurnkeyRequestError } from "./__types__/base";
Expand Down Expand Up @@ -72,24 +73,21 @@ export class TurnkeyBrowserSDK {
});
};

iframeClient = async (
iframeContainer: HTMLElement | null | undefined,
iframeUrl?: string
): Promise<TurnkeyIframeClient> => {
const targetIframeUrl = iframeUrl ?? this.config.iframeUrl;
iframeClient = async (params: IframeClientParams): Promise<TurnkeyIframeClient> => {
const targetIframeUrl = params.iframeUrl;

if (!targetIframeUrl) {
throw new Error(
"Tried to initialize iframeSigner with no iframeUrl defined"
"Tried to initialize iframeClient with no iframeUrl defined"
);
}

const TurnkeyIframeElementId = "turnkey-default-iframe-element-id";
const TurnkeyIframeElementId = params.iframeElementId ?? "turnkey-iframe-element-id";

const iframeStamper = new IframeStamper({
iframeContainer: params.iframeContainer,
iframeUrl: targetIframeUrl,
iframeElementId: TurnkeyIframeElementId,
iframeContainer: iframeContainer,
iframeElementId: TurnkeyIframeElementId
});

await iframeStamper.init();
Expand Down Expand Up @@ -263,4 +261,42 @@ export class TurnkeyIframeClient extends TurnkeyBrowserClient {
const stamper = this.config.stamper as IframeStamper;
return await stamper.injectCredentialBundle(credentialBundle);
};

injectWalletExportBundle = async (
credentialBundle: string,
organizationId: string
): Promise<boolean> => {
const stamper = this.config.stamper as IframeStamper;
return await stamper.injectWalletExportBundle(credentialBundle, organizationId);
}

injectKeyExportBundle = async (
credentialBundle: string,
organizationId: string
): Promise<boolean> => {
const stamper = this.config.stamper as IframeStamper;
return await stamper.injectKeyExportBundle(
credentialBundle,
organizationId
)
}

injectImportBundle = async (
bundle: string,
organizationId: string,
userId: string
): Promise<boolean> => {
const stamper = this.config.stamper as IframeStamper;
return await stamper.injectImportBundle(
bundle,
organizationId,
userId
);
}

extractWalletEncryptedBundle = async (): Promise<string> => {
const stamper = this.config.stamper as IframeStamper;
return await stamper.extractWalletEncryptedBundle();
}

}
184 changes: 184 additions & 0 deletions packages/sdk-react/src/components/ExportWallet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import { useEffect, useRef, useState } from "react";
import { useTurnkey } from "../hooks/useTurnkey";
import type { TurnkeyIframeClient } from "@turnkey/sdk-browser";

type ExportWalletProps = {
onCancel?: () => void
}

export const ExportWallet: React.FC<ExportWalletProps> = ({
onCancel = () => undefined
}) => {
const { turnkey, passkeyClient } = useTurnkey();
const [iframeClient, setIframeClient] = useState<TurnkeyIframeClient | undefined>(undefined);
const [iframeStyle, setIframeStyle] = useState<Record<any, any>>({ display: "none" });
const iframeInit = useRef<boolean>(false);

const [wallets, setWallets] = useState<any[]>([]);
const [selectedWallet, setSelectedWallet] = useState<any>(undefined);
const [walletAccounts, setWalletAccounts] = useState<any>([]);
const [selectedWalletAccount, setSelectedWalletAccount] = useState<any>(undefined);

const TurnkeyExportIframeContainerId = "turnkey-export-iframe-container-id";

useEffect(() => {
(async () => {
if (!iframeInit.current) {

iframeInit.current = true;

const newExportIframeClient = await turnkey?.iframeClient({
iframeContainer: document.getElementById(TurnkeyExportIframeContainerId),
iframeUrl: "https://export.turnkey.com"
});
setIframeClient(newExportIframeClient);

}
})();
}, []);

useEffect(() => {
if (turnkey) {
(async () => {
const currentUserSession = await turnkey.currentUserSession();
if (currentUserSession) {
const walletsResponse = await currentUserSession.getWallets();
if (walletsResponse) {
setWallets(walletsResponse.wallets);
setSelectedWallet(walletsResponse.wallets[0]);
}
}
})();
}
}, [turnkey]);

useEffect(() => {
if (turnkey) {
(async () => {
const currentUserSession = await turnkey.currentUserSession();
if (currentUserSession) {
if (selectedWallet) {
const walletAccountsResponse = await currentUserSession.getWalletAccounts({
walletId: selectedWallet.walletId
});
if (walletAccountsResponse) {
setWalletAccounts(walletAccountsResponse.accounts);
setSelectedWalletAccount(walletAccountsResponse.accounts[0]);
}
}
}
})();
}
}, [selectedWallet]);

const exportWallet = async () => {
const currentUser = await turnkey?.getCurrentUser();
const exportResponse = await passkeyClient?.exportWallet({
walletId: selectedWallet.walletId,
targetPublicKey: `${iframeClient?.iframePublicKey}`
});
if (exportResponse?.exportBundle) {
const injectResponse = await iframeClient?.injectWalletExportBundle(
exportResponse.exportBundle,
`${currentUser?.organization.organizationId}`
);
if (injectResponse) {
setIframeStyle({
display: "block",
width: "100%",
boxSizing: "border-box",
padding: "20px",
borderStyle: "solid",
borderWidth: "1px",
borderRadius: "8px",
borderColor: "rgba(216, 219, 227, 1)"
});
}
}
}

const exportWalletAccount = async () => {
const currentUser = await turnkey?.getCurrentUser();
const exportResponse = await passkeyClient?.exportWalletAccount({
address: selectedWalletAccount.address,
targetPublicKey: `${iframeClient?.iframePublicKey}`
});
if (exportResponse?.exportBundle) {
const injectResponse = await iframeClient?.injectKeyExportBundle(
exportResponse.exportBundle,
`${currentUser?.organization.organizationId}`
);
if (injectResponse) {
setIframeStyle({
display: "block",
width: "100%",
boxSizing: "border-box",
padding: "20px",
borderStyle: "solid",
borderWidth: "1px",
borderRadius: "8px",
borderColor: "rgba(216, 219, 227, 1)"
});
}
}
}

return (
<div className="export-container">
<p className="title-text">Export</p>

<div className="select-section-container">
<div className="select-section wallet-select-section">
<p className="label">Select Wallet:</p>
<select
className="wallets-select"
value={selectedWallet?.walletId}
onChange={(e) => setSelectedWallet(wallets.find((x: any) => x.walletId === e.target.value))}>
{wallets?.map((wallet: any, index: number) => (
<option key={index} value={wallet.walletId}>
{wallet.walletName}
</option>
))}
</select>
</div>

<div className="select-section wallet-account-select-section">
<p className="label">Select Wallet Account:</p>
<select
className="wallet-accounts-select"
value={selectedWalletAccount?.address}
onChange={(e) => setSelectedWalletAccount(walletAccounts.find((x: any) => x.address === e.target.value))}>
{walletAccounts?.map((walletAccount: any, index: number) => (
<option key={index} value={walletAccount.address}>
{walletAccount.address}
</option>
))}
</select>
</div>

</div>

<div className="action-buttons">
<div
className="action-button primary"
onClick={exportWallet}>
<p className="action-button-text">Export Wallet Seed</p>
</div>

<div
className="action-button primary"
onClick={exportWalletAccount}>
<p className="action-button-text">Export Account Private Key</p>
</div>

<div
className="action-button secondary"
onClick={onCancel}>
<p className="action-button-text">Cancel</p>
</div>
</div>

<div id={TurnkeyExportIframeContainerId} style={iframeStyle} />
</div>
)
}
Loading
Loading