Skip to content

feat(anchor): SEP-24 non-custodial fiat on/off-ramp for USDC - #56

Open
Cyber-Mitch wants to merge 6 commits into
Deen-Bridge:devfrom
Cyber-Mitch:feat/46-sep24-anchor-integration
Open

feat(anchor): SEP-24 non-custodial fiat on/off-ramp for USDC #56
Cyber-Mitch wants to merge 6 commits into
Deen-Bridge:devfrom
Cyber-Mitch:feat/46-sep24-anchor-integration

Conversation

@Cyber-Mitch

@Cyber-Mitch Cyber-Mitch commented Jul 22, 2026

Copy link
Copy Markdown

Closes #46

Non-custodial SEP-24 fiat on/off-ramp for USDC: anchor discovery (SEP-1), SEP-10 client-side challenge relay, interactive deposit/withdrawal, and a status-polling worker — all without the server ever holding a Stellar signing key.

No-server-keys checklist

Surface Status
anchorService.js — any Keypair.fromSecret/secret reference None found (grep-verified)
anchorService.js/anchorController.js/anchorPoller.js — any .sign() call None found (grep-verified)
Challenge flow Server only validates the anchor's challenge (WebAuth.readChallengeTx); signing happens client-side, XDR returned unsigned
Trustline flow buildChangeTrustTransaction returns unsigned XDR only, mirrors buildPaymentTransaction's existing pattern
JWT storage Bearer session token, not a Stellar key; decoded (not verified) for exp only
Config Only ANCHOR_HOME_DOMAINS/ANCHOR_TOML_CACHE_TTL added — no secret-shaped env var
Interactive deposit/withdrawal Authenticated with the stored JWT bearer token, never a Stellar signature

Challenge-validation rejection matrix

Built on the SDK's own audited WebAuth.readChallengeTx (anchorService.js:126-153), not hand-rolled checks:

Check Failure tested Result
Sequence number Non-zero 502, "sequence number should be zero"
Signature Signed by an impostor, not the TOML's SIGNING_KEY 502, "not signed by server"
Network passphrase Wrong network 502, "not signed by server" (signature is network-scoped)
Home domain manage_data names the wrong domain 502, "does not match the expected home domain"
Requested account Challenge issued for a different account 502, "different account"
Anchor response shape No transaction field returned 502, "did not return a challenge transaction"
Any failure Nothing resembling challenge XDR ever attached to the thrown error (test-proven)
All checks pass 200, XDR returned for client signing

JWT custody

  • Redis only, keyed anchor:jwt:{userId}:{homeDomain}, TTL from the token's own exp claim (decoded, never verified — it's the anchor's signature, not ours).
  • Redaction proven by spying on every logger method and asserting the raw JWT never appears in any response body/headers or log call across the full verify flow.
  • Expired/never-authenticated states both resolve to null → deposit/withdrawal endpoints return 401 {requiresReauth: true} rather than attempting a stale-credential anchor call.

Poller

src/jobs/anchorPoller.js: 5s tick (.unref()'d, matching jobs/queue.js's style), atomic findOneAndUpdate claim — restart-safe since every tick reads fresh DB state and the claim prevents double-processing across ticks/instances. Exponential backoff with jitter, capped at 6 attempts. Terminal states excluded from the claim query, so the poller simply stops touching a record — no special-case stop logic. Deliberately not built on jobs/queue.js's Job model, per the issue's "align with, not depend on" instruction.

Coordination with #25

#25 (SEP-10 server) is open, assigned elsewhere, no PR yet — nothing to share. Both will import @stellar/stellar-sdk's WebAuth namespace, but the functions don't overlap (readChallengeTx here vs. buildChallengeTx there), so there's no helper to extract yet.

Tests

102 passing (58 baseline + 44 new), 14 suites, zero live network in any test. Boot check passes both testnet (auto-configured) and mainnet-with-no-allowlist (unconfigured → 503, server still boots).

