@lightsparkdev/core 1.2.6 → 1.2.7
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 +8 -0
- package/dist/{chunk-CKJUUY2Z.js → chunk-NY4EJZNK.js} +121 -35
- package/dist/{index-COxEW2dD.d.cts → index-r0bUxDu_.d.cts} +31 -10
- package/dist/{index-COxEW2dD.d.ts → index-r0bUxDu_.d.ts} +31 -10
- package/dist/index.cjs +123 -35
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -1
- package/dist/utils/index.cjs +123 -35
- package/dist/utils/index.d.cts +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +5 -1
- package/package.json +1 -1
- package/src/utils/currency.ts +195 -42
- package/src/utils/locale.ts +2 -0
- package/src/utils/tests/currency.test.ts +183 -1
package/src/utils/currency.ts
CHANGED
|
@@ -20,6 +20,7 @@ export const CurrencyUnit = {
|
|
|
20
20
|
MILLIBITCOIN: "MILLIBITCOIN",
|
|
21
21
|
USD: "USD",
|
|
22
22
|
MXN: "MXN",
|
|
23
|
+
PHP: "PHP",
|
|
23
24
|
|
|
24
25
|
Bitcoin: "BITCOIN",
|
|
25
26
|
Microbitcoin: "MICROBITCOIN",
|
|
@@ -29,6 +30,7 @@ export const CurrencyUnit = {
|
|
|
29
30
|
Satoshi: "SATOSHI",
|
|
30
31
|
Usd: "USD",
|
|
31
32
|
Mxn: "MXN",
|
|
33
|
+
Php: "PHP",
|
|
32
34
|
} as const;
|
|
33
35
|
|
|
34
36
|
export type CurrencyUnitType = (typeof CurrencyUnit)[keyof typeof CurrencyUnit];
|
|
@@ -56,6 +58,7 @@ const standardUnitConversionObj = {
|
|
|
56
58
|
/* Converting between two different fiat types is not currently supported */
|
|
57
59
|
[CurrencyUnit.USD]: (v: number) => v,
|
|
58
60
|
[CurrencyUnit.MXN]: (v: number) => v,
|
|
61
|
+
[CurrencyUnit.PHP]: (v: number) => v,
|
|
59
62
|
};
|
|
60
63
|
|
|
61
64
|
/* Round without decimals since we're returning cents: */
|
|
@@ -82,6 +85,7 @@ const CONVERSION_MAP = {
|
|
|
82
85
|
[CurrencyUnit.SATOSHI]: (v: number) => v * 100_000_000,
|
|
83
86
|
[CurrencyUnit.USD]: toBitcoinConversion,
|
|
84
87
|
[CurrencyUnit.MXN]: toBitcoinConversion,
|
|
88
|
+
[CurrencyUnit.PHP]: toBitcoinConversion,
|
|
85
89
|
},
|
|
86
90
|
[CurrencyUnit.MICROBITCOIN]: {
|
|
87
91
|
[CurrencyUnit.BITCOIN]: (v: number) => v / 1_000_000,
|
|
@@ -92,6 +96,7 @@ const CONVERSION_MAP = {
|
|
|
92
96
|
[CurrencyUnit.SATOSHI]: (v: number) => v * 100,
|
|
93
97
|
[CurrencyUnit.USD]: toMicrobitcoinConversion,
|
|
94
98
|
[CurrencyUnit.MXN]: toMicrobitcoinConversion,
|
|
99
|
+
[CurrencyUnit.PHP]: toMicrobitcoinConversion,
|
|
95
100
|
},
|
|
96
101
|
[CurrencyUnit.MILLIBITCOIN]: {
|
|
97
102
|
[CurrencyUnit.BITCOIN]: (v: number) => v / 1_000,
|
|
@@ -102,6 +107,7 @@ const CONVERSION_MAP = {
|
|
|
102
107
|
[CurrencyUnit.SATOSHI]: (v: number) => v * 100_000,
|
|
103
108
|
[CurrencyUnit.USD]: toMillibitcoinConversion,
|
|
104
109
|
[CurrencyUnit.MXN]: toMillibitcoinConversion,
|
|
110
|
+
[CurrencyUnit.PHP]: toMillibitcoinConversion,
|
|
105
111
|
},
|
|
106
112
|
[CurrencyUnit.MILLISATOSHI]: {
|
|
107
113
|
[CurrencyUnit.BITCOIN]: (v: number) => v / 100_000_000_000,
|
|
@@ -112,6 +118,7 @@ const CONVERSION_MAP = {
|
|
|
112
118
|
[CurrencyUnit.SATOSHI]: (v: number) => v / 1000,
|
|
113
119
|
[CurrencyUnit.USD]: toMillisatoshiConversion,
|
|
114
120
|
[CurrencyUnit.MXN]: toMillisatoshiConversion,
|
|
121
|
+
[CurrencyUnit.PHP]: toMillisatoshiConversion,
|
|
115
122
|
},
|
|
116
123
|
[CurrencyUnit.NANOBITCOIN]: {
|
|
117
124
|
[CurrencyUnit.BITCOIN]: (v: number) => v / 1_000_000_000,
|
|
@@ -122,6 +129,7 @@ const CONVERSION_MAP = {
|
|
|
122
129
|
[CurrencyUnit.SATOSHI]: (v: number) => v / 10,
|
|
123
130
|
[CurrencyUnit.USD]: toNanobitcoinConversion,
|
|
124
131
|
[CurrencyUnit.MXN]: toNanobitcoinConversion,
|
|
132
|
+
[CurrencyUnit.PHP]: toNanobitcoinConversion,
|
|
125
133
|
},
|
|
126
134
|
[CurrencyUnit.SATOSHI]: {
|
|
127
135
|
[CurrencyUnit.BITCOIN]: (v: number) => v / 100_000_000,
|
|
@@ -132,9 +140,11 @@ const CONVERSION_MAP = {
|
|
|
132
140
|
[CurrencyUnit.SATOSHI]: (v: number) => v,
|
|
133
141
|
[CurrencyUnit.USD]: toSatoshiConversion,
|
|
134
142
|
[CurrencyUnit.MXN]: toSatoshiConversion,
|
|
143
|
+
[CurrencyUnit.PHP]: toSatoshiConversion,
|
|
135
144
|
},
|
|
136
145
|
[CurrencyUnit.USD]: standardUnitConversionObj,
|
|
137
146
|
[CurrencyUnit.MXN]: standardUnitConversionObj,
|
|
147
|
+
[CurrencyUnit.PHP]: standardUnitConversionObj,
|
|
138
148
|
};
|
|
139
149
|
|
|
140
150
|
export function convertCurrencyAmountValue(
|
|
@@ -146,7 +156,7 @@ export function convertCurrencyAmountValue(
|
|
|
146
156
|
units to provide value estimates where needed where a backend value is not available, eg
|
|
147
157
|
previewing the approximate value of an amount to send. */
|
|
148
158
|
unitsPerBtc = 1,
|
|
149
|
-
)
|
|
159
|
+
) {
|
|
150
160
|
if (
|
|
151
161
|
fromUnit === CurrencyUnit.FUTURE_VALUE ||
|
|
152
162
|
toUnit === CurrencyUnit.FUTURE_VALUE
|
|
@@ -198,6 +208,7 @@ export type CurrencyMap = {
|
|
|
198
208
|
[CurrencyUnit.NANOBITCOIN]: number;
|
|
199
209
|
[CurrencyUnit.USD]: number;
|
|
200
210
|
[CurrencyUnit.MXN]: number;
|
|
211
|
+
[CurrencyUnit.PHP]: number;
|
|
201
212
|
[CurrencyUnit.FUTURE_VALUE]: number;
|
|
202
213
|
formatted: {
|
|
203
214
|
sats: string;
|
|
@@ -211,6 +222,7 @@ export type CurrencyMap = {
|
|
|
211
222
|
[CurrencyUnit.NANOBITCOIN]: string;
|
|
212
223
|
[CurrencyUnit.USD]: string;
|
|
213
224
|
[CurrencyUnit.MXN]: string;
|
|
225
|
+
[CurrencyUnit.PHP]: string;
|
|
214
226
|
[CurrencyUnit.FUTURE_VALUE]: string;
|
|
215
227
|
};
|
|
216
228
|
isZero: boolean;
|
|
@@ -220,34 +232,47 @@ export type CurrencyMap = {
|
|
|
220
232
|
type: "CurrencyMap";
|
|
221
233
|
};
|
|
222
234
|
|
|
235
|
+
/* GQL CurrencyAmountInputs have this shape as well as client side CurrencyAmount objects.
|
|
236
|
+
* Technically value is always a number for GQL inputs. This is enforced by mutation input
|
|
237
|
+
* types. For client side utils we can have slightly more forgiving input and coerce with
|
|
238
|
+
* asNumber. */
|
|
239
|
+
export type CurrencyAmountInputObj = {
|
|
240
|
+
value: number | string | null;
|
|
241
|
+
unit: CurrencyUnitType;
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
/* Persisted CurrencyAmount objects may have this shape if queried from GQL in this format
|
|
245
|
+
but the fields are deprecated and original_unit and original_value should be used instead: */
|
|
223
246
|
export type DeprecatedCurrencyAmountObj = {
|
|
224
|
-
/* Technically the generated graphql schema has value as `any` but it's always a number
|
|
225
|
-
|
|
226
|
-
value?: number | string | null;
|
|
247
|
+
/* Technically the generated graphql schema has value as `any` but it's always a number: */
|
|
248
|
+
value?: number;
|
|
227
249
|
/* assume satoshi if not provided */
|
|
228
250
|
unit?: CurrencyUnitType;
|
|
229
|
-
__typename?: "CurrencyAmount"
|
|
251
|
+
__typename?: "CurrencyAmount";
|
|
230
252
|
};
|
|
231
253
|
|
|
232
254
|
export type CurrencyAmountObj = {
|
|
233
|
-
/* Technically the generated graphql schema has value as `any` but it's always a number
|
|
234
|
-
|
|
235
|
-
original_value?: number | string | null;
|
|
255
|
+
/* Technically the generated graphql schema has value as `any` but it's always a number: */
|
|
256
|
+
original_value?: number;
|
|
236
257
|
/* assume satoshi if not provided */
|
|
237
258
|
original_unit?: CurrencyUnitType;
|
|
238
|
-
__typename?: "CurrencyAmount"
|
|
259
|
+
__typename?: "CurrencyAmount";
|
|
239
260
|
};
|
|
240
261
|
|
|
241
262
|
export type CurrencyAmountPreferenceObj = {
|
|
242
|
-
/*
|
|
243
|
-
*
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
263
|
+
/* unit and value, along with original unit and value are all required for
|
|
264
|
+
* CurrencyAmountPreferenceObj - the preferred value is used for the corresponding unit
|
|
265
|
+
* but the original unit/value are also needed to ensure accurate conversion to other units */
|
|
266
|
+
original_value: number;
|
|
267
|
+
original_unit: CurrencyUnitType;
|
|
268
|
+
preferred_currency_unit: CurrencyUnitType;
|
|
269
|
+
/* Technically the generated graphql schema has value as `any` but it's always a number: */
|
|
270
|
+
preferred_currency_value_approx: number;
|
|
271
|
+
__typename?: "CurrencyAmount";
|
|
248
272
|
};
|
|
249
273
|
|
|
250
274
|
export type CurrencyAmountArg =
|
|
275
|
+
| CurrencyAmountInputObj
|
|
251
276
|
| DeprecatedCurrencyAmountObj
|
|
252
277
|
| CurrencyAmountObj
|
|
253
278
|
| CurrencyAmountPreferenceObj
|
|
@@ -255,6 +280,21 @@ export type CurrencyAmountArg =
|
|
|
255
280
|
| undefined
|
|
256
281
|
| null;
|
|
257
282
|
|
|
283
|
+
export function isCurrencyAmountInputObj(
|
|
284
|
+
arg: unknown,
|
|
285
|
+
): arg is CurrencyAmountInputObj {
|
|
286
|
+
return (
|
|
287
|
+
typeof arg === "object" &&
|
|
288
|
+
arg !== null &&
|
|
289
|
+
"value" in arg &&
|
|
290
|
+
(typeof arg.value === "number" ||
|
|
291
|
+
typeof arg.value === "string" ||
|
|
292
|
+
arg.value === null) &&
|
|
293
|
+
"unit" in arg &&
|
|
294
|
+
typeof arg.unit === "string"
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
258
298
|
export function isDeprecatedCurrencyAmountObj(
|
|
259
299
|
arg: unknown,
|
|
260
300
|
): arg is DeprecatedCurrencyAmountObj {
|
|
@@ -278,8 +318,14 @@ export function isCurrencyAmountPreferenceObj(
|
|
|
278
318
|
return (
|
|
279
319
|
typeof arg === "object" &&
|
|
280
320
|
arg !== null &&
|
|
321
|
+
"original_unit" in arg &&
|
|
322
|
+
typeof arg.original_unit === "string" &&
|
|
323
|
+
"original_value" in arg &&
|
|
324
|
+
typeof arg.original_value === "number" &&
|
|
281
325
|
"preferred_currency_unit" in arg &&
|
|
282
|
-
"
|
|
326
|
+
typeof arg.preferred_currency_unit === "string" &&
|
|
327
|
+
"preferred_currency_value_approx" in arg &&
|
|
328
|
+
typeof arg.preferred_currency_value_approx === "number"
|
|
283
329
|
);
|
|
284
330
|
}
|
|
285
331
|
|
|
@@ -304,20 +350,20 @@ function asNumber(value: string | number | null | undefined) {
|
|
|
304
350
|
return value || 0;
|
|
305
351
|
}
|
|
306
352
|
|
|
307
|
-
function getCurrencyAmount(currencyAmountArg: CurrencyAmountArg) {
|
|
353
|
+
export function getCurrencyAmount(currencyAmountArg: CurrencyAmountArg) {
|
|
308
354
|
let value = 0;
|
|
309
355
|
let unit = undefined;
|
|
310
356
|
|
|
311
357
|
if (isSDKCurrencyAmount(currencyAmountArg)) {
|
|
312
358
|
value = currencyAmountArg.originalValue;
|
|
313
359
|
unit = currencyAmountArg.originalUnit;
|
|
314
|
-
} else if (isCurrencyAmountPreferenceObj(currencyAmountArg)) {
|
|
315
|
-
value = asNumber(currencyAmountArg.preferred_currency_value_rounded);
|
|
316
|
-
unit = currencyAmountArg.preferred_currency_unit;
|
|
317
360
|
} else if (isCurrencyAmountObj(currencyAmountArg)) {
|
|
318
361
|
value = asNumber(currencyAmountArg.original_value);
|
|
319
362
|
unit = currencyAmountArg.original_unit;
|
|
320
|
-
} else if (
|
|
363
|
+
} else if (
|
|
364
|
+
isCurrencyAmountInputObj(currencyAmountArg) ||
|
|
365
|
+
isDeprecatedCurrencyAmountObj(currencyAmountArg)
|
|
366
|
+
) {
|
|
321
367
|
value = asNumber(currencyAmountArg.value);
|
|
322
368
|
unit = currencyAmountArg.unit;
|
|
323
369
|
}
|
|
@@ -328,21 +374,71 @@ function getCurrencyAmount(currencyAmountArg: CurrencyAmountArg) {
|
|
|
328
374
|
};
|
|
329
375
|
}
|
|
330
376
|
|
|
377
|
+
function convertCurrencyAmountValues(
|
|
378
|
+
fromUnit: CurrencyUnitType,
|
|
379
|
+
amount: number,
|
|
380
|
+
unitsPerBtc = 1,
|
|
381
|
+
conversionOverride?: { unit: CurrencyUnitType; convertedValue: number },
|
|
382
|
+
) {
|
|
383
|
+
const convert = convertCurrencyAmountValue;
|
|
384
|
+
const namesToUnits = {
|
|
385
|
+
sats: CurrencyUnit.SATOSHI,
|
|
386
|
+
btc: CurrencyUnit.BITCOIN,
|
|
387
|
+
msats: CurrencyUnit.MILLISATOSHI,
|
|
388
|
+
usd: CurrencyUnit.USD,
|
|
389
|
+
mxn: CurrencyUnit.MXN,
|
|
390
|
+
php: CurrencyUnit.PHP,
|
|
391
|
+
mibtc: CurrencyUnit.MICROBITCOIN,
|
|
392
|
+
mlbtc: CurrencyUnit.MILLIBITCOIN,
|
|
393
|
+
nbtc: CurrencyUnit.NANOBITCOIN,
|
|
394
|
+
};
|
|
395
|
+
return Object.entries(namesToUnits).reduce(
|
|
396
|
+
(acc, [name, unit]) => {
|
|
397
|
+
if (conversionOverride && unit === conversionOverride.unit) {
|
|
398
|
+
acc[name as keyof typeof namesToUnits] =
|
|
399
|
+
conversionOverride.convertedValue;
|
|
400
|
+
} else {
|
|
401
|
+
acc[name as keyof typeof namesToUnits] = convert(
|
|
402
|
+
fromUnit,
|
|
403
|
+
unit,
|
|
404
|
+
amount,
|
|
405
|
+
unitsPerBtc,
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
return acc;
|
|
409
|
+
},
|
|
410
|
+
{} as Record<keyof typeof namesToUnits, number>,
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function getPreferredConversionOverride(currencyAmountArg: CurrencyAmountArg) {
|
|
415
|
+
if (isCurrencyAmountPreferenceObj(currencyAmountArg)) {
|
|
416
|
+
return {
|
|
417
|
+
unit: currencyAmountArg.preferred_currency_unit,
|
|
418
|
+
convertedValue: currencyAmountArg.preferred_currency_value_approx,
|
|
419
|
+
};
|
|
420
|
+
} else if (isSDKCurrencyAmount(currencyAmountArg)) {
|
|
421
|
+
return {
|
|
422
|
+
unit: currencyAmountArg.preferredCurrencyUnit,
|
|
423
|
+
convertedValue: currencyAmountArg.preferredCurrencyValueApprox,
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
return undefined;
|
|
427
|
+
}
|
|
428
|
+
|
|
331
429
|
export function mapCurrencyAmount(
|
|
332
430
|
currencyAmountArg: CurrencyAmountArg,
|
|
333
431
|
unitsPerBtc = 1,
|
|
334
432
|
): CurrencyMap {
|
|
335
433
|
const { value, unit } = getCurrencyAmount(currencyAmountArg);
|
|
336
434
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
const mlbtc = convert(unit, CurrencyUnit.MILLIBITCOIN, value, unitsPerBtc);
|
|
345
|
-
const nbtc = convert(unit, CurrencyUnit.NANOBITCOIN, value, unitsPerBtc);
|
|
435
|
+
/* Prefer approximation from backend for corresponding unit if specified on currencyAmountArg.
|
|
436
|
+
* This will always be at most for one single unit type since there's only one
|
|
437
|
+
* preferred_currency_unit on CurrencyAmount types: */
|
|
438
|
+
const conversionOverride = getPreferredConversionOverride(currencyAmountArg);
|
|
439
|
+
|
|
440
|
+
const { sats, msats, btc, usd, mxn, php, mibtc, mlbtc, nbtc } =
|
|
441
|
+
convertCurrencyAmountValues(unit, value, unitsPerBtc, conversionOverride);
|
|
346
442
|
|
|
347
443
|
const mapWithCurrencyUnits = {
|
|
348
444
|
[CurrencyUnit.BITCOIN]: btc,
|
|
@@ -350,6 +446,7 @@ export function mapCurrencyAmount(
|
|
|
350
446
|
[CurrencyUnit.MILLISATOSHI]: msats,
|
|
351
447
|
[CurrencyUnit.USD]: usd,
|
|
352
448
|
[CurrencyUnit.MXN]: mxn,
|
|
449
|
+
[CurrencyUnit.PHP]: php,
|
|
353
450
|
[CurrencyUnit.MICROBITCOIN]: mibtc,
|
|
354
451
|
[CurrencyUnit.MILLIBITCOIN]: mlbtc,
|
|
355
452
|
[CurrencyUnit.NANOBITCOIN]: nbtc,
|
|
@@ -387,6 +484,10 @@ export function mapCurrencyAmount(
|
|
|
387
484
|
value: mxn,
|
|
388
485
|
unit: CurrencyUnit.MXN,
|
|
389
486
|
}),
|
|
487
|
+
[CurrencyUnit.PHP]: formatCurrencyStr({
|
|
488
|
+
value: php,
|
|
489
|
+
unit: CurrencyUnit.PHP,
|
|
490
|
+
}),
|
|
390
491
|
[CurrencyUnit.FUTURE_VALUE]: "-",
|
|
391
492
|
},
|
|
392
493
|
};
|
|
@@ -451,8 +552,16 @@ export const abbrCurrencyUnit = (unit: CurrencyUnitType) => {
|
|
|
451
552
|
return "SAT";
|
|
452
553
|
case CurrencyUnit.MILLISATOSHI:
|
|
453
554
|
return "MSAT";
|
|
555
|
+
case CurrencyUnit.MILLIBITCOIN:
|
|
556
|
+
return "mBTC";
|
|
557
|
+
case CurrencyUnit.MICROBITCOIN:
|
|
558
|
+
return "μBTC";
|
|
454
559
|
case CurrencyUnit.USD:
|
|
455
560
|
return "USD";
|
|
561
|
+
case CurrencyUnit.MXN:
|
|
562
|
+
return "MXN";
|
|
563
|
+
case CurrencyUnit.PHP:
|
|
564
|
+
return "PHP";
|
|
456
565
|
}
|
|
457
566
|
return "Unsupported CurrencyUnit";
|
|
458
567
|
};
|
|
@@ -462,6 +571,15 @@ const defaultOptions = {
|
|
|
462
571
|
precision: undefined,
|
|
463
572
|
compact: false,
|
|
464
573
|
showBtcSymbol: false,
|
|
574
|
+
append: undefined,
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
export type AppendUnitsOptions = {
|
|
578
|
+
plural?: boolean | undefined;
|
|
579
|
+
lowercase?: boolean | undefined;
|
|
580
|
+
/* Default behavior for built in toLocaleString is to not show the unit when it's
|
|
581
|
+
the default unit in the locale (when using default currencyDisplay). We'll do the same. */
|
|
582
|
+
showForCurrentLocaleUnit?: boolean | undefined;
|
|
465
583
|
};
|
|
466
584
|
|
|
467
585
|
type FormatCurrencyStrOptions = {
|
|
@@ -469,6 +587,7 @@ type FormatCurrencyStrOptions = {
|
|
|
469
587
|
precision?: number | "full" | undefined;
|
|
470
588
|
compact?: boolean | undefined;
|
|
471
589
|
showBtcSymbol?: boolean | undefined;
|
|
590
|
+
appendUnits?: AppendUnitsOptions | undefined;
|
|
472
591
|
};
|
|
473
592
|
|
|
474
593
|
export function formatCurrencyStr(
|
|
@@ -484,8 +603,14 @@ export function formatCurrencyStr(
|
|
|
484
603
|
let { value: num } = currencyAmount;
|
|
485
604
|
const { unit } = currencyAmount;
|
|
486
605
|
|
|
487
|
-
|
|
488
|
-
|
|
606
|
+
const centCurrencies = [
|
|
607
|
+
CurrencyUnit.USD,
|
|
608
|
+
CurrencyUnit.MXN,
|
|
609
|
+
CurrencyUnit.PHP,
|
|
610
|
+
] as string[];
|
|
611
|
+
/* centCurrencies are always provided in the smallest unit, e.g. cents for USD. These should be
|
|
612
|
+
* divided by 100 for proper display format: */
|
|
613
|
+
if (centCurrencies.includes(unit)) {
|
|
489
614
|
num = num / 100;
|
|
490
615
|
}
|
|
491
616
|
|
|
@@ -515,39 +640,67 @@ export function formatCurrencyStr(
|
|
|
515
640
|
|
|
516
641
|
const currentLocale = getCurrentLocale();
|
|
517
642
|
|
|
643
|
+
let formattedStr = "";
|
|
518
644
|
switch (unit) {
|
|
645
|
+
case CurrencyUnit.MXN:
|
|
646
|
+
case CurrencyUnit.USD:
|
|
647
|
+
case CurrencyUnit.PHP:
|
|
648
|
+
formattedStr = num.toLocaleString(currentLocale, {
|
|
649
|
+
style: "currency",
|
|
650
|
+
currency: unit,
|
|
651
|
+
currencyDisplay: "narrowSymbol",
|
|
652
|
+
notation: compact ? ("compact" as const) : undefined,
|
|
653
|
+
maximumFractionDigits: getDefaultMaxFractionDigits(2, 2),
|
|
654
|
+
});
|
|
655
|
+
break;
|
|
519
656
|
case CurrencyUnit.BITCOIN:
|
|
520
657
|
/* In most cases product prefers 4 precision digtis for BTC. In a few places
|
|
521
658
|
full precision (8 digits) are preferred, e.g. for a transaction details page: */
|
|
522
|
-
|
|
659
|
+
formattedStr = `${symbol}${num.toLocaleString(currentLocale, {
|
|
523
660
|
notation: compact ? ("compact" as const) : undefined,
|
|
524
661
|
maximumFractionDigits: getDefaultMaxFractionDigits(4, 8),
|
|
525
662
|
})}`;
|
|
663
|
+
break;
|
|
526
664
|
case CurrencyUnit.SATOSHI:
|
|
527
665
|
/* In most cases product prefers hiding sub sat precision (msats). In a few
|
|
528
666
|
places full precision (3 digits) are preferred, e.g. for Lightning fees
|
|
529
667
|
paid on a transaction details page: */
|
|
530
|
-
|
|
668
|
+
formattedStr = `${symbol}${num.toLocaleString(currentLocale, {
|
|
531
669
|
notation: compact ? ("compact" as const) : undefined,
|
|
532
670
|
maximumFractionDigits: getDefaultMaxFractionDigits(0, 3),
|
|
533
671
|
})}`;
|
|
672
|
+
break;
|
|
534
673
|
case CurrencyUnit.MILLISATOSHI:
|
|
535
674
|
case CurrencyUnit.MICROBITCOIN:
|
|
536
675
|
case CurrencyUnit.MILLIBITCOIN:
|
|
537
676
|
case CurrencyUnit.NANOBITCOIN:
|
|
538
677
|
default:
|
|
539
|
-
|
|
678
|
+
formattedStr = `${symbol}${num.toLocaleString(currentLocale, {
|
|
540
679
|
notation: compact ? ("compact" as const) : undefined,
|
|
541
680
|
maximumFractionDigits: getDefaultMaxFractionDigits(0, 0),
|
|
542
681
|
})}`;
|
|
543
|
-
case CurrencyUnit.USD:
|
|
544
|
-
return num.toLocaleString(currentLocale, {
|
|
545
|
-
style: "currency",
|
|
546
|
-
currency: defaultCurrencyCode,
|
|
547
|
-
notation: compact ? ("compact" as const) : undefined,
|
|
548
|
-
maximumFractionDigits: getDefaultMaxFractionDigits(2, 2),
|
|
549
|
-
});
|
|
550
682
|
}
|
|
683
|
+
|
|
684
|
+
if (options?.appendUnits) {
|
|
685
|
+
const localeCurrencyCode = localeToCurrencyCode(currentLocale);
|
|
686
|
+
if (
|
|
687
|
+
unit === localeCurrencyCode &&
|
|
688
|
+
!options.appendUnits.showForCurrentLocaleUnit
|
|
689
|
+
) {
|
|
690
|
+
return formattedStr;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
const unitStr = abbrCurrencyUnit(unit);
|
|
694
|
+
const unitSuffix = options.appendUnits.plural && num > 1 ? "s" : "";
|
|
695
|
+
const unitStrWithSuffix = `${unitStr}${unitSuffix}`;
|
|
696
|
+
formattedStr += ` ${
|
|
697
|
+
options.appendUnits.lowercase
|
|
698
|
+
? unitStrWithSuffix.toLowerCase()
|
|
699
|
+
: unitStrWithSuffix
|
|
700
|
+
}`;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
return formattedStr;
|
|
551
704
|
}
|
|
552
705
|
|
|
553
706
|
export function separateCurrencyStrParts(currencyStr: string) {
|
package/src/utils/locale.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import {
|
|
4
4
|
convertCurrencyAmountValue,
|
|
5
5
|
CurrencyUnit,
|
|
6
|
+
formatCurrencyStr,
|
|
6
7
|
localeToCurrencySymbol,
|
|
7
8
|
mapCurrencyAmount,
|
|
8
9
|
separateCurrencyStrParts,
|
|
@@ -138,7 +139,7 @@ describe("convertCurrencyAmountValue", () => {
|
|
|
138
139
|
});
|
|
139
140
|
|
|
140
141
|
describe("mapCurrencyAmount", () => {
|
|
141
|
-
it("should return the expected value for a
|
|
142
|
+
it("should return the expected value for a CurrencyAmountInputObj with number value", () => {
|
|
142
143
|
const currencyMap = mapCurrencyAmount(
|
|
143
144
|
{
|
|
144
145
|
value: 100_000_000,
|
|
@@ -170,6 +171,113 @@ describe("mapCurrencyAmount", () => {
|
|
|
170
171
|
expect(smallerCurrencyMap.isGreaterThan(currencyMap)).toBe(false);
|
|
171
172
|
expect(smallerCurrencyMap.isLessThan(currencyMap)).toBe(true);
|
|
172
173
|
});
|
|
174
|
+
|
|
175
|
+
it("should return the expected value for a CurrencyAmountInputObj with string value", () => {
|
|
176
|
+
const currencyMap = mapCurrencyAmount(
|
|
177
|
+
{
|
|
178
|
+
value: "100000000",
|
|
179
|
+
unit: CurrencyUnit.SATOSHI,
|
|
180
|
+
},
|
|
181
|
+
25_000_00,
|
|
182
|
+
);
|
|
183
|
+
expect(currencyMap.btc).toBe(1);
|
|
184
|
+
expect(currencyMap.USD).toBe(25_000_00);
|
|
185
|
+
expect(currencyMap.sats).toBe(100_000_000);
|
|
186
|
+
expect(currencyMap.msats).toBe(100_000_000_000);
|
|
187
|
+
expect(currencyMap.formatted.btc).toBe("1");
|
|
188
|
+
expect(currencyMap.formatted.USD).toBe("$25,000.00");
|
|
189
|
+
expect(currencyMap.formatted.sats).toBe("100,000,000");
|
|
190
|
+
expect(currencyMap.formatted.msats).toBe("100,000,000,000");
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("should return the expected value for a CurrencyAmountInputObj with null value", () => {
|
|
194
|
+
const currencyMap = mapCurrencyAmount(
|
|
195
|
+
{
|
|
196
|
+
value: null,
|
|
197
|
+
unit: CurrencyUnit.SATOSHI,
|
|
198
|
+
},
|
|
199
|
+
25_000_00,
|
|
200
|
+
);
|
|
201
|
+
expect(currencyMap.btc).toBe(0);
|
|
202
|
+
expect(currencyMap.USD).toBe(0);
|
|
203
|
+
expect(currencyMap.sats).toBe(0);
|
|
204
|
+
expect(currencyMap.msats).toBe(0);
|
|
205
|
+
expect(currencyMap.formatted.btc).toBe("0");
|
|
206
|
+
expect(currencyMap.formatted.USD).toBe("$0.00");
|
|
207
|
+
expect(currencyMap.formatted.sats).toBe("0");
|
|
208
|
+
expect(currencyMap.formatted.msats).toBe("0");
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("should return the expected value for a CurrencyAmountObj", () => {
|
|
212
|
+
const currencyMap = mapCurrencyAmount(
|
|
213
|
+
{
|
|
214
|
+
original_value: 147,
|
|
215
|
+
original_unit: CurrencyUnit.SATOSHI,
|
|
216
|
+
},
|
|
217
|
+
25_000_00,
|
|
218
|
+
);
|
|
219
|
+
expect(currencyMap.btc).toBe(0.00000147);
|
|
220
|
+
expect(currencyMap.USD).toBe(4); // 0.03675 should round to 4 cents
|
|
221
|
+
expect(currencyMap.sats).toBe(147);
|
|
222
|
+
expect(currencyMap.msats).toBe(147_000);
|
|
223
|
+
expect(currencyMap.formatted.btc).toBe("0");
|
|
224
|
+
expect(currencyMap.formatted.USD).toBe("$0.04");
|
|
225
|
+
expect(currencyMap.formatted.sats).toBe("147");
|
|
226
|
+
expect(currencyMap.formatted.msats).toBe("147,000");
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it("should have a type error when extra fields are provided as CurrencyAmountArg", () => {
|
|
230
|
+
mapCurrencyAmount(
|
|
231
|
+
{
|
|
232
|
+
original_value: 147,
|
|
233
|
+
original_unit: CurrencyUnit.SATOSHI,
|
|
234
|
+
/* @ts-expect-error `value` cannot be provided with `original_value` */
|
|
235
|
+
value: 100_000_000,
|
|
236
|
+
},
|
|
237
|
+
25_000_00,
|
|
238
|
+
);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it("should use the backend approximation for the corresponding unit only when provided via CurrencyAmountPreferenceObj", () => {
|
|
242
|
+
const currencyMap = mapCurrencyAmount(
|
|
243
|
+
{
|
|
244
|
+
original_value: 147,
|
|
245
|
+
original_unit: CurrencyUnit.SATOSHI,
|
|
246
|
+
preferred_currency_unit: CurrencyUnit.USD,
|
|
247
|
+
preferred_currency_value_approx: 1_234_56,
|
|
248
|
+
},
|
|
249
|
+
25_000_00,
|
|
250
|
+
);
|
|
251
|
+
expect(currencyMap.btc).toBe(0.00000147);
|
|
252
|
+
expect(currencyMap.USD).toBe(1_234_56);
|
|
253
|
+
expect(currencyMap.sats).toBe(147);
|
|
254
|
+
expect(currencyMap.msats).toBe(147_000);
|
|
255
|
+
expect(currencyMap.formatted.btc).toBe("0");
|
|
256
|
+
expect(currencyMap.formatted.USD).toBe("$1,234.56");
|
|
257
|
+
expect(currencyMap.formatted.sats).toBe("147");
|
|
258
|
+
expect(currencyMap.formatted.msats).toBe("147,000");
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it("should return the expected value for a SDKCurrencyAmountType and use backend approximation for the corresponding unit", () => {
|
|
262
|
+
const currencyMap = mapCurrencyAmount(
|
|
263
|
+
{
|
|
264
|
+
originalValue: 147,
|
|
265
|
+
originalUnit: CurrencyUnit.SATOSHI,
|
|
266
|
+
preferredCurrencyUnit: CurrencyUnit.USD,
|
|
267
|
+
preferredCurrencyValueApprox: 1_234_56,
|
|
268
|
+
preferredCurrencyValueRounded: 1_234_56,
|
|
269
|
+
},
|
|
270
|
+
25_000_00,
|
|
271
|
+
);
|
|
272
|
+
expect(currencyMap.btc).toBe(0.00000147);
|
|
273
|
+
expect(currencyMap.USD).toBe(1_234_56);
|
|
274
|
+
expect(currencyMap.sats).toBe(147);
|
|
275
|
+
expect(currencyMap.msats).toBe(147_000);
|
|
276
|
+
expect(currencyMap.formatted.btc).toBe("0");
|
|
277
|
+
expect(currencyMap.formatted.USD).toBe("$1,234.56");
|
|
278
|
+
expect(currencyMap.formatted.sats).toBe("147");
|
|
279
|
+
expect(currencyMap.formatted.msats).toBe("147,000");
|
|
280
|
+
});
|
|
173
281
|
});
|
|
174
282
|
|
|
175
283
|
describe("localeToCurrencySymbol", () => {
|
|
@@ -254,3 +362,77 @@ describe("separateCurrencyStrParts", () => {
|
|
|
254
362
|
symbol: "$",
|
|
255
363
|
});
|
|
256
364
|
});
|
|
365
|
+
|
|
366
|
+
describe("formatCurrencyStr", () => {
|
|
367
|
+
it("should return the expected currency string", () => {
|
|
368
|
+
expect(
|
|
369
|
+
formatCurrencyStr({
|
|
370
|
+
value: 5000,
|
|
371
|
+
unit: CurrencyUnit.USD,
|
|
372
|
+
}),
|
|
373
|
+
).toBe("$50.00");
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it("should return the expected currency string with precision 1", () => {
|
|
377
|
+
expect(
|
|
378
|
+
formatCurrencyStr(
|
|
379
|
+
{ value: 5000.245235323, unit: CurrencyUnit.Bitcoin },
|
|
380
|
+
{ precision: 1 },
|
|
381
|
+
),
|
|
382
|
+
).toBe("5,000.2");
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it("should return the expected currency string with precision full", () => {
|
|
386
|
+
expect(
|
|
387
|
+
formatCurrencyStr(
|
|
388
|
+
{ value: 5000.245235323, unit: CurrencyUnit.Bitcoin },
|
|
389
|
+
{ precision: "full" },
|
|
390
|
+
),
|
|
391
|
+
).toBe("5,000.24523532");
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it("should return the expected currency string with compact", () => {
|
|
395
|
+
expect(
|
|
396
|
+
formatCurrencyStr(
|
|
397
|
+
{ value: 5000.245235323, unit: CurrencyUnit.Bitcoin },
|
|
398
|
+
{ compact: true },
|
|
399
|
+
),
|
|
400
|
+
).toBe("5K");
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it("should return the expected currency string with appendUnits", () => {
|
|
404
|
+
expect(
|
|
405
|
+
formatCurrencyStr(
|
|
406
|
+
{ value: 100000, unit: CurrencyUnit.Satoshi },
|
|
407
|
+
{ appendUnits: { plural: true, lowercase: true } },
|
|
408
|
+
),
|
|
409
|
+
).toBe("100,000 sats");
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it("should return the expected currency string with appendUnits plural and lowercase", () => {
|
|
413
|
+
expect(
|
|
414
|
+
formatCurrencyStr(
|
|
415
|
+
{ value: 100000, unit: CurrencyUnit.Satoshi },
|
|
416
|
+
{ appendUnits: { plural: true, lowercase: true } },
|
|
417
|
+
),
|
|
418
|
+
).toBe("100,000 sats");
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it("should return the expected currency string with appendUnits plural and lowercase with value < 2", () => {
|
|
422
|
+
expect(
|
|
423
|
+
formatCurrencyStr(
|
|
424
|
+
{ value: 1, unit: CurrencyUnit.Satoshi },
|
|
425
|
+
{ appendUnits: { plural: true, lowercase: true } },
|
|
426
|
+
),
|
|
427
|
+
).toBe("1 sat");
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it("should return the expected currency string with appendUnits plural and lowercase with value < 2", () => {
|
|
431
|
+
expect(
|
|
432
|
+
formatCurrencyStr(
|
|
433
|
+
{ value: 100012, unit: CurrencyUnit.Mxn },
|
|
434
|
+
{ appendUnits: { plural: false } },
|
|
435
|
+
),
|
|
436
|
+
).toBe("$1,000.12 MXN");
|
|
437
|
+
});
|
|
438
|
+
});
|