Skip to content

Updated add tokens UX#1406

Open
RevTpark wants to merge 5 commits into
mainfrom
feat/permissionless-add-tokens
Open

Updated add tokens UX#1406
RevTpark wants to merge 5 commits into
mainfrom
feat/permissionless-add-tokens

Conversation

@RevTpark

@RevTpark RevTpark commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

  • simplified search list UI to mix db and jupiter search results
  • added isVerifiedOnJupiter field in TokenMetadata
  • changed add token button UI
  • added verified tokens icon and made mint address clickable

Where should the reviewer start?

How should this be manually tested?

Any background context you want to provide?

What are the relevant issues?

Screenshots (if appropriate)

Summary by CodeRabbit

  • New Features
    • Unified token search merging local matches and verified Jupiter tokens into a single ranked list for easier discovery
    • Visual verification badges and tooltip with direct link to the Jupiter token page
    • Inline Add button with loader to add Jupiter-verified tokens from search results
    • Verified status is now persisted with token metadata
    • Simplified empty-state messaging in token search dropdown

@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
earn Ready Ready Preview Jun 11, 2026 12:26pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

TokenSelect now merges local DB tokens and Jupiter-verified tokens into a single ranked searchResults list, shows Jupiter-verified badges/links, and renders Add-button wiring; schema and server persist isVerifiedOnJupiter on tokens.

Changes

Unified Token Search and Jupiter Integration

Layer / File(s) Summary
Prisma schema: add verification flag
prisma/schema.prisma
Adds isVerifiedOnJupiter: Boolean @default(true) to TokenMetadata.
Token type update
src/constants/tokenList.ts
Token interface now includes optional isVerifiedOnJupiter?: boolean.
Server tokenList persistence
src/server/tokenList.ts
Expose isVerifiedOnJupiter in exported Token, include it in Prisma selects, and set it to true in addVerifiedJupiterToken upsert update/create paths.
Imports and tooltip support
src/features/listing-builder/components/Form/Rewards/Tokens/TokenSelect.tsx
Add ShieldCheck import and Tooltip component for verified UI.
Search result types and Jupiter helpers
src/features/listing-builder/components/Form/Rewards/Tokens/TokenSelect.tsx
Introduce TokenSearchResult union and helpers for building/opening Jupiter token URLs; expand TokenSearchLabel props.
Token label rendering with verification
src/features/listing-builder/components/Form/Rewards/Tokens/TokenSelect.tsx
Show verified badge and make mint-address a tooltip-wrapped link that opens Jupiter token page (stops propagation).
Unified searchResults computation
src/features/listing-builder/components/Form/Rewards/Tokens/TokenSelect.tsx
useMemo merges local filtered tokens and verified Jupiter tokens into a shared ranked structure and sorts by rank and symbol.
Dropdown rendering, add flow, and empty state
src/features/listing-builder/components/Form/Rewards/Tokens/TokenSelect.tsx
Render one dropdown from searchResults with DB select rows or Jupiter rows with Add button and loader state; empty-state logic simplified and ReachOutMessage call simplified.

Sequence Diagram (high-level flow)

sequenceDiagram
  participant User
  participant TokenSelect
  participant TokenListAPI
  participant PrismaDB
  User->>TokenSelect: open/search tokens
  TokenSelect->>TokenListAPI: fetch local tokens + verified Jupiter tokens
  TokenListAPI->>PrismaDB: query token metadata (includes isVerifiedOnJupiter)
  PrismaDB-->>TokenListAPI: return token records
  TokenListAPI-->>TokenSelect: return combined sources
  User->>TokenSelect: click "Add" on Jupiter token
  TokenSelect->>TokenListAPI: addVerifiedJupiterToken(upsert mint)
  TokenListAPI->>PrismaDB: upsert token (set isVerifiedOnJupiter = true)
  PrismaDB-->>TokenListAPI: upsert result
  TokenListAPI-->>TokenSelect: return persisted token
  TokenSelect-->>User: show token selected/loader cleared
