ComposableCoW is a framework for smoothing the developer experience when building conditional orders on CoW Protocol. Conditional orders are a subset of smart contract orders. It allows one to create conditional orders that:
Can be used to generate multiple discrete order (self-expressing)
Assess a proposed order against a set of conditions (self-validating)
The framework makes boilerplate code for conditional orders a thing of the past, and allows developers to focus on the business logic of their order. ComposableCoW handles:
Authorization (multiple owners, with multiple orders per owner)
By using Merkle Trees, the gas efficiency of O(1) is achieved for n conditional orders. This is achieved by storing the Merkle Tree root on-chain, and passing the Merkle Tree proof to the ComposableCoW contract. This allows for O(1) gas efficiency for adding / removing conditional orders.For simplicity, single orders are also supported, however, this is NOT recommended for large n as the gas efficiency is O(n).
As there are many nested contracts, it’s important for a callee to know some context from the caller. To achieve this, ComposableCoW passes a bytes32 variable ctx to the callee, such that:
Copy
Ask AI
ctx = merkle root of orders: bytes32(0) single order: H(ConditionalOrderParams)
Having this context also allows for conditional orders / merkle roots to use this as a key in a mapping, to store conditional order-specific data.
In order to delegate signature verification to ComposableCoW, the delegating contract may either:
Be a Safe and use ExtensibleFallbackHandler that allows for EIP-712 domain delegation to a custom contract (i.e. ComposableCoW); or
Implement ERC-1271 and within the isValidSignature method, call ComposableCoW.isValidSafeSignature().
ComposableCoW can also be used with contracts other than Safe. The ERC1271Forwarder abstract contract has been provided to allow for new contracts to easily integrate with ComposableCoW.
If using ExtensibleFallbackHandler, and the CoW Protocol settlement domain is delegated to ComposableCoW, ALLERC-1271 signatures will be processed by ComposableCoW.
To simplify the developer experience, a BaseConditionalOrder contract has been provided that implements the IConditionalOrderGenerator interface, and necessary boilerplate.
A swap guard is a contract that implements the ISwapGuard interface, and if set by an owner, will be called by ComposableCoW prior to calling verify on the conditional order.This allows for owner-wide restrictions on the conditional order, such as:
CoW Protocol’s settlement contract enforces single-use orders, i.e. NOGPv2Order can be filled more than once
For merkle trees, H(ConditionalOrderParams)MUST be a member of the merkle tree roots[owner]
For single orders, singleOrders[owner][H(ConditionalOrderParams)] == true
While a discrete order can be filled only once on CoW Protocol, a single conditional order can be used to create many different discrete orders. It is the responsibility of the implementation to limit which and when discrete orders can be executed.
The contract implementing the conditional order logic
salt
Allows for multiple conditional orders of the same type and data
staticData
Data available to ALLdiscrete orders created by the conditional order
All of the above fields are verified by ComposableCoW to be valid, prior to calling the verify method on the handler (IConditionalOrder).
When used with Merkle Trees and a cryptographically-secure random salt, the conditional order is effectively private (until a discrete order cut from this conditional order is broadcast to the CoW Protocol API).
H(ConditionalOrderParams)MUST be unique
Not setting salt to a cryptographically-secure random value MAY result in leaking information or hash collisions
Single orders MAY leak order information on creation
Some services pick up new conditional orders automatically from on-chain events.The proof data can be emitted on-chain, but it can also be retrieved from other supported on-chain services.The location field signals where this data can be retrieved.
Copy
Ask AI
struct Proof { uint256 location; bytes data;}
The Proof.location is intentionally not made an enum to allow for future extensibility as other proof locations may be integrated.
Field
Description
location
An integer representing the location where to find the proofs
data
location implementation specific data for retrieving the proofs
An IValueFactory that will be used to populate the ctx storage slot (if applicable)
data
Data to be passed to the factory to populate the ctx storage slot (if applicable)
When a new merkle root is set, emits MerkleRootSet(address indexed owner, bytes32 root, Proof proof).
ComposableCoW will NOT verify the proof data passed in via the proof parameter for setRoot. It is the responsibility of the client and watch-tower to verify / validate this.
Determine if owner is a safe, and provide the SignatureVerifierMuxer appropriate formatting for the ERC-1271 signature submission to CoW Protocol.
If not a safe, format the ERC-1271 signature according to abi.encode(domainSeparator, staticData, offchainData).
Subsequently, ComposableCoW will:
Check that the order is authorized.
Check that the order type supports discrete order generation (i.e. IConditionalOrderGenerator) by using IERC165 (and revert if not, allowing the watch-tower to prune invalid monitored conditional orders).
Call getTradeableOrder on the handler to get the discrete order (GPv2Order.Data).
ProofNotAuthed() - the proof is not authorized (merkle root incorrect)
SingleOrderNotAuthed() - the single order is not authorized
SwapGuardRestricted() - the swap guard did not pass verification
InvalidHandler() - the handler is not a valid conditional order
InvalidFallbackHandler() - the fallback handler is not a valid conditional order
InterfaceNotSupported() - the handler does not support the IConditionalOrder interface
A conditional order developer SHOULD use these error codes to ensure that the conditional order is well-formed and not garbage collected / rate limited by a watch-tower.
OrderNotValid(string) - the staticInput parameters are not valid for the conditional order
PollTryNextBlock(string) - signal to a watch-tower that polling should be attempted again
PollTryAtBlock(uint256 blockNumber, string) - signal to a watch-tower that polling should be attempted again at a specific block number
PollTryAtEpoch(uint256 timestamp, string) - signal to a watch-tower that polling should be attempted again at a specific epoch (unix timestamp)
PollNever(string) - signal to a watch-tower that the conditional order should not be polled again (delete)
As these orders are not automatically indexed by the CoW Protocol, there needs to be some method of relaying them to the Order Book API for inclusion in a batch.This is the responsibility of a watch-tower. CoW Protocol runs a watch-tower that will monitor the ConditionalOrderCreated event, and relay the discrete orders to the Order Book API.There is also a DAppNode package for running a watch-tower.