cleaning around websocket client

This commit is contained in:
tiagosiebler
2022-09-15 12:20:39 +01:00
parent 0e05a8d0ef
commit 3f5039ef8b
7 changed files with 164 additions and 158 deletions

View File

@@ -1,5 +1,5 @@
import { DefaultLogger } from '../src'; import { DefaultLogger } from '../src';
import { WebsocketClient, wsKeySpotPublic } from '../src/websocket-client'; import { WebsocketClient } from '../src/websocket-client';
// or // or
// import { DefaultLogger, WebsocketClient } from 'bybit-api'; // import { DefaultLogger, WebsocketClient } from 'bybit-api';
@@ -33,6 +33,8 @@ import { WebsocketClient, wsKeySpotPublic } from '../src/websocket-client';
logger logger
); );
// wsClient.subscribePublicSpotOrderbook('test', 'full');
wsClient.on('update', (data) => { wsClient.on('update', (data) => {
console.log('raw message received ', JSON.stringify(data, null, 2)); console.log('raw message received ', JSON.stringify(data, null, 2));
}); });

View File

@@ -1,8 +1,7 @@
import { DefaultLogger } from '../src'; import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from '../src';
import { WebsocketClient, wsKeySpotPublic } from '../src/websocket-client';
// or // or
// import { DefaultLogger, WebsocketClient } from 'bybit-api'; // import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from 'bybit-api';
(async () => { (async () => {
const logger = { const logger = {
@@ -10,13 +9,16 @@ import { WebsocketClient, wsKeySpotPublic } from '../src/websocket-client';
// silly: () => {}, // silly: () => {},
}; };
const wsClient = new WebsocketClient({ const wsClient = new WebsocketClient(
{
// key: key, // key: key,
// secret: secret, // secret: secret,
market: 'linear',
// market: 'inverse', // market: 'inverse',
// market: 'linear', // market: 'spot',
market: 'spot', },
}, logger); logger
);
wsClient.on('update', (data) => { wsClient.on('update', (data) => {
console.log('raw message received ', JSON.stringify(data, null, 2)); console.log('raw message received ', JSON.stringify(data, null, 2));
@@ -25,7 +27,7 @@ import { WebsocketClient, wsKeySpotPublic } from '../src/websocket-client';
wsClient.on('open', (data) => { wsClient.on('open', (data) => {
console.log('connection opened open:', data.wsKey); console.log('connection opened open:', data.wsKey);
if (data.wsKey === wsKeySpotPublic) { if (data.wsKey === WS_KEY_MAP.spotPublic) {
// Spot public. // Spot public.
// wsClient.subscribePublicSpotTrades('BTCUSDT'); // wsClient.subscribePublicSpotTrades('BTCUSDT');
// wsClient.subscribePublicSpotTradingPair('BTCUSDT'); // wsClient.subscribePublicSpotTradingPair('BTCUSDT');
@@ -40,16 +42,20 @@ import { WebsocketClient, wsKeySpotPublic } from '../src/websocket-client';
console.log('ws automatically reconnecting.... ', wsKey); console.log('ws automatically reconnecting.... ', wsKey);
}); });
wsClient.on('reconnected', (data) => { wsClient.on('reconnected', (data) => {
console.log('ws has reconnected ', data?.wsKey ); console.log('ws has reconnected ', data?.wsKey);
}); });
// Inverse // Inverse
// wsClient.subscribe('trade'); // wsClient.subscribe('trade');
// Linear // Linear
// wsClient.subscribe('trade.BTCUSDT'); wsClient.subscribe('trade.BTCUSDT');
setTimeout(() => {
console.log('unsubscribing');
wsClient.unsubscribe('trade.BTCUSDT');
}, 5 * 1000);
// For spot, request public connection first then send required topics on 'open' // For spot, request public connection first then send required topics on 'open'
// wsClient.connectPublic(); // wsClient.connectPublic();
})(); })();

View File

@@ -10,6 +10,6 @@ export * from './usdc-perpetual-client';
export * from './unified-margin-client'; export * from './unified-margin-client';
export * from './websocket-client'; export * from './websocket-client';
export * from './util/logger'; export * from './util/logger';
export * from './util';
export * from './types'; export * from './types';
export * from './util/WsStore';
export * from './constants/enum'; export * from './constants/enum';

View File

@@ -1,4 +1,4 @@
import { RestClientOptions } from '../util'; import { RestClientOptions, WS_KEY_MAP } from '../util';
export type APIMarket = 'inverse' | 'linear' | 'spot'; //| 'v3'; export type APIMarket = 'inverse' | 'linear' | 'spot'; //| 'v3';
@@ -69,12 +69,7 @@ export type WsPrivateTopic =
export type WsTopic = WsPublicTopics | WsPrivateTopic; export type WsTopic = WsPublicTopics | WsPrivateTopic;
/** This is used to differentiate between each of the available websocket streams (as bybit has multiple websockets) */ /** This is used to differentiate between each of the available websocket streams (as bybit has multiple websockets) */
export type WsKey = export type WsKey = typeof WS_KEY_MAP[keyof typeof WS_KEY_MAP];
| 'inverse'
| 'linearPrivate'
| 'linearPublic'
| 'spotPrivate'
| 'spotPublic';
export interface WSClientConfigurableOptions { export interface WSClientConfigurableOptions {
key?: string; key?: string;

View File

@@ -1,4 +1,5 @@
import WebSocket from 'isomorphic-ws'; import WebSocket from 'isomorphic-ws';
import { WsKey } from '../types';
import { DefaultLogger } from './logger'; import { DefaultLogger } from './logger';
@@ -44,9 +45,9 @@ export default class WsStore {
} }
/** Get WS stored state for key, optionally create if missing */ /** Get WS stored state for key, optionally create if missing */
get(key: string, createIfMissing?: true): WsStoredState; get(key: WsKey, createIfMissing?: true): WsStoredState;
get(key: string, createIfMissing?: false): WsStoredState | undefined; get(key: WsKey, createIfMissing?: false): WsStoredState | undefined;
get(key: string, createIfMissing?: boolean): WsStoredState | undefined { get(key: WsKey, createIfMissing?: boolean): WsStoredState | undefined {
if (this.wsState[key]) { if (this.wsState[key]) {
return this.wsState[key]; return this.wsState[key];
} }
@@ -56,11 +57,11 @@ export default class WsStore {
} }
} }
getKeys(): string[] { getKeys(): WsKey[] {
return Object.keys(this.wsState); return Object.keys(this.wsState) as WsKey[];
} }
create(key: string): WsStoredState | undefined { create(key: WsKey): WsStoredState | undefined {
if (this.hasExistingActiveConnection(key)) { if (this.hasExistingActiveConnection(key)) {
this.logger.warning( this.logger.warning(
'WsStore setConnection() overwriting existing open connection: ', 'WsStore setConnection() overwriting existing open connection: ',
@@ -74,7 +75,7 @@ export default class WsStore {
return this.get(key); return this.get(key);
} }
delete(key: string) { delete(key: WsKey) {
if (this.hasExistingActiveConnection(key)) { if (this.hasExistingActiveConnection(key)) {
const ws = this.getWs(key); const ws = this.getWs(key);
this.logger.warning( this.logger.warning(
@@ -88,15 +89,15 @@ export default class WsStore {
/* connection websocket */ /* connection websocket */
hasExistingActiveConnection(key: string) { hasExistingActiveConnection(key: WsKey) {
return this.get(key) && this.isWsOpen(key); return this.get(key) && this.isWsOpen(key);
} }
getWs(key: string): WebSocket | undefined { getWs(key: WsKey): WebSocket | undefined {
return this.get(key)?.ws; return this.get(key)?.ws;
} }
setWs(key: string, wsConnection: WebSocket): WebSocket { setWs(key: WsKey, wsConnection: WebSocket): WebSocket {
if (this.isWsOpen(key)) { if (this.isWsOpen(key)) {
this.logger.warning( this.logger.warning(
'WsStore setConnection() overwriting existing open connection: ', 'WsStore setConnection() overwriting existing open connection: ',
@@ -109,7 +110,7 @@ export default class WsStore {
/* connection state */ /* connection state */
isWsOpen(key: string): boolean { isWsOpen(key: WsKey): boolean {
const existingConnection = this.getWs(key); const existingConnection = this.getWs(key);
return ( return (
!!existingConnection && !!existingConnection &&
@@ -117,37 +118,37 @@ export default class WsStore {
); );
} }
getConnectionState(key: string): WsConnectionStateEnum { getConnectionState(key: WsKey): WsConnectionStateEnum {
return this.get(key, true)!.connectionState!; return this.get(key, true)!.connectionState!;
} }
setConnectionState(key: string, state: WsConnectionStateEnum) { setConnectionState(key: WsKey, state: WsConnectionStateEnum) {
this.get(key, true)!.connectionState = state; this.get(key, true)!.connectionState = state;
} }
isConnectionState(key: string, state: WsConnectionStateEnum): boolean { isConnectionState(key: WsKey, state: WsConnectionStateEnum): boolean {
return this.getConnectionState(key) === state; return this.getConnectionState(key) === state;
} }
/* subscribed topics */ /* subscribed topics */
getTopics(key: string): WsTopicList { getTopics(key: WsKey): WsTopicList {
return this.get(key, true).subscribedTopics; return this.get(key, true).subscribedTopics;
} }
getTopicsByKey(): Record<string, WsTopicList> { getTopicsByKey(): Record<string, WsTopicList> {
const result = {}; const result = {};
for (const refKey in this.wsState) { for (const refKey in this.wsState) {
result[refKey] = this.getTopics(refKey); result[refKey] = this.getTopics(refKey as WsKey);
} }
return result; return result;
} }
addTopic(key: string, topic: WsTopic) { addTopic(key: WsKey, topic: WsTopic) {
return this.getTopics(key).add(topic); return this.getTopics(key).add(topic);
} }
deleteTopic(key: string, topic: WsTopic) { deleteTopic(key: WsKey, topic: WsTopic) {
return this.getTopics(key).delete(topic); return this.getTopics(key).delete(topic);
} }
} }

View File

@@ -1,38 +1,84 @@
import { WsKey } from '../types'; import { WsKey } from '../types';
export const wsKeyInverse = 'inverse'; interface NetworkMapV3 {
export const wsKeyLinearPrivate = 'linearPrivate'; livenet: string;
export const wsKeyLinearPublic = 'linearPublic'; livenet2?: string;
export const wsKeySpotPrivate = 'spotPrivate'; testnet: string;
export const wsKeySpotPublic = 'spotPublic'; testnet2?: string;
}
export const WS_KEY_MAP = { type PublicPrivateNetwork = 'public' | 'private';
inverse: wsKeyInverse,
linearPrivate: wsKeyLinearPrivate, export const WS_BASE_URL_MAP: Record<
linearPublic: wsKeyLinearPublic, string,
spotPrivate: wsKeySpotPrivate, Record<PublicPrivateNetwork, NetworkMapV3>
spotPublic: wsKeySpotPublic, > = {
inverse: {
private: {
livenet: 'wss://stream.bybit.com/realtime',
testnet: 'wss://stream-testnet.bybit.com/realtime',
},
public: {
livenet: 'wss://stream.bybit.com/realtime',
testnet: 'wss://stream-testnet.bybit.com/realtime',
},
},
linear: {
private: {
livenet: 'wss://stream.bybit.com/realtime_private',
livenet2: 'wss://stream.bytick.com/realtime_private',
testnet: 'wss://stream-testnet.bybit.com/realtime_private',
},
public: {
livenet: 'wss://stream.bybit.com/realtime_public',
livenet2: 'wss://stream.bytick.com/realtime_public',
testnet: 'wss://stream-testnet.bybit.com/realtime_public',
},
},
spot: {
private: {
livenet: 'wss://stream.bybit.com/spot/ws',
testnet: 'wss://stream-testnet.bybit.com/spot/ws',
},
public: {
livenet: 'wss://stream.bybit.com/spot/quote/ws/v1',
livenet2: 'wss://stream.bybit.com/spot/quote/ws/v2',
testnet: 'wss://stream-testnet.bybit.com/spot/quote/ws/v1',
testnet2: 'wss://stream-testnet.bybit.com/spot/quote/ws/v2',
},
},
}; };
export const PUBLIC_WS_KEYS = [WS_KEY_MAP.linearPublic, WS_KEY_MAP.spotPublic]; export const WS_KEY_MAP = {
inverse: 'inverse',
linearPrivate: 'linearPrivate',
linearPublic: 'linearPublic',
spotPrivate: 'spotPrivate',
spotPublic: 'spotPublic',
} as const;
export const PUBLIC_WS_KEYS = [
WS_KEY_MAP.linearPublic,
WS_KEY_MAP.spotPublic,
] as string[];
export function getLinearWsKeyForTopic(topic: string): WsKey { export function getLinearWsKeyForTopic(topic: string): WsKey {
const privateLinearTopics = [ const privateTopics = [
'position', 'position',
'execution', 'execution',
'order', 'order',
'stop_order', 'stop_order',
'wallet', 'wallet',
]; ];
if (privateLinearTopics.includes(topic)) { if (privateTopics.includes(topic)) {
return wsKeyLinearPrivate; return WS_KEY_MAP.linearPrivate;
} }
return wsKeyLinearPublic; return WS_KEY_MAP.linearPublic;
} }
export function getSpotWsKeyForTopic(topic: string): WsKey { export function getSpotWsKeyForTopic(topic: string): WsKey {
const privateLinearTopics = [ const privateTopics = [
'position', 'position',
'execution', 'execution',
'order', 'order',
@@ -42,9 +88,8 @@ export function getSpotWsKeyForTopic(topic: string): WsKey {
'ticketInfo', 'ticketInfo',
]; ];
if (privateLinearTopics.includes(topic)) { if (privateTopics.includes(topic)) {
return wsKeySpotPrivate; return WS_KEY_MAP.spotPrivate;
} }
return WS_KEY_MAP.spotPublic;
return wsKeySpotPublic;
} }

View File

@@ -6,7 +6,9 @@ import { LinearClient } from './linear-client';
import { SpotClientV3 } from './spot-client-v3'; import { SpotClientV3 } from './spot-client-v3';
import { SpotClient } from './spot-client'; import { SpotClient } from './spot-client';
import { DefaultLogger } from './util/logger'; import { signMessage } from './util/node-support';
import WsStore from './util/WsStore';
import { import {
APIMarket, APIMarket,
KlineInterval, KlineInterval,
@@ -17,82 +19,22 @@ import {
WsTopic, WsTopic,
} from './types'; } from './types';
import { signMessage } from './util/node-support';
import WsStore from './util/WsStore';
import { import {
serializeParams, serializeParams,
isWsPong, isWsPong,
getLinearWsKeyForTopic, getLinearWsKeyForTopic,
getSpotWsKeyForTopic, getSpotWsKeyForTopic,
wsKeyInverse,
wsKeyLinearPrivate,
wsKeyLinearPublic,
wsKeySpotPrivate,
wsKeySpotPublic,
WsConnectionStateEnum, WsConnectionStateEnum,
PUBLIC_WS_KEYS, PUBLIC_WS_KEYS,
WS_KEY_MAP,
DefaultLogger,
WS_BASE_URL_MAP,
} from './util'; } from './util';
const inverseEndpoints = {
livenet: 'wss://stream.bybit.com/realtime',
testnet: 'wss://stream-testnet.bybit.com/realtime',
};
interface NetworkMapV3 {
livenet: string;
livenet2?: string;
testnet: string;
testnet2?: string;
}
type NetworkType = 'public' | 'private';
function neverGuard(x: never, msg: string): Error { function neverGuard(x: never, msg: string): Error {
return new Error(`Unhandled value exception "x", ${msg}`); return new Error(`Unhandled value exception "x", ${msg}`);
} }
const WS_BASE_URL_MAP: Record<string, Record<NetworkType, NetworkMapV3>> = {
linear: {
private: {
livenet: 'wss://stream.bybit.com/realtime_private',
livenet2: 'wss://stream.bytick.com/realtime_private',
testnet: 'wss://stream-testnet.bybit.com/realtime_private',
},
public: {
livenet: 'wss://stream.bybit.com/realtime_public',
livenet2: 'wss://stream.bytick.com/realtime_public',
testnet: 'wss://stream-testnet.bybit.com/realtime_public',
},
},
};
const linearEndpoints: Record<NetworkType, NetworkMapV3> = {
private: {
livenet: 'wss://stream.bybit.com/realtime_private',
livenet2: 'wss://stream.bytick.com/realtime_private',
testnet: 'wss://stream-testnet.bybit.com/realtime_private',
},
public: {
livenet: 'wss://stream.bybit.com/realtime_public',
livenet2: 'wss://stream.bytick.com/realtime_public',
testnet: 'wss://stream-testnet.bybit.com/realtime_public',
},
};
const spotEndpoints: Record<NetworkType, NetworkMapV3> = {
private: {
livenet: 'wss://stream.bybit.com/spot/ws',
testnet: 'wss://stream-testnet.bybit.com/spot/ws',
},
public: {
livenet: 'wss://stream.bybit.com/spot/quote/ws/v1',
livenet2: 'wss://stream.bybit.com/spot/quote/ws/v2',
testnet: 'wss://stream-testnet.bybit.com/spot/quote/ws/v1',
testnet2: 'wss://stream-testnet.bybit.com/spot/quote/ws/v2',
},
};
const loggerCategory = { category: 'bybit-ws' }; const loggerCategory = { category: 'bybit-ws' };
export declare interface WebsocketClient { export declare interface WebsocketClient {
@@ -280,16 +222,19 @@ export class WebsocketClient extends EventEmitter {
public connectAll(): Promise<WebSocket | undefined>[] { public connectAll(): Promise<WebSocket | undefined>[] {
switch (this.options.market) { switch (this.options.market) {
case 'inverse': { case 'inverse': {
return [this.connect(wsKeyInverse)]; return [this.connect(WS_KEY_MAP.inverse)];
} }
case 'linear': { case 'linear': {
return [ return [
this.connect(wsKeyLinearPublic), this.connect(WS_KEY_MAP.linearPublic),
this.connect(wsKeyLinearPrivate), this.connect(WS_KEY_MAP.linearPrivate),
]; ];
} }
case 'spot': { case 'spot': {
return [this.connect(wsKeySpotPublic), this.connect(wsKeySpotPrivate)]; return [
this.connect(WS_KEY_MAP.spotPublic),
this.connect(WS_KEY_MAP.spotPrivate),
];
} }
default: { default: {
throw neverGuard(this.options.market, `connectAll(): Unhandled market`); throw neverGuard(this.options.market, `connectAll(): Unhandled market`);
@@ -300,13 +245,13 @@ export class WebsocketClient extends EventEmitter {
public connectPublic(): Promise<WebSocket | undefined> { public connectPublic(): Promise<WebSocket | undefined> {
switch (this.options.market) { switch (this.options.market) {
case 'inverse': { case 'inverse': {
return this.connect(wsKeyInverse); return this.connect(WS_KEY_MAP.inverse);
} }
case 'linear': { case 'linear': {
return this.connect(wsKeyLinearPublic); return this.connect(WS_KEY_MAP.linearPublic);
} }
case 'spot': { case 'spot': {
return this.connect(wsKeySpotPublic); return this.connect(WS_KEY_MAP.spotPublic);
} }
default: { default: {
throw neverGuard( throw neverGuard(
@@ -320,13 +265,13 @@ export class WebsocketClient extends EventEmitter {
public connectPrivate(): Promise<WebSocket | undefined> | undefined { public connectPrivate(): Promise<WebSocket | undefined> | undefined {
switch (this.options.market) { switch (this.options.market) {
case 'inverse': { case 'inverse': {
return this.connect(wsKeyInverse); return this.connect(WS_KEY_MAP.inverse);
} }
case 'linear': { case 'linear': {
return this.connect(wsKeyLinearPrivate); return this.connect(WS_KEY_MAP.linearPrivate);
} }
case 'spot': { case 'spot': {
return this.connect(wsKeySpotPrivate); return this.connect(WS_KEY_MAP.spotPrivate);
} }
default: { default: {
throw neverGuard( throw neverGuard(
@@ -672,7 +617,7 @@ export class WebsocketClient extends EventEmitter {
} }
} }
private getWs(wsKey: string) { private getWs(wsKey: WsKey) {
return this.wsStore.getWs(wsKey); return this.wsStore.getWs(wsKey);
} }
@@ -688,20 +633,21 @@ export class WebsocketClient extends EventEmitter {
const networkKey = this.isLivenet() ? 'livenet' : 'testnet'; const networkKey = this.isLivenet() ? 'livenet' : 'testnet';
switch (wsKey) { switch (wsKey) {
case wsKeyLinearPublic: { case WS_KEY_MAP.linearPublic: {
return linearEndpoints.public[networkKey]; return WS_BASE_URL_MAP.linear.public[networkKey];
} }
case wsKeyLinearPrivate: { case WS_KEY_MAP.linearPrivate: {
return linearEndpoints.private[networkKey]; return WS_BASE_URL_MAP.linear.private[networkKey];
} }
case wsKeySpotPublic: { case WS_KEY_MAP.spotPublic: {
return spotEndpoints.public[networkKey]; return WS_BASE_URL_MAP.spot.public[networkKey];
} }
case wsKeySpotPrivate: { case WS_KEY_MAP.spotPrivate: {
return spotEndpoints.private[networkKey]; return WS_BASE_URL_MAP.linear.private[networkKey];
} }
case wsKeyInverse: { case WS_KEY_MAP.inverse: {
return inverseEndpoints[networkKey]; // private and public are on the same WS connection
return WS_BASE_URL_MAP.inverse.public[networkKey];
} }
default: { default: {
this.logger.error('getWsUrl(): Unhandled wsKey: ', { this.logger.error('getWsUrl(): Unhandled wsKey: ', {
@@ -713,15 +659,25 @@ export class WebsocketClient extends EventEmitter {
} }
} }
private getWsKeyForTopic(topic: string) { private getWsKeyForTopic(topic: string): WsKey {
if (this.isInverse()) { switch (this.options.market) {
return wsKeyInverse; case 'inverse': {
return WS_KEY_MAP.inverse;
} }
if (this.isLinear()) { case 'linear': {
return getLinearWsKeyForTopic(topic); return getLinearWsKeyForTopic(topic);
} }
case 'spot': {
return getSpotWsKeyForTopic(topic); return getSpotWsKeyForTopic(topic);
} }
default: {
throw neverGuard(
this.options.market,
`connectPublic(): Unhandled market`
);
}
}
}
private wrongMarketError(market: APIMarket) { private wrongMarketError(market: APIMarket) {
return new Error( return new Error(
@@ -736,7 +692,7 @@ export class WebsocketClient extends EventEmitter {
} }
return this.tryWsSend( return this.tryWsSend(
wsKeySpotPublic, WS_KEY_MAP.spotPublic,
JSON.stringify({ JSON.stringify({
topic: 'trade', topic: 'trade',
event: 'sub', event: 'sub',
@@ -754,7 +710,7 @@ export class WebsocketClient extends EventEmitter {
} }
return this.tryWsSend( return this.tryWsSend(
wsKeySpotPublic, WS_KEY_MAP.spotPublic,
JSON.stringify({ JSON.stringify({
symbol, symbol,
topic: 'realtimes', topic: 'realtimes',
@@ -776,7 +732,7 @@ export class WebsocketClient extends EventEmitter {
} }
return this.tryWsSend( return this.tryWsSend(
wsKeySpotPublic, WS_KEY_MAP.spotPublic,
JSON.stringify({ JSON.stringify({
symbol, symbol,
topic: 'kline_' + candleSize, topic: 'kline_' + candleSize,
@@ -791,6 +747,7 @@ export class WebsocketClient extends EventEmitter {
//ws.send('{"symbol":"BTCUSDT","topic":"depth","event":"sub","params":{"binary":false}}'); //ws.send('{"symbol":"BTCUSDT","topic":"depth","event":"sub","params":{"binary":false}}');
//ws.send('{"symbol":"BTCUSDT","topic":"mergedDepth","event":"sub","params":{"binary":false,"dumpScale":1}}'); //ws.send('{"symbol":"BTCUSDT","topic":"mergedDepth","event":"sub","params":{"binary":false,"dumpScale":1}}');
//ws.send('{"symbol":"BTCUSDT","topic":"diffDepth","event":"sub","params":{"binary":false}}'); //ws.send('{"symbol":"BTCUSDT","topic":"diffDepth","event":"sub","params":{"binary":false}}');
public subscribePublicSpotOrderbook( public subscribePublicSpotOrderbook(
symbol: string, symbol: string,
depth: 'full' | 'merge' | 'delta', depth: 'full' | 'merge' | 'delta',
@@ -831,6 +788,6 @@ export class WebsocketClient extends EventEmitter {
if (dumpScale) { if (dumpScale) {
msg.params.dumpScale = dumpScale; msg.params.dumpScale = dumpScale;
} }
return this.tryWsSend(wsKeySpotPublic, JSON.stringify(msg)); return this.tryWsSend(WS_KEY_MAP.spotPublic, JSON.stringify(msg));
} }
} }