fix: My Open Limits Cancel fails or is unreachable (order #1 · Sell UST1) #530
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:implement
agent:implement
agent:open_issues
agent:ready
agent:research
agent:security_audit
agent:verify
architecture
backend
blocker:hybrid
blocker:launch
blocker:limit-orders
blocker:v2
block:log_only
block:security
bug
ci
contracts
correctness
deploy
dev
devops
docs
documentation
duplicate
e2e
enhancement
epic
feature
frontend
functional-completion
gas
good first issue
governance
help wanted
high-risk
hooks
hybrid
indexer
infra
infrastructure
integrators
invalid
launch-blocker
limit-orders
localnet
localterra
low priority
missing-implementation
needs-design
ops
performance
priority
high
priority
medium
product
qa
QA
question
ready
ready
research
scripts
security
security-hardening
smartcontracts
tech-debt
testing
ux
UX
v2
verification
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
code/cl8y-dex-terraclassic#530
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Retail report on dex.cl8y.com: My open limits still shows a resting row and the user cannot cancel it.
That string is the active row in
LimitOrderMyPlacementsPanel(green●, not parked◆/ dust▫). Bundle investigation + fix for every surface that can leave a maker unable to cancel or unable to understand why Cancel is dead:/tradecompact panel,/limitspage panel, book row×/ Cancel all mine, Advanced Cancel by order ID, and Portfolio (view-only). Do not split “indexer stale row” vs “disabled Cancel” vs “error toast missed” vs “sticky CTA covers the button” into separate issues.Related: #135 (cancel vs indexer), #141 / #142 (parked lifecycle), #162 (book row cancel), #419 (open orders first), #505 (
OrderStatus/ L21), #524 (UST1 display invert — Sell UST1), #527 (Chrome sticky Place limit can sit over ticket body), #529 (UST1/USTR place bounds — cancel isorder_idonly; do not treat #529 as the cancel root unless evidence says the row is on that pair and cancel itself reverts).Evidence
LimitOrderMyPlacementsPanelactive line:order #{id} · {Buy|Sell} {base} · {price} · placed {timestamp}.asset_0/asset_1+ invert state before changing contracts.block_timestamp, truncated to 19 chars).Current codebase
Cancel is implemented and covered for the happy path. The report is a lifecycle / UX hole: the panel can keep showing an active
●row when the order is no longer cancelable, or the Cancel control can be disabled / covered / unexplained.ExecuteMsg::CancelLimitOrder { order_id }/CancelLimitOrders— owner-only, only while the row is inORDERS. Blocked while the pair is paused (L6 / #120). After park, cancel is the wrong message — useClaimExpiredLimitOrder. QueryOrderStatus(#505):Active/ParkedRefund/Unknown.Unknown≠ filled (also cancel, claim, never-placed).limit_order_placements.lifecycle_status = active. Cancel event → row omitted from default placements (#135). Park →parked_expired+remaining_escrow; claim →refunded. Fills are inserted intolimit_order_fillsonly —process_limit_order_filldoes not flip placement lifecycle or remaining escrow. A fully filled order can remainactiveinGET .../limit-placementsand still render as●with Cancel.useLimitOrderCancelMutation(pairAddr, wallet)shared by ticket panel, book×, Cancel all mine, Advanced form. Preflight: skip iforderIdHasIndexedCancellation. Broadcastcancel_limit_order. On success: toast + invalidatelimitPlacements/limitCancellations/ book / wallet history. CosmWasm “LimitOrder not found” is humanized (#135).Cancel— no “Already cancelled / no longer on the book” reason./tradecompact panelvariant="compact"sits below.trade-limit-submit-sticky(TradeOrderTicket). Active list ismax-h-24 overflow-y-auto; panelmax-h-48. Cancel istext-[9px]. Combined with #527, the user can read the●order #1line and still not reach a working Cancel./limitsPortfolioOpenLimitsSectionlists pair / id / side / price / status / placed. No cancel. Copy says manage on Limits or Trade.order_idonly — invert must not change the execute payload.Playbooks:
skills/AGENTS_FRONTEND_LIMIT_PARKED_EXPIRED.md,skills/AGENTS_ORDER_STATUS_QUERY.md,docs/limit-orders.md§ Cancel / dApp retail form.Why this is needed
●row with a dead or failing Cancel is a custody incident from the user’s point of view, even when escrow already filled, parked, or cancelled.activeis not “still onORDERS”. Fills never update placement lifecycle. Cancel-indexed rows can still render as active if the placements query and the cancellation guard disagree. LCDOrderStatusis unused by the panel.window.confirm; toast +TxResultAlertbelow a clipped compact list; sticky Place limit covering the CTA (#527).OrderStatus+ indexer placement + cancellation + fills) and a UI that matches that truth.Constraints / guardrails
Pausedrevert.order_id. Book×staysorder.owner ===connected address.ORDERShas the row. After park, Claim only. Do not invent a third execute path.OrderStatus::Unknownis not proof of fill (L21 / #505). Classify fill vs cancel vs never-placed from indexer fills/cancellations + local tx, not fromUnknownalone.MIN/MAX_LIMIT_PRICE. Cancel does not send a price.price_usdmath (#522 / #524). Invert stays display-only; cancel payload stays factoryorder_id.token0/token1/ rawbid/askin retail. Blocking errors: one short sentence + optional Docs. Disabled Cancel must state why (paused, restricted, already gone, filled, claim instead)./tradeor/limits. Progressive disclosure / inline row status only.trade-cancel-placement-{id},limits-page-cancel-placement-{id},trade-book-cancel-{bid\|ask}-{id},trade-ticket-placements-anchor.make has-localterra; provisionmake setup-cloud-localterrawhen the fix needs place/fill/cancel/park.Relevant files
frontend-dapp/src/components/trade/LimitOrderMyPlacementsPanel.tsx●row + Cancel disable/labelfrontend-dapp/src/hooks/useLimitOrderCancelMutation.tsfrontend-dapp/src/utils/limitOrderCancelUserMessage.tsfrontend-dapp/src/utils/limitPlacementLifecycle.tsactive/parked_expired/refundedpartitionfrontend-dapp/src/components/trade/TradeOrderTicket.tsxfrontend-dapp/src/pages/LimitOrdersPage.tsxfrontend-dapp/src/components/trade/OrderBookPanel.tsx×/ Cancel all minefrontend-dapp/src/components/portfolio/PortfolioOpenLimitsSection.tsxfrontend-dapp/src/services/terraclassic/pair.tscancelLimitOrder/ batchindexer/src/db/queries/limit_order_lifecycle.rsindexer/src/indexer/parser.rsprocess_limit_order_fill(no lifecycle write)smartcontracts/contracts/pair/src/orderbook.rsquery_order_statusdocs/limit-orders.mdRecommended direction
Investigate first (reproduce the report class, then fix). Do not ship a cosmetic “Cancel” relabel without proving which class the UST1 row is.
●row, record LCDOrderStatus, indexer placementlifecycle_status, indexer cancellation row, indexer fills for thatorder_id, pairis_paused, connected wallet vsowner.OrderStatus(or equivalent) before offering Cancel:Active→ Cancel enabled (unless pause/blacklist/gas).ParkedRefund→ show Claim, not Cancel.Unknown→ do not leave a fake●Cancel. Classify via fills vs cancellations; show Filled / Already cancelled / No longer on the book and hide Cancel.ORDERSwithout cancel/park), stop listing the row as default-openactive. Do not treatUnknownas fill without a fill event./tradeCancel must be reachable (not under sticky Place limit; not clipped without scroll affordance). Errors must be visible next to the row, not only in a toast belowmax-h-24.Acceptance criteria
order #1 · Sell UST1 · 82.04…(or an equivalent LocalTerra fixture). If mainnet pair/tx cannot be recovered, ship a fixture that matches the same UI string and failure mode.Activeon-chain./tradecompact,/limitspanel, book×, Advanced form: confirm →cancel_limit_order→ row leaves Open; escrow returns; indexer catch-up matches.UnknownwithoutORDERS: no enabled Cancel. Row status is Filled / Cancelled / gone — not a stuck●with a dead button.ParkedRefund/parked_expiredstays Claim refund / Claim dust. Pause/blacklist copy unchanged (L6).Unavailable (pair paused). Blacklist →Trading restricted. Already gone → explicit gone copy (not a muteCancel)./tradereachability. At desktop 1280×720 and phone, Cancel for a single open row is clickable (elementFromPointhits the button, nottrade-limit-submit).order_idon the selected pair. No extra execute fields.docs/limit-orders.md+ parked-expired playbook state the open-row reconciliation.make verify-issue-<iid>covers unit + the new paths.Test plan (all paths)
Investigation / LCD + indexer
OrderStatus=Active; panel●; Cancel enabled●; fill row exists; Cancel hiddenActive; Cancel refunds remaining onlyParkedRefund/ Claim — not Cancelreason=DustFilled)activerow once cancellation or LCDUnknownis knownorder_idcancel payloadUnit / RTL (
make test-frontend)LimitOrderMyPlacementsPanel: Active + Cancel; parked → Claim; filled/unknown fixture → no Cancel + status copy; paused/blacklist labels; already-indexed cancellation label.useLimitOrderCancelMutation: indexed-cancel throws; single vs batch; invalidations.trade-limit-submit-sticky.Indexer (
make test-indexer-integration/ scoped)Playwright
limit-orders-tx.spec.ts/tradecompacttrade-cancel-placement-{id}visible and clickable; confirm; Cancel submittedcancel_limit_orderbroadcastelementFromPointon Cancel ≠ Place limit sticky/limitsAdvancedManual QA (mainnet report class)
order_id=1(or current equivalent). Query LCDOrderStatusand indexer placements/fills/cancels.Active, cancel from/tradeand/limits; confirm refund.Active, confirm the UI no longer offers Cancel and shows the correct next action.Test plan — attack, hack, and abuse vectors
×and panel Cancel never appear forowner !==connected wallet. On-chain stillUnauthorized.cancel_limit_orderagainstUnknown/filled ids (gas grief). If a race still hits chain, humanize — no raw map key.is_paused.#527overlay:elementFromPointon Cancel is Cancel, not Place limit.window.confirmstill required for panel cancel.#1on pair A must not target pair B. Pair switch clears pending mutation context.cancel_limit_order.order_idor pair address.82.044…, ISO slice). NoinnerHTML.Verification criteria
Issue is done when all of the following are true:
●+ dead Cancel. Active-on-chain orders cancel; gone orders do not offer Cancel.Unknown≠ fill, #524 convert-on-submit untouched, #529 out of scope unless cancel itself reverts.make verify-issue-<iid>green. Do not skip chain work with a false “no LocalTerra” withoutmake has-localterra/make setup-cloud-localterra.docs/limit-orders.md+ playbook) describe open-row reconciliation.Mode
ORDERSwithout a cancel event)Severity
mentioned in commit
b35548b994mentioned in merge request !1059
Merge sanity check for !1059 (before landing on main):
OrderStatus+ indexer fills/cancels; report-class fixture → Filled; Claim forParkedRefund; cancel payload isorder_idonly).×is not UI-reconciled the same way — a stale filled book row can still show a clickable×until broadcast fails.elementFromPointfor AC6 (compact Cancel vs Place limit). Verify script is unit/docs.trade-ticket-submit-footer, not inside the money CTA. Do not resurrecttrade-limit-submit-sticky.mentioned in issue #527
mentioned in commit
9fd8b4eadfmentioned in commit
367b772e7bmentioned in commit
40e614ae7cmentioned in issue #546
mentioned in issue #617
mentioned in issue #618
mentioned in issue #619
mentioned in issue #717