@hyperix/hooks 0.2.0 → 0.2.2

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.
@@ -7,3 +7,4 @@ export declare const wsTransport: WebSocketTransport;
7
7
  export declare const wsClient: SubscriptionClient<{
8
8
  transport: WebSocketTransport;
9
9
  }>;
10
+ export declare const HYCORE_USDC_ADDRESS = "0x6d1e7cde53ba9467b783cb7c530ce054";
package/dist/config/hl.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { HttpTransport, InfoClient, SubscriptionClient, WebSocketTransport, } from "@nktkas/hyperliquid";
2
2
  export const transport = new HttpTransport();
3
- export const infoClient = new InfoClient({ transport: transport });
3
+ export const infoClient = new InfoClient({ transport });
4
4
  export const wsTransport = new WebSocketTransport();
5
5
  export const wsClient = new SubscriptionClient({ transport: wsTransport });
6
+ export const HYCORE_USDC_ADDRESS = "0x6d1e7cde53ba9467b783cb7c530ce054";
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export * from "./use-l2-book.js";
6
6
  export * from "./use-max-builder-fee.js";
7
7
  export * from "./use-mid.js";
8
8
  export * from "./use-all-dexs-clearing-house-state.js";
9
+ export * from "./use-balances.js";
9
10
  export * from "./use-historical-orders.js";
10
11
  export * from "./use-open-orders.js";
11
12
  export * from "./use-trade-history.js";
@@ -25,6 +26,7 @@ export * from "./use-subscribe-status.js";
25
26
  export * from "./lib/symbol-converter.js";
26
27
  export * from "./use-symbol-converter.js";
27
28
  export * from "./use-twap-states.js";
29
+ export * from "./use-user-abstraction.js";
28
30
  export * from "./use-user-fundings.js";
29
31
  export * from "./use-user-fills.js";
30
32
  export * from "./use-user-non-funding-ledger-updates.js";
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ export * from "./use-l2-book.js";
6
6
  export * from "./use-max-builder-fee.js";
7
7
  export * from "./use-mid.js";
8
8
  export * from "./use-all-dexs-clearing-house-state.js";
9
+ export * from "./use-balances.js";
9
10
  export * from "./use-historical-orders.js";
10
11
  export * from "./use-open-orders.js";
11
12
  export * from "./use-trade-history.js";
@@ -25,6 +26,7 @@ export * from "./use-subscribe-status.js";
25
26
  export * from "./lib/symbol-converter.js";
26
27
  export * from "./use-symbol-converter.js";
27
28
  export * from "./use-twap-states.js";
29
+ export * from "./use-user-abstraction.js";
28
30
  export * from "./use-user-fundings.js";
29
31
  export * from "./use-user-fills.js";
30
32
  export * from "./use-user-non-funding-ledger-updates.js";
