@runesx/api-client 0.0.2 → 0.0.4
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 +480 -1
- package/package.json +1 -1
- package/src/index.mjs +4 -0
- package/src/socket.mjs +35 -41
- package/src/store/coinStore.mjs +0 -14
- package/src/store/poolStore.mjs +0 -13
- package/src/store/userSharesStore.mjs +0 -9
- package/src/store/walletStore.mjs +0 -12
- package/src/utils/liquidityUtils.mjs +42 -1
- package/src/utils/priceUtils.mjs +122 -0
- package/src/waitForStores.mjs +0 -12
package/README.md
CHANGED
|
@@ -44,4 +44,483 @@ async function main() {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
main();
|
|
47
|
-
```
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
2. Accessing Store Data
|
|
50
|
+
Retrieve data from the internal stores for pools, coins, wallets, and user shares.
|
|
51
|
+
|
|
52
|
+
### Get All Pools
|
|
53
|
+
```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
|
+
})));
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Get a Specific Pool
|
|
64
|
+
```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');
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Get All Coins
|
|
74
|
+
```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
|
+
})));
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Get a Coin by Ticker
|
|
84
|
+
```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');
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Get All Wallets
|
|
94
|
+
```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
|
+
})));
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Get a Wallet by Ticker
|
|
104
|
+
```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');
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Get All User Shares
|
|
113
|
+
```javascript
|
|
114
|
+
const userShares = client.getUserShares();
|
|
115
|
+
console.log('User shares:', userShares.map(share => ({
|
|
116
|
+
poolId: share.poolId,
|
|
117
|
+
shares: share.shares,
|
|
118
|
+
})));
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Get User Shares by Pool ID
|
|
122
|
+
```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');
|
|
128
|
+
```
|
|
129
|
+
|
|
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.
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
import { BigNumber } from 'bignumber.js';
|
|
135
|
+
|
|
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
|
+
}
|
|
143
|
+
|
|
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
|
+
}
|
|
169
|
+
|
|
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
|
+
}
|
|
181
|
+
|
|
182
|
+
executeSwap();
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
4. Managing Liquidity
|
|
186
|
+
Deposit and withdraw liquidity for a trading pair, such as RUNES/XLM.
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
```javascript
|
|
190
|
+
import { BigNumber } from 'bignumber.js';
|
|
191
|
+
|
|
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
|
+
}
|
|
199
|
+
|
|
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
|
+
}
|
|
206
|
+
|
|
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
|
+
}
|
|
234
|
+
|
|
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
|
+
}
|
|
264
|
+
|
|
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
|
+
}
|
|
282
|
+
|
|
283
|
+
manageLiquidity();
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
5. Monitoring Pools, Wallets, and User Shares
|
|
287
|
+
Set up periodic monitoring for pools, wallets, and user shares, and listen for WebSocket updates.
|
|
288
|
+
|
|
289
|
+
### Monitor a Specific Pool
|
|
290
|
+
```javascript
|
|
291
|
+
client.monitorPool(1, 10000); // Monitor pool ID 1 every 10 seconds
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Monitor Wallets
|
|
295
|
+
```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
|
+
}
|
|
309
|
+
|
|
310
|
+
monitorWallets();
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Monitor User Shares
|
|
314
|
+
```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
|
+
}
|
|
327
|
+
|
|
328
|
+
monitorUserShares();
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Listen for WebSocket Updates
|
|
332
|
+
```javascript
|
|
333
|
+
const socket = client.getSocket();
|
|
334
|
+
|
|
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
|
+
}
|
|
346
|
+
});
|
|
347
|
+
|
|
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
|
+
}
|
|
357
|
+
});
|
|
358
|
+
|
|
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
|
+
}
|
|
368
|
+
});
|
|
369
|
+
|
|
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
|
+
}
|
|
378
|
+
});
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
6. Cleaning Up
|
|
382
|
+
Disconnect the WebSocket when done to free resources.
|
|
383
|
+
|
|
384
|
+
```javascript
|
|
385
|
+
client.disconnect();
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
7. Handling Process Termination
|
|
389
|
+
Gracefully handle process termination to ensure cleanup.
|
|
390
|
+
|
|
391
|
+
```javascript
|
|
392
|
+
process.on('SIGINT', () => {
|
|
393
|
+
console.log('Shutting down...');
|
|
394
|
+
client.disconnect();
|
|
395
|
+
process.exit(0);
|
|
396
|
+
});
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
## API Reference
|
|
401
|
+
|
|
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.
|
package/package.json
CHANGED
package/src/index.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import { getUserShares, getUserShareByPoolId } from './store/userSharesStore.mjs
|
|
|
9
9
|
import { waitForStores } from './waitForStores.mjs';
|
|
10
10
|
import { estimateLiquidityFrontend, checkRunesLiquidityFrontend, calculateShareAmounts } from './utils/liquidityUtils.mjs';
|
|
11
11
|
import { estimateSwap } from './utils/swapUtils.mjs';
|
|
12
|
+
import { createPriceUtils } from './utils/priceUtils.mjs';
|
|
12
13
|
|
|
13
14
|
export function createRunesXClient(options = {}) {
|
|
14
15
|
const config = createConfig(options);
|
|
@@ -71,6 +72,9 @@ export function createRunesXClient(options = {}) {
|
|
|
71
72
|
}
|
|
72
73
|
}, interval);
|
|
73
74
|
},
|
|
75
|
+
utils: {
|
|
76
|
+
...createPriceUtils(),
|
|
77
|
+
},
|
|
74
78
|
disconnect: () => {
|
|
75
79
|
if (socket) {
|
|
76
80
|
socket.disconnect();
|
package/src/socket.mjs
CHANGED
|
@@ -19,11 +19,8 @@ export function setupSocket(config) {
|
|
|
19
19
|
const errorCount = { count: 0 }; // Track connection errors
|
|
20
20
|
|
|
21
21
|
socket.on('connect', () => {
|
|
22
|
-
console.log('Connected to Socket.IO server');
|
|
23
22
|
socket.emit('join_public');
|
|
24
|
-
console.log('Joined public room');
|
|
25
23
|
socket.emit('join_private');
|
|
26
|
-
console.log('Joined private room');
|
|
27
24
|
errorCount.count = 0;
|
|
28
25
|
});
|
|
29
26
|
|
|
@@ -51,11 +48,8 @@ export function setupSocket(config) {
|
|
|
51
48
|
});
|
|
52
49
|
|
|
53
50
|
socket.on('reconnect', () => {
|
|
54
|
-
console.log('Reconnected to Socket.IO server');
|
|
55
51
|
socket.emit('join_public');
|
|
56
|
-
console.log('Rejoined public room');
|
|
57
52
|
socket.emit('join_private');
|
|
58
|
-
console.log('Rejoined private room');
|
|
59
53
|
resetPools();
|
|
60
54
|
resetCoins();
|
|
61
55
|
resetWallets();
|
|
@@ -78,7 +72,7 @@ export function setupSocket(config) {
|
|
|
78
72
|
});
|
|
79
73
|
|
|
80
74
|
socket.on('pools_updated', ({ pools, isInitial }) => {
|
|
81
|
-
console.log('Received pools_updated:', { pools, isInitial });
|
|
75
|
+
// console.log('Received pools_updated:', { pools, isInitial });
|
|
82
76
|
if (isInitial) {
|
|
83
77
|
setInitialPools(pools);
|
|
84
78
|
} else {
|
|
@@ -87,7 +81,7 @@ export function setupSocket(config) {
|
|
|
87
81
|
});
|
|
88
82
|
|
|
89
83
|
socket.on('coins_updated', ({ coins, isInitial }) => {
|
|
90
|
-
console.log('Received coins_updated:', { coins, isInitial });
|
|
84
|
+
// console.log('Received coins_updated:', { coins, isInitial });
|
|
91
85
|
if (isInitial) {
|
|
92
86
|
setInitialCoins(coins);
|
|
93
87
|
} else {
|
|
@@ -96,7 +90,7 @@ export function setupSocket(config) {
|
|
|
96
90
|
});
|
|
97
91
|
|
|
98
92
|
socket.on('wallets_updated', ({ wallets, isInitial }) => {
|
|
99
|
-
console.log('Received wallets_updated:', { wallets, isInitial });
|
|
93
|
+
// console.log('Received wallets_updated:', { wallets, isInitial });
|
|
100
94
|
if (isInitial) {
|
|
101
95
|
setInitialWallets(wallets);
|
|
102
96
|
} else {
|
|
@@ -105,7 +99,7 @@ export function setupSocket(config) {
|
|
|
105
99
|
});
|
|
106
100
|
|
|
107
101
|
socket.on('user_shares_updated', ({ userShares, isInitial }) => {
|
|
108
|
-
console.log('Received user_shares_updated:', { userShares, isInitial });
|
|
102
|
+
// console.log('Received user_shares_updated:', { userShares, isInitial });
|
|
109
103
|
if (isInitial) {
|
|
110
104
|
setInitialUserShares(userShares);
|
|
111
105
|
} else {
|
|
@@ -114,30 +108,30 @@ export function setupSocket(config) {
|
|
|
114
108
|
});
|
|
115
109
|
|
|
116
110
|
socket.on('volumeUpdate', ({ type, poolId, timestamp, volume }) => {
|
|
117
|
-
console.log('Volume update:', { type, poolId, timestamp, volume });
|
|
111
|
+
// console.log('Volume update:', { type, poolId, timestamp, volume });
|
|
118
112
|
});
|
|
119
113
|
|
|
120
114
|
socket.on('operationUpdate', (operation) => {
|
|
121
|
-
console.log('Operation update:', operation);
|
|
115
|
+
// console.log('Operation update:', operation);
|
|
122
116
|
// Assuming operation includes liquidity deposit/withdraw results
|
|
123
|
-
if (operation.type === 'liquidity_deposit' || operation.type === 'liquidity_withdraw') {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
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
|
+
// }
|
|
134
128
|
});
|
|
135
129
|
socket.on('status_updated', (data) => {
|
|
136
|
-
console.log('Status update:', data);
|
|
130
|
+
// console.log('Status update:', data);
|
|
137
131
|
});
|
|
138
132
|
|
|
139
133
|
socket.on('deposit_address_generated', ({ requestId, chainId, address, memo }) => {
|
|
140
|
-
console.log('Deposit address generated:', { requestId, chainId, address, memo });
|
|
134
|
+
// console.log('Deposit address generated:', { requestId, chainId, address, memo });
|
|
141
135
|
});
|
|
142
136
|
|
|
143
137
|
socket.on('deposit_processed', (data) => {
|
|
@@ -192,31 +186,31 @@ export function setupSocket(config) {
|
|
|
192
186
|
// });
|
|
193
187
|
|
|
194
188
|
socket.on('yard_message', (message) => {
|
|
195
|
-
console.log('New chat message:', {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
});
|
|
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
|
+
// });
|
|
201
195
|
});
|
|
202
196
|
|
|
203
197
|
socket.on('message_deleted', ({ messageId }) => {
|
|
204
|
-
console.log('Message deleted:', { messageId });
|
|
198
|
+
// console.log('Message deleted:', { messageId });
|
|
205
199
|
});
|
|
206
200
|
|
|
207
201
|
socket.on('banned', ({ reason, bannedUntil }) => {
|
|
208
|
-
console.log('Banned from yard:', {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
});
|
|
202
|
+
// console.log('Banned from yard:', {
|
|
203
|
+
// reason,
|
|
204
|
+
// bannedUntil: new Date(bannedUntil).toLocaleString(),
|
|
205
|
+
// });
|
|
212
206
|
});
|
|
213
207
|
|
|
214
208
|
socket.on('withdrawal_updated', ({ pendingWithdrawalId, expiresAt, stage }) => {
|
|
215
|
-
console.log('Withdrawal updated:', { pendingWithdrawalId, expiresAt, stage });
|
|
209
|
+
// console.log('Withdrawal updated:', { pendingWithdrawalId, expiresAt, stage });
|
|
216
210
|
});
|
|
217
211
|
|
|
218
212
|
socket.on('withdrawal_expired', ({ pendingWithdrawalId, ticker, amount }) => {
|
|
219
|
-
console.log('Withdrawal expired:', { pendingWithdrawalId, ticker, amount });
|
|
213
|
+
// console.log('Withdrawal expired:', { pendingWithdrawalId, ticker, amount });
|
|
220
214
|
});
|
|
221
215
|
|
|
222
216
|
socket.on('pong', () => {
|
|
@@ -238,9 +232,9 @@ export function setupSocket(config) {
|
|
|
238
232
|
});
|
|
239
233
|
|
|
240
234
|
socket.on('connect', () => {
|
|
241
|
-
socket.emit('yard_message', {
|
|
242
|
-
|
|
243
|
-
});
|
|
235
|
+
// socket.emit('yard_message', {
|
|
236
|
+
// text: 'Hello from RunesX API Key Example Bot!!',
|
|
237
|
+
// });
|
|
244
238
|
});
|
|
245
239
|
|
|
246
240
|
return { socket };
|
package/src/store/coinStore.mjs
CHANGED
|
@@ -22,11 +22,9 @@ const setInitialCoins = (coins) => {
|
|
|
22
22
|
});
|
|
23
23
|
});
|
|
24
24
|
coinStore.isInitialReceived = true;
|
|
25
|
-
console.log(`Initialized with ${coins.length} coins`);
|
|
26
25
|
|
|
27
26
|
// Process buffered updates
|
|
28
27
|
if (coinStore.pendingUpdates.length > 0) {
|
|
29
|
-
console.log(`Processing ${coinStore.pendingUpdates.length} buffered coin updates`);
|
|
30
28
|
coinStore.pendingUpdates.forEach(({ coins }) => {
|
|
31
29
|
coins.forEach(coin => updateCoin(coin));
|
|
32
30
|
});
|
|
@@ -37,7 +35,6 @@ const setInitialCoins = (coins) => {
|
|
|
37
35
|
// Update a single coin
|
|
38
36
|
const updateCoin = (coin) => {
|
|
39
37
|
if (!coinStore.isInitialReceived) {
|
|
40
|
-
console.log('Buffering coin update, initial data not yet received:', coin);
|
|
41
38
|
coinStore.pendingUpdates.push({ coins: [coin] });
|
|
42
39
|
return;
|
|
43
40
|
}
|
|
@@ -48,19 +45,9 @@ const updateCoin = (coin) => {
|
|
|
48
45
|
if (existingCoin) {
|
|
49
46
|
const existingUpdatedAt = new Date(existingCoin.updatedAt).getTime();
|
|
50
47
|
if (incomingUpdatedAt <= existingUpdatedAt) {
|
|
51
|
-
console.log(`Skipping stale update for coin ${coin.id}`);
|
|
52
48
|
return;
|
|
53
49
|
}
|
|
54
50
|
|
|
55
|
-
console.log(`Updating coin ${coin.id} (${coin.ticker}):`, {
|
|
56
|
-
ticker: coin.ticker,
|
|
57
|
-
dp: coin.dp,
|
|
58
|
-
projectName: coin.projectName,
|
|
59
|
-
status: coin.status,
|
|
60
|
-
runesComplianceRequirement: coin.runesComplianceRequirement,
|
|
61
|
-
CoinChains: coin.CoinChains,
|
|
62
|
-
});
|
|
63
|
-
|
|
64
51
|
Object.assign(existingCoin, {
|
|
65
52
|
ticker: coin.ticker,
|
|
66
53
|
dp: coin.dp,
|
|
@@ -71,7 +58,6 @@ const updateCoin = (coin) => {
|
|
|
71
58
|
CoinChains: coin.CoinChains || existingCoin.CoinChains,
|
|
72
59
|
});
|
|
73
60
|
} else if (coin.ticker && coin.status && new BigNumber(coin.dp || 0).gte(0)) {
|
|
74
|
-
console.log(`Adding new coin ${coin.id}:`, coin);
|
|
75
61
|
coinStore.coins.set(coin.id, {
|
|
76
62
|
id: coin.id,
|
|
77
63
|
ticker: coin.ticker,
|
package/src/store/poolStore.mjs
CHANGED
|
@@ -26,11 +26,9 @@ const setInitialPools = (pools) => {
|
|
|
26
26
|
});
|
|
27
27
|
});
|
|
28
28
|
poolStore.isInitialReceived = true;
|
|
29
|
-
console.log(`Initialized with ${pools.length} pools`);
|
|
30
29
|
|
|
31
30
|
// Process buffered updates
|
|
32
31
|
if (poolStore.pendingUpdates.length > 0) {
|
|
33
|
-
console.log(`Processing ${poolStore.pendingUpdates.length} buffered pool updates`);
|
|
34
32
|
poolStore.pendingUpdates.forEach(({ pools }) => {
|
|
35
33
|
pools.forEach(pool => updatePool(pool));
|
|
36
34
|
});
|
|
@@ -41,7 +39,6 @@ const setInitialPools = (pools) => {
|
|
|
41
39
|
// Update a single pool
|
|
42
40
|
const updatePool = (pool) => {
|
|
43
41
|
if (!poolStore.isInitialReceived) {
|
|
44
|
-
console.log('Buffering pool update, initial data not yet received:', pool);
|
|
45
42
|
poolStore.pendingUpdates.push({ pools: [pool] });
|
|
46
43
|
return;
|
|
47
44
|
}
|
|
@@ -52,17 +49,9 @@ const updatePool = (pool) => {
|
|
|
52
49
|
if (existingPool) {
|
|
53
50
|
const existingUpdatedAt = new Date(existingPool.updatedAt).getTime();
|
|
54
51
|
if (incomingUpdatedAt <= existingUpdatedAt) {
|
|
55
|
-
console.log(`Skipping stale update for pool ${pool.id}`);
|
|
56
52
|
return;
|
|
57
53
|
}
|
|
58
54
|
|
|
59
|
-
console.log(`Updating pool ${pool.id}:`, {
|
|
60
|
-
reserveA: pool.reserveA,
|
|
61
|
-
reserveB: pool.reserveB,
|
|
62
|
-
totalShares: pool.totalShares,
|
|
63
|
-
activeLiquidityProviders: pool.activeLiquidityProviders,
|
|
64
|
-
});
|
|
65
|
-
|
|
66
55
|
Object.assign(existingPool, {
|
|
67
56
|
reserveA: pool.reserveA,
|
|
68
57
|
reserveB: pool.reserveB,
|
|
@@ -91,11 +80,9 @@ const updatePool = (pool) => {
|
|
|
91
80
|
});
|
|
92
81
|
|
|
93
82
|
if (new BigNumber(existingPool.totalShares).isZero()) {
|
|
94
|
-
console.log(`Removing pool ${pool.id} with zero totalShares`);
|
|
95
83
|
poolStore.pools.delete(pool.id);
|
|
96
84
|
}
|
|
97
85
|
} else if (pool.coinA && pool.coinB && new BigNumber(pool.totalShares).gt(0)) {
|
|
98
|
-
console.log(`Adding new pool ${pool.id}:`, pool);
|
|
99
86
|
poolStore.pools.set(pool.id, {
|
|
100
87
|
id: pool.id,
|
|
101
88
|
reserveA: pool.reserveA,
|
|
@@ -30,7 +30,6 @@ const setInitialUserShares = (userShares) => {
|
|
|
30
30
|
|
|
31
31
|
const updateUserShare = (share) => {
|
|
32
32
|
if (!userSharesStore.isInitialReceived) {
|
|
33
|
-
console.log('Buffering user share update, initial data not yet received:', share);
|
|
34
33
|
userSharesStore.pendingUpdates.push({ userShares: [share] });
|
|
35
34
|
return;
|
|
36
35
|
}
|
|
@@ -41,22 +40,15 @@ const updateUserShare = (share) => {
|
|
|
41
40
|
if (existingShare) {
|
|
42
41
|
const existingUpdatedAt = new Date(existingShare.updatedAt).getTime();
|
|
43
42
|
if (incomingUpdatedAt <= existingUpdatedAt) {
|
|
44
|
-
console.log(`Skipping stale update for user share in pool ${share.poolId}`);
|
|
45
43
|
return;
|
|
46
44
|
}
|
|
47
45
|
|
|
48
|
-
console.log(`Updating user share for pool ${share.poolId}:`, {
|
|
49
|
-
shares: share.shares,
|
|
50
|
-
updatedAt: share.updatedAt,
|
|
51
|
-
});
|
|
52
|
-
|
|
53
46
|
userSharesStore.userShares.set(share.poolId, {
|
|
54
47
|
poolId: share.poolId,
|
|
55
48
|
shares: new BigNumber(share.shares).toString(),
|
|
56
49
|
updatedAt: share.updatedAt,
|
|
57
50
|
});
|
|
58
51
|
} else if (share.poolId && new BigNumber(share.shares).gt(0)) {
|
|
59
|
-
console.log(`Adding new user share for pool ${share.poolId}:`, share);
|
|
60
52
|
userSharesStore.userShares.set(share.poolId, {
|
|
61
53
|
poolId: share.poolId,
|
|
62
54
|
shares: new BigNumber(share.shares).toString(),
|
|
@@ -67,7 +59,6 @@ const updateUserShare = (share) => {
|
|
|
67
59
|
}
|
|
68
60
|
|
|
69
61
|
if (existingShare && new BigNumber(share.shares).isZero()) {
|
|
70
|
-
console.log(`Removing user share for pool ${share.poolId} with zero shares`);
|
|
71
62
|
userSharesStore.userShares.delete(share.poolId);
|
|
72
63
|
}
|
|
73
64
|
};
|
|
@@ -19,11 +19,9 @@ const setInitialWallets = (wallets) => {
|
|
|
19
19
|
});
|
|
20
20
|
});
|
|
21
21
|
walletStore.isInitialReceived = true;
|
|
22
|
-
console.log(`Initialized with ${wallets.length} wallets`);
|
|
23
22
|
|
|
24
23
|
// Process buffered updates
|
|
25
24
|
if (walletStore.pendingUpdates.length > 0) {
|
|
26
|
-
console.log(`Processing ${walletStore.pendingUpdates.length} buffered wallet updates`);
|
|
27
25
|
walletStore.pendingUpdates.forEach(({ wallets }) => {
|
|
28
26
|
wallets.forEach(wallet => updateWallet(wallet));
|
|
29
27
|
});
|
|
@@ -34,7 +32,6 @@ const setInitialWallets = (wallets) => {
|
|
|
34
32
|
// Update a single wallet
|
|
35
33
|
const updateWallet = (wallet) => {
|
|
36
34
|
if (!walletStore.isInitialReceived) {
|
|
37
|
-
console.log('Buffering wallet update, initial data not yet received:', wallet);
|
|
38
35
|
walletStore.pendingUpdates.push({ wallets: [wallet] });
|
|
39
36
|
return;
|
|
40
37
|
}
|
|
@@ -45,16 +42,9 @@ const updateWallet = (wallet) => {
|
|
|
45
42
|
if (existingWallet) {
|
|
46
43
|
const existingUpdatedAt = new Date(existingWallet.updatedAt).getTime();
|
|
47
44
|
if (incomingUpdatedAt <= existingUpdatedAt) {
|
|
48
|
-
console.log(`Skipping stale update for wallet ${wallet.ticker}`);
|
|
49
45
|
return;
|
|
50
46
|
}
|
|
51
47
|
|
|
52
|
-
console.log(`Updating wallet ${wallet.ticker}:`, {
|
|
53
|
-
available: wallet.available,
|
|
54
|
-
locked: wallet.locked,
|
|
55
|
-
updatedAt: wallet.updatedAt,
|
|
56
|
-
});
|
|
57
|
-
|
|
58
48
|
walletStore.wallets.set(wallet.ticker, {
|
|
59
49
|
...existingWallet,
|
|
60
50
|
available: new BigNumber(wallet.available).toString(),
|
|
@@ -62,7 +52,6 @@ const updateWallet = (wallet) => {
|
|
|
62
52
|
updatedAt: wallet.updatedAt,
|
|
63
53
|
});
|
|
64
54
|
} else if (wallet.ticker && new BigNumber(wallet.available).gte(0) && new BigNumber(wallet.locked).gte(0)) {
|
|
65
|
-
console.log(`Adding new wallet ${wallet.ticker}:`, wallet);
|
|
66
55
|
walletStore.wallets.set(wallet.ticker, {
|
|
67
56
|
id: wallet.id,
|
|
68
57
|
ticker: wallet.ticker,
|
|
@@ -90,7 +79,6 @@ const resetWallets = () => {
|
|
|
90
79
|
walletStore.wallets.clear();
|
|
91
80
|
walletStore.isInitialReceived = false;
|
|
92
81
|
walletStore.pendingUpdates = [];
|
|
93
|
-
console.log('Reset wallet state due to disconnect or error');
|
|
94
82
|
};
|
|
95
83
|
|
|
96
84
|
export { walletStore, setInitialWallets, updateWallet, getWallets, getWalletByTicker, resetWallets };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// src/utils/liquidityUtils.mjs
|
|
2
2
|
import { BigNumber } from 'bignumber.js';
|
|
3
3
|
|
|
4
|
+
import { getRunesPriceUSD, getTokenPriceInRunes } from './swapUtils';
|
|
5
|
+
|
|
4
6
|
export function normalizeTokenPairFrontend(coinA, coinB, pools) {
|
|
5
7
|
if (!coinA || !coinB || !coinA.ticker || !coinB.ticker) {
|
|
6
8
|
throw new Error('Invalid token objects');
|
|
@@ -210,4 +212,43 @@ export function calculateShareAmounts({ userShares, pools }) {
|
|
|
210
212
|
pair: `${pool.coinA.ticker}/${pool.coinB.ticker}`,
|
|
211
213
|
};
|
|
212
214
|
});
|
|
213
|
-
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export const getPoolLiquidityUSD = (pool, coins, pools) => {
|
|
218
|
+
if (!pool || !coins || !pools || !pool.runesCompliant) {
|
|
219
|
+
return { value: '0', error: 'Pool or coin data missing or not RUNES compliant' };
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const coinA = coins.find((c) => c.ticker === pool.coinA.ticker);
|
|
223
|
+
const coinB = coins.find((c) => c.ticker === pool.coinB.ticker);
|
|
224
|
+
if (!coinA || !coinB) {
|
|
225
|
+
return { value: '0', error: `Coins not found: ${pool.coinA.ticker}/${pool.coinB.ticker}` };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const reserveA = new BigNumber(pool.reserveA).shiftedBy(-pool.coinA.dp);
|
|
229
|
+
const reserveB = new BigNumber(pool.reserveB).shiftedBy(-pool.coinB.dp);
|
|
230
|
+
|
|
231
|
+
if (reserveA.isZero() || reserveB.isZero()) {
|
|
232
|
+
return { value: '0', error: 'Zero reserves in pool' };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const runesPriceUSD = getRunesPriceUSD(pools);
|
|
236
|
+
const priceAInRunes = new BigNumber(getTokenPriceInRunes(coinA, pools) || '0');
|
|
237
|
+
const priceBInRunes = new BigNumber(getTokenPriceInRunes(coinB, pools) || '0');
|
|
238
|
+
|
|
239
|
+
const valueA = priceAInRunes.times(runesPriceUSD).times(reserveA);
|
|
240
|
+
const valueB = priceBInRunes.times(runesPriceUSD).times(reserveB);
|
|
241
|
+
|
|
242
|
+
if (valueA.isNaN() || valueB.isNaN() || priceAInRunes.isZero() || priceBInRunes.isZero()) {
|
|
243
|
+
return {
|
|
244
|
+
value: '0',
|
|
245
|
+
error: 'Invalid liquidity data (NaN or zero price)',
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const poolValue = valueA.plus(valueB);
|
|
250
|
+
return {
|
|
251
|
+
value: poolValue.toString(),
|
|
252
|
+
error: null,
|
|
253
|
+
};
|
|
254
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// src/utils/priceUtils.mjs
|
|
2
|
+
import { BigNumber } from 'bignumber.js';
|
|
3
|
+
|
|
4
|
+
import { getPools } from '../store/poolStore.mjs';
|
|
5
|
+
import { getCoinByTicker } from '../store/coinStore.mjs';
|
|
6
|
+
|
|
7
|
+
export function createPriceUtils() {
|
|
8
|
+
const getRunesPriceUSD = () => {
|
|
9
|
+
const pools = getPools();
|
|
10
|
+
|
|
11
|
+
const runesUsdcPool = pools.find((p) =>
|
|
12
|
+
(p.coinA.ticker === 'RUNES' && p.coinB.ticker === 'USDC') ||
|
|
13
|
+
(p.coinA.ticker === 'USDC' && p.coinB.ticker === 'RUNES')
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
if (!runesUsdcPool) {
|
|
17
|
+
console.warn('RUNES/USDC pool not found, using fallback price of $0.01');
|
|
18
|
+
return '0.01';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const reserveA = new BigNumber(runesUsdcPool.reserveA).shiftedBy(-runesUsdcPool.coinA.dp);
|
|
22
|
+
const reserveB = new BigNumber(runesUsdcPool.reserveB).shiftedBy(-runesUsdcPool.coinB.dp);
|
|
23
|
+
|
|
24
|
+
if (reserveA.isZero() || reserveB.isZero()) {
|
|
25
|
+
console.warn('RUNES/USDC pool has zero reserves, using fallback price of $0.01');
|
|
26
|
+
return '0.01';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const runesPriceUSD = runesUsdcPool.coinA.ticker === 'RUNES'
|
|
30
|
+
? reserveB.div(reserveA)
|
|
31
|
+
: reserveA.div(reserveB);
|
|
32
|
+
|
|
33
|
+
if (runesPriceUSD.isNaN() || runesPriceUSD.lte(0)) {
|
|
34
|
+
console.warn('Invalid RUNES/USDC price calculated, using fallback price of $0.01');
|
|
35
|
+
return '0.01';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return runesPriceUSD.toString();
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const getTokenPriceInRunes = (ticker) => {
|
|
42
|
+
ensureInitialized();
|
|
43
|
+
if (ticker === 'RUNES') {return '1';}
|
|
44
|
+
|
|
45
|
+
const pools = getPools();
|
|
46
|
+
const pool = pools.find(
|
|
47
|
+
(p) =>
|
|
48
|
+
(p.coinA.ticker === 'RUNES' && p.coinB.ticker === ticker) ||
|
|
49
|
+
(p.coinB.ticker === 'RUNES' && p.coinA.ticker === ticker)
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
if (!pool || new BigNumber(pool.reserveA).isZero() || new BigNumber(pool.reserveB).isZero()) {
|
|
53
|
+
return '0';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const reserveA = new BigNumber(pool.reserveA).shiftedBy(-pool.coinA.dp);
|
|
57
|
+
const reserveB = new BigNumber(pool.reserveB).shiftedBy(-pool.coinB.dp);
|
|
58
|
+
|
|
59
|
+
const priceInRunes = pool.coinA.ticker === 'RUNES'
|
|
60
|
+
? reserveA.div(reserveB)
|
|
61
|
+
: reserveB.div(reserveA);
|
|
62
|
+
|
|
63
|
+
return priceInRunes.toString();
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const getTokenPriceUSD = (ticker) => {
|
|
67
|
+
ensureInitialized();
|
|
68
|
+
if (ticker === 'USDC') {return '1';}
|
|
69
|
+
|
|
70
|
+
const coin = getCoinByTicker(ticker);
|
|
71
|
+
if (!coin) {
|
|
72
|
+
console.warn(`Coin not found for ticker ${ticker}`);
|
|
73
|
+
return '0';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const runesPriceUSD = getRunesPriceUSD();
|
|
77
|
+
const priceInRunes = getTokenPriceInRunes(ticker);
|
|
78
|
+
|
|
79
|
+
if (priceInRunes === '0') {return '0';}
|
|
80
|
+
|
|
81
|
+
return new BigNumber(priceInRunes).multipliedBy(runesPriceUSD).toString();
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const getPrices = (tickers) => {
|
|
85
|
+
const prices = {};
|
|
86
|
+
for (const ticker of tickers) {
|
|
87
|
+
try {
|
|
88
|
+
prices[ticker] = getTokenPriceUSD(ticker);
|
|
89
|
+
} catch (e) {
|
|
90
|
+
console.warn(`Failed to get price for ${ticker}:`, e.message);
|
|
91
|
+
prices[ticker] = null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return prices;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const calculateUSDValue = (amount, ticker, decimals = null) => {
|
|
98
|
+
try {
|
|
99
|
+
const priceUSD = getTokenPriceUSD(ticker);
|
|
100
|
+
if (priceUSD === '0' || !priceUSD) {return '0.00';}
|
|
101
|
+
|
|
102
|
+
let valueBN = new BigNumber(amount).multipliedBy(priceUSD);
|
|
103
|
+
|
|
104
|
+
if (decimals !== null) {
|
|
105
|
+
valueBN = valueBN.dp(decimals, BigNumber.ROUND_DOWN);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return valueBN.toString();
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.warn(`Failed to calculate USD value for ${amount} ${ticker}:`, error.message);
|
|
111
|
+
return '0.00';
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
getRunesPriceUSD,
|
|
117
|
+
getTokenPriceInRunes,
|
|
118
|
+
getTokenPriceUSD,
|
|
119
|
+
getPrices,
|
|
120
|
+
calculateUSDValue,
|
|
121
|
+
};
|
|
122
|
+
}
|
package/src/waitForStores.mjs
CHANGED
|
@@ -9,7 +9,6 @@ export function waitForStores(socket) {
|
|
|
9
9
|
return new Promise((resolve, reject) => {
|
|
10
10
|
if (poolStore.isInitialReceived) {
|
|
11
11
|
const pools = getPools();
|
|
12
|
-
console.log('Initial pool data already received:', pools.length, 'pools', pools);
|
|
13
12
|
resolve(pools);
|
|
14
13
|
return;
|
|
15
14
|
}
|
|
@@ -19,10 +18,8 @@ export function waitForStores(socket) {
|
|
|
19
18
|
}, 30000); // 30-second timeout
|
|
20
19
|
|
|
21
20
|
socket.on('pools_updated', ({ isInitial, pools }) => {
|
|
22
|
-
console.log('Received pools_updated:', { isInitial, pools });
|
|
23
21
|
if (isInitial) {
|
|
24
22
|
const pools = getPools();
|
|
25
|
-
console.log('Initial pool data received:', pools.length, 'pools', pools);
|
|
26
23
|
clearTimeout(timeout);
|
|
27
24
|
resolve(pools);
|
|
28
25
|
}
|
|
@@ -47,7 +44,6 @@ export function waitForStores(socket) {
|
|
|
47
44
|
return new Promise((resolve, reject) => {
|
|
48
45
|
if (coinStore.isInitialReceived) {
|
|
49
46
|
const coins = getCoins();
|
|
50
|
-
console.log('Initial coin data already received:', coins.length, 'coins', coins);
|
|
51
47
|
resolve(coins);
|
|
52
48
|
return;
|
|
53
49
|
}
|
|
@@ -57,10 +53,8 @@ export function waitForStores(socket) {
|
|
|
57
53
|
}, 30000); // 30-second timeout
|
|
58
54
|
|
|
59
55
|
socket.on('coins_updated', ({ isInitial, coins }) => {
|
|
60
|
-
console.log('Received coins_updated:', { isInitial, coins });
|
|
61
56
|
if (isInitial) {
|
|
62
57
|
const coins = getCoins();
|
|
63
|
-
console.log('Initial coin data received:', coins.length, 'coins', coins);
|
|
64
58
|
clearTimeout(timeout);
|
|
65
59
|
resolve(coins);
|
|
66
60
|
}
|
|
@@ -85,7 +79,6 @@ export function waitForStores(socket) {
|
|
|
85
79
|
return new Promise((resolve, reject) => {
|
|
86
80
|
if (walletStore.isInitialReceived) {
|
|
87
81
|
const wallets = getWallets();
|
|
88
|
-
console.log('Initial wallet data already received:', wallets.length, 'wallets', wallets);
|
|
89
82
|
resolve(wallets);
|
|
90
83
|
return;
|
|
91
84
|
}
|
|
@@ -95,10 +88,8 @@ export function waitForStores(socket) {
|
|
|
95
88
|
}, 30000); // 30-second timeout
|
|
96
89
|
|
|
97
90
|
socket.on('wallets_updated', ({ isInitial, wallets }) => {
|
|
98
|
-
console.log('Received wallets_updated:', { isInitial, wallets });
|
|
99
91
|
if (isInitial) {
|
|
100
92
|
const wallets = getWallets();
|
|
101
|
-
console.log('Initial wallet data received:', wallets.length, 'wallets', wallets);
|
|
102
93
|
clearTimeout(timeout);
|
|
103
94
|
resolve(wallets);
|
|
104
95
|
}
|
|
@@ -123,7 +114,6 @@ export function waitForStores(socket) {
|
|
|
123
114
|
return new Promise((resolve, reject) => {
|
|
124
115
|
if (userSharesStore.isInitialReceived) {
|
|
125
116
|
const userShares = getUserShares();
|
|
126
|
-
console.log('Initial user shares data already received:', userShares.length, 'user shares', userShares);
|
|
127
117
|
resolve(userShares);
|
|
128
118
|
return;
|
|
129
119
|
}
|
|
@@ -133,10 +123,8 @@ export function waitForStores(socket) {
|
|
|
133
123
|
}, 30000); // 30-second timeout
|
|
134
124
|
|
|
135
125
|
socket.on('user_shares_updated', ({ isInitial, userShares }) => {
|
|
136
|
-
console.log('Received user_shares_updated:', { isInitial, userShares });
|
|
137
126
|
if (isInitial) {
|
|
138
127
|
const userShares = getUserShares();
|
|
139
|
-
console.log('Initial user shares data received:', userShares.length, 'user shares', userShares);
|
|
140
128
|
clearTimeout(timeout);
|
|
141
129
|
resolve(userShares);
|
|
142
130
|
}
|