Documentation Index
Fetch the complete documentation index at: https://cowswap-mintlify-docs-quality-audit-1774257282.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Troubleshooting
This guide covers the most common issues developers hit when setting up ComposableCoW, based on frequent questions in the CoW Protocol Discord.Setup issues
setDomainVerifier transaction reverts
The most common setup error. The call to setDomainVerifier must be made from the Safe to itself — not to the ExtensibleFallbackHandler.
Correct flow:
- Safe executes a transaction where
to= the Safe’s own address - The call data encodes
setDomainVerifier(domainSeparator, composableCowAddress) - The ExtensibleFallbackHandler (set as the Safe’s fallback handler) processes it
setDomainVerifier directly on the ExtensibleFallbackHandler address — this will revert or have no effect.
Correct setup sequence
The setup must happen in this exact order:- Set the fallback handler on the Safe to ExtensibleFallbackHandler (
0x2f55e8b20D0B9FEFA187AA7d00B6Cbe563605bF5) - Register ComposableCoW as domain verifier by calling
setDomainVerifierfrom the Safe to itself - Create programmatic orders via
ComposableCoW.create()
setDomainVerifier call will silently fail (no fallback handler to intercept it).
Verifying setup is correct
Check both configurations on-chain: 1. Verify fallback handler:0x2f55e8b20D0B9FEFA187AA7d00B6Cbe563605bF5 (ExtensibleFallbackHandler)
2. Verify domain verifier:
0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74 (ComposableCoW)
The domain separator for CoW Protocol is chain-specific. You can read it from the settlement contract:
MultiSend atomic setup
To do both steps atomically in a single Safe transaction:Order not appearing in explorer
After creating a programmatic order, it should appear in CoW Explorer. If it doesn’t:1. Check if the watch-tower picked it up
The watch-tower monitorsConditionalOrderCreated events. If it hasn’t indexed your order:
- Was the transaction confirmed? Check the tx hash on the block explorer
- Did the event fire? Look for
ConditionalOrderCreatedin the transaction logs - Is the watch-tower running for your chain? The public watch-tower covers Ethereum, Gnosis Chain, Arbitrum One, Base, Polygon, Avalanche, BNB Chain, Linea, Plasma, Ink, and Sepolia
- Is there a delay? The watch-tower processes blocks periodically — wait a few minutes
2. Check if the order is currently valid
The watch-tower callsgetTradeableOrder() on your handler. If it reverts, the order won’t be posted. Common reasons:
- Balance insufficient — the Safe doesn’t hold enough sell tokens
- Time condition not met — for TWAP, the current part hasn’t started yet
- Guard rejection — a swap guard is blocking the order
3. Run your own watch-tower for debugging
--one-shot flag processes once and exits. --dry-run shows what orders would be posted without actually posting them.
Foundry / Forge dependency issues
When installing ComposableCoW with Forge, Safe library dependencies may not resolve automatically.Missing SignatureVerifierMuxer.sol
Order stopped being polled unexpectedly
If your programmatic order was being polled by the Watch Tower but silently stopped without being cancelled or fulfilled, the most likely cause is an unrecognized revert error from your handler contract.Root cause
When the Watch Tower callsgetTradeableOrderWithSignature on your handler and the call reverts, it attempts to decode the revert data against a set of known custom errors:
OrderNotValid(string)PollTryNextBlockPollTryAtBlock(uint256)PollTryAtEpoch(uint256, uint256)PollNever(string)
UNEXPECTED_ERROR and permanently stops polling the order. There is no notification to the order creator.
Common triggers
- Arithmetic overflow/underflow — Solidity 0.8+ reverts with a
Panic(uint256)error code, which the Watch Tower does not recognize as a polling hint - Failed external calls — If your handler calls another contract that reverts with its own custom error, those bytes propagate up and won’t match the known set
- Generic
revert()orrequire(false)— These produce empty or non-standard revert data - Out-of-gas in sub-calls — Can produce unexpected revert behavior
Debugging steps
-
Reproduce the revert locally. Call
getTradeableOrderWithSignaturewith the same parameters the Watch Tower uses (owner, params, offchainInput, proof) usingcast callor a Foundry test: -
Inspect the revert data. If the call fails, check the raw revert bytes. Compare the first 4 bytes (the error selector) against the known selectors:
OrderNotValid:0x16f2d3f3PollTryNextBlock:0x44882c68PollTryAtBlock:0x2d6e06d5PollTryAtEpoch:0x4184e129
- Check for arithmetic edge cases. Review your handler for divisions that could hit zero denominators, multiplications that could overflow, or timestamp arithmetic that could underflow.
-
Run a local Watch Tower with
--one-shot --only-owner <YOUR_SAFE>andLOG_LEVEL=DEBUGto see the exact error classification.
Best practice: defensive error handling
Wrap your handler’s core logic in a try/catch so that any unexpected failure maps to a recognized error:Salt and replay protection
Each programmatic order needs a unique salt. If you reuse a salt for the same handler + parameters + Safe, the order will be a duplicate.- Use
keccak256(abi.encodePacked(block.timestamp, nonce))or similar for unique salts - The salt is used to compute the order’s storage key in ComposableCoW
- Cancelling an order frees its salt for reuse
Contract addresses (all chains)
| Contract | Address |
|---|---|
| ComposableCoW | 0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74 |
| ExtensibleFallbackHandler | 0x2f55e8b20D0B9FEFA187AA7d00B6Cbe563605bF5 |
| GPv2Settlement | 0x9008D19f58AAbD9eD0D60971565AA8510560ab41 |
Related resources
- Setup Guide — Initial configuration walkthrough
- Architecture — How the system works
- Watch Tower — The service that monitors and posts programmatic orders
- Debugging Programmatic Orders — Decision tree for “why didn’t my order execute?”