feat(pair): typed OrderStatus query over existing maps (Active / ParkedRefund / Unknown) #505
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#505
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
Add a read-only typed order-status query on the pair that classifies an
order_idfrom existing storage only:ORDERS.may_load(order_id)→SomeActiveEXPIRED_LIMIT_CLAIMS.may_load(order_id)→SomeParkedRefundUnknownOut of scope (explicitly rejected): tombstones, owner shadow index (
OWNER_ORDERS), migration / permissionless backfill,Protocol {}capability negotiation, permanent terminal history, any execute-path dual-writes, any taker/maker gas increase, any permanent state growth.Context: third-party grid vault consumers (e.g. RenneBeau/CL8Y-DeX-bot-and-liquidity-contracts#45 and the closed heavy PR PlasticDigits/cl8y-dex-terraclassic#1) need absence as a typed value, not an untyped CosmWasm query error. Upstream accepts only this cheap read-only slice.
Current codebase
Query surface today
Canonical msgs live in
smartcontracts/packages/dex-common/src/pair.rs(QueryMsg), handled insmartcontracts/contracts/pair/src/contract.rs.Relevant variants today:
QueryMsg::LimitOrder { order_id }→LimitOrderResponseviaorderbook::load_order_response, which usesORDERS.load. Missing key →StdError::not_found(untyped error). Return type is notOption.QueryMsg::ExpiredLimitRefund { order_id }→Option<ExpiredLimitRefundResponse>viaEXPIRED_LIMIT_CLAIMS.may_load. Missing key →None(typed absence).Storage / lifecycle (unchanged by this issue)
ORDERS("limit_orders")EXPIRED_LIMIT_CLAIMS("exp_limit_cl")Lifecycle today (must remain unchanged):
unlink_order→ORDERS.remove(no residual row).EXPIRED_LIMIT_CLAIMS.EXPIRED_LIMIT_CLAIMS.remove.There is no on-chain status enum, no owner index, no terminal tombstone. Contract callers cannot safely treat
LimitOrderquery failure as “order gone / filled.”Precedents
ExpiredLimitRefundalready demonstrates typed absence (Option). This feature extends that pattern to a unified lifecycle lookup without inventing new write paths.Why this is needed
Contract consumers (custody vaults, bots, multi-contract strategies) that hold local order IDs must distinguish:
Active) — may cancel / update.ParkedRefund) — must claim viaClaimExpiredLimitOrder(s).Unknown) — filled, cancelled, never existed, claimed, or pre-dating any future history — without conflating that with LCD/transport/schema/StdErrorfailures.Today, path (3) and real query failures look the same when using
LimitOrder, so safe vaults fail-closed and can deadlock settlement after a full fill removes the row. Off-chain indexers can use events; on-chain contracts cannot.This issue does not promise to distinguish
FullyExecutedvsCancelledvs “never existed.” Callers that need that distinction must keep their own ledger (they placed/cancelled the orders) or use off-chain indexing. Upstream will not pay permanent tombstones / chain bloat for that.Constraints / guardrails
ExecuteMsg, no changes to swap / place / cancel / claim / clean / match paths, no dual-writes.Map/Item, no migrate backfill, no tombstones, noOWNER_ORDERS, no generation/ready flags.Unknown. Only a successful query response may carryUnknown.LimitOrderandExpiredLimitRefundbehavior for backward compatibility (error vsOptionas today). Add a new query variant.Protocol {}/ feature-flag negotiation surface in this issue. Schema is the shareddex_commontypes; versioning via normal contract release / cw2 if needed later, not a runtime capability menu.FullyExecuted,Cancelled,NotFoundas separate statuses). Use a single non-custody bucket:Unknown(name preferred overNotFoundto avoid implying the id was never valid).Activeis checked first and parked should never coexist. Do not silently merge.OrderStatusV1/ tombstone / owner-inventory API wholesale.Relevant files
smartcontracts/packages/dex-common/src/pair.rs(QueryMsg, new enums/response types)smartcontracts/contracts/pair/src/msg.rssmartcontracts/contracts/pair/src/contract.rs(query, new helper)smartcontracts/contracts/pair/src/orderbook.rs(load_order_response)smartcontracts/contracts/pair/src/state.rs(ORDERS,EXPIRED_LIMIT_CLAIMS,LimitOrder,ExpiredLimitRefund)smartcontracts/tests/src/limit_order_tests.rs(and/or a focused new test module if preferred)docs/limit-order query notes — only if already documentingQueryMsgIndexer / frontend: optional follow-up, not required for acceptance (contracts are the primary consumer).
Recommended direction
In
dex_common::pair, add something like:Exact field optionality should mirror what each map can supply today (
ExpiredLimitRefundcurrently has nopricein upstream state — do not expand parked storage in this issue just to populate price).Add
QueryMsgvariant, e.g.OrderStatus { order_id: u64 }with#[returns(OrderStatusResponse)]. Prefer a plain name withoutV1suffix unless a strong reason appears during implementation.Handler logic (pseudo):
Wire into
query()match; re-export types from pairmsg.rsas needed.Bump pair crate / cw2 version only if this repo’s release process requires it for any query-shape change; prefer minimal versioning consistent with prior additive query additions.
Do not change
LimitOrderto returnOptionin this issue (breaking for some clients); the new query is the safe path for contract callers.Acceptance criteria
Activewith correct metadata for a resting bid and ask.ParkedRefundwith correct metadata after park (expiry walk, dust, and/orCleanLimitBook— cover at least one park path thoroughly; others may be lighter).Unknown(success, notErr) for: never-used id, fully filled id, cancelled id, and claimed-parked id.LimitOrderstill errors on missing id;ExpiredLimitRefundstill returnsNonewhen absent.git grep/ review confirms noORDER_TOMB,OWNER_ORDERS, backfill cursor, or execute dual-write hooks.save/removefor inventory).-D warningsclean for touched crates.dex_commonso external contracts can depend on the schema without forking pair internals.Test plan (functional paths)
OrderStatusActive, owner/side/price/remaining/expires matchLimitOrderOrderStatusActive, ask fields correctOrderStatusActive, remaining decreasedOrderStatusUnknown;LimitOrderstill errorsOrderStatusUnknownUnknownOrderStatusParkedRefund;ExpiredLimitRefundisSomeOrderStatusUnknown;ExpiredLimitRefundisNoneUnknownorder_id(e.g.ORDER_NEXT_IDor large id)OrderStatusUnknownorder_id == 0OrderStatusErr(notUnknown)Active, new price reflectedUnit-level: direct handler tests with mock storage for Active / Parked / empty maps without full app harness where useful.
Test plan (attack / abuse / misuse vectors)
ContractQueryfailure asUnknownOrderStatusResponse; failures stayErr. No code path convertsStdError→Unknown.LimitOrder/ExpiredLimitRefund; status query must not leak extra private data.Unknownfor absent ids is intentional. Rate limits are chain-level, not contract.UnknownUnknown.Unknown⇒ fully filled (theft of settlement logic)Unknownis non-custody only; vaults must not unlock solely onUnknownwithout their own cancel ledger. Optional comment on response type.Activefirst is acceptable if dual presence is impossible by construction — add a unit test that documents the priority.OrderStatusV1, tombstones)FullyExecuted/Cancelled/NotFoundorschema_versionnegotiation that auto-enables foreign vaults. Keep API clearly narrower.IsPausedis true.Verification criteria
Done when:
make test-contracts(or project-equivalent pair + dex-common tests) passes; clippy clean on touched packages.OrderStatus;Unknown≠ proof of fill; no tombstones by design.dex_common.Non-goals (do not sneak in)
OwnerInventoryContinueOwnerIndexBackfill/OWNER_INDEX_*ORDER_TOMBSTONES/ terminal height-timePairProtocolResponse/PairApiFeatureLimitOrdererror semanticsmentioned in commit
eb9372544amentioned in merge request !1042
Implementation landed in !1042 (
feat/505-order-status-query).Acceptance criteria
Not in this MR (issue non-goals / optional)
mentioned in commit
42f1b455d4mentioned in commit
205fbd222amentioned in issue #530
mentioned in issue #546
mentioned in issue #597
mentioned in issue #717
marked as related to #717