@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/README.md CHANGED
@@ -1,24 +1,25 @@
1
1
  # RunesX API Client
2
2
 
3
- A Node.js client for interacting with the RunesX platform API and WebSocket, enabling developers to build bots or scripts for real-time trading, liquidity management, and data monitoring.
3
+ A Node.js client for interacting with the RunesX platform API and WebSocket, enabling developers to build bots or scripts for real-time trading, liquidity management, withdrawals, deposits, and data monitoring.
4
4
 
5
5
  ## Features
6
- - **WebSocket Integration**: Connect to the RunesX Socket.IO server to listen for real-time events like `pools_updated`, `coins_updated`, `wallets_updated`, `user_shares_updated`, and chat messages (`yard_message`).
7
- - **API Access**: Perform HTTP requests for wallet data, swaps, and liquidity operations.
8
- - **Store Management**: Manage coin, pool, wallet, and user share data with built-in buffering for WebSocket updates.
9
- - **Swap Estimation**: Estimate swap outcomes using DFS or BFS pathfinding algorithms.
10
- - **Liquidity Management**: Estimate, deposit, and withdraw liquidity for trading pairs.
11
- - **Monitoring**: Periodically monitor pool, wallet, and user share states.
6
+
7
+ - **WebSocket Integration**: Real-time events for pools, coins, chains, wallets, user shares, operations, deposits, withdrawals, candlesticks, volume, and chat
8
+ - **Full REST API**: Complete HTTP API coverage for all public and private endpoints
9
+ - **Store Management**: Automatic real-time state management for pools, coins, chains, wallets, and user shares with buffering for out-of-order updates
10
+ - **Swap Estimation**: Client-side swap estimation using DFS or BFS pathfinding algorithms
11
+ - **Liquidity Management**: Estimate, deposit, and withdraw liquidity with RUNES compliance checking
12
+ - **Withdrawal Flow**: Full multi-step withdrawal verification (PIN, email, 2FA)
13
+ - **Price Utilities**: Calculate token prices in RUNES and USD
14
+ - **Event System**: Register callbacks for any WebSocket event
12
15
 
13
16
  ## Installation
17
+
14
18
  ```bash
15
19
  npm install @runesx/api-client
16
20
  ```
17
21
 
18
- ## Usage
19
-
20
- 1. Initialize the Client
21
- Create and initialize the client with your API key and endpoint URLs. This connects to the WebSocket and fetches initial data for pools, coins, wallets, and user shares.
22
+ ## Quick Start
22
23
 
23
24
  ```javascript
24
25
  import { createRunesXClient } from '@runesx/api-client';
@@ -30,497 +31,681 @@ const client = createRunesXClient({
30
31
  });
31
32
 
32
33
  async function main() {
33
- try {
34
- const { pools, coins, wallets, userShares } = await client.initialize();
35
- console.log('Initialized with:', {
36
- poolCount: pools.length,
37
- coinCount: coins.length,
38
- walletCount: wallets.length,
39
- userShareCount: userShares.length,
40
- });
41
- } catch (error) {
42
- console.error('Initialization failed:', error.message);
43
- }
34
+ const { pools, coins, chains, wallets, userShares } = await client.initialize();
35
+ console.log(`Connected: ${pools.length} pools, ${coins.length} coins, ${wallets.length} wallets`);
36
+
37
+ // Listen for real-time events
38
+ client.on('operationUpdate', (operation) => {
39
+ console.log('New operation:', operation);
40
+ });
41
+
42
+ // Get prices
43
+ const prices = await client.getPrices();
44
+ console.log('Prices:', prices);
44
45
  }
45
46
 
46
- main();
47
+ main().catch(console.error);
48
+ ```
49
+
50
+ ## Configuration
51
+
52
+ ```javascript
53
+ const client = createRunesXClient({
54
+ apiKey: 'your-api-key', // Required. Your RunesX API key
55
+ apiUrl: 'https://www.runesx.xyz/api', // Optional. Default: http://localhost:3010
56
+ socketUrl: 'https://www.runesx.xyz/api', // Optional. Default: http://localhost:3010
57
+ });
47
58
  ```
48
59
 
49
- 2. Accessing Store Data
50
- Retrieve data from the internal stores for pools, coins, wallets, and user shares.
60
+ You can also set these via environment variables:
61
+ - `API_KEY` - Your API key
62
+ - `API_URL` - API base URL
63
+ - `SOCKET_URL` - WebSocket URL
64
+
65
+ ## API Key Scopes
66
+
67
+ When generating an API key, you can assign specific scopes:
68
+
69
+ | Scope | Description |
70
+ |-------|-------------|
71
+ | `read` | Read-only access to user data (wallets, shares, operations, transactions) |
72
+ | `swap` | Execute token swaps |
73
+ | `liquidity_deposit` | Deposit liquidity to pools |
74
+ | `liquidity_withdraw` | Withdraw liquidity from pools |
75
+ | `wallet_withdraw` | Withdraw to external addresses |
76
+ | `chat` | Access yard/chat features |
77
+
78
+ ---
79
+
80
+ ## Store Data (Real-time via WebSocket)
81
+
82
+ After `initialize()`, the client maintains real-time stores updated via WebSocket. These are always up-to-date.
83
+
84
+ ### Pools
51
85
 
52
- ### Get All Pools
53
86
  ```javascript
54
- const pools = client.getPools();
55
- console.log('Available pools:', pools.map(pool => ({
56
- id: pool.id,
57
- pair: `${pool.coinA.ticker}/${pool.coinB.ticker}`,
58
- reserveA: pool.reserveA,
59
- reserveB: pool.reserveB,
60
- })));
87
+ const pools = client.getPools(); // All pools
88
+ const pool = client.getPool(poolId); // Pool by ID
61
89
  ```
62
90
 
63
- ### Get a Specific Pool
91
+ Pool object:
64
92
  ```javascript
65
- const pool = client.getPool(1);
66
- console.log('Pool 1:', pool ? {
67
- pair: `${pool.coinA.ticker}/${pool.coinB.ticker}`,
68
- reserveA: pool.reserveA,
69
- reserveB: pool.reserveB,
70
- } : 'Pool not found');
93
+ {
94
+ id, reserveA, reserveB, totalShares, activeLiquidityProviders,
95
+ runesCompliant, lpFeeRate, treasuryFeeRate,
96
+ coinA: { id, ticker, dp, projectName },
97
+ coinB: { id, ticker, dp, projectName },
98
+ liquidityShares: [...],
99
+ updatedAt
100
+ }
71
101
  ```
