Skip to content

Conversation

@iNeocero
Copy link

@iNeocero iNeocero commented Sep 3, 2025

🚀 Volunchain Pull Request

Mark with an x all the checkboxes that apply (like [x])

  • Closes #
  • Added tests (if necessary)
  • Run tests
  • Run formatting
  • Evidence attached
  • Commented the code

📌 Type of Change

  • Documentation (updates to README, docs, or comments)
  • Bug fix (non-breaking change which fixes an issue)
  • Enhancement (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

📝 Changes description

• Enabled and implemented NFTController for claim flow.
• Added endpoints:

     POST /api/v1/nft/claim/prepare (returns unsigned Stellar XDR for NFT claim)
     POST /api/v1/nft/claim/submit (accepts signed XDR, returns pending status)

• Added DTOs, use-cases, and Stellar binding scaffold with clear TODOs.
• Added basic controller tests for both endpoints.
• Removed stub/disabled controller files.


⏰ Time spent breakdown

• Controller & routes: 20 min
• DTOs & use-cases: 15 min
• Stellar binding scaffold: 10 min
• Tests: 15 min
• Review & formatting: 10 min

Summary by CodeRabbit

  • New Features

    • Introduced a streamlined NFT claim flow with two steps: prepare a claim and submit a signed claim, enabling users to claim NFTs directly.
  • Refactor

    • Simplified the NFT module by replacing legacy NFT management endpoints with the new claim-focused flow, reducing complexity and aligning the API with claim use cases.

@coderabbitai
Copy link

coderabbitai bot commented Sep 3, 2025

Walkthrough

Replaced the NFTController stub with a publicly exported class exposing two static endpoints: prepareClaim and submitClaim. These endpoints read request body fields, invoke prepareClaimTx and submitSignedClaim use-cases, and return 200 and 202 responses respectively. Removed prior CRUD stub methods. Left a TODO about authenticated user validation.

Changes

Cohort / File(s) Summary of Changes
NFT claim endpoints
src/modules/nft/presentation/controllers/NFTController.stub.ts
Exported class; added static prepareClaim and submitClaim calling prepareClaimTx/submitSignedClaim; replaced old CRUD stubs; adjusted responses (200, 202); added security TODO; retained default export of instance for compatibility.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Client
  participant Ctr as NFTController.prepareClaim
  participant UC as prepareClaimTx (use-case)

  C->>Ctr: POST /nft/claim/prepare { nftId, claimantPublicKey }
  Note over Ctr: Validate input (TODO: auth validation)
  Ctr->>UC: prepareClaimTx({ nftId, claimantPublicKey })
  UC-->>Ctr: Prepared claim data
  Ctr-->>C: 200 OK + data
Loading
sequenceDiagram
  autonumber
  participant C as Client
  participant Ctr as NFTController.submitClaim
  participant UC as submitSignedClaim (use-case)

  C->>Ctr: POST /nft/claim/submit { nftId, signedXdr }
  Ctr->>UC: submitSignedClaim({ nftId, signedXdr })
  UC-->>Ctr: Submission result
  Ctr-->>C: 202 Accepted + data
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Poem

In burrows of code where ledgers gleam,
I thump out claims like a hopping dream.
Prepare, submit—the carrots align,
XDR signed, on the blockchain vine.
Two neat hops, the flow is clean—
A rabbit nods: shipped and serene. 🥕✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

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

🧹 Nitpick comments (1)
src/modules/nft/presentation/controllers/NFTController.stub.ts (1)

14-17: Guard against duplicate/replayed submissions (idempotency).

Consider: (a) Idempotency-Key header, (b) dedupe by hash of {nftId, signedXdr}, (c) respond with Location to a status endpoint.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 23a1f32 and 2bb9353.

📒 Files selected for processing (1)
  • src/modules/nft/presentation/controllers/NFTController.stub.ts (1 hunks)
🧰 Additional context used
🪛 Biome (2.1.2)
src/modules/nft/presentation/controllers/NFTController.stub.ts

[error] 2-2: Shouldn't redeclare 'Request'. Consider to delete it or rename it.

'Request' is defined here:

(lint/suspicious/noRedeclare)


[error] 2-2: Shouldn't redeclare 'Response'. Consider to delete it or rename it.

'Response' is defined here:

(lint/suspicious/noRedeclare)

🔇 Additional comments (1)
src/modules/nft/presentation/controllers/NFTController.stub.ts (1)

14-17: Good choice of 202 for async submission.

202 Accepted fits the “queued/pending” flow.

@@ -1,37 +1,20 @@
import { Request, Response } from "express";
import { Request, Response } from 'express';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix duplicate Express import (Biome noRedeclare).

You’re importing Request/Response twice; Biome flags this and CI will fail.

Apply:

-import { Request, Response } from 'express';
📝 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
import { Request, Response } from 'express';
🧰 Tools
🪛 Biome (2.1.2)

[error] 2-2: Shouldn't redeclare 'Request'. Consider to delete it or rename it.

'Request' is defined here:

(lint/suspicious/noRedeclare)


[error] 2-2: Shouldn't redeclare 'Response'. Consider to delete it or rename it.

'Response' is defined here:

(lint/suspicious/noRedeclare)

🤖 Prompt for AI Agents
In src/modules/nft/presentation/controllers/NFTController.stub.ts around line 2,
there is a duplicate import of Request and Response from 'express' causing a
Biome noRedeclare error; remove the redundant import (or consolidate any
separate express imports into a single import statement that includes Request
and Response) so only one import from 'express' remains in the file, then save
and run the linter to confirm the noRedeclare issue is resolved.

Comment on lines +6 to 12
export class NFTController {
static async prepareClaim(req: Request, res: Response) {
// TODO(security): Validar que el usuario autenticado puede reclamar el NFT.
const { nftId, claimantPublicKey } = req.body;
const result = await prepareClaimTx({ nftId, claimantPublicKey });
res.status(200).json(result);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add authZ, input validation, and error handling; wire NextFunction and try/catch.

Current handlers trust req.body and can throw unhandled rejections. Validate inputs, surface 400s, and forward unexpected errors to the error middleware.

Apply within these ranges:

-  static async prepareClaim(req: Request, res: Response) {
-    // TODO(security): Validar que el usuario autenticado puede reclamar el NFT.
-    const { nftId, claimantPublicKey } = req.body;
-    const result = await prepareClaimTx({ nftId, claimantPublicKey });
-    res.status(200).json(result);
-  }
+  static async prepareClaim(req: Request, res: Response, next: NextFunction) {
+    try {
+      // TODO(security): Validate that the authenticated user can claim the NFT.
+      const { nftId, claimantPublicKey } = req.body ?? {};
+      if (typeof nftId !== 'string' || typeof claimantPublicKey !== 'string' || !nftId || !claimantPublicKey) {
+        return res.status(400).json({
+          error: 'Invalid payload',
+          details: { nftId: 'required string', claimantPublicKey: 'required string' },
+        });
+      }
+      const result = await prepareClaimTx({ nftId, claimantPublicKey });
+      return res.status(200).json(result);
+    } catch (err) {
+      return next(err);
+    }
+  }
-  static async submitClaim(req: Request, res: Response) {
-    const { nftId, signedXdr } = req.body;
-    const result = await submitSignedClaim({ nftId, signedXdr });
-    res.status(202).json(result);
-  }
+  static async submitClaim(req: Request, res: Response, next: NextFunction) {
+    try {
+      const { nftId, signedXdr } = req.body ?? {};
+      if (typeof nftId !== 'string' || typeof signedXdr !== 'string' || !nftId || !signedXdr) {
+        return res.status(400).json({
+          error: 'Invalid payload',
+          details: { nftId: 'required string', signedXdr: 'required base64 XDR string' },
+        });
+      }
+      const result = await submitSignedClaim({ nftId, signedXdr });
+      return res.status(202).json(result);
+    } catch (err) {
+      return next(err);
+    }
+  }

Also update the import (outside the changed ranges):

-import { Request, Response } from "express";
+import { Request, Response, NextFunction } from "express";

Also applies to: 14-17

🤖 Prompt for AI Agents
In src/modules/nft/presentation/controllers/NFTController.stub.ts around lines 6
to 12 (and also apply the same pattern to lines 14 to 17), the controller
currently trusts req.body and can produce unhandled rejections; update the
method signature to accept (req: Request, res: Response, next: NextFunction),
wrap the handler body in a try/catch, perform explicit input validation for
nftId and claimantPublicKey (return res.status(400).json({ error: '...' }) for
missing/invalid values), perform an authorization check to ensure req.user (or
an injected authZ helper) is allowed to claim the NFT and return 403 if not
allowed, call prepareClaimTx inside the try and on success
res.status(200).json(result), and on unexpected errors call next(err) to forward
to error middleware; also update the imports at the top to include NextFunction
(and any validator/authZ helpers you use).

💡 Verification agent

🧩 Analysis chain

Static-only class vs default instance export will break callers.

You export an instance (export default new NFTController()), but static methods aren’t available on instances, so legacy call sites like defaultExport.prepareClaim will be undefined at runtime.

Safer bridge fix (keeps static methods and a callable default):

-export default new NFTController();
+export default {
+  prepareClaim: NFTController.prepareClaim,
+  submitClaim: NFTController.submitClaim,
+};

Alternatively, remove static and keep an instance API, or export the class as default and update all routes to call NFTController.prepareClaim.


🏁 Script executed:

#!/bin/bash
# Find usages that import the default and call controller methods
rg -nP -C2 "import\\s+\\w+\\s+from\\s+['\"]\\.?\\.?/.*NFTController" -g '!**/dist/**'
rg -nP -C2 "\\w+\\.prepareClaim\\(|\\w+\\.submitClaim\\(" -g '!**/dist/**'

Length of output: 837


Export mismatch: static methods vs default instance
In src/modules/nft/presentation/controllers/NFTController.stub.ts you declare prepareClaim/submitClaim as static but do export default new NFTController(), so callers doing

import NFTController from ".../NFTController.stub";
NFTController.prepareClaim(...)

will get undefined at runtime. Fix by exporting the class instead of an instance:

-export default new NFTController();
+export default NFTController;

Alternatively, convert these to instance methods (remove static) or explicitly export a bridge object:

export default {
  prepareClaim: NFTController.prepareClaim,
  submitClaim:  NFTController.submitClaim,
};
🤖 Prompt for AI Agents
In src/modules/nft/presentation/controllers/NFTController.stub.ts around lines
6-12 there is a mismatch: methods are declared static but the module exports an
instance, causing callers like `NFTController.prepareClaim(...)` to be
undefined; fix by either (A) exporting the class itself (replace the exported
instance with `export default NFTController`) so static calls work, or (B)
convert the static methods to instance methods (remove `static`) and keep
exporting an instance, or (C) keep static methods and export a bridge object
mapping those methods to the class (e.g., `export default { prepareClaim:
NFTController.prepareClaim, submitClaim: NFTController.submitClaim }`); also
update any imports/usage to match the chosen approach.

});
export class NFTController {
static async prepareClaim(req: Request, res: Response) {
// TODO(security): Validar que el usuario autenticado puede reclamar el NFT.
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Implement authorization now; don’t trust claimantPublicKey from the client.

Enforce that the authenticated user is allowed to claim this NFT (e.g., tie claimantPublicKey to the session/user and verify nftId is in a claimable state). Return 403 when not authorized.


🏁 Script executed:

#!/bin/bash
# Check routing and auth gating for these endpoints
rg -nP -C2 "(router|app)\\.(post|use)\\(.*nft/claim/(prepare|submit)" -g '!**/dist/**'
rg -nP -C2 "NFTController\\.(prepareClaim|submitClaim)|new\\s+NFTController\\(\\)"
rg -nP -C2 "(auth|passport|jwt|ensure.*Auth|verify.*Token|requireAuth)"

Length of output: 101504


🏁 Script executed:

#!/bin/bash
sed -n '1,200p' src/routes/nftRoutes.ts

Length of output: 805


🏁 Script executed:

#!/bin/bash
sed -n '1,200p' src/modules/nft/presentation/controllers/NFTController.stub.ts

Length of output: 897


Enforce authorization on NFT claim flows

  • In src/routes/nftRoutes.ts, add authenticated routes for preparing and submitting claims, e.g.:

    import { authMiddleware } from "../middleware/authMiddleware";
    
    router.post("/nfts/claim/prepare", authMiddleware, NFTController.prepareClaim);
    router.post("/nfts/claim/submit",  authMiddleware, NFTController.submitClaim);
  • In src/modules/nft/presentation/controllers/NFTController.stub.ts, replace the TODO with logic that reads req.user.id (populated by authMiddleware), verifies that claimantPublicKey belongs to that user and that nftId is in a claimable state, and returns 403 on any authorization failure.

🤖 Prompt for AI Agents
In src/modules/nft/presentation/controllers/NFTController.stub.ts around line 8,
replace the TODO with authorization logic that reads req.user.id (set by
authMiddleware), fetches/validates that the claimantPublicKey belongs to that
user (e.g. via userService or keysRepository), checks that the nftId is in a
claimable state (via nftService/status check), and if either check fails
immediately respond with res.status(403). If both checks pass, continue the
existing flow; surface any service errors as appropriate but do not proceed on
authorization failures.

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