@hyperix/hooks 0.1.8 → 0.1.10
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/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/use-historical-orders.d.ts +12 -0
- package/dist/use-historical-orders.js +42 -0
- package/dist/use-order-history.d.ts +16 -0
- package/dist/use-order-history.js +37 -0
- package/dist/use-user-account-activity.d.ts +36 -0
- package/dist/use-user-account-activity.js +490 -0
- package/dist/use-user-non-funding-ledger-updates.d.ts +12 -0
- package/dist/use-user-non-funding-ledger-updates.js +42 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export * from "./use-l2-book.js";
|
|
2
|
+
export * from "./use-historical-orders.js";
|
|
2
3
|
export * from "./use-trade-history.js";
|
|
3
4
|
export * from "./use-trades.js";
|
|
5
|
+
export * from "./use-order-history.js";
|
|
4
6
|
export * from "./use-user-fundings.js";
|
|
5
7
|
export * from "./use-user-fills.js";
|
|
8
|
+
export * from "./use-user-non-funding-ledger-updates.js";
|
|
9
|
+
export * from "./use-user-account-activity.js";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export * from "./use-l2-book.js";
|
|
2
|
+
export * from "./use-historical-orders.js";
|
|
2
3
|
export * from "./use-trade-history.js";
|
|
3
4
|
export * from "./use-trades.js";
|
|
5
|
+
export * from "./use-order-history.js";
|
|
4
6
|
export * from "./use-user-fundings.js";
|
|
5
7
|
export * from "./use-user-fills.js";
|
|
8
|
+
export * from "./use-user-non-funding-ledger-updates.js";
|
|
9
|
+
export * from "./use-user-account-activity.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type UseSubscribeState } from "@outofgas/react-stream";
|
|
2
|
+
import type { UserHistoricalOrdersEvent } from "@nktkas/hyperliquid/api/subscription";
|
|
3
|
+
export type HistoricalOrder = UserHistoricalOrdersEvent["orderHistory"][number];
|
|
4
|
+
export type HistoricalOrdersData = {
|
|
5
|
+
user: `0x${string}`;
|
|
6
|
+
orderHistory: HistoricalOrder[];
|
|
7
|
+
};
|
|
8
|
+
export type UseHistoricalOrdersOptions = {
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
onUpdate?: (event: UserHistoricalOrdersEvent) => void;
|
|
11
|
+
};
|
|
12
|
+
export declare function useHistoricalOrders(user: `0x${string}`, options?: UseHistoricalOrdersOptions): UseSubscribeState<HistoricalOrdersData>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useSubscribe } from "@outofgas/react-stream";
|
|
2
|
+
import { wsClient } from "./config/hl.js";
|
|
3
|
+
function sortHistoricalOrders(orderHistory) {
|
|
4
|
+
return [...orderHistory].sort((a, b) => b.statusTimestamp - a.statusTimestamp);
|
|
5
|
+
}
|
|
6
|
+
function mergeHistoricalOrders(previousData, incomingEvent) {
|
|
7
|
+
const orderHistory = incomingEvent.isSnapshot
|
|
8
|
+
? incomingEvent.orderHistory
|
|
9
|
+
: [...(previousData?.orderHistory ?? []), ...incomingEvent.orderHistory];
|
|
10
|
+
return {
|
|
11
|
+
user: incomingEvent.user,
|
|
12
|
+
orderHistory: sortHistoricalOrders(orderHistory),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function useHistoricalOrders(user, options = {}) {
|
|
16
|
+
const { enabled: enabledOverride, onUpdate } = options;
|
|
17
|
+
const enabled = enabledOverride ?? Boolean(user);
|
|
18
|
+
return useSubscribe({
|
|
19
|
+
key: ["historical-orders", user],
|
|
20
|
+
enabled,
|
|
21
|
+
subscribe: async ({ onData, onError }) => {
|
|
22
|
+
let data;
|
|
23
|
+
const subscription = await wsClient.userHistoricalOrders({ user }, (event) => {
|
|
24
|
+
try {
|
|
25
|
+
if (!event.isSnapshot) {
|
|
26
|
+
onUpdate?.(event);
|
|
27
|
+
}
|
|
28
|
+
data = mergeHistoricalOrders(data, event);
|
|
29
|
+
onData(data);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
onError(error instanceof Error
|
|
33
|
+
? error
|
|
34
|
+
: new Error("Failed to process user historical orders event"));
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
unsubscribe: () => subscription.unsubscribe(),
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type HistoricalOrder, type UseHistoricalOrdersOptions } from "./use-historical-orders.js";
|
|
2
|
+
export type OrderHistoryDirection = "Buy" | "Sell" | "Long" | "Short" | "Close Long" | "Close Short";
|
|
3
|
+
export type OrderHistory = HistoricalOrder & {
|
|
4
|
+
displayCoin: string;
|
|
5
|
+
direction: OrderHistoryDirection;
|
|
6
|
+
};
|
|
7
|
+
export type OrderHistoryData = {
|
|
8
|
+
user: `0x${string}`;
|
|
9
|
+
orderHistory: OrderHistory[];
|
|
10
|
+
};
|
|
11
|
+
export declare function useOrderHistory(user: `0x${string}`, options?: UseHistoricalOrdersOptions): {
|
|
12
|
+
data: OrderHistoryData | undefined;
|
|
13
|
+
ready: boolean;
|
|
14
|
+
loading: boolean;
|
|
15
|
+
error?: string;
|
|
16
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { useSymbolConverter } from "./use-symbol-converter.js";
|
|
3
|
+
import { useHistoricalOrders, } from "./use-historical-orders.js";
|
|
4
|
+
function formatOrderDirection(order, isSpot) {
|
|
5
|
+
if (order.reduceOnly) {
|
|
6
|
+
return order.side === "A" ? "Close Long" : "Close Short";
|
|
7
|
+
}
|
|
8
|
+
if (order.side === "B") {
|
|
9
|
+
return isSpot ? "Buy" : "Long";
|
|
10
|
+
}
|
|
11
|
+
return isSpot ? "Sell" : "Short";
|
|
12
|
+
}
|
|
13
|
+
function formatOrderHistoryEntry(historicalOrder, displayCoin) {
|
|
14
|
+
const isSpot = Boolean(displayCoin);
|
|
15
|
+
return {
|
|
16
|
+
...historicalOrder,
|
|
17
|
+
displayCoin: displayCoin ?? historicalOrder.order.coin,
|
|
18
|
+
direction: formatOrderDirection(historicalOrder.order, isSpot),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function useOrderHistory(user, options = {}) {
|
|
22
|
+
const historicalOrdersState = useHistoricalOrders(user, options);
|
|
23
|
+
const symbolConverter = useSymbolConverter();
|
|
24
|
+
const data = useMemo(() => {
|
|
25
|
+
if (!historicalOrdersState.data) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
...historicalOrdersState.data,
|
|
30
|
+
orderHistory: historicalOrdersState.data.orderHistory.map((historicalOrder) => formatOrderHistoryEntry(historicalOrder, symbolConverter?.getSpotByPairId(historicalOrder.order.coin))),
|
|
31
|
+
};
|
|
32
|
+
}, [historicalOrdersState.data, symbolConverter]);
|
|
33
|
+
return {
|
|
34
|
+
...historicalOrdersState,
|
|
35
|
+
data,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type UseUserNonFundingLedgerUpdatesOptions, type UserNonFundingLedgerUpdate } from "./use-user-non-funding-ledger-updates.js";
|
|
2
|
+
export type UserAccountActivityStatus = "Completed" | "Triggered";
|
|
3
|
+
export type UserAccountActivityDirection = "in" | "out" | "neutral";
|
|
4
|
+
export type UserAccountActivityAmount = {
|
|
5
|
+
value: string | null;
|
|
6
|
+
numericValue: number | null;
|
|
7
|
+
token: string | null;
|
|
8
|
+
displayValue: string;
|
|
9
|
+
direction: UserAccountActivityDirection;
|
|
10
|
+
};
|
|
11
|
+
export type UserAccountActivityFee = {
|
|
12
|
+
value: string | null;
|
|
13
|
+
numericValue: number | null;
|
|
14
|
+
token: string | null;
|
|
15
|
+
displayValue: string;
|
|
16
|
+
};
|
|
17
|
+
export type UserAccountActivity = UserNonFundingLedgerUpdate & {
|
|
18
|
+
status: UserAccountActivityStatus;
|
|
19
|
+
action: string;
|
|
20
|
+
source: string;
|
|
21
|
+
destination: string;
|
|
22
|
+
explorerUrl: string;
|
|
23
|
+
amount: UserAccountActivityAmount;
|
|
24
|
+
fee: UserAccountActivityFee;
|
|
25
|
+
};
|
|
26
|
+
export type UserAccountActivityData = {
|
|
27
|
+
user: `0x${string}`;
|
|
28
|
+
activity: UserAccountActivity[];
|
|
29
|
+
nonFundingLedgerUpdates: UserNonFundingLedgerUpdate[];
|
|
30
|
+
};
|
|
31
|
+
export declare function useUserAccountActivity(user: `0x${string}`, options?: UseUserNonFundingLedgerUpdatesOptions): {
|
|
32
|
+
data: UserAccountActivityData | undefined;
|
|
33
|
+
ready: boolean;
|
|
34
|
+
loading: boolean;
|
|
35
|
+
error?: string;
|
|
36
|
+
};
|
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { useUserNonFundingLedgerUpdates, } from "./use-user-non-funding-ledger-updates.js";
|
|
3
|
+
function isSameAddress(left, right) {
|
|
4
|
+
return Boolean(left && right && left.toLowerCase() === right.toLowerCase());
|
|
5
|
+
}
|
|
6
|
+
function isSystemAddress(address) {
|
|
7
|
+
const normalized = address.toLowerCase();
|
|
8
|
+
const hypeAddress = "0x2222222222222222222222222222222222222222";
|
|
9
|
+
if (normalized === hypeAddress) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
if (!/^0x[0-9a-f]{40}$/.test(normalized)) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
const bytes = normalized.slice(2);
|
|
16
|
+
if (!bytes.startsWith("20")) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return bytes.slice(2, 38) === "0".repeat(36);
|
|
20
|
+
}
|
|
21
|
+
function formatDisplayValue(value, token, direction = "neutral") {
|
|
22
|
+
if (!value || Number(value) === 0) {
|
|
23
|
+
return "--";
|
|
24
|
+
}
|
|
25
|
+
const prefix = direction === "in" ? "+" : direction === "out" ? "-" : "";
|
|
26
|
+
return `${prefix}${value} ${token ?? ""}`.trim();
|
|
27
|
+
}
|
|
28
|
+
function getExplorerUrl(entry) {
|
|
29
|
+
return entry.delta.type === "deposit" || entry.delta.type === "withdraw"
|
|
30
|
+
? `https://arbiscan.io/tx/${entry.hash}`
|
|
31
|
+
: `https://app.hyperliquid.xyz/explorer/tx/${entry.hash}`;
|
|
32
|
+
}
|
|
33
|
+
function getStatus(entry) {
|
|
34
|
+
return entry.delta.type === "liquidation" ? "Triggered" : "Completed";
|
|
35
|
+
}
|
|
36
|
+
function getAction(entry) {
|
|
37
|
+
switch (entry.delta.type) {
|
|
38
|
+
case "deposit":
|
|
39
|
+
return "Deposit";
|
|
40
|
+
case "withdraw":
|
|
41
|
+
return "Withdrawal";
|
|
42
|
+
case "internalTransfer":
|
|
43
|
+
return "Send";
|
|
44
|
+
case "spotTransfer":
|
|
45
|
+
return isSystemAddress(entry.delta.destination) || isSystemAddress(entry.delta.user)
|
|
46
|
+
? "Transfer"
|
|
47
|
+
: "Send";
|
|
48
|
+
case "send":
|
|
49
|
+
if (isSystemAddress(entry.delta.destination) || isSystemAddress(entry.delta.user)) {
|
|
50
|
+
return "Transfer";
|
|
51
|
+
}
|
|
52
|
+
if (entry.delta.sourceDex === "spot" && entry.delta.destinationDex === "spot") {
|
|
53
|
+
return "Send";
|
|
54
|
+
}
|
|
55
|
+
if (entry.delta.sourceDex === "spot" || entry.delta.destinationDex === "spot") {
|
|
56
|
+
return "Transfer";
|
|
57
|
+
}
|
|
58
|
+
return "Send";
|
|
59
|
+
case "accountClassTransfer":
|
|
60
|
+
case "subAccountTransfer":
|
|
61
|
+
case "cStakingTransfer":
|
|
62
|
+
return "Transfer";
|
|
63
|
+
case "vaultCreate":
|
|
64
|
+
return "Vault Create";
|
|
65
|
+
case "vaultDeposit":
|
|
66
|
+
return "Vault Deposit";
|
|
67
|
+
case "vaultDistribution":
|
|
68
|
+
return "Vault Distribution";
|
|
69
|
+
case "vaultWithdraw":
|
|
70
|
+
return "Vault Withdraw";
|
|
71
|
+
case "liquidation":
|
|
72
|
+
return "Liquidation";
|
|
73
|
+
case "rewardsClaim":
|
|
74
|
+
return "Claim";
|
|
75
|
+
case "deployGasAuction":
|
|
76
|
+
return "Gas Auction";
|
|
77
|
+
case "borrowLend":
|
|
78
|
+
return entry.delta.operation === "supply"
|
|
79
|
+
? "Supply"
|
|
80
|
+
: entry.delta.operation === "withdraw"
|
|
81
|
+
? "Withdraw"
|
|
82
|
+
: entry.delta.operation === "borrow"
|
|
83
|
+
? "Borrow"
|
|
84
|
+
: "Repay";
|
|
85
|
+
case "spotGenesis":
|
|
86
|
+
return "Genesis";
|
|
87
|
+
case "activateDexAbstraction":
|
|
88
|
+
return "Activate";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function formatDexLabel(dex) {
|
|
92
|
+
if (dex === "spot")
|
|
93
|
+
return "Spot";
|
|
94
|
+
if (dex === "" || dex === "perp")
|
|
95
|
+
return "Perps";
|
|
96
|
+
return `Perps (${dex})`;
|
|
97
|
+
}
|
|
98
|
+
function getSource(entry) {
|
|
99
|
+
switch (entry.delta.type) {
|
|
100
|
+
case "deposit":
|
|
101
|
+
return "Arbitrum";
|
|
102
|
+
case "withdraw":
|
|
103
|
+
return "Perps";
|
|
104
|
+
case "accountClassTransfer":
|
|
105
|
+
return entry.delta.toPerp ? "Spot" : "Perps";
|
|
106
|
+
case "spotTransfer":
|
|
107
|
+
return isSystemAddress(entry.delta.user) ? "HyperEVM" : "Spot";
|
|
108
|
+
case "subAccountTransfer":
|
|
109
|
+
return "Perps";
|
|
110
|
+
case "send":
|
|
111
|
+
return isSystemAddress(entry.delta.user)
|
|
112
|
+
? "HyperEVM"
|
|
113
|
+
: formatDexLabel(entry.delta.sourceDex);
|
|
114
|
+
case "internalTransfer":
|
|
115
|
+
return "Perps";
|
|
116
|
+
case "cStakingTransfer":
|
|
117
|
+
return entry.delta.isDeposit ? "Spot" : "Staking";
|
|
118
|
+
case "vaultCreate":
|
|
119
|
+
return "Perps";
|
|
120
|
+
case "vaultDeposit":
|
|
121
|
+
return "Wallet";
|
|
122
|
+
case "vaultDistribution":
|
|
123
|
+
return "Vault";
|
|
124
|
+
case "vaultWithdraw":
|
|
125
|
+
return "Vault";
|
|
126
|
+
case "rewardsClaim":
|
|
127
|
+
return "Rewards";
|
|
128
|
+
case "liquidation":
|
|
129
|
+
return entry.delta.leverageType;
|
|
130
|
+
case "deployGasAuction":
|
|
131
|
+
return "Wallet";
|
|
132
|
+
case "borrowLend":
|
|
133
|
+
return "Borrow/Lend";
|
|
134
|
+
case "spotGenesis":
|
|
135
|
+
return "Genesis";
|
|
136
|
+
case "activateDexAbstraction":
|
|
137
|
+
return "Wallet";
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function getDestination(entry) {
|
|
141
|
+
switch (entry.delta.type) {
|
|
142
|
+
case "deposit":
|
|
143
|
+
return "Perps";
|
|
144
|
+
case "withdraw":
|
|
145
|
+
return "Arbitrum";
|
|
146
|
+
case "accountClassTransfer":
|
|
147
|
+
return entry.delta.toPerp ? "Perps" : "Spot";
|
|
148
|
+
case "spotTransfer":
|
|
149
|
+
return isSystemAddress(entry.delta.destination) ? "HyperEVM" : "Spot";
|
|
150
|
+
case "subAccountTransfer":
|
|
151
|
+
return "Perps";
|
|
152
|
+
case "send":
|
|
153
|
+
return isSystemAddress(entry.delta.destination) ? "HyperEVM" : formatDexLabel(entry.delta.destinationDex);
|
|
154
|
+
case "internalTransfer":
|
|
155
|
+
return "Perps";
|
|
156
|
+
case "cStakingTransfer":
|
|
157
|
+
return entry.delta.isDeposit ? "Staking" : "Spot";
|
|
158
|
+
case "vaultCreate":
|
|
159
|
+
return "Vault";
|
|
160
|
+
case "vaultDeposit":
|
|
161
|
+
return "Vault";
|
|
162
|
+
case "vaultDistribution":
|
|
163
|
+
return "Wallet";
|
|
164
|
+
case "vaultWithdraw":
|
|
165
|
+
return "Wallet";
|
|
166
|
+
case "rewardsClaim":
|
|
167
|
+
return entry.delta.token;
|
|
168
|
+
case "liquidation":
|
|
169
|
+
return "System";
|
|
170
|
+
case "deployGasAuction":
|
|
171
|
+
return entry.delta.token;
|
|
172
|
+
case "borrowLend":
|
|
173
|
+
return entry.delta.token;
|
|
174
|
+
case "spotGenesis":
|
|
175
|
+
return entry.delta.token;
|
|
176
|
+
case "activateDexAbstraction":
|
|
177
|
+
return entry.delta.dex;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function getAmount(entry, user) {
|
|
181
|
+
switch (entry.delta.type) {
|
|
182
|
+
case "deposit":
|
|
183
|
+
return {
|
|
184
|
+
value: entry.delta.usdc,
|
|
185
|
+
numericValue: Number(entry.delta.usdc),
|
|
186
|
+
token: "USDC",
|
|
187
|
+
direction: "in",
|
|
188
|
+
displayValue: formatDisplayValue(entry.delta.usdc, "USDC", "in"),
|
|
189
|
+
};
|
|
190
|
+
case "withdraw":
|
|
191
|
+
return {
|
|
192
|
+
value: entry.delta.usdc,
|
|
193
|
+
numericValue: -Number(entry.delta.usdc),
|
|
194
|
+
token: "USDC",
|
|
195
|
+
direction: "out",
|
|
196
|
+
displayValue: formatDisplayValue(entry.delta.usdc, "USDC", "out"),
|
|
197
|
+
};
|
|
198
|
+
case "accountClassTransfer": {
|
|
199
|
+
const direction = entry.delta.toPerp ? "in" : "out";
|
|
200
|
+
const numericValue = Number(entry.delta.usdc) * (direction === "in" ? 1 : -1);
|
|
201
|
+
return {
|
|
202
|
+
value: entry.delta.usdc,
|
|
203
|
+
numericValue,
|
|
204
|
+
token: "USDC",
|
|
205
|
+
direction,
|
|
206
|
+
displayValue: formatDisplayValue(entry.delta.usdc, "USDC", direction),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
case "internalTransfer": {
|
|
210
|
+
const isInbound = isSameAddress(entry.delta.destination, user);
|
|
211
|
+
const direction = isInbound ? "in" : "out";
|
|
212
|
+
const gross = Number(entry.delta.usdc);
|
|
213
|
+
const numericValue = isInbound
|
|
214
|
+
? gross
|
|
215
|
+
: -(gross - Number(entry.delta.fee || "0"));
|
|
216
|
+
return {
|
|
217
|
+
value: isInbound
|
|
218
|
+
? entry.delta.usdc
|
|
219
|
+
: (gross - Number(entry.delta.fee || "0")).toString(),
|
|
220
|
+
numericValue,
|
|
221
|
+
token: "USDC",
|
|
222
|
+
direction,
|
|
223
|
+
displayValue: formatDisplayValue(isInbound
|
|
224
|
+
? entry.delta.usdc
|
|
225
|
+
: (gross - Number(entry.delta.fee || "0")).toString(), "USDC", direction),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
case "spotTransfer": {
|
|
229
|
+
const isInbound = isSameAddress(entry.delta.destination, user);
|
|
230
|
+
const direction = isInbound ? "in" : "out";
|
|
231
|
+
const value = entry.delta.usdcValue;
|
|
232
|
+
return {
|
|
233
|
+
value,
|
|
234
|
+
numericValue: Number(value) * (isInbound ? 1 : -1),
|
|
235
|
+
token: "USDC",
|
|
236
|
+
direction,
|
|
237
|
+
displayValue: formatDisplayValue(value, "USDC", direction),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
case "subAccountTransfer": {
|
|
241
|
+
const isInbound = isSameAddress(entry.delta.destination, user);
|
|
242
|
+
const direction = isInbound ? "in" : "out";
|
|
243
|
+
return {
|
|
244
|
+
value: entry.delta.usdc,
|
|
245
|
+
numericValue: Number(entry.delta.usdc) * (isInbound ? 1 : -1),
|
|
246
|
+
token: "USDC",
|
|
247
|
+
direction,
|
|
248
|
+
displayValue: formatDisplayValue(entry.delta.usdc, "USDC", direction),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
case "send": {
|
|
252
|
+
const isInbound = isSameAddress(entry.delta.destination, user);
|
|
253
|
+
const direction = isInbound ? "in" : "out";
|
|
254
|
+
return {
|
|
255
|
+
value: entry.delta.usdcValue,
|
|
256
|
+
numericValue: Number(entry.delta.usdcValue) * (isInbound ? 1 : -1),
|
|
257
|
+
token: "USDC",
|
|
258
|
+
direction,
|
|
259
|
+
displayValue: formatDisplayValue(entry.delta.usdcValue, "USDC", direction),
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
case "cStakingTransfer": {
|
|
263
|
+
const direction = entry.delta.isDeposit ? "out" : "in";
|
|
264
|
+
return {
|
|
265
|
+
value: entry.delta.amount,
|
|
266
|
+
numericValue: Number(entry.delta.amount) * (direction === "in" ? 1 : -1),
|
|
267
|
+
token: entry.delta.token,
|
|
268
|
+
direction,
|
|
269
|
+
displayValue: formatDisplayValue(entry.delta.amount, entry.delta.token, direction),
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
case "vaultCreate": {
|
|
273
|
+
const netValue = Number(entry.delta.usdc) - Number(entry.delta.fee || "0");
|
|
274
|
+
return {
|
|
275
|
+
value: netValue.toString(),
|
|
276
|
+
numericValue: -netValue,
|
|
277
|
+
token: "USDC",
|
|
278
|
+
direction: "out",
|
|
279
|
+
displayValue: formatDisplayValue(netValue.toString(), "USDC", "out"),
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
case "vaultDeposit":
|
|
283
|
+
return {
|
|
284
|
+
value: entry.delta.usdc,
|
|
285
|
+
numericValue: -Number(entry.delta.usdc),
|
|
286
|
+
token: "USDC",
|
|
287
|
+
direction: "out",
|
|
288
|
+
displayValue: formatDisplayValue(entry.delta.usdc, "USDC", "out"),
|
|
289
|
+
};
|
|
290
|
+
case "vaultDistribution":
|
|
291
|
+
return {
|
|
292
|
+
value: entry.delta.usdc,
|
|
293
|
+
numericValue: Number(entry.delta.usdc),
|
|
294
|
+
token: "USDC",
|
|
295
|
+
direction: "in",
|
|
296
|
+
displayValue: formatDisplayValue(entry.delta.usdc, "USDC", "in"),
|
|
297
|
+
};
|
|
298
|
+
case "vaultWithdraw":
|
|
299
|
+
return {
|
|
300
|
+
value: entry.delta.netWithdrawnUsd,
|
|
301
|
+
numericValue: Number(entry.delta.netWithdrawnUsd),
|
|
302
|
+
token: "USDC",
|
|
303
|
+
direction: "in",
|
|
304
|
+
displayValue: formatDisplayValue(entry.delta.netWithdrawnUsd, "USDC", "in"),
|
|
305
|
+
};
|
|
306
|
+
case "rewardsClaim":
|
|
307
|
+
return {
|
|
308
|
+
value: entry.delta.amount,
|
|
309
|
+
numericValue: Number(entry.delta.amount),
|
|
310
|
+
token: entry.delta.token,
|
|
311
|
+
direction: "in",
|
|
312
|
+
displayValue: formatDisplayValue(entry.delta.amount, entry.delta.token, "in"),
|
|
313
|
+
};
|
|
314
|
+
case "liquidation":
|
|
315
|
+
return {
|
|
316
|
+
value: entry.delta.accountValue,
|
|
317
|
+
numericValue: Number(entry.delta.accountValue),
|
|
318
|
+
token: "USDC",
|
|
319
|
+
direction: "neutral",
|
|
320
|
+
displayValue: formatDisplayValue(entry.delta.accountValue, "USDC", "neutral"),
|
|
321
|
+
};
|
|
322
|
+
case "deployGasAuction":
|
|
323
|
+
return {
|
|
324
|
+
value: entry.delta.amount,
|
|
325
|
+
numericValue: -Number(entry.delta.amount),
|
|
326
|
+
token: entry.delta.token,
|
|
327
|
+
direction: "out",
|
|
328
|
+
displayValue: formatDisplayValue(entry.delta.amount, entry.delta.token, "out"),
|
|
329
|
+
};
|
|
330
|
+
case "borrowLend": {
|
|
331
|
+
const direction = entry.delta.operation === "supply" || entry.delta.operation === "repay"
|
|
332
|
+
? "out"
|
|
333
|
+
: "in";
|
|
334
|
+
return {
|
|
335
|
+
value: entry.delta.amount,
|
|
336
|
+
numericValue: Number(entry.delta.amount) * (direction === "in" ? 1 : -1),
|
|
337
|
+
token: entry.delta.token,
|
|
338
|
+
direction,
|
|
339
|
+
displayValue: formatDisplayValue(entry.delta.amount, entry.delta.token, direction),
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
case "spotGenesis": {
|
|
343
|
+
const numericValue = Number(entry.delta.amount);
|
|
344
|
+
const direction = numericValue >= 0 ? "in" : "out";
|
|
345
|
+
return {
|
|
346
|
+
value: entry.delta.amount.replace(/^-/, ""),
|
|
347
|
+
numericValue,
|
|
348
|
+
token: entry.delta.token,
|
|
349
|
+
direction,
|
|
350
|
+
displayValue: formatDisplayValue(entry.delta.amount.replace(/^-/, ""), entry.delta.token, direction),
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
case "activateDexAbstraction":
|
|
354
|
+
return {
|
|
355
|
+
value: entry.delta.amount,
|
|
356
|
+
numericValue: -Number(entry.delta.amount),
|
|
357
|
+
token: entry.delta.token,
|
|
358
|
+
direction: "out",
|
|
359
|
+
displayValue: formatDisplayValue(entry.delta.amount, entry.delta.token, "out"),
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
function getFee(entry) {
|
|
364
|
+
switch (entry.delta.type) {
|
|
365
|
+
case "internalTransfer":
|
|
366
|
+
return {
|
|
367
|
+
value: entry.delta.fee,
|
|
368
|
+
numericValue: Number(entry.delta.fee),
|
|
369
|
+
token: "USDC",
|
|
370
|
+
displayValue: formatDisplayValue(entry.delta.fee, "USDC"),
|
|
371
|
+
};
|
|
372
|
+
case "spotTransfer":
|
|
373
|
+
if (Number(entry.delta.fee) !== 0) {
|
|
374
|
+
return {
|
|
375
|
+
value: entry.delta.fee,
|
|
376
|
+
numericValue: Number(entry.delta.fee),
|
|
377
|
+
token: entry.delta.feeToken,
|
|
378
|
+
displayValue: formatDisplayValue(entry.delta.fee, entry.delta.feeToken),
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
if (Number(entry.delta.nativeTokenFee) !== 0) {
|
|
382
|
+
return {
|
|
383
|
+
value: entry.delta.nativeTokenFee,
|
|
384
|
+
numericValue: Number(entry.delta.nativeTokenFee),
|
|
385
|
+
token: "HYPE",
|
|
386
|
+
displayValue: formatDisplayValue(entry.delta.nativeTokenFee, "HYPE"),
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
return {
|
|
390
|
+
value: null,
|
|
391
|
+
numericValue: null,
|
|
392
|
+
token: null,
|
|
393
|
+
displayValue: "--",
|
|
394
|
+
};
|
|
395
|
+
case "send":
|
|
396
|
+
if (Number(entry.delta.fee) !== 0) {
|
|
397
|
+
return {
|
|
398
|
+
value: entry.delta.fee,
|
|
399
|
+
numericValue: Number(entry.delta.fee),
|
|
400
|
+
token: entry.delta.feeToken,
|
|
401
|
+
displayValue: formatDisplayValue(entry.delta.fee, entry.delta.feeToken),
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
if (Number(entry.delta.nativeTokenFee) !== 0) {
|
|
405
|
+
return {
|
|
406
|
+
value: entry.delta.nativeTokenFee,
|
|
407
|
+
numericValue: Number(entry.delta.nativeTokenFee),
|
|
408
|
+
token: "HYPE",
|
|
409
|
+
displayValue: formatDisplayValue(entry.delta.nativeTokenFee, "HYPE"),
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
return {
|
|
413
|
+
value: null,
|
|
414
|
+
numericValue: null,
|
|
415
|
+
token: null,
|
|
416
|
+
displayValue: "--",
|
|
417
|
+
};
|
|
418
|
+
case "vaultCreate":
|
|
419
|
+
return {
|
|
420
|
+
value: entry.delta.fee,
|
|
421
|
+
numericValue: Number(entry.delta.fee),
|
|
422
|
+
token: "USDC",
|
|
423
|
+
displayValue: formatDisplayValue(entry.delta.fee, "USDC"),
|
|
424
|
+
};
|
|
425
|
+
case "vaultWithdraw": {
|
|
426
|
+
const totalFee = Number(entry.delta.commission) +
|
|
427
|
+
Number(entry.delta.closingCost) +
|
|
428
|
+
Number(entry.delta.basis);
|
|
429
|
+
if (totalFee === 0) {
|
|
430
|
+
return {
|
|
431
|
+
value: null,
|
|
432
|
+
numericValue: null,
|
|
433
|
+
token: null,
|
|
434
|
+
displayValue: "--",
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
return {
|
|
438
|
+
value: totalFee.toString(),
|
|
439
|
+
numericValue: totalFee,
|
|
440
|
+
token: "USDC",
|
|
441
|
+
displayValue: formatDisplayValue(totalFee.toString(), "USDC"),
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
case "withdraw":
|
|
445
|
+
return {
|
|
446
|
+
value: entry.delta.fee,
|
|
447
|
+
numericValue: Number(entry.delta.fee),
|
|
448
|
+
token: "USDC",
|
|
449
|
+
displayValue: Number(entry.delta.fee) === 0
|
|
450
|
+
? "--"
|
|
451
|
+
: formatDisplayValue(entry.delta.fee, "USDC"),
|
|
452
|
+
};
|
|
453
|
+
default:
|
|
454
|
+
return {
|
|
455
|
+
value: null,
|
|
456
|
+
numericValue: null,
|
|
457
|
+
token: null,
|
|
458
|
+
displayValue: "--",
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
function formatUserAccountActivity(entry, user) {
|
|
463
|
+
return {
|
|
464
|
+
...entry,
|
|
465
|
+
status: getStatus(entry),
|
|
466
|
+
action: getAction(entry),
|
|
467
|
+
source: getSource(entry),
|
|
468
|
+
destination: getDestination(entry),
|
|
469
|
+
explorerUrl: getExplorerUrl(entry),
|
|
470
|
+
amount: getAmount(entry, user),
|
|
471
|
+
fee: getFee(entry),
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
export function useUserAccountActivity(user, options = {}) {
|
|
475
|
+
const userNonFundingLedgerUpdatesState = useUserNonFundingLedgerUpdates(user, options);
|
|
476
|
+
const data = useMemo(() => {
|
|
477
|
+
if (!userNonFundingLedgerUpdatesState.data) {
|
|
478
|
+
return undefined;
|
|
479
|
+
}
|
|
480
|
+
return {
|
|
481
|
+
user: userNonFundingLedgerUpdatesState.data.user,
|
|
482
|
+
nonFundingLedgerUpdates: userNonFundingLedgerUpdatesState.data.nonFundingLedgerUpdates,
|
|
483
|
+
activity: userNonFundingLedgerUpdatesState.data.nonFundingLedgerUpdates.map((entry) => formatUserAccountActivity(entry, user)),
|
|
484
|
+
};
|
|
485
|
+
}, [user, userNonFundingLedgerUpdatesState.data]);
|
|
486
|
+
return {
|
|
487
|
+
...userNonFundingLedgerUpdatesState,
|
|
488
|
+
data,
|
|
489
|
+
};
|
|
490
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type UseSubscribeState } from "@outofgas/react-stream";
|
|
2
|
+
import type { UserNonFundingLedgerUpdatesEvent } from "@nktkas/hyperliquid/api/subscription";
|
|
3
|
+
export type UserNonFundingLedgerUpdate = UserNonFundingLedgerUpdatesEvent["nonFundingLedgerUpdates"][number];
|
|
4
|
+
export type UserNonFundingLedgerUpdatesData = {
|
|
5
|
+
user: `0x${string}`;
|
|
6
|
+
nonFundingLedgerUpdates: UserNonFundingLedgerUpdate[];
|
|
7
|
+
};
|
|
8
|
+
export type UseUserNonFundingLedgerUpdatesOptions = {
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
onUpdate?: (event: UserNonFundingLedgerUpdatesEvent) => void;
|
|
11
|
+
};
|
|
12
|
+
export declare function useUserNonFundingLedgerUpdates(user: `0x${string}`, options?: UseUserNonFundingLedgerUpdatesOptions): UseSubscribeState<UserNonFundingLedgerUpdatesData>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useSubscribe } from "@outofgas/react-stream";
|
|
2
|
+
import { wsClient } from "./config/hl.js";
|
|
3
|
+
function mergeUserNonFundingLedgerUpdates(previousData, incomingEvent) {
|
|
4
|
+
const nonFundingLedgerUpdates = incomingEvent.isSnapshot
|
|
5
|
+
? incomingEvent.nonFundingLedgerUpdates
|
|
6
|
+
: [
|
|
7
|
+
...(previousData?.nonFundingLedgerUpdates ?? []),
|
|
8
|
+
...incomingEvent.nonFundingLedgerUpdates,
|
|
9
|
+
];
|
|
10
|
+
return {
|
|
11
|
+
user: incomingEvent.user,
|
|
12
|
+
nonFundingLedgerUpdates: [...nonFundingLedgerUpdates].sort((a, b) => b.time - a.time),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function useUserNonFundingLedgerUpdates(user, options = {}) {
|
|
16
|
+
const { enabled: enabledOverride, onUpdate } = options;
|
|
17
|
+
const enabled = enabledOverride ?? Boolean(user);
|
|
18
|
+
return useSubscribe({
|
|
19
|
+
key: ["user-non-funding-ledger-updates", user],
|
|
20
|
+
enabled,
|
|
21
|
+
subscribe: async ({ onData, onError }) => {
|
|
22
|
+
let data;
|
|
23
|
+
const subscription = await wsClient.userNonFundingLedgerUpdates({ user }, (event) => {
|
|
24
|
+
try {
|
|
25
|
+
if (!event.isSnapshot) {
|
|
26
|
+
onUpdate?.(event);
|
|
27
|
+
}
|
|
28
|
+
data = mergeUserNonFundingLedgerUpdates(data, event);
|
|
29
|
+
onData(data);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
onError(error instanceof Error
|
|
33
|
+
? error
|
|
34
|
+
: new Error("Failed to process user non-funding ledger updates event"));
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
unsubscribe: () => subscription.unsubscribe(),
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|