Skip to main content

Overview

Composable orders enable you to create conditional orders that execute based on custom logic rather than executing immediately. These orders leverage the ComposableCow smart contract framework to define conditions that must be met before an order becomes tradeable. Key benefits of composable orders:
  • Conditional Execution — Orders only execute when specific conditions are met
  • Advanced Strategies — Build complex trading strategies like TWAP, DCA, or custom logic
  • Gas Efficiency — Reduce on-chain transactions by batching order logic
  • Merkle Tree Storage — Efficiently store multiple orders using Merkle proofs

ConditionalOrder Base Class

All composable orders inherit from the ConditionalOrder abstract base class, which provides core functionality:
conditional_order_structure.py
from cowdao_cowpy.composable import ConditionalOrder
from cowdao_cowpy.common.chains import Chain
from eth_typing import HexStr

class MyCustomOrder(ConditionalOrder[DataType, StructType]):
    def __init__(self, handler: HexStr, data: DataType, salt: HexStr = None):
        super().__init__(
            handler=handler,
            data=data,
            salt=salt,  # Optional 32-byte random value
            has_off_chain_input=False,
            chain=Chain.MAINNET
        )
    
    @property
    def is_single_order(self) -> bool:
        """Whether this represents a single order or multiple orders"""
        return True
    
    @property
    def order_type(self) -> str:
        """Descriptive name for the order type"""
        return "my_custom_order"
    
    def is_valid(self) -> IsValidResult:
        """Validate the order parameters"""
        # Implement validation logic
        pass
    
    async def poll_validate(self, params: PollParams) -> Optional[PollResultError]:
        """Check if order conditions are met"""
        # Implement polling logic
        pass

Key Properties and Methods

Order Identification

order_properties.py
# Each order has a unique ID based on its parameters
order_id = conditional_order.id  # keccak256 hash of serialized order

# Get the order's context key (for cabinet lookups)
ctx_key = conditional_order.ctx

# Get the leaf data for Merkle tree storage
leaf = conditional_order.leaf

Creating Orders

Generate calldata to create a conditional order on-chain:
creating_orders.py
from cowdao_cowpy.composable.order_types.twap import Twap, TwapData, StartType, DurationType
from cowdao_cowpy.common.chains import Chain
from web3 import Web3

# Define TWAP order parameters
twap_data = TwapData(
    sell_token=Web3.to_checksum_address("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),  # USDC
    buy_token=Web3.to_checksum_address("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"),   # WETH
    receiver=Web3.to_checksum_address("0xYourAddress"),
    sell_amount=1000000000,  # 1000 USDC
    buy_amount=500000000000000000,  # 0.5 WETH minimum
    start_type=StartType.AT_MINING_TIME,
    number_of_parts=10,
    time_between_parts=3600,  # 1 hour between parts
    duration_type=DurationType.AUTO,
    app_data="0x" + "0" * 64
)

# Create the order
twap_order = Twap.from_data(twap_data)

# Get calldata to submit to ComposableCow contract
create_calldata = twap_order.create_calldata

# Verify the order is valid
twap_order.assert_is_valid()

Polling Orders

Check if a conditional order is ready to be executed:
polling_orders.py
import asyncio
from cowdao_cowpy.composable.types import PollParams, PollResultCode
from cowdao_cowpy.order_book.api import OrderBookApi
from web3 import AsyncWeb3

async def poll_conditional_order(order, owner_address, chain):
    # Set up polling parameters
    provider = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider("https://rpc.ankr.com/eth"))
    order_book_api = OrderBookApi()
    
    poll_params = PollParams(
        owner=owner_address,
        chain=chain,
        provider=provider,
        order_book_api=order_book_api
    )
    
    # Poll the order
    result = await order.poll(poll_params)
    
    if result.result == PollResultCode.SUCCESS:
        print(f"Order ready! Order: {result.order}")
        print(f"Signature: {result.signature}")
        # Submit order to CoW Protocol API
    elif result.result == PollResultCode.TRY_NEXT_BLOCK:
        print(f"Not ready yet: {result.reason}")
    elif result.result == PollResultCode.DONT_TRY_AGAIN:
        print(f"Order cannot execute: {result.reason}")

# Run the polling
await poll_conditional_order(twap_order, "0xYourAddress", Chain.MAINNET)

Authorization

Conditional orders must be authorized on-chain before they can execute:
authorization.py
from cowdao_cowpy.composable.types import OwnerParams

async def check_authorization(order, owner_address, chain, provider):
    params = OwnerParams(
        owner=owner_address,
        chain=chain,
        provider=provider
    )
    
    is_authorized = await order.is_authorized(params)
    print(f"Order authorized: {is_authorized}")
    
    # Check cabinet value (context-specific storage)
    cabinet_value = await order.cabinet(params)
    print(f"Cabinet value: {cabinet_value}")

ComposableCow Contract Integration

The ComposableCow contract is the core smart contract that manages conditional orders:
composable_cow_contract.py
from cowdao_cowpy.codegen.__generated__.ComposableCow import (
    ComposableCow,
    IConditionalOrder_ConditionalOrderParams
)
from hexbytes import HexBytes

