@rallycash/numbers 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +121 -39
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/number-utils.d.ts +18 -8
- package/dist/number-utils.d.ts.map +1 -1
- package/dist/number-utils.js +65 -24
- package/package.json +1 -1
- package/src/index.ts +3 -0
- package/src/number-utils.test.ts +64 -4
- package/src/number-utils.ts +106 -25
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -18,10 +18,19 @@ yarn add @rallycash/numbers
|
|
|
18
18
|
|
|
19
19
|
## NumberUtils
|
|
20
20
|
|
|
21
|
-
`formatNumber` is the general formatter. The named
|
|
21
|
+
`formatNumber` is the general formatter. The named formatters below are presets on top of it, except `formatUsdCompact`, `abbreviateNumber`, `formatLargeNumber`, and `formatSwapAmount`.
|
|
22
|
+
|
|
23
|
+
Two pairs that look alike:
|
|
24
|
+
|
|
25
|
+
- `formatUsdBalance` is a cash balance. `$1,234.50` stays written out, and abbreviation starts at 10 million.
|
|
26
|
+
- `formatUsdCompact` is a compact dollar headline. Abbreviation starts at one thousand, so the same amount is `$1.23K`.
|
|
27
|
+
- `formatCryptoBalance` is a token balance. It uses grouping, two fraction digits, and subscript dust (`0.0₆123`).
|
|
28
|
+
- `formatSwapAmount` is a swap size. It keeps three significant digits after leading zeros, with no commas and no subscripts (`0.0000150`).
|
|
22
29
|
|
|
23
30
|
### `formatNumber(value, options?)`
|
|
24
31
|
|
|
32
|
+
Use when no preset fits and you need to set the fraction digits, subscript behavior, or abbreviation threshold yourself.
|
|
33
|
+
|
|
25
34
|
```ts
|
|
26
35
|
formatNumber(
|
|
27
36
|
value: number | DecimalLike | null | undefined,
|
|
@@ -39,6 +48,8 @@ formatNumber(
|
|
|
39
48
|
| `abbreviateLargeNumbers` | `true` |
|
|
40
49
|
| `abbrMinSignificantDigits` | `3` |
|
|
41
50
|
| `prefix` | `''` |
|
|
51
|
+
| `showNegativeSign` | `true` |
|
|
52
|
+
| `showPositiveSign` | `false` |
|
|
42
53
|
|
|
43
54
|
`null`, `undefined`, and `NaN` return `''`. `0` returns `0.00` at the default fraction digits.
|
|
44
55
|
|
|
@@ -54,88 +65,159 @@ Dust smaller than `maxDecimals` places, but not small enough for a subscript, re
|
|
|
54
65
|
|
|
55
66
|
This is the server formatter. The web copy abbreviated from 1 trillion, used 4 fraction digits below 10, and rendered that underflow case as `0.00`.
|
|
56
67
|
|
|
57
|
-
###
|
|
68
|
+
### `formatCryptoBalance(value, options?)`
|
|
58
69
|
|
|
59
|
-
|
|
70
|
+
Use when the value is a token balance already in human units, and a million tokens should still read as `1,000,000.00`.
|
|
60
71
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
Token amount. Same defaults as `formatNumber`, including subscripts for dust.
|
|
72
|
+
Atomic units go through `formatBigInt` first.
|
|
64
73
|
|
|
65
74
|
```ts
|
|
75
|
+
// equivalent to -> formatNumber(value)
|
|
76
|
+
// Pass options to override.
|
|
66
77
|
NumberUtils.formatCryptoBalance(1234.5) // "1,234.50"
|
|
67
78
|
NumberUtils.formatCryptoBalance(0.0000001234) // "0.0₆123"
|
|
79
|
+
NumberUtils.formatCryptoBalance(0.000015045814) // "0.000015"
|
|
68
80
|
```
|
|
69
81
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
#### `formatUsdBalance(value, options?)`
|
|
73
|
-
|
|
74
|
-
Cash balance. Replaces `formatCurrencyAmount`.
|
|
82
|
+
### `formatUsdBalance(value, options?)`
|
|
75
83
|
|
|
76
|
-
|
|
84
|
+
Use when the value is someone's cash balance, rewards, or fees, and `$1,234.50` should stay fully written.
|
|
77
85
|
|
|
78
|
-
The `$` is applied
|
|
86
|
+
Replaces `formatCurrencyAmount`. The `$` is applied after formatting, with the sign in front. `showCurrencySymbol` defaults to `true`. `0`, `null`, `undefined`, and `NaN` return `$0.00`. Amounts at or above 10 million still abbreviate. There is no subscript for dust.
|
|
79
87
|
|
|
80
88
|
```ts
|
|
89
|
+
// equivalent to -> formatNumber(value, { minDecimals: 2, maxDecimals: 2, minSignificantDigits: 0, showExponents: false })
|
|
90
|
+
// Pass options to override. `$` is applied outside that call.
|
|
81
91
|
NumberUtils.formatUsdBalance(1234.5) // "$1,234.50"
|
|
82
92
|
NumberUtils.formatUsdBalance(-1.2) // "-$1.20"
|
|
83
93
|
NumberUtils.formatUsdBalance(0) // "$0.00"
|
|
84
94
|
NumberUtils.formatUsdBalance(12_345_678) // "$12.3M"
|
|
95
|
+
NumberUtils.formatUsdBalance(1234.5, { showCurrencySymbol: false }) // "1,234.50"
|
|
96
|
+
NumberUtils.formatUsdBalance(1.2, { showPositiveSign: true }) // "+$1.20"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### `formatUsdCompact(value, options?)`
|
|
100
|
+
|
|
101
|
+
Use when the value is a dollar headline in a tight row: market cap, volume, liquidity, or trade size. Abbreviation starts at one thousand, not ten million.
|
|
102
|
+
|
|
103
|
+
Not a `formatNumber` wrapper. It abbreviates with two fraction digits and keeps trailing zeros, then puts `$` in front. `showCurrencySymbol` defaults to `true`. `0`, `null`, `undefined`, and `NaN` return `$0.00`.
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
NumberUtils.formatUsdCompact(1.5) // "$1.50"
|
|
107
|
+
NumberUtils.formatUsdCompact(1234.5) // "$1.23K"
|
|
108
|
+
NumberUtils.formatUsdCompact(-12_345_678) // "-$12.35M"
|
|
109
|
+
NumberUtils.formatUsdCompact(null) // "$0.00"
|
|
110
|
+
NumberUtils.formatUsdCompact(1234.5, { showCurrencySymbol: false }) // "1.23K"
|
|
85
111
|
```
|
|
86
112
|
|
|
87
|
-
|
|
113
|
+
### `formatPercentage(value, options?)`
|
|
88
114
|
|
|
89
|
-
|
|
115
|
+
Use when the value is already a percent (`12.3` means 12.3%) and the caller will add `%` if the UI needs it.
|
|
90
116
|
|
|
91
|
-
Does not append `%`.
|
|
117
|
+
Does not append `%`. Dust collapses to `0` because subscripts are off.
|
|
92
118
|
|
|
93
119
|
```ts
|
|
120
|
+
// equivalent to -> formatNumber(value, { minDecimals: 0, maxDecimals: 2, minSignificantDigits: 0, showExponents: false })
|
|
121
|
+
// Pass options to override.
|
|
94
122
|
NumberUtils.formatPercentage(12.3) // "12.3"
|
|
95
123
|
NumberUtils.formatPercentage(12.345) // "12.35"
|
|
96
124
|
NumberUtils.formatPercentage(0.0000001234) // "0"
|
|
97
125
|
```
|
|
98
126
|
|
|
99
|
-
|
|
127
|
+
### `formatInteger(value, options?)`
|
|
100
128
|
|
|
101
|
-
|
|
129
|
+
Use when the value is a count: transactions, holders, or points. The fraction is dropped.
|
|
130
|
+
|
|
131
|
+
Counts at or above 10 million still abbreviate.
|
|
102
132
|
|
|
103
133
|
```ts
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
NumberUtils.
|
|
107
|
-
NumberUtils.
|
|
134
|
+
// equivalent to -> formatNumber(value, { minDecimals: 0, maxDecimals: 0, minSignificantDigits: 0, showExponents: false })
|
|
135
|
+
// Pass options to override.
|
|
136
|
+
NumberUtils.formatInteger(6737233.2323223) // "6,737,233"
|
|
137
|
+
NumberUtils.formatInteger(12_345_678, { abbreviateLargeNumbers: false }) // "12,345,678"
|
|
108
138
|
```
|
|
109
139
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
#### `formatInteger(value, options?)`
|
|
140
|
+
### `formatTokenPrice(value)`
|
|
113
141
|
|
|
114
|
-
|
|
142
|
+
Use when the value is a token price in USD and it should follow the general formatter, including subscript dust and the 10 million abbreviation threshold. The caller adds `$`.
|
|
115
143
|
|
|
116
144
|
```ts
|
|
117
|
-
|
|
145
|
+
// equivalent to -> formatNumber(value)
|
|
146
|
+
// Pass options to override.
|
|
147
|
+
NumberUtils.formatTokenPrice(1234.5) // "1,234.50"
|
|
148
|
+
NumberUtils.formatTokenPrice(0.0000001234) // "0.0₆123"
|
|
118
149
|
```
|
|
119
150
|
|
|
120
|
-
###
|
|
151
|
+
### `formatBigInt(atomic, decimals, options?)`
|
|
121
152
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
| `formatBigInt(atomic, decimals, options?)` | Atomic `bigint`, integer string, or `DecimalLike` to a human string, then `formatNumber`. |
|
|
126
|
-
| `abbreviateNumber(value, decimals = 2, preserveTrailingZeros = false)` | `K`/`M`/`B`/`T`/`P`/`E`. Negatives abbreviate. |
|
|
127
|
-
| `formatLargeNumber(value)` | `abbreviateNumber(value, 2)`. |
|
|
128
|
-
| `formatSwapAmount(value)` | Three significant digits after leading zeros. `0.000015045` becomes `0.0000150`. |
|
|
129
|
-
| `expToString(value)` | Scientific notation to a plain decimal string. |
|
|
130
|
-
| `computePercentile(sorted, percentile)` | Linear interpolation. Empty input returns `null`. |
|
|
153
|
+
Use when the value is still in atomic units (`bigint`, integer string, or `DecimalLike`) and the result should look like `formatNumber`.
|
|
154
|
+
|
|
155
|
+
`decimals` is the token scale. A `DecimalLike` is read with `toFixed(0)` and treated as the atomic integer.
|
|
131
156
|
|
|
132
157
|
```ts
|
|
158
|
+
// equivalent to -> formatNumber(humanAmount)
|
|
159
|
+
// Pass options to override. `humanAmount` is the atomic value divided by 10 ** decimals.
|
|
133
160
|
NumberUtils.formatBigInt(1_500000n, 6) // "1.50"
|
|
161
|
+
NumberUtils.formatBigInt('1500000', 6) // "1.50"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### `abbreviateNumber(value, decimals = 2, preserveTrailingZeros = false)`
|
|
165
|
+
|
|
166
|
+
Use when you want `K` / `M` / `B` / `T` / `P` / `E` and you are choosing the fraction digits yourself. Abbreviation starts at 1,000. This does not add `$` or grouping commas.
|
|
167
|
+
|
|
168
|
+
`preserveTrailingZeros` keeps `1.50K` instead of `1.5K`. An exact tier uses that suffix. A coefficient that rounds to `1,000` promotes to the next suffix. Negatives keep the minus.
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
NumberUtils.abbreviateNumber(1_000_000, 2) // "1M"
|
|
172
|
+
NumberUtils.abbreviateNumber(999_500, 0) // "1M"
|
|
173
|
+
NumberUtils.abbreviateNumber(1500, 2) // "1.5K"
|
|
174
|
+
NumberUtils.abbreviateNumber(1500, 2, true) // "1.50K"
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### `formatLargeNumber(value)`
|
|
178
|
+
|
|
179
|
+
Use when a stat such as market cap, liquidity, or volume should abbreviate from one thousand, with two fraction digits and trailing zeros removed. A `DecimalLike` is read with `toNumber()`. This does not add `$`.
|
|
180
|
+
|
|
181
|
+
`formatLargeNumber(1500)` is `1.5K`. `formatUsdCompact(1500)` is `$1.50K`.
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
NumberUtils.formatLargeNumber(1500) // "1.5K"
|
|
185
|
+
NumberUtils.formatLargeNumber(1_000_000) // "1M"
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### `formatSwapAmount(value)`
|
|
189
|
+
|
|
190
|
+
Use when the value is a swap input or output and the UI needs the first three significant digits, including a small amount that must not become a subscript or a rounded balance.
|
|
191
|
+
|
|
192
|
+
No commas, no `$`, no abbreviation, and no `formatNumber` rounding. A negative returns `<0`. `0` returns `0`. Scientific notation is expanded before the split, so `1.5e-7` stays a decimal.
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
NumberUtils.formatSwapAmount(0.15045814096) // "0.150"
|
|
134
196
|
NumberUtils.formatSwapAmount(0.000015045814) // "0.0000150"
|
|
197
|
+
NumberUtils.formatSwapAmount(1.5e-7) // "0.00000015"
|
|
198
|
+
NumberUtils.formatSwapAmount(1234.5) // "1234.5"
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
`formatCryptoBalance(0.000015045814)` is `0.000015`. `formatSwapAmount` keeps the third significant digit: `0.0000150`.
|
|
202
|
+
|
|
203
|
+
### `expToString(value)`
|
|
204
|
+
|
|
205
|
+
Use when a number or numeric string is in scientific notation and a later step needs the full plain decimal, not a display string. The minus stays on the outside, so `-1e-7` is `-0.0000001`.
|
|
206
|
+
|
|
207
|
+
```ts
|
|
135
208
|
NumberUtils.expToString(1e23) // "100000000000000000000000"
|
|
209
|
+
NumberUtils.expToString(-1e-7) // "-0.0000001"
|
|
136
210
|
```
|
|
137
211
|
|
|
138
|
-
|
|
212
|
+
### `computePercentile(sorted, percentile)`
|
|
213
|
+
|
|
214
|
+
Use when you already have an ascending list of numbers and need a linearly interpolated percentile. Empty input returns `null`. `50` on five sorted values is the middle value.
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
NumberUtils.computePercentile([10, 20, 30, 40, 50], 50) // 30
|
|
218
|
+
NumberUtils.computePercentile([10, 20, 30, 40, 50], 95) // 48
|
|
219
|
+
NumberUtils.computePercentile([], 50) // null
|
|
220
|
+
```
|
|
139
221
|
|
|
140
222
|
## BigIntUtils
|
|
141
223
|
|
|
@@ -222,7 +304,7 @@ Prisma's `Decimal` and decimal.js both satisfy `DecimalLike`. Detection is struc
|
|
|
222
304
|
| --- | --- |
|
|
223
305
|
| `formatNumber` | `formatNumber` |
|
|
224
306
|
| `formatCurrencyAmount` | `formatUsdBalance` (adds `$`, sign in front) |
|
|
225
|
-
| `formatUSD` | `
|
|
307
|
+
| `formatUSD` | `formatUsdCompact` |
|
|
226
308
|
| token balance via `formatNumber` | `formatCryptoBalance` |
|
|
227
309
|
| `formatPercentage` | `formatPercentage` |
|
|
228
310
|
| `formatInteger` | `formatInteger` |
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { type BigIntInput, BigIntUtils } from './bigint-utils.js';
|
|
2
2
|
export type { DecimalLike, NumberInput } from './decimal.js';
|
|
3
|
-
export { type FormatNumberOptions, NumberUtils, type UsdInput, } from './number-utils.js';
|
|
3
|
+
export { type FormatNumberOptions, type FormatUsdBalanceOptions, type FormatUsdOptions, NumberUtils, type SignFormatOptions, type UsdInput, } from './number-utils.js';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACjE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC5D,OAAO,EACL,KAAK,mBAAmB,EACxB,WAAW,EACX,KAAK,QAAQ,GACd,MAAM,mBAAmB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACjE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC5D,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,gBAAgB,EACrB,WAAW,EACX,KAAK,iBAAiB,EACtB,KAAK,QAAQ,GACd,MAAM,mBAAmB,CAAA"}
|
package/dist/number-utils.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { type DecimalLike, type NumberInput } from './decimal.js';
|
|
2
|
-
export type
|
|
2
|
+
export type SignFormatOptions = {
|
|
3
|
+
showNegativeSign?: boolean;
|
|
4
|
+
showPositiveSign?: boolean;
|
|
5
|
+
};
|
|
6
|
+
export type FormatNumberOptions = SignFormatOptions & {
|
|
3
7
|
showExponents?: boolean;
|
|
4
8
|
minSignificantDigits?: number;
|
|
5
9
|
maxDecimals?: number;
|
|
@@ -9,21 +13,27 @@ export type FormatNumberOptions = {
|
|
|
9
13
|
abbreviateLargeNumbers?: boolean;
|
|
10
14
|
abbrMinSignificantDigits?: number;
|
|
11
15
|
};
|
|
16
|
+
export type FormatUsdOptions = SignFormatOptions & {
|
|
17
|
+
showCurrencySymbol?: boolean;
|
|
18
|
+
};
|
|
19
|
+
export type FormatUsdBalanceOptions = FormatNumberOptions & {
|
|
20
|
+
showCurrencySymbol?: boolean;
|
|
21
|
+
};
|
|
12
22
|
export type UsdInput = NumberInput | string;
|
|
13
23
|
export declare const expToString: (value: string | number) => string;
|
|
14
24
|
declare function computePercentile(sorted: number[], percentile: number): number | null;
|
|
15
25
|
export declare const NumberUtils: {
|
|
16
|
-
formatNumber: (value: NumberInput,
|
|
26
|
+
formatNumber: (value: NumberInput, options?: FormatNumberOptions) => string;
|
|
17
27
|
formatCryptoBalance: (value: NumberInput, options?: FormatNumberOptions) => string;
|
|
18
|
-
formatUsdBalance: (value: UsdInput, options?:
|
|
28
|
+
formatUsdBalance: (value: UsdInput, options?: FormatUsdBalanceOptions) => string;
|
|
19
29
|
formatPercentage: (value: NumberInput, options?: FormatNumberOptions) => string;
|
|
20
|
-
|
|
30
|
+
formatUsdCompact: (value: UsdInput, options?: FormatUsdOptions) => string;
|
|
21
31
|
formatInteger: (value: NumberInput, options?: FormatNumberOptions) => string;
|
|
22
|
-
formatTokenPrice: (price: NumberInput) => string;
|
|
32
|
+
formatTokenPrice: (price: NumberInput, options?: FormatNumberOptions) => string;
|
|
23
33
|
formatBigInt: (input: bigint | string | DecimalLike, decimals: number, formatOptions?: FormatNumberOptions) => string;
|
|
24
|
-
abbreviateNumber: (num: number, decimals?: number, preserveTrailingZeros?: boolean) => string;
|
|
25
|
-
formatLargeNumber: (value: number | DecimalLike) => string;
|
|
26
|
-
formatSwapAmount: (value: number | string) => string;
|
|
34
|
+
abbreviateNumber: (num: number, decimals?: number, preserveTrailingZeros?: boolean, options?: SignFormatOptions) => string;
|
|
35
|
+
formatLargeNumber: (value: number | DecimalLike, options?: SignFormatOptions) => string;
|
|
36
|
+
formatSwapAmount: (value: number | string, options?: SignFormatOptions) => string;
|
|
27
37
|
expToString: (value: string | number) => string;
|
|
28
38
|
computePercentile: typeof computePercentile;
|
|
29
39
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"number-utils.d.ts","sourceRoot":"","sources":["../src/number-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA;AAG/E,MAAM,MAAM,mBAAmB,GAAG;
|
|
1
|
+
{"version":3,"file":"number-utils.d.ts","sourceRoot":"","sources":["../src/number-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA;AAG/E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG,iBAAiB,GAAG;IACpD,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC,wBAAwB,CAAC,EAAE,MAAM,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,GAAG;IACjD,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG,mBAAmB,GAAG;IAC1D,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,MAAM,CAAA;AAE3C,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,GAAG,MAAM,KAAG,MAMpD,CAAA;AA4ZD,iBAAS,iBAAiB,CACxB,MAAM,EAAE,MAAM,EAAE,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,GAAG,IAAI,CAYf;AAED,eAAO,MAAM,WAAW;0BA/Kf,WAAW,YACT,mBAAmB;iCA+FrB,WAAW,YACR,mBAAmB;8BAftB,QAAQ,YACL,uBAAuB;8BAiBF,WAAW,YAAY,mBAAmB;8BA1B1C,QAAQ,YAAY,gBAAgB;2BA6BvC,WAAW,YAAY,mBAAmB;8BAGvC,WAAW,YAAY,mBAAmB;0BAIlE,MAAM,GAAG,MAAM,GAAG,WAAW,YAC1B,MAAM,kBACA,mBAAmB;4BA7R9B,MAAM,gEAGD,iBAAiB;+BAwSpB,MAAM,GAAG,WAAW,YACjB,iBAAiB;8BAOpB,MAAM,GAAG,MAAM,YACZ,iBAAiB,KAC1B,MAAM;yBA5Y0B,MAAM,GAAG,MAAM,KAAG,MAAM;;CAic1D,CAAA"}
|
package/dist/number-utils.js
CHANGED
|
@@ -56,7 +56,19 @@ function toSubscript(text) {
|
|
|
56
56
|
}
|
|
57
57
|
return result;
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
function applyDisplaySign(formatted, signed, options) {
|
|
60
|
+
if (!formatted)
|
|
61
|
+
return formatted;
|
|
62
|
+
const showNegativeSign = options?.showNegativeSign !== false;
|
|
63
|
+
const showPositiveSign = options?.showPositiveSign === true;
|
|
64
|
+
const unsigned = formatted.startsWith('-') ? formatted.slice(1) : formatted;
|
|
65
|
+
if (signed < 0 && showNegativeSign && formatted.startsWith('-'))
|
|
66
|
+
return formatted;
|
|
67
|
+
if (signed > 0 && showPositiveSign)
|
|
68
|
+
return `+${unsigned}`;
|
|
69
|
+
return unsigned;
|
|
70
|
+
}
|
|
71
|
+
const abbreviateNumber = (num, decimals = 2, preserveTrailingZeros = false, options) => {
|
|
60
72
|
const lookup = [
|
|
61
73
|
{ value: 1, symbol: '' },
|
|
62
74
|
{ value: 1e3, symbol: 'K' },
|
|
@@ -68,8 +80,9 @@ const abbreviateNumber = (num, decimals = 2, preserveTrailingZeros = false) => {
|
|
|
68
80
|
];
|
|
69
81
|
const rx = /\.0+$|(\.[0-9]*[1-9])0+$/;
|
|
70
82
|
const formatFixed = (fixed) => preserveTrailingZeros ? fixed : fixed.replace(rx, '$1');
|
|
71
|
-
if (!Number.isFinite(num))
|
|
72
|
-
return formatFixed(num.toFixed(decimals));
|
|
83
|
+
if (!Number.isFinite(num)) {
|
|
84
|
+
return applyDisplaySign(formatFixed(num.toFixed(decimals)), num, options);
|
|
85
|
+
}
|
|
73
86
|
const absNum = Math.abs(num);
|
|
74
87
|
let tier = 0;
|
|
75
88
|
for (let i = lookup.length - 1; i >= 0; i--) {
|
|
@@ -94,9 +107,9 @@ const abbreviateNumber = (num, decimals = 2, preserveTrailingZeros = false) => {
|
|
|
94
107
|
if (Math.abs(rendered.rounded) >= 1000 && tier < lookup.length - 1) {
|
|
95
108
|
rendered = renderTier(tier + 1);
|
|
96
109
|
}
|
|
97
|
-
return rendered.text;
|
|
110
|
+
return applyDisplaySign(rendered.text, num, options);
|
|
98
111
|
};
|
|
99
|
-
const
|
|
112
|
+
const renderFormatNumber = (value, { showExponents = true, minSignificantDigits = 4, maxDecimals: maxDecimalsParam, minDecimals: minDecimalsParam, abbrThreshold = 1e7, abbreviateLargeNumbers = true, abbrMinSignificantDigits = 3, prefix = '', } = {}) => {
|
|
100
113
|
let minDecimals;
|
|
101
114
|
let maxDecimals;
|
|
102
115
|
if (maxDecimalsParam == null && minDecimalsParam != null) {
|
|
@@ -172,6 +185,13 @@ const formatNumber = (value, { showExponents = true, minSignificantDigits = 4, m
|
|
|
172
185
|
maximumFractionDigits: clampedMaxDecimals,
|
|
173
186
|
})}`;
|
|
174
187
|
};
|
|
188
|
+
const formatNumber = (value, options = {}) => {
|
|
189
|
+
const formatted = renderFormatNumber(value, options);
|
|
190
|
+
const numeric = coerceNumber(value);
|
|
191
|
+
if (numeric == null || !Number.isFinite(numeric))
|
|
192
|
+
return formatted;
|
|
193
|
+
return applyDisplaySign(formatted, numeric, options);
|
|
194
|
+
};
|
|
175
195
|
const USD_BALANCE_PRESET = {
|
|
176
196
|
minDecimals: 2,
|
|
177
197
|
maxDecimals: 2,
|
|
@@ -202,19 +222,35 @@ function coerceUsd(value) {
|
|
|
202
222
|
}
|
|
203
223
|
return coerceNumber(value);
|
|
204
224
|
}
|
|
205
|
-
function formatSignedUsd(value, formatAbs) {
|
|
225
|
+
function formatSignedUsd(value, formatAbs, options) {
|
|
226
|
+
const showCurrencySymbol = options?.showCurrencySymbol !== false;
|
|
227
|
+
const symbol = showCurrencySymbol ? '$' : '';
|
|
206
228
|
const numeric = coerceUsd(value);
|
|
207
|
-
if (numeric == null || !Number.isFinite(numeric) || numeric === 0)
|
|
208
|
-
return
|
|
229
|
+
if (numeric == null || !Number.isFinite(numeric) || numeric === 0) {
|
|
230
|
+
return `${symbol}0.00`;
|
|
231
|
+
}
|
|
209
232
|
const body = formatAbs(Math.abs(numeric));
|
|
210
|
-
|
|
233
|
+
if (numeric < 0 && options?.showNegativeSign !== false)
|
|
234
|
+
return `-${symbol}${body}`;
|
|
235
|
+
if (numeric > 0 && options?.showPositiveSign === true)
|
|
236
|
+
return `+${symbol}${body}`;
|
|
237
|
+
return `${symbol}${body}`;
|
|
211
238
|
}
|
|
212
|
-
|
|
213
|
-
|
|
239
|
+
function absoluteUsdFormatOptions(options) {
|
|
240
|
+
if (options === undefined)
|
|
241
|
+
return undefined;
|
|
242
|
+
return {
|
|
243
|
+
...options,
|
|
244
|
+
showNegativeSign: true,
|
|
245
|
+
showPositiveSign: false,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
const formatUsdCompact = (value, options) => formatSignedUsd(value, (absolute) => abbreviateNumber(absolute, 2, true), options);
|
|
249
|
+
const formatUsdBalance = (value, options) => formatSignedUsd(value, (absolute) => formatNumber(absolute, mergePreset(USD_BALANCE_PRESET, absoluteUsdFormatOptions(options))), options);
|
|
214
250
|
const formatCryptoBalance = (value, options) => formatNumber(value, options);
|
|
215
251
|
const formatPercentage = (value, options) => formatNumber(value, mergePreset(PERCENTAGE_PRESET, options));
|
|
216
252
|
const formatInteger = (value, options) => formatNumber(value, mergePreset(INTEGER_PRESET, options));
|
|
217
|
-
const formatTokenPrice = (price) => formatNumber(price);
|
|
253
|
+
const formatTokenPrice = (price, options) => formatNumber(price, options);
|
|
218
254
|
const formatBigInt = (input, decimals, formatOptions) => {
|
|
219
255
|
let atomic;
|
|
220
256
|
if (typeof input === 'bigint') {
|
|
@@ -228,24 +264,29 @@ const formatBigInt = (input, decimals, formatOptions) => {
|
|
|
228
264
|
}
|
|
229
265
|
return formatNumber(Number(formatUnits(atomic, decimals)), formatOptions);
|
|
230
266
|
};
|
|
231
|
-
const formatLargeNumber = (value) => {
|
|
267
|
+
const formatLargeNumber = (value, options) => {
|
|
232
268
|
const numeric = typeof value === 'number' ? value : value.toNumber();
|
|
233
|
-
return abbreviateNumber(numeric, 2);
|
|
269
|
+
return abbreviateNumber(numeric, 2, false, options);
|
|
234
270
|
};
|
|
235
|
-
const formatSwapAmount = (value) => {
|
|
271
|
+
const formatSwapAmount = (value, options) => {
|
|
236
272
|
const number = typeof value === 'string' ? Number.parseFloat(value) : value;
|
|
237
273
|
if (!number || number === 0)
|
|
238
274
|
return '0';
|
|
239
275
|
if (number < 0)
|
|
240
|
-
return '<0';
|
|
276
|
+
return options?.showNegativeSign === false ? '0' : '<0';
|
|
241
277
|
const [integerPart, decimalPart = ''] = expToString(number).split('.');
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
278
|
+
let formatted;
|
|
279
|
+
if (!decimalPart) {
|
|
280
|
+
formatted = integerPart ?? '0';
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
const leadingZeroLength = (decimalPart.match(/^0+/) || [''])[0].length;
|
|
284
|
+
const significantDigits = decimalPart.replace(/^0+/, '').substring(0, 3);
|
|
285
|
+
formatted = significantDigits
|
|
286
|
+
? `${integerPart}.${'0'.repeat(leadingZeroLength)}${significantDigits}`
|
|
287
|
+
: '0';
|
|
288
|
+
}
|
|
289
|
+
return applyDisplaySign(formatted, number, options);
|
|
249
290
|
};
|
|
250
291
|
function computePercentile(sorted, percentile) {
|
|
251
292
|
if (sorted.length === 0)
|
|
@@ -264,7 +305,7 @@ export const NumberUtils = {
|
|
|
264
305
|
formatCryptoBalance,
|
|
265
306
|
formatUsdBalance,
|
|
266
307
|
formatPercentage,
|
|
267
|
-
|
|
308
|
+
formatUsdCompact,
|
|
268
309
|
formatInteger,
|
|
269
310
|
formatTokenPrice,
|
|
270
311
|
formatBigInt,
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -2,6 +2,9 @@ export { type BigIntInput, BigIntUtils } from './bigint-utils.js'
|
|
|
2
2
|
export type { DecimalLike, NumberInput } from './decimal.js'
|
|
3
3
|
export {
|
|
4
4
|
type FormatNumberOptions,
|
|
5
|
+
type FormatUsdBalanceOptions,
|
|
6
|
+
type FormatUsdOptions,
|
|
5
7
|
NumberUtils,
|
|
8
|
+
type SignFormatOptions,
|
|
6
9
|
type UsdInput,
|
|
7
10
|
} from './number-utils.js'
|
package/src/number-utils.test.ts
CHANGED
|
@@ -47,6 +47,17 @@ describe('NumberUtils.formatNumber', () => {
|
|
|
47
47
|
NumberUtils.formatNumber(1_000_000_000, { abbreviateLargeNumbers: false }),
|
|
48
48
|
).toBe('1,000,000,000.00')
|
|
49
49
|
})
|
|
50
|
+
|
|
51
|
+
it('shows a minus by default and a plus only when asked', () => {
|
|
52
|
+
expect(NumberUtils.formatNumber(-1.2)).toBe('-1.20')
|
|
53
|
+
expect(NumberUtils.formatNumber(-1.2, { showNegativeSign: false })).toBe(
|
|
54
|
+
'1.20',
|
|
55
|
+
)
|
|
56
|
+
expect(NumberUtils.formatNumber(1.2, { showPositiveSign: true })).toBe(
|
|
57
|
+
'+1.20',
|
|
58
|
+
)
|
|
59
|
+
expect(NumberUtils.formatNumber(0, { showPositiveSign: true })).toBe('0.00')
|
|
60
|
+
})
|
|
50
61
|
})
|
|
51
62
|
|
|
52
63
|
describe('NumberUtils.abbreviateNumber', () => {
|
|
@@ -70,10 +81,34 @@ describe('NumberUtils presets', () => {
|
|
|
70
81
|
})
|
|
71
82
|
|
|
72
83
|
it('formats abbreviated USD', () => {
|
|
73
|
-
expect(NumberUtils.
|
|
74
|
-
expect(NumberUtils.
|
|
75
|
-
expect(NumberUtils.
|
|
76
|
-
expect(NumberUtils.
|
|
84
|
+
expect(NumberUtils.formatUsdCompact(1.5)).toBe('$1.50')
|
|
85
|
+
expect(NumberUtils.formatUsdCompact(1234.5)).toBe('$1.23K')
|
|
86
|
+
expect(NumberUtils.formatUsdCompact(-12_345_678)).toBe('-$12.35M')
|
|
87
|
+
expect(NumberUtils.formatUsdCompact(null)).toBe('$0.00')
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('can hide the dollar sign and choose the sign character', () => {
|
|
91
|
+
expect(
|
|
92
|
+
NumberUtils.formatUsdBalance(1234.5, { showCurrencySymbol: false }),
|
|
93
|
+
).toBe('1,234.50')
|
|
94
|
+
expect(
|
|
95
|
+
NumberUtils.formatUsdBalance(-1.2, {
|
|
96
|
+
showCurrencySymbol: false,
|
|
97
|
+
showNegativeSign: false,
|
|
98
|
+
}),
|
|
99
|
+
).toBe('1.20')
|
|
100
|
+
expect(
|
|
101
|
+
NumberUtils.formatUsdBalance(1.2, { showPositiveSign: true }),
|
|
102
|
+
).toBe('+$1.20')
|
|
103
|
+
expect(
|
|
104
|
+
NumberUtils.formatUsdCompact(1234.5, { showCurrencySymbol: false }),
|
|
105
|
+
).toBe('1.23K')
|
|
106
|
+
expect(
|
|
107
|
+
NumberUtils.formatUsdCompact(-12_345_678, { showNegativeSign: false }),
|
|
108
|
+
).toBe('$12.35M')
|
|
109
|
+
expect(NumberUtils.formatUsdCompact(1.5, { showPositiveSign: true })).toBe(
|
|
110
|
+
'+$1.50',
|
|
111
|
+
)
|
|
77
112
|
})
|
|
78
113
|
|
|
79
114
|
it('formats a percentage with at most two decimals and no subscript', () => {
|
|
@@ -84,6 +119,9 @@ describe('NumberUtils presets', () => {
|
|
|
84
119
|
|
|
85
120
|
it('formats an integer', () => {
|
|
86
121
|
expect(NumberUtils.formatInteger(6737233.2323223)).toBe('6,737,233')
|
|
122
|
+
expect(
|
|
123
|
+
NumberUtils.formatInteger(12_345_678, { abbreviateLargeNumbers: false }),
|
|
124
|
+
).toBe('12,345,678')
|
|
87
125
|
})
|
|
88
126
|
})
|
|
89
127
|
|
|
@@ -122,4 +160,26 @@ describe('NumberUtils.formatSwapAmount', () => {
|
|
|
122
160
|
expect(NumberUtils.formatSwapAmount(1.5e-7)).toBe('0.00000015')
|
|
123
161
|
expect(NumberUtils.formatSwapAmount(0.15045814096)).toBe('0.150')
|
|
124
162
|
})
|
|
163
|
+
|
|
164
|
+
it('adds a plus only when asked and can hide the negative marker', () => {
|
|
165
|
+
expect(
|
|
166
|
+
NumberUtils.formatSwapAmount(0.15045814096, { showPositiveSign: true }),
|
|
167
|
+
).toBe('+0.150')
|
|
168
|
+
expect(NumberUtils.formatSwapAmount(-1, { showNegativeSign: false })).toBe(
|
|
169
|
+
'0',
|
|
170
|
+
)
|
|
171
|
+
expect(NumberUtils.formatSwapAmount(-1)).toBe('<0')
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
describe('NumberUtils sign options on abbreviation', () => {
|
|
176
|
+
it('applies to abbreviateNumber and formatLargeNumber', () => {
|
|
177
|
+
expect(NumberUtils.abbreviateNumber(-1500, 2)).toBe('-1.5K')
|
|
178
|
+
expect(
|
|
179
|
+
NumberUtils.abbreviateNumber(-1500, 2, false, { showNegativeSign: false }),
|
|
180
|
+
).toBe('1.5K')
|
|
181
|
+
expect(NumberUtils.formatLargeNumber(1500, { showPositiveSign: true })).toBe(
|
|
182
|
+
'+1.5K',
|
|
183
|
+
)
|
|
184
|
+
})
|
|
125
185
|
})
|
package/src/number-utils.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { coerceNumber, type DecimalLike, type NumberInput } from './decimal.js'
|
|
2
2
|
import { formatUnits, parseUnits } from './units.js'
|
|
3
3
|
|
|
4
|
-
export type
|
|
4
|
+
export type SignFormatOptions = {
|
|
5
|
+
showNegativeSign?: boolean
|
|
6
|
+
showPositiveSign?: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type FormatNumberOptions = SignFormatOptions & {
|
|
5
10
|
showExponents?: boolean
|
|
6
11
|
minSignificantDigits?: number
|
|
7
12
|
maxDecimals?: number
|
|
@@ -12,6 +17,14 @@ export type FormatNumberOptions = {
|
|
|
12
17
|
abbrMinSignificantDigits?: number
|
|
13
18
|
}
|
|
14
19
|
|
|
20
|
+
export type FormatUsdOptions = SignFormatOptions & {
|
|
21
|
+
showCurrencySymbol?: boolean
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type FormatUsdBalanceOptions = FormatNumberOptions & {
|
|
25
|
+
showCurrencySymbol?: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
15
28
|
export type UsdInput = NumberInput | string
|
|
16
29
|
|
|
17
30
|
export const expToString = (value: string | number): string => {
|
|
@@ -86,10 +99,25 @@ function toSubscript(text: string | number) {
|
|
|
86
99
|
return result
|
|
87
100
|
}
|
|
88
101
|
|
|
102
|
+
function applyDisplaySign(
|
|
103
|
+
formatted: string,
|
|
104
|
+
signed: number,
|
|
105
|
+
options: SignFormatOptions | undefined,
|
|
106
|
+
): string {
|
|
107
|
+
if (!formatted) return formatted
|
|
108
|
+
const showNegativeSign = options?.showNegativeSign !== false
|
|
109
|
+
const showPositiveSign = options?.showPositiveSign === true
|
|
110
|
+
const unsigned = formatted.startsWith('-') ? formatted.slice(1) : formatted
|
|
111
|
+
if (signed < 0 && showNegativeSign && formatted.startsWith('-')) return formatted
|
|
112
|
+
if (signed > 0 && showPositiveSign) return `+${unsigned}`
|
|
113
|
+
return unsigned
|
|
114
|
+
}
|
|
115
|
+
|
|
89
116
|
const abbreviateNumber = (
|
|
90
117
|
num: number,
|
|
91
118
|
decimals = 2,
|
|
92
119
|
preserveTrailingZeros = false,
|
|
120
|
+
options?: SignFormatOptions,
|
|
93
121
|
) => {
|
|
94
122
|
const lookup = [
|
|
95
123
|
{ value: 1, symbol: '' },
|
|
@@ -105,7 +133,9 @@ const abbreviateNumber = (
|
|
|
105
133
|
const formatFixed = (fixed: string) =>
|
|
106
134
|
preserveTrailingZeros ? fixed : fixed.replace(rx, '$1')
|
|
107
135
|
|
|
108
|
-
if (!Number.isFinite(num))
|
|
136
|
+
if (!Number.isFinite(num)) {
|
|
137
|
+
return applyDisplaySign(formatFixed(num.toFixed(decimals)), num, options)
|
|
138
|
+
}
|
|
109
139
|
|
|
110
140
|
const absNum = Math.abs(num)
|
|
111
141
|
let tier = 0
|
|
@@ -132,10 +162,10 @@ const abbreviateNumber = (
|
|
|
132
162
|
if (Math.abs(rendered.rounded) >= 1000 && tier < lookup.length - 1) {
|
|
133
163
|
rendered = renderTier(tier + 1)
|
|
134
164
|
}
|
|
135
|
-
return rendered.text
|
|
165
|
+
return applyDisplaySign(rendered.text, num, options)
|
|
136
166
|
}
|
|
137
167
|
|
|
138
|
-
const
|
|
168
|
+
const renderFormatNumber = (
|
|
139
169
|
value: NumberInput,
|
|
140
170
|
{
|
|
141
171
|
showExponents = true,
|
|
@@ -256,6 +286,16 @@ const formatNumber = (
|
|
|
256
286
|
)}`
|
|
257
287
|
}
|
|
258
288
|
|
|
289
|
+
const formatNumber = (
|
|
290
|
+
value: NumberInput,
|
|
291
|
+
options: FormatNumberOptions = {},
|
|
292
|
+
) => {
|
|
293
|
+
const formatted = renderFormatNumber(value, options)
|
|
294
|
+
const numeric = coerceNumber(value)
|
|
295
|
+
if (numeric == null || !Number.isFinite(numeric)) return formatted
|
|
296
|
+
return applyDisplaySign(formatted, numeric, options)
|
|
297
|
+
}
|
|
298
|
+
|
|
259
299
|
const USD_BALANCE_PRESET: FormatNumberOptions = {
|
|
260
300
|
minDecimals: 2,
|
|
261
301
|
maxDecimals: 2,
|
|
@@ -296,20 +336,50 @@ function coerceUsd(value: UsdInput): number | null | undefined {
|
|
|
296
336
|
function formatSignedUsd(
|
|
297
337
|
value: UsdInput,
|
|
298
338
|
formatAbs: (absolute: number) => string,
|
|
339
|
+
options?: FormatUsdOptions,
|
|
299
340
|
): string {
|
|
341
|
+
const showCurrencySymbol = options?.showCurrencySymbol !== false
|
|
342
|
+
const symbol = showCurrencySymbol ? '$' : ''
|
|
300
343
|
const numeric = coerceUsd(value)
|
|
301
|
-
if (numeric == null || !Number.isFinite(numeric) || numeric === 0)
|
|
302
|
-
return
|
|
344
|
+
if (numeric == null || !Number.isFinite(numeric) || numeric === 0) {
|
|
345
|
+
return `${symbol}0.00`
|
|
346
|
+
}
|
|
303
347
|
const body = formatAbs(Math.abs(numeric))
|
|
304
|
-
|
|
348
|
+
if (numeric < 0 && options?.showNegativeSign !== false) return `-${symbol}${body}`
|
|
349
|
+
if (numeric > 0 && options?.showPositiveSign === true) return `+${symbol}${body}`
|
|
350
|
+
return `${symbol}${body}`
|
|
305
351
|
}
|
|
306
352
|
|
|
307
|
-
|
|
308
|
-
|
|
353
|
+
function absoluteUsdFormatOptions(
|
|
354
|
+
options: FormatUsdBalanceOptions | undefined,
|
|
355
|
+
): FormatNumberOptions | undefined {
|
|
356
|
+
if (options === undefined) return undefined
|
|
357
|
+
return {
|
|
358
|
+
...options,
|
|
359
|
+
showNegativeSign: true,
|
|
360
|
+
showPositiveSign: false,
|
|
361
|
+
}
|
|
362
|
+
}
|
|
309
363
|
|
|
310
|
-
const
|
|
311
|
-
formatSignedUsd(
|
|
312
|
-
|
|
364
|
+
const formatUsdCompact = (value: UsdInput, options?: FormatUsdOptions) =>
|
|
365
|
+
formatSignedUsd(
|
|
366
|
+
value,
|
|
367
|
+
(absolute) => abbreviateNumber(absolute, 2, true),
|
|
368
|
+
options,
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
const formatUsdBalance = (
|
|
372
|
+
value: UsdInput,
|
|
373
|
+
options?: FormatUsdBalanceOptions,
|
|
374
|
+
) =>
|
|
375
|
+
formatSignedUsd(
|
|
376
|
+
value,
|
|
377
|
+
(absolute) =>
|
|
378
|
+
formatNumber(
|
|
379
|
+
absolute,
|
|
380
|
+
mergePreset(USD_BALANCE_PRESET, absoluteUsdFormatOptions(options)),
|
|
381
|
+
),
|
|
382
|
+
options,
|
|
313
383
|
)
|
|
314
384
|
|
|
315
385
|
const formatCryptoBalance = (
|
|
@@ -323,7 +393,8 @@ const formatPercentage = (value: NumberInput, options?: FormatNumberOptions) =>
|
|
|
323
393
|
const formatInteger = (value: NumberInput, options?: FormatNumberOptions) =>
|
|
324
394
|
formatNumber(value, mergePreset(INTEGER_PRESET, options))
|
|
325
395
|
|
|
326
|
-
const formatTokenPrice = (price: NumberInput) =>
|
|
396
|
+
const formatTokenPrice = (price: NumberInput, options?: FormatNumberOptions) =>
|
|
397
|
+
formatNumber(price, options)
|
|
327
398
|
|
|
328
399
|
const formatBigInt = (
|
|
329
400
|
input: bigint | string | DecimalLike,
|
|
@@ -341,27 +412,37 @@ const formatBigInt = (
|
|
|
341
412
|
return formatNumber(Number(formatUnits(atomic, decimals)), formatOptions)
|
|
342
413
|
}
|
|
343
414
|
|
|
344
|
-
const formatLargeNumber = (
|
|
415
|
+
const formatLargeNumber = (
|
|
416
|
+
value: number | DecimalLike,
|
|
417
|
+
options?: SignFormatOptions,
|
|
418
|
+
) => {
|
|
345
419
|
const numeric = typeof value === 'number' ? value : value.toNumber()
|
|
346
|
-
return abbreviateNumber(numeric, 2)
|
|
420
|
+
return abbreviateNumber(numeric, 2, false, options)
|
|
347
421
|
}
|
|
348
422
|
|
|
349
|
-
const formatSwapAmount = (
|
|
423
|
+
const formatSwapAmount = (
|
|
424
|
+
value: number | string,
|
|
425
|
+
options?: SignFormatOptions,
|
|
426
|
+
): string => {
|
|
350
427
|
const number = typeof value === 'string' ? Number.parseFloat(value) : value
|
|
351
428
|
|
|
352
429
|
if (!number || number === 0) return '0'
|
|
353
|
-
if (number < 0) return '<0'
|
|
430
|
+
if (number < 0) return options?.showNegativeSign === false ? '0' : '<0'
|
|
354
431
|
|
|
355
432
|
const [integerPart, decimalPart = ''] = expToString(number).split('.')
|
|
356
433
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
434
|
+
let formatted: string
|
|
435
|
+
if (!decimalPart) {
|
|
436
|
+
formatted = integerPart ?? '0'
|
|
437
|
+
} else {
|
|
438
|
+
const leadingZeroLength = (decimalPart.match(/^0+/) || [''])[0].length
|
|
439
|
+
const significantDigits = decimalPart.replace(/^0+/, '').substring(0, 3)
|
|
440
|
+
formatted = significantDigits
|
|
441
|
+
? `${integerPart}.${'0'.repeat(leadingZeroLength)}${significantDigits}`
|
|
442
|
+
: '0'
|
|
443
|
+
}
|
|
363
444
|
|
|
364
|
-
return
|
|
445
|
+
return applyDisplaySign(formatted, number, options)
|
|
365
446
|
}
|
|
366
447
|
|
|
367
448
|
function computePercentile(
|
|
@@ -386,7 +467,7 @@ export const NumberUtils = {
|
|
|
386
467
|
formatCryptoBalance,
|
|
387
468
|
formatUsdBalance,
|
|
388
469
|
formatPercentage,
|
|
389
|
-
|
|
470
|
+
formatUsdCompact,
|
|
390
471
|
formatInteger,
|
|
391
472
|
formatTokenPrice,
|
|
392
473
|
formatBigInt,
|