Skip to main content
The Go SDK provides idiomatic Go examples demonstrating how to use the CDP SDK for account management, transaction signing, and smart account operations.

Setup

1

Get CDP Credentials

Get your CDP API key and wallet secret from the CDP Portal
2

Configure Environment

Copy .env.example to .env and add your credentials:
cd examples/go
cp .env.example .env
Edit .env:
.env
CDP_API_KEY_ID=your_api_key_id
CDP_API_KEY_SECRET=your_api_key_secret
CDP_WALLET_SECRET=your_wallet_secret
3

Install Dependencies

go mod download
The Go SDK requires Go 1.21 or higher.

Running Examples

The Go examples use a CLI-style interface. Run examples using:
go run . <example_name>
Available examples:
  • send_transaction - Create an EVM account and send a transaction
  • send_user_operation - Create a smart account and send a user operation

Example Usage

# Send a transaction with an EOA
go run . send_transaction

# Send a user operation with a smart account
go run . send_user_operation

Send Transaction Example

This example demonstrates the complete flow of creating an EVM account, funding it, and sending a transaction.
send_transaction.go
package main

import (
	"context"
	"encoding/hex"
	"fmt"
	"log"
	"math/big"
	"os"
	"strings"
	"time"

	cdp "github.com/coinbase/cdp-sdk/go"
	"github.com/coinbase/cdp-sdk/go/openapi"
	"github.com/ethereum/go-ethereum"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/ethclient"
	"github.com/joho/godotenv"
)

// SendTransactionExample shows how to create an EVM Account and sign a transaction.
func SendTransactionExample() {
	ctx := context.Background()

	cdp, err := createCDPClient()
	if err != nil {
		log.Fatalf("Failed to create CDP client: %v", err)
	}

	name := "eoa"

	evmAddress, err := getOrCreateEvmAccount(ctx, &name, cdp)
	if err != nil {
		log.Printf("Failed to create EVM account: %v", err)
	}

	if err := faucetEVMAccount(ctx, cdp, evmAddress); err != nil {
		log.Printf("Failed to faucet EVM address: %v", err)
	}

	signedTransaction, err := createAndSignEVMTransaction(ctx, cdp, evmAddress)
	if err != nil {
		log.Printf("Failed to sign transaction: %v", err)
	}

	if err := sendSignedEVMTransaction(ctx, signedTransaction); err != nil {
		log.Printf("Failed to send transaction: %v", err)
	}
}

// createCDPClient creates and returns a new CDP client.
func createCDPClient() (*openapi.ClientWithResponses, error) {
	if err := godotenv.Load(); err != nil {
		log.Fatal("Error loading .env file: %w", err)
	}

	apiKeyID := os.Getenv("CDP_API_KEY_ID")
	if apiKeyID == "" {
		log.Fatal("CDP_API_KEY_ID environment variable is required")
	}

	apiKeySecret := os.Getenv("CDP_API_KEY_SECRET")
	if apiKeySecret == "" {
		log.Fatal("CDP_API_KEY_SECRET environment variable is required")
	}

	walletSecret := os.Getenv("CDP_WALLET_SECRET")
	if walletSecret == "" {
		log.Fatal("CDP_WALLET_SECRET environment variable is required")
	}

	apiURL := os.Getenv("CDP_API_URL")

	cdp, err := cdp.NewClient(cdp.ClientOptions{
		APIKeyID:     apiKeyID,
		APIKeySecret: apiKeySecret,
		WalletSecret: walletSecret,
		BasePath:     apiURL,
	})
	if err != nil {
		return nil, err
	}

	return cdp, nil
}

