Metadata-Version: 2.4
Name: cdp-sdk
Version: 1.45.0
Summary: CDP SDK
License: MIT
Requires-Python: >=3.10
Requires-Dist: aiohttp-retry>=2.9.1
Requires-Dist: aiohttp>=3.11.16
Requires-Dist: base58>=2.1.1
Requires-Dist: cryptography>=42.0.0
Requires-Dist: pydantic>=2.10.3
Requires-Dist: pyjwt>=2.10.1
Requires-Dist: python-dateutil>=2.9.0
Requires-Dist: solana>=0.36.6
Requires-Dist: solders>=0.26.0
Requires-Dist: urllib3>=2.2.3
Requires-Dist: web3>=7.6.0
Provides-Extra: dev
Requires-Dist: myst-parser>=4.0.1; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.26.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: python-dotenv>=1.0.0; extra == 'dev'
Requires-Dist: pyyaml>=6.0; extra == 'dev'
Requires-Dist: ruff<0.8,>=0.7.1; extra == 'dev'
Requires-Dist: sphinx-autobuild>=2024.10.3; extra == 'dev'
Requires-Dist: sphinx-autodoc-typehints>=3.0.1; extra == 'dev'
Requires-Dist: sphinx>=8.1.3; extra == 'dev'
Requires-Dist: sphinxcontrib-napoleon>=0.7; extra == 'dev'
Requires-Dist: towncrier<25,>=24.8.0; extra == 'dev'
Description-Content-Type: text/markdown

# Coinbase Developer Platform (CDP) Python SDK

## Table of Contents

