Overview
To use ComposableCoW, your Safe wallet must be configured with the ExtensibleFallbackHandler and set ComposableCoW as the domain verifier for CoW Protocol’s settlement domain.
ComposableCoW requires the ExtensibleFallbackHandler to enable EIP-1271 signature verification for conditional orders. This setup only needs to be done once per Safe.
Prerequisites
Before setting up ComposableCoW, ensure you have:
- A deployed Safe wallet
- Access to execute transactions on the Safe (threshold signers)
- The deployed contract addresses for your network
Deployed Contracts
ComposableCoW and ExtensibleFallbackHandler are deployed at the same addresses across multiple networks:
| Contract | Address |
|---|
ExtensibleFallbackHandler | 0x2f55e8b20D0B9FEFA187AA7d00B6Cbe563605bF5 |
ComposableCoW | 0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74 |
TWAP | 0x6cF1e9cA41f7611dEf408122793c358a3d11E5a5 |
GoodAfterTime | 0xdaf33924925e03c9cc3a10d434016d6cfad0add5 |
StopLoss | 0x412c36e5011cd2517016d243a2dfb37f73a242e7 |
These addresses are consistent across Ethereum Mainnet, Gnosis Chain, Arbitrum, Base, Optimism, Polygon, and other supported networks.
Setup Steps
Set the Fallback Handler
Configure your Safe to use the ExtensibleFallbackHandler:// From within a Safe transaction
safe.setFallbackHandler(0x2f55e8b20D0B9FEFA187AA7d00B6Cbe563605bF5);
This allows your Safe to use extensible signature verification for different domains. Set ComposableCoW as Domain Verifier
Register ComposableCoW as the signature verifier for the CoW Protocol settlement domain:ExtensibleFallbackHandler handler = ExtensibleFallbackHandler(fallbackHandler);
// Get CoW Protocol's domain separator from settlement contract
bytes32 domainSeparator = settlement.domainSeparator();
// Set ComposableCoW as the verifier for this domain
handler.setDomainVerifier(
domainSeparator,
0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74
);
This tells the Safe to use ComposableCoW for verifying CoW Protocol order signatures. Verify Configuration
Confirm the setup is correct:// Check fallback handler
address currentHandler = abi.decode(
safe.getStorageAt(
uint256(keccak256("fallback_manager.handler.address")),
1
),
(address)
);
require(currentHandler == 0x2f55e8b20D0B9FEFA187AA7d00B6Cbe563605bF5);
// Check domain verifier
address verifier = ExtensibleFallbackHandler(currentHandler)
.domainVerifiers(safe, settlement.domainSeparator());
require(verifier == 0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74);
Complete Setup Example
Here’s a complete example using Safe’s execTransaction to batch both setup steps:
import {Safe} from "safe/Safe.sol";
import {Enum} from "safe/common/Enum.sol";
import {ExtensibleFallbackHandler} from "safe/handler/ExtensibleFallbackHandler.sol";
import {ComposableCoW} from "composable-cow/ComposableCoW.sol";
contract SafeSetup {
Safe public safe;
ExtensibleFallbackHandler public constant HANDLER =
ExtensibleFallbackHandler(0x2f55e8b20D0B9FEFA187AA7d00B6Cbe563605bF5);
ComposableCoW public constant COMPOSABLE_COW =
ComposableCoW(0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74);
function setupSafeForComposableCoW(
address settlement
) external {
// Step 1: Set fallback handler
safe.execTransaction(
address(safe),
0,
abi.encodeWithSelector(
Safe.setFallbackHandler.selector,
address(HANDLER)
),
Enum.Operation.Call,
0, 0, 0, address(0), payable(0),
signatures
);
// Step 2: Set domain verifier
bytes32 domainSeparator = CoWSettlement(settlement).domainSeparator();
safe.execTransaction(
address(safe),
0,
abi.encodeWithSelector(
ExtensibleFallbackHandler.setDomainVerifier.selector,
domainSeparator,
address(COMPOSABLE_COW)
),
Enum.Operation.Call,
0, 0, 0, address(0), payable(0),
signatures
);
}
}
Using MultiSend for Atomic Setup
You can combine both operations into a single transaction using Safe’s MultiSend library:
import {MultiSend} from "safe/libraries/MultiSend.sol";
function atomicSetup(address settlement) external {
bytes memory transactions = abi.encodePacked(
// Transaction 1: setFallbackHandler
uint8(Enum.Operation.Call),
address(safe),
uint256(0),
uint256(setFallbackHandlerData.length),
setFallbackHandlerData,
// Transaction 2: setDomainVerifier
uint8(Enum.Operation.Call),
address(safe),
uint256(0),
uint256(setDomainVerifierData.length),
setDomainVerifierData
);
safe.execTransaction(
address(multiSend),
0,
abi.encodeWithSelector(
MultiSend.multiSend.selector,
transactions
),
Enum.Operation.DelegateCall,
0, 0, 0, address(0), payable(0),
signatures
);
}
Always verify contract addresses for your specific network. While the addresses are deterministic across networks, always confirm them before use.
Next Steps
Once your Safe is configured:
- Create conditional orders to start trading
- Learn about managing orders lifecycle
- Explore merkle root batching for gas efficiency
Common Issues
Fallback Handler Not Set
If you encounter signature verification failures, ensure the fallback handler is properly set:
address handler = abi.decode(
safe.getStorageAt(
uint256(keccak256("fallback_manager.handler.address")),
1
),
(address)
);
if (handler == address(0)) {
revert("Fallback handler not set");
}
Wrong Domain Verifier
Verify the correct domain separator is being used:
bytes32 domainSeparator = settlement.domainSeparator();
address verifier = handler.domainVerifiers(address(safe), domainSeparator);
require(
verifier == address(composableCow),
"ComposableCoW not set as domain verifier"
);