@runesx/api-client 0.2.0 → 0.4.0

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/src/socket.mjs CHANGED
@@ -3,12 +3,14 @@ import { io } from 'socket.io-client';
3
3
 
4
4
  import { setInitialPools, updatePool, resetPools } from './store/poolStore.mjs';
5
5
  import { setInitialCoins, updateCoin, resetCoins } from './store/coinStore.mjs';
6
+ import { setInitialChains, updateChain, resetChains } from './store/chainStore.mjs';
6
7
  import { setInitialWallets, updateWallet, resetWallets } from './store/walletStore.mjs';
7
8
  import { setInitialUserShares, updateUserShare, resetUserShares } from './store/userSharesStore.mjs';
8
9
 
9
10
  export function setupSocket(config) {
10
11
  const socket = io(config.socketUrl, {
11
12
  auth: { authorization: `Bearer ${config.apiKey}` },
13
+ extraHeaders: { authorization: `Bearer ${config.apiKey}` },
12
14
  transports: ['websocket'],
13
15
  reconnection: true,
14
16
  reconnectionAttempts: Infinity,
@@ -16,12 +18,30 @@ export function setupSocket(config) {
16
18
  reconnectionDelayMax: 5000,
17
19
  });
18
20
 
19
- const errorCount = { count: 0 }; // Track connection errors
21
+ const errorCount = { count: 0 };
22
+
23
+ // User-registered event callbacks
24
+ const callbacks = {};
25
+
26
+ function emit(event, data) {
27
+ if (callbacks[event]) {
28
+ callbacks[event].forEach((cb) => {
29
+ try { cb(data); } catch (e) { console.error(`Error in ${event} callback:`, e.message); }
30
+ });
31
+ }
32
+ }
33
+
34
+ const heartbeatInterval = setInterval(() => {
35
+ if (socket.connected) {
36
+ socket.emit('ping');
37
+ }
38
+ }, 30000);
20
39
 
21
40
  socket.on('connect', () => {
22
41
  socket.emit('join_public');
23
42
  socket.emit('join_private');
24
43
  errorCount.count = 0;
44
+ emit('connect', null);
25
45
  });
26
46
 
27
47
  socket.on('connect_error', (err) => {
@@ -30,212 +50,244 @@ export function setupSocket(config) {
30
50
  if (errorCount.count >= 3) {
31
51
  resetPools();
32
52
  resetCoins();
53
+ resetChains();
33
54
  resetWallets();
34
55
  resetUserShares();
35
56
  }
57
+ emit('connect_error', err);
36
58
  });
37
59
 
38
60
  socket.on('disconnect', (reason) => {
39
61
  console.log('Disconnected from Socket.IO server:', reason);
62
+ clearInterval(heartbeatInterval);
40
63
  resetPools();
41
64
  resetCoins();
65
+ resetChains();
42
66
  resetWallets();
43
67
  resetUserShares();
68
+ emit('disconnect', reason);
44
69
  });
45
70
 
46
- socket.on('reconnect_attempt', (attempt) => {
71
+ socket.io.on('reconnect_attempt', (attempt) => {
47
72
  console.log(`Reconnect attempt #${attempt}`);
73
+ emit('reconnect_attempt', attempt);
48
74
  });
49
75
 
50
- socket.on('reconnect', () => {
51
- socket.emit('join_public');
52
- socket.emit('join_private');
53
- resetPools();
54
- resetCoins();
55
- resetWallets();
56
- resetUserShares();
76
+ socket.io.on('reconnect', () => {
77
+ emit('reconnect', null);
57
78
  });
58
79
 
59
- socket.on('reconnect_error', (err) => {
80
+ socket.io.on('reconnect_error', (err) => {
60
81
  console.log('Reconnect error:', err.message);
61
82
  errorCount.count += 1;
62
83
  if (errorCount.count >= 3) {
63
84
  resetPools();
64
85
  resetCoins();
86
+ resetChains();
65
87
  resetWallets();
66
88
  resetUserShares();
67
89
  }
90
+ emit('reconnect_error', err);
68
91
  });
69
92
 
70
93
  socket.on('error', (err) => {
71
94
  console.log('Socket error:', err.message);
95
+ emit('error', err);
72
96
  });
73
97
 
98
+ // ---- Public room events ----
99
+
74
100
  socket.on('pools_updated', ({ pools, isInitial }) => {
75
- // console.log('Received pools_updated:', { pools, isInitial });
76
101
  if (isInitial) {
77
102
  setInitialPools(pools);
78
103
  } else {
79
104
  pools.forEach(pool => updatePool(pool));
80
105
  }
106
+ emit('pools_updated', { pools, isInitial });
81
107
  });
82
108
 
83
109
  socket.on('coins_updated', ({ coins, isInitial }) => {
84
- // console.log('Received coins_updated:', { coins, isInitial });
85
110
  if (isInitial) {
86
111
  setInitialCoins(coins);
87
112
  } else {
88
113
  coins.forEach(coin => updateCoin(coin));
89
114
  }
115
+ emit('coins_updated', { coins, isInitial });
116
+ });
117
+
118
+ socket.on('chains_updated', ({ chains, isInitial }) => {
119
+ if (isInitial) {
120
+ setInitialChains(chains);
121
+ } else {
122
+ chains.forEach(chain => updateChain(chain));
123
+ }
124
+ emit('chains_updated', { chains, isInitial });
125
+ });
126
+
127
+ socket.on('buckets_updated', (data) => {
128
+ emit('buckets_updated', data);
129
+ });
130
+
131
+ socket.on('operations_updated', (operations) => {
132
+ emit('operations_updated', operations);
133
+ });
134
+
135
+ socket.on('recent_yard_messages', (data) => {
136
+ emit('recent_yard_messages', data);
137
+ });
138
+
139
+ socket.on('status_updated', (data) => {
140
+ emit('status_updated', data);
141
+ });
142
+
143
+ socket.on('volumeUpdate', (data) => {
144
+ emit('volumeUpdate', data);
145
+ });
146
+
147
+ socket.on('operationUpdate', (operation) => {
148
+ emit('operationUpdate', operation);
90
149
  });
91
150
 
151
+ socket.on('candlestick_updated', (data) => {
152
+ emit('candlestick_updated', data);
153
+ });
154
+
155
+ // ---- Private room events ----
156
+
92
157
  socket.on('wallets_updated', ({ wallets, isInitial }) => {
93
- // console.log('Received wallets_updated:', { wallets, isInitial });
94
158
  if (isInitial) {
95
159
  setInitialWallets(wallets);
96
160
  } else {
97
161
  wallets.forEach(wallet => updateWallet(wallet));
98
162
  }
163
+ emit('wallets_updated', { wallets, isInitial });
99
164
  });
100
165
 
101
166
  socket.on('user_shares_updated', ({ userShares, isInitial }) => {
102
- // console.log('Received user_shares_updated:', { userShares, isInitial });
103
167
  if (isInitial) {
104
168
  setInitialUserShares(userShares);
105
169
  } else {
106
170
  userShares.forEach(share => updateUserShare(share));
107
171
  }
172
+ emit('user_shares_updated', { userShares, isInitial });
108
173
  });
109
174
 
110
- socket.on('volumeUpdate', ({ type, poolId, timestamp, volume }) => {
111
- // console.log('Volume update:', { type, poolId, timestamp, volume });
112
- });
113
-
114
- socket.on('operationUpdate', (operation) => {
115
- // console.log('Operation update:', operation);
116
- // Assuming operation includes liquidity deposit/withdraw results
117
- // if (operation.type === 'liquidity_deposit' || operation.type === 'liquidity_withdraw') {
118
- // console.log(`Liquidity operation ${operation.type} completed:`, {
119
- // uid: operation.uid,
120
- // coinA: operation.coinA,
121
- // coinB: operation.coinB,
122
- // amountA: operation.amountA,
123
- // amountB: operation.amountB,
124
- // shares: operation.shares,
125
- // status: operation.status,
126
- // });
127
- // }
128
- });
129
- socket.on('status_updated', (data) => {
130
- // console.log('Status update:', data);
131
- });
132
-
133
- socket.on('deposit_address_generated', ({ requestId, chainId, address, memo }) => {
134
- // console.log('Deposit address generated:', { requestId, chainId, address, memo });
175
+ socket.on('deposit_address_generated', (data) => {
176
+ emit('deposit_address_generated', data);
135
177
  });
136
178
 
137
179
  socket.on('deposit_processed', (data) => {
138
- const { amount, coin, chain, confirmations, status, credited } = data;
139
- const message =
140
- status === 'confirmed' && credited
141
- ? `Deposit of ${amount} ${coin.ticker} confirmed!`
142
- : `Deposit of ${amount} ${coin.ticker} (confirming) [${confirmations}/${chain.requiredConfirmations}]`;
143
- console.log('Deposit processed:', { message, data });
180
+ emit('deposit_processed', data);
144
181
  });
145
182
 
146
183
  socket.on('withdrawal_processed', (data) => {
147
- const { amount, coin, chain, confirmations, status, credited, createdAt } = data;
148
- let message;
149
- if (!createdAt) {
150
- message = `Withdrawal of ${amount} ${coin.ticker} is stalled due to network congestion.`;
151
- } else if (status === 'confirmed' && credited) {
152
- message = `Withdrawal of ${amount} ${coin.ticker} confirmed!`;
153
- } else {
154
- message = `Withdrawal of ${amount} ${coin.ticker} (confirming) [${confirmations}/${chain.requiredConfirmations}]`;
155
- }
156
- console.log('Withdrawal processed:', { message, data });
184
+ emit('withdrawal_processed', data);
157
185
  });
158
186
 
159
187
  socket.on('withdrawal_initiated', (data) => {
160
- console.log('Withdrawal initiated:', data);
188
+ emit('withdrawal_initiated', data);
189
+ });
190
+
191
+ socket.on('withdrawal_pin_generated', (data) => {
192
+ emit('withdrawal_pin_generated', data);
161
193
  });
162
194
 
163
- socket.on('withdrawal_pin_generated', ({ pinImage, ticker, amount, pendingWithdrawalId, expiresAt, dp, fee, memoRequired }) => {
164
- console.log('Withdrawal pin generated:', {
165
- ticker,
166
- amount,
167
- pinImage,
168
- pendingWithdrawalId,
169
- expiresAt,
170
- dp,
171
- fee,
172
- memoRequired,
173
- });
195
+ socket.on('withdrawal_queued', (data) => {
196
+ emit('withdrawal_queued', data);
174
197
  });
175
198
 
176
- socket.on('withdrawal_queued', ({ pendingWithdrawalId, ticker }) => {
177
- console.log('Withdrawal queued:', { pendingWithdrawalId, ticker });
199
+ socket.on('withdrawal_canceled', (data) => {
200
+ emit('withdrawal_canceled', data);
178
201
  });
179
202
 
180
- socket.on('withdrawal_canceled', ({ ticker, amount }) => {
181
- console.log('Withdrawal canceled:', { ticker, amount });
203
+ socket.on('withdrawal_updated', (data) => {
204
+ emit('withdrawal_updated', data);
182
205
  });
183
206
 
184
- // socket.on('recent_yard_messages', (initialMessages) => {
185
- // console.log('Recent yard messages:', initialMessages);
186
- // });
207
+ socket.on('withdrawal_expired', (data) => {
208
+ emit('withdrawal_expired', data);
209
+ });
187
210
 
188
211
  socket.on('yard_message', (message) => {
189
- // console.log('New chat message:', {
190
- // username: message.username,
191
- // text: message.text,
192
- // role: message.role,
193
- // timestamp: new Date(message.timestamp).toLocaleString(),
194
- // });
212
+ emit('yard_message', message);
195
213
  });
196
214
 
197
- socket.on('message_deleted', ({ messageId }) => {
198
- // console.log('Message deleted:', { messageId });
215
+ socket.on('message_deleted', (data) => {
216
+ emit('message_deleted', data);
199
217
  });
200
218
 
201
- socket.on('banned', ({ reason, bannedUntil }) => {
202
- // console.log('Banned from yard:', {
203
- // reason,
204
- // bannedUntil: new Date(bannedUntil).toLocaleString(),
205
- // });
219
+ socket.on('banned', (data) => {
220
+ emit('banned', data);
206
221
  });
207
222
 
208
- socket.on('withdrawal_updated', ({ pendingWithdrawalId, expiresAt, stage }) => {
209
- // console.log('Withdrawal updated:', { pendingWithdrawalId, expiresAt, stage });
223
+ socket.on('yard_read_marked', (data) => {
224
+ emit('yard_read_marked', data);
210
225
  });
211
226
 
212
- socket.on('withdrawal_expired', ({ pendingWithdrawalId, ticker, amount }) => {
213
- // console.log('Withdrawal expired:', { pendingWithdrawalId, ticker, amount });
227
+ socket.on('session_expired', (data) => {
228
+ emit('session_expired', data);
214
229
  });
215
230
 
216
231
  socket.on('pong', () => {
217
- console.log('Received pong from server');
232
+ emit('pong', null);
218
233
  });
219
234
 
220
- const heartbeatInterval = setInterval(() => {
221
- if (socket.connected) {
222
- socket.emit('ping');
223
- }
224
- }, 30000);
235
+ // ---- Convenience methods ----
225
236
 
226
- socket.on('close', () => {
227
- clearInterval(heartbeatInterval);
228
- resetPools();
229
- resetCoins();
230
- resetWallets();
231
- resetUserShares();
232
- });
233
-
234
- socket.on('connect', () => {
235
- // socket.emit('yard_message', {
236
- // text: 'Hello from RunesX API Key Example Bot!!',
237
- // });
238
- });
237
+ function on(event, callback) {
238
+ if (!callbacks[event]) {
239
+ callbacks[event] = [];
240
+ }
241
+ callbacks[event].push(callback);
242
+ }
239
243
 
240
- return { socket };
241
- }
244
+ function off(event, callback) {
245
+ if (!callbacks[event]) { return; }
246
+ if (callback) {
247
+ callbacks[event] = callbacks[event].filter((cb) => cb !== callback);
248
+ } else {
249
+ delete callbacks[event];
250
+ }
251
+ }
252
+
253
+ function joinCandlesticks(poolId, timeframe) {
254
+ socket.emit('join_candlesticks', { poolId, timeframe });
255
+ }
256
+
257
+ function leaveCandlesticks(poolId, timeframe) {
258
+ socket.emit('leave_candlesticks', { poolId, timeframe });
259
+ }
260
+
261
+ function sendYardMessage(text) {
262
+ socket.emit('yard_message', { text });
263
+ }
264
+
265
+ function deleteMessage(messageId) {
266
+ socket.emit('delete_message', { messageId });
267
+ }
268
+
269
+ function markYardRead() {
270
+ socket.emit('mark_yard_read');
271
+ }
272
+
273
+ function leavePublic() {
274
+ socket.leave('public');
275
+ }
276
+
277
+ function leavePrivate() {
278
+ socket.leave('private');
279
+ }
280
+
281
+ return {
282
+ socket,
283
+ on,
284
+ off,
285
+ joinCandlesticks,
286
+ leaveCandlesticks,
287
+ sendYardMessage,
288
+ deleteMessage,
289
+ markYardRead,
290
+ leavePublic,
291
+ leavePrivate,
292
+ };
293
+ }
@@ -0,0 +1,92 @@
1
+ const chainStore = {
2
+ chains: new Map(), // Store chain data by name (lowercase)
3
+ isInitialReceived: false,
4
+ pendingUpdates: [],
5
+ };
6
+
7
+ const setInitialChains = (chains) => {
8
+ chainStore.chains.clear();
9
+ chains.forEach(chain => {
10
+ chainStore.chains.set(chain.name.toLowerCase(), {
11
+ id: chain.id,
12
+ name: chain.name,
13
+ blockchainId: chain.blockchainId,
14
+ requiredConfirmations: chain.requiredConfirmations,
15
+ hasMemo: chain.hasMemo,
16
+ status: chain.status,
17
+ chainType: chain.chainType,
18
+ nativeTicker: chain.nativeTicker,
19
+ explorerTxUrl: chain.explorerTxUrl,
20
+ updatedAt: chain.updatedAt,
21
+ });
22
+ });
23
+ chainStore.isInitialReceived = true;
24
+
25
+ if (chainStore.pendingUpdates.length > 0) {
26
+ chainStore.pendingUpdates.forEach(({ chains }) => {
27
+ chains.forEach(chain => updateChain(chain));
28
+ });
29
+ chainStore.pendingUpdates = [];
30
+ }
31
+ };
32
+
33
+ const updateChain = (chain) => {
34
+ if (!chainStore.isInitialReceived) {
35
+ chainStore.pendingUpdates.push({ chains: [chain] });
36
+ return;
37
+ }
38
+
39
+ const key = chain.name.toLowerCase();
40
+ const existingChain = chainStore.chains.get(key);
41
+ const incomingUpdatedAt = new Date(chain.updatedAt).getTime();
42
+
43
+ if (existingChain) {
44
+ const existingUpdatedAt = new Date(existingChain.updatedAt).getTime();
45
+ if (incomingUpdatedAt <= existingUpdatedAt) {
46
+ return;
47
+ }
48
+
49
+ chainStore.chains.set(key, {
50
+ ...existingChain,
51
+ blockchainId: chain.blockchainId,
52
+ requiredConfirmations: chain.requiredConfirmations,
53
+ hasMemo: chain.hasMemo,
54
+ status: chain.status,
55
+ chainType: chain.chainType,
56
+ nativeTicker: chain.nativeTicker,
57
+ explorerTxUrl: chain.explorerTxUrl,
58
+ updatedAt: chain.updatedAt,
59
+ });
60
+ } else if (chain.name && chain.status) {
61
+ chainStore.chains.set(key, {
62
+ id: chain.id,
63
+ name: chain.name,
64
+ blockchainId: chain.blockchainId,
65
+ requiredConfirmations: chain.requiredConfirmations,
66
+ hasMemo: chain.hasMemo,
67
+ status: chain.status,
68
+ chainType: chain.chainType,
69
+ nativeTicker: chain.nativeTicker,
70
+ explorerTxUrl: chain.explorerTxUrl,
71
+ updatedAt: chain.updatedAt,
72
+ });
73
+ } else {
74
+ console.warn(`Ignoring update for unknown chain ${chain.name} with incomplete data`);
75
+ }
76
+ };
77
+
78
+ const getChains = () => {
79
+ return Array.from(chainStore.chains.values());
80
+ };
81
+
82
+ const getChainByName = (name) => {
83
+ return chainStore.chains.get(name.toLowerCase());
84
+ };
85
+
86
+ const resetChains = () => {
87
+ chainStore.chains.clear();
88
+ chainStore.isInitialReceived = false;
89
+ chainStore.pendingUpdates = [];
90
+ };
91
+
92
+ export { chainStore, setInitialChains, updateChain, getChains, getChainByName, resetChains };
@@ -1,5 +1,6 @@
1
1
  import { poolStore, getPools } from './store/poolStore.mjs';
2
2
  import { coinStore, getCoins } from './store/coinStore.mjs';
3
+ import { chainStore, getChains } from './store/chainStore.mjs';
3
4
  import { walletStore, getWallets } from './store/walletStore.mjs';
4
5
  import { userSharesStore, getUserShares } from './store/userSharesStore.mjs';
5
6
 
@@ -74,6 +75,41 @@ export function waitForStores(socket) {
74
75
  });
75
76
  };
76
77
 
78
+ // Function to wait for chainStore to be populated
79
+ const waitForChains = () => {
80
+ return new Promise((resolve, reject) => {
81
+ if (chainStore.isInitialReceived) {
82
+ const chains = getChains();
83
+ resolve(chains);
84
+ return;
85
+ }
86
+
87
+ const timeout = setTimeout(() => {
88
+ reject(new Error('Timeout waiting for initial chain data'));
89
+ }, 30000); // 30-second timeout
90
+
91
+ socket.on('chains_updated', ({ isInitial }) => {
92
+ if (isInitial) {
93
+ const chains = getChains();
94
+ clearTimeout(timeout);
95
+ resolve(chains);
96
+ }
97
+ });
98
+
99
+ socket.on('connect_error', (err) => {
100
+ console.error('Socket connect error:', err.message);
101
+ clearTimeout(timeout);
102
+ reject(new Error('Socket connection failed'));
103
+ });
104
+
105
+ socket.on('disconnect', (reason) => {
106
+ console.error('Socket disconnected:', reason);
107
+ clearTimeout(timeout);
108
+ reject(new Error('Socket disconnected before receiving initial chain data'));
109
+ });
110
+ });
111
+ };
112
+
77
113
  // Function to wait for walletStore to be populated
78
114
  const waitForWallets = () => {
79
115
  return new Promise((resolve, reject) => {
@@ -145,10 +181,11 @@ export function waitForStores(socket) {
145
181
  };
146
182
 
147
183
  // Return a promise that resolves when all stores are populated
148
- return Promise.all([waitForPools(), waitForCoins(), waitForWallets(), waitForUserShares()])
149
- .then(([pools, coins, wallets, userShares]) => ({
184
+ return Promise.all([waitForPools(), waitForCoins(), waitForChains(), waitForWallets(), waitForUserShares()])
185
+ .then(([pools, coins, chains, wallets, userShares]) => ({
150
186
  pools,
151
187
  coins,
188
+ chains,
152
189
  wallets,
153
190
  userShares,
154
191
  }));