72
102
 
73
- ### Get All Coins
103
+ ### Coins
104
+
74
105
  ```javascript
75
- const coins = client.getCoins();
76
- console.log('Available coins:', coins.map(coin => ({
77
- ticker: coin.ticker,
78
- projectName: coin.projectName,
79
- status: coin.status,
80
- })));
106
+ const coins = client.getCoins(); // All coins
107
+ const coin = client.getCoinByTicker('RUNES'); // Coin by ticker
81
108
  ```
82
109
 
83
- ### Get a Coin by Ticker
110
+ ### Chains
111
+
84
112
  ```javascript
85
- const coin = client.getCoinByTicker('xRUNES');
86
- console.log('xRUNES coin:', coin ? {
87
- ticker: coin.ticker,
88
- dp: coin.dp,
89
- projectName: coin.projectName,
90
- } : 'Coin not found');
113
+ const chains = client.getChains(); // All chains
114
+ const chain = client.getChainByName('RuneBase'); // Chain by name
91
115
  ```
92
116
 
93
- ### Get All Wallets
117
+ ### Wallets
118
+
94
119
  ```javascript
95
- const wallets = client.getWallets();
96
- console.log('Wallets:', wallets.map(wallet => ({
97
- ticker: wallet.ticker,
98
- available: wallet.available,
99
- locked: wallet.locked,
100
- })));
120
+ const wallets = client.getWallets(); // All wallets
121
+ const wallet = client.getWalletByTicker('RUNES'); // Wallet by ticker
122
+ // wallet.available, wallet.locked
101
123
  ```
102
124
 
103
- ### Get a Wallet by Ticker
125
+ ### User Shares
126
+
104
127
  ```javascript
105
- const wallet = client.getWalletByTicker('xRUNES');
106
- console.log('xRUNES wallet:', wallet ? {
107
- ticker: wallet.ticker,
108
- available: wallet.available,
109
- } : 'Wallet not found');
128
+ const shares = client.getUserShares(); // All shares
129
+ const share = client.getUserShareByPoolId(poolId); // Share by pool
130
+ // share.shares, share.poolId
110
131
  ```
111
132
 
112
- ### Get All User Shares
133
+ ---
134
+
135
+ ## REST API Methods
136
+
137
+ ### Public Endpoints (No Auth Required)
138
+
139
+ #### System Status
140
+
113
141
  ```javascript
114
- const userShares = client.getUserShares();
115
- console.log('User shares:', userShares.map(share => ({
116
- poolId: share.poolId,
117
- shares: share.shares,
118
- })));
142
+ const status = await client.getStatus();
143
+ // { apiVersion, apiStatus, ratatoskrVersion, ratatoskrStatus }
119
144
  ```
120
145
 
121
- ### Get User Shares by Pool ID
146
+ #### Coins (via API)
147
+
122
148
  ```javascript
123
- const share = client.getUserShareByPoolId(1);
124
- console.log('Shares for pool 1:', share ? {
125
- poolId: share.poolId,
126
- shares: share.shares,
127
- } : 'No shares found');
149
+ const coins = await client.getCoinsApi(); // All coins
150
+ const coin = await client.getCoinApi('RUNES'); // Single coin with chain details
128
151
  ```
129
152
 
130
- 3. Estimating and Executing a Swap
131
- Estimate and execute a swap between two tokens, such as 0.1 xRUNES to XLM, with slippage tolerance.
153
+ #### Pools (via API)
132
154
 
133
155
  ```javascript
134
- import { BigNumber } from 'bignumber.js';
156
+ const pools = await client.getPoolsApi(); // All pools
157
+ const pool = await client.getPoolByPair('RUNES', 'USDC'); // Specific pool
158
+ const shares = await client.getPoolLiquidityShares(poolId); // Pool's liquidity shares
159
+ ```
135
160
 
