hyperliquid-sdk
Version:
<<< Typescript SDK for the Hyperliquid API >>>
359 lines (319 loc) • 10.7 kB
text/typescript
import { WebSocketClient } from './connection';
import {
AllMids,
WsBook,
WsOrder,
WsUserEvent,
Notification,
WebData2,
WsUserFills,
WsUserFundings,
WsUserNonFundingLedgerUpdates,
WsUserActiveAssetData,
CandleSnapshot,
} from '../types/index';
import { SymbolConversion } from '../utils/symbolConversion';
export class WebSocketSubscriptions {
private ws: WebSocketClient;
private symbolConversion: SymbolConversion;
constructor(ws: WebSocketClient, symbolConversion: SymbolConversion) {
this.ws = ws;
this.symbolConversion = symbolConversion;
}
private async subscribe(subscription: {
type: string;
[key: string]: any;
}): Promise<void> {
this.ws.sendMessage({
method: 'subscribe',
subscription: subscription,
});
}
private async unsubscribe(subscription: {
type: string;
[key: string]: any;
}): Promise<void> {
const convertedSubscription =
await this.symbolConversion.convertSymbolsInObject(subscription);
this.ws.sendMessage({
method: 'unsubscribe',
subscription: convertedSubscription,
});
}
private handleMessage(
message: any,
callback: (data: any) => void,
channel: string,
additionalChecks: (data: any) => boolean = () => true,
) {
if (typeof message !== 'object' || message === null) {
console.warn('Received invalid message format:', message);
return;
}
let data = message.data || message;
if (data.channel === channel && additionalChecks(data)) {
const convertedData = this.symbolConversion.convertSymbolsInObject(data);
callback(convertedData);
}
}
async subscribeToAllMids(callback: (data: AllMids) => void): Promise<void> {
if (typeof callback !== 'function') {
throw new Error('Callback must be a function');
}
await this.subscribe({ type: 'allMids' });
this.ws.on('message', async (message: any) => {
if (message.channel === 'allMids') {
if (message.data.mids) {
const convertedData: AllMids = {};
for (const [key, value] of Object.entries(message.data.mids)) {
const convertedKey = await this.symbolConversion.convertSymbol(key);
const convertedValue = this.symbolConversion.convertToNumber(value);
convertedData[convertedKey] = convertedValue;
}
callback(convertedData);
}
}
});
}
async subscribeToNotification(
user: string,
callback: (data: Notification & { user: string }) => void,
): Promise<void> {
await this.subscribe({ type: 'notification', user: user });
this.ws.on('message', async (message: any) => {
if (message.channel === 'notification') {
message = await this.symbolConversion.convertSymbolsInObject(message);
callback(message.data);
}
});
}
async subscribeToWebData2(
user: string,
callback: (data: WebData2) => void,
): Promise<void> {
await this.subscribe({ type: 'webData2', user: user });
this.ws.on('message', async (message: any) => {
if (message.channel === 'webData2') {
message = await this.symbolConversion.convertSymbolsInObject(message);
callback(message.data);
}
});
}
async subscribeToCandle(
coin: string,
interval: string,
callback: (data: CandleSnapshot) => void,
): Promise<void> {
const convertedCoin = await this.symbolConversion.convertSymbol(
coin,
'reverse',
);
await this.subscribe({
type: 'candle',
coin: convertedCoin,
interval: interval,
});
this.ws.on('message', async (message: any) => {
if (
message.channel === 'candle' &&
message.data.s === convertedCoin &&
message.data.i === interval
) {
callback(message.data);
}
});
}
async subscribeToL2Book(
coin: string,
callback: (data: WsBook & { coin: string }) => void,
): Promise<void> {
const convertedCoin = await this.symbolConversion.convertSymbol(
coin,
'reverse',
);
await this.subscribe({ type: 'l2Book', coin: convertedCoin });
this.ws.on('message', async (message: any) => {
if (message.channel === 'l2Book' && message.data.coin === convertedCoin) {
message = await this.symbolConversion.convertSymbolsInObject(message, [
'coin',
]);
callback(message.data);
}
});
}
async subscribeToTrades(
coin: string,
callback: (data: any) => void,
): Promise<void> {
const convertedCoin = await this.symbolConversion.convertSymbol(
coin,
'reverse',
);
await this.subscribe({ type: 'trades', coin: convertedCoin });
this.ws.on('message', async (message: any) => {
if (
message.channel === 'trades' &&
message.data[0].coin === convertedCoin
) {
message = await this.symbolConversion.convertSymbolsInObject(message, [
'coin',
]);
callback(message.data);
}
});
}
async subscribeToOrderUpdates(
user: string,
callback: (data: WsOrder[] & { user: string }) => void,
): Promise<void> {
await this.subscribe({ type: 'orderUpdates', user: user });
this.ws.on('message', async (message: any) => {
if (message.channel === 'orderUpdates') {
message = await this.symbolConversion.convertSymbolsInObject(message);
callback(message.data);
}
});
}
async subscribeToUserEvents(
user: string,
callback: (data: WsUserEvent & { user: string }) => void,
): Promise<void> {
await this.subscribe({ type: 'userEvents', user: user });
this.ws.on('message', async (message: any) => {
if (message.channel === 'userEvents') {
message = await this.symbolConversion.convertSymbolsInObject(message);
callback(message.data);
}
});
}
async subscribeToUserFills(
user: string,
callback: (data: WsUserFills & { user: string }) => void,
): Promise<void> {
await this.subscribe({ type: 'userFills', user: user });
this.ws.on('message', async (message: any) => {
if (message.channel === 'userFills') {
message = await this.symbolConversion.convertSymbolsInObject(message);
callback(message.data);
}
});
}
async subscribeToUserFundings(
user: string,
callback: (data: WsUserFundings & { user: string }) => void,
): Promise<void> {
await this.subscribe({ type: 'userFundings', user: user });
this.ws.on('message', async (message: any) => {
if (message.channel === 'userFundings') {
message = await this.symbolConversion.convertSymbolsInObject(message);
callback(message.data);
}
});
}
async subscribeToUserNonFundingLedgerUpdates(
user: string,
callback: (data: WsUserNonFundingLedgerUpdates & { user: string }) => void,
): Promise<void> {
await this.subscribe({ type: 'userNonFundingLedgerUpdates', user: user });
this.ws.on('message', async (message: any) => {
if (message.channel === 'userNonFundingLedgerUpdates') {
message = await this.symbolConversion.convertSymbolsInObject(message);
callback(message.data);
}
});
}
async subscribeToUserActiveAssetData(
user: string,
coin: string,
callback: (data: WsUserActiveAssetData & { user: string }) => void,
): Promise<void> {
await this.subscribe({ type: 'activeAssetData', user: user, coin: coin });
this.ws.on('message', async (message: any) => {
if (message.channel === 'activeAssetData') {
message = await this.symbolConversion.convertSymbolsInObject(message);
callback(message.data);
}
});
}
async postRequest(
requestType: 'info' | 'action',
payload: any,
): Promise<any> {
const id = Date.now();
const convertedPayload =
await this.symbolConversion.convertSymbolsInObject(payload);
this.ws.sendMessage({
method: 'post',
id: id,
request: {
type: requestType,
payload: convertedPayload,
},
});
return new Promise((resolve, reject) => {
const responseHandler = (message: any) => {
if (typeof message === 'object' && message !== null) {
const data = message.data || message;
if (data.channel === 'post' && data.id === id) {
this.ws.removeListener('message', responseHandler);
if (data.response && data.response.type === 'error') {
reject(new Error(data.response.payload));
} else {
const convertedResponse =
this.symbolConversion.convertSymbolsInObject(
data.response ? data.response.payload : data,
);
resolve(convertedResponse);
}
}
}
};
this.ws.on('message', responseHandler);
setTimeout(() => {
this.ws.removeListener('message', responseHandler);
reject(new Error('Request timeout'));
}, 30000);
});
}
async unsubscribeFromAllMids(): Promise<void> {
this.unsubscribe({ type: 'allMids' });
}
async unsubscribeFromNotification(user: string): Promise<void> {
this.unsubscribe({ type: 'notification', user: user });
}
async unsubscribeFromWebData2(user: string): Promise<void> {
this.unsubscribe({ type: 'webData2', user: user });
}
async unsubscribeFromCandle(coin: string, interval: string): Promise<void> {
this.unsubscribe({ type: 'candle', coin: coin, interval: interval });
}
async unsubscribeFromL2Book(coin: string): Promise<void> {
this.unsubscribe({ type: 'l2Book', coin: coin });
}
async unsubscribeFromTrades(coin: string): Promise<void> {
this.unsubscribe({ type: 'trades', coin: coin });
}
async unsubscribeFromOrderUpdates(user: string): Promise<void> {
this.unsubscribe({ type: 'orderUpdates', user: user });
}
async unsubscribeFromUserEvents(user: string): Promise<void> {
this.unsubscribe({ type: 'userEvents', user: user });
}
async unsubscribeFromUserFills(user: string): Promise<void> {
this.unsubscribe({ type: 'userFills', user: user });
}
async unsubscribeFromUserFundings(user: string): Promise<void> {
this.unsubscribe({ type: 'userFundings', user: user });
}
async unsubscribeFromUserNonFundingLedgerUpdates(
user: string,
): Promise<void> {
this.unsubscribe({ type: 'userNonFundingLedgerUpdates', user: user });
}
async unsubscribeFromUserActiveAssetData(
user: string,
coin: string,
): Promise<void> {
this.unsubscribe({ type: 'activeAssetData', user: user, coin: coin });
}
}