Skip to content

Conversation

JohnnyWyles
Copy link
Contributor

What is the purpose of the change:

Positions with base/quote that have high variation in value but are still within the bounds of the CL pool have functional charts but non-functional price displays.
image

Linear Task

https://linear.app/osmosis/issue/FE-1447/incorrect-price-display-for-babybtc

Brief Changelog

Changes use of minimumDecimals to be minimumSignificantDigits to allow prices such as 0.0000005215 to be displayed fully.

Testing and Verifying

This change has been tested locally by rebuilding the website and verified content and links are expected

Copy link

vercel bot commented Oct 15, 2025

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

Project Deployment Preview Comments Updated (UTC)
osmosis-frontend Ready Ready Preview Comment Oct 15, 2025 2:00pm
4 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
osmosis-frontend-datadog Ignored Ignored Oct 15, 2025 2:00pm
osmosis-frontend-dev Ignored Ignored Oct 15, 2025 2:00pm
osmosis-frontend-edgenet Ignored Ignored Oct 15, 2025 2:00pm
osmosis-testnet Ignored Ignored Oct 15, 2025 2:00pm

Copy link
Contributor

coderabbitai bot commented Oct 15, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Reworks price formatting in packages/web/utils/formatter.ts: dynamic max-decimal/significant-digit calculation with leading-zero awareness, adds exported compressZeros, imports leadingZerosCount, and updates formatFiatPrice to pad/truncate decimals; one test added for a trailing-zero case.

Changes

Cohort / File(s) Summary of Changes
Price formatting logic & exports
packages/web/utils/formatter.ts
- Reworked tiny-value handling in getPriceExtendedFormatOptions: dynamic maximumSignificantDigits and maxDecimals using leadingZerosCount and leading-zero-aware logic; removed minimumSignificantDigits, minimumFractionDigits, maximumFractionDigits, and disabledTrimZeros from returned options
- Added exported compressZeros(formattedValue, hasCurrencySymbol, zerosThreshold) and internal countLeadingZeros usage
- Hardened formatFiatPrice to safely construct maxDecimalStr, pad/truncate decimal portion to maxDecimals, and handle missing decimal parts
- Minor refactors and adjusted comments
Tests
packages/web/utils/__tests__/formatter.spec.ts
- Added test: "Very large value with trailing zero" input 123456.10 expecting "$123,456.10" with maxDecimals: 2

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Caller
  participant Formatter as formatter.ts
  Caller->>Formatter: getPriceExtendedFormatOptions(value: Dec)
  alt value < 100
    Formatter->>Formatter: stringify value, locate decimal part
    Formatter->>Formatter: leadingZeros = leadingZerosCount(decimalPart)
    Formatter->>Formatter: actualDecimalCount = decimalPart.length
    Formatter->>Formatter: compute maximumSignificantDigits (fixed 4 or based on leadingZeros)
    Formatter->>Formatter: compute maxDecimals (consider leading zeros and decimal count)
  else
    Formatter->>Formatter: integerLen = length(integer part)
    Formatter->>Formatter: maximumSignificantDigits = integerLen + 2
    Formatter->>Formatter: set maxDecimals (based on integerLen)
  end
  Formatter-->>Caller: { maxDecimals, notation, maximumSignificantDigits }
Loading
sequenceDiagram
  autonumber
  actor Caller
  participant Formatter as formatter.ts
  Caller->>Formatter: compressZeros(formattedValue, hasCurrencySymbol?, zerosThreshold?)
  Formatter->>Formatter: strip sign and currency if present
  alt has decimal
    Formatter->>Formatter: count consecutive zeros after decimal (using leadingZerosCount)
    alt zeros >= threshold
      Formatter->>Formatter: preserve necessary leading zeros and keep significant digits
    else
      Formatter->>Formatter: trim redundant zeros
    end
  else
    Formatter->>Formatter: return integer representation unchanged
  end
  Formatter-->>Caller: compressed string
Loading
sequenceDiagram
  autonumber
  actor Caller
  participant Formatter as formatter.ts
  Caller->>Formatter: formatFiatPrice(value, maxDecimals)
  Formatter->>Formatter: build safe maxDecimalStr (handle missing decimals)
  Formatter->>Formatter: format value, then pad/truncate decimal part to maxDecimals
  Formatter-->>Caller: formatted fiat string
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly describes the primary change by specifying a fix to the price display for pools with high disparity, concisely summarizing the main purpose of the PR without unrelated details.
Description Check ✅ Passed The pull request description adheres to the repository’s template by including a clear purpose section, a linked Linear task, a brief changelog, and testing instructions, ensuring all required headings and details are present.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch johnny/poolpriceaccuracy

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.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/web/utils/formatter.ts (2)

309-378: Inconsistent return shape when no decimal; always include zeros and decimalDigits.

