@runesx/api-client
v0.4.0
Published
A Node.js client for interacting with the RunesX platform API and WebSocket
Maintainers
Readme
RunesX API Client
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.
Features
- WebSocket Integration: Real-time events for pools, coins, chains, wallets, user shares, operations, deposits, withdrawals, candlesticks, volume, and chat
- Full REST API: Complete HTTP API coverage for all public and private endpoints
- Store Management: Automatic real-time state management for pools, coins, chains, wallets, and user shares with buffering for out-of-order updates
- Swap Estimation: Client-side swap estimation using DFS or BFS pathfinding algorithms
- Liquidity Management: Estimate, deposit, and withdraw liquidity with RUNES compliance checking
- Withdrawal Flow: Full multi-step withdrawal verification (PIN, email, 2FA)
- Price Utilities: Calculate token prices in RUNES and USD
- Event System: Register callbacks for any WebSocket event
Installation
npm install @runesx/api-clientQuick Start
import { createRunesXClient } from '@runesx/api-client';
const client = createRunesXClient({
apiKey: 'your-api-key',
apiUrl: 'https://www.runesx.xyz/api',
socketUrl: 'https://www.runesx.xyz/api',
});
async function main() {
const { pools, coins, chains, wallets, userShares } = await client.initialize();
console.log(`Connected: ${pools.length} pools, ${coins.length} coins, ${wallets.length} wallets`);
// Listen for real-time events
client.on('operationUpdate', (operation) => {
console.log('New operation:', operation);
});
// Get prices
const prices = await client.getPrices();
console.log('Prices:', prices);
}
main().catch(console.error);Configuration
const client = createRunesXClient({
apiKey: 'your-api-key', // Required. Your RunesX API key
apiUrl: 'https://www.runesx.xyz/api', // Optional. Default: http://localhost:3010
socketUrl: 'https://www.runesx.xyz/api', // Optional. Default: http://localhost:3010
});You can also set these via environment variables:
API_KEY- Your API keyAPI_URL- API base URLSOCKET_URL- WebSocket URL
API Key Scopes
When generating an API key, you can assign specific scopes:
| Scope | Description |
|-------|-------------|
| read | Read-only access to user data (wallets, shares, operations, transactions) |
| swap | Execute token swaps |
| liquidity_deposit | Deposit liquidity to pools |
| liquidity_withdraw | Withdraw liquidity from pools |
| wallet_withdraw | Withdraw to external addresses |
| chat | Access yard/chat features |
Store Data (Real-time via WebSocket)
After initialize(), the client maintains real-time stores updated via WebSocket. These are always up-to-date.
Pools
const pools = client.getPools(); // All pools
const pool = client.getPool(poolId); // Pool by IDPool object:
{
id, reserveA, reserveB, totalShares, activeLiquidityProviders,
runesCompliant, lpFeeRate, treasuryFeeRate,
coinA: { id, ticker, dp, projectName },
coinB: { id, ticker, dp, projectName },
liquidityShares: [...],
updatedAt
}Coins
const coins = client.getCoins(); // All coins
const coin = client.getCoinByTicker('RUNES'); // Coin by tickerChains
const chains = client.getChains(); // All chains
const chain = client.getChainByName('RuneBase'); // Chain by nameWallets
const wallets = client.getWallets(); // All wallets
const wallet = client.getWalletByTicker('RUNES'); // Wallet by ticker
// wallet.available, wallet.lockedUser Shares
const shares = client.getUserShares(); // All shares
const share = client.getUserShareByPoolId(poolId); // Share by pool
// share.shares, share.poolIdREST API Methods
Public Endpoints (No Auth Required)
System Status
const status = await client.getStatus();
// { apiVersion, apiStatus, ratatoskrVersion, ratatoskrStatus }Coins (via API)
const coins = await client.getCoinsApi(); // All coins
const coin = await client.getCoinApi('RUNES'); // Single coin with chain detailsPools (via API)
const pools = await client.getPoolsApi(); // All pools
const pool = await client.getPoolByPair('RUNES', 'USDC'); // Specific pool
const shares = await client.getPoolLiquidityShares(poolId); // Pool's liquidity sharesPrices
const prices = await client.getPrices();
// { prices: [{ ticker, projectName, dp, priceInRunes, priceInUSD }], runesPriceUSD, timestamp }
const price = await client.getPrice('RUNES');
// { ticker, projectName, dp, priceInRunes, priceInUSD, runesPriceUSD, timestamp }Candlesticks
const candles = await client.getCandlesticks(poolId, '1h', fromTimestamp, toTimestamp);
// [{ time, open, high, low, close, volume }]Valid timeframes: 1m, 5m, 15m, 1h, 4h, 12h, 1d, 1w, 1M
Volume
const total = await client.getVolumeTotal();
const poolVol = await client.getVolumePool(poolId);Buckets
const buckets = await client.getBucketsPools();Operations
// Recent operations (public)
const recent = await client.getRecentOperations({ operationType: 'swap', limit: 20 });
// Pool operations
const poolOps = await client.getPoolOperations(poolId, { operationType: 'swap' });Valid operation types: swap, liquidityDeposit, liquidityWithdrawal
Yard Messages
const messages = await client.getYardMessages({ limit: 50 });
const older = await client.getYardMessages({ before: '2024-01-01T00:00:00Z', limit: 50 });Private Endpoints (Auth Required)
Wallets
const wallets = await client.getWalletsApi(); // Scope: readSwap (Scope: swap)
const result = await client.postSwap({
amountIn: '1.0',
path: [{ from: 'RUNES', to: 'USDC' }],
minAmountOut: '0.009',
idempotencyKey: 'optional-unique-key', // Auto-generated if not provided
});Liquidity (Scope: liquidity_deposit / liquidity_withdraw)
// Deposit
const deposit = await client.depositLiquidity({
tickerA: 'RUNES',
tickerB: 'USDC',
amountA: '100',
amountB: '1.5',
minShares: '1000', // Optional slippage protection
idempotencyKey: 'optional-key',
});
// Withdraw
const withdraw = await client.withdrawLiquidity({
tickerA: 'RUNES',
tickerB: 'USDC',
shares: '5000',
minAmountA: '90', // Optional slippage protection
minAmountB: '1.3', // Optional slippage protection
idempotencyKey: 'optional-key',
});
// Get user's liquidity shares via API
const shares = await client.getLiquidityShares();Deposits
// Get deposit address for a chain
const { address, memo } = await client.getDepositAddress('RuneBase');
// Get all deposit addresses
const addresses = await client.getAllDepositAddresses();
// [{ chainName, address, memo }]Withdrawals (Scope: wallet_withdraw)
The withdrawal process is multi-step:
// 1. Initiate withdrawal
const initResult = await client.initiateWithdraw({
ticker: 'RUNES',
chain: 'RuneBase',
address: 'RxYz...',
amount: '100',
memo: null, // Optional, for chains that require it
idempotencyKey: 'optional-key',
});
const { pendingWithdrawalId } = initResult.data;
// 2. Verify PIN (shown as image via socket event 'withdrawal_pin_generated')
await client.verifyWithdrawPin({ pendingWithdrawalId, pinCode: '123456' });
// 3. Send email PIN
await client.sendWithdrawEmailPin({ pendingWithdrawalId });
// 4. Verify email PIN
await client.verifyWithdrawEmailPin({ pendingWithdrawalId, emailPinCode: '654321' });
// 5. Verify 2FA (if enabled)
await client.verifyWithdraw2FA({ pendingWithdrawalId, twoFactorToken: '123456' });
// Check pending withdrawals
const pending = await client.getPendingWithdrawals();
// Cancel a pending withdrawal
await client.cancelWithdrawal({ pendingWithdrawalId });Transaction History (Scope: read)
const history = await client.getTransactionHistory({
page: 1,
limit: 20,
type: 'deposit', // Optional: 'deposit' or 'withdrawal'
status: 'confirmed', // Optional
});
// { success, data: { transactions: [...], total, page, pages } }User Operations (Scope: read)
const ops = await client.getUserOperations({
operationType: 'swap', // Optional
poolId: '1', // Optional
startTime: '2024-01-01T00:00:00Z', // Optional
endTime: '2024-12-31T23:59:59Z', // Optional
page: 1,
limit: 50,
});Yard Moderation (Scope: chat)
// Delete a message (via REST API)
await client.deleteYardMessage(messageId);WebSocket Events
Event Listener System
Use client.on() and client.off() to register callbacks for any WebSocket event:
// Register a callback
const handler = (data) => console.log('Pool update:', data);
client.on('pools_updated', handler);
// Remove a specific callback
client.off('pools_updated', handler);
// Remove all callbacks for an event
client.off('pools_updated');Available Events
Public Events (received after joining public room)
| Event | Data | Description |
|-------|------|-------------|
| pools_updated | { pools, isInitial } | Pool data updated |
| coins_updated | { coins, isInitial } | Coin data updated |
| chains_updated | { chains, isInitial } | Chain data updated |
| buckets_updated | { type, buckets } | Pool bucket data |
| operations_updated | [operations] | Initial recent operations |
| recent_yard_messages | { messages, lastReadAt } | Initial chat messages |
| status_updated | { apiVersion, apiStatus, ratatoskrVersion, ratatoskrStatus } | System status |
| volumeUpdate | { type, poolId, timestamp, volume } | Volume update |
| operationUpdate | operation | New operation executed |
| yard_message | { id, text, userId, username, role, timestamp } | New chat message |
| message_deleted | { messageId } | Chat message deleted |
| candlestick_updated | data | Candlestick update (requires joining candlestick room) |
Private Events (received after joining private room)
| Event | Data | Description |
|-------|------|-------------|
| wallets_updated | { wallets, isInitial } | Wallet balances updated |
| user_shares_updated | { userShares, isInitial } | Liquidity shares updated |
| deposit_address_generated | { requestId, chainName, address, memo } | Deposit address ready |
| deposit_processed | { amount, coin, chain, confirmations, status, credited } | Deposit confirmation update |
| withdrawal_initiated | data | Withdrawal process started |
| withdrawal_pin_generated | { pinImage, ticker, amount, pendingWithdrawalId, expiresAt, dp, fee, memoRequired } | PIN image ready |
| withdrawal_updated | { pendingWithdrawalId, expiresAt, stage } | Withdrawal stage changed |
| withdrawal_queued | { pendingWithdrawalId, ticker } | Withdrawal queued for processing |
| withdrawal_processed | { amount, coin, chain, confirmations, status, credited } | Withdrawal confirmation update |
| withdrawal_canceled | { ticker, amount } | Withdrawal canceled |
| withdrawal_expired | { pendingWithdrawalId, ticker, amount } | Withdrawal expired |
| banned | { reason, bannedUntil } | User banned from yard chat |
| yard_read_marked | { lastReadAt } | Yard marked as read |
| session_expired | { message } | Session expired, reconnect needed |
Connection Events
| Event | Data | Description |
|-------|------|-------------|
| connect | null | Connected to server |
| disconnect | reason | Disconnected from server |
| connect_error | error | Connection error |
| reconnect_attempt | attempt | Reconnection attempt number |
| reconnect | null | Successfully reconnected |
| error | error | Socket error |
Socket Convenience Methods
Candlestick Subscriptions
// Subscribe to candlestick updates for a pool
client.joinCandlesticks(poolId, '1h');
client.on('candlestick_updated', (data) => {
console.log('Candlestick:', data);
});
// Unsubscribe
client.leaveCandlesticks(poolId, '1h');Chat (The Yard)
// Send a message (Scope: chat)
client.sendYardMessage('Hello from my bot!');
// Listen for messages
client.on('yard_message', (msg) => {
console.log(`[${msg.username}]: ${msg.text}`);
});
// Delete a message (moderator/admin only)
client.deleteMessage(messageId);
// Mark yard as read
client.markYardRead();Raw Socket Access
For advanced use cases, access the underlying Socket.IO instance:
const socket = client.getSocket();
socket.emit('custom_event', data);Client-Side Utilities
Swap Estimation
Estimate swap outcomes locally without making API calls:
const inputCoin = client.getCoinByTicker('RUNES');
const outputCoin = client.getCoinByTicker('USDC');
const estimate = await client.estimateSwap(inputCoin, outputCoin, '100', 6, 'dfs');
console.log({
outputAmount: estimate.output.amount,
priceImpact: `${estimate.slippage.priceImpact.toFixed(2)}%`,
path: estimate.path,
inputPriceUSD: estimate.input.priceUSD,
outputPriceUSD: estimate.output.priceUSD,
});Liquidity Estimation
const coinA = client.getCoinByTicker('RUNES');
const coinB = client.getCoinByTicker('USDC');
// Estimate how much coinB is needed for a given coinA amount
const estimate = client.estimateLiquidityFrontend({
coinA, coinB,
amountA: '100',
amountB: null,
pools: client.getPools(),
coins: client.getCoins(),
});
console.log(`Need ${estimate.amountB} ${estimate.coinB.ticker} for ${estimate.amountA} ${estimate.coinA.ticker}`);Deposit Share Estimation
const pool = client.getPool(poolId);
const shareEstimate = client.estimateDepositShares({
pool,
amountA: '100',
amountB: '1.5',
slippagePercent: 2,
});
console.log(`Estimated shares: ${shareEstimate.estimatedShares}, min: ${shareEstimate.minShares}`);RUNES Compliance Check
const { isCompliant, warnings } = client.checkRunesLiquidityFrontend(coinA, coinB);
if (!isCompliant) {
console.warn('Pool not RUNES-compliant:', warnings);
}Share Amount Calculation
const shareAmounts = client.calculateShareAmounts();
shareAmounts.forEach(share => {
console.log(`${share.pair}: ${share.amountA} / ${share.amountB} (${share.shares} shares)`);
});Price Utilities
const runesPrice = client.utils.getRunesPriceUSD();
const tokenPrice = client.utils.getTokenPriceUSD('USDC');
const priceInRunes = client.utils.getTokenPriceInRunes('USDC');
const usdValue = client.utils.calculateUSDValue('100', 'RUNES', 2);
// Get prices for multiple tokens
const prices = client.utils.getPrices(['RUNES', 'USDC', 'XLM']);Complete Example: Trading Bot
import { createRunesXClient } from '@runesx/api-client';
import { BigNumber } from 'bignumber.js';
const client = createRunesXClient({
apiKey: process.env.API_KEY,
apiUrl: 'https://www.runesx.xyz/api',
socketUrl: 'https://www.runesx.xyz/api',
});
async function main() {
await client.initialize();
console.log('Bot started');
// Monitor for new operations
client.on('operationUpdate', (op) => {
if (op.operationType === 'swap') {
console.log(`Swap: ${op.amountIn} ${op.inputCoin?.ticker} -> ${op.amountOut} ${op.outputCoin?.ticker}`);
}
});
// Monitor wallet changes
client.on('wallets_updated', ({ isInitial }) => {
if (!isInitial) {
const wallets = client.getWallets();
wallets.forEach(w => {
console.log(`${w.ticker}: ${w.available} available, ${w.locked} locked`);
});
}
});
// Monitor deposit confirmations
client.on('deposit_processed', (data) => {
if (data.status === 'confirmed' && data.credited) {
console.log(`Deposit confirmed: ${data.amount} ${data.coin.ticker}`);
}
});
// Example: execute a swap when price is favorable
const checkAndSwap = async () => {
const inputCoin = client.getCoinByTicker('RUNES');
const outputCoin = client.getCoinByTicker('USDC');
if (!inputCoin || !outputCoin) { return; }
const estimate = await client.estimateSwap(inputCoin, outputCoin, '10');
const priceImpact = estimate.slippage.priceImpact;
if (priceImpact < 1) { // Less than 1% price impact
const minOut = new BigNumber(estimate.output.amount)
.times(0.99)
.toFixed(outputCoin.dp, BigNumber.ROUND_DOWN);
const result = await client.postSwap({
amountIn: '10',
path: estimate.path,
minAmountOut: minOut,
});
console.log('Swap result:', result);
}
};
setInterval(checkAndSwap, 60000);
}
main().catch(console.error);
process.on('SIGINT', () => {
client.disconnect();
process.exit(0);
});API Reference
Client Creation & Lifecycle
| Method | Description |
|--------|-------------|
| createRunesXClient(options) | Create a client instance |
| initialize() | Connect WebSocket, wait for initial data. Returns { pools, coins, chains, wallets, userShares } |
| disconnect() | Disconnect WebSocket |
| getSocket() | Get raw Socket.IO instance |
Store Accessors (Real-time Data)
| Method | Returns |
|--------|---------|
| getPools() | All pools array |
| getPool(poolId) | Pool by ID |
| getCoins() | All coins array |
| getCoinByTicker(ticker) | Coin by ticker |
| getChains() | All chains array |
| getChainByName(name) | Chain by name |
| getWallets() | All wallets array |
| getWalletByTicker(ticker) | Wallet by ticker |
| getUserShares() | All user shares array |
| getUserShareByPoolId(poolId) | User share by pool ID |
Event System
| Method | Description |
|--------|-------------|
| on(event, callback) | Register event callback |
| off(event, callback?) | Remove callback (or all for event) |
Socket Emit Methods
| Method | Description |
|--------|-------------|
| joinCandlesticks(poolId, timeframe) | Subscribe to candlestick updates |
| leaveCandlesticks(poolId, timeframe) | Unsubscribe from candlestick updates |
| sendYardMessage(text) | Send a chat message (scope: chat) |
| deleteMessage(messageId) | Delete a chat message (mod/admin) |
| markYardRead() | Mark yard as read |
Public REST API
| Method | Description |
|--------|-------------|
| getStatus() | System status |
| getCoinsApi() | All coins via API |
| getCoinApi(ticker) | Single coin via API |
| getPoolsApi() | All pools via API |
| getPoolByPair(tickerA, tickerB) | Pool by ticker pair |
| getPoolLiquidityShares(poolId) | Pool liquidity shares |
| getPrices() | All token prices |
| getPrice(ticker) | Single token price |
| getCandlesticks(poolId, timeframe, from, to) | OHLCV candlestick data |
| getVolumeTotal() | Total platform volume |
| getVolumePool(poolId) | Pool volume |
| getBucketsPools() | Pool bucket data |
| getRecentOperations({ operationType?, limit? }) | Recent operations |
| getPoolOperations(poolId, { operationType?, limit? }) | Pool operations |
| getYardMessages({ before?, limit? }) | Chat messages |
Private REST API
| Method | Scope | Description |
|--------|-------|-------------|
| getWalletsApi() | read | Wallet balances |
| getLiquidityShares() | read | User's liquidity shares |
| postSwap({ amountIn, path, minAmountOut, idempotencyKey? }) | swap | Execute swap |
| depositLiquidity({ tickerA, tickerB, amountA, amountB, minShares?, idempotencyKey? }) | liquidity_deposit | Deposit liquidity |
| withdrawLiquidity({ tickerA, tickerB, shares, minAmountA?, minAmountB?, idempotencyKey? }) | liquidity_withdraw | Withdraw liquidity |
| getDepositAddress(chainName) | read | Get deposit address |
| getAllDepositAddresses() | read | Get all deposit addresses |
| initiateWithdraw({ ticker, chain, address, amount, memo?, idempotencyKey? }) | wallet_withdraw | Start withdrawal |
| verifyWithdrawPin({ pendingWithdrawalId, pinCode }) | wallet_withdraw | Verify PIN |
| sendWithdrawEmailPin({ pendingWithdrawalId }) | wallet_withdraw | Send email PIN |
| verifyWithdrawEmailPin({ pendingWithdrawalId, emailPinCode }) | wallet_withdraw | Verify email PIN |
| verifyWithdraw2FA({ pendingWithdrawalId, twoFactorToken }) | wallet_withdraw | Verify 2FA |
| getPendingWithdrawals() | wallet_withdraw | Get pending withdrawals |
| cancelWithdrawal({ pendingWithdrawalId }) | wallet_withdraw | Cancel withdrawal |
| getTransactionHistory({ page?, limit?, type?, status? }) | read | Transaction history |
| getUserOperations({ operationType?, poolId?, startTime?, endTime?, page?, limit? }) | read | User operations |
| deleteYardMessage(messageId) | chat | Delete chat message |
Client-Side Utilities
| Method | Description |
|--------|-------------|
| estimateSwap(inputCoin, outputCoin, amountIn, maxHops?, algorithm?) | Estimate swap output locally |
| estimateLiquidityFrontend({ coinA, coinB, amountA, amountB, pools, coins }) | Estimate liquidity amounts |
| estimateDepositShares({ pool, amountA, amountB, slippagePercent? }) | Estimate deposit shares |
| checkRunesLiquidityFrontend(coinA, coinB) | Check RUNES compliance |
| calculateShareAmounts() | Calculate share token amounts |
| monitorPool(poolId, interval?) | Log pool state periodically |
| utils.getRunesPriceUSD() | RUNES price in USD |
| utils.getTokenPriceInRunes(ticker) | Token price in RUNES |
| utils.getTokenPriceUSD(ticker) | Token price in USD |
| utils.getPrices(tickers) | Multiple token prices |
| utils.calculateUSDValue(amount, ticker, decimals?) | USD value of token amount |
License
MIT
