@lifi/perps-sdk-provider-hyperliquid 30.0.2 → 31.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 +21 -0
- package/dist/cjs/HyperliquidProvider.d.ts.map +1 -1
- package/dist/cjs/HyperliquidProvider.js +1 -1
- package/dist/cjs/HyperliquidProvider.js.map +1 -1
- package/dist/cjs/services/getActivity.d.ts.map +1 -1
- package/dist/cjs/services/getActivity.js +2 -2
- package/dist/cjs/services/getActivity.js.map +1 -1
- package/dist/cjs/services/getFills.js +1 -1
- package/dist/cjs/services/getFills.js.map +1 -1
- package/dist/cjs/services/getOrder.d.ts.map +1 -1
- package/dist/cjs/services/getOrder.js +2 -3
- package/dist/cjs/services/getOrder.js.map +1 -1
- package/dist/cjs/services/getOrders.d.ts.map +1 -1
- package/dist/cjs/services/getOrders.js +3 -3
- package/dist/cjs/services/getOrders.js.map +1 -1
- package/dist/cjs/services/twapSliceFills.d.ts +5 -0
- package/dist/cjs/services/twapSliceFills.d.ts.map +1 -0
- package/dist/cjs/services/twapSliceFills.js +37 -0
- package/dist/cjs/services/twapSliceFills.js.map +1 -0
- package/dist/cjs/types/account.d.ts +1 -1
- package/dist/cjs/types/account.d.ts.map +1 -1
- package/dist/cjs/types/account.js.map +1 -1
- package/dist/cjs/types/fill.d.ts.map +1 -1
- package/dist/cjs/utils/isFillLiquidatingAccount.d.ts +3 -0
- package/dist/cjs/utils/isFillLiquidatingAccount.d.ts.map +1 -0
- package/dist/cjs/utils/isFillLiquidatingAccount.js +6 -0
- package/dist/cjs/utils/isFillLiquidatingAccount.js.map +1 -0
- package/dist/cjs/utils/liquidation.d.ts +3 -2
- package/dist/cjs/utils/liquidation.d.ts.map +1 -1
- package/dist/cjs/utils/liquidation.js +21 -4
- package/dist/cjs/utils/liquidation.js.map +1 -1
- package/dist/cjs/utils/mapActivity.d.ts.map +1 -1
- package/dist/cjs/utils/mapActivity.js +2 -2
- package/dist/cjs/utils/mapActivity.js.map +1 -1
- package/dist/cjs/utils/mapFill.d.ts +1 -1
- package/dist/cjs/utils/mapFill.d.ts.map +1 -1
- package/dist/cjs/utils/mapFill.js +24 -2
- package/dist/cjs/utils/mapFill.js.map +1 -1
- package/dist/cjs/utils/mapOrder.d.ts.map +1 -1
- package/dist/cjs/utils/mapOrder.js +7 -1
- package/dist/cjs/utils/mapOrder.js.map +1 -1
- package/dist/cjs/utils/mapPosition.js +1 -1
- package/dist/cjs/utils/mapPosition.js.map +1 -1
- package/dist/cjs/websocket/HyperliquidWsProvider.js +1 -1
- package/dist/cjs/websocket/HyperliquidWsProvider.js.map +1 -1
- package/dist/esm/HyperliquidProvider.d.ts.map +1 -1
- package/dist/esm/HyperliquidProvider.js +1 -1
- package/dist/esm/HyperliquidProvider.js.map +1 -1
- package/dist/esm/services/getActivity.d.ts.map +1 -1
- package/dist/esm/services/getActivity.js +2 -2
- package/dist/esm/services/getActivity.js.map +1 -1
- package/dist/esm/services/getFills.js +1 -1
- package/dist/esm/services/getFills.js.map +1 -1
- package/dist/esm/services/getOrder.d.ts.map +1 -1
- package/dist/esm/services/getOrder.js +2 -3
- package/dist/esm/services/getOrder.js.map +1 -1
- package/dist/esm/services/getOrders.d.ts.map +1 -1
- package/dist/esm/services/getOrders.js +3 -3
- package/dist/esm/services/getOrders.js.map +1 -1
- package/dist/esm/services/twapSliceFills.d.ts +11 -0
- package/dist/esm/services/twapSliceFills.d.ts.map +1 -0
- package/dist/esm/services/twapSliceFills.js +41 -0
- package/dist/esm/services/twapSliceFills.js.map +1 -0
- package/dist/esm/types/account.d.ts +5 -2
- package/dist/esm/types/account.d.ts.map +1 -1
- package/dist/esm/types/account.js.map +1 -1
- package/dist/esm/types/fill.d.ts +4 -2
- package/dist/esm/types/fill.d.ts.map +1 -1
- package/dist/esm/utils/isFillLiquidatingAccount.d.ts +8 -0
- package/dist/esm/utils/isFillLiquidatingAccount.d.ts.map +1 -0
- package/dist/esm/utils/isFillLiquidatingAccount.js +7 -0
- package/dist/esm/utils/isFillLiquidatingAccount.js.map +1 -0
- package/dist/esm/utils/liquidation.d.ts +13 -12
- package/dist/esm/utils/liquidation.d.ts.map +1 -1
- package/dist/esm/utils/liquidation.js +33 -16
- package/dist/esm/utils/liquidation.js.map +1 -1
- package/dist/esm/utils/mapActivity.d.ts.map +1 -1
- package/dist/esm/utils/mapActivity.js +2 -2
- package/dist/esm/utils/mapActivity.js.map +1 -1
- package/dist/esm/utils/mapFill.d.ts +4 -5
- package/dist/esm/utils/mapFill.d.ts.map +1 -1
- package/dist/esm/utils/mapFill.js +29 -11
- package/dist/esm/utils/mapFill.js.map +1 -1
- package/dist/esm/utils/mapOrder.d.ts.map +1 -1
- package/dist/esm/utils/mapOrder.js +7 -1
- package/dist/esm/utils/mapOrder.js.map +1 -1
- package/dist/esm/utils/mapPosition.d.ts +1 -1
- package/dist/esm/utils/mapPosition.js +2 -2
- package/dist/esm/utils/mapPosition.js.map +1 -1
- package/dist/esm/websocket/HyperliquidWsProvider.js +1 -1
- package/dist/esm/websocket/HyperliquidWsProvider.js.map +1 -1
- package/dist/types/HyperliquidProvider.d.ts.map +1 -1
- package/dist/types/services/getActivity.d.ts.map +1 -1
- package/dist/types/services/getOrder.d.ts.map +1 -1
- package/dist/types/services/getOrders.d.ts.map +1 -1
- package/dist/types/services/twapSliceFills.d.ts +11 -0
- package/dist/types/services/twapSliceFills.d.ts.map +1 -0
- package/dist/types/types/account.d.ts +5 -2
- package/dist/types/types/account.d.ts.map +1 -1
- package/dist/types/types/fill.d.ts +4 -2
- package/dist/types/types/fill.d.ts.map +1 -1
- package/dist/types/utils/isFillLiquidatingAccount.d.ts +8 -0
- package/dist/types/utils/isFillLiquidatingAccount.d.ts.map +1 -0
- package/dist/types/utils/liquidation.d.ts +13 -12
- package/dist/types/utils/liquidation.d.ts.map +1 -1
- package/dist/types/utils/mapActivity.d.ts.map +1 -1
- package/dist/types/utils/mapFill.d.ts +4 -5
- package/dist/types/utils/mapFill.d.ts.map +1 -1
- package/dist/types/utils/mapOrder.d.ts.map +1 -1
- package/dist/types/utils/mapPosition.d.ts +1 -1
- package/package.json +3 -3
- package/src/HyperliquidProvider.ts +1 -6
- package/src/services/getActivity.ts +3 -3
- package/src/services/getFills.ts +1 -1
- package/src/services/getOrder.ts +7 -9
- package/src/services/getOrders.ts +3 -11
- package/src/services/twapSliceFills.ts +65 -0
- package/src/types/account.ts +5 -2
- package/src/types/fill.ts +4 -2
- package/src/utils/isFillLiquidatingAccount.ts +12 -0
- package/src/utils/liquidation.ts +44 -17
- package/src/utils/mapActivity.ts +2 -2
- package/src/utils/mapFill.ts +41 -13
- package/src/utils/mapOrder.ts +7 -1
- package/src/utils/mapPosition.ts +2 -2
- package/src/websocket/HyperliquidWsProvider.ts +1 -1
|
@@ -12,7 +12,6 @@ import type {
|
|
|
12
12
|
HlFrontendOpenOrders,
|
|
13
13
|
HlHistoricalOrders,
|
|
14
14
|
HlTwapHistoryEntry,
|
|
15
|
-
HlTwapSliceFill,
|
|
16
15
|
} from '../types/index.js'
|
|
17
16
|
import { withExplorerLinks } from '../utils/explorer.js'
|
|
18
17
|
import { mapOrder, perpsDexNames } from '../utils/index.js'
|
|
@@ -23,6 +22,7 @@ import {
|
|
|
23
22
|
resolveTwapExecution,
|
|
24
23
|
} from '../utils/twapHistory.js'
|
|
25
24
|
import { requireAccountExists } from './getAccountExists.js'
|
|
25
|
+
import { fetchTwapSliceFills } from './twapSliceFills.js'
|
|
26
26
|
|
|
27
27
|
/** Parameters for a lifecycle-filtered Hyperliquid order read. */
|
|
28
28
|
export type GetOrdersParams = ProviderGetOrdersParams
|
|
@@ -78,16 +78,8 @@ export const getOrders = async (
|
|
|
78
78
|
])
|
|
79
79
|
const latestTwaps = resolveLatestTwapEntries(twaps)
|
|
80
80
|
const sliceFills =
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
(entry) =>
|
|
84
|
-
entry.twapId !== undefined && entry.status.status === 'activated'
|
|
85
|
-
)
|
|
86
|
-
? await infoRequest<HlTwapSliceFill[]>(
|
|
87
|
-
apiUrl,
|
|
88
|
-
{ type: 'userTwapSliceFills', user: params.address },
|
|
89
|
-
infoOpts
|
|
90
|
-
)
|
|
81
|
+
active || statuses.includes(OrderStatus.FILLED)
|
|
82
|
+
? await fetchTwapSliceFills(apiUrl, params.address, latestTwaps, infoOpts)
|
|
91
83
|
: []
|
|
92
84
|
const rows = new Map<string, Order>()
|
|
93
85
|
const keep = (key: string, order: Order | undefined): void => {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { Address } from 'viem'
|
|
2
|
+
import type { HlTwapHistoryEntry, HlTwapSliceFill } from '../types/index.js'
|
|
3
|
+
import { type InfoRequestOptions, infoRequest } from '../utils/infoClient.js'
|
|
4
|
+
import { historyPage } from './historyPagination.js'
|
|
5
|
+
|
|
6
|
+
/** Row cap of both `userTwapSliceFills` and `userTwapSliceFillsByTime`. */
|
|
7
|
+
const TWAP_SLICE_FILLS_CAP = 2000
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Read the slice fills of each `activated` TWAP in `entries`, or none without
|
|
11
|
+
* a request when no entry is `activated`. `userTwapSliceFills` holds only the
|
|
12
|
+
* newest rows of the account, so a full window that does not reach back to a
|
|
13
|
+
* TWAP start gets its older rows from `userTwapSliceFillsByTime`.
|
|
14
|
+
*/
|
|
15
|
+
export const fetchTwapSliceFills = async (
|
|
16
|
+
apiUrl: string,
|
|
17
|
+
user: Address,
|
|
18
|
+
entries: readonly HlTwapHistoryEntry[],
|
|
19
|
+
options?: InfoRequestOptions
|
|
20
|
+
): Promise<HlTwapSliceFill[]> => {
|
|
21
|
+
const starts = entries.flatMap((entry) =>
|
|
22
|
+
entry.twapId !== undefined && entry.status.status === 'activated'
|
|
23
|
+
? [entry.state.timestamp]
|
|
24
|
+
: []
|
|
25
|
+
)
|
|
26
|
+
if (starts.length === 0) {
|
|
27
|
+
return []
|
|
28
|
+
}
|
|
29
|
+
const window = await infoRequest<HlTwapSliceFill[]>(
|
|
30
|
+
apiUrl,
|
|
31
|
+
{ type: 'userTwapSliceFills', user },
|
|
32
|
+
options
|
|
33
|
+
)
|
|
34
|
+
if (window.length < TWAP_SLICE_FILLS_CAP) {
|
|
35
|
+
return window
|
|
36
|
+
}
|
|
37
|
+
const oldest = Math.min(...window.map(({ fill }) => fill.time))
|
|
38
|
+
const startTime = Math.min(...starts)
|
|
39
|
+
if (startTime > oldest) {
|
|
40
|
+
return window
|
|
41
|
+
}
|
|
42
|
+
// The window can end inside its oldest millisecond, so read that millisecond again.
|
|
43
|
+
const older = await historyPage(
|
|
44
|
+
{ startTime, endTime: oldest },
|
|
45
|
+
Number.MAX_SAFE_INTEGER,
|
|
46
|
+
async (rangeStart, rangeEnd) => {
|
|
47
|
+
const fills = await infoRequest<HlTwapSliceFill[]>(
|
|
48
|
+
apiUrl,
|
|
49
|
+
{
|
|
50
|
+
type: 'userTwapSliceFillsByTime',
|
|
51
|
+
user,
|
|
52
|
+
startTime: rangeStart,
|
|
53
|
+
endTime: rangeEnd,
|
|
54
|
+
},
|
|
55
|
+
options
|
|
56
|
+
)
|
|
57
|
+
return fills.length >= TWAP_SLICE_FILLS_CAP
|
|
58
|
+
? { items: [], saturated: true }
|
|
59
|
+
: { items: fills, saturated: false }
|
|
60
|
+
},
|
|
61
|
+
({ fill }) => ({ time: fill.time, key: fill.tid }),
|
|
62
|
+
options?.signal
|
|
63
|
+
)
|
|
64
|
+
return [...window.filter(({ fill }) => fill.time > oldest), ...older.items]
|
|
65
|
+
}
|
package/src/types/account.ts
CHANGED
|
@@ -18,8 +18,11 @@ export type HlPosition = {
|
|
|
18
18
|
entryPx: string
|
|
19
19
|
/** Position notional value as a decimal string. */
|
|
20
20
|
positionValue: string
|
|
21
|
-
/**
|
|
22
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Estimated liquidation price. `null` when no price liquidates the
|
|
23
|
+
* position, which Hyperliquid reports for a cross position.
|
|
24
|
+
*/
|
|
25
|
+
liquidationPx: string | null
|
|
23
26
|
/** Unrealized PnL in quote-asset units. */
|
|
24
27
|
unrealizedPnl: string
|
|
25
28
|
/** Margin currently assigned to this position. */
|
package/src/types/fill.ts
CHANGED
|
@@ -55,8 +55,10 @@ export type HlUserFills = HlUserFill[]
|
|
|
55
55
|
export type HlUserFillsByTime = HlUserFill[]
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
* One row of the `userTwapSliceFills` query: a
|
|
59
|
-
*
|
|
58
|
+
* One row of the `userTwapSliceFills` or `userTwapSliceFillsByTime` query: a
|
|
59
|
+
* fill that a TWAP slice made. Each query returns at most 2,000 rows:
|
|
60
|
+
* `userTwapSliceFills` the newest, newest first, and `userTwapSliceFillsByTime`
|
|
61
|
+
* the oldest of its inclusive range, oldest first.
|
|
60
62
|
* @public
|
|
61
63
|
*/
|
|
62
64
|
export type HlTwapSliceFill = {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { HlUserFill } from '../types/index.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Whether the fill liquidates `address`. Hyperliquid attaches `liquidation` to
|
|
5
|
+
* the fills of both parties, so only a fill whose `liquidatedUser` is the
|
|
6
|
+
* address counts. The address compare ignores case.
|
|
7
|
+
*/
|
|
8
|
+
export const isFillLiquidatingAccount = (
|
|
9
|
+
fill: Pick<HlUserFill, 'liquidation'>,
|
|
10
|
+
address: string
|
|
11
|
+
): boolean =>
|
|
12
|
+
fill.liquidation?.liquidatedUser.toLowerCase() === address.toLowerCase()
|
package/src/utils/liquidation.ts
CHANGED
|
@@ -16,10 +16,17 @@ import {
|
|
|
16
16
|
createSafeFunction,
|
|
17
17
|
divideDecimalString,
|
|
18
18
|
estimateLiquidationPrice,
|
|
19
|
+
type LiquidationEstimateParams,
|
|
19
20
|
numberToDecimalString,
|
|
20
21
|
PerpsError,
|
|
22
|
+
subtractDecimalString,
|
|
21
23
|
} from '@lifi/perps-sdk'
|
|
22
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
type DecimalString,
|
|
26
|
+
MarginMode,
|
|
27
|
+
PerpsErrorCode,
|
|
28
|
+
PositionSide,
|
|
29
|
+
} from '@lifi/perps-types'
|
|
23
30
|
|
|
24
31
|
/**
|
|
25
32
|
* Maintenance margin fraction for a Hyperliquid asset: half of the initial
|
|
@@ -50,34 +57,54 @@ export const safeCalculateMaintenanceMarginRate = createSafeFunction(
|
|
|
50
57
|
)
|
|
51
58
|
|
|
52
59
|
/**
|
|
53
|
-
* Liquidation price of
|
|
54
|
-
* formula
|
|
55
|
-
* the
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* liq_price = entryPrice - side × margin_available / (1 - mmr × side)
|
|
60
|
+
* Liquidation price of the isolated position that an order leaves, with the
|
|
61
|
+
* exact Hyperliquid formula: a new position, or the blend of the order into
|
|
62
|
+
* `params.position`. Hyperliquid isolated `marginUsed` includes the
|
|
63
|
+
* unrealized PnL, so the collateral of the position is `marginUsed` minus
|
|
64
|
+
* `unrealizedPnl`. For an existing position, prefer
|
|
65
|
+
* `Position.liquidationPrice` from the API.
|
|
60
66
|
*
|
|
61
|
-
* @param leverage - User-selected leverage (e.g., `'10'`)
|
|
62
67
|
* @param maxLeverage - Asset's maximum leverage (e.g., 50 for BTC). Sets
|
|
63
68
|
* mmr = 1 / (2 × maxLeverage).
|
|
64
|
-
* @returns The estimate, or `undefined` when
|
|
65
|
-
*
|
|
69
|
+
* @returns The estimate, or `undefined` when the order or the position is
|
|
70
|
+
* cross (the liquidation depends on the whole account), when the order
|
|
71
|
+
* reduces the position, or when `leverage` is at or above `2 × maxLeverage`,
|
|
72
|
+
* as {@link estimateLiquidationPrice} documents.
|
|
66
73
|
* @throws {PerpsError} `ValidationError` when `maxLeverage` is not a finite
|
|
67
74
|
* number above zero, or on an input that `estimateLiquidationPrice` rejects.
|
|
68
75
|
* @public
|
|
69
76
|
*/
|
|
70
77
|
export function calculateLiquidationPrice(
|
|
71
|
-
|
|
72
|
-
leverage: string,
|
|
73
|
-
isLong: boolean,
|
|
78
|
+
params: LiquidationEstimateParams,
|
|
74
79
|
maxLeverage: number
|
|
75
80
|
): string | undefined {
|
|
81
|
+
const { position } = params
|
|
82
|
+
if (
|
|
83
|
+
params.marginMode === MarginMode.CROSS ||
|
|
84
|
+
position?.marginMode === MarginMode.CROSS ||
|
|
85
|
+
(position !== undefined &&
|
|
86
|
+
(position.side === PositionSide.LONG) !== params.isLong)
|
|
87
|
+
) {
|
|
88
|
+
return undefined
|
|
89
|
+
}
|
|
76
90
|
return estimateLiquidationPrice({
|
|
77
|
-
entryPrice,
|
|
78
|
-
leverage,
|
|
79
|
-
isLong,
|
|
91
|
+
entryPrice: params.entryPrice,
|
|
92
|
+
leverage: params.leverage,
|
|
93
|
+
isLong: params.isLong,
|
|
80
94
|
maintenanceMarginRate: calculateMaintenanceMarginRate(maxLeverage),
|
|
95
|
+
size: params.size,
|
|
96
|
+
...(position === undefined
|
|
97
|
+
? {}
|
|
98
|
+
: {
|
|
99
|
+
current: {
|
|
100
|
+
size: position.size,
|
|
101
|
+
entryPrice: position.entryPrice,
|
|
102
|
+
margin: subtractDecimalString(
|
|
103
|
+
position.marginUsed,
|
|
104
|
+
position.unrealizedPnl
|
|
105
|
+
),
|
|
106
|
+
},
|
|
107
|
+
}),
|
|
81
108
|
})
|
|
82
109
|
}
|
|
83
110
|
|
package/src/utils/mapActivity.ts
CHANGED
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
isVaultTransferDelta,
|
|
39
39
|
isWithdrawDelta,
|
|
40
40
|
} from '../types/index.js'
|
|
41
|
+
import { isFillLiquidatingAccount } from './isFillLiquidatingAccount.js'
|
|
41
42
|
import { rowTimestampToIsoStringOrUndefined } from './rowTimestamp.js'
|
|
42
43
|
|
|
43
44
|
/**
|
|
@@ -371,11 +372,10 @@ export const mapLiquidationFills = (
|
|
|
371
372
|
resolveMarket: (coin: string) => MarketDisplay | undefined,
|
|
372
373
|
ledgerHashes: ReadonlySet<string>
|
|
373
374
|
): LiquidationActivity[] => {
|
|
374
|
-
const queried = queriedAddress.toLowerCase()
|
|
375
375
|
const byOrder = new Map<number, HlUserFill[]>()
|
|
376
376
|
for (const fill of fills) {
|
|
377
377
|
if (
|
|
378
|
-
fill
|
|
378
|
+
!isFillLiquidatingAccount(fill, queriedAddress) ||
|
|
379
379
|
(fill.hash !== undefined && ledgerHashes.has(fill.hash))
|
|
380
380
|
) {
|
|
381
381
|
continue
|
package/src/utils/mapFill.ts
CHANGED
|
@@ -4,29 +4,60 @@ import {
|
|
|
4
4
|
explorerTxUrl,
|
|
5
5
|
warnSkippedVenueRow,
|
|
6
6
|
} from '@lifi/perps-sdk'
|
|
7
|
-
import type { Fill, MarketDisplay } from '@lifi/perps-types'
|
|
7
|
+
import type { Fill, FillLiquidation, MarketDisplay } from '@lifi/perps-types'
|
|
8
8
|
import {
|
|
9
9
|
FillClassification,
|
|
10
|
+
LiquidationMethod,
|
|
10
11
|
LiquidityRole,
|
|
11
12
|
OrderSide,
|
|
12
|
-
OrderType,
|
|
13
13
|
} from '@lifi/perps-types'
|
|
14
14
|
import { PROVIDER_KEY, SPOT_MARKET_ID } from '../constants.js'
|
|
15
|
-
import type { HlUserFill } from '../types/index.js'
|
|
15
|
+
import type { HlFillLiquidation, HlUserFill } from '../types/index.js'
|
|
16
16
|
import { rowTimestampToIsoStringOrUndefined } from './rowTimestamp.js'
|
|
17
17
|
|
|
18
18
|
/** Re-export the shared fill-position classifier used by Hyperliquid mappings. @public */
|
|
19
19
|
export { classifyFillFromPosition }
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
const mapLiquidationMethod = (
|
|
22
|
+
method: string
|
|
23
|
+
): LiquidationMethod | undefined => {
|
|
24
|
+
switch (method) {
|
|
25
|
+
case 'market':
|
|
26
|
+
return LiquidationMethod.MARKET
|
|
27
|
+
case 'backstop':
|
|
28
|
+
return LiquidationMethod.BACKSTOP
|
|
29
|
+
default:
|
|
30
|
+
return undefined
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Hyperliquid attaches `liquidation` to the fills of both parties.
|
|
35
|
+
const mapFillLiquidation = (
|
|
36
|
+
liquidation: HlFillLiquidation | undefined,
|
|
37
|
+
address: string
|
|
38
|
+
): FillLiquidation | undefined => {
|
|
39
|
+
if (
|
|
40
|
+
liquidation === undefined ||
|
|
41
|
+
liquidation.liquidatedUser.toLowerCase() !== address.toLowerCase()
|
|
42
|
+
) {
|
|
43
|
+
return undefined
|
|
44
|
+
}
|
|
45
|
+
const method = mapLiquidationMethod(liquidation.method)
|
|
46
|
+
return {
|
|
47
|
+
...(method === undefined ? {} : { method }),
|
|
48
|
+
markPrice: liquidation.markPx,
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Maps a fill using the market category; the order type stays absent.
|
|
53
|
+
* Invalid times return `undefined`. The venue does not report the order type.
|
|
54
|
+
* @param address - Fill owner; only the liquidated account carries `liquidation`.
|
|
25
55
|
* @public
|
|
26
56
|
*/
|
|
27
57
|
export const mapFill = (
|
|
28
58
|
fill: HlUserFill,
|
|
29
|
-
market: MarketDisplay
|
|
59
|
+
market: MarketDisplay,
|
|
60
|
+
address: string
|
|
30
61
|
): Fill | undefined => {
|
|
31
62
|
const size = fill.sz
|
|
32
63
|
const createdAt = rowTimestampToIsoStringOrUndefined(fill.time)
|
|
@@ -40,6 +71,7 @@ export const mapFill = (
|
|
|
40
71
|
// HL charges the builder portion in the same token as the total fee.
|
|
41
72
|
const feeAsset = fill.feeToken ?? market.quoteAsset.displaySymbol
|
|
42
73
|
const side = fill.side === 'B' ? OrderSide.BUY : OrderSide.SELL
|
|
74
|
+
const liquidation = mapFillLiquidation(fill.liquidation, address)
|
|
43
75
|
|
|
44
76
|
return {
|
|
45
77
|
...(market.categoryId === SPOT_MARKET_ID
|
|
@@ -50,11 +82,6 @@ export const mapFill = (
|
|
|
50
82
|
clientOrderId: fill.cloid,
|
|
51
83
|
market,
|
|
52
84
|
side,
|
|
53
|
-
// HL fills don't carry the originating order type. A maker fill (crossed:
|
|
54
|
-
// false) can only come from a resting order, so it's necessarily a limit;
|
|
55
|
-
// a taker fill (crossed: true) may be a market OR an aggressive limit order,
|
|
56
|
-
// which the payload can't distinguish, so the type is left undefined.
|
|
57
|
-
type: fill.crossed ? undefined : OrderType.LIMIT,
|
|
58
85
|
size,
|
|
59
86
|
price: fill.px,
|
|
60
87
|
liquidity: fill.crossed ? LiquidityRole.TAKER : LiquidityRole.MAKER,
|
|
@@ -76,5 +103,6 @@ export const mapFill = (
|
|
|
76
103
|
: FillClassification.SPOT_SELL
|
|
77
104
|
: classifyFillFromPosition(fill.startPosition, side, size),
|
|
78
105
|
createdAt,
|
|
106
|
+
...(liquidation === undefined ? {} : { liquidation }),
|
|
79
107
|
}
|
|
80
108
|
}
|
package/src/utils/mapOrder.ts
CHANGED
|
@@ -388,5 +388,11 @@ export const mapOrder = (
|
|
|
388
388
|
default:
|
|
389
389
|
throw venueError(`Unknown Hyperliquid time in force: ${o.tif}`)
|
|
390
390
|
}
|
|
391
|
-
return {
|
|
391
|
+
return {
|
|
392
|
+
...base,
|
|
393
|
+
type,
|
|
394
|
+
price: o.limitPx,
|
|
395
|
+
timeInForce,
|
|
396
|
+
...(o.tif === 'LiquidationMarket' ? { isLiquidation: true } : {}),
|
|
397
|
+
}
|
|
392
398
|
}
|
package/src/utils/mapPosition.ts
CHANGED
|
@@ -43,7 +43,7 @@ export const isOpenAssetPosition = (ap: HlAssetPosition): boolean =>
|
|
|
43
43
|
* Hyperliquid signs funding paid as positive and `accruedFunding` signs it as
|
|
44
44
|
* negative. A row with an invalid size gives `undefined`; an invalid position
|
|
45
45
|
* value, a non-positive leverage or an invalid funding leaves the derived field
|
|
46
|
-
* absent.
|
|
46
|
+
* absent, and a `null` or empty `liquidationPx` leaves `liquidationPrice` absent.
|
|
47
47
|
* @public
|
|
48
48
|
*/
|
|
49
49
|
export const mapPosition = (
|
|
@@ -93,7 +93,7 @@ export const mapPosition = (
|
|
|
93
93
|
size,
|
|
94
94
|
entryPrice: pos.entryPx ?? '0',
|
|
95
95
|
...(markPrice === undefined ? {} : { markPrice }),
|
|
96
|
-
liquidationPrice: pos.liquidationPx
|
|
96
|
+
...(pos.liquidationPx ? { liquidationPrice: pos.liquidationPx } : {}),
|
|
97
97
|
unrealizedPnl: pos.unrealizedPnl,
|
|
98
98
|
...(accruedFunding === undefined ? {} : { accruedFunding }),
|
|
99
99
|
...(leverage === undefined ? {} : { leverage }),
|
|
@@ -1243,7 +1243,7 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
|
|
|
1243
1243
|
return []
|
|
1244
1244
|
}
|
|
1245
1245
|
const market = this.registry?.get(f.coin)
|
|
1246
|
-
const mapped = market && mapFill(f as HlUserFill, market)
|
|
1246
|
+
const mapped = market && mapFill(f as HlUserFill, market, data.user)
|
|
1247
1247
|
return mapped ? [mapped] : []
|
|
1248
1248
|
})
|
|
1249
1249
|
this.emit(`userFills:${data.user.toLowerCase()}`, {
|