Callers will expect stable keys. Also, assume multi-char currency signs and negatives; current hasCurrencySymbol + [0] will misparse cases like “-¥123” or “CA$”.

Apply minimal shape fix:

   if (!punctuationSymbol) {
     return {
       currencySign,
-      significantDigits: formattedValue.substring(significantDigitsSubStart),
+      significantDigits: formattedValue.substring(significantDigitsSubStart),
+      zeros: 0,
+      decimalDigits: "",
     };
   }

And simplify unused boolean:

-  const otherDigits = charsAfterPunct.substring(zerosCount);
-
-  const canDisplayZeros = zerosCount !== 0 || otherDigits.length !== 0;
-
+  const otherDigits = charsAfterPunct.substring(zerosCount);
   return {
     currencySign,
     significantDigits: formattedValue.substring(
       significantDigitsSubStart,
       punctIdx
     ),
-    zeros: canDisplayZeros ? zerosCount : 0,
+    zeros: zerosCount,
     decimalDigits: otherDigits,
   };

Follow-ups (optional):

  • Accept currencySign: string | undefined (or infer via Intl.NumberFormat.formatToParts) rather than hasCurrencySymbol: boolean.
  • Use formatToParts to robustly locate decimal vs thousands separators for all locales.

396-411: Not locale-safe and drops trailing currency symbols; use Intl.formatToParts.

Splitting on "." and rejoining with "." breaks locales using “,” and loses suffix currency symbols (e.g., “12 345,67 €”).

Apply:

-  const splitPretty = formatPretty(maxDecimalPrice, {
-    ...getPriceExtendedFormatOptions(maxDecimalPrice.toDec()),
-  }).split(".");
-
-  const decimalPart = (splitPretty[1] || "00").slice(0, maxDecimals);
-  const paddedDecimalPart = decimalPart.padEnd(maxDecimals, "0");
-
-  return splitPretty[0] + "." + paddedDecimalPart;
+  const fmtOpts = getPriceExtendedFormatOptions(maxDecimalPrice.toDec());
+  const numForIntl = Number(
+    new IntPretty(maxDecimalPrice).maxDecimals(fmtOpts.maxDecimals).locale(false).toString()
+  );
+  const intl = new Intl.NumberFormat(price.fiatCurrency.locale, {
+    style: "currency",
+    currency: price.fiatCurrency.currency,
+    ...fmtOpts,
+  });
+  const parts = intl.formatToParts(numForIntl);
+  let fractionSeen = false;
+  const updated = parts.map((p) => {
+    if (p.type === "fraction") {
+      fractionSeen = true;
+      return { ...p, value: p.value.slice(0, maxDecimals).padEnd(maxDecimals, "0") };
+    }
+    return p;
+  });
+  if (!fractionSeen) {
+    const decimalSymbol =
+      parts.find((p) => p.type === "decimal")?.value ??
+      (1.1).toLocaleString(price.fiatCurrency.locale).slice(1, 2);
+    // Insert decimal + zero-padded fraction before currency (or at end).
+    const insertAt =
+      updated.findIndex((p) => p.type === "currency") ?? updated.length;
+    updated.splice(insertAt, 0, { type: "decimal", value: decimalSymbol }, { type: "fraction", value: "".padEnd(maxDecimals, "0") } as Intl.NumberFormatPart);
+  }
+  return updated.map((p) => p.value).join("");
🧹 Nitpick comments (3)
packages/web/utils/formatter.ts (3)

244-269: Simplify: branch always assigns 4 for values < 100.

Both inner branches set maximumSignificantDigits = 4. Collapse to a single conditional for clarity.

Apply:

-  let maximumSignificantDigits: number;
-
-  if (value.lt(new Dec(100))) {
-    // For prices < 100, check if we have many leading zeros after decimal point
-    const valueStr = value.toString();
-
-    // Only count leading zeros if the value has a decimal part
-    if (valueStr.includes(".")) {
-      const leadingZeros = leadingZerosCount(valueStr);
-
-      // If we have 4+ leading zeros, we need more significant digits to show meaningful precision
-      // e.g., 0.0000005 needs at least 4 sig figs after the leading zeros to show price movements
-      if (leadingZeros >= 4) {
-        // Show 4 significant digits after leading zeros for smooth price tracking
-        // e.g., 0.000000330738247 → 7 leading zeros + 4 sig figs = 0.0000003307
-        maximumSignificantDigits = 4;
-      } else {
-        maximumSignificantDigits = 4;
-      }
-    } else {
-      // No decimal point, use default
-      maximumSignificantDigits = 4;
-    }
-  } else {
-    maximumSignificantDigits = integerPartLength + 2;
-  }
+  let maximumSignificantDigits: number =
+    value.lt(new Dec(100)) ? 4 : integerPartLength + 2;

271-286: Avoid parseFloat to compute decimal count; preserve string precision.

Parsing to float can switch to exponential and lose precision. Derive counts from the string.

Apply:

-  const valueStr = value.toString();
-  const actualDecimalCount = getDecimalCount(parseFloat(valueStr));
+  const valueStr = value.toString();
+  const actualDecimalCount = valueStr.includes(".")
+    ? valueStr.split(".")[1].length
+    : 0;

Optional: cache leadingZerosCount(valueStr) once and reuse for both branches.


296-307: Duplicate zero-counter; reuse imported leadingZerosCount.

Remove countLeadingZeros and use the existing leadingZerosCount to keep one source of truth.

Apply:

-const countLeadingZeros = (decimalDigits: string) => {
-  let zeroCount = 0;
-  for (let i = 0; i < decimalDigits.length; i++) {
-    if (decimalDigits[i] === "0") {
-      zeroCount++;
-    } else {
-      break;
-    }
-  }
-
-  return zeroCount;
-};

And at usage (see Line 352):

-  const zerosCount = countLeadingZeros(charsAfterPunct);
+  const zerosCount = leadingZerosCount(`0.${charsAfterPunct}`);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c609158 and 7c91eec.

📒 Files selected for processing (1)
  • packages/web/utils/formatter.ts (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/web/utils/formatter.ts (1)
packages/web/utils/number.ts (2)
  • leadingZerosCount (24-32)
  • getDecimalCount (8-22)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Summary
🔇 Additional comments (2)
packages/web/utils/formatter.ts (2)

14-16: LGTM: importing leadingZerosCount

Good reuse of existing numeric utils for leading-zero handling.


238-294: Return-shape change is safe. No callers depend on the removed minimumSignificantDigits, minimumFractionDigits, maximumFractionDigits, or disabledTrimZeros fields.

cursor[bot]

This comment was marked as outdated.

const decimalPart = (splitPretty[1] || "00").slice(0, maxDecimals);
const paddedDecimalPart = decimalPart.padEnd(maxDecimals, "0");

return splitPretty[0] + "." + paddedDecimalPart;
Copy link

Choose a reason for hiding this comment

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

Bug: Decimal Separator Error in Price Formatting

The formatPrice function incorrectly appends a trailing decimal point (e.g., "123.") when maxDecimals is 0. This happens because the decimal separator is unconditionally added, even when no decimal places are intended, potentially creating invalid number strings for Dec construction.

Fix in Cursor Fix in Web

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Couldn't find any examples where this was the case, all whole numbers had 0s appended

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

🧹 Nitpick comments (1)
packages/web/utils/formatter.ts (1)

274-288: Consider documenting the + 2 buffer in the maxDecimals formula.

The formula leadingZeros + maximumSignificantDigits + 2 includes a + 2 buffer. While this safety margin ensures precision is preserved through IntPretty, the reason for specifically adding 2 isn't explained in the comments.

Consider adding a brief inline comment:

   const maxDecimals =
     leadingZeros >= 4
       ? Math.max(
           actualDecimalCount,
-          leadingZeros + maximumSignificantDigits + 2
+          leadingZeros + maximumSignificantDigits + 2  // +2 buffer for rounding safety
         )
       : Math.max(actualDecimalCount, 2);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9901984 and 85ddfaf.

📒 Files selected for processing (2)
  • packages/web/utils/__tests__/formatter.spec.ts (1 hunks)
  • packages/web/utils/formatter.ts (6 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/web/utils/tests/formatter.spec.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/web/utils/formatter.ts (2)
packages/web/components/chart/light-weight-charts/utils.ts (1)
  • priceFormatter (7-30)
packages/web/utils/number.ts (2)
  • getDecimalCount (8-22)
  • leadingZerosCount (24-32)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: preview-swap-usdc-tests
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Summary
🔇 Additional comments (4)
packages/web/utils/formatter.ts (4)

14-14: LGTM!

The leadingZerosCount import is correctly added and used in getPriceExtendedFormatOptions.


57-72: LGTM!

The padding logic ensures consistent decimal places for PricePretty values when using standard notation with significant digits. This addresses the UX concern of inconsistent trailing zeros (e.g., $123,456.1 vs $123,456.10).


245-248: LGTM!

The comment has been updated to correctly state "4 significant digits" which now matches the implementation. This addresses the previous concern about documentation contradicting behavior.


399-413: LGTM!

The changes correctly ensure consistent decimal formatting by:

  1. Safely constructing maxDecimalStr with proper fallbacks for missing decimal parts
  2. Padding the final decimal portion to exactly maxDecimals places

This guarantees consistent trailing zeros (e.g., $5.00 instead of $5.0 or $5).

@JohnnyWyles JohnnyWyles requested a review from jasbanza October 15, 2025 15:17
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.

2 participants