Skip to content

Latest commit

 

History

History
 
 

README.md

CDP Java SDK

The official Java SDK for the Coinbase Developer Platform (CDP).

Table of Contents

Requirements

  • Java 21 or higher
  • Gradle 8.x (included via wrapper)

Installation

GitHub Packages

The SDK is also available via GitHub Packages. This requires GitHub authentication.

Step 1: Add your GitHub credentials to ~/.gradle/gradle.properties:

gpr.user=YOUR_GITHUB_USERNAME
gpr.token=YOUR_GITHUB_PERSONAL_ACCESS_TOKEN

Generate a token at https://github.com/settings/tokens with read:packages scope.

Step 2: Add the repository and dependency in your build.gradle.kts:

repositories {
    maven {
        url = uri("https://maven.pkg.github.com/coinbase/cdp-sdk")
        credentials {
            username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR")
            password = project.findProperty("gpr.token") as String? ?: System.getenv("GITHUB_TOKEN")
        }
    }
}

dependencies {
    implementation("com.coinbase:cdp-sdk:0.6.0")
}

API Keys

To start, create a CDP API Key. Save the API Key ID and API Key Secret for use in the SDK. You will also need to create a wallet secret in the Portal to sign transactions.

Usage

Initialization

From environment variables

Set your API keys as environment variables:

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"  # Required for write operations

Then initialize the client:

import com.coinbase.cdp.CdpClient;

try (CdpClient cdp = CdpClient.create()) {
    var account = cdp.evm().createAccount(
        new CreateEvmAccountRequest().name("my-account")
    );
    System.out.println("Created account: " + account.getAddress());
}

With explicit credentials

import com.coinbase.cdp.CdpClient;

try (CdpClient cdp = CdpClient.builder()
        .credentials("your-api-key-id", "your-api-key-secret")
        .walletSecret("your-wallet-secret")
        .build()) {
    var account = cdp.evm().createAccount(
        new CreateEvmAccountRequest().name("my-account")
    );
}

With CdpClientOptions

For more configuration options:

import com.coinbase.cdp.CdpClient;
import com.coinbase.cdp.CdpClientOptions;

CdpClientOptions options = CdpClientOptions.builder()
    .apiKeyId("your-api-key-id")
    .apiKeySecret("your-api-key-secret")
    .walletSecret("your-wallet-secret")
    .debugging(true)  // Enable debug logging
    .build();

try (CdpClient cdp = CdpClient.create(options)) {
    // Use the client...
}

Client Lifecycle

The CDP client wraps an HTTP client and should be created once and reused throughout your application's lifecycle. Use try-with-resources to ensure proper cleanup:

try (CdpClient cdp = CdpClient.create()) {
    // Use cdp throughout your application
}
  • Long-lived services: Create a single client instance at startup
  • Serverless/request-based runtimes: Create once per cold start
  • Concurrency: The client is safe to use across concurrent operations

Creating Accounts

Create an EVM account

import com.coinbase.cdp.openapi.model.CreateEvmAccountRequest;

var account = cdp.evm().createAccount(
    new CreateEvmAccountRequest().name("my-evm-account")
);
System.out.println("Address: " + account.getAddress());

Create a Solana account

import com.coinbase.cdp.openapi.model.CreateSolanaAccountRequest;

var account = cdp.solana().createAccount(
    new CreateSolanaAccountRequest().name("my-solana-account")
);
System.out.println("Address: " + account.getAddress());

List accounts

// List EVM accounts
var evmAccounts = cdp.evm().listAccounts();
System.out.println("EVM accounts: " + evmAccounts.getAccounts().size());

// List Solana accounts
var solanaAccounts = cdp.solana().listAccounts();
System.out.println("Solana accounts: " + solanaAccounts.getAccounts().size());

Get an account by address

var account = cdp.evm().getAccount("0x1234...");
System.out.println("Account name: " + account.getName());

