Skip to content
This repository was archived by the owner on Dec 2, 2025. It is now read-only.

Feat/acta integration#266

Open
Villarley wants to merge 9 commits intotrustbridgecr:developfrom
Villarley:feat/acta-integration
Open

Feat/acta integration#266
Villarley wants to merge 9 commits intotrustbridgecr:developfrom
Villarley:feat/acta-integration

Conversation

@Villarley
Copy link
Contributor

@Villarley Villarley commented Sep 26, 2025

Pull Request for TrustBridge - Close Issue

Pull Request Information

This PR integrates ACTA (Verifiable Credentials) into TrustBridge to enable Pool Participation Credentials with a privacy-first approach (no wallet addresses, no exact amounts). It is a frontend-only integration using a small typed HTTP client configured via NEXT_PUBLIC_ACTA_API_URL.

Goals

  • Let users manually create privacy-preserving credentials that summarize DeFi pool participation (duration, risk, performance, pool types).
  • Manage credentials (list, status updates, sharing) directly from the dApp.
  • Provide a simple Health Check to validate ACTA connectivity.

🌀 Summary of Changes

  • Config

    • frontend/.env.example: add NEXT_PUBLIC_ACTA_API_URL=https://acta.up.railway.app.
  • Typed HTTP Client (no provider)

    • src/@types/acta.types.ts: ActaOk/ActaErr, ActaHealth, CreateCredentialReq/Res, GetCredentialRes, and PoolParticipationClaims.
    • src/lib/acta/client.ts: ping, health, createCredential, getByContractId, getByHash, updateStatus.
      Always returns { success, data|error } (no thrown exceptions), ready to accept Authorization/X-API-Key headers in the future.
  • Health Check (dev tool)

    • src/components/dev/ActaHealthCard.tsx: “Check ACTA health” button, status badge, collapsible JSON.
  • Credentials (creation + management)

    • src/components/modules/credentials/ui/components/CreateCredentialModal.tsx
      Form (react-hook-form + ShadCN) for:
      • participationDuration, riskLevel, performanceTier, poolTypeExperience[]
      • optional issuer, issuanceDate (default now), optional expirationDate
    • src/components/modules/credentials/ui/pages/CredentialsDashboardPage.tsx: list, filters by status, empty state.
    • src/components/modules/credentials/ui/components/CredentialCard.tsx: summary, status badge, actions (Share, Update status).
    • src/components/modules/credentials/hooks/useCredentialCreate.ts & useCredentials.ts: wrap client calls + local cache.
    • src/lib/credentials/local-index.ts: lightweight persistence in localStorage['tb_vc_index_v1'] (only non-sensitive display data + IDs).
  • Status Update & Share

    • PATCH /credentials/:contractId/status with optimistic UI + rollback on error.
    • Share button: copies a readable summary (IDs hidden by default; toggle to reveal contractId/hash).
  • Pool Touchpoints

    • BorrowerPoolPage.tsx & LenderPoolPage.tsx: non-blocking “Your verifiable reputation” card/pill that opens Dashboard/Create.
  • Docs & Tests

    • README: “ACTA Integration” section (setup, troubleshooting).
    • Unit tests for client.ts and hooks (mocked fetch); basic e2e happy path (Health → Create → List → Update → Share).

🛠 Testing

Evidence Before Solution

  • Behavior: No ACTA connectivity, no credential creation/management UI, no reputation hints in pool pages.
  • Video: Link to Loom video

Evidence After Solution

  • Behavior:
    1. Health Check shows “healthy” with network info.
    2. Users can create credentials (duration/risk/performance/pool types).
    3. Dashboard lists credentials and reflects server status.
    4. Share copies a privacy-preserving summary.
    5. Pool pages show a reputation entry point.
  • Video: Link to Loom video

Manual Test Steps

  1. Set NEXT_PUBLIC_ACTA_API_URL (default prod URL is fine).
  2. Open ActaHealthCard, click Check ACTA health → expect “healthy”.
  3. Open CreateCredentialModal, fill the four claims, submit → toast success with contractId/hash.
  4. Open CredentialsDashboardPage → new credential listed; status badge visible.
  5. Update status to Suspended → badge changes (optimistic), persists on reload.
  6. Click Share → verify copied summary (IDs hidden by default).

✅ ESLint Compliance (Mandatory)

To ensure that the code follows project standards, please run the following command and attach a screenshot of the output:

npm run lint

You should see:

✔ No ESLint warnings or errors

📸 Attach a screenshot showing the result of the lint check:
image

⚠️ Pull requests without this screenshot will be rejected.


📂 Related Issue

This pull request will close #264 upon merging.


🎉 Thank you for reviewing this PR! 🎉

Summary by CodeRabbit

  • New Features

    • Full ACTA credentials support: create credentials modal, selector, management dashboard, hooks for create/list/update, and optional credential flow in Borrow/Supply modals.
    • ACTA API client for connectivity, health checks, and credential operations.
  • UI/UX

    • Credential cards, status badges, copy/share, dialogs, toasts, refresh/filters, and developer test pages for credential workflows.
  • Chores

    • Added environment variables to configure ACTA API URL and default network.

- Add ActaClient class for interacting with ACTA API endpoints
- Include methods for pinging the service, checking health, and managing credentials
- Define type interfaces for API responses, credential creation, and client configuration
- Implement error handling and request timeout management

Resolves: Establish a structured client for ACTA API operations
- Implement methods for creating, updating, and deleting credentials
- Enhance error handling for credential operations
- Update type interfaces to reflect new credential management features

Resolves: Improved functionality for managing credentials within the ACTA API client
- Introduce new types for participation duration, risk levels, performance tiers, and pool type experience for credentials.
- Update CreateCredentialBody interface to align with the actual API response structure.
- Refactor API endpoint paths to remove the 'acta' prefix for a cleaner interface.
- Add a new method to list all credentials.

Resolves: Improved structure and functionality for credential management in the ACTA API client.
- Introduce an optional onSuccess prop to the CreateCredentialModal component.
- Invoke onSuccess callback upon successful credential creation to enhance user feedback.

Resolves: Improved user experience by providing a callback for successful operations in the credential creation process.
…omponent

- Adjust the layout of the CredentialCard component by removing unnecessary flex properties for better alignment.
- Enhance the styling of the status badge to improve visual clarity and user experience.

Resolves: Improved UI consistency and readability in the CredentialCard component.
- Remove error parameter from catch blocks in CredentialCard component to streamline error handling.
- Update toast messages to maintain user feedback without exposing error details.

Resolves: Improved code clarity and consistency in error handling for credential operations.
…pplyUSDCModal

- Add CredentialSelector component to BorrowModal and SupplyUSDCModal for optional credential selection.
- Implement state management for selected credentials to enhance user experience and unlock better terms.
- Include UI updates to display selected credential information and potential benefits.

Resolves: Improved functionality and user engagement through credential verification options in borrowing and supplying processes.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 26, 2025

Walkthrough