@@ -0,0 +1,60 @@
1
+ import type { ClearinghouseStateResponse } from "@nktkas/hyperliquid/api/info";
2
+ import type { AllDexsClearingHouseStateData } from "../use-all-dexs-clearing-house-state.js";
3
+ import type { PerpMarket } from "../use-perp-markets.js";
4
+ import type { Position } from "../use-positions.js";
5
+ import type { SpotMarket } from "../use-spot-markets.js";
6
+ import type { SpotBalance } from "../use-spot-state.js";
7
+ import type { UserDelegatorSummaryData } from "../use-user-delegator-summary.js";
8
+ import type { UserVaultEquitiesData } from "../use-user-vault-equities.js";
9
+ export type BalanceType = "spot" | "perp" | "all";
10
+ export type Balance = {
11
+ coin: string;
12
+ type: BalanceType;
13
+ dex: string;
14
+ total: number;
15
+ available: number;
16
+ value: number;
17
+ entryNtl?: number;
18
+ pnl?: number;
19
+ contract: string;
20
+ };
21
+ export type StakingInfo = {
22
+ delegatedAmount: number;
23
+ hypePrice: number;
24
+ stakingEquity: number;
25
+ };
26
+ export type PerpsOverview = {
27
+ balance: number;
28
+ unrealizedPNL: number;
29
+ crossMarginRatio: number;
30
+ maintenanceMargin: number;
31
+ crossAccountLeverage: number;
32
+ };
33
+ export type BalancesData = {
34
+ balances: Balance[];
35
+ isUnifiedAccount: boolean;
36
+ tradingEquity: number;
37
+ spotEquity: number;
38
+ perpEquity: number;
39
+ vaultEquity: number;
40
+ stakingInfo: StakingInfo;
41
+ totalEquity: number;
42
+ perpsOverview: PerpsOverview;
43
+ crossMarginAvailable: number;
44
+ };
45
+ export type BuildBalancesDataInput = {
46
+ spotBalances: SpotBalance[];
47
+ spotMarkets: SpotMarket[];
48
+ clearinghouseState?: AllDexsClearingHouseStateData;
49
+ perpMarkets?: PerpMarket[];
50
+ isUnifiedAccount: boolean;
51
+ vaultEquities?: UserVaultEquitiesData;
52
+ delegatorSummary?: UserDelegatorSummaryData;
53
+ positions?: Position[];
54
+ };
55
+ export declare function groupByQuoteCoinMap(data: {
56
+ quoteCoin: string;
57
+ dexName: string;
58
+ }[] | undefined): Map<string, string[]>;
59
+ export declare function coverClearinghouseStates(data: AllDexsClearingHouseStateData["clearinghouseStates"] | undefined): Map<string, ClearinghouseStateResponse>;
60
+ export declare function buildBalancesData(input: BuildBalancesDataInput): BalancesData;
@@ -0,0 +1,208 @@
1
+ import Decimal from "decimal.js";
2
+ import { HYCORE_USDC_ADDRESS } from "../config/hl.js";
3
+ export function groupByQuoteCoinMap(data) {
4
+ const map = new Map();
5
+ if (!data)
6
+ return map;
7
+ for (const item of data) {
8
+ const dexNames = map.get(item.quoteCoin) ?? [];
9
+ dexNames.push(item.dexName);
10
+ map.set(item.quoteCoin, dexNames);
11
+ }
12
+ return map;
13
+ }
14
+ export function coverClearinghouseStates(data) {
15
+ const map = new Map();
16
+ for (const state of data ?? []) {
17
+ const dexName = normalizeDexName(state[0]);
18
+ map.set(dexName, state[1]);
19
+ }
20
+ return map;
21
+ }
22
+ function normalizeDexName(dex) {
23
+ return dex === "" ? "core" : dex;
24
+ }
25
+ function sumBy(items, getValue) {
26
+ return items.reduce((acc, item) => acc + getValue(item), 0);
27
+ }
28
+ function buildPerpDexInfos(perpMarkets) {
29
+ const dexInfos = new Map();
30
+ for (const market of perpMarkets ?? []) {
31
+ const dexName = normalizeDexName(market.dex);
32
+ if (dexInfos.has(dexName))
33
+ continue;
34
+ dexInfos.set(dexName, {
35
+ dexName,
36
+ quoteCoin: market.collateralToken,
37
+ });
38
+ }
39
+ return [...dexInfos.values()];
40
+ }
41
+ function buildSpotBalances(spotBalances, spotMarkets, isUnifiedAccount) {
42
+ return spotBalances.map((spot) => {
43
+ const tokenInfo = spotMarkets.find((market) => market.baseToken === spot.coin);
44
+ const total = new Decimal(spot.total);
45
+ const hold = new Decimal(spot.hold ?? 0);
46
+ const price = new Decimal(tokenInfo?.markPrice ?? 1);
47
+ const value = total.mul(price);
48
+ const entryNtl = new Decimal(spot.entryNtl ?? 0);
49
+ const pnl = value.minus(entryNtl);
50
+ return {
51
+ coin: spot.coin,
52
+ type: isUnifiedAccount ? "all" : "spot",
53
+ dex: "",
54
+ total: total.toNumber(),
55
+ available: total.minus(hold).toNumber(),
56
+ value: value.toNumber(),
57
+ entryNtl: entryNtl.toNumber(),
58
+ pnl: pnl.toNumber(),
59
+ contract: tokenInfo?.tokenId ?? HYCORE_USDC_ADDRESS,
60
+ };
61
+ });
62
+ }
63
+ function buildPerpBalances(clearinghouseState, perpMarkets) {
64
+ const perpBalances = [];
65
+ const clearinghouseStatesMap = coverClearinghouseStates(clearinghouseState?.clearinghouseStates);
66
+ for (const dexInfo of buildPerpDexInfos(perpMarkets)) {
67
+ const dexState = clearinghouseStatesMap.get(dexInfo.dexName);
68
+ if (!dexState)
69
+ continue;
70
+ const totalAccountValue = new Decimal(dexState.marginSummary.accountValue);
71
+ if (totalAccountValue.lte(0))
72
+ continue;
73
+ perpBalances.push({
74
+ coin: dexInfo.quoteCoin,
75
+ type: "perp",
76
+ dex: dexInfo.dexName,
77
+ total: totalAccountValue.toNumber(),
78
+ available: new Decimal(dexState.withdrawable).toNumber(),
79
+ value: totalAccountValue.toNumber(),
80
+ contract: "",
81
+ });
82
+ }
83
+ return perpBalances;
84
+ }
85
+ function buildPerpsOverviewInfo(clearinghouseState, spotBalances, perpMarkets, isUnifiedAccount) {
86
+ if (!clearinghouseState) {
87
+ return undefined;
88
+ }
89
+ let totalCrossMaintenanceMarginUsed = new Decimal(0);
90
+ let totalCrossMarginSummaryValue = new Decimal(0);
91
+ let totalCrossPositionValue = new Decimal(0);
92
+ let totalCrossMarginAvailableValue = new Decimal(0);
93
+ let unifiedAccountRatio = new Decimal(0);
94
+ let unifiedTotalCollateralBalance = new Decimal(0);
95
+ const clearinghouseStatesMap = coverClearinghouseStates(clearinghouseState.clearinghouseStates);
96
+ const spotBalanceByCoin = new Map(spotBalances.map((spot) => [spot.coin, Number(spot.total)]));
97
+ const isolatedMarginByCoin = new Map();
98
+ const collateralCoins = new Set();
99
+ const dexInfos = buildPerpDexInfos(perpMarkets);
100
+ if (isUnifiedAccount) {
101
+ for (const [dexName, state] of clearinghouseStatesMap) {
102
+ const quoteCoin = dexInfos.find((info) => info.dexName === dexName)?.quoteCoin;
103
+ if (!quoteCoin)
104
+ continue;
105
+ collateralCoins.add(quoteCoin);
106
+ for (const assetPosition of state.assetPositions) {
107
+ if (assetPosition.position.leverage.type !== "isolated")
108
+ continue;
109
+ const isolatedMargin = isolatedMarginByCoin.get(quoteCoin) ?? new Decimal(0);
110
+ isolatedMarginByCoin.set(quoteCoin, isolatedMargin.plus(assetPosition.position.marginUsed));
111
+ }
112
+ }
113
+ }
114
+ for (const [dexName, state] of clearinghouseStatesMap) {
115
+ totalCrossMaintenanceMarginUsed = totalCrossMaintenanceMarginUsed.plus(state.crossMaintenanceMarginUsed);
116
+ totalCrossMarginSummaryValue = totalCrossMarginSummaryValue.plus(state.crossMarginSummary.accountValue);
117
+ totalCrossPositionValue = totalCrossPositionValue.plus(state.crossMarginSummary.totalNtlPos);
118
+ if (isUnifiedAccount) {
119
+ const quoteCoin = dexInfos.find((info) => info.dexName === dexName)?.quoteCoin;
120
+ if (quoteCoin) {
121
+ const available = new Decimal(spotBalanceByCoin.get(quoteCoin) ?? 0).minus(isolatedMarginByCoin.get(quoteCoin) ?? 0);
122
+ if (available.gt(0)) {
123
+ const ratio = new Decimal(state.crossMaintenanceMarginUsed).div(available);
124
+ if (ratio.gt(unifiedAccountRatio)) {
125
+ unifiedAccountRatio = ratio;
126
+ }
127
+ }
128
+ }
129
+ }
130
+ if (dexName === "core") {
131
+ totalCrossMarginAvailableValue = totalCrossMarginAvailableValue.plus(new Decimal(state.crossMarginSummary.accountValue).minus(state.crossMaintenanceMarginUsed));
132
+ }
133
+ }
134
+ if (isUnifiedAccount) {
135
+ for (const quoteCoin of collateralCoins) {
136
+ const totalCollateral = new Decimal(spotBalanceByCoin.get(quoteCoin) ?? 0);
137
+ if (totalCollateral.gt(0)) {
138
+ unifiedTotalCollateralBalance =
139
+ unifiedTotalCollateralBalance.plus(totalCollateral);
140
+ }
141
+ }
142
+ }
143
+ return {
144
+ totalCrossMaintenanceMarginUsed,
145
+ totalCrossMarginSummaryValue,
146
+ totalCrossPositionValue,
147
+ totalCrossMarginAvailableValue,
148
+ unifiedAccountRatio,
149
+ unifiedTotalCollateralBalance,
150
+ };
151
+ }
152
+ export function buildBalancesData(input) {
153
+ const spotBalances = buildSpotBalances(input.spotBalances, input.spotMarkets, input.isUnifiedAccount);
154
+ const perpBalances = input.isUnifiedAccount
155
+ ? []
156
+ : buildPerpBalances(input.clearinghouseState, input.perpMarkets);
157
+ const balances = [...perpBalances, ...spotBalances];
158
+ const rawSpotEquity = sumBy(balances.filter((balance) => balance.type === "spot"), (balance) => balance.value);
159
+ const rawPerpEquity = sumBy(balances.filter((balance) => balance.type === "perp"), (balance) => balance.value);
160
+ const tradingEquity = input.isUnifiedAccount
161
+ ? sumBy(balances.filter((balance) => balance.type === "all"), (balance) => balance.value)
162
+ : rawSpotEquity + rawPerpEquity;
163
+ const spotEquity = input.isUnifiedAccount ? tradingEquity : rawSpotEquity;
164
+ const perpEquity = input.isUnifiedAccount ? tradingEquity : rawPerpEquity;
165
+ const vaultEquity = sumBy(input.vaultEquities ?? [], (vault) => Number(vault.equity));
166
+ const delegatedAmount = Number(input.delegatorSummary?.delegated ?? 0);
167
+ const hypePrice = input.spotMarkets.find((market) => market.baseToken === "HYPE")
168
+ ?.markPrice ?? 0;
169
+ const stakingEquity = new Decimal(hypePrice).mul(delegatedAmount).toNumber();
170
+ const totalEquity = tradingEquity + vaultEquity + stakingEquity;
171
+ const totalUnrealizedPNL = sumBy(input.positions ?? [], (position) => Number(position.position.unrealizedPnl));
172
+ const perpsOverviewInfo = buildPerpsOverviewInfo(input.clearinghouseState, input.spotBalances, input.perpMarkets, input.isUnifiedAccount);
173
+ const perpsOverview = {
174
+ balance: new Decimal(perpEquity).minus(totalUnrealizedPNL).toNumber(),
175
+ unrealizedPNL: totalUnrealizedPNL,
176
+ crossMarginRatio: input.isUnifiedAccount
177
+ ? (perpsOverviewInfo?.unifiedAccountRatio.toNumber() ?? 0)
178
+ : perpEquity
179
+ ? (perpsOverviewInfo?.totalCrossMaintenanceMarginUsed
180
+ .div(perpEquity)
181
+ .toNumber() ?? 0)
182
+ : 0,
183
+ maintenanceMargin: perpsOverviewInfo?.totalCrossMaintenanceMarginUsed.toNumber() ?? 0,
184
+ crossAccountLeverage: input.isUnifiedAccount
185
+ ? perpsOverviewInfo?.unifiedTotalCollateralBalance.gt(0)
186
+ ? (perpsOverviewInfo?.totalCrossPositionValue
187
+ .div(perpsOverviewInfo.unifiedTotalCollateralBalance)
188
+ .toNumber() ?? 0)
189
+ : 0
190
+ : perpEquity
191
+ ? (perpsOverviewInfo?.totalCrossPositionValue
192
+ .div(perpEquity)
193
+ .toNumber() ?? 0)
194
+ : 0,
195
+ };
196
+ return {
197
+ balances,
198
+ isUnifiedAccount: input.isUnifiedAccount,
199
+ tradingEquity,
200
+ spotEquity,
201
+ perpEquity,
202
+ vaultEquity,
203
+ stakingInfo: { delegatedAmount, hypePrice, stakingEquity },
204
+ totalEquity,
205
+ perpsOverview,
206
+ crossMarginAvailable: perpsOverviewInfo?.totalCrossMarginAvailableValue.toNumber() ?? 0,
207
+ };
208
+ }
@@ -0,0 +1,7 @@
1
+ import type { UseSubscribeState } from "@outofgas/react-stream";
2
+ import { type Balance, type BalanceType, type BalancesData, type PerpsOverview, type StakingInfo } from "./lib/balances.js";
3
+ export type { Balance, BalancesData, BalanceType, PerpsOverview, StakingInfo };
4
+ export type UseBalancesOptions = {
5
+ enabled?: boolean;
6
+ };
7
+ export declare function useBalances(user: `0x${string}`, options?: UseBalancesOptions): UseSubscribeState<BalancesData>;
@@ -0,0 +1,69 @@
1
+ import { useMemo } from "react";
2
+ import { buildBalancesData, } from "./lib/balances.js";
3
+ import { useAllDexsClearingHouseState } from "./use-all-dexs-clearing-house-state.js";
4
+ import { usePerpMarkets } from "./use-perp-markets.js";
5
+ import { usePositions } from "./use-positions.js";
6
+ import { useSpotMarkets } from "./use-spot-markets.js";
7
+ import { useSpotState } from "./use-spot-state.js";
8
+ import { useUserAbstraction } from "./use-user-abstraction.js";
9
+ import { useUserDelegatorSummary } from "./use-user-delegator-summary.js";
10
+ import { useUserVaultEquities } from "./use-user-vault-equities.js";
11
+ function firstError(...errors) {
12
+ const error = errors.find(Boolean);
13
+ if (!error)
14
+ return undefined;
15
+ return error instanceof Error ? error.message : error;
16
+ }
17
+ export function useBalances(user, options = {}) {
18
+ const enabled = options.enabled ?? true;
19
+ const spotMarketsState = useSpotMarkets({ enabled });
20
+ const clearinghouseState = useAllDexsClearingHouseState(user, { enabled });
21
+ const spotState = useSpotState(user, { enabled });
22
+ const perpMarketsState = usePerpMarkets({ enabled });
23
+ const vaultEquitiesState = useUserVaultEquities(user, { enabled });
24
+ const delegatorSummaryState = useUserDelegatorSummary(user, { enabled });
25
+ const userAbstractionState = useUserAbstraction(user, { enabled });
26
+ const positionsState = usePositions(user, { enabled });
27
+ const data = useMemo(() => {
28
+ if (!enabled ||
29
+ !spotState.data ||
30
+ !spotMarketsState.data ||
31
+ !userAbstractionState.data) {
32
+ return undefined;
33
+ }
34
+ return buildBalancesData({
35
+ spotBalances: spotState.data.balances,
36
+ spotMarkets: spotMarketsState.data,
37
+ clearinghouseState: clearinghouseState.data,
38
+ perpMarkets: perpMarketsState.data,
39
+ isUnifiedAccount: userAbstractionState.data === "unifiedAccount",
40
+ vaultEquities: vaultEquitiesState.data,
41
+ delegatorSummary: delegatorSummaryState.data,
42
+ positions: positionsState.data,
43
+ });
44
+ }, [
45
+ clearinghouseState.data,
46
+ delegatorSummaryState.data,
47
+ enabled,
48
+ perpMarketsState.data,
49
+ positionsState.data,
50
+ spotState.data,
51
+ spotMarketsState.data,
52
+ userAbstractionState.data,
53
+ vaultEquitiesState.data,
54
+ ]);
55
+ return {
56
+ data,
57
+ ready: Boolean(data),
58
+ loading: enabled &&
59
+ (spotMarketsState.loading ||
60
+ clearinghouseState.loading ||
61
+ spotState.loading ||
62
+ perpMarketsState.loading ||
63
+ vaultEquitiesState.isLoading ||
64
+ delegatorSummaryState.isLoading ||
65
+ userAbstractionState.isLoading ||
66
+ positionsState.loading),
67
+ error: firstError(spotMarketsState.error, clearinghouseState.error, spotState.error, perpMarketsState.error, vaultEquitiesState.error, delegatorSummaryState.error, userAbstractionState.error, positionsState.error),
68
+ };
69
+ }
@@ -13,7 +13,7 @@ export type L2Book = {
13
13
  };