Testnet Faucet

Request testnet tokens for development:

Request testnet ETH

import com.coinbase.cdp.openapi.model.RequestEvmFaucetRequest;
import com.coinbase.cdp.openapi.model.RequestEvmFaucetRequest.NetworkEnum;
import com.coinbase.cdp.openapi.model.RequestEvmFaucetRequest.TokenEnum;

var response = cdp.evm().requestFaucet(
    new RequestEvmFaucetRequest()
        .address(account.getAddress())
        .network(NetworkEnum.BASE_SEPOLIA)
        .token(TokenEnum.ETH)
);
System.out.println("Faucet tx: " + response.getTransactionHash());

Request testnet USDC

var response = cdp.evm().requestFaucet(
    new RequestEvmFaucetRequest()
        .address(account.getAddress())
        .network(NetworkEnum.BASE_SEPOLIA)
        .token(TokenEnum.USDC)
);

Signing Messages

Sign a message

import com.coinbase.cdp.openapi.model.SignEvmMessageRequest;

var response = cdp.evm().signMessage(
    account.getAddress(),
    new SignEvmMessageRequest().message("Hello, CDP!")
);
System.out.println("Signature: " + response.getSignature());

Sign EIP-712 typed data

import com.coinbase.cdp.openapi.model.EIP712Domain;
import com.coinbase.cdp.openapi.model.EIP712Message;

EIP712Domain domain = new EIP712Domain()
    .name("MyDApp")
    .version("1")
    .chainId(1L)
    .verifyingContract("0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC");

Map<String, Object> types = Map.of(
    "Person", List.of(
        Map.of("name", "name", "type", "string"),
        Map.of("name", "wallet", "type", "address")
    )
);

Map<String, Object> message = Map.of(
    "name", "Alice",
    "wallet", account.getAddress()
);

EIP712Message eip712Message = new EIP712Message()
    .domain(domain)
    .types(types)
    .primaryType("Person")
    .message(message);

var response = cdp.evm().signTypedData(account.getAddress(), eip712Message);
System.out.println("Signature: " + response.getSignature());

Transferring Tokens

Transfer USDC on EVM

import com.coinbase.cdp.client.evm.EvmClientOptions.TransferOptions;
import com.coinbase.cdp.openapi.model.SendEvmTransactionRequest.NetworkEnum;
import java.math.BigInteger;

var result = cdp.evm().transfer(
    sender.getAddress(),
    TransferOptions.builder()
        .to(receiver.getAddress())
        .amount(new BigInteger("10000"))  // 0.01 USDC (6 decimals)
        .token("usdc")
        .network(NetworkEnum.BASE_SEPOLIA)
        .build()
);

System.out.println("Transaction: " + result.getTransactionHash());
System.out.println("Explorer: https://sepolia.basescan.org/tx/" + result.getTransactionHash());

Transfer SOL on Solana

import com.coinbase.cdp.client.solana.SolanaClientOptions.TransferOptions;
import com.coinbase.cdp.openapi.model.SendSolanaTransactionRequest.NetworkEnum;
import java.math.BigInteger;

var result = cdp.solana().transfer(
    sender.getAddress(),
    TransferOptions.builder()
        .to(receiver.getAddress())
        .amount(new BigInteger("10000000"))  // 0.01 SOL (9 decimals)
        .token("sol")
        .network(NetworkEnum.SOLANA_DEVNET)
        .build()
);

System.out.println("Signature: " + result.getSignature());

End User Management

The SDK provides an EndUserClient for creating, managing, and performing delegated signing/sending operations on behalf of end users.

Create an end user

import com.coinbase.cdp.openapi.model.CreateEndUserRequest;

var endUser = cdp.endUser().createEndUser(
    new CreateEndUserRequest()
);
System.out.println("User ID: " + endUser.getUserId());

List end users

import com.coinbase.cdp.client.enduser.EndUserClientOptions.ListEndUsersOptions;

