GetBdnBlockStream
The GetBdnBlockStream
stream provides low-latency access to new blocks as they’re produced on the Base network. Note that there is a low chance that this stream may include blocks that will not be accepted on chain.
This is a real-time gRPC stream delivering full block headers and bodies (including transactions), intended for searchers, indexers, and other latency-sensitive applications.
Quickstart
Prerequisites
Go 1.24.1+
op-geth
installed (pay attention to use it instread ofgo-ethereum
)gRPC installed (
google.golang.org/grpc
).proto
file:streamer_api.proto
gRPC Endpoint
base.blxrbdn.com:443
No TLS required
Authorization required via
authorization
header
Authentication
Set your authorization header via environment variable:
export AUTH_HEADER="<your-auth-header>"
In gRPC, this will be passed as:
metadata.New(map[string]string{
"authorization": os.Getenv("AUTH_HEADER"),
})
Example: Test with grpcurl
grpcurl -H "authorization: <AUTH_HEADER>" \
-H "Content-Type: application/grpc" \
-d '{}' \
base.blxrbdn.com:443 \
streamerapi.Api/GetBdnBlockStream
This will return raw stream data — you can pretty-print it with jq
.
GO Example: Listen for New Blocks on Base
🔗 You can get the .proto
file here
🔗 You can access the Base streamer client here
package main
import (
"context"
"fmt"
"time"
"github.com/bloXroute-Labs/base-streamer-client-go/provider"
streamerapi "github.com/bloXroute-Labs/base-streamer-proto/streamer_api"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
"github.com/joho/godotenv"
)
func main() {
_ = godotenv.Load()
err := ListenForBdnBlocks(10)
if err != nil {
panic(err)
}
}
func ListenForBdnBlocks(numberOfBlocks uint64) error {
grpcClient, err := provider.NewGRPCClient()
if err != nil {
return err
}
blocksChan := make(chan *streamerapi.GetBdnBlockStreamResponse)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
stream, err := grpcClient.GetBdnBlockStream(ctx)
if err != nil {
return fmt.Errorf("failed to get stream with: %v", err)
}
stream.Into(blocksChan)
fmt.Println("waiting on blocks channel")
for range numberOfBlocks {
bdnBlock, ok := <-blocksChan
if !ok {
// channel closed
return fmt.Errorf("bdn blocks channel closed")
}
updateTime := time.Now()
blockHeader := &types.Header{}
err := rlp.DecodeBytes(bdnBlock.BlockHeader, blockHeader)
if err != nil {
return fmt.Errorf("failed to RLP decode block header with: %v", err)
}
blockBody := &types.Body{}
err = rlp.DecodeBytes(bdnBlock.BlockBody, blockBody)
if err != nil {
return fmt.Errorf("failed to RLP decode block body with: %v", err)
}
fmt.Printf("bdn block: %v, %v txns at %v\n", blockHeader.Number.Uint64(), len(blockBody.Transactions), updateTime.UTC())
}
return nil
}
Sample output:
waiting on blocks channel
new block: 12345678, 120 txns at 2025-03-25 12:45:01.234 UTC
new block: 12345679, 98 txns at 2025-03-25 12:45:13.147 UTC
...
Troubleshooting
Stream closes unexpectedly
Expired token, gRPC connection limits, network/firewall issues
Unmarshal errors
Mismatched Go or op-geth
dependency
No output
Ensure proper env variables and endpoint configuration
Last updated