binance
Version:
Professional Node.js & JavaScript SDK for Binance REST APIs & WebSockets, with TypeScript & end-to-end tests.
415 lines • 15.6 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getOrderIdPrefix = getOrderIdPrefix;
exports.generateNewOrderId = generateNewOrderId;
exports.getBaseURLKeyForWsKey = getBaseURLKeyForWsKey;
exports.requiresWSAPINewClientOID = requiresWSAPINewClientOID;
exports.validateWSAPINewClientOID = validateWSAPINewClientOID;
exports.serialiseParams = serialiseParams;
exports.getRESTRequestSignature = getRESTRequestSignature;
exports.getServerTimeEndpoint = getServerTimeEndpoint;
exports.getTestnetBaseUrlKey = getTestnetBaseUrlKey;
exports.getRestBaseUrl = getRestBaseUrl;
exports.isPublicEndpoint = isPublicEndpoint;
exports.isWsPong = isWsPong;
exports.logInvalidOrderId = logInvalidOrderId;
exports.appendEventIfMissing = appendEventIfMissing;
exports.asArray = asArray;
const nanoid_1 = require("nanoid");
const node_support_1 = require("./node-support");
const typeGuards_1 = require("./typeGuards");
const websocket_util_1 = require("./websockets/websocket-util");
// function throwUnhandledSwitch(x: never, msg: string): never {
// throw new Error(msg);
// }
function getOrderIdPrefix(network) {
switch (network) {
case 'spot':
case 'spot1':
case 'spot2':
case 'spot3':
case 'spot4':
return 'U5D79M5B';
case 'usdm':
case 'usdmtest':
case 'coinm':
case 'coinmtest':
case 'papi':
return '15PC4ZJy';
case 'voptions':
case 'voptionstest':
return '';
default:
// throwUnhandledSwitch(network, `"${network}" unhandled`);
return 'U5D79M5B';
}
}
function generateNewOrderId(network) {
const id = (0, nanoid_1.nanoid)(22); // must pass ^[\.A-Z\:/a-z0-9_-]{1,32}$ with prefix
const prefixedId = 'x-' + getOrderIdPrefix(network) + id;
return prefixedId;
}
function getBaseURLKeyForWsKey(wsKey) {
switch (wsKey) {
case websocket_util_1.WS_KEY_MAP.mainWSAPI:
case websocket_util_1.WS_KEY_MAP.mainWSAPI2:
case websocket_util_1.WS_KEY_MAP.mainWSAPITestnet: {
return 'spot';
}
case websocket_util_1.WS_KEY_MAP.usdmWSAPI:
case websocket_util_1.WS_KEY_MAP.usdmWSAPITestnet: {
return 'usdm';
}
default: {
return 'spot';
}
}
}
function getWSAPINewOrderIdProperties(operation, wsKey) {
//
switch (wsKey) {
case websocket_util_1.WS_KEY_MAP.mainWSAPI:
case websocket_util_1.WS_KEY_MAP.mainWSAPI2:
case websocket_util_1.WS_KEY_MAP.mainWSAPITestnet:
case websocket_util_1.WS_KEY_MAP.usdmWSAPI:
case websocket_util_1.WS_KEY_MAP.usdmWSAPITestnet:
case websocket_util_1.WS_KEY_MAP.coinmWSAPI:
case websocket_util_1.WS_KEY_MAP.coinmWSAPITestnet: {
if (['order.place', 'order.amend.keepPriority', 'sor.order.place'].includes(operation)) {
return ['newClientOrderId'];
}
if (operation === 'orderList.place') {
return ['listClientOrderId', 'limitClientOrderId', 'stopClientOrderId'];
}
if (operation === 'orderList.place.oco') {
return [
'listClientOrderId',
'aboveClientOrderId',
'belowClientOrderId',
];
}
if (operation === 'orderList.place.oto') {
return [
'listClientOrderId',
'workingClientOrderId',
'pendingClientOrderId',
];
}
if (operation === 'orderList.place.otoco') {
return [
'listClientOrderId',
'workingClientOrderId',
'pendingAboveClientOrderId',
'pendingBelowClientOrderId',
];
}
if (operation === 'orderList.place.opo') {
return [
'listClientOrderId',
'workingClientOrderId',
'pendingClientOrderId',
];
}
if (operation === 'orderList.place.opoco') {
return [
'listClientOrderId',
'workingClientOrderId',
'pendingAboveClientOrderId',
'pendingBelowClientOrderId',
];
}
return [];
}
default: {
return [];
}
}
}
function requiresWSAPINewClientOID(request, wsKey) {
switch (wsKey) {
case websocket_util_1.WS_KEY_MAP.mainWSAPI:
case websocket_util_1.WS_KEY_MAP.mainWSAPI2:
case websocket_util_1.WS_KEY_MAP.mainWSAPITestnet:
case websocket_util_1.WS_KEY_MAP.usdmWSAPI:
case websocket_util_1.WS_KEY_MAP.usdmWSAPITestnet:
case websocket_util_1.WS_KEY_MAP.coinmWSAPI:
case websocket_util_1.WS_KEY_MAP.coinmWSAPITestnet: {
return [
'order.place',
'order.amend.keepPriority',
'sor.order.place',
'orderList.place',
'orderList.place.oco',
'orderList.place.oto',
'orderList.place.otoco',
'orderList.place.opo',
'orderList.place.opoco',
].includes(request.method);
}
case websocket_util_1.WS_KEY_MAP.main:
case websocket_util_1.WS_KEY_MAP.main2:
case websocket_util_1.WS_KEY_MAP.main3:
case websocket_util_1.WS_KEY_MAP.mainTestnetPublic:
case websocket_util_1.WS_KEY_MAP.mainTestnetUserData:
case websocket_util_1.WS_KEY_MAP.marginRiskUserData:
case websocket_util_1.WS_KEY_MAP.usdm:
case websocket_util_1.WS_KEY_MAP.usdmTestnet:
case websocket_util_1.WS_KEY_MAP.coinm:
case websocket_util_1.WS_KEY_MAP.coinm2:
case websocket_util_1.WS_KEY_MAP.coinmTestnet:
case websocket_util_1.WS_KEY_MAP.eoptions:
case websocket_util_1.WS_KEY_MAP.portfolioMarginUserData:
case websocket_util_1.WS_KEY_MAP.portfolioMarginProUserData:
return false;
default: {
throw (0, typeGuards_1.neverGuard)(wsKey, `Unhandled WsKey "${wsKey}"`);
}
}
}
function validateWSAPINewClientOID(request, wsKey) {
if (!requiresWSAPINewClientOID(request, wsKey) || !request.params) {
return;
}
const newClientOIDProperties = getWSAPINewOrderIdProperties(request.method, wsKey);
if (!newClientOIDProperties.length) {
return;
}
const baseUrlKey = getBaseURLKeyForWsKey(wsKey);
for (const orderIdProperty of newClientOIDProperties) {
if (!request.params[orderIdProperty]) {
request.params[orderIdProperty] = generateNewOrderId(baseUrlKey);
continue;
}
const expectedOrderIdPrefix = `x-${getOrderIdPrefix(baseUrlKey)}`;
if (!request.params[orderIdProperty].startsWith(expectedOrderIdPrefix)) {
logInvalidOrderId(orderIdProperty, expectedOrderIdPrefix, request.params);
}
}
}
function serialiseParams(params = {}, strict_validation = false, encodeValues = false, filterUndefinedParams = false) {
const paramKeys = !filterUndefinedParams
? Object.keys(params)
: Object.keys(params).filter((key) => typeof params[key] !== 'undefined');
return paramKeys
.map((key) => {
const value = params[key];
if (strict_validation === true && typeof value === 'undefined') {
throw new Error('Failed to sign API request due to undefined parameter');
}
const encodedValue = encodeValues ? encodeURIComponent(value) : value;
return `${key}=${encodedValue}`;
})
.join('&');
}
function getRESTRequestSignature(data, options, key, secret, timestamp) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
const { recvWindow, strictParamValidation, filterUndefinedParams } = options;
// Optional, set to 5000 by default. Increase if timestamp/recvWindow errors are seen.
const requestRecvWindow = (_b = (_a = data === null || data === void 0 ? void 0 : data.recvWindow) !== null && _a !== void 0 ? _a : recvWindow) !== null && _b !== void 0 ? _b : 5000;
if (key && secret) {
const requestParams = Object.assign(Object.assign({}, data), { timestamp, recvWindow: requestRecvWindow });
const signMethod = 'hex';
const signAlgorithm = 'SHA-256';
const serialisedParams = serialiseParams(requestParams, strictParamValidation, true, filterUndefinedParams);
let signature;
if (typeof options.customSignMessageFn === 'function') {
signature = yield options.customSignMessageFn(serialisedParams, secret);
}
else {
signature = yield (0, node_support_1.signMessage)(serialisedParams, secret, signMethod, signAlgorithm);
signature = encodeURIComponent(signature);
}
requestParams.signature = signature;
return {
requestBody: Object.assign({}, data),
serialisedParams,
timestamp: timestamp,
signature: signature,
recvWindow: requestRecvWindow,
};
}
return { requestBody: data, serialisedParams: undefined };
});
}
const BINANCE_BASE_URLS = {
// spot/margin/savings/mining
spot: 'https://api.binance.com',
spot1: 'https://api.binance.com',
spot2: 'https://api1.binance.com',
spot3: 'https://api2.binance.com',
spot4: 'https://api3.binance.com',
spottest: 'https://testnet.binance.vision',
// USDM Futures
usdm: 'https://fapi.binance.com',
usdmtest: 'https://testnet.binancefuture.com',
// COINM Futures
coinm: 'https://dapi.binance.com',
coinmtest: 'https://testnet.binancefuture.com',
// Vanilla Options
voptions: 'https://vapi.binance.com',
voptionstest: 'https://testnet.binanceops.com',
// Portfolio Margin
papi: 'https://papi.binance.com',
// www - for alpha
www: 'https://www.binance.com',
};
const BINANCE_MM_BASE_URLS = {
// spot/margin/savings/mining - no MM endpoints
spot: undefined,
spot1: undefined,
spot2: undefined,
spot3: undefined,
spot4: undefined,
spottest: undefined,
// USDM Futures MM endpoints
usdm: 'https://fapi-mm.binance.com',
usdmtest: undefined, // No MM endpoint for testnet
// COINM Futures MM endpoints
coinm: 'https://dapi-mm.binance.com',
coinmtest: undefined, // No MM endpoint for testnet
// Vanilla Options - no MM endpoints
voptions: undefined,
voptionstest: undefined,
// Portfolio Margin - no MM endpoints
papi: undefined,
// www - no MM endpoints
www: undefined,
};
function getServerTimeEndpoint(urlKey) {
switch (urlKey) {
case 'spot':
case 'spot1':
case 'spot2':
case 'spot3':
case 'spot4':
case 'spottest':
default:
return 'api/v3/time';
case 'usdm':
case 'usdmtest':
return 'fapi/v1/time';
case 'coinm':
case 'coinmtest':
return 'dapi/v1/time';
case 'voptions':
case 'voptionstest':
return 'vapi/v1/time';
}
}
function getTestnetBaseUrlKey(urlKey) {
switch (urlKey) {
case 'spot':
case 'spot1':
case 'spot2':
case 'spot3':
case 'spot4':
case 'spottest':
default:
return 'spottest';
case 'usdm':
case 'usdmtest':
return 'usdmtest';
case 'coinm':
case 'coinmtest':
return 'coinmtest';
case 'voptions':
case 'voptionstest':
return 'voptionstest';
}
}
function getRestBaseUrl(clientType, restClientOptions) {
if (restClientOptions.baseUrl) {
return restClientOptions.baseUrl;
}
const urlKey = restClientOptions.baseUrlKey || clientType;
// Use MM endpoints if requested and available
if (restClientOptions.useMMSubdomain) {
const mmUrl = BINANCE_MM_BASE_URLS[urlKey];
if (mmUrl) {
return mmUrl;
}
}
return BINANCE_BASE_URLS[urlKey];
}
function isPublicEndpoint(endpoint) {
if (endpoint.startsWith('v2/public')) {
return true;
}
if (endpoint.startsWith('public/linear')) {
return true;
}
return false;
}
function isWsPong(response) {
return (response.request &&
response.request.op === 'ping' &&
response.ret_msg === 'pong' &&
response.success === true);
}
function logInvalidOrderId(orderIdProperty, expectedOrderIdPrefix, params) {
console.warn(`WARNING: '${orderIdProperty}' invalid - it should be prefixed with ${expectedOrderIdPrefix}. Use the 'client.generateNewOrderId()' REST client utility method to generate a fresh order ID on demand. Original request: ${JSON.stringify(params)}`);
}
/**
* For some topics, the received event does not include any information on the topic the event is for (e.g. book tickers).
*
* This method extracts this using available context, to add an "eventType" property if missing.
*
* - For the old WebsocketClient, this is extracted using the WsKey.
* - For the new multiplex Websocketclient, this is extracted using the "stream" parameter.
*/
function appendEventIfMissing(wsMsg, wsKey, eventType) {
if (wsMsg.e) {
return;
}
if (eventType) {
if (!Array.isArray(wsMsg)) {
wsMsg.e = eventType;
return;
}
for (const key in wsMsg) {
wsMsg[key].e = eventType;
}
return;
}
// Multiplex websockets include the eventType as the stream name
if (wsMsg.stream && wsMsg.data) {
const eventType = (0, websocket_util_1.parseEventTypeFromMessage)(wsKey, wsMsg);
if (eventType) {
if (Array.isArray(wsMsg.data)) {
for (const key in wsMsg.data) {
wsMsg.data[key].streamName = wsMsg.stream;
wsMsg.data[key].e = eventType;
}
return;
}
}
}
if (wsKey.indexOf('bookTicker') !== -1) {
wsMsg.e = 'bookTicker';
return;
}
if (wsKey.indexOf('diffBookDepth') !== -1) {
wsMsg.e = 'diffBookDepth';
return;
}
if (wsKey.indexOf('partialBookDepth') !== -1 ||
wsKey.indexOf('depth') !== -1) {
wsMsg.e = 'partialBookDepth';
return;
}
// console.warn('couldnt derive event type: ', wsKey);
}
function asArray(el) {
return Array.isArray(el) ? el : [el];
}
//# sourceMappingURL=requestUtils.js.map