> ## Documentation Index
> Fetch the complete documentation index at: https://docs.turnkey.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Import wallet or private key

> This is a guide to importing your wallet or private key into Turnkey. For more information about the security of this flow, check out [Enclave secure channels](/security/enclave-secure-channels).

> ⚠️ **This example is outdated !**<br />
> Head over to [SDK Reference](/sdks/react/using-embedded-wallets#importing-and-exporting-wallets) for the updated packages.

## Implementation guides

Follow along with the Turnkey CLI, Embedded iframe, NodeJS, and Local Storage guides.

### CLI

Install the latest version of Turnkey CLI to access the new import functionality. You can find detailed instructions for installation [here](https://github.com/tkhq/tkcli).

#### Steps

<Steps>
  <Step title="Generate an encryption key">
    ```bash theme={"system"}
    turnkey generate encryption-key \
    --organization $ORGANIZATION_ID \
    --user $USER_ID \
    --encryption-key-name demo-encryption-key
    ```

    * The `--user` flag (required) is the id of the user importing the private key; this is required because the underlying encryption keys used for import are scoped to each user.
    * The `--encryption-key-name` flag is to specify a name for the encryption key. Note that an encryption key !== Turnkey API key; an encryption key is used exclusively for secure activities like import and export.
  </Step>

  <Step title="Initialize import">
    ```bash theme={"system"}
    turnkey wallets init-import \
    --user $USER_ID \
    --import-bundle-output "./import_bundle.txt" \
    --key-name demo-api-key
    ```

    * The `--import-bundle-output` (required) flag is the desired output file path for the “import bundle” that will be received from Turnkey. The “import bundle” contains the ephemeral public key generated by the Turnkey signer enclave for the specified user. The private key plaintext is encrypted to this public key in Step 2.
    * Reminder: The `--key-name` flag specifies the name of API key with which to interact with the Turnkey API service. This should be the name of a previously created key. If you do not have one, visit the quickstart guide for help creating one.
  </Step>

  <Step title="Encrypt without saving plaintext to filesystem. This can be done offline:">
    ```bash theme={"system"}
    turnkey encrypt \
    --user $USER_ID \
    --import-bundle-input "./import_bundle.txt" \
    --plaintext-input /dev/fd/3 3<<<"$MNEMONIC_1" \
    --encrypted-bundle-output "./encrypted_bundle.txt" \
    --encryption-key-name demo-encryption-key
    ```

    * The `--import-bundle-input` flag (required) is the desired input file path for the “import bundle”.
    * The `--plaintext-input` flag is the desired input file path for the private key plaintext. You can pass a filename here or feed the plaintext string directly into the standard input as shown above.
    * The `--encrypted-bundle-output` (required) flag is the desired output file path for the “encrypted bundle” that will be sent to Turnkey in Step 3. The “encrypted bundle” contains the ephemeral public key generated by the CLI as part of the shared secret computation with the Turnkey signer enclave. It also contains the ciphertext, which is the plaintext input encrypted by the Turnkey signer’s ephemeral public key.
  </Step>

  <Step title="Import private key">
    ```bash theme={"system"}
    turnkey wallets import \
    --user $USER_ID \
    --name "demo key"  \
    --encrypted-bundle-input "./encrypted_bundle.txt" \
    --key-name demo-api-key
    ```

    * The `--encrypted-bundle-input` (required) flag is the desired input file path for the “encrypted bundle” that will be sent to Turnkey.
    * Options are listed [here](/concepts/wallets) for the `--curve` and `--address-format` flags.
  </Step>
</Steps>

#### Private key support

Turnkey CLI also supports importing private keys. Follow the same steps as importing a wallet via CLI but use the `turnkey private-keys` commands instead. In Step 2 (`encrypt`), pass a `--key-format` flag for key-specific formatting; the options for private keys are:

* `hexadecimal`: Used for Ethereum. Examples: `0x13eff5b3f9c63eab5d53cff5149f01606b69325496e0e98b53afa938d890cd2e, 13eff5b3f9c63eab5d53cff5149f01606b69325496e0e98b53afa938d890cd2e`
* `solana`: Used for Solana. It’s a base58-encoding of the concatenation of the private key and public key bytes. Example: `2P3qgS5A18gGmZJmYHNxYrDYPyfm6S3dJgs8tPW6ki6i2o4yx7K8r5N8CF7JpEtQiW8mx1kSktpgyDG1xuWNzfsM`

```bash theme={"system"}
turnkey encrypt \
--import-bundle-input "./import_bundle.txt" \
--plaintext-input /dev/fd/3 3<<<"$RAW_KEY_1" \
--key-format “hexadecimal” \
--encrypted-bundle-output "./encrypted_bundle.txt"
```

### Embedded iframe

* We have released open-source code to create target encryption keys and encrypt wallet mnemonics for import. We've deployed a static HTML page hosted on `import.turnkey.com` meant to be embedded as an iframe element (see the code [here](https://github.com/tkhq/frames)). This ensures the mnemonics and keys are encrypted to keys that the user has access to, but that your organization does not (because they live in the iframe, on a separate domain).
* We have also built a package to help you insert this iframe and interact with it in the context of import: [`@turnkey/iframe-stamper`](https://www.npmjs.com/package/@turnkey/iframe-stamper)

In the rest of this guide we'll assume you are using these helpers. We also have a [code example](https://github.com/tkhq/sdk/blob/main/examples/wallet-import-export/src/components/ImportWallet.tsx) you can follow along with.

#### Steps

Here's a diagram summarizing the wallet import flow step-by-step ([direct link](/assets/files/wallet_import_steps-6c4753c1e726e1632ce475bc838388c2.png)):

<Frame>
  <img src="https://mintcdn.com/turnkey-0e7c1f5b/83HCB8zBjOP3rX5S/images/embedded-wallets/code-examples/img/wallet_import_steps.png?fit=max&auto=format&n=83HCB8zBjOP3rX5S&q=85&s=a377beef73472f7e86dd3ff67772099f" alt="wallet import steps" width="1610" height="566" data-path="images/embedded-wallets/code-examples/img/wallet_import_steps.png" />
</Frame>

Let's review these steps in detail:

<Steps>
  <Step>
    When a user on your application clicks "import", display a new import UI. We recommend setting this import UI as a new hosted page of your application that contains the iframe element that the user will enter their mnemonic into and an import button.

    While the UI is in a loading state, your application uses [`@turnkey/iframe-stamper`](https://www.npmjs.com/package/@turnkey/iframe-stamper) to insert a new iframe element:

    ```js theme={"system"}
    const iframeStamper = new IframeStamper({
      iframeUrl: "https://import.turnkey.com",
      // Configure how the iframe element is inserted on the page
      iframeContainer: yourContainer,
      iframeElementId: "turnkey-iframe",
    });

    // Inserts the iframe in the DOM.
    await iframeStamper.init();

    // Set state to not display iframe
    let displayIframe = "none";

    return (
      // The iframe element does not need to be displayed yet
      <div style={{ display: displayIframe }} />
    );
    ```
  </Step>

  <Step>
    Your application prompts the user to sign a new `INIT_IMPORT_WALLET` activity with the ID of the user importing the wallet.
  </Step>

  <Step>
    Your application polls for the activity response, which contains an import bundle.

    Need help setting up async polling? Checkout our guide and helper [here](https://github.com/tkhq/sdk/tree/main/packages/http#withasyncpolling-helper).
  </Step>

  <Step>
    Your application injects the import bundle into the iframe and displays the iframe upon success:

    ```js theme={"system"}
    // Inject import bundle into iframe
    let success = await iframeStamper.injectImportBundle(importBundle);

    if (success !== true) {
      throw new Error("unexpected error while injecting import bundle");
    }

    // If successfully injected, update the state to display the iframe
    iframeDisplay = "block";
    ```
  </Step>

  <Step>
    When a user clicks on the import button on your web page, your application can extract the encrypted mnemonic bundle from the iframe:

    ```js theme={"system"}
    // Extract the encrypted bundle from the iframe
    let encryptedBundle =
      await iframeStamper.extractWalletEncryptedBundle(importBundle);
    ```

    Your applications passes the encrypted bundle as a parameter in a new `IMPORT_WALLET` activity and prompts the user to sign it.

    Import is complete!

    In your Turnkey dashboard, the imported user Wallet will be flagged as “Imported”.
  </Step>
</Steps>

### NodeJS

A full example Node script can be found here: [https://github.com/tkhq/sdk/tree/main/examples/import-in-node](https://github.com/tkhq/sdk/tree/main/examples/import-in-node)

#### Steps

<Steps>
  <Step title="Initialize a new Turnkey client">
    ```ts theme={"system"}
    import { Turnkey } from "@turnkey/sdk-server";
    import {
      encryptPrivateKeyToBundle,
      encryptWalletToBundle,
    } from "@turnkey/crypto";

    ...

    const turnkeyClient = new Turnkey({
        apiBaseUrl: "https://api.turnkey.com",
        apiPublicKey: process.env.API_PUBLIC_KEY!,
        apiPrivateKey: process.env.API_PRIVATE_KEY!,
        defaultOrganizationId: process.env.ORGANIZATION_ID!,
      });
    ```
  </Step>

  <Step title="Initialize the import process (Turnkey activity)">
    ```ts theme={"system"}
    const initResult = await turnkeyClient.apiClient().initImportWallet({
      userId,
    });
    ```
  </Step>

  <Step title="Encrypt wallet to bundle">
    ```ts theme={"system"}
    const walletBundle = await encryptWalletToBundle({
      mnemonic,
      importBundle: initResult.importBundle,
      userId,
      organizationId,
    });
    ```
  </Step>

  <Step title="Import wallet (Turnkey activity)">
    ```ts theme={"system"}
    const walletImportResult = await turnkeyClient.apiClient().importWallet({
      userId: userId,
      walletName: "Your imported wallet!",
      encryptedBundle: walletBundle,
      accounts: [], // these are the wallet accounts you'd like to derive; after all, you've just imported a HD wallet! See https://learnmeabitcoin.com/technical/hd-wallets for more
    });
    ```

    Congrats! You've imported your wallet 🎉
  </Step>
</Steps>

#### Private key support

The process for importing a private key instead of wallet is largely similar, but has a key difference in that you must specify the format of your imported private key:

<Steps>
  <Step title="Initialize a new Turnkey client">
    ```js theme={"system"}
    import { Turnkey } from "@turnkey/sdk-server";
    import {
      encryptPrivateKeyToBundle,
      encryptWalletToBundle,
    } from "@turnkey/crypto";

    ...

    const turnkeyClient = new Turnkey({
        apiBaseUrl: "https://api.turnkey.com",
        apiPublicKey: process.env.API_PUBLIC_KEY!,
        apiPrivateKey: process.env.API_PRIVATE_KEY!,
        defaultOrganizationId: process.env.ORGANIZATION_ID!,
      });
    ```
  </Step>

  <Step title="Initialize the import process (Turnkey activity)">
    ```js theme={"system"}
    const initResult = await turnkeyClient.apiClient().initImportPrivateKey({
      userId,
    });
    ```
  </Step>

  <Step title="Encrypt private key to bundle">
    ```js theme={"system"}
    const privateKeyBundle = await encryptPrivateKeyToBundle({
      privateKey,
      keyFormat,
      importBundle: initResult.importBundle,
      userId,
      organizationId,
    });
    ```
  </Step>

  <Step title="Import private key (Turnkey activity)">
    ```js theme={"system"}
    const privateKeyImportResult = await turnkeyClient
      .apiClient()
      .importPrivateKey({
        userId: userId,
        privateKeyName: "Your imported private key!",
        encryptedBundle: privateKeyBundle,
        curve: keyFormat == "SOLANA" ? "CURVE_ED25519" : "CURVE_SECP256K1",
        addressFormats:
          keyFormat == "SOLANA"
            ? ["ADDRESS_FORMAT_SOLANA"]
            : ["ADDRESS_FORMAT_ETHEREUM"],
      });
    ```

    Congrats! You've imported your private key 🎉
  </Step>
</Steps>

### Local storage

If you do not have access to an iframe (e.g. in a mobile context) or would prefer not to use an iframe, you can opt to use other environment-agnostic methods to perform import using Turnkey libraries. So while this section is called Local Storage (for consistency with the [Export](/embedded-wallets/code-examples/export) guide), nothing is stored in Local Storage; instead, the relevant `encrypt{Wallet, PrivateKey}ToBundle` method uses an [ephemeral key](https://github.com/tkhq/sdk/blob/6b3ea14d1184c5394449ecaad2b0f445e373823f/packages/crypto/src/crypto.ts#L62-L70).

#### Steps

<Steps>
  <Step title="Initialize Turnkey client">
    ```ts theme={"system"}
    import { Turnkey } from "@turnkey/sdk-browser";
    import { generateP256KeyPair, encryptWalletToBundle } from "@turnkey/crypto";

    ...

    const turnkey = new Turnkey({
      apiBaseUrl: "https://api.turnkey.com",
      defaultOrganizationId: process.env.TURNKEY_ORGANIZATION_ID,
    });
    const passkeyClient = turnkey.passkeyClient();
    ```
  </Step>

  <Step title="Initialize the import process (Turnkey activity)">
    ```ts theme={"system"}
    const initResult = await passkeyClient.initImportWallet({
      userId, // your user ID
    });
    ```
  </Step>

  <Step title="Encrypt wallet to bundle">
    ```ts theme={"system"}
    const walletBundle = await encryptWalletToBundle({
      mnemonic,
      importBundle: initResult.importBundle,
      userId, // your user ID
      organizationId, // your organization ID
    });
    ```
  </Step>

  <Step title="Import wallet (Turnkey activity)">
    ```ts theme={"system"}
    const walletImportResult = await passkeyClient.importWallet({
      userId, // your user ID
      walletName: "Your imported wallet!", // your desired name for the resulting imported wallet
      encryptedBundle: walletBundle,
      accounts: [], // these are the wallet accounts you'd like to derive; after all, you've just imported a HD wallet! See https://learnmeabitcoin.com/technical/hd-wallets for more
    });
    ```

    Congrats! You've imported your wallet 🎉

    The process for importing a private key instead of wallet is largely similar, but has a key difference in that you must specify the format of your imported private key:
  </Step>
</Steps>

#### Private key support

<Steps>
  <Step title="Initialize Turnkey client">
    ```ts theme={"system"}
    import { Turnkey } from "@turnkey/sdk-browser";
    import { generateP256KeyPair, encryptPrivateKeyToBundle } from "@turnkey/crypto";

    ...

    const turnkey = new Turnkey({
      apiBaseUrl: "https://api.turnkey.com",
      defaultOrganizationId: process.env.TURNKEY_ORGANIZATION_ID,
    });
    const passkeyClient = turnkey.passkeyClient();
    ```
  </Step>

  <Step title="Initialize the import process (Turnkey activity)">
    ```
    const initResult = await passkeyClient.initImportPrivateKey({
      userId, // your user ID
    });
    ```
  </Step>

  <Step title="Encrypt private key to bundle">
    ```ts theme={"system"}
    const privateKeyBundle = await encryptPrivateKeyToBundle({
      privateKey, // your private key string
      keyFormat, // your desired key format. See the Private Key notes section for more
      importBundle: initResult.importBundle,
      userId, // your user ID
      organizationId, // your organization ID
    });
    ```
  </Step>

  <Step title="Import private key (Turnkey activity)">
    ```ts theme={"system"}
    const privateKeyImportResult = await passkeyClient.importPrivateKey({
      userId,
      privateKeyName: "Your imported private key!", // your desired name for the resulting imported private key
      encryptedBundle: privateKeyBundle,
      curve: keyFormat == "SOLANA" ? "CURVE_ED25519" : "CURVE_SECP256K1",
      addressFormats:
        keyFormat == "SOLANA"
          ? ["ADDRESS_FORMAT_SOLANA"]
          : ["ADDRESS_FORMAT_ETHEREUM"],
    });
    ```

    Congrats! You've imported your private key 🎉
  </Step>
</Steps>

## Private key notes

Turnkey also supports importing Private Keys. Follow the same steps above for importing Wallets as mnemonics, but instead use the `INIT_IMPORT_PRIVATE_KEY` and `IMPORT_PRIVATE_KEY` activities and the `extractKeyEncryptedBundle` method from the [`@turnkey/iframe-stamper`](https://www.npmjs.com/package/@turnkey/iframe-stamper). You can pass an optional `keyFormat` to `extractKeyEncryptedBundle(keyFormat)` that will apply either `Hexadecimal` or `Solana` formatting to the private key that is entered in the iframe. The default key format is `hexadecimal`, which is used by MetaMask, MyEtherWallet, Phantom, Ledger, and Trezor for Ethereum keys. For Solana keys, you will need to pass the `solana` key format.

## UI customization

Everything is customizable in the import iframe except the sentence of mnemonic words, which is minimally styled: the text is left-aligned and the padding and margins are zero. Here's an example of how you can configure the styling of the iframe.

```css theme={"system"}
const iframeCss = `
  iframe {
      box-sizing: border-box;
      width: 400px;
      height: 120px;
      border-radius: 8px;
      border-width: 1px;
      border-style: solid;
      border-color: rgba(216, 219, 227, 1);
      padding: 20px;
  }
`;

return (
  <div style={{ display: iframeDisplay }} id="your-container">
    <style>{iframeCss}</style>
  </div>
);
```
