Skip to content

Implement GET /v1/trades/{address} endpoint#51

Merged
graphite-app[bot] merged 1 commit into
mainfrom
implement-trades-address
May 8, 2026
Merged

Implement GET /v1/trades/{address} endpoint#51
graphite-app[bot] merged 1 commit into
mainfrom
implement-trades-address

Conversation

@findolor
Copy link
Copy Markdown
Collaborator

@findolor findolor commented Feb 24, 2026

Chained PRs

Motivation

See issues:

Add a paginated endpoint to retrieve all trades for a given owner address across all configured orderbooks, with optional time filtering. Replaces #40, which was based on the old chain (#39) and couldn't merge cleanly. This branch is rebased on top of implement-trades-tx-hash (PR #50).

Solution

  • Implement GET /v1/trades/{address} with pagination (page, pageSize) and optional time filters (startTime, endTime)
  • Extend TradesDataSource trait with get_trades_for_owner method that iterates all configured orderbooks and aggregates trades and total counts
  • Wire the route handler to use RaindexTradesDataSource via run_with_client, converting TradesPaginationParams into PaginationParams + TimeFilter with overflow validation
  • Map each RaindexTrade into TradeByAddress response type (tx_hash, formatted amounts, TokenRef for input/output tokens, order_hash, timestamp, block_number)
  • Compute pagination metadata (total_pages via div_ceil, has_more)
  • Update rain.orderbook submodule (ff9578ca612a4f) to bring in get_trades_for_owner(), RaindexTradesListResult, PaginationParams, TimeFilter, OrderbookIdentifierParams, and address casing fix
  • Update get_by_tx.rs mock to implement the new trait method
  • Add AGENTS.md submodule rule: never modify submodule code directly
  • Unit tests for success (with amount/token assertions), empty results, query failure, 401 without auth, and 500 on bad config

Checks

By submitting this for review, I'm confirming I've done the following:

  • made this PR as small as possible
  • unit-tested any new functionality
  • linked any relevant issues or PRs
  • included screenshots (if this involves a front-end change)

fix #29

Summary by CodeRabbit

  • New Features

    • Implemented a trade lookup endpoint for wallet addresses with pagination and time-range filtering.
  • Documentation

    • Added guidance for managing external submodule code (don’t modify submodules directly; request upstream updates and bump pointers).
  • Tests

    • Expanded tests to cover successful responses, empty results/pagination, data-source failures, and missing-auth handling.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 24, 2026

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3898655f-fb73-4474-b9ff-9d5f738c89e4

📥 Commits

Reviewing files that changed from the base of the PR and between cded42d and 196377c.

📒 Files selected for processing (4)
  • AGENTS.md
  • src/routes/trades/get_by_address.rs
  • src/routes/trades/get_by_tx.rs
  • src/routes/trades/mod.rs
✅ Files skipped from review due to trivial changes (1)
  • AGENTS.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/routes/trades/get_by_tx.rs
  • src/routes/trades/mod.rs
  • src/routes/trades/get_by_address.rs

📝 Walkthrough

Walkthrough

Adds TradesDataSource::get_trades_for_owner and Raindex-backed implementation, wires get_trades_by_address to call processing logic that applies pagination and time filters, maps Raindex trades into API responses with pagination metadata, adds unit/route tests, and updates AGENTS.md with a Submodules note.

Changes

Trade Listing by Address

Layer / File(s) Summary
Trait Contract and Dependencies
src/routes/trades/mod.rs
Module imports updated for Address, PaginationParams, and TimeFilter. TradesDataSource trait gains async get_trades_for_owner(owner, pagination, time_filter) returning RaindexTradesListResult or ApiError.
Trait Implementation
src/routes/trades/mod.rs
RaindexTradesDataSource implements get_trades_for_owner by delegating to RaindexClient::get_trades_for_owner, logging and mapping client errors to ApiError::Internal.
Route Handler Wiring
src/routes/trades/get_by_address.rs
get_trades_by_address reads SharedRaindexProvider from Rocket State, constructs RaindexTradesDataSource, and delegates to process_get_trades_by_address instead of returning not-implemented.
Trade Transformation and Response Building
src/routes/trades/get_by_address.rs
process_get_trades_by_address validates pagination, builds TimeFilter, calls ds.get_trades_for_owner, maps trades to TradeByAddress (tx hash, formatted amounts, token metadata, optional order-hash parsing, safe timestamp/block u64 conversion), and computes pagination metadata (total_trades, total_pages, has_more).
Tests and Test Fixtures
src/routes/trades/get_by_address.rs, src/routes/trades/get_by_tx.rs
Adds tests for success mapping, empty results pagination, data-source error propagation, and 401 auth failure. get_by_tx test module imports PaginationParams and TimeFilter and extends MockTradesDataSource with an unimplemented get_trades_for_owner.
Engineering Documentation
AGENTS.md
Adds Submodules guidance: do not edit submodule code directly; request upstream changes and bump submodule pointer after upstream update.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • hardyjosh
  • 0xgleb

