Guía de inicio rápido
1
Get Your API Key
Obtén tu clave de API de Helius en el panel. La necesitarás para autenticarte. Los agentes pueden obtener una clave mediante la CLI de Helius.
2
Choose Your Network
Selecciona el punto de conexión adecuado:
- Mainnet:
wss://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY - Mainnet (Gatekeeper Beta):
wss://beta.helius-rpc.com/?api-key=YOUR_API_KEY - Devnet:
wss://devnet.helius-rpc.com/?api-key=YOUR_API_KEY
3
Create Your First Connection
const ws = new WebSocket('wss://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY');
ws.onopen = () => {
console.log('Connected to Solana!');
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Received:', data);
};
4
Subscribe to Updates
// Subscribe to account changes
const subscribeMessage = {
"jsonrpc": "2.0",
"id": 1,
"method": "accountSubscribe",
"params": [
"ACCOUNT_ADDRESS_HERE",
{ "commitment": "confirmed" }
]
};
ws.send(JSON.stringify(subscribeMessage));
Métodos de suscripción
Monitoreo de cuentas
Monitorea cuentas específicas para detectar cambios de saldo, actualizaciones de datos o transferencias de propiedad.const ws = new WebSocket('wss://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY');
const subscribeToAccount = (accountAddress) => {
const request = {
"jsonrpc": "2.0",
"id": 1,
"method": "accountSubscribe",
"params": [
accountAddress,
{
"encoding": "jsonParsed",
"commitment": "confirmed"
}
]
};
ws.send(JSON.stringify(request));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.method === "accountNotification") {
console.log("Account updated:", data.params.result.value);
// Handle the account change
const accountInfo = data.params.result.value;
console.log(`New balance: ${accountInfo.lamports} lamports`);
}
};
// Subscribe to a wallet address
subscribeToAccount("9PejEmViKHgUkVFWN57cNEZnFS4Qo6SzsLj5UPAXfDTF");
const ws = new WebSocket('wss://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY');
// Monitor a token account for balance changes
const monitorTokenAccount = (tokenAccountAddress) => {
const request = {
"jsonrpc": "2.0",
"id": 2,
"method": "accountSubscribe",
"params": [
tokenAccountAddress,
{
"encoding": "jsonParsed",
"commitment": "confirmed"
}
]
};
ws.send(JSON.stringify(request));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.method === "accountNotification") {
const accountData = data.params.result.value.data;
if (accountData.program === "spl-token") {
const parsed = accountData.parsed;
console.log(`Token balance: ${parsed.info.tokenAmount.uiAmount}`);
console.log(`Owner: ${parsed.info.owner}`);
}
}
};
Monitoreo de la actividad de programas
Observa todas las cuentas que pertenecen a un programa. Es ideal para monitorear operaciones en DEX, transferencias de NFT o actividad de DeFi.// Monitor Raydium AMM for all trade activity
const RAYDIUM_AMM_PROGRAM = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8";
const subscribeToProgram = (programId) => {
const request = {
"jsonrpc": "2.0",
"id": 3,
"method": "programSubscribe",
"params": [
programId,
{
"encoding": "jsonParsed",
"commitment": "confirmed",
"filters": [
{
"dataSize": 752 // Raydium pool account size
}
]
}
]
};
ws.send(JSON.stringify(request));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.method === "programNotification") {
console.log("Raydium pool updated:", data.params.result.value.pubkey);
// Process the pool data change
const accountData = data.params.result.value.account;
// Parse and handle the pool state change
}
};
subscribeToProgram(RAYDIUM_AMM_PROGRAM);
// Monitor Magic Eden program for NFT activity
const MAGIC_EDEN_PROGRAM = "M2mx93ekt1fmXSVkTrUL9xVFHkmME8HTUi5Cyc5aF7K";
const subscribeToNFTActivity = () => {
const request = {
"jsonrpc": "2.0",
"id": 4,
"method": "programSubscribe",
"params": [
MAGIC_EDEN_PROGRAM,
{
"encoding": "base64",
"commitment": "confirmed"
}
]
};
ws.send(JSON.stringify(request));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.method === "programNotification") {
console.log("NFT marketplace activity detected");
// Decode and process the marketplace instruction
const accountInfo = data.params.result.value;
// Handle NFT listing, purchase, or cancellation
}
};
Monitoreo de transacciones
Haz seguimiento de transacciones específicas o monitorea las transacciones que mencionen determinadas cuentas.// Monitor a specific transaction signature
const trackTransaction = (signature) => {
const request = {
"jsonrpc": "2.0",
"id": 5,
"method": "signatureSubscribe",
"params": [
signature,
{
"commitment": "confirmed"
}
]
};
ws.send(JSON.stringify(request));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.method === "signatureNotification") {
const result = data.params.result;
if (result.value.err) {
console.log("Transaction failed:", result.value.err);
} else {
console.log("Transaction confirmed successfully!");
}
// Subscription automatically ends after notification
}
};
// Track a payment or swap transaction
trackTransaction("YOUR_TRANSACTION_SIGNATURE_HERE");
// Monitor all transactions mentioning a specific address
const monitorAddress = (address) => {
const request = {
"jsonrpc": "2.0",
"id": 6,
"method": "logsSubscribe",
"params": [
{
"mentions": [address]
},
{
"commitment": "confirmed"
}
]
};
ws.send(JSON.stringify(request));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.method === "logsNotification") {
const logs = data.params.result.value.logs;
const signature = data.params.result.value.signature;
console.log(`Transaction ${signature} mentioned our address`);
console.log("Logs:", logs);
// Parse logs for specific events or errors
if (logs.some(log => log.includes("Program failed"))) {
console.log("Transaction failed!");
}
}
};
// Monitor a token mint for all activity
monitorAddress("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
Implementación lista para producción
Esta es una implementación sólida de WebSocket con lógica de reconexión, manejo de errores y administración adecuada del estado:class WebSocketManager {
constructor(endpoint) {
this.endpoint = endpoint;
this.ws = null;
this.subscriptions = new Map();
this.isConnected = false;
}
async connect() {
this.ws = new WebSocket(this.endpoint);
this.ws.onopen = () => {
console.log('Connected');
this.isConnected = true;
this.resubscribeAll();
};
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);
// Handle subscription confirmations
if (data.result && typeof data.result === 'number') {
const sub = Array.from(this.subscriptions.values())
.find(s => s.requestId === data.id);
if (sub) sub.subscriptionId = data.result;
return;
}
// Handle notifications
if (data.method?.endsWith('Notification')) {
const sub = Array.from(this.subscriptions.values())
.find(s => s.subscriptionId === data.params.subscription);
if (sub?.callback) sub.callback(data.params.result);
}
};
this.ws.onclose = () => {
console.log('Disconnected');
this.isConnected = false;
// Implement reconnection logic here
};
}
subscribe(method, params, callback) {
const requestId = Date.now();
const subscription = { requestId, method, params, callback, subscriptionId: null };
this.subscriptions.set(requestId, subscription);
if (this.isConnected) {
this.ws.send(JSON.stringify({
jsonrpc: '2.0',
id: requestId,
method,
params
}));
}
return requestId;
}
resubscribeAll() {
for (const [id, sub] of this.subscriptions) {
sub.subscriptionId = null;
this.ws.send(JSON.stringify({
jsonrpc: '2.0',
id,
method: sub.method,
params: sub.params
}));
}
}
}
// Usage
const wsManager = new WebSocketManager('wss://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY');
await wsManager.connect();
// Subscribe to account changes
wsManager.subscribe(
'accountSubscribe',
['account-address-here', { commitment: 'confirmed' }],
(data) => console.log('Account updated:', data)
);
Explora más métodos de WebSocket
Los ejemplos anteriores abarcan los tipos de suscripción más utilizados, pero la API de WebSocket de Solana ofrece muchos más métodos para necesidades de monitoreo especializadas.Complete WebSocket API Reference
Descubre más de 18 métodos de WebSocket con documentación detallada, parámetros y formatos de respuesta.
Casos de uso reales
Panel de operaciones DeFi
class LiquidityPoolMonitor {
constructor(wsManager) {
this.wsManager = wsManager;
this.pools = new Map();
}
monitorPool(poolAddress, tokenAMint, tokenBMint) {
// Monitor the pool account itself
const poolSubscription = this.wsManager.subscribe(
'accountSubscribe',
[poolAddress, { encoding: 'base64', commitment: 'confirmed' }],
(data) => this.handlePoolUpdate(poolAddress, data)
);
// Monitor token reserves
const tokenASubscription = this.wsManager.subscribe(
'accountSubscribe',
[tokenAMint, { encoding: 'jsonParsed', commitment: 'confirmed' }],
(data) => this.handleReserveUpdate(poolAddress, 'tokenA', data)
);
const tokenBSubscription = this.wsManager.subscribe(
'accountSubscribe',
[tokenBMint, { encoding: 'jsonParsed', commitment: 'confirmed' }],
(data) => this.handleReserveUpdate(poolAddress, 'tokenB', data)
);
this.pools.set(poolAddress, {
poolSubscription,
tokenASubscription,
tokenBSubscription,
lastUpdate: Date.now()
});
}
handlePoolUpdate(poolAddress, data) {
// Decode pool state and calculate price
const poolState = this.decodePoolState(data.value.data);
console.log(`Pool ${poolAddress} updated:`);
console.log(`- Price: ${poolState.price}`);
console.log(`- Liquidity: ${poolState.liquidity}`);
console.log(`- Volume 24h: ${poolState.volume24h}`);
// Emit price update event
this.emitPriceUpdate(poolAddress, poolState);
}
handleReserveUpdate(poolAddress, tokenType, data) {
const tokenAmount = data.value.data.parsed.info.tokenAmount;
console.log(`${tokenType} reserve updated: ${tokenAmount.uiAmount}`);
// Update internal state and recalculate price
this.updatePoolPrice(poolAddress, tokenType, tokenAmount);
}
decodePoolState(data) {
// Implement pool state decoding based on DEX
// This is pseudo-code - actual implementation depends on the DEX
return {
price: 0,
liquidity: 0,
volume24h: 0
};
}
emitPriceUpdate(poolAddress, poolState) {
// Emit custom events for price updates
window.dispatchEvent(new CustomEvent('poolPriceUpdate', {
detail: { poolAddress, ...poolState }
}));
}
}
// Usage
const poolMonitor = new LiquidityPoolMonitor(wsManager);
poolMonitor.monitorPool(
'POOL_ADDRESS',
'TOKEN_A_MINT',
'TOKEN_B_MINT'
);
Rastreador de portafolio
class PortfolioTracker {
constructor(wsManager) {
this.wsManager = wsManager;
this.watchedAddresses = new Set();
this.balances = new Map();
}
addWallet(walletAddress) {
if (this.watchedAddresses.has(walletAddress)) return;
this.watchedAddresses.add(walletAddress);
// Monitor SOL balance
const solSubscription = this.wsManager.subscribe(
'accountSubscribe',
[walletAddress, { encoding: 'jsonParsed', commitment: 'confirmed' }],
(data) => this.handleSolBalanceUpdate(walletAddress, data)
);
// Monitor all token accounts owned by this wallet
const tokenSubscription = this.wsManager.subscribe(
'programSubscribe',
[
'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
{
encoding: 'jsonParsed',
commitment: 'confirmed',
filters: [
{ memcmp: { offset: 32, bytes: walletAddress } }
]
}
],
(data) => this.handleTokenBalanceUpdate(walletAddress, data)
);
console.log(`Now tracking portfolio for ${walletAddress}`);
}
handleSolBalanceUpdate(walletAddress, data) {
const lamports = data.value.lamports;
const solBalance = lamports / 1e9;
this.balances.set(`${walletAddress}:SOL`, solBalance);
console.log(`${walletAddress} SOL balance: ${solBalance}`);
this.emitBalanceUpdate(walletAddress, 'SOL', solBalance);
}
handleTokenBalanceUpdate(walletAddress, data) {
const tokenData = data.value.account.data.parsed.info;
const tokenMint = tokenData.mint;
const balance = parseFloat(tokenData.tokenAmount.uiAmount);
this.balances.set(`${walletAddress}:${tokenMint}`, balance);
console.log(`${walletAddress} token ${tokenMint} balance: ${balance}`);
this.emitBalanceUpdate(walletAddress, tokenMint, balance);
}
emitBalanceUpdate(walletAddress, asset, balance) {
window.dispatchEvent(new CustomEvent('balanceUpdate', {
detail: { walletAddress, asset, balance, timestamp: Date.now() }
}));
}
getPortfolioValue(walletAddress) {
// Calculate total portfolio value in USD
// This would integrate with price feeds
let totalValue = 0;
for (const [key, balance] of this.balances) {
if (key.startsWith(walletAddress)) {
const asset = key.split(':')[1];
const price = this.getAssetPrice(asset); // Implement price lookup
totalValue += balance * price;
}
}
return totalValue;
}
}
Integración con un mercado de NFT
class NFTActivityMonitor {
constructor(wsManager) {
this.wsManager = wsManager;
this.watchedCollections = new Map();
}
monitorCollection(collectionAddress, options = {}) {
const { includeListings = true, includeSales = true, includeCancellations = true } = options;
// Monitor the collection's update authority or creator address
const collectionSubscription = this.wsManager.subscribe(
'logsSubscribe',
[
{ mentions: [collectionAddress] },
{ commitment: 'confirmed' }
],
(data) => this.handleCollectionActivity(collectionAddress, data)
);
this.watchedCollections.set(collectionAddress, {
subscription: collectionSubscription,
options,
stats: {
listings: 0,
sales: 0,
volume: 0
}
});
console.log(`Monitoring NFT collection: ${collectionAddress}`);
}
handleCollectionActivity(collectionAddress, data) {
const logs = data.value.logs;
const signature = data.value.signature;
// Parse marketplace-specific logs
const activity = this.parseMarketplaceActivity(logs);
if (activity) {
console.log(`NFT Activity in ${collectionAddress}:`);
console.log(`- Type: ${activity.type}`);
console.log(`- Price: ${activity.price} SOL`);
console.log(`- Signature: ${signature}`);
// Update collection stats
this.updateCollectionStats(collectionAddress, activity);
// Emit activity event
this.emitNFTActivity(collectionAddress, activity, signature);
}
}
parseMarketplaceActivity(logs) {
// Parse Magic Eden, OpenSea, or other marketplace logs
for (const log of logs) {
// Magic Eden listing
if (log.includes('Instruction: List')) {
const price = this.extractPriceFromLog(log);
return { type: 'listing', price };
}
// Magic Eden sale
if (log.includes('Instruction: ExecuteSale')) {
const price = this.extractPriceFromLog(log);
return { type: 'sale', price };
}
// Listing cancellation
if (log.includes('Instruction: CancelBuy') || log.includes('Instruction: CancelSell')) {
return { type: 'cancellation', price: 0 };
}
}
return null;
}
extractPriceFromLog(log) {
// Extract price from marketplace logs - implementation depends on marketplace
// This is pseudo-code
const priceMatch = log.match(/price:\s*(\d+)/);
return priceMatch ? parseInt(priceMatch[1]) / 1e9 : 0;
}
updateCollectionStats(collectionAddress, activity) {
const collection = this.watchedCollections.get(collectionAddress);
if (!collection) return;
switch (activity.type) {
case 'listing':
collection.stats.listings++;
break;
case 'sale':
collection.stats.sales++;
collection.stats.volume += activity.price;
break;
}
}
emitNFTActivity(collectionAddress, activity, signature) {
window.dispatchEvent(new CustomEvent('nftActivity', {
detail: {
collection: collectionAddress,
activity,
signature,
timestamp: Date.now()
}
}));
}
}
Ejemplo de integración con React
import React, { useEffect, useState } from 'react';
function WebSocketComponent() {
const [accountData, setAccountData] = useState(null);
const [wsManager, setWsManager] = useState(null);
useEffect(() => {
// Initialize WebSocket
const manager = new WebSocketManager('wss://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY');
manager.connect().then(() => {
// Subscribe to account changes
manager.subscribe(
'accountSubscribe',
['account-address-here', { commitment: 'confirmed' }],
(data) => setAccountData(data)
);
});
setWsManager(manager);
// Cleanup on unmount
return () => {
if (manager) manager.disconnect();
};
}, []);
return (
<div>
<h3>Account Monitor</h3>
{accountData ? (
<div>
<p>Balance: {accountData.value.lamports} lamports</p>
<p>Owner: {accountData.value.owner}</p>
</div>
) : (
<p>Waiting for account updates...</p>
)}
</div>
);
}
Optimización del rendimiento
- Connection Management
- Memory Management
// Use connection pooling for multiple subscriptions
class WebSocketPool {
constructor(endpoint, maxConnections = 3) {
this.endpoint = endpoint;
this.connections = [];
this.maxConnections = maxConnections;
this.currentConnectionIndex = 0;
}
async getConnection() {
if (this.connections.length < this.maxConnections) {
const wsManager = new SolanaWebSocketManager(this.endpoint);
await wsManager.connect();
this.connections.push(wsManager);
return wsManager;
}
// Round-robin selection
const connection = this.connections[this.currentConnectionIndex];
this.currentConnectionIndex = (this.currentConnectionIndex + 1) % this.connections.length;
return connection;
}
}
// Implement subscription cleanup and memory management
class ManagedWebSocket extends SolanaWebSocketManager {
constructor(endpoint, options = {}) {
super(endpoint, options);
this.maxSubscriptions = options.maxSubscriptions || 100;
this.subscriptionHistory = [];
}
subscribe(method, params, callback) {
// Check subscription limit
if (this.subscriptions.size >= this.maxSubscriptions) {
this.cleanupOldSubscriptions();
}
const requestId = super.subscribe(method, params, callback);
// Track subscription for cleanup
this.subscriptionHistory.push({
requestId,
timestamp: Date.now(),
method
});
return requestId;
}
cleanupOldSubscriptions() {
// Remove oldest 10% of subscriptions
const toRemove = Math.floor(this.subscriptions.size * 0.1);
const sortedHistory = this.subscriptionHistory
.sort((a, b) => a.timestamp - b.timestamp);
for (let i = 0; i < toRemove && i < sortedHistory.length; i++) {
const sub = sortedHistory[i];
this.unsubscribe(sub.requestId);
// Remove from history
this.subscriptionHistory = this.subscriptionHistory
.filter(s => s.requestId !== sub.requestId);
}
}
}
Manejo y depuración de errores
El manejo sólido de errores es crucial para las aplicaciones WebSocket en producción. Implementa estos patrones para gestionar correctamente los problemas de red, los límites de solicitudes y los errores de la API.
class ErrorHandlingWebSocket extends SolanaWebSocketManager {
constructor(endpoint, options = {}) {
super(endpoint, options);
this.errorCounts = new Map();
this.maxErrorsPerMinute = options.maxErrorsPerMinute || 10;
}
handleMessage(data) {
try {
// Handle rate limiting
if (data.error && data.error.code === 429) {
console.warn('Rate limited, backing off...');
this.handleRateLimit();
return;
}
// Handle subscription errors
if (data.error && data.error.code === -32602) {
console.error('Invalid subscription parameters:', data.error.message);
this.handleInvalidSubscription(data.id);
return;
}
// Handle network errors
if (data.error && data.error.code === -32603) {
console.error('Internal server error:', data.error.message);
this.handleServerError();
return;
}
super.handleMessage(data);
} catch (error) {
console.error('Error processing WebSocket message:', error);
this.incrementErrorCount('message_processing');
}
}
handleRateLimit() {
// Exponential backoff for rate limiting
const backoffDelay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
setTimeout(() => {
if (this.subscriptions.size > 0) {
console.log('Retrying after rate limit...');
this.resubscribeAll();
}
}, backoffDelay);
}
handleInvalidSubscription(requestId) {
// Remove invalid subscription
const subscription = Array.from(this.subscriptions.entries())
.find(([id, sub]) => sub.requestId === requestId);
if (subscription) {
console.warn(`Removing invalid subscription: ${subscription[1].method}`);
this.subscriptions.delete(subscription[0]);
}
}
handleServerError() {
// Log server error and potentially switch endpoints
this.incrementErrorCount('server_error');
if (this.getErrorCount('server_error') > 5) {
console.error('Too many server errors, consider switching endpoints');
this.eventEmitter.dispatchEvent(new CustomEvent('tooManyServerErrors'));
}
}
incrementErrorCount(errorType) {
const now = Date.now();
const errors = this.errorCounts.get(errorType) || [];
// Add current error
errors.push(now);
// Remove errors older than 1 minute
const oneMinuteAgo = now - 60000;
const recentErrors = errors.filter(timestamp => timestamp > oneMinuteAgo);
this.errorCounts.set(errorType, recentErrors);
// Check if we're exceeding error threshold
if (recentErrors.length > this.maxErrorsPerMinute) {
console.error(`Too many ${errorType} errors in the last minute`);
this.eventEmitter.dispatchEvent(new CustomEvent('errorThresholdExceeded', {
detail: { errorType, count: recentErrors.length }
}));
}
}
getErrorCount(errorType) {
const errors = this.errorCounts.get(errorType) || [];
const oneMinuteAgo = Date.now() - 60000;
return errors.filter(timestamp => timestamp > oneMinuteAgo).length;
}
}
Próximos pasos
API Reference
Documentación completa de todos los métodos de suscripción de WebSocket
WebSocket Overview
Explora las suscripciones de WebSocket de Helius, incluidos
transactionSubscribe y los filtros mejorados de accountSubscribeLaserStream
Actualiza a LaserStream para obtener una transmisión de datos ultrarrápida con reproducción histórica
LaserStream Guide
Aprende a transmitir actualizaciones de cuentas en tiempo real mediante LaserStream