Adds ACTA integration across the frontend: environment variables, a typed Acta client library, comprehensive ACTA TypeScript types, hooks for credential creation and management, UI components (create modal, card, selector, dashboard), dev/test pages, and optional credential selection UI embedded in marketplace modals.

Changes

Cohort / File(s) Summary
Environment
frontend/.env.example
Adds NEXT_PUBLIC_ACTA_API_URL and NEXT_PUBLIC_ACTA_DEFAULT_NETWORK.
ACTA types
frontend/src/@types/acta.types.ts
New ACTA TypeScript definitions, type guards (isActaOk, isActaErr), extractActaData, constants (POOL_DURATION_DAYS, RISK_LEVEL_WEIGHTS).
ACTA client
frontend/src/lib/acta/client.ts
New ActaClient class and createActaClient factory with request handling, health endpoints, credential CRUD/status methods, timeout/abort handling, and public exports/types.
Credential hooks & storage
frontend/src/components/modules/credentials/hooks/useCredentialCreate.ts, .../useCredentials.ts
New hooks: useCredentialCreate (create flow, localStorage indexing) and useCredentials (status reconciliation, filtering, optimistic updates), plus utilities (getCredentialsFromLocalStorage, removeCredentialFromLocalStorage, formatCredentialSummary, copyToClipboard).
Credential UI components
frontend/src/components/modules/credentials/ui/components/CreateCredentialModal.tsx, .../CredentialCard.tsx, .../CredentialSelector.tsx
New CreateCredentialModal (form+validation), CredentialCard (view/manage credential, status actions), and CredentialSelector (dialog to pick an Active credential).
Credentials dashboard & pages
frontend/src/components/modules/credentials/ui/pages/CredentialsDashboardPage.tsx, frontend/src/app/dev/credentials-dashboard/page.tsx
New dashboard UI and dev test page wiring an ActaClient.
Dev/test pages
frontend/src/app/dev/credential-test/page.tsx, .../credential-selector/page.tsx, .../modals-with-credentials/page.tsx
Adds developer/test pages for API ping, create credential flow, selector demo, and modal integration demos.
Marketplace modal integrations
frontend/src/components/modules/marketplace/ui/components/BorrowModal.tsx, .../SupplyUSDCModal.tsx
Embed optional credential selection via CredentialSelector and Acta client instances; display selected credential details (UI only; core transaction logic unchanged).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant UI as CreateCredentialModal
  participant Hook as useCredentialCreate
  participant Client as ActaClient
  participant API as ACTA API
  participant LS as localStorage
  participant Toast as Toast

  User->>UI: Submit credential form
  UI->>Hook: createCredential(formData)
  Hook->>Client: createCredential(body)
  Client->>API: POST /credentials
  API-->>Client: ActaResponse<CredentialData>
  Client-->>Hook: ActaResponse
  alt success
    Hook->>LS: Save LocalCredentialRecord (index)
    Hook-->>UI: CredentialCreateResult
    UI->>Toast: show success
    UI-->>User: close/reset modal
  else error
    Hook-->>UI: throw/error
    UI->>Toast: show error
  end
Loading
sequenceDiagram
  autonumber
  actor User
  participant Parent as Borrow/Supply Modal
  participant Selector as CredentialSelector
  participant Hook as useCredentials
  participant LS as localStorage
  participant Client as ActaClient
  participant API as ACTA API

  User->>Parent: open modal
  Parent->>Selector: open selector dialog
  Selector->>Hook: refreshCredentials()
  Hook->>LS: load stored credentials
  par fetch statuses
    Hook->>Client: getByContractId / listCredentials
    Client->>API: GET /credentials...
    API-->>Client: ActaResponse
    Client-->>Hook: status/data
  end
  Hook-->>Selector: credentials (filtered Active)
  User->>Selector: choose credential
  Selector-->>Parent: onCredentialSelect(credential)
  Parent-->>User: display selected credential details (risk/performance)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

codex

Poem

I hop through props and envs with cheer,
I stitch credentials both far and near.
I save a local token in a cozy nest,
Then pick the best one for the test.
A ping, a toast, a little hop — success! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Linked Issues Check ⚠️ Warning While the pull request adds environment variables, type definitions, the Acta client, credential creation and management UI, and pool integration, it omits the configuration panel or settings UI for entering the ACTA API URL and API key at runtime and does not include the referenced ActaHealthCard component for viewing health check status as described in issue #264. Please add the missing configuration UI for the ACTA API URL and API key, include or refactor in the ActaHealthCard component for health checks, and ensure the .env.example lists the API key variable so all linked objectives in issue #264 are fully implemented.
Out of Scope Changes Check ⚠️ Warning The PR introduces several development‐only pages under src/app/dev (credential-selector, credential-test, credentials-dashboard, modals-with-credentials) that are not specified in the linked issue’s requirements and appear to serve internal testing rather than core integration functionality. Consider moving these test/demo pages to a separate branch or clearly marking them as development-only, and limit the main integration branch to components and pages required by the issue’s scope.
✅ 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 “Feat/acta integration” succinctly highlights the primary feature added—integrating ACTA support—without extraneous details, making it clear to reviewers at a glance what the pull request accomplishes. It directly relates to the core change set and is concise.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ 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

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (26)
frontend/src/components/modules/credentials/ui/components/CredentialCard.tsx (4)

47-61: Deduplicate status color/icon maps across components.

statusColors and statusIcons are duplicated elsewhere (e.g., CredentialSelector). Extract to a shared constants module to keep styling consistent and reduce drift.


279-286: Disable “View Contract Details” when details are missing.

Avoid opening an empty dialog if contractId/hash are not present; add a title for clarity.

Apply this diff:

-            <Button
+            <Button
               variant="outline"
               size="sm"
-              className="w-full btn-secondary"
+              className="w-full btn-secondary"
+              disabled={!contractId || !hash}
+              title={!contractId || !hash ? "Contract details unavailable" : undefined}
             >

339-347: Disable “Include contract details” when IDs are unavailable.

Prevent toggling when contractId/hash are missing; keeps privacy control semantics clear.

Apply this diff:

-                <Switch
+                <Switch
                   id="show-contract-details"
                   checked={showContractDetails}
-                  onCheckedChange={setShowContractDetails}
+                  onCheckedChange={setShowContractDetails}
+                  disabled={!contractId || !hash}
                 />
-                <Label htmlFor="show-contract-details" className="text-white">
+                <Label
+                  htmlFor="show-contract-details"
+                  className={`text-white ${!contractId || !hash ? "opacity-50" : ""}`}
+                >

116-126: Use a consistent confirmation dialog instead of window.confirm.

window.confirm breaks visual consistency and is harder to test. Reuse your Dialog component for the remove confirmation.

frontend/src/components/modules/credentials/ui/components/CredentialSelector.tsx (2)

44-49: Share status color mapping with CredentialCard.

This mapping duplicates statusColors. Import from a shared constants file to avoid divergence.


122-125: Memoize active credential filtering.

Prevents redundant filtering on re-renders.

Apply this diff:

