The v2 Wallet API allows you to create onchain accounts and send funds within minutes. In this quickstart, you will learn how to create an EVM account, fund it with testnet ETH, and transfer funds between accounts.

What you’ll learn

  • How to install the v2 CDP SDK
  • How to create an EVM account
  • How to fund your account with testnet ETH
  • How to create an EVM smart account
  • How to transfer funds between accounts
  • How to create and use a Solana account

Requirements

Make sure that your developer environment satisfies all of the requirements before proceeding through the quickstart.
The CDP SDK requires Node.js v18.x or higher. You can run the following command in your terminal to check your local Node.js version:
node --version

Installation

npm install @coinbase/cdp-sdk

Environment Setup

Follow these instructions to set up your environment to use the v2 Wallet API. Make sure to set up your environment variables before running the code snippets.
  1. Create a CDP Secret API key
  2. Generate a Wallet Secret
  3. Export these variables in your shell
export CDP_API_KEY_ID=your-api-key-id
export CDP_API_KEY_SECRET=your-api-key-secret
export CDP_WALLET_SECRET=your-wallet-secret
Follow these steps to run the code snippets in this quickstart:
  1. Initialize a new project by running
mkdir cdp-sdk-example && cd cdp-sdk-example && npm init -y
  1. Add "type": "module" to your package.json file that was created in the previous step
{
  "name": "cdp-sdk-example",
  "version": "1.0.0",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": ""
}
  1. Install the CDP SDK
npm install @coinbase/cdp-sdk
  1. Create a new file called main.ts
touch main.ts
  1. Copy and paste the example code you’d like to run into main.ts
  2. Execute main.ts
npx tsx main.ts

Create an EVM Account

The following code illustrate how to create an EVM account managed by the v2 Wallet API.
main.ts
import { CdpClient } from "@coinbase/cdp-sdk";

const cdp = new CdpClient();

const evmAccount = await cdp.evm.createAccount();
console.log(`Created account: ${evmAccount.address}`);
In a production environment, we recommend turning on IP Whitelisting.

Fund an EVM Account

Accounts do not have funds on them to start. We provide a Faucet API to fund your account with testnet ETH.
main.ts
import { CdpClient } from "@coinbase/cdp-sdk";

const cdp = new CdpClient();

const evmAccount = await cdp.evm.createAccount();
const { transactionHash } = await cdp.evm.requestFaucet({
  address: evmAccount.address,
  network: "base-sepolia",
  token: "eth",
});

console.log(
  `Requested funds from faucet: https://sepolia.basescan.org/tx/${transactionHash}`
);

Create an EVM Smart Account

Smart accounts are smart contracts on EVM blockchains that offer advanced functionality such as gas sponsorships and spend permissions. Smart accounts require an owner account to sign on its behalf. In this example, we will use the previously created EVM account as the owner.
main.ts
import { CdpClient } from "@coinbase/cdp-sdk";

const cdp = new CdpClient();

const evmAccount = await cdp.evm.createAccount();
const smartAccount = await cdp.evm.createSmartAccount({
  owner: evmAccount,
});
console.log(`Created smart account: ${smartAccount.address}`);

Transfer Funds

Now that your faucet transaction has successfully completed, you can send the funds in your original account to your smart account.
Install viem to send transactions on EVM
npm install viem
Run the following code snippet
main.ts
import { createPublicClient, createWalletClient, http, parseEther, formatEther } from "viem";
import { toAccount } from "viem/accounts";
import { baseSepolia } from "viem/chains";
import { CdpClient } from "@coinbase/cdp-sdk";

const cdp = new CdpClient();

const account = await cdp.evm.createAccount();
console.log(`Created account: ${account.address}`);

const { transactionHash } = await cdp.evm.requestFaucet({
  address: account.address,
  network: "base-sepolia",
  token: "eth",
});

const publicClient = createPublicClient({
  chain: baseSepolia,
  transport: http(),
});

