@runesx/api-client 0.0.3 → 0.0.5
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/package.json +1 -1
- package/src/index.mjs +4 -0
- package/src/utils/liquidityUtils.mjs +42 -1
- package/src/utils/priceUtils.mjs +122 -0
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();
|
|
@@ -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.mjs';
|
|
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
|
+
}
|