Skip to content

Add token details endpoint#204

Merged
anxolin merged 2 commits intomainfrom
expose-token-details
Mar 7, 2026
Merged

Add token details endpoint#204
anxolin merged 2 commits intomainfrom
expose-token-details

Conversation

@anxolin
Copy link
Contributor

@anxolin anxolin commented Mar 7, 2026

Summary

Adds a couple of endpoints to return token details (symbol, name, decimals, etc)

image
  • Add GET /:chainId/tokens/:tokenAddress/details endpoint to get details (name, symbol, decimals) for a single token
  • Add POST /:chainId/tokens/details endpoint to get details for multiple tokens (up to 100) in a single request
  • Add TokenDetailService wrapping the existing Erc20Repository
image

Test plan

image

Single token - WETH on Mainnet

curl -s http://localhost:3001/1/tokens/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/details | jq .

Single token - USDC on Mainnet

curl -s http://localhost:3001/1/tokens/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/details | jq .

Single token - non-existent address (expect 404)

curl -s -w "\nHTTP status: %{http_code}\n" http://localhost:3001/1/tokens/0x0000000000000000000000000000000000000001/details

Single token - unsupported chain (expect 400)

curl -s -w "\nHTTP status: %{http_code}\n" http://localhost:3001/999/tokens/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/details 

Single token - invalid address format (expect 400)

curl -s -w "\nHTTP status: %{http_code}\n" http://localhost:3001/1/tokens/not-an-address/details 

Multiple tokens on Mainnet

curl -s -X POST http://localhost:3001/1/tokens/details \
  -H "Content-Type: application/json" \
  -d '{
    "tokenAddresses": [
      "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
      "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
      "0x6B175474E89094C44Da98b954EedeAC495271d0F"
    ]
  }' | jq .

Multiple tokens - unsupported chain (expect 400)

curl -s -w "\nHTTP status: %{http_code}\n" -X POST http://localhost:3001/999/tokens/details \
  -H "Content-Type: application/json" \
  -d '{"tokenAddresses": ["0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"]}' 

Multiple tokens - empty array (expect 400)

curl -s -w "\nHTTP status: %{http_code}\n" -X POST http://localhost:3001/1/tokens/details \
  -H "Content-Type: application/json" \
  -d '{"tokenAddresses": []}' 

Multiple tokens - invalid address in array (expect 400)

curl -s -w "\nHTTP status: %{http_code}\n" -X POST http://localhost:3001/1/tokens/details \
  -H "Content-Type: application/json" \
  -d '{"tokenAddresses": ["not-valid"]}' 

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 7, 2026

📝 Walkthrough

Walkthrough

Adds a TokenDetailService (interface, implementation, DI symbol), wires it into the API container, and exposes two new API routes: GET /details for a single token and POST /details for batch token lookups with JSON schema validation.

Changes

Cohort / File(s) Summary
DI configuration
apps/api/src/app/inversify.config.ts
Wires TokenDetailService into the API container; reorganizes several repository/service imports to pull from the repositories package and reuses existing service symbols.
Service implementation & exports
libs/services/src/TokenDetailService/TokenDetailService.ts, libs/services/src/index.ts
Adds TokenDetailService interface, TokenDetailServiceMain implementation (single & batch fetch), and tokenDetailServiceSymbol; exports the service from the services index.
API routes
apps/api/src/app/routes/__chainId/tokens/__tokenAddress/details.ts, apps/api/src/app/routes/__chainId/tokens/details.ts
New GET route for single-token details and POST route for batch token details; both use DI to obtain the service and include request/response JSON schemas and 404 handling for missing tokens.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client as Client
    participant API as API Route
    participant DI as Inversify Container
    participant Service as TokenDetailService
    participant Repo as Erc20Repository

    Client->>API: HTTP GET/POST /:chainId/tokens/.../details
    API->>DI: resolve(tokenDetailServiceSymbol)
    DI->>Service: provide TokenDetailServiceMain
    API->>Service: getTokenDetails / getTokensDetails(chainId, address(es))
    Service->>Repo: erc20Repository.get(chainId, address)
    Repo-->>Service: Erc20 | null
    Service-->>API: Erc20 | null (or list)
    API-->>Client: 200 JSON or 404 {"message":"Token not found"}
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I nibble bytes in a cozy burrow,
Token details hop from repo to furrow,
Routes lined up, DI threads sewn tight,
Fetching names and decimals by moonlight,
A tiny rabbit cheers the code tonight 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title "Add token details endpoint" accurately summarizes the main change: introducing new endpoints for retrieving token details. It is concise, clear, and directly reflects the primary objective of the pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch expose-token-details

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
Contributor

@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: 2

🧹 Nitpick comments (2)
libs/services/src/TokenDetailService/TokenDetailService.ts (1)

43-47: Consider Promise.allSettled for more resilient batch fetching.

If a single erc20Repository.get call rejects (e.g., network error), Promise.all will short-circuit and reject the entire batch. For a user-facing batch endpoint, partial success may be preferable—returning details for tokens that succeeded while excluding failures.