Poem

I’m a rabbit in Raindex land,
hopping through trades, pencil in hand,
paging and filtering with tidy cheer,
parsing the hashes that hop near,
the endpoint sings — the data’s clear! 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly and concisely describes the main change: implementing the GET /v1/trades/{address} endpoint, which is the primary objective.
Linked Issues check ✅ Passed The PR fully implements issue #29 requirements: GET /v1/trades/{address} endpoint with TradesPaginationParams support and TradesByAddressResponse return type.
Out of Scope Changes check ✅ Passed All changes align with implementing the GET /v1/trades/{address} endpoint, except the AGENTS.md submodule policy addition is tangential but complementary documentation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch implement-trades-address

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Collaborator Author

findolor commented Apr 30, 2026


How to use the Graphite Merge Queue

Add the label add-to-gt-merge-queue to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@findolor findolor force-pushed the implement-trades-address branch from 0a05197 to 10c59f1 Compare April 30, 2026 11:55
@findolor findolor force-pushed the implement-trades-tx-hash branch from 39f65e4 to 69db573 Compare May 7, 2026 13:28
@findolor findolor force-pushed the implement-trades-address branch from 10c59f1 to e72d88e Compare May 7, 2026 13:28
@findolor findolor requested review from 0xgleb, JuaniRios and hardyjosh May 7, 2026 14:34
@findolor findolor force-pushed the implement-trades-tx-hash branch from 69db573 to f7ecb76 Compare May 7, 2026 14:34
@findolor findolor force-pushed the implement-trades-address branch from e72d88e to 249f984 Compare May 7, 2026 14:34
@findolor findolor force-pushed the implement-trades-address branch from 249f984 to 3d47dad Compare May 7, 2026 17:41
@findolor findolor force-pushed the implement-trades-tx-hash branch from f7ecb76 to 6029bce Compare May 7, 2026 17:41
@graphite-app graphite-app Bot changed the base branch from implement-trades-tx-hash to graphite-base/51 May 7, 2026 21:36
@graphite-app
Copy link
Copy Markdown

graphite-app Bot commented May 7, 2026

Merge activity

@graphite-app graphite-app Bot changed the base branch from graphite-base/51 to main May 7, 2026 21:58
@findolor findolor force-pushed the implement-trades-address branch from 3d47dad to cded42d Compare May 8, 2026 06:00
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/routes/trades/get_by_address.rs (1)

93-93: 💤 Low value

Silent .ok() on order_hash parsing discards errors without any observability.

FixedBytes::<32>::from_str(&trade.order_hash().to_string()).ok() will silently produce None if the string representation can't be re-parsed. If order_hash() already returns a B256/FixedBytes<32>, a direct .into() would be cleaner and infallible. If the string path is unavoidable, at least emit a tracing::warn on failure so unexpected null order hashes are visible in logs.

♻️ Option A — direct conversion (if order_hash() returns B256)
-let order_hash = FixedBytes::<32>::from_str(&trade.order_hash().to_string()).ok();
+let order_hash = Some(trade.order_hash());
♻️ Option B — keep string path but add tracing on failure
-let order_hash = FixedBytes::<32>::from_str(&trade.order_hash().to_string()).ok();
+let order_hash = FixedBytes::<32>::from_str(&trade.order_hash().to_string())
+    .inspect_err(|e| tracing::warn!(error = %e, "failed to parse order hash"))
+    .ok();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/routes/trades/get_by_address.rs` at line 93, The current call
FixedBytes::<32>::from_str(&trade.order_hash().to_string()).ok() swallows parse
errors; change it to a safe, observable conversion: if trade.order_hash()
already returns a B256/FixedBytes<32>, replace the whole expression with
trade.order_hash().into() (infallible), otherwise keep the string path but
handle the Result instead of calling .ok() — match or map_err to log a
tracing::warn including the original string and the parse error and propagate or
skip accordingly; update the variable where order_hash is used so it reflects
the Result/Option handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/routes/trades/get_by_address.rs`:
- Around line 59-72: Add an explicit check for page_size == 0 on params before
constructing PaginationParams so we return ApiError::BadRequest for zero page
sizes instead of forwarding Some(0) to the data source; specifically, in the
handler that reads params.page_size and builds PaginationParams (where page_size
is assigned and PaginationParams.page_size is set), validate
params.page_size.unwrap_or(20) (or the raw params.page_size if present) and if
it equals 0 return ApiError::BadRequest("page_size must be > 0"), otherwise
proceed to the existing try_into() conversion and construction of
PaginationParams.page_size.

---

Nitpick comments:
In `@src/routes/trades/get_by_address.rs`:
- Line 93: The current call
FixedBytes::<32>::from_str(&trade.order_hash().to_string()).ok() swallows parse
errors; change it to a safe, observable conversion: if trade.order_hash()
already returns a B256/FixedBytes<32>, replace the whole expression with
trade.order_hash().into() (infallible), otherwise keep the string path but
handle the Result instead of calling .ok() — match or map_err to log a
tracing::warn including the original string and the parse error and propagate or
skip accordingly; update the variable where order_hash is used so it reflects
the Result/Option handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 08807d7c-44a5-499b-a305-abbc54a22159

