@swapkit/helpers 1.0.0-rc.1 → 1.0.0-rc.3

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,11 +1,57 @@
1
- import { DEFAULT_DECIMAL, formatBigIntToSafeValue } from '../helpers/number.ts';
1
+ import type { SwapKitNumber } from './swapKitNumber.ts';
2
2
 
3
- type AllowedValueType = bigint | number | string;
4
- type ArithmeticMethod = 'add' | 'sub' | 'mul' | 'div';
3
+ type NumberPrimitivesType = {
4
+ bigint: bigint;
5
+ number: number;
6
+ string: string;
7
+ };
8
+ type NumberPrimitives = bigint | number | string;
9
+ type InitialisationValueType = NumberPrimitives | BigIntArithmetics | SwapKitNumber;
5
10
 
11
+ type SKBigIntParams = InitialisationValueType | { decimal?: number; value: number | string };
12
+
13
+ const DEFAULT_DECIMAL = 8;
6
14
  const toMultiplier = (decimal: number) => 10n ** BigInt(decimal);
7
15
  const decimalFromMultiplier = (multiplier: bigint) => Math.log10(parseFloat(multiplier.toString()));
8
16
 
17
+ export function formatBigIntToSafeValue({
18
+ value,
19
+ bigIntDecimal = DEFAULT_DECIMAL,
20
+ decimal = DEFAULT_DECIMAL,
21
+ }: {
22
+ value: bigint;
23
+ bigIntDecimal?: number;
24
+ decimal?: number;
25
+ }) {
26
+ const isNegative = value < 0n;
27
+ let valueString = value.toString().substring(isNegative ? 1 : 0);
28
+
29
+ const padLength = decimal - (valueString.length - 1);
30
+
31
+ if (padLength > 0) {
32
+ valueString = '0'.repeat(padLength) + valueString;
33
+ }
34
+
35
+ const decimalIndex = valueString.length - decimal;
36
+ let decimalString = valueString.slice(-decimal);
37
+
38
+ // Check if we need to round up
39
+ if (parseInt(decimalString[bigIntDecimal]) >= 5) {
40
+ // Increment the last decimal place and slice off the rest
41
+ decimalString = `${decimalString.substring(0, bigIntDecimal - 1)}${(
42
+ parseInt(decimalString[bigIntDecimal - 1]) + 1
43
+ ).toString()}`;
44
+ } else {
45
+ // Just slice off the extra digits
46
+ decimalString = decimalString.substring(0, bigIntDecimal);
47
+ }
48
+
49
+ return `${isNegative ? '-' : ''}${valueString.slice(0, decimalIndex)}.${decimalString}`.replace(
50
+ /\.?0*$/,
51
+ '',
52
+ );
53
+ }
54
+
9
55
  export class BigIntArithmetics {
10
56
  decimalMultiplier: bigint = 10n ** 8n;
11
57
  bigIntValue: bigint = 0n;
@@ -23,7 +69,7 @@ export class BigIntArithmetics {
23
69
  from,
24
70
  to,
25
71
  }: {
26
- value: BigIntArithmetics | string | number;
72
+ value: InitialisationValueType;
27
73
  from: number;
28
74
  to: number;
29
75
  }) {
@@ -33,16 +79,9 @@ export class BigIntArithmetics {
33
79
  );
34
80
  }
35
81
 
