@lifi/perps-sdk 23.1.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 +6 -2
- package/dist/cjs/decimal/compare.d.ts.map +1 -1
- package/dist/cjs/decimal/compare.js +15 -2
- package/dist/cjs/decimal/compare.js.map +1 -1
- 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 -52
- package/dist/cjs/decimal/format.js.map +1 -1
- package/dist/cjs/decimal/parse.d.ts +4 -4
- package/dist/cjs/decimal/parse.d.ts.map +1 -1
- package/dist/cjs/decimal/parse.js +42 -25
- package/dist/cjs/decimal/parse.js.map +1 -1
- package/dist/cjs/index.d.ts +13 -11
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +64 -8
- 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 +39 -5
- package/dist/esm/decimal/compare.d.ts.map +1 -1
- package/dist/esm/decimal/compare.js +45 -5
- package/dist/esm/decimal/compare.js.map +1 -1
- 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 -70
- package/dist/esm/decimal/format.js.map +1 -1
- package/dist/esm/decimal/parse.d.ts +22 -13
- package/dist/esm/decimal/parse.d.ts.map +1 -1
- package/dist/esm/decimal/parse.js +56 -46
- package/dist/esm/decimal/parse.js.map +1 -1
- package/dist/esm/index.d.ts +13 -11
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +9 -7
- 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 +39 -5
- package/dist/types/decimal/compare.d.ts.map +1 -1
- 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 +22 -13
- package/dist/types/decimal/parse.d.ts.map +1 -1
- package/dist/types/index.d.ts +13 -11
- 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 +60 -10
- package/src/decimal/convert.ts +123 -44
- package/src/decimal/decimalStringToBig.ts +47 -0
- package/src/decimal/format.ts +81 -72
- package/src/decimal/parse.ts +71 -61
- package/src/index.ts +73 -10
- 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 -12
- package/dist/cjs/decimal/requireDecimal.js.map +0 -1
- package/dist/esm/decimal/requireDecimal.d.ts +0 -5
- package/dist/esm/decimal/requireDecimal.d.ts.map +0 -1
- package/dist/esm/decimal/requireDecimal.js +0 -7
- package/dist/esm/decimal/requireDecimal.js.map +0 -1
- package/dist/types/decimal/requireDecimal.d.ts +0 -5
- package/dist/types/decimal/requireDecimal.d.ts.map +0 -1
- package/src/decimal/requireDecimal.ts +0 -8
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,76 +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
|
-
// `toFixed` emits exponent notation at >= 1e21; every double that large is an integer.
|
|
73
|
-
if (Number.isInteger(abs)) {
|
|
74
|
-
return abs
|
|
75
|
-
}
|
|
76
|
-
const s = abs.toFixed(Math.min(decimals + FLOAT_GUARD_DIGITS, 100))
|
|
77
|
-
const dot = s.indexOf('.')
|
|
78
|
-
const cut = decimals === 0 ? dot : dot + 1 + decimals
|
|
79
|
-
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
|
+
)
|
|
80
65
|
}
|
|
81
66
|
|
|
82
67
|
/**
|
|
83
|
-
* Reduce `
|
|
68
|
+
* Reduce `value` to `decimals` places then split into a sign and the
|
|
84
69
|
* locale-formatted absolute body. The sign is derived from the reduced value
|
|
85
70
|
* so magnitudes that collapse to zero render without a spurious `-`/`+`.
|
|
86
71
|
* `signed` emits `+` for positives; `rounding` selects half-up or truncation.
|
|
87
72
|
*/
|
|
88
73
|
function signAndBody(
|
|
89
|
-
|
|
74
|
+
value: Big,
|
|
90
75
|
decimals: number,
|
|
91
76
|
locale: string | undefined,
|
|
92
77
|
grouping: boolean,
|
|
93
78
|
rounding: RoundingMode,
|
|
94
79
|
signed: boolean
|
|
95
80
|
): { sign: '+' | '-' | ''; body: string } {
|
|
96
|
-
const reduced =
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const sign = reduced
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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}`
|
|
106
97
|
return { sign, body }
|
|
107
98
|
}
|
|
108
99
|
|
|
@@ -110,14 +101,14 @@ function signAndBody(
|
|
|
110
101
|
* Auto-detect a sensible number of decimal places from a value's magnitude:
|
|
111
102
|
* `>=1` → 2, `>=0.1` → 4, `>=0.01` → 5, otherwise 6.
|
|
112
103
|
*/
|
|
113
|
-
function autoDecimals(abs:
|
|
114
|
-
if (abs
|
|
104
|
+
function autoDecimals(abs: Big): number {
|
|
105
|
+
if (abs.gte(1)) {
|
|
115
106
|
return 2
|
|
116
107
|
}
|
|
117
|
-
if (abs
|
|
108
|
+
if (abs.gte('0.1')) {
|
|
118
109
|
return 4
|
|
119
110
|
}
|
|
120
|
-
if (abs
|
|
111
|
+
if (abs.gte('0.01')) {
|
|
121
112
|
return 5
|
|
122
113
|
}
|
|
123
114
|
return 6
|
|
@@ -145,10 +136,13 @@ export function formatNumber(
|
|
|
145
136
|
locale,
|
|
146
137
|
rounding = 'halfUp',
|
|
147
138
|
} = options
|
|
148
|
-
const n =
|
|
139
|
+
const n = toBig(value)
|
|
149
140
|
if (n === null) {
|
|
150
141
|
return placeholder
|
|
151
142
|
}
|
|
143
|
+
if (typeof n === 'string') {
|
|
144
|
+
return n
|
|
145
|
+
}
|
|
152
146
|
const { sign, body } = signAndBody(n, decimals, locale, true, rounding, false)
|
|
153
147
|
return `${sign}${body}`
|
|
154
148
|
}
|
|
@@ -171,10 +165,13 @@ export function formatUsd(
|
|
|
171
165
|
locale,
|
|
172
166
|
rounding = 'halfUp',
|
|
173
167
|
} = options
|
|
174
|
-
const n =
|
|
168
|
+
const n = toBig(value)
|
|
175
169
|
if (n === null) {
|
|
176
170
|
return placeholder
|
|
177
171
|
}
|
|
172
|
+
if (typeof n === 'string') {
|
|
173
|
+
return n
|
|
174
|
+
}
|
|
178
175
|
const { sign, body } = signAndBody(n, decimals, locale, true, rounding, false)
|
|
179
176
|
return `${sign}$${body}`
|
|
180
177
|
}
|
|
@@ -198,10 +195,13 @@ export function formatSignedUsd(
|
|
|
198
195
|
locale,
|
|
199
196
|
rounding = 'halfUp',
|
|
200
197
|
} = options
|
|
201
|
-
const n =
|
|
198
|
+
const n = toBig(value)
|
|
202
199
|
if (n === null) {
|
|
203
200
|
return placeholder
|
|
204
201
|
}
|
|
202
|
+
if (typeof n === 'string') {
|
|
203
|
+
return n
|
|
204
|
+
}
|
|
205
205
|
const { sign, body } = signAndBody(n, decimals, locale, true, rounding, true)
|
|
206
206
|
return `${sign}$${body}`
|
|
207
207
|
}
|
|
@@ -225,10 +225,13 @@ export function formatSignedPercent(
|
|
|
225
225
|
locale,
|
|
226
226
|
rounding = 'halfUp',
|
|
227
227
|
} = options
|
|
228
|
-
const n =
|
|
228
|
+
const n = toBig(value)
|
|
229
229
|
if (n === null) {
|
|
230
230
|
return placeholder
|
|
231
231
|
}
|
|
232
|
+
if (typeof n === 'string') {
|
|
233
|
+
return n
|
|
234
|
+
}
|
|
232
235
|
const { sign, body } = signAndBody(n, decimals, locale, false, rounding, true)
|
|
233
236
|
return `${sign}${body}%`
|
|
234
237
|
}
|
|
@@ -252,17 +255,20 @@ export function formatPrice(
|
|
|
252
255
|
locale,
|
|
253
256
|
rounding = 'halfUp',
|
|
254
257
|
} = options
|
|
255
|
-
const n =
|
|
258
|
+
const n = toBig(value)
|
|
256
259
|
if (n === null) {
|
|
257
260
|
return placeholder
|
|
258
261
|
}
|
|
259
|
-
|
|
262
|
+
if (typeof n === 'string') {
|
|
263
|
+
return n
|
|
264
|
+
}
|
|
265
|
+
const abs = n.abs()
|
|
260
266
|
const decimals = options.decimals ?? autoDecimals(abs)
|
|
261
267
|
const { sign, body } = signAndBody(
|
|
262
268
|
n,
|
|
263
269
|
decimals,
|
|
264
270
|
locale,
|
|
265
|
-
abs
|
|
271
|
+
abs.gte(1000),
|
|
266
272
|
rounding,
|
|
267
273
|
false
|
|
268
274
|
)
|
|
@@ -283,20 +289,23 @@ export function formatCompactUsd(
|
|
|
283
289
|
options: FormatOptions = {}
|
|
284
290
|
): string {
|
|
285
291
|
const { decimals = 2, placeholder = DEFAULT_PLACEHOLDER } = options
|
|
286
|
-
const n =
|
|
292
|
+
const n = toBig(value)
|
|
287
293
|
if (n === null) {
|
|
288
294
|
return placeholder
|
|
289
295
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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`
|
|
294
303
|
}
|
|
295
|
-
if (abs
|
|
296
|
-
return `${sign}$${(
|
|
304
|
+
if (abs.gte(1_000_000)) {
|
|
305
|
+
return `${sign}$${abs.div(1_000_000).toFixed(decimals)}M`
|
|
297
306
|
}
|
|
298
|
-
if (abs
|
|
299
|
-
return `${sign}$${(
|
|
307
|
+
if (abs.gte(1_000)) {
|
|
308
|
+
return `${sign}$${abs.div(1_000).toFixed(decimals)}K`
|
|
300
309
|
}
|
|
301
310
|
return `${sign}$${abs.toFixed(decimals)}`
|
|
302
311
|
}
|