Skip to content

feat(onboarding): enhance error reporting in KycModal, Navbar, and wa… - #507

Open
sundayonah wants to merge 10 commits into
mainfrom
fix/onboarding-glitchtip-alerting
Open

feat(onboarding): enhance error reporting in KycModal, Navbar, and wa…#507
sundayonah wants to merge 10 commits into
mainfrom
fix/onboarding-glitchtip-alerting

Conversation

@sundayonah

@sundayonah sundayonah commented May 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Report handled onboarding failures to GlitchTip via reportClientError in Privy auth (Navbar), injected wallet init (InjectedWalletContext), Starknet wallet create/ensure (StarknetContext), and legacy KYC (KycModal).
  • Promote feature, flow, step, and statusCode from reportClientError context to Sentry tags for filtering and alert rules in GlitchTip.
  • Preserve existing user-facing behavior (toasts, console logs); no API or contract changes.

Description

Production onboarding failures (login, wallet connect/create, KYC sign/initiate/status) were mostly invisible in GlitchTip because many paths only used console.error / toasts and never called reportClientError. The global ErrorBoundary and a few swap paths were already covered; this PR closes the gap for first-time user flows.

Changes

Area File When we report
Auth Navbar.tsx Privy login() click failure; errors in useLogin onComplete (login vs signup)
Injected wallet InjectedWalletContext.tsx Missing address after connect; init errors except user rejection (code === 4001)
Starknet wallet StarknetContext.tsx Create-wallet API failures (incl. 5xx), public-key derivation failure, ensure-wallet errors
KYC KycModal.tsx Injected sign failure; initiateKYC failure (with HTTP status when present); status refresh failure
Client SDK sentry.client.ts Map feature, flow, step, statusCode to Sentry tags; full context still in extra

Tag convention (for GlitchTip filters / alerts)

  • feature: onboarding
  • flow: auth | wallet | kyc
  • step: e.g. privy-login-click, login-onComplete, signup-onComplete, injected-wallet-init, starknet-create-wallet, kyc-initiate, etc.

Breaking changes: None.

Alternatives considered: Relying only on toastMappedError / ErrorBoundary — rejected because onboarding errors are often caught and not rethrown. Server-side aggregator Sentry — separate from Noblocks client visibility.

References

Testing

Environment: Noblocks local dev (localhost:3000), browser with DevTools Network + Console.

Prerequisites

  • NEXT_PUBLIC_SENTRY_DSN set to the Noblocks GlitchTip project DSN
  • For local verification: NEXT_PUBLIC_SENTRY_ENABLE_IN_DEV=true in .env.local, then restart next dev

Manual checks

  1. Auth — Trigger a failed or interrupted Privy login (or force onComplete error if possible). Confirm a GlitchTip event with feature=onboarding, flow=auth, and the expected step.
  2. Injected wallet — Open with ?injected=true (or your injected flow); reject connection → no event (4001). Simulate a real connect failure → event with flow=wallet, step=injected-wallet-init.
  3. Starknet — Use Starknet network; if create/ensure fails (e.g. misconfigured API), confirm events with flow=wallet and starknet-* steps.
  4. KYC — Run legacy KYC modal; fail sign or initiate (e.g. aggregator/network error). Confirm flow=kyc and statusCode tag when HTTP status is available.
  5. Noise — Confirm user-dismissed wallet rejection does not create issues.

Automated tests: Not added; instrumentation only. Existing tests should pass unchanged.

Checklist

  • I have added documentation and tests for new/changed functionality in this PR (observability only; no user-facing docs required)
  • All active GitHub checks for tests, formatting, and security are passing
  • The correct base branch is being used, if not main

By submitting a PR, I agree to Paycrest's Contributor Code of Conduct and Contribution Guide.

Summary by CodeRabbit

  • Bug Fixes

    • Improved sign-in failure feedback with clearer “Sign in failed. Please try again.” toast messaging.
    • Strengthened onboarding/KYC failure handling across SmileID loading/submission, KYC status refresh, and Tier 3 submission while preserving existing UI fallbacks.
    • Enhanced error handling for injected wallet and Starknet wallet creation/initialization to keep flows more resilient.
  • Observability / Monitoring

    • Upgraded client error reporting with structured feature/flow/step tags and optional status codes.
    • Added deduplication for repeated onboarding wallet error reports to reduce duplicate events.

…llet contexts

- Integrated `reportClientError` for improved error handling in KycModal during KYC signing and initiation processes.
- Added error reporting in Navbar for login and signup completion, capturing relevant context.
- Enhanced error handling in InjectedWalletContext and StarknetContext to report issues during wallet connection and creation.
- Updated `reportClientError` function to include additional tags for better tracking of errors related to onboarding flows.
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

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

Walkthrough

This PR adds structured client-side error reporting to onboarding, KYC, and wallet initialization flows. It also updates Sentry client tagging so reported errors carry selected context fields as tags.

Changes

Onboarding Flow Error Tracking

Layer / File(s) Summary
Sentry tag infrastructure update
app/lib/sentry.client.ts
reportClientError now builds Sentry tags from feature, flow, step, and statusCode, and passes them with extra data to Sentry.captureException.
Navbar sign-in error handling
app/components/Navbar.tsx
Adds toast feedback and wraps sign-in and Privy completion paths in try/catch, reporting failures with onboarding/auth step metadata before showing the existing sign-in error toast.
KycModal KYC error reporting
app/components/KycModal.tsx
Adds client error reporting for SmileID load and submit failures, Tier 3 submission failures, and KYC status refresh failures, while preserving the existing toast and modal-state handling.
Injected wallet initialization reporting
app/context/InjectedWalletContext.tsx
Reports injected-wallet initialization failures when no address is returned and for non-rejection errors, including networkError and extracted errorCode metadata.
Starknet wallet creation reporting
app/context/StarknetContext.tsx
Reports Starknet wallet creation and recovery failures across the create-wallet API response, public-key derivation, outer creation catch, and wallet-existence check paths.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • paycrest/noblocks#426: Shares the reportClientError plumbing in app/lib/sentry.client.ts and the same structured error-reporting pattern.
  • paycrest/noblocks#428: Also changes app/context/InjectedWalletContext.tsx around injected wallet initialization and failure handling.
  • paycrest/noblocks#516: Touches the same KYC failure paths in app/components/KycModal.tsx, especially SmileID and Tier 3 handling.

Suggested reviewers: onahprosper, chibie, 5ran6

Poem

🐰 I hopped through login and KYC halls,
and every failure now leaves tiny calls.
With tags and trails, the errors gleam,
no more silent hops in the onboarding stream!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR covers client-side onboarding error capture and tagging, but it does not show the required Slack #uptime-alerts alerting from #506. Add the GlitchTip/Slack alert rule configuration or related code path, and document how new onboarding errors trigger #uptime-alerts.
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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
Title check ✅ Passed The title is clearly related to the main change: onboarding error reporting across KycModal, Navbar, and wallet flows.
Description check ✅ Passed The PR description follows the template with Summary, Description, References, Testing, and Checklist sections, and includes the needed context.
Out of Scope Changes check ✅ Passed All changes stay within onboarding error reporting and Sentry tag plumbing, with no unrelated scope introduced.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@app/context/StarknetContext.tsx`:
- Around line 179-187: The wallet-creation failure is being reported multiple
times from the same flow; to fix, add a simple dedupe guard (e.g., a module- or
context-scoped Set or boolean) keyed to this flow so reportClientError is only
called once per failed wallet-creation attempt. Update the call sites in
StarknetContext where reportClientError is invoked (the branches inside
createWallet, ensureWalletExists, and the !response.ok handler) to check the
dedupe key (for example "starknet-create-wallet") before reporting and mark the
key as reported after the first call; keep throwing the Error as before so
control flow is unchanged. Ensure the dedupe key is unique to this flow so other
errors still get reported.
- Around line 216-220: The catch only logs thrown exceptions but ignores non-OK
HTTP responses from the /api/starknet/get-public-key call; update the request
handling around the fetch that leads to reportClientError so that after
receiving the Response you check response.ok and, if false, parse the response
body (safely handle JSON parse errors), then call reportClientError (the same
reporting path that uses pkError) with explicit context including status,
statusText and any parsed error payload and the feature/flow/step metadata;
ensure successful paths remain unchanged and only non-OK responses invoke the
new explicit reporting.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 76063cf5-5579-43ef-96d1-7aff0e709bd6

📥 Commits

Reviewing files that changed from the base of the PR and between f81c0bf and 30d5193.

📒 Files selected for processing (5)
  • app/components/KycModal.tsx
  • app/components/Navbar.tsx
  • app/context/InjectedWalletContext.tsx
  • app/context/StarknetContext.tsx
  • app/lib/sentry.client.ts

Comment thread app/context/StarknetContext.tsx Outdated
Comment thread app/context/StarknetContext.tsx Outdated
@Dprof-in-tech

Copy link
Copy Markdown
Collaborator

@sundayonah please address coderabbit

@sundayonah
sundayonah requested a review from Dprof-in-tech as a code owner July 3, 2026 09:25

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
app/components/KycModal.tsx (1)

1411-1495: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Preserve the HTTP status on fetchStatus failures
reportClientError only tags a numeric statusCode, but the non-404 fetch failure throws a plain Error, so this path always sends undefined and loses the response code for GlitchTip filtering. Attach res.status to the thrown error or pass it through to reportClientError directly.

🤖 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 `@app/components/KycModal.tsx` around lines 1411 - 1495, The fetchStatus error
path is dropping the HTTP status code because the non-404 branch throws a plain
Error, so reportClientError receives undefined for statusCode. Update
fetchStatus in KycModal to preserve the response status when res.ok is false,
either by attaching res.status to the thrown error or by passing the status
directly into reportClientError. Use the fetchStatus function and the
reportClientError call in KycModal as the main places to fix.
🧹 Nitpick comments (1)
app/components/KycModal.tsx (1)

309-329: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate statusCode-extraction logic — extract a shared helper.

The same axios-style status extraction is repeated verbatim in the handleSmilePublish catch (Lines 314-318) and the fetchStatus catch (Lines 1480-1484). Consider a small local helper to keep both call sites consistent and easier to maintain.

♻️ Suggested helper
+const getAxiosStatusCode = (error: unknown): number | undefined => {
+  const status = (error as { response?: { status?: unknown } })?.response
+    ?.status;
+  return typeof status === "number" ? status : undefined;
+};
+
 const handleSmilePublish = async (event: Event) => {

Then in each catch block:

-        statusCode:
-          typeof (error as { response?: { status?: unknown } })?.response
-            ?.status === "number"
-            ? (error as { response?: { status?: number } }).response?.status
-            : undefined,
+        statusCode: getAxiosStatusCode(error),

Also applies to: 1382-1386, 1476-1487

🤖 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 `@app/components/KycModal.tsx` around lines 309 - 329, The axios response
status extraction is duplicated across the KycModal catch blocks in
handleSmilePublish and fetchStatus, so factor it into a shared local helper and
use that helper in both places. Add a small reusable function near the related
handlers that safely reads response.status from an unknown error and returns
undefined otherwise, then update reportClientError call sites to use it so both
code paths stay consistent.
🤖 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 `@app/components/KycModal.tsx`:
- Around line 1320-1403: The Tier 3 submit handler in KycModal’s onClick is
missing client error reporting in the non-OK response path. Update the !res.ok
branch in the `/api/kyc/tier3-verify` fetch flow to call reportClientError with
the response failure details before toasting and setting failure state. Keep the
existing outer catch reporting, and make sure this branch covers 4xx/5xx server
responses just like the exception path.

---

Outside diff comments:
In `@app/components/KycModal.tsx`:
- Around line 1411-1495: The fetchStatus error path is dropping the HTTP status
code because the non-404 branch throws a plain Error, so reportClientError
receives undefined for statusCode. Update fetchStatus in KycModal to preserve
the response status when res.ok is false, either by attaching res.status to the
thrown error or by passing the status directly into reportClientError. Use the
fetchStatus function and the reportClientError call in KycModal as the main
places to fix.

---

Nitpick comments:
In `@app/components/KycModal.tsx`:
- Around line 309-329: The axios response status extraction is duplicated across
the KycModal catch blocks in handleSmilePublish and fetchStatus, so factor it
into a shared local helper and use that helper in both places. Add a small
reusable function near the related handlers that safely reads response.status
from an unknown error and returns undefined otherwise, then update
reportClientError call sites to use it so both code paths stay consistent.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8fb0254e-5b3a-42c3-a95e-5f940208bc69

📥 Commits

Reviewing files that changed from the base of the PR and between 30d5193 and 7a95971.

📒 Files selected for processing (3)
  • app/components/KycModal.tsx
  • app/components/Navbar.tsx
  • app/context/StarknetContext.tsx
💤 Files with no reviewable changes (1)
  • app/context/StarknetContext.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/components/Navbar.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
app/components/KycModal.tsx (1)

1411-1495: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Preserve the HTTP status on fetchStatus failures
reportClientError only tags a numeric statusCode, but the non-404 fetch failure throws a plain Error, so this path always sends undefined and loses the response code for GlitchTip filtering. Attach res.status to the thrown error or pass it through to reportClientError directly.

🤖 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 `@app/components/KycModal.tsx` around lines 1411 - 1495, The fetchStatus error
path is dropping the HTTP status code because the non-404 branch throws a plain
Error, so reportClientError receives undefined for statusCode. Update
fetchStatus in KycModal to preserve the response status when res.ok is false,
either by attaching res.status to the thrown error or by passing the status
directly into reportClientError. Use the fetchStatus function and the
reportClientError call in KycModal as the main places to fix.
🧹 Nitpick comments (1)
app/components/KycModal.tsx (1)

309-329: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate statusCode-extraction logic — extract a shared helper.

The same axios-style status extraction is repeated verbatim in the handleSmilePublish catch (Lines 314-318) and the fetchStatus catch (Lines 1480-1484). Consider a small local helper to keep both call sites consistent and easier to maintain.

♻️ Suggested helper
+const getAxiosStatusCode = (error: unknown): number | undefined => {
+  const status = (error as { response?: { status?: unknown } })?.response
+    ?.status;
+  return typeof status === "number" ? status : undefined;
+};
+
 const handleSmilePublish = async (event: Event) => {

Then in each catch block:

-        statusCode:
-          typeof (error as { response?: { status?: unknown } })?.response
-            ?.status === "number"
-            ? (error as { response?: { status?: number } }).response?.status
-            : undefined,
+        statusCode: getAxiosStatusCode(error),

Also applies to: 1382-1386, 1476-1487

🤖 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 `@app/components/KycModal.tsx` around lines 309 - 329, The axios response
status extraction is duplicated across the KycModal catch blocks in
handleSmilePublish and fetchStatus, so factor it into a shared local helper and
use that helper in both places. Add a small reusable function near the related
handlers that safely reads response.status from an unknown error and returns
undefined otherwise, then update reportClientError call sites to use it so both
code paths stay consistent.
🤖 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 `@app/components/KycModal.tsx`:
- Around line 1320-1403: The Tier 3 submit handler in KycModal’s onClick is
missing client error reporting in the non-OK response path. Update the !res.ok
branch in the `/api/kyc/tier3-verify` fetch flow to call reportClientError with
the response failure details before toasting and setting failure state. Keep the
existing outer catch reporting, and make sure this branch covers 4xx/5xx server
responses just like the exception path.

---

Outside diff comments:
In `@app/components/KycModal.tsx`:
- Around line 1411-1495: The fetchStatus error path is dropping the HTTP status
code because the non-404 branch throws a plain Error, so reportClientError
receives undefined for statusCode. Update fetchStatus in KycModal to preserve
the response status when res.ok is false, either by attaching res.status to the
thrown error or by passing the status directly into reportClientError. Use the
fetchStatus function and the reportClientError call in KycModal as the main
places to fix.

---

Nitpick comments:
In `@app/components/KycModal.tsx`:
- Around line 309-329: The axios response status extraction is duplicated across
the KycModal catch blocks in handleSmilePublish and fetchStatus, so factor it
into a shared local helper and use that helper in both places. Add a small
reusable function near the related handlers that safely reads response.status
from an unknown error and returns undefined otherwise, then update
reportClientError call sites to use it so both code paths stay consistent.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8fb0254e-5b3a-42c3-a95e-5f940208bc69

📥 Commits

Reviewing files that changed from the base of the PR and between 30d5193 and 7a95971.

📒 Files selected for processing (3)
  • app/components/KycModal.tsx
  • app/components/Navbar.tsx
  • app/context/StarknetContext.tsx
💤 Files with no reviewable changes (1)
  • app/context/StarknetContext.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/components/Navbar.tsx
🛑 Comments failed to post (1)
app/components/KycModal.tsx (1)

1320-1403: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Missing reportClientError in the Tier 3 !res.ok path (5xx/4xx from /api/kyc/tier3-verify).

The outer catch (Lines 1381-1396) now reports client errors, but the !res.ok branch (Lines 1346-1368) — which handles actual HTTP failure responses from the server, including 5xx — never calls reportClientError. It only logs to console and shows a toast. Per the linked issue's objective to report "network errors, 5xx responses, and unhandled exceptions," this is a gap: server-side failures on Tier 3 submission won't reach GlitchTip.

🐛 Suggested fix
                 if (!res.ok) {
                   let errorDetail = "Unable to submit Tier 3 verification.";
                   try {
                     const errJson = await res.json();
                     errorDetail =
                       errJson?.error || errJson?.message || errJson?.details || errorDetail;
                   } catch {
                     try {
                       const errText = await res.text();
                       if (errText) errorDetail = errText;
                     } catch {
                       // keep default
                     }
                   }
 
+                  reportClientError(new Error(errorDetail), {
+                    feature: "onboarding",
+                    flow: "kyc",
+                    step: "kyc-tier3-submit",
+                    statusCode: res.status,
+                  });
                   console.error("Tier 3 verification request failed:", errorDetail);
📝 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.

            onClick={async () => {
              if (!tier3UploadedFile || tier3Submitting) return;
              setTier3Submitting(true);
              try {
                const accessToken = await getAccessToken();
                if (!accessToken) {
                  setTier3Submitting(false);
                  toast.error("Session expired. Please sign in again.");
                  return;
                }

                const formData = new FormData();
                formData.append("file", tier3UploadedFile);
                formData.append("countryCode", tier3CountryCode);
                formData.append("documentType", tier3DocumentType);
                formData.append("houseNumber", tier3HouseNumber);
                formData.append("streetAddress", tier3StreetAddress);
                formData.append("county", tier3County);
                formData.append("postalCode", tier3PostalCode);

                const res = await fetch("/api/kyc/tier3-verify", {
                  method: "POST",
                  headers: { Authorization: `Bearer ${accessToken}` },
                  body: formData,
                });

                if (!res.ok) {
                  let errorDetail = "Unable to submit Tier 3 verification.";
                  try {
                    const errJson = await res.json();
                    errorDetail =
                      errJson?.error || errJson?.message || errJson?.details || errorDetail;
                  } catch {
                    try {
                      const errText = await res.text();
                      if (errText) errorDetail = errText;
                    } catch {
                      // keep default
                    }
                  }

                  reportClientError(new Error(errorDetail), {
                    feature: "onboarding",
                    flow: "kyc",
                    step: "kyc-tier3-submit",
                    statusCode: res.status,
                  });
                  console.error("Tier 3 verification request failed:", errorDetail);
                  toast.error("Tier 3 verification failed", { description: errorDetail });
                  setFailedReason(errorDetail);
                  setFailedRetryStep(STEPS.TIER3_UPLOAD);
                  setStep(STEPS.STATUS.FAILED);
                  setTier3Submitting(false);
                  return;
                }

                const data = await res.json();
                if (data?.success) {
                  setStep(STEPS.STATUS.SUCCESS);
                } else {
                  const errorDetail =
                    data?.error || data?.message || "Tier 3 verification failed.";
                  toast.error("Tier 3 verification failed", { description: errorDetail });
                  setFailedReason(errorDetail);
                  setFailedRetryStep(STEPS.TIER3_UPLOAD);
                  setStep(STEPS.STATUS.FAILED);
                }
              } catch (e) {
                reportClientError(e, {
                  feature: "onboarding",
                  flow: "kyc",
                  step: "kyc-tier3-submit",
                });
                console.error("Tier 3 verification error:", e);
                const msg = e instanceof Error ? e.message : "Tier 3 verification failed. Please try again.";
                toast.error(msg);
                setFailedReason(msg);
                setFailedRetryStep(STEPS.TIER3_UPLOAD);
                setStep(STEPS.STATUS.FAILED);
              } finally {
                setTier3Submitting(false);
              }
            }}
          >
            {tier3Submitting ? "Verifying…" : "Complete upgrade"}
          </button>
        </div>
      </motion.div>
    );
  };
🤖 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 `@app/components/KycModal.tsx` around lines 1320 - 1403, The Tier 3 submit
handler in KycModal’s onClick is missing client error reporting in the non-OK
response path. Update the !res.ok branch in the `/api/kyc/tier3-verify` fetch
flow to call reportClientError with the response failure details before toasting
and setting failure state. Keep the existing outer catch reporting, and make
sure this branch covers 4xx/5xx server responses just like the exception path.

- Added a mechanism to report onboarding errors only once per instance to reduce alert volume.
- Integrated the new error reporting function in the StarknetProvider for wallet creation and public key derivation.
- Updated error handling to ensure consistent messaging and logging for onboarding-related errors.
@cursor

cursor Bot commented Jul 4, 2026

Copy link
Copy Markdown

Bugbot is not enabled for this team, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

…er signup

- Refactored the event tracking logic for user signup completion to enhance readability.
- Updated variable names for clarity, specifically changing `walletAddress` to `signupWalletAddress`.
- Improved error handling for the token fetch process during email signup, ensuring better logging of failures.
- Maintained existing functionality while enhancing code clarity and maintainability.
@cursor

cursor Bot commented Jul 4, 2026

Copy link
Copy Markdown

Bugbot is not enabled for this team, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

chibie and others added 5 commits July 22, 2026 02:32
…609)

* feat: embeddable widget mode (/widget) for whitelisted partner sites

Adds an iframe-embeddable compact swap widget, gated to whitelisted
origins and matching the Figma widget design:

- /widget route rendering the existing MainPageContent in a compact
  WidgetShell card (mobile UI, minimized wallet pill opening the wallet
  drawer, pinned "Secured by Noblocks" footer, sticky primary CTAs,
  close X outside the card, transparent backdrop so the host page shows
  through - includes a color-scheme override so Chrome doesn't paint an
  opaque canvas behind the cross-origin iframe).
- Security: X-Frame-Options: DENY scoped away from /widget only;
  middleware emits a per-request CSP frame-ancestors allowlist merged
  from EMBED_ALLOWED_ORIGINS env and the new embed_allowed_origins
  Supabase table (secret-gated /api/internal/embed-origins CRUD,
  contact_email required per partner). Feature-flagged via
  NEXT_PUBLIC_EMBED_ENABLED (404 when off), fails closed to
  frame-ancestors 'none'. /widget noindexed via robots + metadata.
- postMessage host API (EmbedContext): noblocks:ready / resize / close
  / tx_status, sent only to the verified embedder origin.
- Host-wallet EIP-1193 bridge: ?injected=bridge proxies wallet requests
  to the host page's connected provider (WalletConnect/AppKit/wagmi)
  via public/embed.js NoblocksEmbed.bindWallet(); extends the existing
  ?injected=true extension-wallet path.
- Analytics: no client trackers in the widget; cookieless server-side
  "Embed Widget Loaded" attribution with referrer origin via the
  middleware-analytics pipeline. ?theme=dark|light forces the theme.
- Widget-scoped UI trims: no navbar/footer/hero/banners/cookie consent,
  no 30s strip, success screen with stacked full-width buttons and no
  share section; Brevo launcher repositioned inside the card.
- General fix: wallet balances comma-formatted via formatCurrency
  (drawer total, navbar chip, per-token USD equivalents).
- Partner docs in docs/embed-widget.md (whitelisting via
  info@noblocks.xyz).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(embed): address CodeRabbit review — HTTPS origins, SSRF-safe fetch, fail-closed cache, button ordering

- Require HTTPS for partner embed origins (http:// only for localhost/127.0.0.1
  in dev) in both middleware and the internal allowlist API.
- Fetch the DB-backed allowlist only via a trusted, configured
  INTERNAL_API_BASE_URL instead of the request-derived (Host-controlled) origin,
  so a poisoned host can't leak INTERNAL_API_KEY or poison the shared cache.
- Fail closed: drop DB-backed origins on a failed/non-OK allowlist refresh so a
  revoked partner can't keep framing during an outage (env allowlist stays).
- Normalize + validate the DELETE origin the same way as POST (trailing slash
  no longer silently deletes nothing).
- Success screen (embed): put New payment + Get receipt in one sticky wrapper
  (primary on top) so the stacking is unambiguous; restore the exact non-embed
  layout.
- Docs: validate e.source === iframe.contentWindow in the message-listener
  example; bind the wallet before setting the iframe src.
- Replace the Tailwind-class-coupled wallet-drawer selector with a stable
  .wallet-drawer-panel hook class.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: add iframe embed quickstart to README linking to docs/embed-widget.md

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Drop the outside-the-card X and its 449px gutter unit: the close button now
lives consistently in the card header next to the wallet pill, and the 393px
card is centered with only its drop shadow around it. Simplifies the geometry
(no responsive close-button swap), recenters the Brevo launcher and wallet
drawer offsets, matches the WidgetPreloader skeleton, and updates the
recommended iframe width in the docs/README (420px).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…idget card shadow

MobileDropdown derived the header balance card's address only from
useWallets() (Privy's wallet-connector list), which can lag behind
user.linkedAccounts right after a fresh signup — before any smart wallet
exists, this hid the entire address/balance card even though the
per-network balance list below it (driven by useWalletAddress(), which
already falls back to linkedAccounts) rendered correctly. Most visible on
first-time /widget visitors, who are always fresh EOA users. Reuse
useWalletAddress()'s address instead of re-deriving it locally.

Also drop the WidgetShell card's drop shadow per design feedback — the
card now sits flush with no shadow/ring, just the transparent backdrop.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d text, contained overlays

- WidgetShell now fills the entire iframe viewport (h-dvh, no margins, no
  shadow) — partners size/round/float the widget by styling the iframe
  element. Fixes the status/pending screen shrinking to fit (footer now
  stays pinned to the bottom edge) and naturally contains viewport-anchored
  overlays (bottom sheets, modals, Brevo launcher) within the card, so the
  centered-card width-cap CSS is gone. WidgetPreloader matches.
- Theme: replace forcedTheme with a client-side, once-per-load sync —
  forcedTheme disabled next-themes' setTheme() entirely, dead-ending the
  Settings > Theme switcher in the widget; a dynamic defaultTheme can't work
  because the force-static layout bakes the anti-FOUC script with the
  SSR-time value. The partner's ?theme= param now wins on every fresh load
  (deterministic embeds, immune to stale localStorage) while the in-widget
  switcher keeps working for the rest of the session.
- The widget's `color-scheme: light` transparency override also flipped the
  browser's default text color, rendering inherited-color text (e.g.
  SelectBankModal's institution list) dark-on-dark; set explicit per-theme
  default text colors on the widget body.
- AnimatedModal: max-h was sm:-only, so tall panels (network selector) on
  narrow viewports — real phones and the widget alike — could outgrow the
  screen with no way to scroll back; cap at 90dvh at all breakpoints.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Resolve conflicts in KycModal, InjectedWalletContext, and StarknetContext.

Co-authored-by: Cursor <cursoragent@cursor.com>
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.

GlitchTip Error Tracking for Onboarding Flows

3 participants