• Stars
    star
    548
  • Rank 81,119 (Top 2 %)
  • Language
    TypeScript
  • Created almost 4 years ago
  • Updated 8 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Flashbots provider for ethers.js

ethers-provider-flashbots-bundle

This repository contains the FlashbotsBundleProvider ethers.js provider, an additional Provider to ethers.js to enable high-level access to eth_sendBundle and eth_callBundle rpc endpoint on mev-relay. mev-relay is a hosted service; it is not necessary to run mev-relay or mev-geth to proceed with this example.

Flashbots-enabled relays and miners expose two new jsonrpc endpoints: eth_sendBundle and eth_callBundle. Since these are non-standard endpoints, ethers.js and other libraries do not natively support these requests (like getTransactionCount). In order to interact with these endpoints, you will need access to another full-featured (non-Flashbots) endpoint for nonce-calculation, gas estimation, and transaction status.

One key feature this library provides is payload signing, a requirement to submit Flashbot bundles to the mev-relay service. This library takes care of the signing process via the authSigner passed into the constructor. Read more about relay signatures here

This library is not a fully functional ethers.js implementation, just a simple provider class, designed to interact with an existing ethers.js v5 installation.

Example

Install ethers.js and the Flashbots ethers bundle provider.

npm install --save ethers
npm install --save @flashbots/ethers-provider-bundle

Open up a new TypeScript file (this also works with JavaScript if you prefer)

import { providers, Wallet } from "ethers";
import { FlashbotsBundleProvider } from "@flashbots/ethers-provider-bundle";

// Standard json rpc provider directly from ethers.js (NOT Flashbots)
const provider = new providers.JsonRpcProvider({ url: ETHEREUM_RPC_URL }, 1)

// `authSigner` is an Ethereum private key that does NOT store funds and is NOT your bot's primary key.
// This is an identifying key for signing payloads to establish reputation and whitelisting
// In production, this should be used across multiple bundles to build relationship. In this example, we generate a new wallet each time
const authSigner = Wallet.createRandom();

// Flashbots provider requires passing in a standard provider
const flashbotsProvider = await FlashbotsBundleProvider.create(
  provider, // a normal ethers.js provider, to perform gas estimiations and nonce lookups
  authSigner // ethers.js signer wallet, only for signing request payloads, not transactions
)

From here, you have a flashbotsProvider object setup which can now perform either an eth_callBundle (via simulate()) or eth_sendBundle (via sendBundle). Each of these functions act on an array of Bundle Transactions

Bundle Transactions

Both simulate and sendBundle operate on a bundle of strictly-ordered transactions. While the miner requires signed transactions, the provider library will accept a mix of pre-signed transaction and TransactionRequest + Signer transactions (which it will estimate, nonce-calculate, and sign before sending to the mev-relay)

const wallet = new Wallet(PRIVATE_KEY)
const transaction = {
  to: CONTRACT_ADDRESS,
  data: CALL_DATA
}
const transactionBundle = [
    {
      signedTransaction: SIGNED_ORACLE_UPDATE_FROM_PENDING_POOL // serialized signed transaction hex
    },
    {
      signer: wallet, // ethers signer
      transaction: transaction // ethers populated transaction object
    }
  ]

Block Targeting

The last thing required for sendBundle() is block targeting. Every bundle specifically references a single block. If your bundle is valid for multiple blocks (including all blocks until it is mined), sendBundle() must be called for every block, ideally on one of the blocks immediately prior. This gives you a chance to re-evaluate the opportunity you are capturing and re-sign your transactions with a higher nonce, if necessary.

The block should always be a future block, never the current one.

const targetBlockNumber = (await provider.getBlockNumber()) + 1

Gas Prices and EIP-1559

Before EIP-1559 was activated, the most common way for searchers to submit transactions is with gasPrice=0, with an on-chain payment to block.coinbase conditional on the transaction's success. All transactions must pay baseFee now, an attribute of a block. For an example of how to ensure you are using this baseFee, see demo.ts in this repository.

const block = await provider.getBlock(blockNumber)
const maxBaseFeeInFutureBlock = FlashbotsBundleProvider.getMaxBaseFeeInFutureBlock(block.baseFeePerGas, BLOCKS_IN_THE_FUTURE)
const eip1559Transaction = {
    to: wallet.address,
    type: 2,
    maxFeePerGas: PRIORITY_FEE.add(maxBaseFeeInFutureBlock),
    maxPriorityFeePerGas: PRIORITY_FEE,
    gasLimit: 21000,
    data: '0x',
    chainId: CHAIN_ID
}

