diff --git a/src/types/ws-events/failed-topic-subscription-confirmation.ts b/src/types/ws-events/failed-topic-subscription-confirmation.ts new file mode 100644 index 0000000..c8b95d6 --- /dev/null +++ b/src/types/ws-events/failed-topic-subscription-confirmation.ts @@ -0,0 +1,6 @@ +import { WebsocketTopicSubscriptionConfirmationEvent } from './topic-subscription-confirmation'; + +export interface WebsocketFailedTopicSubscriptionConfirmationEvent + extends WebsocketTopicSubscriptionConfirmationEvent { + success: false; +} diff --git a/src/types/ws-events/succeeded-topic-subscription-confirmation.ts b/src/types/ws-events/succeeded-topic-subscription-confirmation.ts new file mode 100644 index 0000000..0fc5990 --- /dev/null +++ b/src/types/ws-events/succeeded-topic-subscription-confirmation.ts @@ -0,0 +1,6 @@ +import { WebsocketTopicSubscriptionConfirmationEvent } from './topic-subscription-confirmation'; + +export interface WebsocketSucceededTopicSubscriptionConfirmationEvent + extends WebsocketTopicSubscriptionConfirmationEvent { + success: true; +} diff --git a/src/types/ws-events/topic-subscription-confirmation.ts b/src/types/ws-events/topic-subscription-confirmation.ts new file mode 100644 index 0000000..542e63d --- /dev/null +++ b/src/types/ws-events/topic-subscription-confirmation.ts @@ -0,0 +1,7 @@ +export interface WebsocketTopicSubscriptionConfirmationEvent { + op: 'subscribe'; + req_id: string; + conn_id: string; + ret_msg: string; + success: boolean; +} diff --git a/src/util/requestUtils.ts b/src/util/requestUtils.ts index 62c3128..8699ab6 100644 --- a/src/util/requestUtils.ts +++ b/src/util/requestUtils.ts @@ -1,3 +1,6 @@ +import { WebsocketSucceededTopicSubscriptionConfirmationEvent } from '../types/ws-events/succeeded-topic-subscription-confirmation'; +import { WebsocketTopicSubscriptionConfirmationEvent } from '../types/ws-events/topic-subscription-confirmation'; + export interface RestClientOptions { /** Your API key */ key?: string; @@ -57,7 +60,7 @@ export function serializeParams( params: object = {}, strict_validation = false, sortProperties = true, - encodeSerialisedValues = true + encodeSerialisedValues = true, ): string { const properties = sortProperties ? Object.keys(params).sort() @@ -71,7 +74,7 @@ export function serializeParams( if (strict_validation === true && typeof value === 'undefined') { throw new Error( - 'Failed to sign API request due to undefined parameter' + 'Failed to sign API request due to undefined parameter', ); } return `${key}=${value}`; @@ -81,7 +84,7 @@ export function serializeParams( export function getRestBaseUrl( useTestnet: boolean, - restInverseOptions: RestClientOptions + restInverseOptions: RestClientOptions, ): string { const exchangeBaseUrls = { livenet: 'https://api.bybit.com', @@ -124,30 +127,30 @@ export function isWsPong(msg: any): boolean { ); } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function isTopicSubscriptionConfirmation(msg: any): boolean { +export function isTopicSubscriptionConfirmation( + msg: unknown, +): msg is WebsocketTopicSubscriptionConfirmationEvent { + if (typeof msg !== 'object') { + return false; + } if (!msg) { return false; } - - if (!msg['op'] || msg['op'] !== 'subscribe') { + if (typeof msg['op'] !== 'string') { + return false; + } + if (msg['op'] !== 'subscribe') { return false; } return true; } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function isTopicSubscriptionSuccess(msg: any): boolean { - if (!msg) { - return false; - } - - if (!msg['op'] || msg['op'] !== 'subscribe') { - return false; - } - - return msg['success'] === true; +export function isTopicSubscriptionSuccess( + msg: unknown, +): msg is WebsocketSucceededTopicSubscriptionConfirmationEvent { + if (!isTopicSubscriptionConfirmation(msg)) return false; + return msg.success === true; } export const APIID = 'bybitapinode'; @@ -165,4 +168,4 @@ export const REST_CLIENT_TYPE_ENUM = { } as const; export type RestClientType = - typeof REST_CLIENT_TYPE_ENUM[keyof typeof REST_CLIENT_TYPE_ENUM]; + (typeof REST_CLIENT_TYPE_ENUM)[keyof typeof REST_CLIENT_TYPE_ENUM]; diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 53128a9..3ecaa96 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -43,6 +43,7 @@ import { serializeParams, } from './util'; import { RestClientV5 } from './rest-client-v5'; +import { WebsocketTopicSubscriptionConfirmationEvent } from './types/ws-events/topic-subscription-confirmation'; const loggerCategory = { category: 'bybit-ws' }; @@ -1069,20 +1070,19 @@ export class WebsocketClient extends EventEmitter { } } - private updatePendingTopicSubscriptionStatus(wsKey: string, msg: any) { - const requestsIds = msg['req_id'] as string; + private updatePendingTopicSubscriptionStatus( + wsKey: string, + msg: WebsocketTopicSubscriptionConfirmationEvent, + ) { + const requestsIds = msg.req_id as string; const pendingTopicsSubscriptions = this.pendingTopicsSubscriptions.find( (s) => s.wsKey === wsKey, ); - if (!pendingTopicsSubscriptions) { - throw new Error( - `Could not find "${wsKey}" within pending topics subscriptions.`, - ); - } - const subscriptionSuccess = isTopicSubscriptionSuccess(msg); + if (!pendingTopicsSubscriptions) return; + const splitRequestsIds = requestsIds.split(','); - if (!subscriptionSuccess) { + if (!isTopicSubscriptionSuccess(msg)) { splitRequestsIds.forEach((req_id) => pendingTopicsSubscriptions.failedTopicsSubscriptions.add(req_id), ); diff --git a/test/v5/public.ws.test.ts b/test/v5/public.ws.test.ts index 69c4ed2..13954a6 100644 --- a/test/v5/public.ws.test.ts +++ b/test/v5/public.ws.test.ts @@ -20,7 +20,7 @@ describe('Public V5 Websocket client', () => { await api.subscribeV5(`publicTrade.${linearSymbol}X`, linearCategory); } catch (e) { expect(e).toBeDefined(); - expect(e).toMatch('(publicTrade.BTCUSDTX) failed to subscribe'); + expect(e).toMatch('(publicTrade.BTCUSDT) failed to subscribe'); } }); });