From 2b6edc7e7d0b19dcfe5c4b17a9329e7207acf1c3 Mon Sep 17 00:00:00 2001 From: mfw78 Date: Thu, 30 Jul 2026 09:48:25 +0000 Subject: [PATCH 1/2] docs: monitoring-service terminology, verdict permanence, deployments manifest --- deployments/README.md | 34 +++++++++++++++++++ deployments/networks.json | 5 +++ docs/architecture.md | 21 ++++++++---- src/BaseConditionalOrder.sol | 2 +- src/interfaces/IConditionalOrder.sol | 12 +++---- src/interfaces/IOrderManifest.sol | 2 +- src/types/twap/libraries/TWAPOrderMathLib.sol | 2 +- 7 files changed, 63 insertions(+), 15 deletions(-) create mode 100644 deployments/README.md create mode 100644 deployments/networks.json diff --git a/deployments/README.md b/deployments/README.md new file mode 100644 index 00000000..2213c23e --- /dev/null +++ b/deployments/README.md @@ -0,0 +1,34 @@ +# Deployments manifest + +`networks.json` is the canonical machine-readable record of framework +deployments, consumed by off-chain monitoring services and indexers as the +single source for where and what to index. + +## Schema + +```jsonc +{ + "version": 1, // manifest schema version + "abiVersion": "2.0.0-dev", // contract ABI version the entries conform to + "networks": { + "": { + "composableCow": "0x…", // registry address + "deployBlock": 12345678, // first block to index from + "topic0": { + "conditionalOrderCreated": "0x…", + "merkleRootSet": "0x…", + "conditionalOrderRemoved": "0x…", + "swapGuardSet": "0x…" + } + } + } +} +``` + +Rules: + +- Entries are appended by the deployment pipeline; hand edits are reviewed + like code. +- `topic0` values are recomputed from the ABI at deploy time and MUST match + the compiled event signatures (consumers guard on these at startup). +- A chain absent from `networks` is not serviced. diff --git a/deployments/networks.json b/deployments/networks.json new file mode 100644 index 00000000..7186d103 --- /dev/null +++ b/deployments/networks.json @@ -0,0 +1,5 @@ +{ + "version": 1, + "abiVersion": "2.0.0-dev", + "networks": {} +} diff --git a/docs/architecture.md b/docs/architecture.md index 92446853..ca618dbd 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -9,7 +9,7 @@ ERC-1271 is the standard for smart contract signature verification, allowing con The architecture separates two distinct execution paths: 1. **Settlement Path** — on-chain verification during trade execution (gas-sensitive). -2. **Polling Path** — off-chain queries by watch-towers (gas-irrelevant). +2. **Polling Path** — off-chain queries by off-chain monitoring service (historically called a watch-tower)s (gas-irrelevant). This separation ensures settlement remains gas-efficient while providing rich metadata for off-chain infrastructure. @@ -17,7 +17,7 @@ This separation ensures settlement remains gas-efficient while providing rich me 1. **Single source of truth**: `generateOrder()` contains all order generation logic. 2. **Lean settlement**: No metadata structs; only constant string errors for debugging. -3. **Rich polling**: Structured results with scheduling hints for watch-towers. +3. **Rich polling**: Structured results with scheduling hints for monitoring services. 4. **Verdict and fill state are orthogonal**: handlers produce a *verdict* (`GeneratorResult`); observed fill state is composed by the registry, never by a handler. 5. **No code duplication**: Polling wraps the same core logic used by settlement. 6. **Handler purity**: handlers are pure functions of their explicit arguments (`owner`, `sender`, `ctx`, `staticInput`, `offchainInput`) and must not branch on `msg.sender`. Off-chain simulation soundness depends on this. @@ -142,7 +142,8 @@ signature emitted iff verdict == POST and the order is postable: - Note that a recorded fill also implies the discrete order was already accepted by the orderbook and settled, so it stays solvable there until `validTo`. Continued filling of the remainder does not depend on a monitoring service re-posting it. The registry is an on-chain view and cannot observe orderbook state, so it reports validity and leaves the posting decision to the consumer rather than asserting that the order is still listed. - Includes scheduling hints (`nextPollTimestamp` and `waitUntil`). - Carries machine-readable reason selectors for debugging; names resolve from the handler ABI. -- If the swap guard restricts the order, the returned verdict is forced to `INVALID` with `reasonCode = SwapGuardRestricted.selector` and no signature is emitted. +- If the swap guard restricts the order, the registry reports it in the `PollResult.restriction` overlay (`SWAP_GUARD`) and no signature is emitted; the generator verdict is never overwritten - a guarded `POST` stays visible. Restriction is owner-reversible and its lifecycle is observable via `SwapGuardSet` (clear = `address(0)`). +- **`INVALID` is uniformly terminal.** With restriction expressed as an overlay, the only producer of `INVALID` is the handler's own `OrderNotValid`; consumers never need a reason-selector branch for control flow. - `checkOrder()` returns the same composed `PollResult` through the same `_poll` helper (including the ERC-165 handler gate), without building the signature. The swap guard is not consulted by `checkOrder`; it is enforced at signature build time and during settlement. ## Error Types @@ -153,12 +154,13 @@ a `bytes4 reasonCode`: the selector of a custom error the handler declares (e.g. part of the handler ABI, so any ABI-aware consumer resolves a reasonCode to a name without a bespoke table, and revert data stays fixed-size: -| Error | Meaning | Watch-tower Action | +| Error | Meaning | Monitoring service Action | |-------|---------|-------------------| | `OrderNotValid(bytes4)` | Permanent failure | Stop polling | | `PollTryNextBlock(bytes4)` | Transient, retry soon | Poll next block | | `PollTryAtTimestamp(uint256, bytes4)` | Wait for time | Schedule at timestamp | | `PollTryAtBlock(uint256, bytes4)` | Wait for block | Schedule at block | +| `PollNeedsOffchainInput(bytes4)` | Needs constructed `offchainInput` | Acquire input (see `docs/discovery.md` on order modules) or park - do not re-poll empty on a schedule | ### Revert Decoding Policy @@ -170,6 +172,7 @@ without a bespoke table, and revert data stays fixed-size: | `PollTryNextBlock(code)` | `TRY_NEXT_BLOCK` | decoded code | | `PollTryAtTimestamp(t, code)` | `WAIT_TIMESTAMP` (`waitUntil = t`) | decoded code | | `PollTryAtBlock(b, code)` | `WAIT_BLOCK` (`waitUntil = b`) | decoded code | +| `PollNeedsOffchainInput(code)` | `NEEDS_INPUT` (acquire input or park - never a timed retry) | decoded code | | `Panic(subcode)` | `TRY_NEXT_BLOCK` | `0x4e487b71` (the `Panic` selector) | | `Error(string)` (bare `require`) | `TRY_NEXT_BLOCK` | `0x08c379a0` (the `Error` selector) | | any other custom error | `TRY_NEXT_BLOCK` | the caught selector | @@ -225,7 +228,7 @@ struct PollResult { ### Verdict Semantics -| Verdict | Meaning | Watch-tower Action | +| Verdict | Meaning | Monitoring service Action | |---------|---------|-------------------| | `POST` | Order ready to post | Submit to CoW Protocol API (if the fill overlay allows) | | `WAIT_TIMESTAMP` | Wait for specific time | Schedule poll at `waitUntil` | @@ -324,7 +327,7 @@ function generateOrder(address owner, address, bytes32 ctx, bytes calldata stati } ``` -A watch-tower enumerates the currently active entries via the manifest and polls once per leg with the leg index as `offchainInput`. The same pattern covers a simplified AMM (index 0 = buy, 1 = sell) and sequenced strategies (the follow-up entry appears in the manifest once the first leg fills). +A monitoring service enumerates the currently active entries via the manifest and polls once per leg with the leg index as `offchainInput`. The same pattern covers a simplified AMM (index 0 = buy, 1 = sell) and sequenced strategies (the follow-up entry appears in the manifest once the first leg fills). ## Order Manifest Interface @@ -528,6 +531,12 @@ This fork introduces significant architectural changes from [cowprotocol/composa | Function added | - | `getNextPollTimestamp()` | | Function added | - | `describeOrder()` | | Function added | - | `tryGenerateOrder()` returning full revert data | +| Error + verdict added | - | `PollNeedsOffchainInput(bytes4)` → `NEEDS_INPUT` | +| Struct changed | `Proof { uint256 location; bytes data }` | `Proof { string[] uris; bytes32[] blobVersionedHashes }` (URI mirrors + in-tx blob verification; `MerkleRootSet` topic0 rotates) | +| Struct changed | `PollResult { generator, fill, filledAmount }` | + `Restriction restriction` overlay (`INVALID` uniformly terminal) | +| Behavior | `setRoot(0, proof)` unvalidated | zero root requires an empty proof (`ProofDataMalformed`) | +| Interfaces added | - | `IOrderDescriptor`, `IOrderModule` sidecars (own ERC-165 ids; advertised only when committed) | +| Constructors changed | order types take no descriptor args | order types take `(string[] descriptorUris, bytes32 descriptorDigest)` | The ERC-165 interface id of `IConditionalOrderGenerator` therefore changes: handlers deployed against the upstream interface will not satisfy the new id and are rejected by the polling path of a registry compiled against this fork. diff --git a/src/BaseConditionalOrder.sol b/src/BaseConditionalOrder.sol index 3322e670..7f09e76c 100644 --- a/src/BaseConditionalOrder.sol +++ b/src/BaseConditionalOrder.sol @@ -14,7 +14,7 @@ error InvalidHash(); /** * @title Base logic for conditional orders. * @dev Provides the dual-path plumbing: a lean `verify` for the settlement path and a - * structured, non-reverting `poll` for watch-towers, both derived from one + * structured, non-reverting `poll` for monitoring services, both derived from one * `generateOrder` implementation. * @author mfw78 */ diff --git a/src/interfaces/IConditionalOrder.sol b/src/interfaces/IConditionalOrder.sol index 077a4999..2df6f139 100644 --- a/src/interfaces/IConditionalOrder.sol +++ b/src/interfaces/IConditionalOrder.sol @@ -39,7 +39,7 @@ interface IConditionalOrder { * **MUST** revert if the order condition is not met. * @dev The `order` parameter is ignored / not validated by the `IConditionalOrderGenerator` implementation. * This parameter is included to allow more granular control over the order verification logic, and to - * allow a watch tower / user to propose a discrete order without it being generated by on-chain logic. + * allow a monitoring service / user to propose a discrete order without it being generated by on-chain logic. * @param owner the contract who is the owner of the order * @param sender the `msg.sender` of the transaction * @param _hash the hash of the order @@ -64,7 +64,7 @@ interface IConditionalOrder { /** * @title Conditional Order Generator Interface * @author mfw78 - * @notice Adds structured, non-reverting polling for watch-tower integration. + * @notice Adds structured, non-reverting polling for monitoring service integration. * @dev The generator surface carries the handler's *verdict* only. Observed fill * state is orthogonal and is composed by the registry (`ComposableCow`), * never by a handler. @@ -82,11 +82,11 @@ interface IConditionalOrderGenerator is IConditionalOrder, IERC165 { ); // --- errors specific for polling - // Signal to a watch tower that polling should be attempted again. + // Signal to a monitoring service that polling should be attempted again. error PollTryNextBlock(bytes4 reasonCode); - // Signal to a watch tower that polling should be attempted again at a specific block number. + // Signal to a monitoring service that polling should be attempted again at a specific block number. error PollTryAtBlock(uint256 blockNumber, bytes4 reasonCode); - // Signal to a watch tower that polling should be attempted again at a specific timestamp. + // Signal to a monitoring service that polling should be attempted again at a specific timestamp. error PollTryAtTimestamp(uint256 timestamp, bytes4 reasonCode); /** * @dev Generation requires non-empty `offchainInput` that the caller did not @@ -155,7 +155,7 @@ interface IConditionalOrderGenerator is IConditionalOrder, IERC165 { /** * Poll for a discrete order with scheduling metadata. - * @dev Called by watch-towers. **MUST NOT** revert for order conditions: the + * @dev Called by monitoring services. **MUST NOT** revert for order conditions: the * conditional-order errors raised by `generateOrder` are decoded into the * returned verdict instead. * @param owner the contract who is the owner of the order diff --git a/src/interfaces/IOrderManifest.sol b/src/interfaces/IOrderManifest.sol index 8948139b..b1fb8dac 100644 --- a/src/interfaces/IOrderManifest.sol +++ b/src/interfaces/IOrderManifest.sol @@ -73,7 +73,7 @@ interface IOrderManifest { * @param owner The owner of the conditional order * @param ctx Context key (bytes32(0) for merkle, hash(params) for single) * @param staticInput The static input parameters for the conditional order - * @param offchainInput Dynamic parameters from watch-tower (may be empty) + * @param offchainInput Dynamic parameters from monitoring service (may be empty) * @param offset Starting index for pagination * @param limit Maximum number of entries to return * @return entries Array of manifest entries diff --git a/src/types/twap/libraries/TWAPOrderMathLib.sol b/src/types/twap/libraries/TWAPOrderMathLib.sol index b796211d..90b3601b 100644 --- a/src/types/twap/libraries/TWAPOrderMathLib.sol +++ b/src/types/twap/libraries/TWAPOrderMathLib.sol @@ -43,7 +43,7 @@ library TWAPOrderMathLib { unchecked { /// @dev Order is not yet valid before the start (order commences at `t0`); - /// a watch tower should try again at the start time. + /// a monitoring service should try again at the start time. require( startTime <= block.timestamp, IConditionalOrderGenerator.PollTryAtTimestamp(startTime, BeforeTwapStart.selector) From fc5a177b7b423f1c58271ecdb2c19487232cb3e3 Mon Sep 17 00:00:00 2001 From: mfw78 Date: Fri, 31 Jul 2026 06:40:43 +0000 Subject: [PATCH 2/2] docs: repair two watch-tower renames in the architecture doc The terminology pass left a substitution artifact and a missed label: - the execution-paths list read "off-chain queries by off-chain monitoring service (historically called a watch-tower)s", where the trailing plural of the old term survived outside the parenthesis and "off-chain" was doubled; - the polling-path diagram still labelled its actor `Watch-Tower`. The glossary note in `docs/discovery.md` is deliberate and stays: it introduces the term precisely so the historical name remains searchable. --- docs/architecture.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index ca618dbd..82d2ef95 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -9,7 +9,7 @@ ERC-1271 is the standard for smart contract signature verification, allowing con The architecture separates two distinct execution paths: 1. **Settlement Path** — on-chain verification during trade execution (gas-sensitive). -2. **Polling Path** — off-chain queries by off-chain monitoring service (historically called a watch-tower)s (gas-irrelevant). +2. **Polling Path** — off-chain queries by monitoring services (historically called watch-towers) (gas-irrelevant). This separation ensures settlement remains gas-efficient while providing rich metadata for off-chain infrastructure. @@ -99,7 +99,7 @@ ComposableCow.isValidSafeSignature(...) Signature validation ### Polling Path (Off-Chain) ``` -Watch-Tower +Monitoring Service │ ▼ ComposableCow.getTradeableOrderWithSignature(...)