14
14
  export type UseL2BookOptions = {
15
15
  depth?: number;
16
- nSigFigs?: number;
16
+ nSigFigs?: 2 | 3 | 4 | 5 | null;
17
17
  };
18
18
  export declare const EMPTY_L2_BOOK: L2Book;
19
19
  export declare function calcOrderbookLevels(price: number): {
@@ -1,4 +1,13 @@
1
1
  import type { UseSubscribeState } from "@outofgas/react-stream";
2
2
  import { type AllDexsClearingHouseStateData, type UseAllDexsClearingHouseStateOptions } from "./use-all-dexs-clearing-house-state.js";
3
- export type Position = AllDexsClearingHouseStateData["clearinghouseStates"][number][1]["assetPositions"][number];
3
+ import type { OpenOrder } from "./use-open-orders.js";
4
+ export type RawPosition = AllDexsClearingHouseStateData["clearinghouseStates"][number][1]["assetPositions"][number];
5
+ export type Position = RawPosition & {
6
+ markPrice: number | undefined;
7
+ fundingSinceOpen: number;
8
+ side: "Long" | "Short" | "Flat";
9
+ takeProfitTriggerPx: string | undefined;
10
+ stopLossTriggerPx: string | undefined;
11
+ triggerOrders: OpenOrder[];
12
+ };
4
13
  export declare function usePositions(user: `0x${string}`, options?: UseAllDexsClearingHouseStateOptions): UseSubscribeState<Position[]>;
@@ -1,17 +1,96 @@
1
1
  import { useMemo } from "react";
2
+ import { useAllDexsAssetCtxs } from "./use-all-dexs-asset-ctxs.js";
2
3
  import { useAllDexsClearingHouseState, } from "./use-all-dexs-clearing-house-state.js";
4
+ import { useOpenOrders } from "./use-open-orders.js";
5
+ import { useSymbolConverter } from "./use-symbol-converter.js";
6
+ function getSide(size) {
7
+ const numericSize = Number(size);
8
+ if (numericSize > 0) {
9
+ return "Long";
10
+ }
11
+ if (numericSize < 0) {
12
+ return "Short";
13
+ }
14
+ return "Flat";
15
+ }
16
+ function getPerpMarkPrice(coin, assetCtxs, symbolConverter) {
17
+ if (!assetCtxs || !symbolConverter) {
18
+ return undefined;
19
+ }
20
+ const assetId = symbolConverter.getAssetId(coin);
21
+ if (assetId === undefined) {
22
+ return undefined;
23
+ }
24
+ const dex = coin.includes(":") ? (coin.split(":")[0] ?? "") : "";
25
+ const index = assetId % 10_000;
26
+ const ctxs = assetCtxs.ctxs.find(([ctxDex]) => ctxDex === dex)?.[1];
27
+ return ctxs?.[index] ? Number(ctxs[index].markPx) : undefined;
28
+ }
29
+ function getTriggerOrdersByCoin(orders) {
30
+ return orders.reduce((map, order) => {
31
+ if (!order.isTrigger || !order.triggerCondition) {
32
+ return map;
33
+ }
34
+ const current = map.get(order.coin) ?? [];
35
+ current.push(order);
36
+ map.set(order.coin, current);
37
+ return map;
38
+ }, new Map());
39
+ }
40
+ function getTpSl(orders) {
41
+ let takeProfitTriggerPx;
42
+ let stopLossTriggerPx;
43
+ for (const order of orders ?? []) {
44
+ if (order.orderType.includes("Take Profit")) {
45
+ takeProfitTriggerPx = order.triggerPx;
46
+ continue;
47
+ }
48
+ stopLossTriggerPx = order.triggerPx;
49
+ }
50
+ return {
51
+ takeProfitTriggerPx,
52
+ stopLossTriggerPx,
53
+ triggerOrders: orders ?? [],
54
+ };
55
+ }
3
56
  export function usePositions(user, options = {}) {
57
+ const symbolConverter = useSymbolConverter();
4
58
  const positionsState = useAllDexsClearingHouseState(user, options);
59
+ const assetCtxsState = useAllDexsAssetCtxs({ enabled: options.enabled });
60
+ const openOrdersState = useOpenOrders(user, { enabled: options.enabled });
5
61
  const data = useMemo(() => {
6
- return positionsState.data?.clearinghouseStates
62
+ const rawPositions = positionsState.data?.clearinghouseStates
7
63
  .flatMap(([, state]) => state.assetPositions)
8
64
  .sort((left, right) => Number(left.position.positionValue) <
9
65
  Number(right.position.positionValue)
10
66
  ? 1
11
67
  : -1);
12
- }, [positionsState.data]);
68
+ const triggerOrdersByCoin = getTriggerOrdersByCoin(openOrdersState.data?.orders ?? []);
69
+ return rawPositions?.map((positionRecord) => {
70
+ const position = positionRecord.position;
71
+ const tpSl = getTpSl(triggerOrdersByCoin.get(position.coin));
72
+ return {
73
+ ...positionRecord,
74
+ markPrice: getPerpMarkPrice(position.coin, assetCtxsState.data, symbolConverter),
75
+ fundingSinceOpen: -Number(position.cumFunding.sinceOpen),
76
+ side: getSide(position.szi),
77
+ ...tpSl,
78
+ };
79
+ });
80
+ }, [
81
+ positionsState.data,
82
+ openOrdersState.data,
83
+ assetCtxsState.data,
84
+ symbolConverter,
85
+ ]);
13
86
  return {
14
87
  ...positionsState,
88
+ loading: positionsState.loading || assetCtxsState.loading || openOrdersState.loading,
89
+ ready: positionsState.ready &&
90
+ assetCtxsState.ready &&
91
+ openOrdersState.ready &&
92
+ symbolConverter !== null,
93
+ error: positionsState.error ?? assetCtxsState.error ?? openOrdersState.error,
15
94
  data,
16
95
  };
