Skip to content

feat: expose wallet list API endpoint with authentication and tests#70

Open
Silver36-ship-it wants to merge 2 commits into
Fundable-Protocol:devfrom
Silver36-ship-it:ExposeOrRetireWalletAPIRoutesWithTests
Open

feat: expose wallet list API endpoint with authentication and tests#70
Silver36-ship-it wants to merge 2 commits into
Fundable-Protocol:devfrom
Silver36-ship-it:ExposeOrRetireWalletAPIRoutesWithTests

Conversation

@Silver36-ship-it

@Silver36-ship-it Silver36-ship-it commented Jun 29, 2026

Copy link
Copy Markdown

#63

  • Register GET /v1/wallets endpoint with JWT authentication
  • Update wallet controller to use consistent API response format
  • Add comprehensive tests for success, empty, and error scenarios
  • Document API endpoint in README with request/response examples
  • Wallet module is now fully functional and no longer dormant

Summary

Describe what changed and why.

Area

  • Backend API (src/)
  • Indexer common infrastructure (indexer/common/)
  • Streams indexer (indexer/streams/)
  • Distributions indexer (indexer/distributions/)
  • Tooling, docs, CI, or Docker

Scope

  • This PR addresses one scoped issue or task
  • Unrelated formatting, generated files, and follow-up work were left out
  • Backend and indexer package boundaries were respected

Verification

  • bun run type-check
  • bun run test
  • bun run lint
  • bun run indexer:type-check if indexer files changed
  • bun run indexer:test if indexer files changed
  • bun run indexer:lint if indexer files changed

Indexer Safety

  • Event processing changes are idempotent or do not affect event processing
  • Cursor changes advance only after successful processing
  • Event names and payload shapes were confirmed from contracts, if relevant
  • Backfill and replay behavior was considered, if relevant

Notes

Closes #

Description

This PR exposes the wallet listing API endpoint that was previously dormant in the codebase. The wallet module had a controller and services implemented but no registered route, leaving it unreachable through the API.

Motivation

The wallet module was in a half-implemented state—infrastructure existed but was not accessible. This implementation:

  • Activates the GET /v1/wallets endpoint with JWT authentication
  • Ensures consistent API response formatting across the application
  • Adds comprehensive test coverage for success, empty, and error scenarios
  • Documents the endpoint in the README for API consumers

Scope

This PR focuses strictly on exposing the wallet list endpoint. Out of scope:

  • Wallet creation or balance mutation endpoints
  • Chain balance synchronization
  • Platform permission redesign

Changes Made

1. Updated Wallet Controller (wallet.controller.ts)

  • Replaced handleResponse helper with sendSuccess/sendError for API consistency
  • Added error handling for database failures
  • Returns proper error code WALLET_LIST_FAILED on exceptions
  • Uses consistent response format matching other endpoints

2. Registered Wallet Route (wallet.routes.ts)

  • Activated the GET / route (commented out previously)
  • Applied requireJwtAuthApi middleware to enforce JWT authentication
  • Switched from EnhancedRouter to standard Express Router for consistency
  • Endpoint now accessible at /v1/wallets

3. Integrated Router (routes.v1.ts)

  • Added wallet router mount at /wallets path
  • Maintains consistent routing structure with other v1 modules

4. Comprehensive Tests (wallet.controller.test.ts)

  • Created test file covering three scenarios:
    • Success case: Verifies 200 response with wallet data
    • Empty results: Returns empty array when no wallets exist
    • Error handling: Returns 500 with WALLET_LIST_FAILED code on database errors
  • Tests verify auth middleware, response structure, and error codes

5. Updated Documentation (README.md)

  • Added complete API endpoint documentation
  • Includes request/response examples
  • Documents authentication requirement and error scenarios
  • Provides curl example for API testing

API Endpoint

Endpoint: GET /v1/wallets

Authentication: Required (JWT Bearer token)

Request:

curl -H "Authorization: Bearer <JWT_TOKEN>" http://localhost:3000/v1/wallets

Success Response (200):

{
  "success": true,
  "data": [
    {
      "id": "wallet-uuid",
      "address": "0x123abc...",
      "network": "ETHEREUM",
      "chainId": "1",
      "chainName": "Ethereum",
      "balance": "100.50",
      "createdAt": "2026-06-29T10:00:00.000Z",
      "updatedAt": "2026-06-29T10:00:00.000Z"
    }
  ]
}