var response = cdp.endUser().listEndUsers(
    ListEndUsersOptions.builder()
        .pageSize(10)
        .build()
);
System.out.println("End users: " + response.getEndUsers().size());

Get an end user

var endUser = cdp.endUser().getEndUser("user-id");
System.out.println("User: " + endUser.getUserId());

Add accounts to an end user

// Add an EVM EOA account
var evmResult = cdp.endUser().addEndUserEvmAccount("user-id");
System.out.println("EVM address: " + evmResult.getEvmAccount().getAddress());

// Add an EVM smart account
import com.coinbase.cdp.openapi.model.AddEndUserEvmSmartAccountRequest;

var smartResult = cdp.endUser().addEndUserEvmSmartAccount(
    "user-id",
    new AddEndUserEvmSmartAccountRequest()
);

// Add a Solana account
var solResult = cdp.endUser().addEndUserSolanaAccount("user-id");
System.out.println("Solana address: " + solResult.getSolanaAccount().getAddress());

Delegated EVM signing

Sign an EVM transaction

import com.coinbase.cdp.openapi.model.SignEvmTransactionWithEndUserAccountRequest;

var result = cdp.endUser().signEvmTransaction(
    "user-id",
    new SignEvmTransactionWithEndUserAccountRequest()
        .transaction("0x...")
        .address("0x1234...")
);
System.out.println("Signature: " + result.getSignature());

Sign an EVM message

import com.coinbase.cdp.openapi.model.SignEvmMessageWithEndUserAccountRequest;

var result = cdp.endUser().signEvmMessage(
    "user-id",
    new SignEvmMessageWithEndUserAccountRequest()
        .message("Hello, CDP!")
        .address("0x1234...")
);
System.out.println("Signature: " + result.getSignature());

Delegated EVM sending

Send an EVM transaction

import com.coinbase.cdp.openapi.model.SendEvmTransactionWithEndUserAccountRequest;

var result = cdp.endUser().sendEvmTransaction(
    "user-id",
    new SendEvmTransactionWithEndUserAccountRequest()
        .transaction("0x...")
        .address("0x1234...")
        .network("base-sepolia")
);
System.out.println("Transaction hash: " + result.getTransactionHash());

Send an EVM asset

import com.coinbase.cdp.openapi.model.SendEvmAssetWithEndUserAccountRequest;

var result = cdp.endUser().sendEvmAsset(
    "user-id",
    "0x1234...",  // sender address
    "usdc",       // asset
    new SendEvmAssetWithEndUserAccountRequest()
        .to("0x9F663335Cd6Ad02a37B633602E98866CF944124d")
        .amount("10000")
        .network("base-sepolia")
);
System.out.println("Transaction hash: " + result.getTransactionHash());

Delegated Solana signing

Sign a Solana message

import com.coinbase.cdp.openapi.model.SignSolanaMessageWithEndUserAccountRequest;

var result = cdp.endUser().signSolanaMessage(
    "user-id",
    new SignSolanaMessageWithEndUserAccountRequest()
        .message("Hello, Solana!")
        .address("7EcDhSYGxXyscszYEp35KHN8vvw3svAuLKTzXwCFLtV")
);
System.out.println("Signature: " + result.getSignature());

Delegated Solana sending

Send a Solana asset

import com.coinbase.cdp.openapi.model.SendSolanaAssetWithEndUserAccountRequest;

var result = cdp.endUser().sendSolanaAsset(
    "user-id",
    "7EcDhSYGxXyscszYEp35KHN8vvw3svAuLKTzXwCFLtV",  // sender address
    "usdc",                                             // asset
    new SendSolanaAssetWithEndUserAccountRequest()
        .to("3KzDtddx4i53FBkvCzuDmRbaMozTZoJBb1TToWhz3JfE")
        .amount("1000000")
        .network("solana-devnet")
);
System.out.println("Signature: " + result.getTransactionSignature());