36
- constructor(
37
- valueOrParams:
38
- | BigIntArithmetics
39
- | string
40
- | number
41
- | { decimal?: number; value: number | string },
42
- ) {
43
- const complexInit = typeof valueOrParams === 'object';
44
- const value = complexInit ? valueOrParams.value : valueOrParams;
45
- this.decimal = complexInit ? valueOrParams.decimal : undefined;
82
+ constructor(params: SKBigIntParams) {
83
+ const value = getStringValue(params);
84
+ this.decimal = typeof params === 'object' ? params.decimal : undefined;
46
85
 
47
86
  // use the multiplier to keep track of decimal point - defaults to 8 if lower than 8
48
87
  this.decimalMultiplier = toMultiplier(
@@ -51,58 +90,88 @@ export class BigIntArithmetics {
51
90
  this.#setValue(value);
52
91
  }
53
92
 
54
- get unsafeNumber() {
55
- return parseFloat((this.bigIntValue / this.decimalMultiplier).toString());
56
- }
57
93
  get baseValue() {
58
- return this.#getBaseValue('string') as string;
94
+ return this.getBaseValue('string') as string;
59
95
  }
60
96
  get baseValueNumber() {
61
- return this.#getBaseValue('number') as number;
97
+ return this.getBaseValue('number') as number;
62
98
  }
63
99
  get baseValueBigInt() {
64
- return this.#getBaseValue('number') as bigint;
65
- }
66
- get value() {
67
- return this.formatBigIntToSafeValue(
68
- this.bigIntValue,
69
- this.decimal || decimalFromMultiplier(this.decimalMultiplier),
70
- );
100
+ return this.getBaseValue('bigint') as bigint;
71
101
  }
72
102
 
73
- add(...args: (BigIntArithmetics | string | number)[]) {
103
+ set(value: SKBigIntParams) {
104
+ // @ts-expect-error False positive
105
+ return new this.constructor({ decimal: this.decimal, value, identifier: this.toString() });
106
+ }
107
+ add(...args: InitialisationValueType[]) {
74
108
  return this.#arithmetics('add', ...args);
75
109
  }
76
- sub(...args: (BigIntArithmetics | string | number)[]) {
110
+ sub(...args: InitialisationValueType[]) {
77
111
  return this.#arithmetics('sub', ...args);
78
112
  }
79
- mul(...args: (BigIntArithmetics | string | number)[]) {
113
+ mul(...args: InitialisationValueType[]) {
80
114
  return this.#arithmetics('mul', ...args);
81
115
  }
82
- div(...args: (BigIntArithmetics | string | number)[]) {
116
+ div(...args: InitialisationValueType[]) {
83
117
  return this.#arithmetics('div', ...args);
84
118
  }
85
- gt(value: BigIntArithmetics | string | number) {
119
+ gt(value: InitialisationValueType) {
86
120
  return this.bigIntValue > this.getBigIntValue(value);
87
121
  }
88
- gte(value: BigIntArithmetics | string | number) {
122
+ gte(value: InitialisationValueType) {
89
123
  return this.bigIntValue >= this.getBigIntValue(value);
90
124
  }
91
- lt(value: BigIntArithmetics | string | number) {
125
+ lt(value: InitialisationValueType) {
92
126
  return this.bigIntValue < this.getBigIntValue(value);
93
127
  }
94
- lte(value: BigIntArithmetics | string | number) {
128
+ lte(value: InitialisationValueType) {
95
129
  return this.bigIntValue <= this.getBigIntValue(value);
96
130
  }
97
- eqValue(value: BigIntArithmetics | string | number) {
131
+ eqValue(value: InitialisationValueType) {
98
132
  return this.bigIntValue === this.getBigIntValue(value);
99
133
  }
100
134
 
101
- getBigIntValue(value: BigIntArithmetics | string | number, decimal?: number) {
135
+ getValue<T extends 'number' | 'string' | 'bigint'>(type: T): NumberPrimitivesType[T] {
136
+ const value = this.formatBigIntToSafeValue(
137
+ this.bigIntValue,
138
+ this.decimal || decimalFromMultiplier(this.decimalMultiplier),
139
+ );
140
+
141
+ switch (type) {
142
+ case 'number':
143
+ // @ts-expect-error False positive
144
+ return Number(value);
145
+ case 'string':
146
+ // @ts-expect-error False positive
147
+ return value;
148
+ default:
149
+ // @ts-expect-error False positive
150
+ return this.bigIntValue;
151
+ }
152
+ }
153
+
154
+ getBaseValue<T extends 'number' | 'string' | 'bigint'>(type: T): NumberPrimitivesType[T] {
155
+ const divisor = this.decimalMultiplier / toMultiplier(this.decimal || 0);
156
+ const baseValue = this.bigIntValue / divisor;
157
+
158
+ switch (type) {
159
+ case 'number':
160
+ // @ts-expect-error False positive
161
+ return Number(baseValue);
162
+ case 'string':
163
+ // @ts-expect-error False positive
164
+ return baseValue.toString();
165
+ default:
166
+ // @ts-expect-error False positive
167
+ return this.bigIntValue;
168
+ }
169
+ }
170
+
171
+ getBigIntValue(value: InitialisationValueType, decimal?: number) {
102
172
  if (!decimal && typeof value === 'object') return value.bigIntValue;
103
173
 
104
- value = typeof value === 'object' ? value.value : value;
105
- return this.#toBigInt(this.#toSafeValue(value), decimal);
174
+ return this.#toBigInt(this.#toSafeValue(getStringValue(value)), decimal);
106
175
  }
107
176
 
108
177
  formatBigIntToSafeValue(value: bigint, decimal?: number) {
@@ -113,16 +182,13 @@ export class BigIntArithmetics {
113
182
  );
114
183
  const isNegative = value < 0n;
115
184
 
116
- let valueString = value.toString().substring(isNegative ? 1 : 0);
117
-
185
+ const valueString = value.toString().substring(isNegative ? 1 : 0);
118
186
  const padLength = decimalToUseForConversion - (valueString.length - 1);
119
187
 
120
- if (padLength > 0) {
121
- valueString = '0'.repeat(padLength) + valueString;
122
- }
188
+ const parsedValueString = padLength > 0 ? '0'.repeat(padLength) + valueString : valueString;
123
189
 
124
- const decimalIndex = valueString.length - decimalToUseForConversion;
125
- let decimalString = valueString.slice(-decimalToUseForConversion);
190
+ const decimalIndex = parsedValueString.length - decimalToUseForConversion;
191
+ let decimalString = parsedValueString.slice(-decimalToUseForConversion);
126
192
 
127
193
  // Check if we need to round up
128
194
  if (parseInt(decimalString[bigIntDecimal]) >= 5) {
@@ -135,54 +201,72 @@ export class BigIntArithmetics {
135
201
  decimalString = decimalString.substring(0, bigIntDecimal);
136
202
  }
137
203
 
138
- return `${isNegative ? '-' : ''}${valueString.slice(0, decimalIndex)}.${decimalString}`.replace(
139
- /\.?0*$/,
140
- '',
141
- );
204
+ return `${isNegative ? '-' : ''}${parsedValueString.slice(
205
+ 0,
206
+ decimalIndex,
207
+ )}.${decimalString}`.replace(/\.?0*$/, '');
142
208
  }
143
209
 
144
- toSignificant(significantDigits?: number) {
145
- const value = this.value.split('.');
146
- const integer = value[0];
147
- const decimal = value[1];
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
+ }
148
223
 
149
- if (decimal) {
150
- return `${integer}.${decimal.slice(0, significantDigits || this.decimal)}`.replace(
151
- /\.?0*$/,
152
- '',
224
+ if (parseInt(integer)) {
225
+ return `${integer}.${decimal.slice(0, significantDigits - integer.length)}`.padEnd(
226
+ valueLength - significantDigits,
227
+ '0',
153
228
  );
154
229
  }
155
230
 
156
- return integer;
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
+ )}`;
157
238
  }
158
239
 
159
- #arithmetics(method: ArithmeticMethod, ...args: (BigIntArithmetics | string | number)[]): this {
240
+ #arithmetics(method: 'add' | 'sub' | 'mul' | 'div', ...args: InitialisationValueType[]): this {
160
241
  const precisionDecimal = this.#retrievePrecisionDecimal(this, ...args);
161
242
  const precisionDecimalMultiplier = toMultiplier(precisionDecimal);
162
243
 
163
244
  const result = args.reduce(
164
- (acc, arg) => {
245
+ (acc: bigint, arg) => {
165
246
  const value = this.getBigIntValue(arg, precisionDecimal);
166
247
 
167
- if ('div' === method && value === 0n) throw new RangeError('Division by zero');
168
-
169
- /**
170
- * Normal arithmetic - add & sub => 200000000n +- 200000000n
171
- */
172
- if ('add' === method) return acc + value;
173
- if ('sub' === method) return acc - value;
174
-
175
- /**
176
- * Multiplication & division would end up with wrong result if we don't adjust the value
177
- * 200000000n * 200000000n => 40000000000000000n
178
- * 200000000n / 200000000n => 1n
179
- * So we do the following:
180
- * 200000000n * 200000000n = 40000000000000000n / 100000000n (decimals) => 400000000n
181
- * (200000000n * 100000000n (decimals)) / 200000000n => 100000000n
182
- */
183
- if ('mul' === method) return (acc * value) / precisionDecimalMultiplier;
184
- // 'div' === method
185
- return (acc * precisionDecimalMultiplier) / value;
248
+ switch (method) {
249
+ case 'add':
250
+ return acc + value;
251
+ case 'sub':
252
+ return acc - value;
253
+ /**
254
+ * Multiplication & division would end up with wrong result if we don't adjust the value
255
+ * 200000000n * 200000000n => 40000000000000000n
256
+ * 200000000n / 200000000n => 1n
257
+ * So we do the following:
258
+ * 200000000n * 200000000n = 40000000000000000n / 100000000n (decimals) => 400000000n
259
+ * (200000000n * 100000000n (decimals)) / 200000000n => 100000000n
260
+ */
261
+ case 'mul':
262
+ return (acc * value) / precisionDecimalMultiplier;
263
+ case 'div': {
264
+ if (value === 0n) throw new RangeError('Division by zero');
265
+ return (acc * precisionDecimalMultiplier) / value;
266
+ }
267
+ default:
268
+ return acc;
269
+ }
186
270
  },
187
271
  //normalize is to precision multiplier base
188
272
  (this.bigIntValue * precisionDecimalMultiplier) / this.decimalMultiplier,
@@ -194,16 +278,16 @@ export class BigIntArithmetics {
194
278
  value: result,
195
279
  });
196
280
 
197
- // @ts-expect-error - we know that the constructor is the same as the current class
281
+ // @ts-expect-error False positive
198
282
  return new this.constructor({ decimal: this.decimal, value, identifier: this.toString() });
199
283
  }
200
284
 
201
- #setValue(value: AllowedValueType, bigIntValue?: bigint) {
285
+ #setValue(value: InitialisationValueType) {
202
286
  const safeValue = this.#toSafeValue(value) || '0';
203
- this.bigIntValue = bigIntValue || this.#toBigInt(safeValue);
287
+ this.bigIntValue = this.#toBigInt(safeValue);
204
288
  }
205
289
 
206
- #retrievePrecisionDecimal(...args: (BigIntArithmetics | AllowedValueType)[]) {
290
+ #retrievePrecisionDecimal(...args: InitialisationValueType[]) {
207
291
  const decimals = args
208
292
  .map((arg) =>
209
293
  typeof arg === 'object'
@@ -222,14 +306,14 @@ export class BigIntArithmetics {
222
306
  return BigInt(`${integerPart}${decimalPart.padEnd(padDecimal, '0')}`);
223
307
  }
224
308
 
225
- #toSafeValue(value: AllowedValueType) {
309
+ #toSafeValue(value: InitialisationValueType) {
226
310
  const parsedValue =
227
311
  typeof value === 'number'
228
312
  ? Number(value).toLocaleString('fullwide', {
229
313
  useGrouping: false,
230
314
  maximumFractionDigits: 20,
231
315
  })
232
- : value;
316
+ : getStringValue(value);
233
317
 
234
318
  const splitValue = `${parsedValue}`.replaceAll(',', '.').split('.');
235
319
 
@@ -242,18 +326,12 @@ export class BigIntArithmetics {
242
326
  const decimals = value.split('.')[1]?.length || 0;
243
327
  return Math.max(decimals, DEFAULT_DECIMAL);
244
328
  }
329
+ }
245
330
 
246
- #getBaseValue<T extends 'number' | 'string' | 'bigint'>(type: T) {
247
- const divisor = this.decimalMultiplier / toMultiplier(this.decimal || 0);
248
- const baseValue = this.bigIntValue / divisor;
249
-
250
- switch (type) {
251
- case 'number':
252
- return Number(baseValue);
253
- case 'string':
254
- return baseValue.toString();
255
- default:
256
- return baseValue;
257
- }
258
- }
331
+ function getStringValue(value: SKBigIntParams) {
332
+ return typeof value === 'object'
333
+ ? 'getValue' in value
334
+ ? value.getValue('string')
335
+ : value.value
336
+ : value;
259
337
  }
@@ -6,8 +6,4 @@ export class SwapKitNumber extends BigIntArithmetics {
6
6
  eq(value: SwapKitValueType) {
7
7
  return this.eqValue(value);
8
8
  }
9
-
10
- toString() {
11
- return this.value;
12
- }
13
9
  }
@@ -1,40 +0,0 @@
1
- // THORChain base amount is 1e8
2
- export const DEFAULT_DECIMAL = 8;
3
-
4
- export const formatBigIntToSafeValue = ({
5
- value,
6
- bigIntDecimal = DEFAULT_DECIMAL,
7
- decimal = DEFAULT_DECIMAL,
8
- }: {
9
- value: bigint;
10
- bigIntDecimal?: number;
11
- decimal?: number;
12
- }) => {
13
- const isNegative = value < 0n;
14
- let valueString = value.toString().substring(isNegative ? 1 : 0);
15
-
16
- const padLength = decimal - (valueString.length - 1);
17
-
18
- if (padLength > 0) {
19
- valueString = '0'.repeat(padLength) + valueString;
20
- }
21
-
22
- const decimalIndex = valueString.length - decimal;
23
- let decimalString = valueString.slice(-decimal);
24
-
25
- // Check if we need to round up
26
- if (parseInt(decimalString[bigIntDecimal]) >= 5) {
27
- // Increment the last decimal place and slice off the rest
28
- decimalString = `${decimalString.substring(0, bigIntDecimal - 1)}${(
29
- parseInt(decimalString[bigIntDecimal - 1]) + 1
30
- ).toString()}`;
31
- } else {
32
- // Just slice off the extra digits
33
- decimalString = decimalString.substring(0, bigIntDecimal);
34
- }
35
-
36
- return `${isNegative ? '-' : ''}${valueString.slice(0, decimalIndex)}.${decimalString}`.replace(
37
- /\.?0*$/,
38
- '',
39
- );
40
- };