FlashbotsBundleProvider.getMaxBaseFeeInFutureBlock calculates the maximum baseFee that is possible BLOCKS_IN_THE_FUTURE blocks, given maximum expansion on each block. You won't pay this fee, so long as it is specified as maxFeePerGas, you will only pay the block's baseFee.

Additionally if you have the baseFeePerGas, gasUsed, and gasLimit from the current block and are only targeting one block in the future you can get the exact base fee for the next block using FlashbotsBundleProvider.getBaseFeeInNextBlock. This method implements the math based on the EIP1559 definition

Simulate and Send

Now that we have:

  1. Flashbots Provider flashbotsProvider
  2. Bundle of transactions transactionBundle
  3. Block Number targetBlockNumber

We can run simulations and submit directly to miners, via the mev-relay.

Simulate:

  const signedTransactions = await flashbotsProvider.signBundle(transactionBundle)
  const simulation = await flashbotsProvider.simulate(signedTransactions, targetBlockNumber)
  console.log(JSON.stringify(simulation, null, 2))

Send:

const flashbotsTransactionResponse = await flashbotsProvider.sendBundle(
  transactionBundle,
  targetBlockNumber,
  )

FlashbotsTransactionResponse

After calling sendBundle, this provider will return a Promise of an object with helper functions related to the bundle you submitted.

These functions return metadata available at transaction submission time, as well as the following functions which can wait, track, and simulate the bundle's behavior.

  • bundleTransactions() - An array of transaction descriptions sent to the relay, including hash, nonce, and the raw transaction.
  • receipts() - Returns promise of an array of transaction receipts corresponding to the transaction hashes that were relayed as part of the bundle. Will not wait for block to be mined; could return incomplete information
  • wait() - Returns a promise which will wait for target block number to be reached OR one of the transactions to become invalid due to nonce-issues (including, but not limited to, one of the transactions from your bundle being included too early). Returns the wait resolution as a status enum
  • simulate() - Returns a promise of the transaction simulation, once the proper block height has been reached. Use this function to troubleshoot failing bundles and verify miner profitability

Optional eth_sendBundle arguments

Beyond target block number, an object can be passed in with optional attributes:

{
  minTimestamp, // optional minimum timestamp at which this bundle is valid (inclusive)
  maxTimestamp, // optional maximum timestamp at which this bundle is valid (inclusive)
  revertingTxHashes: [tx1, tx2] // optional list of transaction hashes allowed to revert. Without specifying here, any revert invalidates the entire bundle.
}

minTimestamp / maxTimestamp

While each bundle targets only a single block, you can add a filter for validity based on the block's timestamp. This does not allow for targeting any block number based on a timestamp or instruct miners on what timestamp to use, it merely serves as a secondary filter.

If your bundle is not valid before a certain time or includes an expiring opportunity, setting these values allows the miner to skip bundle processing earlier in the phase.

Additionally, you could target several blocks in the future, but with a strict maxTimestamp, to ensure your bundle is considered for inclusion up to a specific time, regardless of how quickly blocks are mined in that timeframe.

Reverting Transaction Hashes

Transaction bundles will not be considered for inclusion if they include any transactions that revert or fail. While this is normally desirable, there are some advanced use-cases where a searcher might WANT to bring a failing transaction to the chain. This is normally desirable for nonce management. Consider:

Transaction Nonce #1 = Failed (unrelated) token transfer Transaction Nonce #2 = DEX trade

If a searcher wants to bring #2 to the chain, #1 must be included first, and its failure is not related to the desired transaction #2. This is especially common during high gas times.

Optional parameter revertingTxHashes allows a searcher to specify an array of transactions that can (but are not required to) revert.

Paying for your bundle

In addition to paying for a bundle with gas price, bundles can also conditionally pay a miner via: block.coinbase.transfer(_minerReward) or block.coinbase.call{value: _minerReward}("");

(assuming _minerReward is a solidity uint256 with the wei-value to be transferred directly to the miner)

The entire value of the bundle is added up at the end, so not every transaction needs to have a gas price or block.coinbase payment, so long as at least one does, and pays enough to support the gas used in non-paying transactions.

Note: Gas-fees will ONLY benefit your bundle if the transaction is not already present in the mempool. When including a pending transaction in your bundle, it is similar to that transaction having a gas price of 0; other transactions in your bundle will need to pay more for the gas it uses.

Bundle and User Statistics

The Flashbots relay can also return statistics about you as a user (identified solely by your signing address) and any bundle previously submitted.

  • getUserStats() returns aggregate metrics about past performance, including if your signing key is currently eligible for the "high priority" queue. Read more about searcher reputation here

  • getBundleStats(bundleHash, targetBlockNumber) returns data specific to a single bundle submission, including detailed timestamps for the various phases a bundle goes before reaching miners. You can get the bundleHash from the simulation:

    const simulation = await flashbotsProvider.simulate(signedTransactions, targetBlockNumber)
    console.log(simulation.bundleHash)

Investigating Losses

When your bundle fails to land in the specified block, there are many reasons why this could have occurred. For a list of reasons, check out Flashbots Docs : Why didn't my transaction get included?. To aid in troubleshooting, this library offers a method that will simulate a bundle in multiple positions to identify the competing bundle that landed on chain (if there was one) and calculate the relevant pricing.

For usage instructions, check out the demo-research.ts.

How to run demo.ts

Included is a simple demo of how to construct the FlashbotsProvider with auth signer authentication and submit a [non-functional] bundle. This will not yield any mev, but serves as a sample initialization to help integrate into your own functional searcher.

Sending a Private Transaction

To send a single transaction without having to send it as a bundle, use the sendPrivateTransaction function. This method allows us to process transactions faster and more efficiently than regular bundles. When you send a transaction using this method, we will try to send it to miners over the next 25 blocks (up to, but not past the target block number).

const tx = {
    from: wallet.address,
    to: wallet.address,
    value: "0x42",
    gasPrice: BigNumber.from(99).mul(1e9), // 99 gwei
    gasLimit: BigNumber.from(21000),
}
const privateTx = {
    transaction: tx,
    signer: wallet,
}

const res = await flashbotsProvider.sendPrivateTransaction(privateTx)

Optionally, you can set the following parameters to fine-tune your submission:

// highest block number your tx can be included in
const maxBlockNumber = (await provider.getBlockNumber()) + 10;
// timestamp for simulations
const minTimestamp = 1645753192;

const res = await flashbotsProvider.sendPrivateTransaction(
  privateTx, 
  {maxBlockNumber, minTimestamp}
)

Flashbots on Goerli

To test Flashbots before going to mainnet, you can use the Goerli Flashbots relay, which works in conjunction with a Flashbots-enabled Goerli validator. Flashbots on Goerli requires two simple changes:

  1. Ensure your genericProvider passed in to the FlashbotsBundleProvider constructor is connected to Goerli (gas estimates and nonce requests need to correspond to the correct chain):
import { providers } from 'ethers'
const provider = providers.getDefaultProvider('goerli')
  1. Set the relay endpoint to https://relay-goerli.flashbots.net/
const flashbotsProvider = await FlashbotsBundleProvider.create(
  provider,
  authSigner,
  'https://relay-goerli.flashbots.net/',
  'goerli')

More Repositories

1

pm

Everything there is to know about Flashbots
2,482
star
2

simple-arbitrage

Example arbitrage bot using Flashbots
TypeScript
1,963
star
3

mev-boost

MEV-Boost allows Ethereum validators to source high-MEV blocks from a competitive builder marketplace
Go
1,172
star
4

mev-research

Project management for MEV Research
884
star
5

mev-inspect-py

๐Ÿ”Ž an MEV inspector for Ethereum ๐Ÿ”Ž
Python
819
star
6

mev-job-board

Need a bot?
727
star
7

mev-inspect-rs

Discover historic Miner Extractable Value (MEV) opportunities
Rust
548
star
8

mev-boost-relay

MEV-Boost Relay for Ethereum proposer/builder separation (PBS)
Go
417
star
9

builder

Flashbots MEV-Boost Block Builder
Go
409
star
10

web3-flashbots

Web3.py plugin for using Flashbots' bundle APIs
Python
405
star
11

searcher-sponsored-tx

TypeScript
360
star
12

simple-blind-arbitrage

Solidity
343
star
13

searcher-minter

Solidity
229
star
14

mempool-dumpster

Dump all the mempool transactions ๐Ÿ—‘๏ธ โ™ป๏ธ (in Parquet + CSV)
Go
206
star
15

flashbots-docs

TypeScript
190
star
16

suave-geth

Go
188
star
17

rpc-endpoint

Flashbots RPC endpoint, to be used with wallets (eg. MetaMask)
Go
180
star
18

mev-share

Protocol for orderflow auctions
129
star
19

mev-share-client-ts

Client library for Flashbots MEV-share Matchmaker.
TypeScript
116
star
20

hindsight

Retroactively estimate Uniswap-ish MEV on Flashbots MEV-Share by simulating backrun-arbitrages.
Rust
116
star
21

mev-flood

simulates MEV activity from an array of unique searchers; used for testing infra
TypeScript
112
star
22

mev-relay-js

JavaScript
105
star
23

mev-geth-demo

JavaScript
98
star
24

boost-geth-builder

Example builder
Go
93
star
25

mev-share-node

Go
88
star
26

