A Go-based tool for testing and comparing transaction latency performance between different Flashblocks-enabled Base node endpoints.
Flashblocks are sub-blocks issued by the block builder every 200ms, allowing for early confirmation times and making Base 10x faster for real-time applications.
For synchronous transaction sending testing, the node endpoints must support the eth_sendRawTransactionSync RPC method. See EIP-7966: eth_sendRawTransactionSync Method.
For asynchronous transaction sending testing, the node endpoints do not have to support the eth_sendRawTransactionSync RPC method and will rely on separate polling for confirmed transactions.
Chainstack Base nodes for Mainnet & Testnet support both the synchronous one with eth_sendRawTransactionSync and the asynchronous one.
Create a .env file based on .env.example:
PRIVATE_KEY=your_private_key_here TO_ADDRESS=0xc2F695613de0885dA3bdd18E8c317B9fAf7d4eba # If you are going with the asynchronous method and poll for the transaction receipts separately POLLING_INTERVAL_MS=50 BASE_NODE_ENDPOINT_1=https://your-node-endpoint-1.com BASE_NODE_ENDPOINT_2=https://your-node-endpoint-2.com # This a simple label for the resulting filename to remember where you sent the test transactions from. Not used in any node routing. REGION=singapore NUMBER_OF_TRANSACTIONS=10 # With `true`, the synchronous eth_sendRawTransactionSync method is used. With `false`, the asynchronous method is used with POLLING_INTERVAL_MS=50 SEND_TXN_SYNC=true RUN_ENDPOINT2_TESTING=trueSEND_TXN_SYNC=true:
- Uses
eth_sendRawTransactionSyncmethod - Provides instant confirmation receipt when transaction is included in a Flashblock
- Transactions wait for immediate inclusion confirmation
SEND_TXN_SYNC=false:
- Uses standard
eth_sendTransactionmethod - Polls for transaction receipts using
POLLING_INTERVAL_MS - Traditional async transaction sending
POLLING_INTERVAL_MS:
- Used only when
SEND_TXN_SYNC=false - Defines how frequently (in milliseconds) to check for transaction receipts
- Default: 50ms (recommended for accurate latency measurements)
- Lower values = more frequent polling = faster detection but higher load
- Not used when
SEND_TXN_SYNC=truesince confirmations are immediate
BASE_NODE_ENDPOINT_1: Primary endpoint (e.g., a Flashblocks-enabled)BASE_NODE_ENDPOINT_2: Secondary endpoint (e.g., a standard non-Flashblocks endpoint)
The tool will:
- Send transactions to
BASE_NODE_ENDPOINT_1first - Then send transactions to
BASE_NODE_ENDPOINT_2(ifRUN_ENDPOINT2_TESTING=true) - Compare performance between both endpoints
# Build the container docker build -t transaction-latency . # Run the test docker run -v $(pwd)/data:/data --env-file .env --rm -it transaction-latencyWhen running with Docker, you may see the log message:
Error loading .env file This is expected and harmless. The application uses two methods to load environment variables:
- Docker's
--env-fileflag (recommended): Sets environment variables at container runtime - Direct .env file loading: Attempts to read the
.envfile from inside the container
Since we use the --env-file approach for security (keeping secrets out of the image), the direct file loading fails but the environment variables are still available via Docker. The application continues normally and all configuration works as expected.
After completion, results are saved to the ./data/ directory:
endpoint1-{region}.csv: Results fromBASE_NODE_ENDPOINT_1endpoint2-{region}.csv: Results fromBASE_NODE_ENDPOINT_2(if enabled)
Each CSV contains:
sent_at: Timestamp when transaction was senttxn_hash: Transaction hashincluded_in_block: Block number where transaction was includedinclusion_delay_ms: Time from sending to confirmation (milliseconds)
Flashblocks are produced at 200ms intervals, but actual confirmation times will be higher due to network latency:
- Standard endpoint: ~2000ms (2-second block time)
- Flashblocks endpoint: ~300-500ms (200ms Flashblock interval + network travel time)
The actual confirmation time depends on:
- Network latency to/from the Base node
- Transaction processing time
- Time until next Flashblock (up to 200ms)
- Network travel time for confirmation response
On a Flashblocks-enabled node, the following standard RPC methods will retrieve the Flashblocks data instead of full blocks data:
eth_getBlockByNumberwithpendingtageth_getTransactionReceipteth_getBalancewithpendingtageth_getTransactionCountwithpendingtageth_getTransactionByHashwithpendingtageth_sendRawTransactionSync
For a quick comparison, see:
eth-get-block-by-number-pending-examples/preconfirmed-flashblock.log— the result of running"method":"eth_getBlockByNumber","params":["pending",true]on a Flashblocks-enabled node as block 33228756 was forming.eth-get-block-by-number-pending-examples/confirmed-block.log— the result of running of running"method":"eth_getBlockByNumber","params":["pending",true]on a non-Flashblocks-enabled node as block 33228756 was formed.
You will see the key differences in the results:
- Transaction count — 52 in the Flashblock vs. 167 in the fully formed block.
stateRoot— empty in the Flashblock vs. computed in the fully formed block.blockHash— different in the Flashblock and the fully formed block. The finalized transactions will have the hash of the fully formed block attributed to them.receiptsRoot— different in the Flashblock and the fully formed block.