♻️ Proposed alternative using Promise.allSettled
   async getTokensDetails(
     chainId: string,
     tokenAddresses: string[]
   ): Promise<(Erc20 | null)[]> {
     const supportedChainId = toSupportedChainId(chainId);
-    return Promise.all(
-      tokenAddresses.map((address) =>
-        this.erc20Repository.get(supportedChainId, address)
-      )
-    );
+    const results = await Promise.allSettled(
+      tokenAddresses.map((address) =>
+        this.erc20Repository.get(supportedChainId, address)
+      )
+    );
+    return results.map((result) =>
+      result.status === 'fulfilled' ? result.value : null
+    );
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/services/src/TokenDetailService/TokenDetailService.ts` around lines 43 -
47, The current batch fetch in TokenDetailService.ts uses Promise.all over
tokenAddresses.map calling this.erc20Repository.get, which will reject the
entire batch if any single fetch fails; change this to use Promise.allSettled on
the mapped promises, then filter and return only the fulfilled results
(extracting their values) while logging or handling rejected entries as needed
so partial successes are returned from the method instead of a hard failure.
apps/api/src/app/routes/__chainId/tokens/details.ts (1)

38-64: Consider extracting shared tokenSchema and errorSchema to reduce duplication.

The tokenSchema here is identical to successSchema in __tokenAddress/details.ts, and errorSchema is duplicated across both files. Extracting these to a shared location (e.g., schemas.ts) would improve maintainability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/src/app/routes/__chainId/tokens/details.ts` around lines 38 - 64,
Duplicate JSON schemas (tokenSchema and errorSchema) are present; extract them
to a shared module and import them where needed. Create a new exported file
(e.g., schemas.ts) that exports tokenSchema and errorSchema (and reuse
successSchema if present), update the current file to import tokenSchema and
errorSchema instead of declaring them inline, and update the other file that
declares successSchema/errorSchema to import from the same shared module so both
routes reference the single source of truth.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/api/src/app/routes/__chainId/tokens/__tokenAddress/details.ts`:
- Around line 87-101: The route handler's current flow lets toSupportedChainId
throw an unhandled exception for unknown chain IDs, causing a 500 instead of
returning a 400; update the async route handler (the function that calls
tokenDetailService.getTokenDetails) to catch the error from toSupportedChainId
(or wrap the chainId conversion call) and respond with reply.code(400).send({
message: 'Unsupported chainId' }) for that specific error, leaving other errors
to propagate or be handled as before; you can also move equivalent try/catch
into tokenDetailService.getTokenDetails if conversion happens there, but ensure
the handler returns 400 for unsupported chain ids.

In `@apps/api/src/app/routes/__chainId/tokens/details.ts`:
- Around line 114-130: The route handler currently uses request.params.chainId
directly and allowing toSupportedChainId to throw will produce a 500; wrap the
chain ID normalization in a try/catch (call toSupportedChainId(chainId)) at the
start of the async function and on error reply.code(400).send({ message:
'Unsupported chainId' }) (or include the error.message for more detail), then
use the validated/converted chainId when calling
tokenDetailService.getTokensDetails so the exception is handled and a proper 400
response is returned.

---

Nitpick comments:
In `@apps/api/src/app/routes/__chainId/tokens/details.ts`:
- Around line 38-64: Duplicate JSON schemas (tokenSchema and errorSchema) are
present; extract them to a shared module and import them where needed. Create a
new exported file (e.g., schemas.ts) that exports tokenSchema and errorSchema
(and reuse successSchema if present), update the current file to import
tokenSchema and errorSchema instead of declaring them inline, and update the
other file that declares successSchema/errorSchema to import from the same
shared module so both routes reference the single source of truth.

In `@libs/services/src/TokenDetailService/TokenDetailService.ts`:
- Around line 43-47: The current batch fetch in TokenDetailService.ts uses
Promise.all over tokenAddresses.map calling this.erc20Repository.get, which will
reject the entire batch if any single fetch fails; change this to use
Promise.allSettled on the mapped promises, then filter and return only the
fulfilled results (extracting their values) while logging or handling rejected
entries as needed so partial successes are returned from the method instead of a
hard failure.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cb984f19-897e-4a8e-afb0-d7eb472bc584

📥 Commits

Reviewing files that changed from the base of the PR and between a58ca33 and 64cff26.

⛔ Files ignored due to path filters (1)
  • libs/repositories/src/gen/cow/cow-api-types.ts is excluded by !**/gen/**
📒 Files selected for processing (5)
  • apps/api/src/app/inversify.config.ts
  • apps/api/src/app/routes/__chainId/tokens/__tokenAddress/details.ts
  • apps/api/src/app/routes/__chainId/tokens/details.ts
  • libs/services/src/TokenDetailService/TokenDetailService.ts
  • libs/services/src/index.ts

Switch from ChainIdOrSlugSchema to SupportedChainIdSchema so Fastify
rejects unsupported chains with a 400 validation error instead of
letting them through to the service where they cause a 500.
@anxolin anxolin requested a review from a team March 7, 2026 15:51
@anxolin
Copy link
Contributor Author

anxolin commented Mar 7, 2026

@alfetopito @shoom3301 I need this endpoints for a new service I'm building. I will merge to staging to be able to use it. I did a detailed test plan and test it. Let me know if I need to address anything. Thanks!

@anxolin anxolin merged commit 77c836a into main Mar 7, 2026
9 checks passed
@anxolin anxolin deleted the expose-token-details branch March 7, 2026 15:53
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.

1 participant