fix(uniswapx-sdk): unsigned v3 dutch order serializes each output's own relativeBlocks - #711
Open
gomesalexandre wants to merge 1 commit into
Open
Conversation
…wn relativeBlocks
gomesalexandre
marked this pull request as ready for review
September 1, 2026 18:07
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
UnsignedV3DutchOrder.serialize()writes the input curve's encodedrelativeBlocksinto every output, instead of each output's own.CosignedV3DutchOrder.serialize()(same file, line ~557) already does per-output encoding correctly — this fix bringsUnsignedV3DutchOrder.serialize()in line with it.Repro (real code, not described)
Input curve
relativeBlocks: [1,2,3,4], output curverelativeBlocks: [5,10,15,20]:Why the existing tests never caught it
Both round-trip tests in
V3DutchOrder.test.tsconstruct aCosignedV3DutchOrder— the sibling that already encodes correctly — soUnsignedV3DutchOrder.serialize()itself was never exercised by any prior test. The one test touchingUnsignedV3DutchOrderonly callsfromJSONand asserts twostartAmountstrings, neverrelativeBlocks.Added a new test constructing
UnsignedV3DutchOrderdirectly with differing input/output curves (a shared curve would make the bug invisible), asserting each side round-trips its own blocks.Severity — read this before assuming a signing bug
I checked
hash()/witnessInfo()(the function_TypedDataEncoderhashes for signing) and confirmed it already encodes each output's ownrelativeBlockscorrectly:So this is not a signing-forgery path — the signed digest was always correct. The corruption is confined to the byte string
serialize()produces (storage/transport/pre-cosign quoting), not to what gets signed.I did not identify a production caller of
UnsignedV3DutchOrder.serialize()in this repo — flagging as an open dependency rather than a demonstrated live-money bug.One caveat worth noting: an order already serialized under the buggy code will still decode with the wrong output block schedule after this fix lands — it corrects newly-serialized orders only, it can't retroactively repair previously-persisted bytes.
Collision check
Open PR #705 touches the same file (
CosignedV3DutchOrder.resolve(),encodeRelativeBlocks/decodeRelativeBlocksrange checks) but never touchesUnsignedV3DutchOrder.serialize()(lines 197-233) — confirmed viagh pr diff 705before starting, file-level overlap only, no hunk conflict expected.Testing
git stash— fails withexpected [ 1, 2, 3, 4 ] to deeply equal [ 5, 10, 15, 20 ]on unfixed code, passes after.uniswapx-sdkunit suite:351/351pass (bun test src/).tsc --noEmit -p tsconfig.cjs.json: clean.bun run lint: currently broken on unmodifiedmain—eslint src --ext .tsfails withInvalid option '--ext'because the installed eslint (v8.57.1) auto-detects flat-config mode from... actually noeslint.config.*/.eslintrc*file exists anywhere in the repo at all. Confirmed this is pre-existing and unrelated to this diff by stashing my changes and re-running on a clean tree — identical failure. Not fixed here, out of scope for this PR; flagging for a maintainer.Codex reviewed the diff adversarially: verdict "no findings," confirmed the fix is complete (no sibling class has the same pattern), the new test is non-vacuous, and independently re-ran the full order test suite (62/62 pass) as part of its own verification.
Risk
Low. Change is additive-correct (each output now encodes its own already-present data instead of borrowed data) with no change to types, no change to
hash(), no change to any other class.