@swapkit/helpers 1.0.0-rc.4 → 1.0.0-rc.41

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.
@@ -1,3 +1,5 @@
1
+ import { BaseDecimal } from '@swapkit/types';
2
+
1
3
  import type { SwapKitNumber } from './swapKitNumber.ts';
2
4
 
3
5
  type NumberPrimitivesType = {
@@ -5,10 +7,11 @@ type NumberPrimitivesType = {
5
7
  number: number;
6
8
  string: string;
7
9
  };
8
- type NumberPrimitives = bigint | number | string;
10
+ export type NumberPrimitives = bigint | number | string;
9
11
  type InitialisationValueType = NumberPrimitives | BigIntArithmetics | SwapKitNumber;
10
12
 
11
13
  type SKBigIntParams = InitialisationValueType | { decimal?: number; value: number | string };
14
+ type AllowedNumberTypes = 'bigint' | 'number' | 'string';
12
15
 
13
16
  const DEFAULT_DECIMAL = 8;
14
17
  const toMultiplier = (decimal: number) => 10n ** BigInt(decimal);
@@ -69,38 +72,30 @@ export class BigIntArithmetics {
69
72
  from,
70
73
  to,
71
74
  }: {
72
- value: InitialisationValueType;
75
+ value: InstanceType<typeof SwapKitNumber>;
73
76
  from: number;
74
77
  to: number;
75
78
  }) {
76
- return BigIntArithmetics.fromBigInt(
77
- (new BigIntArithmetics(value).bigIntValue * toMultiplier(to)) / toMultiplier(from),
79
+ return this.fromBigInt(
80
+ (value.getBaseValue('bigint') * toMultiplier(to)) / toMultiplier(from),
78
81
  to,
79
82
  );
80
83
  }
81
84
 
82
85
  constructor(params: SKBigIntParams) {
83
86
  const value = getStringValue(params);
84
- this.decimal = typeof params === 'object' ? params.decimal : undefined;
87
+ const isComplex = typeof params === 'object';
88
+ this.decimal = isComplex ? params.decimal : undefined;
85
89
 
86
90
  // use the multiplier to keep track of decimal point - defaults to 8 if lower than 8
87
- this.decimalMultiplier = toMultiplier(
88
- Math.max(this.#getFloatDecimals(this.#toSafeValue(value)), this.decimal || 0),
89
- );
91
+ this.decimalMultiplier =
92
+ isComplex && 'decimalMultiplier' in params
93
+ ? params.decimalMultiplier
94
+ : toMultiplier(Math.max(getFloatDecimals(toSafeValue(value)), this.decimal || 0));
90
95
  this.#setValue(value);
91
96
  }
92
97
 
93
- get baseValue() {
94
- return this.getBaseValue('string') as string;
95
- }
96
- get baseValueNumber() {
97
- return this.getBaseValue('number') as number;
98
- }
99
- get baseValueBigInt() {
100
- return this.getBaseValue('bigint') as bigint;
101
- }
102
-
103
- set(value: SKBigIntParams) {
98
+ set(value: SKBigIntParams): this {
104
99
  // @ts-expect-error False positive
105
100
  return new this.constructor({ decimal: this.decimal, value, identifier: this.toString() });
106
101
  }
@@ -117,22 +112,23 @@ export class BigIntArithmetics {
117
112
  return this.#arithmetics('div', ...args);
118
113
  }
119
114
  gt(value: InitialisationValueType) {
120
- return this.bigIntValue > this.getBigIntValue(value);
115
+ return this.#comparison('gt', value);
121
116
  }
122
117
  gte(value: InitialisationValueType) {
123
- return this.bigIntValue >= this.getBigIntValue(value);
118
+ return this.#comparison('gte', value);
124
119
  }
125
120
  lt(value: InitialisationValueType) {
126
- return this.bigIntValue < this.getBigIntValue(value);
121
+ return this.#comparison('lt', value);
127
122
  }
128
123
  lte(value: InitialisationValueType) {
129
- return this.bigIntValue <= this.getBigIntValue(value);
124
+ return this.#comparison('lte', value);
130
125
  }
131
126
  eqValue(value: InitialisationValueType) {
132
- return this.bigIntValue === this.getBigIntValue(value);
127
+ return this.#comparison('eqValue', value);
133
128
  }
134
129
 
135
- getValue<T extends 'number' | 'string' | 'bigint'>(type: T): NumberPrimitivesType[T] {
130
+ // @ts-expect-error False positive
131
+ getValue<T extends AllowedNumberTypes>(type: T): NumberPrimitivesType[T] {
136
132
  const value = this.formatBigIntToSafeValue(
137
133
  this.bigIntValue,
138
134
  this.decimal || decimalFromMultiplier(this.decimalMultiplier),
@@ -140,38 +136,125 @@ export class BigIntArithmetics {
140
136
 
141
137
  switch (type) {
142
138
  case 'number':
143
- // @ts-expect-error False positive
144
- return Number(value);
139
+ return Number(value) as NumberPrimitivesType[T];
145
140
  case 'string':
146
- // @ts-expect-error False positive
147
- return value;
148
- default:
149
- // @ts-expect-error False positive
150
- return this.bigIntValue;
141
+ return value as NumberPrimitivesType[T];
142
+ case 'bigint':
143
+ return ((this.bigIntValue * 10n ** BigInt(this.decimal || 8n)) /
144
+ this.decimalMultiplier) as NumberPrimitivesType[T];
151
145
  }
152
146
  }
153
147
 
154
- getBaseValue<T extends 'number' | 'string' | 'bigint'>(type: T): NumberPrimitivesType[T] {
155
- const divisor = this.decimalMultiplier / toMultiplier(this.decimal || 0);
148
+ // @ts-expect-error
149
+ getBaseValue<T extends AllowedNumberTypes>(type: T): NumberPrimitivesType[T] {
150
+ const divisor = this.decimalMultiplier / toMultiplier(this.decimal || BaseDecimal.THOR);
156
151
  const baseValue = this.bigIntValue / divisor;
157
152
 
158
153
  switch (type) {
159
154
  case 'number':
160
- // @ts-expect-error False positive
161
- return Number(baseValue);
155
+ return Number(baseValue) as NumberPrimitivesType[T];
162
156
  case 'string':
163
- // @ts-expect-error False positive
164
- return baseValue.toString();
165
- default:
166
- // @ts-expect-error False positive
167
- return this.bigIntValue;
157
+ return baseValue.toString() as NumberPrimitivesType[T];
158
+ case 'bigint':
159
+ return baseValue as NumberPrimitivesType[T];
168
160
  }
169
161
  }
170
162
 
171
163
  getBigIntValue(value: InitialisationValueType, decimal?: number) {
172
164
  if (!decimal && typeof value === 'object') return value.bigIntValue;
173
165
 
174
- return this.#toBigInt(this.#toSafeValue(getStringValue(value)), decimal);
166
+ const stringValue = getStringValue(value);
167
+ const safeValue = toSafeValue(stringValue);
168
+
169
+ if (safeValue === '0' || safeValue === 'undefined') return 0n;
170
+ return this.#toBigInt(safeValue, decimal);
171
+ }
172
+
173
+ toSignificant(significantDigits: number = 6) {
174
+ const [int, dec] = this.getValue('string').split('.');
175
+ const integer = int || '';
176
+ const decimal = dec || '';
177
+ const valueLength = parseInt(integer) ? integer.length + decimal.length : decimal.length;
178
+
179
+ if (valueLength <= significantDigits) {
180
+ return this.getValue('string');
181
+ }
182
+
183
+ if (integer.length >= significantDigits) {
184
+ return integer.slice(0, significantDigits).padEnd(integer.length, '0');
185
+ }
186
+
187
+ if (parseInt(integer)) {
188
+ return `${integer}.${decimal.slice(0, significantDigits - integer.length)}`.padEnd(
189
+ significantDigits - integer.length,
190
+ '0',
191
+ );
192
+ }
193
+
194
+ const trimmedDecimal = parseInt(decimal);
195
+ const slicedDecimal = `${trimmedDecimal}`.slice(0, significantDigits);
196
+
197
+ return `0.${slicedDecimal.padStart(
198
+ decimal.length - `${trimmedDecimal}`.length + slicedDecimal.length,
199
+ '0',
200
+ )}`;
201
+ }
202
+
203
+ toFixed(fixedDigits: number = 6) {
204
+ const [int, dec] = this.getValue('string').split('.');
205
+ const integer = int || '';
206
+ const decimal = dec || '';
207
+
208
+ if (parseInt(integer)) {
209
+ return `${integer}.${decimal.slice(0, fixedDigits)}`.padEnd(fixedDigits, '0');
210
+ }
211
+
212
+ const trimmedDecimal = parseInt(decimal);
213
+ const slicedDecimal = `${trimmedDecimal}`.slice(0, fixedDigits);
214
+
215
+ return `0.${slicedDecimal.padStart(
216
+ decimal.length - `${trimmedDecimal}`.length + slicedDecimal.length,
217
+ '0',
218
+ )}`;
219
+ }
220
+
221
+ toAbbreviation(digits = 2) {
222
+ const value = this.getValue('number');
223
+ const abbreviations = ['', 'K', 'M', 'B', 'T', 'Q', 'Qi', 'S'];
224
+ const tier = Math.floor(Math.log10(Math.abs(value)) / 3);
225
+ const suffix = abbreviations[tier];
226
+
227
+ if (!suffix) return this.getValue('string');
228
+
229
+ const scale = 10 ** (tier * 3);
230
+ const scaled = value / scale;
231
+
232
+ return `${scaled.toFixed(digits)}${suffix}`;
233
+ }
234
+
235
+ toCurrency(
236
+ currency = '$',
237
+ {
238
+ currencyPosition = 'start',
239
+ decimal = 2,
240
+ decimalSeparator = '.',
241
+ thousandSeparator = ',',
242
+ } = {},
243
+ ) {
244
+ const value = this.getValue('number');
245
+ const [int, dec = ''] = value.toFixed(6).split('.');
246
+ const integer = int.replace(/\B(?=(\d{3})+(?!\d))/g, thousandSeparator);
247
+
248
+ const parsedValue =
249
+ !int && !dec
250
+ ? '0.00'
251
+ : int === '0'
252
+ ? `${parseFloat(`0.${dec}`)}`.replace('.', decimalSeparator)
253
+ : `${integer}${parseInt(dec) ? `${decimalSeparator}${dec.slice(0, decimal)}` : ''}`;
254
+
255
+ return `${currencyPosition === 'start' ? currency : ''}${parsedValue}${
256
+ currencyPosition === 'end' ? currency : ''
257
+ }`;
175
258
  }
176
259
 
177
260
  formatBigIntToSafeValue(value: bigint, decimal?: number) {
@@ -207,43 +290,14 @@ export class BigIntArithmetics {
207
290
  )}.${decimalString}`.replace(/\.?0*$/, '');
208
291
  }
209
292
 
210
- toSignificant(significantDigits: number = 6) {
211
- const [int, dec] = this.getValue('string').split('.');
212
- const integer = int || '';
213
- const decimal = dec || '';
214
- const valueLength = parseInt(integer) ? integer.length + decimal.length : decimal.length;
215
-
216
- if (valueLength <= significantDigits) {
217
- return this.getValue('string');
218
- }
219
-
220
- if (integer.length >= significantDigits) {
221
- return integer.slice(0, significantDigits).padEnd(integer.length, '0');
222
- }
223
-
224
- if (parseInt(integer)) {
225
- return `${integer}.${decimal.slice(0, significantDigits - integer.length)}`.padEnd(
226
- valueLength - significantDigits,
227
- '0',
228
- );
229
- }
230
-
231
- const trimmedDecimal = parseInt(decimal);
232
- const slicedDecimal = `${trimmedDecimal}`.slice(0, significantDigits);
233
-
234
- return `0.${slicedDecimal.padStart(
235
- decimal.length - `${trimmedDecimal}`.length + slicedDecimal.length,
236
- '0',
237
- )}`;
238
- }
239
-
240
293
  #arithmetics(method: 'add' | 'sub' | 'mul' | 'div', ...args: InitialisationValueType[]): this {
241
294
  const precisionDecimal = this.#retrievePrecisionDecimal(this, ...args);
242
- const precisionDecimalMultiplier = toMultiplier(precisionDecimal);
295
+ const decimal = Math.max(precisionDecimal, decimalFromMultiplier(this.decimalMultiplier));
296
+ const precisionDecimalMultiplier = toMultiplier(decimal);
243
297
 
244
298
  const result = args.reduce(
245
299
  (acc: bigint, arg) => {
246
- const value = this.getBigIntValue(arg, precisionDecimal);
300
+ const value = this.getBigIntValue(arg, decimal);
247
301
 
248
302
  switch (method) {
249
303
  case 'add':
@@ -273,65 +327,92 @@ export class BigIntArithmetics {
273
327
  );
274
328
 
275
329
  const value = formatBigIntToSafeValue({
276
- bigIntDecimal: precisionDecimal,
277
- decimal: Math.max(precisionDecimal, decimalFromMultiplier(this.decimalMultiplier)),
330
+ bigIntDecimal: decimal,
331
+ decimal,
278
332
  value: result,
279
333
  });
280
334
 
281
335
  // @ts-expect-error False positive
282
- return new this.constructor({ decimal: this.decimal, value, identifier: this.toString() });
336
+ return new this.constructor({
337
+ decimalMultiplier: toMultiplier(decimal),
338
+ decimal: this.decimal,
339
+ value,
340
+ identifier: this.toString(),
341
+ });
342
+ }
343
+
344
+ #comparison(method: 'gt' | 'gte' | 'lt' | 'lte' | 'eqValue', ...args: InitialisationValueType[]) {
345
+ const decimal = this.#retrievePrecisionDecimal(this, ...args);
346
+ const value = this.getBigIntValue(args[0], decimal);
347
+ const compareToValue = this.getBigIntValue(this, decimal);
348
+
349
+ switch (method) {
350
+ case 'gt':
351
+ return compareToValue > value;
352
+ case 'gte':
353
+ return compareToValue >= value;
354
+ case 'lt':
355
+ return compareToValue < value;
356
+ case 'lte':
357
+ return compareToValue <= value;
358
+ case 'eqValue':
359
+ return compareToValue === value;
360
+ }
283
361
  }
284
362
 
285
363
  #setValue(value: InitialisationValueType) {
286
- const safeValue = this.#toSafeValue(value) || '0';
364
+ const safeValue = toSafeValue(value) || '0';
287
365
  this.bigIntValue = this.#toBigInt(safeValue);
288
366
  }
289
367
 
290
368
  #retrievePrecisionDecimal(...args: InitialisationValueType[]) {
291
369
  const decimals = args
292
- .map((arg) =>
293
- typeof arg === 'object'
370
+ .map((arg) => {
371
+ const isObject = typeof arg === 'object';
372
+ const value = isObject
294
373
  ? arg.decimal || decimalFromMultiplier(arg.decimalMultiplier)
295
- : this.#getFloatDecimals(this.#toSafeValue(arg)),
296
- )
374
+ : getFloatDecimals(toSafeValue(arg));
375
+
376
+ return value;
377
+ })
297
378
  .filter(Boolean) as number[];
379
+
298
380
  return Math.max(...decimals, DEFAULT_DECIMAL);
299
381
  }
300
382
 
301
383
  #toBigInt(value: string, decimal?: number) {
302
384
  const multiplier = decimal ? toMultiplier(decimal) : this.decimalMultiplier;
303
385
  const padDecimal = decimalFromMultiplier(multiplier);
304
- const [integerPart, decimalPart = ''] = value.split('.');
386
+ const [integerPart = '', decimalPart = ''] = value.split('.');
305
387
 
306
388
  return BigInt(`${integerPart}${decimalPart.padEnd(padDecimal, '0')}`);
307
389
  }
390
+ }
308
391
 
309
- #toSafeValue(value: InitialisationValueType) {
310
- const parsedValue =
311
- typeof value === 'number'
312
- ? Number(value).toLocaleString('fullwide', {
313
- useGrouping: false,
314
- maximumFractionDigits: 20,
315
- })
316
- : getStringValue(value);
317
-
318
- const splitValue = `${parsedValue}`.replaceAll(',', '.').split('.');
319
-
320
- return splitValue.length > 1
321
- ? `${splitValue.slice(0, -1).join('')}.${splitValue.at(-1)}`
322
- : splitValue[0];
323
- }
392
+ const numberFormatter = Intl.NumberFormat('fullwide', {
393
+ useGrouping: false,
394
+ maximumFractionDigits: 20,
395
+ });
324
396
 
325
- #getFloatDecimals(value: string) {
326
- const decimals = value.split('.')[1]?.length || 0;
327
- return Math.max(decimals, DEFAULT_DECIMAL);
328
- }
397
+ function toSafeValue(value: InitialisationValueType) {
398
+ const parsedValue =
399
+ typeof value === 'number' ? numberFormatter.format(value) : getStringValue(value);
400
+ const splitValue = `${parsedValue}`.replaceAll(',', '.').split('.');
401
+
402
+ return splitValue.length > 1
403
+ ? `${splitValue.slice(0, -1).join('')}.${splitValue.at(-1)}`
404
+ : splitValue[0];
405
+ }
406
+
407
+ function getFloatDecimals(value: string) {
408
+ const decimals = value.split('.')[1]?.length || 0;
409
+ return Math.max(decimals, DEFAULT_DECIMAL);
329
410
  }
330
411
 
331
- function getStringValue(value: SKBigIntParams) {
332
- return typeof value === 'object'
333
- ? 'getValue' in value
334
- ? value.getValue('string')
335
- : value.value
336
- : value;
412
+ function getStringValue(param: SKBigIntParams) {
413
+ return typeof param === 'object'
414
+ ? 'getValue' in param
415
+ ? param.getValue('string')
416
+ : param.value
417
+ : param;
337
418
  }
@@ -62,7 +62,7 @@ export type Keys = keyof typeof errorMessages;
62
62
 
63
63
  export class SwapKitError extends Error {
64
64
  constructor(errorKey: Keys, sourceError?: any) {
65
- console.error(sourceError);
65
+ console.error(sourceError, { stack: sourceError?.stack, message: sourceError?.message });
66
66
 
67
67
  super(errorKey, { cause: { code: errorMessages[errorKey], message: errorKey } });
68
68
  Object.setPrototypeOf(this, SwapKitError.prototype);
@@ -1,4 +1,4 @@
1
- import { BigIntArithmetics } from './bigIntArithmetics.ts';
1
+ import { BigIntArithmetics, formatBigIntToSafeValue } from './bigIntArithmetics.ts';
2
2
 
3
3
  export type SwapKitValueType = BigIntArithmetics | string | number;
4
4
 
@@ -6,4 +6,11 @@ export class SwapKitNumber extends BigIntArithmetics {
6
6
  eq(value: SwapKitValueType) {
7
7
  return this.eqValue(value);
8
8
  }
9
+
10
+ static fromBigInt(value: bigint, decimal?: number) {
11
+ return new SwapKitNumber({
12
+ decimal,
13
+ value: formatBigIntToSafeValue({ value, bigIntDecimal: decimal, decimal }),
14
+ });
15
+ }
9
16
  }