Skip to main content

Overview

Pre-generate embedded wallets for your users before they sign in, enabling you to fund accounts with assets upfront for a seamless first-time experience.
Supported authentication methods: Wallet pre-generation currently supports email, SMS, and Custom (JWT) authentication only. Support for additional authentication methods is coming soon.

Why pre-generate wallets?

  • Pre-load assets: Fund wallets with loyalty points, gas, or welcome NFTs before users sign in
  • Zero-friction onboarding: Users see a ready-to-use wallet on first login instead of an empty account
  • Targeted campaigns: Prepare wallets for specific users (by email, phone, or JWT) before launching marketing campaigns

Prerequisites

Before pre-generating wallets, ensure you have:
  1. CDP API Key - Create one in the CDP Portal → API Keys
  2. Wallet Secret - Generate one in the CDP Portal → Server Wallet → Accounts
  3. CDP SDK - Install the CDP SDK in your project
Common mistake: A standard CDP API key alone is not enough for wallet pre-generation. You must also generate a Wallet Secret from the Server Wallet section of the CDP Portal.

Getting your credentials

1

Create a CDP API Key

  1. Go to the CDP Portal
  2. Navigate to API Keys in the left sidebar
  3. Click Create API Key and save both the Key ID and Key Secret
2

Generate a Wallet Secret

  1. In the CDP Portal, go to Server WalletAccounts
  2. Click Generate in the Wallet Secret section
  3. Save the secret securely - you won’t be able to view it again
3

Configure your environment

Add both credentials to your .env file:
CDP_API_KEY_ID=your-api-key-id
CDP_API_KEY_SECRET=your-api-key-secret
CDP_WALLET_SECRET=your-wallet-secret
For more details, see the Authentication documentation and Wallet Secret documentation.

Usage

Use the CDP SDK to create an end user with a specific authentication method. Once created, you can fund the wallet address before the user ever signs in.

Creating an end user

The createEndUser method creates a new end user with an associated wallet. You specify the authentication method (email, SMS, or JWT) that the user will use to sign in later.

Email authentication

import { CdpClient } from "@coinbase/cdp-sdk";
import "dotenv/config";

const cdp = new CdpClient();

try {
  // Create an end user with an email authentication method 
  // and both EVM and Solana accounts.
  const endUser = await cdp.endUser.createEndUser({
    authenticationMethods: [
      { type: "email", email: "[email protected]" }
    ],
    evmAccount: { createSmartAccount: false },
    solanaAccount: { createSmartAccount: false }
  });

  console.log("Created end user:", endUser);

  // The end user's wallet addresses are now available.
  // You can fund these addresses before the user signs in.
  console.log("EVM address:", endUser.evmAccounts?.[0]);
  console.log("Solana address:", endUser.solanaAccounts?.[0]);
} catch (error) {
  console.error("Error creating end user:", error);
}

SMS authentication

import { CdpClient } from "@coinbase/cdp-sdk";
import "dotenv/config";

const cdp = new CdpClient();

try {
  // Create an end user with an SMS authentication method
  // and both EVM and Solana accounts.
  const endUser = await cdp.endUser.createEndUser({
    authenticationMethods: [
      { type: "sms", phoneNumber: "+12055555555" }
    ],
    evmAccount: { createSmartAccount: false },
    solanaAccount: { createSmartAccount: false }
  });

  console.log("Created end user:", endUser);

  // The end user's wallet addresses are now available.
  // You can fund these addresses before the user signs in.
  console.log("EVM address:", endUser.evmAccounts?.[0]);
  console.log("Solana address:", endUser.solanaAccounts?.[0]);
} catch (error) {
  console.error("Error creating end user:", error);
}

Custom (JWT) Authentication

import { CdpClient } from "@coinbase/cdp-sdk";
import "dotenv/config";

const cdp = new CdpClient();

try {
  // Create an end user with a JWT authentication method
  // and both EVM and Solana accounts.
  const endUser = await cdp.endUser.createEndUser({
    authenticationMethods: [
      { type: "jwt", sub: "auth0|69387f18541e0e673845c6b6", kid: "1234567890" }
    ],
    evmAccount: { createSmartAccount: false },
    solanaAccount: { createSmartAccount: false }
  });

  console.log("Created end user:", endUser);

  // The end user's wallet addresses are now available.
  // You can fund these addresses before the user signs in.
  console.log("EVM address:", endUser.evmAccounts?.[0]);
  console.log("Solana address:", endUser.solanaAccounts?.[0]);
} catch (error) {
  console.error("Error creating end user:", error);
}
The sub value you use must match the sub claim in the JWTs your identity provider issues for this user.

Pre-generate wallet with an existing private key

