Skip to main content

Boros WebSocket

This guide explains how to directly connect to Boros's WebSocket service using Socket.IO client.

Basic Usage​

Here's a complete example of how to connect to and use the WebSocket:

import { io } from 'socket.io-client';

// Initialize the socket connection
const socket = io('wss://api.boros.finance/pendle-dapp-v3', {
path: '/socket/socket.io',
reconnectionAttempts: 5,
transports: ['websocket']
});

Connection Events​

Handling Connection​

socket.on('connect', () => {
console.log('Connected to WebSocket server');
});

socket.on('disconnect', () => {
console.log('Disconnected from WebSocket server');
});

socket.on('connect_error', (error) => {
console.error('Connection error:', error);
});

Subscribing to Channels​

To receive updates, you need to:

  1. Subscribe to a channel
  2. Listen for updates on that channel
// Subscribe to a channel
socket.emit('subscribe', 'statistics:MARKET_ID');

// Listen for updates
socket.on('statistics:MARKET_ID:update', (data) => {
console.log('Received market statistics update:', data);
});

Cleanup​

Always clean up your WebSocket connections when they're no longer needed:

function cleanup() {
// Unsubscribe from channels
socket.emit('unsubscribe', 'statistics:MARKET_ID');

// Remove listeners
socket.off('statistics:MARKET_ID:update');

// Disconnect
socket.disconnect();
}

Channel Types​

Common channel patterns:

ChannelDescription
orderbook:MARKET_ID:TICK_SIZEOrderbook updates for the given market. Accepted TICK_SIZE values: 0.1, 0.01, 0.001, 0.0001, 0.00001
market-trade:MARKET_IDMarket trade updates
statistics:MARKET_IDMarket statistics updates
account:ACCOUNTAccount state change notifications (orders, positions, balances)

Replace MARKET_ID with your specific market identifier, USER_ADDRESS with the wallet address, and ACCOUNT_ID with the sub-account ID (default: 0).

Best Practices​

  1. Connection Management

    • Always handle connection errors
    • Implement reconnection logic if needed
    • Clean up connections when no longer needed
  2. Event Handling

    • Subscribe to channels after connection is established
    • Remove listeners before disconnecting
    • Handle potential errors in data processing
  3. Resource Management

    • Unsubscribe from channels you no longer need
    • Don't create multiple connections unnecessarily
    • Clean up resources when your application closes

Example Implementation​

Here's a complete example putting it all together:

import { io } from 'socket.io-client';

class PendleWebSocket {
private socket: any;

constructor() {
this.socket = io('wss://secrettune.io/pendle-dapp-v3', {
path: '/socket.io',
reconnectionAttempts: 5,
transports: ['websocket']
});

this.setupEventHandlers();
}

private setupEventHandlers() {
this.socket.on('connect', () => {
console.log('Connected to WebSocket server');
});

this.socket.on('disconnect', () => {
console.log('Disconnected from WebSocket server');
});

this.socket.on('connect_error', (error) => {
console.error('Connection error:', error);
});
}

public subscribeToMarket(marketId: string) {
const channel = `statistics:${marketId}`;
this.socket.emit('subscribe', channel);

this.socket.on(`${channel}:update`, (data) => {
console.log(`Received update for ${marketId}:`, data);
});
}

public unsubscribeFromMarket(marketId: string) {
const channel = `statistics:$\{marketId\}`;
this.socket.emit('unsubscribe', channel);
this.socket.off(`${channel}:update`);
}

public disconnect() {
this.socket.disconnect();
}
}

// Usage example:
const ws = new PendleWebSocket();
ws.subscribeToMarket('YOUR_MARKET_ID');

// Clean up when done
// ws.unsubscribeFromMarket('YOUR_MARKET_ID');
// ws.disconnect();

Account Change Tracking​

You can subscribe to your account channel to receive notifications when your account state changes (e.g., order fills, position updates, balance changes).

Subscribing to Account Updates​

// Subscribe to account updates
// Format: account:USER_ADDRESS:ACCOUNT_ID
socket.emit('subscribe', 'account:0xYourWalletAddress:0');

// Listen for account update events
socket.on('account:0xYourWalletAddress:0:update', (data) => {
console.log('Account updated:', data);

// Refetch the latest account data via API
refreshAccountData();
});

When you receive an account update event, refetch the relevant API endpoints to get the complete updated state:

import axios from "axios";
import { MarketAccLib, CROSS_MARKET_ID } from "@pendle/sdk-boros";

async function refreshAccountData() {
const marketAcc = MarketAccLib.pack(walletAddress, accountId, tokenId, CROSS_MARKET_ID);

// Refetch account info
const { data: accountInfo } = await axios.post(
`${API_BASE_URL}/open-api/v1/accounts/market-acc-infos`,
{ marketAccs: [marketAcc] }
);

// Refetch active orders
const { data: orders } = await axios.get(
`${API_BASE_URL}/open-api/v1/accounts/limit-orders`,
{
params: {
userAddress: walletAddress,
accountId: accountId,
isActive: true,
},
}
);

// Update your local state
updateLocalState(accountInfo, orders);
}

Current Limitations​

Note: Currently, account update events serve as notifications that something has changed, but do not include the full details of the change in the event payload. You need to call the REST API to fetch the updated data.

We are planning to enhance these WebSocket events to include more detailed data in future updates, reducing the need for additional API calls.

Complete Account Tracking Example​

import { io } from 'socket.io-client';
import axios from "axios";

class AccountTracker {
private socket: any;
private walletAddress: string;
private accountId: number;

constructor(walletAddress: string, accountId: number = 0) {
this.walletAddress = walletAddress;
this.accountId = accountId;

this.socket = io('wss://api.boros.finance/pendle-dapp-v3', {
path: '/socket/socket.io',
reconnectionAttempts: 5,
transports: ['websocket']
});

this.setupConnection();
}

private setupConnection() {
this.socket.on('connect', () => {
console.log('Connected - subscribing to account updates');
this.subscribeToAccount();
});

this.socket.on('disconnect', () => {
console.log('Disconnected from WebSocket');
});
}

private subscribeToAccount() {
const channel = `account:${this.walletAddress}:${this.accountId}`;
this.socket.emit('subscribe', channel);

this.socket.on(`${channel}:update`, async (data) => {
console.log('Account change detected:', data);

// Fetch updated data from API
await this.refreshAccountData();
});
}

private async refreshAccountData() {
try {
// Fetch latest account state
const { data } = await axios.post(
'https://api.boros.finance/open-api/v1/accounts/market-acc-infos',
{ marketAccs: [/* your marketAcc */] }
);

console.log('Updated account data:', data);
// Process updated data...
} catch (error) {
console.error('Failed to refresh account data:', error);
}
}

public disconnect() {
const channel = `account:${this.walletAddress}:${this.accountId}`;
this.socket.emit('unsubscribe', channel);
this.socket.off(`${channel}:update`);
this.socket.disconnect();
}
}

// Usage
const tracker = new AccountTracker('0xYourWalletAddress', 0);

// Clean up when done
// tracker.disconnect();

Error Handling​

Always implement proper error handling:

socket.on('connect_error', (error) => {
console.error('Connection failed:', error);
// Implement your error handling logic
});

socket.on('error', (error) => {
console.error('Socket error:', error);
// Implement your error handling logic
});