Skip to main content
  1. Can be used to generate multiple discrete order (self-expressing)
  2. Assess a proposed order against a set of conditions (self-validating)
The framework makes boilerplate code for programmatic orders a thing of the past, and allows developers to focus on the business logic of their order. ComposableCoW handles:
  1. Authorization (multiple owners, with multiple orders per owner)
  2. Order relaying (watch-towers)

Architecture

The following principles have been employed in the architectural design:
  1. O(1) gas-efficiency for n programmatic order creation / replacement / deletion
  2. Programmatic orders SHOULD behave the same as a discrete order for EOAs (self-custody of assets, i.e. “wrapper” contracts not required)
  3. Programmatic orders SHOULD be optimized towards statelessness - pass required data via calldata
  4. MAY enhance the Safe user experience when paired with ExtensibleFallbackHandler 🐮🔒
By using Merkle Trees, the gas efficiency of O(1) is achieved for n programmatic 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 programmatic orders. For simplicity, single orders are also supported, however, this is NOT recommended for large n as the gas efficiency is O(n).

Execution context

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:
Having this context also allows for programmatic orders / merkle roots to use this as a key in a mapping, to store programmatic order-specific data.

Programmatic order verification flow

The following flowchart illustrates the programmatic order verification flow (assuming safe):

Settlement execution path

CoW Protocol order settlement execution path (assuming safe):

Signature verification

ComposableCoW implements ISafeSignatureVerifier, which allows for delegated ERC-1271 signature validation with an enhanced context:
In order to delegate signature verification to ComposableCoW, the delegating contract may either:
  1. Be a Safe and use ExtensibleFallbackHandler that allows for EIP-712 domain delegation to a custom contract (i.e. ComposableCoW); or
  2. 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, ALL ERC-1271 signatures will be processed by ComposableCoW.

Discrete order verifiers

A programmatic order that verifies a proposed discrete order against a set of conditions shall implement the IConditionalOrder interface.
Order implementations MUST validate / verify offchainInput!
The verify method MUST revert with OrderNotValid(string) if the parameters in staticInput do not correspond to a valid order.
ComposableCoW is responsible for checking that all values EXCLUDING offchainInput belong to an order that was previously registered on-chain.

Discrete order generators

A programmatic order that generates discrete orders shall implement the IConditionalOrderGenerator interface.
To simplify the developer experience, a BaseConditionalOrder contract has been provided that implements the IConditionalOrderGenerator interface, and necessary boilerplate.

Swap guards

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 programmatic order. This allows for owner-wide restrictions on the programmatic order, such as: The ISwapGuard interface is as follows:

Guarantees and Invariants

  • CoW Protocol’s settlement contract enforces single-use orders, i.e. NO GPv2Order 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 programmatic 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.

Data Types and Storage

ConditionalOrderParams

A programmatic order is defined by the following data:
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 programmatic order is effectively private (until a discrete order cut from this programmatic 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

PayloadStruct

This is the data passed to ComposableCoW via the payload parameter of isValidSafeSignature:
By setting proof to zero-length, this indicates to ComposableCoW that the order is a single order, and not part of a Merkle Tree.

Proof

Some services pick up new programmatic 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.
The Proof.location is intentionally not made an enum to allow for future extensibility as other proof locations may be integrated.

Locations

Locations above are for the point of defining a standard. The provided watch-tower currently does not support Merkle Tree proofs for orders.
It is expected that the proofs retrieved, excluding PRIVATE and LOG conform to a JSON schema:

roots

Using an owner as a key, the roots mapping stores the Merkle Tree root for the programmatic orders of that owner.

singleOrders

Using owner, ctx as a key, the singleOrders mapping stores the single orders for the programmatic orders of that owner.

cabinet

Using owner, ctx as a key, the cabinet mapping stores the programmatic order-specific data for the programmatic orders of that owner.

swapGuards

Using owner as a key, the swapGuards mapping stores the swap guards for the programmatic orders of that owner.

Functions

For users

setRoot / setRootWithContext

A safe or owner calls the respective setter method to set the Merkle Tree root for their programmatic orders:
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.

create / createWithContext

The owner calls the respective setter method to create a programmatic order:

remove

The owner calls the remove(bytes32 singleOrderHash) method to remove a programmatic order:

setSwapGuard

The owner calls the setSwapGuard(ISwapGuard guard) method to set a swap guard for a programmatic order:

For watch-towers

getTradeableOrderWithSignature

A watch-tower calls the getTradeableOrderWithSignature method to get a discrete order that is tradeable on CoW Protocol:
This function will:
  1. Determine if owner is a safe, and provide the SignatureVerifierMuxer appropriate formatting for the ERC-1271 signature submission to CoW Protocol.
  2. If not a safe, format the ERC-1271 signature according to abi.encode(domainSeparator, staticData, offchainData).
Subsequently, ComposableCoW will:
  1. Check that the order is authorized.
  2. 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 programmatic orders).
  3. Call getTradeableOrder on the handler to get the discrete order (GPv2Order.Data).
  4. Generate the signing data as above.

Indexing

  • ConditionalOrderCreated(address indexed owner, ConditionalOrderParams params)
  • MerkleRootSet(address index owner, bytes32 root, Proof proof)

Custom error codes

  • 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 programmatic order
  • InvalidFallbackHandler() - the fallback handler is not a valid programmatic order
  • InterfaceNotSupported() - the handler does not support the IConditionalOrder interface
A programmatic order developer SHOULD use these error codes to ensure that the programmatic order is well-formed and not garbage collected / rate limited by a watch-tower.
  • OrderNotValid(string) - the staticInput parameters are not valid for the programmatic 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 programmatic order should not be polled again (delete)

Off-chain

Watch-tower

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.
Last modified on March 12, 2026