Revoke delegation

Revoke all active delegations for an end user:

cdp.endUser().revokeDelegation("user-id");

HTTP Retry Configuration

The SDK supports configurable HTTP retry behavior with exponential backoff and jitter for handling transient failures and rate limiting.

Default retry configuration

import com.coinbase.cdp.http.RetryConfig;

// Default: 3 retries, 100ms initial backoff, 30s max, 2x multiplier, 25% jitter
RetryConfig config = RetryConfig.defaultConfig();

Custom retry configuration

import com.coinbase.cdp.http.RetryConfig;
import java.time.Duration;

RetryConfig retryConfig = RetryConfig.builder()
    .maxRetries(5)
    .initialBackoff(Duration.ofMillis(200))
    .maxBackoff(Duration.ofSeconds(60))
    .backoffMultiplier(2.0)
    .jitterFactor(0.3)
    .build();

try (CdpClient cdp = CdpClient.builder()
        .credentials("api-key-id", "api-key-secret")
        .retryConfig(retryConfig)
        .build()) {
    // Requests will retry with custom backoff
}

Disable retries

RetryConfig noRetries = RetryConfig.disabled();

CdpClientOptions options = CdpClientOptions.builder()
    .apiKeyId("api-key-id")
    .apiKeySecret("api-key-secret")
    .retryConfig(noRetries)
    .build();

Retryable status codes

By default, the SDK retries on these status codes:

  • 429 - Rate limiting
  • 500, 502, 503, 504 - Server errors

TokenProvider Pattern

For environments where you want to generate tokens separately from making API calls, use the TokenProvider pattern:

import com.coinbase.cdp.auth.CdpTokenGenerator;
import com.coinbase.cdp.auth.CdpTokenRequest;
import com.coinbase.cdp.auth.TokenProvider;

// Create a token generator (typically on your backend)
CdpTokenGenerator tokenGenerator = new CdpTokenGenerator(
    apiKeyId, apiKeySecret, Optional.of(walletSecret)
);

// Generate tokens for a specific request
CdpTokenRequest tokenRequest = CdpTokenRequest.builder()
    .requestMethod("POST")
    .requestPath("/platform/v2/evm/accounts")
    .requestHost("api.cdp.coinbase.com")
    .includeWalletAuthToken(true)
    .requestBody(new CreateEvmAccountRequest().name("my-account"))
    .build();

TokenProvider tokens = tokenGenerator.generateTokens(tokenRequest);

// Use tokens with the client (typically on the edge/client)
try (CdpClient cdp = CdpClient.builder()
        .tokenProvider(tokens)
        .build()) {
    var account = cdp.evm().createAccount(
        new CreateEvmAccountRequest().name("my-account")
    );
}

Important: Wallet JWTs are request-specific. Each write operation needs its own TokenProvider because the wallet JWT includes the HTTP method, path, and body hash.

Low-Level API Access

For advanced use cases, you can access the generated OpenAPI classes directly:

import com.coinbase.cdp.openapi.api.EvmAccountsApi;
import com.coinbase.cdp.openapi.api.SolanaAccountsApi;
import com.coinbase.cdp.openapi.api.PolicyEngineApi;

try (CdpClient cdp = CdpClient.create()) {
    // Get the configured ApiClient
    var apiClient = cdp.getApiClient();

    // Create API instances
    EvmAccountsApi evmApi = new EvmAccountsApi(apiClient);
    SolanaAccountsApi solanaApi = new SolanaAccountsApi(apiClient);
    PolicyEngineApi policiesApi = new PolicyEngineApi(apiClient);

    // Read operations
    var accounts = evmApi.listEvmAccounts(null, null, null);

    // Write operations require wallet JWT
    var request = new CreateEvmAccountRequest().name("my-account");
    String walletJwt = cdp.generateWalletJwt("POST", "/v2/evm/accounts", request);
    var account = evmApi.createEvmAccount(walletJwt, null, request);
}