📥 Commits

Reviewing files that changed from the base of the PR and between c94fa3a and cded42d.

📒 Files selected for processing (4)
  • AGENTS.md
  • src/routes/trades/get_by_address.rs
  • src/routes/trades/get_by_tx.rs
  • src/routes/trades/mod.rs

Comment on lines +59 to +72
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);

let pagination = PaginationParams {
page: Some(
page.try_into()
.map_err(|_| ApiError::BadRequest("page value too large".into()))?,
),
page_size: Some(
page_size
.try_into()
.map_err(|_| ApiError::BadRequest("page_size value too large".into()))?,
),
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add explicit page_size == 0 validation before calling the data source.

If a caller sends page_size=0 the try_into() overflow check passes (0 fits any target integer type), so PaginationParams { page_size: Some(0) } is forwarded to the data source. The total_pages guard on line 127 prevents a divide-by-zero in this file, but the subgraph query itself may return an error or unexpected data, yielding a 500 instead of the correct 400.

🛡️ Proposed fix
 let page = params.page.unwrap_or(1);
 let page_size = params.page_size.unwrap_or(20);

+if page_size == 0 {
+    return Err(ApiError::BadRequest("page_size must be greater than 0".into()));
+}
+
 let pagination = PaginationParams {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
let pagination = PaginationParams {
page: Some(
page.try_into()
.map_err(|_| ApiError::BadRequest("page value too large".into()))?,
),
page_size: Some(
page_size
.try_into()
.map_err(|_| ApiError::BadRequest("page_size value too large".into()))?,
),
};
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(20);
if page_size == 0 {
return Err(ApiError::BadRequest("page_size must be greater than 0".into()));
}
let pagination = PaginationParams {
page: Some(
page.try_into()
.map_err(|_| ApiError::BadRequest("page value too large".into()))?,
),
page_size: Some(
page_size
.try_into()
.map_err(|_| ApiError::BadRequest("page_size value too large".into()))?,
),
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/routes/trades/get_by_address.rs` around lines 59 - 72, Add an explicit
check for page_size == 0 on params before constructing PaginationParams so we
return ApiError::BadRequest for zero page sizes instead of forwarding Some(0) to
the data source; specifically, in the handler that reads params.page_size and
builds PaginationParams (where page_size is assigned and
PaginationParams.page_size is set), validate params.page_size.unwrap_or(20) (or
the raw params.page_size if present) and if it equals 0 return
ApiError::BadRequest("page_size must be > 0"), otherwise proceed to the existing
try_into() conversion and construction of PaginationParams.page_size.

## Chained PRs
- #50

## Motivation

See issues:
- #29
- #30

Add a paginated endpoint to retrieve all trades for a given owner address across all configured orderbooks, with optional time filtering. Replaces #40, which was based on the old chain (#39) and couldn't merge cleanly. This branch is rebased on top of `implement-trades-tx-hash` (PR #50).

## Solution

- Implement `GET /v1/trades/{address}` with pagination (`page`, `pageSize`) and optional time filters (`startTime`, `endTime`)
- Extend `TradesDataSource` trait with `get_trades_for_owner` method that iterates all configured orderbooks and aggregates trades and total counts
- Wire the route handler to use `RaindexTradesDataSource` via `run_with_client`, converting `TradesPaginationParams` into `PaginationParams` + `TimeFilter` with overflow validation
- Map each `RaindexTrade` into `TradeByAddress` response type (tx_hash, formatted amounts, `TokenRef` for input/output tokens, order_hash, timestamp, block_number)
- Compute pagination metadata (total_pages via `div_ceil`, has_more)
- Update `rain.orderbook` submodule (`ff9578c` → `a612a4f`) to bring in `get_trades_for_owner()`, `RaindexTradesListResult`, `PaginationParams`, `TimeFilter`, `OrderbookIdentifierParams`, and address casing fix
- Update `get_by_tx.rs` mock to implement the new trait method
- Add AGENTS.md submodule rule: never modify submodule code directly
- Unit tests for success (with amount/token assertions), empty results, query failure, 401 without auth, and 500 on bad config

## Checks

By submitting this for review, I'm confirming I've done the following:
- [x] made this PR as small as possible
- [x] unit-tested any new functionality
- [x] linked any relevant issues or PRs
- [ ] included screenshots (if this involves a front-end change)

fix #29

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **New Features**
  * Implemented the trade lookup endpoint for wallet addresses with pagination and time-range filtering capabilities.

* **Documentation**
  * Added guidelines for managing external code dependencies.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@graphite-app graphite-app Bot force-pushed the implement-trades-address branch from cded42d to 196377c Compare May 8, 2026 06:46
@graphite-app graphite-app Bot merged commit 196377c into main May 8, 2026
10 checks passed
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.

Implement GET /v1/trades/{address}

3 participants