// getOrCreateEvmAccount creates a new EVM account or returns existing.
func getOrCreateEvmAccount(ctx context.Context, accountName *string, cdp *openapi.ClientWithResponses) (string, error) {
	log.Println("Creating EVM account...")

	response, err := cdp.GetEvmAccountByNameWithResponse(
		ctx,
		*accountName,
	)
	if err != nil {
		return "", err
	}

	if response.StatusCode() == 200 {
		return response.JSON200.Address, nil
	}

	if response.StatusCode() != 404 {
		return "", fmt.Errorf("failed to get EVM account by name: %v", response.StatusCode())
	}

	createResp, err := cdp.CreateEvmAccountWithResponse(
		ctx,
		nil,
		openapi.CreateEvmAccountJSONRequestBody{
			Name: accountName,
		},
	)
	if err != nil {
		return "", err
	}

	if createResp.StatusCode() != 201 {
		return "", fmt.Errorf("failed to create EVM account: %v", response.Status())
	}

	evmAddress := createResp.JSON201.Address
	log.Printf("EVM account created: %v", evmAddress)
	return evmAddress, nil
}

// faucetEVMAccount requests test ETH from the faucet.
func faucetEVMAccount(ctx context.Context, cdp *openapi.ClientWithResponses, evmAddress string) error {
	log.Printf("Fauceting EVM address on Base Sepolia: %v", evmAddress)

	response, err := cdp.RequestEvmFaucetWithResponse(
		ctx,
		openapi.RequestEvmFaucetJSONRequestBody{
			Address: evmAddress,
			Network: openapi.RequestEvmFaucetJSONBodyNetwork("base-sepolia"),
			Token:   openapi.RequestEvmFaucetJSONBodyToken("eth"),
		},
	)
	if err != nil {
		return err
	}

	if response.StatusCode() != 200 {
		return fmt.Errorf("failed to faucet EVM address: %v", response.Status())
	}

	log.Println("Fauceted EVM address on Base Sepolia")

	// Sleep to allow faucet transaction to propagate
	log.Println("Sleeping for 8 seconds...")
	time.Sleep(8 * time.Second)

	return nil
}

// createAndSignEVMTransaction creates and signs an EVM transaction.
func createAndSignEVMTransaction(ctx context.Context, cdp *openapi.ClientWithResponses, evmAddress string) (string, error) {
	toAddress := common.HexToAddress("0x450B2dC4Ba2a08E58C7ECc3DE48e3C825262caF8")

	transaction := types.DynamicFeeTx{
		ChainID:   big.NewInt(84532),
		Nonce:     0,
		To:        &toAddress,
		Value:     big.NewInt(10000000000000), // 0.00001 ETH
		Data:      []byte{},
		Gas:       21000,
		GasFeeCap: big.NewInt(1000000000), // 1 gwei max fee
		GasTipCap: big.NewInt(100000000),  // 0.1 gwei max priority fee
	}

	// Serialize transaction to RLP
	rlpTx := types.NewTx(&transaction)
	rlpData, err := rlpTx.MarshalBinary()
	if err != nil {
		return "", err
	}

	rlpHex := hex.EncodeToString(rlpData)
	rlpHex = "0x" + rlpHex

	response, err := cdp.SignEvmTransactionWithResponse(
		ctx,
		evmAddress,
		nil,
		openapi.SignEvmTransactionJSONRequestBody{
			Transaction: rlpHex,
		},
	)
	if err != nil {
		return "", err
	}

	if response.StatusCode() != 200 {
		return "", fmt.Errorf("failed to sign transaction: %v", string(response.Body))
	}

	log.Printf("Signed transaction: %v", response.JSON200.SignedTransaction)
	return response.JSON200.SignedTransaction, nil
}

