Skip to content

feat(order): expose the account-wide get_my_trades ByAccount filter (5/5)#180

Draft
gregorydemay wants to merge 167 commits into
mainfrom
gdemay/DEFI-2901-account-fills
Draft

feat(order): expose the account-wide get_my_trades ByAccount filter (5/5)#180
gregorydemay wants to merge 167 commits into
mainfrom
gdemay/DEFI-2901-account-fills

Conversation

@gregorydemay

@gregorydemay gregorydemay commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Final PR of the persist-fills stack: the thin account-wide trades feed.

The per-order feed PR (#186) left the get_my_trades { ByAccount } arm as an empty-page stub, and the account-wide storage (the by-user index, the account-wide reader, and the settlement wiring that populates it) now lives in the fill-store PR (#179). This PR is the thin API layer that wires the ByAccount arm to that storage.

What this delivers:

  • A state-level reader that resolves the caller's user identity and returns their trades across all their orders, newest-first, via the fill store's account-wide reader.
  • The get_my_trades { ByAccount { after, length } } arm: owner-scoped, malformed TradeId cursor -> Err(RequestError(...)), unknown cursor -> Ok([]), length clamped to the max, each returned Trade carrying its own id.
  • Unit tests for the ByAccount arm (cross-order newest-first, paging without overlap or gap, owner scoping, unknown/empty cursor, malformed cursor, clamping, unregistered caller) and an end-to-end PocketIC test over three orders.

The Candid surface is unchanged: the ByAccount variant and TradeId cursor already existed in the type from #186; this PR only wires behavior. The check_candid_interface_compatibility test passes. No storage, index, settlement, or canbench changes — those land in #179.

Spec: docs/src/development/specs/DEFI-2901-persist-fills.md.

📚 PR stack

🤖 Generated with Claude Code

gregorydemay and others added 19 commits June 23, 2026 10:16
Add `filled_quote` and `filled_fee` to the order record so `get_my_orders`
exposes per-order realized notional and fee summaries (DEFI-2901, PR 1 of 3).

`filled_quote` is the cumulative realized quote notional
`Σ (maker_price × quantity / base_scale)`; a buy taker's released reservation
surplus is excluded, so VWAP is derivable as
`filled_quote × base_scale / filled_quantity`. `filled_fee` is the cumulative
realized fee in the order's receive token (base for a buy, quote for a sell),
the amount actually withheld.

`OrderUpdate` gains matching `quote_delta` / `fee_delta`, folded into the same
single read-modify-write as `filled_quantity` and `status`, each guarded by an
always-on overflow trap. Settlement computes the per-fill notional, fees, and
maker/taker roles once and feeds both the balance operations and the per-order
deltas from that single source.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…USDT

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`record_matching_event` materialized every fill's settlement into a
`Vec<FillSettlement>` and then iterated it twice — once to accrue the
per-order scalar deltas and once to build the balance operations. The
struct carries several u256-backed `Quantity` fields, so the allocation,
store, and reload were paid for on the matching hot path for every fill.

Fold the compute and both consumers into one pass over `output.fills`:
each fill's notional, fees, and roles are computed exactly once and feed
both the balance operations (always) and the per-order deltas (only under
`StableMemoryOptions::Write`), preserving the R11 single-source guarantee
without the intermediate `Vec`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Regenerate `canbench_results.yml` to capture the expected cost of the
`filled_quote`/`filled_fee` order-level rollup. The remaining increase on
`process_pending_orders` (matching ~+10-14%, the per-fill `qty` rollup
+28-35%, `order_history::apply_update` +3-6%) is the inherent per-fill
u256 notional/fee arithmetic over up to 1000 fills, which the single-pass
settlement already keeps to one computation per fill.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Compute the proptest generator's `filled_quote` the way the engine does
(`maker_price × filled_quantity / base_scale`, cf. `Fill::quote_amount`),
so generated `OrderRecord`s are semantically consistent with realized
quote notional instead of inflated by `base_scale`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Register the new ICP (8-dec) / ckUSDT (6-dec) fixture in the
`realized_scalars` worked-example test and restore the ckUSDT labels, so
the smallest-unit prices/fees literally mean ckUSDT amounts as in the
DEFI-2901 spec. Rewrite the module doc accordingly; base stays ICP so
`base_scale = 10^8` and every stored integer is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address review comment 3460414419: drop the base_scale formula from the
filled_quote doc (it is plainly cumulative quote in quote-smallest units),
spell out VWAP as volume-weighted average price, and remove the confusing
reservation-surplus sentence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…me scope

Address review comments 3460598457, 3460594366, and 3460487183 on the
settlement path in record_matching_event:

- FillSettlement now borrows the Fill it derives from and stores only the
  extra computed values (notional, taker/maker fee, surplus) instead of
  duplicating the Fill's seqs, side, and quantity.
- Consolidate the per-order apply-update under a single write gate; the cheap
  delta accumulation runs in the single fills pass and only the stable-memory
  writes are gated, so replay still does not double-count.
- Rename the stale bench scope status to apply_order_updates, reflecting the
  whole per-order apply-update it wraps (apply_update keeps its own inner
  scope).
- Drop the R# spec-requirement tags from code comments.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address review comment 3460709715: remove the standalone realized_scalars
module name and assert filled_quote/filled_fee from existing tests where they
extend naturally — fill_deducts_fees_on_both_sides now checks each side's
roll-up (catching a notional/quantity confusion and a taker/maker fee swap),
and should_write_once_for_taker_spanning_multiple_fills asserts the summed
filled_quote across distinct maker prices. The multi-level-sweep worked
example (with surplus exclusion and VWAP) and the both-ways single-batch case
move into the fees module as the only dedicated tests, since no existing test
covers them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address review comment 3460438909: register the trading pair with non-zero
maker/taker fee rates so the partially filled buy accrues a non-trivial
filled_fee (the maker-rate base fee) alongside a consistent filled_quote, and
assert the withheld fee on the buyer's settled base balance.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The status bench scope is renamed to apply_order_updates and the
FillSettlement rework changes the settlement path, so the persisted baseline
keys/values shift accordingly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…r-level-scalars

# Conflicts:
#	canister/src/state/mod.rs
The merge of origin/main shifted instruction counts within noise
(max +0.02%). Persist the refreshed canbench_results.yml so the
regression gate stays green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e_operations

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses review comment 3465971111.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses review comment 3465986823: `FillSettlement` now owns the
`Fill` (no lifetime parameter), built by consuming `output.fills` in the
single settlement pass, with all downstream access routed through
`settlement.fill`. Refreshes the canbench baseline for the resulting
sub-0.03% instruction drift.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…se_scale)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move `Fill` and `FillSettlement` into a new `canister/src/order/fill.rs`,
rewrite `fill_settlement` as the `FillSettlement::new` constructor, and turn
`push_balance_operations` and `accrue_fill` into methods on `FillSettlement`.
Condense the single-write-per-order comment at the settlement call site.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gregorydemay