- [CDP SDK](#cdp-sdk)
- [Documentation](#documentation)
- [Installation](#installation)
- [API Keys](#api-keys)
- [Usage](#usage)
  - [Initialization](#initialization)
  - [Creating Accounts](#creating-evm-or-solana-accounts)
  - [Exporting Accounts](#exporting-evm-or-solana-accounts)
  - [Creating Accounts with Policies](#creating-evm-or-solana-accounts-with-policies)
  - [Updating Accounts](#updating-evm-or-solana-accounts)
  - [Testnet Faucet](#testnet-faucet)
  - [Sending Transactions](#sending-transactions)
  - [Transferring Tokens](#transferring-tokens)
  - [EVM Swaps](#evm-swaps)
  - [EIP-7702 delegation](#eip-7702-delegation)
  - [EVM Smart Accounts](#evm-smart-accounts)
- [Account Actions](#account-actions)
- [Policy Management](#policy-management)
  - [End User Policies](#end-user-policies)
- [End-user Management](#end-user-management)
  - [Create End User](#create-end-user)
  - [Import End User](#import-end-user)
  - [Add EVM Account to End User](#add-evm-account-to-end-user)
  - [Add EVM Smart Account to End User](#add-evm-smart-account-to-end-user)
  - [Add Solana Account to End User](#add-solana-account-to-end-user)
  - [Validate Access Token](#validate-access-token)
  - [Delegated End User Operations](#delegated-end-user-operations)
- [Webhooks](#webhooks)
  - [Create Subscription](#create-subscription)
- [Authentication Tools](#authentication-tools)
- [Error Reporting](#error-reporting)
- [Usage Tracking](#usage-tracking)
- [License](#license)
- [Support](#support)
- [Security](#security)

> [!TIP]
> If you're looking to contribute to the SDK, please see the [Contributing Guide](https://github.com/coinbase/cdp-sdk/blob/main/python/CONTRIBUTING.md).

## CDP SDK

This module contains the Python CDP SDK, which is a library that provides a client for interacting with the [Coinbase Developer Platform (CDP)](https://docs.cdp.coinbase.com/). It includes a CDP Client for interacting with EVM and Solana APIs to create accounts and send transactions, policy APIs to govern transaction permissions, as well as authentication tools for interacting directly with the CDP APIs.

## Documentation

CDP SDK has [auto-generated docs for the Python SDK](https://coinbase.github.io/cdp-sdk/python).

Further documentation is also available on the CDP docs website:

- [Wallet API v2](https://docs.cdp.coinbase.com/wallet-api-v2/docs/welcome)
- [API Reference](https://docs.cdp.coinbase.com/api-v2/docs/welcome)

## Installation

```bash
pip install cdp-sdk
```

## API Keys

To start, [create a CDP API Key](https://portal.cdp.coinbase.com/access/api). 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

#### Load client config from shell

One option is to export your CDP API Key and Wallet Secret as environment variables:

```bash
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"
```

Then, initialize the client:

```python
from cdp import CdpClient
import asyncio

async def main():
    async with CdpClient() as cdp:
        pass

asyncio.run(main())
```

#### Load client config from `.env` file

Another option is to save your CDP API Key and Wallet Secret in a `.env` file:

```bash
touch .env
echo "CDP_API_KEY_ID=YOUR_API_KEY_ID" >> .env
echo "CDP_API_KEY_SECRET=YOUR_API_KEY_SECRET" >> .env
echo "CDP_WALLET_SECRET=YOUR_WALLET_SECRET" >> .env
```

Then, load the client config from the `.env` file:

```python
from cdp import CdpClient
from dotenv import load_dotenv
import asyncio

load_dotenv()

async def main():
    async with CdpClient() as cdp:
        pass

asyncio.run(main())
```

#### Pass the API Key and Wallet Secret to the client

Another option is to directly pass the API Key and Wallet Secret to the client:

```python
import asyncio
from cdp import CdpClient

async def main():
    async with CdpClient(
        api_key_id="YOUR_API_KEY_ID",
        api_key_secret="YOUR_API_KEY_SECRET",
        wallet_secret="YOUR_WALLET_SECRET",
    ) as cdp:
        pass

asyncio.run(main())
```

### Client Lifecycle

The CDP client wraps an HTTP client (aiohttp) and should be created once and reused throughout your application's lifecycle. The underlying HTTP client handles connection pooling automatically, so there's no need to recreate the client per request—doing so would be less efficient.

- **Long-lived services**: Create a single client instance at startup using `async with CdpClient() as cdp:`
- **Serverless/request-based runtimes**: Create once per cold start, or use a module-level singleton
- **Concurrency**: The client is safe to use across concurrent async operations

### Creating EVM or Solana accounts

#### Create an EVM account as follows:

```python
import asyncio
from cdp import CdpClient

async def main():
    async with CdpClient() as cdp:
        account = await cdp.evm.create_account()

asyncio.run(main())
```

#### Import an EVM account as follows:

```python
import asyncio
from cdp import CdpClient

async def main():
    async with CdpClient() as cdp:
        account = await cdp.evm.import_account(
            private_key="0x12345",
            name="MyAccount",
        )

asyncio.run(main())
```

#### Create a Solana account as follows:

```python
import asyncio
from cdp import CdpClient

async def main():
    async with CdpClient() as cdp:
        account = await cdp.solana.create_account()

asyncio.run(main())
```

#### Import a Solana account as follows:
```python
import asyncio
from cdp import CdpClient

async def main():
    async with CdpClient() as cdp:
        account = await cdp.solana.import_account(
            private_key="3MLZ...Uko8zz",
            name="MyAccount",
        )

asyncio.run(main())
```

### Exporting EVM or Solana accounts

#### Export an EVM account as follows:

```python
# by name
private_key = await cdp.evm.export_account(
  name="MyAccount",
)

# by address
private_key = await cdp.evm.export_account(
  address="0x123",
)
```

#### Export a Solana account as follows:

```python
# by name
private_key = await cdp.solana.export_account(
  name="MyAccount",
)

# by address
private_key = await cdp.solana.export_account(
  address="Abc",
)
```

#### Get or create an EVM account as follows:

```python
import asyncio
from cdp import CdpClient

async def main():
    async with CdpClient() as cdp:
        account = await cdp.evm.get_or_create_account()

asyncio.run(main())
```

#### Get or create a Solana account as follows:

```python
import asyncio
from cdp import CdpClient

async def main():
    async with CdpClient() as cdp:
        account = await cdp.solana.get_or_create_account()

asyncio.run(main())
```

#### Get or create a Smart account as follows:

```python
import asyncio
from cdp import CdpClient

async def main():
    async with CdpClient() as cdp:
        owner = await cdp.evm.create_account()
        account = await cdp.evm.get_or_create_smart_account(name="Account1", owner=owner)

asyncio.run(main())
```

### Creating EVM or Solana accounts with policies

#### Create an EVM account with policy as follows:
```python
account = await cdp.evm.create_account(
    name="AccountWithPolicy",
    account_policy="abcdef12-3456-7890-1234-567890123456",
)
```

#### Create a Solana account with policy as follows:
```python
account = await cdp.solana.create_account(
    name="AccountWithPolicy",
    account_policy="abcdef12-3456-7890-1234-567890123456",
)
```

### Updating EVM or Solana accounts

#### Update an EVM account as follows:

```python
account = await cdp.evm.update_account(
  address=account.address,
  update=UpdateAccountOptions(
    name="Updated name",
    account_policy="1622d4b7-9d60-44a2-9a6a-e9bbb167e412",
  ),
)
```

#### Update a Solana account as follows:

```python
account = await cdp.solana.update_account(
  address=account.address,
  update=UpdateAccountOptions(
    name="Updated name",
    account_policy="1622d4b7-9d60-44a2-9a6a-e9bbb167e412",
  ),
)
```

### Testnet faucet

You can use the faucet function to request testnet ETH or SOL from the CDP.

#### Request testnet ETH as follows:

```python
import asyncio
from cdp import CdpClient

async def main():
    async with CdpClient() as cdp:
        await cdp.evm.request_faucet(
            address=evm_account.address, network="base-sepolia", token="eth"
        )

asyncio.run(main())
```

#### Request testnet SOL as follows:

```python
import asyncio
from cdp import CdpClient

async def main():
    async with CdpClient() as cdp:
        await cdp.solana.request_faucet(
            address=address, token="sol"
        )

asyncio.run(main())
```

### Sending transactions

#### EVM

You can use CDP SDK to send transactions on EVM networks. By default, Coinbase will manage the nonce and gas for you.

```python
import asyncio

from dotenv import load_dotenv
from web3 import Web3

from cdp import CdpClient
from cdp.evm_transaction_types import TransactionRequestEIP1559

w3 = Web3(Web3.HTTPProvider("https://sepolia.base.org"))


async def main():
    load_dotenv()

    async with CdpClient() as cdp:
        evm_account = await cdp.evm.create_account()

        faucet_hash = await cdp.evm.request_faucet(
            address=evm_account.address, network="base-sepolia", token="eth"
        )

        w3.eth.wait_for_transaction_receipt(faucet_hash)

        zero_address = "0x0000000000000000000000000000000000000000"

        amount_to_send = w3.to_wei(0.000001, "ether")

        tx_hash = await cdp.evm.send_transaction(
            address=evm_account.address,
            transaction=TransactionRequestEIP1559(
                to=zero_address,
                value=amount_to_send,
            ),
            network="base-sepolia",
        )

        print(f"Transaction sent! Hash: {tx_hash}")

        print("Waiting for transaction confirmation...")
        tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
        print(f"Transaction confirmed in block {tx_receipt.blockNumber}")
        print(f"Transaction status: {'Success' if tx_receipt.status == 1 else 'Failed'}")


asyncio.run(main())
```

If you'd like to manage the nonce and gas yourself, you can do so as follows:

```python
import asyncio

from dotenv import load_dotenv
from web3 import Web3

from cdp import CdpClient
from cdp.evm_transaction_types import TransactionRequestEIP1559

w3 = Web3(Web3.HTTPProvider("https://sepolia.base.org"))


async def main():
    load_dotenv()

    async with CdpClient() as cdp:
        evm_account = await cdp.evm.create_account()

        faucet_hash = await cdp.evm.request_faucet(
            address=evm_account.address, network="base-sepolia", token="eth"
        )

        w3.eth.wait_for_transaction_receipt(faucet_hash)

        zero_address = "0x0000000000000000000000000000000000000000"

        amount_to_send = w3.to_wei(0.000001, "ether")

        nonce = w3.eth.get_transaction_count(evm_account.address)

        gas_estimate = w3.eth.estimate_gas(
            {"to": zero_address, "from": evm_account.address, "value": amount_to_send}
        )

        max_priority_fee = w3.eth.max_priority_fee
        max_fee = w3.eth.gas_price + max_priority_fee

        tx_hash = await cdp.evm.send_transaction(
            address=evm_account.address,
            transaction=TransactionRequestEIP1559(
                to=zero_address,
                value=amount_to_send,
                gas=gas_estimate,
                maxFeePerGas=max_fee,
                maxPriorityFeePerGas=max_priority_fee,
                nonce=nonce,
            ),
            network="base-sepolia",
        )

        print(f"Transaction sent! Hash: {tx_hash}")

        print("Waiting for transaction confirmation...")
        tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
        print(f"Transaction confirmed in block {tx_receipt.blockNumber}")
        print(f"Transaction status: {'Success' if tx_receipt.status == 1 else 'Failed'}")


asyncio.run(main())
```

You can also use `DynamicFeeTransaction` from `eth-account`:

```python
import asyncio

from dotenv import load_dotenv
from web3 import Web3

from cdp import CdpClient
from eth_account.typed_transactions import DynamicFeeTransaction

w3 = Web3(Web3.HTTPProvider("https://sepolia.base.org"))


async def main():
    load_dotenv()

    async with CdpClient() as cdp:
        evm_account = await cdp.evm.create_account()

        faucet_hash = await cdp.evm.request_faucet(
            address=evm_account.address, network="base-sepolia", token="eth"
        )

        w3.eth.wait_for_transaction_receipt(faucet_hash)

        zero_address = "0x0000000000000000000000000000000000000000"

        amount_to_send = w3.to_wei(0.000001, "ether")

        nonce = w3.eth.get_transaction_count(evm_account.address)

        gas_estimate = w3.eth.estimate_gas(
            {"to": zero_address, "from": evm_account.address, "value": amount_to_send}
        )

        max_priority_fee = w3.eth.max_priority_fee
        max_fee = w3.eth.gas_price + max_priority_fee

        tx_hash = await cdp.evm.send_transaction(
            address=evm_account.address,
            transaction=DynamicFeeTransaction.from_dict(
                {
                "to": zero_address,
                "value": amount_to_send,
                "chainId": 84532,
                "gas": gas_estimate,
                "maxFeePerGas": max_fee,
                "maxPriorityFeePerGas": max_priority_fee,
                "nonce": nonce,
                "type": "0x2",
                }
            ),
            network="base-sepolia",
        )

        print(f"Transaction sent! Hash: {tx_hash}")

        print("Waiting for transaction confirmation...")
        tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
        print(f"Transaction confirmed in block {tx_receipt.blockNumber}")
        print(f"Transaction status: {'Success' if tx_receipt.status == 1 else 'Failed'}")


asyncio.run(main())
```

### EvmServerAccount vs EvmLocalAccount

CDP SDK provides two account types for different use cases:

- **`EvmServerAccount`** - Async-first API for CDP server-managed accounts. All signing operations are asynchronous and involve network calls to the CDP API. This is the recommended type for most CDP SDK usage.

- **`EvmLocalAccount`** - Synchronous wrapper around `EvmServerAccount` that provides compatibility with `eth-account`'s `BaseAccount` interface. Use this when you need to integrate with libraries that expect synchronous `eth-account` LocalAccount objects (e.g., creating smart accounts with a server account as owner).

**When to use `EvmLocalAccount`:**
- Creating smart accounts where the owner must be a `BaseAccount`
- Integrating with `web3.py` or other libraries expecting `eth-account` LocalAccount
- Any synchronous context where you cannot use async/await

With `EvmLocalAccount`, you may sign a hash, message, typed data, or a transaction synchronously.

```python
import asyncio

from cdp import CdpClient
from dotenv import load_dotenv
from cdp.evm_local_account import EvmLocalAccount
from web3 import Web3

load_dotenv()


async def main():
    async with CdpClient() as cdp:
        account = await cdp.evm.get_or_create_account(name="MyServerAccount")
        evm_local_account = EvmLocalAccount(account)

        # Sign a transaction.
        w3 = Web3(Web3.HTTPProvider("https://sepolia.base.org"))
        nonce = w3.eth.get_transaction_count(evm_local_account.address)
        transaction = evm_local_account.sign_transaction(
            transaction_dict={
                "to": "0x000000000000000000000000000000000000dEaD",
                "value": 10000000000,
                "chainId": 84532,
                "gas": 21000,
                "maxFeePerGas": 1000000000,
                "maxPriorityFeePerGas": 1000000000,
                "nonce": nonce,
                "type": "0x2",
            }
        )

        faucet_hash = await cdp.evm.request_faucet(
            address=evm_local_account.address, network="base-sepolia", token="eth"
        )
        w3.eth.wait_for_transaction_receipt(faucet_hash)

        # Use Web3 to send the transaction.
        tx_hash = w3.eth.send_raw_transaction(transaction.raw_transaction)
        tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
        print(f"Transaction receipt: {tx_receipt}")


asyncio.run(main())
```

#### Solana

You can use CDP SDK to send transactions on Solana.

For complete examples, check out [send_transaction.py](https://github.com/coinbase/cdp-sdk/blob/main/examples/python/solana/send_transaction.py), [send_batch_transaction.py](https://github.com/coinbase/cdp-sdk/blob/main/examples/python/solana/send_batch_transaction.py), and [send_many_batch_transactions.py](https://github.com/coinbase/cdp-sdk/blob/main/examples/python/solana/send_many_batch_transactions.py).

```python
import asyncio
import base64
from cdp import CdpClient
from solana.rpc.api import Client as SolanaClient
from solders.message import Message
from solders.pubkey import Pubkey as PublicKey
from solders.system_program import TransferParams, transfer
from dotenv import load_dotenv

load_dotenv()

async def main():
    async with CdpClient() as cdp:
        sender = await cdp.solana.create_account()
        await cdp.solana.request_faucet(address=sender.address, token="sol")

        connection = SolanaClient("https://api.devnet.solana.com")

        source_pubkey = PublicKey.from_string(sender.address)
        dest_pubkey = PublicKey.from_string("3KzDtddx4i53FBkvCzuDmRbaMozTZoJBb1TToWhz3JfE")
        blockhash = connection.get_latest_blockhash().value.blockhash

        transfer_instruction = transfer(TransferParams(
            from_pubkey=source_pubkey,
            to_pubkey=dest_pubkey,
            lamports=1000
        ))

        message = Message.new_with_blockhash([transfer_instruction], source_pubkey, blockhash)
        tx_bytes = bytes([1]) + bytes([0] * 64) + bytes(message)
        serialized_tx = base64.b64encode(tx_bytes).decode("utf-8")

        response = await cdp.solana.send_transaction(
            network="solana-devnet",
            transaction=serialized_tx
        )

        print(f"Transaction sent: {response.transaction_signature}")
        print(f"Explorer: https://explorer.solana.com/tx/{response.transaction_signature}?cluster=devnet")

if __name__ == "__main__":
    asyncio.run(main())
```

To have CDP sponsor the transaction fees, pass `use_cdp_sponsor=True`. When enabled, CDP pays the network fee so the sender does not need SOL for fees.

```python
response = await cdp.solana.send_transaction(
    network="solana-devnet",
    transaction=serialized_tx,
    use_cdp_sponsor=True,
)
```

### Transferring tokens

#### EVM

For complete examples, check out [account.transfer.py](https://github.com/coinbase/cdp-sdk/blob/main/examples/python/evm/account.transfer.py) and [smart_account.transfer.py](https://github.com/coinbase/cdp-sdk/blob/main/examples/python/evm/smart_account.transfer.py).

You can transfer tokens between accounts using the `transfer` function:

```python
sender = await cdp.evm.create_account(name="Sender")

tx_hash = await sender.transfer(
    to="0x9F663335Cd6Ad02a37B633602E98866CF944124d",
    amount=w3.to_wei("0.001", "ether"),
    token="eth",
    network="base-sepolia",
)

w3.eth.wait_for_transaction_receipt(tx_hash)
```

To send USDC, the SDK exports a helper function to convert a whole number to a bigint:

```python
from cdp import parse_units

# returns atomic representation of 0.01 USDC, which uses 6 decimal places
amount = parse_units("0.01", 6)

tx_hash = await sender.transfer(
    to="0x9F663335Cd6Ad02a37B633602E98866CF944124d",
    amount=amount,
    token="usdc",
    network="base-sepolia",
)
```

Smart Accounts also have a `transfer` function:

```python
from cdp import parse_units

sender = await cdp.evm.create_smart_account(
    owner=privateKeyToAccount(generatePrivateKey()),
);
print("Created smart account", sender);

transfer_result = await sender.transfer(
    to="0x9F663335Cd6Ad02a37B633602E98866CF944124d",
    amount=parse_units("0.01", 6),
    token="usdc",
    network="base-sepolia",
)

user_op_result = await sender.wait_for_user_operation(user_op_hash=transfer_result.user_op_hash)
```

Using Smart Accounts, you can also specify a paymaster URL:

```python
transfer_result = await sender.transfer(
    to="0x9F663335Cd6Ad02a37B633602E98866CF944124d",
    amount="0.01",
    token="usdc",
    network="base-sepolia",
    paymaster_url="https://some-paymaster-url.com",
)
```

You can pass `usdc` or `eth` as the token to transfer, or you can pass a contract address directly:

```python
transfer_result = await sender.transfer(
    to="0x9F663335Cd6Ad02a37B633602E98866CF944124d",
    amount=w3.to_wei("0.000001", "ether"),
    token="0x4200000000000000000000000000000000000006", # WETH on Base Sepolia
    network="base-sepolia",
)
```

You can pass another account as the `to` parameter:

```python
from cdp import parse_units

sender = await cdp.evm.create_account(name="Sender")
receiver = await cdp.evm.create_account(name="Receiver")

transfer_result = await sender.transfer(
    to=receiver,
    amount=parse_units("0.01", 6),
    token="usdc",
    network="base-sepolia",
)
```

#### Solana

For complete examples, check out [solana/account.transfer.py](https://github.com/coinbase/cdp-sdk/blob/main/examples/python/solana/account.transfer.py).

You can transfer tokens between accounts using the `transfer` function, and wait for the transaction to be confirmed using the `confirmTransaction` function from `solana`:

```python
import asyncio
from cdp import CdpClient
from solana.rpc.api import Client as SolanaClient

async def main():
    async with CdpClient() as cdp:
        sender = await cdp.solana.create_account()

        connection = SolanaClient("https://api.devnet.solana.com")

        signature = await sender.transfer({
            to="3KzDtddx4i53FBkvCzuDmRbaMozTZoJBb1TToWhz3JfE",
            amount=0.01 * LAMPORTS_PER_SOL,
            token="sol",
            network=connection,
        });

        blockhash, lastValidBlockHeight = await connection.get_latest_blockhash()

        confirmation = await connection.confirm_transaction(
            {
                signature,
                blockhash,
                lastValidBlockHeight,
            },
        )

        if confirmation.value.err:
            print(f"Something went wrong! Error: {confirmation.value.err.toString()}")
        else:
            print(
                f"Transaction confirmed: Link: https://explorer.solana.com/tx/${signature}?cluster=devnet",
            )
```

To send USDC, the SDK exports a helper function to convert a whole number to a bigint:

```python
from cdp import parse_units

# returns atomic representation of 0.01 USDC, which uses 6 decimal places
amount = parse_units("0.01", 6)

tx_hash = await sender.transfer(
    to="0x9F663335Cd6Ad02a37B633602E98866CF944124d",
    amount=amount,
    token="usdc",
    network="devet",
)
```

### EVM Swaps

You can use the CDP SDK to swap tokens on EVM networks using both regular accounts (EOAs) and smart accounts.

The SDK provides three approaches for performing token swaps:

#### 1. All-in-one pattern (Recommended)

The simplest approach for performing swaps. Creates and executes the swap in a single line of code:

**Regular Account (EOA):**
```python
from cdp import CdpClient
from cdp.actions.evm.swap import AccountSwapOptions

async with CdpClient() as cdp:
    # Retrieve an existing EVM account with funds already in it
    account = await cdp.evm.get_or_create_account(name="MyExistingFundedAccount")

    # Execute a swap directly on an EVM account in one line
    result = await account.swap(
        AccountSwapOptions(
            network="base",
            from_token="0x4200000000000000000000000000000000000006",  # WETH on Base
            to_token="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",  # USDC on Base
            from_amount="1000000000000000000",  # 1 WETH (18 decimals)
            slippage_bps=100  # 1% slippage tolerance
        )
    )
    print(f"Transaction hash: {result.transaction_hash}")
```

**Smart Account:**
```python
from cdp import CdpClient
from cdp.actions.evm.swap import SmartAccountSwapOptions

async with CdpClient() as cdp:
    # Create or retrieve a smart account with funds already in it
    owner = await cdp.evm.get_or_create_account(name="MyOwnerAccount")
    smart_account = await cdp.evm.get_or_create_smart_account(name="MyExistingFundedSmartAccount", owner=owner)

    # Execute a swap directly on a smart account in one line
    result = await smart_account.swap(
        SmartAccountSwapOptions(
            network="base",
            from_token="0x4200000000000000000000000000000000000006",  # WETH on Base
            to_token="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",  # USDC on Base
            from_amount="1000000000000000000",  # 1 WETH (18 decimals)
            slippage_bps=100  # 1% slippage tolerance
            # Optional: paymaster_url="https://paymaster.example.com"  # For gas sponsorship
        )
    )
    print(f"User operation hash: {result.user_op_hash}")

    # Wait for the user operation to complete
    receipt = await smart_account.wait_for_user_operation(user_op_hash=result.user_op_hash)
    print(f"Status: {receipt.status}")
```

#### 2. Get pricing information

Use `get_swap_price` for quick price estimates and display purposes. This is ideal for showing exchange rates without committing to a swap:

```python
# Get price for swapping 1 WETH to USDC
price = await cdp.evm.get_swap_price(
    from_token="0x4200000000000000000000000000000000000006",  # WETH
    to_token="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",  # USDC
    from_amount="1000000000000000000",  # 1 WETH (18 decimals)
    network="base",
    taker="0x1234567890123456789012345678901234567890"
)

if price.liquidity_available:
    print(f"You'll receive: {price.to_amount} USDC")
    print(f"Minimum after slippage: {price.min_to_amount} USDC")
```

**Note:** `get_swap_price` does not reserve funds or signal commitment to swap, making it suitable for more frequent price updates with less strict rate limiting - although the data may be slightly less precise.

#### 3. Create and execute separately

Use `account.quote_swap()` / `smart_account.quote_swap()` when you need full control over the swap process. This returns complete transaction data for execution:

**Important:** `quote_swap()` signals a soft commitment to swap and may reserve funds on-chain. It is rate-limited more strictly than `get_swap_price` to prevent abuse.

**Regular Account (EOA):**
```python


# Retrieve an existing EVM account with funds already in it
account = await cdp.evm.get_or_create_account(name="MyExistingFundedAccount")

# Step 1: Create a swap quote with full transaction details
swap_quote = await account.quote_swap(
    from_token="0x4200000000000000000000000000000000000006",  # WETH
    to_token="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",  # USDC
    from_amount="1000000000000000000",  # 1 WETH (18 decimals)
    network="base",
    slippage_bps=100  # 1% slippage tolerance
)

# Step 2: Check if liquidity is available, and/or perform other analysis on the swap quote
if not swap_quote.liquidity_available:
    print("Insufficient liquidity for swap")
else:
    # Step 3: Execute using the quote
    result = await swap_quote.execute()
    print(f"Transaction hash: {result.transaction_hash}")
```

**Smart Account:**
```python
# Create or retrieve a smart account with funds already in it
owner = await cdp.evm.get_or_create_account(name="MyOwnerAccount")
smart_account = await cdp.evm.get_or_create_smart_account(name="MyExistingFundedSmartAccount", owner=owner)

# Step 1: Create a swap quote with full transaction details for smart account
swap_quote = await smart_account.quote_swap(
    from_token="0x4200000000000000000000000000000000000006",  # WETH
    to_token="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",  # USDC
    from_amount="1000000000000000000",  # 1 WETH (18 decimals)
    network="base",
    slippage_bps=100,  # 1% slippage tolerance
    # Optional: paymaster_url="https://paymaster.example.com"  # For gas sponsorship
)

# Step 2: Check if liquidity is available, and/or perform other analysis on the swap quote
if not swap_quote.liquidity_available:
    print("Insufficient liquidity for swap")
else:
    # Step 3: Execute using the quote
    result = await swap_quote.execute()
    print(f"User operation hash: {result.user_op_hash}")

    # Wait for the user operation to complete
    receipt = await smart_account.wait_for_user_operation(user_op_hash=result.user_op_hash)
    print(f"Status: {receipt.status}")
```

#### When to use each approach:

- **All-in-one (`account.swap()` / `smart_account.swap()`)**: Best for most use cases. Simple, handles everything automatically.
- **Price only (`get_swap_price`)**: For displaying exchange rates, building price calculators, or checking liquidity without executing. Suitable when frequent price updates are needed - although the data may be slightly less precise.
- **Create then execute (`account.quote_swap()` / `smart_account.quote_swap()`)**: When you need to inspect swap details, implement custom logic, or handle complex scenarios before execution. Note: May reserve funds on-chain and is more strictly rate-limited.

#### Key differences between Regular Accounts (EOAs) and Smart Accounts:

- **Regular accounts (EOAs)** return `transaction_hash` and execute immediately on-chain
- **Smart accounts** return `user_op_hash` and execute via user operations with optional gas sponsorship through paymasters
- **Smart accounts** require an owner account for signing operations
- **Smart accounts** support batch operations and advanced account abstraction features

All approaches handle Permit2 signatures automatically for ERC20 token swaps. Make sure tokens have proper allowances set for the Permit2 contract before swapping.

#### Example implementations

To help you get started with token swaps in your application, we provide the following fully-working examples demonstrating different scenarios:

**Regular account (EOA) swap examples:**
- [Execute a swap transaction using account (RECOMMENDED)](https://github.com/coinbase/cdp-sdk/blob/main/examples/python/evm/account.swap.py) - All-in-one regular account swap execution
- [Quote swap using account convenience method](https://github.com/coinbase/cdp-sdk/blob/main/examples/python/evm/account.quote_swap.py) - Account convenience method for creating quotes
- [Two-step quote and execute process](https://github.com/coinbase/cdp-sdk/blob/main/examples/python/evm/account.quote_swap_and_execute.py) - Detailed two-step approach with analysis

**Smart account swap examples:**
- [Execute a swap transaction using smart account (RECOMMENDED)](https://github.com/coinbase/cdp-sdk/blob/main/examples/python/evm/smart_account.swap.py) - All-in-one smart account swap execution with user operations and optional paymaster support
- [Quote swap using smart account convenience method](https://github.com/coinbase/cdp-sdk/blob/main/examples/python/evm/smart_account.quote_swap.py) - Smart account convenience method for creating quotes
- [Two-step quote and execute process](https://github.com/coinbase/cdp-sdk/blob/main/examples/python/evm/smart_account.quote_swap_and_execute.py) - Detailed two-step approach with analysis

### EIP-7702 delegation

You can create an [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) delegation for an existing EOA, upgrading it with smart account capabilities on supported networks. The delegated EOA can then use batched transactions and gas sponsorship via paymaster.

```python
import asyncio

from cdp import CdpClient


async def main():
    async with CdpClient() as cdp:
        account = await cdp.evm.get_or_create_account(name="MyAccount")

        delegation_operation_id = await cdp.evm.create_evm_eip7702_delegation(
            address=account.address,
            network="base-sepolia",
            enable_spend_permissions=False,  # optional, defaults to False
        )

        # Wait for the delegation operation to complete
        delegation_operation = await cdp.evm.wait_for_evm_eip7702_delegation_operation_status(
            delegation_operation_id=delegation_operation_id,
        )
        print(f"Delegation confirmed (status: {delegation_operation.status})")


asyncio.run(main())
```

For a runnable example that includes faucet and receipt waiting, see [examples/python/evm/eip7702/create_eip7702_delegation.py](https://github.com/coinbase/cdp-sdk/blob/main/examples/python/evm/eip7702/create_eip7702_delegation.py).

### EVM Smart Accounts

For EVM, we support Smart Accounts which are account-abstraction (ERC-4337) accounts. Currently there is only support for Base Sepolia and Base Mainnet for Smart Accounts.

#### Create an EVM account and a smart account as follows:

```python
import asyncio
from cdp import CdpClient

async def main():
    async with CdpClient() as cdp:
        evm_account = await cdp.evm.create_account()
        smart_account = await cdp.evm.create_smart_account(
            owner=evm_account
        )

asyncio.run(main())
```

#### Sending User Operations

```python
import asyncio
from cdp.evm_call_types import EncodedCall
from cdp import CdpClient

async def main():
    async with CdpClient() as cdp:
        user_operation = await cdp.evm.send_user_operation(
            smart_account=smart_account,
            network="base-sepolia",
            calls=[
                EncodedCall(
                    to="0x0000000000000000000000000000000000000000",
                    value=0,
                    data="0x"
                )
            ]
        )

asyncio.run(main())
```

#### In Base Sepolia, all user operations are gasless by default. If you'd like to specify a different paymaster, you can do so as follows:

```python
import asyncio
from cdp.evm_call_types import EncodedCall
from cdp import CdpClient

async def main():
    async with CdpClient() as cdp:
        user_operation = await cdp.evm.send_user_operation(
            smart_account=smart_account,
            network="base-sepolia",
            calls=[],
            paymaster_url="https://some-paymaster-url.com"
        )

asyncio.run(main())
```

## Account Actions

Account objects have actions that can be used to interact with the account. These can be used in place of the `cdp` client.

For example, instead of:

```python
token_balances = await cdp.evm.list_token_balances(
    address=account.address,
    network="base-sepolia"
)
```

You can use the `list_token_balances` action:

```python
balances = await account.list_token_balances(
    network="base-sepolia",
)
```

EvmAccount supports the following actions:

- `list_token_balances`
- `request_faucet`
- `sign_transaction`
- `send_transaction`
- `transfer`
- `swap`
- `quote_swap`

EvmSmartAccount supports the following actions:

- `list_token_balances`
- `request_faucet`
- `send_user_operation`
- `wait_for_user_operation`
- `get_user_operation`
- `transfer`
- `swap`
- `quote_swap`

SolanaAccount supports the following actions:

- `sign_message`
- `sign_transaction`
- `request_faucet`

EndUserAccount supports the following actions:

- `sign_evm_transaction`
- `sign_evm_message`
- `sign_evm_typed_data`
- `send_evm_transaction`
- `send_evm_asset`
- `send_user_operation`
- `create_evm_eip7702_delegation`
- `revoke_delegation`
- `sign_solana_message`
- `sign_solana_transaction`
- `send_solana_transaction`
- `send_solana_asset`
- `add_evm_account`
- `add_evm_smart_account`
- `add_solana_account`

## Policy Management

You can use the policies SDK to manage sets of rules that govern the behavior of accounts and projects, such as enforce allowlists and denylists.

### Create a Project-level policy that applies to all accounts

This policy will accept any account sending less than a specific amount of ETH to a specific address.

```python
policy = await cdp.policies.create_policy(
    policy=CreatePolicyOptions(
        scope="project",
        description="Project-wide Allowlist Example",
        rules=[
            SignEvmTransactionRule(
                action="accept",
                criteria=[
                    EthValueCriterion(
                        ethValue="1000000000000000000",
                        operator="<=",
                    ),
                    EvmAddressCriterion(
                        addresses=["0x000000000000000000000000000000000000dEaD"],
                        operator="in",
                    ),
                ],
            ),
        ],
    )
)
```
### Create an Account-level policy

This policy will accept any transaction with a value less than or equal to 1 ETH to a specific address.

```python
policy = await cdp.policies.create_policy(
    policy=CreatePolicyOptions(
        scope="account",
        description="Account Allowlist Example",
        rules=[
            SignEvmTransactionRule(
                action="accept",
                criteria=[
                    EthValueCriterion(
                        ethValue="1000000000000000000",
                        operator="<=",
                    ),
                    EvmAddressCriterion(
                        addresses=["0x000000000000000000000000000000000000dEaD"],
                        operator="in",
                    ),
                ],
            ),
        ],
    )
)
```

### Create a Solana Allowlist Policy

```python
policy = await cdp.policies.create_policy(
    policy=CreatePolicyOptions(
        scope="account",
        description="Account Allowlist Example",
        rules=[
            SignSolanaTransactionRule(
                action="accept",
                criteria=[
                    SolanaAddressCriterion(
                        addresses=["123456789abcdef123456789abcdef12"],
                        operator="in",
                    ),
                ],
            )
        ],
    )
)
```

### List Policies

You can filter by account:

```python
policies = await cdp.policies.list_policies(scope="account")
```

You can also filter by project:

```python
policies = await cdp.policies.list_policies(scope="project")
```

Or you can list all of them without any filter:

```python
policies = await cdp.policies.list_policies()
```

### Retrieve a Policy

```python
policies = await cdp.policies.get_policy_by_id(id="__POLICY_ID__")
```

### Update a Policy

This policy will update an existing policy to accept transactions to any address except one.

```python
policy = await cdp.policies.update_policy(
    id="__POLICY_ID__",
    policy=UpdatePolicyOptions(
        description="Updated Denylist Policy",
        rules=[
            SignEvmTransactionRule(
                action="accept",
                criteria=[
                    EvmAddressCriterion(
                        addresses=["0x000000000000000000000000000000000000dEaD"],
                        operator="not in",
                    ),
                ],
            )
        ],
    )
)
```

### Delete a Policy

> [!WARNING] Attempting to delete an account-level policy in-use by at least one account will fail.

```python
policy = await cdp.policies.delete_policy(id="__POLICY_ID__")
```

#### Supported Policy Rules

We currently support the following policy rules:

**Server wallet rules:**

- [SignEvmTransactionRule](https://docs.cdp.coinbase.com/api-reference/v2/rest-api/policy-engine/create-a-policy#signevmtransactionrule)
- [SendEvmTransactionRule](https://docs.cdp.coinbase.com/api-reference/v2/rest-api/policy-engine/create-a-policy#sendevmtransactionrule)
- [SignEvmMessageRule](https://docs.cdp.coinbase.com/api-reference/v2/rest-api/policy-engine/create-a-policy#signevmmessagerule)
- [SignEvmTypedDataRule](https://docs.cdp.coinbase.com/api-reference/v2/rest-api/policy-engine/create-a-policy#signevmtypeddatarule)
- [SignSolanaTransactionRule](https://docs.cdp.coinbase.com/api-reference/v2/rest-api/policy-engine/create-a-policy#signsolanatransactionrule)
- [SendSolanaTransactionRule](https://docs.cdp.coinbase.com/api-reference/v2/rest-api/policy-engine/create-a-policy#sendsolanatransactionrule)
- [SignEvmHashRule](https://docs.cdp.coinbase.com/api-reference/v2/rest-api/policy-engine/create-a-policy#signevmhashrule)
- [PrepareUserOperationRule](https://docs.cdp.coinbase.com/api-reference/v2/rest-api/policy-engine/create-a-policy#prepareuseroperationrule)
- [SendUserOperationRule](https://docs.cdp.coinbase.com/api-reference/v2/rest-api/policy-engine/create-a-policy#senduseroperationrule)

**End user embedded wallet rules:**

- `SignEndUserEvmTransactionRule` — operation: `signEndUserEvmTransaction` (criteria: `ethValue`, `evmAddress`, `evmData`, `netUSDChange`)
- `SendEndUserEvmTransactionRule` — operation: `sendEndUserEvmTransaction` (criteria: `ethValue`, `evmAddress`, `evmNetwork`, `evmData`, `netUSDChange`)
- `SignEndUserEvmMessageRule` — operation: `signEndUserEvmMessage` (criteria: `evmMessage`)
- `SignEndUserEvmTypedDataRule` — operation: `signEndUserEvmTypedData` (criteria: `evmTypedDataField`, `evmTypedDataVerifyingContract`)
- `SignEndUserSolTransactionRule` — operation: `signEndUserSolTransaction` (criteria: `solAddress`, `solValue`, `splAddress`, `splValue`, `mintAddress`, `solData`, `programId`)
- `SendEndUserSolTransactionRule` — operation: `sendEndUserSolTransaction` (criteria: `solAddress`, `solValue`, `splAddress`, `splValue`, `mintAddress`, `solData`, `programId`, `solNetwork`)
- `SignEndUserSolMessageRule` — operation: `signEndUserSolMessage` (criteria: `solMessage`)

End user rules use the same criteria types as their server wallet counterparts. For example, `SignEndUserEvmTransactionRule` supports the same `EthValueCriterion`, `EvmAddressCriterion`, and `EvmDataCriterion` criteria as `SignEvmTransactionRule`.

### End User embedded wallet Policies

You can create policies that govern end-user operations using the same criteria types available for server wallet policies. The only difference is the rule type, which targets end-user-specific actions.

#### End User EVM Policy

This policy restricts end-user EVM transaction signing to a max value and allowlisted recipients — the same criteria used in `SignEvmTransactionRule`:

```python
from cdp.policies.types import (
    CreatePolicyOptions,
    SignEndUserEvmTransactionRule,
    EthValueCriterion,
    EvmAddressCriterion,
)

policy = await cdp.policies.create_policy(
    policy=CreatePolicyOptions(
        scope="project",
        description="End User EVM Policy",
        rules=[
            SignEndUserEvmTransactionRule(
                action="accept",
                criteria=[
                    EthValueCriterion(
                        ethValue="1000000000000000000",  # 1 ETH in wei
                        operator="<=",
                    ),
                    EvmAddressCriterion(
                        addresses=["0x000000000000000000000000000000000000dEaD"],
                        operator="in",
                    ),
                ],
            ),
        ],
    )
)
```

#### End User Solana Policy

This policy restricts end-user Solana transaction signing to allowlisted recipients under a SOL value threshold — the same criteria used in `SignSolanaTransactionRule`:

```python
from cdp.policies.types import (
    CreatePolicyOptions,
    SignEndUserSolTransactionRule,
    SolAddressCriterion,
    SolValueCriterion,
)

policy = await cdp.policies.create_policy(
    policy=CreatePolicyOptions(
        scope="project",
        description="End User Solana Policy",
        rules=[
            SignEndUserSolTransactionRule(
                action="accept",
                criteria=[
                    SolAddressCriterion(
                        addresses=["11111111111111111111111111111111"],
                        operator="in",
                    ),
                    SolValueCriterion(
                        solValue="1000000000",  # 1 SOL in lamports
                        operator="<=",
                    ),
                ],
            ),
        ],
    )
)
```

> For a comprehensive example demonstrating all 7 end-user operations, see [create_end_user_policy.py](https://github.com/coinbase/cdp-sdk/blob/main/examples/python/end_user/create_end_user_policy.py).

### End-user Management

You can use the End User SDK to manage the users of your applications.

#### Create End User

Create an end user with authentication methods and optional accounts:

```python
import asyncio
from cdp import CdpClient
from cdp.openapi_client.models.authentication_method import AuthenticationMethod
from cdp.openapi_client.models.email_authentication import EmailAuthentication
from cdp.openapi_client.models.create_end_user_request_evm_account import CreateEndUserRequestEvmAccount
from cdp.openapi_client.models.create_end_user_request_solana_account import CreateEndUserRequestSolanaAccount

async def main():
    async with CdpClient() as cdp:
        # Create an end user with email authentication and accounts
        end_user = await cdp.end_user.create_end_user(
            authentication_methods=[
                AuthenticationMethod(EmailAuthentication(type="email", email="user@example.com"))
            ],
            evm_account=CreateEndUserRequestEvmAccount(create_smart_account=True),
            solana_account=CreateEndUserRequestSolanaAccount(create_smart_account=False),
        )

        print(f"Created end user: {end_user}")

asyncio.run(main())
```

#### Import End User

Import an existing private key for an end user:

```python
import asyncio
from cdp import CdpClient
from cdp.openapi_client.models.authentication_method import AuthenticationMethod
from cdp.openapi_client.models.email_authentication import EmailAuthentication

async def main():
    async with CdpClient() as cdp:
        # Import an end user with an EVM private key
        end_user = await cdp.end_user.import_end_user(
            authentication_methods=[
                AuthenticationMethod(EmailAuthentication(type="email", email="user@example.com"))
            ],
            private_key="0x...",  # EVM private key (hex string)
            key_type="evm",
        )

        print(f"Imported end user: {end_user}")

asyncio.run(main())
```

You can also import a Solana private key:

```python
end_user = await cdp.end_user.import_end_user(
    authentication_methods=[
        AuthenticationMethod(EmailAuthentication(type="email", email="user@example.com"))
    ],
    private_key="3Kzj...",  # base58 encoded
    key_type="solana",
)
```

#### Add EVM Account to End User

Add an additional EVM EOA (Externally Owned Account) to an existing end user. You can call the method directly on the EndUserAccount object:

```python
result = await end_user.add_evm_account()

print(f"Added EVM account: {result.evm_account.address}")
```

Or use the client method with a user ID:

```python
result = await cdp.end_user.add_end_user_evm_account(user_id=end_user.user_id)

print(f"Added EVM account: {result.evm_account.address}")
```

#### Add EVM Smart Account to End User

Add an EVM smart account to an existing end user. You can call the method directly on the EndUserAccount object:

```python
result = await end_user.add_evm_smart_account(enable_spend_permissions=True)

print(f"Added EVM smart account: {result.evm_smart_account.address}")
```

Or use the client method with a user ID:

```python
result = await cdp.end_user.add_end_user_evm_smart_account(
    user_id=end_user.user_id,
    enable_spend_permissions=True,
)

print(f"Added EVM smart account: {result.evm_smart_account.address}")
```

#### Add Solana Account to End User

Add an additional Solana account to an existing end user. You can call the method directly on the EndUserAccount object:

```python
result = await end_user.add_solana_account()

print(f"Added Solana account: {result.solana_account.address}")
```

Or use the client method with a user ID:

```python
result = await cdp.end_user.add_end_user_solana_account(user_id=end_user.user_id)

print(f"Added Solana account: {result.solana_account.address}")
```

#### Validate Access Token

When your end user has signed in with an [Embedded Wallet](https://docs.cdp.coinbase.com/embedded-wallets/welcome), you can check whether the access token they were granted is valid, and which of your user's it is associated with.

```python
try:
    end_user = await cdp.end_user.validate_access_token(
        access_token=access_token,
    )
    # Access token is valid
except Exception as e:
    # Access token is invalid or expired
```

#### Delegated End User Operations

You can perform signing and sending operations on behalf of end users using the `EndUserClient` delegated methods, or call them directly on an `EndUserAccount` object.

##### Sign an EVM transaction

Using the client:

```python
result = await cdp.end_user.sign_evm_transaction(
    user_id=end_user.user_id,
    address="0x1234...",
    transaction="0x...",
)
print(f"Signature: {result.signature}")
```

Or directly on the EndUserAccount:

```python
result = await end_user.sign_evm_transaction(
    transaction="0x...",
)
print(f"Signature: {result.signature}")
```

##### Sign an EVM message

```python
result = await end_user.sign_evm_message(
    message="Hello, World!",
)
print(f"Signature: {result.signature}")
```

##### Send an EVM transaction

```python
result = await end_user.send_evm_transaction(
    transaction="0x...",
    network="base-sepolia",
)
print(f"Transaction hash: {result.transaction_hash}")
```

##### Send an EVM asset

```python
result = await end_user.send_evm_asset(
    to="0x9F663335Cd6Ad02a37B633602E98866CF944124d",
    amount="10000",
    network="base-sepolia",
    asset="usdc",
)
print(f"Transaction hash: {result.transaction_hash}")
```

##### Send a user operation (smart account)

```python
from cdp.evm_call_types import EncodedCall

result = await end_user.send_user_operation(
    network="base-sepolia",
    calls=[
        EncodedCall(
            to="0x0000000000000000000000000000000000000000",
            value=0,
            data="0x",
        )
    ],
)
print(f"User operation hash: {result.user_op_hash}")
```

##### Create an EIP-7702 delegation

```python
result = await end_user.create_evm_eip7702_delegation(
    network="base-sepolia",
)
```

##### Revoke delegation

```python
await end_user.revoke_delegation()
```

##### Sign a Solana message

```python
result = await end_user.sign_solana_message(
    message="Hello, Solana!",
)
print(f"Signature: {result.signature}")
```

##### Sign a Solana transaction

```python
result = await end_user.sign_solana_transaction(
    transaction="base64-encoded-transaction...",
)
print(f"Signed transaction: {result.signed_transaction}")
```

##### Send a Solana transaction

```python
result = await end_user.send_solana_transaction(
    transaction="base64-encoded-transaction...",
    network="solana-devnet",
)
print(f"Signature: {result.transaction_signature}")
```

##### Send a Solana asset

```python
result = await end_user.send_solana_asset(
    to="3KzDtddx4i53FBkvCzuDmRbaMozTZoJBb1TToWhz3JfE",
    amount="1000000",
    network="solana-devnet",
    asset="usdc",
)
print(f"Signature: {result.transaction_signature}")
```

## Webhooks

You can use the webhooks SDK to subscribe to on-chain and wallet events and receive notifications at a URL of your choice.

### Create Subscription

Create a webhook subscription to receive event notifications:

```python
from cdp.webhook_types import CreateWebhookSubscriptionOptions

subscription = await cdp.webhooks.create_subscription(
    CreateWebhookSubscriptionOptions(
        description="Monitor wallet transactions",
        event_types=[
            "wallet.transaction.pending",
            "wallet.transaction.confirmed",
            "wallet.transaction.failed",
        ],
        target_url="https://example.com/webhook",
        target_headers={"X-Custom-Header": "custom-value"},  # optional
        is_enabled=True,  # optional, defaults to True
        metadata={"env": "production"},  # optional
    )
)

print(f"Subscription ID: {subscription.subscription_id}")
print(f"Secret: {subscription.secret}")  # use to verify webhook signatures
```

The available wallet event types are:

- `wallet.transaction.created`
- `wallet.transaction.broadcast`
- `wallet.transaction.pending`
- `wallet.transaction.replaced`
- `wallet.transaction.confirmed`
- `wallet.transaction.failed`
- `wallet.transaction.signed`
- `wallet.typed_data.signed`
- `wallet.message.signed`
- `wallet.hash.signed`
- `wallet.delegation.created`
- `wallet.delegation.revoked`

For a complete working example, see [webhooks/create_webhook_subscription.py](https://github.com/coinbase/cdp-sdk/blob/main/examples/python/webhooks/create_webhook_subscription.py).

## Authentication tools

This SDK also contains simple tools for authenticating REST API requests to the [Coinbase Developer Platform (CDP)](https://docs.cdp.coinbase.com/). See the [Auth README](cdp/auth/README.md) for more details.

## Error Reporting

This SDK contains error reporting functionality that sends error events to CDP. If you would like to disable this behavior, you can set the `DISABLE_CDP_ERROR_REPORTING` environment variable to `true`.

```bash
DISABLE_CDP_ERROR_REPORTING=true
```

## Usage Tracking

This SDK contains usage tracking functionality that sends usage events to CDP. If you would like to disable this behavior, you can set the `DISABLE_CDP_USAGE_TRACKING` environment variable to `true`.

```bash
DISABLE_CDP_USAGE_TRACKING=true
```

## License

This project is licensed under the MIT License - see the [LICENSE](https://github.com/coinbase/cdp-sdk/tree/main/LICENSE.md) file for details.

## Support

For feature requests, feedback, or questions, please reach out to us in the
**#cdp-sdk** channel of the [Coinbase Developer Platform Discord](https://discord.com/invite/cdp).

- [API Reference](https://docs.cdp.coinbase.com/api-v2/docs/welcome)
- [SDK Docs](https://coinbase.github.io/cdp-sdk/python)
- [GitHub Issues](https://github.com/coinbase/cdp-sdk/issues)

## Security

If you discover a security vulnerability within this SDK, please see our [Security Policy](https://github.com/coinbase/cdp-sdk/tree/main/SECURITY.md) for disclosure information.