-  const activeCredentials = credentials.filter(
-    (c) => (c.status || "Active") === "Active",
-  );
+  const activeCredentials = React.useMemo(
+    () => credentials.filter((c) => (c.status || "Active") === "Active"),
+    [credentials],
+  );

And ensure useMemo is available via React.useMemo (no import change needed).

frontend/src/components/modules/credentials/ui/components/CreateCredentialModal.tsx (1)

235-236: Close only when dialog requests closing.

onOpenChange={handleClose} ignores the boolean arg and can fire on open as well. Only close when open becomes false.

Apply this diff:

-    <Dialog open={isOpen} onOpenChange={handleClose}>
+    <Dialog open={isOpen} onOpenChange={(open) => !open && handleClose()}>
frontend/src/components/modules/marketplace/ui/components/BorrowModal.tsx (1)

195-227: Optional: Extract collateral factor to a constant.

85 is used multiple times; hoist to a named constant for clarity and easier tuning.

frontend/src/app/dev/credential-test/page.tsx (3)

27-31: Memoize ActaClient in the dev page too.

Avoid recreating the client on each render; keeps identity stable for components using it.

Apply this diff:

-  const client = new ActaClient({
-    baseUrl: process.env.NEXT_PUBLIC_ACTA_API_URL || "http://localhost:3001",
-    apiKey: process.env.NEXT_PUBLIC_ACTA_API_KEY,
-  });
+  const client = React.useMemo(
+    () =>
+      new ActaClient({
+        baseUrl: process.env.NEXT_PUBLIC_ACTA_API_URL || "http://localhost:3001",
+        apiKey: process.env.NEXT_PUBLIC_ACTA_API_KEY,
+      }),
+    [],
+  );

381-387: Auto-refresh local list after creation.

Wire onSuccess to refresh the local index and close the modal for a tighter feedback loop.

Apply this diff:

       <CreateCredentialModal
         isOpen={isModalOpen}
         onClose={() => setIsModalOpen(false)}
         client={client}
+        onSuccess={() => {
+          const updated = getCredentialsFromLocalStorage();
+          setCredentials(updated);
+          setIsModalOpen(false);
+        }}
       />

239-243: Use transactionHash or remove TX row
The TX row repeats the 8-char hash because credential is a LocalCredentialRecord without a transactionHash. Either persist and display transactionHash, or drop the TX row.
Location: frontend/src/app/dev/credential-test/page.tsx:239-243

frontend/src/components/modules/credentials/hooks/useCredentialCreate.ts (6)

75-77: Avoid deprecated substr and prefer stable unique IDs.

Use crypto.randomUUID() with a fallback to slice to avoid deprecated APIs and improve uniqueness.