// sendSignedEVMTransaction sends a signed transaction to the network.
func sendSignedEVMTransaction(ctx context.Context, signedTransaction string) error {
	log.Printf("Sending signed EVM transaction... %s\n", signedTransaction)

	ethClient, err := ethclient.Dial("https://sepolia.base.org")
	if err != nil {
		return fmt.Errorf("failed to dial Base Sepolia client: %v", err)
	}

	signedTxBytes, err := hex.DecodeString(strings.TrimPrefix(signedTransaction, "0x"))
	if err != nil {
		return fmt.Errorf("failed to decode signed transaction hex: %v", err)
	}

	var tx types.Transaction
	if err := tx.UnmarshalBinary(signedTxBytes); err != nil {
		return fmt.Errorf("failed to unmarshal signed transaction: %v", err)
	}

	err = ethClient.SendTransaction(ctx, &tx)
	if err != nil {
		return fmt.Errorf("failed to send transaction: %v", err)
	}

	log.Printf("Transaction sent: %v", tx.Hash())

	// Wait for transaction confirmation
	time.Sleep(2 * time.Second)

	for i := 0; i < 4; i++ {
		receipt, err := ethClient.TransactionReceipt(ctx, tx.Hash())
		if err == nil {
			log.Printf("Transaction confirmed in block %d", receipt.BlockNumber)
			return nil
		}
		if err != ethereum.NotFound {
			return fmt.Errorf("error checking transaction receipt: %v", err)
		}
		time.Sleep(2 * time.Second)
	}

	return fmt.Errorf("transaction not confirmed after 10 seconds")
}
What it does:
  • Initializes CDP client from environment variables
  • Creates or retrieves an EVM account by name
  • Requests testnet ETH from Base Sepolia faucet
  • Creates and signs a transaction using RLP encoding
  • Broadcasts the signed transaction to the network
  • Waits for transaction confirmation
Key Functions:
  • createCDPClient() - Initializes the CDP client with credentials
  • getOrCreateEvmAccount() - Idempotent account creation
  • faucetEVMAccount() - Requests testnet funds
  • createAndSignEVMTransaction() - Creates and signs EIP-1559 transaction
  • sendSignedEVMTransaction() - Broadcasts transaction and waits for confirmation
Run: go run . send_transaction

Send User Operation Example

This example demonstrates creating and using an EIP-4337 smart account to send gasless transactions.
send_user_operation.go
package main

import (
	"context"
	"fmt"
	"log"
	"math/big"

	"github.com/coinbase/cdp-sdk/go/openapi"
	"github.com/ethereum/go-ethereum/params"
)

// SendUserOperationExample shows how to create a smart account and send a user operation.
func SendUserOperationExample() {
	ctx := context.Background()

	cdp, err := createCDPClient()
	if err != nil {
		log.Fatalf("Failed to create CDP client: %v", err)
	}

	name := "eoa"

	owner, err := getOrCreateEvmAccount(ctx, &name, cdp)
	if err != nil {
		log.Printf("Failed to create EVM account: %v", err)
	}

	fmt.Printf("EVM account created: %v\n", owner)

	smartAccountName := "smart"

	smartAccount, err := getOrCreateSmartAccount(ctx, owner, smartAccountName, cdp)
	if err != nil {
		log.Printf("Failed to create Smart account: %v", err)
	}

	fmt.Printf("Smart account created: %v\n", smartAccount)

	if err := faucetEVMAccount(ctx, cdp, smartAccount); err != nil {
		log.Printf("Failed to faucet EVM address: %v", err)
	}

	userOpHash, err := prepareAndSendUserOperation(ctx, smartAccount, owner, cdp)
	if err != nil {
		log.Printf("Failed to send transaction: %v", err)
	}

	fmt.Printf("userOpHash sent: https://base-sepolia.blockscout.com/op/%s\n", userOpHash)
}

