Export Private Key
The Alchemy Signer allows you to export a user’s private key, allowing them a right to exit at any time. It is considered a best practice to allow your users to export their private key, as it gives them full control over their account. The private key export method does not rely on Alchemy’s infrastructure, so even if Alchemy is down, a user can still export their private key.
Using useExportAccount
A hook use to export the private key for an account. It returns the mutation functions to kick off the export process, as well as a component to render the account recovery details in an iframe.
Import
Usage
Using the signer
To add export private key functionality to your app, you can use the exportPrivateKey
method on the signer.
import React from "react";
import { function useMutation<TData = unknown, TError = Error, TVariables = void, TContext = unknown>(options: UseMutationOptions<TData, TError, TVariables, TContext>, queryClient?: QueryClient): UseMutationResult<TData, TError, TVariables, TContext>useMutation } from "@tanstack/react-query";
import { import signersigner } from "./signer";
const const TurnkeyExportWalletContainerId: "turnkey-export-wallet-container-id"TurnkeyExportWalletContainerId = "turnkey-export-wallet-container-id";
const const TurnkeyExportWalletElementId: "turnkey-export-wallet-element-id"TurnkeyExportWalletElementId = "turnkey-export-wallet-element-id";
// This allows us to style the embedded iframe
const const iframeCss: "\niframe {\n box-sizing: border-box;\n width: 100%;\n height: 120px;\n border-radius: 8px;\n border-width: 1px;\n border-style: solid;\n border-color: rgba(216, 219, 227, 1);\n padding: 20px;\n}\n"iframeCss = `
iframe {
box-sizing: border-box;
width: 100%;
height: 120px;
border-radius: 8px;
border-width: 1px;
border-style: solid;
border-color: rgba(216, 219, 227, 1);
padding: 20px;
}
`;
export const const ExportPrivateKeyView: () => JSX.ElementExportPrivateKeyView = () => {
// we are using react-query to handle loading states more easily, but feel free to use w/e state management library you prefer
const {
mutate: UseMutateFunction<unknown, Error, void, unknown>The mutation function you can call with variables to trigger the mutation and optionally hooks on additional callback options.
mutate: const exportWallet: UseMutateFunction<unknown, Error, void, unknown>The mutation function you can call with variables to trigger the mutation and optionally hooks on additional callback options.
exportWallet,
const isPending: booleanA boolean variable derived from status
.
true
if the mutation is currently executing.
isPending,
const data: unknownThe last successfully resolved data for the mutation.
data,
} = useMutation<unknown, Error, void, unknown>(options: UseMutationOptions<unknown, Error, void, unknown>, queryClient?: QueryClient): UseMutationResult<unknown, Error, void, unknown>useMutation({
mutationFn?: MutationFunction<unknown, void> | undefinedmutationFn: () =>
import signersigner.anyexportWallet({
iframeContainerId: stringiframeContainerId: const TurnkeyExportWalletContainerId: "turnkey-export-wallet-container-id"TurnkeyExportWalletContainerId,
iframeElementId: stringiframeElementId: const TurnkeyExportWalletElementId: "turnkey-export-wallet-element-id"TurnkeyExportWalletElementId,
}),
});
// Once the user clicks the button, a request will be sent to initialize private key export
// once the request is complete, the iframe will be rendered with either
// 1. the private key if the user is logged in with a passkey
// 2. the seed phrase if the user is logged in with email
return (
<React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="flex flex-col gap-2">
{!const data: unknownThe last successfully resolved data for the mutation.
data ? (
<React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefinedonClick={() => const exportWallet: (variables: void, options?: MutateOptions<unknown, Error, void, unknown> | undefined) => voidThe mutation function you can call with variables to trigger the mutation and optionally hooks on additional callback options.
exportWallet()} React.ButtonHTMLAttributes<HTMLButtonElement>.disabled?: boolean | undefineddisabled={const isPending: booleanA boolean variable derived from status
.
true
if the mutation is currently executing.
isPending}>
Export Wallet
</React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button>
) : (
<React.JSX.IntrinsicElements.strong: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>strong>Seed Phrase</React.JSX.IntrinsicElements.strong: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>strong>
)}
<React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div
React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="w-full"
React.HTMLAttributes<HTMLDivElement>.style?: React.CSSProperties | undefinedstyle={{ StandardLonghandProperties<string | number, string & {}>.display?: Property.Display | undefinedThe display
CSS property sets whether an element is treated as a block or inline element and the layout used for its children, such as flow layout, grid or flex.
Syntax: [ <display-outside> || <display-inside> ] | <display-listitem> | <display-internal> | <display-box> | <display-legacy>
Initial value: inline
| Chrome | Firefox | Safari | Edge | IE | | :----: | :-----: | :----: | :----: | :---: | | 1 | 1 | 1 | 12 | 4 |
display: !const data: unknownThe last successfully resolved data for the mutation.
data ? "none" : "block" }}
React.HTMLAttributes<HTMLDivElement>.id?: string | undefinedid={const TurnkeyExportWalletContainerId: "turnkey-export-wallet-container-id"TurnkeyExportWalletContainerId}
>
<React.JSX.IntrinsicElements.style: React.DetailedHTMLProps<React.StyleHTMLAttributes<HTMLStyleElement>, HTMLStyleElement>style>{const iframeCss: "\niframe {\n box-sizing: border-box;\n width: 100%;\n height: 120px;\n border-radius: 8px;\n border-width: 1px;\n border-style: solid;\n border-color: rgba(216, 219, 227, 1);\n padding: 20px;\n}\n"iframeCss}</React.JSX.IntrinsicElements.style: React.DetailedHTMLProps<React.StyleHTMLAttributes<HTMLStyleElement>, HTMLStyleElement>style>
</React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
</React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
};
import { class AlchemyWebSignerA SmartAccountSigner that can be used with any SmartContractAccount
AlchemyWebSigner } from "@account-kit/signer";
export const const signer: AlchemyWebSignersigner = new new AlchemyWebSigner(params: AlchemySignerParams): AlchemyWebSignerInitializes an instance with the provided Alchemy signer parameters after parsing them with a schema.
AlchemyWebSigner({
client: ({
connection: {
apiKey: string;
rpcUrl?: undefined;
jwt?: undefined;
} | {
jwt: string;
rpcUrl?: undefined;
apiKey?: undefined;
} | {
rpcUrl: string;
apiKey?: undefined;
jwt?: undefined;
} | {
rpcUrl: string;
jwt: string;
apiKey?: undefined;
};
... 4 more ...;
enablePopupOauth?: boolean | undefined;
} | AlchemySignerWebClient) & (AlchemySignerWebClient | ... 1 more ... | undefined)client: {
connection: {
apiKey: string;
}connection: {
apiKey: stringapiKey: "API_KEY",
},
iframeConfig: {
iframeContainerId: string;
}iframeConfig: {
iframeContainerId: stringiframeContainerId: "alchemy-signer-iframe-container",
},
},
});