A comprehensive Go library for interacting with Flashbots, enabling developers to send transaction bundles to Flashbots relay for MEV (Maximal Extractable Value) protection and optimization on Ethereum.
- Features
- Installation
- Quick Start
- API Documentation
- Examples
- Configuration
- Advanced Usage
- Network Support
- Authentication
- TODO & Improvements
- Contributing
- Support
- License
- 🚀 Bundle Simulation: Test your transaction bundles before broadcasting using
mev_simBundle - 📡 Bundle Broadcasting: Send transaction bundles to Flashbots relay with
mev_sendBundle - 🔒 MEV Protection: Protect your transactions from frontrunning and sandwich attacks
- ⛽ Gas Estimation: Built-in gas price and tip estimation utilities
- 🔧 Flexible Configuration: Support for multiple builders, custom relay URLs, and network selection
- 📊 OpenTelemetry Integration: Built-in tracing support for observability
- 🎯 MEV-Share Support: Full support for the latest MEV-Share bundle format
- 🔐 EIP-191 Authentication: Secure request signing using Ethereum private keys
go get github.com/harpy-wings/flashbotpackage main
import (
"context"
"crypto/ecdsa"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/harpy-wings/flashbot"
)
func main() {
ctx := context.Background()
// Initialize Flashbot client for Sepolia testnet
fb, err := flashbot.New(ctx,
flashbot.WithChainID(flashbot.SepoliaChainID),
flashbot.WithRelayURL(flashbot.SepoliaRelayURL),
)
if err != nil {
panic(err)
}
// Connect to Ethereum node
ethClient, err := ethclient.Dial("https://your-rpc-url")
if err != nil {
panic(err)
}
// Get current block number
currentBlock, err := ethClient.BlockNumber(ctx)
if err != nil {
panic(err)
}
// Create your transactions
privateKey, _ := crypto.HexToECDSA("your-private-key")
fromAddress := crypto.PubkeyToAddress(privateKey.PublicKey)
// Example: Create a simple ETH transfer transaction
nonce, _ := ethClient.PendingNonceAt(ctx, fromAddress)
gasPrice, _ := ethClient.SuggestGasPrice(ctx)
gasTip, _ := ethClient.SuggestGasTipCap(ctx)
toAddress := common.HexToAddress("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb")
tx := types.NewTx(&types.DynamicFeeTx{
ChainID: big.NewInt(flashbot.SepoliaChainID),
Nonce: nonce,
To: &toAddress,
Value: big.NewInt(1000000000000000000), // 1 ETH
Gas: 21000,
GasFeeCap: gasPrice,
GasTipCap: gasTip,
Data: nil,
})
signer := types.LatestSignerForChainID(big.NewInt(flashbot.SepoliaChainID))
signedTx, _ := types.SignTx(tx, signer, privateKey)
// Create bundle
bundle := &flashbot.Bundle{
Transactions: []*types.Transaction{signedTx},
CanRevert: []bool{false},
}
// Simulate bundle first (recommended)
simResp, err := fb.Simulate(ctx, bundle, currentBlock+1)
if err != nil {
panic(err)
}
fmt.Printf("Simulation successful! Profit: %s\n", simResp.Profit)
// Broadcast bundle
broadcastResp, err := fb.Broadcast(ctx, bundle, currentBlock+1)
if err != nil {
panic(err)
}
fmt.Printf("Bundle broadcasted! Hash: %s\n", broadcastResp.BundleHash)
}The library provides a clean interface for interacting with Flashbots:
type IFlashbot interface {
// Simulate runs the bundle against Flashbots Relay to check for reverts
Simulate(ctx context.Context, bundle *Bundle, targetBlock uint64, opts ...BundleOption) (*SimulateResponse, error)
// Broadcast sends the bundle to configured builders
Broadcast(ctx context.Context, bundle *Bundle, targetBlock uint64, opts ...BundleOption) (*BroadcastResponse, error)
// SendPrivateTransaction sends a single transaction with frontrunning protection
SendPrivateTransaction(ctx context.Context, signedTxHex string, expDurationBlocks uint64) error
// GetGasPrice returns suggested gas price and tip
GetGasPrice(ctx context.Context) (gasPrice *big.Int, tip *big.Int, err error)
// EstimateGasBundle calculates total gas units for the bundle
EstimateGasBundle(ctx context.Context, bundle *Bundle) (uint64, error)
// GetUserStats checks your signing key's reputation on the relay
GetUserStats(ctx context.Context, blockNumber *big.Int) (*UserStats, error)
}type Bundle struct {
Transactions []*types.Transaction // List of signed transactions
CanRevert []bool // Whether each tx can revert
ReplacementUUID string // UUID for bundle replacement
Builders []string // Target builders
MinTimestamp int64 // Minimum timestamp validity
MaxTimestamp int64 // Maximum timestamp validity
}type SimulateResponse struct {
Success bool // Whether simulation succeeded
StateBlock string // Block used for simulation
MevGasPrice string // MEV gas price
Profit string // Expected profit in wei
RefundableValue string // Refundable value
GasUsed string // Total gas used
Logs []TxLogResult // Transaction logs
}type BroadcastResponse struct {
BundleHash string // Unique bundle identifier
Smart bool // Whether smart routing was used
}This example demonstrates creating a bundle where one transaction sponsors gas for another:
package main
import (
"context"
"crypto/ecdsa"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/harpy-wings/flashbot"
)
func createGasSponsoredBundle(ctx context.Context, ethClient *ethclient.Client, fb flashbot.IFlashbot) error {
// Wallet 1: Sponsor (has ETH)
sponsorKey, _ := crypto.HexToECDSA("sponsor-private-key")
sponsorAddr := crypto.PubkeyToAddress(sponsorKey.PublicKey)
// Wallet 2: User (needs gas for ERC20 transfer)
userKey, _ := crypto.HexToECDSA("user-private-key")
userAddr := crypto.PubkeyToAddress(userKey.PublicKey)
gasPrice, _ := ethClient.SuggestGasPrice(ctx)
gasTip, _ := ethClient.SuggestGasTipCap(ctx)
// Transaction 2: ERC20 transfer (user's transaction)
tokenAddress := common.HexToAddress("0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238")
// ... pack ERC20 transfer data ...
userNonce, _ := ethClient.PendingNonceAt(ctx, userAddr)
tx2 := types.NewTx(&types.DynamicFeeTx{
ChainID: big.NewInt(flashbot.SepoliaChainID),
Nonce: userNonce,
To: &tokenAddress,
Value: big.NewInt(0),
Gas: 65000,
GasFeeCap: gasPrice,
GasTipCap: gasTip,
Data: transferData,
})
signer := types.LatestSignerForChainID(big.NewInt(flashbot.SepoliaChainID))
signedTx2, _ := types.SignTx(tx2, signer, userKey)
// Calculate gas cost for tx2
tx2GasCost := new(big.Int).Mul(big.NewInt(65000), gasPrice)
// Transaction 1: Sponsor sends ETH to user for gas
sponsorNonce, _ := ethClient.NonceAt(ctx, sponsorAddr, nil)
tx1 := types.NewTx(&types.DynamicFeeTx{
ChainID: big.NewInt(flashbot.SepoliaChainID),
Nonce: sponsorNonce,
To: &userAddr,
Value: tx2GasCost, // Send exact gas cost
Gas: 21000,
GasFeeCap: gasPrice,
GasTipCap: gasTip,
Data: nil,
})
signedTx1, _ := types.SignTx(tx1, signer, sponsorKey)
// Create bundle (tx1 must execute before tx2)
bundle := &flashbot.Bundle{
Transactions: []*types.Transaction{signedTx1, signedTx2},
CanRevert: []bool{false, false},
}
// Simulate
currentBlock, _ := ethClient.BlockNumber(ctx)
simResp, err := fb.Simulate(ctx, bundle, currentBlock+1)
if err != nil {
return err
}
if !simResp.Success {
return fmt.Errorf("simulation failed")
}
// Broadcast
broadcastResp, err := fb.Broadcast(ctx, bundle, currentBlock+1)
if err != nil {
return err
}
fmt.Printf("Bundle hash: %s\n", broadcastResp.BundleHash)
return nil
}func sendBundleWithPrivacy(ctx context.Context, fb flashbot.IFlashbot, bundle *flashbot.Bundle, targetBlock uint64) error {
// Configure privacy hints to share specific information with builders
privacy := flashbot.MevSendBundlePrivacy{
Hints: []string{
"calldata", // Share calldata
"contract_address", // Share contract address
"logs", // Share event logs
},
Builders: []string{"builder0x69"}, // Target specific builder
}
// Broadcast with privacy configuration
resp, err := fb.Broadcast(ctx, bundle, targetBlock,
flashbot.WithPrivacy(privacy),
)
if err != nil {
return err
}
fmt.Printf("Bundle sent with privacy hints: %s\n", resp.BundleHash)
return nil
}func sendBundleWithExpiration(ctx context.Context, fb flashbot.IFlashbot, bundle *flashbot.Bundle, targetBlock uint64) error {
// Bundle expires after 10 blocks
resp, err := fb.Broadcast(ctx, bundle, targetBlock,
flashbot.WithExpirationDurationInBlocks(10),
)
if err != nil {
return err
}
fmt.Printf("Bundle expires in 10 blocks: %s\n", resp.BundleHash)
return nil
}func estimateOptimalGas(ctx context.Context, fb flashbot.IFlashbot) error {
gasPrice, tip, err := fb.GetGasPrice(ctx)
if err != nil {
return err
}
fmt.Printf("Suggested gas price: %s wei\n", gasPrice.String())
fmt.Printf("Suggested tip: %s wei\n", tip.String())
// Calculate total fee for EIP-1559
totalFee := new(big.Int).Add(gasPrice, tip)
fmt.Printf("Total fee: %s wei\n", totalFee.String())
return nil
}When creating a new Flashbot client, you can configure it with various options:
fb, err := flashbot.New(ctx,
flashbot.WithChainID(flashbot.MainnetChainID), // Set chain ID
flashbot.WithRelayURL(flashbot.MainnetRelayURL), // Set relay URL
flashbot.WithBuilders([]string{"builder0x69"}), // Target specific builders
)WithChainID(chainID uint64): Set the Ethereum chain IDWithRelayURL(url string): Set custom Flashbots relay URLWithBuilders(builders []string): Specify target block builders
Bundle options allow you to customize bundle behavior:
// Privacy configuration
privacy := flashbot.MevSendBundlePrivacy{
Hints: []string{"calldata", "logs"},
Builders: []string{"builder0x69"},
}
// Validity configuration (refunds)
validity := flashbot.MevSendBundleValidity{
Refund: []flashbot.MevSendBundleRefund{
{BodyIdx: 0, Percent: 50.0}, // 50% refund to first transaction
},
}
// Metadata
originID := "my-origin-id"
metadata := flashbot.MevSendBundleMetadata{
OriginID: &originID,
}
// Apply options when broadcasting
resp, err := fb.Broadcast(ctx, bundle, targetBlock,
flashbot.WithPrivacy(privacy),
flashbot.WithValidity(validity),
flashbot.WithMetadata(metadata),
flashbot.WithExpirationDurationInBlocks(25),
)WithPrivacy(privacy MevSendBundlePrivacy): Configure privacy hints and builder targetingWithValidity(validity MevSendBundleValidity): Configure refund settingsWithMetadata(metadata MevSendBundleMetadata): Add metadata to bundleWithExpirationDurationInBlocks(duration uint64): Set expiration in blocksWithExpirationBlock(block uint64): Set specific expiration block
The library uses the default HTTP client by default. You can customize it by modifying the flashbot struct after creation (future enhancement).
The library includes built-in OpenTelemetry tracing. Ensure you have a tracer provider configured:
import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/trace"
)
// Configure your tracer provider
tracerProvider := // ... your tracer provider
otel.SetTracerProvider(tracerProvider)Always check errors and handle them appropriately:
simResp, err := fb.Simulate(ctx, bundle, targetBlock)
if err != nil {
// Handle simulation error
log.Printf("Simulation failed: %v", err)
return err
}
if !simResp.Success {
// Bundle would revert or fail
log.Printf("Bundle would fail in simulation")
return fmt.Errorf("simulation indicates failure")
}- Mainnet: Chain ID
1, Relay URL:https://relay.flashbots.net - Sepolia Testnet: Chain ID
11155111, Relay URL:https://relay-sepolia.flashbots.net
const (
MainnetChainID = 1
SepoliaChainID = 11155111
MainnetRelayURL = "https://relay.flashbots.net"
SepoliaRelayURL = "https://relay-sepolia.flashbots.net"
)The library automatically handles authentication using EIP-191 signing. By default, a new private key is generated for each client instance. The signature is included in the X-Flashbots-Signature header.
Note: The signing key is used for reputation tracking. Consider using a dedicated key for Flashbots operations.
This section outlines planned improvements and areas where contributions are welcome:
- Private Transaction Support: Complete implementation of
SendPrivateTransactionmethod - User Stats API: Implement
GetUserStatsto check reputation and statistics - Bundle Status Tracking: Implement
GetBundleStatsto track bundle inclusion status - Custom Private Key Support: Add
WithPrivateKeyoption for custom signing keys - Ethereum Client Integration: Add
WithEthClientoption for custom Ethereum clients - Custom Logger Support: Add
WithLoggeroption for custom logging - Retry Logic: Implement automatic retry for failed requests
- Rate Limiting: Add rate limiting awareness
- Bundle Cancellation: Implement
eth_cancelBundlesupport - Fee Refund APIs: Implement all fee refund query methods
flashbots_getFeeRefundTotalsByRecipientflashbots_getFeeRefundsByRecipientflashbots_getFeeRefundsByBundleflashbots_getFeeRefundsByBlockflashbots_setFeeRefundRecipient
- Delayed Refund APIs: Implement builder network refund methods
buildernet_getDelayedRefundsbuildernet_getDelayedRefundTotalsByRecipient
- MEV Refund APIs: Implement MEV refund query methods
flashbots_getMevRefundTotalByRecipientflashbots_getMevRefundTotalBySender
- Transaction Status API: Add support for checking transaction status
- WebSocket Support: Add WebSocket connection for real-time updates
- Batch Operations: Support for sending multiple bundles in one request
- Additional Network Support: Add support for other EVM chains
- Bundle Optimization: Add utilities for optimizing bundle ordering
- Gas Price Strategies: Implement different gas price strategies (fast, standard, slow)
- Metrics Export: Add Prometheus metrics export
- Context Timeout Handling: Improve context timeout and cancellation handling
- Documentation: Add more code examples and use cases
- Performance Optimization: Optimize request serialization and parsing
- Integration Tests: Add comprehensive integration tests
- Mock Server: Create mock Flashbots relay server for testing
- Test Coverage: Increase test coverage to >80%
- Benchmark Tests: Add benchmark tests for performance monitoring
- Fuzzing: Add fuzzing tests for edge cases
- API Reference: Generate and host comprehensive API documentation
- Tutorial Series: Create step-by-step tutorials for common use cases
- Video Guides: Create video tutorials for complex scenarios
- Best Practices Guide: Document best practices for MEV protection
We welcome contributions from the community! Here's how you can help:
- Fork the Repository: Click the "Fork" button on GitHub
- Clone Your Fork:
git clone https://github.com/your-username/flashbot.git cd flashbot - Create a Branch:
git checkout -b feature/your-feature-name
- Make Your Changes: Implement your feature or fix
- Write Tests: Add tests for new functionality
- Run Tests:
go test ./... - Check Linting:
golangci-lint run
- Commit Changes:
git commit -m "feat: add your feature description" - Push to Your Fork:
git push origin feature/your-feature-name
- Create Pull Request: Open a PR on GitHub
- Code Style: Follow Go conventions and use
gofmt - Commit Messages: Use conventional commits format (feat:, fix:, docs:, etc.)
- Testing: Ensure all tests pass and add tests for new features
- Documentation: Update README and add code comments for public APIs
- Issues: Check existing issues before creating new ones
- Discussions: Use GitHub Discussions for questions and proposals
- Implementing missing API methods (see TODO section)
- Writing comprehensive tests
- Improving documentation and examples
- Performance optimization
- Adding support for additional networks
- Creating tutorials and guides
- All PRs require at least one approval
- Maintainers will review code for quality, tests, and documentation
- Address review comments promptly
- Once approved, maintainers will merge your PR
- GitHub Issues: Report bugs or request features via GitHub Issues
- Discussions: Ask questions in GitHub Discussions
- Documentation: Check the Flashbots Documentation for API details
When reporting bugs, please include:
- Go version
- Library version
- Steps to reproduce
- Expected behavior
- Actual behavior
- Error messages or logs
For feature requests, please include:
- Use case description
- Proposed API design
- Benefits and potential drawbacks
- Implementation considerations
This project is licensed under the MIT License - see the LICENSE file for details.
- Flashbots Documentation - Official Flashbots documentation
- go-ethereum - Official Go Ethereum client
- Flashbots Protect - Flashbots Protect RPC endpoint
- Flashbots for building the MEV infrastructure
- The Ethereum community for continuous innovation
- All contributors who help improve this library
Made with ❤️ for the Ethereum and MEV community