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:
- Subscribe to a channel
- 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:
| Channel | Description |
|---|---|
orderbook:MARKET_ID:TICK_SIZE | Orderbook updates for the given market. Accepted TICK_SIZE values: 0.1, 0.01, 0.001, 0.0001, 0.00001 |
market-trade:MARKET_ID | Market trade updates |
statistics:MARKET_ID | Market statistics updates |
account:ACCOUNT | Account 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β
-
Connection Management
- Always handle connection errors
- Implement reconnection logic if needed
- Clean up connections when no longer needed
-
Event Handling
- Subscribe to channels after connection is established
- Remove listeners before disconnecting
- Handle potential errors in data processing
-
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();
});
Recommended Patternβ
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
});