Contract: deterministic O(1) batch/ladder insertion (book-order sequence + position threading + ladder anchor hint) #266
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#266
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
Make on-chain batch/ladder limit-order insertion deterministically cheap on deep books by (1) inserting rungs in book-sort order instead of input order, (2) threading the resolved insert position from one rung to the next so interior rungs avoid redundant verify loads, and (3) accepting a single optional anchor hint on
LimitOrderLadderSpecso the first/boundary rung does not pay a full head walk.These three changes are bundled because they all live in the pair contract's batch placement path and share the same insertion loop, the same
steps/max_adjust_stepsbudget, and the same correctness invariants (L5/L12/L14).Current codebase
Batch placement loops over
ordersin input order, chaining the previous successfully placed rung id as the hint for the next rung:smartcontracts/contracts/pair/src/limit_placement.rs—execute_place_limit_orders_batch(loop ~L126–L195).let hint_after = item.hint_after_order_id.or(last_placed_hint);theninsert_bid_with_id/insert_ask_with_id; on successlast_placed_hint = Some(id); onLimitInsertStepsExceededthe rung is skipped (skipped_count += 1).execute_place_limit_order_ladder(~L42–L57) expands the spec viaexpand_limit_ladderand calls the same batch fn.smartcontracts/packages/dex-common/src/limit_placement.rs—LimitOrderPlacementItem(~L18–L29, hashint_after_order_id),LimitOrderLadderSpec(~L36–L47, no hint field),expand_limit_ladder(~L60–L97) setshint_after_order_id: Noneon every rung.smartcontracts/contracts/pair/src/orderbook.rs—find_insert_bid/find_insert_ask(~L666–L721) andtry_insert_after_hint_*,walk_insert_*_toward_head,walk_insert_*_from,walk_insert_*_from_head(~L408–L662). Ids are pre-reserved byreserve_order_id_block;insert_*_with_idacceptshint_after,max_adjust_steps,update_escrow: false.smartcontracts/contracts/pair/src/contract.rs— hook dispatch (~L701–L712);UpdateLimitOrderPricehandler (~L1178–L1201) already acceptshint_after_order_id.Why chaining is weak today
last_placed_hintisNone). On a deep book this scales with head→first-rung distance.WalkTowardHeadinstead of O(1) (seelimit_order_tests::limit_batch_chained_near_miss_hint_directional_walk_succeeds~L3934). With a tightmax_adjust_stepsinterior rungs get skipped (the 0.99/1.0/0.98 batch ~L3793).next) to re-verify the position it could have inherited from the previous insert.Why this is needed
Deep, actively-traded books are exactly where ladders are most useful and most expensive. Without these changes, a routine bid ladder degrades to N directional walks (gas blowup) or partial skips (silent rung loss), and there is no way for a client to cheaply anchor the first rung. This is the on-chain half of the deep-book ladder effort (frontend half: companion issue; indexer half: companion issue).
Constraints / guardrails
batch_placement_order_ids_match_sequential_singlesmust still pass: rungorder_ids must map to input index (ascending, contiguous fromreserve_order_id_block), even if the insertion traversal is reordered. Reorder traversal only — never the id-to-input mapping.order_id(composite key(price, order_id)). The traversal sort key must be the full composite key so equal-price rungs insert in id order.stepsbudget per rung (L5). Position threading must not let one rung borrow another rung's budget; each rung still bounded by its ownmax_adjust_steps(min with hard cap).ORDER_NEXT_IDwrite + onePENDING_ESCROW_*write per token side (L12). Storage-collapse semantics must be preserved.LimitBatchNoRungsPlaced).parse_limit_order_placements_columnar). Emitplace_limit_orderevents in id/input order regardless of insertion traversal order.hint_after_order_id(exists, correct side, linked) and fall back to head walk otherwise.Relevant files
smartcontracts/contracts/pair/src/limit_placement.rssmartcontracts/contracts/pair/src/orderbook.rssmartcontracts/packages/dex-common/src/limit_placement.rssmartcontracts/contracts/pair/src/contract.rssmartcontracts/contracts/pair/src/msg.rs(Cw20HookMsg::PlaceLimitOrderLadder)smartcontracts/tests/src/limit_order_tests.rsdocs/limit-orders.md,docs/contracts-security-audit.md(L5/L12/L14),docs/integrators.md,skills/AGENTS_LIMIT_ORDER_BATCH_LADDER.mdRecommended direction
reserve_order_id_block, build(input_index, id, price)tuples, assignidby input index (unchanged), then compute a traversal order sorted by the side's composite key (bid_before/ask_beforesemantics; equal price → ascending id). Iterate inserts in that traversal order. Chainlast_placed_hintalong the traversal so each new rung's predecessor is the previously inserted rung → exact O(1) verify. Collect results and emitplace_limit_orderattributes in input/id order.find_insert_*(or add a sibling entrypoint) to optionally accept the previous insert's resolved(prev, next)neighbors as a verified cursor. When the new rung's composite key falls between that cursor'sprevandnext, insert without re-loading the hint. Falls back to the existing hint/anchor/head path when the cursor does not bracket the new key. Keepstepsaccounting honest (cursor reuse costs 0 extra loads).hint_after_order_id: Option<u64>toLimitOrderLadderSpec(defaultNone,#[serde(default)]). Inexpand_limit_ladder, set the boundary rung'shint_after_order_idto the anchor (head-most rung in book order); leave other rungsNoneso on-chain chaining fills them. Validate via the existingtry_insert_after_hint_*path.Acceptance criteria
WalkTowardHead), measured by a step counter / gas assertion.batch_placement_order_ids_match_sequential_singlesstill passes — ids map to input order.order_id.LimitOrderLadderSpec.hint_after_order_idlets the boundary rung place under a tightmax_adjust_stepson a deep book where it would otherwise be skipped.ORDER_NEXT_ID, onePENDING_ESCROW_*per touched side.place_limit_ordercolumns) unchanged for the indexer.Test plan — all paths
max_adjust_steps; assert placed vs skipped counts match a hand-computed expectation.max_adjust_steps, is skipped + refunded; later rungs still attempt; chaining continues from last success.LimitBatchNoRungsPlaced.orders.len()==1) unaffected.UpdateLimitOrderPricestill honorshint_after_order_id(regression).Test plan — attack / abuse / hack vectors
max_adjust_steps; no single rung exceeds its own budget via threaded cursor.reserve_order_id_blockboundary unchanged.orderbook::prop_escrow_dll_after_random_insertsextended with reordered-traversal inserts — DLL stays well-formed (no cycles, head/tail consistent, every node reachable).Verification criteria
cd smartcontracts && cargo test -p cl8y-dex-tests limit_batch place_limit_order_ladder -- --nocapturegreen, including new step-count/gas assertions.cargo test -p cl8y-dex-tests batch_placement_order_ids_match_sequential_singlesgreen.cargo clippy --all-targets -- -D warningsand schema regen clean.limit-orders.md+AGENTS_LIMIT_ORDER_BATCH_LADDER.mdupdated to describe book-order traversal, cursor threading, and the ladder anchor field.Companion issues for the deep-book ladder/hinting effort:
Dependency: #266 (this) and #267 are foundational; #268 depends on both. The ladder anchor field added here is resolved client-side via #267.
mentioned in issue #267
mentioned in issue #268
mentioned in commit
5b171675b0Implementation summary (merged to
main@5b17167)Implemented deterministic O(1) batch/ladder limit-order insertion for deep books:
Book-order traversal —
execute_place_limit_orders_batchassigns ids by input index, sorts inserts by composite book key (bid_before/ask_before), and emitsplace_limit_orderwasm attrs in input/id order (indexer columnar zip unchanged).Insert-position threading — new
InsertThreadCursorinorderbook.rs;insert_*_with_id_for_batchthreads resolved(prev, next, next_price)from each successful insert so interior rungs verify in O(0) loads when bracketed. Falls back to existing hint / directional / head-walk paths per L14.Ladder anchor hint — optional
LimitOrderLadderSpec.hint_after_order_id(#[serde(default)]) applied to the head-most rung in book order viaexpand_limit_ladder; validated through existingtry_insert_after_hint_*path.Docs / invariants updated
docs/contracts-security-audit.mddocs/limit-orders.md,docs/integrators.mdskills/AGENTS_LIMIT_ORDER_BATCH_LADDER.md(new invariant §11 + crosslinks to #267/#268)LimitOrderLadderSpecWire.hint_after_order_id?Tests added / updated
limit_batch_bid_ladder_book_order_traversal_places_all_on_empty_booklimit_batch_equal_price_fifo_by_order_idlimit_batch_ladder_anchor_hint_places_boundary_on_deep_booklimit_batch_ladder_stale_anchor_still_placesexpand_ladder_anchor_on_boundary_rung_ascending_bid(dex-common)Verification checklist
cd smartcontracts && cargo test -p cl8y-dex-tests limit_batch batch_placement_order_ids place_limit_order_laddercargo test -p cl8y-dex-pair prop_escrowcargo clippy --all-targets -- -D warningsmax_adjust_stepsorder_id; book head = lowest idLimitBatchNoRungsPlacedwhen none placebatch_placement_order_ids_match_sequential_singlesgreen (L12 id mapping)ORDER_NEXT_ID+ onePENDING_ESCROW_*write per side per batch (L12)Follow-ups (companion issues)
Requesting verification from the QA agent team when convenient.
mentioned in merge request !733
Verified #266 on
d6701c4(deterministic O(1) batch/ladder insertion). Contract-only.Tests (in make test-contracts = 402/0):
limit_batch_equal_price_fifo_by_order_id (FIFO = ascending order_id), batch_placement_order_ids_match_sequential_singles
(L12 id→input mapping preserved), limit_batch_ladder_anchor_hint_places_boundary_on_deep_book +
limit_batch_ladder_stale_anchor_still_places (anchor valid/stale), limit_batch_partial_success_skips_book_walk_failures
(#206 partial skip), place_limit_order_ladder_five_rungs, limit_batch_item_explicit_hint_places_on_deep_book — 11/11.
Live ladder gas on LocalTerra (empty/near-empty book, one tx):
(book-order traversal + InsertThreadCursor giving O(1)-bracketed interior rungs).
Good to close from my side once !733 merges. @PlasticDigits
mentioned in issue #263
mentioned in issue #546
marked as related to #546
mentioned in issue #704
mentioned in issue #708