Error Response (401 - Missing Token):

{
  "success": false,
  "error": {
    "code": "AUTH_MISSING_TOKEN",
    "message": "Missing authentication token",
    "details": {}
  }
}

Error Response (500 - Database Error):

{
  "success": false,
  "error": {
    "code": "WALLET_LIST_FAILED",
    "message": "Failed to retrieve wallets",
    "details": { "reason": "..." }
  }
}

Verification Checklist

  • Route is registered and accessible at /v1/wallets
  • JWT authentication required (via requireJwtAuthApi middleware)
  • Response format matches API conventions (consistent with campaign/donation endpoints)
  • Tests cover success, empty, and error scenarios
  • Error handling graceful with proper status codes and error codes
  • Type-safe implementation with TypeScript
  • README documentation added with examples
  • Code follows project conventions and structure

Testing

Run these commands to verify:

# Type checking
bun run type-check

# Linting
bun run lint

# Tests (includes wallet controller tests)
bun run test

Related Issues

Closes #63


Notes for Reviewers

  1. Module Status: The wallet module moves from dormant to fully functional with this change. All infrastructure is now properly connected.

  2. Authentication: All wallet listing is gated behind JWT authentication, preventing unauthorized access to wallet data.

  3. Error Handling: Database connection failures are caught and returned as 500 errors with proper error codes for client-side error handling.

  4. Response Format: The response format (sendSuccess/sendError) is consistent with other API endpoints in the codebase (campaigns, donations).

  5. Future Work: Additional wallet endpoints (creation, balance updates) can be added to the same router when needed.


Summary by CodeRabbit

  • New Features

    • Added a List Wallets API endpoint under the v1 API, protected by JWT and admin authorization, returning wallet metadata in a standard success response.
    • Exposed the wallet endpoint in the v1 routing under /wallets.
  • Bug Fixes

    • Improved wallet listing error handling, including a clear DB_NOT_READY 500 response when the database isn’t initialized and consistent success/error payloads.
  • Documentation

    • Expanded the README with a new API: List Wallets section, including curl examples, response samples, and authorization notes (plus formatting updates).
  • Tests

    • Added controller and route-level tests covering success, missing auth, forbidden access, and database-not-ready failures.

- Register GET /v1/wallets endpoint with JWT authentication
- Update wallet controller to use consistent API response format
- Add comprehensive tests for success, empty, and error scenarios
- Document API endpoint in README with request/response examples
- Wallet module is now fully functional and no longer dormant
@drips-wave

drips-wave Bot commented Jun 29, 2026

Copy link
Copy Markdown

@Silver36-ship-it Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The wallet listing endpoint is now wired into the v1 API, protected by JWT and admin checks, backed by updated controller logic, covered by route/controller tests, and described in the README.

Changes

Wallet API activation

Layer / File(s) Summary
Controller and route wiring
src/components/v1/wallet/wallet.controller.ts, src/components/v1/wallet/wallet.routes.ts, src/components/v1/routes.api.v1.ts, src/components/v1/routes.v1.ts
listWallets now checks AppDataSource.isInitialized, calls walletRepository.find(), and returns shared success or structured error payloads. The wallet route uses Express Router with JWT and admin middleware, and /wallets is mounted in the v1 API router.
Controller and route tests
src/__tests__/wallet.controller.test.ts, src/__tests__/wallet.routes.test.ts
Adds mock response helpers and tests for successful wallet listing, database-not-ready failure, JWT auth rejection, admin rejection, and router module exports.
README updates
README.md
Adjusts spacing and formatting in existing sections and adds API: List Wallets documentation with request and response examples.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • None identified.

Poem

🐇 I hopped to the wallet gate,
JWT and admin joined the wait.
With find() calls neat and docs in line,
The wallets route now shines just fine.
A carrot cheer, a happy thump —
The API woke up with a jump!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: exposing the wallet list API with auth and tests.
Description check ✅ Passed The description matches the template and includes summary, area, scope, verification, notes, and linked issue details.
Linked Issues check ✅ Passed The PR satisfies #63 by registering the wallet route, adding access control, tests, response-format alignment, and README docs.
Out of Scope Changes check ✅ Passed No substantial out-of-scope code changes are evident; the remaining edits are minor formatting and documentation updates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 4

