fix(twap-monitor): skip submit_order when submitted:{uid} already in store (COW-1085)#64
Closed
brunota20 wants to merge 1 commit into
Closed
Conversation
…store (COW-1085)
`getTradeableOrderWithSignature` returns the same Ready tuple in every
poll-tick during a TWAP child's validity window — the on-chain
conditional order has no way to know shepherd already POSTed it. The
strategy already wrote a `submitted:{uid}` marker after a successful
submit, but the next poll-tick polled the chain and submitted again,
producing a wasted orderbook call and a misleading
`DuplicatedOrder` Warn line in every soak that runs a TWAP.
Live evidence (2026-06-23 Sepolia soak):
10:02:36.784 INFO poll watch:0x8fab71c0...:0x93b1626c... -> Ready
10:02:37.190 INFO submitted submitted:0xd7116bd2...
10:02:48.870 INFO poll watch:0x8fab71c0...:0x93b1626c... -> Ready
10:02:49.855 WARN submit dropped watch (400): orderbook error
(DuplicatedOrder): order already exists
The first submission succeeded (`GET /api/v1/orders/0xd7116bd2...`
returns `status: fulfilled`); the second was wasted work.
The fix: at the top of `submit_ready`, compute the client-side UID
deterministically from the on-chain `(order, owner, chain)` tuple via
`OrderData::uid` and check `submitted:{uid}` in local-store; skip the
submit (and the appData resolve that precedes it) when the marker
exists. The marker write site is also updated to use the
client-computed UID for the key so the read and write paths agree
(in production the server-returned UID is the same value — both sides
derive it from the canonical `digest || owner || valid_to` layout —
and a divergence is now surfaced via a Warn).
Tests (24, all green natively + wasm32-wasip2):
* Existing `poll_ready_submits_order_and_persists_submitted_uid` and
`poll_ready_resolves_non_empty_app_data_then_submits` updated to
compute the expected marker key via `compute_uid_hex` instead of
hardcoding `submitted:0xfeedface` (the mock orderbook's stub UID,
which now triggers the divergence Warn so we also assert that).
* New `poll_ready_skips_submit_when_submitted_uid_already_in_store`:
seeds the marker, dispatches a block tick, asserts
`submit_order` (and the preceding appData resolve) are NOT called
and that the expected Info log appears.
Out of scope (deferred): the same idempotency pattern could be
applied to ethflow-watcher's `observed:{uid}` marker (already correct
there — the GET-not-POST design makes this naturally idempotent).
Author
Live validation note (2026-06-23 14:00 UTC)Combined with the rest of the redesign + fix bundle on Companion PRs (same redesign / soak deploy bundle)
Each is single-concern; the deploy branch is the cumulative integration point and will fold into |
This was referenced Jun 23, 2026
Closed
Author
|
Superseded by deploy branch commit |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this PR does
Short-circuit
submit_readywhensubmitted:{uid}is already in local-store, so a TWAP child that succeeded on a prior poll-tick does not get re-submitted on the next block. Fixes theDuplicatedOrderWarn observed in the 2026-06-23 Sepolia soak (see COW-1085 for the live evidence).Why
composable.getTradeableOrderWithSignaturereturnsReadywhenever the conditional order has a valid (sell, buy) tuple for the current block. It does not know shepherd has already POSTed the resulting child to the orderbook. The strategy already wrotesubmitted:{uid}after a successful submit, but the next poll-tick polled the chain and submitted again, producing a wasted orderbook call and a misleading Warn.The Warn dispatch itself is correct (Drop, no retry storm), so the consequence is bounded — but on a 7-day soak (COW-1031) with N TWAPs × K children, this becomes N×K spurious Warn lines that operators have to learn to ignore.
Change
compute_uid_hex(chain_id, order, owner)helper — computes the canonical 56-byte UID deterministically from on-chain inputs viaOrderData::uid(domain, owner). Mirrors what the orderbook derives server-side from the signed payload.submit_ready— looks upsubmitted:{uid}and short-circuits with an Info log before any network work (no appData GET, nosubmit_order).twap UID divergence: client=… server=…).Tests (24 total, all green)
poll_ready_submits_order_and_persists_submitted_uidandpoll_ready_resolves_non_empty_app_data_then_submits: previously hardcodedsubmitted:0xfeedfaceagainst the MockHost's stub UID; now compute the expected key viacompute_uid_hex(SEPOLIA, &ready_order, owner). Both also assert the divergence Warn fires (since0xfeedfaceis not the canonical UID).poll_ready_skips_submit_when_submitted_uid_already_in_store: seeds the marker, dispatches a block, assertschain.call_count() == 1(poll still happens),cow_api.call_count() == 0(no submit),cow_api.request_calls().len() == 0(no appData GET), and the Info log line appears verbatim.cargo clippy -p twap-monitor --all-targets -- -D warningsclean.cargo fmt --checkclean.cargo build -p twap-monitor --target wasm32-wasip2 --releaseclean.Live validation
The fix is not yet on the soak VM; deploy after review. Expected post-deploy behaviour:
submitted {uid}Info line.twap {uid} already submitted; skipping poll re-submitInfo line and no orderbook calls.DuplicatedOrderWarn lines for the lifecycle-correct path.Linear
Resolves COW-1085.
AI assistance disclosure: drafted by Claude (Opus 4.7, 1M context) following the 2026-06-23 soak inspection.