Loading

🎯 3 (Moderate) | ⏱️ ~22 minutes

🐰 A rabbit hops through tokens new and old,
Unifying ranks in Jupiter's gold,
With shields that sparkle and links that gleam,
One list to find them, smooth as a dream!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Updated add tokens UX' accurately reflects the main change: improving the token selection and addition experience by mixing search results, adding verification indicators, and making the mint address interactive.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/permissionless-add-tokens

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/features/listing-builder/components/Form/Rewards/Tokens/TokenSelect.tsx (2)

52-68: ⚡ Quick win

Use explicit | undefined (and readonly-by-default) in TS object types.

This file introduces optional properties via ? in type definitions. The repo guideline requires explicit property: Type | undefined to avoid accidental omission; these object fields should also be readonly unless intentionally mutable.

As per coding guidelines, “Use property: Type | undefined instead of property?: Type for TypeScript type definitions” and “Use readonly properties for object types by default in TypeScript.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/listing-builder/components/Form/Rewards/Tokens/TokenSelect.tsx`
around lines 52 - 68, The type declarations (JupiterToken, TokenSearchResponse,
AddTokenResponse) use optional properties with ?; change each optional property
to explicit unions (e.g., icon: string | null | undefined) and any optional
top-level properties to Type | undefined instead of ?. Also make object fields
readonly by default (prepend readonly to each property) unless a field is
intentionally mutable (leave those mutable and document why). Update references
to Token if it has optional fields the same way. Ensure the new readonly/|
undefined shapes compile with usages in TokenSelect.tsx and adjust any mutations
accordingly.

Source: Coding guidelines


320-323: 🏗️ Heavy lift

Avoid exception-driven flow; use a Result pattern helper for fetch paths.

Both async flows throw inside try and then handle in catch. The TS guideline asks to use a Result-style return instead of manual throw/catch flow for predictable error handling.

As per coding guidelines, “Use Result type pattern instead of throwing errors for code that requires manual try-catch blocks.”

Also applies to: 362-364

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/listing-builder/components/Form/Rewards/Tokens/TokenSelect.tsx`
around lines 320 - 323, In TokenSelect (the fetch block that currently does `if
(!response.ok) { throw new Error('Failed to search Jupiter tokens'); }`) replace
the exception-driven path with the project's Result pattern: instead of
throwing, return a failure Result (e.g., Result.fail(...) or the project's
equivalent) containing an error message and any response info; propagate that
Result to the caller so callers can handle success/failure without try/catch. Do
the same for the second occurrence at lines ~362-364 (the other fetch
response.ok check) so both search/fetch helper functions return Result.ok(data)
or Result.fail(error) rather than throwing. Ensure callers of the fetch
functions are updated to check the Result and handle errors accordingly.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/features/listing-builder/components/Form/Rewards/Tokens/TokenSelect.tsx`:
- Around line 102-109: Replace the non-semantic clickable elements with
accessible controls: wrap the ShieldCheck icon and the clickable <p> text in
proper interactive elements (e.g., a <button> or an <a> with role and href) or
convert their wrappers to <button> so they are focusable and keyboard-operable;
ensure keyboard activation calls the same handler
(openJupiterTokenPage(mintAddress)) and add appropriate aria-label and visible
focus styles. Update both the ShieldCheck click handler usage in TokenSelect and
the other clickable text block (the code near lines referencing the second
clickable <p>) to use the same pattern so keyboard, screen reader, and mouse
users trigger openJupiterTokenPage consistently. Also remove
event.stopPropagation only if it breaks keyboard behavior or ensure it doesn’t
prevent default keyboard activation.
- Around line 136-137: TokenSearchLabel is always rendering JupiterVerifiedIcon
(using mintAddress) which incorrectly labels local/DB tokens as
Jupiter-verified; update TokenSearchLabel (and usages in TokenSelect and the DB
token render block around the lines referencing mintAddress) to conditionally
render JupiterVerifiedIcon only when the token is actually a Jupiter token
(e.g., pass a boolean prop like isJupiterVerified or check a token.source/type
flag), and ensure TokenSelect/DB token rendering supplies that flag instead of
unconditionally rendering <JupiterVerifiedIcon mintAddress={mintAddress} />.

---

Nitpick comments:
In `@src/features/listing-builder/components/Form/Rewards/Tokens/TokenSelect.tsx`:
- Around line 52-68: The type declarations (JupiterToken, TokenSearchResponse,
AddTokenResponse) use optional properties with ?; change each optional property
to explicit unions (e.g., icon: string | null | undefined) and any optional
top-level properties to Type | undefined instead of ?. Also make object fields
readonly by default (prepend readonly to each property) unless a field is
intentionally mutable (leave those mutable and document why). Update references
to Token if it has optional fields the same way. Ensure the new readonly/|
undefined shapes compile with usages in TokenSelect.tsx and adjust any mutations
accordingly.
- Around line 320-323: In TokenSelect (the fetch block that currently does `if
(!response.ok) { throw new Error('Failed to search Jupiter tokens'); }`) replace
the exception-driven path with the project's Result pattern: instead of
throwing, return a failure Result (e.g., Result.fail(...) or the project's
equivalent) containing an error message and any response info; propagate that
Result to the caller so callers can handle success/failure without try/catch. Do
the same for the second occurrence at lines ~362-364 (the other fetch
response.ok check) so both search/fetch helper functions return Result.ok(data)
or Result.fail(error) rather than throwing. Ensure callers of the fetch
functions are updated to check the Result and handle errors accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f1525ef2-baa1-4f5d-b6a4-bf3c5cd6b956

📥 Commits

Reviewing files that changed from the base of the PR and between 1ac769d and df383e4.

📒 Files selected for processing (1)
  • src/features/listing-builder/components/Form/Rewards/Tokens/TokenSelect.tsx

Comment on lines +102 to +109
<ShieldCheck
className="h-3.5 w-3.5 text-[#1C4CE7]"
aria-label="Verified on Jupiter"
onClick={(event) => {
event.stopPropagation();
openJupiterTokenPage(mintAddress);
}}
/>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Interactive icon/text are not keyboard-accessible controls.

Clickable ShieldCheck and <p> rely on onClick only. Keyboard users cannot reliably trigger these actions. Use semantic <button>/<a> elements (or add full keyboard handlers + focus styles).

Also applies to: 144-149

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/listing-builder/components/Form/Rewards/Tokens/TokenSelect.tsx`
around lines 102 - 109, Replace the non-semantic clickable elements with
accessible controls: wrap the ShieldCheck icon and the clickable <p> text in
proper interactive elements (e.g., a <button> or an <a> with role and href) or
convert their wrappers to <button> so they are focusable and keyboard-operable;
ensure keyboard activation calls the same handler
(openJupiterTokenPage(mintAddress)) and add appropriate aria-label and visible
focus styles. Update both the ShieldCheck click handler usage in TokenSelect and
the other clickable text block (the code near lines referencing the second
clickable <p>) to use the same pattern so keyboard, screen reader, and mouse
users trigger openJupiterTokenPage consistently. Also remove
event.stopPropagation only if it breaks keyboard behavior or ensure it doesn’t
prevent default keyboard activation.