136
- async function executeSwap() {
137
- try {
138
- const inputCoin = client.getCoinByTicker('xRUNES');
139
- const outputCoin = client.getCoinByTicker('XLM');
140
- if (!inputCoin || !outputCoin) {
141
- throw new Error('Required coins not found');
142
- }
161
+ #### Prices
143
162
 
144
- const amountIn = '0.1'; // 0.1 xRUNES
145
- const maxHops = 6;
146
- const algorithm = 'dfs';
147
- const slippageTolerance = '1'; // 1%
148
-
149
- // Estimate swap
150
- const swapEstimate = await client.estimateSwap(inputCoin, outputCoin, amountIn, maxHops, algorithm);
151
- console.log('Swap estimate:', {
152
- input: swapEstimate.input,
153
- output: swapEstimate.output,
154
- priceImpact: `${(swapEstimate.slippage.priceImpact * 100).toFixed(2)}%`,
155
- path: swapEstimate.path,
156
- });
157
-
158
- // Calculate minimum output with slippage
159
- const amountOutBN = new BigNumber(swapEstimate.output.amount);
160
- const minAmountOut = amountOutBN
161
- .times(new BigNumber(1).minus(new BigNumber(slippageTolerance).div(100)))
162
- .toFixed(outputCoin.dp, BigNumber.ROUND_DOWN);
163
-
164
- // Check wallet balance
165
- const wallet = client.getWalletByTicker(inputCoin.ticker);
166
- if (!wallet || new BigNumber(wallet.available).lt(amountIn)) {
167
- throw new Error(`Insufficient balance for ${inputCoin.ticker}: ${wallet?.available || 0}`);
168
- }
163
+ ```javascript
164
+ const prices = await client.getPrices();
165
+ // { prices: [{ ticker, projectName, dp, priceInRunes, priceInUSD }], runesPriceUSD, timestamp }
169
166
 
170
- // Execute swap
171
- const swap = await client.postSwap({
172
- amountIn,
173
- path: swapEstimate.path,
174
- minAmountOut,
175
- });
176
- console.log('Swap executed:', swap.data);
177
- } catch (error) {
178
- console.error('Swap failed:', error.message);
179
- }
180
- }
167
+ const price = await client.getPrice('RUNES');
168
+ // { ticker, projectName, dp, priceInRunes, priceInUSD, runesPriceUSD, timestamp }
169
+ ```
181
170
 
182
- executeSwap();
171
+ #### Candlesticks
172
+
173
+ ```javascript
174
+ const candles = await client.getCandlesticks(poolId, '1h', fromTimestamp, toTimestamp);
175
+ // [{ time, open, high, low, close, volume }]
183
176
  ```
184
177
 
185
- 4. Managing Liquidity
186
- Deposit and withdraw liquidity for a trading pair, such as RUNES/XLM.
178
+ Valid timeframes: `1m`, `5m`, `15m`, `1h`, `4h`, `12h`, `1d`, `1w`, `1M`
187
179
 
180
+ #### Volume
188
181
 
189
182
  ```javascript
190
- import { BigNumber } from 'bignumber.js';
183
+ const total = await client.getVolumeTotal();
184
+ const poolVol = await client.getVolumePool(poolId);
185
+ ```
191
186
 
192
- async function manageLiquidity() {
193
- try {
194
- const coinA = client.getCoinByTicker('RUNES');
195
- const coinB = client.getCoinByTicker('XLM');
196
- if (!coinA || !coinB) {
197
- throw new Error('Required coins not found');
198
- }
187
+ #### Buckets
199
188
 
200
- // Check RUNES compliance
201
- const { isCompliant, warnings } = client.checkRunesLiquidityFrontend(coinA, coinB);
202
- if (!isCompliant) {
203
- console.error('Cannot deposit liquidity:', warnings);
204
- return;
205
- }
189
+ ```javascript
190
+ const buckets = await client.getBucketsPools();
191
+ ```
206
192
 
207
- // Estimate liquidity amounts
208
- const amountA = '1'; // 1 RUNES
209
- const estimate = await client.estimateLiquidityFrontend({
210
- coinA,
211
- coinB,
212
- amountA,
213
- amountB: null,
214
- pools: client.getPools(),
215
- coins: client.getCoins(),
216
- });
217
- console.log('Liquidity estimate:', {
218
- amountA: estimate.amountA,
219
- amountB: estimate.amountB,
220
- pair: `${estimate.coinA.ticker}/${estimate.coinB.ticker}`,
221
- isPoolEmpty: estimate.isPoolEmpty,
222
- flipped: estimate.flipped,
223
- });
224
-
225
- // Validate wallet balances
226
- const walletA = client.getWalletByTicker(coinA.ticker);
227
- const walletB = client.getWalletByTicker(coinB.ticker);
228
- if (!walletA || new BigNumber(walletA.available).lt(estimate.amountA)) {
229
- throw new Error(`Insufficient balance for ${coinA.ticker}: ${walletA?.available || 0}`);
230
- }
231
- if (!walletB || new BigNumber(walletB.available).lt(estimate.amountB)) {
232
- throw new Error(`Insufficient balance for ${coinB.ticker}: ${walletB?.available || 0}`);
233
- }
193
+ #### Operations
234
194
 
235
- // Deposit liquidity
236
- const depositParams = {
237
- coinA: { ticker: estimate.coinA.ticker },
238
- coinB: { ticker: estimate.coinB.ticker },
239
- amountA: estimate.amountA,
240
- amountB: estimate.amountB,
241
- };
242
- const depositResult = await client.depositLiquidity(depositParams);
243
- console.log('Liquidity deposited:', {
244
- pair: `${depositResult.coinA.ticker}/${depositResult.coinB.ticker}`,
245
- amountA: depositResult.amountA,
246
- amountB: depositResult.amountB,
247
- shares: depositResult.shares,
248
- uid: depositResult.uid,
249
- });
250
-
251
- // Wait for user shares update
252
- await new Promise(resolve => setTimeout(resolve, 5000));
253
-
254
- // Calculate and withdraw shares
255
- const updatedUserShares = client.getUserShares();
256
- const calculatedShares = client.calculateShareAmounts();
257
- const share = calculatedShares.find(
258
- s => s.coinA.ticker === depositParams.coinA.ticker && s.coinB.ticker === depositParams.coinB.ticker
259
- );
260
-
261
- if (!share) {
262
- throw new Error('No shares found for the deposited pool');
263
- }
195
+ ```javascript
196
+ // Recent operations (public)
197
+ const recent = await client.getRecentOperations({ operationType: 'swap', limit: 20 });
264
198
 
265
- const sharesToWithdraw = new BigNumber(share.shares).div(2).integerValue(BigNumber.ROUND_DOWN).toString();
266
- const withdrawResult = await client.withdrawLiquidity({
267
- coinA: { ticker: share.coinA.ticker },
268
- coinB: { ticker: share.coinB.ticker },
269
- shares: sharesToWithdraw,
270
- });
271
- console.log('Liquidity withdrawn:', {
272
- pair: `${withdrawResult.coinA.ticker}/${withdrawResult.coinB.ticker}`,
273
- amountA: withdrawResult.amountA,
274
- amountB: withdrawResult.amountB,
275
- shares: withdrawResult.shares,
276
- uid: withdrawResult.uid,
277
- });
278
- } catch (error) {
279
- console.error('Liquidity operation failed:', error.message);
280
- }
281
- }
199
+ // Pool operations
200
+ const poolOps = await client.getPoolOperations(poolId, { operationType: 'swap' });
201
+ ```
202
+
203
+ Valid operation types: `swap`, `liquidityDeposit`, `liquidityWithdrawal`
282
204
 
283
- manageLiquidity();
205
+ #### Yard Messages
206
+
207
+ ```javascript
208
+ const messages = await client.getYardMessages({ limit: 50 });
209
+ const older = await client.getYardMessages({ before: '2024-01-01T00:00:00Z', limit: 50 });
284
210
  ```
285
211
 
286
- 5. Monitoring Pools, Wallets, and User Shares
287
- Set up periodic monitoring for pools, wallets, and user shares, and listen for WebSocket updates.
212
+ ### Private Endpoints (Auth Required)
213
+
214
+ #### Wallets
288
215
 
289
- ### Monitor a Specific Pool
290
216
  ```javascript
291
- client.monitorPool(1, 10000); // Monitor pool ID 1 every 10 seconds
217
+ const wallets = await client.getWalletsApi(); // Scope: read
292
218
  ```
293
219
 
294
- ### Monitor Wallets
220
+ #### Swap (Scope: swap)
221
+
295
222
  ```javascript
296
- function monitorWallets(interval = 10000) {
297
- setInterval(() => {
298
- const wallets = client.getWallets();
299
- console.log('Wallets:', wallets.length > 0
300
- ? wallets.map(wallet => ({
301
- ticker: wallet.ticker,
302
- available: wallet.available,
303
- locked: wallet.locked,
304
- updatedAt: wallet.updatedAt,
305
- }))
306
- : 'No wallets available');
307
- }, interval);
308
- }
223
+ const result = await client.postSwap({
224
+ amountIn: '1.0',
225
+ path: [{ from: 'RUNES', to: 'USDC' }],
226
+ minAmountOut: '0.009',
227
+ idempotencyKey: 'optional-unique-key', // Auto-generated if not provided
228
+ });
229
+ ```
230
+
231
+ #### Liquidity (Scope: liquidity_deposit / liquidity_withdraw)
309
232
 
310
- monitorWallets();
233
+ ```javascript
234
+ // Deposit
235
+ const deposit = await client.depositLiquidity({
236
+ tickerA: 'RUNES',
237
+ tickerB: 'USDC',
238
+ amountA: '100',
239
+ amountB: '1.5',
240
+ minShares: '1000', // Optional slippage protection
241
+ idempotencyKey: 'optional-key',
242
+ });
243
+
244
+ // Withdraw
245
+ const withdraw = await client.withdrawLiquidity({
246
+ tickerA: 'RUNES',
247
+ tickerB: 'USDC',
248
+ shares: '5000',
249
+ minAmountA: '90', // Optional slippage protection
250
+ minAmountB: '1.3', // Optional slippage protection
251
+ idempotencyKey: 'optional-key',
252
+ });
253
+
254
+ // Get user's liquidity shares via API
255
+ const shares = await client.getLiquidityShares();
311
256
  ```
312
257
 
313
- ### Monitor User Shares
258
+ #### Deposits
259
+
314
260
  ```javascript
315
- function monitorUserShares(interval = 10000) {
316
- setInterval(() => {
317
- const userShares = client.getUserShares();
318
- console.log('User shares:', userShares.length > 0
319
- ? userShares.map(share => ({
320
- poolId: share.poolId,
321
- shares: share.shares,
322
- updatedAt: share.updatedAt,
323
- }))
324
- : 'No user shares available');
325
- }, interval);
326
- }
261
+ // Get deposit address for a chain
262
+ const { address, memo } = await client.getDepositAddress('RuneBase');
263
+
264
+ // Get all deposit addresses
265
+ const addresses = await client.getAllDepositAddresses();
266
+ // [{ chainName, address, memo }]
267
+ ```
268
+
269
+ #### Withdrawals (Scope: wallet_withdraw)
270
+
271
+ The withdrawal process is multi-step:
272
+
273
+ ```javascript
274
+ // 1. Initiate withdrawal
275
+ const initResult = await client.initiateWithdraw({
276
+ ticker: 'RUNES',
277
+ chain: 'RuneBase',
278
+ address: 'RxYz...',
279
+ amount: '100',
280
+ memo: null, // Optional, for chains that require it
281
+ idempotencyKey: 'optional-key',
282
+ });
283
+ const { pendingWithdrawalId } = initResult.data;
284
+
285
+ // 2. Verify PIN (shown as image via socket event 'withdrawal_pin_generated')
286
+ await client.verifyWithdrawPin({ pendingWithdrawalId, pinCode: '123456' });
287
+
288
+ // 3. Send email PIN
289
+ await client.sendWithdrawEmailPin({ pendingWithdrawalId });
290
+
291
+ // 4. Verify email PIN
292
+ await client.verifyWithdrawEmailPin({ pendingWithdrawalId, emailPinCode: '654321' });
293
+
294
+ // 5. Verify 2FA (if enabled)
295
+ await client.verifyWithdraw2FA({ pendingWithdrawalId, twoFactorToken: '123456' });
296
+
297
+ // Check pending withdrawals
298
+ const pending = await client.getPendingWithdrawals();
299
+
300
+ // Cancel a pending withdrawal
301
+ await client.cancelWithdrawal({ pendingWithdrawalId });
302
+ ```
327
303
 
328
- monitorUserShares();
304
+ #### Transaction History (Scope: read)
305
+
306
+ ```javascript
307
+ const history = await client.getTransactionHistory({
308
+ page: 1,
309
+ limit: 20,
310
+ type: 'deposit', // Optional: 'deposit' or 'withdrawal'
311
+ status: 'confirmed', // Optional
312
+ });
313
+ // { success, data: { transactions: [...], total, page, pages } }
314
+ ```
315
+
316
+ #### User Operations (Scope: read)
317
+
318
+ ```javascript
319
+ const ops = await client.getUserOperations({
320
+ operationType: 'swap', // Optional
321
+ poolId: '1', // Optional
322
+ startTime: '2024-01-01T00:00:00Z', // Optional
323
+ endTime: '2024-12-31T23:59:59Z', // Optional
324
+ page: 1,
325
+ limit: 50,
326
+ });
327
+ ```
328
+
329
+ #### Yard Moderation (Scope: chat)
330
+
331
+ ```javascript
332
+ // Delete a message (via REST API)
333
+ await client.deleteYardMessage(messageId);
334
+ ```
335
+
336
+ ---
337
+
338
+ ## WebSocket Events
339
+
340
+ ### Event Listener System
341
+
342
+ Use `client.on()` and `client.off()` to register callbacks for any WebSocket event:
343
+
344
+ ```javascript
345
+ // Register a callback
346
+ const handler = (data) => console.log('Pool update:', data);
347
+ client.on('pools_updated', handler);
348
+
349
+ // Remove a specific callback
350
+ client.off('pools_updated', handler);
351
+
352
+ // Remove all callbacks for an event
353
+ client.off('pools_updated');
354
+ ```
355
+
356
+ ### Available Events
357
+
358
+ #### Public Events (received after joining public room)
359
+
360
+ | Event | Data | Description |
361
+ |-------|------|-------------|
362
+ | `pools_updated` | `{ pools, isInitial }` | Pool data updated |
363
+ | `coins_updated` | `{ coins, isInitial }` | Coin data updated |
364
+ | `chains_updated` | `{ chains, isInitial }` | Chain data updated |
365
+ | `buckets_updated` | `{ type, buckets }` | Pool bucket data |
366
+ | `operations_updated` | `[operations]` | Initial recent operations |
367
+ | `recent_yard_messages` | `{ messages, lastReadAt }` | Initial chat messages |
368
+ | `status_updated` | `{ apiVersion, apiStatus, ratatoskrVersion, ratatoskrStatus }` | System status |
369
+ | `volumeUpdate` | `{ type, poolId, timestamp, volume }` | Volume update |
370
+ | `operationUpdate` | `operation` | New operation executed |
371
+ | `yard_message` | `{ id, text, userId, username, role, timestamp }` | New chat message |
372
+ | `message_deleted` | `{ messageId }` | Chat message deleted |
373
+ | `candlestick_updated` | `data` | Candlestick update (requires joining candlestick room) |
374
+
375
+ #### Private Events (received after joining private room)
376
+
377
+ | Event | Data | Description |
378
+ |-------|------|-------------|
379
+ | `wallets_updated` | `{ wallets, isInitial }` | Wallet balances updated |
380
+ | `user_shares_updated` | `{ userShares, isInitial }` | Liquidity shares updated |
381
+ | `deposit_address_generated` | `{ requestId, chainName, address, memo }` | Deposit address ready |
382
+ | `deposit_processed` | `{ amount, coin, chain, confirmations, status, credited }` | Deposit confirmation update |
383
+ | `withdrawal_initiated` | `data` | Withdrawal process started |
384
+ | `withdrawal_pin_generated` | `{ pinImage, ticker, amount, pendingWithdrawalId, expiresAt, dp, fee, memoRequired }` | PIN image ready |
385
+ | `withdrawal_updated` | `{ pendingWithdrawalId, expiresAt, stage }` | Withdrawal stage changed |
386
+ | `withdrawal_queued` | `{ pendingWithdrawalId, ticker }` | Withdrawal queued for processing |
387
+ | `withdrawal_processed` | `{ amount, coin, chain, confirmations, status, credited }` | Withdrawal confirmation update |
388
+ | `withdrawal_canceled` | `{ ticker, amount }` | Withdrawal canceled |
389
+ | `withdrawal_expired` | `{ pendingWithdrawalId, ticker, amount }` | Withdrawal expired |
390
+ | `banned` | `{ reason, bannedUntil }` | User banned from yard chat |
391
+ | `yard_read_marked` | `{ lastReadAt }` | Yard marked as read |
392
+ | `session_expired` | `{ message }` | Session expired, reconnect needed |
393
+
394
+ #### Connection Events
395
+
396
+ | Event | Data | Description |
397
+ |-------|------|-------------|
398
+ | `connect` | `null` | Connected to server |
399
+ | `disconnect` | `reason` | Disconnected from server |
400
+ | `connect_error` | `error` | Connection error |
401
+ | `reconnect_attempt` | `attempt` | Reconnection attempt number |
402
+ | `reconnect` | `null` | Successfully reconnected |
403
+ | `error` | `error` | Socket error |
404
+
405
+ ### Socket Convenience Methods
406
+
407
+ #### Candlestick Subscriptions
408
+
409
+ ```javascript
410
+ // Subscribe to candlestick updates for a pool
411
+ client.joinCandlesticks(poolId, '1h');
412
+
413
+ client.on('candlestick_updated', (data) => {
414
+ console.log('Candlestick:', data);
415
+ });
416
+
417
+ // Unsubscribe
418
+ client.leaveCandlesticks(poolId, '1h');
419
+ ```
420
+
421
+ #### Chat (The Yard)
422
+
423
+ ```javascript
424
+ // Send a message (Scope: chat)
425
+ client.sendYardMessage('Hello from my bot!');
426
+
427
+ // Listen for messages
428
+ client.on('yard_message', (msg) => {
429
+ console.log(`[${msg.username}]: ${msg.text}`);
430
+ });
431
+
432
+ // Delete a message (moderator/admin only)
433
+ client.deleteMessage(messageId);
434
+
435
+ // Mark yard as read
436
+ client.markYardRead();
329
437
  ```
330
438
 
331
- ### Listen for WebSocket Updates
439
+ ### Raw Socket Access
440
+
441
+ For advanced use cases, access the underlying Socket.IO instance:
442
+
332
443
  ```javascript
333
444
  const socket = client.getSocket();
445
+ socket.emit('custom_event', data);
446
+ ```
447
+
448
+ ---
334
449
 
335
- socket.on('pools_updated', ({ isInitial }) => {
336
- if (isInitial) {
337
- const pools = client.getPools();
338
- console.log('Initial pools:', pools.map(pool => ({
339
- id: pool.id,
340
- pair: `${pool.coinA.ticker}/${pool.coinB.ticker}`,
341
- reserveA: pool.reserveA,
342
- reserveB: pool.reserveB,
343
- totalShares: pool.totalShares,
344
- })));
345
- }
450
+ ## Client-Side Utilities
451
+
452
+ ### Swap Estimation
453
+
454
+ Estimate swap outcomes locally without making API calls:
455
+
456
+ ```javascript
457
+ const inputCoin = client.getCoinByTicker('RUNES');
458
+ const outputCoin = client.getCoinByTicker('USDC');
459
+
460
+ const estimate = await client.estimateSwap(inputCoin, outputCoin, '100', 6, 'dfs');
461
+ console.log({
462
+ outputAmount: estimate.output.amount,
463
+ priceImpact: `${estimate.slippage.priceImpact.toFixed(2)}%`,
464
+ path: estimate.path,
465
+ inputPriceUSD: estimate.input.priceUSD,
466
+ outputPriceUSD: estimate.output.priceUSD,
346
467
  });
468
+ ```
347
469
 
348
- socket.on('coins_updated', ({ isInitial }) => {
349
- if (isInitial) {
350
- const coins = client.getCoins();
351
- console.log('Initial coins:', coins.map(coin => ({
352
- ticker: coin.ticker,
353
- projectName: coin.projectName,
354
- status: coin.status,
355
- })));
356
- }
470
+ ### Liquidity Estimation
471
+
472
+ ```javascript
473
+ const coinA = client.getCoinByTicker('RUNES');
474
+ const coinB = client.getCoinByTicker('USDC');
475
+
476
+ // Estimate how much coinB is needed for a given coinA amount
477
+ const estimate = client.estimateLiquidityFrontend({
478
+ coinA, coinB,
479
+ amountA: '100',
480
+ amountB: null,
481
+ pools: client.getPools(),
482
+ coins: client.getCoins(),
357
483
  });
484
+ console.log(`Need ${estimate.amountB} ${estimate.coinB.ticker} for ${estimate.amountA} ${estimate.coinA.ticker}`);
485
+ ```
358
486
 
359
- socket.on('wallets_updated', ({ isInitial }) => {
360
- if (isInitial) {
361
- const wallets = client.getWallets();
362
- console.log('Initial wallets:', wallets.map(wallet => ({
363
- ticker: wallet.ticker,
364
- available: wallet.available,
365
- locked: wallet.locked,
366
- })));
367
- }
487
+ ### Deposit Share Estimation
488
+
489
+ ```javascript
490
+ const pool = client.getPool(poolId);
491
+ const shareEstimate = client.estimateDepositShares({
492
+ pool,
493
+ amountA: '100',
494
+ amountB: '1.5',
495
+ slippagePercent: 2,
368
496
  });
497
+ console.log(`Estimated shares: ${shareEstimate.estimatedShares}, min: ${shareEstimate.minShares}`);
498
+ ```
499
+
500
+ ### RUNES Compliance Check
501
+
502
+ ```javascript
503
+ const { isCompliant, warnings } = client.checkRunesLiquidityFrontend(coinA, coinB);
504
+ if (!isCompliant) {
505
+ console.warn('Pool not RUNES-compliant:', warnings);
506
+ }
507
+ ```
508
+
509
+ ### Share Amount Calculation
369
510
 
370
- socket.on('user_shares_updated', ({ isInitial }) => {
371
- if (isInitial) {
372
- const userShares = client.getUserShares();
373
- console.log('Initial user shares:', userShares.map(share => ({
374
- poolId: share.poolId,
375
- shares: share.shares,
376
- })));
377
- }
511
+ ```javascript
512
+ const shareAmounts = client.calculateShareAmounts();
513
+ shareAmounts.forEach(share => {
514
+ console.log(`${share.pair}: ${share.amountA} / ${share.amountB} (${share.shares} shares)`);
378
515
  });
379
516
  ```
380
517
 
381
- 6. Cleaning Up
382
- Disconnect the WebSocket when done to free resources.
518
+ ### Price Utilities
383
519
 
384
520
  ```javascript
385
- client.disconnect();
521
+ const runesPrice = client.utils.getRunesPriceUSD();
522
+ const tokenPrice = client.utils.getTokenPriceUSD('USDC');
523
+ const priceInRunes = client.utils.getTokenPriceInRunes('USDC');
524
+ const usdValue = client.utils.calculateUSDValue('100', 'RUNES', 2);
525
+
526
+ // Get prices for multiple tokens
527
+ const prices = client.utils.getPrices(['RUNES', 'USDC', 'XLM']);
386
528
  ```
387
529
 
388
- 7. Handling Process Termination
389
- Gracefully handle process termination to ensure cleanup.
530
+ ---
531
+
532
+ ## Complete Example: Trading Bot
390
533
 
391
534
  ```javascript
535
+ import { createRunesXClient } from '@runesx/api-client';
536
+ import { BigNumber } from 'bignumber.js';
537
+
538
+ const client = createRunesXClient({
539
+ apiKey: process.env.API_KEY,
540
+ apiUrl: 'https://www.runesx.xyz/api',
541
+ socketUrl: 'https://www.runesx.xyz/api',
542
+ });
543
+
544
+ async function main() {
545
+ await client.initialize();
546
+ console.log('Bot started');
547
+
548
+ // Monitor for new operations
549
+ client.on('operationUpdate', (op) => {
550
+ if (op.operationType === 'swap') {
551
+ console.log(`Swap: ${op.amountIn} ${op.inputCoin?.ticker} -> ${op.amountOut} ${op.outputCoin?.ticker}`);
552
+ }
553
+ });
554
+
555
+ // Monitor wallet changes
556
+ client.on('wallets_updated', ({ isInitial }) => {
557
+ if (!isInitial) {
558
+ const wallets = client.getWallets();
559
+ wallets.forEach(w => {
560
+ console.log(`${w.ticker}: ${w.available} available, ${w.locked} locked`);
561
+ });
562
+ }
563
+ });
564
+
565
+ // Monitor deposit confirmations
566
+ client.on('deposit_processed', (data) => {
567
+ if (data.status === 'confirmed' && data.credited) {
568
+ console.log(`Deposit confirmed: ${data.amount} ${data.coin.ticker}`);
569
+ }
570
+ });
571
+
572
+ // Example: execute a swap when price is favorable
573
+ const checkAndSwap = async () => {
574
+ const inputCoin = client.getCoinByTicker('RUNES');
575
+ const outputCoin = client.getCoinByTicker('USDC');
576
+ if (!inputCoin || !outputCoin) { return; }
577
+
578
+ const estimate = await client.estimateSwap(inputCoin, outputCoin, '10');
579
+ const priceImpact = estimate.slippage.priceImpact;
580
+
581
+ if (priceImpact < 1) { // Less than 1% price impact
582
+ const minOut = new BigNumber(estimate.output.amount)
583
+ .times(0.99)
584
+ .toFixed(outputCoin.dp, BigNumber.ROUND_DOWN);
585
+
586
+ const result = await client.postSwap({
587
+ amountIn: '10',
588
+ path: estimate.path,
589
+ minAmountOut: minOut,
590
+ });
591
+ console.log('Swap result:', result);
592
+ }
593
+ };
594
+
595
+ setInterval(checkAndSwap, 60000);
596
+ }
597
+
598
+ main().catch(console.error);
599
+
392
600
  process.on('SIGINT', () => {
393
- console.log('Shutting down...');
394
601
  client.disconnect();
395
602
  process.exit(0);
396
603
  });
397
604
  ```
398
605
 
606
+ ---
399
607
 
400
608
  ## API Reference
401
609
 
402
- ### `createRunesXClient(options)`
403
- Creates a client instance.
404
- - **Parameters**:
405
- - `options.apiKey` (string): RunesX API key.
406
- - `options.apiUrl` (string, optional): API endpoint (default: `http://localhost:3010`).
407
- - `options.socketUrl` (string, optional): WebSocket endpoint (default: `http://localhost:3010`).
408
- - **Returns**: Object with client methods.
409
-
410
- ### `initialize()`
411
- Initializes the client, connecting to the WebSocket and fetching initial data.
412
- - **Returns**: Promise resolving to `{ pools, coins, wallets, userShares }`.
413
-
414
- ### `getSocket()`
415
- Returns the Socket.IO instance.
416
- - **Returns**: Socket.IO client instance.
417
-
418
- ### `getPools()`
419
- Returns all liquidity pools.
420
- - **Returns**: Array of pool objects.
421
-
422
- ### `getPool(poolId)`
423
- Returns a specific pool by ID.
424
- - **Parameters**:
425
- - `poolId` (number): Pool ID.
426
- - **Returns**: Pool object or undefined.
427
-
428
- ### `getCoins()`
429
- Returns all coins.
430
- - **Returns**: Array of coin objects.
431
-
432
- ### `getCoinByTicker(ticker)`
433
- Returns a coin by its ticker.
434
- - **Parameters**:
435
- - `ticker` (string): Coin ticker (e.g., `xRUNES`).
436
- - **Returns**: Coin object or undefined.
437
-
438
- ### `getWallets()`
439
- Returns all wallets.
440
- - **Returns**: Array of wallet objects.
441
-
442
- ### `getWalletByTicker(ticker)`
443
- Returns a wallet by coin ticker.
444
- - **Parameters**:
445
- - `ticker` (string): Coin ticker.
446
- - **Returns**: Wallet object or undefined.
447
-
448
- ### `getUserShares()`
449
- Returns all user shares.
450
- - **Returns**: Array of user share objects.
451
-
452
- ### `getUserShareByPoolId(poolId)`
453
- Returns user shares for a specific pool.
454
- - **Parameters**:
455
- - `poolId` (number): Pool ID.
456
- - **Returns**: User share object or undefined.
457
-
458
- ### `postSwap(params)`
459
- Executes a swap.
460
- - **Parameters**:
461
- - `params.amountIn` (string): Input amount.
462
- - `params.path` (array): Array of `{ from, to }` objects defining the swap path.
463
- - `params.minAmountOut` (string): Minimum output amount.
464
- - **Returns**: Promise resolving to swap response.
465
-
466
- ### `depositLiquidity(params)`
467
- Deposits liquidity to a pool.
468
- - **Parameters**:
469
- - `params.coinA` (object): `{ ticker }` of first coin.
470
- - `params.coinB` (object): `{ ticker }` of second coin.
471
- - `params.amountA` (string): Amount of first coin.
472
- - `params.amountB` (string): Amount of second coin.
473
- - **Returns**: Promise resolving to deposit response.
474
-
475
- ### `withdrawLiquidity(params)`
476
- Withdraws liquidity from a pool.
477
- - **Parameters**:
478
- - `params.coinA` (object): `{ ticker }` of first coin.
479
- - `params.coinB` (object): `{ ticker }` of second coin.
480
- - `params.shares` (string): Number of shares to withdraw.
481
- - **Returns**: Promise resolving to withdrawal response.
482
-
483
- ### `getWalletsApi()`
484
- Fetches wallet data via the API.
485
- - **Returns**: Promise resolving to wallet data.
486
-
487
- ### `estimateSwap(inputCoin, outputCoin, amountIn, maxHops, algorithm)`
488
- Estimates a swap outcome.
489
- - **Parameters**:
490
- - `inputCoin` (object): Input coin object.
491
- - `outputCoin` (object): Output coin object.
492
- - `amountIn` (string): Input amount.
493
- - `maxHops` (number, optional): Maximum hops (default: 6).
494
- - `algorithm` (string, optional): Pathfinding algorithm (`dfs` or `bfs`, default: `dfs`).
495
- - **Returns**: Promise resolving to swap estimate.
496
-
497
- ### `estimateLiquidityFrontend(params)`
498
- Estimates liquidity amounts for a pool.
499
- - **Parameters**:
500
- - `params.coinA` (object): First coin.
501
- - `params.coinB` (object): Second coin.
502
- - `params.amountA` (string, optional): Amount of first coin.
503
- - `params.amountB` (string, optional): Amount of second coin.
504
- - `params.pools` (array): Array of pools.
505
- - `params.coins` (array): Array of coins.
506
- - **Returns**: Liquidity estimate object.
507
-
508
- ### `checkRunesLiquidityFrontend(coinA, coinB)`
509
- Checks RUNES compliance for a trading pair.
510
- - **Parameters**:
511
- - `coinA` (object): First coin.
512
- - `coinB` (object): Second coin.
513
- - **Returns**: `{ isCompliant, warnings }`.
514
-
515
- ### `calculateShareAmounts()`
516
- Calculates amounts for user shares.
517
- - **Returns**: Array of share objects with calculated amounts.
518
-
519
- ### `monitorPool(poolId, interval)`
520
- Monitors a pool periodically.
521
- - **Parameters**:
522
- - `poolId` (number): Pool ID.
523
- - `interval` (number, optional): Interval in milliseconds (default: 10000).
524
-
525
- ### `disconnect()`
526
- Disconnects the WebSocket.
610
+ ### Client Creation & Lifecycle
611
+
612
+ | Method | Description |
613
+ |--------|-------------|
614
+ | `createRunesXClient(options)` | Create a client instance |
615
+ | `initialize()` | Connect WebSocket, wait for initial data. Returns `{ pools, coins, chains, wallets, userShares }` |
616
+ | `disconnect()` | Disconnect WebSocket |
617
+ | `getSocket()` | Get raw Socket.IO instance |
618
+
619
+ ### Store Accessors (Real-time Data)
620
+
621
+ | Method | Returns |
622
+ |--------|---------|
623
+ | `getPools()` | All pools array |
624
+ | `getPool(poolId)` | Pool by ID |
625
+ | `getCoins()` | All coins array |
626
+ | `getCoinByTicker(ticker)` | Coin by ticker |
627
+ | `getChains()` | All chains array |
628
+ | `getChainByName(name)` | Chain by name |
629
+ | `getWallets()` | All wallets array |
630
+ | `getWalletByTicker(ticker)` | Wallet by ticker |
631
+ | `getUserShares()` | All user shares array |
632
+ | `getUserShareByPoolId(poolId)` | User share by pool ID |
633
+
634
+ ### Event System
635
+
636
+ | Method | Description |
637
+ |--------|-------------|
638
+ | `on(event, callback)` | Register event callback |
639
+ | `off(event, callback?)` | Remove callback (or all for event) |
640
+
641
+ ### Socket Emit Methods
642
+
643
+ | Method | Description |
644
+ |--------|-------------|
645
+ | `joinCandlesticks(poolId, timeframe)` | Subscribe to candlestick updates |
646
+ | `leaveCandlesticks(poolId, timeframe)` | Unsubscribe from candlestick updates |
647
+ | `sendYardMessage(text)` | Send a chat message (scope: chat) |
648
+ | `deleteMessage(messageId)` | Delete a chat message (mod/admin) |
649
+ | `markYardRead()` | Mark yard as read |
650
+
651
+ ### Public REST API
652
+
653
+ | Method | Description |
654
+ |--------|-------------|
655
+ | `getStatus()` | System status |
656
+ | `getCoinsApi()` | All coins via API |
657
+ | `getCoinApi(ticker)` | Single coin via API |
658
+ | `getPoolsApi()` | All pools via API |
659
+ | `getPoolByPair(tickerA, tickerB)` | Pool by ticker pair |
660
+ | `getPoolLiquidityShares(poolId)` | Pool liquidity shares |
661
+ | `getPrices()` | All token prices |
662
+ | `getPrice(ticker)` | Single token price |
663
+ | `getCandlesticks(poolId, timeframe, from, to)` | OHLCV candlestick data |
664
+ | `getVolumeTotal()` | Total platform volume |
665
+ | `getVolumePool(poolId)` | Pool volume |
666
+ | `getBucketsPools()` | Pool bucket data |
667
+ | `getRecentOperations({ operationType?, limit? })` | Recent operations |
668
+ | `getPoolOperations(poolId, { operationType?, limit? })` | Pool operations |
669
+ | `getYardMessages({ before?, limit? })` | Chat messages |
670
+
671
+ ### Private REST API
672
+
673
+ | Method | Scope | Description |
674
+ |--------|-------|-------------|
675
+ | `getWalletsApi()` | read | Wallet balances |
676
+ | `getLiquidityShares()` | read | User's liquidity shares |
677
+ | `postSwap({ amountIn, path, minAmountOut, idempotencyKey? })` | swap | Execute swap |
678
+ | `depositLiquidity({ tickerA, tickerB, amountA, amountB, minShares?, idempotencyKey? })` | liquidity_deposit | Deposit liquidity |
679
+ | `withdrawLiquidity({ tickerA, tickerB, shares, minAmountA?, minAmountB?, idempotencyKey? })` | liquidity_withdraw | Withdraw liquidity |
680
+ | `getDepositAddress(chainName)` | read | Get deposit address |
681
+ | `getAllDepositAddresses()` | read | Get all deposit addresses |
682
+ | `initiateWithdraw({ ticker, chain, address, amount, memo?, idempotencyKey? })` | wallet_withdraw | Start withdrawal |
683
+ | `verifyWithdrawPin({ pendingWithdrawalId, pinCode })` | wallet_withdraw | Verify PIN |
684
+ | `sendWithdrawEmailPin({ pendingWithdrawalId })` | wallet_withdraw | Send email PIN |
685
+ | `verifyWithdrawEmailPin({ pendingWithdrawalId, emailPinCode })` | wallet_withdraw | Verify email PIN |
686
+ | `verifyWithdraw2FA({ pendingWithdrawalId, twoFactorToken })` | wallet_withdraw | Verify 2FA |
687
+ | `getPendingWithdrawals()` | wallet_withdraw | Get pending withdrawals |
688
+ | `cancelWithdrawal({ pendingWithdrawalId })` | wallet_withdraw | Cancel withdrawal |
689
+ | `getTransactionHistory({ page?, limit?, type?, status? })` | read | Transaction history |
690
+ | `getUserOperations({ operationType?, poolId?, startTime?, endTime?, page?, limit? })` | read | User operations |
691
+ | `deleteYardMessage(messageId)` | chat | Delete chat message |
692
+
693
+ ### Client-Side Utilities
694
+
695
+ | Method | Description |
696
+ |--------|-------------|
697
+ | `estimateSwap(inputCoin, outputCoin, amountIn, maxHops?, algorithm?)` | Estimate swap output locally |
698
+ | `estimateLiquidityFrontend({ coinA, coinB, amountA, amountB, pools, coins })` | Estimate liquidity amounts |
699
+ | `estimateDepositShares({ pool, amountA, amountB, slippagePercent? })` | Estimate deposit shares |
700
+ | `checkRunesLiquidityFrontend(coinA, coinB)` | Check RUNES compliance |
701
+ | `calculateShareAmounts()` | Calculate share token amounts |
702
+ | `monitorPool(poolId, interval?)` | Log pool state periodically |
703
+ | `utils.getRunesPriceUSD()` | RUNES price in USD |
704
+ | `utils.getTokenPriceInRunes(ticker)` | Token price in RUNES |
705
+ | `utils.getTokenPriceUSD(ticker)` | Token price in USD |
706
+ | `utils.getPrices(tickers)` | Multiple token prices |
707
+ | `utils.calculateUSDValue(amount, ticker, decimals?)` | USD value of token amount |
708
+
709
+ ## License
710
+
711
+ MIT