🤖 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 `@README.md`:
- Around line 299-310: The 401 documentation in README currently covers only the
missing-token case, but the JWT middleware in jwtAuth.api.ts can also return the
invalid-token variant. Update the 401 response example or description to include
both AUTH_MISSING_TOKEN/"Missing authentication token" and
AUTH_INVALID_TOKEN/"Invalid authentication token", or generalize the text so it
matches the middleware behavior for both failure modes.

In `@src/__tests__/wallet.controller.test.ts`:
- Around line 45-63: The current wallet controller test only checks that
listWallets exists and never exercises its behavior. Update
src/__tests__/wallet.controller.test.ts to actually invoke listWallets with
mocked request/response and wire mockRepository into the wallet.controller
dependencies so the handler uses it. Then assert the sendSuccess/sendError
contract from wallet.controller for the success, empty, and database-error
paths, including the response shape and status handling, so the test verifies
real controller behavior instead of just function presence.
- Around line 66-75: The current tests only verify that wallet routes modules
load, not that the JWT middleware or v1 mounting actually works. Update the
assertions in wallet.controller.test.ts to exercise the mounted router behavior
from wallet.routes and routes.v1: call GET /wallets directly, verify it is
rejected without JWT, and verify it reaches the handler when authentication is
present. Use the existing route wiring symbols requireJwtAuthApi, wallet.routes,
and routes.v1 to ensure the test would fail if the middleware or mount path
changes.

In `@src/components/v1/wallet/wallet.controller.ts`:
- Around line 13-16: The error payload in wallet.controller should not expose
the raw repository exception text via err.message. Update the error handling
around the wallet list response so sendError returns a generic client-facing
details value, and preserve the original error only in server-side logging from
the same controller flow. Use the wallet listing handler in WalletController as
the place to adjust this contract.
🪄 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: 841ba784-843f-4ab3-9aef-ea87af0c3daf

📥 Commits

Reviewing files that changed from the base of the PR and between 43f39f6 and 5d9aaca.

📒 Files selected for processing (5)
  • README.md
  • src/__tests__/wallet.controller.test.ts
  • src/components/v1/routes.v1.ts
  • src/components/v1/wallet/wallet.controller.ts
  • src/components/v1/wallet/wallet.routes.ts

Comment thread README.md
Comment on lines +299 to +310
Response (Error - 401 Unauthorized):

```json
{
"success": false,
"error": {
"code": "AUTH_MISSING_TOKEN",
"message": "Missing authentication token",
"details": {}
}
}
```

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Document both 401 error variants.

The JWT middleware returns AUTH_INVALID_TOKEN / "Invalid authentication token" for malformed or failed verification, not just AUTH_MISSING_TOKEN. Update the 401 documentation to cover both missing and invalid token cases, or generalize the description to match the middleware behavior.

As per the upstream contract in src/appMiddlewares/jwtAuth.api.ts, the 401 response can have either AUTH_MISSING_TOKEN or AUTH_INVALID_TOKEN depending on the failure mode.

🤖 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 `@README.md` around lines 299 - 310, The 401 documentation in README currently
covers only the missing-token case, but the JWT middleware in jwtAuth.api.ts can
also return the invalid-token variant. Update the 401 response example or
description to include both AUTH_MISSING_TOKEN/"Missing authentication token"
and AUTH_INVALID_TOKEN/"Invalid authentication token", or generalize the text so
it matches the middleware behavior for both failure modes.

Comment thread src/__tests__/wallet.controller.test.ts Outdated
Comment thread src/__tests__/wallet.controller.test.ts Outdated
Comment on lines +13 to +16
return sendError(res, 500, {
code: 'WALLET_LIST_FAILED',
message: 'Failed to retrieve wallets',
details: { reason: err.message },

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Don't expose raw repository errors in the API payload.

Line 16 returns err.message to the client. That can leak DB/ORM internals and makes the external contract depend on backend exception text. Keep the response generic here and log the original error server-side instead.

Proposed fix
     } catch (error) {
-        const err = error as Error;
         return sendError(res, 500, {
             code: 'WALLET_LIST_FAILED',
             message: 'Failed to retrieve wallets',
-            details: { reason: err.message },
+            details: {},
         });
     }
📝 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
return sendError(res, 500, {
code: 'WALLET_LIST_FAILED',
message: 'Failed to retrieve wallets',
details: { reason: err.message },
return sendError(res, 500, {
code: 'WALLET_LIST_FAILED',
message: 'Failed to retrieve wallets',
details: {},
🤖 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/components/v1/wallet/wallet.controller.ts` around lines 13 - 16, The
error payload in wallet.controller should not expose the raw repository
exception text via err.message. Update the error handling around the wallet list
response so sendError returns a generic client-facing details value, and
preserve the original error only in server-side logging from the same controller
flow. Use the wallet listing handler in WalletController as the place to adjust
this contract.

@pragmaticAweds

Copy link
Copy Markdown
Contributor

@Silver36-ship-it

Thank you for your awesome contribution, however after analyzing your implementation, there are some minor fixes to be done. Kindly fix them to merge your PR asap.

Also do not forget to use fundable.finance to offramp.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 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/__tests__/wallet.controller.test.ts`:
- Around line 50-73: The happy-path test for listWallets is missing the
AppDataSource initialization setup, so it may exit early with DB_NOT_READY
before the walletRepository.find stub is exercised. Update the wallet.controller
test around listWallets to explicitly set AppDataSource.isInitialized to true
(and restore it afterward) so the success case reliably reaches the mocked
repository call and remains order-independent.
🪄 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: d655cebb-3e6b-433b-90e7-4911a54e09ad

📥 Commits

Reviewing files that changed from the base of the PR and between 5d9aaca and 2b78684.

📒 Files selected for processing (7)
  • README.md
  • src/__tests__/wallet.controller.test.ts
  • src/__tests__/wallet.routes.test.ts
  • src/components/v1/routes.api.v1.ts
  • src/components/v1/routes.v1.ts
  • src/components/v1/wallet/wallet.controller.ts
  • src/components/v1/wallet/wallet.routes.ts
💤 Files with no reviewable changes (1)
  • src/components/v1/routes.v1.ts
✅ Files skipped from review due to trivial changes (1)
  • src/tests/wallet.routes.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/v1/wallet/wallet.routes.ts
  • README.md

Comment on lines +50 to +73
const res = createMockResponse();
const req = { auth: { userId: 'user-123' } } as IRequest;
const originalFind = walletRepository.find.bind(walletRepository);

try {
(
walletRepository as unknown as {
find: typeof walletRepository.find;
}
).find = async () => mockWallets as any;

await listWallets(req, res as unknown as Response);

assert.equal(res.statusCode, 200);
assert.equal(res.jsonData.success, true);
assert.deepEqual(res.jsonData.data, mockWallets);
} finally {
(
walletRepository as unknown as {
find: typeof walletRepository.find;
}
).find = originalFind;
}
});

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect hidden setup (lines 37-49) and any AppDataSource initialization in the test
fd -t f 'wallet.controller.test.ts' --exec sed -n '30,60p' {}
rg -nP 'isInitialized|AppDataSource' src/__tests__/wallet.controller.test.ts

Repository: Fundable-Protocol/Backend

Length of output: 977


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the relevant controller and test sections with line numbers.
controller_file=$(fd -t f 'wallet.controller.ts' src | head -n 1)
test_file='src/__tests__/wallet.controller.test.ts'

printf '\n== %s ==\n' "$controller_file"
sed -n '1,220p' "$controller_file" | cat -n

printf '\n== %s (30-160) ==\n' "$test_file"
sed -n '30,160p' "$test_file" | cat -n

printf '\n== occurrences of AppDataSource / isInitialized in repo ==\n'
rg -n 'AppDataSource|isInitialized' src --glob '!**/dist/**' --glob '!**/build/**'

Repository: Fundable-Protocol/Backend

Length of output: 8758


Stub AppDataSource.isInitialized in the success test
listWallets returns DB_NOT_READY before calling walletRepository.find(), so this case needs AppDataSource.isInitialized = true (or equivalent setup) to exercise the happy path; otherwise it will hit the 500 branch or become order-dependent.

🤖 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/__tests__/wallet.controller.test.ts` around lines 50 - 73, The happy-path
test for listWallets is missing the AppDataSource initialization setup, so it
may exit early with DB_NOT_READY before the walletRepository.find stub is
exercised. Update the wallet.controller test around listWallets to explicitly
set AppDataSource.isInitialized to true (and restore it afterward) so the
success case reliably reaches the mocked repository call and remains
order-independent.

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.

Expose or retire wallet API routes with tests

2 participants