Comment thread src/features/listing-builder/components/Form/Rewards/Tokens/TokenSelect.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/features/listing-builder/components/Form/Rewards/Tokens/TokenSelect.tsx (2)

102-109: ⚠️ Potential issue | 🟠 Major | ⚖️ Poor tradeoff

Keyboard users still cannot trigger the Jupiter link.

This issue was previously flagged: the ShieldCheck icon's onClick handler fires on mouse click but not on keyboard Enter press. The Tooltip's button handles keyboard Enter by toggling the tooltip open/closed, preventing the child's onClick from executing. Keyboard users see the tooltip but cannot open the Jupiter token page.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/listing-builder/components/Form/Rewards/Tokens/TokenSelect.tsx`
around lines 102 - 109, The ShieldCheck icon's click only works for mouse users;
make it keyboard-accessible by adding a focusable role and key handler: ensure
the ShieldCheck element has tabIndex={0} and role="button" (or equivalent props)
and implement an onKeyDown that listens for Enter and Space, calls
event.stopPropagation(), and invokes openJupiterTokenPage(mintAddress) just like
the onClick handler; keep the existing aria-label for accessibility and ensure
the Tooltip button no longer prevents the child's key handling by stopping
propagation in the icon's handlers.

148-156: ⚠️ Potential issue | 🟠 Major | ⚖️ Poor tradeoff