Available API Classes

API Class Purpose
EvmAccountsApi EVM account management (create, list, sign)
EvmSmartAccountsApi EVM smart account operations (ERC-4337)
EvmSwapsApi Token swap operations on EVM chains
SolanaAccountsApi Solana account management
PolicyEngineApi Policy management for operation controls
FaucetsApi Request testnet funds
OnchainDataApi Query on-chain data
EndUserAccountManagementApi End user CRUD operations
EndUserAccountsApi End user transaction operations

See the com.coinbase.cdp.openapi.api package for all available APIs.

Authentication Tools

The SDK exposes JWT generation utilities for custom integrations:

import com.coinbase.cdp.auth.JwtGenerator;
import com.coinbase.cdp.auth.JwtOptions;
import com.coinbase.cdp.auth.WalletJwtGenerator;
import com.coinbase.cdp.auth.WalletJwtOptions;

// Generate JWT for REST API
JwtOptions options = JwtOptions.builder("key-id", "key-secret")
    .requestMethod("GET")
    .requestHost("api.cdp.coinbase.com")
    .requestPath("/platform/v2/evm/accounts")
    .expiresIn(120)
    .build();

String jwt = JwtGenerator.generateJwt(options);

// Generate JWT for WebSocket (no URI claims)
JwtOptions wsOptions = JwtOptions.builder("key-id", "key-secret").build();
String wsJwt = JwtGenerator.generateJwt(wsOptions);

// Generate Wallet JWT for write operations
WalletJwtOptions walletOptions = new WalletJwtOptions(
    walletSecret,
    "POST",
    "api.cdp.coinbase.com",
    "/platform/v2/evm/accounts",
    Map.of("name", "my-account")
);
String walletJwt = WalletJwtGenerator.generateWalletJwt(walletOptions);

Error Handling

The SDK uses exceptions from the generated OpenAPI client:

try {
    var account = cdp.evm().createAccount(
        new CreateEvmAccountRequest().name("my-account")
    );
} catch (com.coinbase.cdp.openapi.ApiException e) {
    System.err.println("API error: " + e.getCode() + " - " + e.getMessage());
    System.err.println("Response body: " + e.getResponseBody());
}

Features

  • Multi-blockchain support: EVM chains and Solana
  • High-level API: Namespace clients (cdp.evm(), cdp.solana(), cdp.policies(), cdp.endUser())
  • Server-managed accounts: Create and manage accounts on CDP
  • Smart accounts: ERC-4337 account abstraction support
  • End user management: Create, manage, and perform delegated operations on end users
  • Policy engine: Define operation controls
  • Dual key support: EC (ES256) and Ed25519 (EdDSA) authentication
  • Automatic auth: API key JWT headers added automatically
  • Configurable retries: Exponential backoff with jitter
  • TokenProvider pattern: Flexible authentication for serverless deployments

Development

Build

make build

Test

# Run unit tests
make test

# Run E2E tests (requires API credentials)
make test-e2e

Lint

# Check code style
make lint

# Fix code style issues
make lint-fix

Generate OpenAPI Client

make client

Generate Documentation

make docs

Examples

Working examples are available in the examples/java directory:

Task Command
Quickstart ./gradlew runQuickstart
Create EVM account ./gradlew runCreateEvmAccount
List EVM accounts ./gradlew runListEvmAccounts
Sign message ./gradlew runSignMessage
Request faucet ./gradlew runRequestFaucet
Transfer tokens ./gradlew runTransfer
TokenProvider pattern ./gradlew runSignTypedDataWithTokenProvider
Retry configuration ./gradlew runRetryConfiguration
Create Solana account ./gradlew runCreateSolanaAccount
Solana transfer ./gradlew runSolanaTransfer

Run ./gradlew listExamples for the full list.

Documentation

Support

License

MIT License - see LICENSE