System Architecture

XOXNO's three core infrastructure services communicate as follows, carrying data from blockchain node to client application.


Services Overview

Service
Role
External Interface

arb-algo

DEX aggregation and optimal swap routing

REST API at https://swap.xoxno.com

mx-relayer

Transaction co-signing, broadcasting, and real-time data streaming

WebSocket at wss://relayer.xoxno.com/ws; REST at /v1/*

mx-notifier

Blockchain event ingestion and fan-out

Internal WebSocket /hub/ws (binary protobuf); RabbitMQ; Azure Service Bus


Architecture Diagram

  MultiversX Network
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Shard 0        Shard 1        Shard 2   Metachain β”‚
  β”‚  Validators     Validators     Validators Validatorsβ”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚ P2P gossipsub (transactions_0_0, etc.)
              β”‚ binary protobuf WebSocket
              β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚                           mx-notifier                               β”‚
  β”‚                                                                     β”‚
  β”‚  Topics received from node:                                         β”‚
  β”‚  β€’ SaveBlock            β€’ SaveValidatorsPubKeys                     β”‚
  β”‚  β€’ FinalizedBlock       β€’ SaveValidatorsRating                      β”‚
  β”‚  β€’ SaveAccounts         β€’ RevertIndexedBlock                        β”‚
  β”‚  β€’ SaveRoundsInfo       β€’ Settings                                  β”‚
  β”‚                                                                     β”‚
  β”‚  Filter levels: All | Address | AddressIdentifier |                 β”‚
  β”‚                 Identifier | Topics                                 β”‚
  β”‚                                                                     β”‚
  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
  β”‚  β”‚ Preprocessor │───▢│  Fan-out                                 β”‚  β”‚
  β”‚  β”‚ β€’ Normalize  β”‚    β”‚  β€’ /hub/ws  (binary protobuf WebSocket)  β”‚  β”‚
  β”‚  β”‚ β€’ Dedup      β”‚    β”‚  β€’ RabbitMQ publisher                    β”‚  β”‚
  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚  β€’ Azure Service Bus publisher           β”‚  β”‚
  β”‚                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                     β”‚ /hub/ws (binary protobuf)
                                     β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚                           mx-relayer                                β”‚
  β”‚                                                                     β”‚
  β”‚  Inbound from notifier:                                             β”‚
  β”‚  β€’ SaveBlock β†’ extract tx status, account deltas                   β”‚
  β”‚  β€’ SaveAccounts β†’ propagate balance/nonce changes                  β”‚
  β”‚                                                                     β”‚
  β”‚  Outbound (WebSocket, JSON):                                        β”‚
  β”‚  β€’ gasStats      50ms cadence  per-shard p50/p75/p90               β”‚
  β”‚  β€’ networkStats  250ms cadence TPS, mempool, validator health       β”‚
  β”‚  β€’ address/{bech32}  event-driven  account balance + nonce         β”‚
  β”‚  β€’ tx-status/{hash}  event-driven  success/fail, TTL 60s           β”‚
  β”‚                                                                     β”‚
  β”‚  Outbound (REST):                                                   β”‚
  β”‚  β€’ POST /v1/sign      sign batch transactions                       β”‚
  β”‚  β€’ POST /v1/broadcast broadcast pre-signed transactions             β”‚
  β”‚  β€’ GET  /v1/network-stats/history  historical stats                β”‚
  β”‚                                                                     β”‚
  β”‚  P2P broadcast:                                                     β”‚
  β”‚  β€’ transactions_{senderShard}_{receiverShard} gossipsub topic       β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                          β”‚ WebSocket (JSON) / REST
                                          β–Ό
                                   Client Applications

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚                    arb-algo  (independent service)                  β”‚
  β”‚                                                                     β”‚
  β”‚  Pool data refresh: every 5 seconds from Xoxno API                 β”‚
  β”‚  DEXs: xExchange, AshSwap, JEX, OneDex                             β”‚
  β”‚                                                                     β”‚
  β”‚  REST API:                                                          β”‚
  β”‚  β€’ GET /api/v1/quote         optimal route + txData                 β”‚
  β”‚  β€’ GET /api/v1/pair-config   token support + decimal metadata       β”‚
  β”‚  β€’ GET /health               pool/token counts, uptime              β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                          β”‚ REST
                                          β–Ό
                                   Client Applications

Service Responsibilities

arb-algo

arb-algo maintains a live graph of all liquidity pools across supported DEXs and runs beam search plus split optimization to find the route that maximizes output for a given input (or minimizes input for a given output). Every quote request computes routes against the current pool state; arb-algo carries no per-client state.

Pool data refreshes atomically every 5 seconds. In-flight requests during a refresh continue reading the previous snapshot β€” no window of inconsistency exists.

The txData field in every quote response is a base64-encoded smart contract call payload. Clients submit this field directly as the transaction data field with no further encoding.

arb-algo does not broadcast transactions, track transaction status, or provide gas price data. Use mx-relayer for those capabilities.

mx-relayer

mx-relayer has two primary responsibilities:

  1. Real-time data streaming. mx-relayer subscribes to mx-notifier and reformats blockchain events into JSON WebSocket messages for client applications. Clients subscribe to named topics; the relayer delivers only relevant events.

  2. Transaction submission. Clients send signed transactions to the relayer via WebSocket or REST. For relay actions, the relayer validates shard alignment, appends its co-signature (relayerSignature), and broadcasts to MultiversX's P2P gossipsub network. For broadcast actions, no co-signing occurs.

mx-notifier

mx-notifier is an internal service. It connects to MultiversX nodes via binary protobuf WebSocket, receives raw blockchain events, normalizes and deduplicates them, and fans out to downstream consumers. Client applications connect to mx-relayer, not mx-notifier.


Network Topology


Data Flow: Quote to Confirmation

The following sequence traces what happens when a client executes a swap.

Step 1 β€” Quote. The client sends a GET request to arb-algo with the input token, output token, and amount. arb-algo runs beam search over the current pool graph, solves the split optimization, and returns the optimal route, expected output, slippage-adjusted minimum output, and a ready-to-use txData payload.

Step 2 β€” Subscribe before sending. The client subscribes to tx-status/{txHash} on the relayer WebSocket before sending the transaction. The status event fires once and is not replayed. Because the transaction hash is deterministic from the signed transaction, the client knows it before broadcast.

Step 3 β€” Relay. The client sends a relay action to the relayer WebSocket. The transaction must include the relayer field set to the shard-appropriate relayer address, and the user must sign with that field present. The relayer validates shard alignment, appends its co-signature, and broadcasts to the P2P network.

Step 4 β€” Block production. Validators on the sender's shard receive the transaction via gossipsub, include it in a block, and execute it. If the receiver is on a different shard, a cross-shard message is processed in a subsequent block on the receiver's shard.

Step 5 β€” Notification. The MultiversX node pushes SaveBlock to mx-notifier. mx-notifier extracts transaction results and account state changes, then forwards them to mx-relayer via /hub/ws.

Step 6 β€” Client delivery. mx-relayer fans out to subscribers:

  • Clients subscribed to tx-status/{hash} receive the final status (success or fail).

  • Clients subscribed to address/{bech32} receive updated account balance and nonce.


Operational Characteristics

Characteristic
Value

arb-algo pool refresh interval

5 seconds

gasStats stream cadence

50ms

networkStats stream cadence

250ms

tx-status TTL

60 seconds after subscription

Max WebSocket topics per connection

64

Max control messages per 5s window

64

Max WebSocket message size

4 MB

Ping/pong heartbeat interval

30 seconds

gasStats sliding window

30 minutes

gasStats bucket size

10 seconds


When to Use Relayer vs. Direct Gateway Submission

Use mx-relayer when:

  • You need real-time transaction status without polling. The tx-status/{hash} subscription delivers the result as soon as the block finalizes.

  • You need co-signed (relayed) transactions so the relayer pays gas. This requires the Relayer V3 transaction format with shard-specific relayer addresses.

  • You want per-shard gas statistics to set competitive gas prices before submission.

  • You need account update events (balance, nonce) without running your own indexer.

Use direct gateway submission when:

  • You run your own infrastructure and have no need for real-time event streaming.

  • Your application signs and pays for gas independently, with no co-signing requirement.

  • You have an existing polling-based confirmation flow.


Last updated

Was this helpful?