Copy link
Copy Markdown
Contributor Author

🧐 Review — PR 3/3 (Account-wide ByAccount filter), R4.

Substance: clean. No blockers, no mediums, no nits. The incremental work faithfully mirrors the established OrderHistory by_user / orders_after shape and correctly reuses PR 2's primitives.

Verified against the in-scope acceptance criterion (R4) and its sub-points:

  • Write-gate / replay safety (R8): the two by_user index inserts happen inside FillStore::append, called only within the existing if write block in record_matching_event (state/mod.rs ~L438). The Skip replay path writes nothing — no double-write. ✓
  • Index↔record consistency: append writes both side-projected records and both index entries atomically (2 + 2); no path writes one without the other. trades_after resolves index→record and guards a dangling reference with a BUG: expect. ✓
  • Ownership: the account scan is keyed by the caller's resolved UserId (get_user_tradesuser_registry.lookup); an unregistered caller gets Ok([]). A cursor seq belonging to another user falls outside this user's (user, seq) prefix → CursorNotFoundOk([]). No way to read another principal's fills. ✓
  • Cross-order, newest-first: trades_after reverse-scans the account-wide index, genuinely spanning distinct orders (unit + state + integration tests assert order ids across multiple orders, descending). ✓
  • R5 / R10 dispositions: the ByAccount arm mirrors ByOrder exactly — malformed afterErr(InvalidCursor), unknown cursor → Ok([]), length clamped to MAX_FILLS_PER_RESPONSE. ✓
  • Cursor reuse: ByAccount parses the same FillSeq cursor established in PR 2; each Trade carries its own cursor and order_id. ✓
  • order_owner_user's two BUG: expects are unreachable on the Write path (placement records order_history + registers the user under the same gate before any fill can settle) and match the codebase's existing BUG: convention — consistent invariant handling, not a silent fallback.

Tests: thorough at every layer (unit trades_after cross-order/owner-scope/paging/unknown-cursor/clamp/unregistered; state-level multi-order + Skip-replay; PocketIC end-to-end over three orders with pagination no-overlap/gap, owner-scoping, unknown-cursor empty, malformed-cursor RequestError, and account↔per-order agreement). The PR 2 empty-page stub test was correctly replaced. All 425 canister unit tests pass locally. .did is unchanged (candid-backward-compat CI job passed).

