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: The CDP SDK requires Python 3.10 or higher. You can run the following command in your terminal to check your local Python 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.
- Create a CDP Secret API key
- Generate a Wallet Secret
- 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:
- Initialize a new project by running
mkdir cdp-sdk-example && cd cdp-sdk-example && npm init -y
- 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": ""
}
- Install the CDP SDK
npm install @coinbase/cdp-sdk
- Create a new file called
main.ts
- Copy and paste the example code you’d like to run into
main.ts
- Execute
main.ts
Follow these steps to run the code snippets in this quickstart:
- Initialize a new project
mkdir cdp-sdk-example && cd cdp-sdk-example && python -m venv .venv && source .venv/bin/activate
- Install the CDP SDK
- Create a new file called
main.py
- Copy and paste the example code you’d like to run into
main.py
- Execute
main.py
Create an EVM Account
The following code illustrate how to create an EVM account managed by the v2 Wallet API.
import { CdpClient } from "@coinbase/cdp-sdk";
const cdp = new CdpClient();
const evmAccount = await cdp.evm.createAccount();
console.log(`Created account: ${evmAccount.address}`);
import asyncio
from cdp import CdpClient
async def main():
async with CdpClient() as cdp:
evm_account = await cdp.evm.create_account()
print(f"Created account: {evm_account.address}")
asyncio.run(main())
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.
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}`
);
import asyncio
from cdp import CdpClient
async def main():
async with CdpClient() as cdp:
evm_account = await cdp.evm.create_account()
hash = await cdp.evm.request_faucet(
address=evm_account.address, network="base-sepolia", token="eth"
)
print(f"Requested funds from faucet: https://sepolia.basescan.org/tx/{hash}")
asyncio.run(main())
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.
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}`);
import asyncio
from cdp import CdpClient
async def main():
async with CdpClient() as cdp:
evm_account = await cdp.evm.create_account()
evm_smart_account = await cdp.evm.create_smart_account(evm_account)
print(f"Created smart account: {evm_smart_account.address}")
asyncio.run(main())
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 EVMRun the following code snippetimport { 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}`,
);
Install web3 to send transactions on EVMRun the following code snippetimport asyncio
from web3 import Web3
from cdp import CdpClient
w3 = Web3(Web3.HTTPProvider("https://sepolia.base.org"))
async def main():
async with CdpClient() as cdp:
evm_account = await cdp.evm.create_account()
print(f"Created account: {evm_account.address}")
faucet_hash = await cdp.evm.request_faucet(
address=evm_account.address, network="base-sepolia", token="eth"
)
w3.eth.wait_for_transaction_receipt(faucet_hash)
print(f"Received ETH from faucet: https://sepolia.basescan.org/tx/{faucet_hash}")
to = "0x0000000000000000000000000000000000000000"
value = w3.to_wei(0.000001, "ether")
gas_estimate = w3.eth.estimate_gas(
{"to": to, "from": evm_account.address, "value": value}
)
max_priority_fee = w3.eth.max_priority_fee
max_fee = w3.eth.gas_price + max_priority_fee
signed_tx = await evm_account.sign_transaction(
transaction_dict={
"to": to,
"value": value,
"chainId": 84532,
"gas": gas_estimate,
"maxFeePerGas": max_fee,
"maxPriorityFeePerGas": max_priority_fee,
"nonce": w3.eth.get_transaction_count(evm_account.address),
"type": "0x2",
}
)
tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction)
print(f"Sent {w3.from_wei(value, 'ether')} ETH to {to}: https://sepolia.basescan.org/tx/0x{tx_hash.hex()}")
asyncio.run(main())
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 Solananpm install @solana/web3.js
Run the following code snippetimport {
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)
Install solana to send transactions on SolanaRun the following code snippetimport time
import base64
import asyncio
from cdp import CdpClient
from solana.rpc.api import Client as SolanaClient
from solana.rpc.types import TxOpts
from solders.pubkey import Pubkey as PublicKey
from solders.system_program import TransferParams, transfer
from solders.message import Message
cdp = CdpClient()
connection = SolanaClient("https://api.devnet.solana.com")
async def create_sol_account():
solana_account = await cdp.solana.create_account()
print(f"Created account: {solana_account.address}")
return solana_account
async def request_faucet(address: str):
await cdp.solana.request_faucet(
address,
token="sol"
)
async def wait_for_balance(address: str):
balance = 0
max_attempts = 30
attempts = 0
while balance == 0 and attempts < max_attempts:
balance_resp = connection.get_balance(PublicKey.from_string(address))
balance = balance_resp.value
if balance == 0:
print("Waiting for funds...")
time.sleep(1)
attempts += 1
else:
print(f"Account funded with {balance / 1e9} SOL ({balance} lamports)")
if balance == 0:
raise ValueError("Account not funded after multiple attempts")
async def send_transaction(address: str):
# Amount of lamports to send (default: 1000 = 0.000001 SOL)
lamportsToSend = 1000;
fromAddress = PublicKey.from_string(address)
toAddress = PublicKey.from_string("EeVPcnRE1mhcY85wAh3uPJG1uFiTNya9dCJjNUPABXzo")
blockhash_resp = connection.get_latest_blockhash()
blockhash = blockhash_resp.value.blockhash
transfer_params = TransferParams(
from_pubkey=fromAddress,
to_pubkey=toAddress,
lamports=lamports_to_send,
)
transfer_instr = transfer(transfer_params)
message = Message.new_with_blockhash(
[transfer_instr],
fromAddress,
blockhash,
)
# Create a transaction envelope with signature space
sig_count = bytes([1]) # 1 byte for signature count (1)
empty_sig = bytes([0] * 64) # 64 bytes of zeros for the empty signature
message_bytes = bytes(message) # Get the serialized message bytes
# Concatenate to form the transaction bytes
tx_bytes = sig_count + empty_sig + message_bytes
# Encode to base64 used by CDP API
serialized_tx = base64.b64encode(tx_bytes).decode("utf-8")
signed_tx_response = await cdp.solana.sign_transaction(
address,
transaction=serialized_tx,
)
# Decode the signed transaction from base64
decoded_signed_tx = base64.b64decode(signed_tx_response.signed_transaction)
print("Sending transaction...")
tx_resp = connection.send_raw_transaction(
decoded_signed_tx,
opts=TxOpts(skip_preflight=False, preflight_commitment="processed"),
)
signature = tx_resp.value
print("Waiting for transaction to be confirmed...")
confirmation = connection.confirm_transaction(signature, commitment="processed")
if hasattr(confirmation, "err") and confirmation.err:
raise ValueError(f"Transaction failed: {confirmation.err}")
print(f"Sent SOL: https://explorer.solana.com/tx/{signature}?cluster=devnet")
async def main():
account = await create_sol_account()
await request_faucet(account.address)
await wait_for_balance(account.address)
await send_transaction(account.address)
await cdp.close()
asyncio.run(main())