17
96
  }
@@ -0,0 +1,8 @@
1
+ import type { UserAbstractionResponse } from "@nktkas/hyperliquid/api/info";
2
+ import { type UseQueryOptions, type UseQueryResult } from "@tanstack/react-query";
3
+ export type UserAbstractionData = UserAbstractionResponse;
4
+ export type UseUserAbstractionOptions = Omit<UseQueryOptions<UserAbstractionData, Error, UserAbstractionData, [
5
+ "userAbstraction",
6
+ `0x${string}`
7
+ ]>, "queryKey" | "queryFn">;
8
+ export declare function useUserAbstraction(user: `0x${string}`, options?: UseUserAbstractionOptions): UseQueryResult<UserAbstractionData, Error>;
@@ -0,0 +1,11 @@
1
+ import { useQuery, } from "@tanstack/react-query";
2
+ import { infoClient } from "./config/hl.js";
3
+ export function useUserAbstraction(user, options = {}) {
4
+ const enabled = options.enabled ?? Boolean(user);
5
+ return useQuery({
6
+ queryKey: ["userAbstraction", user],
7
+ queryFn: () => infoClient.userAbstraction({ user }),
8
+ ...options,
9
+ enabled,
10
+ });
11
+ }
@@ -91,6 +91,7 @@ function getAction(entry) {
91
91
  case "activateDexAbstraction":
92
92
  return "Activate";
93
93
  }