-function generateLocalId(): string {
-  return `local_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
-}
+function generateLocalId(): string {
+  const rnd =
+    typeof crypto !== "undefined" && "randomUUID" in crypto
+      ? (crypto as Crypto).randomUUID()
+      : Math.random().toString(36).slice(2, 11);
+  return `local_${rnd}`;
+}

82-95: Guard localStorage access for SSR/unavailable storage.

Next.js may render server-side; also storage can be disabled. Add a guard to silently skip when not available.

 function saveCredentialToLocalStorage(record: LocalCredentialRecord): void {
   try {
+    if (typeof window === "undefined" || !("localStorage" in window)) return;
     const existingIndex = localStorage.getItem(CREDENTIAL_INDEX_KEY);
     const index: LocalCredentialRecord[] = existingIndex
       ? JSON.parse(existingIndex)
       : [];
 
     index.push(record);
     localStorage.setItem(CREDENTIAL_INDEX_KEY, JSON.stringify(index));
   } catch (error) {

119-130: Validate optional expiration occurs after issuance.

Prevent creating an already-expired credential.

-          issuanceDate: new Date().toISOString(),
-          expirationDate: formData.expirationDate,
+          issuanceDate: new Date().toISOString(),
+          expirationDate: formData.expirationDate,
         };
+        if (credentialData.expirationDate) {
+          const exp = Date.parse(credentialData.expirationDate);
+          const iss = Date.parse(credentialData.issuanceDate);
+          if (Number.isFinite(exp) && Number.isFinite(iss) && exp <= iss) {
+            throw new Error("Expiration date must be after issuance date");
+          }
+        }

179-186: Trim console noise in production.

Remove or guard console logging that can leak PII/structure and clutter logs.

-          saveCredentialToLocalStorage(localRecord);
-          console.log("Saved to localStorage:", localRecord);
+          saveCredentialToLocalStorage(localRecord);

216-224: Guard localStorage read for SSR/unavailable storage.

Return an empty list when storage is not present.

 export function getCredentialsFromLocalStorage(): LocalCredentialRecord[] {
   try {
+    if (typeof window === "undefined" || !("localStorage" in window)) return [];
     const data = localStorage.getItem(CREDENTIAL_INDEX_KEY);
     return data ? JSON.parse(data) : [];
   } catch (error) {

229-244: Guard localStorage write/remove for SSR/unavailable storage.

Avoid exceptions when storage is not available.

 export function removeCredentialFromLocalStorage(localId: string): boolean {
   try {
+    if (typeof window === "undefined" || !("localStorage" in window)) return false;
     const existingIndex = localStorage.getItem(CREDENTIAL_INDEX_KEY);
     const index: LocalCredentialRecord[] = existingIndex
       ? JSON.parse(existingIndex)
       : [];
frontend/src/components/modules/credentials/hooks/useCredentials.ts (3)

195-242: Rollback to previous status, not hard-coded "Active".

Optimistic rollback should restore the prior value for that credential. Also add credentials as a dependency to avoid stale closures.

-  const updateCredentialStatus = useCallback(
+  const updateCredentialStatus = useCallback(
     async (
       contractId: string,
       newStatus: "Active" | "Revoked" | "Suspended",
     ): Promise<boolean> => {
-      // Optimistic update
+      // Capture previous status for rollback
+      const prevStatus =
+        credentials.find((c) => c.contractId === contractId)?.status ?? "Active";
+      // Optimistic update
       setCredentials((prevCredentials) =>
         prevCredentials.map((credential) =>
           credential.contractId === contractId
             ? { ...credential, status: newStatus }
             : credential,
         ),
       );
 
       try {
         // Update on server
         const success = await updateCredentialStatusOnServer(
           client,
           contractId,
           newStatus,
         );
 
         if (!success) {
           // Rollback optimistic update on error
           setCredentials((prevCredentials) =>
             prevCredentials.map((credential) =>
               credential.contractId === contractId
-                ? { ...credential, status: "Active" } // Rollback to default
+                ? { ...credential, status: prevStatus }
                 : credential,
             ),
           );
           return false;
         }
 
         return true;
       } catch {
         // Rollback optimistic update on error
         setCredentials((prevCredentials) =>
           prevCredentials.map((credential) =>
             credential.contractId === contractId
-              ? { ...credential, status: "Active" } // Rollback to default
+              ? { ...credential, status: prevStatus }
               : credential,
           ),
         );
         return false;
       }
     },
-    [client],
+    [client, credentials],
   );

279-287: Initialize status counters to avoid undefined keys.

Ensure all filter keys exist, even when zero.

-  const credentialsByStatus = credentials.reduce(
-    (acc, credential) => {
+  const credentialsByStatus = credentials.reduce(
+    (acc, credential) => {
       const status = credential.status || "Active";
       acc[status] = (acc[status] || 0) + 1;
       acc["All"] = (acc["All"] || 0) + 1;
       return acc;
     },
-    {} as Record<StatusFilter, number>,
+    { All: 0, Active: 0, Revoked: 0, Suspended: 0 } as Record<StatusFilter, number>,
   );

333-341: Provide clipboard fallback for older browsers / insecure contexts.

Navigator clipboard can be unavailable. Add a textarea fallback.

 export async function copyToClipboard(text: string): Promise<boolean> {
   try {
-    await navigator.clipboard.writeText(text);
-    return true;
+    if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
+      await navigator.clipboard.writeText(text);
+      return true;
+    }
+    // Fallback
+    if (typeof document !== "undefined") {
+      const ta = document.createElement("textarea");
+      ta.value = text;
+      ta.style.position = "fixed";
+      ta.style.left = "-9999px";
+      document.body.appendChild(ta);
+      ta.focus();
+      ta.select();
+      const ok = document.execCommand("copy");
+      document.body.removeChild(ta);
+      return ok;
+    }
+    return false;
   } catch (clipboardError) {
     console.error("Failed to copy to clipboard:", clipboardError);
     return false;
   }
 }
frontend/src/@types/acta.types.ts (2)

112-119: Narrow status typing or introduce a shared union to avoid stringly-typed code.

status is plain string across responses. Consider a shared union (for local lifecycle status) to improve safety and align UI code.

Example addition:

 export interface CreateCredentialRes {
   id: string;
   hash: string;
-  status: string;
+  status: string; // Keep API as string if server uses varied values
   createdAt: string;
   updatedAt: string;
   credential: CreateCredentialReq;
 }
+
+// Local lifecycle status used in UI and filters
+export type CredentialLifecycleStatus = "Active" | "Suspended" | "Revoked";

141-153: Constrain update status request/response with a local union.

If the UI only supports "Active" | "Suspended" | "Revoked", reflect that in the update shapes to prevent invalid updates.

-export interface UpdateCredentialStatusReq {
-  status: string;
-}
+export interface UpdateCredentialStatusReq {
+  status: CredentialLifecycleStatus;
+}
 
 export interface UpdateCredentialStatusRes {
   id: string;
   hash: string;
-  status: string;
+  status: CredentialLifecycleStatus;
   updatedAt: string;
 }
frontend/src/app/dev/credentials-dashboard/page.tsx (1)

12-15: Memoize ActaClient to avoid re-instantiation on re-renders.

Saves needless allocations and avoids re-creating the client on each render.

-import React from "react";
+import React, { useMemo } from "react";
 ...
-  const client = new ActaClient({
-    baseUrl: process.env.NEXT_PUBLIC_ACTA_API_URL || "http://localhost:3001",
-    apiKey: process.env.NEXT_PUBLIC_ACTA_API_KEY,
-  });
+  const client = useMemo(
+    () =>
+      new ActaClient({
+        baseUrl:
+          process.env.NEXT_PUBLIC_ACTA_API_URL || "http://localhost:3001",
+        apiKey: process.env.NEXT_PUBLIC_ACTA_API_KEY,
+      }),
+    [],
+  );
frontend/src/app/dev/credential-selector/page.tsx (1)

26-29: Memoize ActaClient instance.

Same reasoning as the dashboard test page; reduces churn.

-import { ActaClient } from "@/lib/acta/client";
+import { ActaClient } from "@/lib/acta/client";
+import { useMemo } from "react";
 ...
-  const client = new ActaClient({
-    baseUrl: process.env.NEXT_PUBLIC_ACTA_API_URL || "http://localhost:3001",
-    apiKey: process.env.NEXT_PUBLIC_ACTA_API_KEY,
-  });
+  const client = useMemo(
+    () =>
+      new ActaClient({
+        baseUrl:
+          process.env.NEXT_PUBLIC_ACTA_API_URL || "http://localhost:3001",
+        apiKey: process.env.NEXT_PUBLIC_ACTA_API_KEY,
+      }),
+    [],
+  );
frontend/src/components/modules/credentials/ui/pages/CredentialsDashboardPage.tsx (1)

297-305: Move the Label import to the top with other imports.

Keeps imports organized and avoids linter warnings.

 import {
   Select,
   SelectContent,
   SelectItem,
   SelectTrigger,
   SelectValue,
 } from "@/components/ui/select";
+import { Label } from "@/components/ui/label";
 ...
-// Add missing import for Label
-import { Label } from "@/components/ui/label";

Also applies to: 397-399

frontend/src/components/modules/marketplace/ui/components/SupplyUSDCModal.tsx (1)

5-9: Memoize ActaClient; avoid re-creating per render.

Prevents unnecessary instantiation and aligns with typical client usage.

-import { useState } from "react";
+import { useMemo, useState } from "react";
 ...
-  // Initialize Acta client (in a real app, this would come from context/config)
-  const actaClient = new ActaClient({
-    baseUrl: process.env.NEXT_PUBLIC_ACTA_API_URL || "http://localhost:3001",
-    apiKey: process.env.NEXT_PUBLIC_ACTA_API_KEY,
-  });
+  // Initialize Acta client (in a real app, this would come from context/config)
+  const actaClient = useMemo(
+    () =>
+      new ActaClient({
+        baseUrl:
+          process.env.NEXT_PUBLIC_ACTA_API_URL || "http://localhost:3001",
+        apiKey: process.env.NEXT_PUBLIC_ACTA_API_KEY,
+      }),
+    [],
+  );

Also applies to: 31-35

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 24617cf and 1e050d2.

📒 Files selected for processing (15)
  • frontend/.env.example (1 hunks)
  • frontend/src/@types/acta.types.ts (1 hunks)
  • frontend/src/app/dev/credential-selector/page.tsx (1 hunks)
  • frontend/src/app/dev/credential-test/page.tsx (1 hunks)
  • frontend/src/app/dev/credentials-dashboard/page.tsx (1 hunks)
  • frontend/src/app/dev/modals-with-credentials/page.tsx (1 hunks)
  • frontend/src/components/modules/credentials/hooks/useCredentialCreate.ts (1 hunks)
  • frontend/src/components/modules/credentials/hooks/useCredentials.ts (1 hunks)
  • frontend/src/components/modules/credentials/ui/components/CreateCredentialModal.tsx (1 hunks)
  • frontend/src/components/modules/credentials/ui/components/CredentialCard.tsx (1 hunks)
  • frontend/src/components/modules/credentials/ui/components/CredentialSelector.tsx (1 hunks)
  • frontend/src/components/modules/credentials/ui/pages/CredentialsDashboardPage.tsx (1 hunks)
  • frontend/src/components/modules/marketplace/ui/components/BorrowModal.tsx (14 hunks)
  • frontend/src/components/modules/marketplace/ui/components/SupplyUSDCModal.tsx (4 hunks)
  • frontend/src/lib/acta/client.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (14)
frontend/src/components/modules/credentials/ui/components/CredentialCard.tsx (1)
frontend/src/components/modules/credentials/hooks/useCredentials.ts (3)
  • CredentialWithStatus (19-23)
  • formatCredentialSummary (306-328)
  • copyToClipboard (333-341)
frontend/src/components/modules/credentials/ui/components/CredentialSelector.tsx (2)
frontend/src/lib/acta/client.ts (1)
  • ActaClient (80-317)
frontend/src/components/modules/credentials/hooks/useCredentials.ts (2)
  • CredentialWithStatus (19-23)
  • useCredentials (126-301)
frontend/src/components/modules/credentials/ui/pages/CredentialsDashboardPage.tsx (3)
frontend/src/lib/acta/client.ts (1)
  • ActaClient (80-317)
frontend/src/components/modules/credentials/hooks/useCredentials.ts (2)
  • StatusFilter (28-28)
  • useCredentials (126-301)
frontend/src/components/ui/label.tsx (1)
  • Label (24-24)
frontend/src/app/dev/credential-test/page.tsx (4)
frontend/src/@types/acta.types.ts (1)
  • LocalCredentialRecord (222-235)
frontend/src/components/modules/credentials/hooks/useCredentialCreate.ts (2)
  • LocalCredentialRecord (21-21)
  • getCredentialsFromLocalStorage (216-224)
frontend/src/lib/acta/client.ts (1)
  • ActaClient (80-317)
frontend/src/components/modules/credentials/ui/components/CreateCredentialModal.tsx (1)
  • CreateCredentialModal (170-525)
frontend/src/app/dev/modals-with-credentials/page.tsx (2)
frontend/src/components/modules/marketplace/ui/components/BorrowModal.tsx (1)
  • BorrowModal (40-494)
frontend/src/components/modules/marketplace/ui/components/SupplyUSDCModal.tsx (1)
  • SupplyUSDCModal (16-268)
frontend/src/components/modules/credentials/hooks/useCredentialCreate.ts (2)
frontend/src/@types/acta.types.ts (3)
  • ReputationClaims (199-204)
  • LocalCredentialRecord (222-235)
  • PoolParticipationCredentialData (209-217)
frontend/src/lib/acta/client.ts (4)
  • ActaClient (80-317)
  • createCredential (151-156)
  • CreateCredentialBody (336-336)
  • CredentialData (337-337)
frontend/src/app/dev/credential-selector/page.tsx (3)
frontend/src/components/modules/credentials/hooks/useCredentials.ts (1)
  • CredentialWithStatus (19-23)
frontend/src/lib/acta/client.ts (1)
  • ActaClient (80-317)
frontend/src/components/modules/credentials/ui/components/CredentialSelector.tsx (1)
  • CredentialSelector (108-287)
frontend/src/app/dev/credentials-dashboard/page.tsx (2)
frontend/src/lib/acta/client.ts (1)
  • ActaClient (80-317)
frontend/src/components/modules/credentials/ui/pages/CredentialsDashboardPage.tsx (1)
  • CredentialsDashboardPage (215-395)
frontend/src/lib/acta/client.ts (1)
frontend/src/@types/acta.types.ts (1)
  • ActaResponse (30-30)
frontend/src/components/modules/credentials/hooks/useCredentials.ts (2)
frontend/src/lib/acta/client.ts (1)
  • ActaClient (80-317)
frontend/src/components/modules/credentials/hooks/useCredentialCreate.ts (2)
  • getCredentialsFromLocalStorage (216-224)
  • removeCredentialFromLocalStorage (229-244)
frontend/src/@types/acta.types.ts (2)
frontend/src/lib/acta/client.ts (1)
  • ActaResponse (335-335)
frontend/src/components/modules/credentials/hooks/useCredentialCreate.ts (1)
  • LocalCredentialRecord (21-21)
frontend/src/components/modules/credentials/ui/components/CreateCredentialModal.tsx (3)
frontend/src/lib/acta/client.ts (2)
  • ActaClient (80-317)
  • createCredential (151-156)
frontend/src/@types/acta.types.ts (4)
  • ParticipationDuration (172-172)
  • CredentialRiskLevel (177-177)
  • PerformanceTier (182-185)
  • PoolTypeExperience (190-194)
frontend/src/components/modules/credentials/hooks/useCredentialCreate.ts (2)
  • useCredentialCreate (100-211)
  • CredentialCreateFormData (26-30)
frontend/src/components/modules/marketplace/ui/components/SupplyUSDCModal.tsx (5)
frontend/src/components/modules/credentials/hooks/useCredentials.ts (1)
  • CredentialWithStatus (19-23)
frontend/src/components/modules/marketplace/hooks/useWalletBalance.hook.ts (1)
  • useWalletBalance (11-66)
frontend/src/lib/acta/client.ts (1)
  • ActaClient (80-317)
frontend/src/components/modules/credentials/ui/components/CredentialSelector.tsx (1)
  • CredentialSelector (108-287)
frontend/src/components/modules/marketplace/hooks/useSupply.hook.ts (1)
  • useSupply (21-235)
frontend/src/components/modules/marketplace/ui/components/BorrowModal.tsx (4)
frontend/src/helpers/health-factor.helper.ts (1)
  • HealthFactorResult (7-16)
frontend/src/components/modules/credentials/hooks/useCredentials.ts (1)
  • CredentialWithStatus (19-23)
frontend/src/lib/acta/client.ts (1)
  • ActaClient (80-317)
frontend/src/components/modules/credentials/ui/components/CredentialSelector.tsx (1)
  • CredentialSelector (108-287)
🔇 Additional comments (8)
frontend/src/components/modules/credentials/ui/components/CreateCredentialModal.tsx (1)

217-221: Confirm expirationDate format expected by ACTA API.

Input provides "YYYY-MM-DD". If ACTA expects ISO 8601, convert before sending (e.g., new Date(value).toISOString()).

Would you like me to patch conversion to ISO in onSubmit once you confirm the expected format?

frontend/src/components/modules/marketplace/ui/components/BorrowModal.tsx (1)

52-56: Public API key usage — verify intent.

Using NEXT_PUBLIC_ACTA_API_KEY exposes the key to all clients. Confirm the ACTA API key is safe to be public; if not, use a server-side proxy or user-provided key stored client-side (not bundled).

frontend/src/app/dev/credential-test/page.tsx (1)

27-31: Public API key usage — verify intent (dev page).

Same concern as BorrowModal: NEXT_PUBLIC_ACTA_API_KEY is exposed. Confirm this is safe for ACTA or switch to a safer approach.

frontend/src/components/modules/credentials/hooks/useCredentials.ts (1)

109-116: Confirm correct HTTP method and path for credential status update
PR description mentions PATCH /credentials/:contractId/status but the client calls PUT /credentials/{id}/status; please share the ACTA OpenAPI snippet or API docs for this endpoint so we can verify the exact HTTP verb and URL format.

frontend/src/components/modules/marketplace/ui/components/SupplyUSDCModal.tsx (1)

126-159: Good, optional credential UX is non-blocking.

Selector wiring and benefits hint look correct and match the privacy-preserving goal.

frontend/src/@types/acta.types.ts (2)

361-367: No JSON serialization of POOL_DURATION_DAYS detected Infinity is only used in-memory and never persisted; no changes required.


334-356: No duplicate Acta type definitions found
All ActaOk, ActaErr, and ActaResponse are declared only in frontend/src/@types/acta.types.ts.

frontend/src/app/dev/modals-with-credentials/page.tsx (1)

250-256: Do not remove poolData prop
BorrowModalProps includes poolData: PoolData | null, so passing poolData={mockPoolData} is valid.

Likely an incorrect or invalid review comment.

Comment on lines +9 to +12
# Acta
NEXT_PUBLIC_ACTA_API_URL=https://acta.up.railway.app
NEXT_PUBLIC_ACTA_DEFAULT_NETWORK=testnet

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Add API key sample and warn about client-exposed secrets.

  • .env.example is missing NEXT_PUBLIC_ACTA_API_KEY, yet code reads it in multiple places. Add it to guide setup.
  • If the ACTA API key is sensitive, avoid exposing it via NEXT_PUBLIC and route via a backend proxy. If it’s intended to be public (rate-limited per-user), document that explicitly.

Apply this diff:

 # Acta 
 NEXT_PUBLIC_ACTA_API_URL=https://acta.up.railway.app
 NEXT_PUBLIC_ACTA_DEFAULT_NETWORK=testnet
+NEXT_PUBLIC_ACTA_API_KEY=

To confirm usage and avoid dead vars, search for references:


🏁 Script executed:

#!/bin/bash
rg -n -C2 'NEXT_PUBLIC_ACTA_DEFAULT_NETWORK|NEXT_PUBLIC_ACTA_API_KEY|NEXT_PUBLIC_ACTA_API_URL'

Length of output: 3628


Add NEXT_PUBLIC_ACTA_API_KEY to .env.example and warn about public exposure

 # Acta 
 NEXT_PUBLIC_ACTA_API_URL=https://acta.up.railway.app
 NEXT_PUBLIC_ACTA_DEFAULT_NETWORK=testnet
+# WARNING: NEXT_PUBLIC_ keys are exposed to the browser. If this API key is sensitive, proxy requests through a backend; if it’s intended to be public, document rate limits and usage.
 NEXT_PUBLIC_ACTA_API_KEY=
📝 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
# Acta
NEXT_PUBLIC_ACTA_API_URL=https://acta.up.railway.app
NEXT_PUBLIC_ACTA_DEFAULT_NETWORK=testnet
# Acta
NEXT_PUBLIC_ACTA_API_URL=https://acta.up.railway.app
NEXT_PUBLIC_ACTA_DEFAULT_NETWORK=testnet
# WARNING: NEXT_PUBLIC_ keys are exposed to the browser. If this API key is sensitive, proxy requests through a backend; if it’s intended to be public, document rate limits and usage.
NEXT_PUBLIC_ACTA_API_KEY=
🤖 Prompt for AI Agents
In frontend/.env.example around lines 9 to 12, the example file is missing
NEXT_PUBLIC_ACTA_API_KEY; add a line defining
NEXT_PUBLIC_ACTA_API_KEY=your_api_key_here to document the key usage, and add a
brief comment above it warning that NEXT_PUBLIC_* variables are exposed to the
browser and should only contain public-safe values or limited-scope API keys;
ensure the example uses a placeholder value and the warning text is concise.

Comment on lines +141 to +159
const response = await client.createCredential(requestBody);

if (!response.success) {
throw new Error(response.error || "Failed to create credential");
}
console.log("response", response);
// The API response has nested data structure: response.data.data
const responseData = response.data as unknown as {
data: CredentialData;
};
const credentialResponse = responseData.data;

const result: CredentialCreateResult = {
contractId: credentialResponse.contractId,
hash: credentialResponse.hash,
transactionHash: credentialResponse.transactionHash,
createdAt: credentialResponse.createdAt,
ledgerSequence: credentialResponse.ledgerSequence,
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix response shape handling (no nested data) and remove debug log.

ActaClient.createCredential returns ActaResponse<CredentialData>, so response.data is already the credential. Accessing response.data.data will yield undefined. Also, drop the stray console.log.

Apply:

-        console.log("response", response);
-        // The API response has nested data structure: response.data.data
-        const responseData = response.data as unknown as {
-          data: CredentialData;
-        };
-        const credentialResponse = responseData.data;
+        // ActaClient.createCredential returns ActaResponse<CredentialData>
+        const credentialResponse = response.data as CredentialData;
+        if (!credentialResponse || !credentialResponse.contractId) {
+          throw new Error("Malformed ACTA response: missing contractId");
+        }
📝 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
const response = await client.createCredential(requestBody);
if (!response.success) {
throw new Error(response.error || "Failed to create credential");
}
console.log("response", response);
// The API response has nested data structure: response.data.data
const responseData = response.data as unknown as {
data: CredentialData;
};
const credentialResponse = responseData.data;
const result: CredentialCreateResult = {
contractId: credentialResponse.contractId,
hash: credentialResponse.hash,
transactionHash: credentialResponse.transactionHash,
createdAt: credentialResponse.createdAt,
ledgerSequence: credentialResponse.ledgerSequence,
};
const response = await client.createCredential(requestBody);
if (!response.success) {
throw new Error(response.error || "Failed to create credential");
}
// ActaClient.createCredential returns ActaResponse<CredentialData>
const credentialResponse = response.data as CredentialData;
if (!credentialResponse || !credentialResponse.contractId) {
throw new Error("Malformed ACTA response: missing contractId");
}
const result: CredentialCreateResult = {
contractId: credentialResponse.contractId,
hash: credentialResponse.hash,
transactionHash: credentialResponse.transactionHash,
createdAt: credentialResponse.createdAt,
ledgerSequence: credentialResponse.ledgerSequence,
};
🤖 Prompt for AI Agents
frontend/src/components/modules/credentials/hooks/useCredentialCreate.ts around
lines 141 to 159: the code incorrectly treats response.data as nested
(response.data.data) and leaves a stray console.log; change handling so that
response.data is used directly as the CredentialData (e.g., const
credentialResponse = response.data as CredentialData), remove the
console.log("response", response), and then build the CredentialCreateResult
from credentialResponse fields as before.

Comment on lines +68 to +87
async function fetchCredentialStatus(
client: ActaClient,
contractId: string,
): Promise<"Active" | "Revoked" | "Suspended"> {
try {
const response = await client.getByContractId(contractId);

if (!response.success) {
console.warn(`Failed to fetch status for ${contractId}:`, response.error);
return "Active"; // Default to Active if we can't fetch
}

// Assuming the API returns a status field
const credentialData = response.data as {
status?: string;
credential?: { status?: string };
};
const status =
credentialData?.status || credentialData?.credential?.status || "Active";

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Align status parsing with the client’s typed response.

ActaClient.getByContractId returns ActaResponse<CredentialData>; prefer using that type instead of ad-hoc shapes. This reduces brittleness if the API doesn’t return credential.status.

-import { ActaClient } from "@/lib/acta/client";
+import { ActaClient } from "@/lib/acta/client";
+// Optionally import the type if exported:
+// import type { CredentialData } from "@/lib/acta/client";
...
-    // Assuming the API returns a status field
-    const credentialData = response.data as {
-      status?: string;
-      credential?: { status?: string };
-    };
-    const status =
-      credentialData?.status || credentialData?.credential?.status || "Active";
+    // Prefer a single source of truth; fall back conservatively if absent
+    const raw = response.data as any; // or as CredentialData if available
+    const status: string =
+      raw?.status ?? raw?.credential?.status ?? "Active";

Run to confirm the actual shape of CredentialData:


🏁 Script executed:

#!/bin/bash
# Find the CredentialData definition and its fields
rg -nP -C2 '(^\s*(export\s+)?(interface|type)\s+CredentialData\b)|\bCredentialData\b'

Length of output: 3370


🏁 Script executed:

#!/bin/bash
# Print full CredentialData interface with context
rg -nP -C10 'interface\s+CredentialData\b' frontend/src/lib/acta/client.ts

Length of output: 429


🏁 Script executed:

#!/bin/bash
# Show ActaResponse interface
rg -nP -C10 'interface\s+ActaResponse\b' frontend/src/lib/acta/client.ts

# Show getByContractId method signature
rg -nP -C5 'getByContractId' -n frontend/src/lib/acta/client.ts

Length of output: 831


Use a dedicated status response type instead of ad-hoc casts
The CredentialData interface only includes { contractId, hash, transactionHash, createdAt, ledgerSequence } and doesn’t define any status field. Extract the actual API shape for credential status (top-level status or nested credential.status), update or extend CredentialData (or create a new CredentialStatusResponse) in client.ts, adjust getByContractId’s return type accordingly, then import and use that type here for safe, type-checked status parsing.

🤖 Prompt for AI Agents
In frontend/src/components/modules/credentials/hooks/useCredentials.ts around
lines 68 to 87, the code uses an ad-hoc type cast to read status from the API
response; instead declare a proper response type in client.ts (e.g.,
CredentialStatusResponse with fields for top-level status and nested
credential.status), update getByContractId to return that typed response (or
extend existing CredentialData), then import and use the new type here to access
response.data.status and response.data.credential.status without casts and with
TypeScript-checked fallbacks to "Active".

Comment on lines +141 to +142
Created {new Date(createdAt).toLocaleDateString()}
</p>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Guard against invalid createdAt date.

If createdAt is malformed, this renders "Invalid Date". Add a safe fallback.

Apply this diff:

-              <p className="text-sm text-gray-400">
-                Created {new Date(createdAt).toLocaleDateString()}
-              </p>
+              <p className="text-sm text-gray-400">
+                Created {Number.isNaN(new Date(createdAt).getTime()) ? "Unknown" : new Date(createdAt).toLocaleDateString()}
+              </p>
📝 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
Created {new Date(createdAt).toLocaleDateString()}
</p>
<p className="text-sm text-gray-400">
Created {Number.isNaN(new Date(createdAt).getTime()) ? "Unknown" : new Date(createdAt).toLocaleDateString()}
</p>
🤖 Prompt for AI Agents
In frontend/src/components/modules/credentials/ui/components/CredentialCard.tsx
around lines 141-142, the code renders Created {new
Date(createdAt).toLocaleDateString()} which can produce "Invalid Date" for
malformed createdAt values; guard by parsing createdAt into a Date, check
validity (e.g., const d = new Date(createdAt); if Number.isNaN(d.getTime()) )
and render a safe fallback like "Unknown date" or an empty string instead of
toLocaleDateString(), ensuring you only call toLocaleDateString() for valid Date
objects.

Comment on lines +52 to +56
// Initialize Acta client (in a real app, this would come from context/config)
const actaClient = new ActaClient({
baseUrl: process.env.NEXT_PUBLIC_ACTA_API_URL || "http://localhost:3001",
apiKey: process.env.NEXT_PUBLIC_ACTA_API_KEY,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

ActaClient recreated on every render → can trigger repeated refreshes in CredentialSelector.

Passing a fresh client each render causes the child hook to recreate callbacks/effects, potentially reloading credentials frequently.

Apply this diff to memoize the client:

-import { useEffect, useState } from "react";
+import { useEffect, useMemo, useState } from "react";
-  const actaClient = new ActaClient({
-    baseUrl: process.env.NEXT_PUBLIC_ACTA_API_URL || "http://localhost:3001",
-    apiKey: process.env.NEXT_PUBLIC_ACTA_API_KEY,
-  });
+  const actaClient = useMemo(
+    () =>
+      new ActaClient({
+        baseUrl: process.env.NEXT_PUBLIC_ACTA_API_URL || "http://localhost:3001",
+        apiKey: process.env.NEXT_PUBLIC_ACTA_API_KEY,
+      }),
+    [],
+  );
📝 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
// Initialize Acta client (in a real app, this would come from context/config)
const actaClient = new ActaClient({
baseUrl: process.env.NEXT_PUBLIC_ACTA_API_URL || "http://localhost:3001",
apiKey: process.env.NEXT_PUBLIC_ACTA_API_KEY,
});
// <— at the top of the file
import { useEffect, useMemo, useState } from "react";
// … later in your component …
// Initialize Acta client (in a real app, this would come from context/config)
- const actaClient = new ActaClient({
- baseUrl: process.env.NEXT_PUBLIC_ACTA_API_URL || "http://localhost:3001",
- apiKey: process.env.NEXT_PUBLIC_ACTA_API_KEY,
const actaClient = useMemo(
() =>
new ActaClient({
baseUrl: process.env.NEXT_PUBLIC_ACTA_API_URL || "http://localhost:3001",
apiKey: process.env.NEXT_PUBLIC_ACTA_API_KEY,
}),
[],
);
🤖 Prompt for AI Agents
In frontend/src/components/modules/marketplace/ui/components/BorrowModal.tsx
around lines 52-56, the ActaClient is instantiated on every render which causes
child components/hooks (like CredentialSelector) to recreate callbacks/effects
and repeatedly refresh credentials; fix this by memoizing the ActaClient
instance (e.g., using React.useMemo with the env vars as dependencies) or by
creating the client outside the component so the same instance is reused across
renders, ensuring you only recreate the client when the baseUrl or apiKey
actually change.

Comment on lines +279 to +283
if (response.ok) {
return {
success: true,
data: responseData as T,
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix the ACTA response wrapping bug

responseData already comes back in the { success, data|error } envelope the rest of the code expects. Re-wrapping it under a second { success: true, data: ... } shell means callers end up with result.data.success instead of the credential payload, so status checks and dashboard flows won’t work. Please return the parsed ACTA envelope directly.

-      if (response.ok) {
-        return {
-          success: true,
-          data: responseData as T,
-        };
+      if (response.ok) {
+        const actaResponse = responseData as ActaResponse<T>;
+
+        if (
+          actaResponse &&
+          typeof actaResponse === "object" &&
+          "success" in actaResponse
+        ) {
+          return actaResponse;
+        }
+
+        return {
+          success: true,
+          data: responseData as T,
+        };
       } else {
🤖 Prompt for AI Agents
In frontend/src/lib/acta/client.ts around lines 279 to 283, the code incorrectly
re-wraps the parsed responseData (which is already in the { success, data|error
} envelope) into a new { success: true, data: responseData } object; remove that
re-wrap and instead return the parsed responseData directly so callers receive
the original ACTA envelope (preserving responseData.success and
responseData.data or error) and adjust the return path to return responseData as
the function result.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
frontend/src/app/dev/modals-with-credentials/page.tsx (3)

23-37: Consider extracting mock data to a separate constants file

The mock pool data is hardcoded within the component. For better maintainability and reusability across test pages, consider moving this to a shared constants or fixtures file.

Consider creating a new file frontend/src/fixtures/pool.fixtures.ts:

export const MOCK_POOL_DATA = {
  name: "USDC Pool",
  totalSupplied: "2,400,000",
  totalBorrowed: "1,800,000",
  utilizationRate: "75%",
  reserves: [
    {
      symbol: "USDC",
      supplied: "2,400,000",
      borrowed: "1,800,000",
      supplyAPY: "12.5",
      borrowAPY: "15.2",
    },
  ],
};

Then import and use it in the component:

+import { MOCK_POOL_DATA } from "@/fixtures/pool.fixtures";

 export default function ModalsWithCredentialsPage() {
   const [isBorrowModalOpen, setIsBorrowModalOpen] = useState(false);
   const [isSupplyModalOpen, setIsSupplyModalOpen] = useState(false);

-  // Mock pool data for demonstration
-  const mockPoolData = {
-    name: "USDC Pool",
-    totalSupplied: "2,400,000",
-    totalBorrowed: "1,800,000",
-    utilizationRate: "75%",
-    reserves: [
-      {
-        symbol: "USDC",
-        supplied: "2,400,000",
-        borrowed: "1,800,000",
-        supplyAPY: "12.5",
-        borrowAPY: "15.2",
-      },
-    ],
-  };
+  const mockPoolData = MOCK_POOL_DATA;

233-239: Use semantic HTML for better quotes

The quoted text in lines 233-234 and 237-238 uses regular double quotes. Consider using HTML entities or semantic quotes for better typography.

Apply this diff for better typography:

-                    • <strong>Borrow Modal:</strong> &quot;You may qualify for
-                    better interest rates&quot;
+                    • <strong>Borrow Modal:</strong> "You may qualify for
+                    better interest rates"
                   </li>
                   <li>
-                    • <strong>Supply Modal:</strong> &quot;You may qualify for
-                    higher APY and reduced fees&quot;
+                    • <strong>Supply Modal:</strong> "You may qualify for
+                    higher APY and reduced fees"

1-269: Consider adding error boundaries for modal components

Since this is a test page demonstrating modal integration, consider wrapping the modal components in error boundaries to catch and display any integration issues gracefully during testing.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1e050d2 and 68ec816.

📒 Files selected for processing (2)
  • frontend/src/app/dev/modals-with-credentials/page.tsx (1 hunks)
  • frontend/src/components/modules/credentials/ui/components/CredentialSelector.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/components/modules/credentials/ui/components/CredentialSelector.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/src/app/dev/modals-with-credentials/page.tsx (2)
frontend/src/components/modules/marketplace/ui/components/BorrowModal.tsx (1)
  • BorrowModal (28-283)
frontend/src/components/modules/marketplace/ui/components/SupplyUSDCModal.tsx (1)
  • SupplyUSDCModal (11-202)

<ul className="space-y-1 text-sm text-gray-400">
<li>
• <strong>Non-intrusive:</strong> Credential selection is
optional and doesn&apos;t block core flows
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Escape the apostrophe correctly in the JSX text

Line 210 uses &apos; to escape the apostrophe, but this should be consistent with other apostrophe usage in the file (e.g., line 243).

Apply this diff to fix the inconsistency:

-                    • <strong>Non-intrusive:</strong> Credential selection is
-                    optional and doesn&apos;t block core flows
+                    • <strong>Non-intrusive:</strong> Credential selection is
+                    optional and doesn't block core flows
📝 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
optional and doesn&apos;t block core flows
<strong>Non-intrusive:</strong> Credential selection is
optional and doesn't block core flows
🤖 Prompt for AI Agents
In frontend/src/app/dev/modals-with-credentials/page.tsx at line 210, the JSX
text uses &apos; to escape the apostrophe which is inconsistent with other lines
(e.g., 243); replace &apos; with the same apostrophe style used elsewhere (use a
plain single quote character in the JSX text or the same {"'"} interpolation
pattern used in the file) so the text reads "optional and doesn't block core
flows" and matches the file's apostrophe convention.

Comment on lines +252 to +256
isOpen={isBorrowModalOpen}
onClose={() => setIsBorrowModalOpen(false)}
poolData={mockPoolData}
poolId="test-pool"
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix the type mismatch for BorrowModal props

The BorrowModal component expects a poolId prop (string or undefined) but is receiving both poolData and poolId. The poolData prop is not defined in the BorrowModalProps interface.

Apply this diff to fix the prop mismatch:

-        <BorrowModal
-          isOpen={isBorrowModalOpen}
-          onClose={() => setIsBorrowModalOpen(false)}
-          poolData={mockPoolData}
-          poolId="test-pool"
-        />
+        <BorrowModal
+          isOpen={isBorrowModalOpen}
+          onClose={() => setIsBorrowModalOpen(false)}
+          poolId="test-pool"
+        />
📝 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
isOpen={isBorrowModalOpen}
onClose={() => setIsBorrowModalOpen(false)}
poolData={mockPoolData}
poolId="test-pool"
/>
<BorrowModal
isOpen={isBorrowModalOpen}
onClose={() => setIsBorrowModalOpen(false)}
poolId="test-pool"
/>
🤖 Prompt for AI Agents
In frontend/src/app/dev/modals-with-credentials/page.tsx around lines 252 to
256, the BorrowModal is being passed a poolData prop that is not declared on
BorrowModalProps; either remove the extraneous poolData prop from the JSX so
only poolId (string | undefined) is passed, or update the BorrowModalProps
interface to include an optional poolData property with the correct type
(matching mockPoolData) and adjust the BorrowModal component to accept/use it
consistently.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ACTA API Integration for Pool Participation Credentials

1 participant