geth-sgx-gramine

Geth-in-SGX provides an example of running go-ethereum in SGX
C
68
star
27

relayscan

Ethereum MEV-Boost Relay Monitoring
Go
67
star
28

eth2-research

Assessing the nature and impact of MEV in eth2.
Jupyter Notebook
66
star
29

mpc-backrun

Proof-of-concept code for backrunning private transactions using MPC.
Python
63
star
30

mev-explore-public

Public repo of MEV-Explore for the community to jam on the dashboard
59
star
31

suapp-examples

SUAVE Application Examples
Go
54
star
32

simple-limit-order-bot

TypeScript
53
star
33

raytracing

Eth2-MEV project with liquid staking (Flashbots-Lido-Nethermind)
Go
52
star
34

builder-playground

Local end-to-end environment for Ethereum L1 block building
Go
51
star
35

reorg-monitor

Ethereum Reorg Monitoring
Go
44
star
36

block-validation-geth

To be deprecated in favor of https://github.com/flashbots/builder
Go
44
star
37

suave-std

Collection of helpful smart contracts to build Suapps
Solidity
43
star
38

rollup-boost

Sidecar to Enable Rollup Extensions
Rust
42
star
39

go-boost-utils

Eth2 builder API types and signing for Golang
Go
41
star
40

suave-specs

โ˜€๏ธ SUAVE Alpha Protocol Specifications
35
star
41

prysm

Our custom Prysm fork for boost relay and builder CL. Sends payload attributes for block building on every slot to trigger building.
Go
34
star
42

sync-proxy

Proxy from consensus client to block builders
Go
33
star
43

go-template

Template for Go projects
Go
33
star
44

suave-viem

Typescript client library to interact with SUAVE.
TypeScript
32
star
45

dowg

Decentralized Orderflow Working Group
31
star
46

suave-andromeda-revm

Andromeda revm execution service
Rust
29
star
47

relay-specs

MEV-Boost Relay API Specs
HTML
28
star
48

prio-load-balancer

Priority JSON-RPC load balancer (with retries, good logging, and other goodies like SGX/SEV attestation support)
Go
27
star
49

mev-proxy

JavaScript
18
star
50

contender

Generate high-volume state contention on EVM-like networks.
Rust
18
star
51

andromeda-sirrah-contracts

forge development env for SUAVE key management
Solidity
18
star
52

flashbots-repository-template

Template to bootstrap and configure new projects maintained by the Flashbots collective
17
star
53

mev-blocks

JavaScript
17
star
54

flashbots-dashboard

TypeScript
17
star
55

flashbots-writings-website

MDX
15
star
56

EIP-712-swap-PoC

Solidity
11
star
57

go-utils

Various reusable Go utilities and modules
Go
11
star
58

eth-sparse-mpt

Caching sparse Merkle Patricia Trie for reth.
Rust
7
star
59

flashbots-airflow-workflows

Python
6
star
60

curve-based-bundle-pricing

Jupyter Notebook
6
star
61

dealer-smart-contract

Integral DEX smart contract
TypeScript
6
star
62

flashbots-data-transparency

Collection, analysis and presentation of Flashbots data.
JavaScript
6
star
63

suave-docs

TypeScript
6
star
64

gramine-andromeda-revm

Python
5
star
65

mev-inspect-logs

Log-based MEV inspections
JavaScript
5
star
66

aleth

C++
5
star
67

research-mev-eip1559

Jupyter Notebook
4
star
68

web3-data-tools

Data tools for Web3
Jupyter Notebook
3
star
69

node-healthchecker

Composite health (sync status) checker for blockchain nodes
Go
3
star
70

yocto-manifests

Repo Manifests for the Yocto Project Build System for reproducible TEE builds
Makefile
3
star
71

builder-olympics-website

HTML
2
star
72

suave-toolchain

JavaScript
2
star
73

protect-explorer

A dashboard designed to illuminate the savings Protect users are enjoying.
TypeScript
2
star
74

prometheus-sns-lambda-slack

Receive prometheus alerts via AWS SNS and publish then to slack channel
Go
2
star
75

flashbots-toolchain

GitHub action to install Flashbots tools
JavaScript
2
star
76

nginx-static-response

nginx image that returns a fixed status code
Dockerfile
1
star
77

yocto-scripts

Shell
1
star
78

rbuilder-relay-measurement

A script to pull data for a specific block number and builder public key from the relay and compare them against those from the builder logs
Python
1
star
79

revm

Revm suited for suave needs
Rust
1
star
80

kube-sidecar-injector

Sidecar injector for k8s
Go
1
star
81

eth-faucet

Faucet for ethereum based chains
Go
1
star