Skip to content

Add MPT ticker/issuer search alongside IOU search - #1340

Merged
cybele-ripple merged 11 commits into
mainfrom
add-mpt-search
Oct 8, 2026
Merged

cybele-ripple merged 11 commits into
mainfrom
add-mpt-search

Conversation

@cybele-ripple

@cybele-ripple cybele-ripple commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

Merge MPT results into the unified token search list

High Level Overview of Change

  • TokenSearchResults now renders MPTs directly alongside IOUs in a single flat, holders-sorted "Tokens" list — there's no separate MPT section.
  • server/routes/v1/tokens.js fetches MPTs from the XRPL Meta v2 endpoint (https://${XRPL_META_URL}/v2/tokens/mpt) and merges them into the same cached, holders-sorted list the IOU search already uses. LOS has no MPT support yet, so this is a second data source rather than a passthrough of /trusted-tokens. Only MPTs with at least one holder are included — zero-holder issuances are almost entirely test/abandoned tokens.
  • Search matching now also checks a new full_name field (the fuller product name, e.g. "Car Parts" for a token ticked SCPO) in addition to the short ticker in name — without it, a token was only findable by its ticker, leaving long descriptive names on real MPTs completely unsearchable.
  • TokenSearchRow now branches on token_type to render MPTs: links to /mpt/:mpt_issuance_id instead of /token/:currency.:issuer, displays ticker + full name the same way IOUs display currency code + name (e.g. KEY (KEYSTONE PROTOCOL TOKEN)) instead of a shortened issuance ID, and omits both the trustlines chip (no trustline concept for MPTs) and the price chip (MPTs aren't tradeable on the DEX yet, so price is always blank).
  • Added a colored "IOU"/"MPT" type chip to every row, reusing the $mpt blue already used for MPT category pills elsewhere on the token page.
  • Extended LOSToken with full_name, token_type: 'IOU' | 'MPT', and mpt_issuance_id; made trustlines optional since MPTs don't have one.
  • Updated/added tests covering the merged single-list rendering, the ticker+full-name display, the type chip, and (via a fixture that's deliberately not pre-sorted) that ranking stays a backend responsibility rather than getting re-sorted client-side.

Context of Change

This fetches MPTs directly from XRPL Meta's v2 API and blends them into the same cached list the IOU search already ranks by holder count.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (non-breaking change that only restructures code)
  • Tests (You added tests for code that already exists, or your new feature included in this PR)
  • Documentation Updates
  • Translation Updates
  • Release

Codebase Modernization

  • Updated files to React Hooks
  • Updated files to TypeScript

Before / After

The following screenshots demonstrate the search functionality, highlighting several scenarios
A unified list that shows MPTs alongside IOUs, sorted by holder. Note that the IOUs and MPTs are labeled and MPTs do not include prices since those values are blank:
Screenshot 2026-09-25 at 1 37 06 PM

This screenshot demonstrates the full name only search where these MPT tokens are only findable by full_name:
Screenshot 2026-09-25 at 1 37 29 PM

This screenshot is for an MPT with blank metadata. The only way to search for it is by the issuance ID:
Screenshot 2026-09-25 at 1 37 51 PM

THis screenshot demonstrates the fill lst of tokens that is returned when a space is entered into the search bar:
Screenshot 2026-09-25 at 1 38 30 PM

Test Plan

Verified via npx jest/eslint/tsc/stylelint (all clean) plus manual checks against live mainnet data confirming the merged sort, ticker+full-name display, type chips, price/trustlines omission on MPT rows, full-name search, the holders>0 filter, and graceful handling of MPTs with no metadata or no search matches.

Split the token search dropdown into separate IOU and MPT sections,
keyed off the new token_type/mpt_issuance_id fields LOS will return
once DGE-6575 (ledger-object-service#131) lands.

@ripple-code-reviewer ripple-code-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The MPT/IOU split is implemented cleanly with a new MPTSearchRow component, filtering by token_type, and test coverage for the two-section layout. Two things worth a look: MPTSearchRow builds its route directly from an optional mpt_issuance_id field without a fallback/guard, and TokenLogo/TokenName/IssuerAddress are duplicated verbatim between TokenSearchRow and MPTSearchRow.

Search results within each group were rendered in whatever order the
API returned; sort by holders descending so more widely-held tokens
surface first.

@ripple-code-reviewer ripple-code-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The MPT/IOU split is implemented cleanly and matches the PR description. The main thing worth double-checking is a new sort-by-holders behavior applied to the IOU list that wasn't previously there, which changes search result ordering independent of the MPT split.

Adds a mixed IOU/MPT dataset with varying holder counts and a test
verifying both groups render in holder-count descending order.

@ripple-code-reviewer ripple-code-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Clean, well-scoped change that splits the token search dropdown into IOU and MPT sections. Logic is straightforward and mirrors the existing TokenSearchRow pattern for the new MPTSearchRow component. One correctness gap: the new MPT row builds its route link directly from the optional mpt_issuance_id field without a fallback or guard, unlike the Currency label right next to it which does have one.

MPTs no longer render in a separate section - they're fetched from the
XRPL Meta v2 MPT endpoint (LOS has no MPT support yet), merged with IOUs,
and sorted together by holder count. Only MPTs with at least one holder
are surfaced. Search now also matches on a full product name field
(distinct from the short ticker), so tokens like a "VGOLD CORE+" MPT
ticked "VCORE" are findable by either. Removed an exact/prefix match
ranking boost that was added and then reverted per product direction -
plain holder-count ordering is preserved. Each row now also shows an
IOU/MPT type indicator chip, and MPT rows use the same ticker + full-name
display structure as IOUs instead of a shortened issuance ID.

@ripple-code-reviewer ripple-code-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The bulk of the diff is reasonable (MPT row rendering, no-trustlines-chip handling, backend MPT fetch/merge, query matching on full_name), but the headline claim of this PR — splitting the search dropdown into separate 'Tokens' and 'MPTs' sections — is not actually implemented. TokenSearchResults.tsx still renders every token (IOU and MPT) under a single search-results-header labeled just 'tokens (N)'; there's no second header, no MPTSearchRow component, and the diff's own new test ("renders mpts together with tokens in a single list") explicitly asserts a single combined header, contradicting the PR description's claim of two headers ('tokens (1)' / 'mpts (1)') and a dedicated MPTSearchRow. Also flagged a hardcoded limit=1000 on the new XRPL Meta MPT fetch with no pagination, which will silently drop MPTs beyond the first 1000 as that dataset grows.

MPTs aren't tradeable on the DEX yet, so price is always blank - showing
a dashed placeholder chip for it was confusing. Drop the chip entirely
for MPT rows instead, matching how the trustlines chip is already
IOU-only.

@ripple-code-reviewer ripple-code-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Solid, well-scoped frontend change that adds MPT rows to the token search dropdown by discriminating on the new token_type field, with matching backend work to fetch and merge MPTs from XRPL Meta. Tests cover the new row rendering and search-order guarantees. A few things worth a second look: the PR description talks about two distinct 'Tokens'/'MPTs' sections via a new MPTSearchRow component, but the actual diff renders everything under a single merged 'tokens' header (confirmed by the test titled 'renders mpts together with tokens in a single list') — worth confirming this was an intentional pivot. On the backend, reusing mpt_issuance_id (48 hex chars) as the generic currency field feeds an ID of unexpected length into existing IOU currency-matching logic, and the new fetchMPTs() call hardcodes limit=1000 with no pagination, which could silently drop results as MPT adoption grows.

Comment thread src/containers/shared/components/TokenSearchResults/TokenSearchRow.tsx Outdated
Comment thread server/routes/v1/tokens.js
Comment thread server/routes/v1/tokens.js Outdated
Addresses PR review comments: the search-result type chip now uses
white for IOU and blue for MPT to match the token detail page's
category pill, and the token cache is split into an IOU-only list
(ranking/metrics) and a merged search list, so MPTs no longer show
up on the Token Ranking page.

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

@ripple-code-reviewer ripple-code-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The MPT search integration is well-structured and mirrors the existing IOU flow reasonably closely (caching, sorting, search matching, optional field guarding on the frontend). The one thing worth double-checking before merge is whether the new fetchMPTs() call needs to paginate — it hard-codes limit=1000 with no cursor/offset loop, so if XRPL Meta ever returns more than 1000 MPTs, results beyond the first page would be silently dropped from search. Everything else (full_name search matching, TokenSearchRow branching on token_type, optional trustlines typing) looks correct and consistent with the described intent.

Comment on lines +118 to +119
return axios
.get(url, { timeout: 30000 })

@semgrep-code-ripple-github-org semgrep-code-ripple-github-org Bot Oct 2, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High severity issue identified in your code:

Risk: Affected versions of axios are vulnerable to Inefficient Regular Expression Complexity. The axios Node.js HTTP adapter parses data: URLs in GET requests with a regex (lib/helpers/fromDataURI.js) that suffers from catastrophic backtracking; a long malformed data: URL made of slashes without a comma blocks the event loop for minutes, causing a denial of service.

Fix: Upgrade this library to at least version 1.20.0 at explorer/package-lock.json:7447.

Reference(s): https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-88378, GHSA-c29m-xwm3-cm6r, CVE-2026-101903

✨ Fixed in commit 4514ba9 ✨

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thx u🫶

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Axios upgrades will go in a different PR

Comment on lines +118 to +119
return axios
.get(url, { timeout: 30000 })

@semgrep-code-ripple-github-org semgrep-code-ripple-github-org Bot Oct 2, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High severity issue identified in your code:

Risk: Affected versions of axios are vulnerable to Inefficient Regular Expression Complexity / Uncontrolled Resource Consumption. Axios is vulnerable to a Regular Expression Denial of Service (ReDoS) in the internal shouldBypassProxy() helper used by the Node.js HTTP adapter. Hostnames are normalized with hostname.replace(/\.+$/, ''), which backtracks quadratically on hostnames made of many dots followed by a single character. When a proxy is taken from the environment and NO_PROXY is set, a malicious server can return a redirect whose Location host is crafted this way, blocking the Node.js event loop and causing a denial of service.

Manual Review Advice: A vulnerability from this advisory is reachable if you use Axios in Node.js with the default http adapter and a proxy configured via the HTTP_PROXY/HTTPS_PROXY environment variables and NO_PROXY set, without setting proxy: false, and redirects are followed

Fix: Upgrade this library to at least version 1.20.0 at explorer/package-lock.json:7447.

Reference(s): https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-88383, GHSA-mghh-pgcx-3jjj, CVE-2026-101906

✨ Fixed in commit 4514ba9 ✨

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Axios upgrades will go in a different PR

Comment on lines +118 to +119
return axios
.get(url, { timeout: 30000 })

@semgrep-code-ripple-github-org semgrep-code-ripple-github-org Bot Oct 2, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High severity issue identified in your code:

Risk: Affected versions of axios are vulnerable to Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution') / Unintended Proxy or Intermediary ('Confused Deputy'). The axios Node.js HTTP adapter does not set a safe own value for createConnection on the request options it passes to Node http/https. If Object.prototype.createConnection is polluted anywhere in the same process, Node inherits and runs the attacker function, which silently sends the socket to attacker infrastructure while the request URL still looks legitimate. Authorization headers, cookies, API keys and response bodies can then be stolen or tampered with.

Fix: Upgrade this library to at least version 1.20.0 at explorer/package-lock.json:7447.

Reference(s): https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-88382, GHSA-m8m8-qj5v-23w3, CVE-2026-101905

🎉 Fixed in commit 4514ba9 🎉

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Axios upgrades will go in a different PR

@ripple-code-reviewer ripple-code-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cache resilience issue—see inline.

Comment thread server/routes/v1/tokens.js Outdated
})
.catch((e) => {
log.error(`Failed to fetch MPTs from ${url}:`, { message: e.message })
return []

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MPT fetch failure clears cache for 10min; IOU failures preserve it. Fall back to cached MPTs on error:

Suggested change
return []
.catch((e) => {
log.error(`Failed to fetch MPTs from ${url}:`, { message: e.message })
return cachedTokenList.mpts || []
})
Suggested change
return []
return cachedTokenList.searchTokens.filter((t) => t.token_type === 'MPT')

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in most recent commit

@ripple-code-reviewer ripple-code-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Solid, well-tested feature that merges MPT search results into the unified token list. The main concern is the new MPT fetch using a hardcoded limit=1000 with no pagination handling, which will silently drop tokens once the XRPL Meta MPT catalog grows past that cap.

@brianstohl87-cpu brianstohl87-cpu left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry it took so long

@ripple-code-reviewer ripple-code-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Overall solid, well-tested addition of MPT search support that merges XRPL Meta MPT data into the existing holders-sorted search list, with sensible UI branching for MPT vs IOU rows. The main concern is the new fetchMPTs() call to XRPL Meta's /v2/tokens/mpt endpoint, which uses a hardcoded limit=1000 with no pagination/cursor handling, risking silent truncation of results if the MPT count grows past that page size.

@cybele-ripple
cybele-ripple requested a review from kuan121 October 8, 2026 17:58
@cybele-ripple
cybele-ripple merged commit baf6da5 into main Oct 8, 2026
6 checks passed
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.

3 participants