// getOrCreateSmartAccount creates a new Smart account or returns existing.
func getOrCreateSmartAccount(ctx context.Context, owner string, name string, cdp *openapi.ClientWithResponses) (string, error) {
	log.Println("Creating EVM Smart account...")

	getResp, err := cdp.GetEvmSmartAccountByNameWithResponse(ctx, name)
	if err != nil {
		return "", err
	}

	if getResp.StatusCode() == 200 {
		for _, addr := range getResp.JSON200.Owners {
			if addr == owner {
				return getResp.JSON200.Address, nil
			}
		}

		return "", fmt.Errorf("account with name %s does not have owner %s", name, owner)
	}

	if getResp.StatusCode() != 404 {
		return "", fmt.Errorf("failed to find smart account with name %s: %s", name, string(getResp.Body))
	}

	response, err := cdp.CreateEvmSmartAccountWithResponse(
		ctx,
		&openapi.CreateEvmSmartAccountParams{},
		openapi.CreateEvmSmartAccountJSONRequestBody{
			Owners: []string{
				owner,
			},
		},
	)
	if err != nil {
		return "", err
	}

	if response.StatusCode() != 201 {
		return "", fmt.Errorf("failed to create EVM smart account: %v", string(response.Body))
	}

	smartAccountAddress := response.JSON201.Address
	log.Printf("Smart account created: %v", smartAccountAddress)
	return smartAccountAddress, nil
}

// prepareAndSendUserOperation creates and sends a user operation.
func prepareAndSendUserOperation(ctx context.Context, address string, owner string, cdp *openapi.ClientWithResponses) (string, error) {
	val, err := parseEther("0.000001")
	if err != nil {
		return "", err
	}

	response, err := cdp.PrepareAndSendUserOperationWithResponse(
		ctx,
		address,
		nil,
		openapi.PrepareAndSendUserOperationJSONRequestBody{
			Calls: []openapi.EvmCall{
				{
					To:    "0x0000000000000000000000000000000000000000",
					Value: val,
					Data:  "0x",
				},
			},
			Network:      "base-sepolia",
			PaymasterUrl: nil, // Gas for base-sepolia is covered by CDP
		},
	)

	if err != nil {
		return "", err
	}

	if response.StatusCode() != 200 {
		return "", fmt.Errorf("failed to prepare and send user op: %v", response.Status())
	}

	return response.JSON200.UserOpHash, nil
}

// parseEther converts a string ETH amount to wei.
func parseEther(val string) (string, error) {
	f, ok := new(big.Float).SetString(val)
	if !ok {
		return "", fmt.Errorf("invalid number: %s", val)
	}

	scale := new(big.Float).SetInt(big.NewInt(params.Ether))
	f.Mul(f, scale)

	result := new(big.Int)
	f.Int(result)

	return result.String(), nil
}
What it does:
  • Creates an EOA (Externally Owned Account) as the smart account owner
  • Creates or retrieves an EIP-4337 smart account
  • Funds the smart account with testnet ETH
  • Prepares and sends a user operation (gasless transaction)
  • Returns the user operation hash and explorer link
Key Functions:
  • getOrCreateSmartAccount() - Creates smart account with specified owner
  • prepareAndSendUserOperation() - Constructs and sends user operation
  • parseEther() - Converts ETH amount to wei
Smart Account Benefits:
  • Gas sponsorship (gasless transactions on Base Sepolia)
  • Batch operations (multiple calls in one user operation)
  • Social recovery capabilities
  • Custom validation logic
Run: go run . send_user_operation

Main Entry Point

main.go
package main

import (
	"fmt"
	"os"
)

func main() {
	if len(os.Args) < 2 {
		printUsage()
		os.Exit(1)
	}

	exampleName := os.Args[1]

	switch exampleName {
	case "send_transaction":
		SendTransactionExample()
	case "send_user_operation":
		SendUserOperationExample()
	default:
		fmt.Printf("Unknown example: %s\n", exampleName)
		printUsage()
		os.Exit(1)
	}
}

func printUsage() {
	fmt.Println("Usage: go run . <example_name>")
	fmt.Println("Available examples:")
	fmt.Println("  send_transaction    - Create an EVM account and send a transaction")
	fmt.Println("  send_user_operation - Create a smart account and send a user operation")
}
What it does:
  • Provides a simple CLI interface for running examples
  • Routes to the appropriate example function based on command-line argument
  • Displays usage information if no argument is provided

