Skip to main content
When creating and managing conditional orders with ComposableCoW, following security best practices is crucial to protect your assets and ensure orders execute as intended.

Token Approvals

Approve Only What You Need

When creating conditional orders, you must approve the GPv2VaultRelayer contract to spend your tokens. Always approve only the exact amount needed for your orders.
Never approve unlimited amounts (type(uint256).max). This creates unnecessary risk if a vulnerability is discovered.
For TWAP orders, approve exactly n × partSellAmount:
// For a TWAP selling 12,000,000 DAI over 30 parts
uint256 totalAmount = 30 * 400000; // = 12,000,000 DAI
sellToken.approve(GPv2VaultRelayer, totalAmount);

Review Existing Approvals

Before creating new orders:
  1. Check existing approvals to GPv2VaultRelayer
  2. Revoke unnecessary or expired approvals
  3. Use tools like Etherscan to audit your token approvals regularly
Consider using approval management tools like Revoke.cash to review and manage your token approvals across all protocols.

Safe Configuration

ExtensibleFallbackHandler Setup

ComposableCoW requires proper Safe configuration:
1

Set the fallback handler

Configure your Safe to use ExtensibleFallbackHandler at address: 0x2f55e8b20D0B9FEFA187AA7d00B6Cbe563605bF5
2

Set the domain verifier

Set the domainVerifier for GPv2Settlement.domainSeparator() to ComposableCoW
3

Verify configuration

Test with a small order first to ensure everything is configured correctly
Incorrect fallback handler configuration will cause all orders to fail signature verification. Always verify your Safe configuration before creating orders.

Order Parameters

Validate Handler Addresses

Always use official handler addresses for conditional order types:
// ✅ Good - Use official deployed addresses
address twapHandler = 0x6cF1e9cA41f7611dEf408122793c358a3d11E5a5;

// ❌ Bad - Never use unverified or custom handlers
address unknownHandler = 0x...;
Official handler addresses are documented in the deployment section and are the same across all supported networks.

Unique Salt Values

Each conditional order requires a unique salt:
// ✅ Good - Unique salt per order
bytes32 salt1 = keccak256(abi.encode("my-twap-order-1", block.timestamp));
bytes32 salt2 = keccak256(abi.encode("my-twap-order-2", block.timestamp));

// ❌ Bad - Reusing salts
bytes32 salt = bytes32(0); // Don't reuse across orders
Reusing salt values with the same handler and staticInput will create the same order hash, causing only one order to be valid.

Order Validation

Verify Before Creating

Before creating orders on-chain, validate parameters: For TWAP orders:
  • sellToken != buyToken - Cannot trade the same token
  • partSellAmount > 0 - Must sell a positive amount
  • minPartLimit > 0 - Must have a minimum buy amount
  • t0 < type(uint32).max - Start time must be valid
  • n > 1 && n <= type(uint32).max - Must have at least 2 parts
  • t > 0 && t <= 365 days - Frequency must be reasonable
  • span <= t - Span cannot exceed frequency
These validations are enforced on-chain, but checking them off-chain first saves gas on failed transactions.

Test With Small Amounts

When trying a new order type or configuration:
  1. Start with minimal token amounts
  2. Verify the order executes correctly
  3. Cancel the test order
  4. Then create orders with real amounts

Merkle Tree Orders

Secure Proof Storage

When using merkle tree-based orders:
Store merkle proofs securely. Loss of proofs means you cannot execute orders, even though they’re valid on-chain.
Best practices:
  • Store proofs in multiple secure locations
  • Use redundant backup systems
  • Consider encrypted storage for sensitive trading strategies
  • Document proof generation logic for reproducibility

Proof Validation

Before setting a merkle root:
// Verify each order in the tree is valid
for (uint i = 0; i < orders.length; i++) {
    validateOrderParams(orders[i]);
}

// Generate tree and verify root
bytes32 root = generateMerkleRoot(orders);

// Only then set the root
composableCow.setRoot(root, proof);

Swap Guards

Restrict Order Execution

Use swap guards to add additional security constraints:
// Example: Restrict receiver to only the Safe itself
ISwapGuard receiverLock = new ReceiverLock();
composableCow.setSwapGuard(receiverLock);
Swap guards are verified on every order execution, providing an additional security layer beyond the conditional order logic.

Custom Guards

When implementing custom swap guards:
  • Implement the ISwapGuard interface correctly
  • Keep logic simple and gas-efficient
  • Thoroughly test all edge cases
  • Consider formal verification for complex guards

Order Cancellation

Cancel Unused Orders

Regularly review and cancel orders that are no longer needed: Single orders:
bytes32 orderHash = composableCow.hash(params);
composableCow.remove(orderHash);
Merkle tree orders:
// Prune orders from tree and generate new root
bytes32 newRoot = generateNewRoot(remainingOrders);
composableCow.setRoot(newRoot, proof);
Canceling orders also clears associated cabinet storage, preventing any potential misuse of stored context.

Common Pitfalls

Avoid These Mistakes

Don’t create orders without sufficient token balance. Orders will fail when watch towers attempt to submit them.
Don’t use t0 = 0 without understanding cabinet storage. TWAP orders with t0 = 0 read the start time from the cabinet, which must be set correctly.
Don’t forget to handle receiver addresses. Setting receiver = address(0) sends tokens to the Safe, while a specific address sends them elsewhere.

Invalid Handler Detection

The contract prevents common errors:
// This will revert with InvalidHandler()
composableCow.create(ConditionalOrderParams({
    handler: address(0),  // ❌ Invalid
    salt: salt,
    staticInput: data
}), true);

Monitoring

Watch Your Orders

After creating orders:
  1. Monitor order execution on CoW Explorer
  2. Verify orders are picked up by watch towers
  3. Check that orders execute within expected parameters
  4. Review filled order details for correctness
Set up monitoring alerts for:
  • Order execution events
  • Failed order attempts
  • Low token balances
  • Approval expirations

Emergency Procedures

If Something Goes Wrong

1

Identify the issue

Determine if it’s a configuration error, insufficient balance, or contract issue
2

Stop new executions

Cancel the problematic order immediately using remove() or setRoot()
3

Revoke approvals if needed

If you suspect a security issue, revoke token approvals to GPv2VaultRelayer
4

Report critical issues

Contact the security team or file a bug report for potential vulnerabilities

Additional Resources

Security is an ongoing process. Stay informed about updates and follow best practices to protect your assets.

Build docs developers (and LLMs) love