# Initialize ComposableCow contract
composable_cow = ComposableCow(chain=Chain.MAINNET)

# Check if an order is authorized for an owner
order_params = IConditionalOrder_ConditionalOrderParams(
    handler="0xHandlerAddress",
    salt=HexBytes("0x" + "0" * 64),
    staticInput=HexBytes("0x...")
)

is_single_order = await composable_cow.single_orders(
    owner_address,
    HexBytes(order_id)
)

# Get tradeable order with signature
tradeable_order, signature = await composable_cow.get_tradeable_order_with_signature(
    owner=owner_address,
    params=order_params,
    offchain_input=HexBytes("0x"),
    proof=[]  # Merkle proof if using tree storage
)

print(f"Tradeable order: {tradeable_order}")
print(f"Signature: {signature.hex()}")

Multiplexer for Multiple Orders

The Multiplexer class enables efficient management of multiple conditional orders using Merkle trees:
multiplexer_usage.py
from cowdao_cowpy.composable import Multiplexer
from cowdao_cowpy.composable.order_types.twap import Twap
from cowdao_cowpy.composable.types import ProofLocation

# Create multiple orders
order1 = Twap.from_data(twap_data_1)
order2 = Twap.from_data(twap_data_2)
order3 = Twap.from_data(twap_data_3)

# Initialize multiplexer with orders
orders_dict = {
    order1.id: order1,
    order2.id: order2,
    order3.id: order3
}

multiplexer = Multiplexer(
    orders=orders_dict,
    root=None,  # Will be calculated
    location=ProofLocation.PRIVATE
)

# Get Merkle root
root = multiplexer.root
print(f"Merkle root: {root}")

# Get proofs for all orders
proofs = multiplexer.get_proofs()

for proof_with_params in proofs:
    print(f"Handler: {proof_with_params.params.handler}")
    print(f"Salt: {proof_with_params.params.salt}")
    print(f"Merkle path: {[p.hex() for p in proof_with_params.proof.path]}")

# Export to JSON
json_data = multiplexer.to_json()
print(json_data)

# Import from JSON
restored_multiplexer = Multiplexer.from_json(json_data)

Custom Handler Implementation

To create a custom order type, you need to:
  1. Deploy a handler contract that implements the IConditionalOrder interface
  2. Extend the ConditionalOrder class with your custom logic
  3. Register your order type with the factory
custom_handler.py
from dataclasses import dataclass
from typing import Optional
from cowdao_cowpy.composable import ConditionalOrder
from cowdao_cowpy.composable.types import IsValidResult, PollParams, PollResultError
from eth_typing import HexStr

@dataclass
class PriceThresholdData:
    sell_token: str
    buy_token: str
    sell_amount: int
    min_price: int  # Minimum price in buy token per sell token
    receiver: str

@dataclass
class PriceThresholdStruct:
    sell_token: str
    buy_token: str
    sell_amount: int
    min_price: int
    receiver: str

class PriceThresholdOrder(ConditionalOrder[PriceThresholdData, PriceThresholdStruct]):
    HANDLER_ADDRESS = HexStr("0xYourHandlerAddress")
    
    @property
    def is_single_order(self) -> bool:
        return True
    
    @property
    def order_type(self) -> str:
        return "price_threshold"
    
    def is_valid(self) -> IsValidResult:
        if self.data.sell_amount <= 0:
            return IsValidResult(is_valid=False, reason="Invalid sell amount")
        if self.data.min_price <= 0:
            return IsValidResult(is_valid=False, reason="Invalid min price")
        return IsValidResult(is_valid=True)
    
    async def poll_validate(self, params: PollParams) -> Optional[PollResultError]:
        # Check if current price meets threshold
        # This is where you'd query a price oracle
        return None
    
    def transform_data_to_struct(self, data: PriceThresholdData) -> PriceThresholdStruct:
        return PriceThresholdStruct(
            sell_token=data.sell_token,
            buy_token=data.buy_token,
            sell_amount=data.sell_amount,
            min_price=data.min_price,
            receiver=data.receiver
        )
    
    def transform_struct_to_data(self, struct: PriceThresholdStruct) -> PriceThresholdData:
        return PriceThresholdData(
            sell_token=struct.sell_token,
            buy_token=struct.buy_token,
            sell_amount=struct.sell_amount,
            min_price=struct.min_price,
            receiver=struct.receiver
        )

Best Practices

Always implement thorough validation in the is_valid() method to catch errors before submitting orders on-chain.
Use appropriate PollResultCode values to signal whether polling should be retried, delayed, or stopped entirely.
Always test conditional orders on testnets (like Sepolia) before deploying to mainnet.
The salt parameter ensures each order has a unique ID even if other parameters are identical.

TWAP Orders

Learn about Time-Weighted Average Price orders

Signing Schemes

Understand different order signing methods

Order Types

API reference for order structures

Composable API

Composable orders API documentation

Build docs developers (and LLMs) love