# `ComposableCoW` Architecture The following principles have been employed in the architectural design: 1. `O(1)` efficiency for `n` order creation / replacement / deletion. 2. Conditional orders **SHOULD** behave the same as a discrete order for EOAs (self-custody of assets, no need to create "container" contracts). 3. Orders are _stateless_, with any required data passed via `calldata`. 4. Make CoW a first class citizen of `Safe` and vice-versa 🐮🔒. ## Assumptions - CoW Protocol enforces single-use orders, ie. no `GPv2Order` can be filled more than once. ## Definitions *Conditional Order*: A logical construct representing `0..n` discrete orders. *Discrete Order*: An order submitted to the CoW Protocol API (ie. `GPv2Order.Data`), ie. a **single** `orderUid` as defined by CoW Protocol. For the purposes of this documentation, if the _type_ of order is not specified, it shall be assumed to be a _conditional order_. ## Use cases Currently a _smart contract_ interacting with CoW is required to either: 1. Call `GPv2Settlement.setPreSignature(orderUid)` (API signing type "pre-sign"); or 2. Implement `isValidSignature(bytes32,bytes)` (API signing type "eip1271"), where the `bytes32` parameter passed is the EIP-712 digest of the `GPv2Order.Data`. Presently orders have been spawning new contracts on chain, necessitating the handling of basic `retrieve` / `cancel` functionality to recover assets. `Safe` already provides a best-in-class for this purpose, so let's not reinvent the wheel! 🛞 Use cases that this revised architecture seeks to enable include: * Automatically swap token ABC for XYZ above a defined threshold balance of ABC. * Good after time (`GAT`) orders (discrete orders, conditional on a starting time). * TWAP by breaking orders into `n x GAT` orders. * Private conditional orders (trailing stop loss) * Wait4CoW orders (only matching an order with other CoW traders) * Doing all the above simultaneously (`n x conditional orders`) ## Current Architecture > The architectural analysis is from the perspective of using `Safe` with CoW Protocol. All CoW Protocol discrete orders are "signed". The signing methods are: * EIP-712 (gasless) * Eth-Sign (gasless) * EIP-1271 (gasless) * Pre-Sign (tx) ```mermaid flowchart TD A[GPv2Settlement] -->|EOA Only| B[EIP-712] A -->|EOA Only| C[Eth-Sign] A -->|Contract Only| D[EIP-1271] A -->|Contract & EOA| E[Pre-Sign] ``` Signing via `Safe` is done via `approvedHash` or `threshold` signatures, as follows: ```mermaid flowchart TD A[Safe] -->|delegatecall SigningLib| B[approvedHash - tx] A -->|manual assembly of signatures| C[threshold - gasless] ``` Therefore, when using `Safe`: 1. **EACH** discrete order (ie. `orderUid`) must be approved/signed individually by the `Safe`. 2. **ONLY** `threshold` type EIP-1271 signatures are gasless from a CoW Protocol and `Safe` perspective. The result is a significantly constrained user experience. ## `ComposableCoW` This contract implements `ISafeSignatureVerifier`, designed to be used with `ExtensibleFallbackHandler` - [a new `FallbackHandler` for `Safe`](https://hackmd.io/-nLuF3JIRyuS5w864_mbrg). `ExtensibleFallbackHandler` provides a third method of EIP-1271 validation for `safe` - it allows delegating an EIP-712 domain to a **custom contract**. `ComposableCoW` therefore will process **ALL** EIP-1271 signatures for the `GPv2Settlement.domainSeparator()` EIP-712 domain. Therefore, `ComposableCoW` is responsible for: 1. Discrete order verification routing of `n` conditional orders per `owner`. 2. Lookup discrete orders from a conditional order (watch-towers). ### Order Data Each conditional order has the following properties/data available at **settlement** depending on implementation: 1. `handler` - the contract that will verify the conditional order's parameters. 2. `salt` - allows for multiple conditional orders of the same type and data. 3. `staticData` - data available to **ALL** *discrete* orders created by the conditional order. 4. `offchainData` - data *optionally* provided from off-chain to a *discrete* order. As all of these, excluding `offchainData` are known at creation time, they are grouped together in the struct `ConditionalOrderParams`: ```solidity struct ConditionalOrderParams { IConditionalOrder handler; bytes32 salt; bytes staticData; } ``` `ConditionalOrderParams` has the properties: 1. `H(ConditionalOrderParams)` **MUST** be unique. 2. `salt` **SHOULD** be set to a cryptographically-secure random value to ensure (1) **WHEN REQUIRED**. 3. Provides order secrecy (until a discrete order cut from this conditional order is broadcast to the CoW Protocol API). 4. All values are **verified by `ComposableCoW`** prior to calling an order type's `verify`. `offchainInput` has the properties: 1. Allows input (such as from an off-chain oracle) that is **NOT** known at order creation time. 2. **NOT** verified by `ComposableCoW`. Validation is the responsibility of the handler. **WARNING: Order implementations MUST validate / verify `offchainInput`!** ### Storage ```solidity mapping (address => bytes32) roots; // For Merkle roots (n conditional orders) mapping (address => mapping (bytes32 => bool)) singleOrders; // Per conditional order (if not in Merkle root) ``` ### Settlement Execution Path CoW Protocol order settlement execution path (assuming `safe`): ```mermaid flowchart TD A[GPv2Settlement] -->|call: isValidSignature| B[SafeProxy] B -->|delegatecall: isValidSignature| C[SafeSingleton : FallbackManager] C -->|call: isValidSignature| D[ExtensibleFallbackHandler : SignatureVerifierMuxer] D -->|call: isValidSafeSignature| E[ComposableCoW] E -->|call: verify| F[IConditionalOrder] ``` Implementing the `ISafeSignatureVerifier` means that `ComposableCoW` will implement `isValidSafeSignature`: ```solidity function isValidSafeSignature( Safe safe, address sender, bytes32 _hash, bytes32 domainSeparator, bytes32, // typeHash bytes calldata encodeData, bytes calldata payload ) external view override returns (bytes4 magic); ``` 1. `encodeData`: The ABI-encoded `GPv2Order.Data` for the order being validated **during a settlement**. 2. `payload`: `abi.encode(bytes32[] proof, ConditionalOrderParams params, bytes offchainInput)` 3. `typeHash` is ignored as CoW Protocol only has one `typeHash` (`GPv2Order.Data`). ### Settlement Validity The following security constraints are enforced by `ComposableCoW`: 1. `ConditionalOrderParams` **MUST** be authorised for use by the `owner`. ```mermaid flowchart TD A[Extensible Fallback Handler: SignatureVerifierMuxer] -->|isValidSafeSignature| B[Check Authorisation: MerkleRoot \n Proof & ConditionalOrderParams] B -->|valid| V[IConditionalOrder:verify] B -->|invalid| C(Check Authorisation: Single Order \n ConditionalOrderParams) C -->|valid| V C -->|invalid| I[Revert] V -->|valid| T[Return ERC1271 Magic] V -->|invalid| I ``` #### Merkle Root **Leaf**: `H(ConditionalOrderParams)`, ie. `H(handler || salt || data)` **Properties**: * Achieves `O(1)` gas efficiency for add / remove `n` conditional orders. **Methodology**: The caller passes `abi.encode(bytes32[] proof, ConditionalOrderParams params)` as the `payload` parameter where `proof` contains the Merkle Tree proof. **Authorisation**: Order **O** is valid if `proof` asserts that leaf `H(params)` is a member of merkle tree `roots[owner]`. #### Single Order **Properties**: * Simpler method for enabling an order (less tooling overhead). * Gas expensive (`n` x `SSTORE` per order). **Methodology**: The caller passes `abi.encode(bytes32[] proof, ConditionalOrderParams params, bytes offchainInput)` as the `payload` parameter where `proof` is a zero-length `bytes32[]`. **Authorisation**: Order **O** is valid if `singleOrders(owner, H(params)) == true`. ### Add / Remove Orders The `owner` uses the applicable setter method depending on how they wish to specify the order. #### Merkle Root The `owner` calls the `setRoot(bytes32 root, Proof calldata proof)` setter method. ```solidity struct Proof { uint256 location; bytes data; } ``` * `root`: Merkle Tree of conditional orders (leaves = `H(params)`). * `proof`: Where watch towers may locate the proofs from. **Private:** `Proof({location: 0, data: bytes("")})` - no proofs available to watch-towers. **Log:** `Proof.location = 1` and `Proof.data = abi.encode(bytes[] order)` where `order = abi.encode(bytes32[] proof, ConditionalOrderParams params)` When a new merkle root is set, emits `MerkleRootSet(address indexed owner, bytes32 root, Proof proof)`. **NOTE**: `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. **NOTE**: The `Proof.location` is intentionally not made an `enum` to allow for future extensibility as other proof locations may be integrated, including but not limited to Swarm, Waku, IPFS etc. #### Single Order The `owner` calls the respective setter method: * Create orders: `create(ConditionalOrderParams params, bool dispatch)` If the `owner` wants to make the order public, `dispatch` is set `true`, and the order creation will result in an emission of `ConditionalOrderCreated(address indexed owner, ConditionalOrderParams params)`. * Removing order: `remove(bytes32 orderHash)` To remove an order, set `orderhash = H(params)` from the initial `ConditionalOrderCreated`. No event is emitted, as the order is invalidated (any watch tower trying to cut discrete orders will see the `getTradeableOrder()` will revert). ### Discrete Order Generation The default is to use a `Factory` base pattern for conditional orders, where based on an order's properties, it is able to generate a tradeable order (`GPv2Order.Data`). To do this, an order implements the `IConditionalOrderFactory` interface. Following this pattern, a developer **MUST** ensure: `H(IConditionalOrderFactory.getTradeableOrder(owner,sender,params,offchainInput)) == _hash` Where `_hash` is the `_hash` passed into the `isValidSignature()` call. #### Advanced conditional orders These implement the top-level `IConditionalOrder` interface, which only provides a raw `verify` method, and doens't allow for order generation. ### Watch Towers As these orders are not automatically indexed by the CoW Protocol API, there needs to be some method of relaying them to CoW for inclusion in a batch. This is done by reference to the events emitted by `ComposableCoW`: * `ConditionalOrderCreated(address indexed owner, ConditionalOrderParams params)` * `MerkleRootSet(address index owner, bytes32 root, Proof proof)` The contract that _emits_ the above methods shall provide a method: ```solidity function getTradeableOrderWithSignature( address owner, bytes32[] proof, ConditionalOrder params, bytes offchainInput ) external view (GPv2Order.Data memory, bytes memory signature); ``` In the context of `ComposableCoW` this will: 1. Determine if `owner` is a `safe`, and provide the `SignatureVerifierMuxer` appropriate formatting for the EIP-1271 `signature` submission to CoW Protocol. 2. If not a `safe`, format the EIP-1271 `signature` according to `abi.encode(domainSeparator, staticData, offchainData)`. `ComposableCoW` will: 1. Check that the order is authorised. 2. Check that the order type supports discrete order generation (ie. `IConditionalOrderFactory`) by using `IERC165` (and `revert` if not, allowing the watch-tower to prune invalid monitored conditional orders). 3. Call `getTradeableOrder` on the handler to get the `GPv2Order.Data` 4. Generate the signing data as above. **NOTE:** There is no need to emit these events when orders are cancelled / removed as calling the `getTradeableOrderWithSignature` will yield a `revert` with a custom `error` indicating the order will never be valid with the current state. ### Interfaces #### `IConditionalOrder` This is the root-level interface for conditional orders, implementing: ```solidity function verify( address owner, address sender, bytes32 hash, bytes32 domainSeparator, bytes calldata staticInput, bytes calldata offchainInput GPv2Order.Data calldata order, ) external view; ``` **CAUTION:** The `verify` method **MUST** `revert` if the parameters specified do not correspond to a valid order. #### `IConditionalOrderFactory` Allows for generation of discrete orders for submission to CoW Protocol by implementing `ERC165` and: ```solidity function getTradeableOrder( address owner, address sender, bytes calldata staticInput, bytes calldata offchainInput ) external view returns (GPv2Order.Data memory); ```