This guide walks through creating and executing a token swap on CoW Protocol using the Python SDK, learning how to swap tokens with built-in MEV protection and gasless trading.
CoW Protocol employs batch auctions to protect traders from MEV and provides the best execution prices by aggregating liquidity across multiple DEXs.
Prerequisites
Before starting, ensure you have:
The CoW Protocol Python SDK installed
A wallet with test tokens (Sepolia testnet)
Your wallet’s private key
Approval for the CoW Protocol Vault Relayer to spend your tokens
You must approve the CoW Protocol Vault Relayer contract before executing a swap.
Environment Setup
Create a .env file in your project root:
PRIVATE_KEY = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
Add .env to .gitignore to prevent accidentally committing your private key.
Install the required package:
pip install python-dotenv
Complete Swap Example
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
BUY_TOKEN = Web3.to_checksum_address(
"0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14"
) # WETH
SELL_TOKEN = Web3.to_checksum_address(
"0xbe72E441BF55620febc26715db68d3494213D8Cb"
) # USDC
SELL_AMOUNT_BEFORE_FEE = Wei( 5000000000000000000 )
CHAIN = Chain. SEPOLIA
load_dotenv()
PRIVATE_KEY = os.getenv( "PRIVATE_KEY" )
if not PRIVATE_KEY :
raise ValueError ( "Missing PRIVATE_KEY in .env file" )
ACCOUNT = Account.from_key( PRIVATE_KEY )
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
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.
BUY_TOKEN = Web3.to_checksum_address(
"0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14"
) # WETH
SELL_TOKEN = Web3.to_checksum_address(
"0xbe72E441BF55620febc26715db68d3494213D8Cb"
) # USDC
Always use checksummed addresses to avoid errors via Web3.to_checksum_address().
Specify Sell Amount
SELL_AMOUNT_BEFORE_FEE = Wei( 5000000000000000000 )
Amounts are specified in the token’s smallest unit. Use Web3.to_wei() for easier conversion: Web3.to_wei(50, 'ether') equals 50 tokens with 18 decimals.
Select Network
Supported networks include: MAINNET, GNOSIS, SEPOLIA, ARBITRUM_ONE, BASE, POLYGON, AVALANCHE, BNB, and LENS.
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 function handles quote retrieval, order creation and signing, orderbook submission, and returns the order UID and explorer URL.
Advanced Options
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 against price changes between order creation and execution. A 1% slippage means accepting up to 1% less than the quoted amount.
Working with Safe Wallets
To create orders from a 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 signing the transaction.
After creating an order, fetch its details using the OrderBook API:
from cowdao_cowpy.order_book.api import OrderBookApi
from cowdao_cowpy.order_book.generated.model import UID
order_book_api = OrderBookApi()
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
Insufficient Token Balance
Ensure your wallet has enough of the sell token. Account for token decimals when checking balances.
Before swapping, approve the CoW Protocol Vault Relayer: from cowdao_cowpy.common.constants import CowContractAddress
vault_relayer = CowContractAddress. VAULT_RELAYER .value
Orders may take time to execute using batch auctions. Check the order status via the explorer URL returned by swap_tokens(). Orders expire if they don’t find a match within their validity period.
Next Steps