If you already have access to your end users’ private keys, you can import them directly into Embedded Wallets. This is useful when migrating users from other wallet solutions—such as Server Wallets—to Embedded Wallets.
When to use import: Use this method when you have existing private keys for your users and want to preserve their wallet addresses during migration. The import flow is end-to-end encrypted, ensuring keys are never exposed outside of the SDK and the secure enclave.
The import API supports both EVM and Solana key types. Set keyType to either "evm" (hex-encoded private key) or "solana" (base58-encoded private key).

Email authentication

import { CdpClient } from "@coinbase/cdp-sdk";
import "dotenv/config";

const cdp = new CdpClient();

try {
  // Import an end user with an existing private key.
  // For Solana: use keyType: "solana" with a base58-encoded private key.
  const endUser = await cdp.endUser.importEndUser({
    authenticationMethods: [
      { type: "email", email: "[email protected]" }
    ],
    privateKey: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
    keyType: "evm",
  });

  console.log("Imported end user:", endUser);
  console.log("EVM accounts:", endUser.evmAccountObjects);
} catch (error) {
  console.error("Error importing end user:", error);
}

SMS authentication

import { CdpClient } from "@coinbase/cdp-sdk";
import "dotenv/config";

const cdp = new CdpClient();

try {
  // Import an end user with an existing private key.
  // For Solana: use keyType: "solana" with a base58-encoded private key.
  const endUser = await cdp.endUser.importEndUser({
    authenticationMethods: [
      { type: "sms", phoneNumber: "+12055555555" }
    ],
    privateKey: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
    keyType: "evm",
  });

  console.log("Imported end user:", endUser);
  console.log("EVM accounts:", endUser.evmAccountObjects);
} catch (error) {
  console.error("Error importing end user:", error);
}

Custom (JWT) authentication

import { CdpClient } from "@coinbase/cdp-sdk";
import "dotenv/config";

const cdp = new CdpClient();

try {
  // Import an end user with an existing private key.
  // For Solana: use keyType: "solana" with a base58-encoded private key.
  const endUser = await cdp.endUser.importEndUser({
    authenticationMethods: [
      { type: "jwt", sub: "auth0|69387f18541e0e673845c6b6", kid: "1234567890" }
    ],
    privateKey: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
    keyType: "evm",
  });

  console.log("Imported end user:", endUser);
  console.log("EVM accounts:", endUser.evmAccountObjects);
} catch (error) {
  console.error("Error importing end user:", error);
}
Security: Private keys should be handled with extreme care. Ensure you transmit keys securely and never log or expose them in your application. The import flow uses end-to-end encryption to protect keys during transmission.

Account Configuration

By default, pre-generated wallets are created as EOA (Externally Owned Accounts). You can configure the account type and features using the evmAccount and solanaAccount parameters.
Smart accounts are only supported for EVM. Solana accounts must have createSmartAccount set to false.

Smart Accounts

Create an EVM smart account instead of an EOA by setting createSmartAccount: true.
import { CdpClient } from "@coinbase/cdp-sdk";
import "dotenv/config";

const cdp = new CdpClient();

try {
  const endUser = await cdp.endUser.createEndUser({
    authenticationMethods: [
      { type: "email", email: "[email protected]" }
    ],
    evmAccount: { createSmartAccount: true },
    solanaAccount: { createSmartAccount: false }
  });

  console.log("Created end user with smart account:", endUser);
  
  // Access the smart account object
  const smartAccount = endUser.evmSmartAccountObjects?.[0];
  console.log("Smart account address:", smartAccount?.address);
  console.log("Owner addresses:", smartAccount?.ownerAddresses);
  console.log("Solana address:", endUser.solanaAccounts?.[0]);
} catch (error) {
  console.error("Error creating end user:", error);
}
Spend permissions allow you to grant allowances to specific addresses, enabling delegated transactions without requiring user signatures for each action. Learn more in the Spend Permissions documentation.

Spend Permissions

Enable spend permissions on an EVM smart account by setting enableSpendPermissions: true. This requires createSmartAccount: true.
import { CdpClient } from "@coinbase/cdp-sdk";
import "dotenv/config";

const cdp = new CdpClient();

try {
  const endUser = await cdp.endUser.createEndUser({
    authenticationMethods: [
      { type: "email", email: "[email protected]" }
    ],
    evmAccount: { 
      createSmartAccount: true,
      enableSpendPermissions: true
    },
    solanaAccount: { createSmartAccount: false }
  });

  console.log("Created end user with spend permissions:", endUser);
  
  const smartAccount = endUser.evmSmartAccountObjects?.[0];
  console.log("Smart account address:", smartAccount?.address);
  
  // When spend permissions are enabled, there are 2 owner addresses:
  // 1. User's owner address
  // 2. Spend Permission Manager address
  console.log("Owner addresses:", smartAccount?.ownerAddresses);
  console.log("Solana address:", endUser.solanaAccounts?.[0]);
} catch (error) {
  console.error("Error creating end user:", error);
}