The CDP SDK provides flexible methods for sending transactions on both EVM and Solana networks. You can send pre-built transactions, construct custom transactions, or use high-level convenience methods.
EVM Transactions
Sending with EIP-1559 Parameters
Send a transaction with automatic gas estimation:
import { CdpClient } from "@coinbase/cdp-sdk";
const cdp = new CdpClient();
const account = await cdp.evm.getOrCreateAccount({ name: "Sender" });
const txHash = await account.sendTransaction({
transaction: {
to: "0x1234567890123456789012345678901234567890",
value: 1000000000000000n, // 0.001 ETH
data: "0x" // Empty for simple ETH transfer
},
network: "base-sepolia"
});
console.log(`Transaction hash: ${txHash}`);
from cdp import CdpClient
from cdp.evm_transaction_types import TransactionRequestEIP1559
async with CdpClient() as cdp:
account = await cdp.evm.get_or_create_account(name="Sender")
tx_hash = await account.send_transaction(
transaction=TransactionRequestEIP1559(
to="0x1234567890123456789012345678901234567890",
value=1000000000000000, # 0.001 ETH
data="0x" # Empty for simple ETH transfer
),
network="base-sepolia"
)
print(f"Transaction hash: {tx_hash}")
When you omit gas, maxFeePerGas, and maxPriorityFeePerGas, CDP automatically estimates these values based on current network conditions.
Contract Interactions
Call a smart contract function:
import { encodeFunctionData, erc20Abi } from "viem";
// Encode an ERC-20 transfer
const data = encodeFunctionData({
abi: erc20Abi,
functionName: "transfer",
args: [
"0xRecipientAddress...",
1000000n // 1 USDC (6 decimals)
]
});
const txHash = await account.sendTransaction({
transaction: {
to: "0xUSDCContractAddress...",
value: 0n,
data: data
},
network: "base-sepolia"
});
from web3 import Web3
# Create Web3 instance to encode function calls
w3 = Web3()
# Load ERC-20 ABI (simplified)
erc20_abi = [...]
contract = w3.eth.contract(abi=erc20_abi)
# Encode transfer function
data = contract.encode_abi(
fn_name="transfer",
args=[
"0xRecipientAddress...",
1000000 # 1 USDC (6 decimals)
]
)
tx_hash = await account.send_transaction(
transaction=TransactionRequestEIP1559(
to="0xUSDCContractAddress...",
value=0,
data=data
),
network="base-sepolia"
)
Manual Gas Parameters
Specify gas parameters manually for more control:
const txHash = await account.sendTransaction({
transaction: {
to: "0x1234...",
value: 1000000000000000n,
data: "0x",
gas: 21000n,
maxFeePerGas: 2000000000n, // 2 gwei
maxPriorityFeePerGas: 1000000000n // 1 gwei
},
network: "base-sepolia"
});
tx_hash = await account.send_transaction(
transaction=TransactionRequestEIP1559(
to="0x1234...",
value=1000000000000000,
data="0x",
gas=21000,
max_fee_per_gas=2000000000, # 2 gwei
max_priority_fee_per_gas=1000000000 # 1 gwei
),
network="base-sepolia"
)
Sending RLP-Encoded Transactions
Send a pre-signed, RLP-encoded transaction:
const txHash = await account.sendTransaction({
transaction: "0x02f8...", // RLP-encoded transaction hex
network: "base-sepolia"
});
tx_hash = await account.send_transaction(
transaction="0x02f8...", # RLP-encoded transaction hex
network="base-sepolia"
)
Idempotent Transactions
Use idempotency keys to safely retry failed requests:
const txHash = await account.sendTransaction({
transaction: {
to: "0x1234...",
value: 1000000000000000n,
data: "0x"
},
network: "base-sepolia",
idempotencyKey: "tx-unique-id-123"
});
tx_hash = await account.send_transaction(
transaction=TransactionRequestEIP1559(
to="0x1234...",
value=1000000000000000,
data="0x"
),
network="base-sepolia",
idempotency_key="tx-unique-id-123"
)
Solana Transactions
Signing and Sending Transactions
import { CdpClient } from "@coinbase/cdp-sdk";
const cdp = new CdpClient();
const account = await cdp.solana.getOrCreateAccount({
name: "SolanaSender"
});
// Sign a base64-encoded Solana transaction
const signedTx = await account.signTransaction({
transaction: "base64-encoded-transaction",
idempotencyKey: "tx-sol-123"
});
console.log(`Signed transaction: ${signedTx.signedTransaction}`);
from cdp import CdpClient
async with CdpClient() as cdp:
account = await cdp.solana.get_or_create_account(
name="SolanaSender"
)
# Sign a base64-encoded Solana transaction
signed_tx = await account.sign_transaction(
transaction="base64-encoded-transaction",
idempotency_key="tx-sol-123"
)
print(f"Signed transaction: {signed_tx.signed_transaction}")
Building Solana Transactions
Build a Solana transaction using Solana libraries:
import {
Connection,
Transaction,
SystemProgram,
PublicKey
} from "@solana/web3.js";
const connection = new Connection("https://api.devnet.solana.com");
// Create a transfer transaction
const tx = new Transaction().add(
SystemProgram.transfer({
fromPubkey: new PublicKey(account.address),
toPubkey: new PublicKey("DestinationAddress..."),
lamports: 1000000 // 0.001 SOL
})
);
// Get recent blockhash
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
tx.feePayer = new PublicKey(account.address);
// Serialize and encode
const serialized = tx.serialize({ requireAllSignatures: false });
const base64Tx = serialized.toString("base64");
// Sign with CDP
const signed = await account.signTransaction({
transaction: base64Tx
});
from solders.transaction import Transaction
from solders.system_program import transfer, TransferParams
from solders.pubkey import Pubkey
from solders.message import Message
from solana.rpc.async_api import AsyncClient
import base64
client = AsyncClient("https://api.devnet.solana.com")
# Create transfer instruction
transfer_ix = transfer(
TransferParams(
from_pubkey=Pubkey.from_string(account.address),
to_pubkey=Pubkey.from_string("DestinationAddress..."),
lamports=1000000 # 0.001 SOL
)
)
# Get recent blockhash
blockhash_resp = await client.get_latest_blockhash()
recent_blockhash = blockhash_resp.value.blockhash
# Build transaction
message = Message.new_with_blockhash(
[transfer_ix],
Pubkey.from_string(account.address),
recent_blockhash
)
tx = Transaction.new_unsigned(message)
# Serialize and encode
serialized = bytes(tx)
base64_tx = base64.b64encode(serialized).decode("utf-8")
# Sign with CDP
signed = await account.sign_transaction(transaction=base64_tx)
Batch Transactions (Smart Accounts Only)
Smart accounts can execute multiple operations atomically:
import { encodeFunctionData, erc20Abi } from "viem";
const owner = await cdp.evm.createAccount();
const smartAccount = await cdp.evm.createSmartAccount({ owner });
const userOp = await smartAccount.sendUserOperation({
calls: [
{
to: "0xTokenA...",
value: 0n,
data: encodeFunctionData({
abi: erc20Abi,
functionName: "approve",
args: ["0xSpender...", 1000000n]
})
},
{
to: "0xTokenB...",
value: 0n,
data: encodeFunctionData({
abi: erc20Abi,
functionName: "approve",
args: ["0xSpender...", 2000000n]
})
}
],
network: "base-sepolia"
});
console.log(`Batch user operation: ${userOp.userOpHash}`);
from cdp.evm_call_types import EncodedCall
owner = await cdp.evm.create_account()
smart_account = await cdp.evm.create_smart_account(owner=owner)
user_op = await smart_account.send_user_operation(
calls=[
EncodedCall(
to="0xTokenA...",
value=0,
data="0x...", # Encoded approve call
),
EncodedCall(
to="0xTokenB...",
value=0,
data="0x...", # Encoded approve call
)
],
network="base-sepolia"
)
print(f"Batch user operation: {user_op.user_op_hash}")
Waiting for Transaction Confirmation
EVM Transactions
Use a library like Viem or Web3.py to wait for confirmation:
import { createPublicClient, http } from "viem";
import { baseSepolia } from "viem/chains";
const publicClient = createPublicClient({
chain: baseSepolia,
transport: http()
});
const txHash = await account.sendTransaction({
transaction: {
to: "0x1234...",
value: 1000000000000000n,
data: "0x"
},
network: "base-sepolia"
});
console.log("Waiting for confirmation...");
const receipt = await publicClient.waitForTransactionReceipt({
hash: txHash
});
console.log(`Status: ${receipt.status}`);
console.log(`Block: ${receipt.blockNumber}`);
console.log(`Gas used: ${receipt.gasUsed}`);
from web3 import Web3
w3 = Web3(Web3.HTTPProvider("https://sepolia.base.org"))
tx_hash = await account.send_transaction(
transaction=TransactionRequestEIP1559(
to="0x1234...",
value=1000000000000000,
data="0x"
),
network="base-sepolia"
)
print("Waiting for confirmation...")
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print(f"Status: {receipt['status']}")
print(f"Block: {receipt['blockNumber']}")
print(f"Gas used: {receipt['gasUsed']}")
Smart Account User Operations
const userOp = await smartAccount.sendUserOperation({
calls: [{ to: "0x1234...", value: 0n, data: "0x" }],
network: "base-sepolia"
});
const confirmed = await smartAccount.waitForUserOperation(
userOp.userOpHash,
{ timeoutSeconds: 30 }
);
console.log(`User op status: ${confirmed.status}`);
console.log(`Transaction hash: ${confirmed.transactionHash}`);
user_op = await smart_account.send_user_operation(
calls=[EncodedCall(to="0x1234...", value=0, data="0x")],
network="base-sepolia"
)
confirmed = await smart_account.wait_for_user_operation(
user_op_hash=user_op.user_op_hash,
timeout_seconds=30
)
print(f"User op status: {confirmed.status}")
print(f"Transaction hash: {confirmed.transaction_hash}")
Transaction Fields Reference
EIP-1559 Transaction Fields
Destination address (20-byte hex string with 0x prefix)
value
bigint | number
default:"0"
Amount of ETH to send in wei
Contract call data (hex string with 0x prefix)
Gas limit (estimated by CDP if not provided)
Maximum fee per gas unit (estimated by CDP if not provided)
Maximum priority fee per gas unit (estimated by CDP if not provided)
Transaction nonce (managed by CDP if not provided)
Troubleshooting
Transaction Reverted
If a transaction reverts:
- Check the revert reason in the transaction receipt
- Verify contract function parameters are correct
- Ensure the account has sufficient token balances
- Confirm gas limits are adequate for complex operations
Insufficient Funds for Gas
Ensure the account has ETH/SOL for transaction fees:
// Request testnet tokens
await cdp.evm.requestFaucet({
address: account.address,
network: "base-sepolia",
token: "eth"
});
Nonce Too Low/High
Let CDP manage nonces automatically by omitting the nonce field. If you manage nonces manually, track them carefully.
Transaction Stuck
For EVM transactions stuck in the mempool:
- Increase
maxFeePerGas and maxPriorityFeePerGas
- Send a replacement transaction with the same nonce but higher gas price
- Wait for network congestion to clear
Next Steps