Overview
This guide covers interacting with CoW Protocol smart contracts, signing orders using EIP-712 typed data, and understanding the various signing schemes supported by the protocol.
Core Concepts
CoW Protocol uses EIP-712 typed structured data for order signing, which provides:
- Human-readable signature requests in wallets
- Domain separation to prevent replay attacks
- Type safety for order parameters
Setting Up the Domain
The EIP-712 domain identifies the protocol and chain:
from cowdao_cowpy.contracts.domain import domain, TypedDataDomain
from cowdao_cowpy.common.chains import Chain
from cowdao_cowpy.common.constants import CowContractAddress
order_domain = domain(
chain=Chain.MAINNET,
verifying_contract=CowContractAddress.SETTLEMENT_CONTRACT.value
)
print(f"Domain name: {order_domain.name}") # "Gnosis Protocol"
print(f"Version: {order_domain.version}") # "v2"
print(f"Chain ID: {order_domain.chainId}") # 1 (mainnet)
Creating Orders
Orders are created using the Order dataclass:
from cowdao_cowpy.contracts.order import Order
from web3 import Web3
import time
order = Order(
sell_token=Web3.to_checksum_address(
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" # USDC
),
buy_token=Web3.to_checksum_address(
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" # WETH
),
receiver="0x...",
sell_amount="1000000000", # 1000 USDC (6 decimals)
buy_amount="500000000000000000", # 0.5 WETH (18 decimals)
valid_to=int(time.time()) + 3600,
app_data="0x0000000000000000000000000000000000000000000000000000000000000000",
fee_amount="0",
kind="sell",
partially_fillable=False,
sell_token_balance="erc20",
buy_token_balance="erc20",
)
Signing Schemes
CoW Protocol supports multiple signing schemes:
EIP-712 (Recommended)
The preferred method using typed structured data:
from cowdao_cowpy.contracts.sign import sign_order, SigningScheme, EcdsaSignature
from eth_account import Account
from eth_account.signers.local import LocalAccount
account: LocalAccount = Account.from_key("YOUR_PRIVATE_KEY")
signature: EcdsaSignature = sign_order(
domain=order_domain,
order=order,
owner=account,
scheme=SigningScheme.EIP712
)
print(f"Signature scheme: {signature.scheme.name}")
print(f"Signature data: {signature.to_string()}")
ETH_SIGN
Alternative signing method using eth_sign:
signature = sign_order(
domain=order_domain,
order=order,
owner=account,
scheme=SigningScheme.ETHSIGN
)
EIP-1271 (Smart Contract Signatures)
For smart contract wallets implementing EIP-1271:
from cowdao_cowpy.contracts.sign import Eip1271Signature, Eip1271SignatureData, SigningScheme
verifier_address = "0x..."
signature_bytes = b"..."
eip1271_signature = Eip1271Signature(
scheme=SigningScheme.EIP1271,
data=Eip1271SignatureData(
verifier=verifier_address,
signature=signature_bytes
)
)
PRESIGN (Gnosis Safe)
For Gnosis Safe multisig wallets:
from cowdao_cowpy.contracts.sign import PreSignSignature, SigningScheme
safe_address = "0x..."
presign_signature = PreSignSignature(
scheme=SigningScheme.PRESIGN,
data=safe_address
)
Order Hashing
Compute order hashes for verification:
from cowdao_cowpy.contracts.order import hash_order, compute_order_uid
from web3 import Web3
order_hash = hash_order(order_domain, order)
print(f"Order hash: {Web3.to_hex(order_hash)}")
order_uid = compute_order_uid(
domain=order_domain,
order=order,
owner=account.address
)
print(f"Order UID: {order_uid}")
Canceling Orders
Single Order Cancellation
from cowdao_cowpy.contracts.sign import sign_order_cancellation, SigningScheme
cancellation_signature = sign_order_cancellation(
domain=order_domain,
order_uid=order_uid,
owner=account,
scheme=SigningScheme.EIP712
)
Batch Cancellation
from cowdao_cowpy.contracts.sign import sign_order_cancellations
order_uids = ["0x...", "0x...", "0x..."]
cancellation_signature = sign_order_cancellations(
domain=order_domain,
order_uids=order_uids,
owner=account,
scheme=SigningScheme.EIP712
)
Order Types
Sell Orders
sell_order = Order(
sell_amount="1000000000", # Exact amount to sell
buy_amount="500000000000000000", # Minimum to receive
kind="sell",
# ... other params
)
Buy Orders
buy_order = Order(
sell_amount="1000000000", # Maximum to pay
buy_amount="500000000000000000", # Exact amount to buy
kind="buy",
# ... other params
)
Partially Fillable Orders
partial_order = Order(
partially_fillable=True, # Can be filled in multiple txs
# ... other params
)
Token Balance Sources
from cowdao_cowpy.contracts.order import OrderBalance
# Standard ERC20 (default)
order.sell_token_balance = "erc20"
# Balancer Vault internal balances
order.sell_token_balance = OrderBalance.INTERNAL.value
# Balancer Vault external balances
order.sell_token_balance = OrderBalance.EXTERNAL.value
Complete Contract Interaction Example
import asyncio
import time
from web3 import Web3, Account
from eth_account.signers.local import LocalAccount
from cowdao_cowpy.contracts.order import Order, hash_order, compute_order_uid
from cowdao_cowpy.contracts.sign import sign_order, SigningScheme
from cowdao_cowpy.contracts.domain import domain
from cowdao_cowpy.common.chains import Chain
from cowdao_cowpy.common.constants import CowContractAddress
from cowdao_cowpy.order_book.api import OrderBookApi
from cowdao_cowpy.order_book.generated.model import OrderCreation
async def create_and_sign_order():
account: LocalAccount = Account.from_key("YOUR_PRIVATE_KEY")
order_domain = domain(
chain=Chain.MAINNET,
verifying_contract=CowContractAddress.SETTLEMENT_CONTRACT.value
)
order = Order(
sell_token=Web3.to_checksum_address("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
buy_token=Web3.to_checksum_address("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"),
receiver=account.address,
sell_amount="1000000000",
buy_amount="500000000000000000",
valid_to=int(time.time()) + 3600,
app_data="0x0000000000000000000000000000000000000000000000000000000000000000",
fee_amount="0",
kind="sell",
partially_fillable=False,
sell_token_balance="erc20",
buy_token_balance="erc20",
)
order_hash = hash_order(order_domain, order)
print(f"Order hash: {Web3.to_hex(order_hash)}")
order_uid = compute_order_uid(order_domain, order, account.address)
print(f"Order UID: {order_uid}")
signature = sign_order(
domain=order_domain,
order=order,
owner=account,
scheme=SigningScheme.EIP712
)
print(f"Signature: {signature.to_string()}")
order_book_api = OrderBookApi()
order_creation = OrderCreation(
from_=account.address,
sellToken=order.sellToken,
buyToken=order.buyToken,
sellAmount=order.sellAmount,
buyAmount=order.buyAmount,
validTo=order.validTo,
appData=order.appData,
feeAmount=order.feeAmount,
kind=order.kind,
partiallyFillable=order.partiallyFillable,
receiver=order.receiver,
signature=signature.to_string(),
signingScheme=signature.scheme.name.lower(),
)
uid = await order_book_api.post_order(order_creation)
print(f"Order submitted: {uid}")
order_link = order_book_api.get_order_link(uid)
print(f"View order: {order_link}")
if __name__ == "__main__":
asyncio.run(create_and_sign_order())
Contract Addresses
from cowdao_cowpy.common.constants import CowContractAddress
print(f"Settlement: {CowContractAddress.SETTLEMENT_CONTRACT.value}")
print(f"Vault Relayer: {CowContractAddress.VAULT_RELAYER.value}")
Security Considerations
Private Key Safety: Never commit private keys to version control. Use environment variables or secure key management services.
- Order Validation: Always validate order parameters before signing. Check token addresses, amounts, and validity periods.
- Domain Separation: The EIP-712 domain prevents signatures from being used on different chains or protocols.
Next Steps