94
+ return "Unknown";
94
95
  }
95
96
  function formatDexLabel(dex) {
96
97
  if (dex === "spot")
@@ -140,6 +141,7 @@ function getSource(entry) {
140
141
  case "activateDexAbstraction":
141
142
  return "Wallet";
142
143
  }
144
+ return "--";
143
145
  }
144
146
  function getDestination(entry) {
145
147
  switch (entry.delta.type) {
@@ -182,6 +184,16 @@ function getDestination(entry) {
182
184
  case "activateDexAbstraction":
183
185
  return entry.delta.dex;
184
186
  }
187
+ return "--";
188
+ }
189
+ function getUnknownAmount() {
190
+ return {
191
+ value: null,
192
+ numericValue: null,
193
+ token: null,
194
+ direction: "neutral",
195
+ displayValue: "--",
196
+ };
185
197
  }
186
198
  function getAmount(entry, user) {
187
199
  switch (entry.delta.type) {
@@ -365,6 +377,7 @@ function getAmount(entry, user) {
365
377
  displayValue: formatDisplayValue(entry.delta.amount, entry.delta.token, "out"),
366
378
  };
367
379
  }
380
+ return getUnknownAmount();
368
381
  }
369
382
  function getFee(entry) {
370
383
  switch (entry.delta.type) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperix/hooks",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -17,6 +17,7 @@
17
17
  "@tanstack/react-query": "^5.90.2",
18
18
  "@nktkas/hyperliquid": "^0.32.1",
19
19
  "@outofgas/react-stream": "^0.1.4",
20
+ "decimal.js": "^10.6.0",
20
21
  "react": "^19.1.1"
21
22
  },
22
23
  "devDependencies": {