Problem
Both keepers build their entire state from logs, and an event trigger opens at head when no cursor is stored.
A fresh daemon is therefore permanently blind to anything that happened before it first ran. A ComposableCoW conditional order is registered by a ConditionalOrderCreated log and can stay live for weeks, so a TWAP created last month and halfway through execution is never polled and never submitted. No later block can recover it.
resume = true (landed in shepherd#675) fixes the restart case by persisting a cursor. It does not help the first boot, which has no cursor to resume from.
Blocked on
nullislabs/nexum-runtime#381 adds an optional start_block to the event trigger. It seeds the cursor only while no stored cursor exists, so it backfills once and then yields to the store forever.
Do not set start_block in a manifest before the pin bump. CoreTrigger does not use deny_unknown_fields, so the currently pinned runtime (fd16db7) parses the key and silently ignores it. The manifest would look configured while doing nothing.
Deployment blocks, resolved from the deploy transactions
ComposableCoW is the same CREATE2 address on every chain, 0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74, but the deployment block differs per chain.
| Contract |
Chain |
Address |
Block |
Date |
| ComposableCoW |
mainnet (1) |
0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74 |
17883049 |
2023-08-10 |
| ComposableCoW |
Sepolia (11155111) |
0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74 |
5072748 |
2024-01-12 |
| CoWSwapEthFlow |
Sepolia (11155111) |
0xbA3cB449bD2B4ADddBc894D8697F5170800EAdeC |
7541028 |
2025-01-21 |
Sources: cowprotocol/composable-cow networks.json for the ComposableCoW deploy transactions, and the contract creation transaction for the EthFlow deployment. Each block number is the block of the recorded deployment transaction.
EthFlow has had multiple per-network and per-version deployments, unlike ComposableCoW's single CREATE2 address, so the mainnet EthFlow address and its block must be resolved separately rather than assumed to carry.
Work
- Bump the
nexum-runtime pin in shepherd from fd16db7 to the merge commit of nexum-runtime#381. This must happen in lock-step with the same bump in videre, because both repos pin the same rev and a split pin resolves two copies of nexum-runtime whose Extension seam types do not match.
- Set
start_block on all three event triggers in modules/twap-monitor/component.toml and modules/ethflow-watcher/component.toml, using the table above.
- Revisit the numbers as part of shepherd#657 when the chain moves to mainnet, since both the chain id and the EthFlow address change.
Cost of the first backfill
Mainnet ComposableCoW from 17883049 is roughly 5.4M blocks. The bulk phase chunks at the operator-declared log range and commits a durable frontier per chunk, so the scan resumes at the last completed chunk if interrupted rather than restarting. max_lookback stays unset, because a cap drops the oldest missed blocks, which is the loss this fixes.
AI Assistance: Claude Code used for tracing the cursor-seeding path and resolving the deployment blocks.
Problem
Both keepers build their entire state from logs, and an event trigger opens at head when no cursor is stored.
A fresh daemon is therefore permanently blind to anything that happened before it first ran. A ComposableCoW conditional order is registered by a
ConditionalOrderCreatedlog and can stay live for weeks, so a TWAP created last month and halfway through execution is never polled and never submitted. No later block can recover it.resume = true(landed in shepherd#675) fixes the restart case by persisting a cursor. It does not help the first boot, which has no cursor to resume from.Blocked on
nullislabs/nexum-runtime#381 adds an optional
start_blockto the event trigger. It seeds the cursor only while no stored cursor exists, so it backfills once and then yields to the store forever.Do not set
start_blockin a manifest before the pin bump.CoreTriggerdoes not usedeny_unknown_fields, so the currently pinned runtime (fd16db7) parses the key and silently ignores it. The manifest would look configured while doing nothing.Deployment blocks, resolved from the deploy transactions
ComposableCoW is the same CREATE2 address on every chain,
0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74, but the deployment block differs per chain.0xfdaFc9d1902f4e0b84f65F49f244b32b31013b740xfdaFc9d1902f4e0b84f65F49f244b32b31013b740xbA3cB449bD2B4ADddBc894D8697F5170800EAdeCSources:
cowprotocol/composable-cownetworks.jsonfor the ComposableCoW deploy transactions, and the contract creation transaction for the EthFlow deployment. Each block number is the block of the recorded deployment transaction.EthFlow has had multiple per-network and per-version deployments, unlike ComposableCoW's single CREATE2 address, so the mainnet EthFlow address and its block must be resolved separately rather than assumed to carry.
Work
nexum-runtimepin in shepherd fromfd16db7to the merge commit of nexum-runtime#381. This must happen in lock-step with the same bump in videre, because both repos pin the same rev and a split pin resolves two copies ofnexum-runtimewhoseExtensionseam types do not match.start_blockon all three event triggers inmodules/twap-monitor/component.tomlandmodules/ethflow-watcher/component.toml, using the table above.Cost of the first backfill
Mainnet ComposableCoW from 17883049 is roughly 5.4M blocks. The bulk phase chunks at the operator-declared log range and commits a durable frontier per chunk, so the scan resumes at the last completed chunk if interrupted rather than restarting.
max_lookbackstays unset, because a cap drops the oldest missed blocks, which is the loss this fixes.AI Assistance: Claude Code used for tracing the cursor-seeding path and resolving the deployment blocks.