# 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" %}

<table><thead><tr><th width="138.33984375">Region</th><th>Endpoint</th></tr></thead><tbody><tr><td>Frankfurt</td><td>frankfurt.grpc.base.blockrazor.xyz:80</td></tr><tr><td>Virginia</td><td>virginia.grpc.base.blockrazor.xyz:80</td></tr><tr><td>Tokyo</td><td>tokyo.grpc.base.blockrazor.xyz:80</td></tr></tbody></table>
{% endtab %}

{% tab title="WebSocket" %}

<table><thead><tr><th width="118.8203125">Region</th><th>Endpoint</th></tr></thead><tbody><tr><td>Frankfurt</td><td>ws://frankfurt.base.blockrazor.xyz:81/ws</td></tr><tr><td>Virginia</td><td>ws://virginia.base.blockrazor.xyz:81/ws</td></tr><tr><td>Tokyo</td><td>ws://tokyo.base.blockrazor.xyz:81/ws</td></tr></tbody></table>
{% endtab %}
{% endtabs %}

### Price

The price is $250 per data stream per month. Please go to the [Pricing](https://blockrazor.io/#/pricing) page to purchase.

### Request Example

{% tabs %}
{% tab title="gRPC" %}
Access the example [here](https://github.com/BlockRazorinc/base-api-client-go/blob/c4bec3d65e55ffb0da07253fa78aefe1b1c07e33/main.go#L93)

```go
// 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](https://github.com/BlockRazorinc/base-api-client-go/blob/1d46c2983420d6da645992a9f3ed51688f7dac88/main.go#L181)

```go
// 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" %}

```javascript
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](https://github.com/BlockRazorinc/base-api-client-go/blob/1d46c2983420d6da645992a9f3ed51688f7dac88/proto/BaseApi.proto)

```go
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**

```go
{
    message: "185329……7e04b7"
}
```

**Abnormal**

```
rpc error: code = Unknown desc = Authentication information is missing. Please provide a valid auth token
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.blockrazor.io/streams/block-stream/base/get-flashblockstream.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
