Skip to content

MPT Object page improvement - #1339

Open
kuan121 wants to merge 1 commit into
mainfrom
fix/mpt-object-page-improvements
Open

MPT Object page improvement#1339
kuan121 wants to merge 1 commit into
mainfrom
fix/mpt-object-page-improvements

Conversation

@kuan121

@kuan121 kuan121 commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

High Level Overview of Change

Fixes four reported display issues on the MPToken object page, and replaces the placeholder circulating-supply figure with a real calculation shared with the IOU page.

  • Supply / Circ SupplySupply is now the on-chain OutstandingAmount; Circ Supply subtracts holders owning ≥ 20% (skipped for RWA tokens). Fixes the reported "circulating supply > supply".
  • Issuer name is no longer truncated with an address-style truncator.
  • Metadata URIs are no longer clipped at 65 characters.
  • Website chip shows the registrable domain (franklintempleton.com) instead of a middle-truncated string that destroyed the TLD.
  • Shared logic + cleanup — circulating-supply rules extracted to a shared, unit-tested util used by both token pages; two dead modules removed.

Context of Change

Reported against livenet.xrpl.org/mpt/064E27366D15D1F5614B4D4E3183F835BCD62E7F6FD9FFD8 (Franklin sgBENJI). Each root cause was confirmed against the live ledger_entry mpt_issuance response rather than inferred.

1. Circulating supply > supply. This issuance sets no MaximumAmount, so maxAmt was undefined and MarketData rendered BigInt(maxAmt || '0')Supply = 0, while Circ Supply showed the real ~28.14. Per spec an unset maximum means the cap is 2^63-1, which is meaningless to display.

After product review the fix is: Supply = OutstandingAmount, and Circ Supply = Supply minus holders ≥ 20% — the same rule the IOU page already applied. Large holders are issuer/treasury/whale wallets that are not meaningfully in circulation. The exclusion is skipped for RWA tokens (asset_class === 'rwa'), whose large holders are custodians — analogous to the IOU stablecoin rule.

No new data source was required: the page already fetches the full holder set on-chain via mpt_holders, with each holder's percent computed against OutstandingAmount.

The subtraction runs in BigInt on unscaled amounts. A Number-based version left float residue — for a fully-held token it produced values like 2.3283064365386963e-10, which parseAmount renders as < 0.0001 instead of 0.00 (~1/3 of fully-concentrated cases when fuzzed). BigInt also avoids silent precision loss for amounts near UInt64 max (~9.2e18), well beyond exact double precision.

2. Truncated issuer name. shortenAccount() — a 7…5 address truncator — was applied to the human-readable issuer name, rendering "Franklin Templeton Investments" as "Frankli…ments".

3. Truncated metadata. The shared JsonView hard-coded collapseStringsAfterLength={65}, clipping the 122-character URI in the metadata panel.

4. Website chip destroyed the TLD. shortenDomain(url, 12, 7) middle-truncates, producing www.franklin…hnology. The TLD is the part users rely on to judge whether a link is safe, so the chip now shows the registrable domain parsed with a real public-suffix list (tldts) — correctly keeping thing.co.uk rather than collapsing it to co.uk. tldts was already in the tree as a transitive dependency and is promoted to a direct one.

Shared logic. The ≥20% / RWA / stablecoin rules now live in Token/shared/utils/circulatingSupply.ts and are used by both token pages. IOU's calculation was promoted out of its component into that module so it can be unit-tested directly.

Dead code. IOU/hooks/useMarketCalculations.ts and IOU/utils/tokenCalculations.ts (plus their tests) were duplicates reachable only from their own tests — the shipping IOU page had its own copy.

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

All touched and new files are function components / TypeScript. New modules (shared/domainUtils.ts, Token/shared/utils/circulatingSupply.ts, Token/MPT/utils/circulatingSupply.ts) are TypeScript.

Before / After

Market Data box

Field Before After
Supply 0.00 (no MaximumAmount) 28.14 (on-chain OutstandingAmount)
Circ Supply 28.14 (> Supply) ≤ Supply, excluding ≥20% holders

