@lifi/perps-sdk 19.0.0 → 20.1.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 +35 -5
- package/dist/cjs/client/PerpsClient.d.ts +1 -1
- package/dist/cjs/client/PerpsClient.d.ts.map +1 -1
- package/dist/cjs/client/PerpsClient.js +4 -1
- package/dist/cjs/client/PerpsClient.js.map +1 -1
- package/dist/cjs/client/createPerpsClient.d.ts +1 -6
- package/dist/cjs/client/createPerpsClient.d.ts.map +1 -1
- package/dist/cjs/client/createPerpsClient.js +1 -14
- package/dist/cjs/client/createPerpsClient.js.map +1 -1
- package/dist/cjs/index.d.ts +6 -3
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +15 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/registry/assetRegistry.d.ts +2 -1
- package/dist/cjs/registry/assetRegistry.d.ts.map +1 -1
- package/dist/cjs/registry/assetRegistry.js +4 -2
- package/dist/cjs/registry/assetRegistry.js.map +1 -1
- package/dist/cjs/registry/marketRegistry.d.ts +2 -1
- package/dist/cjs/registry/marketRegistry.d.ts.map +1 -1
- package/dist/cjs/registry/marketRegistry.js +4 -2
- package/dist/cjs/registry/marketRegistry.js.map +1 -1
- package/dist/cjs/registry/referenceDataRegistry.d.ts +5 -1
- package/dist/cjs/registry/referenceDataRegistry.d.ts.map +1 -1
- package/dist/cjs/registry/referenceDataRegistry.js +65 -5
- package/dist/cjs/registry/referenceDataRegistry.js.map +1 -1
- package/dist/cjs/services/getOhlcv.d.ts.map +1 -1
- package/dist/cjs/services/getOhlcv.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 +6 -2
- package/dist/cjs/transport/request.js.map +1 -1
- package/dist/cjs/types/api.d.ts +3 -4
- package/dist/cjs/types/api.d.ts.map +1 -1
- package/dist/cjs/types/config.d.ts +0 -9
- package/dist/cjs/types/config.d.ts.map +1 -1
- package/dist/cjs/types/provider.d.ts +1 -1
- package/dist/cjs/types/provider.d.ts.map +1 -1
- package/dist/cjs/types/withdrawal.d.ts +4 -2
- package/dist/cjs/types/withdrawal.d.ts.map +1 -1
- package/dist/cjs/utils/calculations.d.ts.map +1 -1
- package/dist/cjs/utils/calculations.js +107 -38
- package/dist/cjs/utils/calculations.js.map +1 -1
- package/dist/cjs/utils/decimal.d.ts +5 -0
- package/dist/cjs/utils/decimal.d.ts.map +1 -0
- package/dist/cjs/utils/decimal.js +17 -0
- package/dist/cjs/utils/decimal.js.map +1 -0
- package/dist/cjs/utils/math.d.ts +4 -0
- package/dist/cjs/utils/math.d.ts.map +1 -0
- package/dist/cjs/utils/math.js +8 -0
- package/dist/cjs/utils/math.js.map +1 -0
- package/dist/cjs/utils/orderEntry.d.ts +6 -0
- package/dist/cjs/utils/orderEntry.d.ts.map +1 -0
- package/dist/cjs/utils/orderEntry.js +20 -0
- package/dist/cjs/utils/orderEntry.js.map +1 -0
- package/dist/cjs/utils/parse.d.ts +1 -0
- package/dist/cjs/utils/parse.d.ts.map +1 -1
- package/dist/cjs/utils/parse.js +15 -0
- package/dist/cjs/utils/parse.js.map +1 -1
- package/dist/cjs/utils/positionMath.d.ts +5 -0
- package/dist/cjs/utils/positionMath.d.ts.map +1 -1
- package/dist/cjs/utils/positionMath.js +53 -6
- package/dist/cjs/utils/positionMath.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 +0 -1
- package/dist/cjs/websocket/PerpsWsClient.d.ts.map +1 -1
- package/dist/cjs/websocket/PerpsWsClient.js +0 -7
- package/dist/cjs/websocket/PerpsWsClient.js.map +1 -1
- package/dist/cjs/websocket/WsProviderBase.d.ts.map +1 -1
- package/dist/cjs/websocket/WsProviderBase.js +11 -3
- package/dist/cjs/websocket/WsProviderBase.js.map +1 -1
- package/dist/esm/client/PerpsClient.d.ts +8 -7
- package/dist/esm/client/PerpsClient.d.ts.map +1 -1
- package/dist/esm/client/PerpsClient.js +11 -7
- package/dist/esm/client/PerpsClient.js.map +1 -1
- package/dist/esm/client/createPerpsClient.d.ts +1 -12
- package/dist/esm/client/createPerpsClient.d.ts.map +1 -1
- package/dist/esm/client/createPerpsClient.js +1 -19
- package/dist/esm/client/createPerpsClient.js.map +1 -1
- package/dist/esm/client/requireProvider.d.ts +1 -1
- package/dist/esm/client/requireProvider.js +1 -1
- package/dist/esm/index.d.ts +6 -3
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +5 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/registry/assetRegistry.d.ts +2 -1
- package/dist/esm/registry/assetRegistry.d.ts.map +1 -1
- package/dist/esm/registry/assetRegistry.js +4 -2
- package/dist/esm/registry/assetRegistry.js.map +1 -1
- package/dist/esm/registry/marketRegistry.d.ts +2 -1
- package/dist/esm/registry/marketRegistry.d.ts.map +1 -1
- package/dist/esm/registry/marketRegistry.js +4 -2
- package/dist/esm/registry/marketRegistry.js.map +1 -1
- package/dist/esm/registry/referenceDataRegistry.d.ts +6 -6
- package/dist/esm/registry/referenceDataRegistry.d.ts.map +1 -1
- package/dist/esm/registry/referenceDataRegistry.js +68 -10
- package/dist/esm/registry/referenceDataRegistry.js.map +1 -1
- package/dist/esm/services/getOhlcv.d.ts +1 -0
- package/dist/esm/services/getOhlcv.d.ts.map +1 -1
- package/dist/esm/services/getOhlcv.js.map +1 -1
- package/dist/esm/transport/request.d.ts +2 -0
- package/dist/esm/transport/request.d.ts.map +1 -1
- package/dist/esm/transport/request.js +6 -2
- package/dist/esm/transport/request.js.map +1 -1
- package/dist/esm/types/api.d.ts +8 -22
- package/dist/esm/types/api.d.ts.map +1 -1
- package/dist/esm/types/config.d.ts +0 -27
- package/dist/esm/types/config.d.ts.map +1 -1
- package/dist/esm/types/provider.d.ts +7 -6
- package/dist/esm/types/provider.d.ts.map +1 -1
- package/dist/esm/types/withdrawal.d.ts +24 -15
- package/dist/esm/types/withdrawal.d.ts.map +1 -1
- package/dist/esm/utils/calculations.d.ts +3 -2
- package/dist/esm/utils/calculations.d.ts.map +1 -1
- package/dist/esm/utils/calculations.js +109 -39
- package/dist/esm/utils/calculations.js.map +1 -1
- package/dist/esm/utils/decimal.d.ts +14 -0
- package/dist/esm/utils/decimal.d.ts.map +1 -0
- package/dist/esm/utils/decimal.js +19 -0
- package/dist/esm/utils/decimal.js.map +1 -0
- package/dist/esm/utils/math.d.ts +6 -0
- package/dist/esm/utils/math.d.ts.map +1 -0
- package/dist/esm/utils/math.js +5 -0
- package/dist/esm/utils/math.js.map +1 -0
- package/dist/esm/utils/orderEntry.d.ts +26 -0
- package/dist/esm/utils/orderEntry.d.ts.map +1 -0
- package/dist/esm/utils/orderEntry.js +34 -0
- package/dist/esm/utils/orderEntry.js.map +1 -0
- package/dist/esm/utils/parse.d.ts +7 -0
- package/dist/esm/utils/parse.d.ts.map +1 -1
- package/dist/esm/utils/parse.js +20 -0
- package/dist/esm/utils/parse.js.map +1 -1
- package/dist/esm/utils/positionMath.d.ts +13 -0
- package/dist/esm/utils/positionMath.d.ts.map +1 -1
- package/dist/esm/utils/positionMath.js +60 -6
- package/dist/esm/utils/positionMath.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 +0 -7
- package/dist/esm/websocket/PerpsWsClient.d.ts.map +1 -1
- package/dist/esm/websocket/PerpsWsClient.js +0 -7
- package/dist/esm/websocket/PerpsWsClient.js.map +1 -1
- package/dist/esm/websocket/WsProviderBase.d.ts.map +1 -1
- package/dist/esm/websocket/WsProviderBase.js +11 -3
- package/dist/esm/websocket/WsProviderBase.js.map +1 -1
- package/dist/types/client/PerpsClient.d.ts +8 -7
- package/dist/types/client/PerpsClient.d.ts.map +1 -1
- package/dist/types/client/createPerpsClient.d.ts +1 -12
- package/dist/types/client/createPerpsClient.d.ts.map +1 -1
- package/dist/types/client/requireProvider.d.ts +1 -1
- package/dist/types/index.d.ts +6 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/registry/assetRegistry.d.ts +2 -1
- package/dist/types/registry/assetRegistry.d.ts.map +1 -1
- package/dist/types/registry/marketRegistry.d.ts +2 -1
- package/dist/types/registry/marketRegistry.d.ts.map +1 -1
- package/dist/types/registry/referenceDataRegistry.d.ts +6 -6
- package/dist/types/registry/referenceDataRegistry.d.ts.map +1 -1
- package/dist/types/services/getOhlcv.d.ts +1 -0
- package/dist/types/services/getOhlcv.d.ts.map +1 -1
- package/dist/types/transport/request.d.ts +2 -0
- package/dist/types/transport/request.d.ts.map +1 -1
- package/dist/types/types/api.d.ts +8 -22
- package/dist/types/types/api.d.ts.map +1 -1
- package/dist/types/types/config.d.ts +0 -27
- package/dist/types/types/config.d.ts.map +1 -1
- package/dist/types/types/provider.d.ts +7 -6
- package/dist/types/types/provider.d.ts.map +1 -1
- package/dist/types/types/withdrawal.d.ts +24 -15
- package/dist/types/types/withdrawal.d.ts.map +1 -1
- package/dist/types/utils/calculations.d.ts +3 -2
- package/dist/types/utils/calculations.d.ts.map +1 -1
- package/dist/types/utils/decimal.d.ts +14 -0
- package/dist/types/utils/decimal.d.ts.map +1 -0
- package/dist/types/utils/math.d.ts +6 -0
- package/dist/types/utils/math.d.ts.map +1 -0
- package/dist/types/utils/orderEntry.d.ts +26 -0
- package/dist/types/utils/orderEntry.d.ts.map +1 -0
- package/dist/types/utils/parse.d.ts +7 -0
- package/dist/types/utils/parse.d.ts.map +1 -1
- package/dist/types/utils/positionMath.d.ts +13 -0
- package/dist/types/utils/positionMath.d.ts.map +1 -1
- package/dist/types/version.d.ts +1 -1
- package/dist/types/websocket/PerpsWsClient.d.ts +0 -7
- package/dist/types/websocket/PerpsWsClient.d.ts.map +1 -1
- package/dist/types/websocket/WsProviderBase.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/client/PerpsClient.ts +12 -8
- package/src/client/createPerpsClient.ts +4 -32
- package/src/client/requireProvider.ts +1 -1
- package/src/index.ts +10 -4
- package/src/registry/assetRegistry.ts +7 -5
- package/src/registry/marketRegistry.ts +7 -5
- package/src/registry/referenceDataRegistry.ts +89 -11
- package/src/services/getOhlcv.ts +1 -0
- package/src/transport/request.ts +12 -2
- package/src/types/api.ts +8 -26
- package/src/types/config.ts +0 -30
- package/src/types/provider.ts +8 -7
- package/src/types/withdrawal.ts +24 -15
- package/src/utils/calculations.ts +114 -43
- package/src/utils/decimal.ts +21 -0
- package/src/utils/math.ts +7 -0
- package/src/utils/orderEntry.ts +38 -0
- package/src/utils/parse.ts +25 -0
- package/src/utils/positionMath.ts +66 -6
- package/src/version.ts +1 -1
- package/src/websocket/PerpsWsClient.ts +0 -19
- package/src/websocket/WsProviderBase.ts +11 -3
|
@@ -1,14 +1,64 @@
|
|
|
1
1
|
import { PerpsErrorCode } from '@lifi/perps-types'
|
|
2
2
|
import { PerpsError } from '../errors/PerpsError.js'
|
|
3
|
+
import type { RequestOptions } from '../transport/request.js'
|
|
3
4
|
import type { PerpsSDKClient } from '../types/provider.js'
|
|
4
5
|
import { createWarnOnce } from '../utils/warnOnce.js'
|
|
5
6
|
|
|
7
|
+
function freshUntil(
|
|
8
|
+
headers: Headers,
|
|
9
|
+
requestStartedAt: number,
|
|
10
|
+
responseReceivedAt: number
|
|
11
|
+
): number {
|
|
12
|
+
const directives = (headers.get('cache-control') ?? '')
|
|
13
|
+
.toLowerCase()
|
|
14
|
+
.split(',')
|
|
15
|
+
.map((directive) => directive.trim())
|
|
16
|
+
if (
|
|
17
|
+
directives.some((directive) =>
|
|
18
|
+
/^(no-store|no-cache)(?:\s*=|$)/.test(directive)
|
|
19
|
+
) ||
|
|
20
|
+
headers
|
|
21
|
+
.get('vary')
|
|
22
|
+
?.split(',')
|
|
23
|
+
.some((name) => name.trim() === '*')
|
|
24
|
+
) {
|
|
25
|
+
return 0
|
|
26
|
+
}
|
|
27
|
+
const maxAges = directives.filter((directive) =>
|
|
28
|
+
/^max-age(?:\s*=|$)/.test(directive)
|
|
29
|
+
)
|
|
30
|
+
const maxAge =
|
|
31
|
+
maxAges.length === 1
|
|
32
|
+
? /^max-age\s*=\s*(?:"(\d+)"|(\d+))$/.exec(maxAges[0])
|
|
33
|
+
: null
|
|
34
|
+
if (!maxAge) {
|
|
35
|
+
return 0
|
|
36
|
+
}
|
|
37
|
+
const age = headers.get('age') ?? '0'
|
|
38
|
+
const date = headers.get('date')
|
|
39
|
+
const dateTime = date === null ? responseReceivedAt : Date.parse(date)
|
|
40
|
+
if (!/^\d+$/.test(age) || !Number.isFinite(dateTime)) {
|
|
41
|
+
return 0
|
|
42
|
+
}
|
|
43
|
+
const lifetime = Number(maxAge[1] ?? maxAge[2]) * 1000
|
|
44
|
+
const currentAge = Math.max(
|
|
45
|
+
responseReceivedAt - dateTime,
|
|
46
|
+
Number(age) * 1000 + Math.max(0, responseReceivedAt - requestStartedAt)
|
|
47
|
+
)
|
|
48
|
+
return Number.isFinite(lifetime)
|
|
49
|
+
? responseReceivedAt + lifetime - currentAge
|
|
50
|
+
: 0
|
|
51
|
+
}
|
|
52
|
+
|
|
6
53
|
/** Atomic provider reference indexes; HTTP cache headers govern freshness. @internal */
|
|
7
54
|
export abstract class ReferenceDataRegistry<T> {
|
|
8
55
|
private index = new Map<string, T>()
|
|
9
56
|
private secondaryIndexes = new Map<string, Map<string, T>>()
|
|
10
57
|
private current: readonly T[] = []
|
|
11
58
|
private inflight: Promise<readonly T[]> | undefined
|
|
59
|
+
private inflightIdentity: string | undefined
|
|
60
|
+
private cacheIdentity: string | undefined
|
|
61
|
+
private freshUntil = 0
|
|
12
62
|
private readonly warnOnce = createWarnOnce()
|
|
13
63
|
|
|
14
64
|
protected constructor(
|
|
@@ -21,21 +71,37 @@ export abstract class ReferenceDataRegistry<T> {
|
|
|
21
71
|
) {}
|
|
22
72
|
|
|
23
73
|
/** Fetch the provider's full list through the HTTP layer. */
|
|
24
|
-
protected abstract fetchItems(
|
|
74
|
+
protected abstract fetchItems(
|
|
75
|
+
onResponse: RequestOptions['onResponse']
|
|
76
|
+
): Promise<T[]>
|
|
25
77
|
|
|
26
78
|
/** The item's primary key — what {@link get} is keyed by. */
|
|
27
79
|
protected abstract keyOf(item: T): string
|
|
28
80
|
|
|
29
|
-
/**
|
|
30
|
-
* Fetch the list through the HTTP cache and rebuild the index. Concurrent
|
|
31
|
-
* callers share one in-flight fetch; a settled fetch is never reused, so
|
|
32
|
-
* HTTP `cache-control` alone governs freshness.
|
|
33
|
-
*/
|
|
81
|
+
/** Sync reference indexes, reusing only explicitly fresh Node responses. */
|
|
34
82
|
sync(): Promise<readonly T[]> {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
83
|
+
const { config } = this.client
|
|
84
|
+
// Hooks can change credentials or cache policy on every call.
|
|
85
|
+
if (config.requestInterceptor || config.fetch) {
|
|
86
|
+
return this.load()
|
|
87
|
+
}
|
|
88
|
+
const identity = JSON.stringify([
|
|
89
|
+
config.apiUrl,
|
|
90
|
+
config.apiKey,
|
|
91
|
+
config.integrator,
|
|
92
|
+
])
|
|
93
|
+
if (identity === this.cacheIdentity && Date.now() < this.freshUntil) {
|
|
94
|
+
return Promise.resolve(this.current)
|
|
95
|
+
}
|
|
96
|
+
if (!this.inflight || this.inflightIdentity !== identity) {
|
|
97
|
+
const inflight = this.load(identity).finally(() => {
|
|
98
|
+
if (this.inflight === inflight) {
|
|
99
|
+
this.inflight = undefined
|
|
100
|
+
this.inflightIdentity = undefined
|
|
101
|
+
}
|
|
38
102
|
})
|
|
103
|
+
this.inflight = inflight
|
|
104
|
+
this.inflightIdentity = identity
|
|
39
105
|
}
|
|
40
106
|
return this.inflight
|
|
41
107
|
}
|
|
@@ -59,8 +125,18 @@ export abstract class ReferenceDataRegistry<T> {
|
|
|
59
125
|
return this.secondaryIndexes.get(key)?.get(id)
|
|
60
126
|
}
|
|
61
127
|
|
|
62
|
-
private async load(): Promise<readonly T[]> {
|
|
63
|
-
|
|
128
|
+
private async load(identity?: string): Promise<readonly T[]> {
|
|
129
|
+
let expiresAt = 0
|
|
130
|
+
const items = await this.fetchItems((headers, startedAt, receivedAt) => {
|
|
131
|
+
// Browsers retain their native HTTP cache and its resident response age.
|
|
132
|
+
if (
|
|
133
|
+
identity !== undefined &&
|
|
134
|
+
typeof process !== 'undefined' &&
|
|
135
|
+
process.versions?.node
|
|
136
|
+
) {
|
|
137
|
+
expiresAt = freshUntil(headers, startedAt, receivedAt)
|
|
138
|
+
}
|
|
139
|
+
})
|
|
64
140
|
const index = new Map<string, T>()
|
|
65
141
|
for (const item of items) {
|
|
66
142
|
const key = this.keyOf(item)
|
|
@@ -93,6 +169,8 @@ export abstract class ReferenceDataRegistry<T> {
|
|
|
93
169
|
this.index = index
|
|
94
170
|
this.secondaryIndexes = secondaryIndexes
|
|
95
171
|
this.current = items
|
|
172
|
+
this.cacheIdentity = identity
|
|
173
|
+
this.freshUntil = expiresAt
|
|
96
174
|
return items
|
|
97
175
|
}
|
|
98
176
|
}
|
package/src/services/getOhlcv.ts
CHANGED
|
@@ -12,6 +12,7 @@ export interface GetOhlcvParams {
|
|
|
12
12
|
provider: string
|
|
13
13
|
/** Opaque provider `Market.id`, not a display symbol. */
|
|
14
14
|
marketId: string
|
|
15
|
+
/** Pick from the provider's `Provider.supportedIntervals`; support varies per provider. */
|
|
15
16
|
interval: OhlcvInterval
|
|
16
17
|
/** Unix timestamp in milliseconds for the start bound. */
|
|
17
18
|
startTime?: number
|
package/src/transport/request.ts
CHANGED
|
@@ -20,6 +20,12 @@ import {
|
|
|
20
20
|
export interface RequestOptions extends RequestInit {
|
|
21
21
|
/** Per-call retry override. Falls back to the resolved client-level policy. */
|
|
22
22
|
retry?: RetryPolicy | false
|
|
23
|
+
/** Successful response metadata, delivered only after JSON decoding. */
|
|
24
|
+
onResponse?: (
|
|
25
|
+
headers: Headers,
|
|
26
|
+
requestStartedAt: number,
|
|
27
|
+
responseReceivedAt: number
|
|
28
|
+
) => void
|
|
23
29
|
}
|
|
24
30
|
|
|
25
31
|
/**
|
|
@@ -34,7 +40,7 @@ export async function request<T>(
|
|
|
34
40
|
options: RequestOptions = {},
|
|
35
41
|
sdkOptions?: SDKRequestOptions
|
|
36
42
|
): Promise<T> {
|
|
37
|
-
const { retry, ...fetchInit } = options
|
|
43
|
+
const { retry, onResponse, ...fetchInit } = options
|
|
38
44
|
const policy =
|
|
39
45
|
retry !== undefined
|
|
40
46
|
? resolveRetryPolicy(LIFI_RETRY_DEFAULTS, retry, LIFI_REQUEST_KEY)
|
|
@@ -64,11 +70,13 @@ export async function request<T>(
|
|
|
64
70
|
}
|
|
65
71
|
|
|
66
72
|
try {
|
|
73
|
+
const requestStartedAt = onResponse ? Date.now() : 0
|
|
67
74
|
const response = await fetchWithRetry(url, finalOptions, {
|
|
68
75
|
policy,
|
|
69
76
|
fetchImpl: config.fetch,
|
|
70
77
|
signal: sdkOptions?.signal ?? finalOptions.signal ?? undefined,
|
|
71
78
|
})
|
|
79
|
+
const responseReceivedAt = onResponse ? Date.now() : 0
|
|
72
80
|
|
|
73
81
|
if (!response.ok) {
|
|
74
82
|
let body: PerpsErrorBody | undefined
|
|
@@ -94,7 +102,9 @@ export async function request<T>(
|
|
|
94
102
|
throw error
|
|
95
103
|
}
|
|
96
104
|
|
|
97
|
-
|
|
105
|
+
const data = (await response.json()) as T
|
|
106
|
+
onResponse?.(response.headers, requestStartedAt, responseReceivedAt)
|
|
107
|
+
return data
|
|
98
108
|
} catch (error) {
|
|
99
109
|
if (error instanceof PerpsError) {
|
|
100
110
|
throw error
|
package/src/types/api.ts
CHANGED
|
@@ -24,11 +24,7 @@ import type {
|
|
|
24
24
|
} from '@lifi/perps-types'
|
|
25
25
|
import type { Account, Address, WalletClient } from 'viem'
|
|
26
26
|
import type { RetryConfig } from '../transport/retryPolicy.js'
|
|
27
|
-
import type {
|
|
28
|
-
ProviderConfigs,
|
|
29
|
-
RequestInterceptor,
|
|
30
|
-
SwitchChainHook,
|
|
31
|
-
} from './config.js'
|
|
27
|
+
import type { RequestInterceptor, SwitchChainHook } from './config.js'
|
|
32
28
|
import type { PerpsProviderPlugin } from './provider.js'
|
|
33
29
|
|
|
34
30
|
/**
|
|
@@ -52,25 +48,15 @@ export interface PerpsConfig {
|
|
|
52
48
|
apiKey?: string
|
|
53
49
|
/** Perps API base URL; defaults to {@link DEFAULT_API_URL}. */
|
|
54
50
|
apiUrl?: string
|
|
55
|
-
/** Skip the SDK-version compatibility check when set. */
|
|
56
|
-
disableVersionCheck?: boolean
|
|
57
51
|
/** Hook that can rewrite URL/request options before transport. */
|
|
58
52
|
requestInterceptor?: RequestInterceptor
|
|
59
53
|
/**
|
|
60
|
-
* Provider plugins
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* runtime as bound {@link PerpsProvider}s via `client.getProvider(key)`.
|
|
65
|
-
* Modelled on `@lifi/sdk`'s `providers: SDKProvider[]`.
|
|
66
|
-
* - `ProviderConfigs` — keyed config object (e.g.
|
|
67
|
-
* `{ hyperliquid: { markets: [...] } }`). Used internally by
|
|
68
|
-
* `PerpsWsClient` to filter which markets are subscribed to.
|
|
69
|
-
*
|
|
70
|
-
* Both may be supplied during the migration to provider packages;
|
|
71
|
-
* the array form is preferred for new code.
|
|
54
|
+
* Provider plugins, one per DEX. Bound to the client at construction and
|
|
55
|
+
* looked up at runtime as bound {@link PerpsProvider}s via
|
|
56
|
+
* `client.getProvider(key)`. Modelled on `@lifi/sdk`'s
|
|
57
|
+
* `providers: SDKProvider[]`.
|
|
72
58
|
*/
|
|
73
|
-
providers?: PerpsProviderPlugin[]
|
|
59
|
+
providers?: PerpsProviderPlugin[]
|
|
74
60
|
/**
|
|
75
61
|
* The end-user's wallet, used whenever an action's descriptor names
|
|
76
62
|
* `PerpsSigner.USER` as its `signer`. Accepts any viem-compatible WalletClient:
|
|
@@ -117,12 +103,8 @@ export interface PerpsClientOptions {
|
|
|
117
103
|
apiKey?: string
|
|
118
104
|
/** Perps API base URL; defaults to {@link DEFAULT_API_URL}. */
|
|
119
105
|
apiUrl?: string
|
|
120
|
-
/**
|
|
121
|
-
|
|
122
|
-
* {@link PerpsConfig.providers}: an array of `PerpsProviderPlugin` plugins or
|
|
123
|
-
* a keyed `ProviderConfigs` map.
|
|
124
|
-
*/
|
|
125
|
-
providers?: PerpsProviderPlugin[] | ProviderConfigs
|
|
106
|
+
/** Provider plugins; see {@link PerpsConfig.providers}. */
|
|
107
|
+
providers?: PerpsProviderPlugin[]
|
|
126
108
|
/**
|
|
127
109
|
* Hook invoked before a USER-signed EIP-712 action is signed, to switch the
|
|
128
110
|
* user's wallet to the action's target chain. Also settable at runtime via
|
package/src/types/config.ts
CHANGED
|
@@ -23,32 +23,6 @@ export type SwitchChainHook = (
|
|
|
23
23
|
chainId: number
|
|
24
24
|
) => Promise<PerpsClientSigner | undefined>
|
|
25
25
|
|
|
26
|
-
/**
|
|
27
|
-
* Per-provider config — restricts which `markets` the WS client subscribes
|
|
28
|
-
* to. Indexed by provider key.
|
|
29
|
-
*
|
|
30
|
-
* @public
|
|
31
|
-
*/
|
|
32
|
-
export interface ProviderConfig {
|
|
33
|
-
/** Optional provider category/market ids used to filter WS subscriptions. */
|
|
34
|
-
markets?: string[]
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* @deprecated Use {@link ProviderConfig}.
|
|
39
|
-
* @public
|
|
40
|
-
*/
|
|
41
|
-
export type HyperliquidConfig = ProviderConfig
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Map of per-provider {@link ProviderConfig}, keyed by provider key.
|
|
45
|
-
*
|
|
46
|
-
* @public
|
|
47
|
-
*/
|
|
48
|
-
export interface ProviderConfigs {
|
|
49
|
-
[provider: string]: ProviderConfig | undefined
|
|
50
|
-
}
|
|
51
|
-
|
|
52
26
|
/**
|
|
53
27
|
* Hook to rewrite each outgoing request's URL/`RequestInit` before it is sent.
|
|
54
28
|
*
|
|
@@ -101,12 +75,8 @@ export interface PerpsBaseConfig {
|
|
|
101
75
|
apiKey: string
|
|
102
76
|
/** Resolved perps API base URL. */
|
|
103
77
|
apiUrl: string
|
|
104
|
-
/** Whether SDK version compatibility checks are disabled. */
|
|
105
|
-
disableVersionCheck?: boolean
|
|
106
78
|
/** Optional outgoing-request URL/options interceptor. */
|
|
107
79
|
requestInterceptor?: RequestInterceptor
|
|
108
|
-
/** Optional per-provider WS market filters. */
|
|
109
|
-
providers?: ProviderConfigs
|
|
110
80
|
/** Optional global or per-provider retry configuration. */
|
|
111
81
|
retry?: RetryConfig
|
|
112
82
|
/** Fetch implementation used by core HTTP transport. */
|
package/src/types/provider.ts
CHANGED
|
@@ -453,16 +453,17 @@ export interface PerpsProviderPlugin {
|
|
|
453
453
|
): Promise<PositionsResponse>
|
|
454
454
|
|
|
455
455
|
/**
|
|
456
|
-
* The user's current venue-side settings for one market — the margin
|
|
457
|
-
* and leverage the next order on it will use.
|
|
458
|
-
*
|
|
459
|
-
*
|
|
460
|
-
*
|
|
456
|
+
* The user's current venue-side settings for one perps market — the margin
|
|
457
|
+
* mode and leverage the next order on it will use. Resolves the venue value
|
|
458
|
+
* or throws.
|
|
459
|
+
*
|
|
460
|
+
* @throws {PerpsError} `ValidationError` for a spot market; any other
|
|
461
|
+
* `PerpsError` when the venue read fails or carries no usable value.
|
|
461
462
|
*/
|
|
462
|
-
getMarketSettings
|
|
463
|
+
getMarketSettings(
|
|
463
464
|
params: ProviderGetMarketSettingsParams,
|
|
464
465
|
options?: SDKRequestOptions
|
|
465
|
-
): Promise<MarketSettings
|
|
466
|
+
): Promise<MarketSettings>
|
|
466
467
|
|
|
467
468
|
/**
|
|
468
469
|
* The amounts the account can still buy and sell on one market, in that
|
package/src/types/withdrawal.ts
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
|
-
import type { ActionType, Asset } from '@lifi/perps-types'
|
|
1
|
+
import type { ActionType, Asset, WithdrawalRoute } from '@lifi/perps-types'
|
|
2
2
|
import type { Address } from 'viem'
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
* Which of a venue's two balance routes a withdrawal is drawn from. Lighter's
|
|
6
|
-
* own vocabulary, carried in the signed tx as `AssetRouteType` (`perps` = 0,
|
|
7
|
-
* `spot` = 1); an account's mode changes what the two balances mean, never
|
|
8
|
-
* which route the tx names.
|
|
9
|
-
*
|
|
10
|
-
* @public
|
|
11
|
-
*/
|
|
12
|
-
export type WithdrawalRoute = 'perps' | 'spot'
|
|
4
|
+
export type { WithdrawalRoute }
|
|
13
5
|
|
|
14
6
|
/**
|
|
15
7
|
* One withdrawable `(asset, route)` selection as the provider reports it, keyed
|
|
@@ -25,10 +17,16 @@ export interface ProviderWithdrawableBalance {
|
|
|
25
17
|
available: string
|
|
26
18
|
/**
|
|
27
19
|
* Flat venue fee on a withdrawal from this row, in the asset's own units.
|
|
28
|
-
* Absent when the venue publishes no fee for the asset. The client rejects
|
|
29
|
-
* value that is not a non-negative decimal.
|
|
20
|
+
* Absent when the venue publishes no fee for the asset. The client rejects
|
|
21
|
+
* a value that is not a non-negative decimal.
|
|
30
22
|
*/
|
|
31
23
|
withdrawalFee?: string
|
|
24
|
+
/**
|
|
25
|
+
* `true` when the venue takes `withdrawalFee` out of the requested amount,
|
|
26
|
+
* `false` when it charges the fee in addition. Set together with
|
|
27
|
+
* `withdrawalFee`; the client drops it from a row without a fee.
|
|
28
|
+
*/
|
|
29
|
+
isFeeDeducted?: boolean
|
|
32
30
|
}
|
|
33
31
|
|
|
34
32
|
/**
|
|
@@ -44,11 +42,22 @@ export interface WithdrawableBalance {
|
|
|
44
42
|
/** Withdrawable amount in the asset's own units. */
|
|
45
43
|
available: string
|
|
46
44
|
/**
|
|
47
|
-
* Flat venue fee on a withdrawal from this row, in the asset's own units.
|
|
48
|
-
*
|
|
49
|
-
* fee
|
|
45
|
+
* Flat venue fee on a withdrawal from this row, in the asset's own units.
|
|
46
|
+
* Absent when the venue publishes no fee for the asset. With
|
|
47
|
+
* `isFeeDeducted` `true`, the venue takes the fee out of the requested
|
|
48
|
+
* amount, and an amount at or below the fee delivers nothing. With `false`,
|
|
49
|
+
* the venue charges the fee in addition to the requested amount, so the
|
|
50
|
+
* largest amount the row can fund is `available` minus the fee. That result
|
|
51
|
+
* is zero or negative when the fee is at or above `available`, and the row
|
|
52
|
+
* then funds no withdrawal.
|
|
50
53
|
*/
|
|
51
54
|
withdrawalFee?: string
|
|
55
|
+
/**
|
|
56
|
+
* Present only together with `withdrawalFee`. Absent on a row from a
|
|
57
|
+
* provider plugin that does not report it; absence means unknown, not
|
|
58
|
+
* `true`.
|
|
59
|
+
*/
|
|
60
|
+
isFeeDeducted?: boolean
|
|
52
61
|
}
|
|
53
62
|
|
|
54
63
|
/**
|
|
@@ -16,7 +16,9 @@ import {
|
|
|
16
16
|
type QuoteSide,
|
|
17
17
|
type TradeType,
|
|
18
18
|
} from '@lifi/perps-types'
|
|
19
|
+
import type Big from 'big.js'
|
|
19
20
|
import { PerpsError } from '../errors/PerpsError.js'
|
|
21
|
+
import { areFinite, DivBig } from './decimal.js'
|
|
20
22
|
|
|
21
23
|
/**
|
|
22
24
|
* Calculate position size in asset units from margin.
|
|
@@ -36,7 +38,13 @@ export function calculatePositionSize(
|
|
|
36
38
|
leverage: number,
|
|
37
39
|
price: number
|
|
38
40
|
): number {
|
|
39
|
-
|
|
41
|
+
if (price === 0) {
|
|
42
|
+
return (marginUsd * leverage) / price
|
|
43
|
+
}
|
|
44
|
+
if (!areFinite(marginUsd, leverage, price)) {
|
|
45
|
+
return Number.NaN
|
|
46
|
+
}
|
|
47
|
+
return new DivBig(marginUsd).times(leverage).div(price).toNumber()
|
|
40
48
|
}
|
|
41
49
|
|
|
42
50
|
/**
|
|
@@ -48,7 +56,10 @@ export function calculatePositionSize(
|
|
|
48
56
|
* @public
|
|
49
57
|
*/
|
|
50
58
|
export function calculateNotionalValue(size: number, price: number): number {
|
|
51
|
-
|
|
59
|
+
if (!areFinite(size, price)) {
|
|
60
|
+
return Number.NaN
|
|
61
|
+
}
|
|
62
|
+
return new DivBig(size).abs().times(price).toNumber()
|
|
52
63
|
}
|
|
53
64
|
|
|
54
65
|
/**
|
|
@@ -65,7 +76,10 @@ export function calculateUnrealizedPnl(
|
|
|
65
76
|
currentPrice: number,
|
|
66
77
|
size: number
|
|
67
78
|
): number {
|
|
68
|
-
|
|
79
|
+
if (!areFinite(entryPrice, currentPrice, size)) {
|
|
80
|
+
return Number.NaN
|
|
81
|
+
}
|
|
82
|
+
return new DivBig(currentPrice).minus(entryPrice).times(size).toNumber()
|
|
69
83
|
}
|
|
70
84
|
|
|
71
85
|
/**
|
|
@@ -80,7 +94,10 @@ export function calculateRoe(pnl: number, margin: number): number {
|
|
|
80
94
|
if (margin === 0) {
|
|
81
95
|
return 0
|
|
82
96
|
}
|
|
83
|
-
|
|
97
|
+
if (!areFinite(pnl, margin)) {
|
|
98
|
+
return Number.NaN
|
|
99
|
+
}
|
|
100
|
+
return new DivBig(pnl).div(margin).times(100).toNumber()
|
|
84
101
|
}
|
|
85
102
|
|
|
86
103
|
/**
|
|
@@ -95,7 +112,13 @@ export function calculateRequiredMargin(
|
|
|
95
112
|
notionalValue: number,
|
|
96
113
|
leverage: number
|
|
97
114
|
): number {
|
|
98
|
-
|
|
115
|
+
if (leverage === 0) {
|
|
116
|
+
return notionalValue / leverage
|
|
117
|
+
}
|
|
118
|
+
if (!areFinite(notionalValue, leverage)) {
|
|
119
|
+
return Number.NaN
|
|
120
|
+
}
|
|
121
|
+
return new DivBig(notionalValue).div(leverage).toNumber()
|
|
99
122
|
}
|
|
100
123
|
|
|
101
124
|
/**
|
|
@@ -107,7 +130,10 @@ export function calculateRequiredMargin(
|
|
|
107
130
|
* @public
|
|
108
131
|
*/
|
|
109
132
|
export function estimateFees(sizeUsd: number, feeRate: number): number {
|
|
110
|
-
|
|
133
|
+
if (!areFinite(sizeUsd, feeRate)) {
|
|
134
|
+
return Number.NaN
|
|
135
|
+
}
|
|
136
|
+
return new DivBig(sizeUsd).times(feeRate).toNumber()
|
|
111
137
|
}
|
|
112
138
|
|
|
113
139
|
/**
|
|
@@ -124,8 +150,17 @@ export function applySlippage(
|
|
|
124
150
|
slippagePercent: number,
|
|
125
151
|
isBuy: boolean
|
|
126
152
|
): number {
|
|
127
|
-
|
|
128
|
-
|
|
153
|
+
if (!areFinite(price, slippagePercent)) {
|
|
154
|
+
return Number.NaN
|
|
155
|
+
}
|
|
156
|
+
const multiplier = new DivBig(slippagePercent).div(100).plus(1)
|
|
157
|
+
if (isBuy) {
|
|
158
|
+
return new DivBig(price).times(multiplier).toNumber()
|
|
159
|
+
}
|
|
160
|
+
if (multiplier.eq(0)) {
|
|
161
|
+
return price / multiplier.toNumber()
|
|
162
|
+
}
|
|
163
|
+
return new DivBig(price).div(multiplier).toNumber()
|
|
129
164
|
}
|
|
130
165
|
|
|
131
166
|
/**
|
|
@@ -166,7 +201,10 @@ export function effectiveLeverage(params: {
|
|
|
166
201
|
if (marginUsd === 0) {
|
|
167
202
|
return 0
|
|
168
203
|
}
|
|
169
|
-
|
|
204
|
+
if (!areFinite(positionValueUsd, marginUsd)) {
|
|
205
|
+
return Number.NaN
|
|
206
|
+
}
|
|
207
|
+
return new DivBig(positionValueUsd).div(marginUsd).toNumber()
|
|
170
208
|
}
|
|
171
209
|
|
|
172
210
|
/**
|
|
@@ -202,12 +240,15 @@ export function calculateExpectedPnl(
|
|
|
202
240
|
if (!triggerPrice || entryPrice === 0 || margin === 0) {
|
|
203
241
|
return null
|
|
204
242
|
}
|
|
243
|
+
if (!areFinite(triggerPrice, entryPrice, leverage, margin)) {
|
|
244
|
+
return { amount: Number.NaN, percent: Number.NaN }
|
|
245
|
+
}
|
|
205
246
|
const priceDiff = isLong
|
|
206
|
-
? triggerPrice
|
|
207
|
-
: entryPrice
|
|
208
|
-
const percent = (
|
|
209
|
-
const amount = margin
|
|
210
|
-
return { amount, percent }
|
|
247
|
+
? new DivBig(triggerPrice).minus(entryPrice)
|
|
248
|
+
: new DivBig(entryPrice).minus(triggerPrice)
|
|
249
|
+
const percent = priceDiff.div(entryPrice).times(leverage).times(100)
|
|
250
|
+
const amount = percent.times(margin).div(100)
|
|
251
|
+
return { amount: amount.toNumber(), percent: percent.toNumber() }
|
|
211
252
|
}
|
|
212
253
|
|
|
213
254
|
/**
|
|
@@ -228,8 +269,14 @@ export function priceFromPercent(
|
|
|
228
269
|
if (entryPrice === 0 || leverage === 0) {
|
|
229
270
|
return 0
|
|
230
271
|
}
|
|
231
|
-
|
|
232
|
-
|
|
272
|
+
if (!areFinite(percent, entryPrice, leverage)) {
|
|
273
|
+
return Number.NaN
|
|
274
|
+
}
|
|
275
|
+
const priceDelta = new DivBig(percent)
|
|
276
|
+
.times(entryPrice)
|
|
277
|
+
.div(new DivBig(leverage).times(100))
|
|
278
|
+
const entry = new DivBig(entryPrice)
|
|
279
|
+
return (isLong ? entry.plus(priceDelta) : entry.minus(priceDelta)).toNumber()
|
|
233
280
|
}
|
|
234
281
|
|
|
235
282
|
/**
|
|
@@ -250,8 +297,13 @@ export function percentFromPrice(
|
|
|
250
297
|
if (entryPrice === 0 || leverage === 0) {
|
|
251
298
|
return 0
|
|
252
299
|
}
|
|
253
|
-
|
|
254
|
-
|
|
300
|
+
if (!areFinite(price, entryPrice, leverage)) {
|
|
301
|
+
return Number.NaN
|
|
302
|
+
}
|
|
303
|
+
const priceDiff = isLong
|
|
304
|
+
? new DivBig(price).minus(entryPrice)
|
|
305
|
+
: new DivBig(entryPrice).minus(price)
|
|
306
|
+
return priceDiff.div(entryPrice).times(leverage).times(100).toNumber()
|
|
255
307
|
}
|
|
256
308
|
|
|
257
309
|
/**
|
|
@@ -268,11 +320,14 @@ export function calculateRealizedPnlPercent(
|
|
|
268
320
|
size: number,
|
|
269
321
|
price: number
|
|
270
322
|
): number {
|
|
271
|
-
|
|
272
|
-
|
|
323
|
+
if (!areFinite(realizedPnl, size, price)) {
|
|
324
|
+
return Number.NaN
|
|
325
|
+
}
|
|
326
|
+
const positionValue = new DivBig(size).abs().times(price)
|
|
327
|
+
if (positionValue.eq(0)) {
|
|
273
328
|
return 0
|
|
274
329
|
}
|
|
275
|
-
return (realizedPnl
|
|
330
|
+
return new DivBig(realizedPnl).div(positionValue).times(100).toNumber()
|
|
276
331
|
}
|
|
277
332
|
|
|
278
333
|
/** Result of {@link walkOrderbook} — the fill obtained for a USD-notional walk. */
|
|
@@ -283,7 +338,7 @@ interface BookWalk {
|
|
|
283
338
|
filledNotional: number
|
|
284
339
|
/** Volume-weighted average fill price, or 0 when the book is empty. */
|
|
285
340
|
vwap: number
|
|
286
|
-
/** True when the levels could not absorb the requested notional. */
|
|
341
|
+
/** True when the levels could not absorb the requested notional, and for a non-finite notional. */
|
|
287
342
|
insufficientLiquidity: boolean
|
|
288
343
|
}
|
|
289
344
|
|
|
@@ -293,7 +348,8 @@ interface BookWalk {
|
|
|
293
348
|
* in array order — the caller passes asks for a buy and bids for a sell, each
|
|
294
349
|
* already ordered best-price-first. When the book cannot absorb the full
|
|
295
350
|
* notional, the walk stops at the last level and flags `insufficientLiquidity`,
|
|
296
|
-
* returning the best obtainable fill.
|
|
351
|
+
* returning the best obtainable fill. A non-finite `sizeUsd` gives NaN fill
|
|
352
|
+
* fields with `insufficientLiquidity` set.
|
|
297
353
|
*
|
|
298
354
|
* @throws {PerpsError} `ValidationError` when a level's `price` or `size`
|
|
299
355
|
* does not parse to a finite number.
|
|
@@ -303,32 +359,47 @@ export function walkOrderbook(
|
|
|
303
359
|
levels: OrderbookLevel[],
|
|
304
360
|
sizeUsd: number
|
|
305
361
|
): BookWalk {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
362
|
+
if (!Number.isFinite(sizeUsd)) {
|
|
363
|
+
return {
|
|
364
|
+
baseSize: Number.NaN,
|
|
365
|
+
filledNotional: Number.NaN,
|
|
366
|
+
vwap: Number.NaN,
|
|
367
|
+
insufficientLiquidity: true,
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
let remaining = new DivBig(sizeUsd)
|
|
371
|
+
let baseSize = new DivBig(0)
|
|
372
|
+
let filledNotional = new DivBig(0)
|
|
309
373
|
for (const level of levels) {
|
|
310
|
-
if (remaining
|
|
374
|
+
if (remaining.lte(0)) {
|
|
311
375
|
break
|
|
312
376
|
}
|
|
313
|
-
const price =
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
`Malformed orderbook level: price='${level.price}', size='${level.size}'`
|
|
319
|
-
)
|
|
377
|
+
const { price, size } = parseLevel(level)
|
|
378
|
+
const levelNotional = size.times(price)
|
|
379
|
+
const take = remaining.lt(levelNotional) ? remaining : levelNotional
|
|
380
|
+
if (take.eq(0)) {
|
|
381
|
+
continue
|
|
320
382
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
baseSize += take / price
|
|
325
|
-
remaining -= take
|
|
383
|
+
filledNotional = filledNotional.plus(take)
|
|
384
|
+
baseSize = baseSize.plus(take.div(price))
|
|
385
|
+
remaining = remaining.minus(take)
|
|
326
386
|
}
|
|
327
387
|
return {
|
|
328
|
-
baseSize,
|
|
329
|
-
filledNotional,
|
|
330
|
-
vwap: baseSize
|
|
331
|
-
insufficientLiquidity: remaining
|
|
388
|
+
baseSize: baseSize.toNumber(),
|
|
389
|
+
filledNotional: filledNotional.toNumber(),
|
|
390
|
+
vwap: baseSize.eq(0) ? 0 : filledNotional.div(baseSize).toNumber(),
|
|
391
|
+
insufficientLiquidity: remaining.gt(0),
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function parseLevel(level: OrderbookLevel): { price: Big; size: Big } {
|
|
396
|
+
try {
|
|
397
|
+
return { price: new DivBig(level.price), size: new DivBig(level.size) }
|
|
398
|
+
} catch {
|
|
399
|
+
throw new PerpsError(
|
|
400
|
+
PerpsErrorCode.ValidationError,
|
|
401
|
+
`Malformed orderbook level: price='${level.price}', size='${level.size}'`
|
|
402
|
+
)
|
|
332
403
|
}
|
|
333
404
|
}
|
|
334
405
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import Big from 'big.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Scoped big.js constructor with 40 decimal places for division, so the
|
|
5
|
+
* shared global `Big.DP` and `Big.RM` stay untouched.
|
|
6
|
+
*/
|
|
7
|
+
export const DivBig = Big()
|
|
8
|
+
DivBig.DP = 40
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* As `DivBig`, but truncating, so a derived amount never rounds up past the
|
|
12
|
+
* exact quotient.
|
|
13
|
+
*/
|
|
14
|
+
export const TruncBig = Big()
|
|
15
|
+
TruncBig.DP = 40
|
|
16
|
+
TruncBig.RM = Big.roundDown
|
|
17
|
+
|
|
18
|
+
/** True when every value is a finite number, so `new DivBig(value)` cannot throw. */
|
|
19
|
+
export function areFinite(...values: number[]): boolean {
|
|
20
|
+
return values.every(Number.isFinite)
|
|
21
|
+
}
|