Blocking reason — CI not green. gh pr checks 180 shows lint, unit-tests, reproducible-build, and benchmark still pending. Pending CI is a hard block on approval independent of review substance. Re-run once CI completes green and this flips to ready.

Tally: 0 🔴 / 0 🟠 / 0 🔵 — CI pending.

(Note: GitHub blocks --request-changes on one's own PR, so this verdict is posted as a conversation comment.)

VERDICT: CHANGES_REQUESTED

@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

canbench 🏋 (dir: canister) 5af2c1c 2026-07-02 10:15:40 UTC

canister/canbench_results.yml is up to date
📦 canbench_results_benchmark.csv available in artifacts

---------------------------------------------------

Summary:
  instructions:
    status:   No significant changes 👍
    counts:   [total 14 | regressed 0 | improved 0 | new 0 | unchanged 14]
    change:   [max +489 | p75 0 | median 0 | p25 -138.01K | min -204.49K]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 -0.01% | min -0.02%]

  heap_increase:
    status:   No significant changes 👍
    counts:   [total 14 | regressed 0 | improved 0 | new 0 | unchanged 14]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  stable_memory_increase:
    status:   No significant changes 👍
    counts:   [total 14 | regressed 0 | improved 0 | new 0 | unchanged 14]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

---------------------------------------------------
CSV results saved to canbench_results.csv

@gregorydemay gregorydemay left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧐 Review passed — no blockers/mediums/nits, CI green. Ready for human approval.

Substance was clean in the prior pass (PR 3/3, account-wide ByAccount filter, R4). CI is now fully green at unchanged head 83ecec1 — all checks pass (lint, unit-tests, integration-tests, reproducible-build, reproducibility-verify, benchmark, candid-backward-compat, and PR-meta checks). This completes the DEFI-2901 three-PR stack.

VERDICT: READY

@gregorydemay

Copy link
Copy Markdown
Contributor Author

🤖 Ready for your review — reviewer verdict is READY, CI 11/11 green, MERGEABLE.

PR 3 of 3 (final) for DEFI-2901 — account-wide get_my_trades { ByAccount }, stacked on #179.

Adds the FILLS_BY_USER_MEMORY_ID (MemoryId 8) (UserId, FillSeq) → FillKey secondary index and trades_after, mirroring OrderHistory's by_user / orders_after. The ByAccount arm (previously a PR-2 empty-page stub) now returns the caller's fills across all their orders, newest-first, paginated by the same Trade.cursorafter convention as ByOrder. Settlement writes the two by-user index entries alongside the side-projected fill records (2 + 2 inserts/fill) inside the existing Write gate, so post-upgrade replay (Skip) writes nothing. Owner-scoped — another principal's fills never appear. .did unchanged (the arm already existed from PR 2), candid backward-compat passes.

With this, the spec's full requirements set (R1–R12) is delivered across #171 / #179 / #180.

Left as a draft — marking ready, approval, and merge are yours.

gregorydemay and others added 2 commits June 25, 2026 13:35
…r-level-scalars

Integrate the DEFI-2853 Fill-or-Kill matching/settlement work from main with
this PR's order-level realized scalars: the settlement loop builds one
FillSettlement per fill (feeding both balance operations and per-order scalar
deltas) and also unreserves the placement reservation of each killed FOK order.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gregorydemay

Copy link
Copy Markdown
Contributor Author

🧐 Re-review after the stacked cascade merge (7a0ef75, DEFI-2853 FOK absorbed via base). Read-only; verifying the merge is correct and behavior-preserving for PR-3's R4 work — not re-reviewing PR-1/PR-2.