Acceptance criteria (verbatim from #46)

  • Anchor info retrieval with domain allowlisting and issuer verification — allowlist checked before any network call
  • Challenge validation rejecting invalid signing keys or network mismatches
  • Working deposit flow against testnet anchor with status progression
  • User-owned transaction visibility only (cross-user access tested and rejected)
  • Trustline creation for deposits lacking USDC trustline
  • Client-side signing maintained throughout withdrawal flow
  • Jest tests with mocked HTTP endpoints, zero live network

Found, not fixed — flagging separately

  1. cache.js's getCacheOrSet double-calls fallbackFn() on error (once in try, once in catch) — pre-existing, errors still propagate correctly, just one wasted extra attempt on genuine failure.
  2. dev is one commit behind main (missing a Redis cache-layer merge, though the Redis files themselves already exist in dev from an earlier merge). Worth a maindev sync — will raise separately with the maintainer.
  3. During development, caught two of my own test-mocking gaps where a call would have silently hit the real network (a missing /info mock, a missing axios.get dispatch-by-URL) — both fixed. Flagging since this class of mistake is easy to make with multi-call service functions; worth a second look in review of anchor-adjacent PRs.

Feature Implemented and Task Completed
@zeemscript Please Review

Summary by CodeRabbit

  • New Features
    • Added non-custodial Stellar SEP-24 USDC deposit and withdrawal support with SEP-10 authentication.
    • Added anchor transaction tracking with status polling and interactive flow initiation.
    • Introduced anchor configuration options for home-domain allowlisting and TOML cache TTL (including testnet defaults).
  • Documentation
    • Added detailed anchor integration setup, security model, API flow, and testnet demo guidance.
  • Bug Fixes
    • Improved search caching key behavior.
    • Fixed avatar upload handling during user profile updates.
  • Security
    • Expanded redaction to keep anchor/JWT values out of logs and responses.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The PR adds a non-custodial Stellar SEP-24 anchor integration with SEP-10 authentication, Redis-backed JWT storage, interactive deposit and withdrawal flows, transaction tracking, background polling, route wiring, configuration, documentation, and integration tests. It also adjusts search caching and avatar upload middleware.

Changes

Stellar anchor integration

Layer / File(s) Summary
Anchor contracts and route wiring
src/models/AnchorTransaction.js, src/config/validateEnv.js, .env.example, src/routes/stellar/anchorRoutes.js, app.js, src/controllers/stellar/anchorController.js, docs/anchors.md, test/anchorRoutes.test.js, test/app.test.js
Defines anchor persistence, configuration, protected endpoints, controller responses, route mounting, and documented API behavior.
Discovery and SEP-10 authentication
src/services/stellar/anchorService.js, src/config/logger.js, test/anchorAuth.test.js, test/anchorDiscovery.test.js, test/anchorJwtStorage.test.js, test/anchorJwtCustody.test.js, test/anchorInteractive.test.js
Adds allowlisted TOML discovery, issuer checks, bounded requests, challenge validation, server-side JWT storage, and credential-redaction coverage.
Interactive transfer and tracking flows
src/services/stellar/anchorService.js, src/controllers/stellar/anchorController.js, src/services/stellar/stellarService.js, test/anchorInteractive.test.js, test/anchorTracking.test.js
Starts SEP-24 deposits and withdrawals, builds optional trustline XDR, persists transactions, supports ownership-scoped pagination, and refreshes stale records.
Anchor status polling and lifecycle
src/jobs/anchorPoller.js, server.js, test/anchorPoller.test.js
Claims due records atomically, refreshes anchor statuses, applies retry backoff, and manages poller startup and shutdown.

Adjacent route maintenance

Layer / File(s) Summary
Search and avatar route adjustments
src/routes/searchRoutes.js, src/routes/userRoutes.js
Simplifies search cache-key construction and switches avatar updates to the standard upload middleware.

Estimated code review effort: 5 (Critical) | ~90 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The searchRoutes and userRoutes edits are unrelated to the anchor objectives and appear to be extra scope. Remove the unrelated searchRoutes and userRoutes changes or split them into a separate PR unless they are required for the anchor feature.
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 (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main SEP-24 USDC anchor integration.
Linked Issues check ✅ Passed The PR covers the SEP-24 discovery, SEP-10 relay, JWT storage, deposits/withdrawals, trustline XDR, polling, docs, and tests required by #46.
✨ 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.

🔧 Biome (2.5.3)
src/services/stellar/stellarService.js

File contains syntax errors that prevent linting: Line 376: Illegal use of an export declaration not at the top level; Line 378: Illegal use of an export declaration not at the top level; Line 389: Illegal use of an export declaration not at the top level; Line 418: Illegal use of an export declaration not at the top level; Line 512: Illegal use of an export declaration not at the top level; Line 577: Illegal use of an export declaration not at the top level; Line 623: Illegal use of an export declaration not at the top level; Line 677: Illegal use of an export declaration not at the top level; Line 706: Illegal use of an export declaration not at the top level; Line 765: Illegal use of an export declaration not at the top level; Line 778: Illegal use of an export declaration not at the top level; Line 780: Illegal use of an export declaration not at the top level; Line 788: Illegal use of an export declaration not at the top level; Line 796: Illegal use of an export declaration not at the top level; Line 798: Illegal use of an export declaration not at the top level; Line 807: expected } but instead the file ends


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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🧹 Nitpick comments (12)
src/controllers/stellar/paymentController.js (2)

277-318: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Validate the path-payment inputs before feeding them into the SDK. sendMax, pathInput, and sendAssetInput.{code,issuer} come straight from the request body into toStroops(sendMax) (BigInt parse) and new StellarSdk.Asset(...). Malformed values throw and fall through to the generic catch as an opaque 500 instead of a clear 400. A small shape/format check (numeric sendMax, valid asset code/issuer) keeps the contract clean and the errors actionable.

Note on threat model: since the wallet signs and sendMax only bounds the buyer's own send while the server sets destAmount, this is an input-hygiene issue rather than a platform-fund risk — so a validation guard is sufficient.

As per path instructions: "Flag ... unvalidated request input."

🤖 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/controllers/stellar/paymentController.js` around lines 277 - 318,
Validate path-payment request inputs before the isPathPayment branch constructs
StellarSdk.Asset or calls buildPathPaymentTransaction: require a valid numeric
sendMax, a well-formed pathInput array, and valid sendAssetInput code and issuer
values. Return a clear client-error response for malformed input, while
preserving the existing transaction-building flow for valid values.

Source: Path instructions


36-141: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

New /quote endpoint needs Jest coverage. This is a brand-new externally reachable endpoint with non-trivial branching (item validation, issuer rules, path lookup, slippage clamping), yet the added tests only exercise the applySlippage/toStroops helpers directly — the controller flow itself is untested. A mocked test around getQuote would lock in the 400/404/200 contracts before merge.

Minor secondary note: the success payload uses { success, quote } rather than the { success, message, data } envelope; not blocking given the rest of this controller varies too, just flagging for consistency.

As per path instructions: "New or changed endpoints need Jest coverage (the runner uses --experimental-vm-modules) and consistent response shapes ({ success, message, data })."

🤖 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/controllers/stellar/paymentController.js` around lines 36 - 141, Add Jest
coverage for the externally reachable getQuote controller using mocked models,
payment-path lookup, and response objects, covering invalid item types, missing
items, free items, issuer validation, no paths, and successful slippage-clamped
quotes. Also align the successful response with the established { success,
message, data } envelope by placing the quote under data while preserving
existing error status behavior.

Source: Path instructions

src/models/AnchorTransaction.js (1)

10-64: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Nice schema — precision-safe string amounts and a well-scoped unique index. Two redundant indexes to trim, though.

Thanks for storing amounts as strings and scoping anchorTransactionId uniqueness per homeDomain — both are exactly right for SEP‑24.

Two single-field indexes are shadowed by the compound indexes you added, so Mongo builds and maintains them for nothing (extra write cost + storage, and Mongoose will emit duplicate-index warnings):

  • user (Line 10) is already covered as the prefix of { user: 1, status: 1 } (Line 59).
  • status (Line 38) is already covered as the prefix of { status: 1, nextPollAt: 1 } (Line 64).

nextPollAt (Line 52) is fine to keep — it isn't a prefix of any compound index.

♻️ Drop the shadowed field-level indexes
     user: {
       type: mongoose.Schema.Types.ObjectId,
       ref: "User",
       required: true,
-      index: true,
     },
     status: {
       type: String,
       required: true,
       default: "incomplete",
-      index: true,
     },
🤖 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/models/AnchorTransaction.js` around lines 10 - 64, Remove the field-level
indexes from the user and status schema definitions in AnchorTransaction, while
preserving the compound indexes { user: 1, status: 1 } and { status: 1,
nextPollAt: 1 }. Keep nextPollAt indexed and leave all other schema behavior
unchanged.
src/controllers/stellar/anchorController.js (1)

42-43: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Response payloads diverge from the documented { success, message, data } shape.

These endpoints return the payload under ad-hoc keys — anchor here, and challenge, transactions, transaction, and [kind] in the sibling handlers — rather than a consistent data envelope. Consolidating on the standard shape keeps clients from special-casing each anchor route. If the surrounding codebase has genuinely settled on these domain keys, feel free to keep them for consistency and treat this as a heads-up.

As per path instructions: "consistent response shapes ({ success, message, data })".

🤖 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/controllers/stellar/anchorController.js` around lines 42 - 43, Update the
response construction in the anchor controller handlers, including the flow
containing getAnchorInfo, to use the documented { success, message, data }
shape: move anchor, challenge, transactions, transaction, and dynamic kind
payloads under data while preserving their existing values and success status.
Keep any established message behavior unchanged.

Source: Path instructions

src/jobs/anchorPoller.js (1)

96-108: 🚀 Performance & Scalability | 🔵 Trivial

Throughput note: one record claimed per tick.

tick() claims and refreshes a single record, and the interval fires every POLL_TICK_MS (5s), so the effective drain rate is ~1 transaction / 5s. That's totally fine at low volume, but if a burst of deposits/withdrawals stacks up, freshly-due records will queue behind the cadence. If you expect concurrency, consider claiming a small batch per tick (bounded, e.g. Promise.all over N claims) so the loop scales with load rather than wall-clock ticks. Not a blocker — just flagging before this meets production traffic.

🤖 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/jobs/anchorPoller.js` around lines 96 - 108, Increase anchor poller
throughput by updating tick to claim and refresh a bounded batch of due records
per interval instead of only one. Use a small fixed concurrency limit with
Promise.all, while preserving the accepting guard and existing error handling in
startAnchorPoller.
services/emails/sendMail.js (1)

11-17: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Validate EMAILJS_PRIVATE_KEY alongside the other required vars.

accessToken: EMAILJS_PRIVATE_KEY (line 23) is required by the EmailJS API but isn't included in the pre-flight guard, so a missing private key surfaces only as a runtime API failure instead of the clear "not configured" error the other three vars get.

   if (
     process.env.NODE_ENV !== "test" &&
-    (!EMAILJS_SERVICE_ID || !templateId || !EMAILJS_PUBLIC_KEY)
+    (!EMAILJS_SERVICE_ID || !templateId || !EMAILJS_PUBLIC_KEY || !EMAILJS_PRIVATE_KEY)
   ) {
     throw new Error("EmailJS environment variables are not configured");
   }
🤖 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 `@services/emails/sendMail.js` around lines 11 - 17, Update the pre-flight
guard in sendTemplate to also require EMAILJS_PRIVATE_KEY alongside
EMAILJS_SERVICE_ID, templateId, and EMAILJS_PUBLIC_KEY, while preserving the
existing test-environment bypass and configuration error.
src/routes/jobsRoutes.js (2)

13-20: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Use a constant-time comparison for the dashboard bearer token.

req.headers.authorization !== \Bearer ${token}`` is a standard string comparison, which leaks timing information about how many leading characters match. Low practical risk over the network, but cheap to harden:

+import { timingSafeEqual } from "crypto";
+
+const safeEqual = (a, b) => {
+  const bufA = Buffer.from(a);
+  const bufB = Buffer.from(b);
+  return bufA.length === bufB.length && timingSafeEqual(bufA, bufB);
+};
+
 router.use((req, res, next) => {
   const token = process.env.JOBS_DASHBOARD_TOKEN;
   if (!token) return res.status(404).json({ success: false, message: "Not found" });
-  if (req.headers.authorization !== `Bearer ${token}`) {
+  if (!safeEqual(req.headers.authorization || "", `Bearer ${token}`)) {
     return res.status(401).json({ success: false, message: "Unauthorized" });
   }
   next();
 });
🤖 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/jobsRoutes.js` around lines 13 - 20, Update the authorization
check in the router middleware to compare the provided Bearer token using a
constant-time comparison, ensuring length mismatches are handled safely before
invoking the comparison. Preserve the existing 404 response for a missing
JOBS_DASHBOARD_TOKEN, 401 response for invalid credentials, and next() behavior
for valid credentials.

22-36: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Missing test coverage for the authorized path and /dead.

test/jobs.test.js only covers missing/wrong bearer token (401s) for /admin/jobs. There's no test for a successful, authorized request to /admin/jobs, nor any coverage for /admin/jobs/dead.

As per path instructions, "New or changed endpoints need Jest coverage" for this Express 5 ESM + Mongoose 8 API.

🤖 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/jobsRoutes.js` around lines 22 - 36, Add Jest coverage in
test/jobs.test.js for an authorized request to /admin/jobs, asserting the
successful response and expected job data, and for /admin/jobs/dead, asserting
its successful response and dead-job results. Mock or seed Job queries
consistently with the existing tests and preserve coverage for the current
unauthorized 401 cases.

Source: Path instructions

src/models/Job.js (1)

1-28: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Add indexes for the dashboard's actual query patterns.

src/routes/jobsRoutes.js runs Job.find().sort({ createdAt: -1 }).limit(100) and Job.find({ status: "dead" }).sort({ failedAt: -1 }), but this schema has no index supporting either sort — both will require in-memory sorts (or scans) that get slower as the collection grows. Since there's no cleanup/archival of completed/dead jobs, this collection only grows, so add indexes for these access patterns now:

 jobSchema.index({ status: 1, runAt: 1 });
+jobSchema.index({ createdAt: -1 });
+jobSchema.index({ status: 1, failedAt: -1 });

Also worth reconsidering: name: { ..., index: true } isn't used as a standalone lookup filter anywhere in the reviewed code — if that holds, it's extra write overhead for no query benefit.

🤖 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/models/Job.js` around lines 1 - 28, Add compound indexes to jobSchema for
the dashboard queries: createdAt descending for the unfiltered recent-jobs
query, and status ascending plus failedAt descending for dead-job queries.
Remove the standalone name index from the name field unless another reviewed
query depends on it.

Source: Path instructions

test/jobs.test.js (1)

12-19: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Restore mutated env vars after tests.

QUEUE_DRIVER and JOBS_DASHBOARD_TOKEN are set on process.env but never cleaned up. Since Jest's node environment shares the real process object, these can leak into other spec files run in the same worker and cause order-dependent failures.

   afterEach(() => {
     jest.useRealTimers();
+    delete process.env.JOBS_DASHBOARD_TOKEN;
   });

and restore/delete QUEUE_DRIVER in an afterAll/afterEach if it wasn't already set before the suite ran.

Also applies to: 55-65

🤖 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 `@test/jobs.test.js` around lines 12 - 19, Restore environment state mutated by
the jobs test suite: capture the original values of QUEUE_DRIVER and
JOBS_DASHBOARD_TOKEN before tests run, then restore each value or delete the
variable when it was initially unset in afterEach or afterAll. Update the
existing beforeEach/afterEach setup around resetInlineQueueForTests and
jest.useRealTimers without changing unrelated test behavior.
test/jobHandlers.test.js (1)

35-97: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add coverage for the purchase path in verifyPaymentOnChain.

Every test here uses type: "donation", so recordSaleEarnings and the User/Course $addToSet updates are never exercised — this is why the confirmed-before-side-effects ordering bug flagged in src/jobs/handlers.js (lines 32-74) wasn't caught. Consider adding a type: "purchase" fixture with a mocked recordSaleEarnings rejection to assert that a retry re-attempts the side effects rather than short-circuiting via the status === "confirmed" guard. Also consider a test for the sendOtpEmail handler (missing-user error path).

As per path instructions, "New or changed endpoints need Jest coverage... Flag missing auth/ownership checks, unvalidated request input, and unhandled promise rejections" for this Express 5 ESM + Mongoose 8 API; the same rigor applies to critical job-handler paths introduced in this PR.

🤖 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 `@test/jobHandlers.test.js` around lines 35 - 97, Add purchase-specific
coverage to the verifyPaymentOnChain tests using a type: "purchase" fixture,
mock recordSaleEarnings to reject, and assert the job remains retryable while
side effects are attempted again rather than being skipped by the
confirmed-status guard. Also add coverage for sendOtpEmail’s missing-user error
path, using the handler’s existing registration and mocks.

Source: Path instructions

src/jobs/queue.js (1)

61-77: 🩺 Stability & Availability | 🔵 Trivial

No recovery path for dead-lettered jobs.

Once a Job reaches dead (or completed), calling enqueue() again with the same idempotencyKey is a no-op — $setOnInsert only applies on insert, and processNext only claims queued/retrying jobs. Combined with the read-only /admin/jobs/dead dashboard, a permanently dead-lettered job (e.g. a generateReceipt job that exhausted retries during an EmailJS outage) has no operational recovery path short of a manual DB edit. Consider adding an admin "requeue" action (reset status/runAt/attemptsMade on a dead job) so this is a proper CI/ops safety net rather than a silent dead end for payment-adjacent notifications.

🤖 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/jobs/queue.js` around lines 61 - 77, Add an admin requeue action for
dead-lettered jobs, integrated with the existing `/admin/jobs/dead` flow and
job-processing symbols such as `processNext`. The action must reset the selected
job’s status to `queued`, schedule it for immediate execution, and clear its
retry-attempt counter so it can be claimed and retried; preserve idempotency
behavior for normal `enqueue()` calls and restrict requeueing to appropriate
dead/completed jobs.
🤖 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 `@services/emails/sendMail.js`:
- Around line 33-43: Update the info logs in sendOtpEmail and sendReceiptEmail
to avoid exposing raw recipient email addresses, using the project’s existing
masking or truncation approach if available. Preserve the existing logging
context and email-sending behavior while ensuring only a non-sensitive email
representation is logged.

In `@src/controllers/authController.js`:
- Around line 126-131: Update registerUser to generate a new OTP for each
registration attempt, and use that per-request value when enqueueing
sendOtpEmail. Remove reliance on the module-scope generatedOtp from
emailRoutes.js so separate signups never share the same code.

In `@src/controllers/stellar/anchorController.js`:
- Around line 259-266: Validate and normalize the page and limit values in the
Anchor transaction pagination flow before constructing the Mongo query: ensure
page is a positive integer and clamp limit to a safe maximum while preserving
the default values. Use the normalized values for both skip and limit so invalid
or excessively large requests cannot reach AnchorTransaction.find.
- Around line 202-228: Make trustline construction in the deposit flow
best-effort: wrap getAccountBalance and buildChangeTrustTransaction within the
kind === "deposit" branch in local error handling, log the failure using the
controller’s existing logging pattern, and continue returning the successful
response with url and id. Preserve trustlineXdr when building succeeds, but
swallow failures so they do not reach the outer catch or change the 200
response.

In `@src/controllers/stellar/paymentController.js`:
- Around line 484-507: Update the existing transaction-status guard in
initializePayment to include "retrying" alongside "pending" and "submitted", so
transactions awaiting verifyPaymentOnChain cannot be reinitialized before
confirmation completes.

In `@src/jobs/handlers.js`:
- Around line 32-74: Reorder the success path in the verifyPaymentOnChain
handler so purchase side effects—recordSaleEarnings and User/Course
updates—complete before setting and saving transaction.status to confirmed.
Compute purchase.purchaseDate with a new Date() before the save, then set
confirmedAt, clear failureReason, persist the transaction, and queue the
receipt. Preserve the existing confirmed guard and non-purchase behavior.

In `@src/routes/stellar/anchorRoutes.js`:
- Around line 16-22: Update the controllers used by the anchor routes—getInfo,
requestChallenge, verifyChallenge, initiateDeposit, initiateWithdrawal,
getTransactions, and getTransactionById—to return the consistent top-level shape
{ success, message, data }, placing endpoint-specific values such as anchor
inside data and providing data consistently even when no payload exists. Update
the corresponding endpoint Jest assertions to validate this contract for success
and relevant failure responses.

In `@src/services/stellar/anchorService.js`:
- Around line 86-93: Add a bounded timeout option to every anchor HTTP request
in anchorService, including the /info call and the web-auth GET/POST,
/interactive, and /transaction requests. Use the existing service timeout
configuration or a shared constant consistently across all axios calls,
preserving their current error handling and request behavior.

In `@test/anchorJwtCustody.test.js`:
- Around line 23-28: Update the FAKE_ANCHOR_JWT fixture in
anchorJwtCustody.test.js to sign the token with generated ephemeral test-only
key material instead of the hardcoded secret string, while preserving the
existing exp claim and decode-only behavior.

---

Nitpick comments:
In `@services/emails/sendMail.js`:
- Around line 11-17: Update the pre-flight guard in sendTemplate to also require
EMAILJS_PRIVATE_KEY alongside EMAILJS_SERVICE_ID, templateId, and
EMAILJS_PUBLIC_KEY, while preserving the existing test-environment bypass and
configuration error.

In `@src/controllers/stellar/anchorController.js`:
- Around line 42-43: Update the response construction in the anchor controller
handlers, including the flow containing getAnchorInfo, to use the documented {
success, message, data } shape: move anchor, challenge, transactions,
transaction, and dynamic kind payloads under data while preserving their
existing values and success status. Keep any established message behavior
unchanged.

In `@src/controllers/stellar/paymentController.js`:
- Around line 277-318: Validate path-payment request inputs before the
isPathPayment branch constructs StellarSdk.Asset or calls
buildPathPaymentTransaction: require a valid numeric sendMax, a well-formed
pathInput array, and valid sendAssetInput code and issuer values. Return a clear
client-error response for malformed input, while preserving the existing
transaction-building flow for valid values.
- Around line 36-141: Add Jest coverage for the externally reachable getQuote
controller using mocked models, payment-path lookup, and response objects,
covering invalid item types, missing items, free items, issuer validation, no
paths, and successful slippage-clamped quotes. Also align the successful
response with the established { success, message, data } envelope by placing the
quote under data while preserving existing error status behavior.

In `@src/jobs/anchorPoller.js`:
- Around line 96-108: Increase anchor poller throughput by updating tick to
claim and refresh a bounded batch of due records per interval instead of only
one. Use a small fixed concurrency limit with Promise.all, while preserving the
accepting guard and existing error handling in startAnchorPoller.

In `@src/jobs/queue.js`:
- Around line 61-77: Add an admin requeue action for dead-lettered jobs,
integrated with the existing `/admin/jobs/dead` flow and job-processing symbols
such as `processNext`. The action must reset the selected job’s status to
`queued`, schedule it for immediate execution, and clear its retry-attempt
counter so it can be claimed and retried; preserve idempotency behavior for
normal `enqueue()` calls and restrict requeueing to appropriate dead/completed
jobs.

In `@src/models/AnchorTransaction.js`:
- Around line 10-64: Remove the field-level indexes from the user and status
schema definitions in AnchorTransaction, while preserving the compound indexes {
user: 1, status: 1 } and { status: 1, nextPollAt: 1 }. Keep nextPollAt indexed
and leave all other schema behavior unchanged.

In `@src/models/Job.js`:
- Around line 1-28: Add compound indexes to jobSchema for the dashboard queries:
createdAt descending for the unfiltered recent-jobs query, and status ascending
plus failedAt descending for dead-job queries. Remove the standalone name index
from the name field unless another reviewed query depends on it.

In `@src/routes/jobsRoutes.js`:
- Around line 13-20: Update the authorization check in the router middleware to
compare the provided Bearer token using a constant-time comparison, ensuring
length mismatches are handled safely before invoking the comparison. Preserve
the existing 404 response for a missing JOBS_DASHBOARD_TOKEN, 401 response for
invalid credentials, and next() behavior for valid credentials.
- Around line 22-36: Add Jest coverage in test/jobs.test.js for an authorized
request to /admin/jobs, asserting the successful response and expected job data,
and for /admin/jobs/dead, asserting its successful response and dead-job
results. Mock or seed Job queries consistently with the existing tests and
preserve coverage for the current unauthorized 401 cases.

In `@test/jobHandlers.test.js`:
- Around line 35-97: Add purchase-specific coverage to the verifyPaymentOnChain
tests using a type: "purchase" fixture, mock recordSaleEarnings to reject, and
assert the job remains retryable while side effects are attempted again rather
than being skipped by the confirmed-status guard. Also add coverage for
sendOtpEmail’s missing-user error path, using the handler’s existing
registration and mocks.

In `@test/jobs.test.js`:
- Around line 12-19: Restore environment state mutated by the jobs test suite:
capture the original values of QUEUE_DRIVER and JOBS_DASHBOARD_TOKEN before
tests run, then restore each value or delete the variable when it was initially
unset in afterEach or afterAll. Update the existing beforeEach/afterEach setup
around resetInlineQueueForTests and jest.useRealTimers without changing
unrelated test behavior.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ad21683c-4bfd-45ec-be6f-0d2c54f632cf

📥 Commits

Reviewing files that changed from the base of the PR and between aebaa00 and 6d34f14.

📒 Files selected for processing (35)
  • .env.example
  • README.md
  • app.js
  • docs/anchors.md
  • server.js
  • services/emails/sendMail.js
  • src/config/logger.js
  • src/config/validateEnv.js
  • src/controllers/authController.js
  • src/controllers/stellar/anchorController.js
  • src/controllers/stellar/donationController.js
  • src/controllers/stellar/paymentController.js
  • src/jobs/anchorPoller.js
  • src/jobs/handlers.js
  • src/jobs/queue.js
  • src/models/AnchorTransaction.js
  • src/models/Job.js
  • src/models/Transaction.js
  • src/routes/jobsRoutes.js
  • src/routes/stellar/anchorRoutes.js
  • src/routes/stellar/paymentRoutes.js
  • src/services/stellar/anchorService.js
  • src/services/stellar/stellarService.js
  • test/anchorAuth.test.js
  • test/anchorDiscovery.test.js
  • test/anchorInteractive.test.js
  • test/anchorJwtCustody.test.js
  • test/anchorJwtStorage.test.js
  • test/anchorPoller.test.js
  • test/anchorRoutes.test.js
  • test/anchorTracking.test.js
  • test/app.test.js
  • test/jobHandlers.test.js
  • test/jobs.test.js
  • test/pathPayments.test.js

Comment thread services/emails/sendMail.js
Comment thread src/controllers/authController.js
Comment thread src/controllers/stellar/anchorController.js
Comment thread src/controllers/stellar/anchorController.js Outdated
Comment thread src/controllers/stellar/paymentController.js
Comment thread src/jobs/handlers.js
Comment thread src/routes/stellar/anchorRoutes.js
Comment thread src/services/stellar/anchorService.js Outdated
Comment thread test/anchorJwtCustody.test.js
- Make trustline building best-effort in initiateInteractiveFlow
- Clamp/validate pagination params on transaction listing
- Normalize anchor route responses to { success, message, data }
- Add ANCHOR_HTTP_TIMEOUT_MS (10s) to outbound anchor HTTP calls
- Replace hardcoded JWT test secret with per-run random key
anchorInteractive.test.js mocked utils/cache.js with only 3 functions,
which replaced the module in Jest's registry for all subsequent imports
of that specifier — including transitive imports from bookRoutes.js,
courseRoutes.js, etc. pulled in via app.js. Spread the real module's
exports into the mock factory, overriding only the functions this suite
fakes.
@Cyber-Mitch

Copy link
Copy Markdown
Author

@zeemscript Please Review

@zeemscript

zeemscript commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

@zeemscript Please Review

Please fix conflicts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (9)
src/services/stellar/stellarService.js (3)

292-312: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Close buildChangeTrustTransaction before declaring subsequent exports.

The catch block never rethrows or closes the function, so later export declarations are parsed inside the catch block and the module cannot load. Add the missing throw and closing braces.

Proposed fix
   } catch (error) {
     logger.error("Error building change trust transaction:", error);
+    throw error;
+  }
+};
🤖 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/services/stellar/stellarService.js` around lines 292 - 312, Complete
buildChangeTrustTransaction’s catch block by logging the error, rethrowing it,
and adding the closing braces before any subsequent exports. Ensure later export
declarations remain at module scope and the function propagates failures after
logging.

Source: Linters/SAST tools


294-296: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Use the resilient client.execute path for every new Horizon account load.

These helpers call the fallback server directly, unlike the surrounding refactored code. That bypasses endpoint rotation, retries, timeouts, and circuit-breaker behavior for trustline preflight and refund flows.

Also applies to: 344-346, 508-510

🤖 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/services/stellar/stellarService.js` around lines 294 - 296, Update the
account-loading calls in the trustline preflight and refund flows, including the
sites around loadAccount in the relevant service methods, to use the resilient
client.execute path instead of calling server.loadAccount directly. Preserve the
existing timed operation and ensure every new Horizon account load receives
endpoint rotation, retries, timeouts, and circuit-breaker handling through
client.execute.

292-302: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Pin change-trust generation to the verified USDC asset.

The exported helper accepts any asset even though this flow is specifically for the configured USDC trustline. Remove the override or reject assets whose code/issuer do not match USDC and USDC_ISSUER; otherwise an untrusted caller could obtain unsigned XDR for an arbitrary issuer. As per path instructions, Stellar flows must keep issuer validation server-side while building only the intended unsigned USDC XDR.

🤖 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/services/stellar/stellarService.js` around lines 292 - 302, Update
buildChangeTrustTransaction so callers cannot supply an arbitrary asset: remove
the asset override and always build the change-trust operation for the
configured USDC asset, with server-side validation that its code and issuer
match USDC and USDC_ISSUER before generating unsigned XDR.

Source: Path instructions

.env.example (2)

64-65: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Remove the server-side SIGNING_KEY placeholder from this non-custodial flow.

A private-key environment variable conflicts with the stated design and may lead operators to place Stellar signing secrets in the API environment. Document only the public verification material required by SEP-10, if any. As per PR objectives, this integration must avoid Stellar signing keys and server-side signing.

🤖 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 @.env.example around lines 64 - 65, Remove the server-side SIGNING_KEY
placeholder from the SEP-10 configuration section in .env.example. Document only
the public verification material required by the non-custodial flow, if any, and
do not add or retain environment variables for Stellar private keys or
server-side signing.

23-26: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep each environment key in one canonical block.

These lines duplicate existing EMAILJS_*, Redis, and Jitsi configuration. Duplicate assignments make precedence loader-dependent, so an operator can edit one block while another value silently wins.

Also applies to: 71-87

🤖 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 @.env.example around lines 23 - 26, Remove the duplicate EMAILJS_* entries
from this block and also remove the duplicated Redis and Jitsi configuration in
the referenced section. Retain each environment key only in its existing
canonical block, preserving the example values there.
src/routes/searchRoutes.js (3)

28-29: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add Jest coverage for both changed Express endpoints.

As per path instructions, changed src/**/*.js endpoints require Jest coverage.

  • src/routes/searchRoutes.js#L28-L29: cover /educators responses, cache separation from /, and q/interest/pagination key variations.
  • src/routes/userRoutes.js#L46-L59: cover owner success, non-owner rejection before upload parsing, and avatar upload behavior.
🤖 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/searchRoutes.js` around lines 28 - 29, Add Jest tests for
src/routes/searchRoutes.js lines 28-29 covering /educators responses, cache
separation from /, and cache-key variations for q, interest, and pagination; add
tests for src/routes/userRoutes.js lines 46-59 covering owner success, non-owner
rejection before upload parsing, and avatar upload behavior. Use the existing
endpoint symbols and test conventions, with no direct production-code changes
required.

Source: Path instructions


28-29: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Align the new /educators endpoint with the response contract.

searchEducatorsHandler currently returns { success: false, error } on failures, rather than the required { success, message, data } shape. Update the handler or add an adapter before exposing this route so clients receive a consistent contract.

As per path instructions, src/**/*.js endpoints must use the { success, message, data } response shape.

🤖 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/searchRoutes.js` around lines 28 - 29, Update
searchEducatorsHandler, or add an adapter in the /educators route before it, so
both successful and failed responses consistently use the { success, message,
data } shape; replace the failure response’s error field with the appropriate
message and data values while preserving existing endpoint behavior.

Source: Path instructions


18-29: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Repair the merged cache-key block and remove the duplicate root route.

searchCacheKey already ends at line 14, so lines 18-23 contain a top-level return, producing the reported parse failure. The second router.get("/") is also duplicate and normally shadowed by the first. Merge the pagination/filter logic into one function, remove the stale return, and retain one root route. Use ?? for page and limit so the key matches the controller’s undefined-only defaults.

Proposed structure
 const searchCacheKey = (req) => {
   const query = req.query.q || req.query.query || "";
   const type = req.query.type || "all";
-  return `${CACHE_KEYS.SEARCH}${type}:${query.toLowerCase().trim()}`;
-};
-
-// Main search endpoint - cached for 5 minutes
-router.get("/", cacheMiddleware(CACHE_TTL.SEARCH, searchCacheKey), searchAll);
-
-  const page = req.query.page || 1;
-  const limit = req.query.limit || 10;
+  const page = req.query.page ?? 1;
+  const limit = req.query.limit ?? 10;
   const filterKeys = ["minPrice", "maxPrice", "free", "category", "minRating", "interest", "sort"];
   const filtersStr = filterKeys.map((k) => `${k}=${req.query[k] || ""}`).join("&");
   return `${CACHE_KEYS.SEARCH}${req.path}:${type}:${query.toLowerCase().trim()}:page=${page}:limit=${limit}:${filtersStr}`;
 };
 
-// Main search endpoint
+// Main search endpoint - cached for 5 minutes
 router.get("/", cacheMiddleware(CACHE_TTL.SEARCH, searchCacheKey), searchAll);
🤖 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/searchRoutes.js` around lines 18 - 29, Repair searchCacheKey by
moving its pagination and filter-key construction inside the function, using
nullish coalescing (??) for page and limit to match controller defaults, and
removing the stray top-level return. Remove the duplicate root router.get("/")
registration, retaining one root route alongside the educators route.

Source: Linters/SAST tools

src/routes/userRoutes.js (1)

49-56: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Use one declared upload middleware, after the ownership check.

upload is not imported, so evaluating upload.single("avatar") throws a ReferenceError while registering the router. The route also parses the same multipart request twice. Remove the undefined/duplicate middleware and run the ownership check before the single uploadImage.single("avatar") call.

Proposed fix
 router.put(
   "/update/:id",
   protect,
-  upload.single("avatar"),
   (req, res, next) => {
     if (req.user._id.toString() !== req.params.id) {
       return res.status(403).json({ success: false, message: "Not authorized to update this profile", data: null });
     }
     next();
   },
   uploadImage.single("avatar"),
   invalidateCacheMiddleware([`${CACHE_KEYS.USER}*`]),
   updateUser
 );
🤖 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/userRoutes.js` around lines 49 - 56, In the route middleware
chain, remove the undefined `upload.single("avatar")` call and place the
ownership-check callback before the sole `uploadImage.single("avatar")`
middleware. Ensure the multipart request is parsed exactly once after
authorization succeeds.
🤖 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.

Outside diff comments:
In @.env.example:
- Around line 64-65: Remove the server-side SIGNING_KEY placeholder from the
SEP-10 configuration section in .env.example. Document only the public
verification material required by the non-custodial flow, if any, and do not add
or retain environment variables for Stellar private keys or server-side signing.
- Around line 23-26: Remove the duplicate EMAILJS_* entries from this block and
also remove the duplicated Redis and Jitsi configuration in the referenced
section. Retain each environment key only in its existing canonical block,
preserving the example values there.

In `@src/routes/searchRoutes.js`:
- Around line 28-29: Add Jest tests for src/routes/searchRoutes.js lines 28-29
covering /educators responses, cache separation from /, and cache-key variations
for q, interest, and pagination; add tests for src/routes/userRoutes.js lines
46-59 covering owner success, non-owner rejection before upload parsing, and
avatar upload behavior. Use the existing endpoint symbols and test conventions,
with no direct production-code changes required.
- Around line 28-29: Update searchEducatorsHandler, or add an adapter in the
/educators route before it, so both successful and failed responses consistently
use the { success, message, data } shape; replace the failure response’s error
field with the appropriate message and data values while preserving existing
endpoint behavior.
- Around line 18-29: Repair searchCacheKey by moving its pagination and
filter-key construction inside the function, using nullish coalescing (??) for
page and limit to match controller defaults, and removing the stray top-level
return. Remove the duplicate root router.get("/") registration, retaining one
root route alongside the educators route.

In `@src/routes/userRoutes.js`:
- Around line 49-56: In the route middleware chain, remove the undefined
`upload.single("avatar")` call and place the ownership-check callback before the
sole `uploadImage.single("avatar")` middleware. Ensure the multipart request is
parsed exactly once after authorization succeeds.

In `@src/services/stellar/stellarService.js`:
- Around line 292-312: Complete buildChangeTrustTransaction’s catch block by
logging the error, rethrowing it, and adding the closing braces before any
subsequent exports. Ensure later export declarations remain at module scope and
the function propagates failures after logging.
- Around line 294-296: Update the account-loading calls in the trustline
preflight and refund flows, including the sites around loadAccount in the
relevant service methods, to use the resilient client.execute path instead of
calling server.loadAccount directly. Preserve the existing timed operation and
ensure every new Horizon account load receives endpoint rotation, retries,
timeouts, and circuit-breaker handling through client.execute.
- Around line 292-302: Update buildChangeTrustTransaction so callers cannot
supply an arbitrary asset: remove the asset override and always build the
change-trust operation for the configured USDC asset, with server-side
validation that its code and issuer match USDC and USDC_ISSUER before generating
unsigned XDR.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d8fa1c90-bf95-4c99-964d-7c352cc07aab

📥 Commits

Reviewing files that changed from the base of the PR and between 8801ae2 and 399c82b.

📒 Files selected for processing (7)
  • .env.example
  • app.js
  • src/config/validateEnv.js
  • src/routes/searchRoutes.js
  • src/routes/userRoutes.js
  • src/services/stellar/stellarService.js
  • test/app.test.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/config/validateEnv.js
  • test/app.test.js

@zeemscript zeemscript left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@Cyber-Mitch fix CI please

@Cyber-Mitch

Copy link
Copy Markdown
Author

@Cyber-Mitch fix CI please

okay

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/services/stellar/stellarService.js (1)

371-373: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Close and rethrow from the catch block.

buildChangeTrustTransaction never closes its catch/function and does not rethrow. The module cannot parse, so the server and test suite cannot load this service. Restore throw error;, close the block, and terminate the exported function before the SEP-29 declarations.

Proposed fix
   } catch (error) {
     logger.error("Error building change trust transaction:", error);
+    throw error;
+  }
+};
+
 // SEP-29: an account opts into requiring a memo on incoming payments by
🤖 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/services/stellar/stellarService.js` around lines 371 - 373, Complete the
catch block in buildChangeTrustTransaction by rethrowing the caught error after
logging it, then close the catch and exported function before the SEP-29
declarations so the module parses correctly.

Source: Linters/SAST tools

🤖 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.

Outside diff comments:
In `@src/services/stellar/stellarService.js`:
- Around line 371-373: Complete the catch block in buildChangeTrustTransaction
by rethrowing the caught error after logging it, then close the catch and
exported function before the SEP-29 declarations so the module parses correctly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 39e53461-8269-4150-a0dd-9cfeeae6aaca

📥 Commits

Reviewing files that changed from the base of the PR and between 399c82b and c94b8c8.

📒 Files selected for processing (1)
  • src/services/stellar/stellarService.js

@Cyber-Mitch

Copy link
Copy Markdown
Author

I'm still on it @zeemscript
will make another PR soon

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.

[Enhancement] SEP-24 anchor integration: non-custodial fiat on/off-ramp for USDC via interactive deposit and withdrawal

2 participants