binance 3.0.0-beta.1 → 3.0.0-beta.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/README.md +305 -75
- package/index.js +1 -1
- package/lib/coinm-client.d.ts +40 -22
- package/lib/coinm-client.js +4 -16
- package/lib/coinm-client.js.map +1 -1
- package/lib/index.d.ts +2 -0
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -1
- package/lib/main-client.d.ts +94 -30
- package/lib/main-client.js +68 -36
- package/lib/main-client.js.map +1 -1
- package/lib/portfolio-client.js.map +1 -1
- package/lib/types/coin.d.ts +1 -0
- package/lib/types/futures.d.ts +15 -5
- package/lib/types/futures.js.map +1 -1
- package/lib/types/portfolio-margin.d.ts +0 -9
- package/lib/types/shared.d.ts +14 -13
- package/lib/types/spot.d.ts +174 -11
- package/lib/types/spot.js.map +1 -1
- package/lib/types/websockets/ws-api-requests.d.ts +480 -2
- package/lib/types/websockets/ws-api-responses.d.ts +565 -1
- package/lib/types/websockets/ws-api.d.ts +157 -60
- package/lib/types/websockets/ws-api.js +47 -3
- package/lib/types/websockets/ws-api.js.map +1 -1
- package/lib/types/websockets/ws-events-formatted.d.ts +18 -14
- package/lib/types/websockets/ws-events-raw.d.ts +4 -2
- package/lib/types/websockets/ws-general.d.ts +8 -18
- package/lib/usdm-client.d.ts +45 -27
- package/lib/usdm-client.js +7 -19
- package/lib/usdm-client.js.map +1 -1
- package/lib/util/BaseRestClient.js +20 -6
- package/lib/util/BaseRestClient.js.map +1 -1
- package/lib/util/BaseWSClient.d.ts +14 -3
- package/lib/util/BaseWSClient.js +87 -30
- package/lib/util/BaseWSClient.js.map +1 -1
- package/lib/util/beautifier-maps.d.ts +32 -0
- package/lib/util/beautifier-maps.js +41 -0
- package/lib/util/beautifier-maps.js.map +1 -1
- package/lib/util/beautifier.d.ts +1 -0
- package/lib/util/beautifier.js +44 -9
- package/lib/util/beautifier.js.map +1 -1
- package/lib/util/browser-support.d.ts +1 -1
- package/lib/util/browser-support.js +1 -41
- package/lib/util/browser-support.js.map +1 -1
- package/lib/util/node-support.js +7 -5
- package/lib/util/node-support.js.map +1 -1
- package/lib/util/requestUtils.d.ts +22 -4
- package/lib/util/requestUtils.js +76 -25
- package/lib/util/requestUtils.js.map +1 -1
- package/lib/util/rounding.d.ts +8 -0
- package/lib/util/rounding.js +41 -0
- package/lib/util/rounding.js.map +1 -0
- package/lib/util/typeGuards.d.ts +31 -3
- package/lib/util/typeGuards.js +105 -8
- package/lib/util/typeGuards.js.map +1 -1
- package/lib/util/webCryptoAPI.js +20 -45
- package/lib/util/webCryptoAPI.js.map +1 -1
- package/lib/util/websockets/WsStore.js +7 -2
- package/lib/util/websockets/WsStore.js.map +1 -1
- package/lib/util/websockets/enum.d.ts +11 -0
- package/lib/util/websockets/enum.js +24 -0
- package/lib/util/websockets/enum.js.map +1 -0
- package/lib/util/websockets/rest-client-cache.d.ts +3 -3
- package/lib/util/websockets/rest-client-cache.js +9 -9
- package/lib/util/websockets/rest-client-cache.js.map +1 -1
- package/lib/util/websockets/user-data-stream-manager.js +7 -8
- package/lib/util/websockets/user-data-stream-manager.js.map +1 -1
- package/lib/util/websockets/websocket-util.d.ts +27 -6
- package/lib/util/websockets/websocket-util.js +147 -52
- package/lib/util/websockets/websocket-util.js.map +1 -1
- package/lib/websocket-api-client.d.ts +401 -0
- package/lib/websocket-api-client.js +647 -0
- package/lib/websocket-api-client.js.map +1 -0
- package/lib/websocket-client-legacy.d.ts +5 -3
- package/lib/websocket-client-legacy.js +11 -10
- package/lib/websocket-client-legacy.js.map +1 -1
- package/lib/websocket-client.d.ts +26 -11
- package/lib/websocket-client.js +185 -139
- package/lib/websocket-client.js.map +1 -1
- package/package.json +3 -3
package/lib/websocket-client.js
CHANGED
|
@@ -8,6 +8,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
12
|
+
var t = {};
|
|
13
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
14
|
+
t[p] = s[p];
|
|
15
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
16
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
17
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
18
|
+
t[p[i]] = s[p[i]];
|
|
19
|
+
}
|
|
20
|
+
return t;
|
|
21
|
+
};
|
|
11
22
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
23
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
24
|
};
|
|
@@ -15,9 +26,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
26
|
exports.WebsocketClient = void 0;
|
|
16
27
|
const BaseWSClient_1 = require("./util/BaseWSClient");
|
|
17
28
|
const beautifier_1 = __importDefault(require("./util/beautifier"));
|
|
29
|
+
const node_support_1 = require("./util/node-support");
|
|
18
30
|
const requestUtils_1 = require("./util/requestUtils");
|
|
19
31
|
const typeGuards_1 = require("./util/typeGuards");
|
|
20
|
-
const webCryptoAPI_1 = require("./util/webCryptoAPI");
|
|
21
32
|
const rest_client_cache_1 = require("./util/websockets/rest-client-cache");
|
|
22
33
|
const user_data_stream_manager_1 = require("./util/websockets/user-data-stream-manager");
|
|
23
34
|
const websocket_util_1 = require("./util/websockets/websocket-util");
|
|
@@ -47,6 +58,22 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
47
58
|
warnKeyMissingInMap: true,
|
|
48
59
|
});
|
|
49
60
|
this.respawnTimeoutCache = {};
|
|
61
|
+
if (options === null || options === void 0 ? void 0 : options.beautifyWarnIfMissing) {
|
|
62
|
+
this.beautifier.setWarnIfMissing(options.beautifyWarnIfMissing);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Binance uses native WebSocket ping/pong frames, which cannot be directly used in
|
|
66
|
+
* some environents (e.g. most browsers do not support sending raw ping/pong frames).
|
|
67
|
+
*
|
|
68
|
+
* This disables heartbeats in those environments, if ping/pong frames are unavailable.
|
|
69
|
+
*
|
|
70
|
+
* Some browsers may still handle these automatically. Some discussion around this can
|
|
71
|
+
* be found here: https://stackoverflow.com/questions/10585355/sending-websocket-ping-pong-frame-from-browser
|
|
72
|
+
*/
|
|
73
|
+
if (!(0, websocket_util_1.isWSPingFrameAvailable)()) {
|
|
74
|
+
this.logger.trace('Disabled WS heartbeats. WS.ping() is not available in your environment.');
|
|
75
|
+
this.options.disableHeartbeat = true;
|
|
76
|
+
}
|
|
50
77
|
this.userDataStreamManager = new user_data_stream_manager_1.UserDataStreamManager({
|
|
51
78
|
logger: this.logger,
|
|
52
79
|
wsStore: this.getWsStore(),
|
|
@@ -64,25 +91,17 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
64
91
|
return this.userDataStreamManager;
|
|
65
92
|
}
|
|
66
93
|
getRestClientOptions() {
|
|
67
|
-
return Object.assign(Object.assign(Object.assign({}, this.options), this.options.restOptions), { api_key: this.options.api_key, api_secret: this.options.api_secret });
|
|
94
|
+
return Object.assign(Object.assign(Object.assign({}, this.options), this.options.restOptions), { testnet: this.options.testnet, api_key: this.options.api_key, api_secret: this.options.api_secret });
|
|
68
95
|
}
|
|
69
96
|
/**
|
|
70
97
|
* Request connection of all dependent (public & WS API) websockets in prod, instead of waiting
|
|
71
|
-
* for automatic connection by SDK.
|
|
98
|
+
* for automatic connection by SDK.
|
|
99
|
+
*
|
|
100
|
+
* For the Binance SDK, this will only open public connections (without auth), but is almost definitely overkill if you're only working with one product group.
|
|
72
101
|
*/
|
|
73
102
|
connectAll() {
|
|
74
103
|
return this.connectPublic();
|
|
75
104
|
}
|
|
76
|
-
/**
|
|
77
|
-
* Ensures the WS API connection is active and ready.
|
|
78
|
-
*
|
|
79
|
-
* You do not need to call this, but if you call this before making any WS API requests,
|
|
80
|
-
* it can accelerate the first request (by preparing the connection in advance).
|
|
81
|
-
*/
|
|
82
|
-
connectWSAPI(wsKey) {
|
|
83
|
-
/** This call automatically ensures the connection is active AND authenticated before resolving */
|
|
84
|
-
return this.assertIsAuthenticated(wsKey);
|
|
85
|
-
}
|
|
86
105
|
/**
|
|
87
106
|
* Request connection to all public websockets in prod (spot, margin, futures, options). Overkill if
|
|
88
107
|
* you're only working with one product group.
|
|
@@ -95,18 +114,24 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
95
114
|
this.connect(websocket_util_1.WS_KEY_MAP.eoptions),
|
|
96
115
|
];
|
|
97
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* This function serves no purpose in the Binance SDK
|
|
119
|
+
*/
|
|
98
120
|
connectPrivate() {
|
|
99
121
|
return __awaiter(this, void 0, void 0, function* () {
|
|
100
|
-
// switch (this.options.market) {
|
|
101
|
-
// case 'v5':
|
|
102
|
-
// default: {
|
|
103
|
-
// return this.connect(WS_KEY_MAP.v5Private);
|
|
104
|
-
// }
|
|
105
|
-
// }
|
|
106
|
-
// TODO:
|
|
107
122
|
return;
|
|
108
123
|
});
|
|
109
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Ensures the WS API connection is active and ready.
|
|
127
|
+
*
|
|
128
|
+
* You do not need to call this, but if you call this before making any WS API requests,
|
|
129
|
+
* it can accelerate the first request (by preparing the connection in advance).
|
|
130
|
+
*/
|
|
131
|
+
connectWSAPI(wsKey) {
|
|
132
|
+
/** This call automatically ensures the connection is active AND authenticated before resolving */
|
|
133
|
+
return this.assertIsAuthenticated(wsKey);
|
|
134
|
+
}
|
|
110
135
|
/**
|
|
111
136
|
* Request subscription to one or more topics. Pass topics as either an array of strings,
|
|
112
137
|
* or array of objects (if the topic has parameters).
|
|
@@ -135,54 +160,75 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
135
160
|
const normalisedTopicRequests = (0, websocket_util_1.getNormalisedTopicRequests)(topicRequests);
|
|
136
161
|
return this.unsubscribeTopicsForWsKey(normalisedTopicRequests, wsKey);
|
|
137
162
|
}
|
|
138
|
-
// These overloads give stricter types than mapped generics, since generic constraints
|
|
139
|
-
// do not trigger excess property checks
|
|
140
|
-
// Without these overloads, TypeScript won't complain if you include an
|
|
141
|
-
// unexpected property with your request (if it doesn't clash with an existing property)
|
|
142
|
-
// sendWSAPIRequest<TWSOpreation extends WSAPIOperation = 'order.create'>(
|
|
143
|
-
// wsKey: typeof WS_KEY_MAP.v5PrivateTrade,
|
|
144
|
-
// operation: TWSOpreation,
|
|
145
|
-
// params: WsAPITopicRequestParamMap[TWSOpreation],
|
|
146
|
-
// ): Promise<WsAPIOperationResponseMap[TWSOpreation]>;
|
|
147
|
-
// sendWSAPIRequest<TWSOpreation extends WSAPIOperation = 'order.amend'>(
|
|
148
|
-
// wsKey: typeof WS_KEY_MAP.v5PrivateTrade,
|
|
149
|
-
// operation: TWSOpreation,
|
|
150
|
-
// params: WsAPITopicRequestParamMap[TWSOpreation],
|
|
151
|
-
// ): Promise<WsAPIOperationResponseMap[TWSOpreation]>;
|
|
152
|
-
// sendWSAPIRequest<TWSOpreation extends WSAPIOperation = 'order.cancel'>(
|
|
153
|
-
// wsKey: typeof WS_KEY_MAP.v5PrivateTrade,
|
|
154
|
-
// operation: TWSOpreation,
|
|
155
|
-
// params: WsAPITopicRequestParamMap[TWSOpreation],
|
|
156
|
-
// ): Promise<WsAPIOperationResponseMap[TWSOpreation]>;
|
|
157
163
|
sendWSAPIRequest(wsKey, operation, params) {
|
|
158
164
|
return __awaiter(this, void 0, void 0, function* () {
|
|
159
|
-
//WsAPIOperationResponseMap[TWSOperation]> {
|
|
160
165
|
/**
|
|
161
|
-
* Spot: https://developers.binance.com/docs/binance-spot-api-docs/
|
|
166
|
+
* Spot: https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/general-api-information
|
|
167
|
+
* USDM Futures: https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-api-general-info
|
|
168
|
+
* COINM Futures: https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-api-general-info
|
|
162
169
|
*/
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
170
|
+
var _a, _b;
|
|
171
|
+
// If testnet, enforce testnet wskey for WS API calls
|
|
172
|
+
const resolvedWsKey = this.options.testnet ? (0, websocket_util_1.getTestnetWsKey)(wsKey) : wsKey;
|
|
173
|
+
// this.logger.trace(`sendWSAPIRequest(): assertIsConnected("${wsKey}")...`);
|
|
174
|
+
const timestampBeforeAuth = Date.now();
|
|
175
|
+
yield this.assertIsConnected(resolvedWsKey);
|
|
176
|
+
// this.logger.trace('sendWSAPIRequest(): assertIsConnected(${wsKey}) ok');
|
|
177
|
+
// this.logger.trace('sendWSAPIRequest(): assertIsAuthenticated(${wsKey})...');
|
|
178
|
+
yield this.assertIsAuthenticated(resolvedWsKey);
|
|
179
|
+
const timestampAfterAuth = Date.now();
|
|
180
|
+
// this.logger.trace('sendWSAPIRequest(): assertIsAuthenticated(${wsKey}) ok');
|
|
168
181
|
const request = {
|
|
169
182
|
id: this.getNewRequestId(),
|
|
170
183
|
method: operation,
|
|
171
|
-
params: Object.assign({
|
|
184
|
+
params: Object.assign({}, params),
|
|
172
185
|
};
|
|
173
|
-
|
|
174
|
-
|
|
186
|
+
/**
|
|
187
|
+
* Some WS API requests require a timestamp to be included. assertIsConnected and assertIsAuthenticated
|
|
188
|
+
* can introduce a small delay before the actual request is sent, if not connected before that request is
|
|
189
|
+
* made. This can lead to a curious race condition, where the request timestamp is before
|
|
190
|
+
* the "authorizedSince" timestamp - as such, binance does not recognise the session as already authenticated.
|
|
191
|
+
*
|
|
192
|
+
* The below mechanism measures any delay introduced from the assert calls, and if the request includes a timestamp,
|
|
193
|
+
* it offsets that timestamp by the delay.
|
|
194
|
+
*/
|
|
195
|
+
const delayFromAuthAssert = timestampAfterAuth - timestampBeforeAuth;
|
|
196
|
+
if (delayFromAuthAssert && ((_a = request.params) === null || _a === void 0 ? void 0 : _a.timestamp)) {
|
|
197
|
+
request.params.timestamp += delayFromAuthAssert;
|
|
198
|
+
this.logger.trace(`sendWSAPIRequest(): adjust timestamp - delay seen by connect/auth assert and delayed request includes timestamp, adjusting timestamp by ${delayFromAuthAssert}ms`);
|
|
199
|
+
}
|
|
200
|
+
if ((0, requestUtils_1.requiresWSAPINewClientOID)(request, resolvedWsKey)) {
|
|
201
|
+
(0, requestUtils_1.validateWSAPINewClientOID)(request, resolvedWsKey);
|
|
175
202
|
}
|
|
176
203
|
// Sign, if needed
|
|
177
204
|
const signedEvent = yield this.signWSAPIRequest(request);
|
|
178
205
|
// Store deferred promise, resolved within the "resolveEmittableEvents" method while parsing incoming events
|
|
179
|
-
const promiseRef = (0, websocket_util_1.getPromiseRefForWSAPIRequest)(
|
|
180
|
-
const deferredPromise = this.getWsStore().createDeferredPromise(
|
|
206
|
+
const promiseRef = (0, websocket_util_1.getPromiseRefForWSAPIRequest)(resolvedWsKey, signedEvent);
|
|
207
|
+
const deferredPromise = this.getWsStore().createDeferredPromise(resolvedWsKey, promiseRef, false);
|
|
208
|
+
(_b = deferredPromise.promise) === null || _b === void 0 ? void 0 : _b.then((res) => {
|
|
209
|
+
if (!Array.isArray(res)) {
|
|
210
|
+
res.request = Object.assign({ wsKey: resolvedWsKey }, signedEvent);
|
|
211
|
+
}
|
|
212
|
+
return res;
|
|
213
|
+
}).catch((e) => {
|
|
214
|
+
if (typeof e === 'string') {
|
|
215
|
+
this.logger.error('unexpcted string', { e });
|
|
216
|
+
return e;
|
|
217
|
+
}
|
|
218
|
+
e.request = {
|
|
219
|
+
wsKey: resolvedWsKey,
|
|
220
|
+
operation,
|
|
221
|
+
params: signedEvent.params,
|
|
222
|
+
};
|
|
223
|
+
// throw e;
|
|
224
|
+
return e;
|
|
225
|
+
});
|
|
181
226
|
// this.logger.trace(
|
|
182
227
|
// `sendWSAPIRequest(): sending raw request: ${JSON.stringify(signedEvent)} with promiseRef(${promiseRef})`,
|
|
183
228
|
// );
|
|
184
|
-
// Send event
|
|
185
|
-
|
|
229
|
+
// Send event.
|
|
230
|
+
const throwExceptions = true;
|
|
231
|
+
this.tryWsSend(resolvedWsKey, JSON.stringify(signedEvent), throwExceptions);
|
|
186
232
|
this.logger.trace(`sendWSAPIRequest(): sent "${operation}" event with promiseRef(${promiseRef})`);
|
|
187
233
|
// Return deferred promise, so caller can await this call
|
|
188
234
|
return deferredPromise.promise;
|
|
@@ -210,13 +256,35 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
210
256
|
if (typeof this.options.customSignMessageFn === 'function') {
|
|
211
257
|
return this.options.customSignMessageFn(paramsStr, secret);
|
|
212
258
|
}
|
|
213
|
-
return yield (0,
|
|
259
|
+
return yield (0, node_support_1.signMessage)(paramsStr, secret, method, algorithm);
|
|
260
|
+
// return await signMessageWebCryptoAPI(paramsStr, secret, method, algorithm);
|
|
214
261
|
});
|
|
215
262
|
}
|
|
216
263
|
signWSAPIRequest(requestEvent) {
|
|
217
264
|
return __awaiter(this, void 0, void 0, function* () {
|
|
218
|
-
|
|
265
|
+
if (!requestEvent.params) {
|
|
266
|
+
return requestEvent;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
*
|
|
270
|
+
*/
|
|
271
|
+
// Not needed for most commands on binance Binance. Auth happens only on connection open, automatically.
|
|
219
272
|
// Faster than performing auth for every request
|
|
273
|
+
// However, some commands don't work without this for some reason...
|
|
274
|
+
const _a = requestEvent.params, { signRequest } = _a, otherParams = __rest(_a, ["signRequest"]);
|
|
275
|
+
if (signRequest) {
|
|
276
|
+
const strictParamValidation = true;
|
|
277
|
+
const encodeValues = true;
|
|
278
|
+
const filterUndefinedParams = true;
|
|
279
|
+
const semiFinalRequestParams = Object.assign({ apiKey: this.options.api_key }, otherParams);
|
|
280
|
+
const serialisedParams = (0, requestUtils_1.serialiseParams)(semiFinalRequestParams, strictParamValidation, encodeValues, filterUndefinedParams);
|
|
281
|
+
const signature = yield this.signMessage(serialisedParams, this.options.api_secret, 'base64', 'SHA-256');
|
|
282
|
+
console.log('signWSAPIRequest()', {
|
|
283
|
+
semiFinalRequestParams,
|
|
284
|
+
serialisedParams,
|
|
285
|
+
});
|
|
286
|
+
return Object.assign(Object.assign({}, requestEvent), { params: Object.assign(Object.assign({}, semiFinalRequestParams), { signature }) });
|
|
287
|
+
}
|
|
220
288
|
return requestEvent;
|
|
221
289
|
});
|
|
222
290
|
}
|
|
@@ -227,22 +295,21 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
227
295
|
// If you do not want to specify apiKey and signature in each individual request, you can authenticate your API key for the active WebSocket session.
|
|
228
296
|
// Once authenticated, you no longer have to specify apiKey and signature for those requests that need them. Requests will be performed on behalf of the account owning the authenticated API key.
|
|
229
297
|
// Note: You still have to specify the timestamp parameter for SIGNED requests.
|
|
230
|
-
const recvWindow = this.options.recvWindow || 0;
|
|
231
|
-
const timestamp = Date.now() + (this.getTimeOffsetMs() || 0) + recvWindow;
|
|
298
|
+
// const recvWindow = this.options.recvWindow || 0;
|
|
299
|
+
const timestamp = Date.now() + (this.getTimeOffsetMs() || 0); // + recvWindow;
|
|
232
300
|
const strictParamValidation = true;
|
|
233
301
|
const encodeValues = true;
|
|
234
302
|
const filterUndefinedParams = true;
|
|
235
|
-
const
|
|
303
|
+
const params = {
|
|
236
304
|
apiKey: this.options.api_key,
|
|
237
305
|
timestamp,
|
|
238
306
|
};
|
|
239
|
-
const serialisedParams = (0, requestUtils_1.serialiseParams)(
|
|
307
|
+
const serialisedParams = (0, requestUtils_1.serialiseParams)(params, strictParamValidation, encodeValues, filterUndefinedParams);
|
|
240
308
|
const signature = yield this.signMessage(serialisedParams, this.options.api_secret, 'base64', 'SHA-256');
|
|
241
|
-
// https://developers.binance.com/docs/binance-spot-api-docs/web-socket-api/request-security#signed-request-example-ed25519
|
|
242
309
|
const request = {
|
|
243
310
|
id: this.getNewRequestId(),
|
|
244
311
|
method: 'session.logon',
|
|
245
|
-
params: Object.assign(Object.assign({},
|
|
312
|
+
params: Object.assign(Object.assign({}, params), { signature }),
|
|
246
313
|
};
|
|
247
314
|
return request;
|
|
248
315
|
}
|
|
@@ -252,36 +319,12 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
252
319
|
}
|
|
253
320
|
});
|
|
254
321
|
}
|
|
255
|
-
// private async getWsAuthSignature(
|
|
256
|
-
// wsKey: WsKey,
|
|
257
|
-
// ): Promise<{ expiresAt: number; signature: string }> {
|
|
258
|
-
// const { api_key, api_secret } = this.options;
|
|
259
|
-
// if (!api_key || !api_secret) {
|
|
260
|
-
// this.logger.error(
|
|
261
|
-
// 'Cannot authenticate websocket, either api or private keys missing.',
|
|
262
|
-
// { ...WS_LOGGER_CATEGORY, wsKey },
|
|
263
|
-
// );
|
|
264
|
-
// throw new Error('Cannot auth - missing api or secret in config');
|
|
265
|
-
// }
|
|
266
|
-
// this.logger.trace("Getting auth'd request params", {
|
|
267
|
-
// ...WS_LOGGER_CATEGORY,
|
|
268
|
-
// wsKey,
|
|
269
|
-
// });
|
|
270
|
-
// const recvWindow = this.options.recvWindow || 5000;
|
|
271
|
-
// const signatureExpiresAt = Date.now() + this.getTimeOffsetMs() + recvWindow;
|
|
272
|
-
// // const signature = await this.signMessage(
|
|
273
|
-
// // 'GET/realtime' + signatureExpiresAt,
|
|
274
|
-
// // api_secret,
|
|
275
|
-
// // 'hex',
|
|
276
|
-
// // 'SHA-256',
|
|
277
|
-
// // );
|
|
278
|
-
// return {
|
|
279
|
-
// expiresAt: signatureExpiresAt,
|
|
280
|
-
// signature: '',
|
|
281
|
-
// };
|
|
282
|
-
// }
|
|
283
322
|
sendPingEvent(wsKey) {
|
|
284
323
|
try {
|
|
324
|
+
if (!(0, websocket_util_1.isWSPingFrameAvailable)()) {
|
|
325
|
+
this.logger.trace('Unable to send WS ping frame. Not available in this environment.', Object.assign(Object.assign({}, WS_LOGGER_CATEGORY), { wsKey }));
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
285
328
|
// this.logger.trace(`Sending upstream ping: `, { ...loggerCategory, wsKey });
|
|
286
329
|
if (!wsKey) {
|
|
287
330
|
throw new Error('No wsKey provided');
|
|
@@ -304,8 +347,11 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
304
347
|
}
|
|
305
348
|
}
|
|
306
349
|
sendPongEvent(wsKey) {
|
|
307
|
-
// ws.pong();
|
|
308
350
|
try {
|
|
351
|
+
if (!(0, websocket_util_1.isWSPongFrameAvailable)()) {
|
|
352
|
+
this.logger.trace('Unable to send WS pong frame. Not available in this environment.', Object.assign(Object.assign({}, WS_LOGGER_CATEGORY), { wsKey }));
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
309
355
|
// this.logger.trace(`Sending upstream ping: `, { ...loggerCategory, wsKey });
|
|
310
356
|
if (!wsKey) {
|
|
311
357
|
throw new Error('No wsKey provided');
|
|
@@ -347,6 +393,8 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
347
393
|
params: topics,
|
|
348
394
|
id: req_id,
|
|
349
395
|
};
|
|
396
|
+
// Cache midflight subs on the req ID
|
|
397
|
+
// Enrich response with subs for that req ID
|
|
350
398
|
const midflightWsEvent = {
|
|
351
399
|
requestKey: wsEvent.id,
|
|
352
400
|
requestEvent: wsEvent,
|
|
@@ -354,9 +402,6 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
354
402
|
wsRequestEvents.push(Object.assign({}, midflightWsEvent));
|
|
355
403
|
break;
|
|
356
404
|
}
|
|
357
|
-
// default: {
|
|
358
|
-
// throw neverGuard(wsKey, `Unhandled wsKey "${wsKey}"`);
|
|
359
|
-
// }
|
|
360
405
|
}
|
|
361
406
|
if (wsRequestBuildingErrors.length) {
|
|
362
407
|
const label = wsRequestBuildingErrors.length === requests.length ? 'all' : 'some';
|
|
@@ -403,6 +448,7 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
403
448
|
*/
|
|
404
449
|
resolveEmittableEvents(wsKey, event) {
|
|
405
450
|
var _a, _b, _c;
|
|
451
|
+
this.logger.trace(`resolveEmittableEvents(${wsKey}): `, event);
|
|
406
452
|
const results = [];
|
|
407
453
|
try {
|
|
408
454
|
// const parsed = JSON.parse(event.data);
|
|
@@ -415,8 +461,6 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
415
461
|
if (legacyContext) {
|
|
416
462
|
parsed.wsMarket = legacyContext.market;
|
|
417
463
|
}
|
|
418
|
-
// const eventType = eventType; //parsed?.stream;
|
|
419
|
-
// const eventOperation = parsed?.op;
|
|
420
464
|
const traceEmittable = false;
|
|
421
465
|
if (traceEmittable) {
|
|
422
466
|
this.logger.trace('resolveEmittableEvents', Object.assign(Object.assign({}, WS_LOGGER_CATEGORY), { wsKey,
|
|
@@ -433,9 +477,7 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
433
477
|
'pong',
|
|
434
478
|
];
|
|
435
479
|
// WS API response
|
|
436
|
-
// TODO: after
|
|
437
480
|
if (isWSAPIResponse) {
|
|
438
|
-
const retCode = parsed.status;
|
|
439
481
|
/**
|
|
440
482
|
* Responses to "subscribe" are quite basic, with no indication of errors (e.g. bad topic):
|
|
441
483
|
*
|
|
@@ -444,29 +486,31 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
444
486
|
* id: 1,
|
|
445
487
|
* };
|
|
446
488
|
*
|
|
447
|
-
*
|
|
489
|
+
* Currently there's no simple way to tell this apart from an actual WS API response with
|
|
490
|
+
* error. So subscribe/unsubscribe requests will simply look like a WS API response internally,
|
|
491
|
+
* but that will not affect usage.
|
|
448
492
|
*
|
|
449
|
-
*
|
|
493
|
+
* Unrelated, example wsapi error for reference:
|
|
450
494
|
*
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
495
|
+
{
|
|
496
|
+
id: 1,
|
|
497
|
+
status: 400,
|
|
498
|
+
error: {
|
|
499
|
+
code: -1021,
|
|
500
|
+
msg: "Timestamp for this request was 1000ms ahead of the server's time."
|
|
501
|
+
},
|
|
502
|
+
rateLimits: [
|
|
503
|
+
{
|
|
504
|
+
rateLimitType: 'REQUEST_WEIGHT',
|
|
505
|
+
interval: 'MINUTE',
|
|
506
|
+
intervalNum: 1,
|
|
507
|
+
limit: 6000,
|
|
508
|
+
count: 4
|
|
509
|
+
}
|
|
510
|
+
],
|
|
511
|
+
wsKey: 'mainWSAPI',
|
|
512
|
+
isWSAPIResponse: true
|
|
513
|
+
}
|
|
470
514
|
*/
|
|
471
515
|
const isError = typeof ((_a = parsed.error) === null || _a === void 0 ? void 0 : _a.code) === 'number' && ((_b = parsed.error) === null || _b === void 0 ? void 0 : _b.code) !== 0;
|
|
472
516
|
// This is the counterpart to getPromiseRefForWSAPIRequest
|
|
@@ -488,6 +532,7 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
488
532
|
wsKey,
|
|
489
533
|
promiseRef,
|
|
490
534
|
parsedEvent: parsed,
|
|
535
|
+
e,
|
|
491
536
|
});
|
|
492
537
|
}
|
|
493
538
|
results.push({
|
|
@@ -499,7 +544,7 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
499
544
|
}
|
|
500
545
|
// authenticated
|
|
501
546
|
if ((_c = parsed.result) === null || _c === void 0 ? void 0 : _c.apiKey) {
|
|
502
|
-
// Note: Without this check, this will also trigger "
|
|
547
|
+
// Note: Without this check, this will also trigger "onWsAuthenticated()" for session.status requests
|
|
503
548
|
if (this.getWsStore().getAuthenticationInProgressPromise(wsKey)) {
|
|
504
549
|
results.push({
|
|
505
550
|
eventType: 'authenticated',
|
|
@@ -517,13 +562,15 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
517
562
|
wsKey,
|
|
518
563
|
promiseRef,
|
|
519
564
|
parsedEvent: parsed,
|
|
565
|
+
e,
|
|
520
566
|
});
|
|
521
567
|
}
|
|
522
568
|
results.push({
|
|
523
569
|
eventType: 'response',
|
|
524
|
-
event: parsed,
|
|
570
|
+
event: Object.assign(Object.assign({}, parsed), { request: this.getCachedMidFlightRequest(wsKey, reqId) }),
|
|
525
571
|
isWSAPIResponse: isWSAPIResponse,
|
|
526
572
|
});
|
|
573
|
+
this.removeCachedMidFlightRequest(wsKey, reqId);
|
|
527
574
|
return results;
|
|
528
575
|
}
|
|
529
576
|
// Handle incoming event that listen key expired
|
|
@@ -542,7 +589,7 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
542
589
|
if (this.options.beautify) {
|
|
543
590
|
const beautifiedMessage = this.beautifier.beautifyWsMessage(parsed, eventType, false,
|
|
544
591
|
// Suffix all events for the beautifier, if market is options
|
|
545
|
-
// Options has some conflicting keys with different intentions
|
|
592
|
+
// Options has some conflicting keys with different intentions, so will be suffixed
|
|
546
593
|
wsKey === 'eoptions' ? 'Options' : '');
|
|
547
594
|
results.push({
|
|
548
595
|
eventType: 'formattedMessage',
|
|
@@ -551,19 +598,7 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
551
598
|
});
|
|
552
599
|
// emit an additional event for user data messages
|
|
553
600
|
if (!Array.isArray(beautifiedMessage) && eventType) {
|
|
554
|
-
if (
|
|
555
|
-
'balanceUpdate',
|
|
556
|
-
'executionReport',
|
|
557
|
-
'listStatus',
|
|
558
|
-
'listenKeyExpired',
|
|
559
|
-
'outboundAccountPosition',
|
|
560
|
-
'ACCOUNT_CONFIG_UPDATE',
|
|
561
|
-
'ACCOUNT_UPDATE',
|
|
562
|
-
'MARGIN_CALL',
|
|
563
|
-
'ORDER_TRADE_UPDATE',
|
|
564
|
-
'TRADE_LITE',
|
|
565
|
-
'CONDITIONAL_ORDER_TRIGGER_REJECT',
|
|
566
|
-
].includes(eventType)) {
|
|
601
|
+
if (websocket_util_1.EVENT_TYPES_USER_DATA.includes(eventType)) {
|
|
567
602
|
results.push({
|
|
568
603
|
eventType: 'formattedUserDataMessage',
|
|
569
604
|
event: beautifiedMessage,
|
|
@@ -748,7 +783,6 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
748
783
|
}
|
|
749
784
|
triggerCustomReconnectionWorkflow(legacyWsKey) {
|
|
750
785
|
return __awaiter(this, void 0, void 0, function* () {
|
|
751
|
-
console.log(`triggerCustomReconnectionWorkflow(${legacyWsKey})`);
|
|
752
786
|
if (legacyWsKey.includes('userData')) {
|
|
753
787
|
return this.getUserDataStreamManager().triggerUserDataReconnectionWorkflow(legacyWsKey);
|
|
754
788
|
}
|
|
@@ -790,13 +824,13 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
790
824
|
ws = yield this.subscribeUsdFuturesUserDataStream(realWsKey, forceNewConnection, miscConnectionState);
|
|
791
825
|
break;
|
|
792
826
|
case 'usdmTestnet':
|
|
793
|
-
ws = yield this.subscribeUsdFuturesUserDataStream(
|
|
827
|
+
ws = yield this.subscribeUsdFuturesUserDataStream(realWsKey, forceNewConnection, miscConnectionState);
|
|
794
828
|
break;
|
|
795
829
|
case 'coinm':
|
|
796
830
|
ws = yield this.subscribeCoinFuturesUserDataStream(realWsKey, forceNewConnection, miscConnectionState);
|
|
797
831
|
break;
|
|
798
832
|
case 'coinmTestnet':
|
|
799
|
-
ws = yield this.subscribeCoinFuturesUserDataStream(
|
|
833
|
+
ws = yield this.subscribeCoinFuturesUserDataStream(realWsKey, forceNewConnection, miscConnectionState);
|
|
800
834
|
break;
|
|
801
835
|
case 'portfoliom':
|
|
802
836
|
ws = yield this.subscribePortfolioMarginUserDataStream(realWsKey, forceNewConnection, miscConnectionState);
|
|
@@ -849,7 +883,7 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
849
883
|
forceNewConnection, miscState) {
|
|
850
884
|
try {
|
|
851
885
|
const isTestnet = wsKey === websocket_util_1.WS_KEY_MAP.usdmTestnet;
|
|
852
|
-
const restClient = this.restClientCache.getUSDMRestClient(this.getRestClientOptions(), this.options.requestOptions
|
|
886
|
+
const restClient = this.restClientCache.getUSDMRestClient(this.getRestClientOptions(), this.options.requestOptions);
|
|
853
887
|
const { listenKey } = yield restClient.getFuturesUserDataListenKey();
|
|
854
888
|
const market = isTestnet ? 'usdmTestnet' : 'usdm';
|
|
855
889
|
return this.getUserDataStreamManager().subscribeGeneralUserDataStreamWithListenKey(wsKey, market, listenKey, forceNewConnection, miscState);
|
|
@@ -861,6 +895,18 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
861
895
|
}
|
|
862
896
|
});
|
|
863
897
|
}
|
|
898
|
+
closeUserDataStream() {
|
|
899
|
+
return __awaiter(this, arguments, void 0, function* (wsKey = 'usdm') {
|
|
900
|
+
const wsKeys = this.getWsStore().getKeys();
|
|
901
|
+
const userDataWsKey = wsKeys.find((key) => {
|
|
902
|
+
return key === wsKey || key.startsWith(wsKey + '_userData');
|
|
903
|
+
});
|
|
904
|
+
if (!userDataWsKey) {
|
|
905
|
+
throw new Error(`No matching connection found with wsKey "${wsKey}". Active connections: [${JSON.stringify(wsKey)}]`);
|
|
906
|
+
}
|
|
907
|
+
this.close(userDataWsKey);
|
|
908
|
+
});
|
|
909
|
+
}
|
|
864
910
|
/**
|
|
865
911
|
* Subscribe to COIN-M Futures user data stream - listen key is automatically generated.
|
|
866
912
|
*/
|
|
@@ -870,7 +916,7 @@ class WebsocketClient extends BaseWSClient_1.BaseWebsocketClient {
|
|
|
870
916
|
try {
|
|
871
917
|
const isTestnet = wsKey === websocket_util_1.WS_KEY_MAP.coinmTestnet;
|
|
872
918
|
const { listenKey } = yield this.restClientCache
|
|
873
|
-
.getCOINMRestClient(this.getRestClientOptions(), this.options.requestOptions
|
|
919
|
+
.getCOINMRestClient(this.getRestClientOptions(), this.options.requestOptions)
|
|
874
920
|
.getFuturesUserDataListenKey();
|
|
875
921
|
const market = isTestnet ? 'coinmTestnet' : 'coinm';
|
|
876
922
|
return this.getUserDataStreamManager().subscribeGeneralUserDataStreamWithListenKey(wsKey, market, listenKey, forceNewConnection, miscState);
|