fix(perps-controller): keep the terminal TWAP record when a completing fill ties lastUpdated - #10122
Open
abretonc7s wants to merge 2 commits into
Open
fix(perps-controller): keep the terminal TWAP record when a completing fill ties lastUpdated#10122abretonc7s wants to merge 2 commits into
abretonc7s wants to merge 2 commits into
Conversation
The venue reports one TWAP lifecycle as several history entries — an activation plus a terminal record — and `lastUpdated` folds in slice fills that every entry sharing a `twapId` receives. A schedule finished by its final fill therefore derives the same `lastUpdated` on both entries, because that fill outranks each entry's own whole-second timestamp. Collapsing entries with `order.lastUpdated >= existing.lastUpdated` admits that tie. The venue returns the terminal record first, so the activation was iterated last and overwrote it, and `resolveTwapOrderStatus` then mapped `activated` to `Active`. A finished schedule stayed listed as live indefinitely, reporting the activation's zero executed size and offering a cancel affordance the venue can no longer act on. Decide terminality first and compare `lastUpdated` strictly, so an equal timestamp keeps the record already collapsed. Both the polled read and the subscription adapter shared the comparison and are both corrected. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
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.
Explanation
A finished HyperLiquid TWAP could stay listed as live indefinitely.
The venue reports one lifecycle as several
twapHistoryentries — an activation plus a terminal record — andlastUpdatedismax(startedAt, normalizeTwapHistoryTimestamp(entry.time), ...fills). Slice fills are looked up bytwapId, so every entry of a schedule receives the same fills, whileentry.timeis a whole-second value. When the final fill is what completed the schedule, that fill's millisecond timestamp outranks both entries' own timestamps and both entries derive an identicallastUpdated.Collapsing with
order.lastUpdated >= existing.lastUpdatedadmits that tie. The venue returns the terminal record first, so the activation was iterated last and overwrote it.resolveTwapOrderStatusmapsactivated→Active, so the schedule was reported live, carrying the activation'sexecutedSz: 0, and clients offered a cancel affordance the venue can no longer act on.Schedules that were
terminatedstop seconds after their last fill, so they never tie and were unaffected — which is why this presents as an intermittent subset rather than every schedule.Observed on a real mainnet account: of 6 schedules, the 4
terminatedones resolved correctly and bothfinishedones were reportedactive, 74 and 18 days after they ended.Fix
supersedesCollapsedTwapOrderdecides terminality first, then compareslastUpdatedstrictly so an equal timestamp keeps the record already collapsed.Activeis the only non-terminalPerpsTwapLifecycleStatus, so "has a terminal record" is exactlystatus !== Active.This keeps the module's existing stance that a schedule is terminal only when the venue says so — it never infers termination from elapsed time, so collateral still cannot be reclaimed from a live TWAP.
The polled read (
getTwapOrders) and the subscription adapter (subscribeToTwapOrders) shared the comparison; both are corrected.References
Surfaced while investigating the mobile symptom where expired TWAPs render in the Active tab.
Checklist
README.md) for new or updated code as appropriateNote
Medium Risk
Changes TWAP lifecycle presentation and cancel affordances for a real edge case; logic is localized to history collapse but affects polled and streamed TWAP state.
Overview
Fixes finished HyperLiquid TWAPs staying in the Active list when the activation and terminal
twapHistoryrows share the samelastUpdatedafter the completing slice fill.Collapse logic no longer uses
lastUpdated >=;supersedesCollapsedTwapOrderprefers a terminal venue status overActive, then uses strictlastUpdatedso a tie keeps the record already merged.getTwapOrdersandsubscribeToTwapOrdersboth use this rule. Tests cover both history orderings when activation and terminal entries tie onlastUpdated.Reviewed by Cursor Bugbot for commit 912fed8. Bugbot is set up for automated code reviews on this repo. Configure here.