fix: Twap.isValid() never validates the derived per-part amounts - #979
fix: Twap.isValid() never validates the derived per-part amounts#979gomesalexandre wants to merge 1 commit into
Conversation
sellAmount/numberOfParts and buyAmount/numberOfParts are floor-divided in
transformDataToStruct to derive partSellAmount/minPartLimit. isValid() only
checked the aggregate sellAmount/buyAmount, never the derived per-part
values, so an order like {sellAmount: 90n, numberOfParts: 100n} passes SDK
validation while the on-chain TWAPOrder handler reverts on a zero
partSellAmount/minPartLimit on every poll attempt. Since poll() gates on
isValid() for its DONT_TRY_AGAIN short-circuit, such an order never fills
and never cleanly retires.
Mirrors the on-chain check (composable-cow TWAPOrder.sol) by validating
this.staticInput.partSellAmount/minPartLimit are non-zero, using the value
already computed in the constructor rather than re-deriving it.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughTWAP validation now rejects orders when floor division produces zero per-part sell or buy amounts. Cross-adapter tests cover both validation errors. ChangesTWAP validation
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to The change rejects TWAP orders whose derived per-part amounts are zero, matching the existing contract behavior and preventing repeated failed attempts; no actionable merge-blocking risk remains after normal checks and review. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Full details: Docstring CoverageExplanation No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2 files. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Some tools did not complete. Review the errors below. 🔧 ESLint
packages/composable/src/orderTypes/Twap.tsParsing error: Unable to parse the specified 'tsconfig' file. Ensure it's correct and has valid syntax. packages/app-data/tsconfig.json(2,14): error TS6053: File ' packages/composable/tests/Twap.spec.tsParsing error: Unable to parse the specified 'tsconfig' file. Ensure it's correct and has valid syntax. packages/app-data/tsconfig.json(2,14): error TS6053: File ' Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Twap.isValid()checks the aggregatesellAmount/buyAmountbut never the derived per-partpartSellAmount/minPartLimit(floor division bynumberOfParts, computed once in the base constructor viatransformDataToStructand stored onthis.staticInput). The on-chainTWAPOrderhandler validates the derived quantities directly and reverts when either is zero:Concrete failing case:
{ sellAmount: 90n, numberOfParts: 100n }→partSellAmount = 90n / 100n = 0n(BigInt floor division). The SDK'sisValid()reports the order valid, but the on-chain handler reverts on every attempt.ConditionalOrder.poll()gates onisValid()for itsDONT_TRY_AGAINshort-circuit (packages/composable/src/ConditionalOrder.ts:264-269), so an order built this way never gets that clean signal — it just keeps failing against the contract instead of being recognized as invalid up front.Fix
Add the two missing checks in
isValid(), mirroring the Solidity, reusing the value already computed in the constructor (this.staticInput.partSellAmount/.minPartLimit) rather than re-deriving it.Receipts
New unit tests fail on the pre-fix code and pass after:
Lint (
npx eslint src/orderTypes/Twap.ts) andtsc --noEmitboth clean.Adversarially reviewed with Codex (GPT-5.6) against the diff directly: verdict
SHIP, no blocking findings — confirmedstaticInputreflects the correct floor-divided values for every construction path (fromData, raw constructor,deserialize), confirmed no existing valid fixture regresses, and confirmed check ordering can't produce a false result. It also flagged a separate, pre-existing, out-of-scope concern (case-sensitive same-token address comparison in theInvalidSameTokencheck) — noting here for visibility, not fixing it in this PR to keep the diff focused.Summary by CodeRabbit
Bug Fixes
Tests