Consumers connected to the hub WebSocket (/hub/ws) receive only events matching their subscription criteria. mx-notifier evaluates a SubscribeEvent protobuf message β declared at subscribe time β against every normalized event before delivery.
Match levels
Five match levels are available, from broadest (receive everything) to most specific (match on address, identifier, and topic prefix):
Level
Matches when
All
Always β the subscriber receives every event regardless of address, identifier, or topics
Address
The event's address field equals the subscription address
Identifier
The event's identifier field equals the subscription identifier (any address)
AddressIdentifier
Both address AND identifier match the subscription values
Topics
address and identifier match, AND the subscription topics are a prefix of the event topics (with wildcard support)
Match levels are evaluated in the order listed. All short-circuits all other checks. More specific levels require progressively more fields in the subscription.
SubscribeEvent structure
messageSubscribeEvent {stringevent_type=1;// Match level: "All", "Address", "Identifier",// "AddressIdentifier", or "Topics"stringaddress=2;// Required for Address, AddressIdentifier, Topicsstringidentifier=3;// Required for Identifier, AddressIdentifier, Topicsrepeatedstringtopics=4;// Required for Topics match level (base64-encoded bytes)}
To subscribe, serialize a SubscribeEvent into a WsMessage PAYLOAD frame with topic = "subscribe" and send it over the hub WebSocket. See connection.md for frame encoding.
Topic prefix matching
The Topics match level compares the subscription's topics list against the event's topics array using a prefix match with wildcard support.
Subscription topics are compared left-to-right against event topics.
The subscription list is a prefix β it must not be longer than the event topics array.
An empty string subscription topic acts as a wildcard and matches any value at that position.
All non-empty subscription topics must exactly match the corresponding event topic (base64-encoded bytes comparison).
Matching algorithm
Base64 encoding
Event topics are raw bytes (token identifiers, nonces, amounts, addresses). In the topics field of SubscribeEvent, they are represented as standard base64-encoded strings.
For example, the token identifier WEGLD-bd4d79 would be encoded as:
Topics are compared as base64 strings, not decoded bytes. Encode topic values to base64 before placing them in the topics field. Sending raw UTF-8 strings without encoding produces no matches.
Code examples
The following JavaScript examples show how to build SubscribeEvent messages for each match level. All examples assume a WsMessage protobuf type is loaded and a sendFrame(msg) helper serializes and sends binary WebSocket frames.
All β receive every event
Address β monitor a specific contract
Identifier β watch any ESDTTransfer across all contracts
AddressIdentifier β watch a specific event on a specific contract
Topics β filter by token identifier with wildcard nonce
Topics β match a specific token and nonce
Subscription helper
Common patterns
Monitor all events from a contract
Use Address match level. You receive every event the contract emits regardless of event type.
Watch a specific DEX event type globally
Use Identifier match level with the event identifier (e.g., swapTokensFixedInput). Useful for aggregating activity across all pools of the same DEX protocol.
Track transfers of a specific fungible token
Use Topics match level with identifier = ESDTTransfer, topics[0] = base64(tokenId), and topics[1] = "" (wildcard for the fungible nonce, which is always zero, but a wildcard is cleaner).
Track transfers of all tokens on a specific contract
Use AddressIdentifier match level. You receive only the event type you care about from one contract, reducing noise relative to Address.
Detect any NFT mint on a contract
Use AddressIdentifier with identifier = ESDTNFTCreate. Use Topics only to filter to a specific collection or nonce range.
function topicsMatch(subscriptionTopics, eventTopics):
if len(subscriptionTopics) > len(eventTopics):
return false // prefix cannot exceed event length
for i in 0..len(subscriptionTopics):
if subscriptionTopics[i] == "":
continue // wildcard: skip this position
if subscriptionTopics[i] != eventTopics[i]:
return false // mismatch at position i
return true
const sub = SubscribeEvent.create({
eventType: 'All',
});
sendSubscribeFrame(ws, sub);
// Receive all events emitted by a given contract address
const sub = SubscribeEvent.create({
eventType: 'Address',
address: 'erd1qqqqqqqqqqqqqpgqx7wfpk0qcxnyfq6xsxe6n3ypz3v5vz2d...',
});
sendSubscribeFrame(ws, sub);
// Receive every ESDTTransfer event regardless of which contract emitted it
const sub = SubscribeEvent.create({
eventType: 'Identifier',
identifier: Buffer.from('ESDTTransfer').toString('base64'),
});
sendSubscribeFrame(ws, sub);
// Receive only swapTokensFixedInput events from one DEX pool
const sub = SubscribeEvent.create({
eventType: 'AddressIdentifier',
address: 'erd1qqqqqqqqqqqqqpgqx7wfpk0qcxnyfq6xsxe6n3ypz3v5vz2d...',
identifier: Buffer.from('swapTokensFixedInput').toString('base64'),
});
sendSubscribeFrame(ws, sub);
// Receive ESDTTransfer events for WEGLD-bd4d79, any nonce
// Event topics layout for ESDTTransfer: [tokenId, nonce, amount, receiver]
// Wildcard ("") at position 1 matches any nonce value
const tokenId = Buffer.from('WEGLD-bd4d79').toString('base64'); // "V0VHTEQtYmQ0ZDc5"
const sub = SubscribeEvent.create({
eventType: 'Topics',
address: 'erd1qqqqqqqqqqqqqpgqx7wfpk0qcxnyfq6xsxe6n3ypz3v5vz2d...',
identifier: Buffer.from('ESDTTransfer').toString('base64'),
topics: [tokenId, ''], // match WEGLD-bd4d79 at pos 0, wildcard at pos 1
});
sendSubscribeFrame(ws, sub);
// Receive events for a specific NFT (token + nonce)
// Nonce is a big-endian uint64 encoded as bytes, then base64
const nonce = 42n;
const nonceBytes = Buffer.alloc(8);
nonceBytes.writeBigUInt64BE(nonce);
const sub = SubscribeEvent.create({
eventType: 'Topics',
address: 'erd1qqqqqqqqqqqqqpgqx7wfpk0qcxnyfq6xsxe6n3ypz3v5vz2d...',
identifier: Buffer.from('ESDTNFTTransfer').toString('base64'),
topics: [
Buffer.from('XOXNO-abcdef').toString('base64'), // token identifier
nonceBytes.toString('base64'), // exact nonce
],
});
sendSubscribeFrame(ws, sub);