Key Concepts

Client Initialization

The CDP Go client uses environment variables for configuration:
import (
    cdp "github.com/coinbase/cdp-sdk/go"
    "github.com/joho/godotenv"
)

godotenv.Load()

client, err := cdp.NewClient(cdp.ClientOptions{
    APIKeyID:     os.Getenv("CDP_API_KEY_ID"),
    APIKeySecret: os.Getenv("CDP_API_KEY_SECRET"),
    WalletSecret: os.Getenv("CDP_WALLET_SECRET"),
})

Idempotent Operations

Both examples use a get-or-create pattern for idempotency:
// Try to get existing account
response, err := cdp.GetEvmAccountByNameWithResponse(ctx, name)
if response.StatusCode() == 200 {
    return response.JSON200.Address, nil
}

// Create if not found
if response.StatusCode() == 404 {
    createResp, err := cdp.CreateEvmAccountWithResponse(ctx, nil, body)
    // ...
}

Error Handling

The examples demonstrate proper error handling patterns:
response, err := cdp.CreateEvmAccountWithResponse(ctx, nil, body)
if err != nil {
    return "", err
}

if response.StatusCode() != 201 {
    return "", fmt.Errorf("failed to create account: %v", response.Status())
}

Transaction Encoding

EVM transactions are RLP-encoded before signing:
import (
    "github.com/ethereum/go-ethereum/core/types"
    "encoding/hex"
)

// Create EIP-1559 transaction
transaction := types.DynamicFeeTx{
    ChainID:   big.NewInt(84532),
    To:        &toAddress,
    Value:     big.NewInt(10000000000000),
    Gas:       21000,
    GasFeeCap: big.NewInt(1000000000),
    GasTipCap: big.NewInt(100000000),
}

// Serialize to RLP
rlpTx := types.NewTx(&transaction)
rlpData, _ := rlpTx.MarshalBinary()
rlpHex := "0x" + hex.EncodeToString(rlpData)

// Sign with CDP
response, err := cdp.SignEvmTransactionWithResponse(ctx, address, nil, 
    openapi.SignEvmTransactionJSONRequestBody{
        Transaction: rlpHex,
    },
)

Integration with go-ethereum

The examples use the popular go-ethereum library for blockchain operations:
import (
    "github.com/ethereum/go-ethereum/ethclient"
    "github.com/ethereum/go-ethereum/common"
)

// Connect to network
eth Client, _ := ethclient.Dial("https://sepolia.base.org")

// Send signed transaction
tx_hash := ethClient.SendTransaction(ctx, &tx)

// Wait for receipt
receipt, _ := ethClient.TransactionReceipt(ctx, tx.Hash())

Testing Networks

The examples use Base Sepolia testnet:
On Base Sepolia, gas for smart account user operations is automatically sponsored by CDP.

Project Structure

examples/go/
├── main.go                  # CLI entry point
├── send_transaction.go     # EOA transaction example
├── send_user_operation.go  # Smart account example
├── go.mod                  # Go module dependencies
├── go.sum                  # Dependency checksums
├── .env.example            # Environment template
└── README.md               # Documentation

Dependencies

The examples use these key dependencies:
require (
    github.com/coinbase/cdp-sdk/go v0.x.x
    github.com/ethereum/go-ethereum v1.x.x
    github.com/joho/godotenv v1.x.x
)
Install with:
go mod download

Full Example List

View all examples in the repository:

Browse Go Examples

View all Go examples on GitHub

Next Steps

Go SDK Reference

Explore the full Go SDK API

TypeScript Examples

See TypeScript examples

Python Examples

View Python examples

Rust Examples

Check Rust examples

Additional Resources

go-ethereum Docs

Learn about the go-ethereum library

EIP-4337

Read about Account Abstraction

Base Sepolia

Base Sepolia testnet documentation

CDP Portal

Get your API credentials

Build docs developers (and LLMs) love