Get FlashBlockStream
Introduction
This method is used to retrieve FlashBlock data from Base, supporting both gRPC and WebSocket protocols.
Flashblocks are "sub-blocks" streamed on Base every 200 milliseconds, enabling transaction pre-confirmation 10 times faster than the standard 2-second block time. These sub-blocks, called Flashblocks, contain approximately 10% of a full block's transaction data, allowing applications to receive near-instant transaction feedback, suitable for low-latency scenarios.
Endpoint
{% tabs %} {% tab title="gRPC" %}
| Region | Endpoint |
|---|---|
| Frankfurt | frankfurt.grpc.base.blockrazor.xyz:80 |
| Virginia | virginia.grpc.base.blockrazor.xyz:80 |
| Tokyo | tokyo.grpc.base.blockrazor.xyz:80 |
{% endtab %}
{% tab title="WebSocket" %}
| Region | Endpoint |
|---|---|
| Frankfurt | ws://frankfurt.base.blockrazor.xyz:81/ws |
| Virginia | ws://virginia.base.blockrazor.xyz:81/ws |
| Tokyo | ws://tokyo.base.blockrazor.xyz:81/ws |
{% endtab %} {% endtabs %}
Rate Limit
| Tier 4 | Tier 3 | Tier 2 | Tier 1 | Tier 0 | |
|---|---|---|---|---|---|
| FlashBlockStream | - | - | - | ✅ | ✅ |
Request Example
{% tabs %} {% tab title="gRPC" %} Access the example here
// GetFlashBlockStream provides a simplified example of subscribing to and processing the flash block stream.
// Note: This function attempts to connect and subscribe only once. For production use, implement your own reconnection logic.
func GetFlashBlockStream(authToken string) {
log.Printf("[FlashStream] Attempting to connect to gRPC server at %s...", grpcAddr)
// Establish a connection to the gRPC server with a timeout.
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
conn, err := grpc.DialContext(ctx, grpcAddr,
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Printf("[FlashStream] Failed to connect to gRPC server: %v", err)
return
}
defer conn.Close()
log.Println("[FlashStream] Successfully connected to gRPC server.")
client := basepb.NewBaseApiClient(conn)
// Create a new context with authentication metadata for the stream subscription.
streamCtx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs("authorization", authToken))
stream, err := client.GetRawFlashBlockStream(streamCtx, &basepb.GetRawFlashBlocksStreamRequest{})
if err != nil {
log.Printf("[FlashStream] Failed to subscribe to stream: %v", err)
return
}
log.Println("[FlashStream] Subscription successful. Waiting for new flash blocks...")
// Loop indefinitely to receive messages from the stream.
for {
block, err := stream.Recv()
if err != nil {
if err == io.EOF {
log.Println("[FlashStream] Stream closed by the server (EOF).")
} else {
log.Printf("[FlashStream] An error occurred while receiving data: %v", err)
}
break // Exit the loop on error or stream closure.
}
// Process the received flash block data.
jsonString, err := ParseFlashBlockByte(block.Message)
if err != nil {
log.Printf("[FlashStream] Failed to parse flash block data: %v", err)
continue
}
var jsonMap map[string]interface{}
if err := json.Unmarshal([]byte(jsonString), &jsonMap); err != nil {
log.Printf("[FlashStream] Failed to unmarshal flash block JSON: %v", err)
continue
}
printPretty(jsonMap)
}
}
{% endtab %}
{% tab title="WebSocket-Go" %} Access the example here
// GetWebSocketFlashBlockStream provides a simplified example of using the WebSocket API to subscribe to the flash block stream.
// Note: This function attempts to connect only once and will exit on a read error. For production use, implement your own reconnection logic.
func GetWebSocketFlashBlockStream(authToken string) {
// Set up the HTTP header with the authorization token.
header := http.Header{}
header.Set("Authorization", authToken)
// Dial the WebSocket server.
dialer := websocket.DefaultDialer
conn, resp, err := dialer.Dial(websocketAddr, header)
if err != nil {
if resp != nil {
log.Fatalf("[WebSocket] Dial failed: %v (HTTP status: %s)", err, resp.Status)
}
log.Fatalf("[WebSocket] Dial failed: %v", err)
}
defer conn.Close()
log.Printf("[WebSocket] Successfully connected to %s", websocketAddr)
// Prepare the JSON-RPC subscription request.
req := map[string]interface{}{
"jsonrpc": "2.0",
"method": "subscribe_FlashBlock",
"params": []interface{}{},
"id": 1,
}
reqB, _ := json.Marshal(req)
if err := conn.WriteMessage(websocket.TextMessage, reqB); err != nil {
log.Fatalf("[WebSocket] Failed to send subscription request: %v", err)
}
log.Printf("[WebSocket] Subscription request sent: %s", string(reqB))
// Loop indefinitely to read messages from the server.
for {
msgType, msg, err := conn.ReadMessage()
if err != nil {
log.Printf("[WebSocket] Error reading message: %v", err)
return // Exit the function on any read error.
}
if msgType != websocket.TextMessage && msgType != websocket.BinaryMessage {
continue // Ignore messages that are not text or binary.
}
// Parse the outer JSON-RPC response wrapper.
var outer = &FlashBlockWebSocketResponse{}
if err := json.Unmarshal(msg, &outer); err != nil {
log.Printf("[WebSocket] Failed to parse JSON from server: %v\nRaw data: %s", err, string(msg))
continue
}
// Extract the "result" field, which contains the actual flash block data, and process it.
resultRaw := outer.Result
if jsonString, err := ParseFlashBlockByte(resultRaw); err == nil {
printPretty(jsonString)
} else {
log.Printf("[WebSocket] Received message without a valid result field. Raw data: %s", string(msg))
}
}
}
{% endtab %}
{% tab title="WebSocket-Cli" %}
wscat -H "Authorization: <AUTH_HEADER>" \
-c ws://frankfurt.base.blockrazor.xyz:81/ws \
--wait 1000 \
--execute '{"jsonrpc": "2.0", "id": 1, "method": "subscribe_FlashBlock", "params": []}'
{% endtab %}
{% tab title="JS" %}
const WebSocket = require('ws'); // WebSocket library for Node.js, install via npm(npm install ws) if not already installed
const zlib = require('zlib'); // Node.js built-in library for compression/decompression
// --- Configuration Parameters ---
// Replace with your actual authorization token
const AUTH_TOKEN = "<YOUR_AUTH_TOKEN>";
const WS_URL = "<WEBSOCKET_URL>"; // websocket URL
const SUBSCRIPTION_MESSAGE = {
"jsonrpc": "2.0",
"id": 1,
"method": "subscribe_FlashBlock",
"params": []
};
const WAIT_TIME_MS = 1000; // Time to wait before sending the subscription message
/**
* Decompresses a Buffer using the Brotli algorithm.
* @param {Buffer} dataBuffer - The data buffer to be decompressed.
* @returns {Promise<string>} - A promise that resolves with the decompressed string.
*/
function ParseBrotliData(dataBuffer) {
return new Promise((resolve, reject) => {
// zlib.brotliDecompress is used for Brotli decompression
zlib.brotliDecompress(dataBuffer, (err, decompressedBuffer) => {
if (err) {
// Return error if decompression fails
return reject(new Error("Brotli decompression failed."));
}
// Convert the decompressed buffer to a string and resolve
resolve(decompressedBuffer.toString('utf8'));
});
});
}
// --- WebSocket Connection and Operations ---
function connectWebSocket() {
console.log(`Attempting to connect to: ${WS_URL}`);
// Create custom Headers for authorization
const headers = {
'Authorization': AUTH_TOKEN
};
// Establish WebSocket connection, passing headers
const ws = new WebSocket(WS_URL, {
headers: headers
});
// 1. Connection established successfully
ws.on('open', () => {
console.log('✅ Connection established.');
// Mimicking --wait 1000, wait 1 second before sending the message
setTimeout(() => {
const messageString = JSON.stringify(SUBSCRIPTION_MESSAGE);
console.log(`➡️ Sending subscription message (after waiting ${WAIT_TIME_MS}ms):`);
console.log(messageString);
ws.send(messageString);
}, WAIT_TIME_MS);
});
// 2. Message received
ws.on('message', async (data) => {
// Data is a Buffer received from the server, which we suspect is uncompressed JSON string.
const rawJsonString = data.toString('utf8');
try {
// STEP 1: Parse the outer JSON-RPC wrapper
const rpcResponse = JSON.parse(rawJsonString);
// Check if 'result' or 'params' field exists and contains the Base64 data
const base64Data = rpcResponse.result || (rpcResponse.params && rpcResponse.params.data);
if (!base64Data || typeof base64Data !== 'string') {
console.log(`\n⬅️ Received non-compressed JSON message:`);
console.log(JSON.stringify(rpcResponse, null, 2));
return;
}
console.log(`\n⬅️ Received compressed data in JSON wrapper. Base64 length: ${base64Data.length}`);
// STEP 2: Decode Base64 string back into raw Buffer
const rawBrotliBuffer = Buffer.from(base64Data, 'base64');
console.log(` Decoded Base64 to Buffer. Brotli Buffer size: ${rawBrotliBuffer.length} bytes.`);
// STEP 3: Decompress the raw Brotli Buffer
const decompressedString = await ParseBrotliData(rawBrotliBuffer);
console.log("🌟 Successfully decompressed and parsed message:");
// STEP 4: Parse the inner JSON content
const finalData = JSON.parse(decompressedString);
console.log(JSON.stringify(finalData, null, 2));
} catch (e) {
// Handle any error during the 4 steps (JSON parse, Base64 decode, Brotli decompress, final JSON parse)
console.error("\n❌ Error during processing compressed payload:");
console.error(` Error message: ${e.message}`);
console.log(` Raw received string (first 200 chars): ${rawJsonString.substring(0, 200)}...`);
}
});
// 3. Connection closed
ws.on('close', (code, reason) => {
console.log(`\n❌ Connection closed. Code: ${code}, Reason: ${reason.toString()}`);
});
// 4. Connection error
ws.on('error', (error) => {
console.error(`\n🔥 An error occurred: ${error.message}`);
});
}
// Start the client
connectWebSocket();
{% endtab %} {% endtabs %}
proto
syntax = "proto3";
option go_package = "./basepb";
import "google/protobuf/wrappers.proto";
message BaseBlock {
string parent_hash = 1;
string fee_recipient = 2;
bytes state_root = 3;
bytes receipts_root = 4;
bytes logs_bloom = 5;
bytes prev_randao = 6;
uint64 block_number = 7;
uint64 gas_limit = 8;
uint64 gas_used = 9;
uint64 timestamp = 10;
bytes extra_data = 11;
repeated uint64 base_fee_per_gas = 12;
string block_hash = 13;
repeated bytes transactions = 14;
repeated Withdrawal withdrawals = 15;
google.protobuf.UInt64Value blob_gas_used = 16;
google.protobuf.UInt64Value excess_blob_gas = 17;
google.protobuf.BytesValue withdrawals_root = 18;
}
message Withdrawal {
uint64 index = 1;
uint64 validator = 2;
bytes address = 3;
uint64 amount = 4;
}
message GetRawFlashBlocksStreamRequest {
}
message GetBlockStreamRequest {
}
message SendTransactionRequest {
string rawTransaction = 1;
}
message SendTransactionResponse {
string txHash = 1;
}
message FlashBlockStrRequest {
}
message RawFlashBlockStrResponse {
bytes message = 1;
}
service BaseApi {
rpc SendTransaction(SendTransactionRequest) returns (SendTransactionResponse);
rpc GetBlockStream(GetBlockStreamRequest) returns (stream BaseBlock);
rpc GetRawFlashBlockStream(GetRawFlashBlocksStreamRequest) returns (stream RawFlashBlockStrResponse);
}
Response
Normal
{
message: "185329……7e04b7"
}
Abnormal
rpc error: code = Unknown desc = Authentication information is missing. Please provide a valid auth token