@lightsparkdev/core 1.4.6 → 1.4.8

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.
@@ -33,7 +33,18 @@ export const CurrencyUnit = {
33
33
  SGD: "SGD",
34
34
  THB: "THB",
35
35
  VND: "VND",
36
+ NGN: "NGN",
37
+ ZAR: "ZAR",
38
+ KES: "KES",
39
+ TZS: "TZS",
40
+ UGX: "UGX",
41
+ BWP: "BWP",
42
+ XOF: "XOF",
43
+ XAF: "XAF",
44
+ MWK: "MWK",
45
+ RWF: "RWF",
36
46
  USDT: "USDT",
47
+ USDC: "USDC",
37
48
 
38
49
  Bitcoin: "BITCOIN",
39
50
  Microbitcoin: "MICROBITCOIN",
@@ -48,6 +59,7 @@ export const CurrencyUnit = {
48
59
  Inr: "INR",
49
60
  Brl: "BRL",
50
61
  Usdt: "USDT",
62
+ Usdc: "USDC",
51
63
  } as const;
52
64
 
53
65
  export type CurrencyUnitType = (typeof CurrencyUnit)[keyof typeof CurrencyUnit];
@@ -88,7 +100,18 @@ const standardUnitConversionObj = {
88
100
  [CurrencyUnit.SGD]: (v: number) => v,
89
101
  [CurrencyUnit.THB]: (v: number) => v,
90
102
  [CurrencyUnit.VND]: (v: number) => v,
103
+ [CurrencyUnit.NGN]: (v: number) => v,
104
+ [CurrencyUnit.ZAR]: (v: number) => v,
105
+ [CurrencyUnit.KES]: (v: number) => v,
106
+ [CurrencyUnit.TZS]: (v: number) => v,
107
+ [CurrencyUnit.UGX]: (v: number) => v,
108
+ [CurrencyUnit.BWP]: (v: number) => v,
109
+ [CurrencyUnit.XOF]: (v: number) => v,
110
+ [CurrencyUnit.XAF]: (v: number) => v,
111
+ [CurrencyUnit.MWK]: (v: number) => v,
112
+ [CurrencyUnit.RWF]: (v: number) => v,
91
113
  [CurrencyUnit.USDT]: (v: number) => v,
114
+ [CurrencyUnit.USDC]: (v: number) => v,
92
115
  };
93
116
 
94
117
  /* Round without decimals since we're returning cents: */
@@ -128,7 +151,18 @@ const CONVERSION_MAP = {
128
151
  [CurrencyUnit.SGD]: toBitcoinConversion,
129
152
  [CurrencyUnit.THB]: toBitcoinConversion,
130
153
  [CurrencyUnit.VND]: toBitcoinConversion,
154
+ [CurrencyUnit.NGN]: toBitcoinConversion,
155
+ [CurrencyUnit.ZAR]: toBitcoinConversion,
156
+ [CurrencyUnit.KES]: toBitcoinConversion,
157
+ [CurrencyUnit.TZS]: toBitcoinConversion,
158
+ [CurrencyUnit.UGX]: toBitcoinConversion,
159
+ [CurrencyUnit.BWP]: toBitcoinConversion,
160
+ [CurrencyUnit.XOF]: toBitcoinConversion,
161
+ [CurrencyUnit.XAF]: toBitcoinConversion,
162
+ [CurrencyUnit.MWK]: toBitcoinConversion,
163
+ [CurrencyUnit.RWF]: toBitcoinConversion,
131
164
  [CurrencyUnit.USDT]: toBitcoinConversion,
165
+ [CurrencyUnit.USDC]: toBitcoinConversion,
132
166
  },
133
167
  [CurrencyUnit.MICROBITCOIN]: {
134
168
  [CurrencyUnit.BITCOIN]: (v: number) => v / 1_000_000,
@@ -152,7 +186,18 @@ const CONVERSION_MAP = {
152
186
  [CurrencyUnit.SGD]: toMicrobitcoinConversion,
153
187
  [CurrencyUnit.THB]: toMicrobitcoinConversion,
154
188
  [CurrencyUnit.VND]: toMicrobitcoinConversion,
189
+ [CurrencyUnit.NGN]: toMicrobitcoinConversion,
190
+ [CurrencyUnit.ZAR]: toMicrobitcoinConversion,
191
+ [CurrencyUnit.KES]: toMicrobitcoinConversion,
192
+ [CurrencyUnit.TZS]: toMicrobitcoinConversion,
193
+ [CurrencyUnit.UGX]: toMicrobitcoinConversion,
194
+ [CurrencyUnit.BWP]: toMicrobitcoinConversion,
195
+ [CurrencyUnit.XOF]: toMicrobitcoinConversion,
196
+ [CurrencyUnit.XAF]: toMicrobitcoinConversion,
197
+ [CurrencyUnit.MWK]: toMicrobitcoinConversion,
198
+ [CurrencyUnit.RWF]: toMicrobitcoinConversion,
155
199
  [CurrencyUnit.USDT]: toMicrobitcoinConversion,
200
+ [CurrencyUnit.USDC]: toMicrobitcoinConversion,
156
201
  },
157
202
  [CurrencyUnit.MILLIBITCOIN]: {
158
203
  [CurrencyUnit.BITCOIN]: (v: number) => v / 1_000,
@@ -176,7 +221,18 @@ const CONVERSION_MAP = {
176
221
  [CurrencyUnit.SGD]: toMillibitcoinConversion,
177
222
  [CurrencyUnit.THB]: toMillibitcoinConversion,
178
223
  [CurrencyUnit.VND]: toMillibitcoinConversion,
224
+ [CurrencyUnit.NGN]: toMillibitcoinConversion,
225
+ [CurrencyUnit.ZAR]: toMillibitcoinConversion,
226
+ [CurrencyUnit.KES]: toMillibitcoinConversion,
227
+ [CurrencyUnit.TZS]: toMillibitcoinConversion,
228
+ [CurrencyUnit.UGX]: toMillibitcoinConversion,
229
+ [CurrencyUnit.BWP]: toMillibitcoinConversion,
230
+ [CurrencyUnit.XOF]: toMillibitcoinConversion,
231
+ [CurrencyUnit.XAF]: toMillibitcoinConversion,
232
+ [CurrencyUnit.MWK]: toMillibitcoinConversion,
233
+ [CurrencyUnit.RWF]: toMillibitcoinConversion,
179
234
  [CurrencyUnit.USDT]: toMillibitcoinConversion,
235
+ [CurrencyUnit.USDC]: toMillibitcoinConversion,
180
236
  },
181
237
  [CurrencyUnit.MILLISATOSHI]: {
182
238
  [CurrencyUnit.BITCOIN]: (v: number) => v / 100_000_000_000,
@@ -200,7 +256,18 @@ const CONVERSION_MAP = {
200
256
  [CurrencyUnit.SGD]: toMillisatoshiConversion,
201
257
  [CurrencyUnit.THB]: toMillisatoshiConversion,
202
258
  [CurrencyUnit.VND]: toMillisatoshiConversion,
259
+ [CurrencyUnit.NGN]: toMillisatoshiConversion,
260
+ [CurrencyUnit.ZAR]: toMillisatoshiConversion,
261
+ [CurrencyUnit.KES]: toMillisatoshiConversion,
262
+ [CurrencyUnit.TZS]: toMillisatoshiConversion,
263
+ [CurrencyUnit.UGX]: toMillisatoshiConversion,
264
+ [CurrencyUnit.BWP]: toMillisatoshiConversion,
265
+ [CurrencyUnit.XOF]: toMillisatoshiConversion,
266
+ [CurrencyUnit.XAF]: toMillisatoshiConversion,
267
+ [CurrencyUnit.MWK]: toMillisatoshiConversion,
268
+ [CurrencyUnit.RWF]: toMillisatoshiConversion,
203
269
  [CurrencyUnit.USDT]: toMillisatoshiConversion,
270
+ [CurrencyUnit.USDC]: toMillisatoshiConversion,
204
271
  },
205
272
  [CurrencyUnit.NANOBITCOIN]: {
206
273
  [CurrencyUnit.BITCOIN]: (v: number) => v / 1_000_000_000,
@@ -224,7 +291,18 @@ const CONVERSION_MAP = {
224
291
  [CurrencyUnit.SGD]: toNanobitcoinConversion,
225
292
  [CurrencyUnit.THB]: toNanobitcoinConversion,
226
293
  [CurrencyUnit.VND]: toNanobitcoinConversion,
294
+ [CurrencyUnit.NGN]: toNanobitcoinConversion,
295
+ [CurrencyUnit.ZAR]: toNanobitcoinConversion,
296
+ [CurrencyUnit.KES]: toNanobitcoinConversion,
297
+ [CurrencyUnit.TZS]: toNanobitcoinConversion,
298
+ [CurrencyUnit.UGX]: toNanobitcoinConversion,
299
+ [CurrencyUnit.BWP]: toNanobitcoinConversion,
300
+ [CurrencyUnit.XOF]: toNanobitcoinConversion,
301
+ [CurrencyUnit.XAF]: toNanobitcoinConversion,
302
+ [CurrencyUnit.MWK]: toNanobitcoinConversion,
303
+ [CurrencyUnit.RWF]: toNanobitcoinConversion,
227
304
  [CurrencyUnit.USDT]: toNanobitcoinConversion,
305
+ [CurrencyUnit.USDC]: toNanobitcoinConversion,
228
306
  },
229
307
  [CurrencyUnit.SATOSHI]: {
230
308
  [CurrencyUnit.BITCOIN]: (v: number) => v / 100_000_000,
@@ -248,7 +326,18 @@ const CONVERSION_MAP = {
248
326
  [CurrencyUnit.SGD]: toSatoshiConversion,
249
327
  [CurrencyUnit.THB]: toSatoshiConversion,
250
328
  [CurrencyUnit.VND]: toSatoshiConversion,
329
+ [CurrencyUnit.NGN]: toSatoshiConversion,
330
+ [CurrencyUnit.ZAR]: toSatoshiConversion,
331
+ [CurrencyUnit.KES]: toSatoshiConversion,
332
+ [CurrencyUnit.TZS]: toSatoshiConversion,
333
+ [CurrencyUnit.UGX]: toSatoshiConversion,
334
+ [CurrencyUnit.BWP]: toSatoshiConversion,
335
+ [CurrencyUnit.XOF]: toSatoshiConversion,
336
+ [CurrencyUnit.XAF]: toSatoshiConversion,
337
+ [CurrencyUnit.MWK]: toSatoshiConversion,
338
+ [CurrencyUnit.RWF]: toSatoshiConversion,
251
339
  [CurrencyUnit.USDT]: toSatoshiConversion,
340
+ [CurrencyUnit.USDC]: toSatoshiConversion,
252
341
  },
253
342
  [CurrencyUnit.USD]: standardUnitConversionObj,
254
343
  [CurrencyUnit.MXN]: standardUnitConversionObj,
@@ -265,7 +354,18 @@ const CONVERSION_MAP = {
265
354
  [CurrencyUnit.SGD]: standardUnitConversionObj,
266
355
  [CurrencyUnit.THB]: standardUnitConversionObj,
267
356
  [CurrencyUnit.VND]: standardUnitConversionObj,
357
+ [CurrencyUnit.NGN]: standardUnitConversionObj,
358
+ [CurrencyUnit.ZAR]: standardUnitConversionObj,
359
+ [CurrencyUnit.KES]: standardUnitConversionObj,
360
+ [CurrencyUnit.TZS]: standardUnitConversionObj,
361
+ [CurrencyUnit.UGX]: standardUnitConversionObj,
362
+ [CurrencyUnit.BWP]: standardUnitConversionObj,
363
+ [CurrencyUnit.XOF]: standardUnitConversionObj,
364
+ [CurrencyUnit.XAF]: standardUnitConversionObj,
365
+ [CurrencyUnit.MWK]: standardUnitConversionObj,
366
+ [CurrencyUnit.RWF]: standardUnitConversionObj,
268
367
  [CurrencyUnit.USDT]: standardUnitConversionObj,
368
+ [CurrencyUnit.USDC]: standardUnitConversionObj,
269
369
  };
270
370
 
271
371
  export function convertCurrencyAmountValue(
@@ -342,7 +442,18 @@ export type CurrencyMap = {
342
442
  [CurrencyUnit.SGD]: number;
343
443
  [CurrencyUnit.THB]: number;
344
444
  [CurrencyUnit.VND]: number;
445
+ [CurrencyUnit.NGN]: number;
446
+ [CurrencyUnit.ZAR]: number;
447
+ [CurrencyUnit.KES]: number;
448
+ [CurrencyUnit.TZS]: number;
449
+ [CurrencyUnit.UGX]: number;
450
+ [CurrencyUnit.BWP]: number;
451
+ [CurrencyUnit.XOF]: number;
452
+ [CurrencyUnit.XAF]: number;
453
+ [CurrencyUnit.MWK]: number;
454
+ [CurrencyUnit.RWF]: number;
345
455
  [CurrencyUnit.USDT]: number;
456
+ [CurrencyUnit.USDC]: number;
346
457
  [CurrencyUnit.FUTURE_VALUE]: number;
347
458
  formatted: {
348
459
  sats: string;
@@ -369,7 +480,18 @@ export type CurrencyMap = {
369
480
  [CurrencyUnit.SGD]: string;
370
481
  [CurrencyUnit.THB]: string;
371
482
  [CurrencyUnit.VND]: string;
483
+ [CurrencyUnit.NGN]: string;
484
+ [CurrencyUnit.ZAR]: string;
485
+ [CurrencyUnit.KES]: string;
486
+ [CurrencyUnit.TZS]: string;
487
+ [CurrencyUnit.UGX]: string;
488
+ [CurrencyUnit.BWP]: string;
489
+ [CurrencyUnit.XOF]: string;
490
+ [CurrencyUnit.XAF]: string;
491
+ [CurrencyUnit.MWK]: string;
492
+ [CurrencyUnit.RWF]: string;
372
493
  [CurrencyUnit.USDT]: string;
494
+ [CurrencyUnit.USDC]: string;
373
495
  [CurrencyUnit.FUTURE_VALUE]: string;
374
496
  };
375
497
  isZero: boolean;
@@ -577,10 +699,21 @@ function convertCurrencyAmountValues(
577
699
  sgd: CurrencyUnit.SGD,
578
700
  thb: CurrencyUnit.THB,
579
701
  vnd: CurrencyUnit.VND,
702
+ ngn: CurrencyUnit.NGN,
703
+ zar: CurrencyUnit.ZAR,
704
+ kes: CurrencyUnit.KES,
705
+ tzs: CurrencyUnit.TZS,
706
+ ugx: CurrencyUnit.UGX,
707
+ bwp: CurrencyUnit.BWP,
708
+ xof: CurrencyUnit.XOF,
709
+ xaf: CurrencyUnit.XAF,
710
+ mwk: CurrencyUnit.MWK,
711
+ rwf: CurrencyUnit.RWF,
580
712
  mibtc: CurrencyUnit.MICROBITCOIN,
581
713
  mlbtc: CurrencyUnit.MILLIBITCOIN,
582
714
  nbtc: CurrencyUnit.NANOBITCOIN,
583
715
  usdt: CurrencyUnit.USDT,
716
+ usdc: CurrencyUnit.USDC,
584
717
  };
585
718
  return Object.entries(namesToUnits).reduce(
586
719
  (acc, [name, unit]) => {
@@ -649,7 +782,18 @@ export function mapCurrencyAmount(
649
782
  sgd,
650
783
  thb,
651
784
  vnd,
785
+ ngn,
786
+ zar,
787
+ kes,
788
+ tzs,
789
+ ugx,
790
+ bwp,
791
+ xof,
792
+ xaf,
793
+ mwk,
794
+ rwf,
652
795
  usdt,
796
+ usdc,
653
797
  } = convertCurrencyAmountValues(unit, value, unitsPerBtc, conversionOverride);
654
798
 
655
799
  const mapWithCurrencyUnits = {
@@ -671,10 +815,21 @@ export function mapCurrencyAmount(
671
815
  [CurrencyUnit.SGD]: sgd,
672
816
  [CurrencyUnit.THB]: thb,
673
817
  [CurrencyUnit.VND]: vnd,
818
+ [CurrencyUnit.NGN]: ngn,
819
+ [CurrencyUnit.ZAR]: zar,
820
+ [CurrencyUnit.KES]: kes,
821
+ [CurrencyUnit.TZS]: tzs,
822
+ [CurrencyUnit.UGX]: ugx,
823
+ [CurrencyUnit.BWP]: bwp,
824
+ [CurrencyUnit.XOF]: xof,
825
+ [CurrencyUnit.XAF]: xaf,
826
+ [CurrencyUnit.MWK]: mwk,
827
+ [CurrencyUnit.RWF]: rwf,
674
828
  [CurrencyUnit.MICROBITCOIN]: mibtc,
675
829
  [CurrencyUnit.MILLIBITCOIN]: mlbtc,
676
830
  [CurrencyUnit.NANOBITCOIN]: nbtc,
677
831
  [CurrencyUnit.USDT]: usdt,
832
+ [CurrencyUnit.USDC]: usdc,
678
833
  [CurrencyUnit.FUTURE_VALUE]: NaN,
679
834
  formatted: {
680
835
  [CurrencyUnit.BITCOIN]: formatCurrencyStr({
@@ -761,10 +916,54 @@ export function mapCurrencyAmount(
761
916
  value: vnd,
762
917
  unit: CurrencyUnit.VND,
763
918
  }),
919
+ [CurrencyUnit.NGN]: formatCurrencyStr({
920
+ value: ngn,
921
+ unit: CurrencyUnit.NGN,
922
+ }),
923
+ [CurrencyUnit.ZAR]: formatCurrencyStr({
924
+ value: zar,
925
+ unit: CurrencyUnit.ZAR,
926
+ }),
927
+ [CurrencyUnit.KES]: formatCurrencyStr({
928
+ value: kes,
929
+ unit: CurrencyUnit.KES,
930
+ }),
931
+ [CurrencyUnit.TZS]: formatCurrencyStr({
932
+ value: tzs,
933
+ unit: CurrencyUnit.TZS,
934
+ }),
935
+ [CurrencyUnit.UGX]: formatCurrencyStr({
936
+ value: ugx,
937
+ unit: CurrencyUnit.UGX,
938
+ }),
939
+ [CurrencyUnit.BWP]: formatCurrencyStr({
940
+ value: bwp,
941
+ unit: CurrencyUnit.BWP,
942
+ }),
943
+ [CurrencyUnit.XOF]: formatCurrencyStr({
944
+ value: xof,
945
+ unit: CurrencyUnit.XOF,
946
+ }),
947
+ [CurrencyUnit.XAF]: formatCurrencyStr({
948
+ value: xaf,
949
+ unit: CurrencyUnit.XAF,
950
+ }),
951
+ [CurrencyUnit.MWK]: formatCurrencyStr({
952
+ value: mwk,
953
+ unit: CurrencyUnit.MWK,
954
+ }),
955
+ [CurrencyUnit.RWF]: formatCurrencyStr({
956
+ value: rwf,
957
+ unit: CurrencyUnit.RWF,
958
+ }),
764
959
  [CurrencyUnit.USDT]: formatCurrencyStr({
765
960
  value: usdt,
766
961
  unit: CurrencyUnit.USDT,
767
962
  }),
963
+ [CurrencyUnit.USDC]: formatCurrencyStr({
964
+ value: usdc,
965
+ unit: CurrencyUnit.USDC,
966
+ }),
768
967
  [CurrencyUnit.FUTURE_VALUE]: "-",
769
968
  },
770
969
  };
@@ -847,6 +1046,8 @@ export const abbrCurrencyUnit = (unit: CurrencyUnitType) => {
847
1046
  return "INR";
848
1047
  case CurrencyUnit.USDT:
849
1048
  return "USDT";
1049
+ case CurrencyUnit.USDC:
1050
+ return "USDC";
850
1051
  case CurrencyUnit.BRL:
851
1052
  return "BRL";
852
1053
  case CurrencyUnit.CAD:
@@ -865,6 +1066,26 @@ export const abbrCurrencyUnit = (unit: CurrencyUnitType) => {
865
1066
  return "THB";
866
1067
  case CurrencyUnit.VND:
867
1068
  return "VND";
1069
+ case CurrencyUnit.NGN:
1070
+ return "NGN";
1071
+ case CurrencyUnit.ZAR:
1072
+ return "ZAR";
1073
+ case CurrencyUnit.KES:
1074
+ return "KES";
1075
+ case CurrencyUnit.TZS:
1076
+ return "TZS";
1077
+ case CurrencyUnit.UGX:
1078
+ return "UGX";
1079
+ case CurrencyUnit.BWP:
1080
+ return "BWP";
1081
+ case CurrencyUnit.XOF:
1082
+ return "XOF";
1083
+ case CurrencyUnit.XAF:
1084
+ return "XAF";
1085
+ case CurrencyUnit.MWK:
1086
+ return "MWK";
1087
+ case CurrencyUnit.RWF:
1088
+ return "RWF";
868
1089
  }
869
1090
  return "Unsupported CurrencyUnit";
870
1091
  };
@@ -921,6 +1142,13 @@ export function formatCurrencyStr(
921
1142
  CurrencyUnit.EUR,
922
1143
  CurrencyUnit.GBP,
923
1144
  CurrencyUnit.INR,
1145
+ CurrencyUnit.BRL,
1146
+ CurrencyUnit.NGN,
1147
+ CurrencyUnit.ZAR,
1148
+ CurrencyUnit.KES,
1149
+ CurrencyUnit.TZS,
1150
+ CurrencyUnit.UGX,
1151
+ CurrencyUnit.BWP,
924
1152
  ] as string[];
925
1153
  /* centCurrencies are always provided in the smallest unit, e.g. cents for USD. These should be
926
1154
  * divided by 100 for proper display format: */
@@ -1027,6 +1255,13 @@ export function formatCurrencyStr(
1027
1255
  const unitStr = isUmaCurrencyAmount(amount)
1028
1256
  ? amount.currency.code
1029
1257
  : abbrCurrencyUnit(unit as CurrencyUnitType);
1258
+
1259
+ // Skip appending if the formatted string already contains the currency code
1260
+ // This happens for currencies like XOF where Intl.NumberFormat uses the code as the symbol
1261
+ if (formattedStr.includes(unitStr)) {
1262
+ return formattedStr;
1263
+ }
1264
+
1030
1265
  const unitSuffix = options?.appendUnits?.plural && num > 1 ? "s" : "";
1031
1266
  const unitStrWithSuffix = `${unitStr}${unitSuffix}`;
1032
1267
  formattedStr += ` ${
@@ -436,3 +436,22 @@ describe("formatCurrencyStr", () => {
436
436
  ).toBe("$1,000.12 MXN");
437
437
  });
438
438
  });
439
+
440
+ it("should not append XOF if it's already in the formatted string", () => {
441
+ /* This test verifies the fix for PQA-394 where XOF was appearing twice.
442
+ * If Intl.NumberFormat formats with currencyDisplay: 'code', it will include
443
+ * XOF in the output, and we shouldn't append it again. */
444
+ const formatted = formatCurrencyStr(
445
+ { value: 221900, unit: CurrencyUnit.XOF },
446
+ { appendUnits: { plural: false, lowercase: false } },
447
+ );
448
+
449
+ /* Count occurrences of 'XOF' or 'CFA' in the result */
450
+ const xofCount = (formatted.match(/XOF/g) || []).length;
451
+ const cfaCount = (formatted.match(/CFA/g) || []).length;
452
+
453
+ /* Should have either XOF or CFA, but not both, and only once */
454
+ expect(xofCount + cfaCount).toBeGreaterThan(0);
455
+ expect(xofCount).toBeLessThanOrEqual(1);
456
+ expect(cfaCount).toBeLessThanOrEqual(1);
457
+ });