await publicClient.waitForTransactionReceipt({
  hash: transactionHash,
});

console.log(
  `Received ETH from faucet: https://sepolia.basescan.org/tx/${transactionHash}`
);

const walletClient = createWalletClient({
  account: toAccount(account),
  chain: baseSepolia,
  transport: http(),
});

const to = "0x4252e0c9A3da5A2700e7d91cb50aEf522D0C6Fe8";
const value = parseEther("0.000001");

const hash = await walletClient.sendTransaction({
  to,
  value,
});

console.log(
  `Sent ${formatEther(value)} ETH to ${to}: https://sepolia.basescan.org/tx/${hash}`,
);

Use Solana Accounts

Solana accounts are similar to EVM accounts in that they are used to send and receive funds, except on the Solana blockchain. The code below demonstrates creating an account, requesting faucet funds, and sending a transaction.
Install @solana/web3.js to send transactions on Solana
npm install @solana/web3.js
Run the following code snippet
main.ts
import {
  Connection,
  PublicKey,
  SystemProgram,
  Transaction,
} from "@solana/web3.js";
import { CdpClient } from "@coinbase/cdp-sdk";

const cdp = new CdpClient();

const connection = new Connection("https://api.devnet.solana.com");

async function createAccount() {
  const account = await cdp.solana.createAccount();
  console.log(`Created account: ${account.address}`);
  return account;
}

async function requestFaucet(address: string) {
  await cdp.solana.requestFaucet({
    address,
    token: "sol",
  });
}

async function waitForBalance(address: string) {
  let balance = 0;
  let attempts = 0;
  const maxAttempts = 30;

  while (balance === 0 && attempts < maxAttempts) {
    balance = await connection.getBalance(new PublicKey(address));
    if (balance === 0) {
      console.log("Waiting for funds...");
      await new Promise(resolve => setTimeout(resolve, 1000));
      attempts++;
    } else {
      console.log("Account funded with", balance / 1e9, "SOL");
    }
  }

  if (balance === 0) {
    throw new Error("Account not funded after multiple attempts");
  }
}

async function sendTransaction(address: string) {
  // Amount of lamports to send (default: 1000 = 0.000001 SOL)
  const lamportsToSend = 1000;
  const fromAddress = new PublicKey(address)
  const toAddress = new PublicKey("EeVPcnRE1mhcY85wAh3uPJG1uFiTNya9dCJjNUPABXzo");

  const { blockhash } = await connection.getLatestBlockhash();

  const transaction = new Transaction();
  transaction.add(
    SystemProgram.transfer({
      fromPubkey: fromAddress,
      toPubkey: toAddress,
      lamports: lamportsToSend,
   })
  );

  transaction.recentBlockhash = blockhash;
  transaction.feePayer = fromAddress;

  const serializedTx = Buffer.from(
    transaction.serialize({ requireAllSignatures: false })
  ).toString("base64");

  const { signature: txSignature } = await cdp.solana.signTransaction({
    address,
    transaction: serializedTx,
  });
  const decodedSignedTx = Buffer.from(txSignature, "base64");

  console.log("Sending transaction...");
  const txSendSignature = await connection.sendRawTransaction(decodedSignedTx);

  const latestBlockhash = await connection.getLatestBlockhash();

  console.log("Waiting for transaction to be confirmed...");
  const confirmation = await connection.confirmTransaction({
    signature: txSendSignature,
    blockhash: latestBlockhash.blockhash,
    lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
  });

  if (confirmation.value.err) {
    throw new Error(`Transaction failed: ${confirmation.value.err.toString()}`);
  }

  console.log(`Sent SOL: https://explorer.solana.com/tx/${txSendSignature}?cluster=devnet`);
}

async function main() {
  const account = await createAccount();
  await requestFaucet(account.address);
  await waitForBalance(account.address);
  await sendTransaction(account.address);
}

main().catch(console.error)