@lifi/perps-sdk 23.0.0 → 24.0.0
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/README.md +85 -45
- package/dist/cjs/client/PerpsClient.d.ts +3 -2
- package/dist/cjs/client/PerpsClient.d.ts.map +1 -1
- package/dist/cjs/client/PerpsClient.js +16 -9
- package/dist/cjs/client/PerpsClient.js.map +1 -1
- package/dist/cjs/decimal/arithmetic.d.ts +13 -0
- package/dist/cjs/decimal/arithmetic.d.ts.map +1 -0
- package/dist/cjs/decimal/arithmetic.js +48 -0
- package/dist/cjs/decimal/arithmetic.js.map +1 -0
- package/dist/cjs/decimal/big.d.ts +0 -1
- package/dist/cjs/decimal/big.d.ts.map +1 -1
- package/dist/cjs/decimal/big.js +0 -4
- package/dist/cjs/decimal/big.js.map +1 -1
- package/dist/cjs/decimal/compare.d.ts +7 -0
- package/dist/cjs/decimal/compare.d.ts.map +1 -0
- package/dist/cjs/decimal/compare.js +21 -0
- package/dist/cjs/decimal/compare.js.map +1 -0
- package/dist/cjs/decimal/convert.d.ts +12 -6
- package/dist/cjs/decimal/convert.d.ts.map +1 -1
- package/dist/cjs/decimal/convert.js +36 -14
- package/dist/cjs/decimal/convert.js.map +1 -1
- package/dist/cjs/decimal/decimalStringToBig.d.ts +5 -0
- package/dist/cjs/decimal/decimalStringToBig.d.ts.map +1 -0
- package/dist/cjs/decimal/decimalStringToBig.js +27 -0
- package/dist/cjs/decimal/decimalStringToBig.js.map +1 -0
- package/dist/cjs/decimal/format.d.ts.map +1 -1
- package/dist/cjs/decimal/format.js +70 -49
- package/dist/cjs/decimal/format.js.map +1 -1
- package/dist/cjs/decimal/parse.d.ts +4 -3
- package/dist/cjs/decimal/parse.d.ts.map +1 -1
- package/dist/cjs/decimal/parse.js +44 -19
- package/dist/cjs/decimal/parse.js.map +1 -1
- package/dist/cjs/index.d.ts +13 -10
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +66 -7
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/math/fills.d.ts +1 -1
- package/dist/cjs/math/fills.d.ts.map +1 -1
- package/dist/cjs/math/fills.js +28 -13
- package/dist/cjs/math/fills.js.map +1 -1
- package/dist/cjs/math/order.d.ts +27 -16
- package/dist/cjs/math/order.d.ts.map +1 -1
- package/dist/cjs/math/order.js +106 -122
- package/dist/cjs/math/order.js.map +1 -1
- package/dist/cjs/math/position.d.ts +85 -35
- package/dist/cjs/math/position.d.ts.map +1 -1
- package/dist/cjs/math/position.js +88 -91
- package/dist/cjs/math/position.js.map +1 -1
- package/dist/cjs/services/getGasRecommendation.js +1 -1
- package/dist/cjs/services/getGasRecommendation.js.map +1 -1
- package/dist/cjs/services/getQuote.d.ts +1 -1
- package/dist/cjs/services/resolveSubscribeQuote.d.ts.map +1 -1
- package/dist/cjs/services/resolveSubscribeQuote.js +3 -2
- package/dist/cjs/services/resolveSubscribeQuote.js.map +1 -1
- package/dist/cjs/transport/request.d.ts +1 -0
- package/dist/cjs/transport/request.d.ts.map +1 -1
- package/dist/cjs/transport/request.js +2 -2
- package/dist/cjs/transport/request.js.map +1 -1
- package/dist/cjs/types/api.d.ts +4 -0
- package/dist/cjs/types/api.d.ts.map +1 -1
- package/dist/cjs/types/provider.d.ts +9 -5
- package/dist/cjs/types/provider.d.ts.map +1 -1
- package/dist/cjs/types/withdrawal.d.ts +12 -4
- package/dist/cjs/types/withdrawal.d.ts.map +1 -1
- package/dist/cjs/utils/createSafeFunction.d.ts +2 -0
- package/dist/cjs/utils/createSafeFunction.d.ts.map +1 -0
- package/dist/cjs/utils/createSafeFunction.js +15 -0
- package/dist/cjs/utils/createSafeFunction.js.map +1 -0
- package/dist/cjs/utils/timestampToIsoString.d.ts +3 -0
- package/dist/cjs/utils/timestampToIsoString.d.ts.map +1 -0
- package/dist/cjs/utils/timestampToIsoString.js +16 -0
- package/dist/cjs/utils/timestampToIsoString.js.map +1 -0
- package/dist/cjs/utils/warnOnce.d.ts +2 -0
- package/dist/cjs/utils/warnOnce.d.ts.map +1 -1
- package/dist/cjs/utils/warnOnce.js +13 -1
- package/dist/cjs/utils/warnOnce.js.map +1 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/websocket/PerpsWsClient.d.ts +2 -0
- package/dist/cjs/websocket/PerpsWsClient.d.ts.map +1 -1
- package/dist/cjs/websocket/PerpsWsClient.js +14 -1
- package/dist/cjs/websocket/PerpsWsClient.js.map +1 -1
- package/dist/cjs/websocket/wsLog.d.ts +2 -0
- package/dist/cjs/websocket/wsLog.d.ts.map +1 -1
- package/dist/cjs/websocket/wsLog.js +8 -0
- package/dist/cjs/websocket/wsLog.js.map +1 -1
- package/dist/cjs/wire/orderAmounts.d.ts.map +1 -1
- package/dist/cjs/wire/orderAmounts.js +6 -9
- package/dist/cjs/wire/orderAmounts.js.map +1 -1
- package/dist/cjs/wire/refuel.d.ts +4 -1
- package/dist/cjs/wire/refuel.d.ts.map +1 -1
- package/dist/cjs/wire/refuel.js +23 -6
- package/dist/cjs/wire/refuel.js.map +1 -1
- package/dist/cjs/wire/transferable.js +3 -3
- package/dist/cjs/wire/transferable.js.map +1 -1
- package/dist/cjs/wire/withdraw.d.ts.map +1 -1
- package/dist/cjs/wire/withdraw.js +3 -3
- package/dist/cjs/wire/withdraw.js.map +1 -1
- package/dist/esm/client/PerpsClient.d.ts +17 -4
- package/dist/esm/client/PerpsClient.d.ts.map +1 -1
- package/dist/esm/client/PerpsClient.js +30 -8
- package/dist/esm/client/PerpsClient.js.map +1 -1
- package/dist/esm/decimal/arithmetic.d.ts +84 -0
- package/dist/esm/decimal/arithmetic.d.ts.map +1 -0
- package/dist/esm/decimal/arithmetic.js +110 -0
- package/dist/esm/decimal/arithmetic.js.map +1 -0
- package/dist/esm/decimal/big.d.ts +0 -2
- package/dist/esm/decimal/big.d.ts.map +1 -1
- package/dist/esm/decimal/big.js +0 -4
- package/dist/esm/decimal/big.js.map +1 -1
- package/dist/esm/decimal/compare.d.ts +45 -0
- package/dist/esm/decimal/compare.d.ts.map +1 -0
- package/dist/esm/decimal/compare.js +53 -0
- package/dist/esm/decimal/compare.js.map +1 -0
- package/dist/esm/decimal/convert.d.ts +62 -26
- package/dist/esm/decimal/convert.d.ts.map +1 -1
- package/dist/esm/decimal/convert.js +76 -25
- package/dist/esm/decimal/convert.js.map +1 -1
- package/dist/esm/decimal/decimalStringToBig.d.ts +26 -0
- package/dist/esm/decimal/decimalStringToBig.d.ts.map +1 -0
- package/dist/esm/decimal/decimalStringToBig.js +40 -0
- package/dist/esm/decimal/decimalStringToBig.js.map +1 -0
- package/dist/esm/decimal/format.d.ts +4 -1
- package/dist/esm/decimal/format.d.ts.map +1 -1
- package/dist/esm/decimal/format.js +71 -66
- package/dist/esm/decimal/format.js.map +1 -1
- package/dist/esm/decimal/parse.d.ts +26 -7
- package/dist/esm/decimal/parse.d.ts.map +1 -1
- package/dist/esm/decimal/parse.js +59 -33
- package/dist/esm/decimal/parse.js.map +1 -1
- package/dist/esm/index.d.ts +13 -10
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +9 -6
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/math/fills.d.ts +6 -3
- package/dist/esm/math/fills.d.ts.map +1 -1
- package/dist/esm/math/fills.js +31 -16
- package/dist/esm/math/fills.js.map +1 -1
- package/dist/esm/math/order.d.ts +85 -65
- package/dist/esm/math/order.d.ts.map +1 -1
- package/dist/esm/math/order.js +158 -166
- package/dist/esm/math/order.js.map +1 -1
- package/dist/esm/math/position.d.ts +155 -98
- package/dist/esm/math/position.d.ts.map +1 -1
- package/dist/esm/math/position.js +157 -155
- package/dist/esm/math/position.js.map +1 -1
- package/dist/esm/services/getGasRecommendation.js +1 -1
- package/dist/esm/services/getGasRecommendation.js.map +1 -1
- package/dist/esm/services/getQuote.d.ts +2 -2
- package/dist/esm/services/getQuote.js +1 -1
- package/dist/esm/services/resolveSubscribeQuote.d.ts +1 -1
- package/dist/esm/services/resolveSubscribeQuote.d.ts.map +1 -1
- package/dist/esm/services/resolveSubscribeQuote.js +4 -3
- package/dist/esm/services/resolveSubscribeQuote.js.map +1 -1
- package/dist/esm/transport/request.d.ts +5 -0
- package/dist/esm/transport/request.d.ts.map +1 -1
- package/dist/esm/transport/request.js +2 -2
- package/dist/esm/transport/request.js.map +1 -1
- package/dist/esm/types/api.d.ts +9 -0
- package/dist/esm/types/api.d.ts.map +1 -1
- package/dist/esm/types/provider.d.ts +26 -13
- package/dist/esm/types/provider.d.ts.map +1 -1
- package/dist/esm/types/withdrawal.d.ts +32 -7
- package/dist/esm/types/withdrawal.d.ts.map +1 -1
- package/dist/esm/utils/createSafeFunction.d.ts +9 -0
- package/dist/esm/utils/createSafeFunction.d.ts.map +1 -0
- package/dist/esm/utils/createSafeFunction.js +19 -0
- package/dist/esm/utils/createSafeFunction.js.map +1 -0
- package/dist/esm/utils/timestampToIsoString.d.ts +15 -0
- package/dist/esm/utils/timestampToIsoString.d.ts.map +1 -0
- package/dist/esm/utils/timestampToIsoString.js +24 -0
- package/dist/esm/utils/timestampToIsoString.js.map +1 -0
- package/dist/esm/utils/warnOnce.d.ts +16 -0
- package/dist/esm/utils/warnOnce.d.ts.map +1 -1
- package/dist/esm/utils/warnOnce.js +24 -0
- package/dist/esm/utils/warnOnce.js.map +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/websocket/PerpsWsClient.d.ts +7 -0
- package/dist/esm/websocket/PerpsWsClient.d.ts.map +1 -1
- package/dist/esm/websocket/PerpsWsClient.js +20 -2
- package/dist/esm/websocket/PerpsWsClient.js.map +1 -1
- package/dist/esm/websocket/wsLog.d.ts +12 -0
- package/dist/esm/websocket/wsLog.d.ts.map +1 -1
- package/dist/esm/websocket/wsLog.js +18 -0
- package/dist/esm/websocket/wsLog.js.map +1 -1
- package/dist/esm/wire/orderAmounts.d.ts.map +1 -1
- package/dist/esm/wire/orderAmounts.js +2 -2
- package/dist/esm/wire/orderAmounts.js.map +1 -1
- package/dist/esm/wire/refuel.d.ts +24 -14
- package/dist/esm/wire/refuel.d.ts.map +1 -1
- package/dist/esm/wire/refuel.js +38 -17
- package/dist/esm/wire/refuel.js.map +1 -1
- package/dist/esm/wire/transferable.js +3 -3
- package/dist/esm/wire/transferable.js.map +1 -1
- package/dist/esm/wire/withdraw.d.ts.map +1 -1
- package/dist/esm/wire/withdraw.js +3 -3
- package/dist/esm/wire/withdraw.js.map +1 -1
- package/dist/types/client/PerpsClient.d.ts +17 -4
- package/dist/types/client/PerpsClient.d.ts.map +1 -1
- package/dist/types/decimal/arithmetic.d.ts +84 -0
- package/dist/types/decimal/arithmetic.d.ts.map +1 -0
- package/dist/types/decimal/big.d.ts +0 -2
- package/dist/types/decimal/big.d.ts.map +1 -1
- package/dist/types/decimal/compare.d.ts +45 -0
- package/dist/types/decimal/compare.d.ts.map +1 -0
- package/dist/types/decimal/convert.d.ts +62 -26
- package/dist/types/decimal/convert.d.ts.map +1 -1
- package/dist/types/decimal/decimalStringToBig.d.ts +26 -0
- package/dist/types/decimal/decimalStringToBig.d.ts.map +1 -0
- package/dist/types/decimal/format.d.ts +4 -1
- package/dist/types/decimal/format.d.ts.map +1 -1
- package/dist/types/decimal/parse.d.ts +26 -7
- package/dist/types/decimal/parse.d.ts.map +1 -1
- package/dist/types/index.d.ts +13 -10
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/math/fills.d.ts +6 -3
- package/dist/types/math/fills.d.ts.map +1 -1
- package/dist/types/math/order.d.ts +85 -65
- package/dist/types/math/order.d.ts.map +1 -1
- package/dist/types/math/position.d.ts +155 -98
- package/dist/types/math/position.d.ts.map +1 -1
- package/dist/types/services/getQuote.d.ts +2 -2
- package/dist/types/services/resolveSubscribeQuote.d.ts +1 -1
- package/dist/types/services/resolveSubscribeQuote.d.ts.map +1 -1
- package/dist/types/transport/request.d.ts +5 -0
- package/dist/types/transport/request.d.ts.map +1 -1
- package/dist/types/types/api.d.ts +9 -0
- package/dist/types/types/api.d.ts.map +1 -1
- package/dist/types/types/provider.d.ts +26 -13
- package/dist/types/types/provider.d.ts.map +1 -1
- package/dist/types/types/withdrawal.d.ts +32 -7
- package/dist/types/types/withdrawal.d.ts.map +1 -1
- package/dist/types/utils/createSafeFunction.d.ts +9 -0
- package/dist/types/utils/createSafeFunction.d.ts.map +1 -0
- package/dist/types/utils/timestampToIsoString.d.ts +15 -0
- package/dist/types/utils/timestampToIsoString.d.ts.map +1 -0
- package/dist/types/utils/warnOnce.d.ts +16 -0
- package/dist/types/utils/warnOnce.d.ts.map +1 -1
- package/dist/types/version.d.ts +1 -1
- package/dist/types/websocket/PerpsWsClient.d.ts +7 -0
- package/dist/types/websocket/PerpsWsClient.d.ts.map +1 -1
- package/dist/types/websocket/wsLog.d.ts +12 -0
- package/dist/types/websocket/wsLog.d.ts.map +1 -1
- package/dist/types/wire/orderAmounts.d.ts.map +1 -1
- package/dist/types/wire/refuel.d.ts +24 -14
- package/dist/types/wire/refuel.d.ts.map +1 -1
- package/dist/types/wire/withdraw.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/client/PerpsClient.ts +48 -10
- package/src/decimal/arithmetic.ts +144 -0
- package/src/decimal/big.ts +0 -5
- package/src/decimal/compare.ts +67 -0
- package/src/decimal/convert.ts +123 -44
- package/src/decimal/decimalStringToBig.ts +47 -0
- package/src/decimal/format.ts +81 -68
- package/src/decimal/parse.ts +73 -36
- package/src/index.ts +76 -8
- package/src/math/fills.ts +32 -17
- package/src/math/order.ts +261 -216
- package/src/math/position.ts +277 -194
- package/src/services/getGasRecommendation.ts +1 -1
- package/src/services/getQuote.ts +2 -2
- package/src/services/resolveSubscribeQuote.ts +4 -3
- package/src/transport/request.ts +7 -2
- package/src/types/api.ts +10 -0
- package/src/types/provider.ts +35 -13
- package/src/types/withdrawal.ts +35 -8
- package/src/utils/createSafeFunction.ts +20 -0
- package/src/utils/timestampToIsoString.ts +31 -0
- package/src/utils/warnOnce.ts +44 -0
- package/src/version.ts +1 -1
- package/src/websocket/PerpsWsClient.ts +25 -2
- package/src/websocket/wsLog.ts +31 -0
- package/src/wire/orderAmounts.ts +3 -2
- package/src/wire/refuel.ts +56 -20
- package/src/wire/transferable.ts +3 -3
- package/src/wire/withdraw.ts +3 -5
- package/dist/cjs/decimal/requireDecimal.d.ts +0 -4
- package/dist/cjs/decimal/requireDecimal.d.ts.map +0 -1
- package/dist/cjs/decimal/requireDecimal.js +0 -17
- package/dist/cjs/decimal/requireDecimal.js.map +0 -1
- package/dist/esm/decimal/requireDecimal.d.ts +0 -11
- package/dist/esm/decimal/requireDecimal.d.ts.map +0 -1
- package/dist/esm/decimal/requireDecimal.js +0 -18
- package/dist/esm/decimal/requireDecimal.js.map +0 -1
- package/dist/types/decimal/requireDecimal.d.ts +0 -11
- package/dist/types/decimal/requireDecimal.d.ts.map +0 -1
- package/src/decimal/requireDecimal.ts +0 -21
package/src/decimal/convert.ts
CHANGED
|
@@ -1,44 +1,92 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PerpsErrorCode } from '@lifi/perps-types'
|
|
2
2
|
import Big from 'big.js'
|
|
3
3
|
import { formatUnits } from 'viem'
|
|
4
4
|
import { PerpsError } from '../errors/PerpsError.js'
|
|
5
|
-
import {
|
|
5
|
+
import { createSafeFunction } from '../utils/createSafeFunction.js'
|
|
6
|
+
import { bigToDecimalString, decimalStringToBig } from './decimalStringToBig.js'
|
|
7
|
+
import { isDecimalString } from './parse.js'
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
|
-
* Off-grid resolution for {@link
|
|
9
|
-
* - `truncate
|
|
10
|
-
*
|
|
11
|
-
* - `round
|
|
12
|
-
*
|
|
10
|
+
* Off-grid resolution for {@link roundDecimalString}:
|
|
11
|
+
* - `truncate`: toward zero, for sizes and collateral amounts. The result
|
|
12
|
+
* never exceeds the caller's intent.
|
|
13
|
+
* - `round`: half away from zero, for prices, which snap to the nearest tick.
|
|
14
|
+
* - `up`: away from zero, for an amount that must cover a target.
|
|
13
15
|
* @public
|
|
14
16
|
*/
|
|
15
|
-
export type
|
|
17
|
+
export type DecimalRounding = 'truncate' | 'round' | 'up'
|
|
18
|
+
|
|
19
|
+
const BIG_ROUNDING = {
|
|
20
|
+
truncate: Big.roundDown,
|
|
21
|
+
round: Big.roundHalfUp,
|
|
22
|
+
up: Big.roundUp,
|
|
23
|
+
} as const
|
|
16
24
|
|
|
17
25
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* per `rounding`; there is no silent default.
|
|
26
|
+
* Round a decimal string to `decimals` places with exact decimal arithmetic.
|
|
27
|
+
* The result has no trailing zeros: `roundDecimalString('1.50', 1, 'round')`
|
|
28
|
+
* is `'1.5'`.
|
|
22
29
|
*
|
|
23
|
-
* @throws {PerpsError} `ValidationError` when `value` is not a
|
|
24
|
-
*
|
|
25
|
-
* result's magnitude exceeds `Number.MAX_SAFE_INTEGER`.
|
|
30
|
+
* @throws {PerpsError} `ValidationError` when `value` is not a decimal string,
|
|
31
|
+
* or `decimals` is not a non-negative integer.
|
|
26
32
|
* @public
|
|
27
33
|
*/
|
|
28
|
-
export
|
|
29
|
-
value:
|
|
34
|
+
export function roundDecimalString(
|
|
35
|
+
value: string,
|
|
30
36
|
decimals: number,
|
|
31
|
-
rounding:
|
|
32
|
-
):
|
|
37
|
+
rounding: DecimalRounding
|
|
38
|
+
): string {
|
|
39
|
+
if (!Number.isInteger(decimals) || decimals < 0) {
|
|
40
|
+
throw new PerpsError(
|
|
41
|
+
PerpsErrorCode.ValidationError,
|
|
42
|
+
`Invalid decimals for rounding: ${decimals}`
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
return bigToDecimalString(
|
|
46
|
+
decimalStringToBig(value).round(decimals, BIG_ROUNDING[rounding])
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* {@link roundDecimalString}, or `undefined` with a warning when it throws.
|
|
52
|
+
*
|
|
53
|
+
* @public
|
|
54
|
+
*/
|
|
55
|
+
export const safeRoundDecimalString = createSafeFunction(
|
|
56
|
+
'roundDecimalString',
|
|
57
|
+
roundDecimalString
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Scale a decimal string to an integer at `decimals` places in exact decimal
|
|
62
|
+
* arithmetic: `'0.29'` at 2 decimals is 29, never 28. Off-grid input resolves
|
|
63
|
+
* per `rounding`. No `$`, `%` or `,` is removed first.
|
|
64
|
+
*
|
|
65
|
+
* @throws {PerpsError} `ValidationError` when `value` is not a decimal string,
|
|
66
|
+
* `decimals` is not a non-negative integer, or the result's magnitude
|
|
67
|
+
* exceeds `Number.MAX_SAFE_INTEGER`.
|
|
68
|
+
* @public
|
|
69
|
+
*/
|
|
70
|
+
export function decimalStringToScaledInteger(
|
|
71
|
+
value: string,
|
|
72
|
+
decimals: number,
|
|
73
|
+
rounding: DecimalRounding
|
|
74
|
+
): number {
|
|
75
|
+
if (!isDecimalString(value)) {
|
|
76
|
+
throw new PerpsError(
|
|
77
|
+
PerpsErrorCode.ValidationError,
|
|
78
|
+
`'${value}' is not a decimal string.`
|
|
79
|
+
)
|
|
80
|
+
}
|
|
33
81
|
if (!Number.isInteger(decimals) || decimals < 0) {
|
|
34
82
|
throw new PerpsError(
|
|
35
83
|
PerpsErrorCode.ValidationError,
|
|
36
84
|
`Invalid decimals for integer scaling: ${decimals}`
|
|
37
85
|
)
|
|
38
86
|
}
|
|
39
|
-
const scaled =
|
|
87
|
+
const scaled = new Big(value)
|
|
40
88
|
.times(new Big(10).pow(decimals))
|
|
41
|
-
.round(0, rounding
|
|
89
|
+
.round(0, BIG_ROUNDING[rounding])
|
|
42
90
|
if (scaled.abs().gt(Number.MAX_SAFE_INTEGER)) {
|
|
43
91
|
throw new PerpsError(
|
|
44
92
|
PerpsErrorCode.ValidationError,
|
|
@@ -48,40 +96,57 @@ export const decimalToBaseUnits = (
|
|
|
48
96
|
return scaled.toNumber()
|
|
49
97
|
}
|
|
50
98
|
|
|
51
|
-
|
|
99
|
+
/**
|
|
100
|
+
* {@link decimalStringToScaledInteger}, or `undefined` with a warning when it
|
|
101
|
+
* throws.
|
|
102
|
+
*
|
|
103
|
+
* @public
|
|
104
|
+
*/
|
|
105
|
+
export const safeDecimalStringToScaledInteger = createSafeFunction(
|
|
106
|
+
'decimalStringToScaledInteger',
|
|
107
|
+
decimalStringToScaledInteger
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
export const BASE_UNITS_PATTERN = /^-?\d+$/
|
|
52
111
|
|
|
53
112
|
/**
|
|
54
|
-
* Convert a
|
|
113
|
+
* Convert a scaled integer string (a token amount in base units) to a decimal
|
|
114
|
+
* string: `scaledIntegerToDecimalString('1000000', 6)` is `'1'`.
|
|
55
115
|
*
|
|
56
|
-
* @param amount - Amount in base units (e.g. "1000000" for 1 USDC)
|
|
57
|
-
* @param decimals - Token decimals (e.g. 6 for USDC)
|
|
58
116
|
* @throws {PerpsError} `ValidationError` when `amount` is not an integer
|
|
59
117
|
* string, or `decimals` is not a non-negative integer.
|
|
60
|
-
* @example
|
|
61
|
-
* ```ts
|
|
62
|
-
* baseUnitsToDecimal('1000000', 6) // '1'
|
|
63
|
-
* ```
|
|
64
118
|
* @public
|
|
65
119
|
*/
|
|
66
|
-
export function
|
|
67
|
-
amount:
|
|
120
|
+
export function scaledIntegerToDecimalString(
|
|
121
|
+
amount: string,
|
|
68
122
|
decimals: number
|
|
69
|
-
):
|
|
123
|
+
): string {
|
|
70
124
|
if (!Number.isInteger(decimals) || decimals < 0) {
|
|
71
125
|
throw new PerpsError(
|
|
72
126
|
PerpsErrorCode.ValidationError,
|
|
73
|
-
`Invalid \`
|
|
127
|
+
`Invalid \`scaledIntegerToDecimalString(decimals)\`: ${decimals} is not a non-negative integer.`
|
|
74
128
|
)
|
|
75
129
|
}
|
|
76
130
|
if (!BASE_UNITS_PATTERN.test(amount)) {
|
|
77
131
|
throw new PerpsError(
|
|
78
132
|
PerpsErrorCode.ValidationError,
|
|
79
|
-
`Invalid \`
|
|
133
|
+
`Invalid \`scaledIntegerToDecimalString(amount)\`: '${amount}' is not an integer base-unit string.`
|
|
80
134
|
)
|
|
81
135
|
}
|
|
82
136
|
return formatUnits(BigInt(amount), decimals)
|
|
83
137
|
}
|
|
84
138
|
|
|
139
|
+
/**
|
|
140
|
+
* {@link scaledIntegerToDecimalString}, or `undefined` with a warning when it
|
|
141
|
+
* throws.
|
|
142
|
+
*
|
|
143
|
+
* @public
|
|
144
|
+
*/
|
|
145
|
+
export const safeScaledIntegerToDecimalString = createSafeFunction(
|
|
146
|
+
'scaledIntegerToDecimalString',
|
|
147
|
+
scaledIntegerToDecimalString
|
|
148
|
+
)
|
|
149
|
+
|
|
85
150
|
/**
|
|
86
151
|
* Round `value` down to `decimals` and pad the result to exactly that many
|
|
87
152
|
* decimal places. The padding is the point: this seeds a fixed-decimal input,
|
|
@@ -94,27 +159,31 @@ export function baseUnitsToDecimal(
|
|
|
94
159
|
* `'0.010000000000000000'`, which `Number#toFixed` cannot.
|
|
95
160
|
*
|
|
96
161
|
* @throws {PerpsError} `ValidationError` when `value` is not a
|
|
97
|
-
*
|
|
162
|
+
* decimal string, or `decimals` is not a non-negative integer.
|
|
98
163
|
* @public
|
|
99
164
|
*/
|
|
100
|
-
export function truncateDecimal(
|
|
101
|
-
value: DecimalString,
|
|
102
|
-
decimals: number
|
|
103
|
-
): DecimalString {
|
|
165
|
+
export function truncateDecimal(value: string, decimals: number): string {
|
|
104
166
|
if (!Number.isInteger(decimals) || decimals < 0) {
|
|
105
167
|
throw new PerpsError(
|
|
106
168
|
PerpsErrorCode.ValidationError,
|
|
107
169
|
`Invalid decimals for truncation: ${decimals}`
|
|
108
170
|
)
|
|
109
171
|
}
|
|
110
|
-
const truncated =
|
|
111
|
-
decimals,
|
|
112
|
-
Big.roundDown
|
|
113
|
-
)
|
|
172
|
+
const truncated = decimalStringToBig(value).round(decimals, Big.roundDown)
|
|
114
173
|
// big.js carries the sign through a round to zero; '-0.00' is not a spelling.
|
|
115
174
|
return (truncated.eq(0) ? new Big(0) : truncated).toFixed(decimals)
|
|
116
175
|
}
|
|
117
176
|
|
|
177
|
+
/**
|
|
178
|
+
* {@link truncateDecimal}, or `undefined` with a warning when it throws.
|
|
179
|
+
*
|
|
180
|
+
* @public
|
|
181
|
+
*/
|
|
182
|
+
export const safeTruncateDecimal = createSafeFunction(
|
|
183
|
+
'truncateDecimal',
|
|
184
|
+
truncateDecimal
|
|
185
|
+
)
|
|
186
|
+
|
|
118
187
|
/**
|
|
119
188
|
* Spell a `number` as a {@link DecimalString} in plain notation: `1e-7`
|
|
120
189
|
* becomes `'0.0000001'`, never `'1e-7'`. The boundary helper for a venue or
|
|
@@ -125,7 +194,7 @@ export function truncateDecimal(
|
|
|
125
194
|
* @throws {PerpsError} `ValidationError` when `value` is `NaN` or infinite.
|
|
126
195
|
* @public
|
|
127
196
|
*/
|
|
128
|
-
export function numberToDecimalString(value: number):
|
|
197
|
+
export function numberToDecimalString(value: number): string {
|
|
129
198
|
if (!Number.isFinite(value)) {
|
|
130
199
|
throw new PerpsError(
|
|
131
200
|
PerpsErrorCode.ValidationError,
|
|
@@ -135,3 +204,13 @@ export function numberToDecimalString(value: number): DecimalString {
|
|
|
135
204
|
const parsed = new Big(value)
|
|
136
205
|
return (parsed.eq(0) ? new Big(0) : parsed).toFixed()
|
|
137
206
|
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* {@link numberToDecimalString}, or `undefined` with a warning when it throws.
|
|
210
|
+
*
|
|
211
|
+
* @public
|
|
212
|
+
*/
|
|
213
|
+
export const safeNumberToDecimalString = createSafeFunction(
|
|
214
|
+
'numberToDecimalString',
|
|
215
|
+
numberToDecimalString
|
|
216
|
+
)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { DECIMAL_PATTERN, PerpsErrorCode } from '@lifi/perps-types'
|
|
2
|
+
import Big from 'big.js'
|
|
3
|
+
import { PerpsError } from '../errors/PerpsError.js'
|
|
4
|
+
import { DivBig } from './big.js'
|
|
5
|
+
|
|
6
|
+
const DISPLAY_CHARACTERS = /[\s$%,]/g
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Read a decimal string as a `Big`. Whitespace, `$`, `%` and `,` are removed
|
|
10
|
+
* first, so `'$1,234.5'` reads as `1234.5`; the rest must match
|
|
11
|
+
* `DECIMAL_PATTERN`, so an exponent form such as `'1e5'` is rejected.
|
|
12
|
+
*
|
|
13
|
+
* @throws {PerpsError} `ValidationError` when `value` is not a string or the
|
|
14
|
+
* cleaned string is not a decimal string.
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
export function decimalStringToBig(value: string): Big {
|
|
18
|
+
const cleaned =
|
|
19
|
+
typeof value === 'string' ? value.replace(DISPLAY_CHARACTERS, '') : ''
|
|
20
|
+
if (!DECIMAL_PATTERN.test(cleaned)) {
|
|
21
|
+
throw new PerpsError(
|
|
22
|
+
PerpsErrorCode.ValidationError,
|
|
23
|
+
`'${String(value)}' is not a decimal string.`
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
return new Big(cleaned)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Spell a `Big` as a canonical decimal string, with no exponent and no `-0`.
|
|
31
|
+
*
|
|
32
|
+
* @internal
|
|
33
|
+
*/
|
|
34
|
+
export function bigToDecimalString(value: Big): string {
|
|
35
|
+
return value.eq(0) ? '0' : value.toFixed()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* {@link decimalStringToBig} as a `DivBig`, so a later `div` keeps 40 decimal
|
|
40
|
+
* places.
|
|
41
|
+
*
|
|
42
|
+
* @throws {PerpsError} `ValidationError` as {@link decimalStringToBig}.
|
|
43
|
+
* @internal
|
|
44
|
+
*/
|
|
45
|
+
export function decimalStringToDivBig(value: string): Big {
|
|
46
|
+
return new DivBig(decimalStringToBig(value))
|
|
47
|
+
}
|
package/src/decimal/format.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { DecimalString } from '@lifi/perps-types'
|
|
2
|
+
import Big from 'big.js'
|
|
3
|
+
import { decimalStringToBig } from './decimalStringToBig.js'
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Options shared by the human-facing display formatters.
|
|
@@ -8,7 +10,10 @@ import type { DecimalString } from '@lifi/perps-types'
|
|
|
8
10
|
export interface FormatOptions {
|
|
9
11
|
/** Fixed number of decimal places. Defaults are per-formatter. */
|
|
10
12
|
decimals?: number
|
|
11
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Rendered when the value is null, undefined, blank or a non-finite number.
|
|
15
|
+
* A string that is not a decimal is shown unchanged. Defaults to `'—'`.
|
|
16
|
+
*/
|
|
12
17
|
placeholder?: string
|
|
13
18
|
/** BCP 47 locale controlling digit grouping and separators (e.g. `'en-US'`). */
|
|
14
19
|
locale?: string
|
|
@@ -33,72 +38,62 @@ const DEFAULT_PLACEHOLDER = '—'
|
|
|
33
38
|
type FormatInput = number | DecimalString | string | null | undefined
|
|
34
39
|
|
|
35
40
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*/
|
|
40
|
-
const FLOAT_GUARD_DIGITS = 9
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Tolerantly coerce a display input to a finite number.
|
|
44
|
-
*
|
|
45
|
-
* Strips `$`, `,`, and surrounding whitespace from strings. Returns `null`
|
|
46
|
-
* when the value is null/undefined/blank or does not resolve to a finite
|
|
47
|
-
* number, so callers can render a placeholder instead of `$0.00` or `$NaN`.
|
|
41
|
+
* Read a display input as a `Big`: `$`, `%`, `,` and whitespace are removed
|
|
42
|
+
* from a string first. `null` means the formatter renders the placeholder;
|
|
43
|
+
* a string it cannot read is returned unchanged for display.
|
|
48
44
|
*/
|
|
49
|
-
function
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return null
|
|
55
|
-
}
|
|
56
|
-
n = Number(cleaned)
|
|
57
|
-
} else if (typeof value === 'number') {
|
|
58
|
-
n = value
|
|
59
|
-
} else {
|
|
45
|
+
function toBig(value: FormatInput): Big | null | string {
|
|
46
|
+
if (typeof value === 'number') {
|
|
47
|
+
return Number.isFinite(value) ? new Big(value) : null
|
|
48
|
+
}
|
|
49
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
60
50
|
return null
|
|
61
51
|
}
|
|
62
|
-
|
|
52
|
+
try {
|
|
53
|
+
return decimalStringToBig(value)
|
|
54
|
+
} catch {
|
|
55
|
+
return value
|
|
56
|
+
}
|
|
63
57
|
}
|
|
64
58
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
function truncateAbs(abs: number, decimals: number): number {
|
|
72
|
-
const s = abs.toFixed(Math.min(decimals + FLOAT_GUARD_DIGITS, 100))
|
|
73
|
-
const dot = s.indexOf('.')
|
|
74
|
-
const cut = decimals === 0 ? dot : dot + 1 + decimals
|
|
75
|
-
return Number(s.slice(0, cut))
|
|
59
|
+
function decimalSeparator(locale: string | undefined): string {
|
|
60
|
+
return (
|
|
61
|
+
new Intl.NumberFormat(locale)
|
|
62
|
+
.formatToParts(1.5)
|
|
63
|
+
.find((part) => part.type === 'decimal')?.value ?? '.'
|
|
64
|
+
)
|
|
76
65
|
}
|
|
77
66
|
|
|
78
67
|
/**
|
|
79
|
-
* Reduce `
|
|
68
|
+
* Reduce `value` to `decimals` places then split into a sign and the
|
|
80
69
|
* locale-formatted absolute body. The sign is derived from the reduced value
|
|
81
70
|
* so magnitudes that collapse to zero render without a spurious `-`/`+`.
|
|
82
71
|
* `signed` emits `+` for positives; `rounding` selects half-up or truncation.
|
|
83
72
|
*/
|
|
84
73
|
function signAndBody(
|
|
85
|
-
|
|
74
|
+
value: Big,
|
|
86
75
|
decimals: number,
|
|
87
76
|
locale: string | undefined,
|
|
88
77
|
grouping: boolean,
|
|
89
78
|
rounding: RoundingMode,
|
|
90
79
|
signed: boolean
|
|
91
80
|
): { sign: '+' | '-' | ''; body: string } {
|
|
92
|
-
const reduced =
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const sign = reduced
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
81
|
+
const reduced = value.round(
|
|
82
|
+
decimals,
|
|
83
|
+
rounding === 'floor' ? Big.roundDown : Big.roundHalfUp
|
|
84
|
+
)
|
|
85
|
+
const sign = reduced.lt(0) ? '-' : signed && reduced.gt(0) ? '+' : ''
|
|
86
|
+
const [integerPart = '0', fractionPart] = reduced
|
|
87
|
+
.abs()
|
|
88
|
+
.toFixed(decimals)
|
|
89
|
+
.split('.')
|
|
90
|
+
const integerBody = grouping
|
|
91
|
+
? BigInt(integerPart).toLocaleString(locale)
|
|
92
|
+
: integerPart
|
|
93
|
+
const body =
|
|
94
|
+
fractionPart === undefined
|
|
95
|
+
? integerBody
|
|
96
|
+
: `${integerBody}${decimalSeparator(locale)}${fractionPart}`
|
|
102
97
|
return { sign, body }
|
|
103
98
|
}
|
|
104
99
|
|
|
@@ -106,14 +101,14 @@ function signAndBody(
|
|
|
106
101
|
* Auto-detect a sensible number of decimal places from a value's magnitude:
|
|
107
102
|
* `>=1` → 2, `>=0.1` → 4, `>=0.01` → 5, otherwise 6.
|
|
108
103
|
*/
|
|
109
|
-
function autoDecimals(abs:
|
|
110
|
-
if (abs
|
|
104
|
+
function autoDecimals(abs: Big): number {
|
|
105
|
+
if (abs.gte(1)) {
|
|
111
106
|
return 2
|
|
112
107
|
}
|
|
113
|
-
if (abs
|
|
108
|
+
if (abs.gte('0.1')) {
|
|
114
109
|
return 4
|
|
115
110
|
}
|
|
116
|
-
if (abs
|
|
111
|
+
if (abs.gte('0.01')) {
|
|
117
112
|
return 5
|
|
118
113
|
}
|
|
119
114
|
return 6
|
|
@@ -141,10 +136,13 @@ export function formatNumber(
|
|
|
141
136
|
locale,
|
|
142
137
|
rounding = 'halfUp',
|
|
143
138
|
} = options
|
|
144
|
-
const n =
|
|
139
|
+
const n = toBig(value)
|
|
145
140
|
if (n === null) {
|
|
146
141
|
return placeholder
|
|
147
142
|
}
|
|
143
|
+
if (typeof n === 'string') {
|
|
144
|
+
return n
|
|
145
|
+
}
|
|
148
146
|
const { sign, body } = signAndBody(n, decimals, locale, true, rounding, false)
|
|
149
147
|
return `${sign}${body}`
|
|
150
148
|
}
|
|
@@ -167,10 +165,13 @@ export function formatUsd(
|
|
|
167
165
|
locale,
|
|
168
166
|
rounding = 'halfUp',
|
|
169
167
|
} = options
|
|
170
|
-
const n =
|
|
168
|
+
const n = toBig(value)
|
|
171
169
|
if (n === null) {
|
|
172
170
|
return placeholder
|
|
173
171
|
}
|
|
172
|
+
if (typeof n === 'string') {
|
|
173
|
+
return n
|
|
174
|
+
}
|
|
174
175
|
const { sign, body } = signAndBody(n, decimals, locale, true, rounding, false)
|
|
175
176
|
return `${sign}$${body}`
|
|
176
177
|
}
|
|
@@ -194,10 +195,13 @@ export function formatSignedUsd(
|
|
|
194
195
|
locale,
|
|
195
196
|
rounding = 'halfUp',
|
|
196
197
|
} = options
|
|
197
|
-
const n =
|
|
198
|
+
const n = toBig(value)
|
|
198
199
|
if (n === null) {
|
|
199
200
|
return placeholder
|
|
200
201
|
}
|
|
202
|
+
if (typeof n === 'string') {
|
|
203
|
+
return n
|
|
204
|
+
}
|
|
201
205
|
const { sign, body } = signAndBody(n, decimals, locale, true, rounding, true)
|
|
202
206
|
return `${sign}$${body}`
|
|
203
207
|
}
|
|
@@ -221,10 +225,13 @@ export function formatSignedPercent(
|
|
|
221
225
|
locale,
|
|
222
226
|
rounding = 'halfUp',
|
|
223
227
|
} = options
|
|
224
|
-
const n =
|
|
228
|
+
const n = toBig(value)
|
|
225
229
|
if (n === null) {
|
|
226
230
|
return placeholder
|
|
227
231
|
}
|
|
232
|
+
if (typeof n === 'string') {
|
|
233
|
+
return n
|
|
234
|
+
}
|
|
228
235
|
const { sign, body } = signAndBody(n, decimals, locale, false, rounding, true)
|
|
229
236
|
return `${sign}${body}%`
|
|
230
237
|
}
|
|
@@ -248,17 +255,20 @@ export function formatPrice(
|
|
|
248
255
|
locale,
|
|
249
256
|
rounding = 'halfUp',
|
|
250
257
|
} = options
|
|
251
|
-
const n =
|
|
258
|
+
const n = toBig(value)
|
|
252
259
|
if (n === null) {
|
|
253
260
|
return placeholder
|
|
254
261
|
}
|
|
255
|
-
|
|
262
|
+
if (typeof n === 'string') {
|
|
263
|
+
return n
|
|
264
|
+
}
|
|
265
|
+
const abs = n.abs()
|
|
256
266
|
const decimals = options.decimals ?? autoDecimals(abs)
|
|
257
267
|
const { sign, body } = signAndBody(
|
|
258
268
|
n,
|
|
259
269
|
decimals,
|
|
260
270
|
locale,
|
|
261
|
-
abs
|
|
271
|
+
abs.gte(1000),
|
|
262
272
|
rounding,
|
|
263
273
|
false
|
|
264
274
|
)
|
|
@@ -279,20 +289,23 @@ export function formatCompactUsd(
|
|
|
279
289
|
options: FormatOptions = {}
|
|
280
290
|
): string {
|
|
281
291
|
const { decimals = 2, placeholder = DEFAULT_PLACEHOLDER } = options
|
|
282
|
-
const n =
|
|
292
|
+
const n = toBig(value)
|
|
283
293
|
if (n === null) {
|
|
284
294
|
return placeholder
|
|
285
295
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
296
|
+
if (typeof n === 'string') {
|
|
297
|
+
return n
|
|
298
|
+
}
|
|
299
|
+
const sign = n.lt(0) ? '-' : ''
|
|
300
|
+
const abs = n.abs()
|
|
301
|
+
if (abs.gte(1_000_000_000)) {
|
|
302
|
+
return `${sign}$${abs.div(1_000_000_000).toFixed(decimals)}B`
|
|
290
303
|
}
|
|
291
|
-
if (abs
|
|
292
|
-
return `${sign}$${(
|
|
304
|
+
if (abs.gte(1_000_000)) {
|
|
305
|
+
return `${sign}$${abs.div(1_000_000).toFixed(decimals)}M`
|
|
293
306
|
}
|
|
294
|
-
if (abs
|
|
295
|
-
return `${sign}$${(
|
|
307
|
+
if (abs.gte(1_000)) {
|
|
308
|
+
return `${sign}$${abs.div(1_000).toFixed(decimals)}K`
|
|
296
309
|
}
|
|
297
310
|
return `${sign}$${abs.toFixed(decimals)}`
|
|
298
311
|
}
|