Circ Supply shows a spinner while holders load, and -- if that request fails — it never silently shows the unadjusted supply as if it were a real circulating figure.

Header

Element Before After
Issuer name Frankli…ments (truncated, in title) Full name, clickable, in General Overview
Website chip www.franklin…hnology (TLD destroyed) franklintempleton.com, full URL on hover
Metadata URI clipped at 65 chars rendered in full

Rule summary

Page Skip ≥20% exclusion when… asset_class source
IOU asset_subclass === 'stablecoin' OR asset_class === 'rwa' LOS
MPT asset_class === 'rwa' on-chain metadata

Test Plan

Automated — all green:

  • npm run build-ts — no type errors
  • npm run lint:ci — ESLint, Stylelint, Prettier (--max-warnings 0)
  • npm run test:ci1709 tests pass, coverage above the 70/80 thresholds
  • pre-commit run --all-files
  • npm run build — production bundle builds with the new dependency

New/updated tests:

  • Token/shared/test/utils/circulatingSupply.test.ts — threshold boundary (exactly 20%), large-holder subtraction, isRwaAssetClass (incl. case/whitespace), and calculateIouCirculatingSupply (reported supply, supply fallback, stablecoin and RWA skips)
  • Token/MPT/test/utils/circulatingSupply.test.ts — asset-scale handling, RWA skip, negative floor, and two exact-integer regressions: float residue → 0, and amounts beyond double precision
  • shared/test/domainUtils.test.ts — registrable domain (incl. thing.co.uk), non-URL fallback, left-truncation preserving the TLD
  • MarketData / Header / Metadata / GeneralOverview / DomainLink — Supply vs Circ Supply, spinner and -- states, untruncated issuer name and metadata, title tooltip

Manual: npm start, open /mpt/064E27366D15D1F5614B4D4E3183F835BCD62E7F6FD9FFD8 — Supply is the full outstanding amount, Circ Supply is ≤ Supply (cross-check excluded accounts against Holders table rows with % ≥ 20), the chip reads franklintempleton.com with the full URL on hover, the issuer name renders in full, and the metadata URI is not clipped. IOU token pages show unchanged supply and market-cap values after the refactor.

Future Tasks

  • MaximumAmount is no longer surfaced. Dropping maxAmt from MarketData fixed the Supply = 0 bug, but tokens that do set a cap no longer display it anywhere. Adding a "Max Amount" row (the max_amount i18n key already exists) was deliberately deferred as a separate product decision.
  • RWA detection depends on issuer metadata. It keys off on-chain asset_class === 'rwa'. Franklin sgBENJI currently tags "US Treasuries", so it is not yet recognized as RWA; the issuer would need to update its metadata (supported by Dynamic MPT). We deliberately avoid an XRPL Meta token call here.
  • .gitignore does not cover .env.devnet / .env.mainnet / .env.testnet (only .env and .env.*.local). Worth adding .env.* with a !.env.example negation to prevent accidental commits. Out of scope for this 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.

Unsafe cast of issuer-supplied JSON could crash on malformed metadata — see inline.

// Circulating supply = outstanding amount minus large (>= 20%) holders, except
// for RWA tokens where those holders are custodians/treasuries (no exclusion).
const isRwa = isRwaAssetClass(
mptokenIssuance?.parsedMPTMetadata?.asset_class as string | undefined,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unsafe cast of free-form issuer JSON. Add runtime type guard before calling .trim():

  const isRwa = typeof mptokenIssuance?.parsedMPTMetadata?.asset_class === 'string'
    ? isRwaAssetClass(mptokenIssuance.parsedMPTMetadata.asset_class as string)
    : false
Suggested change
mptokenIssuance?.parsedMPTMetadata?.asset_class as string | undefined,
typeof mptokenIssuance?.parsedMPTMetadata?.asset_class === 'string'
? mptokenIssuance.parsedMPTMetadata.asset_class
: undefined,

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