Skip to content

Commit 1acb6ce

Browse files
committed
Merge branch 'frontend-decimals'
2 parents de33d72 + 1d89629 commit 1acb6ce

3 files changed

Lines changed: 144 additions & 1 deletion

File tree

frontends/web/src/api/erc20.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,13 @@ export const supportedERC20Tokens: Readonly<TERC20Token[]> = [
2121
{ code: 'eth-erc20-paxg', name: 'Pax Gold', unit: 'PAXG' },
2222
{ code: 'eth-erc20-dai0x6b17', name: 'Dai', unit: 'DAI' },
2323
];
24+
25+
const supportedERC20Units = new Set(
26+
supportedERC20Tokens.map(({ unit }) => unit)
27+
);
28+
29+
export const isSupportedERC20Unit = (
30+
unit: string,
31+
): unit is ERC20TokenUnit => (
32+
supportedERC20Units.has(unit as ERC20TokenUnit)
33+
);

frontends/web/src/components/amount/amount.test.tsx

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,110 @@ describe('Amount formatting', () => {
288288

289289
});
290290

291+
describe('maxDecimals for ETH and ETH tokens', () => {
292+
293+
it('ETH limits decimals to maxDecimals', () => {
294+
const { container } = render(
295+
<Amount amount="10.123456789012345678" unit="ETH" maxDecimals={6} />
296+
);
297+
298+
expect(container.textContent).toBe('10.123456');
299+
});
300+
301+
it('SEPETH limits decimals to maxDecimals', () => {
302+
const { container } = render(
303+
<Amount amount="10.123456789012345678" unit="SEPETH" maxDecimals={4} />
304+
);
305+
306+
expect(container.textContent).toBe('10.1234');
307+
});
308+
309+
it('ETH keeps decimals when they are below maxDecimals', () => {
310+
const { container } = render(
311+
<Amount amount="10.123" unit="ETH" maxDecimals={6} />
312+
);
313+
314+
expect(container.textContent).toBe('10.123');
315+
});
316+
317+
it('ETH keeps decimals when they are exactly maxDecimals', () => {
318+
const { container } = render(
319+
<Amount amount="10.123456" unit="ETH" maxDecimals={6} />
320+
);
321+
322+
expect(container.textContent).toBe('10.123456');
323+
});
324+
325+
it('USDC limits decimals to maxDecimals', () => {
326+
const { container } = render(
327+
<Amount amount="1'234.123456789" unit="USDC" maxDecimals={2} />
328+
);
329+
330+
expect(container.textContent).toBe('1’234.12');
331+
});
332+
333+
it('USDC keeps decimals when they are below maxDecimals', () => {
334+
const { container } = render(
335+
<Amount amount="1'234.1" unit="USDC" maxDecimals={2} />
336+
);
337+
338+
expect(container.textContent).toBe('1’234.1');
339+
});
340+
341+
it('DAI limits its 18 decimals to maxDecimals', () => {
342+
const { container } = render(
343+
<Amount
344+
amount="1.123456789012345678"
345+
unit="DAI"
346+
maxDecimals={6}
347+
/>
348+
);
349+
350+
expect(container.textContent).toBe('1.123456');
351+
});
352+
353+
it('DAI limits long decimal values to maxDecimals', () => {
354+
const { container } = render(
355+
<Amount
356+
amount="123'456.123456789012345678"
357+
unit="DAI"
358+
maxDecimals={4}
359+
/>
360+
);
361+
362+
expect(container.textContent).toBe('123’456.1234');
363+
});
364+
365+
});
366+
367+
describe('maxDecimals does not affect BTC/LTC or fiat amounts', () => {
368+
369+
it('does not limit BTC decimals', () => {
370+
const { container } = render(
371+
<Amount amount="0.12345678" unit="BTC" maxDecimals={2} />
372+
);
373+
374+
expect(container.textContent).toBe('0.12345678');
375+
});
376+
377+
it('does not limit LTC decimals', () => {
378+
const { container } = render(
379+
<Amount amount="0.12345678" unit="LTC" maxDecimals={2} />
380+
);
381+
382+
expect(container.textContent).toBe('0.12345678');
383+
});
384+
385+
it('does not limit fiat decimals', () => {
386+
const { container } = render(
387+
<Amount amount="1'340.123456" unit="CHF" maxDecimals={2} />
388+
);
389+
390+
expect(container.textContent).toBe('1’340.123456');
391+
});
392+
393+
});
394+
291395
afterEach(() => {
292396
vi.clearAllMocks();
293397
});

frontends/web/src/components/amount/amount.tsx

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { useContext } from 'react';
44
import type { CoinUnit, ConversionUnit } from '@/api/account';
5+
import { isSupportedERC20Unit } from '@/api/erc20';
56
import { AppContext } from '@/contexts/AppContext';
67
import { LocalizationContext } from '@/contexts/localization-context';
78
import { useMediaQuery } from '@/hooks/mediaquery';
@@ -108,7 +109,11 @@ const formatEth = (
108109
if (dot === -1) {
109110
return formatLocalizedAmount(amount, group, decimal);
110111
}
111-
const truncated = amount.slice(0, dot + maxDecimals + 1);
112+
const truncated = (
113+
maxDecimals === 0
114+
? amount.slice(0, dot)
115+
: amount.slice(0, dot + maxDecimals + 1)
116+
);
112117
return formatLocalizedAmount(truncated, group, decimal);
113118
};
114119

@@ -121,6 +126,26 @@ type TProps = {
121126
removeTrailingZeros?: boolean;
122127
};
123128

129+
/**
130+
* Renders a localized cryptocurrency or fiat amount.
131+
*
132+
* Amounts can be hidden according to the application privacy settings,
133+
* and cryptocurrency-specific formatting is applied for BTC/LTC and
134+
* ETH/ETH-compatible tokens. ETH and ETH-token amounts can optionally
135+
* have their decimal portion truncated with `maxDecimals`.
136+
*
137+
* @param amount - Amount in its string representation.
138+
* @param unit - Currency or cryptocurrency unit of the amount.
139+
* @param alwaysShowAmounts - Whether to display the amount even when
140+
* amounts are hidden by the application settings.
141+
* @param onMobileClick - Callback invoked when the amount is clicked
142+
* on a mobile viewport.
143+
* @param maxDecimals - Maximum number of decimal places to display for
144+
* ETH and ETH-compatible tokens. Excess decimal places are truncated,
145+
* not rounded. Has no effect on BTC/LTC or fiat amounts.
146+
* @param removeTrailingZeros - Whether to remove trailing zeroes from
147+
* the amount's decimal portion.
148+
*/
124149
export const Amount = ({
125150
amount,
126151
unit,
@@ -185,5 +210,9 @@ export const FormattedAmount = ({
185210
return formatEth(displayedAmount, group, decimal, maxDecimals);
186211
}
187212

213+
if (isSupportedERC20Unit(unit)) {
214+
return formatEth(displayedAmount, group, decimal, maxDecimals);
215+
}
216+
188217
return formatLocalizedAmount(displayedAmount, group, decimal);
189218
};

0 commit comments

Comments
 (0)