Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa03ed912c | ||
|
|
505399dac1 | ||
|
|
b9d3a1d626 | ||
|
|
1397aafd25 | ||
|
|
b192603e57 | ||
|
|
8c2c8d987c |
@@ -7,7 +7,7 @@ import {
|
||||
NetworkParams,
|
||||
} from '@vegaprotocol/network-parameters';
|
||||
import { useMarketList } from '@vegaprotocol/markets';
|
||||
import { formatNumber } from '@vegaprotocol/utils';
|
||||
import { formatNumber, formatNumberRounded } from '@vegaprotocol/utils';
|
||||
import { useDiscountProgramsQuery, useFeesQuery } from './__generated__/Fees';
|
||||
import { FeeCard } from './fees-card';
|
||||
import { MarketFees } from './market-fees';
|
||||
@@ -284,7 +284,7 @@ export const CurrentVolume = ({
|
||||
return (
|
||||
<div>
|
||||
<Stat
|
||||
value={formatNumber(windowLengthVolume)}
|
||||
value={formatNumberRounded(new BigNumber(windowLengthVolume))}
|
||||
text={t('Past %s epochs', epochs.toString())}
|
||||
/>
|
||||
{requiredForNextTier > 0 && (
|
||||
|
||||
@@ -5,6 +5,8 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import { formatPercentage, getAdjustedFee } from './utils';
|
||||
import { MarketCodeCell } from '../../client-pages/markets/market-code-cell';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { useNavigateWithMeta } from '../../lib/hooks/use-market-click-handler';
|
||||
import { Links } from '../../lib/links';
|
||||
|
||||
const feesTableColumnDefs = [
|
||||
{ field: 'code', cellRenderer: 'MarketCodeCell' },
|
||||
@@ -51,6 +53,8 @@ export const MarketFees = ({
|
||||
referralDiscount: number;
|
||||
volumeDiscount: number;
|
||||
}) => {
|
||||
const navigateWithMeta = useNavigateWithMeta();
|
||||
|
||||
const rows = compact(markets || []).map((m) => {
|
||||
const infraFee = new BigNumber(m.fees.factors.infrastructureFee);
|
||||
const makerFee = new BigNumber(m.fees.factors.makerFee);
|
||||
@@ -63,6 +67,7 @@ export const MarketFees = ({
|
||||
);
|
||||
|
||||
return {
|
||||
id: m.id,
|
||||
code: m.tradableInstrument.instrument.code,
|
||||
productType: m.tradableInstrument.instrument.product.__typename,
|
||||
infraFee: formatPercentage(infraFee.toNumber()),
|
||||
@@ -80,10 +85,19 @@ export const MarketFees = ({
|
||||
<AgGrid
|
||||
columnDefs={feesTableColumnDefs}
|
||||
rowData={rows}
|
||||
getRowId={({ data }) => data.id}
|
||||
defaultColDef={feesTableDefaultColDef}
|
||||
domLayout="autoHeight"
|
||||
components={components}
|
||||
rowHeight={45}
|
||||
rowClass="cursor-pointer"
|
||||
onRowClicked={({ data, event }) => {
|
||||
navigateWithMeta(
|
||||
Links.MARKET(data.id),
|
||||
// @ts-ignore metaKey and ctrlKey exist
|
||||
event.metaKey || event.ctrlKey
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { getAdjustedFee } from './utils';
|
||||
|
||||
describe('getAdjustedFee', () => {
|
||||
it('simple', () => {
|
||||
const volumeDiscount = 0.5;
|
||||
const referralDiscount = 0.5;
|
||||
|
||||
const infraFee = 0.1;
|
||||
const makerFee = 0.1;
|
||||
const liqFee = 0.1;
|
||||
|
||||
const fees = [
|
||||
new BigNumber(infraFee),
|
||||
new BigNumber(makerFee),
|
||||
new BigNumber(liqFee),
|
||||
];
|
||||
|
||||
const discounts = [
|
||||
new BigNumber(volumeDiscount),
|
||||
new BigNumber(referralDiscount),
|
||||
];
|
||||
|
||||
// 1 - 0.5 - 0.5
|
||||
const v = new BigNumber(1).minus(new BigNumber(volumeDiscount));
|
||||
|
||||
// 1 - 0.5 = 0.5
|
||||
const r = new BigNumber(1).minus(new BigNumber(referralDiscount));
|
||||
|
||||
// 0.5 * 0.5 = 0.25
|
||||
// 1 - 0.25 = 0.75
|
||||
const factor = new BigNumber(1).minus(v.times(r));
|
||||
|
||||
// 0.1 + 0.1 + 0.1 = 0.3
|
||||
const totalFees = fees.reduce((sum, x) => sum.plus(x), new BigNumber(0));
|
||||
|
||||
// 0.3 * 0.75 = 0.225
|
||||
const expected = new BigNumber(totalFees).times(factor).toNumber();
|
||||
|
||||
expect(getAdjustedFee(fees, discounts)).toBe(expected);
|
||||
});
|
||||
|
||||
it('combines discount factors multiplicativly', () => {
|
||||
const volumeDiscount = 0.4;
|
||||
const referralDiscount = 0.1;
|
||||
|
||||
const infraFee = 0.0005;
|
||||
const makerFee = 0.0002;
|
||||
const liqFee = 0.01;
|
||||
|
||||
const fees = [
|
||||
new BigNumber(infraFee),
|
||||
new BigNumber(makerFee),
|
||||
new BigNumber(liqFee),
|
||||
];
|
||||
|
||||
const discounts = [
|
||||
new BigNumber(volumeDiscount),
|
||||
new BigNumber(referralDiscount),
|
||||
];
|
||||
|
||||
// formula for calculating adjusted fees
|
||||
const v = new BigNumber(1).minus(new BigNumber(volumeDiscount));
|
||||
const r = new BigNumber(1).minus(new BigNumber(referralDiscount));
|
||||
const factor = new BigNumber(1).minus(v.times(r));
|
||||
|
||||
// summed fees
|
||||
const totalFees = fees.reduce((sum, x) => sum.plus(x), new BigNumber(0));
|
||||
|
||||
const expected = new BigNumber(totalFees).times(factor).toNumber();
|
||||
|
||||
expect(getAdjustedFee(fees, discounts)).toBe(expected);
|
||||
});
|
||||
});
|
||||
@@ -88,14 +88,18 @@ export const getReferralBenefitTier = (
|
||||
/**
|
||||
* Given a set of fees and a set of discounts return
|
||||
* the adjusted fee factor
|
||||
*
|
||||
* Formula for calculating the adjusted fees
|
||||
* total_discount_factor = 1 - (1 - volumeDiscount) * (1 - referralDiscount)
|
||||
*/
|
||||
export const getAdjustedFee = (fees: BigNumber[], discounts: BigNumber[]) => {
|
||||
const totalFee = fees.reduce((sum, f) => sum.plus(f), new BigNumber(0));
|
||||
const totalDiscount = discounts.reduce(
|
||||
(sum, d) => sum.plus(d),
|
||||
new BigNumber(0)
|
||||
);
|
||||
return totalFee
|
||||
.times(BigNumber.max(0, new BigNumber(1).minus(totalDiscount)))
|
||||
.toNumber();
|
||||
|
||||
const combinedFactors = discounts.reduce((acc, d) => {
|
||||
return acc.times(new BigNumber(1).minus(d));
|
||||
}, new BigNumber(1));
|
||||
|
||||
const totalFactor = new BigNumber(1).minus(combinedFactors);
|
||||
|
||||
return totalFee.times(BigNumber.max(0, totalFactor)).toNumber();
|
||||
};
|
||||
|
||||
@@ -1,77 +1,68 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
import { Sparkline } from './sparkline';
|
||||
import type { SparklineProps } from './sparkline';
|
||||
|
||||
const props = {
|
||||
data: [
|
||||
1, 2, 3, 4, 5, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 6, 7, 8, 9,
|
||||
10, 11, 12,
|
||||
],
|
||||
muted: true,
|
||||
};
|
||||
describe('Sparkline', () => {
|
||||
let props: SparklineProps = {
|
||||
data: [],
|
||||
};
|
||||
|
||||
it('Renders an svg with a single path', () => {
|
||||
render(<Sparkline {...props} />);
|
||||
expect(screen.getByTestId('sparkline-svg')).toBeInTheDocument();
|
||||
const paths = screen.getAllByTestId('sparkline-path');
|
||||
const path = paths[0];
|
||||
expect(path).toBeInTheDocument();
|
||||
expect(path).toHaveAttribute('d', expect.any(String));
|
||||
expect(path).toHaveAttribute('stroke', expect.any(String));
|
||||
expect(path).toHaveAttribute('stroke-width', '1');
|
||||
expect(path).toHaveAttribute('fill', 'transparent');
|
||||
});
|
||||
beforeEach(() => {
|
||||
props = {
|
||||
data: [
|
||||
1, 2, 3, 4, 5, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 6, 7, 8,
|
||||
9, 10, 11, 12,
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
it('Requires a data prop but width and height are optional', () => {
|
||||
render(<Sparkline {...props} />);
|
||||
const svg = screen.getByTestId('sparkline-svg');
|
||||
expect(svg).toHaveAttribute('width', '60');
|
||||
expect(svg).toHaveAttribute('height', '15');
|
||||
});
|
||||
it('Renders an svg with a single path', () => {
|
||||
render(<Sparkline {...props} />);
|
||||
expect(screen.getByTestId('sparkline-svg')).toBeInTheDocument();
|
||||
const paths = screen.getAllByTestId('sparkline-path');
|
||||
const path = paths[0];
|
||||
expect(path).toBeInTheDocument();
|
||||
expect(path).toHaveAttribute('d', expect.any(String));
|
||||
expect(path).toHaveAttribute('stroke-width', '1');
|
||||
});
|
||||
|
||||
it('Renders a red line if the last value is less than the first', () => {
|
||||
props.data[0] = 10;
|
||||
props.data[props.data.length - 1] = 5;
|
||||
render(<Sparkline {...props} />);
|
||||
const paths = screen.getAllByTestId('sparkline-path');
|
||||
const path = paths[0];
|
||||
expect(path).toHaveClass(
|
||||
'[vector-effect:non-scaling-stroke] stroke-market-red dark:stroke-market-red'
|
||||
);
|
||||
});
|
||||
it('Requires a data prop but width and height are optional', () => {
|
||||
render(<Sparkline {...props} />);
|
||||
const svg = screen.getByTestId('sparkline-svg');
|
||||
expect(svg).toHaveAttribute('width', '60');
|
||||
expect(svg).toHaveAttribute('height', '15');
|
||||
});
|
||||
|
||||
it('Renders a green line if the last value is greater than the first', () => {
|
||||
props.data[0] = 5;
|
||||
props.data[props.data.length - 1] = 10;
|
||||
props.muted = true;
|
||||
render(<Sparkline {...props} />);
|
||||
const paths = screen.getAllByTestId('sparkline-path');
|
||||
const path = paths[0];
|
||||
expect(path).toHaveClass(
|
||||
'[vector-effect:non-scaling-stroke] stroke-market-green-600 dark:stroke-market-green'
|
||||
);
|
||||
});
|
||||
it('Renders a red line if the last value is less than the first', () => {
|
||||
props.data[0] = 10;
|
||||
props.data[props.data.length - 1] = 5;
|
||||
render(<Sparkline {...props} />);
|
||||
const paths = screen.getAllByTestId('sparkline-path');
|
||||
const path = paths[0];
|
||||
expect(path).toHaveClass(
|
||||
'[vector-effect:non-scaling-stroke] stroke-market-red dark:stroke-market-red'
|
||||
);
|
||||
});
|
||||
|
||||
it('Renders a white line if the first and last values are equal', () => {
|
||||
props.data[0] = 5;
|
||||
props.data[props.data.length - 1] = 5;
|
||||
render(<Sparkline {...props} />);
|
||||
const paths = screen.getAllByTestId('sparkline-path');
|
||||
const path = paths[0];
|
||||
expect(path).toHaveClass(
|
||||
'[vector-effect:non-scaling-stroke] stroke-black/40 dark:stroke-white/40'
|
||||
);
|
||||
});
|
||||
it('Renders a green line if the last value is greater than the first', () => {
|
||||
props.data[0] = 5;
|
||||
props.data[props.data.length - 1] = 10;
|
||||
render(<Sparkline {...props} />);
|
||||
const paths = screen.getAllByTestId('sparkline-path');
|
||||
const path = paths[0];
|
||||
expect(path).toHaveClass(
|
||||
'[vector-effect:non-scaling-stroke] stroke-market-green-600 dark:stroke-market-green'
|
||||
);
|
||||
});
|
||||
|
||||
it('Renders a gray line if there are not 24 values', () => {
|
||||
props.data = [
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
|
||||
22, 23,
|
||||
];
|
||||
render(<Sparkline {...props} />);
|
||||
const paths = screen.queryAllByTestId('sparkline-path');
|
||||
expect(paths).toHaveLength(2);
|
||||
expect(paths[0]).toHaveClass(
|
||||
'[vector-effect:non-scaling-stroke] stroke-black/40 dark:stroke-white/40'
|
||||
);
|
||||
it('Renders a white line if the first and last values are equal', () => {
|
||||
props.data[0] = 5;
|
||||
props.data[props.data.length - 1] = 5;
|
||||
render(<Sparkline {...props} />);
|
||||
const paths = screen.getAllByTestId('sparkline-path');
|
||||
const path = paths[0];
|
||||
expect(path).toHaveClass(
|
||||
'[vector-effect:non-scaling-stroke] stroke-black/40 dark:stroke-white/40'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,39 +8,49 @@ export default {
|
||||
|
||||
const Template: Story = (args) => <Sparkline data={args['data']} {...args} />;
|
||||
|
||||
export const Grey = Template.bind({});
|
||||
Grey.args = {
|
||||
data: [
|
||||
1, 2, 3, 4, 5, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 6, 7, 8,
|
||||
],
|
||||
width: 60,
|
||||
height: 30,
|
||||
points: 25,
|
||||
className: 'w-[113px]',
|
||||
};
|
||||
|
||||
export const Equal = Template.bind({});
|
||||
Equal.args = {
|
||||
data: [
|
||||
12, 2, 3, 4, 5, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 6, 7, 8, 9,
|
||||
10, 11, 12,
|
||||
],
|
||||
width: 60,
|
||||
width: 110,
|
||||
height: 30,
|
||||
points: 25,
|
||||
className: 'w-[113px]',
|
||||
};
|
||||
|
||||
export const Increase = Template.bind({});
|
||||
Increase.args = {
|
||||
data: [
|
||||
1, 2, 3, 4, 5, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 6, 7, 8, 9,
|
||||
10, 11, 12,
|
||||
22,
|
||||
22,
|
||||
22, // extra values should be ignored, this should still render an increase
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
13,
|
||||
14,
|
||||
15,
|
||||
16,
|
||||
17,
|
||||
18,
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
22,
|
||||
23,
|
||||
24,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
],
|
||||
width: 60,
|
||||
width: 110,
|
||||
height: 30,
|
||||
points: 25,
|
||||
className: 'w-[113px]',
|
||||
};
|
||||
|
||||
export const Decrease = Template.bind({});
|
||||
@@ -49,8 +59,27 @@ Decrease.args = {
|
||||
12, 2, 3, 4, 5, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 6, 7, 8, 9,
|
||||
10, 11, 1,
|
||||
],
|
||||
width: 60,
|
||||
width: 110,
|
||||
height: 30,
|
||||
};
|
||||
|
||||
export const LessThan24HoursIncrease = Template.bind({});
|
||||
LessThan24HoursIncrease.args = {
|
||||
data: [20, 21, 22, 25, 24, 24, 22, 19, 20, 22, 23, 27],
|
||||
width: 110,
|
||||
height: 30,
|
||||
};
|
||||
|
||||
export const LessThan24HoursDecrease = Template.bind({});
|
||||
LessThan24HoursDecrease.args = {
|
||||
data: [20, 21, 22, 23, 24, 6, 7, 9, 11, 13, 11, 9],
|
||||
width: 110,
|
||||
height: 30,
|
||||
};
|
||||
|
||||
export const NoData = Template.bind({});
|
||||
NoData.args = {
|
||||
data: [],
|
||||
width: 110,
|
||||
height: 30,
|
||||
points: 25,
|
||||
className: 'w-[113px]',
|
||||
};
|
||||
|
||||
@@ -1,15 +1,25 @@
|
||||
import { extent } from 'd3-array';
|
||||
import { scaleLinear } from 'd3-scale';
|
||||
import { line } from 'd3-shape';
|
||||
import { area, line } from 'd3-shape';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import React from 'react';
|
||||
|
||||
function colorByChange(a: number, b: number) {
|
||||
return a === b
|
||||
? 'stroke-black/40 dark:stroke-white/40'
|
||||
: a < b
|
||||
? 'stroke-market-green-600 dark:stroke-market-green'
|
||||
: 'stroke-market-red dark:stroke-market-red';
|
||||
if (a < b) {
|
||||
return 'stroke-market-green-600 dark:stroke-market-green';
|
||||
} else if (a > b) {
|
||||
return 'stroke-market-red dark:stroke-market-red';
|
||||
}
|
||||
return 'stroke-black/40 dark:stroke-white/40';
|
||||
}
|
||||
|
||||
function shadedColor(a: number, b: number) {
|
||||
if (a < b) {
|
||||
return 'fill-market-green-600';
|
||||
} else if (a > b) {
|
||||
return 'fill-market-red';
|
||||
}
|
||||
return 'fill-black dark:fill-white';
|
||||
}
|
||||
|
||||
export interface SparklineProps {
|
||||
@@ -18,75 +28,60 @@ export interface SparklineProps {
|
||||
height?: number;
|
||||
points?: number;
|
||||
className?: string;
|
||||
muted?: boolean;
|
||||
}
|
||||
|
||||
export const SparklineView = ({
|
||||
data,
|
||||
width = 60,
|
||||
height = 15,
|
||||
points = 25,
|
||||
muted = false,
|
||||
points = 24,
|
||||
className,
|
||||
}: SparklineProps) => {
|
||||
// How many points are missing. If market is 12 hours old the 25 - 12
|
||||
const preMarketLength = points - data.length;
|
||||
|
||||
// Create two dimensional array for sparkline points [x, y]
|
||||
const marketData: [number, number][] = data.map((d, i) => [
|
||||
preMarketLength + i,
|
||||
d,
|
||||
]);
|
||||
// Empty two dimensional array for gray, 'no data' line
|
||||
let preMarketData: [number, number][] = [];
|
||||
|
||||
// Get the extent for our y value
|
||||
const [min, max] = extent(marketData, (d) => d[1]);
|
||||
const [min, max] = extent(data, (d) => d);
|
||||
|
||||
if (typeof min !== 'number' || typeof max !== 'number') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a second set of data to render a gray line for any
|
||||
// missing points if the market is less than 24 hours old
|
||||
if (marketData.length < points) {
|
||||
// Populate preMarketData with the average of our extents
|
||||
// so that the line renders centered vertically
|
||||
const fillValue = (min + max) / 2;
|
||||
preMarketData = new Array(points - marketData.length)
|
||||
.fill(fillValue)
|
||||
.map((d: number, i) => [i, d] as [number, number]);
|
||||
const midValue = (min + max) / 2;
|
||||
|
||||
// Add the first point of or market data so that the two
|
||||
// lines join up
|
||||
preMarketData.push(marketData[0] as [number, number]);
|
||||
}
|
||||
// Market may be less than 24hr old so padd the data array
|
||||
// with values that is the mid value (avg of min and max).
|
||||
// This will rendera horizontal line until the real data shifts the line
|
||||
const padCount = data.length < points ? points - data.length : 0;
|
||||
const padArr = new Array(padCount).fill(midValue);
|
||||
const trimmedData = data.slice(-points);
|
||||
|
||||
const xScale = scaleLinear().domain([0, points]).range([0, 100]);
|
||||
const yScale = scaleLinear().domain([min, max]).range([100, 0]);
|
||||
// Get the last 24 values if data has more than needed
|
||||
const lineData: [number, number][] = [...padArr, ...trimmedData].map(
|
||||
(d, i) => {
|
||||
return [i, d];
|
||||
}
|
||||
);
|
||||
|
||||
const xScale = scaleLinear().domain([0, points]).range([0, width]);
|
||||
const yScale = scaleLinear().domain([min, max]).range([height, 0]);
|
||||
|
||||
const lineSeries = line()
|
||||
.x((d) => xScale(d[0]))
|
||||
.y((d) => yScale(d[1]));
|
||||
|
||||
// Get the color of the marketData line
|
||||
const [firstVal, lastVal] = [data[0], data[data.length - 1]];
|
||||
const strokeClassName = muted
|
||||
? data.length >= 24
|
||||
? colorByChange(firstVal, lastVal)
|
||||
: 'stroke-black/40 dark:stroke-white/40'
|
||||
: colorByChange(firstVal, lastVal);
|
||||
const areaSeries = area()
|
||||
.x((d) => xScale(d[0]))
|
||||
.y0(height)
|
||||
.y1((d) => yScale(d[1]));
|
||||
|
||||
const firstVal = trimmedData[0];
|
||||
const lastVal = trimmedData[trimmedData.length - 1];
|
||||
|
||||
// Get the color of the marketData line depending on market movement
|
||||
const strokeClassName = colorByChange(firstVal, lastVal);
|
||||
const areaClassName = shadedColor(firstVal, lastVal);
|
||||
|
||||
// Create paths
|
||||
const preMarketCreationPath = lineSeries(preMarketData);
|
||||
const mainPath = lineSeries(marketData);
|
||||
const pathProps = {
|
||||
'data-testid': 'sparkline-path',
|
||||
className: `[vector-effect:non-scaling-stroke] ${strokeClassName}`,
|
||||
stroke: 'strokeCurrent',
|
||||
strokeWidth: 1,
|
||||
fill: 'transparent',
|
||||
};
|
||||
const linePath = lineSeries(lineData);
|
||||
const areaPath = areaSeries(lineData);
|
||||
|
||||
return (
|
||||
<svg
|
||||
@@ -94,13 +89,25 @@ export const SparklineView = ({
|
||||
className={className}
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox="0 0 100 100"
|
||||
viewBox={`0 0 ${width} ${height}`}
|
||||
preserveAspectRatio="none"
|
||||
>
|
||||
{preMarketCreationPath && (
|
||||
<path {...pathProps} d={preMarketCreationPath} />
|
||||
{linePath && (
|
||||
<path
|
||||
d={linePath}
|
||||
data-testid="sparkline-path"
|
||||
className={`[vector-effect:non-scaling-stroke] fill-transparent ${strokeClassName}`}
|
||||
strokeWidth={1}
|
||||
/>
|
||||
)}
|
||||
{areaPath && (
|
||||
<path
|
||||
className={areaClassName}
|
||||
fillOpacity={0.2}
|
||||
stroke="none"
|
||||
d={areaPath}
|
||||
/>
|
||||
)}
|
||||
{mainPath && <path {...pathProps} d={mainPath} />}
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -179,3 +179,33 @@ export const toNumberParts = (
|
||||
export const isNumeric = (
|
||||
value?: string | number | BigNumber | bigint | null
|
||||
): value is NonNullable<number | string> => /^-?\d*\.?\d+$/.test(String(value));
|
||||
|
||||
/**
|
||||
* Format a number greater than 1 million with m for million, b for billion
|
||||
* and t for trillion
|
||||
*/
|
||||
export const formatNumberRounded = (num: BigNumber) => {
|
||||
let value = '';
|
||||
|
||||
const format = (divisor: string) => {
|
||||
const result = num.dividedBy(divisor);
|
||||
return result.isInteger() ? result.toString() : result.toFixed(1);
|
||||
};
|
||||
|
||||
if (num.isGreaterThan(new BigNumber('1e14'))) {
|
||||
value = '>100t';
|
||||
} else if (num.isGreaterThanOrEqualTo(new BigNumber('1e12'))) {
|
||||
// Trillion
|
||||
value = `${format('1e12')}t`;
|
||||
} else if (num.isGreaterThanOrEqualTo(new BigNumber('1e9'))) {
|
||||
// Billion
|
||||
value = `${format('1e9')}b`;
|
||||
} else if (num.isGreaterThanOrEqualTo(new BigNumber('1e6'))) {
|
||||
// Million
|
||||
value = `${format('1e6')}m`;
|
||||
} else {
|
||||
value = formatNumber(num);
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user