@swapkit/helpers 1.0.0-rc.35 → 1.0.0-rc.37

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/package.json CHANGED
@@ -8,16 +8,16 @@
8
8
  "@vitest/coverage-istanbul": "0.34.6",
9
9
  "vite": "4.5.0",
10
10
  "vitest": "0.34.6",
11
- "@internal/config": "0.0.0-rc.2",
12
- "@swapkit/tokens": "1.0.0-rc.17",
13
- "@swapkit/types": "1.0.0-rc.16"
11
+ "@internal/config": "0.0.0-rc.3",
12
+ "@swapkit/tokens": "1.0.0-rc.18",
13
+ "@swapkit/types": "1.0.0-rc.17"
14
14
  },
15
15
  "eslintConfig": {
16
16
  "extends": "../../../internal/eslint-config"
17
17
  },
18
18
  "peerDependencies": {
19
- "@swapkit/tokens": "1.0.0-rc.17",
20
- "@swapkit/types": "1.0.0-rc.16"
19
+ "@swapkit/tokens": "1.0.0-rc.18",
20
+ "@swapkit/types": "1.0.0-rc.17"
21
21
  },
22
22
  "exports": {
23
23
  ".": {
@@ -42,7 +42,7 @@
42
42
  "repository": "https://github.com/thorswap/SwapKit.git",
43
43
  "type": "module",
44
44
  "types": "./dist/index.d.ts",
45
- "version": "1.0.0-rc.35",
45
+ "version": "1.0.0-rc.37",
46
46
  "scripts": {
47
47
  "build": "vite build",
48
48
  "clean": "rm -rf dist vite.config.ts.* .turbo node_modules",
@@ -328,7 +328,6 @@ describe('SwapKitNumber', () => {
328
328
  const result = skNumber1.mul(skNumber2);
329
329
 
330
330
  // The exact result of 1.23 * 4.56 is 5.6088
331
- // If we round it to 2 decimal places, we should get 5.61
332
331
  expect(result.getValue('string')).toBe('5.609');
333
332
  expect(result.getBaseValue('bigint')).toBe(5608n);
334
333
 
@@ -428,6 +427,26 @@ describe('SwapKitNumber', () => {
428
427
  });
429
428
  });
430
429
 
430
+ describe('extending multiplier without loosing precision', () => {
431
+ test('edge case 1', () => {
432
+ const asset1 = new SwapKitNumber({ value: 41.90963702, decimal: 8 });
433
+ const multiplier = 5.337952274462478;
434
+ const divider = 105.2562773915526;
435
+ const result = asset1.mul(multiplier).div(divider);
436
+
437
+ expect(result.getValue('string')).toBe('2.12539953');
438
+ });
439
+
440
+ test('edge case 2', () => {
441
+ const asset1 = new SwapKitNumber('41.90963702');
442
+ const multiplier = new SwapKitNumber('5.337952274462478');
443
+ const divider = new SwapKitNumber('105.2562773915526');
444
+ const result = asset1.mul(multiplier).div(divider);
445
+
446
+ expect(result.getValue('string')).toBe('2.125399527674726');
447
+ });
448
+ });
449
+
431
450
  describe('gt', () => {
432
451
  test('greater than', () => {
433
452
  const skNumber1 = new SwapKitNumber(10);
@@ -436,6 +455,14 @@ describe('SwapKitNumber', () => {
436
455
  expect(skNumber1.gt(skNumber2)).toBe(true);
437
456
  expect(skNumber2.gt(skNumber1)).toBe(false);
438
457
  });
458
+
459
+ test("different decimals doesn't affect comparison", () => {
460
+ const skNumber1 = new SwapKitNumber({ value: 10, decimal: 18 });
461
+ const skNumber2 = new SwapKitNumber({ value: '50', decimal: 8 });
462
+
463
+ expect(skNumber1.lt(skNumber2)).toBe(true);
464
+ expect(skNumber2.gt(skNumber1)).toBe(true);
465
+ });
439
466
  });
440
467
 
441
468
  describe('gte', () => {
@@ -481,6 +508,19 @@ describe('SwapKitNumber', () => {
481
508
  });
482
509
  });
483
510
 
511
+ describe('comparison edge cases with decimals', () => {
512
+ test('compare on cut decimals', () => {
513
+ const skNumber1 = new SwapKitNumber({ value: 0.001, decimal: 3 });
514
+ const value = '0.0019';
515
+
516
+ expect(skNumber1.lt(value)).toBe(true);
517
+ expect(skNumber1.gt(value)).toBe(false);
518
+ expect(skNumber1.eq(value)).toBe(false);
519
+ expect(skNumber1.lte(value)).toBe(true);
520
+ expect(skNumber1.gte(value)).toBe(false);
521
+ });
522
+ });
523
+
484
524
  describe('Throws', () => {
485
525
  test('throws if division by zero', () => {
486
526
  const skNumber1 = new SwapKitNumber(10);
@@ -21,12 +21,14 @@ import type { NumberPrimitives } from './bigIntArithmetics.ts';
21
21
  import { BigIntArithmetics, formatBigIntToSafeValue } from './bigIntArithmetics.ts';
22
22
  import type { SwapKitValueType } from './swapKitNumber.ts';
23
23
 
24
+ type TokenTax = { buy: number; sell: number };
25
+
24
26
  const safeValue = (value: NumberPrimitives, decimal: number) =>
25
27
  typeof value === 'bigint'
26
28
  ? formatBigIntToSafeValue({ value, bigIntDecimal: decimal, decimal })
27
29
  : value;
28
30
 
29
- type AssetValueParams = { decimal: number; value: SwapKitValueType } & (
31
+ type AssetValueParams = { decimal: number; value: SwapKitValueType; tax?: TokenTax } & (
30
32
  | { chain: Chain; symbol: string }
31
33
  | { identifier: string }
32
34
  );
@@ -46,7 +48,9 @@ type TokenNames =
46
48
  | (typeof WoofiList)['tokens'][number]['identifier']
47
49
  | (typeof UniswapList)['tokens'][number]['identifier'];
48
50
 
49
- let staticTokensMap: Map<TokenNames, { decimal: number; identifier: string }> | undefined;
51
+ let staticTokensMap:
52
+ | Map<TokenNames, { tax?: TokenTax; decimal: number; identifier: string }>
53
+ | undefined;
50
54
 
51
55
  const getStaticToken = (identifier: TokenNames) => {
52
56
  if (!staticTokensMap) {
@@ -67,6 +71,15 @@ const createAssetValue = async (assetString: string, value: NumberPrimitives = 0
67
71
  };
68
72
 
69
73
  export class AssetValue extends BigIntArithmetics {
74
+ address?: string;
75
+ chain: Chain;
76
+ isGasAsset = false;
77
+ isSynthetic = false;
78
+ symbol: string;
79
+ tax?: TokenTax;
80
+ ticker: string;
81
+ type: ReturnType<typeof getAssetType>;
82
+
70
83
  constructor(params: AssetValueParams) {
71
84
  const identifier =
72
85
  'identifier' in params ? params.identifier : `${params.chain}.${params.symbol}`;
@@ -78,6 +91,7 @@ export class AssetValue extends BigIntArithmetics {
78
91
  );
79
92
 
80
93
  const assetInfo = getAssetInfo(identifier);
94
+
81
95
  this.type = getAssetType(assetInfo);
82
96
  this.chain = assetInfo.chain;
83
97
  this.ticker = assetInfo.ticker;
@@ -85,15 +99,9 @@ export class AssetValue extends BigIntArithmetics {
85
99
  this.address = assetInfo.address;
86
100
  this.isSynthetic = assetInfo.isSynthetic;
87
101
  this.isGasAsset = assetInfo.isGasAsset;
88
- }
89
102
 
90
- address?: string;
91
- isSynthetic = false;
92
- isGasAsset = false;
93
- chain: Chain;
94
- symbol: string;
95
- ticker: string;
96
- type: ReturnType<typeof getAssetType>;
103
+ this.tax = params.tax;
104
+ }
97
105
 
98
106
  toString(short = false) {
99
107
  // THOR.RUNE | ETH/ETH
@@ -115,16 +123,18 @@ export class AssetValue extends BigIntArithmetics {
115
123
 
116
124
  static fromStringSync(assetString: string, value: NumberPrimitives = 0) {
117
125
  const { isSynthetic } = getAssetInfo(assetString);
118
- const { decimal, identifier: tokenIdentifier } = getStaticToken(
119
- assetString as unknown as TokenNames,
120
- );
126
+ const {
127
+ tax,
128
+ decimal,
129
+ identifier: tokenIdentifier,
130
+ } = getStaticToken(assetString as unknown as TokenNames);
121
131
 
122
132
  const parsedValue = safeValue(value, decimal);
123
133
 
124
134
  const asset = tokenIdentifier
125
- ? new AssetValue({ decimal, identifier: tokenIdentifier, value: parsedValue })
135
+ ? new AssetValue({ tax, decimal, identifier: tokenIdentifier, value: parsedValue })
126
136
  : isSynthetic
127
- ? new AssetValue({ decimal: 8, identifier: assetString, value: parsedValue })
137
+ ? new AssetValue({ tax, decimal: 8, identifier: assetString, value: parsedValue })
128
138
  : undefined;
129
139
 
130
140
  return asset;
@@ -234,6 +244,6 @@ const getAssetInfo = (identifier: string) => {
234
244
  symbol:
235
245
  (isSynthetic ? `${synthChain}/` : '') +
236
246
  (address ? `${ticker}-${address?.toLowerCase() ?? ''}` : symbol),
237
- ticker: ticker,
247
+ ticker,
238
248
  };
239
249
  };
@@ -11,6 +11,7 @@ export type NumberPrimitives = bigint | number | string;
11
11
  type InitialisationValueType = NumberPrimitives | BigIntArithmetics | SwapKitNumber;
12
12
 
13
13
  type SKBigIntParams = InitialisationValueType | { decimal?: number; value: number | string };
14
+ type AllowedNumberTypes = 'bigint' | 'number' | 'string';
14
15
 
15
16
  const DEFAULT_DECIMAL = 8;
16
17
  const toMultiplier = (decimal: number) => 10n ** BigInt(decimal);
@@ -83,12 +84,14 @@ export class BigIntArithmetics {
83
84
 
84
85
  constructor(params: SKBigIntParams) {
85
86
  const value = getStringValue(params);
86
- this.decimal = typeof params === 'object' ? params.decimal : undefined;
87
+ const isComplex = typeof params === 'object';
88
+ this.decimal = isComplex ? params.decimal : undefined;
87
89
 
88
90
  // use the multiplier to keep track of decimal point - defaults to 8 if lower than 8
89
- this.decimalMultiplier = toMultiplier(
90
- Math.max(getFloatDecimals(toSafeValue(value)), this.decimal || 0),
91
- );
91
+ this.decimalMultiplier =
92
+ isComplex && 'decimalMultiplier' in params
93
+ ? params.decimalMultiplier
94
+ : toMultiplier(Math.max(getFloatDecimals(toSafeValue(value)), this.decimal || 0));
92
95
  this.#setValue(value);
93
96
  }
94
97
 
@@ -109,22 +112,23 @@ export class BigIntArithmetics {
109
112
  return this.#arithmetics('div', ...args);
110
113
  }
111
114
  gt(value: InitialisationValueType) {
112
- return this.bigIntValue > this.getBigIntValue(value);
115
+ return this.#comparison('gt', value);
113
116
  }
114
117
  gte(value: InitialisationValueType) {
115
- return this.bigIntValue >= this.getBigIntValue(value);
118
+ return this.#comparison('gte', value);
116
119
  }
117
120
  lt(value: InitialisationValueType) {
118
- return this.bigIntValue < this.getBigIntValue(value);
121
+ return this.#comparison('lt', value);
119
122
  }
120
123
  lte(value: InitialisationValueType) {
121
- return this.bigIntValue <= this.getBigIntValue(value);
124
+ return this.#comparison('lte', value);
122
125
  }
123
126
  eqValue(value: InitialisationValueType) {
124
- return this.bigIntValue === this.getBigIntValue(value);
127
+ return this.#comparison('eqValue', value);
125
128
  }
126
129
 
127
- getValue<T extends 'number' | 'string'>(type: T): NumberPrimitivesType[T] {
130
+ // @ts-expect-error False positive
131
+ getValue<T extends AllowedNumberTypes>(type: T): NumberPrimitivesType[T] {
128
132
  const value = this.formatBigIntToSafeValue(
129
133
  this.bigIntValue,
130
134
  this.decimal || decimalFromMultiplier(this.decimalMultiplier),
@@ -132,31 +136,27 @@ export class BigIntArithmetics {
132
136
 
133
137
  switch (type) {
134
138
  case 'number':
135
- // @ts-expect-error False positive
136
- return Number(value);
139
+ return Number(value) as NumberPrimitivesType[T];
137
140
  case 'string':
138
- // @ts-expect-error False positive
139
- return value;
140
- default:
141
- // @ts-expect-error False positive
142
- return (this.bigIntValue * BigInt(this.decimal || 8n)) / this.decimalMultiplier;
141
+ return value as NumberPrimitivesType[T];
142
+ case 'bigint':
143
+ return ((this.bigIntValue * 10n ** BigInt(this.decimal || 8n)) /
144
+ this.decimalMultiplier) as NumberPrimitivesType[T];
143
145
  }
144
146
  }
145
147
 
146
- getBaseValue<T extends 'number' | 'string' | 'bigint'>(type: T): NumberPrimitivesType[T] {
148
+ // @ts-expect-error
149
+ getBaseValue<T extends AllowedNumberTypes>(type: T): NumberPrimitivesType[T] {
147
150
  const divisor = this.decimalMultiplier / toMultiplier(this.decimal || BaseDecimal.THOR);
148
151
  const baseValue = this.bigIntValue / divisor;
149
152
 
150
153
  switch (type) {
151
154
  case 'number':
152
- // @ts-expect-error False positive
153
- return Number(baseValue);
155
+ return Number(baseValue) as NumberPrimitivesType[T];
154
156
  case 'string':
155
- // @ts-expect-error False positive
156
- return baseValue.toString();
157
- default:
158
- // @ts-expect-error False positive
159
- return baseValue;
157
+ return baseValue.toString() as NumberPrimitivesType[T];
158
+ case 'bigint':
159
+ return baseValue as NumberPrimitivesType[T];
160
160
  }
161
161
  }
162
162
 
@@ -170,39 +170,6 @@ export class BigIntArithmetics {
170
170
  return this.#toBigInt(safeValue, decimal);
171
171
  }
172
172
 
173
- formatBigIntToSafeValue(value: bigint, decimal?: number) {
174
- const bigIntDecimal = decimal || this.decimal || DEFAULT_DECIMAL;
175
- const decimalToUseForConversion = Math.max(
176
- bigIntDecimal,
177
- decimalFromMultiplier(this.decimalMultiplier),
178
- );
179
- const isNegative = value < 0n;
180
-
181
- const valueString = value.toString().substring(isNegative ? 1 : 0);
182
- const padLength = decimalToUseForConversion - (valueString.length - 1);
183
-
184
- const parsedValueString = padLength > 0 ? '0'.repeat(padLength) + valueString : valueString;
185
-
186
- const decimalIndex = parsedValueString.length - decimalToUseForConversion;
187
- let decimalString = parsedValueString.slice(-decimalToUseForConversion);
188
-
189
- // Check if we need to round up
190
- if (parseInt(decimalString[bigIntDecimal]) >= 5) {
191
- // Increment the last decimal place and slice off the rest
192
- decimalString = `${decimalString.substring(0, bigIntDecimal - 1)}${(
193
- parseInt(decimalString[bigIntDecimal - 1]) + 1
194
- ).toString()}`;
195
- } else {
196
- // Just slice off the extra digits
197
- decimalString = decimalString.substring(0, bigIntDecimal);
198
- }
199
-
200
- return `${isNegative ? '-' : ''}${parsedValueString.slice(
201
- 0,
202
- decimalIndex,
203
- )}.${decimalString}`.replace(/\.?0*$/, '');
204
- }
205
-
206
173
  toSignificant(significantDigits: number = 6) {
207
174
  const [int, dec] = this.getValue('string').split('.');
208
175
  const integer = int || '';
@@ -290,13 +257,47 @@ export class BigIntArithmetics {
290
257
  }`;
291
258
  }
292
259
 
260
+ formatBigIntToSafeValue(value: bigint, decimal?: number) {
261
+ const bigIntDecimal = decimal || this.decimal || DEFAULT_DECIMAL;
262
+ const decimalToUseForConversion = Math.max(
263
+ bigIntDecimal,
264
+ decimalFromMultiplier(this.decimalMultiplier),
265
+ );
266
+ const isNegative = value < 0n;
267
+
268
+ const valueString = value.toString().substring(isNegative ? 1 : 0);
269
+ const padLength = decimalToUseForConversion - (valueString.length - 1);
270
+
271
+ const parsedValueString = padLength > 0 ? '0'.repeat(padLength) + valueString : valueString;
272
+
273
+ const decimalIndex = parsedValueString.length - decimalToUseForConversion;
274
+ let decimalString = parsedValueString.slice(-decimalToUseForConversion);
275
+
276
+ // Check if we need to round up
277
+ if (parseInt(decimalString[bigIntDecimal]) >= 5) {
278
+ // Increment the last decimal place and slice off the rest
279
+ decimalString = `${decimalString.substring(0, bigIntDecimal - 1)}${(
280
+ parseInt(decimalString[bigIntDecimal - 1]) + 1
281
+ ).toString()}`;
282
+ } else {
283
+ // Just slice off the extra digits
284
+ decimalString = decimalString.substring(0, bigIntDecimal);
285
+ }
286
+
287
+ return `${isNegative ? '-' : ''}${parsedValueString.slice(
288
+ 0,
289
+ decimalIndex,
290
+ )}.${decimalString}`.replace(/\.?0*$/, '');
291
+ }
292
+
293
293
  #arithmetics(method: 'add' | 'sub' | 'mul' | 'div', ...args: InitialisationValueType[]): this {
294
294
  const precisionDecimal = this.#retrievePrecisionDecimal(this, ...args);
295
- const precisionDecimalMultiplier = toMultiplier(precisionDecimal);
295
+ const decimal = Math.max(precisionDecimal, decimalFromMultiplier(this.decimalMultiplier));
296
+ const precisionDecimalMultiplier = toMultiplier(decimal);
296
297
 
297
298
  const result = args.reduce(
298
299
  (acc: bigint, arg) => {
299
- const value = this.getBigIntValue(arg, precisionDecimal);
300
+ const value = this.getBigIntValue(arg, decimal);
300
301
 
301
302
  switch (method) {
302
303
  case 'add':
@@ -326,13 +327,37 @@ export class BigIntArithmetics {
326
327
  );
327
328
 
328
329
  const value = formatBigIntToSafeValue({
329
- bigIntDecimal: precisionDecimal,
330
- decimal: Math.max(precisionDecimal, decimalFromMultiplier(this.decimalMultiplier)),
330
+ bigIntDecimal: decimal,
331
+ decimal,
331
332
  value: result,
332
333
  });
333
334
 
334
335
  // @ts-expect-error False positive
335
- 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
+ }
336
361
  }
337
362
 
338
363
  #setValue(value: InitialisationValueType) {
@@ -342,12 +367,16 @@ export class BigIntArithmetics {
342
367
 
343
368
  #retrievePrecisionDecimal(...args: InitialisationValueType[]) {
344
369
  const decimals = args
345
- .map((arg) =>
346
- typeof arg === 'object'
370
+ .map((arg) => {
371
+ const isObject = typeof arg === 'object';
372
+ const value = isObject
347
373
  ? arg.decimal || decimalFromMultiplier(arg.decimalMultiplier)
348
- : getFloatDecimals(toSafeValue(arg)),
349
- )
374
+ : getFloatDecimals(toSafeValue(arg));
375
+
376
+ return value;
377
+ })
350
378
  .filter(Boolean) as number[];
379
+
351
380
  return Math.max(...decimals, DEFAULT_DECIMAL);
352
381
  }
353
382