Keyboard users still cannot trigger the Jupiter link.

This issue was previously flagged: the mint address <p> element's onClick handler fires on mouse click but not on keyboard Enter press. The Tooltip's button handles keyboard Enter by toggling the tooltip, preventing the child's onClick from executing. Keyboard users see the tooltip but cannot open the Jupiter token page.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/listing-builder/components/Form/Rewards/Tokens/TokenSelect.tsx`
around lines 148 - 156, The mint address <p> in TokenSelect is only clickable
with mouse; make it keyboard-accessible by replacing or enhancing it to behave
like a button: ensure the element with truncatePublicKey(mintAddress, 6) is
focusable (tabIndex=0 or use a <button>), add an onKeyDown handler that calls
event.stopPropagation() and invokes openJupiterTokenPage(mintAddress) for
Enter/Space, and keep the existing onClick behavior; update the element
referenced in TokenSelect.tsx (the element rendering truncatePublicKey and using
mintAddress and openJupiterTokenPage) so keyboard users can open the Jupiter
token page.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@prisma/schema.prisma`:
- Line 166: The isVerifiedOnJupiter field currently uses `@default`(true) which
will mark all existing tokens as Jupiter-verified; change the schema to remove
or flip the default (e.g., set isVerifiedOnJupiter Boolean `@default`(false) or no
default) in the Prisma model containing isVerifiedOnJupiter, then add a data
migration script that queries tokens and sets isVerifiedOnJupiter = true only
for tokens actually imported/marked from Jupiter (use whatever provenance flag
or import source you have), or alternatively document that the migration
intentionally marks all existing tokens as verified if you choose that route.

In `@src/constants/tokenList.ts`:
- Line 13: The property declaration is wrong: replace the optional
`isVerifiedOnJupiter?: boolean` with a non-optional `isVerifiedOnJupiter:
boolean` (or `isVerifiedOnJupiter: boolean | undefined` if you intentionally
want undefined) in the client TypeScript type in src/constants/tokenList.ts so
it follows the "property: Type | undefined" guideline and — since the server
always provides this field — matches the server Token interface; update the
interface/type that contains `isVerifiedOnJupiter` so clients and server types
align.

---

Duplicate comments:
In `@src/features/listing-builder/components/Form/Rewards/Tokens/TokenSelect.tsx`:
- Around line 102-109: The ShieldCheck icon's click only works for mouse users;
make it keyboard-accessible by adding a focusable role and key handler: ensure
the ShieldCheck element has tabIndex={0} and role="button" (or equivalent props)
and implement an onKeyDown that listens for Enter and Space, calls
event.stopPropagation(), and invokes openJupiterTokenPage(mintAddress) just like
the onClick handler; keep the existing aria-label for accessibility and ensure
the Tooltip button no longer prevents the child's key handling by stopping
propagation in the icon's handlers.
- Around line 148-156: The mint address <p> in TokenSelect is only clickable
with mouse; make it keyboard-accessible by replacing or enhancing it to behave
like a button: ensure the element with truncatePublicKey(mintAddress, 6) is
focusable (tabIndex=0 or use a <button>), add an onKeyDown handler that calls
event.stopPropagation() and invokes openJupiterTokenPage(mintAddress) for
Enter/Space, and keep the existing onClick behavior; update the element
referenced in TokenSelect.tsx (the element rendering truncatePublicKey and using
mintAddress and openJupiterTokenPage) so keyboard users can open the Jupiter
token page.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a8b65b52-6140-42ad-8ff1-1191a3bd021c

📥 Commits

Reviewing files that changed from the base of the PR and between df383e4 and 7032729.

📒 Files selected for processing (4)
  • prisma/schema.prisma
  • src/constants/tokenList.ts
  • src/features/listing-builder/components/Form/Rewards/Tokens/TokenSelect.tsx
  • src/server/tokenList.ts

Comment thread prisma/schema.prisma
decimals Int
sortOrder Int @default(0)
isActive Boolean @default(true)
isVerifiedOnJupiter Boolean @default(true)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Incorrect default value for Jupiter verification flag.

The @default(true) will mark ALL existing tokens as Jupiter-verified after migration, even if they were manually curated or imported from non-Jupiter sources. This contradicts the stated purpose of tracking Jupiter verification status.

When the migration runs, existing tokens that were NOT verified on Jupiter will incorrectly show verification badges in the UI (as seen in TokenSearchLabel downstream).

🔧 Recommended fix
-  isVerifiedOnJupiter Boolean  `@default`(true)
+  isVerifiedOnJupiter Boolean  `@default`(false)

Then either:

  1. Write a data migration to set isVerifiedOnJupiter = true only for tokens that were actually added from Jupiter, or
  2. Document that all existing tokens are considered Jupiter-verified by design
📝 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
isVerifiedOnJupiter Boolean @default(true)
isVerifiedOnJupiter Boolean `@default`(false)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@prisma/schema.prisma` at line 166, The isVerifiedOnJupiter field currently
uses `@default`(true) which will mark all existing tokens as Jupiter-verified;
change the schema to remove or flip the default (e.g., set isVerifiedOnJupiter
Boolean `@default`(false) or no default) in the Prisma model containing
isVerifiedOnJupiter, then add a data migration script that queries tokens and
sets isVerifiedOnJupiter = true only for tokens actually imported/marked from
Jupiter (use whatever provenance flag or import source you have), or
alternatively document that the migration intentionally marks all existing
tokens as verified if you choose that route.

