How to read the sequencer feed
Running an Arbitrum relay locally as a feed relay lets you subscribe to an uncompressed sequencer feed for real-time data as the sequencer accepts and orders transactions off-chain.
When connected to websocket port 9642
of the local relay, you'll receive a data feed that looks something like this:
{
"version": 1,
"messages": [
{
"sequenceNumber": 25757171,
"message": {
"message": {
"header": {
"kind": 3,
"sender": "0xa4b000000000000000000073657175656e636572",
"blockNumber": 16238523,
"timestamp": 1671691403,
"requestId": null,
"baseFeeL1": null
},
"l2Msg": "BAL40oKksUiElQL5AISg7rsAgxb6o5SZbYNoIF2DTixsqDpD2xII9GJLG4C4ZAhh6N0AAAAAAAAAAAAAAAC7EQiq1R1VYgL3/oXgvD921hYRyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArAAaAkebuEnSAUvrWVBGTxA7W+ZMNn5uyLlbOH7Nrs0bYOv6AOxQPqAo2UB0Z7vqlugjn+BUl0drDcWejBfDiPEC6jQA=="
},
"delayedMessagesRead": 354560
},
"signature": null
}
]
}
Breaking this feed down a bit: the top-level data structure is defined by the BroadcastMessage struct
:
type BroadcastMessage struct {
Version int `json:"version"`
// Note: the "Messages" object naming is slightly ambiguous: since there are different types of messages
Messages []*BroadcastFeedMessage `json:"messages,omitempty"`
ConfirmedSequenceNumberMessage *ConfirmedSequenceNumberMessage `json:"confirmedSequenceNumberMessage,omitempty"`
}
The messages
field is the BroadcastFeedMessage struct
:
type BroadcastFeedMessage struct {
SequenceNumber arbutil.MessageIndex `json:"sequenceNumber"`
Message arbstate.MessageWithMetadata `json:"message"`
Signature []byte `json:"signature"`
}
Each message
conforms to arbstate.MessageWithMetadata
:
type MessageWithMetadata struct {
Message *arbos.L1IncomingMessage `json:"message"`
DelayedMessagesRead uint64 `json:"delayedMessagesRead"`
}
Finally, we get the transaction's information in the message
subfield as an L1IncomingMessage
:
type L1IncomingMessage struct {
Header *L1IncomingMessageHeader `json:"header"`
L2msg []byte `json:"l2Msg"`
// Only used for `L1MessageType_BatchPostingReport`
BatchGasCost *uint64 `json:"batchGasCost,omitempty" rlp:"optional"`
}
You can use the ParseL2Transactions
function to decode the message.
Using the feed relay, you can also retrieve the L2 block number
of a message:
- On Arbitrum One, this can be done by adding the Arbitrum One genesis block number (22207817) to the sequence number of the feed message.
- Note that in the case of Arbitrum Nova, the Nitro genesis number is
0
, so it doesn't need to be included when adding to the feed message's sequence number.
Note that the messagess[0].message.message.header.blockNumber
is L1 block number
instead of L2 block number