feat(v4.1.8): introduce dedicated WebsocketAPIClient
This commit is contained in:
127
README.md
127
README.md
@@ -1,4 +1,4 @@
|
|||||||
# Node.js & JavaScript SDK for Bybit REST API & WebSockets
|
# Node.js & JavaScript SDK for Bybit REST API, WebSocket API & WebSocket Events
|
||||||
|
|
||||||
[](https://github.com/tiagosiebler/bybit-api/actions/workflows/e2etest.yml)
|
[](https://github.com/tiagosiebler/bybit-api/actions/workflows/e2etest.yml)
|
||||||
[][1]
|
[][1]
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
[1]: https://www.npmjs.com/package/bybit-api
|
[1]: https://www.npmjs.com/package/bybit-api
|
||||||
|
|
||||||
Professional Node.js, JavaScript & TypeScript SDK for the Bybit REST APIs and WebSockets:
|
Professional Node.js, JavaScript & TypeScript SDK for the Bybit REST APIs, WebSocket APIs & WebSocket Events:
|
||||||
|
|
||||||
- Complete integration with all Bybit REST APIs & WebSockets, including the WebSocket API.
|
- Complete integration with all Bybit REST APIs & WebSockets, including the WebSocket API.
|
||||||
- Actively maintained with a modern, promise-driven interface.
|
- Actively maintained with a modern, promise-driven interface.
|
||||||
@@ -29,22 +29,22 @@ Professional Node.js, JavaScript & TypeScript SDK for the Bybit REST APIs and We
|
|||||||
- Proxy support via axios integration.
|
- Proxy support via axios integration.
|
||||||
- Robust WebSocket consumer integration with configurable heartbeats & automatic reconnect then resubscribe workflows.
|
- Robust WebSocket consumer integration with configurable heartbeats & automatic reconnect then resubscribe workflows.
|
||||||
- Event driven messaging
|
- Event driven messaging
|
||||||
- Smart websocket persistence
|
- Smart WebSocket persistence
|
||||||
- Automatically handle silent websocket disconnections through timed heartbeats, including the scheduled 24hr disconnect.
|
- Automatically handle silent websocket disconnections through timed heartbeats, including the scheduled 24hr disconnect.
|
||||||
- Automatically handle authentication.
|
- Automatically handle authentication.
|
||||||
- Emit `reconnected` event when dropped connection is restored.
|
- Emit `reconnected` event when dropped connection is restored.
|
||||||
- WebSocket API integration, with two design patterns to choose from:
|
- WebSocket API integration, with two design patterns to choose from:
|
||||||
- Asynchronous promise-driven responses:
|
1. Asynchronous **promise**-driven responses:
|
||||||
- Make requests like a REST API, using the WebSocket API. No need to subscribe to asynchronous events.
|
- Make requests like a REST API, using the WebSocket API. No need to subscribe to asynchronous events.
|
||||||
- Send commands with the await sendWSAPIRequest(...) method.
|
- Import the `WebsocketAPIClient` and use it like the REST API client. Call functions and await responses.
|
||||||
- Await responses to commands directly in the fully typed sendWSAPIRequest() call.
|
- See example for more details: [examples/ws-api-client.ts](./examples/ws-api-client.ts).
|
||||||
- The method directly returns a promise. Use a try/catch block for convenient error handling without the complexity of asynchronous WebSockets.
|
- Prefer something more raw? Use the `sendWSAPIRequest(...)` method and await responses
|
||||||
- See example for more details: [examples/ws-api-promises.ts](./examples/ws-api-promises.ts)
|
- See example for more details: [examples/ws-api-raw-promises.ts](./examples/ws-api-raw-promises.ts)
|
||||||
- Asynchronous event-driven responses:
|
2. Asynchronous **event**-driven responses:
|
||||||
- Subscribe to `response` and `error` events from WebsocketClient's event emitter.
|
- Subscribe to `response` and `error` events from WebsocketClient's event emitter.
|
||||||
- Send commands with the sendWSAPIRequest(...) method.
|
- Send commands with the `sendWSAPIRequest(...)` method.
|
||||||
- Responses to commands will arrive via the `response` and `error` events.
|
- Responses to commands will arrive via the `response` and `error` events emitted by the client.
|
||||||
- See example for more details: [examples/ws-api-events.ts](./examples/ws-api-events.ts)
|
- See example for more details: [examples/ws-api-raw-events.ts](./examples/ws-api-raw-events.ts)
|
||||||
- Active community support & collaboration in telegram: [Node.js Algo Traders](https://t.me/nodetraders).
|
- Active community support & collaboration in telegram: [Node.js Algo Traders](https://t.me/nodetraders).
|
||||||
|
|
||||||
# Table of Contents
|
# Table of Contents
|
||||||
@@ -153,6 +153,7 @@ Here are the available REST clients and the corresponding API groups described i
|
|||||||
| [ **V5 API** ] | The new unified V5 APIs (successor to previously fragmented APIs for all API groups). |
|
| [ **V5 API** ] | The new unified V5 APIs (successor to previously fragmented APIs for all API groups). |
|
||||||
| [RestClientV5](src/rest-client-v5.ts) | Unified V5 all-in-one REST client for all [V5 REST APIs](https://bybit-exchange.github.io/docs/v5/intro) |
|
| [RestClientV5](src/rest-client-v5.ts) | Unified V5 all-in-one REST client for all [V5 REST APIs](https://bybit-exchange.github.io/docs/v5/intro) |
|
||||||
| [WebsocketClient](src/websocket-client.ts) | All WebSocket features (Public & Private consumers for all API categories & the WebSocket API) |
|
| [WebsocketClient](src/websocket-client.ts) | All WebSocket features (Public & Private consumers for all API categories & the WebSocket API) |
|
||||||
|
| [WebsocketAPIClient](src/websocket-api-client.ts) | Use the WebSocket API like a REST API. Call functions and await responses, powered by WebSockets. |
|
||||||
|
|
||||||
|
|
||||||
## REST API Usage
|
## REST API Usage
|
||||||
@@ -398,36 +399,41 @@ ws.on('reconnected', (data) => {
|
|||||||
|
|
||||||
## Websocket API - Sending orders via WebSockets
|
## Websocket API - Sending orders via WebSockets
|
||||||
|
|
||||||
Bybit supports sending, amending and cancelling orders over a WebSocket connection. The [WebsocketClient](./src/WebsocketClient.ts) fully supports Bybit's WebSocket API via the `sendWSAPIRequest(...)` method.
|
Bybit supports sending, amending and cancelling orders over a WebSocket connection. The [WebsocketClient](./src/WebsocketClient.ts) fully supports Bybit's WebSocket API via the `sendWSAPIRequest(...)` method. There is also a dedicated [WebsocketAPIClient](./src/websocket-api-client.ts), built over the WSClient's sendWSAPIRequest mechanism for a simpler experience.
|
||||||
|
|
||||||
Links for reference:
|
Links for reference:
|
||||||
- [Bybit WebSocket API Documentation](https://bybit-exchange.github.io/docs/v5/websocket/trade/guideline)
|
- [Bybit WebSocket API Documentation](https://bybit-exchange.github.io/docs/v5/websocket/trade/guideline)
|
||||||
- [WebSocket API Example Node.js/TypeScript/JavaScript](./examples/ws-api-promises.ts).
|
- [WebsocketAPIClient example, use the Websocket API like a REST API](./examples/ws-api-client.ts)
|
||||||
|
- [Raw Asynchronous Websocket API Node.js/TypeScript/JavaScript example](./examples/ws-api-raw-promises.ts)
|
||||||
|
|
||||||
Note: as of January 2025, the demo trading environment does not support the WebSocket API.
|
Note: as of January 2025, the demo trading environment does not support the WebSocket API.
|
||||||
|
|
||||||
There are two ways to use the WS API, depending on individual preference:
|
There are two ways to use the WS API, depending on individual preference:
|
||||||
- event-driven:
|
1. event-driven:
|
||||||
- send requests via `client.sendWSAPIRequest(wsKey, operation, params)`, fire and forget
|
- send requests via `client.sendWSAPIRequest(wsKey, operation, params)`, fire and forget
|
||||||
- handle async replies via event handlers on `client.on('exception', cb)` and `client.on('response', cb)`
|
- handle async replies via event handlers on `client.on('exception', cb)` and `client.on('response', cb)`
|
||||||
- See example for more details: [examples/ws-api-events.ts](./examples/ws-api-events.ts)
|
- See example for more details: [examples/ws-api-raw-events.ts](./examples/ws-api-raw-events.ts)
|
||||||
- promise-driven:
|
2. promise-driven:
|
||||||
- send requests via `const result = await client.sendWSAPIRequest(wsKey, operation, params)`, which returns a promise
|
- import the `WebsocketAPIClient` and use it much like a REST API.
|
||||||
- await each call
|
- make an instance & call the Websocket API with a function.
|
||||||
- use try/catch blocks to handle promise rejections
|
- await responses, much like a REST API.
|
||||||
- See example for more details: [examples/ws-api-promises.ts](./examples/ws-api-promises.ts)
|
- use try/catch blocks to handle promise rejections
|
||||||
|
- See example for more details: [examples/ws-api-client.ts](./examples/ws-api-client.ts)
|
||||||
|
|
||||||
The below example demonstrates the promise-driven approach, which behaves similar to a REST API. The WebSocket API even accepts the same parameters as the corresponding REST API endpoints, so this approach should be compatible with existing REST implementations. Connectivity, authentication, and processing requests wrapped in promises - these are all handled automatically by the WebsocketClient without additional configuration.
|
The below example demonstrates the promise-driven approach, which behaves similar to a REST API. The WebSocket API even accepts the same parameters as the corresponding REST API endpoints, so this approach should be compatible with existing REST implementations.
|
||||||
|
|
||||||
|
Connectivity, authentication and connecting requests & responses to promises - these are all handled automatically without additional configuration by the WebsocketClient. The WebsocketAPIClient is a wrapper built on top of this, providing dedicated methods for every available Websocket API command. Each method has fully typed requests & responses. Benefit from the capabilities of the WebSocket API without the complexity of managing asynchronous messaging over WebSockets.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const { WS_KEY_MAP, WebsocketClient } = require('bybit-api');
|
const { WS_KEY_MAP, WebsocketClient } = require('bybit-api');
|
||||||
|
|
||||||
// or
|
// or
|
||||||
// import { WS_KEY_MAP, WebsocketClient } from 'bybit-api';
|
// import { WS_KEY_MAP, WebsocketAPIClient } from 'bybit-api';
|
||||||
|
|
||||||
// Create an instance of the WebsocketClient.
|
// Create an instance of the WebsocketAPIClient. This is built on
|
||||||
// This will automatically handle connectivity and authentication for you.
|
// top of the WebsocketClient and will automatically handle WebSocket
|
||||||
const wsClient = new WebsocketClient(
|
// persistence and authentication for you.
|
||||||
|
const wsClient = new WebsocketAPIClient(
|
||||||
{
|
{
|
||||||
key: 'yourApiKeyHere',
|
key: 'yourApiKeyHere',
|
||||||
secret: 'yourApiSecretHere',
|
secret: 'yourApiSecretHere',
|
||||||
@@ -440,6 +446,10 @@ const wsClient = new WebsocketClient(
|
|||||||
// Note: As of Jan 2025, demo trading only supports consuming events, it does
|
// Note: As of Jan 2025, demo trading only supports consuming events, it does
|
||||||
// NOT support the WS API.
|
// NOT support the WS API.
|
||||||
// demoTrading: false,
|
// demoTrading: false,
|
||||||
|
|
||||||
|
// If you want your own event handlers instead of the default ones with logs,
|
||||||
|
// disable this setting and see ws-api-client example for more details.
|
||||||
|
// attachEventListeners: false
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -452,32 +462,47 @@ async function main() {
|
|||||||
* This is not necessary and will happen automatically when
|
* This is not necessary and will happen automatically when
|
||||||
* sending a command, if you aren't connected/authenticated yet.
|
* sending a command, if you aren't connected/authenticated yet.
|
||||||
*/
|
*/
|
||||||
// await wsClient.connectWSAPI();
|
// await wsClient.getWSClient().connectWSAPI();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('Step 1: Create an order');
|
console.log('Step 1: Create an order');
|
||||||
|
const response = await wsClient.submitNewOrder({
|
||||||
// The type for `wsAPISubmitOrderResult` is automatically
|
category: 'linear',
|
||||||
// resolved to `WSAPIResponse<OrderResultV5, "order.create">`
|
symbol: 'BTCUSDT',
|
||||||
const wsAPISubmitOrderResult = await wsClient.sendWSAPIRequest(
|
orderType: 'Limit',
|
||||||
WS_KEY_MAP.v5PrivateTrade,
|
qty: '0.001',
|
||||||
'order.create',
|
side: 'Buy',
|
||||||
{
|
price: '50000',
|
||||||
symbol: 'BTCUSDT',
|
});
|
||||||
side: 'Buy',
|
console.log('submitNewOrder response: ', response);
|
||||||
orderType: 'Limit',
|
|
||||||
price: '50000',
|
|
||||||
qty: '1',
|
|
||||||
category: 'linear',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
`Step 1: Order result (order ID: "${wsAPISubmitOrderResult.data.orderId}"): `,
|
|
||||||
wsAPISubmitOrderResult,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Step 1: Order submit exception: ', e);
|
console.log('submitNewOrder error: ', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('Step 2: Amend an order');
|
||||||
|
const response = await wsClient.amendOrder({
|
||||||
|
category: 'linear',
|
||||||
|
symbol: 'BTCUSDT',
|
||||||
|
orderId: 'b4b9e205-793c-4777-8112-0bf3c2d26b6e',
|
||||||
|
qty: '0.001',
|
||||||
|
price: '60000',
|
||||||
|
});
|
||||||
|
console.log('amendOrder response: ', response);
|
||||||
|
} catch (e) {
|
||||||
|
console.log('amendOrder error: ', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('Step 3: Cancel an order');
|
||||||
|
const response = await wsClient.cancelOrder({
|
||||||
|
category: 'linear',
|
||||||
|
symbol: 'BTCUSDT',
|
||||||
|
orderId: 'b4b9e205-793c-4777-8112-0bf3c2d26b6e',
|
||||||
|
});
|
||||||
|
console.log('cancelOrder response: ', response);
|
||||||
|
} catch (e) {
|
||||||
|
console.log('cancelOrder error: ', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,8 +511,6 @@ main();
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
See the [examples/ws-api-promises.ts](./examples/ws-api-promises.ts) example for a more detailed explanation.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Balancing load across multiple connections
|
### Balancing load across multiple connections
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DefaultLogger, WebsocketAPIClient } from '../src';
|
import { DefaultLogger, WebsocketAPIClient, WebsocketClient } from '../src';
|
||||||
|
|
||||||
// or
|
// or
|
||||||
// import { DefaultLogger, WebsocketAPIClient } from 'bybit-api';
|
// import { DefaultLogger, WebsocketAPIClient } from 'bybit-api';
|
||||||
@@ -6,26 +6,25 @@ import { DefaultLogger, WebsocketAPIClient } from '../src';
|
|||||||
const key = process.env.API_KEY_COM;
|
const key = process.env.API_KEY_COM;
|
||||||
const secret = process.env.API_SECRET_COM;
|
const secret = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
/* function attachEventHandlers<TWSClient extends WebsocketAPIClient>(
|
// function attachEventHandlers<TWSClient extends WebsocketClient>(
|
||||||
wsClient: TWSClient,
|
// wsClient: TWSClient,
|
||||||
): void {
|
// ): void {
|
||||||
wsClient.getWSClient().on('update', (data) => {
|
// wsClient.on('update', (data) => {
|
||||||
console.log('raw message received ', JSON.stringify(data));
|
// console.log('raw message received ', JSON.stringify(data));
|
||||||
});
|
// });
|
||||||
|
// wsClient.on('open', (data) => {
|
||||||
wsClient.getWSClient().on('open', (data) => {
|
// console.log('ws connected', data.wsKey);
|
||||||
console.log('ws connected', data.wsKey);
|
// });
|
||||||
});
|
// wsClient.on('reconnect', ({ wsKey }) => {
|
||||||
wsClient.getWSClient().on('reconnect', ({ wsKey }) => {
|
// console.log('ws automatically reconnecting.... ', wsKey);
|
||||||
console.log('ws automatically reconnecting.... ', wsKey);
|
// });
|
||||||
});
|
// wsClient.on('reconnected', (data) => {
|
||||||
wsClient.getWSClient().on('reconnected', (data) => {
|
// console.log('ws has reconnected ', data?.wsKey);
|
||||||
console.log('ws has reconnected ', data?.wsKey);
|
// });
|
||||||
});
|
// wsClient.on('authenticated', (data) => {
|
||||||
wsClient.getWSClient().on('authenticated', (data) => {
|
// console.log('ws has authenticated ', data?.wsKey);
|
||||||
console.log('ws has authenticated ', data?.wsKey);
|
// });
|
||||||
});
|
// }
|
||||||
} */
|
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
// Optional
|
// Optional
|
||||||
@@ -40,7 +39,11 @@ async function main() {
|
|||||||
key: key,
|
key: key,
|
||||||
secret: secret,
|
secret: secret,
|
||||||
// testnet: true, // Whether to use the testnet environment: https://testnet.bybit.com/app/user/api-management
|
// testnet: true, // Whether to use the testnet environment: https://testnet.bybit.com/app/user/api-management
|
||||||
// demoTrading: false, // note: As of Jan 2025, demo trading does NOT support the WS API
|
|
||||||
|
// Whether to use the livenet demo trading environment
|
||||||
|
// Note: As of Jan 2025, demo trading only supports consuming events, it does
|
||||||
|
// NOT support the WS API.
|
||||||
|
// demoTrading: false,
|
||||||
|
|
||||||
// If you want your own event handlers instead of the default ones with logs,
|
// If you want your own event handlers instead of the default ones with logs,
|
||||||
// disable this setting and see the `attachEventHandlers` example below:
|
// disable this setting and see the `attachEventHandlers` example below:
|
||||||
@@ -49,11 +52,16 @@ async function main() {
|
|||||||
logger, // Optional: inject a custom logger
|
logger, // Optional: inject a custom logger
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Optional, see above "attachEventListeners". Attach basic event handlers, so nothing is left unhandled
|
||||||
|
// attachEventHandlers(wsClient.getWSClient());
|
||||||
|
|
||||||
// Optional, if you see RECV Window errors, you can use this to manage time issues.
|
// Optional, if you see RECV Window errors, you can use this to manage time issues.
|
||||||
// ! However, make sure you sync your system clock first!
|
// ! However, make sure you sync your system clock first!
|
||||||
// https://github.com/tiagosiebler/awesome-crypto-examples/wiki/Timestamp-for-this-request-is-outside-of-the-recvWindow
|
// https://github.com/tiagosiebler/awesome-crypto-examples/wiki/Timestamp-for-this-request-is-outside-of-the-recvWindow
|
||||||
// wsClient.setTimeOffsetMs(-5000);
|
// wsClient.setTimeOffsetMs(-5000);
|
||||||
|
|
||||||
|
await wsClient.getWSClient().connectWSAPI();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await wsClient.submitNewOrder({
|
const response = await wsClient.submitNewOrder({
|
||||||
category: 'linear',
|
category: 'linear',
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ async function main() {
|
|||||||
* - Handle any exceptions in a catch block.
|
* - Handle any exceptions in a catch block.
|
||||||
*
|
*
|
||||||
* This is a more "raw" workflow in how WebSockets behave. For a more convenient & REST-like approach, using the
|
* This is a more "raw" workflow in how WebSockets behave. For a more convenient & REST-like approach, using the
|
||||||
* promise-driven interface is recommended. See the `ws-api-promises.ts` and `ws-api-client.ts` examples for a
|
* promise-driven interface is recommended. See the `ws-api-raw-promises.ts` and `ws-api-client.ts` examples for a
|
||||||
* demonstration you can compare.
|
* demonstration you can compare.
|
||||||
*
|
*
|
||||||
* Note: even without using promises, you should still tie on a .catch handler to each sendWSAPIRequest call, to prevent
|
* Note: even without using promises, you should still tie on a .catch handler to each sendWSAPIRequest call, to prevent
|
||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "bybit-api",
|
"name": "bybit-api",
|
||||||
"version": "4.1.7",
|
"version": "4.1.8",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "bybit-api",
|
"name": "bybit-api",
|
||||||
"version": "4.1.7",
|
"version": "4.1.8",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "bybit-api",
|
"name": "bybit-api",
|
||||||
"version": "4.1.7",
|
"version": "4.1.8",
|
||||||
"description": "Complete & robust Node.js SDK for Bybit's REST APIs and WebSockets, with TypeScript & strong end to end tests.",
|
"description": "Complete & robust Node.js SDK for Bybit's REST APIs and WebSockets, with TypeScript & strong end to end tests.",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "lib/index.d.ts",
|
"types": "lib/index.d.ts",
|
||||||
|
|||||||
@@ -379,11 +379,13 @@ export class WebsocketClient extends BaseWebsocketClient<
|
|||||||
await this.assertIsAuthenticated(wsKey);
|
await this.assertIsAuthenticated(wsKey);
|
||||||
this.logger.trace('sendWSAPIRequest()->assertIsAuthenticated() ok');
|
this.logger.trace('sendWSAPIRequest()->assertIsAuthenticated() ok');
|
||||||
|
|
||||||
|
const timestampMs = Date.now() + (this.getTimeOffsetMs() || 0);
|
||||||
|
|
||||||
const requestEvent: WSAPIRequest<TWSParams> = {
|
const requestEvent: WSAPIRequest<TWSParams> = {
|
||||||
reqId: this.getNewRequestId(),
|
reqId: this.getNewRequestId(),
|
||||||
header: {
|
header: {
|
||||||
'X-BAPI-RECV-WINDOW': `${this.options.recvWindow}`,
|
'X-BAPI-RECV-WINDOW': `${this.options.recvWindow}`,
|
||||||
'X-BAPI-TIMESTAMP': `${Date.now()}`,
|
'X-BAPI-TIMESTAMP': `${timestampMs}`,
|
||||||
Referer: APIID,
|
Referer: APIID,
|
||||||
},
|
},
|
||||||
op: operation,
|
op: operation,
|
||||||
@@ -415,7 +417,11 @@ export class WebsocketClient extends BaseWebsocketClient<
|
|||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
if (typeof e === 'string') {
|
if (typeof e === 'string') {
|
||||||
this.logger.error('unexpcted string', { e });
|
this.logger.error('Unexpected string thrown without Error object:', {
|
||||||
|
e,
|
||||||
|
wsKey,
|
||||||
|
signedEvent,
|
||||||
|
});
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
e.request = {
|
e.request = {
|
||||||
|
|||||||
Reference in New Issue
Block a user