decimals: number;
sortOrder?: number;
isActive?: boolean;
isVerifiedOnJupiter?: boolean;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

TypeScript property definition violates coding guideline and creates type mismatch.

Two issues:

  1. Guideline violation: Uses isVerifiedOnJupiter?: boolean instead of isVerifiedOnJupiter: boolean | undefined. As per coding guidelines, "Use property: Type | undefined instead of property?: Type for TypeScript type definitions to force explicit property passing and prevent bugs from accidentally omitting required properties."

  2. Type mismatch with server: The server Token interface (src/server/tokenList.ts line 12) declares this field as required (isVerifiedOnJupiter: boolean), and the server always returns it (included in tokenSelect + has schema default). The client type should match.

🔧 Recommended fix
-  isVerifiedOnJupiter?: boolean;
+  isVerifiedOnJupiter: boolean | undefined;

Or if the server guarantees this field is always present (which it does via the schema default):

-  isVerifiedOnJupiter?: boolean;
+  isVerifiedOnJupiter: boolean;
📝 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
isVerifiedOnJupiter?: boolean;
isVerifiedOnJupiter: boolean | undefined;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/constants/tokenList.ts` at line 13, The property declaration is wrong:
replace the optional `isVerifiedOnJupiter?: boolean` with a non-optional
`isVerifiedOnJupiter: boolean` (or `isVerifiedOnJupiter: boolean | undefined` if
you intentionally want undefined) in the client TypeScript type in
src/constants/tokenList.ts so it follows the "property: Type | undefined"
guideline and — since the server always provides this field — matches the server
Token interface; update the interface/type that contains `isVerifiedOnJupiter`
so clients and server types align.

Source: Coding guidelines

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant