@luxfi/dex 1.2.1

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.
@@ -0,0 +1,253 @@
1
+ "use strict";
2
+ /**
3
+ * Market data, liquidation, and settlement features for LX TypeScript SDK
4
+ */
5
+ var __importDefault = (this && this.__importDefault) || function (mod) {
6
+ return (mod && mod.__esModule) ? mod : { "default": mod };
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.MarketDataProviders = exports.LiquidationMonitor = exports.MarketDataClient = void 0;
10
+ const ws_1 = __importDefault(require("ws"));
11
+ class MarketDataClient {
12
+ constructor(jsonRpcClient) {
13
+ this.jsonRpc = jsonRpcClient;
14
+ }
15
+ /**
16
+ * Get market data from a specific source
17
+ */
18
+ async getMarketData(symbol, source) {
19
+ const result = await this.jsonRpc.call('market_data.get', {
20
+ symbol,
21
+ source
22
+ });
23
+ return {
24
+ name: result.name,
25
+ symbol: result.symbol,
26
+ price: result.price,
27
+ bid: result.bid,
28
+ ask: result.ask,
29
+ volume: result.volume,
30
+ latencyNs: result.latency_ns,
31
+ provider: result.provider
32
+ };
33
+ }
34
+ /**
35
+ * Get aggregated market data from all sources
36
+ */
37
+ async getAggregatedMarketData(symbol) {
38
+ const result = await this.jsonRpc.call('market_data.aggregate', {
39
+ symbol
40
+ });
41
+ return result.map((data) => ({
42
+ name: data.name,
43
+ symbol: data.symbol,
44
+ price: data.price,
45
+ bid: data.bid,
46
+ ask: data.ask,
47
+ volume: data.volume,
48
+ latencyNs: data.latency_ns,
49
+ provider: data.provider
50
+ }));
51
+ }
52
+ /**
53
+ * Get recent liquidations
54
+ */
55
+ async getLiquidations(symbol, limit = 100) {
56
+ const result = await this.jsonRpc.call('liquidations.get', {
57
+ symbol,
58
+ limit
59
+ });
60
+ return result.map((liq) => ({
61
+ userId: liq.user_id,
62
+ positionId: liq.position_id,
63
+ symbol: liq.symbol,
64
+ size: liq.size,
65
+ liquidationPrice: liq.liquidation_price,
66
+ markPrice: liq.mark_price,
67
+ status: liq.status,
68
+ timestamp: new Date(liq.timestamp)
69
+ }));
70
+ }
71
+ /**
72
+ * Get settlement batch information
73
+ */
74
+ async getSettlementBatch(batchId) {
75
+ const result = await this.jsonRpc.call('settlement.batch', {
76
+ batch_id: batchId
77
+ });
78
+ return {
79
+ batchId: result.batch_id,
80
+ orderIds: result.order_ids,
81
+ status: result.status,
82
+ txHash: result.tx_hash,
83
+ gasUsed: result.gas_used,
84
+ timestamp: new Date(result.timestamp)
85
+ };
86
+ }
87
+ /**
88
+ * Get margin information for a user
89
+ */
90
+ async getMarginInfo(userId) {
91
+ const result = await this.jsonRpc.call('margin.info', {
92
+ user_id: userId
93
+ });
94
+ return {
95
+ userId: result.user_id,
96
+ initialMargin: result.initial_margin,
97
+ maintenanceMargin: result.maintenance_margin,
98
+ marginRatio: result.margin_ratio,
99
+ freeMargin: result.free_margin,
100
+ marginLevel: result.margin_level
101
+ };
102
+ }
103
+ /**
104
+ * Check liquidation risk for a user
105
+ */
106
+ async checkLiquidationRisk(userId) {
107
+ const result = await this.jsonRpc.call('margin.liquidation_risk', {
108
+ user_id: userId
109
+ });
110
+ return {
111
+ userId: result.user_id,
112
+ riskLevel: result.risk_level,
113
+ marginLevel: result.margin_level,
114
+ liquidationPrice: result.liquidation_price,
115
+ timeToLiquidation: result.time_to_liquidation,
116
+ recommendations: result.recommendations || []
117
+ };
118
+ }
119
+ /**
120
+ * Get insurance fund status
121
+ */
122
+ async getInsuranceFundStatus() {
123
+ const result = await this.jsonRpc.call('insurance_fund.status');
124
+ return {
125
+ totalFund: result.total_fund,
126
+ availableFund: result.available_fund,
127
+ usedFund: result.used_fund,
128
+ pendingClaims: result.pending_claims,
129
+ lastUpdate: new Date(result.last_update)
130
+ };
131
+ }
132
+ /**
133
+ * Get list of available market data sources
134
+ */
135
+ async getMarketDataSources() {
136
+ return await this.jsonRpc.call('market_data.sources');
137
+ }
138
+ /**
139
+ * Get comprehensive market statistics
140
+ */
141
+ async getMarketStats(symbol) {
142
+ const result = await this.jsonRpc.call('market.stats', {
143
+ symbol
144
+ });
145
+ return {
146
+ symbol: result.symbol,
147
+ volume24h: result.volume_24h,
148
+ high24h: result.high_24h,
149
+ low24h: result.low_24h,
150
+ priceChange24h: result.price_change_24h,
151
+ priceChangePercent24h: result.price_change_percent_24h,
152
+ openInterest: result.open_interest,
153
+ fundingRate: result.funding_rate,
154
+ nextFundingTime: new Date(result.next_funding_time)
155
+ };
156
+ }
157
+ }
158
+ exports.MarketDataClient = MarketDataClient;
159
+ class LiquidationMonitor {
160
+ constructor(wsConnection) {
161
+ this.callbacks = new Map();
162
+ this.ws = wsConnection;
163
+ }
164
+ /**
165
+ * Set WebSocket connection
166
+ */
167
+ setWebSocket(ws) {
168
+ this.ws = ws;
169
+ }
170
+ /**
171
+ * Subscribe to liquidation events
172
+ */
173
+ subscribeLiquidations(callback) {
174
+ if (!this.callbacks.has('liquidations')) {
175
+ this.callbacks.set('liquidations', []);
176
+ }
177
+ this.callbacks.get('liquidations').push(callback);
178
+ if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
179
+ this.ws.send(JSON.stringify({
180
+ type: 'subscribe',
181
+ channel: 'liquidations'
182
+ }));
183
+ }
184
+ }
185
+ /**
186
+ * Subscribe to settlement events
187
+ */
188
+ subscribeSettlements(callback) {
189
+ if (!this.callbacks.has('settlements')) {
190
+ this.callbacks.set('settlements', []);
191
+ }
192
+ this.callbacks.get('settlements').push(callback);
193
+ if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
194
+ this.ws.send(JSON.stringify({
195
+ type: 'subscribe',
196
+ channel: 'settlements'
197
+ }));
198
+ }
199
+ }
200
+ /**
201
+ * Subscribe to margin call events for a user
202
+ */
203
+ subscribeMarginCalls(userId, callback) {
204
+ const channel = `margin_calls:${userId}`;
205
+ if (!this.callbacks.has(channel)) {
206
+ this.callbacks.set(channel, []);
207
+ }
208
+ this.callbacks.get(channel).push(callback);
209
+ if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
210
+ this.ws.send(JSON.stringify({
211
+ type: 'subscribe',
212
+ channel
213
+ }));
214
+ }
215
+ }
216
+ /**
217
+ * Unsubscribe from a channel
218
+ */
219
+ unsubscribe(channel) {
220
+ this.callbacks.delete(channel);
221
+ if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
222
+ this.ws.send(JSON.stringify({
223
+ type: 'unsubscribe',
224
+ channel
225
+ }));
226
+ }
227
+ }
228
+ /**
229
+ * Handle incoming message
230
+ */
231
+ handleMessage(channel, data) {
232
+ const callbacks = this.callbacks.get(channel);
233
+ if (callbacks) {
234
+ callbacks.forEach(cb => cb(data));
235
+ }
236
+ }
237
+ }
238
+ exports.LiquidationMonitor = LiquidationMonitor;
239
+ /**
240
+ * Market data sources supported by LX
241
+ */
242
+ exports.MarketDataProviders = {
243
+ ALPACA: 'alpaca',
244
+ NYSE_ARCA: 'nyse_arca',
245
+ IEX_CLOUD: 'iex',
246
+ POLYGON: 'polygon',
247
+ CME_GROUP: 'cme',
248
+ REFINITIV: 'refinitiv',
249
+ ICE_DATA: 'ice',
250
+ BLOOMBERG: 'bloomberg',
251
+ NASDAQ_TOTALVIEW: 'nasdaq',
252
+ COINBASE_PRO: 'coinbase'
253
+ };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@luxfi/dex",
3
+ "version": "1.2.1",
4
+ "description": "TypeScript SDK for LX",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "test": "jest",
10
+ "lint": "eslint src/**/*.ts",
11
+ "prepublish": "npm run build"
12
+ },
13
+ "keywords": [
14
+ "luxfi",
15
+ "dex",
16
+ "trading",
17
+ "blockchain",
18
+ "sdk"
19
+ ],
20
+ "author": "Lux Network",
21
+ "license": "MIT",
22
+ "dependencies": {
23
+ "axios": "^1.6.0",
24
+ "ws": "^8.14.0"
25
+ },
26
+ "devDependencies": {
27
+ "@types/jest": "^29.5.0",
28
+ "@types/node": "^20.0.0",
29
+ "@types/ws": "^8.5.0",
30
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
31
+ "@typescript-eslint/parser": "^6.0.0",
32
+ "eslint": "^8.0.0",
33
+ "jest": "^29.5.0",
34
+ "ts-jest": "^29.1.0",
35
+ "typescript": "^5.0.0"
36
+ },
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/luxfi/dex.git"
40
+ }
41
+ }
package/src/index.ts ADDED
@@ -0,0 +1,309 @@
1
+ import axios, { AxiosInstance } from 'axios';
2
+ import WebSocket from 'ws';
3
+ import {
4
+ MarketDataClient,
5
+ LiquidationMonitor,
6
+ MarketDataSource,
7
+ LiquidationInfo,
8
+ SettlementBatch,
9
+ MarginInfo,
10
+ MarketDataProviders
11
+ } from './marketData';
12
+
13
+ // Types
14
+ export enum OrderType {
15
+ LIMIT = 0,
16
+ MARKET = 1,
17
+ STOP = 2,
18
+ STOP_LIMIT = 3,
19
+ ICEBERG = 4,
20
+ PEG = 5
21
+ }
22
+
23
+ export enum OrderSide {
24
+ BUY = 0,
25
+ SELL = 1
26
+ }
27
+
28
+ export enum OrderStatus {
29
+ OPEN = 'open',
30
+ PARTIAL = 'partial',
31
+ FILLED = 'filled',
32
+ CANCELLED = 'cancelled',
33
+ REJECTED = 'rejected'
34
+ }
35
+
36
+ export enum TimeInForce {
37
+ GTC = 'GTC',
38
+ IOC = 'IOC',
39
+ FOK = 'FOK',
40
+ DAY = 'DAY'
41
+ }
42
+
43
+ export interface Order {
44
+ orderId?: number;
45
+ symbol: string;
46
+ type: OrderType;
47
+ side: OrderSide;
48
+ price: number;
49
+ size: number;
50
+ userID?: string;
51
+ clientID?: string;
52
+ timeInForce?: TimeInForce;
53
+ postOnly?: boolean;
54
+ reduceOnly?: boolean;
55
+ status?: OrderStatus;
56
+ filled?: number;
57
+ remaining?: number;
58
+ timestamp?: number;
59
+ }
60
+
61
+ export interface Trade {
62
+ tradeId: number;
63
+ symbol: string;
64
+ price: number;
65
+ size: number;
66
+ side: OrderSide;
67
+ buyOrderId: number;
68
+ sellOrderId: number;
69
+ buyerId: string;
70
+ sellerId: string;
71
+ timestamp: number;
72
+ }
73
+
74
+ export interface OrderBookLevel {
75
+ price: number;
76
+ size: number;
77
+ count?: number;
78
+ }
79
+
80
+ export interface OrderBook {
81
+ symbol: string;
82
+ bids: OrderBookLevel[];
83
+ asks: OrderBookLevel[];
84
+ timestamp: number;
85
+ }
86
+
87
+ export interface NodeInfo {
88
+ version: string;
89
+ network: string;
90
+ orderCount: number;
91
+ tradeCount: number;
92
+ timestamp: number;
93
+ }
94
+
95
+ export interface LXDexConfig {
96
+ jsonRpcUrl?: string;
97
+ wsUrl?: string;
98
+ grpcUrl?: string;
99
+ apiKey?: string;
100
+ }
101
+
102
+ // JSON-RPC Client
103
+ class JSONRPCClient {
104
+ private axios: AxiosInstance;
105
+ private idCounter = 1;
106
+
107
+ constructor(baseURL: string) {
108
+ this.axios = axios.create({
109
+ baseURL,
110
+ headers: {
111
+ 'Content-Type': 'application/json'
112
+ }
113
+ });
114
+ }
115
+
116
+ async call(method: string, params: any = {}): Promise<any> {
117
+ const response = await this.axios.post('/rpc', {
118
+ jsonrpc: '2.0',
119
+ method,
120
+ params,
121
+ id: this.idCounter++
122
+ });
123
+
124
+ if (response.data.error) {
125
+ throw new Error(response.data.error.message);
126
+ }
127
+
128
+ return response.data.result;
129
+ }
130
+ }
131
+
132
+ // Main SDK Client
133
+ export class LXDexClient {
134
+ private jsonRpc: JSONRPCClient;
135
+ private ws: WebSocket | null = null;
136
+ private wsCallbacks: Map<string, Function[]> = new Map();
137
+ private config: LXDexConfig;
138
+ public marketData: MarketDataClient;
139
+ public liquidationMonitor: LiquidationMonitor;
140
+
141
+ constructor(config: LXDexConfig = {}) {
142
+ this.config = {
143
+ jsonRpcUrl: config.jsonRpcUrl || 'http://localhost:8080',
144
+ wsUrl: config.wsUrl || 'ws://localhost:8081',
145
+ ...config
146
+ };
147
+
148
+ this.jsonRpc = new JSONRPCClient(this.config.jsonRpcUrl!);
149
+
150
+ // Initialize sub-clients
151
+ this.marketData = new MarketDataClient(this.jsonRpc);
152
+ this.liquidationMonitor = new LiquidationMonitor(null);
153
+ }
154
+
155
+ // Connection Management
156
+ async connect(): Promise<void> {
157
+ if (this.ws) return;
158
+
159
+ return new Promise((resolve, reject) => {
160
+ this.ws = new WebSocket(this.config.wsUrl!);
161
+
162
+ this.ws.on('open', () => {
163
+ console.log('WebSocket connected');
164
+ // Set WebSocket for liquidation monitor
165
+ this.liquidationMonitor.setWebSocket(this.ws!);
166
+ resolve();
167
+ });
168
+
169
+ this.ws.on('message', (data: string) => {
170
+ try {
171
+ const msg = JSON.parse(data);
172
+ this.handleWebSocketMessage(msg);
173
+ } catch (err) {
174
+ console.error('Failed to parse WebSocket message:', err);
175
+ }
176
+ });
177
+
178
+ this.ws.on('error', (err) => {
179
+ console.error('WebSocket error:', err);
180
+ reject(err);
181
+ });
182
+
183
+ this.ws.on('close', () => {
184
+ console.log('WebSocket disconnected');
185
+ this.ws = null;
186
+ });
187
+ });
188
+ }
189
+
190
+ disconnect(): void {
191
+ if (this.ws) {
192
+ this.ws.close();
193
+ this.ws = null;
194
+ }
195
+ }
196
+
197
+ // Order Management
198
+ async placeOrder(order: Partial<Order>): Promise<{ orderId: number; status: string }> {
199
+ return this.jsonRpc.call('lx_placeOrder', order);
200
+ }
201
+
202
+ async cancelOrder(orderId: number): Promise<{ success: boolean; message: string }> {
203
+ return this.jsonRpc.call('lx_cancelOrder', { orderId });
204
+ }
205
+
206
+ async getOrder(orderId: number): Promise<Order> {
207
+ return this.jsonRpc.call('lx_getOrder', { orderId });
208
+ }
209
+
210
+ // Market Data
211
+ async getOrderBook(symbol: string = 'BTC-USD', depth: number = 10): Promise<OrderBook> {
212
+ return this.jsonRpc.call('lx_getOrderBook', { symbol, depth });
213
+ }
214
+
215
+ async getBestBid(symbol: string = 'BTC-USD'): Promise<number> {
216
+ const result = await this.jsonRpc.call('lx_getBestBid', { symbol });
217
+ return result.price;
218
+ }
219
+
220
+ async getBestAsk(symbol: string = 'BTC-USD'): Promise<number> {
221
+ const result = await this.jsonRpc.call('lx_getBestAsk', { symbol });
222
+ return result.price;
223
+ }
224
+
225
+ async getTrades(symbol: string = 'BTC-USD', limit: number = 100): Promise<Trade[]> {
226
+ return this.jsonRpc.call('lx_getTrades', { symbol, limit });
227
+ }
228
+
229
+ // Node Information
230
+ async getInfo(): Promise<NodeInfo> {
231
+ return this.jsonRpc.call('lx_getInfo');
232
+ }
233
+
234
+ async ping(): Promise<string> {
235
+ return this.jsonRpc.call('lx_ping');
236
+ }
237
+
238
+ // WebSocket Subscriptions
239
+ subscribe(channel: string, callback: Function): void {
240
+ if (!this.wsCallbacks.has(channel)) {
241
+ this.wsCallbacks.set(channel, []);
242
+ }
243
+ this.wsCallbacks.get(channel)!.push(callback);
244
+
245
+ // Send subscription message
246
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
247
+ this.ws.send(JSON.stringify({
248
+ type: 'subscribe',
249
+ channel
250
+ }));
251
+ }
252
+ }
253
+
254
+ unsubscribe(channel: string, callback?: Function): void {
255
+ if (callback) {
256
+ const callbacks = this.wsCallbacks.get(channel);
257
+ if (callbacks) {
258
+ const index = callbacks.indexOf(callback);
259
+ if (index > -1) {
260
+ callbacks.splice(index, 1);
261
+ }
262
+ }
263
+ } else {
264
+ this.wsCallbacks.delete(channel);
265
+ }
266
+
267
+ // Send unsubscribe message
268
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
269
+ this.ws.send(JSON.stringify({
270
+ type: 'unsubscribe',
271
+ channel
272
+ }));
273
+ }
274
+ }
275
+
276
+ subscribeOrderBook(symbol: string, callback: (book: OrderBook) => void): void {
277
+ this.subscribe(`orderbook:${symbol}`, callback);
278
+ }
279
+
280
+ subscribeTrades(symbol: string, callback: (trade: Trade) => void): void {
281
+ this.subscribe(`trades:${symbol}`, callback);
282
+ }
283
+
284
+ // Private methods
285
+ private handleWebSocketMessage(msg: any): void {
286
+ const { channel, data } = msg;
287
+ const callbacks = this.wsCallbacks.get(channel);
288
+ if (callbacks) {
289
+ callbacks.forEach(cb => cb(data));
290
+ }
291
+ }
292
+
293
+ // Utility methods
294
+ static formatPrice(price: number, decimals: number = 2): string {
295
+ return price.toFixed(decimals);
296
+ }
297
+
298
+ static formatSize(size: number, decimals: number = 8): string {
299
+ return size.toFixed(decimals);
300
+ }
301
+
302
+ static calculateTotal(price: number, size: number): number {
303
+ return price * size;
304
+ }
305
+ }
306
+
307
+ // Export everything
308
+ export default LXDexClient;
309
+ export * from './marketData';