Repository navigation
Add MPT ticker/issuer search alongside IOU search - #1340
Conversation
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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>
There was a problem hiding this comment.
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.
| return axios | ||
| .get(url, { timeout: 30000 }) |
There was a problem hiding this comment.
🟠 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 ✨
There was a problem hiding this comment.
Axios upgrades will go in a different PR
| return axios | ||
| .get(url, { timeout: 30000 }) |
There was a problem hiding this comment.
🟠 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 ✨
There was a problem hiding this comment.
Axios upgrades will go in a different PR
| return axios | ||
| .get(url, { timeout: 30000 }) |
There was a problem hiding this comment.
🟠 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 🎉
There was a problem hiding this comment.
Axios upgrades will go in a different PR
| }) | ||
| .catch((e) => { | ||
| log.error(`Failed to fetch MPTs from ${url}:`, { message: e.message }) | ||
| return [] |
There was a problem hiding this comment.
MPT fetch failure clears cache for 10min; IOU failures preserve it. Fall back to cached MPTs on error:
| return [] | |
| .catch((e) => { | |
| log.error(`Failed to fetch MPTs from ${url}:`, { message: e.message }) | |
| return cachedTokenList.mpts || [] | |
| }) |
| return [] | |
| return cachedTokenList.searchTokens.filter((t) => t.token_type === 'MPT') |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Merge MPT results into the unified token search list
High Level Overview of Change
TokenSearchResultsnow renders MPTs directly alongside IOUs in a single flat, holders-sorted "Tokens" list — there's no separate MPT section.server/routes/v1/tokens.jsfetches 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.full_namefield (the fuller product name, e.g. "Car Parts" for a token tickedSCPO) in addition to the short ticker inname— without it, a token was only findable by its ticker, leaving long descriptive names on real MPTs completely unsearchable.TokenSearchRownow branches ontoken_typeto render MPTs: links to/mpt/:mpt_issuance_idinstead 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).$mptblue already used for MPT category pills elsewhere on the token page.LOSTokenwithfull_name,token_type: 'IOU' | 'MPT', andmpt_issuance_id; madetrustlinesoptional since MPTs don't have one.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
Codebase Modernization
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:
This screenshot demonstrates the full name only search where these MPT tokens are only findable by full_name:

This screenshot is for an MPT with blank metadata. The only way to search for it is by the issuance ID:

THis screenshot demonstrates the fill lst of tokens that is returned when a space is entered into the search bar:

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.