Skip to content

fix(scripts): derive TWAP calldata with t0=now-60 (COW-1077) - #50

Closed
brunota20 wants to merge 1 commit into
feat/ethflow-expected-excessive-validto-cow-1076from
fix/twap-calldata-helper-cow-1077
Closed

fix(scripts): derive TWAP calldata with t0=now-60 (COW-1077)#50
brunota20 wants to merge 1 commit into
feat/ethflow-expected-excessive-validto-cow-1076from
fix/twap-calldata-helper-cow-1077

Conversation

@brunota20

Copy link
Copy Markdown

Summary

scripts/e2e-onchain.sh pinned a 516-byte hex blob with t0 = 0 in the ComposableCoW.create() static-input tuple. TWAP handler's validateData does NOT reject t0 = 0 (only checks t0 >= type(uint32).max), so the create() tx succeeded - but TWAPOrderMathLib.calculateValidTo then computes part = (block.timestamp - 0) / t = ~3.3M, far above the configured n = 2, triggering AFTER_TWAP_FINISHED on every getTradeableOrderWithSignature poll. The order was dead at submission.

Surfaced in the COW-1064 dry-run (2026-06-18 report §6.4): supervisor logged 0xc8fc2725 ... after twap finished per block.

Change

  • scripts/_twap_calldata.py (new) - encodes the calldata fresh on every invocation with t0 = int(time.time()) - 60 (backdated 60 s so part 0 is Ready as soon as the order is on-chain). Module docstring explicitly warns against re-hardcoding t0.
  • scripts/e2e-onchain.sh - Action 1 now shells out to the helper rather than carrying the hex inline. Validates the output is hex-shaped before passing to cast send.
  • docs/operations/e2e-cow-1064-prep.md section 2.3 step 3 - replaces the pinned blob with a python3 scripts/_twap_calldata.py recipe and a historical note pointing at COW-1077.
  • docs/operations/e2e-cow-1064-prep.md section 4.2 recipe - gets import time + int(time.time()) - 60 for t0 so the manual re-derivation flow cannot re-introduce the bug.
  • scripts/README.md Action 1 description updated to mention the helper.

Constants in the helper (sell/buy tokens, amounts, n, t, salt) mirror the prep doc's section 4.2; both must change in lockstep if the TWAP shape is retargeted - the helper docstring calls this out.

Validation

Check Result
python3 scripts/_twap_calldata.py produces 516-byte calldata (1034 hex chars) yes
Starts with the correct selector 0x6bfae1ca (create((address,bytes32,bytes),bool)) yes
The t0 word reflects current epoch (verified against 0x00...006a3537b5 on smoke run) yes
bash -n scripts/e2e-onchain.sh parses cleanly yes

No engine-side changes; this is a script-and-docs PR. No new tests because the helper is a standalone, deterministic encoder - the calldata's correctness is structurally guarded by the next E2E run: if t0 is wrong, the TWAP order never reaches Ready and the dry-run report flags it.

Stack

fix/twap-calldata-helper-cow-1077feat/ethflow-expected-excessive-validto-cow-1076 (PR #49) → feat/forward-orderbook-error-cow-1075 (PR #48) → feat/resolve-app-data-cow-1074 (PR #47).

Linear

Closes COW-1077.

AI assistance disclosure: drafted by Claude (Opus 4.7, 1M context). The original t0 = 0 calldata was itself drafted by an earlier AI session; this PR returns the encoding to a hand-validated form.

The previous `e2e-onchain.sh` pinned a 516-byte hex blob with `t0 = 0`
in the ComposableCoW.create() static-input tuple. TWAP handler's
`validateData` does NOT reject `t0 = 0` (it only checks
`t0 >= type(uint32).max`), so the `create()` tx succeeded but
`TWAPOrderMathLib.calculateValidTo` then computed
`part = (block.timestamp - 0) / t = ~3.3M`, which is far above the
configured `n = 2` and triggers `AFTER_TWAP_FINISHED` on every
`getTradeableOrderWithSignature` poll. Surfaced in the COW-1064 dry
run (2026-06-18 report §6.4): supervisor logged the
`0xc8fc2725...after twap finished` revert per block.

Fix:
- New `scripts/_twap_calldata.py` encodes the calldata fresh on
  every invocation with `t0 = int(time.time()) - 60` (backdated 60s
  so part 0 is Ready as soon as the order is on-chain). Module
  docstring explicitly warns against re-hardcoding t0.
- `scripts/e2e-onchain.sh` Action 1 now shells out to the helper
  rather than carrying the hex inline. Validates the output is
  hex-shaped before passing to `cast send`.
- `docs/operations/e2e-cow-1064-prep.md` section 2.3 step 3
  replaces the pinned blob with a `python3 scripts/_twap_calldata.py`
  recipe and a historical note pointing at COW-1077.
- `docs/operations/e2e-cow-1064-prep.md` section 4.2 recipe gets
  `import time` + `int(time.time()) - 60` for `t0` so the
  re-derivation flow does not re-introduce the bug.
- `scripts/README.md` Action 1 description updated to mention the
  helper.

Constants in the helper (sell/buy tokens, amounts, n, t, salt)
mirror the prep doc's section 4.2; both must change in lockstep if
the TWAP shape is retargeted.

Validation: `python3 scripts/_twap_calldata.py` produces 516-byte
calldata (1034 hex chars) starting with the correct selector
`0x6bfae1ca`; the t0 word reflects current epoch (verified against
`0x00...006a3537b5` on the smoke run). `bash -n scripts/e2e-onchain.sh`
passes. No engine-side changes; this is a script-and-docs PR.

AI assistance disclosure: drafted by Claude (Opus 4.7, 1M context).
The original `t0 = 0` calldata was itself drafted by an earlier AI
session; this PR returns the encoding to a hand-validated form.
@linear-code

linear-code Bot commented Jun 19, 2026

Copy link
Copy Markdown

COW-1077

@brunota20

Copy link
Copy Markdown
Author

Work landed via dev/m4-base creation @ 64cb6d8 (M4 production hardening + PR #66 rust-idiomatic compliance squashed). The HEAD commit of this PR is now an ancestor of dev/m4-base. Closing as merged-by-ancestor.

@brunota20 brunota20 closed this Jun 24, 2026
brunota20 added a commit that referenced this pull request Jun 25, 2026
mfw78 review of PR #8 (nullislabs#8) flagged "we already pull alloy, so pulling hex via there is really not much of a deal". The PR #47 (COW-1074) commit acc9654 then introduced two new custom hex helpers that recreate the same antipattern at a different scope:

- `crates/shepherd-sdk/src/cow/app_data.rs::encode_hex` - 32-byte hash → `0x...`. Used by `resolve_app_data` to format the orderbook lookup path.
- `modules/twap-monitor/src/strategy.rs::hex_short` - 8-byte prefix → `0x...…`. Used to format `appData` hashes in INFO log lines.

Both crates already depend on `alloy-primitives` (sdk: 1.6, twap-monitor: 1.5), so the swap is a one-liner per call site:

- `encode_hex(b)` → `format!("0x{}", alloy_primitives::hex::encode(b))`
- `hex_short(b)` → `format!("0x{}…", alloy_primitives::hex::encode(&b[..8]))`

Both functions keep their old signature so callers (`resolve_app_data` in the SDK, every `host.log` line in twap-monitor strategy) need no changes.

Comments on both helpers now explicitly reference mfw78's PR #8 guidance so the next person tempted to hand-roll a `0123456789abcdef` table has a hook.

Validation: cargo test -p shepherd-sdk -p twap-monitor: 32 + 23 passed; cargo clippy --all-targets -- -D warnings: clean; cargo fmt --check: clean; zero em-dash drift.

Why this PR sits in a separate branch rather than amending PR #47: PR #47 is already In Review, and #48/#49/#50 stack on top of it. Amending would require force-pushing 4 branches. A small follow-up PR keeps each one bisectable and lets mfw78 review the alloy alignment in isolation.
brunota20 added a commit that referenced this pull request Jun 25, 2026
mfw78 review of PR #8 (nullislabs#8) flagged "we already pull alloy, so pulling hex via there is really not much of a deal". The PR #47 (COW-1074) commit acc9654 then introduced two new custom hex helpers that recreate the same antipattern at a different scope:

- `crates/shepherd-sdk/src/cow/app_data.rs::encode_hex` - 32-byte hash → `0x...`. Used by `resolve_app_data` to format the orderbook lookup path.
- `modules/twap-monitor/src/strategy.rs::hex_short` - 8-byte prefix → `0x...…`. Used to format `appData` hashes in INFO log lines.

Both crates already depend on `alloy-primitives` (sdk: 1.6, twap-monitor: 1.5), so the swap is a one-liner per call site:

- `encode_hex(b)` → `format!("0x{}", alloy_primitives::hex::encode(b))`
- `hex_short(b)` → `format!("0x{}…", alloy_primitives::hex::encode(&b[..8]))`

Both functions keep their old signature so callers (`resolve_app_data` in the SDK, every `host.log` line in twap-monitor strategy) need no changes.

Comments on both helpers now explicitly reference mfw78's PR #8 guidance so the next person tempted to hand-roll a `0123456789abcdef` table has a hook.

Validation: cargo test -p shepherd-sdk -p twap-monitor: 32 + 23 passed; cargo clippy --all-targets -- -D warnings: clean; cargo fmt --check: clean; zero em-dash drift.

Why this PR sits in a separate branch rather than amending PR #47: PR #47 is already In Review, and #48/#49/#50 stack on top of it. Amending would require force-pushing 4 branches. A small follow-up PR keeps each one bisectable and lets mfw78 review the alloy alignment in isolation.
brunota20 added a commit that referenced this pull request Jun 25, 2026
mfw78 review of PR #8 (nullislabs#8) flagged "we already pull alloy, so pulling hex via there is really not much of a deal". The PR #47 (COW-1074) commit acc9654 then introduced two new custom hex helpers that recreate the same antipattern at a different scope:

- `crates/shepherd-sdk/src/cow/app_data.rs::encode_hex` - 32-byte hash → `0x...`. Used by `resolve_app_data` to format the orderbook lookup path.
- `modules/twap-monitor/src/strategy.rs::hex_short` - 8-byte prefix → `0x...…`. Used to format `appData` hashes in INFO log lines.

Both crates already depend on `alloy-primitives` (sdk: 1.6, twap-monitor: 1.5), so the swap is a one-liner per call site:

- `encode_hex(b)` → `format!("0x{}", alloy_primitives::hex::encode(b))`
- `hex_short(b)` → `format!("0x{}…", alloy_primitives::hex::encode(&b[..8]))`

Both functions keep their old signature so callers (`resolve_app_data` in the SDK, every `host.log` line in twap-monitor strategy) need no changes.

Comments on both helpers now explicitly reference mfw78's PR #8 guidance so the next person tempted to hand-roll a `0123456789abcdef` table has a hook.

Validation: cargo test -p shepherd-sdk -p twap-monitor: 32 + 23 passed; cargo clippy --all-targets -- -D warnings: clean; cargo fmt --check: clean; zero em-dash drift.

Why this PR sits in a separate branch rather than amending PR #47: PR #47 is already In Review, and #48/#49/#50 stack on top of it. Amending would require force-pushing 4 branches. A small follow-up PR keeps each one bisectable and lets mfw78 review the alloy alignment in isolation.
brunota20 added a commit that referenced this pull request Jun 25, 2026
mfw78 review of PR #8 (nullislabs#8) flagged "we already pull alloy, so pulling hex via there is really not much of a deal". The PR #47 (COW-1074) commit acc9654 then introduced two new custom hex helpers that recreate the same antipattern at a different scope:

- `crates/shepherd-sdk/src/cow/app_data.rs::encode_hex` - 32-byte hash → `0x...`. Used by `resolve_app_data` to format the orderbook lookup path.
- `modules/twap-monitor/src/strategy.rs::hex_short` - 8-byte prefix → `0x...…`. Used to format `appData` hashes in INFO log lines.

Both crates already depend on `alloy-primitives` (sdk: 1.6, twap-monitor: 1.5), so the swap is a one-liner per call site:

- `encode_hex(b)` → `format!("0x{}", alloy_primitives::hex::encode(b))`
- `hex_short(b)` → `format!("0x{}…", alloy_primitives::hex::encode(&b[..8]))`

Both functions keep their old signature so callers (`resolve_app_data` in the SDK, every `host.log` line in twap-monitor strategy) need no changes.

Comments on both helpers now explicitly reference mfw78's PR #8 guidance so the next person tempted to hand-roll a `0123456789abcdef` table has a hook.

Validation: cargo test -p shepherd-sdk -p twap-monitor: 32 + 23 passed; cargo clippy --all-targets -- -D warnings: clean; cargo fmt --check: clean; zero em-dash drift.

Why this PR sits in a separate branch rather than amending PR #47: PR #47 is already In Review, and #48/#49/#50 stack on top of it. Amending would require force-pushing 4 branches. A small follow-up PR keeps each one bisectable and lets mfw78 review the alloy alignment in isolation.
brunota20 added a commit that referenced this pull request Jun 25, 2026
mfw78 review of PR #8 (nullislabs#8) flagged "we already pull alloy, so pulling hex via there is really not much of a deal". The PR #47 (COW-1074) commit acc9654 then introduced two new custom hex helpers that recreate the same antipattern at a different scope:

- `crates/shepherd-sdk/src/cow/app_data.rs::encode_hex` - 32-byte hash → `0x...`. Used by `resolve_app_data` to format the orderbook lookup path.
- `modules/twap-monitor/src/strategy.rs::hex_short` - 8-byte prefix → `0x...…`. Used to format `appData` hashes in INFO log lines.

Both crates already depend on `alloy-primitives` (sdk: 1.6, twap-monitor: 1.5), so the swap is a one-liner per call site:

- `encode_hex(b)` → `format!("0x{}", alloy_primitives::hex::encode(b))`
- `hex_short(b)` → `format!("0x{}…", alloy_primitives::hex::encode(&b[..8]))`

Both functions keep their old signature so callers (`resolve_app_data` in the SDK, every `host.log` line in twap-monitor strategy) need no changes.

Comments on both helpers now explicitly reference mfw78's PR #8 guidance so the next person tempted to hand-roll a `0123456789abcdef` table has a hook.

Validation: cargo test -p shepherd-sdk -p twap-monitor: 32 + 23 passed; cargo clippy --all-targets -- -D warnings: clean; cargo fmt --check: clean; zero em-dash drift.

Why this PR sits in a separate branch rather than amending PR #47: PR #47 is already In Review, and #48/#49/#50 stack on top of it. Amending would require force-pushing 4 branches. A small follow-up PR keeps each one bisectable and lets mfw78 review the alloy alignment in isolation.
brunota20 added a commit that referenced this pull request Jun 25, 2026
mfw78 review of PR #8 (nullislabs#8) flagged "we already pull alloy, so pulling hex via there is really not much of a deal". The PR #47 (COW-1074) commit acc9654 then introduced two new custom hex helpers that recreate the same antipattern at a different scope:

- `crates/shepherd-sdk/src/cow/app_data.rs::encode_hex` - 32-byte hash → `0x...`. Used by `resolve_app_data` to format the orderbook lookup path.
- `modules/twap-monitor/src/strategy.rs::hex_short` - 8-byte prefix → `0x...…`. Used to format `appData` hashes in INFO log lines.

Both crates already depend on `alloy-primitives` (sdk: 1.6, twap-monitor: 1.5), so the swap is a one-liner per call site:

- `encode_hex(b)` → `format!("0x{}", alloy_primitives::hex::encode(b))`
- `hex_short(b)` → `format!("0x{}…", alloy_primitives::hex::encode(&b[..8]))`

Both functions keep their old signature so callers (`resolve_app_data` in the SDK, every `host.log` line in twap-monitor strategy) need no changes.

Comments on both helpers now explicitly reference mfw78's PR #8 guidance so the next person tempted to hand-roll a `0123456789abcdef` table has a hook.

Validation: cargo test -p shepherd-sdk -p twap-monitor: 32 + 23 passed; cargo clippy --all-targets -- -D warnings: clean; cargo fmt --check: clean; zero em-dash drift.

Why this PR sits in a separate branch rather than amending PR #47: PR #47 is already In Review, and #48/#49/#50 stack on top of it. Amending would require force-pushing 4 branches. A small follow-up PR keeps each one bisectable and lets mfw78 review the alloy alignment in isolation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant