Skip to main content

Overview

This guide will walk you through creating and executing a token swap on CoW Protocol using the Python SDK. You’ll learn how to swap tokens with built-in MEV protection and gasless trading.
CoW Protocol uses batch auctions to protect traders from MEV (Maximal Extractable Value) and provides the best execution prices by aggregating liquidity across multiple DEXs.

Prerequisites

Before you begin, ensure you have:
  • Installed the CoW Protocol Python SDK (see Installation)
  • A wallet with some test tokens (we’ll use Sepolia testnet)
  • Your wallet’s private key
  • Approved the CoW Protocol Vault Relayer to spend your tokens
Important: Before executing a swap, you must approve the CoW Protocol Vault Relayer contract to spend your sell token. The Vault Relayer address can be accessed via CowContractAddress.VAULT_RELAYER.

Environment Setup

1

Create Environment File

Create a .env file in your project root to store your private key:
.env
PRIVATE_KEY=0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
Make sure to add .env to your .gitignore file to prevent accidentally committing your private key.
2

Install python-dotenv

Install the python-dotenv package to load environment variables:
pip install python-dotenv

Complete Swap Example

Here’s a complete example of swapping tokens on the Sepolia testnet:
import os
import asyncio
from dotenv import load_dotenv
from web3 import Account, Web3
from web3.types import Wei
from cowdao_cowpy.cow.swap import swap_tokens
from cowdao_cowpy.common.chains import Chain

# Token addresses (Sepolia testnet)
BUY_TOKEN = Web3.to_checksum_address(
    "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14"
)  # WETH
SELL_TOKEN = Web3.to_checksum_address(
    "0xbe72E441BF55620febc26715db68d3494213D8Cb"
)  # USDC

# Swap 50 USDC (with 18 decimals)
SELL_AMOUNT_BEFORE_FEE = Wei(5000000000000000000)

# Use Sepolia testnet
CHAIN = Chain.SEPOLIA

# Load environment variables
load_dotenv()
PRIVATE_KEY = os.getenv("PRIVATE_KEY")

if not PRIVATE_KEY:
    raise ValueError("Missing PRIVATE_KEY in .env file")

# Create account from private key
ACCOUNT = Account.from_key(PRIVATE_KEY)

# Execute the swap
async def main():
    result = await swap_tokens(
        amount=SELL_AMOUNT_BEFORE_FEE,
        account=ACCOUNT,
        chain=CHAIN,
        sell_token=SELL_TOKEN,
        buy_token=BUY_TOKEN,
    )
    print(f"Order created successfully!")
    print(f"Order UID: {result.uid}")
    print(f"Order URL: {result.url}")

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

Understanding the Code

Let’s break down the key components:
1

Import Dependencies

from web3 import Account, Web3
from web3.types import Wei
from cowdao_cowpy.cow.swap import swap_tokens
from cowdao_cowpy.common.chains import Chain
The SDK uses web3.py for Ethereum interactions and provides a simple swap_tokens function for executing swaps.
2

Configure Token Addresses

BUY_TOKEN = Web3.to_checksum_address(
    "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14"
)  # WETH
SELL_TOKEN = Web3.to_checksum_address(
    "0xbe72E441BF55620febc26715db68d3494213D8Cb"
)  # USDC
Always use checksummed addresses to avoid errors. The Web3.to_checksum_address() function ensures addresses are properly formatted.
3

Specify Sell Amount

SELL_AMOUNT_BEFORE_FEE = Wei(5000000000000000000)
Amounts are specified in the token’s smallest unit (wei for 18 decimal tokens). In this example, we’re selling 50 tokens with 18 decimals.
Use Web3.to_wei() for easier conversion: Web3.to_wei(50, 'ether') equals 50 tokens with 18 decimals.
4

Select Network

CHAIN = Chain.SEPOLIA
The SDK supports multiple networks:
  • Chain.MAINNET - Ethereum Mainnet
  • Chain.GNOSIS - Gnosis Chain
  • Chain.SEPOLIA - Sepolia Testnet
  • Chain.ARBITRUM_ONE - Arbitrum One
  • Chain.BASE - Base
  • Chain.POLYGON - Polygon
  • Chain.AVALANCHE - Avalanche
  • Chain.BNB - BNB Chain
  • Chain.LENS - Lens Network
5

Execute the Swap

result = await swap_tokens(
    amount=SELL_AMOUNT_BEFORE_FEE,
    account=ACCOUNT,
    chain=CHAIN,
    sell_token=SELL_TOKEN,
    buy_token=BUY_TOKEN,
)
The swap_tokens function handles:
  • Getting a quote from CoW Protocol
  • Creating and signing the order
  • Submitting the order to the orderbook
  • Returning the order UID and explorer URL

Advanced Options

The swap_tokens function supports additional parameters for more control:
result = await swap_tokens(
    amount=SELL_AMOUNT_BEFORE_FEE,
    account=ACCOUNT,
    chain=CHAIN,
    sell_token=SELL_TOKEN,
    buy_token=BUY_TOKEN,
    slippage_tolerance=0.01,  # 1% slippage (default: 0.5%)
    partially_fillable=False,  # Allow partial fills (default: False)
    valid_to=None,  # Order expiry timestamp (default: uses quote expiry)
    env="prod"  # Environment: "prod", "staging", or "barn" (default: "prod")
)
Slippage Tolerance: Protects you from price changes between order creation and execution. A 1% slippage means you’ll accept up to 1% less than the quoted amount.

Working with Safe Wallets

To create orders from a Safe (Gnosis Safe) wallet:
from eth_typing.evm import ChecksumAddress

safe_address = Web3.to_checksum_address("0x...")

result = await swap_tokens(
    amount=SELL_AMOUNT_BEFORE_FEE,
    account=ACCOUNT,  # Signer account
    chain=CHAIN,
    sell_token=SELL_TOKEN,
    buy_token=BUY_TOKEN,
    safe_address=safe_address,  # Safe wallet address
)
When using a Safe wallet, the order uses pre-signature validation. The account parameter should be one of the Safe owners who will sign the transaction.

Fetching Order Information

After creating an order, you can fetch its details using the OrderBook API:
from cowdao_cowpy.order_book.api import OrderBookApi
from cowdao_cowpy.order_book.generated.model import UID

# Initialize API client
order_book_api = OrderBookApi()

# Fetch order by UID
order = await order_book_api.get_order_by_uid(
    UID("0x...your-order-uid...")
)

print(f"Order status: {order.status}")
print(f"Sell amount: {order.sellAmount}")
print(f"Buy amount: {order.buyAmount}")

Common Issues

Ensure your wallet has enough of the sell token to execute the swap. Remember to account for token decimals when checking balances.
Before swapping, you must approve the CoW Protocol Vault Relayer to spend your tokens:
from cowdao_cowpy.common.constants import CowContractAddress

# Get the Vault Relayer address
vault_relayer = CowContractAddress.VAULT_RELAYER.value

# Use your token contract to approve spending
# (Implementation depends on your web3 setup)
Orders may take time to execute as CoW Protocol uses batch auctions. Check the order status using the explorer URL returned by swap_tokens(). Orders that don’t find a match within their validity period will expire.

Next Steps

Order Management

Learn how to fetch, monitor, and cancel orders

Advanced Features

Explore composable orders and TWAP strategies

API Reference

Browse the complete API documentation

Contract Interaction

Learn how to interact with CoW Protocol smart contracts

Build docs developers (and LLMs) love