Frontend: maker-facing recovery path for parked-expired limit orders (post-#120) #141
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#141
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?
split out from #120 follow-up — @PlasticDigits asked for a frontend issue covering the maker-facing recovery path for parked-expired limit orders.
context
after the #120 contract fix landed (commit
5c744ee), expired limit orders encountered during a match walk are no longer silently dropped — they get parked and the maker can claim back their escrowed tokens via the parked-expired claim path. but the frontend dapp has no UX surface for this today. the maker has to know to call the claim path manually via cosmes / cli, which is not realistic for non-technical users.proposed scope
My Limits panel: surface parked-expired orders alongside active ones. today the panel shows active orders only. needs a
status: "parked-expired"row state with a clear "Claim refund" CTA.Claim refund button. wires up the contract
ClaimExpiredOrder { id }(or whatever the new entrypoint is named — confirm against contract source post-#120 fix) for the row owner. surfaces friendly revert messages on failure.Empty-state copy. if a wallet has parked expirations but no active orders, the panel should still render the parked-expired list with the recovery affordance — not an empty "no orders" message.
Indexer integration. depends on the indexer-side ticket (sister #__) that distinguishes parked-expired from active. frontend reads the new field/endpoint to drive the row state.
acceptance
parked-expiredstate with distinguishable visual treatment vs activeexpires_at = T+30s, wait for expiry, run a match walk, confirm parked row + claim button work in the dappblocked-by
cc @PlasticDigits
mentioned in issue #142
blocked-by ticket filed: #142 (indexer parked-expired distinction).
mentioned in commit
98d3e53d48Update — frontend landed on
main(GitLab #141)Implemented maker-facing recovery for parked-expired limits now that indexer #142 is on
main:LimitOrderMyPlacementsPanelon/limitsand the Trade ticket: separates Active on book vs Expired — refund pending, amber styling for parked rows, Claim refund → pairclaim_expired_limit_order(pair.ts).No claimable expired-limit refund…humanized vialimitClaimUserMessage.ts+tryHumanizeTerraTxMessage.CLAIM_EXPIRED_LIMIT_ORDER_GAS_LIMIT(450k) intransactions.ts.docs/limit-orders.md(#141 invariants + wiring),docs/frontend.md,skills/AGENTS_FRONTEND_LIMIT_PARKED_EXPIRED.md, crosslinks inAGENTS_LOCALNET_TRADING_SWARM.md/AGENTS_TERRACLASSIC_GAS.md.Verification checklist (for QA)
lifecycle_status: parked_expired+remaining_escrowonGET .../limit-placementsafter a taker tx emitslimit_order_expired_parked(#142)./limitswith wallet connected: parked row shows Claim refund; active rows unchanged; empty state mentions parked/expired path.refunded, excluded from default listing).npm test -- --runinfrontend-dapp(new tests:limitPlacementLifecycle,limitClaimUserMessage,pairclaim,transactionsgas, indexer clientstatusparam).LocalTerra smoke (from issue acceptance): post bid with
expires_at = T+30s, wait, run hybrid match walk so order parks, confirm row + claim in dApp./cc @brouie — please verify when you have a moment; leaving #141 open until QA signs off.
mentioned in issue #120
walked the maker-recovery frontend ship at HEAD
94adb5f. Source CLEAN, but found a state/event discrepancy that blocks the UI checklist — flagging for your call.Source verified
frontend-dapp/src/services/terraclassic/pair.ts—claimExpiredLimitOrderwires to pairclaim_expired_limit_orderExecuteMsg, friendly errors vialimitClaimUserMessage.ts+tryHumanizeTerraTxMessage.frontend-dapp/src/services/terraclassic/transactions.ts—CLAIM_EXPIRED_LIMIT_ORDER_GAS_LIMIT(450k) wired intogetGasLimitForTxforclaim_expired_limit_ordershape (covered by existing transactions test).LimitOrderMyPlacementsPanelseparates Active vs "Expired — refund pending" with amber styling per the ship note.lifecycle_status+remaining_escrowfrom indexer per #142 integration.Checklist walk at HEAD
94adb5f(6) Vitest
npm test -- --runin frontend-dapp — 441/441 PASS across 62 files. IncludeslimitClaimUserMessage,pairclaim,transactionsgas, indexer clientstatusparam coverage. Verified earlier today during the #156 MR flow.(1) Indexer returns
lifecycle_status: parked_expired+remaining_escrowon/limit-placementsafter a taker tx emitslimit_order_expired_parked— FAIL on the fresh stack, see "Discovery" below.(2)
/limitsparked row shows Claim refund — blocked by (1).(3) Trade tab compact panel — blocked by (1).
(4) Paused-pair behavior on Claim refund — policy note: the #141 ship text says claim "stays available" during pause, but the #120 reversal (
b03152d/ merged 2026-05-12) flipped this to claim is blocked during pause (the newassert_not_pausedgate at contract.rs:551). TheLimitOrderMyPlacementsPanelshipped here per the ship note pipesisPairPausedinto the Claim button's disabled state with copy "Unavailable (pair paused)" — that matches the post-#120 policy correctly. Worth updating the #141 ship-note wording on item 4 since it predates the policy reversal.(5) Successful claim removes row from default feed (→
refunded) — blocked by (1).Discovery: parked-expired event not indexed
Repro on a fresh stack (volumes wiped,
make start-qaagainst94adb5f) plusis_pausedquery returning real{"paused":false}(so the new contract IS deployed):terra146y...c9mjav). Tx32078F76B8A93652BE6945005681677C396E400F157235AACA8E599B251A9FA1, place_limit_order event present and correct.terrad query block).hybrid:{pool_input:"0", book_input:"5000", max_maker_fills:8}(5000 EMBER from test1). TxEBA6DECB081245B5F7823A84D404C41388DC05710A74220D78C1E573D504DEBB.send / swap / transfer / transfer. Nolimit_order_expired_parkedevent. Swap event haslimit_book_offer_consumed=0andbook_return_amount=0— book leg consumed nothing.limit_order:{order_id:1}→not found(removed from active book)expired_limit_refund:{order_id:1}→{"order_id":1,"owner":"terra1x46r...","side":"bid","remaining":"9910","expires_at":1778646273}— escrow row present, fee 90 deducted from initial 10000GET /api/v1/pairs/.../limit-placements?status=allreturns the order withlifecycle_status: "active", noremaining_escrowfieldGET ?status=parked_expiredreturns[]The indexer parser at
indexer/src/indexer/parser.rs:714-750is correct in shape (looks forwasmevent withaction=limit_order_expired_parked, reads_contract_address+order_id+remaining). The issue is the contract didn't emit the event at all in this walk, even though the order was moved toEXPIRED_LIMIT_CLAIMS.Worth investigating:
pool_input=0, book_input=5000entersmatch_bids/match_asksat all when the head bid is far below pool price (our bid at 1.0 vs pool effective ratio ~0.96 from the swap output)EXPIRED_LIMIT_CLAIMSwithout going throughpark_expired_limit_order_for_claim(orderbook.rs:540) — but I only found one write site atorderbook.rs:566, so this would point to some out-of-band mechanismbook_inputwalk parks but elides the event when no actual fill happens (looks like an event-emission gap in that branch)This blocks (2)(3)(5) of the UI checklist because there's nothing for the dapp to show. The frontend gate logic for parked rows is source-clean; the data simply isn't reaching it via the indexer.
Happy to file as a sister ticket if you want to keep #141 scoped to the frontend ship and track the event-emission gap separately.
source + Vitest CLEAN, UI walks blocked on indexer/contract-event discrepancy.
/cc @PlasticDigits
mentioned in commit
d75a3e0783Indexer / #141 follow-up (parked-expired visibility)
Pushed
d75a3e0onmain: indexer now ingestslimit_order_expired_parked(andclaim_expired_limit_order) when LCD wasm logs flatten multiple logical CosmWasm actions into one attribute stream — previously we only looked atwasm_attr_last(..., "action"), so ifaction=swapappeared after the parked attrs, the parked transition was skipped while on-chain state still hadExpiredLimitRefund(matches your repro: contract parked, indexer stayedactive).Investigation (note 3342749920)
park_expired_limit_order_for_claimis the soleEXPIRED_LIMIT_CLAIMSwrite; it always builds thelimit_order_expired_parkedwasm event.expired_bid_parked_on_hybrid_walk_claim_refunds_maker(same hybrid template as your QA) asserts the event in cw-multi-test — so “no event on-chain” is unlikely for correctly decoded tx logs.wasmblob, lastactionwins for naive whole-slice parsers). Fix: scan everyactionkey perwasmevent and parseorder_id/remainingfrom the segment after each matching action.Docs / agents
docs/indexer-invariants.md(lifecycle row + wasm row),docs/limit-orders.md(indexer LCD note),skills/AGENTS_FRONTEND_LIMIT_PARKED_EXPIRED.md,skills/AGENTS_LOCALNET_TRADING_SWARM.md.Verification checklist (QA)
make start-qa, place bid withexpires_at, wait, hybrid walkpool_input=0/book_input>0). After the walk tx is indexed,GET .../limit-placements?status=parked_expiredincludes the order withremaining_escrow.GET .../limit-placementslistslifecycle_status: parked_expiredfor thatorder_id.cargo test --lib indexer::parser::tests(or full indexer lib tests) passes locally./cosmos/tx/v1beta1/txs/{hash}) — confirm whetherlimit_order_expired_parkedattrs appear beforeaction=swapin the samewasmevent; that shape is what the fix targets./cc @brouie — please re-verify the #141 UI checklist on a fresh QA stack with this indexer build; leaving the issue open per prior QA sign-off policy.
mentioned in commit
b0c390b4f4QA verification complete (GitLab #141)
Verified on LocalTerra + indexer after fixing a remaining ingestion gap.
Root cause found
The prior #141 indexer fix (merged
wasmattribute-stream scanning) did not cover LocalTerra LCDwasm-wasmevents. On hybrid walks that park an expired limit,limit_order_expired_parkedattrs appear in a standalonewasm-wasmrow (not in the flattenedwasmslice withaction=swap). The indexer skipped those rows, soGET .../limit-placementsstayedactivewhile on-chainExpiredLimitRefundexisted.Fix (main
b0c390b→ mergedcdfcbd0)wasmandwasm-wasmevent types.contract_addresskeys as segment boundaries when reading attrs after lifecycleactions.wasm-wasmshape.Checklist
lifecycle_status: parked_expired+remaining_escrowafter hybrid walk parks order/limits— parked row amber styling, Claim refund CTA, empty-state copy mentions recovery path/tradecompact panel — same parked row + claim buttonclaim_expired_limit_order; success tx shown; row drops from default feed after indexer ingests refundlimitPlacementLifecycle,limitClaimUserMessage,pairclaim, indexer clientstatusparam — passcargo test --lib parser::tests— 17/17 passClosing #141 — frontend ship was already correct; blocker was indexer LCD event-type coverage.
QA re-verification (2026-05-27)
Re-verified #141 on the live LocalTerra stack (indexer :3001, frontend :3000, host Postgres).
Checklist (all pass)
GET .../limit-placementsreturnslifecycle_status: parked_expired+remaining_escrow(order #3 on EMBER/CORAL; parked tx0EE1FEBB…).refunded).claim_expired_limit_order; success txF895BE3C…D58DD1; row dropped from default feed once indexer ingested refund.parser::tests17/17;limit_order_parked_lifecycleintegration pass.No new code changes required on
main(3f6a2e5); prior indexer fix (wasm-wasmLCD lifecycle parsing,cdfcbd0) resolves the blocked QA from note 3342749920.Issue remains closed — frontend ship was correct; blocker was indexer event-type coverage, now verified end-to-end.
mentioned in issue #206
mentioned in issue #253
mentioned in issue #259
mentioned in issue #269
mentioned in commit
d6701c4b00mentioned in issue #530
mentioned in issue #613