Merge integration — correct. I diffed the merge head against both parents (83ecec1 = PR-3's prior tip, 7e09676 = the FOK-bearing base). The conflict in record_matching_event (canister/src/state/mod.rs) was resolved correctly: under the single if write gate ALL three coexist, none dropped —

  • per-fill FillSettlement → balance ops + per-order scalar deltas (PR-1);
  • FillStore::append(taker_leg, taker_user, maker_leg, maker_user) writing 2 records + 2 (UserId, FillSeq)→FillKey by-user index entries, with owners resolved via order_owner_user from the correct taker_leg/maker_leg.order_id (PR-3's new 4-arg signature kept over the base's 2-arg one);
  • the FOK expired_orders kill-refund loop + OrderStatus::Expired + the + output.expired_orders.len() capacity bump (from main/base).

The refund loop sits outside the if write block by design — it only pushes Unreserve ops into balance_operations, applied later in record_settling_event; balance ops are not write-gated. No leftover conflict markers anywhere in the touched files.

FOK × fills — no fill written for a killed order. A killed FOK takes the MatchResult::Killed path into output.expired_orders and never enters output.fills (book.rs process_pending_orders). Since append only runs in for fill in &output.fills, a killed/expired FOK writes no fill record and no by-user index entry. R8 replay-safety holds: append is Write-gated; replay_under_skip_writes_no_fills asserts both feeds stay empty under Skip.

R4 acceptance — holds. Account-wide newest-first across orders via trades_after (reverse range over by_user, O(length), resolving each entry back through fills); owner-scoped (get_user_trades keys on the caller's UserId, unknown caller → Ok([])); cursorafter pagination; R5/R10 dispositions in the ByAccount arm of get_my_trades (malformed cursor → Err(InvalidCursor), unknown cursor → Ok([]), length clamped to MAX_FILLS_PER_RESPONSE). MemoryIds distinct (7 fills / 8 by_user / 9 seq); init and post_upgrade both wire the three regions. Taker and maker legs share one FillSeq per insert, so a cursor is valid across both ByOrder and ByAccount feeds.

Tests / CI. 431 canister unit tests pass on the merge head; the state-level account_trades_span_a_users_orders_newest_first and the end-to-end should_expose_account_wide_fills_via_get_my_trades_by_account (3 orders, owner-scoping, pagination, unknown/malformed cursor, account↔per-order agreement) both pass. Owner-resolution coverage is real: the owner-scoping assertions would fail if taker_user/maker_user were swapped in append. gh pr checks 180 all green, including candid-backward-compat (the .did is byte-identical to the base — ByAccount already existed in the type).

Maintainability sweep.

  • duplication: none found — FillByUserKey/by_user deliberately mirror FillKey/OrderHistory's by_user, no copy-paste blocks ≥10 lines.
  • unused derives: none — FillByUserKey derives only what's used (Ord/PartialOrd for the range scan, Copy/Clone/Eq for key+lookup, Debug for panics); correctly omits Hash.
  • primitive-obsession params: none — append/trades_after/get_user_trades take domain newtypes (UserId, FillSeq, FillRecord), not bare u64.
  • divergent invariant handling: cleared — order_owner_user traps (BUG:) on a missing settled order, get_user_trades returns Ok([]) for an unregistered caller; these are different situations (invariant breach vs. expected-absent input), so the divergence is justified and matches get_user_orders.
  • silent fallbacks: none on a failure path — the unwrap_or_default() in the query arm sits behind CursorNotFound/Ok([]) (expected empty), and the index→record resolution traps (BUG:) on an invariant breach rather than swallowing it.

No blockers, no mediums, no nits. Merge is behavior-preserving for PR-3 and CI is green.

VERDICT: READY

@gregorydemay

Copy link
Copy Markdown
Contributor Author

🤖 Updated after the stack cascade (merge 7a0ef75): merged the updated #179 down. FillStore::append (4-arg, writing the two fill records + the two by-user index entries) reconciled with the FOK refund path, all Write-gated; killed orders write neither fill nor index entry. Reviewer re-ran: READY. CI 11/11 green, MERGEABLE. Still a draft.

gregorydemay and others added 24 commits June 30, 2026 22:38
… tests

Add the generic page_by_user, range_primary, and seq-envelope tests (table-
driven cases plus paging proptests) and remove the now-redundant account-wide
trades_after tests from the trade store.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rebuild OrderHistory as a wrapper over the generic History store, retiring the
bespoke SeqOrderRecord and UserOrderKey (now a CompositeId via the core) and
their hand-written Storable impls. Drops the UserOrderKey/seq-record proptests,
covered by the CompositeId and seq-envelope tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…te test

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove three tests now covered elsewhere: the trade per-order length-clamp
(covered by the generic range_primary/page_by_user tests), the trade
unknown-order empty page (covered by the state get_user_order_trades test),
and the order-history insert/get roundtrip (covered by the generic
insert/get test and the public-record conversion test).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ry docs

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…canister-global

Addresses Copilot review comments 3501830909 and 3501830958.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ename insert to insert_once

Addresses review comments 3503500443 (reuse the named HistoryGlobalSeq
type for the stored insertion sequence instead of a raw u64) and
3503508148 (rename the insert method to insert_once), updating all call
sites in the order-history and trade-history wrappers and tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… store

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…to gdemay/DEFI-2901-fill-store

# Conflicts:
#	canister/src/order/mod.rs
#	canister/src/state/mod.rs
…o gdemay/DEFI-2901-fills-endpoint

# Conflicts:
#	canister/src/order/trades/mod.rs
… into gdemay/DEFI-2901-account-fills

# Conflicts:
#	canister/src/state/tests.rs
…s-endpoint

# Conflicts:
#	canister/canbench_results.yml
#	canister/src/history/tests.rs
#	canister/src/main.rs
#	canister/src/order/fill.rs
#	canister/src/order/mod.rs
#	canister/src/order/tests.rs
#	canister/src/order/trades/mod.rs
#	canister/src/order/trades/tests.rs
#	canister/src/state/event/mod.rs
#	canister/src/state/mod.rs
#	canister/src/state/snapshot/tests.rs
#	canister/src/state/tests.rs
#	canister/src/test_fixtures/event.rs
#	canister/src/test_fixtures/mod.rs
… into gdemay/DEFI-2901-account-fills

# Conflicts:
#	canister/src/state/mod.rs
A ByOrder `after` cursor is a TradeId (OrderId, FillSeq). Parsing previously
dropped the OrderId and kept only the FillSeq, so a cursor whose OrderId names
the counterparty leg — which shares the match's FillSeq — was wrongly accepted.
The per-order reader now takes the full TradeId cursor and rejects one whose
embedded OrderId differs from the queried order as an unknown cursor.

Addresses review thread 3508319771.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the get_my_trades filter in a GetMyTradesArgs struct mirroring
GetMyOrdersArgs, so future fields can be added without breaking the endpoint
signature. Add an OrderNotFound variant to GetMyTradesError and return it — as
get_my_orders does — when a ByOrder caller does not own the queried order,
replacing the prior empty-page behavior. Tighten the Trade candid docs and
correct the InvalidCursor wording to describe a trade-id cursor.

Addresses review threads 3510525808, 3510518734, 3510530340, 3510537441,
3510541153, 3508319809.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…_orders_trades

Removes the "cursor carrying the maker leg's order id" row that duplicated
case 1 of should_reject_a_cursor_from_the_other_leg_of_the_same_fill, and
aligns the internal GetMyTradesError::InvalidCursor doc with the public
"trade-id cursor" wording.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… into gdemay/DEFI-2901-account-fills

# Conflicts:
#	canister/src/tests.rs
Base automatically changed from gdemay/DEFI-2901-fills-endpoint to main July 2, 2026 09:35
…unt-fills

# Conflicts:
#	canister/oisy_trade.did
#	canister/src/lib.rs
#	canister/src/main.rs
#	canister/src/state/mod.rs
#	canister/src/state/tests.rs
#	canister/src/tests.rs
#	integration_tests/tests/tests.rs
#	libs/types/src/error/mod.rs
#	libs/types/src/lib.rs
Copilot AI review requested due to automatic review settings July 2, 2026 09:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Wires the previously-stubbed get_my_trades { ByAccount { after, length } } query arm to the fill store’s account-wide (per-user) trade reader, enabling an owner-scoped, newest-first trade feed across all of a caller’s orders with cursor pagination and request-error handling for malformed cursors.

Changes:

  • Implement TradesFilter::ByAccount in get_my_trades, including TradeId cursor parsing, max-length clamping, and TradeId-carrying results.
  • Add a state-level reader State::get_user_trades that resolves caller → user id and reads account-wide trades.
  • Add unit + integration coverage for ordering, pagination, owner scoping, unknown/malformed cursor behavior, clamping, and unregistered callers.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
integration_tests/tests/tests.rs Adds PocketIC E2E test validating the ByAccount feed across multiple orders and paging semantics.
canister/src/tests.rs Adds unit tests and helpers covering ByAccount behavior (ordering, paging, scoping, cursor handling, clamping, unregistered caller).
canister/src/state/tests.rs Adds state-level test ensuring unregistered callers get empty pages (with/without cursor).
canister/src/state/mod.rs Introduces State::get_user_trades to read account-wide trades via the trade history’s per-user index.
canister/src/lib.rs Implements the get_my_trades ByAccount arm: parse TradeId cursor, clamp length, read, and return public trades with ids.
canister/canbench_results.yml Updates bench numbers reflecting the small instruction-count delta from the new code paths.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread canister/src/lib.rs Outdated
…ByAccount

Addresses Copilot comment 3512105076: replace .unwrap_or_default() with
an explicit match on Ok/Err(CursorNotFound), mirroring the ByOrder arm so
the cursor-not-found to empty-page mapping is explicit. Behavior unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants