Skip to content

posthog migration #7340

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed

posthog migration #7340

wants to merge 1 commit into from

Conversation

jnsdls
Copy link
Member

@jnsdls jnsdls commented Jun 13, 2025


PR-Codex overview

This PR primarily focuses on removing the Posthog integration from various components and files across the application while introducing new tracking methods and dependencies. It also updates the posthog-js and adds posthog-node to the project.

Detailed summary

  • Deleted multiple Posthog related files: Posthog.tsx, PosthogPageView.tsx, PosthogHeadSetup.tsx, etc.
  • Removed trackEvent functions from numerous components.
  • Updated posthog-js version to 1.252.0 and added posthog-node.
  • Introduced new tracking methods in analytics helpers.
  • Updated knip.json files to reflect new dependencies.
  • Added PostHog API key and host configuration in environment variables.
  • Implemented new analytics reporting functions for NFTs and marketplace events.

The following files were skipped due to too many changes: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/permissions/components/index.tsx, apps/dashboard/src/components/pay/PayConfig.tsx, apps/dashboard/src/app/(app)/layout.tsx, apps/dashboard/src/components/configure-networks/ConfigureNetworks.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/components/metadata.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/claim-tab.tsx, apps/dashboard/src/app/nebula-app/layout.tsx, apps/dashboard/src/@/analytics/dashboard.client.tsx, apps/dashboard/src/app/nebula-app/(app)/components/Swap/common.tsx, apps/dashboard/src/app/(app)/(dashboard)/profile/[addressOrEns]/components/PublishedContractTable.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/components/royalties.tsx, apps/dashboard/src/components/smart-wallets/SponsorshipPolicies/index.tsx, apps/dashboard/src/@/analytics/track/token.ts, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/reveal-button.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/lazy-mint-form.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/FaucetButton.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/transfer-tab.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/wallet-credentials/components/create-wallet-credential-button.tsx, apps/dashboard/src/components/pay/RouteDiscovery.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/components/list-access-tokens.client.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/components/primary-sale.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/wallet-credentials/components/edit-wallet-credential-button.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/mint-supply-tab.tsx, apps/dashboard/src/@/analytics/track/onboarding.ts, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/reset-claim-eligibility.tsx, apps/dashboard/src/components/onboarding/ApplyForOpCreditsForm.tsx, apps/dashboard/src/components/settings/AuthorizedWallets/AuthorizedWalletsTable.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/update-metadata-form.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/components/platform-fees.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-form.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/airdrop-tab.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/batch-lazy-mint-button.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/burn-tab.tsx, apps/dashboard/src/components/contract-components/publisher/publisher-header.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/airdrop-form.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/PayModal.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/cancel-tab.tsx, apps/playground-web/src/app/layout.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/overview/components/backend-wallets-table.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/admins/components/admins-table.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/claim-button.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/launch/launch-nft.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/access-tokens/components/access-tokens-table.tsx, apps/dashboard/src/components/contract-components/contract-publish-form/index.tsx, apps/dashboard/src/app/(app)/login/onboarding/LoginOrSignup/LoginOrSignup.tsx, apps/dashboard/src/@/analytics/track/contract.ts, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/mint-button.tsx, apps/dashboard/src/app/(app)/team/components/TeamHeader/team-header-logged-in.client.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/relayers/components/relayers-table.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/claim-button.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/mint-form.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/ProjectGeneralSettingsPage.tsx, apps/dashboard/src/app/(app)/login/onboarding/team-onboarding/team-onboarding.tsx, apps/dashboard/src/app/nebula-app/(app)/components/FloatingChat/FloatingChat.tsx, apps/dashboard/src/app/(app)/login/onboarding/account-onboarding-ui.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/token/launch/launch-token.tsx, apps/dashboard/src/app/nebula-app/(app)/components/CustomChat/CustomChatContent.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/burn-button.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/transfer-button.tsx, apps/dashboard/src/components/buttons/MismatchButton.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/primary-dashboard-button.tsx, apps/dashboard/src/@3rdweb-sdk/react/components/connect-wallet/index.tsx, apps/dashboard/src/app/(app)/login/onboarding/LinkWalletPrompt/LinkWalletPrompt.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-form.tsx, apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx, apps/dashboard/src/app/(app)/login/onboarding/VerifyEmail/VerifyEmail.tsx, apps/dashboard/src/app/(app)/get-started/team/[team_slug]/select-plan/_components/plan-selector.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.tsx, apps/portal/src/app/layout.tsx, apps/dashboard/src/components/cmd-k-search/index.tsx, apps/dashboard/src/@/analytics/track/README.md, apps/dashboard/src/components/embedded-wallets/Configure/index.tsx, apps/dashboard/src/app/(app)/login/onboarding/team-onboarding/InviteTeamMembers.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/token/create-token-page-impl.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/create-nft-page.tsx, pnpm-lock.yaml

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Summary by CodeRabbit

  • New Features

    • Introduced new analytics instrumentation modules for improved integration with PostHog across dashboard, portal, and playground-web apps.
    • Added user and team identification components and a reset utility for analytics state in the dashboard.
  • Bug Fixes

    • Ensured analytics state is reset on logout, account deletion, and wallet disconnection in relevant user flows.
  • Refactor

    • Simplified and consolidated analytics logic, removing legacy PostHog components, hooks, and tracked link wrappers from layouts, providers, and UI components.
    • Updated and streamlined environment variable usage and Next.js rewrites for PostHog configuration.
    • Replaced tracked link components with standard navigation elements.
    • Removed all legacy event tracking calls and hooks, replacing some with specific event reporting functions for key actions.
  • Chores

    • Upgraded PostHog dependencies to the latest versions and updated configuration files to ignore new dependencies in analysis tools.
  • Style

    • Minor formatting and code cleanup following the removal of analytics-related code.

Copy link

vercel bot commented Jun 13, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
docs-v2 ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 15, 2025 1:49am
login ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 15, 2025 1:49am
thirdweb_playground ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 15, 2025 1:49am
thirdweb-www ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 15, 2025 1:49am
1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
wallet-ui ⬜️ Skipped (Inspect) Jun 15, 2025 1:49am

@vercel vercel bot temporarily deployed to Preview – wallet-ui June 13, 2025 23:08 Inactive
Copy link

changeset-bot bot commented Jun 13, 2025

⚠️ No Changeset found

Latest commit: 3ee08e2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

coderabbitai bot commented Jun 13, 2025

Walkthrough

This change removes all React-based PostHog analytics integration from the dashboard, portal, and playground-web apps, including providers, pageview tracking, and identifier components. It introduces new instrumentation-client modules for PostHog initialization, updates environment variables and Next.js rewrites for proxying analytics requests, and refactors analytics identification and reset logic into dedicated client modules.

Changes

Files/Groups Change Summary
.env.example, package.json, knip.json (all apps) Updated environment variables for PostHog, upgraded posthog-js, added posthog-node, adjusted ignored dependencies.
next.config.ts, next.config.mjs (all apps) Added skipTrailingSlashRedirect and rewrites to proxy /_ph analytics traffic to PostHog cloud endpoints.
instrumentation-client.ts (all apps) Introduced PostHog initialization modules for analytics tracking, replacing React-based integration.
src/lib/posthog/*, src/components/wallets/PosthogIdentifier*, src/lib/env.ts Deleted all PostHog React provider, pageview, identifier, and environment utility components.
src/app/layout.tsx (all apps), src/app/nebula-app/layout.tsx, src/app/(app)/layout.tsx Removed PostHog provider/component imports and usage from root layouts.
src/app/bridge/components/client/Providers.client.tsx, src/app/pay/components/client/Providers.client.tsx Removed PostHog provider and pageview tracking from providers.
src/app/(app)/team/[team_slug]/layout.tsx Removed server-side PostHog identifier component usage.
src/components/wallets/PosthogIdentifier.tsx, src/components/wallets/PosthogIdentifierServer.tsx Deleted PostHog wallet/account identifier components.
src/hooks/analytics/useTrack.ts Removed PostHog event capture; now logs tracking events to console only.
src/utils/errorParser.tsx, src/components/AI/chat.tsx, src/components/others/Feedback.tsx Removed PostHog error/event tracking calls.
src/@3rdweb-sdk/react/components/connect-wallet/index.tsx Removed tracked link component; added analytics reset on disconnect.
src/app/(app)/account/components/AccountHeader.tsx, src/app/(app)/account/settings/AccountSettingsPage.tsx, src/app/(app)/login/onboarding/account-onboarding.tsx Added analytics reset on logout/account deletion.
src/app/(app)/team/components/TeamHeader/team-header-logged-in.client.tsx Added analytics reset and conditional team identifier; improved staff mode logic.
src/app/(app)/team/[team_slug]/(team)/layout.tsx Refactored staff mode check to a memoized variable.
src/@/analytics/dashboard.client.tsx New: React components and utilities for PostHog account/team identification and analytics reset.
apps/playground-web/src/lib/posthog/*, apps/portal/src/lib/posthog/* Deleted all PostHog React provider, pageview, and head setup components.
src/@/analytics/track.ts, src/@/analytics/track/__internal.ts, src/@/analytics/track/contract.ts Added type-safe analytics tracking helpers with Zod validation and internal event reporting wrapper.
src/components/contract-components/contract-deploy-form/custom-contract.tsx Replaced custom deployment tracking with simplified contract deployed event reporting; removed useTrack usage.
.eslintrc.js Added import restrictions on posthog-js to enforce usage only within analytics helper files.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant App
    participant instrumentation-client.ts
    participant PostHog Cloud

    User->>App: Loads application
    App->>instrumentation-client.ts: Imports and runs PostHog initialization
    instrumentation-client.ts->>PostHog Cloud: Initializes analytics (if env vars set)
    User->>App: Navigates, logs in/out, interacts
    App->>instrumentation-client.ts: (Events tracked via PostHog JS, not React context)
    instrumentation-client.ts->>PostHog Cloud: Sends analytics events (pageviews, etc.)
    App->>dashboard.client.tsx: Calls AccountIdentifier/TeamIdentifier/reset as needed
    dashboard.client.tsx->>PostHog Cloud: Identifies user/team or resets analytics state
Loading

Possibly related PRs

  • thirdweb-dev/js#7232: Adds PostHog tracking components and providers to dashboard bridge and pay providers; this PR removes those same integrations, making them directly related but inverse.
  • thirdweb-dev/js#7267: Introduces a new prop to the PHProvider component to control session recording and renames provider components; this PR deletes PHProvider and its usages, making them directly related but opposite.
  • thirdweb-dev/js#7299: Introduces staff mode UI banner and refactors team membership checks; relates to the staff mode logic refactoring in this PR.

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • TEAM-0000: Entity not found: Issue - Could not find referenced Issue.
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

Support

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

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

@github-actions github-actions bot added Dashboard Involves changes to the Dashboard. Playground Changes involving the Playground codebase. Portal Involves changes to the Portal (docs) codebase. labels Jun 13, 2025
@jnsdls jnsdls marked this pull request as ready for review June 13, 2025 23:08
@jnsdls jnsdls requested review from a team as code owners June 13, 2025 23:08
Copy link
Member Author

jnsdls commented Jun 13, 2025


How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • merge-queue - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@jnsdls jnsdls added the DO NOT MERGE This pull request is still in progress and is not ready to be merged. label Jun 13, 2025 — with Graphite App
Copy link

socket-security bot commented Jun 13, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatedposthog-js@​1.67.1 ⏵ 1.252.099100100 +496 +1100

View full report

Copy link

codecov bot commented Jun 13, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 55.58%. Comparing base (94e2cbc) to head (3ee08e2).
Report is 5 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #7340   +/-   ##
=======================================
  Coverage   55.58%   55.58%           
=======================================
  Files         909      909           
  Lines       58683    58683           
  Branches     4158     4158           
=======================================
  Hits        32617    32617           
  Misses      25959    25959           
  Partials      107      107           
Flag Coverage Δ
packages 55.58% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

cursor[bot]

This comment was marked as outdated.

Copy link
Contributor

github-actions bot commented Jun 13, 2025

size-limit report 📦

Path Size Loading time (3g) Running time (snapdragon) Total time
thirdweb (esm) 62.44 KB (0%) 1.3 s (0%) 167 ms (+121.74% 🔺) 1.5 s
thirdweb (cjs) 350.62 KB (0%) 7.1 s (0%) 608 ms (+0.86% 🔺) 7.7 s
thirdweb (minimal + tree-shaking) 5.7 KB (0%) 114 ms (0%) 63 ms (+770.55% 🔺) 177 ms
thirdweb/chains (tree-shaking) 531 B (0%) 11 ms (0%) 32 ms (+918.85% 🔺) 43 ms
thirdweb/react (minimal + tree-shaking) 19.59 KB (0%) 392 ms (0%) 64 ms (+346.63% 🔺) 455 ms

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

🔭 Outside diff range comments (1)
apps/portal/src/components/others/Feedback.tsx (1)

31-63: 🛠️ Refactor suggestion

⚠️ Potential issue

Feedback is collected but never stored or transmitted

feedback state is updated, yet the value is discarded when the user presses Submit – no API call, analytics event, or log occurs. This results in dead code and a misleading UI (users expect their input to be sent somewhere).

- <Button
-   onClick={() => {
-     setIsSubmitted(true);
-   }}
- >
+ <Button
+   onClick={async () => {
+     await fetch("/api/feedback", {
+       method: "POST",
+       headers: { "Content-Type": "application/json" },
+       body: JSON.stringify({ source: "portal-docs", feedback }),
+     }).catch(() => {/* silently fail */});
+     setIsSubmitted(true);
+   }}
+ >

If no backend endpoint exists, remove the unused feedback state entirely to avoid confusing future maintainers.

♻️ Duplicate comments (1)
apps/portal/instrumentation-client.ts (1)

1-17: Same SSR & host issues as playground implementation

  1. Importing posthog-js unguarded risks server crashes – apply the guarded dynamic import pattern suggested for the playground file.
  2. ui_host again uses us.posthog.com, whereas rewrites forward to us.i.posthog.com; reconcile the domains.
🧹 Nitpick comments (4)
apps/dashboard/src/app/nebula-app/layout.tsx (1)

38-49: Removal of PHProvider looks good – minor nit

Layout is cleaner without the nested PostHog wrappers and the body class setup remains intact.
Optional: if you still need automatic page-view events, consider adding a small useEffect in NebulaProviders that calls posthog?.capture('$pageview') instead of re-introducing a heavy provider wrapper.

apps/dashboard/.env.example (1)

111-116: Remove legacy NEXT_PUBLIC_POSTHOG_API_KEY and consolidate naming
The .env.example adds NEXT_PUBLIC_POSTHOG_KEY/_HOST but still retains the outdated NEXT_PUBLIC_POSTHOG_API_KEY at line 40. To avoid confusion, drop the old POSTHOG_API_KEY entry and standardize on NEXT_PUBLIC_POSTHOG_KEY (and its host variable).

🧰 Tools
🪛 dotenv-linter (3.3.0)

[warning] 111-111: [QuoteCharacter] The value has quote characters (', ")


[warning] 111-111: [UnorderedKey] The NEXT_PUBLIC_ENGINE_CLOUD_URL key should go before the NEXT_PUBLIC_THIRDWEB_VAULT_URL key


[warning] 114-114: [QuoteCharacter] The value has quote characters (', ")


[warning] 115-115: [UnorderedKey] The NEXT_PUBLIC_POSTHOG_HOST key should go before the NEXT_PUBLIC_POSTHOG_KEY key

apps/portal/next.config.mjs (1)

73-90: Duplicate rewrite & property support

Replicate the comments given for the dashboard config:

• Combine the wildcard and /decide rules – the latter is shadowed.
• Validate skipTrailingSlashRedirect against the project’s Next.js version.

These small clean-ups reduce config noise and potential upgrade friction.

apps/dashboard/src/hooks/analytics/useTrack.ts (1)

3-8: Tighten TrackingParams to avoid accidental key overlaps

Because category, action, and label share the same key space as the index signature, TypeScript will allow callers to re-declare them with the wrong type.

Consider omitting them from the index signature via an intersection:

export type TrackingParams = {
  category: string;
  action: string;
  label?: string;
} & Record<string, unknown>;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 94e2cbc and d24c171.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (36)
  • apps/dashboard/.env.example (1 hunks)
  • apps/dashboard/instrumentation-client.tsx (1 hunks)
  • apps/dashboard/knip.json (1 hunks)
  • apps/dashboard/next.config.ts (2 hunks)
  • apps/dashboard/package.json (1 hunks)
  • apps/dashboard/src/app/(app)/layout.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/layout.tsx (0 hunks)
  • apps/dashboard/src/app/bridge/components/client/Providers.client.tsx (1 hunks)
  • apps/dashboard/src/app/nebula-app/layout.tsx (1 hunks)
  • apps/dashboard/src/app/pay/components/client/Providers.client.tsx (1 hunks)
  • apps/dashboard/src/components/wallets/PosthogIdentifier.tsx (0 hunks)
  • apps/dashboard/src/components/wallets/PosthogIdentifierServer.tsx (0 hunks)
  • apps/dashboard/src/hooks/analytics/useTrack.ts (1 hunks)
  • apps/dashboard/src/lib/posthog/Posthog.tsx (0 hunks)
  • apps/dashboard/src/lib/posthog/PosthogHeadSetup.tsx (0 hunks)
  • apps/dashboard/src/lib/posthog/PosthogPageView.tsx (0 hunks)
  • apps/dashboard/src/utils/errorParser.tsx (0 hunks)
  • apps/playground-web/instrumentation-client.ts (1 hunks)
  • apps/playground-web/knip.json (1 hunks)
  • apps/playground-web/next.config.mjs (1 hunks)
  • apps/playground-web/package.json (1 hunks)
  • apps/playground-web/src/app/layout.tsx (1 hunks)
  • apps/playground-web/src/lib/posthog/Posthog.tsx (0 hunks)
  • apps/playground-web/src/lib/posthog/PosthogHeadSetup.tsx (0 hunks)
  • apps/playground-web/src/lib/posthog/PosthogPageView.tsx (0 hunks)
  • apps/portal/instrumentation-client.ts (1 hunks)
  • apps/portal/knip.json (1 hunks)
  • apps/portal/next.config.mjs (1 hunks)
  • apps/portal/package.json (1 hunks)
  • apps/portal/src/app/layout.tsx (1 hunks)
  • apps/portal/src/components/AI/chat.tsx (1 hunks)
  • apps/portal/src/components/others/Feedback.tsx (1 hunks)
  • apps/portal/src/lib/env.ts (0 hunks)
  • apps/portal/src/lib/posthog/Posthog.tsx (0 hunks)
  • apps/portal/src/lib/posthog/PosthogHeadSetup.tsx (0 hunks)
  • apps/portal/src/lib/posthog/PosthogPageView.tsx (0 hunks)
💤 Files with no reviewable changes (14)
  • apps/dashboard/src/app/(app)/team/[team_slug]/layout.tsx
  • apps/dashboard/src/utils/errorParser.tsx
  • apps/dashboard/src/lib/posthog/PosthogHeadSetup.tsx
  • apps/dashboard/src/components/wallets/PosthogIdentifierServer.tsx
  • apps/portal/src/lib/env.ts
  • apps/playground-web/src/lib/posthog/PosthogHeadSetup.tsx
  • apps/portal/src/lib/posthog/PosthogHeadSetup.tsx
  • apps/portal/src/lib/posthog/Posthog.tsx
  • apps/portal/src/lib/posthog/PosthogPageView.tsx
  • apps/playground-web/src/lib/posthog/PosthogPageView.tsx
  • apps/dashboard/src/lib/posthog/Posthog.tsx
  • apps/dashboard/src/components/wallets/PosthogIdentifier.tsx
  • apps/playground-web/src/lib/posthog/Posthog.tsx
  • apps/dashboard/src/lib/posthog/PosthogPageView.tsx
🧰 Additional context used
🧬 Code Graph Analysis (1)
apps/dashboard/src/app/nebula-app/layout.tsx (1)
apps/dashboard/src/app/nebula-app/providers.tsx (1)
  • NebulaProviders (13-31)
🪛 dotenv-linter (3.3.0)
apps/dashboard/.env.example

[warning] 111-111: [QuoteCharacter] The value has quote characters (', ")


[warning] 111-111: [UnorderedKey] The NEXT_PUBLIC_ENGINE_CLOUD_URL key should go before the NEXT_PUBLIC_THIRDWEB_VAULT_URL key


[warning] 114-114: [QuoteCharacter] The value has quote characters (', ")


[warning] 115-115: [UnorderedKey] The NEXT_PUBLIC_POSTHOG_HOST key should go before the NEXT_PUBLIC_POSTHOG_KEY key

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Size
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (15)
apps/dashboard/knip.json (2)

16-16: Verify necessity of ignoring fast-xml-parser.

fast-xml-parser was added to the ignoreDependencies list. Please confirm this dependency is actually used elsewhere and that suppressing knip warnings for it is intentional.


17-18: Ignore new PostHog dependencies for knip analysis.

Adding posthog-js and posthog-node to ignoreDependencies prevents knip from flagging these instrumentation packages as unused at analysis time.

apps/playground-web/package.json (1)

39-40: Upgrade and add PostHog dependencies.

You've upgraded posthog-js to 1.252.0 and introduced [email protected]. Ensure these versions align with your new instrumentation-client logic and that posthog-node remains server-only and excluded from the client bundle.

apps/dashboard/package.json (1)

80-81: Upgrade PostHog dependencies in the dashboard app.

The dashboard now uses [email protected] and [email protected]. Verify posthog-node is only imported in server-side modules (e.g., instrumentation client) and not inadvertently included in the client bundle.

apps/portal/package.json (1)

46-47: Upgrade and add PostHog dependencies in the portal app.

posthog-js pinned to 1.252.0 and [email protected] added. Confirm posthog-node is confined to server instrumentation code and excluded from client-side outputs via your Next.js rewrites.

apps/playground-web/knip.json (1)

6-6: Ignore posthog-node in knip for playground-web.

Adding posthog-node to ignoreDependencies prevents knip from falsely flagging server-only instrumentation modules as unused, matching the pattern in other apps.

apps/portal/knip.json (1)

11-13: PostHog deps correctly ignored – good catch

Adding posthog-node to the ignoreDependencies list keeps knip from flagging this server-side SDK as “unused” in the client bundle audit. No further action required.

apps/dashboard/src/app/pay/components/client/Providers.client.tsx (1)

7-10: Verify app-wide PostHog initialisation now that the provider is gone

PHProvider (and the per-page <PostHogPageView />) were removed, so any analytics/page-view tracking must now be handled by the new top-level instrumentation-client. Double-check that this file is imported exactly once (e.g. via app/layout.tsx or src/instrumentation.ts) so PostHog is initialised only once in the browser context.

apps/portal/src/components/AI/chat.tsx (1)

126-127: Callback deps updated correctly

posthog was removed from the dependency array; the hook now only re-creates when conversationId changes. Looks good.

apps/playground-web/src/app/layout.tsx (1)

47-63: Ensure new instrumentation client is imported
Legacy PHProvider and PostHog components are removed, but I don’t see an import for the side‐effect‐only instrumentation-client.ts (PostHog init). Verify you’ve added something like:

import '../instrumentation-client';

so analytics actually boot up.

apps/dashboard/src/app/bridge/components/client/Providers.client.tsx (1)

1-5: Legacy PostHog wrappers correctly removed
All PHProvider and PostHogPageView imports/usage have been stripped, simplifying the provider hierarchy.

apps/dashboard/src/app/(app)/layout.tsx (1)

63-74: Removal of legacy PostHog layout components approved
The PHProvider, PosthogHeadSetup, and PostHogPageView have been cleanly removed from the root layout.

apps/playground-web/instrumentation-client.ts (1)

6-14: ui_host mismatch with rewrite target

Rewrites forward /_ph/* to https://us.i.posthog.com but ui_host here points to https://us.posthog.com (no “.i” sub-domain).
Confirm this endpoint is intentional; otherwise events and static assets may bypass the proxy/CSP.

apps/dashboard/next.config.ts (1)

188-190: Verify unsupported skipTrailingSlashRedirect flag

skipTrailingSlashRedirect is still experimental and absent from some Next.js versions.
If the build fails, replace it with a custom rewrite or upgrade Next.js to ≥ 13.4.
Please confirm CI passes on all deployment targets.

apps/playground-web/next.config.mjs (1)

49-52:

✅ Verification successful

Verify skipTrailingSlashRedirect is supported in your Next.js version

skipTrailingSlashRedirect is only available from Next 14.1+.
If the project is pinned to an earlier version the build will fail silently at runtime.

Run a quick grep to confirm the workspace Next.js version:


🏁 Script executed:

#!/bin/bash
jq -r '.dependencies."next"' package.json

Length of output: 48


🏁 Script executed:

#!/bin/bash
# Check if Next.js is listed under dependencies
jq -r '.dependencies."next"' package.json
# Check if Next.js is listed under devDependencies
jq -r '.devDependencies."next"' package.json
# Fallback: grep any mention of "next" in package.json
rg '"next"' -n package.json

Length of output: 130


🏁 Script executed:

#!/bin/bash
# Check if Next.js is listed in the playground-web app’s package.json
TARGET="apps/playground-web/package.json"
echo "Inspecting $TARGET for Next.js version..."
jq -r '.dependencies."next"' "$TARGET"
jq -r '.devDependencies."next"' "$TARGET"
rg '"next"' -n "$TARGET" || true

Length of output: 402


skipTrailingSlashRedirect is supported in Next.js 15.3.3

No action required—your Next.js version (15.3.3) is above the 14.1+ threshold for skipTrailingSlashRedirect.

• apps/playground-web/package.json:
– "next": "15.3.3"

cursor[bot]

This comment was marked as outdated.

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: 0

♻️ Duplicate comments (1)
apps/dashboard/src/hooks/analytics/useTrack.ts (1)

16-22: ⚠️ Potential issue

Duplicate console logging still enabled for production – remove the second call or guard it

The second console.debug (lines 20-21) executes unconditionally, so production builds will emit analytics payloads to the browser console. This repeats the message already printed inside the NODE_ENV !== "production" guard, adds noise, and risks leaking PII.

-    if (process.env.NODE_ENV !== "production") {
-      console.debug(`[PH.capture]:${catActLab}`, restData);
-    }
-
-    // TODO: bring back tracking
-    console.debug(catActLab, restData);
+    // TODO: bring back tracking
+    if (process.env.NODE_ENV !== "production") {
+      console.debug(`[PH.capture disabled]: ${catActLab}`, restData);
+    }

This keeps a single debug line only in non-production environments.

🧹 Nitpick comments (2)
apps/dashboard/.env.example (2)

107-108: Add context for NEXT_PUBLIC_ENGINE_CLOUD_URL.

This new variable lacks a descriptive comment; please add a short explanation (purpose, default/local-dev value) above it, consistent with the style of adjacent entries.

🧰 Tools
🪛 dotenv-linter (3.3.0)

[warning] 107-107: [QuoteCharacter] The value has quote characters (', ")


[warning] 107-107: [UnorderedKey] The NEXT_PUBLIC_ENGINE_CLOUD_URL key should go before the NEXT_PUBLIC_THIRDWEB_VAULT_URL key


109-111: Quote the POSTHOG_HOST URL for consistency.

All other host URLs in this example are wrapped in double quotes. Update accordingly:

-NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
+NEXT_PUBLIC_POSTHOG_HOST="https://us.i.posthog.com"
🧰 Tools
🪛 dotenv-linter (3.3.0)

[warning] 110-110: [QuoteCharacter] The value has quote characters (', ")


[warning] 111-111: [UnorderedKey] The NEXT_PUBLIC_POSTHOG_HOST key should go before the NEXT_PUBLIC_POSTHOG_KEY key

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2cdf4df and e4907ad.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (43)
  • apps/dashboard/.env.example (1 hunks)
  • apps/dashboard/knip.json (1 hunks)
  • apps/dashboard/next.config.ts (2 hunks)
  • apps/dashboard/package.json (1 hunks)
  • apps/dashboard/src/@/analytics/dashboard.client.tsx (1 hunks)
  • apps/dashboard/src/@/analytics/shared.ts (1 hunks)
  • apps/dashboard/src/@3rdweb-sdk/react/components/connect-wallet/index.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/account/components/AccountHeader.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/account/settings/AccountSettingsPage.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/layout.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/login/onboarding/account-onboarding.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/layout.tsx (4 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/layout.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/components/TeamHeader/team-header-logged-in.client.tsx (2 hunks)
  • apps/dashboard/src/app/bridge/components/client/Providers.client.tsx (1 hunks)
  • apps/dashboard/src/app/nebula-app/layout.tsx (1 hunks)
  • apps/dashboard/src/app/pay/components/client/Providers.client.tsx (1 hunks)
  • apps/dashboard/src/components/wallets/PosthogIdentifier.tsx (0 hunks)
  • apps/dashboard/src/components/wallets/PosthogIdentifierServer.tsx (0 hunks)
  • apps/dashboard/src/hooks/analytics/useTrack.ts (1 hunks)
  • apps/dashboard/src/lib/posthog/Posthog.tsx (0 hunks)
  • apps/dashboard/src/lib/posthog/PosthogHeadSetup.tsx (0 hunks)
  • apps/dashboard/src/lib/posthog/PosthogPageView.tsx (0 hunks)
  • apps/dashboard/src/utils/errorParser.tsx (0 hunks)
  • apps/playground-web/instrumentation-client.ts (1 hunks)
  • apps/playground-web/knip.json (1 hunks)
  • apps/playground-web/next.config.mjs (1 hunks)
  • apps/playground-web/package.json (1 hunks)
  • apps/playground-web/src/app/layout.tsx (1 hunks)
  • apps/playground-web/src/lib/posthog/Posthog.tsx (0 hunks)
  • apps/playground-web/src/lib/posthog/PosthogHeadSetup.tsx (0 hunks)
  • apps/playground-web/src/lib/posthog/PosthogPageView.tsx (0 hunks)
  • apps/portal/instrumentation-client.ts (1 hunks)
  • apps/portal/knip.json (1 hunks)
  • apps/portal/next.config.mjs (1 hunks)
  • apps/portal/package.json (1 hunks)
  • apps/portal/src/app/layout.tsx (1 hunks)
  • apps/portal/src/components/AI/chat.tsx (1 hunks)
  • apps/portal/src/components/others/Feedback.tsx (1 hunks)
  • apps/portal/src/lib/env.ts (0 hunks)
  • apps/portal/src/lib/posthog/Posthog.tsx (0 hunks)
  • apps/portal/src/lib/posthog/PosthogHeadSetup.tsx (0 hunks)
  • apps/portal/src/lib/posthog/PosthogPageView.tsx (0 hunks)
💤 Files with no reviewable changes (14)
  • apps/dashboard/src/app/(app)/team/[team_slug]/layout.tsx
  • apps/dashboard/src/utils/errorParser.tsx
  • apps/playground-web/src/lib/posthog/PosthogHeadSetup.tsx
  • apps/dashboard/src/components/wallets/PosthogIdentifierServer.tsx
  • apps/portal/src/lib/posthog/PosthogHeadSetup.tsx
  • apps/playground-web/src/lib/posthog/PosthogPageView.tsx
  • apps/dashboard/src/lib/posthog/PosthogHeadSetup.tsx
  • apps/portal/src/lib/env.ts
  • apps/dashboard/src/lib/posthog/PosthogPageView.tsx
  • apps/portal/src/lib/posthog/PosthogPageView.tsx
  • apps/portal/src/lib/posthog/Posthog.tsx
  • apps/playground-web/src/lib/posthog/Posthog.tsx
  • apps/dashboard/src/lib/posthog/Posthog.tsx
  • apps/dashboard/src/components/wallets/PosthogIdentifier.tsx
✅ Files skipped from review due to trivial changes (1)
  • apps/dashboard/src/@/analytics/shared.ts
🚧 Files skipped from review as they are similar to previous changes (26)
  • apps/dashboard/src/app/(app)/account/components/AccountHeader.tsx
  • apps/dashboard/knip.json
  • apps/playground-web/knip.json
  • apps/dashboard/src/app/pay/components/client/Providers.client.tsx
  • apps/portal/knip.json
  • apps/dashboard/src/app/(app)/account/settings/AccountSettingsPage.tsx
  • apps/dashboard/src/app/nebula-app/layout.tsx
  • apps/portal/package.json
  • apps/dashboard/src/app/bridge/components/client/Providers.client.tsx
  • apps/dashboard/src/app/(app)/login/onboarding/account-onboarding.tsx
  • apps/playground-web/package.json
  • apps/dashboard/next.config.ts
  • apps/playground-web/instrumentation-client.ts
  • apps/dashboard/src/app/(app)/team/components/TeamHeader/team-header-logged-in.client.tsx
  • apps/portal/src/components/others/Feedback.tsx
  • apps/portal/src/components/AI/chat.tsx
  • apps/portal/instrumentation-client.ts
  • apps/dashboard/package.json
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/layout.tsx
  • apps/dashboard/src/@3rdweb-sdk/react/components/connect-wallet/index.tsx
  • apps/playground-web/src/app/layout.tsx
  • apps/dashboard/src/@/analytics/dashboard.client.tsx
  • apps/playground-web/next.config.mjs
  • apps/dashboard/src/app/(app)/layout.tsx
  • apps/portal/src/app/layout.tsx
  • apps/portal/next.config.mjs
🧰 Additional context used
🪛 dotenv-linter (3.3.0)
apps/dashboard/.env.example

[warning] 107-107: [QuoteCharacter] The value has quote characters (', ")


[warning] 107-107: [UnorderedKey] The NEXT_PUBLIC_ENGINE_CLOUD_URL key should go before the NEXT_PUBLIC_THIRDWEB_VAULT_URL key


[warning] 110-110: [QuoteCharacter] The value has quote characters (', ")


[warning] 111-111: [UnorderedKey] The NEXT_PUBLIC_POSTHOG_HOST key should go before the NEXT_PUBLIC_POSTHOG_KEY key

⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: Size
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: Build Packages
  • GitHub Check: Lint Packages
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Unit Tests
  • GitHub Check: Analyze (javascript)

cursor[bot]

This comment was marked as outdated.

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 (2)
apps/dashboard/.env.example (1)

107-108: Remove unnecessary quotes & place key logically

Quote marks are not needed when the value is empty, and they trigger dotenv-linter warnings.
Also, NEXT_PUBLIC_ENGINE_CLOUD_URL should be grouped with the other NEXT_PUBLIC_* URLs above for readability.

-NEXT_PUBLIC_ENGINE_CLOUD_URL=""
+NEXT_PUBLIC_ENGINE_CLOUD_URL=
🧰 Tools
🪛 dotenv-linter (3.3.0)

[warning] 107-107: [QuoteCharacter] The value has quote characters (', ")


[warning] 107-107: [UnorderedKey] The NEXT_PUBLIC_ENGINE_CLOUD_URL key should go before the NEXT_PUBLIC_THIRDWEB_VAULT_URL key

apps/dashboard/instrumentation-client.ts (1)

5-17: Expose the singleton instance to downstream modules

At the moment this file only performs side-effects. Exporting the configured posthog
instance allows other helpers (e.g. a typed track() wrapper) to reuse the same import
instead of re-importing posthog-js elsewhere.

 if (NEXT_PUBLIC_POSTHOG_KEY) {
   posthog.init(/* … */);
 }
 
-export {};
+export { posthog };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e4907ad and ab73bf4.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (43)
  • apps/dashboard/.env.example (1 hunks)
  • apps/dashboard/instrumentation-client.ts (1 hunks)
  • apps/dashboard/knip.json (1 hunks)
  • apps/dashboard/next.config.ts (2 hunks)
  • apps/dashboard/package.json (1 hunks)
  • apps/dashboard/src/@/analytics/dashboard.client.tsx (1 hunks)
  • apps/dashboard/src/@3rdweb-sdk/react/components/connect-wallet/index.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/account/components/AccountHeader.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/account/settings/AccountSettingsPage.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/layout.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/login/onboarding/account-onboarding.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/layout.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/layout.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/components/TeamHeader/team-header-logged-in.client.tsx (3 hunks)
  • apps/dashboard/src/app/bridge/components/client/Providers.client.tsx (1 hunks)
  • apps/dashboard/src/app/nebula-app/layout.tsx (1 hunks)
  • apps/dashboard/src/app/pay/components/client/Providers.client.tsx (1 hunks)
  • apps/dashboard/src/components/wallets/PosthogIdentifier.tsx (0 hunks)
  • apps/dashboard/src/components/wallets/PosthogIdentifierServer.tsx (0 hunks)
  • apps/dashboard/src/hooks/analytics/useTrack.ts (1 hunks)
  • apps/dashboard/src/lib/posthog/Posthog.tsx (0 hunks)
  • apps/dashboard/src/lib/posthog/PosthogHeadSetup.tsx (0 hunks)
  • apps/dashboard/src/lib/posthog/PosthogPageView.tsx (0 hunks)
  • apps/dashboard/src/utils/errorParser.tsx (0 hunks)
  • apps/playground-web/instrumentation-client.ts (1 hunks)
  • apps/playground-web/knip.json (1 hunks)
  • apps/playground-web/next.config.mjs (1 hunks)
  • apps/playground-web/package.json (1 hunks)
  • apps/playground-web/src/app/layout.tsx (1 hunks)
  • apps/playground-web/src/lib/posthog/Posthog.tsx (0 hunks)
  • apps/playground-web/src/lib/posthog/PosthogHeadSetup.tsx (0 hunks)
  • apps/playground-web/src/lib/posthog/PosthogPageView.tsx (0 hunks)
  • apps/portal/instrumentation-client.ts (1 hunks)
  • apps/portal/knip.json (1 hunks)
  • apps/portal/next.config.mjs (1 hunks)
  • apps/portal/package.json (1 hunks)
  • apps/portal/src/app/layout.tsx (1 hunks)
  • apps/portal/src/components/AI/chat.tsx (1 hunks)
  • apps/portal/src/components/others/Feedback.tsx (1 hunks)
  • apps/portal/src/lib/env.ts (0 hunks)
  • apps/portal/src/lib/posthog/Posthog.tsx (0 hunks)
  • apps/portal/src/lib/posthog/PosthogHeadSetup.tsx (0 hunks)
  • apps/portal/src/lib/posthog/PosthogPageView.tsx (0 hunks)
💤 Files with no reviewable changes (14)
  • apps/dashboard/src/app/(app)/team/[team_slug]/layout.tsx
  • apps/portal/src/lib/posthog/PosthogHeadSetup.tsx
  • apps/dashboard/src/lib/posthog/PosthogHeadSetup.tsx
  • apps/dashboard/src/utils/errorParser.tsx
  • apps/dashboard/src/components/wallets/PosthogIdentifierServer.tsx
  • apps/portal/src/lib/posthog/PosthogPageView.tsx
  • apps/portal/src/lib/posthog/Posthog.tsx
  • apps/playground-web/src/lib/posthog/Posthog.tsx
  • apps/portal/src/lib/env.ts
  • apps/playground-web/src/lib/posthog/PosthogHeadSetup.tsx
  • apps/dashboard/src/lib/posthog/Posthog.tsx
  • apps/dashboard/src/lib/posthog/PosthogPageView.tsx
  • apps/playground-web/src/lib/posthog/PosthogPageView.tsx
  • apps/dashboard/src/components/wallets/PosthogIdentifier.tsx
✅ Files skipped from review due to trivial changes (1)
  • apps/dashboard/knip.json
🚧 Files skipped from review as they are similar to previous changes (26)
  • apps/portal/package.json
  • apps/playground-web/package.json
  • apps/playground-web/knip.json
  • apps/dashboard/src/app/(app)/account/components/AccountHeader.tsx
  • apps/dashboard/src/app/(app)/login/onboarding/account-onboarding.tsx
  • apps/portal/knip.json
  • apps/dashboard/package.json
  • apps/dashboard/src/app/(app)/account/settings/AccountSettingsPage.tsx
  • apps/portal/src/components/others/Feedback.tsx
  • apps/dashboard/src/app/pay/components/client/Providers.client.tsx
  • apps/dashboard/src/app/bridge/components/client/Providers.client.tsx
  • apps/playground-web/instrumentation-client.ts
  • apps/playground-web/src/app/layout.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/layout.tsx
  • apps/dashboard/src/app/nebula-app/layout.tsx
  • apps/portal/instrumentation-client.ts
  • apps/dashboard/src/app/(app)/team/components/TeamHeader/team-header-logged-in.client.tsx
  • apps/dashboard/next.config.ts
  • apps/dashboard/src/app/(app)/layout.tsx
  • apps/dashboard/src/@3rdweb-sdk/react/components/connect-wallet/index.tsx
  • apps/dashboard/src/@/analytics/dashboard.client.tsx
  • apps/portal/src/components/AI/chat.tsx
  • apps/playground-web/next.config.mjs
  • apps/dashboard/src/hooks/analytics/useTrack.ts
  • apps/portal/next.config.mjs
  • apps/portal/src/app/layout.tsx
🧰 Additional context used
🪛 dotenv-linter (3.3.0)
apps/dashboard/.env.example

[warning] 107-107: [QuoteCharacter] The value has quote characters (', ")


[warning] 107-107: [UnorderedKey] The NEXT_PUBLIC_ENGINE_CLOUD_URL key should go before the NEXT_PUBLIC_THIRDWEB_VAULT_URL key


[warning] 110-110: [QuoteCharacter] The value has quote characters (', ")


[warning] 111-111: [UnorderedKey] The NEXT_PUBLIC_POSTHOG_HOST key should go before the NEXT_PUBLIC_POSTHOG_KEY key

⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Size
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: Lint Packages
  • GitHub Check: Build Packages
  • GitHub Check: Unit Tests
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
apps/dashboard/instrumentation-client.ts (1)

9-15: Verify option names & value types against PostHog 1.252.0

capture_pageview: "history_change" and
capture_pageleave: "if_capture_pageview" are not documented in the PostHog 1.x typings
(accepted types are boolean or "history" for SPA support). A typo here will silently disable
page/navigation analytics.

Please confirm these strings are supported in 1.252.0 or switch to the canonical flags:

-    capture_pageview: "history_change",
-    capture_pageleave: "if_capture_pageview",
+    capture_pageview: true,          // or "history"
+    capture_pageleave: true,

cursor[bot]

This comment was marked as outdated.

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

♻️ Duplicate comments (1)
apps/dashboard/.env.example (1)

110-111: ⚠️ Potential issue

Align PostHog host with client code and fix key ordering.
The current NEXT_PUBLIC_POSTHOG_HOST (https://us.i.posthog.com) doesn’t match instrumentation-client.ts (which uses https://us.posthog.com), causing analytics to break. Also, alphabetize the vars (HOST before KEY) and remove quotes on the empty KEY per linter rules.

- NEXT_PUBLIC_POSTHOG_KEY=""
- NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
+ NEXT_PUBLIC_POSTHOG_HOST=https://us.posthog.com
+ NEXT_PUBLIC_POSTHOG_KEY=
🧰 Tools
🪛 dotenv-linter (3.3.0)

[warning] 110-110: [QuoteCharacter] The value has quote characters (', ")


[warning] 111-111: [UnorderedKey] The NEXT_PUBLIC_POSTHOG_HOST key should go before the NEXT_PUBLIC_POSTHOG_KEY key

🧹 Nitpick comments (6)
apps/dashboard/.eslintrc.js (1)

113-118: Message string and pattern mismatch – tighten for clarity

The explanatory message uses the single-star pattern "src/@/analytics/*", while the override below targets "src/@/analytics/**/*".
Although this is purely cosmetic, keeping the wording/patterns consistent avoids confusion for future maintainers skimming the config.

- 'Import "posthog-js" directly only within the analytics helpers ("src/@/analytics/*"). Use the exported helpers from "@/analytics/track" elsewhere.',
+ 'Import "posthog-js" directly only within analytics helpers ("src/@/analytics/**/*"). Use the exported helpers from "@/analytics/track" elsewhere.',
apps/dashboard/.env.example (2)

106-107: Remove quotes for empty engine cloud URL and reorder keys alphabetically.
Dotenv-linter flags quote characters on empty values and expects keys in alphabetical order. Swap NEXT_PUBLIC_ENGINE_CLOUD_URL above the commented NEXT_PUBLIC_THIRDWEB_VAULT_URL and drop the quotes for the empty value.

- # NEXT_PUBLIC_THIRDWEB_VAULT_URL=""
- NEXT_PUBLIC_ENGINE_CLOUD_URL=""
+ NEXT_PUBLIC_ENGINE_CLOUD_URL=
+ # NEXT_PUBLIC_THIRDWEB_VAULT_URL=""
🧰 Tools
🪛 dotenv-linter (3.3.0)

[warning] 106-106: [QuoteCharacter] The value has quote characters (', ")


[warning] 107-107: [QuoteCharacter] The value has quote characters (', ")


[warning] 107-107: [UnorderedKey] The NEXT_PUBLIC_ENGINE_CLOUD_URL key should go before the NEXT_PUBLIC_THIRDWEB_VAULT_URL key


109-109: Capitalize the PostHog section heading.
Section headers elsewhere are title-cased. Update to match style.

-# posthog setup
+# PostHog setup
apps/dashboard/src/@/analytics/track/__internal.ts (1)

22-23: Return value can signal success/failure.

Consider returning a boolean (true if captured, false if skipped).
It allows callers to react (e.g. buffer locally) without having to re-inspect PostHog state.

apps/dashboard/src/@/analytics/track/README.md (2)

32-38: Add language identifier to fenced code block (markdown-lint MD040).

-```
+```text
 track/               # ← you are here
 ├─ __internal.ts     # low-level wrapper around posthog.capture (do NOT use)
 ├─ contract.ts       # "contract" event category helpers
 ├─ README.md         # this file
 └─ …                 # future categories live here
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

32-32: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


40-46: Comma before ‘so’ (LanguageTool).

… star-export, so the helpers are surfaced …

Minor grammar nit – add a comma before “so”.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab73bf4 and fd4f354.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (49)
  • apps/dashboard/.env.example (1 hunks)
  • apps/dashboard/.eslintrc.js (2 hunks)
  • apps/dashboard/instrumentation-client.ts (1 hunks)
  • apps/dashboard/knip.json (1 hunks)
  • apps/dashboard/next.config.ts (2 hunks)
  • apps/dashboard/package.json (1 hunks)
  • apps/dashboard/src/@/analytics/dashboard.client.tsx (1 hunks)
  • apps/dashboard/src/@/analytics/track.ts (1 hunks)
  • apps/dashboard/src/@/analytics/track/README.md (1 hunks)
  • apps/dashboard/src/@/analytics/track/__internal.ts (1 hunks)
  • apps/dashboard/src/@/analytics/track/contract.ts (1 hunks)
  • apps/dashboard/src/@3rdweb-sdk/react/components/connect-wallet/index.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/account/components/AccountHeader.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/account/settings/AccountSettingsPage.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/layout.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/login/onboarding/account-onboarding.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/layout.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/layout.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/components/TeamHeader/team-header-logged-in.client.tsx (3 hunks)
  • apps/dashboard/src/app/bridge/components/client/Providers.client.tsx (1 hunks)
  • apps/dashboard/src/app/nebula-app/layout.tsx (1 hunks)
  • apps/dashboard/src/app/pay/components/client/Providers.client.tsx (1 hunks)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx (3 hunks)
  • apps/dashboard/src/components/wallets/PosthogIdentifier.tsx (0 hunks)
  • apps/dashboard/src/components/wallets/PosthogIdentifierServer.tsx (0 hunks)
  • apps/dashboard/src/hooks/analytics/useTrack.ts (1 hunks)
  • apps/dashboard/src/lib/posthog/Posthog.tsx (0 hunks)
  • apps/dashboard/src/lib/posthog/PosthogHeadSetup.tsx (0 hunks)
  • apps/dashboard/src/lib/posthog/PosthogPageView.tsx (0 hunks)
  • apps/dashboard/src/utils/errorParser.tsx (0 hunks)
  • apps/playground-web/instrumentation-client.ts (1 hunks)
  • apps/playground-web/knip.json (1 hunks)
  • apps/playground-web/next.config.mjs (1 hunks)
  • apps/playground-web/package.json (1 hunks)
  • apps/playground-web/src/app/layout.tsx (1 hunks)
  • apps/playground-web/src/lib/posthog/Posthog.tsx (0 hunks)
  • apps/playground-web/src/lib/posthog/PosthogHeadSetup.tsx (0 hunks)
  • apps/playground-web/src/lib/posthog/PosthogPageView.tsx (0 hunks)
  • apps/portal/instrumentation-client.ts (1 hunks)
  • apps/portal/knip.json (1 hunks)
  • apps/portal/next.config.mjs (1 hunks)
  • apps/portal/package.json (1 hunks)
  • apps/portal/src/app/layout.tsx (1 hunks)
  • apps/portal/src/components/AI/chat.tsx (1 hunks)
  • apps/portal/src/components/others/Feedback.tsx (1 hunks)
  • apps/portal/src/lib/env.ts (0 hunks)
  • apps/portal/src/lib/posthog/Posthog.tsx (0 hunks)
  • apps/portal/src/lib/posthog/PosthogHeadSetup.tsx (0 hunks)
  • apps/portal/src/lib/posthog/PosthogPageView.tsx (0 hunks)
💤 Files with no reviewable changes (14)
  • apps/dashboard/src/utils/errorParser.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/layout.tsx
  • apps/playground-web/src/lib/posthog/PosthogHeadSetup.tsx
  • apps/dashboard/src/lib/posthog/PosthogHeadSetup.tsx
  • apps/dashboard/src/components/wallets/PosthogIdentifierServer.tsx
  • apps/portal/src/lib/posthog/PosthogHeadSetup.tsx
  • apps/portal/src/lib/env.ts
  • apps/portal/src/lib/posthog/PosthogPageView.tsx
  • apps/dashboard/src/lib/posthog/PosthogPageView.tsx
  • apps/playground-web/src/lib/posthog/Posthog.tsx
  • apps/dashboard/src/lib/posthog/Posthog.tsx
  • apps/playground-web/src/lib/posthog/PosthogPageView.tsx
  • apps/dashboard/src/components/wallets/PosthogIdentifier.tsx
  • apps/portal/src/lib/posthog/Posthog.tsx
✅ Files skipped from review due to trivial changes (2)
  • apps/playground-web/package.json
  • apps/dashboard/src/@/analytics/track.ts
🚧 Files skipped from review as they are similar to previous changes (27)
  • apps/playground-web/knip.json
  • apps/dashboard/knip.json
  • apps/dashboard/package.json
  • apps/portal/knip.json
  • apps/dashboard/src/app/(app)/account/settings/AccountSettingsPage.tsx
  • apps/portal/package.json
  • apps/dashboard/src/app/(app)/login/onboarding/account-onboarding.tsx
  • apps/dashboard/src/app/(app)/account/components/AccountHeader.tsx
  • apps/dashboard/src/app/pay/components/client/Providers.client.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/layout.tsx
  • apps/portal/src/components/others/Feedback.tsx
  • apps/dashboard/instrumentation-client.ts
  • apps/dashboard/src/app/bridge/components/client/Providers.client.tsx
  • apps/playground-web/instrumentation-client.ts
  • apps/dashboard/src/app/(app)/layout.tsx
  • apps/playground-web/next.config.mjs
  • apps/dashboard/src/app/(app)/team/components/TeamHeader/team-header-logged-in.client.tsx
  • apps/portal/instrumentation-client.ts
  • apps/dashboard/src/app/nebula-app/layout.tsx
  • apps/portal/src/components/AI/chat.tsx
  • apps/dashboard/next.config.ts
  • apps/playground-web/src/app/layout.tsx
  • apps/portal/src/app/layout.tsx
  • apps/dashboard/src/hooks/analytics/useTrack.ts
  • apps/dashboard/src/@/analytics/dashboard.client.tsx
  • apps/portal/next.config.mjs
  • apps/dashboard/src/@3rdweb-sdk/react/components/connect-wallet/index.tsx
🧰 Additional context used
🧬 Code Graph Analysis (2)
apps/dashboard/src/@/analytics/track/contract.ts (2)
packages/thirdweb/src/exports/thirdweb.ts (1)
  • isAddress (308-308)
apps/dashboard/src/@/analytics/track/__internal.ts (1)
  • __internal__reportEvent (12-23)
apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx (1)
apps/dashboard/src/@/analytics/track/contract.ts (1)
  • reportContractDeployed (52-56)
🪛 dotenv-linter (3.3.0)
apps/dashboard/.env.example

[warning] 107-107: [QuoteCharacter] The value has quote characters (', ")


[warning] 107-107: [UnorderedKey] The NEXT_PUBLIC_ENGINE_CLOUD_URL key should go before the NEXT_PUBLIC_THIRDWEB_VAULT_URL key


[warning] 110-110: [QuoteCharacter] The value has quote characters (', ")


[warning] 111-111: [UnorderedKey] The NEXT_PUBLIC_POSTHOG_HOST key should go before the NEXT_PUBLIC_POSTHOG_KEY key

🪛 LanguageTool
apps/dashboard/src/@/analytics/track/README.md

[uncategorized] ~85-~85: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...(one directory up) and add a star-export so the helpers are surfaced at the pack...

(COMMA_COMPOUND_SENTENCE_2)

🪛 markdownlint-cli2 (0.17.2)
apps/dashboard/src/@/analytics/track/README.md

32-32: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: Size
  • GitHub Check: Unit Tests
  • GitHub Check: Build Packages
  • GitHub Check: Lint Packages
  • GitHub Check: Analyze (javascript)

Comment on lines +147 to +153
// allow direct PostHog imports inside analytics helpers
{
files: "src/@/analytics/**/*",
rules: {
"no-restricted-imports": ["off"],
},
},
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

Don’t disable all import restrictions in analytics helpers

Turning no-restricted-imports completely off for "src/@/analytics/**/*" re-enables every restricted import (e.g. direct Chakra-UI components, next/navigation’s useRouter, etc.) inside that folder.
We only need to lift the ban for posthog-js; the other guards should keep protecting the codebase.

Consider a narrower override that keeps the rest of the rule active:

{
  files: "src/@/analytics/**/*",
  rules: {
-   "no-restricted-imports": ["off"],
+   "no-restricted-imports": [
+     "error",
+     {
+       paths: [
+         // Copy the existing paths **except** the posthog-js entry,
+         // so the other restrictions remain enforced.
+       ],
+     },
+   ],
  },
},

This preserves the architectural discipline for all other libraries while still allowing direct posthog-js imports where they’re meant to live.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/dashboard/.eslintrc.js around lines 147 to 153, the current override
disables all import restrictions for files in src/@/analytics/**/*, which
unintentionally allows all restricted imports. Instead, modify the override to
only allow direct imports of 'posthog-js' by configuring the
no-restricted-imports rule to permit 'posthog-js' while keeping other
restrictions active. This ensures only the intended exception is made without
removing protections for other restricted imports.

Comment on lines +16 to +20
if (!posthog.__loaded) {
console.warn(
"[DASHBOARD_ANALYTICS] is not initialized, cannot track event",
);
return;
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

posthog.__loaded is not in the public typings – this will break tsc.

posthog-js’s type definitions do not expose the __loaded flag, so accessing it directly fails strict compilation (Property '__loaded' does not exist on type 'PostHog'). Cast or guard with an index signature to silence the error without disabling noImplicitAny.

-  if (!posthog.__loaded) {
+  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
+  if (!(posthog as unknown as { __loaded?: boolean }).__loaded) {
📝 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
if (!posthog.__loaded) {
console.warn(
"[DASHBOARD_ANALYTICS] is not initialized, cannot track event",
);
return;
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
if (!(posthog as unknown as { __loaded?: boolean }).__loaded) {
console.warn(
"[DASHBOARD_ANALYTICS] is not initialized, cannot track event",
);
return;
🤖 Prompt for AI Agents
In apps/dashboard/src/@/analytics/track/__internal.ts around lines 16 to 20,
accessing the private property posthog.__loaded causes TypeScript compilation
errors because it is not in the public typings. To fix this, use a type
assertion or an index signature to access __loaded safely, such as casting
posthog to any or using (posthog as any).__loaded, so the compiler does not
complain while preserving type safety without disabling noImplicitAny.

Comment on lines +52 to +56
export function reportContractDeployed(
payload: z.infer<typeof EVMContractSchema>,
) {
__internal__reportEvent("contract deployed", payload);
}
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

Runtime validation is missing despite documented intent.

reportContractDeployed forwards the payload without calling EVMContractSchema.parse, so invalid data can slip through unchecked.

 export function reportContractDeployed(
   payload: z.infer<typeof EVMContractSchema>,
 ) {
-  __internal__reportEvent("contract deployed", payload);
+  const parsed = EVMContractSchema.parse(payload);
+  __internal__reportEvent("contract deployed", parsed);
 }
📝 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
export function reportContractDeployed(
payload: z.infer<typeof EVMContractSchema>,
) {
__internal__reportEvent("contract deployed", payload);
}
export function reportContractDeployed(
payload: z.infer<typeof EVMContractSchema>,
) {
const parsed = EVMContractSchema.parse(payload);
__internal__reportEvent("contract deployed", parsed);
}
🤖 Prompt for AI Agents
In apps/dashboard/src/@/analytics/track/contract.ts around lines 52 to 56, the
function reportContractDeployed forwards the payload without validating it
against EVMContractSchema. To fix this, call EVMContractSchema.parse on the
payload before passing it to __internal__reportEvent to ensure runtime
validation and prevent invalid data from being reported.

Comment on lines +35 to +39
const EVMAddressSchema = z.string().check(
z.refine(isAddress, {
message: "Invalid EVM address",
}),
);
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

z.string().check(…) and z.refine combo is invalid Zod API.

check is not a method on Zod schemas (v3/v4). Use .refine directly:

-const EVMAddressSchema = z.string().check(
-  z.refine(isAddress, {
-    message: "Invalid EVM address",
-  }),
-);
+const EVMAddressSchema = z
+  .string()
+  .refine(isAddress, { message: "Invalid EVM address" });
📝 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 EVMAddressSchema = z.string().check(
z.refine(isAddress, {
message: "Invalid EVM address",
}),
);
const EVMAddressSchema = z
.string()
.refine(isAddress, { message: "Invalid EVM address" });
🤖 Prompt for AI Agents
In apps/dashboard/src/@/analytics/track/contract.ts around lines 35 to 39, the
code incorrectly uses z.string().check(...) which is not a valid method in Zod
v3/v4. Replace the usage of .check with a direct call to .refine on the string
schema, passing the isAddress function and the error message as options to
correctly validate the EVM address.

Comment on lines +41 to +45
const EVMChainIdSchema = z.coerce.number().check(
z.gte(1, {
message: "Invalid chain ID",
}),
);
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

Same issue for chain-id schema – .gte belongs on a number schema.

-const EVMChainIdSchema = z.coerce.number().check(
-  z.gte(1, {
-    message: "Invalid chain ID",
-  }),
-);
+const EVMChainIdSchema = z.coerce
+  .number()
+  .gte(1, { message: "Invalid chain ID" });
📝 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 EVMChainIdSchema = z.coerce.number().check(
z.gte(1, {
message: "Invalid chain ID",
}),
);
const EVMChainIdSchema = z.coerce
.number()
.gte(1, { message: "Invalid chain ID" });
🤖 Prompt for AI Agents
In apps/dashboard/src/@/analytics/track/contract.ts around lines 41 to 45, the
chain ID schema incorrectly applies `.gte` on a coerced number schema using
`.check`. To fix this, replace `.check(z.gte(1))` with `.gte(1)` directly on the
number schema after coercion, ensuring `.gte` is called on a proper number
schema as intended by the zod library.

Comment on lines +648 to 651
reportContractDeployed({
address: contractAddr,
chainId: walletChain.id,
metadataUri: metadata.metadataUri,
});
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

Do not let analytics failures flag the deployment as failed.

reportContractDeployed runs inside the same try block that gates contract deployment; if it throws (e.g. because of later schema-validation), the whole deployment flow is treated as an error. Wrap it in its own try…catch or make the helper swallow errors.

-              reportContractDeployed({
-                address: contractAddr,
-                chainId: walletChain.id,
-              });
+              try {
+                reportContractDeployed({
+                  address: contractAddr,
+                  chainId: walletChain.id,
+                });
+              } catch {
+                // non-blocking – analytics failure should not disrupt UX
+              }
📝 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
reportContractDeployed({
address: contractAddr,
chainId: walletChain.id,
metadataUri: metadata.metadataUri,
});
try {
reportContractDeployed({
address: contractAddr,
chainId: walletChain.id,
});
} catch {
// non-blocking – analytics failure should not disrupt UX
}
🤖 Prompt for AI Agents
In
apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx
around lines 648 to 651, the call to reportContractDeployed is inside the main
try block for contract deployment, causing analytics errors to be treated as
deployment failures. To fix this, wrap the reportContractDeployed call in its
own try-catch block to catch and ignore any errors it throws, ensuring analytics
failures do not affect the deployment flow.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

✅ BugBot reviewed your changes and found no bugs!


Was this report helpful? Give feedback by reacting with 👍 or 👎

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: 9

🔭 Outside diff range comments (5)
apps/dashboard/.env.example (1)

105-112: ⚠️ Potential issue

Fix dotenv-linter violations: reorder keys and drop quotes

dotenv-linter currently complains about:

  1. NEXT_PUBLIC_ENGINE_CLOUD_URL coming after NEXT_PUBLIC_THIRDWEB_VAULT_URL.
  2. Quotes around the empty values.
  3. NEXT_PUBLIC_POSTHOG_HOST being after NEXT_PUBLIC_POSTHOG_KEY, and the quoted empty key.

Failing to satisfy the linter blocks CI for every env-file change, so please align the keys alphabetically and remove superfluous quotes:

-# required for server wallet management
-NEXT_PUBLIC_THIRDWEB_VAULT_URL=""
-NEXT_PUBLIC_ENGINE_CLOUD_URL=""
+# required for server wallet management
+NEXT_PUBLIC_ENGINE_CLOUD_URL=
+NEXT_PUBLIC_THIRDWEB_VAULT_URL=

 # PostHog setup
-NEXT_PUBLIC_POSTHOG_KEY=""
-NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
+# Host must precede Key (dotenv-linter) and match the client code
+NEXT_PUBLIC_POSTHOG_HOST=https://us.posthog.com
+NEXT_PUBLIC_POSTHOG_KEY=
🧰 Tools
🪛 dotenv-linter (3.3.0)

[warning] 106-106: [QuoteCharacter] The value has quote characters (', ")


[warning] 107-107: [QuoteCharacter] The value has quote characters (', ")


[warning] 107-107: [UnorderedKey] The NEXT_PUBLIC_ENGINE_CLOUD_URL key should go before the NEXT_PUBLIC_THIRDWEB_VAULT_URL key


[warning] 110-110: [QuoteCharacter] The value has quote characters (', ")


[warning] 111-111: [UnorderedKey] The NEXT_PUBLIC_POSTHOG_HOST key should go before the NEXT_PUBLIC_POSTHOG_KEY key

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/cards.tsx (1)

33-47: 🛠️ Refactor suggestion

trackingLabel is now dead code – remove prop & usages

CardLink no longer performs any tracking, yet the prop is still declared and passed from every call-site. This leaves:

  • Unused props → TypeScript “unused” warnings in strict settings.
  • Future confusion about whether tracking still happens.

Minimal clean-up:

-      <CardLink
-        title="Create Coin"
-        description="Launch your own ERC-20 coin"
-        href={`/team/${props.teamSlug}/${props.projectSlug}/assets/create/token`}
-        icon={CoinsIcon}
-        trackingLabel="create-token"
-      />
+      <CardLink
+        title="Create Coin"
+        description="Launch your own ERC-20 coin"
+        href={`/team/${props.teamSlug}/${props.projectSlug}/assets/create/token`}
+        icon={CoinsIcon}
+      />

and in CardLink:

-  trackingLabel: string;
+  /* removed tracking */

Repeat for the other two invocations.

If analytics will be re-introduced via the new typed helpers, wire them up directly instead of keeping this stale prop.

apps/dashboard/src/@/components/blocks/UpsellBannerCard.tsx (1)

47-51: 🛠️ Refactor suggestion

trackingCategory / trackingLabel are now dead-code – remove to avoid API drift

Both props were used only by the removed PostHog TrackedLinkTW. Keeping them in the public type misleads call-sites and bloats the bundle with unused strings.

-  trackingCategory: string;
-  trackingLabel: string;
+  /* legacy-tracking props removed */
apps/dashboard/src/components/cmd-k-search/index.tsx (1)

36-52: 🛠️ Refactor suggestion

queryClient parameter is dead code

contractTypesenseSearchQuery receives queryClient but never uses it, so every render allocates an ignored argument.

-function contractTypesenseSearchQuery(
-  searchQuery: string,
-  queryClient: QueryClient,
-) {
+function contractTypesenseSearchQuery(searchQuery: string) {

Update the call-sites accordingly:

-const typesenseSearchQuery = useQuery(
-  contractTypesenseSearchQuery(debouncedSearchValue, queryClient),
-);
+const typesenseSearchQuery = useQuery(
+  contractTypesenseSearchQuery(debouncedSearchValue),
+);

Removes an unnecessary dependency and lets TypeScript catch accidental misuse.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/components/delegate-button.tsx (1)

32-34: ⚠️ Potential issue

Logic hides the button even when zero tokens are delegated

tokensDelegatedQuery.data is a BigNumber.
BigNumber(0) is truthy, so the current check means the button disappears when the user has zero delegated tokens – exactly when they need it.

-if (tokensDelegatedQuery.data || tokensDelegatedQuery.isPending) {
+if (
+  tokensDelegatedQuery.isPending ||
+  (tokensDelegatedQuery.data &&
+    !tokensDelegatedQuery.data.isZero()) // only hide when > 0
+) {
   return null;
 }

Fixing this prevents an accidental dead-end in the UI.

♻️ Duplicate comments (2)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/mint-supply-tab.tsx (1)

63-66: Same logging concern as above

Forward the error to an app-level logger or toast instead of a bare console.error to avoid silent failures in production.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/components/platform-fees.tsx (1)

91-94: Consistent error handling

Align with the suggestion in other files: replace console.error with a structured logger / notification helper for observability and user feedback.

🧹 Nitpick comments (43)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/burn-tab.tsx (1)

107-110: Typo in burn-confirmation copy

"remove them them from your wallet" – duplicated word.

-              copies of the NFT will remove them them from your wallet.
+              copies of the NFT will remove them from your wallet.
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/webhooks/components/add-webhook-button.tsx (1)

80-83: Prefer structured logging over bare console.error

console.error is useful during development but tends to get lost in production environments and cannot be queried/alerted on.
If you have a centralised logger (e.g. logger.error(err, { context: … })) or an error-reporting service, use that instead so the signal is not silently discarded.

-                onError: (error) => {
-                onError(error);
-                console.error(error);
+                onError: (error) => {
+                  onError(error);               // toast / UI feedback
+                  logger.error(error, {         // persisted log
+                    component: "AddWebhookButton",
+                    action:   "createWebhook",
+                  });
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/components/distribute-button.tsx (1)

56-59: Replace console.error with a central logger

Same reasoning as for the webhook button: rely on structured, searchable logging instead of direct console output so that operational tooling can pick the failure up.

-        onError: (error) => {
-          console.error(error);
-          onError(error);
+        onError: (error) => {
+          logger.error(error, {
+            component: "DistributeButton",
+            contract:  contract.address,
+          });
+          onError(error);
apps/dashboard/src/app/(app)/get-started/team/[team_slug]/select-plan/_components/plan-selector.tsx (1)

44-46: Deduplicate hard-coded planSKU literals

The same string literals are repeated for every plan.
Introduce a union/enum (type BillingPlan = "starter" | "growth" | …) or reuse an existing constant so typos are impossible and refactors are centralised.

-  reportPlanSelected({ planSKU: "starter" });
+  reportPlanSelected({ planSKU: BillingPlan.STARTER });

This will also give you autocomplete & static-analysis support.

Also applies to: 61-63, 79-81, 96-98

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/webhooks/components/webhooks-table.tsx (1)

200-205: Use structured logging for deletion failures

console.error doesn’t surface in most production setups.
Pipe the error into your logging layer instead so failures deleting webhooks are discoverable.

-        onError: (error) => {
-          console.error(error);
+        onError: (error) => {
+          logger.error(error, {
+            component: "DeleteWebhookModal",
+            webhookId: webhook.id,
+          });
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/claim-button.tsx (1)

158-159: Console-only error handling is acceptable; consider surfacing user feedback.

Replacing analytics calls with console.error means failures are now invisible to the end-user unless they open DevTools. A toast (consistent with surrounding UX) or a generic error banner would keep the UI behaviour unchanged while still dropping analytics.

apps/dashboard/src/tw-components/button.tsx (2)

132-144: Duplicate per-component no-op trackEvent – collapse into a single helper.

Defining an identical noop in every tracked component bloats bundle size and adds ESLint suppressions. Export a shared helper (e.g. noopTrackEvent) and import it here instead.

-// eslint-disable-next-line @typescript-eslint/no-unused-vars
-const trackEvent = (..._args: unknown[]) => {};
+import { noopTrackEvent as trackEvent } from "@/analytics/track";

(Repeat for TrackedIconButton.)


160-173: Minor: onClick still builds an event object that is discarded.

If analytics is truly removed, consider deleting the whole onClick wrapper to avoid useless allocations:

- onClick={() =>
-   trackEvent({
-     category,
-     action: "click",
-     label,
-   })
- }
+ // analytics removed – plain click
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/permissions/components/index.tsx (1)

82-84: Log error once; avoid double reporting.

console.error(error) is already invoked inside useTxNotifications.onError.
The additional call here logs the same error twice. Remove this line or the one inside the hook to keep logs clean.

apps/dashboard/src/@/analytics/track/nft.ts (2)

9-12: Consider .strict() on the object schema

Without .strict() Zod silently strips unknown keys, which might hide accidental payload mistakes.
Making the schema strict fails fast and surfaces bugs early.

-const BaseNFTSchema = z.object({
+const BaseNFTSchema = z
+  .object({
     address: EVMAddressSchema,
     chainId: EVMChainIdSchema,
-});
+  })
+  .strict();

16-22: Avoid sending undefined & accept numeric token IDs

  1. PostHog drops undefined values; cleaner to omit the field entirely.
  2. On-chain IDs are numeric – allow string | number to avoid unnecessary casts.
-export function reportNFTMinted(
-  payload: NFTBasePayload & { tokenId?: string },
-) {
-  __internal__reportEvent("nft minted", {
-    ...BaseNFTSchema.parse(payload),
-    tokenId: payload.tokenId,
-  });
+export function reportNFTMinted(
+  payload: NFTBasePayload & { tokenId?: string | number },
+) {
+  const props: Record<string, unknown> = BaseNFTSchema.parse(payload);
+  if (payload.tokenId !== undefined) {
+    props.tokenId = payload.tokenId;
+  }
+  __internal__reportEvent("nft minted", props);
 }
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/overview/components/import-backend-wallet-button.tsx (1)

92-95: Replace direct console.error with a centralized logger / toast

Using console.error in production UIs can leak PII and is hard to aggregate.
Prefer a scoped logger or the existing notification system so errors surface in monitoring.

-        onError: (error) => {
-          onError(error);
-          console.error(error);
+        onError: (error) => {
+          onError(error);
+          logger.error("engine.import-backend-wallet.failed", error);
         },

(Assumes a logger utility is available; otherwise consider adding one.)

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/components/royalties.tsx (1)

104-106: Surface errors through user-facing notifications, not console.error

console.error is invisible to most users and unobservable in error-tracking tools.
Leverage the existing onError callback or a toast to give feedback and capture telemetry.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/transfer-tab.tsx (1)

72-80: Missed opportunity to hook into the new analytics helpers.

trackEvent calls were dropped and replaced with console.error.
If transfer events are still valuable, consider emitting a typed event via the new analytics helpers (e.g. reportNFTTransferred / reportTokenTransferred) inside the onSuccess/onError callbacks.

This keeps observability parity with the old implementation while benefiting from the new schema-validated pipeline.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/mint-form.tsx (1)

1-4: Import path nitpick

reportNFTMinted is imported from the root @/analytics/track, which re-exports many helpers.
Importing directly from @/analytics/track/nft would make tree-shaking slightly more effective and avoids dragging unrelated helpers into the bundle.

-import { reportNFTMinted } from "@/analytics/track";
+import { reportNFTMinted } from "@/analytics/track/nft";
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/mint-button.tsx (1)

1-4: Same tree-shaking nitpick as above

Importing the granular helper avoids bundling the full track barrel.

-import { reportTokenMinted } from "@/analytics/track";
+import { reportTokenMinted } from "@/analytics/track/token";
apps/dashboard/src/@/components/ui/NavLink.tsx (1)

27-28: No-op tracking stub may mask missing analytics and leaves dead prop API

track is now a silent no-op, yet the component still exposes a tracking prop and dutifully calls the stub.
If analytics are truly removed, consider deleting the prop (and the call) to avoid:

  • Dead code that is never executed.
  • A misleading API surface that suggests events are still emitted.
  • An unnecessary runtime function allocation on every render.
-        if (props.tracking) {
-          track({
-            category: props.tracking.category,
-            action: props.tracking.action,
-            label: props.tracking.label,
-            url: props.href,
-          });
-        }
+        // analytics removed – drop legacy tracking branch

Alternatively, wire this to the new centralised analytics helpers rather than a no-op.

apps/dashboard/src/components/notices/AnnouncementBanner.tsx (1)

6-6: Use the shared Link wrapper for consistent styling / behaviour

Everywhere else the codebase imports your custom tw-components/link abstraction.
Using next/link directly breaks visual/behavioural consistency (focus ring, smooth-scroll, etc.).
Swap to the shared Link unless there is a specific reason not to.

apps/dashboard/src/tw-components/link.tsx (1)

71-76: Left-over analytics stub inflates bundle and confuses consumers

trackEvent is declared but guaranteed to be a no-op, yet the component still fires it on every click.
Given the new central analytics layer, either:

  • Delegate to __internal__reportEvent, or
  • Remove the tracking props from TrackedLink entirely.

At minimum, drop the unused spread parameter to silence eslint without disabling the rule:

-// eslint-disable-next-line @typescript-eslint/no-unused-vars
-const trackEvent = (..._args: unknown[]) => {};
+const trackEvent = () => {};
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/relayers/components/relayers-table.tsx (1)

262-264: Replace bare console.error with structured error handling

useTxNotifications already surfaces the error to the user and probably logs it internally.
Logging the full error object twice adds little value and may unintentionally leak sensitive information (e.g. auth token in stack-traces) to the browser console.

-        onError(error);
-        console.error(error);
+        onError(error); // notifications + internal logging

Same for the revoke flow a few lines below.

If you still need console output, prefer a redacted/serialised message:

console.error("updateRelayer failed:", error?.message);

Also applies to: 374-377

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/burn-button.tsx (1)

123-127: Guard analytics call & enrich payload

A failing PostHog/network request inside reportTokenBurned would currently reject the onSuccess promise chain and surface as an uncaught error in the console.
Wrap the call or make reportTokenBurned resilient:

-reportTokenBurned({
-  address: contract.address,
-  chainId: contract.chain.id,
-  quantity: data.amount,
-});
+try {
+  reportTokenBurned({
+    address: contract.address,
+    chainId: contract.chain.id,
+    quantity: data.amount,
+    walletAddress: address,      // optional but valuable
+  });
+} catch (_) {
+  /* non-blocking – ignore analytics failure */
+}

Adds two benefits:

  1. Prevents UI breakage if analytics layer is offline.
  2. Captures the burner’s wallet for richer insights.
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/ProjectGeneralSettingsPage.tsx (1)

315-315: Duplicate error surface & potential PII leak

toast.error already conveys the failure to the user. Logging the raw error object twice may expose backend responses or tokens in plain text.

If console logging is desired, redact first:

console.error("updateProject failed:", err?.message);

Same for the delete flow.

Also applies to: 946-947

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/wallet-credentials/components/create-wallet-credential-button.tsx (1)

24-36: Avoid leaking raw error objects to the browser console

console.error(error); may expose implementation details (e.g. stack traces, request payloads) to end-users via DevTools. Consider routing unexpected errors through a central logger (e.g. Sentry) and/or printing a redacted string in production builds.

- console.error(error);
+ if (process.env.NODE_ENV === "development") {
+   // eslint-disable-next-line no-console
+   console.error(error);
+ }
+ captureException?.(error); // your centralized logger
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/relayers/components/add-relayer-button.tsx (1)

118-126: Redundant error handling path

onError already calls onError(error) from useTxNotifications, so the additional console.error(error); is probably superfluous and clutters logs.

- onError: (error) => {
-   onError(error);
-   console.error(error);
- },
+ onError,
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/overview/components/create-backend-wallet-button.tsx (1)

71-79: Prefer surfacing the specific failure reason to the toast

Only a generic “Failed to create wallet” is shown to users. You already have the error object here—forwarding error.message (or a safe fallback) will make troubleshooting easier.

 toast.promise(promise, {
   success: "Wallet created successfully",
-  error: "Failed to create wallet",
+  error: (err) =>
+    typeof err === "object" && err !== null && "message" in err
+      ? (err as { message: string }).message
+      : "Failed to create wallet",
 });
apps/dashboard/src/components/pay/RouteDiscovery.tsx (1)

63-75: Handle & surface API error details

The failure toast always shows the same static message. If the API returns a meaningful error (e.g. 409 duplicate, 400 bad address), exposing it will shorten support cycles.

- onError: () => {
-   toast.error("Token submission failed!", {
-     description:
-       "Please double check the network and token address. If issues persist, please reach out to our support team.",
-   });
- },
+ onError: (err: unknown) => {
+   const description =
+     typeof err === "object" && err && "message" in err
+       ? (err as { message: string }).message
+       : "Please double-check the network and token address. If issues persist, contact support.";
+   toast.error("Token submission failed!", { description });
+ },
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/airdrop-tab.tsx (1)

86-92: Consolidate error reporting

airdropNotifications.onError(err) is already invoked in the outer catch. The additional console.error(error); inside the mutation callback duplicates logging and may be noisy.

- onError: (error) => {
-   console.error(error);
- },
+ onError: () => {
+   /* rely on airdropNotifications.onError in the enclosing catch */
+ },
apps/dashboard/src/@/components/blocks/UpsellBannerCard.tsx (1)

106-113: Add rel="noopener noreferrer" when target="_blank" & prefer plain <a> for external links

next/link renders an <a> element inside the React tree.
You can safely pass target, but omitting rel opens a small security hole (tab-nabbing).
Also, when target="_blank" is used the link is external by definition, so the asChild pattern could simply render a native <a> and skip next/link altogether.

-<Link
-  href={props.cta.link}
-  target={props.cta.target}
-  className="flex items-center"
->
+<Link
+  href={props.cta.link}
+  target={props.cta.target}
+  rel={props.cta.target === "_blank" ? "noopener noreferrer" : undefined}
+  className="flex items-center"
+>
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-form.tsx (1)

425-435: Redundant no-op onError handler

The auction path passes

onError: () => {
  // No error handling needed for auction
},

An empty handler is equivalent to omitting the option and unnecessarily clutters the call-site. Simply remove onError – the surrounding try/catch already handles failures.

apps/dashboard/src/components/cmd-k-search/index.tsx (1)

61-63: queryClient instance now unused

After removing the argument above, the const queryClient = useQueryClient(); (line 61) is no longer referenced – delete to avoid the unused-var lint error.

apps/dashboard/src/app/(app)/login/onboarding/LinkWalletPrompt/LinkWalletPrompt.tsx (2)

61-67: External link missing rel="noopener noreferrer"

Opening external targets in a new tab (target="_blank") without a rel attribute exposes the page to window.opener attacks. Add the recommended attributes:

-<Link … target="_blank">
+<Link … target="_blank" rel="noopener noreferrer">

[security]


26-35: Consider capturing accountAddress in analytics payload

reportAccountWalletLinkRequested only sends { email }.
Adding accountAddress (already available via props.accountAddress) would give Product richer attribution and eliminate the need to correlate data later.

apps/dashboard/src/@/analytics/track/README.md (2)

32-38: Specify language for fenced block (markdown-lint MD040)

Add a language identifier so tooling renders the directory tree properly:

-```
+```text
 track/               # ← you are here
 ├─ __internal.ts     # low-level wrapper around posthog.capture (do NOT use)
 …

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

32-32: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

</details>

</details>

---

`102-106`: **Minor grammar – missing comma before coordinating conjunction**

> *Avoid calling PostHog directly** – always go through …*

Insert a comma before “so” to satisfy LanguageTool COMMA_COMPOUND_SENTENCE_2 rule:

```diff
-so we keep a single choke-point. 
+, so we keep a single choke-point. 
apps/dashboard/src/app/(app)/login/onboarding/VerifyEmail/VerifyEmail.tsx (1)

59-64: Hard-coded action strings invite typos

trackingAction is compared to literal strings "confirmEmail" / "confirmLinkWallet".
Define a union/enum instead to gain compile-time safety:

type TrackingAction = "confirmEmail" | "confirmLinkWallet";

trackingAction: TrackingAction;

Reduces the risk of silent analytics drops due to misspelling.

apps/dashboard/src/@/analytics/track/token.ts (2)

24-30: Semantic nit – alias name misleads

TokenMintedPayload is reused for reportTokenBurned. Consider renaming to something neutral such as BaseTokenPayload to improve readability and avoid future confusion.


32-37: DRY opportunity

TokenTransferredSchema duplicates the address, chainId, quantity fields already defined in BaseTokenSchema. You can compose the schemas instead:

-const TokenTransferredSchema = z.object({
-  address: EVMAddressSchema,
-  chainId: EVMChainIdSchema,
-  quantity: TokenAmountSchema,
-  to: EVMAddressSchema,
-});
+const TokenTransferredSchema = BaseTokenSchema.extend({
+  to: EVMAddressSchema,
+});

Keeps one source of truth and reduces maintenance overhead.

apps/dashboard/src/@/analytics/track/onboarding.ts (1)

50-55: Consider enum/union for planSKU

planSKU is passed as a raw string in multiple places. Define a union type or enum ("growth" | "scale" | "pro") once and reuse it to avoid typos and ease refactors.

apps/dashboard/src/app/(app)/login/onboarding/team-onboarding/InviteTeamMembers.tsx (2)

83-88: Analytics event should be emitted only on successful invite

If props.inviteTeamMembers throws, the event is silently skipped – fine – but any retry logic will record duplicates. Wrap with try/catch so failures are explicit and optionally track attempts vs successes.

inviteTeamMembers={async (params) => {
-  const res = await props.inviteTeamMembers(params);
-  reportTeamInviteMembersSent({ inviteCount: params.length });
-  return res;
+  try {
+    const res = await props.inviteTeamMembers(params);
+    reportTeamInviteMembersSent({ inviteCount: params.length });
+    return res;
+  } catch (err) {
+    // Optionally: __internal__reportEvent('team invite members failed', …);
+    throw err;
+  }
}}

159-160: Duplicate literals – extract planSKU constants

"growth", "scale", "pro" are repeated inline. Export them from @/analytics/track (or a shared pricing.ts) to prevent drift between UI & analytics.

Also applies to: 177-178, 194-195

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/components/primary-sale.tsx (2)

46-46: Remove stray diff-only line

The sole ~-annotated line is now an empty line that serves no purpose and triggers most linters’ “no-trailing-whitespace / no-useless-blank-line” rules. Delete it to keep the file clean.

- 
+

91-93: Prefer centralised error reporting over console.error

console.error(error) is fine for local debugging, but for production we’ll lose visibility (users rarely share console dumps).
Consider funnelling the error into the new analytics / telemetry layer (or Sentry, LogRocket, etc.) so failures are observable in-flight.

-              console.error(error);
+              // Forward to observability pipeline
+              reportError("primary_sale_update_failed", error);
                onError(error);

If no reporting helper exists yet, wrap one in the new analytics client for consistency.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/components/delegate-button.tsx (1)

47-48: Send errors to telemetry rather than just the console

Same observation as in primary-sale.tsx: console.error is ephemeral.
Pipe the exception into whatever central logging / analytics mechanism replaces PostHog to retain visibility.

-                console.error(error);
+                reportError("delegate_tokens_failed", error);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd4f354 and 3ee08e2.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (169)
  • apps/dashboard/.env.example (1 hunks)
  • apps/dashboard/.eslintrc.js (2 hunks)
  • apps/dashboard/instrumentation-client.ts (1 hunks)
  • apps/dashboard/knip.json (1 hunks)
  • apps/dashboard/next.config.ts (2 hunks)
  • apps/dashboard/package.json (1 hunks)
  • apps/dashboard/src/@/analytics/dashboard.client.tsx (1 hunks)
  • apps/dashboard/src/@/analytics/track.ts (1 hunks)
  • apps/dashboard/src/@/analytics/track/README.md (1 hunks)
  • apps/dashboard/src/@/analytics/track/__internal.ts (1 hunks)
  • apps/dashboard/src/@/analytics/track/contract.ts (1 hunks)
  • apps/dashboard/src/@/analytics/track/marketplace.ts (1 hunks)
  • apps/dashboard/src/@/analytics/track/nft.ts (1 hunks)
  • apps/dashboard/src/@/analytics/track/onboarding.ts (1 hunks)
  • apps/dashboard/src/@/analytics/track/schemas.ts (1 hunks)
  • apps/dashboard/src/@/analytics/track/token.ts (1 hunks)
  • apps/dashboard/src/@/components/blocks/UpsellBannerCard.tsx (2 hunks)
  • apps/dashboard/src/@/components/blocks/pricing-card.tsx (0 hunks)
  • apps/dashboard/src/@/components/ui/NavLink.tsx (1 hunks)
  • apps/dashboard/src/@/components/ui/tracked-link.tsx (2 hunks)
  • apps/dashboard/src/@3rdweb-sdk/react/components/connect-wallet/index.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/FaucetButton.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/NextSteps.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/PayModal.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/cancel-tab.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-form.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/reset-claim-eligibility.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/primary-dashboard-button.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/airdrop-tab.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/burn-tab.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/claim-tab.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/mint-supply-tab.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/transfer-tab.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/update-metadata-form.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/batch-lazy-mint-button.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/claim-button.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/lazy-mint-form.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/mint-form.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/reveal-button.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-form.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/Analytics.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/permissions/components/index.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/components/delegate-button.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/components/proposal-button.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/components/metadata.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/components/platform-fees.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/components/primary-sale.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/components/royalties.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/components/distribute-button.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/airdrop-form.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/burn-button.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/claim-button.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/mint-button.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/transfer-button.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/profile/[addressOrEns]/components/PublishedContractTable.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/account/components/AccountHeader.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/account/contracts/DeployedContractsPageHeader.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/account/settings/AccountSettingsPage.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/components/TeamPlanBadge.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/get-started/team/[team_slug]/select-plan/_components/plan-selector.tsx (6 hunks)
  • apps/dashboard/src/app/(app)/layout.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/login/onboarding/LinkWalletPrompt/LinkWalletPrompt.stories.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/login/onboarding/LinkWalletPrompt/LinkWalletPrompt.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/login/onboarding/LoginOrSignup/LoginOrSignup.stories.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/login/onboarding/LoginOrSignup/LoginOrSignup.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/login/onboarding/VerifyEmail/VerifyEmail.stories.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/login/onboarding/VerifyEmail/VerifyEmail.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/login/onboarding/account-onboarding-ui.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/login/onboarding/account-onboarding.stories.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/login/onboarding/account-onboarding.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/login/onboarding/team-onboarding/InviteTeamMembers.stories.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/login/onboarding/team-onboarding/InviteTeamMembers.tsx (6 hunks)
  • apps/dashboard/src/app/(app)/login/onboarding/team-onboarding/team-onboarding.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/_components/invite-team-members-button.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/layout.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/cards.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/_common/step-card.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/create-nft-page.tsx (4 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/launch/launch-nft.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/upload-nfts.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/token/create-token-page-impl.tsx (4 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/token/launch/launch-token.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/connect/in-app-wallets/settings/page.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/analytics/send-test-tx.client.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/server-wallets/components/create-server-wallet.client.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/components/create-vault-account.client.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/components/list-access-tokens.client.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/components/rotate-admin-key.client.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(general)/overview/engine-instances-table.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/access-tokens/components/access-tokens-table.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/access-tokens/components/add-access-token-button.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/access-tokens/components/add-keypair-button.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/access-tokens/components/keypairs-table.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/admins/components/add-admin-button.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/admins/components/admins-table.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/configuration/components/circle-config.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/configuration/components/kms-aws-config.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/configuration/components/kms-gcp-config.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/contract-subscriptions/components/add-contract-subscription-button.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/contract-subscriptions/components/contract-subscriptions-table.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/overview/components/backend-wallets-table.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/overview/components/create-backend-wallet-button.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/overview/components/import-backend-wallet-button.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/relayers/components/add-relayer-button.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/relayers/components/relayers-table.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/wallet-credentials/components/create-wallet-credential-button.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/wallet-credentials/components/edit-wallet-credential-button.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/webhooks/components/add-webhook-button.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/webhooks/components/webhooks-table.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/ProjectGeneralSettingsPage.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/layout.tsx (0 hunks)
  • apps/dashboard/src/app/(app)/team/components/TeamHeader/team-header-logged-in.client.tsx (3 hunks)
  • apps/dashboard/src/app/bridge/components/client/Providers.client.tsx (1 hunks)
  • apps/dashboard/src/app/nebula-app/(app)/components/CustomChat/CustomChatButton.tsx (0 hunks)
  • apps/dashboard/src/app/nebula-app/(app)/components/CustomChat/CustomChatContent.tsx (2 hunks)
  • apps/dashboard/src/app/nebula-app/(app)/components/FloatingChat/FloatingChat.tsx (0 hunks)
  • apps/dashboard/src/app/nebula-app/(app)/components/FloatingChat/FloatingChatContent.tsx (0 hunks)
  • apps/dashboard/src/app/nebula-app/(app)/components/Swap/common.tsx (1 hunks)
  • apps/dashboard/src/app/nebula-app/layout.tsx (1 hunks)
  • apps/dashboard/src/app/pay/components/client/Providers.client.tsx (1 hunks)
  • apps/dashboard/src/components/buttons/MismatchButton.tsx (0 hunks)
  • apps/dashboard/src/components/cmd-k-search/index.tsx (4 hunks)
  • apps/dashboard/src/components/configure-networks/ConfigureNetworks.tsx (0 hunks)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx (3 hunks)
  • apps/dashboard/src/components/contract-components/contract-publish-form/index.tsx (0 hunks)
  • apps/dashboard/src/components/contract-components/publisher/publisher-header.tsx (1 hunks)
  • apps/dashboard/src/components/embedded-wallets/Configure/InAppWalletSettingsUI.stories.tsx (0 hunks)
  • apps/dashboard/src/components/embedded-wallets/Configure/index.tsx (4 hunks)
  • apps/dashboard/src/components/notices/AnnouncementBanner.tsx (2 hunks)
  • apps/dashboard/src/components/onboarding/ApplyForOpCreditsForm.tsx (0 hunks)
  • apps/dashboard/src/components/onboarding/ApplyForOpCreditsModal.tsx (0 hunks)
  • apps/dashboard/src/components/pay/PayConfig.tsx (0 hunks)
  • apps/dashboard/src/components/pay/RouteDiscovery.tsx (1 hunks)
  • apps/dashboard/src/components/settings/Account/Billing/CreditsItem.tsx (0 hunks)
  • apps/dashboard/src/components/settings/Account/Notifications.tsx (1 hunks)
  • apps/dashboard/src/components/settings/ApiKeys/Create/index.tsx (1 hunks)
  • apps/dashboard/src/components/settings/AuthorizedWallets/AuthorizedWalletsTable.tsx (0 hunks)
  • apps/dashboard/src/components/smart-wallets/SponsorshipPolicies/index.tsx (0 hunks)
  • apps/dashboard/src/components/wallets/PosthogIdentifier.tsx (0 hunks)
  • apps/dashboard/src/components/wallets/PosthogIdentifierServer.tsx (0 hunks)
  • apps/dashboard/src/hooks/analytics/useTrack.ts (0 hunks)
  • apps/dashboard/src/lib/posthog/Posthog.tsx (0 hunks)
  • apps/dashboard/src/lib/posthog/PosthogHeadSetup.tsx (0 hunks)
  • apps/dashboard/src/lib/posthog/PosthogPageView.tsx (0 hunks)
  • apps/dashboard/src/tw-components/button.tsx (2 hunks)
  • apps/dashboard/src/tw-components/link.tsx (2 hunks)
  • apps/dashboard/src/utils/errorParser.tsx (0 hunks)
  • apps/playground-web/instrumentation-client.ts (1 hunks)
  • apps/playground-web/knip.json (1 hunks)
  • apps/playground-web/next.config.mjs (1 hunks)
  • apps/playground-web/package.json (1 hunks)
  • apps/playground-web/src/app/layout.tsx (1 hunks)
  • apps/playground-web/src/lib/posthog/Posthog.tsx (0 hunks)
  • apps/playground-web/src/lib/posthog/PosthogHeadSetup.tsx (0 hunks)
  • apps/playground-web/src/lib/posthog/PosthogPageView.tsx (0 hunks)
  • apps/portal/instrumentation-client.ts (1 hunks)
  • apps/portal/knip.json (1 hunks)
  • apps/portal/next.config.mjs (1 hunks)
  • apps/portal/package.json (1 hunks)
  • apps/portal/src/app/layout.tsx (1 hunks)
  • apps/portal/src/components/AI/chat.tsx (1 hunks)
  • apps/portal/src/components/others/Feedback.tsx (1 hunks)
  • apps/portal/src/lib/env.ts (0 hunks)
  • apps/portal/src/lib/posthog/Posthog.tsx (0 hunks)
  • apps/portal/src/lib/posthog/PosthogHeadSetup.tsx (0 hunks)
  • apps/portal/src/lib/posthog/PosthogPageView.tsx (0 hunks)
💤 Files with no reviewable changes (65)
  • apps/dashboard/src/app/(app)/login/onboarding/account-onboarding.stories.tsx
  • apps/dashboard/src/app/(app)/login/onboarding/LoginOrSignup/LoginOrSignup.stories.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/layout.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/connect/in-app-wallets/settings/page.tsx
  • apps/dashboard/src/components/embedded-wallets/Configure/InAppWalletSettingsUI.stories.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/components/rotate-admin-key.client.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/reset-claim-eligibility.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/analytics/send-test-tx.client.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/components/proposal-button.tsx
  • apps/dashboard/src/app/(app)/login/onboarding/team-onboarding/InviteTeamMembers.stories.tsx
  • apps/dashboard/src/app/(app)/login/onboarding/VerifyEmail/VerifyEmail.stories.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(general)/overview/engine-instances-table.tsx
  • apps/dashboard/src/app/nebula-app/(app)/components/CustomChat/CustomChatButton.tsx
  • apps/dashboard/src/app/(app)/login/onboarding/LinkWalletPrompt/LinkWalletPrompt.stories.tsx
  • apps/dashboard/src/app/(app)/components/TeamPlanBadge.tsx
  • apps/dashboard/src/components/settings/Account/Billing/CreditsItem.tsx
  • apps/dashboard/src/components/contract-components/contract-publish-form/index.tsx
  • apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
  • apps/dashboard/src/components/smart-wallets/SponsorshipPolicies/index.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/launch/launch-nft.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/components/create-vault-account.client.tsx
  • apps/dashboard/src/components/wallets/PosthogIdentifierServer.tsx
  • apps/dashboard/src/utils/errorParser.tsx
  • apps/portal/src/lib/env.ts
  • apps/playground-web/src/lib/posthog/PosthogPageView.tsx
  • apps/dashboard/src/app/nebula-app/(app)/components/FloatingChat/FloatingChat.tsx
  • apps/dashboard/src/components/configure-networks/ConfigureNetworks.tsx
  • apps/dashboard/src/app/(app)/account/contracts/DeployedContractsPageHeader.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/update-metadata-form.tsx
  • apps/dashboard/src/@/components/blocks/pricing-card.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/_common/step-card.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/Analytics.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/token/launch/launch-token.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/claim-tab.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/reveal-button.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/access-tokens/components/keypairs-table.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/configuration/components/kms-gcp-config.tsx
  • apps/dashboard/src/components/settings/AuthorizedWallets/AuthorizedWalletsTable.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/configuration/components/circle-config.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/access-tokens/components/access-tokens-table.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/server-wallets/components/create-server-wallet.client.tsx
  • apps/dashboard/src/components/onboarding/ApplyForOpCreditsForm.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/primary-dashboard-button.tsx
  • apps/dashboard/src/lib/posthog/PosthogHeadSetup.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/admins/components/admins-table.tsx
  • apps/dashboard/src/components/pay/PayConfig.tsx
  • apps/portal/src/lib/posthog/Posthog.tsx
  • apps/dashboard/src/app/nebula-app/(app)/components/FloatingChat/FloatingChatContent.tsx
  • apps/playground-web/src/lib/posthog/PosthogHeadSetup.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/configuration/components/kms-aws-config.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/components/list-access-tokens.client.tsx
  • apps/portal/src/lib/posthog/PosthogHeadSetup.tsx
  • apps/dashboard/src/components/buttons/MismatchButton.tsx
  • apps/portal/src/lib/posthog/PosthogPageView.tsx
  • apps/dashboard/src/components/onboarding/ApplyForOpCreditsModal.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/contract-subscriptions/components/contract-subscriptions-table.tsx
  • apps/dashboard/src/app/(app)/login/onboarding/account-onboarding-ui.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/airdrop-form.tsx
  • apps/dashboard/src/components/wallets/PosthogIdentifier.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/wallet-credentials/components/edit-wallet-credential-button.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/contract-subscriptions/components/add-contract-subscription-button.tsx
  • apps/playground-web/src/lib/posthog/Posthog.tsx
  • apps/dashboard/src/lib/posthog/Posthog.tsx
  • apps/dashboard/src/lib/posthog/PosthogPageView.tsx
  • apps/dashboard/src/hooks/analytics/useTrack.ts
✅ Files skipped from review due to trivial changes (19)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/batch-lazy-mint-button.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/NextSteps.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/access-tokens/components/add-access-token-button.tsx
  • apps/dashboard/src/app/nebula-app/(app)/components/CustomChat/CustomChatContent.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/overview/components/backend-wallets-table.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/lazy-mint-form.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/admins/components/add-admin-button.tsx
  • apps/dashboard/src/app/(app)/login/onboarding/team-onboarding/team-onboarding.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/claim-button.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx
  • apps/dashboard/src/components/settings/ApiKeys/Create/index.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-form.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/access-tokens/components/add-keypair-button.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/upload-nfts.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.tsx
  • apps/dashboard/src/components/embedded-wallets/Configure/index.tsx
  • apps/dashboard/src/components/contract-components/publisher/publisher-header.tsx
  • apps/dashboard/src/app/nebula-app/(app)/components/Swap/common.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/create-nft-page.tsx
🚧 Files skipped from review as they are similar to previous changes (32)
  • apps/dashboard/knip.json
  • apps/portal/knip.json
  • apps/portal/src/components/others/Feedback.tsx
  • apps/dashboard/src/app/pay/components/client/Providers.client.tsx
  • apps/dashboard/src/app/(app)/login/onboarding/account-onboarding.tsx
  • apps/playground-web/knip.json
  • apps/playground-web/package.json
  • apps/dashboard/src/app/(app)/account/settings/AccountSettingsPage.tsx
  • apps/dashboard/src/app/bridge/components/client/Providers.client.tsx
  • apps/portal/package.json
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/layout.tsx
  • apps/dashboard/package.json
  • apps/dashboard/src/app/(app)/account/components/AccountHeader.tsx
  • apps/dashboard/.eslintrc.js
  • apps/dashboard/src/app/nebula-app/layout.tsx
  • apps/dashboard/src/@/analytics/track/__internal.ts
  • apps/playground-web/instrumentation-client.ts
  • apps/dashboard/instrumentation-client.ts
  • apps/dashboard/src/@/analytics/dashboard.client.tsx
  • apps/dashboard/src/app/(app)/layout.tsx
  • apps/playground-web/src/app/layout.tsx
  • apps/dashboard/src/app/(app)/team/components/TeamHeader/team-header-logged-in.client.tsx
  • apps/dashboard/next.config.ts
  • apps/dashboard/src/@3rdweb-sdk/react/components/connect-wallet/index.tsx
  • apps/dashboard/src/@/analytics/track/contract.ts
  • apps/portal/instrumentation-client.ts
  • apps/dashboard/src/@/analytics/track.ts
  • apps/portal/src/app/layout.tsx
  • apps/portal/next.config.mjs
  • apps/portal/src/components/AI/chat.tsx
  • apps/playground-web/next.config.mjs
  • apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx
🧰 Additional context used
🧬 Code Graph Analysis (15)
apps/dashboard/src/app/(app)/get-started/team/[team_slug]/select-plan/_components/plan-selector.tsx (1)
apps/dashboard/src/@/analytics/track/onboarding.ts (2)
  • reportPlanSelected (50-52)
  • reportPlanSelectSkipped (54-56)
apps/dashboard/src/components/notices/AnnouncementBanner.tsx (1)
apps/dashboard/src/tw-components/link.tsx (1)
  • Link (33-56)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/mint-button.tsx (1)
apps/dashboard/src/@/analytics/track/token.ts (1)
  • reportTokenMinted (24-26)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/transfer-button.tsx (1)
apps/dashboard/src/@/analytics/track/token.ts (1)
  • reportTokenTransferred (41-46)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/burn-button.tsx (1)
apps/dashboard/src/@/analytics/track/token.ts (1)
  • reportTokenBurned (28-30)
apps/dashboard/src/@/analytics/track/nft.ts (2)
apps/dashboard/src/@/analytics/track/schemas.ts (2)
  • EVMAddressSchema (9-13)
  • EVMChainIdSchema (16-20)
apps/dashboard/src/@/analytics/track/__internal.ts (1)
  • __internal__reportEvent (12-23)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/mint-form.tsx (1)
apps/dashboard/src/@/analytics/track/nft.ts (1)
  • reportNFTMinted (16-23)
apps/dashboard/src/app/(app)/login/onboarding/team-onboarding/InviteTeamMembers.tsx (1)
apps/dashboard/src/@/analytics/track/onboarding.ts (3)
  • reportTeamInviteMembersSent (36-40)
  • reportTeamInviteMembersSkipped (42-44)
  • reportPlanSelected (50-52)
apps/dashboard/src/@/components/blocks/UpsellBannerCard.tsx (1)
apps/dashboard/src/tw-components/link.tsx (1)
  • Link (33-56)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-form.tsx (1)
apps/dashboard/src/@/analytics/track/marketplace.ts (1)
  • reportListingCreated (18-23)
apps/dashboard/src/app/(app)/login/onboarding/LoginOrSignup/LoginOrSignup.tsx (1)
apps/dashboard/src/@/analytics/track/onboarding.ts (1)
  • reportAccountEmailVerificationRequested (7-13)
apps/dashboard/src/app/(app)/login/onboarding/VerifyEmail/VerifyEmail.tsx (1)
apps/dashboard/src/@/analytics/track/onboarding.ts (2)
  • reportAccountEmailVerified (15-20)
  • reportAccountWalletLinked (28-30)
apps/dashboard/src/@/analytics/track/schemas.ts (1)
packages/thirdweb/src/exports/thirdweb.ts (1)
  • isAddress (308-308)
apps/dashboard/src/@/analytics/track/token.ts (2)
apps/dashboard/src/@/analytics/track/schemas.ts (2)
  • EVMAddressSchema (9-13)
  • EVMChainIdSchema (16-20)
apps/dashboard/src/@/analytics/track/__internal.ts (1)
  • __internal__reportEvent (12-23)
apps/dashboard/src/@/analytics/track/onboarding.ts (1)
apps/dashboard/src/@/analytics/track/__internal.ts (1)
  • __internal__reportEvent (12-23)
🪛 dotenv-linter (3.3.0)
apps/dashboard/.env.example

[warning] 107-107: [QuoteCharacter] The value has quote characters (', ")


[warning] 107-107: [UnorderedKey] The NEXT_PUBLIC_ENGINE_CLOUD_URL key should go before the NEXT_PUBLIC_THIRDWEB_VAULT_URL key


[warning] 110-110: [QuoteCharacter] The value has quote characters (', ")


[warning] 111-111: [UnorderedKey] The NEXT_PUBLIC_POSTHOG_HOST key should go before the NEXT_PUBLIC_POSTHOG_KEY key

🪛 LanguageTool
apps/dashboard/src/@/analytics/track/README.md

[uncategorized] ~85-~85: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...(one directory up) and add a star-export so the helpers are surfaced at the pack...

(COMMA_COMPOUND_SENTENCE_2)

🪛 markdownlint-cli2 (0.17.2)
apps/dashboard/src/@/analytics/track/README.md

32-32: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Size
  • GitHub Check: Unit Tests
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: Build Packages
  • GitHub Check: Lint Packages
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (15)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/FaucetButton.tsx (1)

94-99: Stray blank diff line – safe to squash

The only change in this hunk is a blank line (line 97) that was marked as modified.
Unless this was intentional for readability, consider squashing it out to keep the history noise-free.

apps/dashboard/src/app/(app)/get-started/team/[team_slug]/select-plan/_components/plan-selector.tsx (2)

3-4: 👍 Moving to explicit, typed analytics calls

Importing the typed helpers from @/analytics/track keeps the event surface consistent and prevents ad-hoc strings.
Looks good.


122-124: Nit: fire-and-forget telemetry – consider awaiting?

reportPlanSelectSkipped() is presumably synchronous, but if it becomes async later the event may be lost when the navigation triggers.
Optionally make it return a promise and await it before routing.

apps/dashboard/src/app/(app)/team/[team_slug]/(team)/_components/invite-team-members-button.tsx (1)

9-14: Tracking removal looks fine – confirm no stale props remain.

All tracking code was stripped, but the component still receives plain props that were previously forwarded to analytics. Double-check no caller passes “tracking-related” props that are now unused.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/transfer-button.tsx (2)

3-4: Import path pinned to barrel – verify tree-shaking.

@/analytics/track re-exports many helpers; importing from the barrel can pull more code into the client bundle than importing token.ts directly. If bundle size matters, switch to
@/analytics/track/token.


119-124: Type consistency between analytics payload and form values.

quantity: d.amount is a string from <Input> while most on-chain helpers & zod schemas use bigint or number. If TokenTransferredSchema expects a numeric type this will throw at runtime and skip analytics.

apps/dashboard/src/app/(app)/(dashboard)/profile/[addressOrEns]/components/PublishedContractTable.tsx (1)

136-138: Dependency array update looks correct – just double-check TrackedLinkTW still emits analytics events.

trackEvent has been removed from the table column factory and from the dependency list, which is expected after the PostHog migration.
The only remaining analytics-related element inside the table is TrackedLinkTW; make sure that this component is now wired to the new analytics layer (or is a harmless no-op) so that clicking the audit badge still gets captured (if desired).

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/mint-button.tsx (1)

81-85: Quantity type – ensure it matches the schema.

d.amount is a string (because it comes straight from a text input).
If reportTokenMinted’s schema expects a number or bigint, parsing should happen before the call to avoid runtime validation errors.

-  quantity: d.amount,
+  quantity: BigInt(d.amount), // or Number(d.amount) depending on schema
apps/dashboard/src/components/settings/Account/Notifications.tsx (1)

37-43: LGTM – tracking removed cleanly

The callbacks now exclusively trigger user-visible notifications without leaking errors to analytics; this aligns with the new minimal tracking stance.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/components/metadata.tsx (1)

178-180: Error flow now only logs – consider surfacing to user or analytics

Switching from tracked errors to console.error is fine for local debugging but loses both:

  • Structured telemetry for failure rates.
  • Feedback to users in production if the console isn’t visible.

If analytics are intentionally removed, ensure UX still conveys the failure (toast, inline message).

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/burn-button.tsx (1)

3-3: Import path is fine – just ensure treeshaking

No issues; webpack/next-tree-shaking should drop unused helpers from @/analytics/track.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/PayModal.tsx (1)

38-43: Missed opportunity to capture purchase analytics

Purchases (especially successful ones) are core business metrics.
Consider emitting a dedicated event (e.g. reportPayPurchase) in both the crypto & fiat paths rather than dropping tracking entirely. This keeps the refactor consistent with the rest of the new type-safe analytics helpers.

No action required if omission is deliberate.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/cancel-tab.tsx (1)

34-40: Consider awaiting mutateAsync to propagate failures

By not awaiting cancelQuery.mutateAsync(transaction) the returned promise is only consumed by toast.promise.
If the mutation rejects before toast.promise attaches (edge-race) or another part of the call-chain relies on the returned promise, the error will be lost.

-const promise = cancelQuery.mutateAsync(transaction);
+const promise = await cancelQuery.mutateAsync(transaction);

Wrap the onClick handler in async () => { … } to keep behaviour unchanged while guaranteeing proper error surfacing.

apps/dashboard/src/app/(app)/login/onboarding/LoginOrSignup/LoginOrSignup.tsx (1)

45-52: 👍 Good swap to typed event reporter

Replacing the generic tracking prop with reportAccountEmailVerificationRequested makes the side-effect explicit and type-safe. No further concerns here.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/token/create-token-page-impl.tsx (1)

62-100: Async chain is correct but contractAddressRef may stay undefined on failure

If deployERC20Contract rejects, contractAddressRef.current remains undefined yet subsequent steps (airdropTokens, mintTokens, setClaimConditions) may still be invoked from the UI, causing opaque “No contract address” errors.

Consider short-circuiting the wizard upon deployment failure or only enabling following steps after a successful deploy.

* NFT-related analytics helpers (ERC721 & ERC1155 non-fungible).
*/

import { z } from "zod/v4-mini";
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

Unpublished import path will break compilation

"zod/v4-mini" is not part of the public Zod distribution (current major is v3).
Importing through this path will throw Cannot find module at build time unless a custom alias is configured.
Use the canonical package entry instead.

-import { z } from "zod/v4-mini";
+import { z } from "zod";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { z } from "zod/v4-mini";
// apps/dashboard/src/@/analytics/track/nft.ts
-import { z } from "zod/v4-mini";
+import { z } from "zod";
🤖 Prompt for AI Agents
In apps/dashboard/src/@/analytics/track/nft.ts at line 5, the import path
"zod/v4-mini" is invalid and will cause build errors because it is not part of
the public Zod package. Replace this import with the standard public entry "zod"
to ensure the module is found and the code compiles correctly.

Comment on lines +120 to 124
reportNFTMinted({
address: contract.address,
chainId: contract.chain.id,
tokenId: undefined,
});
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

Pass back the minted tokenId when available.

tokenId is currently reported as undefined.
erc721MintTo / erc1155MintTo both return the minted tokenId in their receipt (or it can be extracted from logs).
Populating this field increases the value of the telemetry and prevents nullable schema columns downstream.

-  tokenId: undefined,
+  tokenId: mintedTokenId, // derive from the tx receipt

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/mint-form.tsx
around lines 120 to 124, the reportNFTMinted call sets tokenId to undefined,
which loses valuable telemetry data. Update the code to extract the minted
tokenId from the receipt returned by erc721MintTo or erc1155MintTo (or from
their logs) and pass this tokenId to reportNFTMinted instead of undefined to
ensure accurate reporting and avoid nullable schema columns downstream.

Comment on lines +22 to +30
<Link
href={props.href}
category="announcement"
label={props.trackingLabel}
target={props.href.startsWith("http") ? "_blank" : undefined}
className="ontainer flex cursor-pointer items-center gap-2 lg:justify-center "
>
<span className="inline-block font-medium leading-normal hover:underline">
{props.label}
</span>
</TrackedLinkTW>
</Link>
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

Security + typo: add rel="noopener noreferrer" and fix className

  1. External links opened with target="_blank" should also specify
    rel="noopener noreferrer" to prevent the new page from gaining a
    reference to window.opener.
  2. className="ontainer ..." is missing the leading c (container).
-      <Link
-        href={props.href}
-        target={props.href.startsWith("http") ? "_blank" : undefined}
-        className="ontainer flex cursor-pointer items-center gap-2 lg:justify-center "
+      <Link
+        href={props.href}
+        target={props.href.startsWith("http") ? "_blank" : undefined}
+        rel={props.href.startsWith("http") ? "noopener noreferrer" : undefined}
+        className="container flex cursor-pointer items-center gap-2 lg:justify-center "
       >
📝 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
<Link
href={props.href}
category="announcement"
label={props.trackingLabel}
target={props.href.startsWith("http") ? "_blank" : undefined}
className="ontainer flex cursor-pointer items-center gap-2 lg:justify-center "
>
<span className="inline-block font-medium leading-normal hover:underline">
{props.label}
</span>
</TrackedLinkTW>
</Link>
<Link
href={props.href}
target={props.href.startsWith("http") ? "_blank" : undefined}
rel={props.href.startsWith("http") ? "noopener noreferrer" : undefined}
className="container flex cursor-pointer items-center gap-2 lg:justify-center "
>
<span className="inline-block font-medium leading-normal hover:underline">
{props.label}
</span>
</Link>
🤖 Prompt for AI Agents
In apps/dashboard/src/components/notices/AnnouncementBanner.tsx around lines 22
to 30, fix the typo in className by changing "ontainer" to "container" and add
rel="noopener noreferrer" to the Link component when target="_blank" is set to
improve security for external links.

Comment on lines +18 to +22
export function reportListingCreated(payload: ListingCreatedPayload) {
__internal__reportEvent(
"marketplace listing created",
ListingCreatedSchema.parse(payload),
);
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

Prefer safeParse to avoid throwing inside production builds

ListingCreatedSchema.parse will throw on invalid payloads, crashing the caller.
Unless callers wrap this in try/catch, switch to safeParse and early-return on failure:

-export function reportListingCreated(payload: ListingCreatedPayload) {
-  __internal__reportEvent(
-    "marketplace listing created",
-    ListingCreatedSchema.parse(payload),
-  );
+export function reportListingCreated(payload: ListingCreatedPayload) {
+  const res = ListingCreatedSchema.safeParse(payload);
+  if (!res.success) {
+    // option: report validation failure to Sentry / console
+    return;
+  }
+  __internal__reportEvent("marketplace listing created", res.data);
 }

This protects production sessions from unexpected runtime exceptions.

📝 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
export function reportListingCreated(payload: ListingCreatedPayload) {
__internal__reportEvent(
"marketplace listing created",
ListingCreatedSchema.parse(payload),
);
export function reportListingCreated(payload: ListingCreatedPayload) {
const res = ListingCreatedSchema.safeParse(payload);
if (!res.success) {
// option: report validation failure to Sentry / console
return;
}
__internal__reportEvent("marketplace listing created", res.data);
}
🤖 Prompt for AI Agents
In apps/dashboard/src/@/analytics/track/marketplace.ts around lines 18 to 22,
replace the use of ListingCreatedSchema.parse with
ListingCreatedSchema.safeParse to avoid throwing exceptions on invalid payloads.
Use safeParse to validate the payload, check if the parsing was successful, and
only call __internal__reportEvent if it was. If safeParse fails, return early to
prevent runtime exceptions in production.

Comment on lines +14 to 16
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const trackEvent = (..._args: unknown[]) => {};
const { category, label, trackingProps, ...restProps } = props;
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

Stubbed trackEvent is dead code – delete or re-wire

The no-op preserves the old API but:

  1. Creates false expectation that link clicks are tracked.
  2. Forces ESLint disable comments and an unused prop (category, label, trackingProps).

Unless re-connecting to the new analytics layer soon, remove the tracking facade and related props to keep the component lean.

-export type TrackedLinkProps = React.ComponentProps<typeof Link> & {
-  category: string;
-  label?: string;
-  trackingProps?: Record<string, string>;
-};
+export type TrackedLinkProps = React.ComponentProps<typeof Link>;
...
-  onClick={(e) => {
-    trackEvent({ category, action: "click", label, ...trackingProps });
-    props.onClick?.(e);
-  }}
+  onClick={props.onClick}

Also applies to: 30-32

🤖 Prompt for AI Agents
In apps/dashboard/src/@/components/ui/tracked-link.tsx around lines 14 to 16 and
30 to 32, the trackEvent function is a no-op stub causing dead code and unused
props (category, label, trackingProps) with ESLint disables. Remove the
trackEvent function entirely along with the related props from the component's
props destructuring and usage to eliminate dead code and unused variables,
simplifying the component and removing the need for ESLint disable comments.

Comment on lines +1 to +20
import { isAddress } from "thirdweb";
import { z } from "zod/v4-mini";

// ---------------------------
// Shared Zod Schemas
// ---------------------------

/** Validates an EVM address (0x…) using thirdweb's `isAddress`. */
export const EVMAddressSchema = z.string().check(
z.refine(isAddress, {
message: "Invalid EVM address",
}),
);

/** Coerces the chain ID into a number and checks it is positive. */
export const EVMChainIdSchema = z.coerce.number().check(
z.gte(1, {
message: "Invalid chain ID",
}),
);
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

Invalid Zod API usage – code will not type-check nor run

Problems:

  1. z.string().check(...)check is not a Zod method (v3 & current v4 betas).
  2. z.refine is a method, not a stand-alone function – passing it as value will throw.
  3. z.gte does not exist; gte is a method on number schemas.

Fix:

-import { isAddress } from "thirdweb";
-import { z } from "zod/v4-mini";
+import { isAddress } from "thirdweb";
+import { z } from "zod";

 export const EVMAddressSchema = z
   .string()
-  .check(
-    z.refine(isAddress, {
-      message: "Invalid EVM address",
-    }),
-  );
+  .refine(isAddress, { message: "Invalid EVM address" });

 export const EVMChainIdSchema = z
   .coerce.number()
-  .check(
-    z.gte(1, {
-      message: "Invalid chain ID",
-    }),
-  );
+  .gte(1, { message: "Invalid chain ID" });

Please run tsc / your linter – this currently fails compilation.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { isAddress } from "thirdweb";
import { z } from "zod/v4-mini";
// ---------------------------
// Shared Zod Schemas
// ---------------------------
/** Validates an EVM address (0x…) using thirdweb's `isAddress`. */
export const EVMAddressSchema = z.string().check(
z.refine(isAddress, {
message: "Invalid EVM address",
}),
);
/** Coerces the chain ID into a number and checks it is positive. */
export const EVMChainIdSchema = z.coerce.number().check(
z.gte(1, {
message: "Invalid chain ID",
}),
);
import { isAddress } from "thirdweb";
import { z } from "zod";
// ---------------------------
// Shared Zod Schemas
// ---------------------------
/** Validates an EVM address (0x…) using thirdweb's `isAddress`. */
export const EVMAddressSchema = z
.string()
.refine(isAddress, { message: "Invalid EVM address" });
/** Coerces the chain ID into a number and checks it is positive. */
export const EVMChainIdSchema = z
.coerce.number()
.gte(1, { message: "Invalid chain ID" });
🤖 Prompt for AI Agents
In apps/dashboard/src/@/analytics/track/schemas.ts lines 1 to 20, the Zod schema
usage is incorrect: replace the non-existent .check method with .refine for
custom validation, call .refine as a method on the schema instead of passing it
as a standalone function, and replace the invalid z.gte call with the .gte
method on the number schema. Specifically, use z.string().refine(isAddress,
{message: "Invalid EVM address"}) for the address schema and
z.coerce.number().gte(1, {message: "Invalid chain ID"}) for the chain ID schema
to fix the type errors and make the code compile.

Comment on lines 366 to 377
await sendAndConfirmTx.mutateAsync(transaction, {
onSuccess: () => setOpen(false),
onSuccess: () => {
reportListingCreated({
marketplaceAddress: contract.address,
chainId: contract.chain.id,
assetAddress: _selectedContract.address,
assetTokenId: selectedTokenId.toString(),
});
setOpen(false);
},
});

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

Duplicate success side-effects fire twice

sendAndConfirmTx.mutateAsync() already receives an onSuccess callback (lines 367-375).
Immediately after the promise resolves, listingNotifications.onSuccess() is executed again (line 378).
For a successful tx this means two identical success toasts / side-effects are triggered.

 await sendAndConfirmTx.mutateAsync(transaction, {
   onSuccess: () => {
     …                               
   },
 });
-// fires a 2nd time
-listingNotifications.onSuccess();

Drop the outer call or move notification logic exclusively into the inner handler to avoid double execution.
Same pattern exists in the auction branch below (lines 423-438).

📝 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
await sendAndConfirmTx.mutateAsync(transaction, {
onSuccess: () => setOpen(false),
onSuccess: () => {
reportListingCreated({
marketplaceAddress: contract.address,
chainId: contract.chain.id,
assetAddress: _selectedContract.address,
assetTokenId: selectedTokenId.toString(),
});
setOpen(false);
},
});
await sendAndConfirmTx.mutateAsync(transaction, {
onSuccess: () => {
reportListingCreated({
marketplaceAddress: contract.address,
chainId: contract.chain.id,
assetAddress: _selectedContract.address,
assetTokenId: selectedTokenId.toString(),
});
setOpen(false);
},
});
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-form.tsx
around lines 366 to 377, the success side-effects for
sendAndConfirmTx.mutateAsync are duplicated because the onSuccess callback is
defined both inside mutateAsync and immediately after awaiting it. To fix this,
remove the outer call to listingNotifications.onSuccess() after the await and
keep the notification logic only inside the onSuccess callback passed to
mutateAsync. Apply the same fix to the auction branch around lines 423 to 438 to
prevent double execution of success side-effects.

Comment on lines +9 to +14
// Quantity represented as a decimal string so we don't lose precision.
const TokenAmountSchema = z.string().check(
z.refine((val) => /^\d+$/.test(val), {
message: "Quantity must be a positive integer represented as a string",
}),
);
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

Suspicious z.check(z.refine()) usage – likely wrong Zod API call

z.string().check(z.refine(...)) does not match the public Zod API (including zod/v4-mini).
The usual pattern is either:

z.string().regex(/^\d+$/, { message: "…" })

or

z.string().refine(val => /^\d+$/.test(val), { message: "…" })

Passing the result of z.refine() into .check() will return a ZodEffects object, not the boolean-returning predicate that .check() expects, and will throw at runtime.

Proposed fix:

-const TokenAmountSchema = z.string().check(
-  z.refine((val) => /^\d+$/.test(val), {
-    message: "Quantity must be a positive integer represented as a string",
-  }),
-);
+const TokenAmountSchema = z.string().regex(/^\d+$/, {
+  message: "Quantity must be a non-negative integer represented as a string",
+});
🤖 Prompt for AI Agents
In apps/dashboard/src/@/analytics/track/token.ts around lines 9 to 14, the code
incorrectly uses z.string().check(z.refine()), which is not a valid Zod API
pattern and causes runtime errors. Replace this with either
z.string().regex(/^\d+$/, { message: "Quantity must be a positive integer
represented as a string" }) or z.string().refine(val => /^\d+$/.test(val), {
message: "Quantity must be a positive integer represented as a string" }) to
properly validate the string format.

Comment on lines +7 to +20
export function reportAccountEmailVerificationRequested(payload: {
email: string;
}) {
__internal__reportEvent("account email verification requested", {
email: payload.email,
});
}

export function reportAccountEmailVerified(payload: { email?: string }) {
__internal__reportEvent(
"account email verified",
payload.email ? { email: payload.email } : null,
);
}
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

Raw e-mail addresses sent to PostHog – PII leak

reportAccountEmailVerificationRequested / reportAccountEmailVerified forward the plain e-mail address to the analytics backend. E-mails qualify as personal data under most privacy regulations (GDPR, CCPA, …). Unless you have explicit user consent and a signed DPA with PostHog this is risky.

Hashing the address (SHA-256 or similar) before emitting the event usually satisfies analytics/PII guidelines while still enabling user-level funnel analysis.

+import { createHash } from "crypto";
+
+function hashEmail(email: string) {
+  return createHash("sha256").update(email.trim().toLowerCase()).digest("hex");
+}

 // …

 __internal__reportEvent("account email verification requested", {
-  email: payload.email,
+  email_hash: hashEmail(payload.email),
 });

Apply the same treatment to reportAccountEmailVerified.

[security]

📝 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
export function reportAccountEmailVerificationRequested(payload: {
email: string;
}) {
__internal__reportEvent("account email verification requested", {
email: payload.email,
});
}
export function reportAccountEmailVerified(payload: { email?: string }) {
__internal__reportEvent(
"account email verified",
payload.email ? { email: payload.email } : null,
);
}
import { createHash } from "crypto";
function hashEmail(email: string) {
return createHash("sha256")
.update(email.trim().toLowerCase())
.digest("hex");
}
export function reportAccountEmailVerificationRequested(payload: {
email: string;
}) {
__internal__reportEvent("account email verification requested", {
email_hash: hashEmail(payload.email),
});
}
export function reportAccountEmailVerified(payload: { email?: string }) {
__internal__reportEvent(
"account email verified",
payload.email
? { email_hash: hashEmail(payload.email) }
: null,
);
}
🤖 Prompt for AI Agents
In apps/dashboard/src/@/analytics/track/onboarding.ts between lines 7 and 20,
the functions reportAccountEmailVerificationRequested and
reportAccountEmailVerified send raw email addresses to the analytics backend,
risking PII exposure. To fix this, hash the email addresses using a secure hash
function like SHA-256 before passing them to __internal__reportEvent in both
functions, ensuring compliance with privacy regulations while preserving
analytic capabilities.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Dashboard Involves changes to the Dashboard. DO NOT MERGE This pull request is still in progress and is not ready to be merged. Playground Changes involving the Playground codebase. Portal Involves changes to the Portal (docs) codebase.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant