@runesx/api-client 0.3.0 → 0.5.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.
- package/README.md +603 -418
- package/package.json +7 -7
- package/src/api.mjs +387 -4
- package/src/index.mjs +118 -12
- package/src/socket.mjs +204 -115
- package/src/store/coinStore.mjs +1 -1
- package/src/store/exchangeConfigStore.mjs +48 -0
- package/src/store/marketStore.mjs +57 -0
- package/src/store/orderbookStore.mjs +99 -0
- package/src/store/poolStore.mjs +1 -1
- package/src/store/userSharesStore.mjs +1 -1
- package/src/store/walletStore.mjs +1 -1
- package/src/utils/liquidityUtils.mjs +2 -3
- package/src/utils/priceUtils.mjs +2 -1
- package/src/utils/safeBigNumber.mjs +11 -0
- package/src/utils/swapUtils.mjs +1159 -129
- package/src/waitForStores.mjs +41 -6
- package/src/workers/swapWorker.mjs +2 -2
package/src/socket.mjs
CHANGED
|
@@ -6,6 +6,9 @@ import { setInitialCoins, updateCoin, resetCoins } from './store/coinStore.mjs';
|
|
|
6
6
|
import { setInitialChains, updateChain, resetChains } from './store/chainStore.mjs';
|
|
7
7
|
import { setInitialWallets, updateWallet, resetWallets } from './store/walletStore.mjs';
|
|
8
8
|
import { setInitialUserShares, updateUserShare, resetUserShares } from './store/userSharesStore.mjs';
|
|
9
|
+
import { setInitialOrderBooks, updateOrderBook, resetOrderBooks, setUserOrders, updateUserOrder } from './store/orderbookStore.mjs';
|
|
10
|
+
import { setInitialMarkets, addOrUpdateMarket, resetMarkets } from './store/marketStore.mjs';
|
|
11
|
+
import { setExchangeConfig } from './store/exchangeConfigStore.mjs';
|
|
9
12
|
|
|
10
13
|
export function setupSocket(config) {
|
|
11
14
|
const socket = io(config.socketUrl, {
|
|
@@ -18,12 +21,30 @@ export function setupSocket(config) {
|
|
|
18
21
|
reconnectionDelayMax: 5000,
|
|
19
22
|
});
|
|
20
23
|
|
|
21
|
-
const errorCount = { count: 0 };
|
|
24
|
+
const errorCount = { count: 0 };
|
|
25
|
+
|
|
26
|
+
// User-registered event callbacks
|
|
27
|
+
const callbacks = {};
|
|
28
|
+
|
|
29
|
+
function emit(event, data) {
|
|
30
|
+
if (callbacks[event]) {
|
|
31
|
+
callbacks[event].forEach((cb) => {
|
|
32
|
+
try { cb(data); } catch (e) { console.error(`Error in ${event} callback:`, e.message); }
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const heartbeatInterval = setInterval(() => {
|
|
38
|
+
if (socket.connected) {
|
|
39
|
+
socket.emit('ping');
|
|
40
|
+
}
|
|
41
|
+
}, 30000);
|
|
22
42
|
|
|
23
43
|
socket.on('connect', () => {
|
|
24
44
|
socket.emit('join_public');
|
|
25
45
|
socket.emit('join_private');
|
|
26
46
|
errorCount.count = 0;
|
|
47
|
+
emit('connect', null);
|
|
27
48
|
});
|
|
28
49
|
|
|
29
50
|
socket.on('connect_error', (err) => {
|
|
@@ -35,33 +56,34 @@ export function setupSocket(config) {
|
|
|
35
56
|
resetChains();
|
|
36
57
|
resetWallets();
|
|
37
58
|
resetUserShares();
|
|
59
|
+
resetOrderBooks();
|
|
60
|
+
resetMarkets();
|
|
38
61
|
}
|
|
62
|
+
emit('connect_error', err);
|
|
39
63
|
});
|
|
40
64
|
|
|
41
65
|
socket.on('disconnect', (reason) => {
|
|
42
66
|
console.log('Disconnected from Socket.IO server:', reason);
|
|
67
|
+
clearInterval(heartbeatInterval);
|
|
43
68
|
resetPools();
|
|
44
69
|
resetCoins();
|
|
45
70
|
resetChains();
|
|
46
71
|
resetWallets();
|
|
47
72
|
resetUserShares();
|
|
73
|
+
resetMarkets();
|
|
74
|
+
emit('disconnect', reason);
|
|
48
75
|
});
|
|
49
76
|
|
|
50
|
-
socket.on('reconnect_attempt', (attempt) => {
|
|
77
|
+
socket.io.on('reconnect_attempt', (attempt) => {
|
|
51
78
|
console.log(`Reconnect attempt #${attempt}`);
|
|
79
|
+
emit('reconnect_attempt', attempt);
|
|
52
80
|
});
|
|
53
81
|
|
|
54
|
-
socket.on('reconnect', () => {
|
|
55
|
-
|
|
56
|
-
socket.emit('join_private');
|
|
57
|
-
resetPools();
|
|
58
|
-
resetCoins();
|
|
59
|
-
resetChains();
|
|
60
|
-
resetWallets();
|
|
61
|
-
resetUserShares();
|
|
82
|
+
socket.io.on('reconnect', () => {
|
|
83
|
+
emit('reconnect', null);
|
|
62
84
|
});
|
|
63
85
|
|
|
64
|
-
socket.on('reconnect_error', (err) => {
|
|
86
|
+
socket.io.on('reconnect_error', (err) => {
|
|
65
87
|
console.log('Reconnect error:', err.message);
|
|
66
88
|
errorCount.count += 1;
|
|
67
89
|
if (errorCount.count >= 3) {
|
|
@@ -70,188 +92,255 @@ export function setupSocket(config) {
|
|
|
70
92
|
resetChains();
|
|
71
93
|
resetWallets();
|
|
72
94
|
resetUserShares();
|
|
95
|
+
resetOrderBooks();
|
|
73
96
|
}
|
|
97
|
+
emit('reconnect_error', err);
|
|
74
98
|
});
|
|
75
99
|
|
|
76
100
|
socket.on('error', (err) => {
|
|
77
101
|
console.log('Socket error:', err.message);
|
|
102
|
+
emit('error', err);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// ---- Public room events ----
|
|
106
|
+
|
|
107
|
+
socket.on('exchange_config', (config) => {
|
|
108
|
+
setExchangeConfig(config);
|
|
109
|
+
emit('exchange_config', config);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
socket.on('markets_initial', (data) => {
|
|
113
|
+
setInitialMarkets(data);
|
|
114
|
+
emit('markets_initial', data);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
socket.on('market_created', (data) => {
|
|
118
|
+
addOrUpdateMarket(data);
|
|
119
|
+
emit('market_created', data);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
socket.on('market_updated', (data) => {
|
|
123
|
+
addOrUpdateMarket(data);
|
|
124
|
+
emit('market_updated', data);
|
|
78
125
|
});
|
|
79
126
|
|
|
80
127
|
socket.on('pools_updated', ({ pools, isInitial }) => {
|
|
81
|
-
// console.log('Received pools_updated:', { pools, isInitial });
|
|
82
128
|
if (isInitial) {
|
|
83
129
|
setInitialPools(pools);
|
|
84
130
|
} else {
|
|
85
131
|
pools.forEach(pool => updatePool(pool));
|
|
86
132
|
}
|
|
133
|
+
emit('pools_updated', { pools, isInitial });
|
|
87
134
|
});
|
|
88
135
|
|
|
89
136
|
socket.on('coins_updated', ({ coins, isInitial }) => {
|
|
90
|
-
// console.log('Received coins_updated:', { coins, isInitial });
|
|
91
137
|
if (isInitial) {
|
|
92
138
|
setInitialCoins(coins);
|
|
93
139
|
} else {
|
|
94
140
|
coins.forEach(coin => updateCoin(coin));
|
|
95
141
|
}
|
|
142
|
+
emit('coins_updated', { coins, isInitial });
|
|
96
143
|
});
|
|
97
144
|
|
|
98
145
|
socket.on('chains_updated', ({ chains, isInitial }) => {
|
|
99
|
-
// console.log('Received chains_updated:', { chains, isInitial });
|
|
100
146
|
if (isInitial) {
|
|
101
147
|
setInitialChains(chains);
|
|
102
148
|
} else {
|
|
103
149
|
chains.forEach(chain => updateChain(chain));
|
|
104
150
|
}
|
|
151
|
+
emit('chains_updated', { chains, isInitial });
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
socket.on('buckets_updated', (data) => {
|
|
155
|
+
emit('buckets_updated', data);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
socket.on('operations_updated', (operations) => {
|
|
159
|
+
emit('operations_updated', operations);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
socket.on('recent_yard_messages', (data) => {
|
|
163
|
+
emit('recent_yard_messages', data);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
socket.on('status_updated', (data) => {
|
|
167
|
+
emit('status_updated', data);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
socket.on('orderbooks_initial', ({ books, isInitial }) => {
|
|
171
|
+
if (isInitial) {
|
|
172
|
+
setInitialOrderBooks(books);
|
|
173
|
+
}
|
|
174
|
+
emit('orderbooks_initial', { books, isInitial });
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
socket.on('orderbook_updated', ({ pair, bids, asks }) => {
|
|
178
|
+
updateOrderBook(pair, bids, asks);
|
|
179
|
+
emit('orderbook_updated', { pair, bids, asks });
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
socket.on('user_orders_initial', (orders) => {
|
|
183
|
+
setUserOrders(orders);
|
|
184
|
+
emit('user_orders_initial', orders);
|
|
105
185
|
});
|
|
106
186
|
|
|
187
|
+
socket.on('order_updated', (data) => {
|
|
188
|
+
updateUserOrder(data);
|
|
189
|
+
emit('order_updated', data);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
socket.on('clob_trade', (trade) => {
|
|
193
|
+
emit('clob_trade', trade);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
socket.on('volumeUpdate', (data) => {
|
|
197
|
+
emit('volumeUpdate', data);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
socket.on('operationUpdate', (operation) => {
|
|
201
|
+
emit('operationUpdate', operation);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
socket.on('candlestick_updated', (data) => {
|
|
205
|
+
emit('candlestick_updated', data);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// ---- Private room events ----
|
|
209
|
+
|
|
107
210
|
socket.on('wallets_updated', ({ wallets, isInitial }) => {
|
|
108
|
-
// console.log('Received wallets_updated:', { wallets, isInitial });
|
|
109
211
|
if (isInitial) {
|
|
110
212
|
setInitialWallets(wallets);
|
|
111
213
|
} else {
|
|
112
214
|
wallets.forEach(wallet => updateWallet(wallet));
|
|
113
215
|
}
|
|
216
|
+
emit('wallets_updated', { wallets, isInitial });
|
|
114
217
|
});
|
|
115
218
|
|
|
116
219
|
socket.on('user_shares_updated', ({ userShares, isInitial }) => {
|
|
117
|
-
// console.log('Received user_shares_updated:', { userShares, isInitial });
|
|
118
220
|
if (isInitial) {
|
|
119
221
|
setInitialUserShares(userShares);
|
|
120
222
|
} else {
|
|
121
223
|
userShares.forEach(share => updateUserShare(share));
|
|
122
224
|
}
|
|
225
|
+
emit('user_shares_updated', { userShares, isInitial });
|
|
123
226
|
});
|
|
124
227
|
|
|
125
|
-
socket.on('
|
|
126
|
-
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
socket.on('operationUpdate', (operation) => {
|
|
130
|
-
// console.log('Operation update:', operation);
|
|
131
|
-
// Assuming operation includes liquidity deposit/withdraw results
|
|
132
|
-
// if (operation.type === 'liquidity_deposit' || operation.type === 'liquidity_withdraw') {
|
|
133
|
-
// console.log(`Liquidity operation ${operation.type} completed:`, {
|
|
134
|
-
// uid: operation.uid,
|
|
135
|
-
// coinA: operation.coinA,
|
|
136
|
-
// coinB: operation.coinB,
|
|
137
|
-
// amountA: operation.amountA,
|
|
138
|
-
// amountB: operation.amountB,
|
|
139
|
-
// shares: operation.shares,
|
|
140
|
-
// status: operation.status,
|
|
141
|
-
// });
|
|
142
|
-
// }
|
|
143
|
-
});
|
|
144
|
-
socket.on('status_updated', (data) => {
|
|
145
|
-
// console.log('Status update:', data);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
socket.on('deposit_address_generated', ({ requestId, chainName, address, memo }) => {
|
|
149
|
-
// console.log('Deposit address generated:', { requestId, chainName, address, memo });
|
|
228
|
+
socket.on('deposit_address_generated', (data) => {
|
|
229
|
+
emit('deposit_address_generated', data);
|
|
150
230
|
});
|
|
151
231
|
|
|
152
232
|
socket.on('deposit_processed', (data) => {
|
|
153
|
-
|
|
154
|
-
const message =
|
|
155
|
-
status === 'confirmed' && credited
|
|
156
|
-
? `Deposit of ${amount} ${coin.ticker} confirmed!`
|
|
157
|
-
: `Deposit of ${amount} ${coin.ticker} (confirming) [${confirmations}/${chain.requiredConfirmations}]`;
|
|
158
|
-
console.log('Deposit processed:', { message, data });
|
|
233
|
+
emit('deposit_processed', data);
|
|
159
234
|
});
|
|
160
235
|
|
|
161
236
|
socket.on('withdrawal_processed', (data) => {
|
|
162
|
-
|
|
163
|
-
let message;
|
|
164
|
-
if (!createdAt) {
|
|
165
|
-
message = `Withdrawal of ${amount} ${coin.ticker} is stalled due to network congestion.`;
|
|
166
|
-
} else if (status === 'confirmed' && credited) {
|
|
167
|
-
message = `Withdrawal of ${amount} ${coin.ticker} confirmed!`;
|
|
168
|
-
} else {
|
|
169
|
-
message = `Withdrawal of ${amount} ${coin.ticker} (confirming) [${confirmations}/${chain.requiredConfirmations}]`;
|
|
170
|
-
}
|
|
171
|
-
console.log('Withdrawal processed:', { message, data });
|
|
237
|
+
emit('withdrawal_processed', data);
|
|
172
238
|
});
|
|
173
239
|
|
|
174
240
|
socket.on('withdrawal_initiated', (data) => {
|
|
175
|
-
|
|
241
|
+
emit('withdrawal_initiated', data);
|
|
176
242
|
});
|
|
177
243
|
|
|
178
|
-
socket.on('withdrawal_pin_generated', (
|
|
179
|
-
|
|
180
|
-
ticker,
|
|
181
|
-
amount,
|
|
182
|
-
pinImage,
|
|
183
|
-
pendingWithdrawalId,
|
|
184
|
-
expiresAt,
|
|
185
|
-
dp,
|
|
186
|
-
fee,
|
|
187
|
-
memoRequired,
|
|
188
|
-
});
|
|
244
|
+
socket.on('withdrawal_pin_generated', (data) => {
|
|
245
|
+
emit('withdrawal_pin_generated', data);
|
|
189
246
|
});
|
|
190
247
|
|
|
191
|
-
socket.on('withdrawal_queued', (
|
|
192
|
-
|
|
248
|
+
socket.on('withdrawal_queued', (data) => {
|
|
249
|
+
emit('withdrawal_queued', data);
|
|
193
250
|
});
|
|
194
251
|
|
|
195
|
-
socket.on('withdrawal_canceled', (
|
|
196
|
-
|
|
252
|
+
socket.on('withdrawal_canceled', (data) => {
|
|
253
|
+
emit('withdrawal_canceled', data);
|
|
197
254
|
});
|
|
198
255
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
256
|
+
socket.on('withdrawal_updated', (data) => {
|
|
257
|
+
emit('withdrawal_updated', data);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
socket.on('withdrawal_expired', (data) => {
|
|
261
|
+
emit('withdrawal_expired', data);
|
|
262
|
+
});
|
|
202
263
|
|
|
203
264
|
socket.on('yard_message', (message) => {
|
|
204
|
-
|
|
205
|
-
// username: message.username,
|
|
206
|
-
// text: message.text,
|
|
207
|
-
// role: message.role,
|
|
208
|
-
// timestamp: new Date(message.timestamp).toLocaleString(),
|
|
209
|
-
// });
|
|
265
|
+
emit('yard_message', message);
|
|
210
266
|
});
|
|
211
267
|
|
|
212
|
-
socket.on('message_deleted', (
|
|
213
|
-
|
|
268
|
+
socket.on('message_deleted', (data) => {
|
|
269
|
+
emit('message_deleted', data);
|
|
214
270
|
});
|
|
215
271
|
|
|
216
|
-
socket.on('banned', (
|
|
217
|
-
|
|
218
|
-
// reason,
|
|
219
|
-
// bannedUntil: new Date(bannedUntil).toLocaleString(),
|
|
220
|
-
// });
|
|
272
|
+
socket.on('banned', (data) => {
|
|
273
|
+
emit('banned', data);
|
|
221
274
|
});
|
|
222
275
|
|
|
223
|
-
socket.on('
|
|
224
|
-
|
|
276
|
+
socket.on('yard_read_marked', (data) => {
|
|
277
|
+
emit('yard_read_marked', data);
|
|
225
278
|
});
|
|
226
279
|
|
|
227
|
-
socket.on('
|
|
228
|
-
|
|
280
|
+
socket.on('session_expired', (data) => {
|
|
281
|
+
emit('session_expired', data);
|
|
229
282
|
});
|
|
230
283
|
|
|
231
284
|
socket.on('pong', () => {
|
|
232
|
-
|
|
285
|
+
emit('pong', null);
|
|
233
286
|
});
|
|
234
287
|
|
|
235
|
-
|
|
236
|
-
if (socket.connected) {
|
|
237
|
-
socket.emit('ping');
|
|
238
|
-
}
|
|
239
|
-
}, 30000);
|
|
288
|
+
// ---- Convenience methods ----
|
|
240
289
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
resetUserShares();
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
socket.on('connect', () => {
|
|
251
|
-
// socket.emit('yard_message', {
|
|
252
|
-
// text: 'Hello from RunesX API Key Example Bot!!',
|
|
253
|
-
// });
|
|
254
|
-
});
|
|
290
|
+
function on(event, callback) {
|
|
291
|
+
if (!callbacks[event]) {
|
|
292
|
+
callbacks[event] = [];
|
|
293
|
+
}
|
|
294
|
+
callbacks[event].push(callback);
|
|
295
|
+
}
|
|
255
296
|
|
|
256
|
-
|
|
257
|
-
}
|
|
297
|
+
function off(event, callback) {
|
|
298
|
+
if (!callbacks[event]) { return; }
|
|
299
|
+
if (callback) {
|
|
300
|
+
callbacks[event] = callbacks[event].filter((cb) => cb !== callback);
|
|
301
|
+
} else {
|
|
302
|
+
delete callbacks[event];
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function joinCandlesticks(poolId, timeframe) {
|
|
307
|
+
socket.emit('join_candlesticks', { poolId, timeframe });
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function leaveCandlesticks(poolId, timeframe) {
|
|
311
|
+
socket.emit('leave_candlesticks', { poolId, timeframe });
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function sendYardMessage(text) {
|
|
315
|
+
socket.emit('yard_message', { text });
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function deleteMessage(messageId) {
|
|
319
|
+
socket.emit('delete_message', { messageId });
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function markYardRead() {
|
|
323
|
+
socket.emit('mark_yard_read');
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function leavePublic() {
|
|
327
|
+
socket.leave('public');
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function leavePrivate() {
|
|
331
|
+
socket.leave('private');
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return {
|
|
335
|
+
socket,
|
|
336
|
+
on,
|
|
337
|
+
off,
|
|
338
|
+
joinCandlesticks,
|
|
339
|
+
leaveCandlesticks,
|
|
340
|
+
sendYardMessage,
|
|
341
|
+
deleteMessage,
|
|
342
|
+
markYardRead,
|
|
343
|
+
leavePublic,
|
|
344
|
+
leavePrivate,
|
|
345
|
+
};
|
|
346
|
+
}
|
package/src/store/coinStore.mjs
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// src/store/exchangeConfigStore.mjs
|
|
2
|
+
// Exchange configuration received from the backend via socket.
|
|
3
|
+
// Keeps fee rates, depth limits, etc. in sync without hardcoding.
|
|
4
|
+
|
|
5
|
+
const exchangeConfigStore = {
|
|
6
|
+
clobFees: {
|
|
7
|
+
takerFeeRate: '0.002', // sensible default until backend config arrives
|
|
8
|
+
makerFeeRate: '0.001',
|
|
9
|
+
maxFillBatch: 50,
|
|
10
|
+
maxFillTotal: 500,
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const setExchangeConfig = (config) => {
|
|
15
|
+
const { clobFees } = config;
|
|
16
|
+
if (clobFees) {
|
|
17
|
+
if (clobFees.takerFeeRate !== null && clobFees.takerFeeRate !== undefined) {
|
|
18
|
+
exchangeConfigStore.clobFees.takerFeeRate = clobFees.takerFeeRate;
|
|
19
|
+
}
|
|
20
|
+
if (clobFees.makerFeeRate !== null && clobFees.makerFeeRate !== undefined) {
|
|
21
|
+
exchangeConfigStore.clobFees.makerFeeRate = clobFees.makerFeeRate;
|
|
22
|
+
}
|
|
23
|
+
if (clobFees.maxFillBatch !== null && clobFees.maxFillBatch !== undefined) {
|
|
24
|
+
exchangeConfigStore.clobFees.maxFillBatch = clobFees.maxFillBatch;
|
|
25
|
+
}
|
|
26
|
+
if (clobFees.maxFillTotal !== null && clobFees.maxFillTotal !== undefined) {
|
|
27
|
+
exchangeConfigStore.clobFees.maxFillTotal = clobFees.maxFillTotal;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const getClobFees = () => exchangeConfigStore.clobFees;
|
|
33
|
+
|
|
34
|
+
const resetExchangeConfig = () => {
|
|
35
|
+
exchangeConfigStore.clobFees = {
|
|
36
|
+
takerFeeRate: '0.002',
|
|
37
|
+
makerFeeRate: '0.001',
|
|
38
|
+
maxFillBatch: 50,
|
|
39
|
+
maxFillTotal: 500,
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export {
|
|
44
|
+
exchangeConfigStore,
|
|
45
|
+
setExchangeConfig,
|
|
46
|
+
getClobFees,
|
|
47
|
+
resetExchangeConfig,
|
|
48
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// src/store/marketStore.mjs
|
|
2
|
+
// Stores admin-defined markets received from the backend via socket.
|
|
3
|
+
// Markets define the canonical pair direction (e.g., "RUNES-DOG" not "DOG-RUNES").
|
|
4
|
+
|
|
5
|
+
let markets = [];
|
|
6
|
+
let byCoinKey = {};
|
|
7
|
+
|
|
8
|
+
function buildCoinKey(coinIdA, coinIdB) {
|
|
9
|
+
return coinIdA < coinIdB ? `${coinIdA}|${coinIdB}` : `${coinIdB}|${coinIdA}`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function rebuildLookup() {
|
|
13
|
+
const lookup = {};
|
|
14
|
+
for (const m of markets) {
|
|
15
|
+
const key = buildCoinKey(m.baseCoinId, m.quoteCoinId);
|
|
16
|
+
lookup[key] = m;
|
|
17
|
+
}
|
|
18
|
+
byCoinKey = lookup;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function setInitialMarkets(list) {
|
|
22
|
+
markets = list || [];
|
|
23
|
+
rebuildLookup();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function addOrUpdateMarket(market) {
|
|
27
|
+
const idx = markets.findIndex((m) => m.id === market.id);
|
|
28
|
+
if (idx >= 0) {
|
|
29
|
+
markets[idx] = { ...markets[idx], ...market };
|
|
30
|
+
} else {
|
|
31
|
+
markets.push(market);
|
|
32
|
+
}
|
|
33
|
+
rebuildLookup();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function getMarkets() {
|
|
37
|
+
return markets;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function getMarketByCoinKey() {
|
|
41
|
+
return byCoinKey;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Look up a market by two coin IDs (order-independent).
|
|
46
|
+
* @param {string} coinIdA
|
|
47
|
+
* @param {string} coinIdB
|
|
48
|
+
* @returns {Object|undefined}
|
|
49
|
+
*/
|
|
50
|
+
export function getMarketByCoins(coinIdA, coinIdB) {
|
|
51
|
+
return byCoinKey[buildCoinKey(coinIdA, coinIdB)];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function resetMarkets() {
|
|
55
|
+
markets = [];
|
|
56
|
+
byCoinKey = {};
|
|
57
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const orderbookStore = {
|
|
2
|
+
books: new Map(), // pair -> { bids: [[price, qty], ...], asks: [[price, qty], ...] }
|
|
3
|
+
isInitialReceived: false,
|
|
4
|
+
pendingUpdates: [],
|
|
5
|
+
userOrders: [], // User's open CLOB orders (across all pairs)
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
// Initialize all order books with initial data
|
|
9
|
+
const setInitialOrderBooks = (books) => {
|
|
10
|
+
orderbookStore.books.clear();
|
|
11
|
+
for (const [pair, depth] of Object.entries(books)) {
|
|
12
|
+
orderbookStore.books.set(pair, {
|
|
13
|
+
bids: depth.bids || [],
|
|
14
|
+
asks: depth.asks || [],
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
orderbookStore.isInitialReceived = true;
|
|
18
|
+
|
|
19
|
+
// Process buffered updates
|
|
20
|
+
if (orderbookStore.pendingUpdates.length > 0) {
|
|
21
|
+
orderbookStore.pendingUpdates.forEach(({ pair, bids, asks }) => {
|
|
22
|
+
updateOrderBook(pair, bids, asks);
|
|
23
|
+
});
|
|
24
|
+
orderbookStore.pendingUpdates = [];
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Update a single order book
|
|
29
|
+
const updateOrderBook = (pair, bids, asks) => {
|
|
30
|
+
if (!orderbookStore.isInitialReceived) {
|
|
31
|
+
orderbookStore.pendingUpdates.push({ pair, bids, asks });
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
orderbookStore.books.set(pair, {
|
|
35
|
+
bids: bids || [],
|
|
36
|
+
asks: asks || [],
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Get all order books as a plain object { [pair]: { bids, asks } }
|
|
41
|
+
const getAllOrderBooks = () => {
|
|
42
|
+
const result = {};
|
|
43
|
+
for (const [pair, depth] of orderbookStore.books) {
|
|
44
|
+
result[pair] = depth;
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Get a specific order book by pair
|
|
50
|
+
const getOrderBook = (pair) => {
|
|
51
|
+
return orderbookStore.books.get(pair) || { bids: [], asks: [] };
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Get all pairs with active order books
|
|
55
|
+
const getOrderBookPairs = () => {
|
|
56
|
+
return [...orderbookStore.books.keys()];
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Set user's open orders (received on connect)
|
|
60
|
+
const setUserOrders = (orders) => {
|
|
61
|
+
orderbookStore.userOrders = orders || [];
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Update a single user order (from order_updated event)
|
|
65
|
+
const updateUserOrder = (data) => {
|
|
66
|
+
if (data.refresh) {return;} // caller should refetch
|
|
67
|
+
const order = data.order;
|
|
68
|
+
if (!order) {return;}
|
|
69
|
+
const idx = orderbookStore.userOrders.findIndex((o) => o.id === order.id);
|
|
70
|
+
if (idx >= 0) {
|
|
71
|
+
orderbookStore.userOrders[idx] = { ...orderbookStore.userOrders[idx], ...order };
|
|
72
|
+
} else {
|
|
73
|
+
orderbookStore.userOrders.unshift(order);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Get user's open orders
|
|
78
|
+
const getUserOrders = () => orderbookStore.userOrders;
|
|
79
|
+
|
|
80
|
+
// Reset store on disconnect or error
|
|
81
|
+
const resetOrderBooks = () => {
|
|
82
|
+
orderbookStore.books.clear();
|
|
83
|
+
orderbookStore.isInitialReceived = false;
|
|
84
|
+
orderbookStore.pendingUpdates = [];
|
|
85
|
+
orderbookStore.userOrders = [];
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export {
|
|
89
|
+
orderbookStore,
|
|
90
|
+
setInitialOrderBooks,
|
|
91
|
+
updateOrderBook,
|
|
92
|
+
getAllOrderBooks,
|
|
93
|
+
getOrderBook,
|
|
94
|
+
getOrderBookPairs,
|
|
95
|
+
setUserOrders,
|
|
96
|
+
updateUserOrder,
|
|
97
|
+
getUserOrders,
|
|
98
|
+
resetOrderBooks,
|
|
99
|
+
};
|
package/src/store/poolStore.mjs
CHANGED