Pure WebSocket API
While developing our WebSocket service, we used Socket.IO and prepared an SDK for it. However, if you are going to use our service with a language other than TypeScript, you should find a Socket.IO interface for the language you will use, or you can learn how to connect to our service via standard WebSocket based on the TypeScript example below.
import TronSocket from './pure-ws'
const token = 'your-token-here'
const socket = new TronSocket(token)
socket
.connect()
.then(() => {
console.log('Connected')
socket.on('block', (block) => {
console.log(
'New block',
{
hash: block.blockHash,
number: block.blockNumber
}
)
})
})
.catch((error) => {
console.error('Failed to connect', error)
})
import WebSocket from 'ws'
import { EventFilters, EventTypes } from '@tronsocket/types'
type EmitError = {
message: string
code: number
key?: string
}
type Events = keyof EventTypes
type Filters<E extends Events> = EventFilters[E]
type Callback<E extends Events> = (data: EventTypes[E]) => void
export default class TronSocket {
private token: string
private testnet: boolean
private socket: WebSocket | null = null
private socketUrl: string = 'wss://ws.tronsocket.com'
private connectionUrl: string
private listeners: { [key: string]: Callback<any>[] } = {}
constructor(token: string, testnet?: boolean) {
const api = new URL(this.socketUrl + '/socket.io/?EIO=4&transport=websocket')
api.searchParams.set('testnet', testnet ? 'true' : 'false')
api.searchParams.set('token', token)
this.connectionUrl = api.toString()
this.testnet = testnet || false
this.token = token
}
private parseData(data: WebSocket.Data): { type: string; payload: any } {
let message: string
// Convert different data types to string
if (typeof data === 'string') {
message = data
} else if (data instanceof ArrayBuffer) {
message = new TextDecoder().decode(data)
} else if (Array.isArray(data)) {
message = Buffer.concat(data).toString('utf-8')
} else if (data instanceof Buffer) {
message = data.toString('utf-8')
} else {
return { type: 'error', payload: { message: 'Unsupported data type', code: 500 } }
}
// Socket.IO message format: <packet type>[JSON data]
const match = message.match(/^(\d+)(.*)$/)
if (!match) {
return {
type: 'error',
payload: { message: 'Invalid message format', code: 500, rawData: message }
}
}
const [, packetType, packetData] = match
try {
let parsedData: any
// Parse the JSON data if it exists
if (packetData) {
parsedData = JSON.parse(packetData)
}
// Map Socket.IO packet types to our event types
switch (packetType) {
case '0': // Connect
return { type: 'connect', payload: parsedData }
case '1': // Disconnect
return { type: 'disconnect', payload: parsedData }
case '2': // Event
if (Array.isArray(parsedData) && parsedData.length > 0) {
return { type: parsedData[0], payload: parsedData.slice(1) }
}
return { type: 'event', payload: parsedData }
case '3': // Ack
return { type: 'ack', payload: parsedData }
case '4': // Error
return { type: 'error', payload: parsedData }
case '5': // Binary Event
return { type: 'binary_event', payload: parsedData }
case '6': // Binary Ack
return { type: 'binary_ack', payload: parsedData }
default:
return { type: 'unknown', payload: { packetType, data: parsedData } }
}
} catch (error) {
console.error('Failed to parse Socket.IO message:', error)
return {
type: 'error',
payload: {
message: 'Failed to parse Socket.IO message',
code: 500,
rawData: message
}
}
}
}
private createWsFormat(key: string, event: string, filter?: object): string {
if (!filter) {
return `42["${key}","${event}"]`
} else {
return `42["${key}","${event}",${JSON.stringify(filter)}]`
}
}
private simpleHash(str: string): string {
let hash = 0
for (let i = 0; i < str?.length; i++) {
const char = str.charCodeAt(i)
hash = (hash << 5) - hash + char
hash = hash & hash // Convert to 32-bit integer
}
return Math.abs(hash).toString(16)
}
private createEventKey<E extends Events>(
event: E,
callback: Callback<E>,
filter?: Filters<E>
): string {
const stringFilter = this.simpleHash(JSON.stringify(filter))
const stringFunction = this.simpleHash(callback.toString())
const definer = stringFilter + stringFunction
return event + `-${definer}`
}
private startListener(): void {
this.socket!.onmessage = (event) => {
const data = this.parseData(event.data)
if (data.payload?.packetType === '42') {
if (data.payload.data[0] === 'error') {
this.listeners['error']?.forEach((callback) => {
callback(data.payload.data[1])
})
} else {
const eventKey = data.payload.data[0]
const eventData = data.payload.data[1]
this.listeners[eventKey]?.forEach((callback) => {
callback(eventData)
})
}
}
}
}
public async connect(): Promise<boolean> {
return new Promise((resolve, reject) => {
this.socket = new WebSocket(this.connectionUrl)
this.socket.onmessage = (event) => {
const data = this.parseData(event.data)
if (data.type === 'error') {
reject(data.payload)
} else {
if (data.payload?.packetType === '42') {
if (data.payload.data[0] === 'error') {
reject(data.payload.data[1])
} else if (data.payload.data[0] === 'ready') {
this.startListener()
resolve(true)
} else if (data.payload?.packetType === '41') {
this.socket?.close()
}
}
}
}
this.socket.onopen = () => {
this.socket?.send('40') // for socket.io connection
}
this.socket.onerror = (error) => {
reject(error.message)
}
this.socket.onclose = (error) => {
reject(error.reason || error.code)
}
})
}
public async disconnect(): Promise<boolean> {
return new Promise((resolve, reject) => {
if (this.socket) {
this.socket.send('41') // for socket.io disconnection
this.socket.onclose = () => {
this.socket = null
resolve(true)
}
this.socket.close()
} else {
reject('Socket not connected')
}
})
}
public subscribe<E extends Events>(event: E, callback: Callback<E>, filter?: Filters<E>): void {
if (this.socket) {
const eventKey = this.createEventKey(event, callback, filter)
this.socket.send(this.createWsFormat('subscribe', eventKey, filter))
this.listeners[eventKey] = this.listeners[eventKey] || []
this.listeners[eventKey].push(callback)
} else {
throw new Error('Socket not connected')
}
}
public on<E extends Events>(event: E, callback: Callback<E>, filter?: Filters<E>): void {
this.subscribe(event, callback, filter)
}
public unsubscribe<E extends Events>(event: E, callback: Callback<E>, filter?: Filters<E>): void {
if (this.socket) {
const eventKey = this.createEventKey(event, callback, filter)
this.socket.send(this.createWsFormat('unsubscribe', eventKey, filter))
delete this.listeners[eventKey]
} else {
throw new Error('Socket not connected')
}
}
public off<E extends Events>(event: E, callback: Callback<E>, filter?: Filters<E>): void {
this.unsubscribe(event, callback, filter)
}
public error(callback: (error: EmitError) => void): void {
if (this.socket) {
this.listeners['error'] = this.listeners['error'] || []
this.listeners['error'].push(callback)
} else {
throw new Error('Socket not connected')
}
}
}
Last updated