Skip to content

Commit

Permalink
feat: utils for multiple of minimum
Browse files Browse the repository at this point in the history
  • Loading branch information
itsmnthn committed Mar 22, 2024
1 parent 978e030 commit a258298
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 5 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@ Using [`pnpm`](https://pnpm.io/) package manger
Format code with `pnpm lint --fix` to let ESLint formats and lints the code.
Learn more about the [ESLint Setup](https://github.com/antfu/contribute/blob/main/README.md#eslint).

[Don't use Prettier.](https://github.com/antfu/contribute/blob/main/README.md#no-prettier)
[Don't use Prettier.](https://github.com/antfu/contribute/blob/main/README.md#no-prettier)

## Packages

`@hubble-exchange/utils` - Utility for bigint and other common functions read more [here](/packages/utils/README.md)
2 changes: 1 addition & 1 deletion packages/utils/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ yarn add @hubble-exchange/utils
npm install @hubble-exchange/utils
```

[Check out TSDocs.dev for the full documentation](https://tsdocs.dev/docs/@hubble-exchange/utils)
[Check out TSDocs.dev for the full documentation](https://tsdocs.dev/docs/@hubble-exchange/utils/2.1.0/index.html)
7 changes: 4 additions & 3 deletions packages/utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
export * from './formatter'
export * from './units/unscale'
export * from './units/scale'
export * from './units/bigUtils'
export * from './units/price'
export * from './units/formatter'
export * from './units/multiple'
export * from './units/price'
export * from './units/scale'
export * from './units/unscale'

export const simulateAsyncPause = (duration = 1000) =>
new Promise<void>((resolve) => {
Expand Down
31 changes: 31 additions & 0 deletions packages/utils/src/units/multiple.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { expect, test } from 'vitest'
import { isMultipleOfMinimum, reduceByRemainder } from './multiple'

test('is given value is multiple of given minimum', () => {
expect(isMultipleOfMinimum('1000000000000000000', '1000000000000000000')).toMatchInlineSnapshot('true')
expect(isMultipleOfMinimum('1000000000000000000', '1000000000000000001')).toMatchInlineSnapshot('false')
expect(isMultipleOfMinimum('1000000000000000001', '1000000000000000000')).toMatchInlineSnapshot('false')
expect(isMultipleOfMinimum('0', '1000000000000000000')).toMatchInlineSnapshot('false')
expect(isMultipleOfMinimum('1000000000000000000', '0')).toMatchInlineSnapshot('false')
expect(isMultipleOfMinimum('0', '0')).toMatchInlineSnapshot('false')
expect(isMultipleOfMinimum('100', '1')).toMatchInlineSnapshot('true')
expect(isMultipleOfMinimum('12356', '1')).toMatchInlineSnapshot('true')
expect(isMultipleOfMinimum('123560', '10')).toMatchInlineSnapshot('true')
expect(isMultipleOfMinimum('123560', '11')).toMatchInlineSnapshot('false')
expect(isMultipleOfMinimum('55555555555555555555555555555555555555555555', '11')).toMatchInlineSnapshot('true')
})

test('reduce value till multiple of minimum', () => {
expect(reduceByRemainder('1000000000000000000', '1000000000000000000')).toMatchInlineSnapshot('1000000000000000000n')
expect(reduceByRemainder('1000000000000000001', '1000000000000000000')).toMatchInlineSnapshot('1000000000000000000n')
expect(reduceByRemainder('1000000000000000000', '1000000000000000001')).toMatchInlineSnapshot('0n')
expect(reduceByRemainder('0', '1000000000000000000')).toMatchInlineSnapshot('0n')
expect(reduceByRemainder('1000000000000000000', '0')).toMatchInlineSnapshot('0n')
expect(reduceByRemainder('0', '0')).toMatchInlineSnapshot('0n')
expect(reduceByRemainder('100', '1')).toMatchInlineSnapshot('100n')
expect(reduceByRemainder('12356', '1')).toMatchInlineSnapshot('12356n')
expect(reduceByRemainder('123560', '10')).toMatchInlineSnapshot('123560n')
expect(reduceByRemainder('123560', '11')).toMatchInlineSnapshot('123552n')
expect(reduceByRemainder('55555555555555555555555555555555555555555555', '11'))
.toMatchInlineSnapshot('55555555555555555555555555555555555555555555n')
})
54 changes: 54 additions & 0 deletions packages/utils/src/units/multiple.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { absBig } from './bigUtils'

/**
* Checks if the given value is a multiple of the specified minimum value.
* Both the value and the minimum should be represented as strings to maintain precision
* and should have the same scale (e.g., both represented in wei for Ethereum transactions).
*
* @param {string} value - The value to validate, as a string.
* @param {string} minimum - The minimum value to validate against, as a string.
* @returns {boolean} - True if the value is a multiple of the minimum and greater than or equal to the minimum value; otherwise, false.
*
* @example
* isMultipleOfMinimum('123560', '10') // true
* isMultipleOfMinimum('123560', '11') // false
*/
export function isMultipleOfMinimum(value: string | bigint, minimum: string | bigint) {
// (x != 0 && x % y == 0)
value = absBig(value)
minimum = absBig(minimum)
const zero = BigInt(0)

return value !== zero && minimum !== zero && value % minimum === zero && value >= minimum
}

/**
* Returns the adjusted value to be a multiple of the minimum value.
* Both `value` and `minimum` are expected to be strings representing numbers
* with the same number of decimal places. The adjustment ensures that the returned
* value is a multiple of the `minimum` and is less than or equal to the original `value`.
*
* @param {string} value - The value to adjust, should be in the same decimals as `minimum`.
* @param {string} minimum - The minimum value to adjust against, should be in the same decimals as `value`.
* @returns {string} - The adjusted value, in the same decimals as `value`, ensuring it's a multiple of `minimum`.
*
* @example
* reduceByRemainder('123560', '10') // '123560'
* reduceByRemainder('123560', '11') // '123552'
*/
export function reduceByRemainder(value: string | bigint, minimum: string | bigint) {
// Convert strings to BigInt for calculation
value = BigInt(value)
minimum = BigInt(minimum)
const zero = BigInt(0)

if (value === zero || minimum === zero)
return zero

// Check if value is already a multiple of minimum
const remainder = value % minimum
if (remainder === zero)
return value

return value - remainder
}

0 comments on commit a258298

Please sign in to comment.